mirror of
https://github.com/gpxstudio/gpx.studio.git
synced 2025-09-02 08:42:31 +00:00
better use immer
This commit is contained in:
190
gpx/src/gpx.ts
190
gpx/src/gpx.ts
@@ -1,5 +1,5 @@
|
|||||||
import { Coordinates, GPXFileAttributes, GPXFileType, Link, Metadata, TrackExtensions, TrackPointExtensions, TrackPointType, TrackSegmentType, TrackType, WaypointType } from "./types";
|
import { Coordinates, GPXFileAttributes, GPXFileType, Link, Metadata, TrackExtensions, TrackPointExtensions, TrackPointType, TrackSegmentType, TrackType, WaypointType } from "./types";
|
||||||
import { immerable } from "immer";
|
import { Draft, castDraft, immerable, produce } from "immer";
|
||||||
|
|
||||||
function cloneJSON<T>(obj: T): T {
|
function cloneJSON<T>(obj: T): T {
|
||||||
if (obj === null || typeof obj !== 'object') {
|
if (obj === null || typeof obj !== 'object') {
|
||||||
@@ -13,18 +13,18 @@ export abstract class GPXTreeElement<T extends GPXTreeElement<any>> {
|
|||||||
_data: { [key: string]: any } = {};
|
_data: { [key: string]: any } = {};
|
||||||
|
|
||||||
abstract isLeaf(): boolean;
|
abstract isLeaf(): boolean;
|
||||||
abstract getChildren(): T[];
|
abstract getChildren(): ReadonlyArray<T>;
|
||||||
|
|
||||||
abstract append(points: TrackPoint[]): void;
|
|
||||||
abstract reverse(originalNextTimestamp?: Date, newPreviousTimestamp?: Date): void;
|
|
||||||
|
|
||||||
abstract getStartTimestamp(): Date;
|
abstract getStartTimestamp(): Date;
|
||||||
abstract getEndTimestamp(): Date;
|
abstract getEndTimestamp(): Date;
|
||||||
abstract getTrackPoints(): TrackPoint[];
|
|
||||||
abstract getStatistics(): GPXStatistics;
|
abstract getStatistics(): GPXStatistics;
|
||||||
abstract getSegments(): TrackSegment[];
|
abstract getSegments(): TrackSegment[];
|
||||||
|
|
||||||
abstract toGeoJSON(): GeoJSON.Feature | GeoJSON.Feature[] | GeoJSON.FeatureCollection | GeoJSON.FeatureCollection[];
|
abstract toGeoJSON(): GeoJSON.Feature | GeoJSON.Feature[] | GeoJSON.FeatureCollection | GeoJSON.FeatureCollection[];
|
||||||
|
|
||||||
|
// Producers
|
||||||
|
abstract replace(segment: number, start: number, end: number, points: TrackPoint[]);
|
||||||
|
abstract reverse(originalNextTimestamp?: Date, newPreviousTimestamp?: Date);
|
||||||
}
|
}
|
||||||
|
|
||||||
export type AnyGPXTreeElement = GPXTreeElement<GPXTreeElement<any>>;
|
export type AnyGPXTreeElement = GPXTreeElement<GPXTreeElement<any>>;
|
||||||
@@ -35,36 +35,6 @@ abstract class GPXTreeNode<T extends GPXTreeElement<any>> extends GPXTreeElement
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
append(points: TrackPoint[]): void {
|
|
||||||
let children = this.getChildren();
|
|
||||||
|
|
||||||
if (children.length === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
children[children.length - 1].append(points);
|
|
||||||
}
|
|
||||||
|
|
||||||
reverse(originalNextTimestamp?: Date, newPreviousTimestamp?: Date): void {
|
|
||||||
const children = this.getChildren();
|
|
||||||
|
|
||||||
if (!originalNextTimestamp && !newPreviousTimestamp) {
|
|
||||||
originalNextTimestamp = children[children.length - 1].getEndTimestamp();
|
|
||||||
newPreviousTimestamp = children[0].getStartTimestamp();
|
|
||||||
}
|
|
||||||
|
|
||||||
children.reverse();
|
|
||||||
|
|
||||||
for (let i = 0; i < children.length; i++) {
|
|
||||||
let originalStartTimestamp = children[i].getStartTimestamp();
|
|
||||||
|
|
||||||
children[i].reverse(originalNextTimestamp, newPreviousTimestamp);
|
|
||||||
|
|
||||||
originalNextTimestamp = originalStartTimestamp;
|
|
||||||
newPreviousTimestamp = children[i].getEndTimestamp();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getStartTimestamp(): Date {
|
getStartTimestamp(): Date {
|
||||||
return this.getChildren()[0].getStartTimestamp();
|
return this.getChildren()[0].getStartTimestamp();
|
||||||
}
|
}
|
||||||
@@ -73,10 +43,6 @@ abstract class GPXTreeNode<T extends GPXTreeElement<any>> extends GPXTreeElement
|
|||||||
return this.getChildren()[this.getChildren().length - 1].getEndTimestamp();
|
return this.getChildren()[this.getChildren().length - 1].getEndTimestamp();
|
||||||
}
|
}
|
||||||
|
|
||||||
getTrackPoints(): TrackPoint[] {
|
|
||||||
return this.getChildren().flatMap((child) => child.getTrackPoints());
|
|
||||||
}
|
|
||||||
|
|
||||||
getStatistics(): GPXStatistics {
|
getStatistics(): GPXStatistics {
|
||||||
let statistics = new GPXStatistics();
|
let statistics = new GPXStatistics();
|
||||||
for (let child of this.getChildren()) {
|
for (let child of this.getChildren()) {
|
||||||
@@ -88,6 +54,44 @@ abstract class GPXTreeNode<T extends GPXTreeElement<any>> extends GPXTreeElement
|
|||||||
getSegments(): TrackSegment[] {
|
getSegments(): TrackSegment[] {
|
||||||
return this.getChildren().flatMap((child) => child.getSegments());
|
return this.getChildren().flatMap((child) => child.getSegments());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Producers
|
||||||
|
replace(segment: number, start: number, end: number, points: TrackPoint[]) {
|
||||||
|
return produce(this, (draft: Draft<GPXTreeNode<T>>) => {
|
||||||
|
let children = castDraft(draft.getChildren());
|
||||||
|
let cumul = 0;
|
||||||
|
for (let i = 0; i < children.length; i++) {
|
||||||
|
let childSegments = children[i].getSegments();
|
||||||
|
if (segment < cumul + childSegments.length) {
|
||||||
|
children[i] = children[i].replace(segment - cumul, start, end, points);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
cumul += childSegments.length;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
reverse(originalNextTimestamp?: Date, newPreviousTimestamp?: Date) {
|
||||||
|
return produce(this, (draft: Draft<GPXTreeNode<T>>) => {
|
||||||
|
const children = castDraft(draft.getChildren());
|
||||||
|
|
||||||
|
if (!originalNextTimestamp && !newPreviousTimestamp) {
|
||||||
|
originalNextTimestamp = children[children.length - 1].getEndTimestamp();
|
||||||
|
newPreviousTimestamp = children[0].getStartTimestamp();
|
||||||
|
}
|
||||||
|
|
||||||
|
children.reverse();
|
||||||
|
|
||||||
|
for (let i = 0; i < children.length; i++) {
|
||||||
|
let originalStartTimestamp = children[i].getStartTimestamp();
|
||||||
|
|
||||||
|
children[i] = children[i].reverse(originalNextTimestamp, newPreviousTimestamp);
|
||||||
|
|
||||||
|
originalNextTimestamp = originalStartTimestamp;
|
||||||
|
newPreviousTimestamp = children[i].getEndTimestamp();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// An abstract class that TrackSegment extends to implement the GPXTreeElement interface
|
// An abstract class that TrackSegment extends to implement the GPXTreeElement interface
|
||||||
@@ -96,21 +100,21 @@ abstract class GPXTreeLeaf extends GPXTreeElement<GPXTreeLeaf> {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
getChildren(): GPXTreeLeaf[] {
|
getChildren(): ReadonlyArray<GPXTreeLeaf> {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// A class that represents a set of GPX files
|
// A class that represents a set of GPX files
|
||||||
export class GPXFiles extends GPXTreeNode<GPXFile> {
|
export class GPXFiles extends GPXTreeNode<GPXFile> {
|
||||||
files: GPXFile[];
|
readonly files: ReadonlyArray<GPXFile>;
|
||||||
|
|
||||||
constructor(files: GPXFile[]) {
|
constructor(files: GPXFile[]) {
|
||||||
super();
|
super();
|
||||||
this.files = files;
|
this.files = files;
|
||||||
}
|
}
|
||||||
|
|
||||||
getChildren(): GPXFile[] {
|
getChildren(): ReadonlyArray<GPXFile> {
|
||||||
return this.files;
|
return this.files;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -123,12 +127,12 @@ export class GPXFiles extends GPXTreeNode<GPXFile> {
|
|||||||
export class GPXFile extends GPXTreeNode<Track>{
|
export class GPXFile extends GPXTreeNode<Track>{
|
||||||
[immerable] = true;
|
[immerable] = true;
|
||||||
|
|
||||||
attributes: GPXFileAttributes;
|
readonly attributes: GPXFileAttributes;
|
||||||
metadata: Metadata;
|
readonly metadata: Metadata;
|
||||||
wpt: Waypoint[];
|
readonly wpt: ReadonlyArray<Readonly<Waypoint>>;
|
||||||
trk: Track[];
|
readonly trk: ReadonlyArray<Track>;
|
||||||
|
|
||||||
constructor(gpx?: GPXFileType | GPXFile) {
|
constructor(gpx?: GPXFileType & { _data?: any } | GPXFile) {
|
||||||
super();
|
super();
|
||||||
if (gpx) {
|
if (gpx) {
|
||||||
this.attributes = gpx.attributes
|
this.attributes = gpx.attributes
|
||||||
@@ -146,7 +150,7 @@ export class GPXFile extends GPXTreeNode<Track>{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getChildren(): Track[] {
|
getChildren(): ReadonlyArray<Track> {
|
||||||
return this.trk;
|
return this.trk;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -181,16 +185,16 @@ export class GPXFile extends GPXTreeNode<Track>{
|
|||||||
export class Track extends GPXTreeNode<TrackSegment> {
|
export class Track extends GPXTreeNode<TrackSegment> {
|
||||||
[immerable] = true;
|
[immerable] = true;
|
||||||
|
|
||||||
name?: string;
|
readonly name?: string;
|
||||||
cmt?: string;
|
readonly cmt?: string;
|
||||||
desc?: string;
|
readonly desc?: string;
|
||||||
src?: string;
|
readonly src?: string;
|
||||||
link?: Link;
|
readonly link?: Link;
|
||||||
type?: string;
|
readonly type?: string;
|
||||||
trkseg: TrackSegment[];
|
readonly trkseg: ReadonlyArray<TrackSegment>;
|
||||||
extensions?: TrackExtensions;
|
readonly extensions?: TrackExtensions;
|
||||||
|
|
||||||
constructor(track?: TrackType | Track) {
|
constructor(track?: TrackType & { _data?: any } | Track) {
|
||||||
super();
|
super();
|
||||||
if (track) {
|
if (track) {
|
||||||
this.name = track.name;
|
this.name = track.name;
|
||||||
@@ -209,7 +213,7 @@ export class Track extends GPXTreeNode<TrackSegment> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getChildren(): TrackSegment[] {
|
getChildren(): ReadonlyArray<TrackSegment> {
|
||||||
return this.trkseg;
|
return this.trkseg;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -263,9 +267,9 @@ export class Track extends GPXTreeNode<TrackSegment> {
|
|||||||
export class TrackSegment extends GPXTreeLeaf {
|
export class TrackSegment extends GPXTreeLeaf {
|
||||||
[immerable] = true;
|
[immerable] = true;
|
||||||
|
|
||||||
trkpt: TrackPoint[];
|
readonly trkpt: ReadonlyArray<Readonly<TrackPoint>>;
|
||||||
|
|
||||||
constructor(segment?: TrackSegmentType | TrackSegment) {
|
constructor(segment?: TrackSegmentType & { _data?: any } | TrackSegment) {
|
||||||
super();
|
super();
|
||||||
if (segment) {
|
if (segment) {
|
||||||
this.trkpt = segment.trkpt.map((point) => new TrackPoint(point));
|
this.trkpt = segment.trkpt.map((point) => new TrackPoint(point));
|
||||||
@@ -280,7 +284,7 @@ export class TrackSegment extends GPXTreeLeaf {
|
|||||||
_computeStatistics(): GPXStatistics {
|
_computeStatistics(): GPXStatistics {
|
||||||
let statistics = new GPXStatistics();
|
let statistics = new GPXStatistics();
|
||||||
|
|
||||||
statistics.local.points = this.trkpt;
|
statistics.local.points = this.trkpt.map((point) => point);
|
||||||
|
|
||||||
statistics.local.elevation.smoothed = this._computeSmoothedElevation();
|
statistics.local.elevation.smoothed = this._computeSmoothedElevation();
|
||||||
statistics.local.slope = this._computeSlope();
|
statistics.local.slope = this._computeSlope();
|
||||||
@@ -366,33 +370,6 @@ export class TrackSegment extends GPXTreeLeaf {
|
|||||||
return distanceWindowSmoothingWithDistanceAccumulator(points, 50, (accumulated, start, end) => 100 * (points[end].ele - points[start].ele) / accumulated);
|
return distanceWindowSmoothingWithDistanceAccumulator(points, 50, (accumulated, start, end) => 100 * (points[end].ele - points[start].ele) / accumulated);
|
||||||
}
|
}
|
||||||
|
|
||||||
append(points: TrackPoint[]): void {
|
|
||||||
this.trkpt = this.trkpt.concat(points);
|
|
||||||
}
|
|
||||||
|
|
||||||
replace(start: number, end: number, points: TrackPoint[]): void {
|
|
||||||
this.trkpt.splice(start, end - start + 1, ...points);
|
|
||||||
}
|
|
||||||
|
|
||||||
reverse(originalNextTimestamp?: Date, newPreviousTimestamp?: Date): void {
|
|
||||||
if (originalNextTimestamp !== undefined && newPreviousTimestamp !== undefined) {
|
|
||||||
let originalEndTimestamp = this.getEndTimestamp();
|
|
||||||
let newStartTimestamp = new Date(
|
|
||||||
newPreviousTimestamp.getTime() + originalNextTimestamp.getTime() - originalEndTimestamp.getTime()
|
|
||||||
);
|
|
||||||
|
|
||||||
this.trkpt.reverse();
|
|
||||||
|
|
||||||
for (let i = 0; i < this.trkpt.length; i++) {
|
|
||||||
this.trkpt[i].time = new Date(
|
|
||||||
newStartTimestamp.getTime() + (originalEndTimestamp.getTime() - this.trkpt[i].time.getTime())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.trkpt.reverse();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getStartTimestamp(): Date {
|
getStartTimestamp(): Date {
|
||||||
return this.trkpt[0].time;
|
return this.trkpt[0].time;
|
||||||
}
|
}
|
||||||
@@ -401,10 +378,6 @@ export class TrackSegment extends GPXTreeLeaf {
|
|||||||
return this.trkpt[this.trkpt.length - 1].time;
|
return this.trkpt[this.trkpt.length - 1].time;
|
||||||
}
|
}
|
||||||
|
|
||||||
getTrackPoints(): TrackPoint[] {
|
|
||||||
return this.trkpt;
|
|
||||||
}
|
|
||||||
|
|
||||||
getStatistics(): GPXStatistics {
|
getStatistics(): GPXStatistics {
|
||||||
return this._computeStatistics();
|
return this._computeStatistics();
|
||||||
}
|
}
|
||||||
@@ -436,6 +409,31 @@ export class TrackSegment extends GPXTreeLeaf {
|
|||||||
_data: cloneJSON(this._data),
|
_data: cloneJSON(this._data),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Producers
|
||||||
|
replace(segment: number, start: number, end: number, points: TrackPoint[]) {
|
||||||
|
return produce(this, (draft) => {
|
||||||
|
draft.trkpt.splice(start, end - start + 1, ...points);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
reverse(originalNextTimestamp?: Date, newPreviousTimestamp?: Date) {
|
||||||
|
return produce(this, (draft) => {
|
||||||
|
if (originalNextTimestamp !== undefined && newPreviousTimestamp !== undefined) {
|
||||||
|
let originalEndTimestamp = draft.getEndTimestamp();
|
||||||
|
let newStartTimestamp = new Date(
|
||||||
|
newPreviousTimestamp.getTime() + originalNextTimestamp.getTime() - originalEndTimestamp.getTime()
|
||||||
|
);
|
||||||
|
|
||||||
|
for (let i = 0; i < draft.trkpt.length; i++) {
|
||||||
|
draft.trkpt[i].time = new Date(
|
||||||
|
newStartTimestamp.getTime() + (originalEndTimestamp.getTime() - draft.trkpt[i].time.getTime())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
draft.trkpt.reverse();
|
||||||
|
});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export class TrackPoint {
|
export class TrackPoint {
|
||||||
@@ -447,7 +445,7 @@ export class TrackPoint {
|
|||||||
extensions?: TrackPointExtensions;
|
extensions?: TrackPointExtensions;
|
||||||
_data: { [key: string]: any } = {};
|
_data: { [key: string]: any } = {};
|
||||||
|
|
||||||
constructor(point: TrackPointType | TrackPoint) {
|
constructor(point: TrackPointType & { _data?: any } | TrackPoint) {
|
||||||
this.attributes = point.attributes;
|
this.attributes = point.attributes;
|
||||||
this.ele = point.ele;
|
this.ele = point.ele;
|
||||||
this.time = point.time;
|
this.time = point.time;
|
||||||
@@ -701,7 +699,7 @@ export function distance(coord1: Coordinates, coord2: Coordinates): number {
|
|||||||
return maxMeters;
|
return maxMeters;
|
||||||
}
|
}
|
||||||
|
|
||||||
function distanceWindowSmoothing(points: TrackPoint[], distanceWindow: number, accumulate: (index: number) => number, compute: (accumulated: number, start: number, end: number) => number, remove?: (index: number) => number): number[] {
|
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[] {
|
||||||
let result = [];
|
let result = [];
|
||||||
|
|
||||||
let start = 0, end = 0, accumulated = 0;
|
let start = 0, end = 0, accumulated = 0;
|
||||||
@@ -724,6 +722,6 @@ function distanceWindowSmoothing(points: TrackPoint[], distanceWindow: number, a
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
function distanceWindowSmoothingWithDistanceAccumulator(points: TrackPoint[], distanceWindow: number, compute: (accumulated: number, start: number, end: number) => number): number[] {
|
function distanceWindowSmoothingWithDistanceAccumulator(points: ReadonlyArray<Readonly<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()));
|
||||||
}
|
}
|
@@ -1,8 +1,8 @@
|
|||||||
export type GPXFileType = {
|
export type GPXFileType = {
|
||||||
attributes: GPXFileAttributes;
|
attributes: GPXFileAttributes;
|
||||||
metadata: Metadata;
|
metadata: Metadata;
|
||||||
wpt: WaypointType[];
|
wpt: ReadonlyArray<WaypointType>;
|
||||||
trk: TrackType[];
|
trk: ReadonlyArray<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: TrackSegmentType[];
|
trkseg: ReadonlyArray<TrackSegmentType>;
|
||||||
extensions?: TrackExtensions;
|
extensions?: TrackExtensions;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -67,7 +67,7 @@ export type LineStyleExtension = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type TrackSegmentType = {
|
export type TrackSegmentType = {
|
||||||
trkpt: TrackPointType[];
|
trkpt: ReadonlyArray<TrackPointType>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TrackPointType = {
|
export type TrackPointType = {
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
import { distance, type Coordinates, TrackPoint, TrackSegment } from "gpx";
|
import { distance, type Coordinates, TrackPoint, TrackSegment } from "gpx";
|
||||||
|
import { original } from "immer";
|
||||||
import { get, type Readable } from "svelte/store";
|
import { get, type Readable } from "svelte/store";
|
||||||
import { computeAnchorPoints } from "./Simplify";
|
import { computeAnchorPoints } from "./Simplify";
|
||||||
import mapboxgl from "mapbox-gl";
|
import mapboxgl from "mapbox-gl";
|
||||||
@@ -297,19 +298,13 @@ export class RoutingControls {
|
|||||||
let [previousAnchor, nextAnchor] = this.getNeighbouringAnchors(anchor);
|
let [previousAnchor, nextAnchor] = this.getNeighbouringAnchors(anchor);
|
||||||
|
|
||||||
if (previousAnchor === null && nextAnchor === null) { // Only one point, remove it
|
if (previousAnchor === null && nextAnchor === null) { // Only one point, remove it
|
||||||
dbUtils.applyToFile(this.fileId, (file) => {
|
dbUtils.applyToFile(this.fileId, (file) => file.replace(anchor.segmentIndex, 0, 0, []));
|
||||||
let segment = file.getSegments()[anchor.segmentIndex];
|
|
||||||
segment.replace(0, 0, []);
|
|
||||||
});
|
|
||||||
} else if (previousAnchor === null) { // First point, remove trackpoints until nextAnchor
|
} else if (previousAnchor === null) { // First point, remove trackpoints until nextAnchor
|
||||||
dbUtils.applyToFile(this.fileId, (file) => {
|
dbUtils.applyToFile(this.fileId, (file) => file.replace(anchor.segmentIndex, 0, nextAnchor.point._data.index - 1, []));
|
||||||
let segment = file.getSegments()[anchor.segmentIndex];
|
|
||||||
segment.replace(0, nextAnchor.point._data.index - 1, []);
|
|
||||||
});
|
|
||||||
} 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.getSegments()[anchor.segmentIndex];
|
let segment = file.getSegments()[anchor.segmentIndex];
|
||||||
segment.replace(previousAnchor.point._data.index + 1, segment.trkpt.length - 1, []);
|
return file.replace(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()]);
|
||||||
@@ -334,10 +329,7 @@ export class RoutingControls {
|
|||||||
|
|
||||||
if (!lastAnchor) {
|
if (!lastAnchor) {
|
||||||
// TODO, create segment if it does not exist
|
// TODO, create segment if it does not exist
|
||||||
dbUtils.applyToFile(this.fileId, (file) => {
|
dbUtils.applyToFile(this.fileId, (file) => file.replace(0, 0, 0, [newPoint]));
|
||||||
let segment = file.getSegments()[0];
|
|
||||||
segment.replace(0, 0, [newPoint]);
|
|
||||||
});
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -384,10 +376,10 @@ export class RoutingControls {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dbUtils.applyToFile(this.fileId, (file) => {
|
dbUtils.applyToFile(this.fileId, (file) => {
|
||||||
let segment = file.getSegments()[segments.length - 1];
|
let segment = original(file).getSegments()[segments.length - 1];
|
||||||
let newSegment = segment.clone();
|
let newSegment = segment.clone();
|
||||||
newSegment.reverse(undefined, undefined);
|
newSegment = newSegment.reverse(segment.getEndTimestamp(), segment.getEndTimestamp());
|
||||||
segment.replace(segment.trkpt.length, segment.trkpt.length, newSegment.trkpt);
|
return file.replace(segments.length - 1, segment.trkpt.length, segment.trkpt.length, newSegment.trkpt.map((point) => point));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -416,12 +408,9 @@ export class RoutingControls {
|
|||||||
let segment = anchors[0].segment;
|
let segment = anchors[0].segment;
|
||||||
|
|
||||||
if (anchors.length === 1) { // Only one anchor, update the point in the segment
|
if (anchors.length === 1) { // Only one anchor, update the point in the segment
|
||||||
dbUtils.applyToFile(this.fileId, (file) => {
|
dbUtils.applyToFile(this.fileId, (file) => file.replace(anchors[0].segmentIndex, 0, 0, [new TrackPoint({
|
||||||
let segment = file.getSegments()[anchors[0].segmentIndex];
|
attributes: targetCoordinates[0],
|
||||||
segment.replace(0, 0, [new TrackPoint({
|
})]));
|
||||||
attributes: targetCoordinates[0],
|
|
||||||
})]);
|
|
||||||
});
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -476,10 +465,7 @@ export class RoutingControls {
|
|||||||
anchor.point._data.zoom = 0; // Make these anchors permanent
|
anchor.point._data.zoom = 0; // Make these anchors permanent
|
||||||
});
|
});
|
||||||
|
|
||||||
dbUtils.applyToFile(this.fileId, (file) => {
|
dbUtils.applyToFile(this.fileId, (file) => file.replace(anchors[0].segmentIndex, start, end, response));
|
||||||
let segment = file.getSegments()[anchors[0].segmentIndex];
|
|
||||||
segment.replace(start, end, response);
|
|
||||||
});
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
import Dexie, { liveQuery } from 'dexie';
|
import Dexie, { liveQuery } from 'dexie';
|
||||||
import { GPXFile, GPXStatistics } from 'gpx';
|
import { GPXFile, GPXStatistics } from 'gpx';
|
||||||
import { enableMapSet, enablePatches, produceWithPatches, applyPatches, type Patch } from 'immer';
|
import { enableMapSet, enablePatches, produceWithPatches, applyPatches, type Patch, type WritableDraft, castDraft } from 'immer';
|
||||||
import { writable, get, derived, type Readable, type Writable } from 'svelte/store';
|
import { writable, get, derived, type Readable, type Writable } from 'svelte/store';
|
||||||
import { fileOrder, initTargetMapBounds, selectedFiles, updateTargetMapBounds } from './stores';
|
import { fileOrder, initTargetMapBounds, selectedFiles, updateTargetMapBounds } from './stores';
|
||||||
import { mode } from 'mode-watcher';
|
import { mode } from 'mode-watcher';
|
||||||
@@ -218,12 +218,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: GPXFile) => void) {
|
function applyToFiles(fileIds: string[], callback: (file: WritableDraft<GPXFile>) => GPXFile) {
|
||||||
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) {
|
||||||
callback(file);
|
draft.set(fileId, castDraft(callback(file)));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -298,10 +298,10 @@ export const dbUtils = {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
applyToFile: (id: string, callback: (file: GPXFile) => void) => {
|
applyToFile: (id: string, callback: (file: WritableDraft<GPXFile>) => GPXFile) => {
|
||||||
applyToFiles([id], callback);
|
applyToFiles([id], callback);
|
||||||
},
|
},
|
||||||
applyToSelectedFiles: (callback: (file: GPXFile) => void) => {
|
applyToSelectedFiles: (callback: (file: WritableDraft<GPXFile>) => GPXFile) => {
|
||||||
applyToFiles(get(fileOrder).filter(fileId => get(selectedFiles).has(fileId)), callback);
|
applyToFiles(get(fileOrder).filter(fileId => get(selectedFiles).has(fileId)), callback);
|
||||||
},
|
},
|
||||||
duplicateSelectedFiles: () => {
|
duplicateSelectedFiles: () => {
|
||||||
|
@@ -203,7 +203,7 @@ export function exportSelectedFiles() {
|
|||||||
if (get(selectedFiles).has(fileId)) {
|
if (get(selectedFiles).has(fileId)) {
|
||||||
let f = get(file);
|
let f = get(file);
|
||||||
if (f) {
|
if (f) {
|
||||||
exportFile(f);
|
exportFile(f.file);
|
||||||
await new Promise(resolve => setTimeout(resolve, 200));
|
await new Promise(resolve => setTimeout(resolve, 200));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -214,7 +214,7 @@ export function exportAllFiles() {
|
|||||||
get(fileObservers).forEach(async (file) => {
|
get(fileObservers).forEach(async (file) => {
|
||||||
let f = get(file);
|
let f = get(file);
|
||||||
if (f) {
|
if (f) {
|
||||||
exportFile(f);
|
exportFile(f.file);
|
||||||
await new Promise(resolve => setTimeout(resolve, 200));
|
await new Promise(resolve => setTimeout(resolve, 200));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
Reference in New Issue
Block a user