diff --git a/gpx/src/gpx.ts b/gpx/src/gpx.ts index 084d90a1..04cbde56 100644 --- a/gpx/src/gpx.ts +++ b/gpx/src/gpx.ts @@ -68,33 +68,31 @@ abstract class GPXTreeNode> extends GPXTreeElement // Producers _reverse(originalNextTimestamp?: Date, newPreviousTimestamp?: Date) { - return produce(this, (draft: Draft>) => { - let og = getOriginal(draft); - if (!originalNextTimestamp && !newPreviousTimestamp) { - originalNextTimestamp = og.getEndTimestamp(); - newPreviousTimestamp = og.getStartTimestamp(); - } + let og = getOriginal(this); + if (!originalNextTimestamp && !newPreviousTimestamp) { + originalNextTimestamp = og.getEndTimestamp(); + newPreviousTimestamp = og.getStartTimestamp(); + } - let children = og.children.slice(); - children.reverse(); + let children = og.children.slice(); + children.reverse(); - for (let i = 0; i < og.children.length; i++) { - let originalStartTimestamp = og.children[og.children.length - i - 1].getStartTimestamp(); + for (let i = 0; i < og.children.length; i++) { + let originalStartTimestamp = og.children[og.children.length - i - 1].getStartTimestamp(); - children[i] = children[i]._reverse(originalNextTimestamp, newPreviousTimestamp); + children[i]._reverse(originalNextTimestamp, newPreviousTimestamp); - originalNextTimestamp = originalStartTimestamp; - newPreviousTimestamp = children[i].getEndTimestamp(); - } + originalNextTimestamp = originalStartTimestamp; + newPreviousTimestamp = children[i].getEndTimestamp(); + } - if (this instanceof GPXFile) { - // @ts-ignore - draft.trk = freeze(children); - } else if (this instanceof Track) { - // @ts-ignore - draft.trkseg = freeze(children); - } - }); + if (this instanceof GPXFile) { + // @ts-ignore + this.trk = freeze(children); + } else if (this instanceof Track) { + // @ts-ignore + this.trkseg = freeze(children); + } } } @@ -115,8 +113,8 @@ export class GPXFile extends GPXTreeNode{ attributes: GPXFileAttributes; metadata: Metadata; - readonly wpt: ReadonlyArray>; - readonly trk: ReadonlyArray; + wpt: Waypoint[]; + trk: Track[]; constructor(gpx?: GPXFileType & { _data?: any } | GPXFile) { super(); @@ -211,51 +209,23 @@ export class GPXFile extends GPXTreeNode{ } // Producers - replaceTracks(start: number, end: number, tracks: Track[]): [GPXFile, Track[]] { - let removed = []; - let result = produce(this, (draft) => { - 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(); - removed = trk.splice(start, end - start + 1, ...tracks); - draft.trk = freeze(trk); // Pre-freeze the array, faster as well - }); - return [result, removed]; + replaceTracks(start: number, end: number, tracks: Track[]): Track[] { + if (this._data.style) { + tracks.forEach((track) => track.setStyle(this._data.style, false)); + } + return this.trk.splice(start, end - start + 1, ...tracks); } - replaceTrackSegments(trackIndex: number, start: number, end: number, segments: TrackSegment[]): [GPXFile, TrackSegment[]] { - let removed = []; - 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]; + replaceTrackSegments(trackIndex: number, start: number, end: number, segments: TrackSegment[]): TrackSegment[] { + return this.trk[trackIndex].replaceTrackSegments(start, end, segments); } replaceTrackPoints(trackIndex: number, segmentIndex: number, start: number, end: number, points: TrackPoint[], speed?: number, startTime?: Date) { - 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(); - trk[trackIndex] = trk[trackIndex].replaceTrackPoints(segmentIndex, start, end, points, speed, startTime); - draft.trk = freeze(trk); // Pre-freeze the array, faster as well - }); + this.trk[trackIndex].replaceTrackPoints(segmentIndex, start, end, points, speed, startTime); } - replaceWaypoints(start: number, end: number, waypoints: Waypoint[]): [GPXFile, Waypoint[]] { - let removed = []; - 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]; + replaceWaypoints(start: number, end: number, waypoints: Waypoint[]): Waypoint[] { + return this.wpt.splice(start, end - start + 1, ...waypoints); } reverse() { @@ -263,152 +233,117 @@ export class GPXFile extends GPXTreeNode{ } reverseTrack(trackIndex: 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(); - trk[trackIndex] = trk[trackIndex]._reverse(); - draft.trk = freeze(trk); // Pre-freeze the array, faster as well - }); + this.trk[trackIndex]._reverse(); } reverseTrackSegment(trackIndex: number, segmentIndex: 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(); - trk[trackIndex] = trk[trackIndex].reverseTrackSegment(segmentIndex); - draft.trk = freeze(trk); // Pre-freeze the array, faster as well - }); + this.trk[trackIndex].reverseTrackSegment(segmentIndex); } 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 trackIndex = 0; + while (i < this.trk.length) { + let length = this.trk[i].getNumberOfTrackPoints(); + if (trackIndices === undefined || trackIndices.includes(trackIndex)) { + if (start >= length || end < 0) { + this.trk.splice(i, 1); + } else { + if (start > 0 || end < length - 1) { + this.trk[i].crop(Math.max(0, start), Math.min(length - 1, end), segmentIndices); + } + i++; + } + start -= length; + end -= length; + } else { + i++; + } + trackIndex++; + } + } + + clean(bounds: [Coordinates, Coordinates], inside: boolean, deleteTrackPoints: boolean, deleteWaypoints: boolean, trackIndices?: number[], segmentIndices?: number[], waypointIndices?: number[]) { + if (deleteTrackPoints) { let i = 0; let trackIndex = 0; - while (i < trk.length) { - let length = trk[i].getNumberOfTrackPoints(); + while (i < this.trk.length) { if (trackIndices === undefined || trackIndices.includes(trackIndex)) { - if (start >= length || end < 0) { - trk.splice(i, 1); + this.trk[i].clean(bounds, inside, segmentIndices); + if (this.trk[i].getNumberOfTrackPoints() === 0) { + this.trk.splice(i, 1); } else { - if (start > 0 || end < length - 1) { - trk[i] = trk[i].crop(Math.max(0, start), Math.min(length - 1, end), segmentIndices); - } i++; } - start -= length; - end -= length; } else { i++; } 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[]) { - return produce(this, (draft) => { - let og = getOriginal(draft); // Read as much as possible from the original object because it is faster - if (deleteTrackPoints) { - let trk = og.trk.slice(); - let i = 0; - let trackIndex = 0; - while (i < trk.length) { - if (trackIndices === undefined || trackIndices.includes(trackIndex)) { - trk[i] = trk[i].clean(bounds, inside, segmentIndices); - if (trk[i].getNumberOfTrackPoints() === 0) { - trk.splice(i, 1); - } else { - i++; - } - } else { - i++; - } - trackIndex++; + } + 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) => { + 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; + return inBounds !== inside; + } else { + return true; } - draft.trk = freeze(trk); // Pre-freeze the array, faster as well - } - if (deleteWaypoints) { - let wpt = og.wpt.filter((point, 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; - return inBounds !== inside; - } else { - 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) { let lastPoint = undefined; - 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.map((track, index) => { - if (trackIndex === undefined || trackIndex === index) { - return track.changeTimestamps(startTime, speed, ratio, lastPoint, segmentIndex); - } else { - return track; - } - }); - draft.trk = freeze(trk); // Pre-freeze the array, faster as well + this.trk.forEach((track, index) => { + if (trackIndex === undefined || trackIndex === index) { + track.changeTimestamps(startTime, speed, ratio, lastPoint, segmentIndex); + } }); } setStyle(style: LineStyleExtension) { - 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.map((track) => track.setStyle(style)); - draft.trk = freeze(trk); // Pre-freeze the array, faster as well - if (!draft._data.style) { - draft._data.style = {}; - } - if (style.color) { - draft._data.style.color = style.color; - } - if (style.opacity) { - draft._data.style.opacity = style.opacity; - } - if (style.weight) { - draft._data.style.weight = style.weight; - } + this.trk.forEach((track) => { + track.setStyle(style); }); + if (!this._data.style) { + this._data.style = {}; + } + if (style.color) { + this._data.style.color = style.color; + } + if (style.opacity) { + this._data.style.opacity = style.opacity; + } + if (style.weight) { + this._data.style.weight = style.weight; + } } 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 trk = og.trk.map((track, index) => { - if (trackIndices === undefined || trackIndices.includes(index)) { - return track.setHidden(hidden, segmentIndices); - } else { - allHidden = allHidden && (track._data.hidden === true); - return track; - } - }); - draft.trk = freeze(trk); // Pre-freeze the array, faster as well - - let wpt = og.wpt.map((waypoint) => { - if (trackIndices === undefined && segmentIndices === undefined) { - return waypoint.setHidden(hidden); - } else { - allHidden = allHidden && (waypoint._data.hidden === true); - return waypoint; - } - }); - draft.wpt = freeze(wpt); // Pre-freeze the array, faster as well - - if (trackIndices === undefined && segmentIndices === undefined) { - draft._data.hiddenWpt = hidden; + let allHidden = hidden; + this.trk.forEach((track, index) => { + if (trackIndices === undefined || trackIndices.includes(index)) { + track.setHidden(hidden, segmentIndices); + } else { + allHidden = allHidden && (track._data.hidden === true); } - - draft._data.hidden = allHidden; }); + this.wpt.forEach((waypoint) => { + if (trackIndices === undefined && segmentIndices === undefined) { + waypoint.setHidden(hidden); + } else { + allHidden = allHidden && (waypoint._data.hidden === true); + } + }); + + if (trackIndices === undefined && segmentIndices === undefined) { + this._data.hiddenWpt = hidden; + } + + this._data.hidden = allHidden; } setHiddenWaypoints(hidden: boolean, waypointIndices?: number[]) { @@ -441,14 +376,14 @@ export class GPXFile extends GPXTreeNode{ export class Track extends GPXTreeNode { [immerable] = true; - readonly name?: string; - readonly cmt?: string; - readonly desc?: string; - readonly src?: string; - readonly link?: Link; - readonly type?: string; - readonly trkseg: ReadonlyArray; - readonly extensions?: TrackExtensions; + name?: string; + cmt?: string; + desc?: string; + src?: string; + link?: Link; + type?: string; + trkseg: TrackSegment[]; + extensions?: TrackExtensions; constructor(track?: TrackType & { _data?: any } | Track) { super(); @@ -523,74 +458,50 @@ export class Track extends GPXTreeNode { } // Producers - replaceTrackSegments(start: number, end: number, segments: TrackSegment[]): [Track, TrackSegment[]] { - let removed = []; - 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]; + replaceTrackSegments(start: number, end: number, segments: TrackSegment[]): TrackSegment[] { + return this.trkseg.splice(start, end - start + 1, ...segments); } replaceTrackPoints(segmentIndex: number, start: number, end: number, points: TrackPoint[], speed?: number, startTime?: Date) { - 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(); - trkseg[segmentIndex] = trkseg[segmentIndex].replaceTrackPoints(start, end, points, speed, startTime); - draft.trkseg = freeze(trkseg); // Pre-freeze the array, faster as well - }); + this.trkseg[segmentIndex].replaceTrackPoints(start, end, points, speed, startTime); } reverseTrackSegment(segmentIndex: 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(); - trkseg[segmentIndex] = trkseg[segmentIndex]._reverse(trkseg[segmentIndex].getEndTimestamp(), trkseg[segmentIndex].getStartTimestamp()); - draft.trkseg = freeze(trkseg); // Pre-freeze the array, faster as well - }); + this.trkseg[segmentIndex]._reverse(this.trkseg[segmentIndex].getEndTimestamp(), this.trkseg[segmentIndex].getStartTimestamp()); } 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 segmentIndex = 0; - while (i < trkseg.length) { - let length = trkseg[i].getNumberOfTrackPoints(); - if (segmentIndices === undefined || segmentIndices.includes(segmentIndex)) { - if (start >= length || end < 0) { - trkseg.splice(i, 1); - } else { - if (start > 0 || end < length - 1) { - trkseg[i] = trkseg[i].crop(Math.max(0, start), Math.min(length - 1, end)); - } - i++; - } - start -= length; - end -= length; + let i = 0; + let segmentIndex = 0; + while (i < this.trkseg.length) { + let length = this.trkseg[i].getNumberOfTrackPoints(); + if (segmentIndices === undefined || segmentIndices.includes(segmentIndex)) { + if (start >= length || end < 0) { + this.trkseg.splice(i, 1); } else { + if (start > 0 || end < length - 1) { + this.trkseg[i].crop(Math.max(0, start), Math.min(length - 1, end)); + } i++; } - segmentIndex++; + start -= length; + end -= length; + } else { + i++; } - draft.trkseg = freeze(trkseg); // Pre-freeze the array, faster as well - }); + segmentIndex++; + } } clean(bounds: [Coordinates, Coordinates], inside: 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 trkseg = og.trkseg.slice(); let i = 0; let segmentIndex = 0; - while (i < trkseg.length) { + while (i < this.trkseg.length) { if (segmentIndices === undefined || segmentIndices.includes(segmentIndex)) { - trkseg[i] = trkseg[i].clean(bounds, inside); - if (trkseg[i].getNumberOfTrackPoints() === 0) { - trkseg.splice(i, 1); + this.trkseg[i].clean(bounds, inside); + if (this.trkseg[i].getNumberOfTrackPoints() === 0) { + this.trkseg.splice(i, 1); } else { i++; } @@ -599,64 +510,48 @@ export class Track extends GPXTreeNode { } segmentIndex++; } - draft.trkseg = freeze(trkseg); // Pre-freeze the array, faster as well }); } changeTimestamps(startTime: Date, speed: number, ratio: number, lastPoint?: TrackPoint, segmentIndex?: 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(); - trkseg = trkseg.map((segment, index) => { - if (segmentIndex === undefined || segmentIndex === index) { - let seg = segment.changeTimestamps(startTime, speed, ratio, lastPoint); - if (seg.trkpt.length > 0) { - lastPoint = seg.trkpt[seg.trkpt.length - 1]; - } - return seg; - } else { - return segment; + this.trkseg.forEach((segment, index) => { + if (segmentIndex === undefined || segmentIndex === index) { + segment.changeTimestamps(startTime, speed, ratio, lastPoint); + if (segment.trkpt.length > 0) { + lastPoint = segment.trkpt[segment.trkpt.length - 1]; } - }); - draft.trkseg = freeze(trkseg); // Pre-freeze the array, faster as well + } }); } setStyle(style: LineStyleExtension, force: boolean = true) { - return produce(this, (draft) => { - if (!draft.extensions) { - draft.extensions = {}; - } - if (!draft.extensions['gpx_style:line']) { - draft.extensions['gpx_style:line'] = {}; - } - if (style.color !== undefined && (force || draft.extensions['gpx_style:line'].color === undefined)) { - draft.extensions['gpx_style:line'].color = style.color; - } - if (style.opacity !== undefined && (force || draft.extensions['gpx_style:line'].opacity === undefined)) { - draft.extensions['gpx_style:line'].opacity = style.opacity; - } - if (style.weight !== undefined && (force || draft.extensions['gpx_style:line'].weight === undefined)) { - draft.extensions['gpx_style:line'].weight = style.weight; - } - }); + if (!this.extensions) { + this.extensions = {}; + } + if (!this.extensions['gpx_style:line']) { + this.extensions['gpx_style:line'] = {}; + } + if (style.color !== undefined && (force || this.extensions['gpx_style:line'].color === undefined)) { + this.extensions['gpx_style:line'].color = style.color; + } + if (style.opacity !== undefined && (force || this.extensions['gpx_style:line'].opacity === undefined)) { + this.extensions['gpx_style:line'].opacity = style.opacity; + } + if (style.weight !== undefined && (force || this.extensions['gpx_style:line'].weight === undefined)) { + this.extensions['gpx_style:line'].weight = style.weight; + } } 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 trkseg = og.trkseg.map((segment, index) => { - if (segmentIndices === undefined || segmentIndices.includes(index)) { - return segment.setHidden(hidden); - } else { - allHidden = allHidden && (segment._data.hidden === true); - return segment; - } - }); - draft.trkseg = freeze(trkseg); // Pre-freeze the array, faster as well - draft._data.hidden = allHidden; + let allHidden = hidden; + this.trkseg.forEach((segment, index) => { + if (segmentIndices === undefined || segmentIndices.includes(index)) { + segment.setHidden(hidden); + } else { + allHidden = allHidden && (segment._data.hidden === true); + } }); + this._data.hidden = allHidden; } } @@ -664,7 +559,7 @@ export class Track extends GPXTreeNode { export class TrackSegment extends GPXTreeLeaf { [immerable] = true; - readonly trkpt: ReadonlyArray>; + trkpt: TrackPoint[]; constructor(segment?: TrackSegmentType & { _data?: any } | TrackSegment) { super(); @@ -871,83 +766,79 @@ export class TrackSegment extends GPXTreeLeaf { // Producers replaceTrackPoints(start: number, end: number, points: TrackPoint[], speed?: number, startTime?: Date) { - return produce(this, (draft) => { - let og = getOriginal(draft); // Read as much as possible from the original object because it is faster - let trkpt = og.trkpt.slice(); + let og = getOriginal(this); // Read as much as possible from the original object because it is faster + let trkpt = og.trkpt.slice(); - if (speed !== undefined || (trkpt.length > 0 && trkpt[0].time !== undefined)) { - if (start > 0 && trkpt[0].time === undefined) { - trkpt.splice(0, 0, withTimestamps(trkpt.splice(0, start), speed, undefined, startTime)); - } - if (points.length > 0) { - let last = start > 0 ? trkpt[start - 1] : undefined; - if (points[0].time === undefined || (points.length > 1 && points[1].time === undefined)) { - points = withTimestamps(points, speed, last, startTime); - } else if (last !== undefined && points[0].time < last.time) { - points = withShiftedAndCompressedTimestamps(points, speed, 1, last); - } - } - if (end < trkpt.length - 1) { - let last = points.length > 0 ? points[points.length - 1] : start > 0 ? trkpt[start - 1] : undefined; - if (trkpt[end + 1].time === undefined) { - trkpt.splice(end + 1, 0, withTimestamps(trkpt.splice(end + 1), speed, last, startTime)); - } else if (last !== undefined && trkpt[end + 1].time < last.time) { - points = withShiftedAndCompressedTimestamps(points, speed, 1, last); - } + if (speed !== undefined || (trkpt.length > 0 && trkpt[0].time !== undefined)) { + if (start > 0 && trkpt[0].time === undefined) { + trkpt.splice(0, 0, ...withTimestamps(trkpt.splice(0, start), speed, undefined, startTime)); + } + if (points.length > 0) { + let last = start > 0 ? trkpt[start - 1] : undefined; + if (points[0].time === undefined || (points.length > 1 && points[1].time === undefined)) { + points = withTimestamps(points, speed, last, startTime); + } else if (last !== undefined && points[0].time < last.time) { + points = withShiftedAndCompressedTimestamps(points, speed, 1, last); } } + if (end < trkpt.length - 1) { + let last = points.length > 0 ? points[points.length - 1] : start > 0 ? trkpt[start - 1] : undefined; + if (trkpt[end + 1].time === undefined) { + trkpt.splice(end + 1, 0, ...withTimestamps(trkpt.splice(end + 1), speed, last, startTime)); + } else if (last !== undefined && trkpt[end + 1].time < last.time) { + points = withShiftedAndCompressedTimestamps(points, speed, 1, last); + } + } + } - trkpt.splice(start, end - start + 1, ...points); - draft.trkpt = freeze(trkpt); // Pre-freeze the array, faster as well - }); + trkpt.splice(start, end - start + 1, ...points); + this.trkpt = freeze(trkpt); // Pre-freeze the array, faster as well } _reverse(originalNextTimestamp?: Date, newPreviousTimestamp?: Date) { - return produce(this, (draft) => { - if (originalNextTimestamp !== undefined && newPreviousTimestamp !== undefined) { - 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 + let originalStartTimestamp = og.getStartTimestamp(); + 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( + newPreviousTimestamp.getTime() + originalNextTimestamp.getTime() - originalEndTimestamp.getTime() + ); - let originalEndTimestamp = og.getEndTimestamp(); - let newStartTimestamp = new Date( - newPreviousTimestamp.getTime() + originalNextTimestamp.getTime() - originalEndTimestamp.getTime() - ); + let trkpt = og.trkpt.map((point, i) => new TrackPoint({ + attributes: cloneJSON(point.attributes), + ele: point.ele, + time: new Date( + newStartTimestamp.getTime() + (originalEndTimestamp.getTime() - og.trkpt[i].time.getTime()) + ), + extensions: cloneJSON(point.extensions), + _data: cloneJSON(point._data), + })); - let trkpt = og.trkpt.map((point, i) => new TrackPoint({ - attributes: cloneJSON(point.attributes), - ele: point.ele, - time: new Date( - newStartTimestamp.getTime() + (originalEndTimestamp.getTime() - og.trkpt[i].time.getTime()) - ), - extensions: cloneJSON(point.extensions), - _data: cloneJSON(point._data), - })); + trkpt.reverse(); - trkpt.reverse(); - - draft.trkpt = freeze(trkpt); // Pre-freeze the array, faster as well - } else { - draft.trkpt.reverse(); - } - }); + this.trkpt = freeze(trkpt); // Pre-freeze the array, faster as well + } else { + this.trkpt.reverse(); + } } crop(start: number, end: number) { - return produce(this, (draft) => { - 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 - }); + this.trkpt = this.trkpt.slice(start, end + 1); } clean(bounds: [Coordinates, Coordinates], inside: boolean) { - return produce(this, (draft) => { - let og = getOriginal(draft); // Read as much as possible from the original object because it is faster - 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; - return inBounds !== inside; - }); - draft.trkpt = freeze(trkpt); // Pre-freeze the array, faster as well + let og = getOriginal(this); // Read as much as possible from the original object because it is faster + 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; + return inBounds !== inside; }); + this.trkpt = freeze(trkpt); // Pre-freeze the array, faster as well } changeTimestamps(startTime: Date, speed: number, ratio: number, lastPoint?: TrackPoint) { @@ -955,21 +846,18 @@ export class TrackSegment extends GPXTreeLeaf { lastPoint = this.trkpt[0].clone(); lastPoint.time = startTime; } - return produce(this, (draft) => { - let og = getOriginal(draft); // Read as much as possible from the original object because it is faster - if (og.trkpt.length > 0 && og.trkpt[0].time === undefined) { - let trkpt = withTimestamps(og.trkpt, speed, lastPoint, startTime); - draft.trkpt = freeze(trkpt); // Pre-freeze the array, faster as well - } else { - let trkpt = withShiftedAndCompressedTimestamps(og.trkpt, speed, ratio, lastPoint); - draft.trkpt = freeze(trkpt); // Pre-freeze the array, faster as well - } - }); + + 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) { + let trkpt = withTimestamps(og.trkpt, speed, lastPoint, startTime); + this.trkpt = freeze(trkpt); // Pre-freeze the array, faster as well + } else { + let trkpt = withShiftedAndCompressedTimestamps(og.trkpt, speed, ratio, lastPoint); + this.trkpt = freeze(trkpt); // Pre-freeze the array, faster as well + } } setHidden(hidden: boolean) { - return produce(this, (draft) => { - draft._data.hidden = hidden; - }); + this._data.hidden = hidden; } }; @@ -1122,9 +1010,7 @@ export class Waypoint { // Producers setHidden(hidden: boolean) { - return produce(this, (draft) => { - draft._data.hidden = hidden; - }); + this._data.hidden = hidden; } } @@ -1307,7 +1193,7 @@ export function distance(coord1: Coordinates, coord2: Coordinates): number { return maxMeters; } -function distanceWindowSmoothing(points: ReadonlyArray>, 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 start = 0, end = 0, accumulated = 0; @@ -1330,7 +1216,7 @@ function distanceWindowSmoothing(points: ReadonlyArray>, di return result; } -function distanceWindowSmoothingWithDistanceAccumulator(points: ReadonlyArray>, 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())); } diff --git a/gpx/src/simplify.ts b/gpx/src/simplify.ts index dfa880c2..16d6d8fe 100644 --- a/gpx/src/simplify.ts +++ b/gpx/src/simplify.ts @@ -5,7 +5,7 @@ export type SimplifiedTrackPoint = { point: TrackPoint, distance?: number }; 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) { return []; } else if (points.length == 1) { @@ -24,7 +24,7 @@ export function ramerDouglasPeucker(points: readonly TrackPoint[], epsilon: numb 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 = { index: 0, distance: 0 diff --git a/gpx/src/types.ts b/gpx/src/types.ts index a6ab6fc0..05033174 100644 --- a/gpx/src/types.ts +++ b/gpx/src/types.ts @@ -1,8 +1,8 @@ export type GPXFileType = { attributes: GPXFileAttributes; metadata: Metadata; - wpt: ReadonlyArray; - trk: ReadonlyArray; + wpt: WaypointType[]; + trk: TrackType[]; }; export type GPXFileAttributes = { @@ -52,7 +52,7 @@ export type TrackType = { src?: string; link?: Link; type?: string; - trkseg: ReadonlyArray; + trkseg: TrackSegmentType[]; extensions?: TrackExtensions; }; @@ -67,7 +67,7 @@ export type LineStyleExtension = { }; export type TrackSegmentType = { - trkpt: ReadonlyArray; + trkpt: TrackPointType[]; }; export type TrackPointType = { diff --git a/website/src/lib/components/file-list/FileList.ts b/website/src/lib/components/file-list/FileList.ts index c72f682e..61929df6 100644 --- a/website/src/lib/components/file-list/FileList.ts +++ b/website/src/lib/components/file-list/FileList.ts @@ -1,4 +1,4 @@ -import { dbUtils, getFile, getFileIds } from "$lib/db"; +import { dbUtils, getFile } from "$lib/db"; import { castDraft, freeze } from "immer"; import { GPXFile, Track, TrackSegment, Waypoint } from "gpx"; import { selection } from "./Selection"; @@ -353,59 +353,41 @@ export function moveItems(fromParent: ListItem, toParent: ListItem, fromItems: L let files = [fromParent.getFileId(), toParent.getFileId()]; let callbacks = [ (file, context: (GPXFile | Track | TrackSegment | Waypoint[] | Waypoint)[]) => { - let newFile = file; fromItems.forEach((item) => { if (item instanceof ListTrackItem) { - let [result, removed] = newFile.replaceTracks(item.getTrackIndex(), item.getTrackIndex(), []); - newFile = castDraft(result); - context.push(...removed); + context.push(...file.replaceTracks(item.getTrackIndex(), item.getTrackIndex(), [])); } else if (item instanceof ListTrackSegmentItem) { - let [result, removed] = newFile.replaceTrackSegments(item.getTrackIndex(), item.getSegmentIndex(), item.getSegmentIndex(), []); - newFile = castDraft(result); - context.push(...removed); + context.push(...file.replaceTrackSegments(item.getTrackIndex(), item.getSegmentIndex(), item.getSegmentIndex(), [])); } else if (item instanceof ListWaypointsItem) { - let [result, removed] = newFile.replaceWaypoints(0, newFile.wpt.length - 1, []); - newFile = castDraft(result); - context.push(removed); + context.push(file.replaceWaypoints(0, newFile.wpt.length - 1, [])); } else if (item instanceof ListWaypointItem) { - let [result, removed] = newFile.replaceWaypoints(item.getWaypointIndex(), item.getWaypointIndex(), []); - newFile = castDraft(result); - context.push(...removed); + context.push(...file.replaceWaypoints(item.getWaypointIndex(), item.getWaypointIndex(), [])); } }); context.reverse(); - return newFile; }, (file, context: (GPXFile | Track | TrackSegment | Waypoint[] | Waypoint)[]) => { - let newFile = file; toItems.forEach((item, i) => { if (item instanceof ListTrackItem) { if (context[i] instanceof Track) { - let [result, _removed] = newFile.replaceTracks(item.getTrackIndex(), item.getTrackIndex() - 1, [context[i]]); - newFile = castDraft(result); + file.replaceTracks(item.getTrackIndex(), item.getTrackIndex() - 1, [context[i]]); } 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]] })]); - newFile = castDraft(result); } } else if (item instanceof ListTrackSegmentItem && context[i] instanceof TrackSegment) { - let [result, _removed] = newFile.replaceTrackSegments(item.getTrackIndex(), item.getSegmentIndex(), item.getSegmentIndex() - 1, [context[i]]); - newFile = castDraft(result); + file.replaceTrackSegments(item.getTrackIndex(), item.getSegmentIndex(), item.getSegmentIndex() - 1, [context[i]]); } else if (item instanceof ListWaypointsItem) { 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]); - newFile = castDraft(result); + file.replaceWaypoints(file.wpt.length, file.wpt.length - 1, context[i]); } else if (context[i] instanceof Waypoint) { - let [result, _removed] = newFile.replaceWaypoints(newFile.wpt.length, newFile.wpt.length - 1, [context[i]]); - newFile = castDraft(result); + file.replaceWaypoints(file.wpt.length, file.wpt.length - 1, [context[i]]); } } else if (item instanceof ListWaypointItem && context[i] instanceof Waypoint) { - let [result, _removed] = newFile.replaceWaypoints(item.getWaypointIndex(), item.getWaypointIndex() - 1, [context[i]]); - newFile = castDraft(result); + file.replaceWaypoints(item.getWaypointIndex(), item.getWaypointIndex() - 1, [context[i]]); } }); - return newFile; } ]; @@ -433,12 +415,13 @@ export function moveItems(fromParent: ListItem, toParent: ListItem, fromItems: L if (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)); } else if (context[i] instanceof TrackSegment) { let newFile = newGPXFile(); newFile._data.id = item.getFileId(); - newFile = newFile.replaceTracks(0, 0, [new Track({ + newFile.replaceTracks(0, 0, [new Track({ trkseg: [context[i]] })])[0]; files.set(item.getFileId(), freeze(newFile)); diff --git a/website/src/lib/components/file-list/FileListNode.svelte b/website/src/lib/components/file-list/FileListNode.svelte index ad18243d..2e054b9e 100644 --- a/website/src/lib/components/file-list/FileListNode.svelte +++ b/website/src/lib/components/file-list/FileListNode.svelte @@ -27,8 +27,8 @@ export let node: | Map> | GPXTreeElement - | ReadonlyArray> - | Readonly; + | Waypoint[] + | Waypoint; export let item: ListItem; let recursive = getContext('recursive'); diff --git a/website/src/lib/components/file-list/FileListNodeContent.svelte b/website/src/lib/components/file-list/FileListNodeContent.svelte index 62d97804..7515b782 100644 --- a/website/src/lib/components/file-list/FileListNodeContent.svelte +++ b/website/src/lib/components/file-list/FileListNodeContent.svelte @@ -27,7 +27,7 @@ export let node: | Map> | GPXTreeElement - | Readonly; + | Waypoint; export let item: ListItem; export let waypointRoot: boolean = false; diff --git a/website/src/lib/components/file-list/FileListNodeLabel.svelte b/website/src/lib/components/file-list/FileListNodeLabel.svelte index 9becd751..611227d3 100644 --- a/website/src/lib/components/file-list/FileListNodeLabel.svelte +++ b/website/src/lib/components/file-list/FileListNodeLabel.svelte @@ -51,10 +51,7 @@ import MetadataDialog from './MetadataDialog.svelte'; import StyleDialog from './StyleDialog.svelte'; - export let node: - | GPXTreeElement - | ReadonlyArray> - | Readonly; + export let node: GPXTreeElement | Waypoint[] | Waypoint; export let item: ListItem; export let label: string | undefined; @@ -232,9 +229,8 @@ - dbUtils.applyToFile( - item.getFileId(), - (file) => file.replaceTracks(file.trk.length, file.trk.length, [new Track()])[0] + dbUtils.applyToFile(item.getFileId(), (file) => + file.replaceTracks(file.trk.length, file.trk.length, [new Track()]) )} > @@ -246,15 +242,13 @@ disabled={!singleSelection} on:click={() => { let trackIndex = item.getTrackIndex(); - dbUtils.applyToFile( - item.getFileId(), - (file) => - file.replaceTrackSegments( - trackIndex, - file.trk[trackIndex].trkseg.length, - file.trk[trackIndex].trkseg.length, - [new TrackSegment()] - )[0] + dbUtils.applyToFile(item.getFileId(), (file) => + file.replaceTrackSegments( + trackIndex, + file.trk[trackIndex].trkseg.length, + file.trk[trackIndex].trkseg.length, + [new TrackSegment()] + ) ); }} > diff --git a/website/src/lib/components/file-list/MetadataDialog.svelte b/website/src/lib/components/file-list/MetadataDialog.svelte index 317b2f33..3d2ce706 100644 --- a/website/src/lib/components/file-list/MetadataDialog.svelte +++ b/website/src/lib/components/file-list/MetadataDialog.svelte @@ -11,10 +11,7 @@ import { _ } from 'svelte-i18n'; import { editMetadata } from '$lib/stores'; - export let node: - | GPXTreeElement - | ReadonlyArray> - | Readonly; + export let node: GPXTreeElement | Waypoint[] | Waypoint; export let item: ListItem; export let open = false; @@ -54,7 +51,6 @@ file.trk[item.getTrackIndex()].name = name; file.trk[item.getTrackIndex()].desc = description; } - return file; }); open = false; }} diff --git a/website/src/lib/components/gpx-layer/GPXLayer.ts b/website/src/lib/components/gpx-layer/GPXLayer.ts index 27bfc20f..9649410d 100644 --- a/website/src/lib/components/gpx-layer/GPXLayer.ts +++ b/website/src/lib/components/gpx-layer/GPXLayer.ts @@ -6,7 +6,6 @@ import { currentPopupWaypoint, deleteWaypoint, waypointPopup } from "./WaypointP import { addSelectItem, selectItem, selection } from "$lib/components/file-list/Selection"; import { ListTrackSegmentItem, ListWaypointItem, ListWaypointsItem, ListTrackItem, ListFileItem, ListRootItem } from "$lib/components/file-list/FileList"; import type { Waypoint } from "gpx"; -import { produce } from "immer"; import { resetCursor, setCursor, setGrabbingCursor, setPointerCursor } from "$lib/utils"; import { font } from "$lib/assets/layers"; import { selectedWaypoint } from "$lib/components/toolbar/tools/Waypoint.svelte"; @@ -231,13 +230,13 @@ export class GPXLayer { resetCursor(); marker.getElement().style.cursor = ''; dbUtils.applyToFile(this.fileId, (file) => { - return produce(file, (draft) => { - let latLng = marker.getLngLat(); - draft.wpt[marker._waypoint._data.index].setCoordinates({ - lat: latLng.lat, - lon: latLng.lng - }); + let latLng = marker.getLngLat(); + let wpt = file.wpt[marker._waypoint._data.index]; + wpt.setCoordinates({ + lat: latLng.lat, + lon: latLng.lng }); + wpt.ele = this.map.queryTerrainElevation([latLng.lng, latLng.lat], { exaggerated: false }) ?? 0; }); dragEndTimestamp = Date.now() }); diff --git a/website/src/lib/components/gpx-layer/WaypointPopup.ts b/website/src/lib/components/gpx-layer/WaypointPopup.ts index db8696f0..0f8820de 100644 --- a/website/src/lib/components/gpx-layer/WaypointPopup.ts +++ b/website/src/lib/components/gpx-layer/WaypointPopup.ts @@ -21,5 +21,5 @@ export const waypointPopup = new mapboxgl.Popup({ }); export function deleteWaypoint(fileId: string, waypointIndex: number) { - dbUtils.applyToFile(fileId, (file) => file.replaceWaypoints(waypointIndex, waypointIndex, [])[0]); + dbUtils.applyToFile(fileId, (file) => file.replaceWaypoints(waypointIndex, waypointIndex, [])); } \ No newline at end of file diff --git a/website/src/lib/components/toolbar/tools/Time.svelte b/website/src/lib/components/toolbar/tools/Time.svelte index 32d81c70..6524b633 100644 --- a/website/src/lib/components/toolbar/tools/Time.svelte +++ b/website/src/lib/components/toolbar/tools/Time.svelte @@ -303,16 +303,16 @@ let fileId = item.getFileId(); dbUtils.applyToFile(fileId, (file) => { if (item instanceof ListFileItem) { - return file.changeTimestamps(getDate(startDate, startTime), effectiveSpeed, ratio); + file.changeTimestamps(getDate(startDate, startTime), effectiveSpeed, ratio); } else if (item instanceof ListTrackItem) { - return file.changeTimestamps( + file.changeTimestamps( getDate(startDate, startTime), effectiveSpeed, ratio, item.getTrackIndex() ); } else if (item instanceof ListTrackSegmentItem) { - return file.changeTimestamps( + file.changeTimestamps( getDate(startDate, startTime), effectiveSpeed, ratio, diff --git a/website/src/lib/components/toolbar/tools/Waypoint.svelte b/website/src/lib/components/toolbar/tools/Waypoint.svelte index b6e4b08c..76d98355 100644 --- a/website/src/lib/components/toolbar/tools/Waypoint.svelte +++ b/website/src/lib/components/toolbar/tools/Waypoint.svelte @@ -104,19 +104,16 @@ longitude = parseFloat(longitude.toFixed(6)); if ($selectedWaypoint) { dbUtils.applyToFile($selectedWaypoint[1], (file) => { - let waypoint = $selectedWaypoint[0].clone(); - waypoint.name = name; - waypoint.desc = description; - waypoint.cmt = description; - waypoint.setCoordinates({ + let wpt = file.wpt[$selectedWaypoint[0]._data.index]; + wpt.name = name; + wpt.desc = description; + wpt.cmt = description; + wpt.setCoordinates({ lat: latitude, lon: longitude }); - return file.replaceWaypoints( - $selectedWaypoint[0]._data.index, - $selectedWaypoint[0]._data.index, - [waypoint] - )[0]; + wpt.ele = + get(map)?.queryTerrainElevation([longitude, latitude], { exaggerated: false }) ?? 0; }); } else { let fileIds = new Set(); @@ -134,9 +131,8 @@ }); waypoint.ele = get(map)?.queryTerrainElevation([longitude, latitude], { exaggerated: false }) ?? 0; - dbUtils.applyToFiles( - Array.from(fileIds), - (file) => file.replaceWaypoints(file.wpt.length, file.wpt.length, [waypoint])[0] + dbUtils.applyToFiles(Array.from(fileIds), (file) => + file.replaceWaypoints(file.wpt.length, file.wpt.length, [waypoint]) ); } selectedWaypoint.set(undefined); diff --git a/website/src/lib/components/toolbar/tools/routing/Routing.svelte b/website/src/lib/components/toolbar/tools/routing/Routing.svelte index 09bfbc4f..97b446bb 100644 --- a/website/src/lib/components/toolbar/tools/routing/Routing.svelte +++ b/website/src/lib/components/toolbar/tools/routing/Routing.svelte @@ -71,7 +71,7 @@ function createFileWithPoint(e: any) { if ($selection.size === 0) { let file = newGPXFile(); - file = file.replaceTrackPoints(0, 0, 0, 0, [ + file.replaceTrackPoints(0, 0, 0, 0, [ new TrackPoint({ attributes: { lat: e.lngLat.lat, @@ -79,9 +79,7 @@ } }) ]); - file = produce(file, (draft) => { - draft._data.id = getFileIds(1)[0]; - }); + file._data.id = getFileIds(1)[0]; dbUtils.add(file); selectFileWhenLoaded(file._data.id); } diff --git a/website/src/lib/components/toolbar/tools/routing/RoutingControls.ts b/website/src/lib/components/toolbar/tools/routing/RoutingControls.ts index be816b70..632eeb03 100644 --- a/website/src/lib/components/toolbar/tools/routing/RoutingControls.ts +++ b/website/src/lib/components/toolbar/tools/routing/RoutingControls.ts @@ -351,7 +351,7 @@ export class RoutingControls { } else if (nextAnchor === null) { // Last point, remove trackpoints from previousAnchor dbUtils.applyToFile(this.fileId, (file) => { 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 this.routeBetweenAnchors([previousAnchor, nextAnchor], [previousAnchor.point.getCoordinates(), nextAnchor.point.getCoordinates()]); @@ -374,8 +374,8 @@ export class RoutingControls { let segment = anchor.segment; 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); - return newFile.replaceTrackPoints(anchor.trackIndex, anchor.segmentIndex, 0, anchor.point._data.index - 1, []); + 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.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) { let track = new Track(); - track = track.replaceTrackPoints(0, 0, 0, [newPoint]); - return file.replaceTracks(0, 0, [track])[0]; + track.replaceTrackPoints(0, 0, 0, [newPoint]); + file.replaceTracks(0, 0, [track])[0]; } else if (file.trk[trackIndex].trkseg.length === 0) { let segment = new TrackSegment(); - segment = segment.replaceTrackPoints(0, 0, [newPoint]); - return file.replaceTrackSegments(trackIndex, 0, 0, [segment])[0]; + segment.replaceTrackPoints(0, 0, [newPoint]); + file.replaceTrackSegments(trackIndex, 0, 0, [segment]); } else { - return file.replaceTrackPoints(trackIndex, segmentIndex, 0, 0, [newPoint]); + file.replaceTrackPoints(trackIndex, segmentIndex, 0, 0, [newPoint]); } }); return; @@ -458,11 +458,11 @@ export class RoutingControls { let lastAnchor = this.anchors[this.anchors.length - 1]; + let segment = lastAnchor.segment; dbUtils.applyToFile(this.fileId, (file) => { - let segment = original(file).getSegment(lastAnchor.trackIndex, lastAnchor.segmentIndex); let newSegment = segment.clone(); - newSegment = newSegment._reverse(segment.getEndTimestamp(), segment.getEndTimestamp()); - return file.replaceTrackPoints(lastAnchor.trackIndex, lastAnchor.segmentIndex, segment.trkpt.length, segment.trkpt.length, newSegment.trkpt.map((point) => point)); + newSegment._reverse(segment.getEndTimestamp(), segment.getEndTimestamp()); + file.replaceTrackPoints(lastAnchor.trackIndex, lastAnchor.segmentIndex, segment.trkpt.length, segment.trkpt.length, newSegment.trkpt.map((point) => point)); }); } diff --git a/website/src/lib/components/toolbar/tools/routing/Simplify.ts b/website/src/lib/components/toolbar/tools/routing/Simplify.ts index 19b9ed16..a3d7b012 100644 --- a/website/src/lib/components/toolbar/tools/routing/Simplify.ts +++ b/website/src/lib/components/toolbar/tools/routing/Simplify.ts @@ -23,15 +23,11 @@ export function updateAnchorPoints(file: GPXFile) { } if (segment.trkpt.length > 0) { - if (!segment.trkpt[0]._data.anchor) { // First point is not an anchor, make it one - segment.trkpt[0]._data.anchor = true; - 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.zoom = 0; - } + // Ensure first and last points are anchors and always visible + segment.trkpt[0]._data.anchor = true; + segment.trkpt[0]._data.zoom = 0; + segment.trkpt[segment.trkpt.length - 1]._data.anchor = true; + segment.trkpt[segment.trkpt.length - 1]._data.zoom = 0; } } } diff --git a/website/src/lib/db.ts b/website/src/lib/db.ts index df303b90..c7745a2d 100644 --- a/website/src/lib/db.ts +++ b/website/src/lib/db.ts @@ -179,6 +179,7 @@ function dexieGPXFileStore(id: string): Readable & { dest let store = writable(undefined); let query = liveQuery(() => db.files.get(id)).subscribe(value => { if (value !== undefined) { + console.log('File updated', id); let gpx = new GPXFile(value); updateAnchorPoints(gpx); @@ -259,6 +260,8 @@ function updateSelection(updatedFiles: GPXFile[], deletedFileIds: string[]) { // Commit the changes to the file state to the database function commitFileStateChange(newFileState: ReadonlyMap, patch: Patch[]) { + console.log(patch); + let changedFileIds = getChangedFileIds(patch); let updatedFileIds: string[] = [], deletedFileIds: string[] = []; @@ -272,6 +275,7 @@ function commitFileStateChange(newFileState: ReadonlyMap, patch let updatedFiles = updatedFileIds.map(id => newFileState.get(id)).filter(file => file !== undefined) as GPXFile[]; updatedFileIds = updatedFiles.map(file => file._data.id); + console.log(updatedFileIds, deletedFileIds); updateSelection(updatedFiles, deletedFileIds); @@ -284,6 +288,8 @@ function commitFileStateChange(newFileState: ReadonlyMap, patch await db.fileids.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 => { $files.get(id)?.destroy(); $files.delete(id); + console.log('File removed', id); }); return $files; }); @@ -360,12 +367,12 @@ function applyGlobal(callback: (files: Map) => void) { } // Helper function to apply a callback to multiple files -function applyToFiles(fileIds: string[], callback: (file: WritableDraft) => GPXFile) { +function applyToFiles(fileIds: string[], callback: (file: WritableDraft) => void) { const [newFileState, patch, inversePatch] = produceWithPatches(fileState, (draft) => { fileIds.forEach((fileId) => { let file = draft.get(fileId); if (file) { - draft.set(fileId, castDraft(callback(file))); + callback(file); } }); }); @@ -376,12 +383,12 @@ function applyToFiles(fileIds: string[], callback: (file: WritableDraft } // Helper function to apply different callbacks to multiple files -function applyEachToFilesAndGlobal(fileIds: string[], callbacks: ((file: WritableDraft, context?: any) => GPXFile)[], globalCallback: (files: Map, context?: any) => void, context?: any) { +function applyEachToFilesAndGlobal(fileIds: string[], callbacks: ((file: WritableDraft, context?: any) => void)[], globalCallback: (files: Map, context?: any) => void, context?: any) { const [newFileState, patch, inversePatch] = produceWithPatches(fileState, (draft) => { fileIds.forEach((fileId, index) => { let file = draft.get(fileId); if (file) { - draft.set(fileId, castDraft(callbacks[index](file, context))); + callbacks[index](file, context); } }); globalCallback(draft, context); @@ -459,13 +466,13 @@ export const dbUtils = { }); }); }, - applyToFile: (id: string, callback: (file: WritableDraft) => GPXFile) => { + applyToFile: (id: string, callback: (file: WritableDraft) => void) => { applyToFiles([id], callback); }, - applyToFiles: (ids: string[], callback: (file: WritableDraft) => GPXFile) => { + applyToFiles: (ids: string[], callback: (file: WritableDraft) => void) => { applyToFiles(ids, callback); }, - applyEachToFilesAndGlobal: (ids: string[], callbacks: ((file: WritableDraft, context?: any) => GPXFile)[], globalCallback: (files: Map, context?: any) => void, context?: any) => { + applyEachToFilesAndGlobal: (ids: string[], callbacks: ((file: WritableDraft, context?: any) => void)[], globalCallback: (files: Map, context?: any) => void, context?: any) => { applyEachToFilesAndGlobal(ids, callbacks, globalCallback, context); }, duplicateSelection: () => { @@ -476,36 +483,36 @@ export const dbUtils = { let ids = getFileIds(get(settings.fileOrder).length); let index = 0; applyToOrderedSelectedItemsFromFile((fileId, level, items) => { - let file = getFile(fileId); - if (file) { - let newFile = file; - if (level === ListLevel.FILE) { - newFile = file.clone(); + if (level === ListLevel.FILE) { + let file = getFile(fileId); + if (file) { + let newFile = file.clone(); newFile._data.id = ids[index++]; - } else if (level === ListLevel.TRACK) { - for (let item of items) { - let trackIndex = (item as ListTrackItem).getTrackIndex(); - let [result, _removed] = newFile.replaceTracks(trackIndex + 1, trackIndex, [file.trk[trackIndex].clone()]); - newFile = result; - } - } else if (level === ListLevel.SEGMENT) { - for (let item of items) { - let trackIndex = (item as ListTrackSegmentItem).getTrackIndex(); - let segmentIndex = (item as ListTrackSegmentItem).getSegmentIndex(); - let [result, _removed] = newFile.replaceTrackSegments(trackIndex, segmentIndex + 1, segmentIndex, [file.trk[trackIndex].trkseg[segmentIndex].clone()]); - newFile = result; - } - } else if (level === ListLevel.WAYPOINTS) { - let [result, _removed] = newFile.replaceWaypoints(file.wpt.length, file.wpt.length - 1, file.wpt.map((wpt) => wpt.clone())); - newFile = result; - } else if (level === ListLevel.WAYPOINT) { - for (let item of items) { - let waypointIndex = (item as ListWaypointItem).getWaypointIndex(); - let [result, _removed] = newFile.replaceWaypoints(waypointIndex + 1, waypointIndex, [file.wpt[waypointIndex].clone()]); - newFile = result; + draft.set(newFile._data.id, freeze(newFile)); + } + } else { + let file = draft.get(fileId); + if (file) { + if (level === ListLevel.TRACK) { + for (let item of items) { + let trackIndex = (item as ListTrackItem).getTrackIndex(); + file.replaceTracks(trackIndex + 1, trackIndex, [file.trk[trackIndex].clone()]); + } + } else if (level === ListLevel.SEGMENT) { + for (let item of items) { + let trackIndex = (item as ListTrackSegmentItem).getTrackIndex(); + let segmentIndex = (item as ListTrackSegmentItem).getSegmentIndex(); + file.replaceTrackSegments(trackIndex, segmentIndex + 1, segmentIndex, [file.trk[trackIndex].trkseg[segmentIndex].clone()]); + } + } else if (level === ListLevel.WAYPOINTS) { + file.replaceWaypoints(file.wpt.length, file.wpt.length - 1, file.wpt.map((wpt) => wpt.clone())); + } else if (level === ListLevel.WAYPOINT) { + for (let item of items) { + let waypointIndex = (item as ListWaypointItem).getWaypointIndex(); + file.replaceWaypoints(waypointIndex + 1, waypointIndex, [file.wpt[waypointIndex].clone()]); + } } } - draft.set(newFile._data.id, freeze(newFile)); } }); }); @@ -516,24 +523,22 @@ export const dbUtils = { } applyGlobal((draft) => { applyToOrderedSelectedItemsFromFile((fileId, level, items) => { - let file = getFile(fileId); + let file = draft.get(fileId); if (file) { - let newFile = file; if (level === ListLevel.FILE) { - newFile = file.reverse(); + file.reverse(); } else if (level === ListLevel.TRACK) { for (let item of items) { let trackIndex = (item as ListTrackItem).getTrackIndex(); - newFile = newFile.reverseTrack(trackIndex); + file.reverseTrack(trackIndex); } } else if (level === ListLevel.SEGMENT) { for (let item of items) { let trackIndex = (item as ListTrackSegmentItem).getTrackIndex(); 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: [] }; applyToOrderedSelectedItemsFromFile((fileId, level, items) => { - let file = getFile(fileId); + let file = draft.get(fileId); if (file) { - let newFile = file; if (level === ListLevel.FILE) { - { - let [result, removed] = newFile.replaceTracks(0, newFile.trk.length - 1, []); - toMerge.trk.push(...removed); - newFile = result; - } - { - let [result, removed] = newFile.replaceWaypoints(0, newFile.wpt.length - 1, []); - toMerge.wpt.push(...removed); - newFile = result; - } + toMerge.trk.push(...file.replaceTracks(0, file.trk.length - 1, [])); + toMerge.wpt.push(...file.replaceWaypoints(0, file.wpt.length - 1, [])); if (first) { target = items[0]; - targetFile = newFile; + targetFile = file; } else { draft.delete(fileId); } @@ -578,14 +574,11 @@ export const dbUtils = { items.forEach((item, index) => { 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 - let [result, removed] = newFile.replaceTrackSegments(trackIndex, 0, newFile.trk[trackIndex].trkseg.length - 1, []); - toMerge.trkseg.splice(0, 0, ...removed); - newFile = result; + toMerge.trkseg.splice(0, 0, ...file.replaceTrackSegments(trackIndex, 0, file.trk[trackIndex].trkseg.length - 1, [])); target = item; } else { - let [result, removed] = newFile.replaceTracks(trackIndex, trackIndex, []); + let removed = file.replaceTracks(trackIndex, trackIndex, []); toMerge.trkseg.push(...removed[0].trkseg); - newFile = result; } }); } 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 target = item; } - let [result, removed] = newFile.replaceTrackSegments(trackIndex, segmentIndex, segmentIndex, []); - toMerge.trkseg.splice(0, 0, ...removed); - newFile = result; + toMerge.trkseg.splice(0, 0, ...file.replaceTrackSegments(trackIndex, segmentIndex, segmentIndex, [])); }); } - if (first) { - targetFile = newFile; - } else { - draft.set(fileId, freeze(newFile)); - } + targetFile = file; } first = false; } @@ -625,37 +612,47 @@ export const dbUtils = { } } - if (toMerge.trk.length > 0) { - let s = new TrackSegment(); - toMerge.trk.map((track) => { - track.trkseg.forEach((segment) => { - s = s.replaceTrackPoints(s.trkpt.length, s.trkpt.length, segment.trkpt.slice(), speed, startTime); + if (toMerge.trk.length > 0 && toMerge.trk[0].trkseg.length > 0) { + let s = toMerge.trk[0].trkseg[0]; + toMerge.trk.map((track, trackIndex) => { + track.trkseg.forEach((segment, segmentIndex) => { + 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) { - let s = new TrackSegment(); - toMerge.trkseg.forEach((segment) => { - s = s.replaceTrackPoints(s.trkpt.length, s.trkpt.length, segment.trkpt.slice(), speed, startTime); + let s = toMerge.trkseg[0]; + toMerge.trkseg.forEach((segment, segmentIndex) => { + if (segmentIndex === 0) { + return; + } + s.replaceTrackPoints(s.trkpt.length, s.trkpt.length, segment.trkpt.slice(), speed, startTime); }); toMerge.trkseg = [s]; } + console.log(toMerge); } if (targetFile) { + console.log(toMerge, target, targetFile); if (target instanceof ListFileItem) { - targetFile = targetFile.replaceTracks(0, targetFile.trk.length - 1, toMerge.trk)[0]; - targetFile = targetFile.replaceWaypoints(0, targetFile.wpt.length - 1, toMerge.wpt)[0]; + targetFile.replaceTracks(0, targetFile.trk.length - 1, toMerge.trk)[0]; + targetFile.replaceWaypoints(0, targetFile.wpt.length - 1, toMerge.wpt)[0]; } else if (target instanceof ListTrackItem) { 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) { let trackIndex = target.getTrackIndex(); 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) => { applyToOrderedSelectedItemsFromFile((fileId, level, items) => { - let file = getFile(fileId); + let file = draft.get(fileId); if (file) { if (level === ListLevel.FILE) { let length = file.getNumberOfTrackPoints(); if (start >= length || end < 0) { draft.delete(fileId); } else if (start > 0 || end < length - 1) { - let newFile = file.crop(Math.max(0, start), Math.min(length - 1, end)); - draft.set(newFile._data.id, freeze(newFile)); + file.crop(Math.max(0, start), Math.min(length - 1, end)); } start -= length; end -= length; } else if (level === ListLevel.TRACK) { let trackIndices = items.map((item) => (item as ListTrackItem).getTrackIndex()); - let newFile = file.crop(start, end, trackIndices); - draft.set(newFile._data.id, freeze(newFile)); + file.crop(start, end, trackIndices); } else if (level === ListLevel.SEGMENT) { let trackIndices = [(items[0] as ListTrackSegmentItem).getTrackIndex()]; let segmentIndices = items.map((item) => (item as ListTrackSegmentItem).getSegmentIndex()); - let newFile = file.crop(start, end, trackIndices, segmentIndices); - draft.set(newFile._data.id, freeze(newFile)); + file.crop(start, end, trackIndices, segmentIndices); } } }, false); @@ -694,9 +688,9 @@ export const dbUtils = { extractSelection: () => { return applyGlobal((draft) => { applyToOrderedSelectedItemsFromFile((fileId, level, items) => { - let file = getFile(fileId); - if (file) { - if (level === ListLevel.FILE) { + if (level === ListLevel.FILE) { + let file = getFile(fileId); + if (file) { if (file.trk.length > 1) { let fileIds = getFileIds(file.trk.length); @@ -724,27 +718,24 @@ export const dbUtils = { }); file.trk.forEach((track, index) => { + let newFile = file.clone(); 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) { - t = produce(t, (t) => { - t.name = `${track.name} (${segmentIndex + 1})`; - }); + t.name = `${track.name} (${segmentIndex + 1})`; } return t; }); - let newFile = file.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 = produce(newFile, (f) => { - f._data.id = fileIds[index]; - f.metadata.name = track.name ?? `${file.metadata.name} (${index + 1})`; - }); + newFile.replaceTracks(0, file.trk.length - 1, tracks)[0]; + newFile.replaceWaypoints(0, file.wpt.length - 1, closest.filter((c) => c.index.includes(index)).map((c) => file.wpt[c.wptIndex]))[0]; + newFile._data.id = fileIds[index]; + newFile.metadata.name = track.name ?? `${file.metadata.name} (${index + 1})`; draft.set(newFile._data.id, freeze(newFile)); }); } else if (file.trk.length === 1) { let fileIds = getFileIds(file.trk[0].trkseg.length); - let closest = file.wpt.map((wpt, wptIndex) => { return { wptIndex: wptIndex, @@ -767,33 +758,32 @@ export const dbUtils = { }); file.trk[0].trkseg.forEach((segment, index) => { - let newFile = file.replaceTrackSegments(0, 0, file.trk[0].trkseg.length - 1, [segment])[0]; - newFile = 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) => { - f._data.id = fileIds[index]; - f.metadata.name = `${file.trk[0].name ?? file.metadata.name} (${index + 1})`; - }); + let newFile = file.clone(); + newFile.replaceTrackSegments(0, 0, file.trk[0].trkseg.length - 1, [segment])[0]; + newFile.replaceWaypoints(0, file.wpt.length - 1, closest.filter((c) => c.index.includes(index)).map((c) => file.wpt[c.wptIndex]))[0]; + newFile._data.id = fileIds[index]; + newFile.metadata.name = `${file.trk[0].name ?? file.metadata.name} (${index + 1})`; draft.set(newFile._data.id, freeze(newFile)); }); } draft.delete(fileId); - } else if (level === ListLevel.TRACK) { - let newFile = file; + } + } else if (level === ListLevel.TRACK) { + let file = draft.get(fileId); + if (file) { for (let item of items) { let trackIndex = (item as ListTrackItem).getTrackIndex(); let track = file.trk[trackIndex]; 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) { - t = produce(t, (t) => { - t.name = `${track.name} (${segmentIndex + 1})`; - }); + t.name = `${track.name} (${segmentIndex + 1})`; } 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) { - let newFile = file.crop(0, absoluteIndex); - draft.set(newFile._data.id, freeze(newFile)); - let newFile2 = file.clone(); - newFile2._data.id = getFileIds(1)[0]; - newFile2 = newFile2.crop(absoluteIndex, file.getNumberOfTrackPoints() - 1); - draft.set(newFile2._data.id, freeze(newFile2)); + let newFile = draft.get(fileId); + if (newFile) { + newFile.crop(0, absoluteIndex); + let newFile2 = file.clone(); + newFile2._data.id = getFileIds(1)[0]; + newFile2.crop(absoluteIndex, file.getNumberOfTrackPoints() - 1); + draft.set(newFile2._data.id, freeze(newFile2)); + } } 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]; - draft.set(newFile._data.id, freeze(newFile)); + let newFile = draft.get(fileId); + 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) { - let newFile = file.replaceTrackSegments(trackIndex, segmentIndex, segmentIndex, [segment.crop(0, minIndex), segment.crop(minIndex, segment.trkpt.length - 1)])[0]; - draft.set(newFile._data.id, freeze(newFile)); + let newFile = draft.get(fileId); + 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) => { applyToOrderedSelectedItemsFromFile((fileId, level, items) => { - let file = getFile(fileId); + let file = draft.get(fileId); if (file) { - let newFile = file; if (level === ListLevel.FILE) { - newFile = file.clean(bounds, inside, deleteTrackPoints, deleteWaypoints); + file.clean(bounds, inside, deleteTrackPoints, deleteWaypoints); } else if (level === ListLevel.TRACK) { 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) { let trackIndices = [(items[0] as ListTrackSegmentItem).getTrackIndex()]; 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) { - newFile = newFile.clean(bounds, inside, false, deleteWaypoints); + file.clean(bounds, inside, false, deleteWaypoints); } else if (level === ListLevel.WAYPOINT) { 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) => { let allItems = Array.from(itemsAndPoints.keys()); applyToOrderedItemsFromFile(allItems, (fileId, level, items) => { - let file = getFile(fileId); + let file = draft.get(fileId); if (file) { - let newFile = file; for (let item of items) { if (item instanceof ListTrackSegmentItem) { let trackIndex = item.getTrackIndex(); let segmentIndex = item.getSegmentIndex(); let points = itemsAndPoints.get(item); 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) => { applyToOrderedSelectedItemsFromFile((fileId, level, items) => { - let file = getFile(fileId); + let file = draft.get(fileId); if (file && (level === ListLevel.FILE || level === ListLevel.TRACK)) { - let newFile = file; if (level === ListLevel.FILE) { - newFile = file.setStyle(style); + file.setStyle(style); } else if (level === ListLevel.TRACK) { - for (let item of items) { - let trackIndex = (item as ListTrackItem).getTrackIndex(); - newFile = newFile.replaceTracks(trackIndex, trackIndex, [file.trk[trackIndex].setStyle(style)])[0]; - } if (items.length === file.trk.length) { - newFile = newFile.setStyle(style); + file.setStyle(style); + } else { + for (let item of items) { + let trackIndex = (item as ListTrackItem).getTrackIndex(); + file.trk[trackIndex].setStyle(style); + } } } - draft.set(newFile._data.id, freeze(newFile)); } }); }); @@ -926,25 +925,23 @@ export const dbUtils = { } applyGlobal((draft) => { applyToOrderedSelectedItemsFromFile((fileId, level, items) => { - let file = getFile(fileId); + let file = draft.get(fileId); if (file) { - let newFile = file; if (level === ListLevel.FILE) { - newFile = file.setHidden(hidden); + file.setHidden(hidden); } else if (level === ListLevel.TRACK) { let trackIndices = items.map((item) => (item as ListTrackItem).getTrackIndex()); - newFile = newFile.setHidden(hidden, trackIndices); + file.setHidden(hidden, trackIndices); } else if (level === ListLevel.SEGMENT) { let trackIndices = [(items[0] as ListTrackSegmentItem).getTrackIndex()]; 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) { - newFile = newFile.setHiddenWaypoints(hidden); + file.setHiddenWaypoints(hidden); } else if (level === ListLevel.WAYPOINT) { 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) { draft.delete(fileId); } else { - let file = getFile(fileId); + let file = draft.get(fileId); if (file) { - let newFile = file; if (level === ListLevel.TRACK) { for (let item of items) { let trackIndex = (item as ListTrackItem).getTrackIndex(); - let [result, _removed] = newFile.replaceTracks(trackIndex, trackIndex, []); - newFile = result; + file.replaceTracks(trackIndex, trackIndex, []); } } else if (level === ListLevel.SEGMENT) { for (let item of items) { let trackIndex = (item as ListTrackSegmentItem).getTrackIndex(); let segmentIndex = (item as ListTrackSegmentItem).getSegmentIndex(); - let [result, _removed] = newFile.replaceTrackSegments(trackIndex, segmentIndex, segmentIndex, []); - newFile = result; + file.replaceTrackSegments(trackIndex, segmentIndex, segmentIndex, []); } } else if (level === ListLevel.WAYPOINTS) { - let [result, _removed] = newFile.replaceWaypoints(0, newFile.wpt.length - 1, []); - newFile = result; + file.replaceWaypoints(0, file.wpt.length - 1, []); } else if (level === ListLevel.WAYPOINT) { for (let item of items) { let waypointIndex = (item as ListWaypointItem).getWaypointIndex(); - let [result, _removed] = newFile.replaceWaypoints(waypointIndex, waypointIndex, []); - newFile = result; + file.replaceWaypoints(waypointIndex, waypointIndex, []); } } - draft.set(newFile._data.id, freeze(newFile)); } } });