mirror of
https://github.com/gpxstudio/gpx.studio.git
synced 2025-09-02 16:52:31 +00:00
real incremental immer patches
This commit is contained in:
328
gpx/src/gpx.ts
328
gpx/src/gpx.ts
@@ -68,8 +68,7 @@ abstract class GPXTreeNode<T extends GPXTreeElement<any>> extends GPXTreeElement
|
|||||||
|
|
||||||
// Producers
|
// Producers
|
||||||
_reverse(originalNextTimestamp?: Date, newPreviousTimestamp?: Date) {
|
_reverse(originalNextTimestamp?: Date, newPreviousTimestamp?: Date) {
|
||||||
return produce(this, (draft: Draft<GPXTreeNode<T>>) => {
|
let og = getOriginal(this);
|
||||||
let og = getOriginal(draft);
|
|
||||||
if (!originalNextTimestamp && !newPreviousTimestamp) {
|
if (!originalNextTimestamp && !newPreviousTimestamp) {
|
||||||
originalNextTimestamp = og.getEndTimestamp();
|
originalNextTimestamp = og.getEndTimestamp();
|
||||||
newPreviousTimestamp = og.getStartTimestamp();
|
newPreviousTimestamp = og.getStartTimestamp();
|
||||||
@@ -81,7 +80,7 @@ abstract class GPXTreeNode<T extends GPXTreeElement<any>> extends GPXTreeElement
|
|||||||
for (let i = 0; i < og.children.length; i++) {
|
for (let i = 0; i < og.children.length; i++) {
|
||||||
let originalStartTimestamp = og.children[og.children.length - i - 1].getStartTimestamp();
|
let originalStartTimestamp = og.children[og.children.length - i - 1].getStartTimestamp();
|
||||||
|
|
||||||
children[i] = children[i]._reverse(originalNextTimestamp, newPreviousTimestamp);
|
children[i]._reverse(originalNextTimestamp, newPreviousTimestamp);
|
||||||
|
|
||||||
originalNextTimestamp = originalStartTimestamp;
|
originalNextTimestamp = originalStartTimestamp;
|
||||||
newPreviousTimestamp = children[i].getEndTimestamp();
|
newPreviousTimestamp = children[i].getEndTimestamp();
|
||||||
@@ -89,12 +88,11 @@ abstract class GPXTreeNode<T extends GPXTreeElement<any>> extends GPXTreeElement
|
|||||||
|
|
||||||
if (this instanceof GPXFile) {
|
if (this instanceof GPXFile) {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
draft.trk = freeze(children);
|
this.trk = freeze(children);
|
||||||
} else if (this instanceof Track) {
|
} else if (this instanceof Track) {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
draft.trkseg = freeze(children);
|
this.trkseg = freeze(children);
|
||||||
}
|
}
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,8 +113,8 @@ export class GPXFile extends GPXTreeNode<Track>{
|
|||||||
|
|
||||||
attributes: GPXFileAttributes;
|
attributes: GPXFileAttributes;
|
||||||
metadata: Metadata;
|
metadata: Metadata;
|
||||||
readonly wpt: ReadonlyArray<Readonly<Waypoint>>;
|
wpt: Waypoint[];
|
||||||
readonly trk: ReadonlyArray<Track>;
|
trk: Track[];
|
||||||
|
|
||||||
constructor(gpx?: GPXFileType & { _data?: any } | GPXFile) {
|
constructor(gpx?: GPXFileType & { _data?: any } | GPXFile) {
|
||||||
super();
|
super();
|
||||||
@@ -211,51 +209,23 @@ export class GPXFile extends GPXTreeNode<Track>{
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Producers
|
// Producers
|
||||||
replaceTracks(start: number, end: number, tracks: Track[]): [GPXFile, Track[]] {
|
replaceTracks(start: number, end: number, tracks: Track[]): Track[] {
|
||||||
let removed = [];
|
if (this._data.style) {
|
||||||
let result = produce(this, (draft) => {
|
tracks.forEach((track) => track.setStyle(this._data.style, false));
|
||||||
let og = getOriginal(draft); // Read as much as possible from the original object because it is faster
|
|
||||||
if (og._data.style) {
|
|
||||||
tracks = tracks.map((track) => track.setStyle(og._data.style, false));
|
|
||||||
}
|
}
|
||||||
let trk = og.trk.slice();
|
return this.trk.splice(start, end - start + 1, ...tracks);
|
||||||
removed = trk.splice(start, end - start + 1, ...tracks);
|
|
||||||
draft.trk = freeze(trk); // Pre-freeze the array, faster as well
|
|
||||||
});
|
|
||||||
return [result, removed];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
replaceTrackSegments(trackIndex: number, start: number, end: number, segments: TrackSegment[]): [GPXFile, TrackSegment[]] {
|
replaceTrackSegments(trackIndex: number, start: number, end: number, segments: TrackSegment[]): TrackSegment[] {
|
||||||
let removed = [];
|
return this.trk[trackIndex].replaceTrackSegments(start, end, segments);
|
||||||
let result = produce(this, (draft) => {
|
|
||||||
let og = getOriginal(draft); // Read as much as possible from the original object because it is faster
|
|
||||||
let trk = og.trk.slice();
|
|
||||||
let [result, rmv] = trk[trackIndex].replaceTrackSegments(start, end, segments);
|
|
||||||
trk[trackIndex] = result;
|
|
||||||
removed = rmv;
|
|
||||||
draft.trk = freeze(trk); // Pre-freeze the array, faster as well
|
|
||||||
});
|
|
||||||
return [result, removed];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
replaceTrackPoints(trackIndex: number, segmentIndex: number, start: number, end: number, points: TrackPoint[], speed?: number, startTime?: Date) {
|
replaceTrackPoints(trackIndex: number, segmentIndex: number, start: number, end: number, points: TrackPoint[], speed?: number, startTime?: Date) {
|
||||||
return produce(this, (draft) => {
|
this.trk[trackIndex].replaceTrackPoints(segmentIndex, start, end, points, speed, startTime);
|
||||||
let og = getOriginal(draft); // Read as much as possible from the original object because it is faster
|
|
||||||
let trk = og.trk.slice();
|
|
||||||
trk[trackIndex] = trk[trackIndex].replaceTrackPoints(segmentIndex, start, end, points, speed, startTime);
|
|
||||||
draft.trk = freeze(trk); // Pre-freeze the array, faster as well
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
replaceWaypoints(start: number, end: number, waypoints: Waypoint[]): [GPXFile, Waypoint[]] {
|
replaceWaypoints(start: number, end: number, waypoints: Waypoint[]): Waypoint[] {
|
||||||
let removed = [];
|
return this.wpt.splice(start, end - start + 1, ...waypoints);
|
||||||
let result = produce(this, (draft) => {
|
|
||||||
let og = getOriginal(draft); // Read as much as possible from the original object because it is faster
|
|
||||||
let wpt = og.wpt.slice();
|
|
||||||
removed = wpt.splice(start, end - start + 1, ...waypoints);
|
|
||||||
draft.wpt = freeze(wpt); // Pre-freeze the array, faster as well
|
|
||||||
});
|
|
||||||
return [result, removed];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
reverse() {
|
reverse() {
|
||||||
@@ -263,37 +233,24 @@ export class GPXFile extends GPXTreeNode<Track>{
|
|||||||
}
|
}
|
||||||
|
|
||||||
reverseTrack(trackIndex: number) {
|
reverseTrack(trackIndex: number) {
|
||||||
return produce(this, (draft) => {
|
this.trk[trackIndex]._reverse();
|
||||||
let og = getOriginal(draft); // Read as much as possible from the original object because it is faster
|
|
||||||
let trk = og.trk.slice();
|
|
||||||
trk[trackIndex] = trk[trackIndex]._reverse();
|
|
||||||
draft.trk = freeze(trk); // Pre-freeze the array, faster as well
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
reverseTrackSegment(trackIndex: number, segmentIndex: number) {
|
reverseTrackSegment(trackIndex: number, segmentIndex: number) {
|
||||||
return produce(this, (draft) => {
|
this.trk[trackIndex].reverseTrackSegment(segmentIndex);
|
||||||
let og = getOriginal(draft); // Read as much as possible from the original object because it is faster
|
|
||||||
let trk = og.trk.slice();
|
|
||||||
trk[trackIndex] = trk[trackIndex].reverseTrackSegment(segmentIndex);
|
|
||||||
draft.trk = freeze(trk); // Pre-freeze the array, faster as well
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
crop(start: number, end: number, trackIndices?: number[], segmentIndices?: number[]) {
|
crop(start: number, end: number, trackIndices?: number[], segmentIndices?: number[]) {
|
||||||
return produce(this, (draft) => {
|
|
||||||
let og = getOriginal(draft); // Read as much as possible from the original object because it is faster
|
|
||||||
let trk = og.trk.slice();
|
|
||||||
let i = 0;
|
let i = 0;
|
||||||
let trackIndex = 0;
|
let trackIndex = 0;
|
||||||
while (i < trk.length) {
|
while (i < this.trk.length) {
|
||||||
let length = trk[i].getNumberOfTrackPoints();
|
let length = this.trk[i].getNumberOfTrackPoints();
|
||||||
if (trackIndices === undefined || trackIndices.includes(trackIndex)) {
|
if (trackIndices === undefined || trackIndices.includes(trackIndex)) {
|
||||||
if (start >= length || end < 0) {
|
if (start >= length || end < 0) {
|
||||||
trk.splice(i, 1);
|
this.trk.splice(i, 1);
|
||||||
} else {
|
} else {
|
||||||
if (start > 0 || end < length - 1) {
|
if (start > 0 || end < length - 1) {
|
||||||
trk[i] = trk[i].crop(Math.max(0, start), Math.min(length - 1, end), segmentIndices);
|
this.trk[i].crop(Math.max(0, start), Math.min(length - 1, end), segmentIndices);
|
||||||
}
|
}
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
@@ -304,22 +261,17 @@ export class GPXFile extends GPXTreeNode<Track>{
|
|||||||
}
|
}
|
||||||
trackIndex++;
|
trackIndex++;
|
||||||
}
|
}
|
||||||
draft.trk = freeze(trk); // Pre-freeze the array, faster as well
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
clean(bounds: [Coordinates, Coordinates], inside: boolean, deleteTrackPoints: boolean, deleteWaypoints: boolean, trackIndices?: number[], segmentIndices?: number[], waypointIndices?: number[]) {
|
clean(bounds: [Coordinates, Coordinates], inside: boolean, deleteTrackPoints: boolean, deleteWaypoints: boolean, trackIndices?: number[], segmentIndices?: number[], waypointIndices?: number[]) {
|
||||||
return produce(this, (draft) => {
|
|
||||||
let og = getOriginal(draft); // Read as much as possible from the original object because it is faster
|
|
||||||
if (deleteTrackPoints) {
|
if (deleteTrackPoints) {
|
||||||
let trk = og.trk.slice();
|
|
||||||
let i = 0;
|
let i = 0;
|
||||||
let trackIndex = 0;
|
let trackIndex = 0;
|
||||||
while (i < trk.length) {
|
while (i < this.trk.length) {
|
||||||
if (trackIndices === undefined || trackIndices.includes(trackIndex)) {
|
if (trackIndices === undefined || trackIndices.includes(trackIndex)) {
|
||||||
trk[i] = trk[i].clean(bounds, inside, segmentIndices);
|
this.trk[i].clean(bounds, inside, segmentIndices);
|
||||||
if (trk[i].getNumberOfTrackPoints() === 0) {
|
if (this.trk[i].getNumberOfTrackPoints() === 0) {
|
||||||
trk.splice(i, 1);
|
this.trk.splice(i, 1);
|
||||||
} else {
|
} else {
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
@@ -328,9 +280,9 @@ export class GPXFile extends GPXTreeNode<Track>{
|
|||||||
}
|
}
|
||||||
trackIndex++;
|
trackIndex++;
|
||||||
}
|
}
|
||||||
draft.trk = freeze(trk); // Pre-freeze the array, faster as well
|
|
||||||
}
|
}
|
||||||
if (deleteWaypoints) {
|
if (deleteWaypoints) {
|
||||||
|
let og = getOriginal(this); // Read as much as possible from the original object because it is faster
|
||||||
let wpt = og.wpt.filter((point, waypointIndex) => {
|
let wpt = og.wpt.filter((point, waypointIndex) => {
|
||||||
if (waypointIndices === undefined || waypointIndices.includes(waypointIndex)) {
|
if (waypointIndices === undefined || waypointIndices.includes(waypointIndex)) {
|
||||||
let inBounds = point.attributes.lat >= bounds[0].lat && point.attributes.lat <= bounds[1].lat && point.attributes.lon >= bounds[0].lon && point.attributes.lon <= bounds[1].lon;
|
let inBounds = point.attributes.lat >= bounds[0].lat && point.attributes.lat <= bounds[1].lat && point.attributes.lon >= bounds[0].lon && point.attributes.lon <= bounds[1].lon;
|
||||||
@@ -339,76 +291,59 @@ export class GPXFile extends GPXTreeNode<Track>{
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
draft.wpt = freeze(wpt); // Pre-freeze the array, faster as well
|
this.wpt = freeze(wpt); // Pre-freeze the array, faster as well
|
||||||
}
|
}
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
changeTimestamps(startTime: Date, speed: number, ratio: number, trackIndex?: number, segmentIndex?: number) {
|
changeTimestamps(startTime: Date, speed: number, ratio: number, trackIndex?: number, segmentIndex?: number) {
|
||||||
let lastPoint = undefined;
|
let lastPoint = undefined;
|
||||||
return produce(this, (draft) => {
|
this.trk.forEach((track, index) => {
|
||||||
let og = getOriginal(draft); // Read as much as possible from the original object because it is faster
|
|
||||||
let trk = og.trk.map((track, index) => {
|
|
||||||
if (trackIndex === undefined || trackIndex === index) {
|
if (trackIndex === undefined || trackIndex === index) {
|
||||||
return track.changeTimestamps(startTime, speed, ratio, lastPoint, segmentIndex);
|
track.changeTimestamps(startTime, speed, ratio, lastPoint, segmentIndex);
|
||||||
} else {
|
|
||||||
return track;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
draft.trk = freeze(trk); // Pre-freeze the array, faster as well
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setStyle(style: LineStyleExtension) {
|
setStyle(style: LineStyleExtension) {
|
||||||
return produce(this, (draft) => {
|
this.trk.forEach((track) => {
|
||||||
let og = getOriginal(draft); // Read as much as possible from the original object because it is faster
|
track.setStyle(style);
|
||||||
let trk = og.trk.map((track) => track.setStyle(style));
|
});
|
||||||
draft.trk = freeze(trk); // Pre-freeze the array, faster as well
|
if (!this._data.style) {
|
||||||
if (!draft._data.style) {
|
this._data.style = {};
|
||||||
draft._data.style = {};
|
|
||||||
}
|
}
|
||||||
if (style.color) {
|
if (style.color) {
|
||||||
draft._data.style.color = style.color;
|
this._data.style.color = style.color;
|
||||||
}
|
}
|
||||||
if (style.opacity) {
|
if (style.opacity) {
|
||||||
draft._data.style.opacity = style.opacity;
|
this._data.style.opacity = style.opacity;
|
||||||
}
|
}
|
||||||
if (style.weight) {
|
if (style.weight) {
|
||||||
draft._data.style.weight = style.weight;
|
this._data.style.weight = style.weight;
|
||||||
}
|
}
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setHidden(hidden: boolean, trackIndices?: number[], segmentIndices?: number[]) {
|
setHidden(hidden: boolean, trackIndices?: number[], segmentIndices?: number[]) {
|
||||||
return produce(this, (draft) => {
|
|
||||||
let og = getOriginal(draft); // Read as much as possible from the original object because it is faster
|
|
||||||
let allHidden = hidden;
|
let allHidden = hidden;
|
||||||
let trk = og.trk.map((track, index) => {
|
this.trk.forEach((track, index) => {
|
||||||
if (trackIndices === undefined || trackIndices.includes(index)) {
|
if (trackIndices === undefined || trackIndices.includes(index)) {
|
||||||
return track.setHidden(hidden, segmentIndices);
|
track.setHidden(hidden, segmentIndices);
|
||||||
} else {
|
} else {
|
||||||
allHidden = allHidden && (track._data.hidden === true);
|
allHidden = allHidden && (track._data.hidden === true);
|
||||||
return track;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
draft.trk = freeze(trk); // Pre-freeze the array, faster as well
|
this.wpt.forEach((waypoint) => {
|
||||||
|
|
||||||
let wpt = og.wpt.map((waypoint) => {
|
|
||||||
if (trackIndices === undefined && segmentIndices === undefined) {
|
if (trackIndices === undefined && segmentIndices === undefined) {
|
||||||
return waypoint.setHidden(hidden);
|
waypoint.setHidden(hidden);
|
||||||
} else {
|
} else {
|
||||||
allHidden = allHidden && (waypoint._data.hidden === true);
|
allHidden = allHidden && (waypoint._data.hidden === true);
|
||||||
return waypoint;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
draft.wpt = freeze(wpt); // Pre-freeze the array, faster as well
|
|
||||||
|
|
||||||
if (trackIndices === undefined && segmentIndices === undefined) {
|
if (trackIndices === undefined && segmentIndices === undefined) {
|
||||||
draft._data.hiddenWpt = hidden;
|
this._data.hiddenWpt = hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
draft._data.hidden = allHidden;
|
this._data.hidden = allHidden;
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setHiddenWaypoints(hidden: boolean, waypointIndices?: number[]) {
|
setHiddenWaypoints(hidden: boolean, waypointIndices?: number[]) {
|
||||||
@@ -441,14 +376,14 @@ export class GPXFile extends GPXTreeNode<Track>{
|
|||||||
export class Track extends GPXTreeNode<TrackSegment> {
|
export class Track extends GPXTreeNode<TrackSegment> {
|
||||||
[immerable] = true;
|
[immerable] = true;
|
||||||
|
|
||||||
readonly name?: string;
|
name?: string;
|
||||||
readonly cmt?: string;
|
cmt?: string;
|
||||||
readonly desc?: string;
|
desc?: string;
|
||||||
readonly src?: string;
|
src?: string;
|
||||||
readonly link?: Link;
|
link?: Link;
|
||||||
readonly type?: string;
|
type?: string;
|
||||||
readonly trkseg: ReadonlyArray<TrackSegment>;
|
trkseg: TrackSegment[];
|
||||||
readonly extensions?: TrackExtensions;
|
extensions?: TrackExtensions;
|
||||||
|
|
||||||
constructor(track?: TrackType & { _data?: any } | Track) {
|
constructor(track?: TrackType & { _data?: any } | Track) {
|
||||||
super();
|
super();
|
||||||
@@ -523,49 +458,29 @@ export class Track extends GPXTreeNode<TrackSegment> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Producers
|
// Producers
|
||||||
replaceTrackSegments(start: number, end: number, segments: TrackSegment[]): [Track, TrackSegment[]] {
|
replaceTrackSegments(start: number, end: number, segments: TrackSegment[]): TrackSegment[] {
|
||||||
let removed = [];
|
return this.trkseg.splice(start, end - start + 1, ...segments);
|
||||||
let result = produce(this, (draft) => {
|
|
||||||
let og = getOriginal(draft); // Read as much as possible from the original object because it is faster
|
|
||||||
let trkseg = og.trkseg.slice();
|
|
||||||
removed = trkseg.splice(start, end - start + 1, ...segments);
|
|
||||||
draft.trkseg = freeze(trkseg); // Pre-freeze the array, faster as well
|
|
||||||
});
|
|
||||||
return [result, removed];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
replaceTrackPoints(segmentIndex: number, start: number, end: number, points: TrackPoint[], speed?: number, startTime?: Date) {
|
replaceTrackPoints(segmentIndex: number, start: number, end: number, points: TrackPoint[], speed?: number, startTime?: Date) {
|
||||||
return produce(this, (draft) => {
|
this.trkseg[segmentIndex].replaceTrackPoints(start, end, points, speed, startTime);
|
||||||
let og = getOriginal(draft); // Read as much as possible from the original object because it is faster
|
|
||||||
let trkseg = og.trkseg.slice();
|
|
||||||
trkseg[segmentIndex] = trkseg[segmentIndex].replaceTrackPoints(start, end, points, speed, startTime);
|
|
||||||
draft.trkseg = freeze(trkseg); // Pre-freeze the array, faster as well
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
reverseTrackSegment(segmentIndex: number) {
|
reverseTrackSegment(segmentIndex: number) {
|
||||||
return produce(this, (draft) => {
|
this.trkseg[segmentIndex]._reverse(this.trkseg[segmentIndex].getEndTimestamp(), this.trkseg[segmentIndex].getStartTimestamp());
|
||||||
let og = getOriginal(draft); // Read as much as possible from the original object because it is faster
|
|
||||||
let trkseg = og.trkseg.slice();
|
|
||||||
trkseg[segmentIndex] = trkseg[segmentIndex]._reverse(trkseg[segmentIndex].getEndTimestamp(), trkseg[segmentIndex].getStartTimestamp());
|
|
||||||
draft.trkseg = freeze(trkseg); // Pre-freeze the array, faster as well
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
crop(start: number, end: number, segmentIndices?: number[]) {
|
crop(start: number, end: number, segmentIndices?: number[]) {
|
||||||
return produce(this, (draft) => {
|
|
||||||
let og = getOriginal(draft); // Read as much as possible from the original object because it is faster
|
|
||||||
let trkseg = og.trkseg.slice();
|
|
||||||
let i = 0;
|
let i = 0;
|
||||||
let segmentIndex = 0;
|
let segmentIndex = 0;
|
||||||
while (i < trkseg.length) {
|
while (i < this.trkseg.length) {
|
||||||
let length = trkseg[i].getNumberOfTrackPoints();
|
let length = this.trkseg[i].getNumberOfTrackPoints();
|
||||||
if (segmentIndices === undefined || segmentIndices.includes(segmentIndex)) {
|
if (segmentIndices === undefined || segmentIndices.includes(segmentIndex)) {
|
||||||
if (start >= length || end < 0) {
|
if (start >= length || end < 0) {
|
||||||
trkseg.splice(i, 1);
|
this.trkseg.splice(i, 1);
|
||||||
} else {
|
} else {
|
||||||
if (start > 0 || end < length - 1) {
|
if (start > 0 || end < length - 1) {
|
||||||
trkseg[i] = trkseg[i].crop(Math.max(0, start), Math.min(length - 1, end));
|
this.trkseg[i].crop(Math.max(0, start), Math.min(length - 1, end));
|
||||||
}
|
}
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
@@ -576,21 +491,17 @@ export class Track extends GPXTreeNode<TrackSegment> {
|
|||||||
}
|
}
|
||||||
segmentIndex++;
|
segmentIndex++;
|
||||||
}
|
}
|
||||||
draft.trkseg = freeze(trkseg); // Pre-freeze the array, faster as well
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
clean(bounds: [Coordinates, Coordinates], inside: boolean, segmentIndices?: number[]) {
|
clean(bounds: [Coordinates, Coordinates], inside: boolean, segmentIndices?: number[]) {
|
||||||
return produce(this, (draft) => {
|
return produce(this, (draft) => {
|
||||||
let og = getOriginal(draft); // Read as much as possible from the original object because it is faster
|
|
||||||
let trkseg = og.trkseg.slice();
|
|
||||||
let i = 0;
|
let i = 0;
|
||||||
let segmentIndex = 0;
|
let segmentIndex = 0;
|
||||||
while (i < trkseg.length) {
|
while (i < this.trkseg.length) {
|
||||||
if (segmentIndices === undefined || segmentIndices.includes(segmentIndex)) {
|
if (segmentIndices === undefined || segmentIndices.includes(segmentIndex)) {
|
||||||
trkseg[i] = trkseg[i].clean(bounds, inside);
|
this.trkseg[i].clean(bounds, inside);
|
||||||
if (trkseg[i].getNumberOfTrackPoints() === 0) {
|
if (this.trkseg[i].getNumberOfTrackPoints() === 0) {
|
||||||
trkseg.splice(i, 1);
|
this.trkseg.splice(i, 1);
|
||||||
} else {
|
} else {
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
@@ -599,64 +510,48 @@ export class Track extends GPXTreeNode<TrackSegment> {
|
|||||||
}
|
}
|
||||||
segmentIndex++;
|
segmentIndex++;
|
||||||
}
|
}
|
||||||
draft.trkseg = freeze(trkseg); // Pre-freeze the array, faster as well
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
changeTimestamps(startTime: Date, speed: number, ratio: number, lastPoint?: TrackPoint, segmentIndex?: number) {
|
changeTimestamps(startTime: Date, speed: number, ratio: number, lastPoint?: TrackPoint, segmentIndex?: number) {
|
||||||
return produce(this, (draft) => {
|
this.trkseg.forEach((segment, index) => {
|
||||||
let og = getOriginal(draft); // Read as much as possible from the original object because it is faster
|
|
||||||
let trkseg = og.trkseg.slice();
|
|
||||||
trkseg = trkseg.map((segment, index) => {
|
|
||||||
if (segmentIndex === undefined || segmentIndex === index) {
|
if (segmentIndex === undefined || segmentIndex === index) {
|
||||||
let seg = segment.changeTimestamps(startTime, speed, ratio, lastPoint);
|
segment.changeTimestamps(startTime, speed, ratio, lastPoint);
|
||||||
if (seg.trkpt.length > 0) {
|
if (segment.trkpt.length > 0) {
|
||||||
lastPoint = seg.trkpt[seg.trkpt.length - 1];
|
lastPoint = segment.trkpt[segment.trkpt.length - 1];
|
||||||
}
|
}
|
||||||
return seg;
|
|
||||||
} else {
|
|
||||||
return segment;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
draft.trkseg = freeze(trkseg); // Pre-freeze the array, faster as well
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setStyle(style: LineStyleExtension, force: boolean = true) {
|
setStyle(style: LineStyleExtension, force: boolean = true) {
|
||||||
return produce(this, (draft) => {
|
if (!this.extensions) {
|
||||||
if (!draft.extensions) {
|
this.extensions = {};
|
||||||
draft.extensions = {};
|
|
||||||
}
|
}
|
||||||
if (!draft.extensions['gpx_style:line']) {
|
if (!this.extensions['gpx_style:line']) {
|
||||||
draft.extensions['gpx_style:line'] = {};
|
this.extensions['gpx_style:line'] = {};
|
||||||
}
|
}
|
||||||
if (style.color !== undefined && (force || draft.extensions['gpx_style:line'].color === undefined)) {
|
if (style.color !== undefined && (force || this.extensions['gpx_style:line'].color === undefined)) {
|
||||||
draft.extensions['gpx_style:line'].color = style.color;
|
this.extensions['gpx_style:line'].color = style.color;
|
||||||
}
|
}
|
||||||
if (style.opacity !== undefined && (force || draft.extensions['gpx_style:line'].opacity === undefined)) {
|
if (style.opacity !== undefined && (force || this.extensions['gpx_style:line'].opacity === undefined)) {
|
||||||
draft.extensions['gpx_style:line'].opacity = style.opacity;
|
this.extensions['gpx_style:line'].opacity = style.opacity;
|
||||||
}
|
}
|
||||||
if (style.weight !== undefined && (force || draft.extensions['gpx_style:line'].weight === undefined)) {
|
if (style.weight !== undefined && (force || this.extensions['gpx_style:line'].weight === undefined)) {
|
||||||
draft.extensions['gpx_style:line'].weight = style.weight;
|
this.extensions['gpx_style:line'].weight = style.weight;
|
||||||
}
|
}
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setHidden(hidden: boolean, segmentIndices?: number[]) {
|
setHidden(hidden: boolean, segmentIndices?: number[]) {
|
||||||
return produce(this, (draft) => {
|
|
||||||
let og = getOriginal(draft); // Read as much as possible from the original object because it is faster
|
|
||||||
let allHidden = hidden;
|
let allHidden = hidden;
|
||||||
let trkseg = og.trkseg.map((segment, index) => {
|
this.trkseg.forEach((segment, index) => {
|
||||||
if (segmentIndices === undefined || segmentIndices.includes(index)) {
|
if (segmentIndices === undefined || segmentIndices.includes(index)) {
|
||||||
return segment.setHidden(hidden);
|
segment.setHidden(hidden);
|
||||||
} else {
|
} else {
|
||||||
allHidden = allHidden && (segment._data.hidden === true);
|
allHidden = allHidden && (segment._data.hidden === true);
|
||||||
return segment;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
draft.trkseg = freeze(trkseg); // Pre-freeze the array, faster as well
|
this._data.hidden = allHidden;
|
||||||
draft._data.hidden = allHidden;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -664,7 +559,7 @@ export class Track extends GPXTreeNode<TrackSegment> {
|
|||||||
export class TrackSegment extends GPXTreeLeaf {
|
export class TrackSegment extends GPXTreeLeaf {
|
||||||
[immerable] = true;
|
[immerable] = true;
|
||||||
|
|
||||||
readonly trkpt: ReadonlyArray<Readonly<TrackPoint>>;
|
trkpt: TrackPoint[];
|
||||||
|
|
||||||
constructor(segment?: TrackSegmentType & { _data?: any } | TrackSegment) {
|
constructor(segment?: TrackSegmentType & { _data?: any } | TrackSegment) {
|
||||||
super();
|
super();
|
||||||
@@ -871,13 +766,12 @@ export class TrackSegment extends GPXTreeLeaf {
|
|||||||
|
|
||||||
// Producers
|
// Producers
|
||||||
replaceTrackPoints(start: number, end: number, points: TrackPoint[], speed?: number, startTime?: Date) {
|
replaceTrackPoints(start: number, end: number, points: TrackPoint[], speed?: number, startTime?: Date) {
|
||||||
return produce(this, (draft) => {
|
let og = getOriginal(this); // Read as much as possible from the original object because it is faster
|
||||||
let og = getOriginal(draft); // Read as much as possible from the original object because it is faster
|
|
||||||
let trkpt = og.trkpt.slice();
|
let trkpt = og.trkpt.slice();
|
||||||
|
|
||||||
if (speed !== undefined || (trkpt.length > 0 && trkpt[0].time !== undefined)) {
|
if (speed !== undefined || (trkpt.length > 0 && trkpt[0].time !== undefined)) {
|
||||||
if (start > 0 && trkpt[0].time === undefined) {
|
if (start > 0 && trkpt[0].time === undefined) {
|
||||||
trkpt.splice(0, 0, withTimestamps(trkpt.splice(0, start), speed, undefined, startTime));
|
trkpt.splice(0, 0, ...withTimestamps(trkpt.splice(0, start), speed, undefined, startTime));
|
||||||
}
|
}
|
||||||
if (points.length > 0) {
|
if (points.length > 0) {
|
||||||
let last = start > 0 ? trkpt[start - 1] : undefined;
|
let last = start > 0 ? trkpt[start - 1] : undefined;
|
||||||
@@ -890,7 +784,7 @@ export class TrackSegment extends GPXTreeLeaf {
|
|||||||
if (end < trkpt.length - 1) {
|
if (end < trkpt.length - 1) {
|
||||||
let last = points.length > 0 ? points[points.length - 1] : start > 0 ? trkpt[start - 1] : undefined;
|
let last = points.length > 0 ? points[points.length - 1] : start > 0 ? trkpt[start - 1] : undefined;
|
||||||
if (trkpt[end + 1].time === undefined) {
|
if (trkpt[end + 1].time === undefined) {
|
||||||
trkpt.splice(end + 1, 0, withTimestamps(trkpt.splice(end + 1), speed, last, startTime));
|
trkpt.splice(end + 1, 0, ...withTimestamps(trkpt.splice(end + 1), speed, last, startTime));
|
||||||
} else if (last !== undefined && trkpt[end + 1].time < last.time) {
|
} else if (last !== undefined && trkpt[end + 1].time < last.time) {
|
||||||
points = withShiftedAndCompressedTimestamps(points, speed, 1, last);
|
points = withShiftedAndCompressedTimestamps(points, speed, 1, last);
|
||||||
}
|
}
|
||||||
@@ -898,16 +792,20 @@ export class TrackSegment extends GPXTreeLeaf {
|
|||||||
}
|
}
|
||||||
|
|
||||||
trkpt.splice(start, end - start + 1, ...points);
|
trkpt.splice(start, end - start + 1, ...points);
|
||||||
draft.trkpt = freeze(trkpt); // Pre-freeze the array, faster as well
|
this.trkpt = freeze(trkpt); // Pre-freeze the array, faster as well
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_reverse(originalNextTimestamp?: Date, newPreviousTimestamp?: Date) {
|
_reverse(originalNextTimestamp?: Date, newPreviousTimestamp?: Date) {
|
||||||
return produce(this, (draft) => {
|
let og = getOriginal(this); // Read as much as possible from the original object because it is faster
|
||||||
if (originalNextTimestamp !== undefined && newPreviousTimestamp !== undefined) {
|
let originalStartTimestamp = og.getStartTimestamp();
|
||||||
let og = getOriginal(draft); // Read as much as possible from the original object because it is faster
|
|
||||||
|
|
||||||
let originalEndTimestamp = og.getEndTimestamp();
|
let originalEndTimestamp = og.getEndTimestamp();
|
||||||
|
if (!newPreviousTimestamp) {
|
||||||
|
newPreviousTimestamp = originalStartTimestamp;
|
||||||
|
}
|
||||||
|
if (newPreviousTimestamp && originalEndTimestamp && !originalNextTimestamp) {
|
||||||
|
originalNextTimestamp = new Date(newPreviousTimestamp.getTime() + originalEndTimestamp.getTime() - originalStartTimestamp.getTime());
|
||||||
|
}
|
||||||
|
if (originalNextTimestamp !== undefined && newPreviousTimestamp !== undefined && originalEndTimestamp !== undefined) {
|
||||||
let newStartTimestamp = new Date(
|
let newStartTimestamp = new Date(
|
||||||
newPreviousTimestamp.getTime() + originalNextTimestamp.getTime() - originalEndTimestamp.getTime()
|
newPreviousTimestamp.getTime() + originalNextTimestamp.getTime() - originalEndTimestamp.getTime()
|
||||||
);
|
);
|
||||||
@@ -924,30 +822,23 @@ export class TrackSegment extends GPXTreeLeaf {
|
|||||||
|
|
||||||
trkpt.reverse();
|
trkpt.reverse();
|
||||||
|
|
||||||
draft.trkpt = freeze(trkpt); // Pre-freeze the array, faster as well
|
this.trkpt = freeze(trkpt); // Pre-freeze the array, faster as well
|
||||||
} else {
|
} else {
|
||||||
draft.trkpt.reverse();
|
this.trkpt.reverse();
|
||||||
}
|
}
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
crop(start: number, end: number) {
|
crop(start: number, end: number) {
|
||||||
return produce(this, (draft) => {
|
this.trkpt = this.trkpt.slice(start, end + 1);
|
||||||
let og = getOriginal(draft); // Read as much as possible from the original object because it is faster
|
|
||||||
let trkpt = og.trkpt.slice(start, end + 1);
|
|
||||||
draft.trkpt = freeze(trkpt); // Pre-freeze the array, faster as well
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
clean(bounds: [Coordinates, Coordinates], inside: boolean) {
|
clean(bounds: [Coordinates, Coordinates], inside: boolean) {
|
||||||
return produce(this, (draft) => {
|
let og = getOriginal(this); // Read as much as possible from the original object because it is faster
|
||||||
let og = getOriginal(draft); // Read as much as possible from the original object because it is faster
|
|
||||||
let trkpt = og.trkpt.filter((point) => {
|
let trkpt = og.trkpt.filter((point) => {
|
||||||
let inBounds = point.attributes.lat >= bounds[0].lat && point.attributes.lat <= bounds[1].lat && point.attributes.lon >= bounds[0].lon && point.attributes.lon <= bounds[1].lon;
|
let inBounds = point.attributes.lat >= bounds[0].lat && point.attributes.lat <= bounds[1].lat && point.attributes.lon >= bounds[0].lon && point.attributes.lon <= bounds[1].lon;
|
||||||
return inBounds !== inside;
|
return inBounds !== inside;
|
||||||
});
|
});
|
||||||
draft.trkpt = freeze(trkpt); // Pre-freeze the array, faster as well
|
this.trkpt = freeze(trkpt); // Pre-freeze the array, faster as well
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
changeTimestamps(startTime: Date, speed: number, ratio: number, lastPoint?: TrackPoint) {
|
changeTimestamps(startTime: Date, speed: number, ratio: number, lastPoint?: TrackPoint) {
|
||||||
@@ -955,21 +846,18 @@ export class TrackSegment extends GPXTreeLeaf {
|
|||||||
lastPoint = this.trkpt[0].clone();
|
lastPoint = this.trkpt[0].clone();
|
||||||
lastPoint.time = startTime;
|
lastPoint.time = startTime;
|
||||||
}
|
}
|
||||||
return produce(this, (draft) => {
|
|
||||||
let og = getOriginal(draft); // Read as much as possible from the original object because it is faster
|
let og = getOriginal(this); // Read as much as possible from the original object because it is faster
|
||||||
if (og.trkpt.length > 0 && og.trkpt[0].time === undefined) {
|
if (og.trkpt.length > 0 && og.trkpt[0].time === undefined) {
|
||||||
let trkpt = withTimestamps(og.trkpt, speed, lastPoint, startTime);
|
let trkpt = withTimestamps(og.trkpt, speed, lastPoint, startTime);
|
||||||
draft.trkpt = freeze(trkpt); // Pre-freeze the array, faster as well
|
this.trkpt = freeze(trkpt); // Pre-freeze the array, faster as well
|
||||||
} else {
|
} else {
|
||||||
let trkpt = withShiftedAndCompressedTimestamps(og.trkpt, speed, ratio, lastPoint);
|
let trkpt = withShiftedAndCompressedTimestamps(og.trkpt, speed, ratio, lastPoint);
|
||||||
draft.trkpt = freeze(trkpt); // Pre-freeze the array, faster as well
|
this.trkpt = freeze(trkpt); // Pre-freeze the array, faster as well
|
||||||
}
|
}
|
||||||
});
|
|
||||||
}
|
}
|
||||||
setHidden(hidden: boolean) {
|
setHidden(hidden: boolean) {
|
||||||
return produce(this, (draft) => {
|
this._data.hidden = hidden;
|
||||||
draft._data.hidden = hidden;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1122,9 +1010,7 @@ export class Waypoint {
|
|||||||
|
|
||||||
// Producers
|
// Producers
|
||||||
setHidden(hidden: boolean) {
|
setHidden(hidden: boolean) {
|
||||||
return produce(this, (draft) => {
|
this._data.hidden = hidden;
|
||||||
draft._data.hidden = hidden;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1307,7 +1193,7 @@ export function distance(coord1: Coordinates, coord2: Coordinates): number {
|
|||||||
return maxMeters;
|
return maxMeters;
|
||||||
}
|
}
|
||||||
|
|
||||||
function distanceWindowSmoothing(points: ReadonlyArray<Readonly<TrackPoint>>, distanceWindow: number, accumulate: (index: number) => number, compute: (accumulated: number, start: number, end: number) => number, remove?: (index: number) => number): number[] {
|
function distanceWindowSmoothing(points: TrackPoint[], distanceWindow: number, accumulate: (index: number) => number, compute: (accumulated: number, start: number, end: number) => number, remove?: (index: number) => number): number[] {
|
||||||
let result = [];
|
let result = [];
|
||||||
|
|
||||||
let start = 0, end = 0, accumulated = 0;
|
let start = 0, end = 0, accumulated = 0;
|
||||||
@@ -1330,7 +1216,7 @@ function distanceWindowSmoothing(points: ReadonlyArray<Readonly<TrackPoint>>, di
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
function distanceWindowSmoothingWithDistanceAccumulator(points: ReadonlyArray<Readonly<TrackPoint>>, distanceWindow: number, compute: (accumulated: number, start: number, end: number) => number): number[] {
|
function distanceWindowSmoothingWithDistanceAccumulator(points: TrackPoint[], distanceWindow: number, compute: (accumulated: number, start: number, end: number) => number): number[] {
|
||||||
return distanceWindowSmoothing(points, distanceWindow, (index) => index > 0 ? distance(points[index - 1].getCoordinates(), points[index].getCoordinates()) : 0, compute, (index) => distance(points[index].getCoordinates(), points[index + 1].getCoordinates()));
|
return distanceWindowSmoothing(points, distanceWindow, (index) => index > 0 ? distance(points[index - 1].getCoordinates(), points[index].getCoordinates()) : 0, compute, (index) => distance(points[index].getCoordinates(), points[index + 1].getCoordinates()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -5,7 +5,7 @@ export type SimplifiedTrackPoint = { point: TrackPoint, distance?: number };
|
|||||||
|
|
||||||
const earthRadius = 6371008.8;
|
const earthRadius = 6371008.8;
|
||||||
|
|
||||||
export function ramerDouglasPeucker(points: readonly TrackPoint[], epsilon: number = 50, measure: (a: TrackPoint, b: TrackPoint, c: TrackPoint) => number = computeCrossarc): SimplifiedTrackPoint[] {
|
export function ramerDouglasPeucker(points: TrackPoint[], epsilon: number = 50, measure: (a: TrackPoint, b: TrackPoint, c: TrackPoint) => number = computeCrossarc): SimplifiedTrackPoint[] {
|
||||||
if (points.length == 0) {
|
if (points.length == 0) {
|
||||||
return [];
|
return [];
|
||||||
} else if (points.length == 1) {
|
} else if (points.length == 1) {
|
||||||
@@ -24,7 +24,7 @@ export function ramerDouglasPeucker(points: readonly TrackPoint[], epsilon: numb
|
|||||||
return simplified;
|
return simplified;
|
||||||
}
|
}
|
||||||
|
|
||||||
function ramerDouglasPeuckerRecursive(points: readonly TrackPoint[], epsilon: number, measure: (a: TrackPoint, b: TrackPoint, c: TrackPoint) => number, start: number, end: number, simplified: SimplifiedTrackPoint[]) {
|
function ramerDouglasPeuckerRecursive(points: TrackPoint[], epsilon: number, measure: (a: TrackPoint, b: TrackPoint, c: TrackPoint) => number, start: number, end: number, simplified: SimplifiedTrackPoint[]) {
|
||||||
let largest = {
|
let largest = {
|
||||||
index: 0,
|
index: 0,
|
||||||
distance: 0
|
distance: 0
|
||||||
|
@@ -1,8 +1,8 @@
|
|||||||
export type GPXFileType = {
|
export type GPXFileType = {
|
||||||
attributes: GPXFileAttributes;
|
attributes: GPXFileAttributes;
|
||||||
metadata: Metadata;
|
metadata: Metadata;
|
||||||
wpt: ReadonlyArray<WaypointType>;
|
wpt: WaypointType[];
|
||||||
trk: ReadonlyArray<TrackType>;
|
trk: TrackType[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type GPXFileAttributes = {
|
export type GPXFileAttributes = {
|
||||||
@@ -52,7 +52,7 @@ export type TrackType = {
|
|||||||
src?: string;
|
src?: string;
|
||||||
link?: Link;
|
link?: Link;
|
||||||
type?: string;
|
type?: string;
|
||||||
trkseg: ReadonlyArray<TrackSegmentType>;
|
trkseg: TrackSegmentType[];
|
||||||
extensions?: TrackExtensions;
|
extensions?: TrackExtensions;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -67,7 +67,7 @@ export type LineStyleExtension = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type TrackSegmentType = {
|
export type TrackSegmentType = {
|
||||||
trkpt: ReadonlyArray<TrackPointType>;
|
trkpt: TrackPointType[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TrackPointType = {
|
export type TrackPointType = {
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { dbUtils, getFile, getFileIds } from "$lib/db";
|
import { dbUtils, getFile } from "$lib/db";
|
||||||
import { castDraft, freeze } from "immer";
|
import { castDraft, freeze } from "immer";
|
||||||
import { GPXFile, Track, TrackSegment, Waypoint } from "gpx";
|
import { GPXFile, Track, TrackSegment, Waypoint } from "gpx";
|
||||||
import { selection } from "./Selection";
|
import { selection } from "./Selection";
|
||||||
@@ -353,59 +353,41 @@ export function moveItems(fromParent: ListItem, toParent: ListItem, fromItems: L
|
|||||||
let files = [fromParent.getFileId(), toParent.getFileId()];
|
let files = [fromParent.getFileId(), toParent.getFileId()];
|
||||||
let callbacks = [
|
let callbacks = [
|
||||||
(file, context: (GPXFile | Track | TrackSegment | Waypoint[] | Waypoint)[]) => {
|
(file, context: (GPXFile | Track | TrackSegment | Waypoint[] | Waypoint)[]) => {
|
||||||
let newFile = file;
|
|
||||||
fromItems.forEach((item) => {
|
fromItems.forEach((item) => {
|
||||||
if (item instanceof ListTrackItem) {
|
if (item instanceof ListTrackItem) {
|
||||||
let [result, removed] = newFile.replaceTracks(item.getTrackIndex(), item.getTrackIndex(), []);
|
context.push(...file.replaceTracks(item.getTrackIndex(), item.getTrackIndex(), []));
|
||||||
newFile = castDraft(result);
|
|
||||||
context.push(...removed);
|
|
||||||
} else if (item instanceof ListTrackSegmentItem) {
|
} else if (item instanceof ListTrackSegmentItem) {
|
||||||
let [result, removed] = newFile.replaceTrackSegments(item.getTrackIndex(), item.getSegmentIndex(), item.getSegmentIndex(), []);
|
context.push(...file.replaceTrackSegments(item.getTrackIndex(), item.getSegmentIndex(), item.getSegmentIndex(), []));
|
||||||
newFile = castDraft(result);
|
|
||||||
context.push(...removed);
|
|
||||||
} else if (item instanceof ListWaypointsItem) {
|
} else if (item instanceof ListWaypointsItem) {
|
||||||
let [result, removed] = newFile.replaceWaypoints(0, newFile.wpt.length - 1, []);
|
context.push(file.replaceWaypoints(0, newFile.wpt.length - 1, []));
|
||||||
newFile = castDraft(result);
|
|
||||||
context.push(removed);
|
|
||||||
} else if (item instanceof ListWaypointItem) {
|
} else if (item instanceof ListWaypointItem) {
|
||||||
let [result, removed] = newFile.replaceWaypoints(item.getWaypointIndex(), item.getWaypointIndex(), []);
|
context.push(...file.replaceWaypoints(item.getWaypointIndex(), item.getWaypointIndex(), []));
|
||||||
newFile = castDraft(result);
|
|
||||||
context.push(...removed);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
context.reverse();
|
context.reverse();
|
||||||
return newFile;
|
|
||||||
},
|
},
|
||||||
(file, context: (GPXFile | Track | TrackSegment | Waypoint[] | Waypoint)[]) => {
|
(file, context: (GPXFile | Track | TrackSegment | Waypoint[] | Waypoint)[]) => {
|
||||||
let newFile = file;
|
|
||||||
toItems.forEach((item, i) => {
|
toItems.forEach((item, i) => {
|
||||||
if (item instanceof ListTrackItem) {
|
if (item instanceof ListTrackItem) {
|
||||||
if (context[i] instanceof Track) {
|
if (context[i] instanceof Track) {
|
||||||
let [result, _removed] = newFile.replaceTracks(item.getTrackIndex(), item.getTrackIndex() - 1, [context[i]]);
|
file.replaceTracks(item.getTrackIndex(), item.getTrackIndex() - 1, [context[i]]);
|
||||||
newFile = castDraft(result);
|
|
||||||
} else if (context[i] instanceof TrackSegment) {
|
} else if (context[i] instanceof TrackSegment) {
|
||||||
let [result, _removed] = newFile.replaceTracks(item.getTrackIndex(), item.getTrackIndex() - 1, [new Track({
|
file.replaceTracks(item.getTrackIndex(), item.getTrackIndex() - 1, [new Track({
|
||||||
trkseg: [context[i]]
|
trkseg: [context[i]]
|
||||||
})]);
|
})]);
|
||||||
newFile = castDraft(result);
|
|
||||||
}
|
}
|
||||||
} else if (item instanceof ListTrackSegmentItem && context[i] instanceof TrackSegment) {
|
} else if (item instanceof ListTrackSegmentItem && context[i] instanceof TrackSegment) {
|
||||||
let [result, _removed] = newFile.replaceTrackSegments(item.getTrackIndex(), item.getSegmentIndex(), item.getSegmentIndex() - 1, [context[i]]);
|
file.replaceTrackSegments(item.getTrackIndex(), item.getSegmentIndex(), item.getSegmentIndex() - 1, [context[i]]);
|
||||||
newFile = castDraft(result);
|
|
||||||
} else if (item instanceof ListWaypointsItem) {
|
} else if (item instanceof ListWaypointsItem) {
|
||||||
if (Array.isArray(context[i]) && context[i].length > 0 && context[i][0] instanceof Waypoint) {
|
if (Array.isArray(context[i]) && context[i].length > 0 && context[i][0] instanceof Waypoint) {
|
||||||
let [result, _removed] = newFile.replaceWaypoints(newFile.wpt.length, newFile.wpt.length - 1, context[i]);
|
file.replaceWaypoints(file.wpt.length, file.wpt.length - 1, context[i]);
|
||||||
newFile = castDraft(result);
|
|
||||||
} else if (context[i] instanceof Waypoint) {
|
} else if (context[i] instanceof Waypoint) {
|
||||||
let [result, _removed] = newFile.replaceWaypoints(newFile.wpt.length, newFile.wpt.length - 1, [context[i]]);
|
file.replaceWaypoints(file.wpt.length, file.wpt.length - 1, [context[i]]);
|
||||||
newFile = castDraft(result);
|
|
||||||
}
|
}
|
||||||
} else if (item instanceof ListWaypointItem && context[i] instanceof Waypoint) {
|
} else if (item instanceof ListWaypointItem && context[i] instanceof Waypoint) {
|
||||||
let [result, _removed] = newFile.replaceWaypoints(item.getWaypointIndex(), item.getWaypointIndex() - 1, [context[i]]);
|
file.replaceWaypoints(item.getWaypointIndex(), item.getWaypointIndex() - 1, [context[i]]);
|
||||||
newFile = castDraft(result);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return newFile;
|
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -433,12 +415,13 @@ export function moveItems(fromParent: ListItem, toParent: ListItem, fromItems: L
|
|||||||
if (context[i].name) {
|
if (context[i].name) {
|
||||||
newFile.metadata.name = context[i].name;
|
newFile.metadata.name = context[i].name;
|
||||||
}
|
}
|
||||||
newFile = newFile.replaceTracks(0, 0, [context[i]])[0];
|
console.log(context[i]);
|
||||||
|
newFile.replaceTracks(0, 0, [context[i]])[0];
|
||||||
files.set(item.getFileId(), freeze(newFile));
|
files.set(item.getFileId(), freeze(newFile));
|
||||||
} else if (context[i] instanceof TrackSegment) {
|
} else if (context[i] instanceof TrackSegment) {
|
||||||
let newFile = newGPXFile();
|
let newFile = newGPXFile();
|
||||||
newFile._data.id = item.getFileId();
|
newFile._data.id = item.getFileId();
|
||||||
newFile = newFile.replaceTracks(0, 0, [new Track({
|
newFile.replaceTracks(0, 0, [new Track({
|
||||||
trkseg: [context[i]]
|
trkseg: [context[i]]
|
||||||
})])[0];
|
})])[0];
|
||||||
files.set(item.getFileId(), freeze(newFile));
|
files.set(item.getFileId(), freeze(newFile));
|
||||||
|
@@ -27,8 +27,8 @@
|
|||||||
export let node:
|
export let node:
|
||||||
| Map<string, Readable<GPXFileWithStatistics | undefined>>
|
| Map<string, Readable<GPXFileWithStatistics | undefined>>
|
||||||
| GPXTreeElement<AnyGPXTreeElement>
|
| GPXTreeElement<AnyGPXTreeElement>
|
||||||
| ReadonlyArray<Readonly<Waypoint>>
|
| Waypoint[]
|
||||||
| Readonly<Waypoint>;
|
| Waypoint;
|
||||||
export let item: ListItem;
|
export let item: ListItem;
|
||||||
|
|
||||||
let recursive = getContext<boolean>('recursive');
|
let recursive = getContext<boolean>('recursive');
|
||||||
|
@@ -27,7 +27,7 @@
|
|||||||
export let node:
|
export let node:
|
||||||
| Map<string, Readable<GPXFileWithStatistics | undefined>>
|
| Map<string, Readable<GPXFileWithStatistics | undefined>>
|
||||||
| GPXTreeElement<AnyGPXTreeElement>
|
| GPXTreeElement<AnyGPXTreeElement>
|
||||||
| Readonly<Waypoint>;
|
| Waypoint;
|
||||||
export let item: ListItem;
|
export let item: ListItem;
|
||||||
export let waypointRoot: boolean = false;
|
export let waypointRoot: boolean = false;
|
||||||
|
|
||||||
|
@@ -51,10 +51,7 @@
|
|||||||
import MetadataDialog from './MetadataDialog.svelte';
|
import MetadataDialog from './MetadataDialog.svelte';
|
||||||
import StyleDialog from './StyleDialog.svelte';
|
import StyleDialog from './StyleDialog.svelte';
|
||||||
|
|
||||||
export let node:
|
export let node: GPXTreeElement<AnyGPXTreeElement> | Waypoint[] | Waypoint;
|
||||||
| GPXTreeElement<AnyGPXTreeElement>
|
|
||||||
| ReadonlyArray<Readonly<Waypoint>>
|
|
||||||
| Readonly<Waypoint>;
|
|
||||||
export let item: ListItem;
|
export let item: ListItem;
|
||||||
export let label: string | undefined;
|
export let label: string | undefined;
|
||||||
|
|
||||||
@@ -232,9 +229,8 @@
|
|||||||
<ContextMenu.Item
|
<ContextMenu.Item
|
||||||
disabled={!singleSelection}
|
disabled={!singleSelection}
|
||||||
on:click={() =>
|
on:click={() =>
|
||||||
dbUtils.applyToFile(
|
dbUtils.applyToFile(item.getFileId(), (file) =>
|
||||||
item.getFileId(),
|
file.replaceTracks(file.trk.length, file.trk.length, [new Track()])
|
||||||
(file) => file.replaceTracks(file.trk.length, file.trk.length, [new Track()])[0]
|
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<Plus size="16" class="mr-1" />
|
<Plus size="16" class="mr-1" />
|
||||||
@@ -246,15 +242,13 @@
|
|||||||
disabled={!singleSelection}
|
disabled={!singleSelection}
|
||||||
on:click={() => {
|
on:click={() => {
|
||||||
let trackIndex = item.getTrackIndex();
|
let trackIndex = item.getTrackIndex();
|
||||||
dbUtils.applyToFile(
|
dbUtils.applyToFile(item.getFileId(), (file) =>
|
||||||
item.getFileId(),
|
|
||||||
(file) =>
|
|
||||||
file.replaceTrackSegments(
|
file.replaceTrackSegments(
|
||||||
trackIndex,
|
trackIndex,
|
||||||
file.trk[trackIndex].trkseg.length,
|
file.trk[trackIndex].trkseg.length,
|
||||||
file.trk[trackIndex].trkseg.length,
|
file.trk[trackIndex].trkseg.length,
|
||||||
[new TrackSegment()]
|
[new TrackSegment()]
|
||||||
)[0]
|
)
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
@@ -11,10 +11,7 @@
|
|||||||
import { _ } from 'svelte-i18n';
|
import { _ } from 'svelte-i18n';
|
||||||
import { editMetadata } from '$lib/stores';
|
import { editMetadata } from '$lib/stores';
|
||||||
|
|
||||||
export let node:
|
export let node: GPXTreeElement<AnyGPXTreeElement> | Waypoint[] | Waypoint;
|
||||||
| GPXTreeElement<AnyGPXTreeElement>
|
|
||||||
| ReadonlyArray<Readonly<Waypoint>>
|
|
||||||
| Readonly<Waypoint>;
|
|
||||||
export let item: ListItem;
|
export let item: ListItem;
|
||||||
export let open = false;
|
export let open = false;
|
||||||
|
|
||||||
@@ -54,7 +51,6 @@
|
|||||||
file.trk[item.getTrackIndex()].name = name;
|
file.trk[item.getTrackIndex()].name = name;
|
||||||
file.trk[item.getTrackIndex()].desc = description;
|
file.trk[item.getTrackIndex()].desc = description;
|
||||||
}
|
}
|
||||||
return file;
|
|
||||||
});
|
});
|
||||||
open = false;
|
open = false;
|
||||||
}}
|
}}
|
||||||
|
@@ -6,7 +6,6 @@ import { currentPopupWaypoint, deleteWaypoint, waypointPopup } from "./WaypointP
|
|||||||
import { addSelectItem, selectItem, selection } from "$lib/components/file-list/Selection";
|
import { addSelectItem, selectItem, selection } from "$lib/components/file-list/Selection";
|
||||||
import { ListTrackSegmentItem, ListWaypointItem, ListWaypointsItem, ListTrackItem, ListFileItem, ListRootItem } from "$lib/components/file-list/FileList";
|
import { ListTrackSegmentItem, ListWaypointItem, ListWaypointsItem, ListTrackItem, ListFileItem, ListRootItem } from "$lib/components/file-list/FileList";
|
||||||
import type { Waypoint } from "gpx";
|
import type { Waypoint } from "gpx";
|
||||||
import { produce } from "immer";
|
|
||||||
import { resetCursor, setCursor, setGrabbingCursor, setPointerCursor } from "$lib/utils";
|
import { resetCursor, setCursor, setGrabbingCursor, setPointerCursor } from "$lib/utils";
|
||||||
import { font } from "$lib/assets/layers";
|
import { font } from "$lib/assets/layers";
|
||||||
import { selectedWaypoint } from "$lib/components/toolbar/tools/Waypoint.svelte";
|
import { selectedWaypoint } from "$lib/components/toolbar/tools/Waypoint.svelte";
|
||||||
@@ -231,13 +230,13 @@ export class GPXLayer {
|
|||||||
resetCursor();
|
resetCursor();
|
||||||
marker.getElement().style.cursor = '';
|
marker.getElement().style.cursor = '';
|
||||||
dbUtils.applyToFile(this.fileId, (file) => {
|
dbUtils.applyToFile(this.fileId, (file) => {
|
||||||
return produce(file, (draft) => {
|
|
||||||
let latLng = marker.getLngLat();
|
let latLng = marker.getLngLat();
|
||||||
draft.wpt[marker._waypoint._data.index].setCoordinates({
|
let wpt = file.wpt[marker._waypoint._data.index];
|
||||||
|
wpt.setCoordinates({
|
||||||
lat: latLng.lat,
|
lat: latLng.lat,
|
||||||
lon: latLng.lng
|
lon: latLng.lng
|
||||||
});
|
});
|
||||||
});
|
wpt.ele = this.map.queryTerrainElevation([latLng.lng, latLng.lat], { exaggerated: false }) ?? 0;
|
||||||
});
|
});
|
||||||
dragEndTimestamp = Date.now()
|
dragEndTimestamp = Date.now()
|
||||||
});
|
});
|
||||||
|
@@ -21,5 +21,5 @@ export const waypointPopup = new mapboxgl.Popup({
|
|||||||
});
|
});
|
||||||
|
|
||||||
export function deleteWaypoint(fileId: string, waypointIndex: number) {
|
export function deleteWaypoint(fileId: string, waypointIndex: number) {
|
||||||
dbUtils.applyToFile(fileId, (file) => file.replaceWaypoints(waypointIndex, waypointIndex, [])[0]);
|
dbUtils.applyToFile(fileId, (file) => file.replaceWaypoints(waypointIndex, waypointIndex, []));
|
||||||
}
|
}
|
@@ -303,16 +303,16 @@
|
|||||||
let fileId = item.getFileId();
|
let fileId = item.getFileId();
|
||||||
dbUtils.applyToFile(fileId, (file) => {
|
dbUtils.applyToFile(fileId, (file) => {
|
||||||
if (item instanceof ListFileItem) {
|
if (item instanceof ListFileItem) {
|
||||||
return file.changeTimestamps(getDate(startDate, startTime), effectiveSpeed, ratio);
|
file.changeTimestamps(getDate(startDate, startTime), effectiveSpeed, ratio);
|
||||||
} else if (item instanceof ListTrackItem) {
|
} else if (item instanceof ListTrackItem) {
|
||||||
return file.changeTimestamps(
|
file.changeTimestamps(
|
||||||
getDate(startDate, startTime),
|
getDate(startDate, startTime),
|
||||||
effectiveSpeed,
|
effectiveSpeed,
|
||||||
ratio,
|
ratio,
|
||||||
item.getTrackIndex()
|
item.getTrackIndex()
|
||||||
);
|
);
|
||||||
} else if (item instanceof ListTrackSegmentItem) {
|
} else if (item instanceof ListTrackSegmentItem) {
|
||||||
return file.changeTimestamps(
|
file.changeTimestamps(
|
||||||
getDate(startDate, startTime),
|
getDate(startDate, startTime),
|
||||||
effectiveSpeed,
|
effectiveSpeed,
|
||||||
ratio,
|
ratio,
|
||||||
|
@@ -104,19 +104,16 @@
|
|||||||
longitude = parseFloat(longitude.toFixed(6));
|
longitude = parseFloat(longitude.toFixed(6));
|
||||||
if ($selectedWaypoint) {
|
if ($selectedWaypoint) {
|
||||||
dbUtils.applyToFile($selectedWaypoint[1], (file) => {
|
dbUtils.applyToFile($selectedWaypoint[1], (file) => {
|
||||||
let waypoint = $selectedWaypoint[0].clone();
|
let wpt = file.wpt[$selectedWaypoint[0]._data.index];
|
||||||
waypoint.name = name;
|
wpt.name = name;
|
||||||
waypoint.desc = description;
|
wpt.desc = description;
|
||||||
waypoint.cmt = description;
|
wpt.cmt = description;
|
||||||
waypoint.setCoordinates({
|
wpt.setCoordinates({
|
||||||
lat: latitude,
|
lat: latitude,
|
||||||
lon: longitude
|
lon: longitude
|
||||||
});
|
});
|
||||||
return file.replaceWaypoints(
|
wpt.ele =
|
||||||
$selectedWaypoint[0]._data.index,
|
get(map)?.queryTerrainElevation([longitude, latitude], { exaggerated: false }) ?? 0;
|
||||||
$selectedWaypoint[0]._data.index,
|
|
||||||
[waypoint]
|
|
||||||
)[0];
|
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
let fileIds = new Set<string>();
|
let fileIds = new Set<string>();
|
||||||
@@ -134,9 +131,8 @@
|
|||||||
});
|
});
|
||||||
waypoint.ele =
|
waypoint.ele =
|
||||||
get(map)?.queryTerrainElevation([longitude, latitude], { exaggerated: false }) ?? 0;
|
get(map)?.queryTerrainElevation([longitude, latitude], { exaggerated: false }) ?? 0;
|
||||||
dbUtils.applyToFiles(
|
dbUtils.applyToFiles(Array.from(fileIds), (file) =>
|
||||||
Array.from(fileIds),
|
file.replaceWaypoints(file.wpt.length, file.wpt.length, [waypoint])
|
||||||
(file) => file.replaceWaypoints(file.wpt.length, file.wpt.length, [waypoint])[0]
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
selectedWaypoint.set(undefined);
|
selectedWaypoint.set(undefined);
|
||||||
|
@@ -71,7 +71,7 @@
|
|||||||
function createFileWithPoint(e: any) {
|
function createFileWithPoint(e: any) {
|
||||||
if ($selection.size === 0) {
|
if ($selection.size === 0) {
|
||||||
let file = newGPXFile();
|
let file = newGPXFile();
|
||||||
file = file.replaceTrackPoints(0, 0, 0, 0, [
|
file.replaceTrackPoints(0, 0, 0, 0, [
|
||||||
new TrackPoint({
|
new TrackPoint({
|
||||||
attributes: {
|
attributes: {
|
||||||
lat: e.lngLat.lat,
|
lat: e.lngLat.lat,
|
||||||
@@ -79,9 +79,7 @@
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
]);
|
]);
|
||||||
file = produce(file, (draft) => {
|
file._data.id = getFileIds(1)[0];
|
||||||
draft._data.id = getFileIds(1)[0];
|
|
||||||
});
|
|
||||||
dbUtils.add(file);
|
dbUtils.add(file);
|
||||||
selectFileWhenLoaded(file._data.id);
|
selectFileWhenLoaded(file._data.id);
|
||||||
}
|
}
|
||||||
|
@@ -351,7 +351,7 @@ export class RoutingControls {
|
|||||||
} else if (nextAnchor === null) { // Last point, remove trackpoints from previousAnchor
|
} else if (nextAnchor === null) { // Last point, remove trackpoints from previousAnchor
|
||||||
dbUtils.applyToFile(this.fileId, (file) => {
|
dbUtils.applyToFile(this.fileId, (file) => {
|
||||||
let segment = file.getSegment(anchor.trackIndex, anchor.segmentIndex);
|
let segment = file.getSegment(anchor.trackIndex, anchor.segmentIndex);
|
||||||
return file.replaceTrackPoints(anchor.trackIndex, anchor.segmentIndex, previousAnchor.point._data.index + 1, segment.trkpt.length - 1, []);
|
file.replaceTrackPoints(anchor.trackIndex, anchor.segmentIndex, previousAnchor.point._data.index + 1, segment.trkpt.length - 1, []);
|
||||||
});
|
});
|
||||||
} else { // Route between previousAnchor and nextAnchor
|
} else { // Route between previousAnchor and nextAnchor
|
||||||
this.routeBetweenAnchors([previousAnchor, nextAnchor], [previousAnchor.point.getCoordinates(), nextAnchor.point.getCoordinates()]);
|
this.routeBetweenAnchors([previousAnchor, nextAnchor], [previousAnchor.point.getCoordinates(), nextAnchor.point.getCoordinates()]);
|
||||||
@@ -374,8 +374,8 @@ export class RoutingControls {
|
|||||||
|
|
||||||
let segment = anchor.segment;
|
let segment = anchor.segment;
|
||||||
dbUtils.applyToFile(this.fileId, (file) => {
|
dbUtils.applyToFile(this.fileId, (file) => {
|
||||||
let newFile = file.replaceTrackPoints(anchor.trackIndex, anchor.segmentIndex, segment.trkpt.length, segment.trkpt.length - 1, segment.trkpt.slice(0, anchor.point._data.index), speed > 0 ? speed : undefined);
|
file.replaceTrackPoints(anchor.trackIndex, anchor.segmentIndex, segment.trkpt.length, segment.trkpt.length - 1, segment.trkpt.slice(0, anchor.point._data.index), speed > 0 ? speed : undefined);
|
||||||
return newFile.replaceTrackPoints(anchor.trackIndex, anchor.segmentIndex, 0, anchor.point._data.index - 1, []);
|
file.crop(anchor.point._data.index, anchor.point._data.index + segment.trkpt.length - 1, [anchor.trackIndex], [anchor.segmentIndex]);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -412,14 +412,14 @@ export class RoutingControls {
|
|||||||
}
|
}
|
||||||
if (file.trk.length === 0) {
|
if (file.trk.length === 0) {
|
||||||
let track = new Track();
|
let track = new Track();
|
||||||
track = track.replaceTrackPoints(0, 0, 0, [newPoint]);
|
track.replaceTrackPoints(0, 0, 0, [newPoint]);
|
||||||
return file.replaceTracks(0, 0, [track])[0];
|
file.replaceTracks(0, 0, [track])[0];
|
||||||
} else if (file.trk[trackIndex].trkseg.length === 0) {
|
} else if (file.trk[trackIndex].trkseg.length === 0) {
|
||||||
let segment = new TrackSegment();
|
let segment = new TrackSegment();
|
||||||
segment = segment.replaceTrackPoints(0, 0, [newPoint]);
|
segment.replaceTrackPoints(0, 0, [newPoint]);
|
||||||
return file.replaceTrackSegments(trackIndex, 0, 0, [segment])[0];
|
file.replaceTrackSegments(trackIndex, 0, 0, [segment]);
|
||||||
} else {
|
} else {
|
||||||
return file.replaceTrackPoints(trackIndex, segmentIndex, 0, 0, [newPoint]);
|
file.replaceTrackPoints(trackIndex, segmentIndex, 0, 0, [newPoint]);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
@@ -458,11 +458,11 @@ export class RoutingControls {
|
|||||||
|
|
||||||
let lastAnchor = this.anchors[this.anchors.length - 1];
|
let lastAnchor = this.anchors[this.anchors.length - 1];
|
||||||
|
|
||||||
|
let segment = lastAnchor.segment;
|
||||||
dbUtils.applyToFile(this.fileId, (file) => {
|
dbUtils.applyToFile(this.fileId, (file) => {
|
||||||
let segment = original(file).getSegment(lastAnchor.trackIndex, lastAnchor.segmentIndex);
|
|
||||||
let newSegment = segment.clone();
|
let newSegment = segment.clone();
|
||||||
newSegment = newSegment._reverse(segment.getEndTimestamp(), segment.getEndTimestamp());
|
newSegment._reverse(segment.getEndTimestamp(), segment.getEndTimestamp());
|
||||||
return file.replaceTrackPoints(lastAnchor.trackIndex, lastAnchor.segmentIndex, segment.trkpt.length, segment.trkpt.length, newSegment.trkpt.map((point) => point));
|
file.replaceTrackPoints(lastAnchor.trackIndex, lastAnchor.segmentIndex, segment.trkpt.length, segment.trkpt.length, newSegment.trkpt.map((point) => point));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -23,18 +23,14 @@ export function updateAnchorPoints(file: GPXFile) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (segment.trkpt.length > 0) {
|
if (segment.trkpt.length > 0) {
|
||||||
if (!segment.trkpt[0]._data.anchor) { // First point is not an anchor, make it one
|
// Ensure first and last points are anchors and always visible
|
||||||
segment.trkpt[0]._data.anchor = true;
|
segment.trkpt[0]._data.anchor = true;
|
||||||
segment.trkpt[0]._data.zoom = 0;
|
segment.trkpt[0]._data.zoom = 0;
|
||||||
}
|
|
||||||
|
|
||||||
if (!segment.trkpt[segment.trkpt.length - 1]._data.anchor) { // Last point is not an anchor, make it one
|
|
||||||
segment.trkpt[segment.trkpt.length - 1]._data.anchor = true;
|
segment.trkpt[segment.trkpt.length - 1]._data.anchor = true;
|
||||||
segment.trkpt[segment.trkpt.length - 1]._data.zoom = 0;
|
segment.trkpt[segment.trkpt.length - 1]._data.zoom = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
function computeAnchorPoints(segment: TrackSegment) {
|
function computeAnchorPoints(segment: TrackSegment) {
|
||||||
let points = segment.trkpt;
|
let points = segment.trkpt;
|
||||||
|
@@ -179,6 +179,7 @@ function dexieGPXFileStore(id: string): Readable<GPXFileWithStatistics> & { dest
|
|||||||
let store = writable<GPXFileWithStatistics>(undefined);
|
let store = writable<GPXFileWithStatistics>(undefined);
|
||||||
let query = liveQuery(() => db.files.get(id)).subscribe(value => {
|
let query = liveQuery(() => db.files.get(id)).subscribe(value => {
|
||||||
if (value !== undefined) {
|
if (value !== undefined) {
|
||||||
|
console.log('File updated', id);
|
||||||
let gpx = new GPXFile(value);
|
let gpx = new GPXFile(value);
|
||||||
updateAnchorPoints(gpx);
|
updateAnchorPoints(gpx);
|
||||||
|
|
||||||
@@ -259,6 +260,8 @@ function updateSelection(updatedFiles: GPXFile[], deletedFileIds: string[]) {
|
|||||||
|
|
||||||
// Commit the changes to the file state to the database
|
// Commit the changes to the file state to the database
|
||||||
function commitFileStateChange(newFileState: ReadonlyMap<string, GPXFile>, patch: Patch[]) {
|
function commitFileStateChange(newFileState: ReadonlyMap<string, GPXFile>, patch: Patch[]) {
|
||||||
|
console.log(patch);
|
||||||
|
|
||||||
let changedFileIds = getChangedFileIds(patch);
|
let changedFileIds = getChangedFileIds(patch);
|
||||||
let updatedFileIds: string[] = [], deletedFileIds: string[] = [];
|
let updatedFileIds: string[] = [], deletedFileIds: string[] = [];
|
||||||
|
|
||||||
@@ -272,6 +275,7 @@ function commitFileStateChange(newFileState: ReadonlyMap<string, GPXFile>, patch
|
|||||||
|
|
||||||
let updatedFiles = updatedFileIds.map(id => newFileState.get(id)).filter(file => file !== undefined) as GPXFile[];
|
let updatedFiles = updatedFileIds.map(id => newFileState.get(id)).filter(file => file !== undefined) as GPXFile[];
|
||||||
updatedFileIds = updatedFiles.map(file => file._data.id);
|
updatedFileIds = updatedFiles.map(file => file._data.id);
|
||||||
|
console.log(updatedFileIds, deletedFileIds);
|
||||||
|
|
||||||
updateSelection(updatedFiles, deletedFileIds);
|
updateSelection(updatedFiles, deletedFileIds);
|
||||||
|
|
||||||
@@ -284,6 +288,8 @@ function commitFileStateChange(newFileState: ReadonlyMap<string, GPXFile>, patch
|
|||||||
await db.fileids.bulkDelete(deletedFileIds);
|
await db.fileids.bulkDelete(deletedFileIds);
|
||||||
await db.files.bulkDelete(deletedFileIds);
|
await db.files.bulkDelete(deletedFileIds);
|
||||||
}
|
}
|
||||||
|
}).catch((error) => {
|
||||||
|
console.error('Error committing file state change', error);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -309,6 +315,7 @@ liveQuery(() => db.fileids.toArray()).subscribe(dbFileIds => {
|
|||||||
deletedFiles.forEach(id => {
|
deletedFiles.forEach(id => {
|
||||||
$files.get(id)?.destroy();
|
$files.get(id)?.destroy();
|
||||||
$files.delete(id);
|
$files.delete(id);
|
||||||
|
console.log('File removed', id);
|
||||||
});
|
});
|
||||||
return $files;
|
return $files;
|
||||||
});
|
});
|
||||||
@@ -360,12 +367,12 @@ function applyGlobal(callback: (files: Map<string, GPXFile>) => void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Helper function to apply a callback to multiple files
|
// Helper function to apply a callback to multiple files
|
||||||
function applyToFiles(fileIds: string[], callback: (file: WritableDraft<GPXFile>) => GPXFile) {
|
function applyToFiles(fileIds: string[], callback: (file: WritableDraft<GPXFile>) => void) {
|
||||||
const [newFileState, patch, inversePatch] = produceWithPatches(fileState, (draft) => {
|
const [newFileState, patch, inversePatch] = produceWithPatches(fileState, (draft) => {
|
||||||
fileIds.forEach((fileId) => {
|
fileIds.forEach((fileId) => {
|
||||||
let file = draft.get(fileId);
|
let file = draft.get(fileId);
|
||||||
if (file) {
|
if (file) {
|
||||||
draft.set(fileId, castDraft(callback(file)));
|
callback(file);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -376,12 +383,12 @@ function applyToFiles(fileIds: string[], callback: (file: WritableDraft<GPXFile>
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Helper function to apply different callbacks to multiple files
|
// Helper function to apply different callbacks to multiple files
|
||||||
function applyEachToFilesAndGlobal(fileIds: string[], callbacks: ((file: WritableDraft<GPXFile>, context?: any) => GPXFile)[], globalCallback: (files: Map<string, GPXFile>, context?: any) => void, context?: any) {
|
function applyEachToFilesAndGlobal(fileIds: string[], callbacks: ((file: WritableDraft<GPXFile>, context?: any) => void)[], globalCallback: (files: Map<string, GPXFile>, context?: any) => void, context?: any) {
|
||||||
const [newFileState, patch, inversePatch] = produceWithPatches(fileState, (draft) => {
|
const [newFileState, patch, inversePatch] = produceWithPatches(fileState, (draft) => {
|
||||||
fileIds.forEach((fileId, index) => {
|
fileIds.forEach((fileId, index) => {
|
||||||
let file = draft.get(fileId);
|
let file = draft.get(fileId);
|
||||||
if (file) {
|
if (file) {
|
||||||
draft.set(fileId, castDraft(callbacks[index](file, context)));
|
callbacks[index](file, context);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
globalCallback(draft, context);
|
globalCallback(draft, context);
|
||||||
@@ -459,13 +466,13 @@ export const dbUtils = {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
applyToFile: (id: string, callback: (file: WritableDraft<GPXFile>) => GPXFile) => {
|
applyToFile: (id: string, callback: (file: WritableDraft<GPXFile>) => void) => {
|
||||||
applyToFiles([id], callback);
|
applyToFiles([id], callback);
|
||||||
},
|
},
|
||||||
applyToFiles: (ids: string[], callback: (file: WritableDraft<GPXFile>) => GPXFile) => {
|
applyToFiles: (ids: string[], callback: (file: WritableDraft<GPXFile>) => void) => {
|
||||||
applyToFiles(ids, callback);
|
applyToFiles(ids, callback);
|
||||||
},
|
},
|
||||||
applyEachToFilesAndGlobal: (ids: string[], callbacks: ((file: WritableDraft<GPXFile>, context?: any) => GPXFile)[], globalCallback: (files: Map<string, GPXFile>, context?: any) => void, context?: any) => {
|
applyEachToFilesAndGlobal: (ids: string[], callbacks: ((file: WritableDraft<GPXFile>, context?: any) => void)[], globalCallback: (files: Map<string, GPXFile>, context?: any) => void, context?: any) => {
|
||||||
applyEachToFilesAndGlobal(ids, callbacks, globalCallback, context);
|
applyEachToFilesAndGlobal(ids, callbacks, globalCallback, context);
|
||||||
},
|
},
|
||||||
duplicateSelection: () => {
|
duplicateSelection: () => {
|
||||||
@@ -476,36 +483,36 @@ export const dbUtils = {
|
|||||||
let ids = getFileIds(get(settings.fileOrder).length);
|
let ids = getFileIds(get(settings.fileOrder).length);
|
||||||
let index = 0;
|
let index = 0;
|
||||||
applyToOrderedSelectedItemsFromFile((fileId, level, items) => {
|
applyToOrderedSelectedItemsFromFile((fileId, level, items) => {
|
||||||
|
if (level === ListLevel.FILE) {
|
||||||
let file = getFile(fileId);
|
let file = getFile(fileId);
|
||||||
if (file) {
|
if (file) {
|
||||||
let newFile = file;
|
let newFile = file.clone();
|
||||||
if (level === ListLevel.FILE) {
|
|
||||||
newFile = file.clone();
|
|
||||||
newFile._data.id = ids[index++];
|
newFile._data.id = ids[index++];
|
||||||
} else if (level === ListLevel.TRACK) {
|
draft.set(newFile._data.id, freeze(newFile));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let file = draft.get(fileId);
|
||||||
|
if (file) {
|
||||||
|
if (level === ListLevel.TRACK) {
|
||||||
for (let item of items) {
|
for (let item of items) {
|
||||||
let trackIndex = (item as ListTrackItem).getTrackIndex();
|
let trackIndex = (item as ListTrackItem).getTrackIndex();
|
||||||
let [result, _removed] = newFile.replaceTracks(trackIndex + 1, trackIndex, [file.trk[trackIndex].clone()]);
|
file.replaceTracks(trackIndex + 1, trackIndex, [file.trk[trackIndex].clone()]);
|
||||||
newFile = result;
|
|
||||||
}
|
}
|
||||||
} else if (level === ListLevel.SEGMENT) {
|
} else if (level === ListLevel.SEGMENT) {
|
||||||
for (let item of items) {
|
for (let item of items) {
|
||||||
let trackIndex = (item as ListTrackSegmentItem).getTrackIndex();
|
let trackIndex = (item as ListTrackSegmentItem).getTrackIndex();
|
||||||
let segmentIndex = (item as ListTrackSegmentItem).getSegmentIndex();
|
let segmentIndex = (item as ListTrackSegmentItem).getSegmentIndex();
|
||||||
let [result, _removed] = newFile.replaceTrackSegments(trackIndex, segmentIndex + 1, segmentIndex, [file.trk[trackIndex].trkseg[segmentIndex].clone()]);
|
file.replaceTrackSegments(trackIndex, segmentIndex + 1, segmentIndex, [file.trk[trackIndex].trkseg[segmentIndex].clone()]);
|
||||||
newFile = result;
|
|
||||||
}
|
}
|
||||||
} else if (level === ListLevel.WAYPOINTS) {
|
} else if (level === ListLevel.WAYPOINTS) {
|
||||||
let [result, _removed] = newFile.replaceWaypoints(file.wpt.length, file.wpt.length - 1, file.wpt.map((wpt) => wpt.clone()));
|
file.replaceWaypoints(file.wpt.length, file.wpt.length - 1, file.wpt.map((wpt) => wpt.clone()));
|
||||||
newFile = result;
|
|
||||||
} else if (level === ListLevel.WAYPOINT) {
|
} else if (level === ListLevel.WAYPOINT) {
|
||||||
for (let item of items) {
|
for (let item of items) {
|
||||||
let waypointIndex = (item as ListWaypointItem).getWaypointIndex();
|
let waypointIndex = (item as ListWaypointItem).getWaypointIndex();
|
||||||
let [result, _removed] = newFile.replaceWaypoints(waypointIndex + 1, waypointIndex, [file.wpt[waypointIndex].clone()]);
|
file.replaceWaypoints(waypointIndex + 1, waypointIndex, [file.wpt[waypointIndex].clone()]);
|
||||||
newFile = result;
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
draft.set(newFile._data.id, freeze(newFile));
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -516,24 +523,22 @@ export const dbUtils = {
|
|||||||
}
|
}
|
||||||
applyGlobal((draft) => {
|
applyGlobal((draft) => {
|
||||||
applyToOrderedSelectedItemsFromFile((fileId, level, items) => {
|
applyToOrderedSelectedItemsFromFile((fileId, level, items) => {
|
||||||
let file = getFile(fileId);
|
let file = draft.get(fileId);
|
||||||
if (file) {
|
if (file) {
|
||||||
let newFile = file;
|
|
||||||
if (level === ListLevel.FILE) {
|
if (level === ListLevel.FILE) {
|
||||||
newFile = file.reverse();
|
file.reverse();
|
||||||
} else if (level === ListLevel.TRACK) {
|
} else if (level === ListLevel.TRACK) {
|
||||||
for (let item of items) {
|
for (let item of items) {
|
||||||
let trackIndex = (item as ListTrackItem).getTrackIndex();
|
let trackIndex = (item as ListTrackItem).getTrackIndex();
|
||||||
newFile = newFile.reverseTrack(trackIndex);
|
file.reverseTrack(trackIndex);
|
||||||
}
|
}
|
||||||
} else if (level === ListLevel.SEGMENT) {
|
} else if (level === ListLevel.SEGMENT) {
|
||||||
for (let item of items) {
|
for (let item of items) {
|
||||||
let trackIndex = (item as ListTrackSegmentItem).getTrackIndex();
|
let trackIndex = (item as ListTrackSegmentItem).getTrackIndex();
|
||||||
let segmentIndex = (item as ListTrackSegmentItem).getSegmentIndex();
|
let segmentIndex = (item as ListTrackSegmentItem).getSegmentIndex();
|
||||||
newFile = newFile.reverseTrackSegment(trackIndex, segmentIndex);
|
file.reverseTrackSegment(trackIndex, segmentIndex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
draft.set(newFile._data.id, freeze(newFile));
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -553,23 +558,14 @@ export const dbUtils = {
|
|||||||
wpt: []
|
wpt: []
|
||||||
};
|
};
|
||||||
applyToOrderedSelectedItemsFromFile((fileId, level, items) => {
|
applyToOrderedSelectedItemsFromFile((fileId, level, items) => {
|
||||||
let file = getFile(fileId);
|
let file = draft.get(fileId);
|
||||||
if (file) {
|
if (file) {
|
||||||
let newFile = file;
|
|
||||||
if (level === ListLevel.FILE) {
|
if (level === ListLevel.FILE) {
|
||||||
{
|
toMerge.trk.push(...file.replaceTracks(0, file.trk.length - 1, []));
|
||||||
let [result, removed] = newFile.replaceTracks(0, newFile.trk.length - 1, []);
|
toMerge.wpt.push(...file.replaceWaypoints(0, file.wpt.length - 1, []));
|
||||||
toMerge.trk.push(...removed);
|
|
||||||
newFile = result;
|
|
||||||
}
|
|
||||||
{
|
|
||||||
let [result, removed] = newFile.replaceWaypoints(0, newFile.wpt.length - 1, []);
|
|
||||||
toMerge.wpt.push(...removed);
|
|
||||||
newFile = result;
|
|
||||||
}
|
|
||||||
if (first) {
|
if (first) {
|
||||||
target = items[0];
|
target = items[0];
|
||||||
targetFile = newFile;
|
targetFile = file;
|
||||||
} else {
|
} else {
|
||||||
draft.delete(fileId);
|
draft.delete(fileId);
|
||||||
}
|
}
|
||||||
@@ -578,14 +574,11 @@ export const dbUtils = {
|
|||||||
items.forEach((item, index) => {
|
items.forEach((item, index) => {
|
||||||
let trackIndex = (item as ListTrackItem).getTrackIndex();
|
let trackIndex = (item as ListTrackItem).getTrackIndex();
|
||||||
if (index === items.length - 1) { // Order is reversed, so the last track is the first one and the one to keep
|
if (index === items.length - 1) { // Order is reversed, so the last track is the first one and the one to keep
|
||||||
let [result, removed] = newFile.replaceTrackSegments(trackIndex, 0, newFile.trk[trackIndex].trkseg.length - 1, []);
|
toMerge.trkseg.splice(0, 0, ...file.replaceTrackSegments(trackIndex, 0, file.trk[trackIndex].trkseg.length - 1, []));
|
||||||
toMerge.trkseg.splice(0, 0, ...removed);
|
|
||||||
newFile = result;
|
|
||||||
target = item;
|
target = item;
|
||||||
} else {
|
} else {
|
||||||
let [result, removed] = newFile.replaceTracks(trackIndex, trackIndex, []);
|
let removed = file.replaceTracks(trackIndex, trackIndex, []);
|
||||||
toMerge.trkseg.push(...removed[0].trkseg);
|
toMerge.trkseg.push(...removed[0].trkseg);
|
||||||
newFile = result;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else if (level === ListLevel.SEGMENT) {
|
} else if (level === ListLevel.SEGMENT) {
|
||||||
@@ -595,16 +588,10 @@ export const dbUtils = {
|
|||||||
if (index === items.length - 1) { // Order is reversed, so the last segment is the first one and the one to keep
|
if (index === items.length - 1) { // Order is reversed, so the last segment is the first one and the one to keep
|
||||||
target = item;
|
target = item;
|
||||||
}
|
}
|
||||||
let [result, removed] = newFile.replaceTrackSegments(trackIndex, segmentIndex, segmentIndex, []);
|
toMerge.trkseg.splice(0, 0, ...file.replaceTrackSegments(trackIndex, segmentIndex, segmentIndex, []));
|
||||||
toMerge.trkseg.splice(0, 0, ...removed);
|
|
||||||
newFile = result;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (first) {
|
targetFile = file;
|
||||||
targetFile = newFile;
|
|
||||||
} else {
|
|
||||||
draft.set(fileId, freeze(newFile));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
first = false;
|
first = false;
|
||||||
}
|
}
|
||||||
@@ -625,37 +612,47 @@ export const dbUtils = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (toMerge.trk.length > 0) {
|
if (toMerge.trk.length > 0 && toMerge.trk[0].trkseg.length > 0) {
|
||||||
let s = new TrackSegment();
|
let s = toMerge.trk[0].trkseg[0];
|
||||||
toMerge.trk.map((track) => {
|
toMerge.trk.map((track, trackIndex) => {
|
||||||
track.trkseg.forEach((segment) => {
|
track.trkseg.forEach((segment, segmentIndex) => {
|
||||||
s = s.replaceTrackPoints(s.trkpt.length, s.trkpt.length, segment.trkpt.slice(), speed, startTime);
|
if (trackIndex === 0 && segmentIndex === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
s.replaceTrackPoints(s.trkpt.length, s.trkpt.length, segment.trkpt.slice(), speed, startTime);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
toMerge.trk = [toMerge.trk[0].replaceTrackSegments(0, toMerge.trk[0].trkseg.length - 1, [s])[0]];
|
toMerge.trk = [toMerge.trk[0]];
|
||||||
|
toMerge.trk[0].trkseg = [s];
|
||||||
}
|
}
|
||||||
if (toMerge.trkseg.length > 0) {
|
if (toMerge.trkseg.length > 0) {
|
||||||
let s = new TrackSegment();
|
let s = toMerge.trkseg[0];
|
||||||
toMerge.trkseg.forEach((segment) => {
|
toMerge.trkseg.forEach((segment, segmentIndex) => {
|
||||||
s = s.replaceTrackPoints(s.trkpt.length, s.trkpt.length, segment.trkpt.slice(), speed, startTime);
|
if (segmentIndex === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
s.replaceTrackPoints(s.trkpt.length, s.trkpt.length, segment.trkpt.slice(), speed, startTime);
|
||||||
});
|
});
|
||||||
toMerge.trkseg = [s];
|
toMerge.trkseg = [s];
|
||||||
}
|
}
|
||||||
|
console.log(toMerge);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (targetFile) {
|
if (targetFile) {
|
||||||
|
console.log(toMerge, target, targetFile);
|
||||||
if (target instanceof ListFileItem) {
|
if (target instanceof ListFileItem) {
|
||||||
targetFile = targetFile.replaceTracks(0, targetFile.trk.length - 1, toMerge.trk)[0];
|
targetFile.replaceTracks(0, targetFile.trk.length - 1, toMerge.trk)[0];
|
||||||
targetFile = targetFile.replaceWaypoints(0, targetFile.wpt.length - 1, toMerge.wpt)[0];
|
targetFile.replaceWaypoints(0, targetFile.wpt.length - 1, toMerge.wpt)[0];
|
||||||
} else if (target instanceof ListTrackItem) {
|
} else if (target instanceof ListTrackItem) {
|
||||||
let trackIndex = target.getTrackIndex();
|
let trackIndex = target.getTrackIndex();
|
||||||
targetFile = targetFile.replaceTrackSegments(trackIndex, 0, -1, toMerge.trkseg)[0];
|
targetFile.replaceTrackSegments(trackIndex, 0, -1, toMerge.trkseg)[0];
|
||||||
} else if (target instanceof ListTrackSegmentItem) {
|
} else if (target instanceof ListTrackSegmentItem) {
|
||||||
let trackIndex = target.getTrackIndex();
|
let trackIndex = target.getTrackIndex();
|
||||||
let segmentIndex = target.getSegmentIndex();
|
let segmentIndex = target.getSegmentIndex();
|
||||||
targetFile = targetFile.replaceTrackSegments(trackIndex, segmentIndex, segmentIndex - 1, toMerge.trkseg)[0];
|
targetFile.replaceTrackSegments(trackIndex, segmentIndex, segmentIndex - 1, toMerge.trkseg)[0];
|
||||||
}
|
}
|
||||||
draft.set(targetFile._data.id, freeze(targetFile));
|
|
||||||
|
console.log(targetFile);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@@ -665,27 +662,24 @@ export const dbUtils = {
|
|||||||
}
|
}
|
||||||
applyGlobal((draft) => {
|
applyGlobal((draft) => {
|
||||||
applyToOrderedSelectedItemsFromFile((fileId, level, items) => {
|
applyToOrderedSelectedItemsFromFile((fileId, level, items) => {
|
||||||
let file = getFile(fileId);
|
let file = draft.get(fileId);
|
||||||
if (file) {
|
if (file) {
|
||||||
if (level === ListLevel.FILE) {
|
if (level === ListLevel.FILE) {
|
||||||
let length = file.getNumberOfTrackPoints();
|
let length = file.getNumberOfTrackPoints();
|
||||||
if (start >= length || end < 0) {
|
if (start >= length || end < 0) {
|
||||||
draft.delete(fileId);
|
draft.delete(fileId);
|
||||||
} else if (start > 0 || end < length - 1) {
|
} else if (start > 0 || end < length - 1) {
|
||||||
let newFile = file.crop(Math.max(0, start), Math.min(length - 1, end));
|
file.crop(Math.max(0, start), Math.min(length - 1, end));
|
||||||
draft.set(newFile._data.id, freeze(newFile));
|
|
||||||
}
|
}
|
||||||
start -= length;
|
start -= length;
|
||||||
end -= length;
|
end -= length;
|
||||||
} else if (level === ListLevel.TRACK) {
|
} else if (level === ListLevel.TRACK) {
|
||||||
let trackIndices = items.map((item) => (item as ListTrackItem).getTrackIndex());
|
let trackIndices = items.map((item) => (item as ListTrackItem).getTrackIndex());
|
||||||
let newFile = file.crop(start, end, trackIndices);
|
file.crop(start, end, trackIndices);
|
||||||
draft.set(newFile._data.id, freeze(newFile));
|
|
||||||
} else if (level === ListLevel.SEGMENT) {
|
} else if (level === ListLevel.SEGMENT) {
|
||||||
let trackIndices = [(items[0] as ListTrackSegmentItem).getTrackIndex()];
|
let trackIndices = [(items[0] as ListTrackSegmentItem).getTrackIndex()];
|
||||||
let segmentIndices = items.map((item) => (item as ListTrackSegmentItem).getSegmentIndex());
|
let segmentIndices = items.map((item) => (item as ListTrackSegmentItem).getSegmentIndex());
|
||||||
let newFile = file.crop(start, end, trackIndices, segmentIndices);
|
file.crop(start, end, trackIndices, segmentIndices);
|
||||||
draft.set(newFile._data.id, freeze(newFile));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, false);
|
}, false);
|
||||||
@@ -694,9 +688,9 @@ export const dbUtils = {
|
|||||||
extractSelection: () => {
|
extractSelection: () => {
|
||||||
return applyGlobal((draft) => {
|
return applyGlobal((draft) => {
|
||||||
applyToOrderedSelectedItemsFromFile((fileId, level, items) => {
|
applyToOrderedSelectedItemsFromFile((fileId, level, items) => {
|
||||||
|
if (level === ListLevel.FILE) {
|
||||||
let file = getFile(fileId);
|
let file = getFile(fileId);
|
||||||
if (file) {
|
if (file) {
|
||||||
if (level === ListLevel.FILE) {
|
|
||||||
if (file.trk.length > 1) {
|
if (file.trk.length > 1) {
|
||||||
let fileIds = getFileIds(file.trk.length);
|
let fileIds = getFileIds(file.trk.length);
|
||||||
|
|
||||||
@@ -724,27 +718,24 @@ export const dbUtils = {
|
|||||||
});
|
});
|
||||||
|
|
||||||
file.trk.forEach((track, index) => {
|
file.trk.forEach((track, index) => {
|
||||||
|
let newFile = file.clone();
|
||||||
let tracks = track.trkseg.map((segment, segmentIndex) => {
|
let tracks = track.trkseg.map((segment, segmentIndex) => {
|
||||||
let t = track.replaceTrackSegments(0, track.trkseg.length - 1, [segment])[0];
|
let t = track.clone();
|
||||||
|
t.replaceTrackSegments(0, track.trkseg.length - 1, [segment])[0];
|
||||||
if (track.name) {
|
if (track.name) {
|
||||||
t = produce(t, (t) => {
|
|
||||||
t.name = `${track.name} (${segmentIndex + 1})`;
|
t.name = `${track.name} (${segmentIndex + 1})`;
|
||||||
});
|
|
||||||
}
|
}
|
||||||
return t;
|
return t;
|
||||||
});
|
});
|
||||||
let newFile = file.replaceTracks(0, file.trk.length - 1, tracks)[0];
|
newFile.replaceTracks(0, file.trk.length - 1, tracks)[0];
|
||||||
newFile = newFile.replaceWaypoints(0, file.wpt.length - 1, closest.filter((c) => c.index.includes(index)).map((c) => file.wpt[c.wptIndex]))[0];
|
newFile.replaceWaypoints(0, file.wpt.length - 1, closest.filter((c) => c.index.includes(index)).map((c) => file.wpt[c.wptIndex]))[0];
|
||||||
newFile = produce(newFile, (f) => {
|
newFile._data.id = fileIds[index];
|
||||||
f._data.id = fileIds[index];
|
newFile.metadata.name = track.name ?? `${file.metadata.name} (${index + 1})`;
|
||||||
f.metadata.name = track.name ?? `${file.metadata.name} (${index + 1})`;
|
|
||||||
});
|
|
||||||
draft.set(newFile._data.id, freeze(newFile));
|
draft.set(newFile._data.id, freeze(newFile));
|
||||||
});
|
});
|
||||||
} else if (file.trk.length === 1) {
|
} else if (file.trk.length === 1) {
|
||||||
let fileIds = getFileIds(file.trk[0].trkseg.length);
|
let fileIds = getFileIds(file.trk[0].trkseg.length);
|
||||||
|
|
||||||
|
|
||||||
let closest = file.wpt.map((wpt, wptIndex) => {
|
let closest = file.wpt.map((wpt, wptIndex) => {
|
||||||
return {
|
return {
|
||||||
wptIndex: wptIndex,
|
wptIndex: wptIndex,
|
||||||
@@ -767,33 +758,32 @@ export const dbUtils = {
|
|||||||
});
|
});
|
||||||
|
|
||||||
file.trk[0].trkseg.forEach((segment, index) => {
|
file.trk[0].trkseg.forEach((segment, index) => {
|
||||||
let newFile = file.replaceTrackSegments(0, 0, file.trk[0].trkseg.length - 1, [segment])[0];
|
let newFile = file.clone();
|
||||||
newFile = newFile.replaceWaypoints(0, file.wpt.length - 1, closest.filter((c) => c.index.includes(index)).map((c) => file.wpt[c.wptIndex]))[0];
|
newFile.replaceTrackSegments(0, 0, file.trk[0].trkseg.length - 1, [segment])[0];
|
||||||
newFile = produce(newFile, (f) => {
|
newFile.replaceWaypoints(0, file.wpt.length - 1, closest.filter((c) => c.index.includes(index)).map((c) => file.wpt[c.wptIndex]))[0];
|
||||||
f._data.id = fileIds[index];
|
newFile._data.id = fileIds[index];
|
||||||
f.metadata.name = `${file.trk[0].name ?? file.metadata.name} (${index + 1})`;
|
newFile.metadata.name = `${file.trk[0].name ?? file.metadata.name} (${index + 1})`;
|
||||||
});
|
|
||||||
draft.set(newFile._data.id, freeze(newFile));
|
draft.set(newFile._data.id, freeze(newFile));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
draft.delete(fileId);
|
draft.delete(fileId);
|
||||||
|
}
|
||||||
} else if (level === ListLevel.TRACK) {
|
} else if (level === ListLevel.TRACK) {
|
||||||
let newFile = file;
|
let file = draft.get(fileId);
|
||||||
|
if (file) {
|
||||||
for (let item of items) {
|
for (let item of items) {
|
||||||
let trackIndex = (item as ListTrackItem).getTrackIndex();
|
let trackIndex = (item as ListTrackItem).getTrackIndex();
|
||||||
let track = file.trk[trackIndex];
|
let track = file.trk[trackIndex];
|
||||||
let tracks = track.trkseg.map((segment, segmentIndex) => {
|
let tracks = track.trkseg.map((segment, segmentIndex) => {
|
||||||
let t = track.clone().replaceTrackSegments(0, track.trkseg.length - 1, [segment])[0];
|
let t = track.clone();
|
||||||
|
t.replaceTrackSegments(0, track.trkseg.length - 1, [segment])[0];
|
||||||
if (track.name) {
|
if (track.name) {
|
||||||
t = produce(t, (t) => {
|
|
||||||
t.name = `${track.name} (${segmentIndex + 1})`;
|
t.name = `${track.name} (${segmentIndex + 1})`;
|
||||||
});
|
|
||||||
}
|
}
|
||||||
return t;
|
return t;
|
||||||
});
|
});
|
||||||
newFile = newFile.replaceTracks(trackIndex, trackIndex, tracks)[0];
|
file.replaceTracks(trackIndex, trackIndex, tracks)[0];
|
||||||
}
|
}
|
||||||
draft.set(newFile._data.id, freeze(newFile));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -825,18 +815,32 @@ export const dbUtils = {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (splitType === SplitType.FILES) {
|
if (splitType === SplitType.FILES) {
|
||||||
let newFile = file.crop(0, absoluteIndex);
|
let newFile = draft.get(fileId);
|
||||||
draft.set(newFile._data.id, freeze(newFile));
|
if (newFile) {
|
||||||
|
newFile.crop(0, absoluteIndex);
|
||||||
let newFile2 = file.clone();
|
let newFile2 = file.clone();
|
||||||
newFile2._data.id = getFileIds(1)[0];
|
newFile2._data.id = getFileIds(1)[0];
|
||||||
newFile2 = newFile2.crop(absoluteIndex, file.getNumberOfTrackPoints() - 1);
|
newFile2.crop(absoluteIndex, file.getNumberOfTrackPoints() - 1);
|
||||||
draft.set(newFile2._data.id, freeze(newFile2));
|
draft.set(newFile2._data.id, freeze(newFile2));
|
||||||
|
}
|
||||||
} else if (splitType === SplitType.TRACKS) {
|
} else if (splitType === SplitType.TRACKS) {
|
||||||
let newFile = file.replaceTracks(trackIndex, trackIndex, [file.trk[trackIndex].crop(0, absoluteIndex), file.trk[trackIndex].crop(absoluteIndex, file.trk[trackIndex].getNumberOfTrackPoints() - 1)])[0];
|
let newFile = draft.get(fileId);
|
||||||
draft.set(newFile._data.id, freeze(newFile));
|
if (newFile) {
|
||||||
|
let start = file.trk[trackIndex].clone();
|
||||||
|
start.crop(0, absoluteIndex);
|
||||||
|
let end = file.trk[trackIndex].clone();
|
||||||
|
end.crop(absoluteIndex, file.trk[trackIndex].getNumberOfTrackPoints() - 1);
|
||||||
|
newFile.replaceTracks(trackIndex, trackIndex, [start, end]);
|
||||||
|
}
|
||||||
} else if (splitType === SplitType.SEGMENTS) {
|
} else if (splitType === SplitType.SEGMENTS) {
|
||||||
let newFile = file.replaceTrackSegments(trackIndex, segmentIndex, segmentIndex, [segment.crop(0, minIndex), segment.crop(minIndex, segment.trkpt.length - 1)])[0];
|
let newFile = draft.get(fileId);
|
||||||
draft.set(newFile._data.id, freeze(newFile));
|
if (newFile) {
|
||||||
|
let start = segment.clone();
|
||||||
|
start.crop(0, minIndex);
|
||||||
|
let end = segment.clone();
|
||||||
|
end.crop(minIndex, segment.trkpt.length - 1);
|
||||||
|
newFile.replaceTrackSegments(trackIndex, segmentIndex, segmentIndex, [start, end]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -847,25 +851,23 @@ export const dbUtils = {
|
|||||||
}
|
}
|
||||||
applyGlobal((draft) => {
|
applyGlobal((draft) => {
|
||||||
applyToOrderedSelectedItemsFromFile((fileId, level, items) => {
|
applyToOrderedSelectedItemsFromFile((fileId, level, items) => {
|
||||||
let file = getFile(fileId);
|
let file = draft.get(fileId);
|
||||||
if (file) {
|
if (file) {
|
||||||
let newFile = file;
|
|
||||||
if (level === ListLevel.FILE) {
|
if (level === ListLevel.FILE) {
|
||||||
newFile = file.clean(bounds, inside, deleteTrackPoints, deleteWaypoints);
|
file.clean(bounds, inside, deleteTrackPoints, deleteWaypoints);
|
||||||
} else if (level === ListLevel.TRACK) {
|
} else if (level === ListLevel.TRACK) {
|
||||||
let trackIndices = items.map((item) => (item as ListTrackItem).getTrackIndex());
|
let trackIndices = items.map((item) => (item as ListTrackItem).getTrackIndex());
|
||||||
newFile = newFile.clean(bounds, inside, deleteTrackPoints, deleteWaypoints, trackIndices);
|
file.clean(bounds, inside, deleteTrackPoints, deleteWaypoints, trackIndices);
|
||||||
} else if (level === ListLevel.SEGMENT) {
|
} else if (level === ListLevel.SEGMENT) {
|
||||||
let trackIndices = [(items[0] as ListTrackSegmentItem).getTrackIndex()];
|
let trackIndices = [(items[0] as ListTrackSegmentItem).getTrackIndex()];
|
||||||
let segmentIndices = items.map((item) => (item as ListTrackSegmentItem).getSegmentIndex());
|
let segmentIndices = items.map((item) => (item as ListTrackSegmentItem).getSegmentIndex());
|
||||||
newFile = newFile.clean(bounds, inside, deleteTrackPoints, deleteWaypoints, trackIndices, segmentIndices);
|
file.clean(bounds, inside, deleteTrackPoints, deleteWaypoints, trackIndices, segmentIndices);
|
||||||
} else if (level === ListLevel.WAYPOINTS) {
|
} else if (level === ListLevel.WAYPOINTS) {
|
||||||
newFile = newFile.clean(bounds, inside, false, deleteWaypoints);
|
file.clean(bounds, inside, false, deleteWaypoints);
|
||||||
} else if (level === ListLevel.WAYPOINT) {
|
} else if (level === ListLevel.WAYPOINT) {
|
||||||
let waypointIndices = items.map((item) => (item as ListWaypointItem).getWaypointIndex());
|
let waypointIndices = items.map((item) => (item as ListWaypointItem).getWaypointIndex());
|
||||||
newFile = newFile.clean(bounds, inside, false, deleteWaypoints, [], [], waypointIndices);
|
file.clean(bounds, inside, false, deleteWaypoints, [], [], waypointIndices);
|
||||||
}
|
}
|
||||||
draft.set(newFile._data.id, freeze(newFile));
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -877,20 +879,18 @@ export const dbUtils = {
|
|||||||
applyGlobal((draft) => {
|
applyGlobal((draft) => {
|
||||||
let allItems = Array.from(itemsAndPoints.keys());
|
let allItems = Array.from(itemsAndPoints.keys());
|
||||||
applyToOrderedItemsFromFile(allItems, (fileId, level, items) => {
|
applyToOrderedItemsFromFile(allItems, (fileId, level, items) => {
|
||||||
let file = getFile(fileId);
|
let file = draft.get(fileId);
|
||||||
if (file) {
|
if (file) {
|
||||||
let newFile = file;
|
|
||||||
for (let item of items) {
|
for (let item of items) {
|
||||||
if (item instanceof ListTrackSegmentItem) {
|
if (item instanceof ListTrackSegmentItem) {
|
||||||
let trackIndex = item.getTrackIndex();
|
let trackIndex = item.getTrackIndex();
|
||||||
let segmentIndex = item.getSegmentIndex();
|
let segmentIndex = item.getSegmentIndex();
|
||||||
let points = itemsAndPoints.get(item);
|
let points = itemsAndPoints.get(item);
|
||||||
if (points) {
|
if (points) {
|
||||||
newFile = newFile.replaceTrackPoints(trackIndex, segmentIndex, 0, file.trk[trackIndex].trkseg[segmentIndex].getNumberOfTrackPoints() - 1, points);
|
file.replaceTrackPoints(trackIndex, segmentIndex, 0, file.trk[trackIndex].trkseg[segmentIndex].getNumberOfTrackPoints() - 1, points);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
draft.set(newFile._data.id, freeze(newFile));
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -901,21 +901,20 @@ export const dbUtils = {
|
|||||||
}
|
}
|
||||||
applyGlobal((draft) => {
|
applyGlobal((draft) => {
|
||||||
applyToOrderedSelectedItemsFromFile((fileId, level, items) => {
|
applyToOrderedSelectedItemsFromFile((fileId, level, items) => {
|
||||||
let file = getFile(fileId);
|
let file = draft.get(fileId);
|
||||||
if (file && (level === ListLevel.FILE || level === ListLevel.TRACK)) {
|
if (file && (level === ListLevel.FILE || level === ListLevel.TRACK)) {
|
||||||
let newFile = file;
|
|
||||||
if (level === ListLevel.FILE) {
|
if (level === ListLevel.FILE) {
|
||||||
newFile = file.setStyle(style);
|
file.setStyle(style);
|
||||||
} else if (level === ListLevel.TRACK) {
|
} else if (level === ListLevel.TRACK) {
|
||||||
|
if (items.length === file.trk.length) {
|
||||||
|
file.setStyle(style);
|
||||||
|
} else {
|
||||||
for (let item of items) {
|
for (let item of items) {
|
||||||
let trackIndex = (item as ListTrackItem).getTrackIndex();
|
let trackIndex = (item as ListTrackItem).getTrackIndex();
|
||||||
newFile = newFile.replaceTracks(trackIndex, trackIndex, [file.trk[trackIndex].setStyle(style)])[0];
|
file.trk[trackIndex].setStyle(style);
|
||||||
}
|
}
|
||||||
if (items.length === file.trk.length) {
|
|
||||||
newFile = newFile.setStyle(style);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
draft.set(newFile._data.id, freeze(newFile));
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -926,25 +925,23 @@ export const dbUtils = {
|
|||||||
}
|
}
|
||||||
applyGlobal((draft) => {
|
applyGlobal((draft) => {
|
||||||
applyToOrderedSelectedItemsFromFile((fileId, level, items) => {
|
applyToOrderedSelectedItemsFromFile((fileId, level, items) => {
|
||||||
let file = getFile(fileId);
|
let file = draft.get(fileId);
|
||||||
if (file) {
|
if (file) {
|
||||||
let newFile = file;
|
|
||||||
if (level === ListLevel.FILE) {
|
if (level === ListLevel.FILE) {
|
||||||
newFile = file.setHidden(hidden);
|
file.setHidden(hidden);
|
||||||
} else if (level === ListLevel.TRACK) {
|
} else if (level === ListLevel.TRACK) {
|
||||||
let trackIndices = items.map((item) => (item as ListTrackItem).getTrackIndex());
|
let trackIndices = items.map((item) => (item as ListTrackItem).getTrackIndex());
|
||||||
newFile = newFile.setHidden(hidden, trackIndices);
|
file.setHidden(hidden, trackIndices);
|
||||||
} else if (level === ListLevel.SEGMENT) {
|
} else if (level === ListLevel.SEGMENT) {
|
||||||
let trackIndices = [(items[0] as ListTrackSegmentItem).getTrackIndex()];
|
let trackIndices = [(items[0] as ListTrackSegmentItem).getTrackIndex()];
|
||||||
let segmentIndices = items.map((item) => (item as ListTrackSegmentItem).getSegmentIndex());
|
let segmentIndices = items.map((item) => (item as ListTrackSegmentItem).getSegmentIndex());
|
||||||
newFile = newFile.setHidden(hidden, trackIndices, segmentIndices);
|
file.setHidden(hidden, trackIndices, segmentIndices);
|
||||||
} else if (level === ListLevel.WAYPOINTS) {
|
} else if (level === ListLevel.WAYPOINTS) {
|
||||||
newFile = newFile.setHiddenWaypoints(hidden);
|
file.setHiddenWaypoints(hidden);
|
||||||
} else if (level === ListLevel.WAYPOINT) {
|
} else if (level === ListLevel.WAYPOINT) {
|
||||||
let waypointIndices = items.map((item) => (item as ListWaypointItem).getWaypointIndex());
|
let waypointIndices = items.map((item) => (item as ListWaypointItem).getWaypointIndex());
|
||||||
newFile = newFile.setHiddenWaypoints(hidden, waypointIndices);
|
file.setHiddenWaypoints(hidden, waypointIndices);
|
||||||
}
|
}
|
||||||
draft.set(newFile._data.id, freeze(newFile));
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -958,33 +955,27 @@ export const dbUtils = {
|
|||||||
if (level === ListLevel.FILE) {
|
if (level === ListLevel.FILE) {
|
||||||
draft.delete(fileId);
|
draft.delete(fileId);
|
||||||
} else {
|
} else {
|
||||||
let file = getFile(fileId);
|
let file = draft.get(fileId);
|
||||||
if (file) {
|
if (file) {
|
||||||
let newFile = file;
|
|
||||||
if (level === ListLevel.TRACK) {
|
if (level === ListLevel.TRACK) {
|
||||||
for (let item of items) {
|
for (let item of items) {
|
||||||
let trackIndex = (item as ListTrackItem).getTrackIndex();
|
let trackIndex = (item as ListTrackItem).getTrackIndex();
|
||||||
let [result, _removed] = newFile.replaceTracks(trackIndex, trackIndex, []);
|
file.replaceTracks(trackIndex, trackIndex, []);
|
||||||
newFile = result;
|
|
||||||
}
|
}
|
||||||
} else if (level === ListLevel.SEGMENT) {
|
} else if (level === ListLevel.SEGMENT) {
|
||||||
for (let item of items) {
|
for (let item of items) {
|
||||||
let trackIndex = (item as ListTrackSegmentItem).getTrackIndex();
|
let trackIndex = (item as ListTrackSegmentItem).getTrackIndex();
|
||||||
let segmentIndex = (item as ListTrackSegmentItem).getSegmentIndex();
|
let segmentIndex = (item as ListTrackSegmentItem).getSegmentIndex();
|
||||||
let [result, _removed] = newFile.replaceTrackSegments(trackIndex, segmentIndex, segmentIndex, []);
|
file.replaceTrackSegments(trackIndex, segmentIndex, segmentIndex, []);
|
||||||
newFile = result;
|
|
||||||
}
|
}
|
||||||
} else if (level === ListLevel.WAYPOINTS) {
|
} else if (level === ListLevel.WAYPOINTS) {
|
||||||
let [result, _removed] = newFile.replaceWaypoints(0, newFile.wpt.length - 1, []);
|
file.replaceWaypoints(0, file.wpt.length - 1, []);
|
||||||
newFile = result;
|
|
||||||
} else if (level === ListLevel.WAYPOINT) {
|
} else if (level === ListLevel.WAYPOINT) {
|
||||||
for (let item of items) {
|
for (let item of items) {
|
||||||
let waypointIndex = (item as ListWaypointItem).getWaypointIndex();
|
let waypointIndex = (item as ListWaypointItem).getWaypointIndex();
|
||||||
let [result, _removed] = newFile.replaceWaypoints(waypointIndex, waypointIndex, []);
|
file.replaceWaypoints(waypointIndex, waypointIndex, []);
|
||||||
newFile = result;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
draft.set(newFile._data.id, freeze(newFile));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
Reference in New Issue
Block a user