Format with eslint
This commit is contained in:
parent
24ffdb5d9c
commit
a2cd6ee681
24 changed files with 479 additions and 483 deletions
|
@ -1,11 +1,7 @@
|
||||||
/** @type { import("eslint").Linter.Config } */
|
/** @type { import("eslint").Linter.Config } */
|
||||||
module.exports = {
|
module.exports = {
|
||||||
root: true,
|
root: true,
|
||||||
extends: [
|
extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended', 'prettier'],
|
||||||
'eslint:recommended',
|
|
||||||
'plugin:@typescript-eslint/recommended',
|
|
||||||
'prettier',
|
|
||||||
],
|
|
||||||
parser: '@typescript-eslint/parser',
|
parser: '@typescript-eslint/parser',
|
||||||
plugins: ['@typescript-eslint'],
|
plugins: ['@typescript-eslint'],
|
||||||
parserOptions: {
|
parserOptions: {
|
||||||
|
|
134
gpx/src/gpx.ts
134
gpx/src/gpx.ts
|
@ -74,8 +74,8 @@ abstract class GPXTreeNode<T extends GPXTreeElement<any>> extends GPXTreeElement
|
||||||
}
|
}
|
||||||
|
|
||||||
getStatistics(): GPXStatistics {
|
getStatistics(): GPXStatistics {
|
||||||
let statistics = new GPXStatistics();
|
const statistics = new GPXStatistics();
|
||||||
for (let child of this.children) {
|
for (const child of this.children) {
|
||||||
statistics.mergeWith(child.getStatistics());
|
statistics.mergeWith(child.getStatistics());
|
||||||
}
|
}
|
||||||
return statistics;
|
return statistics;
|
||||||
|
@ -91,7 +91,7 @@ abstract class GPXTreeNode<T extends GPXTreeElement<any>> extends GPXTreeElement
|
||||||
|
|
||||||
// Producers
|
// Producers
|
||||||
_reverse(originalNextTimestamp?: Date, newPreviousTimestamp?: Date) {
|
_reverse(originalNextTimestamp?: Date, newPreviousTimestamp?: Date) {
|
||||||
let og = getOriginal(this);
|
const og = getOriginal(this);
|
||||||
if (!originalNextTimestamp && !newPreviousTimestamp) {
|
if (!originalNextTimestamp && !newPreviousTimestamp) {
|
||||||
originalNextTimestamp = og.getEndTimestamp();
|
originalNextTimestamp = og.getEndTimestamp();
|
||||||
newPreviousTimestamp = og.getStartTimestamp();
|
newPreviousTimestamp = og.getStartTimestamp();
|
||||||
|
@ -100,7 +100,7 @@ abstract class GPXTreeNode<T extends GPXTreeElement<any>> extends GPXTreeElement
|
||||||
this.children.reverse();
|
this.children.reverse();
|
||||||
|
|
||||||
for (let i = 0; i < og.children.length; i++) {
|
for (let i = 0; i < og.children.length; i++) {
|
||||||
let originalStartTimestamp =
|
const originalStartTimestamp =
|
||||||
og.children[og.children.length - i - 1].getStartTimestamp();
|
og.children[og.children.length - i - 1].getStartTimestamp();
|
||||||
|
|
||||||
this.children[i]._reverse(originalNextTimestamp, newPreviousTimestamp);
|
this.children[i]._reverse(originalNextTimestamp, newPreviousTimestamp);
|
||||||
|
@ -154,8 +154,8 @@ export class GPXFile extends GPXTreeNode<Track> {
|
||||||
this._data = gpx._data;
|
this._data = gpx._data;
|
||||||
}
|
}
|
||||||
if (!this._data.hasOwnProperty('style')) {
|
if (!this._data.hasOwnProperty('style')) {
|
||||||
let style = this.getStyle();
|
const style = this.getStyle();
|
||||||
let fileStyle = {};
|
const fileStyle = {};
|
||||||
if (style.color.length === 1) {
|
if (style.color.length === 1) {
|
||||||
fileStyle['gpx_style:color'] = style.color[0];
|
fileStyle['gpx_style:color'] = style.color[0];
|
||||||
}
|
}
|
||||||
|
@ -263,7 +263,7 @@ export class GPXFile extends GPXTreeNode<Track> {
|
||||||
}
|
}
|
||||||
|
|
||||||
toGPXFileType(exclude: string[] = []): GPXFileType {
|
toGPXFileType(exclude: string[] = []): GPXFileType {
|
||||||
let file: GPXFileType = {
|
const file: GPXFileType = {
|
||||||
attributes: cloneJSON(this.attributes),
|
attributes: cloneJSON(this.attributes),
|
||||||
metadata: {},
|
metadata: {},
|
||||||
wpt: this.wpt.map((wpt) => wpt.toWaypointType(exclude)),
|
wpt: this.wpt.map((wpt) => wpt.toWaypointType(exclude)),
|
||||||
|
@ -348,7 +348,7 @@ export class GPXFile extends GPXTreeNode<Track> {
|
||||||
let i = 0;
|
let i = 0;
|
||||||
let trackIndex = 0;
|
let trackIndex = 0;
|
||||||
while (i < this.trk.length) {
|
while (i < this.trk.length) {
|
||||||
let length = this.trk[i].getNumberOfTrackPoints();
|
const length = this.trk[i].getNumberOfTrackPoints();
|
||||||
if (trackIndices === undefined || trackIndices.includes(trackIndex)) {
|
if (trackIndices === undefined || trackIndices.includes(trackIndex)) {
|
||||||
if (start >= length || end < 0) {
|
if (start >= length || end < 0) {
|
||||||
this.trk.splice(i, 1);
|
this.trk.splice(i, 1);
|
||||||
|
@ -398,10 +398,10 @@ export class GPXFile extends GPXTreeNode<Track> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (deleteWaypoints) {
|
if (deleteWaypoints) {
|
||||||
let og = getOriginal(this); // Read as much as possible from the original object because it is faster
|
const og = getOriginal(this); // Read as much as possible from the original object because it is faster
|
||||||
let wpt = og.wpt.filter((point, waypointIndex) => {
|
const wpt = og.wpt.filter((point, waypointIndex) => {
|
||||||
if (waypointIndices === undefined || waypointIndices.includes(waypointIndex)) {
|
if (waypointIndices === undefined || waypointIndices.includes(waypointIndex)) {
|
||||||
let inBounds =
|
const inBounds =
|
||||||
point.attributes.lat >= bounds[0].lat &&
|
point.attributes.lat >= bounds[0].lat &&
|
||||||
point.attributes.lat <= bounds[1].lat &&
|
point.attributes.lat <= bounds[1].lat &&
|
||||||
point.attributes.lon >= bounds[0].lon &&
|
point.attributes.lon >= bounds[0].lon &&
|
||||||
|
@ -422,7 +422,7 @@ export class GPXFile extends GPXTreeNode<Track> {
|
||||||
trackIndex?: number,
|
trackIndex?: number,
|
||||||
segmentIndex?: number
|
segmentIndex?: number
|
||||||
) {
|
) {
|
||||||
let lastPoint = undefined;
|
const lastPoint = undefined;
|
||||||
this.trk.forEach((track, index) => {
|
this.trk.forEach((track, index) => {
|
||||||
if (trackIndex === undefined || trackIndex === index) {
|
if (trackIndex === undefined || trackIndex === index) {
|
||||||
track.changeTimestamps(startTime, speed, ratio, lastPoint, segmentIndex);
|
track.changeTimestamps(startTime, speed, ratio, lastPoint, segmentIndex);
|
||||||
|
@ -436,7 +436,7 @@ export class GPXFile extends GPXTreeNode<Track> {
|
||||||
trackIndex?: number,
|
trackIndex?: number,
|
||||||
segmentIndex?: number
|
segmentIndex?: number
|
||||||
) {
|
) {
|
||||||
let lastPoint = undefined;
|
const lastPoint = undefined;
|
||||||
this.trk.forEach((track, index) => {
|
this.trk.forEach((track, index) => {
|
||||||
if (trackIndex === undefined || trackIndex === index) {
|
if (trackIndex === undefined || trackIndex === index) {
|
||||||
track.createArtificialTimestamps(startTime, totalTime, lastPoint, segmentIndex);
|
track.createArtificialTimestamps(startTime, totalTime, lastPoint, segmentIndex);
|
||||||
|
@ -608,7 +608,7 @@ export class Track extends GPXTreeNode<TrackSegment> {
|
||||||
|
|
||||||
toGeoJSON(): GeoJSON.Feature[] {
|
toGeoJSON(): GeoJSON.Feature[] {
|
||||||
return this.children.map((child) => {
|
return this.children.map((child) => {
|
||||||
let geoJSON = child.toGeoJSON();
|
const geoJSON = child.toGeoJSON();
|
||||||
if (this.extensions && this.extensions['gpx_style:line']) {
|
if (this.extensions && this.extensions['gpx_style:line']) {
|
||||||
if (this.extensions['gpx_style:line']['gpx_style:color']) {
|
if (this.extensions['gpx_style:line']['gpx_style:color']) {
|
||||||
geoJSON.properties['color'] =
|
geoJSON.properties['color'] =
|
||||||
|
@ -682,7 +682,7 @@ export class Track extends GPXTreeNode<TrackSegment> {
|
||||||
let i = 0;
|
let i = 0;
|
||||||
let segmentIndex = 0;
|
let segmentIndex = 0;
|
||||||
while (i < this.trkseg.length) {
|
while (i < this.trkseg.length) {
|
||||||
let length = this.trkseg[i].getNumberOfTrackPoints();
|
const length = this.trkseg[i].getNumberOfTrackPoints();
|
||||||
if (segmentIndices === undefined || segmentIndices.includes(segmentIndex)) {
|
if (segmentIndices === undefined || segmentIndices.includes(segmentIndex)) {
|
||||||
if (start >= length || end < 0) {
|
if (start >= length || end < 0) {
|
||||||
this.trkseg.splice(i, 1);
|
this.trkseg.splice(i, 1);
|
||||||
|
@ -814,7 +814,7 @@ export class TrackSegment extends GPXTreeLeaf {
|
||||||
}
|
}
|
||||||
|
|
||||||
_computeStatistics(): GPXStatistics {
|
_computeStatistics(): GPXStatistics {
|
||||||
let statistics = new GPXStatistics();
|
const statistics = new GPXStatistics();
|
||||||
|
|
||||||
statistics.local.points = this.trkpt.map((point) => point);
|
statistics.local.points = this.trkpt.map((point) => point);
|
||||||
|
|
||||||
|
@ -902,7 +902,7 @@ export class TrackSegment extends GPXTreeLeaf {
|
||||||
points[i].extensions['gpxtpx:TrackPointExtension'] &&
|
points[i].extensions['gpxtpx:TrackPointExtension'] &&
|
||||||
points[i].extensions['gpxtpx:TrackPointExtension']['gpxtpx:atemp']
|
points[i].extensions['gpxtpx:TrackPointExtension']['gpxtpx:atemp']
|
||||||
) {
|
) {
|
||||||
let atemp = points[i].extensions['gpxtpx:TrackPointExtension']['gpxtpx:atemp'];
|
const atemp = points[i].extensions['gpxtpx:TrackPointExtension']['gpxtpx:atemp'];
|
||||||
statistics.global.atemp.avg =
|
statistics.global.atemp.avg =
|
||||||
(statistics.global.atemp.count * statistics.global.atemp.avg + atemp) /
|
(statistics.global.atemp.count * statistics.global.atemp.avg + atemp) /
|
||||||
(statistics.global.atemp.count + 1);
|
(statistics.global.atemp.count + 1);
|
||||||
|
@ -912,7 +912,7 @@ export class TrackSegment extends GPXTreeLeaf {
|
||||||
points[i].extensions['gpxtpx:TrackPointExtension'] &&
|
points[i].extensions['gpxtpx:TrackPointExtension'] &&
|
||||||
points[i].extensions['gpxtpx:TrackPointExtension']['gpxtpx:hr']
|
points[i].extensions['gpxtpx:TrackPointExtension']['gpxtpx:hr']
|
||||||
) {
|
) {
|
||||||
let hr = points[i].extensions['gpxtpx:TrackPointExtension']['gpxtpx:hr'];
|
const hr = points[i].extensions['gpxtpx:TrackPointExtension']['gpxtpx:hr'];
|
||||||
statistics.global.hr.avg =
|
statistics.global.hr.avg =
|
||||||
(statistics.global.hr.count * statistics.global.hr.avg + hr) /
|
(statistics.global.hr.count * statistics.global.hr.avg + hr) /
|
||||||
(statistics.global.hr.count + 1);
|
(statistics.global.hr.count + 1);
|
||||||
|
@ -922,7 +922,7 @@ export class TrackSegment extends GPXTreeLeaf {
|
||||||
points[i].extensions['gpxtpx:TrackPointExtension'] &&
|
points[i].extensions['gpxtpx:TrackPointExtension'] &&
|
||||||
points[i].extensions['gpxtpx:TrackPointExtension']['gpxtpx:cad']
|
points[i].extensions['gpxtpx:TrackPointExtension']['gpxtpx:cad']
|
||||||
) {
|
) {
|
||||||
let cad = points[i].extensions['gpxtpx:TrackPointExtension']['gpxtpx:cad'];
|
const cad = points[i].extensions['gpxtpx:TrackPointExtension']['gpxtpx:cad'];
|
||||||
statistics.global.cad.avg =
|
statistics.global.cad.avg =
|
||||||
(statistics.global.cad.count * statistics.global.cad.avg + cad) /
|
(statistics.global.cad.count * statistics.global.cad.avg + cad) /
|
||||||
(statistics.global.cad.count + 1);
|
(statistics.global.cad.count + 1);
|
||||||
|
@ -932,7 +932,7 @@ export class TrackSegment extends GPXTreeLeaf {
|
||||||
points[i].extensions['gpxpx:PowerExtension'] &&
|
points[i].extensions['gpxpx:PowerExtension'] &&
|
||||||
points[i].extensions['gpxpx:PowerExtension']['gpxpx:PowerInWatts']
|
points[i].extensions['gpxpx:PowerExtension']['gpxpx:PowerInWatts']
|
||||||
) {
|
) {
|
||||||
let power = points[i].extensions['gpxpx:PowerExtension']['gpxpx:PowerInWatts'];
|
const power = points[i].extensions['gpxpx:PowerExtension']['gpxpx:PowerInWatts'];
|
||||||
statistics.global.power.avg =
|
statistics.global.power.avg =
|
||||||
(statistics.global.power.count * statistics.global.power.avg + power) /
|
(statistics.global.power.count * statistics.global.power.avg + power) /
|
||||||
(statistics.global.power.count + 1);
|
(statistics.global.power.count + 1);
|
||||||
|
@ -993,7 +993,7 @@ export class TrackSegment extends GPXTreeLeaf {
|
||||||
_computeSmoothedElevation(): number[] {
|
_computeSmoothedElevation(): number[] {
|
||||||
const points = this.trkpt;
|
const points = this.trkpt;
|
||||||
|
|
||||||
let smoothed = distanceWindowSmoothing(
|
const smoothed = distanceWindowSmoothing(
|
||||||
points,
|
points,
|
||||||
100,
|
100,
|
||||||
(index) => points[index].ele ?? 0,
|
(index) => points[index].ele ?? 0,
|
||||||
|
@ -1021,21 +1021,21 @@ export class TrackSegment extends GPXTreeLeaf {
|
||||||
}
|
}
|
||||||
|
|
||||||
_computeSlopeSegments(statistics: GPXStatistics): [number[], number[]] {
|
_computeSlopeSegments(statistics: GPXStatistics): [number[], number[]] {
|
||||||
let simplified = ramerDouglasPeucker(
|
const simplified = ramerDouglasPeucker(
|
||||||
this.trkpt,
|
this.trkpt,
|
||||||
20,
|
20,
|
||||||
getElevationDistanceFunction(statistics)
|
getElevationDistanceFunction(statistics)
|
||||||
);
|
);
|
||||||
|
|
||||||
let slope = [];
|
const slope = [];
|
||||||
let length = [];
|
const length = [];
|
||||||
|
|
||||||
for (let i = 0; i < simplified.length - 1; i++) {
|
for (let i = 0; i < simplified.length - 1; i++) {
|
||||||
let start = simplified[i].point._data.index;
|
const start = simplified[i].point._data.index;
|
||||||
let end = simplified[i + 1].point._data.index;
|
const end = simplified[i + 1].point._data.index;
|
||||||
let dist =
|
const dist =
|
||||||
statistics.local.distance.total[end] - statistics.local.distance.total[start];
|
statistics.local.distance.total[end] - statistics.local.distance.total[start];
|
||||||
let ele = (simplified[i + 1].point.ele ?? 0) - (simplified[i].point.ele ?? 0);
|
const ele = (simplified[i + 1].point.ele ?? 0) - (simplified[i].point.ele ?? 0);
|
||||||
|
|
||||||
for (let j = start; j < end + (i + 1 === simplified.length - 1 ? 1 : 0); j++) {
|
for (let j = start; j < end + (i + 1 === simplified.length - 1 ? 1 : 0); j++) {
|
||||||
slope.push((0.1 * ele) / dist);
|
slope.push((0.1 * ele) / dist);
|
||||||
|
@ -1112,8 +1112,8 @@ export class TrackSegment extends GPXTreeLeaf {
|
||||||
startTime?: Date,
|
startTime?: Date,
|
||||||
removeGaps?: boolean
|
removeGaps?: boolean
|
||||||
) {
|
) {
|
||||||
let og = getOriginal(this); // Read as much as possible from the original object because it is faster
|
const og = getOriginal(this); // Read as much as possible from the original object because it is faster
|
||||||
let trkpt = og.trkpt.slice();
|
const trkpt = og.trkpt.slice();
|
||||||
|
|
||||||
if (speed !== undefined || (trkpt.length > 0 && trkpt[0].time !== undefined)) {
|
if (speed !== undefined || (trkpt.length > 0 && trkpt[0].time !== undefined)) {
|
||||||
// Must handle timestamps (either segment has timestamps or the new points will have timestamps)
|
// Must handle timestamps (either segment has timestamps or the new points will have timestamps)
|
||||||
|
@ -1127,7 +1127,7 @@ export class TrackSegment extends GPXTreeLeaf {
|
||||||
}
|
}
|
||||||
if (points.length > 0) {
|
if (points.length > 0) {
|
||||||
// Adapt timestamps of the new points
|
// Adapt timestamps of the new points
|
||||||
let last = start > 0 ? trkpt[start - 1] : undefined;
|
const last = start > 0 ? trkpt[start - 1] : undefined;
|
||||||
if (
|
if (
|
||||||
points[0].time === undefined ||
|
points[0].time === undefined ||
|
||||||
(points.length > 1 && points[1].time === undefined)
|
(points.length > 1 && points[1].time === undefined)
|
||||||
|
@ -1155,7 +1155,7 @@ export class TrackSegment extends GPXTreeLeaf {
|
||||||
} else {
|
} else {
|
||||||
// Different points, make the new points start one second after the previous point
|
// Different points, make the new points start one second after the previous point
|
||||||
if (points[0].time.getTime() - last.time.getTime() > 1000) {
|
if (points[0].time.getTime() - last.time.getTime() > 1000) {
|
||||||
let artificialLast = points[0].clone();
|
const artificialLast = points[0].clone();
|
||||||
artificialLast.time = new Date(last.time.getTime() + 1000);
|
artificialLast.time = new Date(last.time.getTime() + 1000);
|
||||||
points = withShiftedAndCompressedTimestamps(
|
points = withShiftedAndCompressedTimestamps(
|
||||||
points,
|
points,
|
||||||
|
@ -1169,7 +1169,7 @@ export class TrackSegment extends GPXTreeLeaf {
|
||||||
}
|
}
|
||||||
if (end < trkpt.length - 1) {
|
if (end < trkpt.length - 1) {
|
||||||
// Adapt timestamps of points after [start, end]
|
// Adapt timestamps of points after [start, end]
|
||||||
let last =
|
const last =
|
||||||
points.length > 0
|
points.length > 0
|
||||||
? points[points.length - 1]
|
? points[points.length - 1]
|
||||||
: start > 0
|
: start > 0
|
||||||
|
@ -1198,9 +1198,9 @@ export class TrackSegment extends GPXTreeLeaf {
|
||||||
}
|
}
|
||||||
|
|
||||||
_reverse(originalNextTimestamp?: Date, newPreviousTimestamp?: Date) {
|
_reverse(originalNextTimestamp?: Date, newPreviousTimestamp?: Date) {
|
||||||
let og = getOriginal(this); // Read as much as possible from the original object because it is faster
|
const og = getOriginal(this); // Read as much as possible from the original object because it is faster
|
||||||
let originalStartTimestamp = og.getStartTimestamp();
|
const originalStartTimestamp = og.getStartTimestamp();
|
||||||
let originalEndTimestamp = og.getEndTimestamp();
|
const originalEndTimestamp = og.getEndTimestamp();
|
||||||
if (!newPreviousTimestamp) {
|
if (!newPreviousTimestamp) {
|
||||||
newPreviousTimestamp = originalStartTimestamp;
|
newPreviousTimestamp = originalStartTimestamp;
|
||||||
}
|
}
|
||||||
|
@ -1216,13 +1216,13 @@ export class TrackSegment extends GPXTreeLeaf {
|
||||||
newPreviousTimestamp !== undefined &&
|
newPreviousTimestamp !== undefined &&
|
||||||
originalEndTimestamp !== undefined
|
originalEndTimestamp !== undefined
|
||||||
) {
|
) {
|
||||||
let newStartTimestamp = new Date(
|
const newStartTimestamp = new Date(
|
||||||
newPreviousTimestamp.getTime() +
|
newPreviousTimestamp.getTime() +
|
||||||
originalNextTimestamp.getTime() -
|
originalNextTimestamp.getTime() -
|
||||||
originalEndTimestamp.getTime()
|
originalEndTimestamp.getTime()
|
||||||
);
|
);
|
||||||
|
|
||||||
let trkpt = og.trkpt.map(
|
const trkpt = og.trkpt.map(
|
||||||
(point, i) =>
|
(point, i) =>
|
||||||
new TrackPoint({
|
new TrackPoint({
|
||||||
attributes: cloneJSON(point.attributes),
|
attributes: cloneJSON(point.attributes),
|
||||||
|
@ -1245,8 +1245,8 @@ export class TrackSegment extends GPXTreeLeaf {
|
||||||
}
|
}
|
||||||
|
|
||||||
roundTrip() {
|
roundTrip() {
|
||||||
let og = getOriginal(this); // Read as much as possible from the original object because it is faster
|
const og = getOriginal(this); // Read as much as possible from the original object because it is faster
|
||||||
let newSegment = og.clone();
|
const newSegment = og.clone();
|
||||||
newSegment._reverse(newSegment.getEndTimestamp(), newSegment.getEndTimestamp());
|
newSegment._reverse(newSegment.getEndTimestamp(), newSegment.getEndTimestamp());
|
||||||
this.replaceTrackPoints(this.trkpt.length, this.trkpt.length, newSegment.trkpt);
|
this.replaceTrackPoints(this.trkpt.length, this.trkpt.length, newSegment.trkpt);
|
||||||
}
|
}
|
||||||
|
@ -1256,9 +1256,9 @@ export class TrackSegment extends GPXTreeLeaf {
|
||||||
}
|
}
|
||||||
|
|
||||||
clean(bounds: [Coordinates, Coordinates], inside: boolean) {
|
clean(bounds: [Coordinates, Coordinates], inside: boolean) {
|
||||||
let og = getOriginal(this); // Read as much as possible from the original object because it is faster
|
const og = getOriginal(this); // Read as much as possible from the original object because it is faster
|
||||||
let trkpt = og.trkpt.filter((point) => {
|
const trkpt = og.trkpt.filter((point) => {
|
||||||
let inBounds =
|
const inBounds =
|
||||||
point.attributes.lat >= bounds[0].lat &&
|
point.attributes.lat >= bounds[0].lat &&
|
||||||
point.attributes.lat <= bounds[1].lat &&
|
point.attributes.lat <= bounds[1].lat &&
|
||||||
point.attributes.lon >= bounds[0].lon &&
|
point.attributes.lon >= bounds[0].lon &&
|
||||||
|
@ -1274,12 +1274,12 @@ export class TrackSegment extends GPXTreeLeaf {
|
||||||
lastPoint.time = startTime;
|
lastPoint.time = startTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
let og = getOriginal(this); // Read as much as possible from the original object because it is faster
|
const og = getOriginal(this); // Read as much as possible from the original object because it is faster
|
||||||
if (og.trkpt.length > 0 && og.trkpt[0].time === undefined) {
|
if (og.trkpt.length > 0 && og.trkpt[0].time === undefined) {
|
||||||
let trkpt = withTimestamps(og.trkpt, speed, lastPoint, startTime);
|
const trkpt = withTimestamps(og.trkpt, speed, lastPoint, startTime);
|
||||||
this.trkpt = freeze(trkpt); // Pre-freeze the array, faster as well
|
this.trkpt = freeze(trkpt); // Pre-freeze the array, faster as well
|
||||||
} else {
|
} else {
|
||||||
let trkpt = withShiftedAndCompressedTimestamps(og.trkpt, speed, ratio, lastPoint);
|
const trkpt = withShiftedAndCompressedTimestamps(og.trkpt, speed, ratio, lastPoint);
|
||||||
this.trkpt = freeze(trkpt); // Pre-freeze the array, faster as well
|
this.trkpt = freeze(trkpt); // Pre-freeze the array, faster as well
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1289,9 +1289,9 @@ export class TrackSegment extends GPXTreeLeaf {
|
||||||
totalTime: number,
|
totalTime: number,
|
||||||
lastPoint: TrackPoint | undefined
|
lastPoint: TrackPoint | undefined
|
||||||
) {
|
) {
|
||||||
let og = getOriginal(this); // Read as much as possible from the original object because it is faster
|
const og = getOriginal(this); // Read as much as possible from the original object because it is faster
|
||||||
let slope = og._computeSlope();
|
const slope = og._computeSlope();
|
||||||
let trkpt = withArtificialTimestamps(og.trkpt, totalTime, lastPoint, startTime, slope);
|
const trkpt = withArtificialTimestamps(og.trkpt, totalTime, lastPoint, startTime, slope);
|
||||||
this.trkpt = freeze(trkpt); // Pre-freeze the array, faster as well
|
this.trkpt = freeze(trkpt); // Pre-freeze the array, faster as well
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1854,7 +1854,7 @@ export class GPXStatistics {
|
||||||
end = this.local.points.length - 1;
|
end = this.local.points.length - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
let statistics = new GPXStatistics();
|
const statistics = new GPXStatistics();
|
||||||
|
|
||||||
statistics.local.points = this.local.points.slice(start, end + 1);
|
statistics.local.points = this.local.points.slice(start, end + 1);
|
||||||
|
|
||||||
|
@ -1926,14 +1926,14 @@ export function getElevationDistanceFunction(statistics: GPXStatistics) {
|
||||||
if (point1.ele === undefined || point2.ele === undefined || point3.ele === undefined) {
|
if (point1.ele === undefined || point2.ele === undefined || point3.ele === undefined) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
let x1 = statistics.local.distance.total[point1._data.index] * 1000;
|
const x1 = statistics.local.distance.total[point1._data.index] * 1000;
|
||||||
let x2 = statistics.local.distance.total[point2._data.index] * 1000;
|
const x2 = statistics.local.distance.total[point2._data.index] * 1000;
|
||||||
let x3 = statistics.local.distance.total[point3._data.index] * 1000;
|
const x3 = statistics.local.distance.total[point3._data.index] * 1000;
|
||||||
let y1 = point1.ele;
|
const y1 = point1.ele;
|
||||||
let y2 = point2.ele;
|
const y2 = point2.ele;
|
||||||
let y3 = point3.ele;
|
const y3 = point3.ele;
|
||||||
|
|
||||||
let dist = Math.sqrt(Math.pow(y2 - y1, 2) + Math.pow(x2 - x1, 2));
|
const dist = Math.sqrt(Math.pow(y2 - y1, 2) + Math.pow(x2 - x1, 2));
|
||||||
if (dist === 0) {
|
if (dist === 0) {
|
||||||
return Math.sqrt(Math.pow(x3 - x1, 2) + Math.pow(y3 - y1, 2));
|
return Math.sqrt(Math.pow(x3 - x1, 2) + Math.pow(y3 - y1, 2));
|
||||||
}
|
}
|
||||||
|
@ -1949,12 +1949,12 @@ function distanceWindowSmoothing(
|
||||||
compute: (accumulated: number, start: number, end: number) => number,
|
compute: (accumulated: number, start: number, end: number) => number,
|
||||||
remove?: (index: number) => number
|
remove?: (index: number) => number
|
||||||
): number[] {
|
): number[] {
|
||||||
let result = [];
|
const result = [];
|
||||||
|
|
||||||
let start = 0,
|
let start = 0,
|
||||||
end = 0,
|
end = 0,
|
||||||
accumulated = 0;
|
accumulated = 0;
|
||||||
for (var i = 0; i < points.length; i++) {
|
for (let i = 0; i < points.length; i++) {
|
||||||
while (
|
while (
|
||||||
start + 1 < i &&
|
start + 1 < i &&
|
||||||
distance(points[start].getCoordinates(), points[i].getCoordinates()) > distanceWindow
|
distance(points[start].getCoordinates(), points[i].getCoordinates()) > distanceWindow
|
||||||
|
@ -2010,7 +2010,7 @@ function withTimestamps(
|
||||||
last.time = startTime;
|
last.time = startTime;
|
||||||
}
|
}
|
||||||
return points.map((point) => {
|
return points.map((point) => {
|
||||||
let time = getTimestamp(last, point, speed);
|
const time = getTimestamp(last, point, speed);
|
||||||
last = point.clone();
|
last = point.clone();
|
||||||
last.time = time;
|
last.time = time;
|
||||||
return last;
|
return last;
|
||||||
|
@ -2023,10 +2023,10 @@ function withShiftedAndCompressedTimestamps(
|
||||||
ratio: number,
|
ratio: number,
|
||||||
lastPoint: TrackPoint
|
lastPoint: TrackPoint
|
||||||
): TrackPoint[] {
|
): TrackPoint[] {
|
||||||
let start = getTimestamp(lastPoint, points[0], speed);
|
const start = getTimestamp(lastPoint, points[0], speed);
|
||||||
let last = points[0];
|
let last = points[0];
|
||||||
return points.map((point) => {
|
return points.map((point) => {
|
||||||
let pt = point.clone();
|
const pt = point.clone();
|
||||||
if (point.time === undefined) {
|
if (point.time === undefined) {
|
||||||
pt.time = getTimestamp(last, point, speed);
|
pt.time = getTimestamp(last, point, speed);
|
||||||
} else {
|
} else {
|
||||||
|
@ -2046,19 +2046,19 @@ function withArtificialTimestamps(
|
||||||
startTime: Date,
|
startTime: Date,
|
||||||
slope: number[]
|
slope: number[]
|
||||||
): TrackPoint[] {
|
): TrackPoint[] {
|
||||||
let weight = [];
|
const weight = [];
|
||||||
let totalWeight = 0;
|
let totalWeight = 0;
|
||||||
|
|
||||||
for (let i = 0; i < points.length - 1; i++) {
|
for (let i = 0; i < points.length - 1; i++) {
|
||||||
let dist = distance(points[i].getCoordinates(), points[i + 1].getCoordinates());
|
const dist = distance(points[i].getCoordinates(), points[i + 1].getCoordinates());
|
||||||
let w = dist * (0.5 + 1 / (1 + Math.exp(-0.2 * slope[i])));
|
const w = dist * (0.5 + 1 / (1 + Math.exp(-0.2 * slope[i])));
|
||||||
weight.push(w);
|
weight.push(w);
|
||||||
totalWeight += w;
|
totalWeight += w;
|
||||||
}
|
}
|
||||||
|
|
||||||
let last = lastPoint;
|
let last = lastPoint;
|
||||||
return points.map((point, i) => {
|
return points.map((point, i) => {
|
||||||
let pt = point.clone();
|
const pt = point.clone();
|
||||||
if (i === 0) {
|
if (i === 0) {
|
||||||
pt.time = lastPoint?.time ?? startTime;
|
pt.time = lastPoint?.time ?? startTime;
|
||||||
} else {
|
} else {
|
||||||
|
@ -2077,7 +2077,7 @@ function getTimestamp(a: TrackPoint, b: TrackPoint, speed: number): Date {
|
||||||
} else if (speed === undefined) {
|
} else if (speed === undefined) {
|
||||||
return new Date(a.time.getTime() + 1000);
|
return new Date(a.time.getTime() + 1000);
|
||||||
}
|
}
|
||||||
let dist = distance(a.getCoordinates(), b.getCoordinates()) / 1000;
|
const dist = distance(a.getCoordinates(), b.getCoordinates()) / 1000;
|
||||||
return new Date(a.time.getTime() + (1000 * 3600 * dist) / speed);
|
return new Date(a.time.getTime() + (1000 * 3600 * dist) / speed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,7 @@ export function ramerDouglasPeucker(
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
let simplified = [
|
const simplified = [
|
||||||
{
|
{
|
||||||
point: points[0],
|
point: points[0],
|
||||||
},
|
},
|
||||||
|
@ -40,13 +40,13 @@ function ramerDouglasPeuckerRecursive(
|
||||||
end: number,
|
end: number,
|
||||||
simplified: SimplifiedTrackPoint[]
|
simplified: SimplifiedTrackPoint[]
|
||||||
) {
|
) {
|
||||||
let largest = {
|
const largest = {
|
||||||
index: 0,
|
index: 0,
|
||||||
distance: 0,
|
distance: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
for (let i = start + 1; i < end; i++) {
|
for (let i = start + 1; i < end; i++) {
|
||||||
let distance = measure(points[start], points[end], points[i]);
|
const distance = measure(points[start], points[end], points[i]);
|
||||||
if (distance > largest.distance) {
|
if (distance > largest.distance) {
|
||||||
largest.index = i;
|
largest.index = i;
|
||||||
largest.distance = distance;
|
largest.distance = distance;
|
||||||
|
@ -89,7 +89,7 @@ function crossarc(coord1: Coordinates, coord2: Coordinates, coord3: Coordinates)
|
||||||
// Prerequisites for the formulas
|
// Prerequisites for the formulas
|
||||||
const bear12 = bearing(lat1, lon1, lat2, lon2);
|
const bear12 = bearing(lat1, lon1, lat2, lon2);
|
||||||
const bear13 = bearing(lat1, lon1, lat3, lon3);
|
const bear13 = bearing(lat1, lon1, lat3, lon3);
|
||||||
let dis13 = distance(lat1, lon1, lat3, lon3);
|
const dis13 = distance(lat1, lon1, lat3, lon3);
|
||||||
|
|
||||||
let diff = Math.abs(bear13 - bear12);
|
let diff = Math.abs(bear13 - bear12);
|
||||||
if (diff > Math.PI) {
|
if (diff > Math.PI) {
|
||||||
|
@ -102,11 +102,11 @@ function crossarc(coord1: Coordinates, coord2: Coordinates, coord3: Coordinates)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find the cross-track distance.
|
// Find the cross-track distance.
|
||||||
let dxt = Math.asin(Math.sin(dis13 / earthRadius) * Math.sin(bear13 - bear12)) * earthRadius;
|
const dxt = Math.asin(Math.sin(dis13 / earthRadius) * Math.sin(bear13 - bear12)) * earthRadius;
|
||||||
|
|
||||||
// Is p4 beyond the arc?
|
// Is p4 beyond the arc?
|
||||||
let dis12 = distance(lat1, lon1, lat2, lon2);
|
const dis12 = distance(lat1, lon1, lat2, lon2);
|
||||||
let dis14 =
|
const dis14 =
|
||||||
Math.acos(Math.cos(dis13 / earthRadius) / Math.cos(dxt / earthRadius)) * earthRadius;
|
Math.acos(Math.cos(dis13 / earthRadius) / Math.cos(dxt / earthRadius)) * earthRadius;
|
||||||
if (dis14 > dis12) {
|
if (dis14 > dis12) {
|
||||||
return distance(lat2, lon2, lat3, lon3);
|
return distance(lat2, lon2, lat3, lon3);
|
||||||
|
@ -162,7 +162,7 @@ function projected(coord1: Coordinates, coord2: Coordinates, coord3: Coordinates
|
||||||
// Prerequisites for the formulas
|
// Prerequisites for the formulas
|
||||||
const bear12 = bearing(lat1, lon1, lat2, lon2);
|
const bear12 = bearing(lat1, lon1, lat2, lon2);
|
||||||
const bear13 = bearing(lat1, lon1, lat3, lon3);
|
const bear13 = bearing(lat1, lon1, lat3, lon3);
|
||||||
let dis13 = distance(lat1, lon1, lat3, lon3);
|
const dis13 = distance(lat1, lon1, lat3, lon3);
|
||||||
|
|
||||||
let diff = Math.abs(bear13 - bear12);
|
let diff = Math.abs(bear13 - bear12);
|
||||||
if (diff > Math.PI) {
|
if (diff > Math.PI) {
|
||||||
|
@ -175,11 +175,11 @@ function projected(coord1: Coordinates, coord2: Coordinates, coord3: Coordinates
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find the cross-track distance.
|
// Find the cross-track distance.
|
||||||
let dxt = Math.asin(Math.sin(dis13 / earthRadius) * Math.sin(bear13 - bear12)) * earthRadius;
|
const dxt = Math.asin(Math.sin(dis13 / earthRadius) * Math.sin(bear13 - bear12)) * earthRadius;
|
||||||
|
|
||||||
// Is p4 beyond the arc?
|
// Is p4 beyond the arc?
|
||||||
let dis12 = distance(lat1, lon1, lat2, lon2);
|
const dis12 = distance(lat1, lon1, lat2, lon2);
|
||||||
let dis14 =
|
const dis14 =
|
||||||
Math.acos(Math.cos(dis13 / earthRadius) / Math.cos(dxt / earthRadius)) * earthRadius;
|
Math.acos(Math.cos(dis13 / earthRadius) / Math.cos(dxt / earthRadius)) * earthRadius;
|
||||||
if (dis14 > dis12) {
|
if (dis14 > dis12) {
|
||||||
return coord2;
|
return coord2;
|
||||||
|
|
|
@ -96,10 +96,10 @@ function createPattern(
|
||||||
size: number = 16,
|
size: number = 16,
|
||||||
lineWidth: number = 4
|
lineWidth: number = 4
|
||||||
) {
|
) {
|
||||||
let canvas = document.createElement('canvas');
|
const canvas = document.createElement('canvas');
|
||||||
canvas.width = size;
|
canvas.width = size;
|
||||||
canvas.height = size;
|
canvas.height = size;
|
||||||
let ctx = canvas.getContext('2d');
|
const ctx = canvas.getContext('2d');
|
||||||
if (ctx) {
|
if (ctx) {
|
||||||
ctx.fillStyle = backgroundColor;
|
ctx.fillStyle = backgroundColor;
|
||||||
ctx.fillRect(0, 0, size, size);
|
ctx.fillRect(0, 0, size, size);
|
||||||
|
@ -139,11 +139,11 @@ export function getHighwayColor(
|
||||||
sacScale: string | undefined,
|
sacScale: string | undefined,
|
||||||
mtbScale: string | undefined
|
mtbScale: string | undefined
|
||||||
) {
|
) {
|
||||||
let backgroundColor = highwayColors[highway] ? highwayColors[highway] : highwayColors.missing;
|
const backgroundColor = highwayColors[highway] ? highwayColors[highway] : highwayColors.missing;
|
||||||
let sacScaleColor = sacScale ? sacScaleColors[sacScale] : undefined;
|
const sacScaleColor = sacScale ? sacScaleColors[sacScale] : undefined;
|
||||||
let mtbScaleColor = mtbScale ? mtbScaleColors[mtbScale] : undefined;
|
const mtbScaleColor = mtbScale ? mtbScaleColors[mtbScale] : undefined;
|
||||||
if (sacScale || mtbScale) {
|
if (sacScale || mtbScale) {
|
||||||
let patternId = `${backgroundColor}-${[sacScale, mtbScale].filter((x) => x).join('-')}`;
|
const patternId = `${backgroundColor}-${[sacScale, mtbScale].filter((x) => x).join('-')}`;
|
||||||
if (!patterns[patternId]) {
|
if (!patterns[patternId]) {
|
||||||
patterns[patternId] = createPattern(backgroundColor, sacScaleColor, mtbScaleColor);
|
patterns[patternId] = createPattern(backgroundColor, sacScaleColor, mtbScaleColor);
|
||||||
}
|
}
|
||||||
|
@ -164,8 +164,8 @@ export function getSlopeColor(slope: number): string {
|
||||||
v = 1 / (1 + Math.exp(-6 * v));
|
v = 1 / (1 + Math.exp(-6 * v));
|
||||||
v = v - 0.5;
|
v = v - 0.5;
|
||||||
|
|
||||||
let hue = ((0.5 - v) * 120).toString(10);
|
const hue = ((0.5 - v) * 120).toString(10);
|
||||||
let lightness = 90 - Math.abs(v) * 70;
|
const lightness = 90 - Math.abs(v) * 70;
|
||||||
|
|
||||||
return `hsl(${hue},70%,${lightness}%)`;
|
return `hsl(${hue},70%,${lightness}%)`;
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,7 +69,7 @@ export type Symbol = {
|
||||||
iconSvg?: string;
|
iconSvg?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const symbols: { [key: string]: Symbol } = {
|
export const symbols: { [key: string]: symbol } = {
|
||||||
alert: { value: 'Alert', icon: TriangleAlert, iconSvg: TriangleAlertSvg },
|
alert: { value: 'Alert', icon: TriangleAlert, iconSvg: TriangleAlertSvg },
|
||||||
anchor: { value: 'Anchor', icon: Anchor, iconSvg: AnchorSvg },
|
anchor: { value: 'Anchor', icon: Anchor, iconSvg: AnchorSvg },
|
||||||
bank: { value: 'Bank', icon: Landmark, iconSvg: LandmarkSvg },
|
bank: { value: 'Bank', icon: Landmark, iconSvg: LandmarkSvg },
|
||||||
|
|
|
@ -20,7 +20,7 @@ export class MapPopup {
|
||||||
this.map = map;
|
this.map = map;
|
||||||
this.popup = new mapboxgl.Popup(options);
|
this.popup = new mapboxgl.Popup(options);
|
||||||
|
|
||||||
let component = new MapPopupComponent({
|
const component = new MapPopupComponent({
|
||||||
target: document.body,
|
target: document.body,
|
||||||
props: {
|
props: {
|
||||||
item: this.item,
|
item: this.item,
|
||||||
|
|
|
@ -62,15 +62,15 @@ export const guideIcons: Record<string, string | ComponentType<Icon>> = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export function getPreviousGuide(currentGuide: string): string | undefined {
|
export function getPreviousGuide(currentGuide: string): string | undefined {
|
||||||
let subguides = currentGuide.split('/');
|
const subguides = currentGuide.split('/');
|
||||||
|
|
||||||
if (subguides.length === 1) {
|
if (subguides.length === 1) {
|
||||||
let keys = Object.keys(guides);
|
const keys = Object.keys(guides);
|
||||||
let index = keys.indexOf(currentGuide);
|
const index = keys.indexOf(currentGuide);
|
||||||
if (index === 0) {
|
if (index === 0) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
let previousGuide = keys[index - 1];
|
const previousGuide = keys[index - 1];
|
||||||
if (previousGuide === undefined) {
|
if (previousGuide === undefined) {
|
||||||
return undefined;
|
return undefined;
|
||||||
} else if (guides[previousGuide].length === 0) {
|
} else if (guides[previousGuide].length === 0) {
|
||||||
|
@ -80,7 +80,7 @@ export function getPreviousGuide(currentGuide: string): string | undefined {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (guides.hasOwnProperty(subguides[0])) {
|
if (guides.hasOwnProperty(subguides[0])) {
|
||||||
let subguideIndex = guides[subguides[0]].indexOf(subguides[1]);
|
const subguideIndex = guides[subguides[0]].indexOf(subguides[1]);
|
||||||
if (subguideIndex > 0) {
|
if (subguideIndex > 0) {
|
||||||
return `${subguides[0]}/${guides[subguides[0]][subguideIndex - 1]}`;
|
return `${subguides[0]}/${guides[subguides[0]][subguideIndex - 1]}`;
|
||||||
} else {
|
} else {
|
||||||
|
@ -93,13 +93,13 @@ export function getPreviousGuide(currentGuide: string): string | undefined {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getNextGuide(currentGuide: string): string | undefined {
|
export function getNextGuide(currentGuide: string): string | undefined {
|
||||||
let subguides = currentGuide.split('/');
|
const subguides = currentGuide.split('/');
|
||||||
|
|
||||||
if (subguides.length === 1) {
|
if (subguides.length === 1) {
|
||||||
if (guides.hasOwnProperty(currentGuide)) {
|
if (guides.hasOwnProperty(currentGuide)) {
|
||||||
if (guides[currentGuide].length === 0) {
|
if (guides[currentGuide].length === 0) {
|
||||||
let keys = Object.keys(guides);
|
const keys = Object.keys(guides);
|
||||||
let index = keys.indexOf(currentGuide);
|
const index = keys.indexOf(currentGuide);
|
||||||
return keys[index + 1];
|
return keys[index + 1];
|
||||||
} else {
|
} else {
|
||||||
return `${currentGuide}/${guides[currentGuide][0]}`;
|
return `${currentGuide}/${guides[currentGuide][0]}`;
|
||||||
|
@ -109,12 +109,12 @@ export function getNextGuide(currentGuide: string): string | undefined {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (guides.hasOwnProperty(subguides[0])) {
|
if (guides.hasOwnProperty(subguides[0])) {
|
||||||
let subguideIndex = guides[subguides[0]].indexOf(subguides[1]);
|
const subguideIndex = guides[subguides[0]].indexOf(subguides[1]);
|
||||||
if (subguideIndex < guides[subguides[0]].length - 1) {
|
if (subguideIndex < guides[subguides[0]].length - 1) {
|
||||||
return `${subguides[0]}/${guides[subguides[0]][subguideIndex + 1]}`;
|
return `${subguides[0]}/${guides[subguides[0]][subguideIndex + 1]}`;
|
||||||
} else {
|
} else {
|
||||||
let keys = Object.keys(guides);
|
const keys = Object.keys(guides);
|
||||||
let index = keys.indexOf(subguides[0]);
|
const index = keys.indexOf(subguides[0]);
|
||||||
return keys[index + 1];
|
return keys[index + 1];
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -110,13 +110,13 @@ export function getURLForGoogleDriveFile(fileId: string): string {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function convertOldEmbeddingOptions(options: URLSearchParams): any {
|
export function convertOldEmbeddingOptions(options: URLSearchParams): any {
|
||||||
let newOptions: any = {
|
const newOptions: any = {
|
||||||
token: PUBLIC_MAPBOX_TOKEN,
|
token: PUBLIC_MAPBOX_TOKEN,
|
||||||
files: [],
|
files: [],
|
||||||
ids: [],
|
ids: [],
|
||||||
};
|
};
|
||||||
if (options.has('state')) {
|
if (options.has('state')) {
|
||||||
let state = JSON.parse(options.get('state')!);
|
const state = JSON.parse(options.get('state')!);
|
||||||
if (state.ids) {
|
if (state.ids) {
|
||||||
newOptions.ids.push(...state.ids);
|
newOptions.ids.push(...state.ids);
|
||||||
}
|
}
|
||||||
|
@ -125,7 +125,7 @@ export function convertOldEmbeddingOptions(options: URLSearchParams): any {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (options.has('source')) {
|
if (options.has('source')) {
|
||||||
let basemap = options.get('source')!;
|
const basemap = options.get('source')!;
|
||||||
if (basemap === 'satellite') {
|
if (basemap === 'satellite') {
|
||||||
newOptions.basemap = 'mapboxSatellite';
|
newOptions.basemap = 'mapboxSatellite';
|
||||||
} else if (basemap === 'otm') {
|
} else if (basemap === 'otm') {
|
||||||
|
|
|
@ -336,9 +336,9 @@ export function moveItems(
|
||||||
sortItems(fromItems, false);
|
sortItems(fromItems, false);
|
||||||
sortItems(toItems, false);
|
sortItems(toItems, false);
|
||||||
|
|
||||||
let context: (GPXFile | Track | TrackSegment | Waypoint[] | Waypoint)[] = [];
|
const context: (GPXFile | Track | TrackSegment | Waypoint[] | Waypoint)[] = [];
|
||||||
fromItems.forEach((item) => {
|
fromItems.forEach((item) => {
|
||||||
let file = getFile(item.getFileId());
|
const file = getFile(item.getFileId());
|
||||||
if (file) {
|
if (file) {
|
||||||
if (item instanceof ListFileItem) {
|
if (item instanceof ListFileItem) {
|
||||||
context.push(file.clone());
|
context.push(file.clone());
|
||||||
|
@ -443,14 +443,14 @@ export function moveItems(
|
||||||
toItems.forEach((item, i) => {
|
toItems.forEach((item, i) => {
|
||||||
if (item instanceof ListFileItem) {
|
if (item instanceof ListFileItem) {
|
||||||
if (context[i] instanceof GPXFile) {
|
if (context[i] instanceof GPXFile) {
|
||||||
let newFile = context[i];
|
const newFile = context[i];
|
||||||
if (remove) {
|
if (remove) {
|
||||||
files.delete(newFile._data.id);
|
files.delete(newFile._data.id);
|
||||||
}
|
}
|
||||||
newFile._data.id = item.getFileId();
|
newFile._data.id = item.getFileId();
|
||||||
files.set(item.getFileId(), freeze(newFile));
|
files.set(item.getFileId(), freeze(newFile));
|
||||||
} else if (context[i] instanceof Track) {
|
} else if (context[i] instanceof Track) {
|
||||||
let newFile = newGPXFile();
|
const newFile = newGPXFile();
|
||||||
newFile._data.id = item.getFileId();
|
newFile._data.id = item.getFileId();
|
||||||
if (context[i].name) {
|
if (context[i].name) {
|
||||||
newFile.metadata.name = context[i].name;
|
newFile.metadata.name = context[i].name;
|
||||||
|
@ -458,7 +458,7 @@ export function moveItems(
|
||||||
newFile.replaceTracks(0, 0, [context[i]]);
|
newFile.replaceTracks(0, 0, [context[i]]);
|
||||||
files.set(item.getFileId(), freeze(newFile));
|
files.set(item.getFileId(), freeze(newFile));
|
||||||
} else if (context[i] instanceof TrackSegment) {
|
} else if (context[i] instanceof TrackSegment) {
|
||||||
let newFile = newGPXFile();
|
const newFile = newGPXFile();
|
||||||
newFile._data.id = item.getFileId();
|
newFile._data.id = item.getFileId();
|
||||||
newFile.replaceTracks(0, 0, [
|
newFile.replaceTracks(0, 0, [
|
||||||
new Track({
|
new Track({
|
||||||
|
|
|
@ -29,7 +29,7 @@ export class SelectionTreeType {
|
||||||
|
|
||||||
clear() {
|
clear() {
|
||||||
this.selected = false;
|
this.selected = false;
|
||||||
for (let key in this.children) {
|
for (const key in this.children) {
|
||||||
this.children[key].clear();
|
this.children[key].clear();
|
||||||
}
|
}
|
||||||
this.size = 0;
|
this.size = 0;
|
||||||
|
@ -37,13 +37,13 @@ export class SelectionTreeType {
|
||||||
|
|
||||||
_setOrToggle(item: ListItem, value?: boolean) {
|
_setOrToggle(item: ListItem, value?: boolean) {
|
||||||
if (item.level === this.item.level) {
|
if (item.level === this.item.level) {
|
||||||
let newSelected = value === undefined ? !this.selected : value;
|
const newSelected = value === undefined ? !this.selected : value;
|
||||||
if (this.selected !== newSelected) {
|
if (this.selected !== newSelected) {
|
||||||
this.selected = newSelected;
|
this.selected = newSelected;
|
||||||
this.size += this.selected ? 1 : -1;
|
this.size += this.selected ? 1 : -1;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let id = item.getIdAtLevel(this.item.level);
|
const id = item.getIdAtLevel(this.item.level);
|
||||||
if (id !== undefined) {
|
if (id !== undefined) {
|
||||||
if (!this.children.hasOwnProperty(id)) {
|
if (!this.children.hasOwnProperty(id)) {
|
||||||
this.children[id] = new SelectionTreeType(this.item.extend(id));
|
this.children[id] = new SelectionTreeType(this.item.extend(id));
|
||||||
|
@ -67,7 +67,7 @@ export class SelectionTreeType {
|
||||||
if (item.level === this.item.level) {
|
if (item.level === this.item.level) {
|
||||||
return this.selected;
|
return this.selected;
|
||||||
} else {
|
} else {
|
||||||
let id = item.getIdAtLevel(this.item.level);
|
const id = item.getIdAtLevel(this.item.level);
|
||||||
if (id !== undefined) {
|
if (id !== undefined) {
|
||||||
if (this.children.hasOwnProperty(id)) {
|
if (this.children.hasOwnProperty(id)) {
|
||||||
return this.children[id].has(item);
|
return this.children[id].has(item);
|
||||||
|
@ -85,7 +85,7 @@ export class SelectionTreeType {
|
||||||
) {
|
) {
|
||||||
return this.selected;
|
return this.selected;
|
||||||
}
|
}
|
||||||
let id = item.getIdAtLevel(this.item.level);
|
const id = item.getIdAtLevel(this.item.level);
|
||||||
if (id !== undefined) {
|
if (id !== undefined) {
|
||||||
if (this.children.hasOwnProperty(id)) {
|
if (this.children.hasOwnProperty(id)) {
|
||||||
return this.children[id].hasAnyParent(item, self);
|
return this.children[id].hasAnyParent(item, self);
|
||||||
|
@ -102,7 +102,7 @@ export class SelectionTreeType {
|
||||||
) {
|
) {
|
||||||
return this.selected;
|
return this.selected;
|
||||||
}
|
}
|
||||||
let id = item.getIdAtLevel(this.item.level);
|
const id = item.getIdAtLevel(this.item.level);
|
||||||
if (id !== undefined) {
|
if (id !== undefined) {
|
||||||
if (ignoreIds === undefined || ignoreIds.indexOf(id) === -1) {
|
if (ignoreIds === undefined || ignoreIds.indexOf(id) === -1) {
|
||||||
if (this.children.hasOwnProperty(id)) {
|
if (this.children.hasOwnProperty(id)) {
|
||||||
|
@ -110,7 +110,7 @@ export class SelectionTreeType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (let key in this.children) {
|
for (const key in this.children) {
|
||||||
if (ignoreIds === undefined || ignoreIds.indexOf(key) === -1) {
|
if (ignoreIds === undefined || ignoreIds.indexOf(key) === -1) {
|
||||||
if (this.children[key].hasAnyChildren(item, self, ignoreIds)) {
|
if (this.children[key].hasAnyChildren(item, self, ignoreIds)) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -125,7 +125,7 @@ export class SelectionTreeType {
|
||||||
if (this.selected) {
|
if (this.selected) {
|
||||||
selection.push(this.item);
|
selection.push(this.item);
|
||||||
}
|
}
|
||||||
for (let key in this.children) {
|
for (const key in this.children) {
|
||||||
this.children[key].getSelected(selection);
|
this.children[key].getSelected(selection);
|
||||||
}
|
}
|
||||||
return selection;
|
return selection;
|
||||||
|
@ -135,7 +135,7 @@ export class SelectionTreeType {
|
||||||
if (this.selected) {
|
if (this.selected) {
|
||||||
callback(this.item);
|
callback(this.item);
|
||||||
}
|
}
|
||||||
for (let key in this.children) {
|
for (const key in this.children) {
|
||||||
this.children[key].forEach(callback);
|
this.children[key].forEach(callback);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -190,14 +190,14 @@ export function selectAll() {
|
||||||
$selection.set(new ListFileItem(fileId), true);
|
$selection.set(new ListFileItem(fileId), true);
|
||||||
});
|
});
|
||||||
} else if (item instanceof ListTrackItem) {
|
} else if (item instanceof ListTrackItem) {
|
||||||
let file = getFile(item.getFileId());
|
const file = getFile(item.getFileId());
|
||||||
if (file) {
|
if (file) {
|
||||||
file.trk.forEach((_track, trackId) => {
|
file.trk.forEach((_track, trackId) => {
|
||||||
$selection.set(new ListTrackItem(item.getFileId(), trackId), true);
|
$selection.set(new ListTrackItem(item.getFileId(), trackId), true);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else if (item instanceof ListTrackSegmentItem) {
|
} else if (item instanceof ListTrackSegmentItem) {
|
||||||
let file = getFile(item.getFileId());
|
const file = getFile(item.getFileId());
|
||||||
if (file) {
|
if (file) {
|
||||||
file.trk[item.getTrackIndex()].trkseg.forEach((_segment, segmentId) => {
|
file.trk[item.getTrackIndex()].trkseg.forEach((_segment, segmentId) => {
|
||||||
$selection.set(
|
$selection.set(
|
||||||
|
@ -207,7 +207,7 @@ export function selectAll() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else if (item instanceof ListWaypointItem) {
|
} else if (item instanceof ListWaypointItem) {
|
||||||
let file = getFile(item.getFileId());
|
const file = getFile(item.getFileId());
|
||||||
if (file) {
|
if (file) {
|
||||||
file.wpt.forEach((_waypoint, waypointId) => {
|
file.wpt.forEach((_waypoint, waypointId) => {
|
||||||
$selection.set(new ListWaypointItem(item.getFileId(), waypointId), true);
|
$selection.set(new ListWaypointItem(item.getFileId(), waypointId), true);
|
||||||
|
@ -220,7 +220,7 @@ export function selectAll() {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getOrderedSelection(reverse: boolean = false): ListItem[] {
|
export function getOrderedSelection(reverse: boolean = false): ListItem[] {
|
||||||
let selected: ListItem[] = [];
|
const selected: ListItem[] = [];
|
||||||
applyToOrderedSelectedItemsFromFile((fileId, level, items) => {
|
applyToOrderedSelectedItemsFromFile((fileId, level, items) => {
|
||||||
selected.push(...items);
|
selected.push(...items);
|
||||||
}, reverse);
|
}, reverse);
|
||||||
|
@ -234,7 +234,7 @@ export function applyToOrderedItemsFromFile(
|
||||||
) {
|
) {
|
||||||
get(settings.fileOrder).forEach((fileId) => {
|
get(settings.fileOrder).forEach((fileId) => {
|
||||||
let level: ListLevel | undefined = undefined;
|
let level: ListLevel | undefined = undefined;
|
||||||
let items: ListItem[] = [];
|
const items: ListItem[] = [];
|
||||||
selectedItems.forEach((item) => {
|
selectedItems.forEach((item) => {
|
||||||
if (item.getFileId() === fileId) {
|
if (item.getFileId() === fileId) {
|
||||||
level = item.level;
|
level = item.level;
|
||||||
|
@ -268,7 +268,7 @@ export const copied = writable<ListItem[] | undefined>(undefined);
|
||||||
export const cut = writable(false);
|
export const cut = writable(false);
|
||||||
|
|
||||||
export function copySelection(): boolean {
|
export function copySelection(): boolean {
|
||||||
let selected = get(selection).getSelected();
|
const selected = get(selection).getSelected();
|
||||||
if (selected.length > 0) {
|
if (selected.length > 0) {
|
||||||
copied.set(selected);
|
copied.set(selected);
|
||||||
cut.set(false);
|
cut.set(false);
|
||||||
|
@ -289,7 +289,7 @@ function resetCopied() {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function pasteSelection() {
|
export function pasteSelection() {
|
||||||
let fromItems = get(copied);
|
const fromItems = get(copied);
|
||||||
if (fromItems === undefined || fromItems.length === 0) {
|
if (fromItems === undefined || fromItems.length === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -299,7 +299,7 @@ export function pasteSelection() {
|
||||||
selected = [new ListRootItem()];
|
selected = [new ListRootItem()];
|
||||||
}
|
}
|
||||||
|
|
||||||
let fromParent = fromItems[0].getParent();
|
const fromParent = fromItems[0].getParent();
|
||||||
let toParent = selected[selected.length - 1];
|
let toParent = selected[selected.length - 1];
|
||||||
|
|
||||||
let startIndex: number | undefined = undefined;
|
let startIndex: number | undefined = undefined;
|
||||||
|
@ -315,14 +315,14 @@ export function pasteSelection() {
|
||||||
toParent = toParent.getParent();
|
toParent = toParent.getParent();
|
||||||
}
|
}
|
||||||
|
|
||||||
let toItems: ListItem[] = [];
|
const toItems: ListItem[] = [];
|
||||||
if (toParent.level === ListLevel.ROOT) {
|
if (toParent.level === ListLevel.ROOT) {
|
||||||
let fileIds = getFileIds(fromItems.length);
|
const fileIds = getFileIds(fromItems.length);
|
||||||
fileIds.forEach((fileId) => {
|
fileIds.forEach((fileId) => {
|
||||||
toItems.push(new ListFileItem(fileId));
|
toItems.push(new ListFileItem(fileId));
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
let toFile = getFile(toParent.getFileId());
|
const toFile = getFile(toParent.getFileId());
|
||||||
if (toFile) {
|
if (toFile) {
|
||||||
fromItems.forEach((item, index) => {
|
fromItems.forEach((item, index) => {
|
||||||
if (toParent instanceof ListFileItem) {
|
if (toParent instanceof ListFileItem) {
|
||||||
|
@ -345,7 +345,7 @@ export function pasteSelection() {
|
||||||
}
|
}
|
||||||
} else if (toParent instanceof ListTrackItem) {
|
} else if (toParent instanceof ListTrackItem) {
|
||||||
if (item instanceof ListTrackSegmentItem) {
|
if (item instanceof ListTrackSegmentItem) {
|
||||||
let toTrackIndex = toParent.getTrackIndex();
|
const toTrackIndex = toParent.getTrackIndex();
|
||||||
toItems.push(
|
toItems.push(
|
||||||
new ListTrackSegmentItem(
|
new ListTrackSegmentItem(
|
||||||
toParent.getFileId(),
|
toParent.getFileId(),
|
||||||
|
|
|
@ -30,7 +30,7 @@ export class DistanceMarkers {
|
||||||
update() {
|
update() {
|
||||||
try {
|
try {
|
||||||
if (get(distanceMarkers)) {
|
if (get(distanceMarkers)) {
|
||||||
let distanceSource = this.map.getSource('distance-markers');
|
const distanceSource = this.map.getSource('distance-markers');
|
||||||
if (distanceSource) {
|
if (distanceSource) {
|
||||||
distanceSource.setData(this.getDistanceMarkersGeoJSON());
|
distanceSource.setData(this.getDistanceMarkersGeoJSON());
|
||||||
} else {
|
} else {
|
||||||
|
@ -88,17 +88,17 @@ export class DistanceMarkers {
|
||||||
}
|
}
|
||||||
|
|
||||||
getDistanceMarkersGeoJSON(): GeoJSON.FeatureCollection {
|
getDistanceMarkersGeoJSON(): GeoJSON.FeatureCollection {
|
||||||
let statistics = get(gpxStatistics);
|
const statistics = get(gpxStatistics);
|
||||||
|
|
||||||
let features = [];
|
const features = [];
|
||||||
let currentTargetDistance = 1;
|
let currentTargetDistance = 1;
|
||||||
for (let i = 0; i < statistics.local.distance.total.length; i++) {
|
for (let i = 0; i < statistics.local.distance.total.length; i++) {
|
||||||
if (
|
if (
|
||||||
statistics.local.distance.total[i] >=
|
statistics.local.distance.total[i] >=
|
||||||
currentTargetDistance * (get(distanceUnits) === 'metric' ? 1 : 1.60934)
|
currentTargetDistance * (get(distanceUnits) === 'metric' ? 1 : 1.60934)
|
||||||
) {
|
) {
|
||||||
let distance = currentTargetDistance.toFixed(0);
|
const distance = currentTargetDistance.toFixed(0);
|
||||||
let [level, minzoom] = stops.find(([d]) => currentTargetDistance % d === 0) ?? [
|
const [level, minzoom] = stops.find(([d]) => currentTargetDistance % d === 0) ?? [
|
||||||
0, 0,
|
0, 0,
|
||||||
];
|
];
|
||||||
features.push({
|
features.push({
|
||||||
|
|
|
@ -39,13 +39,13 @@ const colors = [
|
||||||
];
|
];
|
||||||
|
|
||||||
const colorCount: { [key: string]: number } = {};
|
const colorCount: { [key: string]: number } = {};
|
||||||
for (let color of colors) {
|
for (const color of colors) {
|
||||||
colorCount[color] = 0;
|
colorCount[color] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the color with the least amount of uses
|
// Get the color with the least amount of uses
|
||||||
function getColor() {
|
function getColor() {
|
||||||
let color = colors.reduce((a, b) => (colorCount[a] <= colorCount[b] ? a : b));
|
const color = colors.reduce((a, b) => (colorCount[a] <= colorCount[b] ? a : b));
|
||||||
colorCount[color]++;
|
colorCount[color]++;
|
||||||
return color;
|
return color;
|
||||||
}
|
}
|
||||||
|
@ -82,7 +82,7 @@ class KeyDown {
|
||||||
}
|
}
|
||||||
|
|
||||||
function getMarkerForSymbol(symbol: string | undefined, layerColor: string) {
|
function getMarkerForSymbol(symbol: string | undefined, layerColor: string) {
|
||||||
let symbolSvg = symbol ? symbols[symbol]?.iconSvg : undefined;
|
const symbolSvg = symbol ? symbols[symbol]?.iconSvg : undefined;
|
||||||
return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||||
${Square.replace('width="24"', 'width="12"')
|
${Square.replace('width="24"', 'width="12"')
|
||||||
.replace('height="24"', 'height="12"')
|
.replace('height="24"', 'height="12"')
|
||||||
|
@ -138,7 +138,7 @@ export class GPXLayer {
|
||||||
this.unsubscribe.push(file.subscribe(this.updateBinded));
|
this.unsubscribe.push(file.subscribe(this.updateBinded));
|
||||||
this.unsubscribe.push(
|
this.unsubscribe.push(
|
||||||
selection.subscribe(($selection) => {
|
selection.subscribe(($selection) => {
|
||||||
let newSelected = $selection.hasAnyChildren(new ListFileItem(this.fileId));
|
const newSelected = $selection.hasAnyChildren(new ListFileItem(this.fileId));
|
||||||
if (this.selected || newSelected) {
|
if (this.selected || newSelected) {
|
||||||
this.selected = newSelected;
|
this.selected = newSelected;
|
||||||
this.update();
|
this.update();
|
||||||
|
@ -170,7 +170,7 @@ export class GPXLayer {
|
||||||
}
|
}
|
||||||
|
|
||||||
update() {
|
update() {
|
||||||
let file = get(this.file)?.file;
|
const file = get(this.file)?.file;
|
||||||
if (!file) {
|
if (!file) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -185,7 +185,7 @@ export class GPXLayer {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let source = this.map.getSource(this.fileId);
|
const source = this.map.getSource(this.fileId);
|
||||||
if (source) {
|
if (source) {
|
||||||
source.setData(this.getGeoJSON());
|
source.setData(this.getGeoJSON());
|
||||||
} else {
|
} else {
|
||||||
|
@ -251,7 +251,7 @@ export class GPXLayer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let visibleItems: [number, number][] = [];
|
const visibleItems: [number, number][] = [];
|
||||||
file.forEachSegment((segment, trackIndex, segmentIndex) => {
|
file.forEachSegment((segment, trackIndex, segmentIndex) => {
|
||||||
if (!segment._data.hidden) {
|
if (!segment._data.hidden) {
|
||||||
visibleItems.push([trackIndex, segmentIndex]);
|
visibleItems.push([trackIndex, segmentIndex]);
|
||||||
|
@ -294,7 +294,7 @@ export class GPXLayer {
|
||||||
if (get(selection).hasAnyChildren(new ListFileItem(this.fileId))) {
|
if (get(selection).hasAnyChildren(new ListFileItem(this.fileId))) {
|
||||||
file.wpt.forEach((waypoint) => {
|
file.wpt.forEach((waypoint) => {
|
||||||
// Update markers
|
// Update markers
|
||||||
let symbolKey = getSymbolKey(waypoint.sym);
|
const symbolKey = getSymbolKey(waypoint.sym);
|
||||||
if (markerIndex < this.markers.length) {
|
if (markerIndex < this.markers.length) {
|
||||||
this.markers[markerIndex].getElement().innerHTML = getMarkerForSymbol(
|
this.markers[markerIndex].getElement().innerHTML = getMarkerForSymbol(
|
||||||
symbolKey,
|
symbolKey,
|
||||||
|
@ -306,10 +306,10 @@ export class GPXLayer {
|
||||||
writable: true,
|
writable: true,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
let element = document.createElement('div');
|
const element = document.createElement('div');
|
||||||
element.classList.add('w-8', 'h-8', 'drop-shadow-xl');
|
element.classList.add('w-8', 'h-8', 'drop-shadow-xl');
|
||||||
element.innerHTML = getMarkerForSymbol(symbolKey, this.layerColor);
|
element.innerHTML = getMarkerForSymbol(symbolKey, this.layerColor);
|
||||||
let marker = new mapboxgl.Marker({
|
const marker = new mapboxgl.Marker({
|
||||||
draggable: this.draggable,
|
draggable: this.draggable,
|
||||||
element,
|
element,
|
||||||
anchor: 'bottom',
|
anchor: 'bottom',
|
||||||
|
@ -367,8 +367,8 @@ export class GPXLayer {
|
||||||
marker.getElement().style.cursor = '';
|
marker.getElement().style.cursor = '';
|
||||||
getElevation([marker._waypoint]).then((ele) => {
|
getElevation([marker._waypoint]).then((ele) => {
|
||||||
dbUtils.applyToFile(this.fileId, (file) => {
|
dbUtils.applyToFile(this.fileId, (file) => {
|
||||||
let latLng = marker.getLngLat();
|
const latLng = marker.getLngLat();
|
||||||
let wpt = file.wpt[marker._waypoint._data.index];
|
const wpt = file.wpt[marker._waypoint._data.index];
|
||||||
wpt.setCoordinates({
|
wpt.setCoordinates({
|
||||||
lat: latLng.lat,
|
lat: latLng.lat,
|
||||||
lon: latLng.lng,
|
lon: latLng.lng,
|
||||||
|
@ -446,8 +446,8 @@ export class GPXLayer {
|
||||||
}
|
}
|
||||||
|
|
||||||
layerOnMouseEnter(e: any) {
|
layerOnMouseEnter(e: any) {
|
||||||
let trackIndex = e.features[0].properties.trackIndex;
|
const trackIndex = e.features[0].properties.trackIndex;
|
||||||
let segmentIndex = e.features[0].properties.segmentIndex;
|
const segmentIndex = e.features[0].properties.segmentIndex;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
get(currentTool) === Tool.SCISSORS &&
|
get(currentTool) === Tool.SCISSORS &&
|
||||||
|
@ -467,8 +467,8 @@ export class GPXLayer {
|
||||||
|
|
||||||
layerOnMouseMove(e: any) {
|
layerOnMouseMove(e: any) {
|
||||||
if (inspectKeyDown?.isDown()) {
|
if (inspectKeyDown?.isDown()) {
|
||||||
let trackIndex = e.features[0].properties.trackIndex;
|
const trackIndex = e.features[0].properties.trackIndex;
|
||||||
let segmentIndex = e.features[0].properties.segmentIndex;
|
const segmentIndex = e.features[0].properties.segmentIndex;
|
||||||
|
|
||||||
const file = get(this.file)?.file;
|
const file = get(this.file)?.file;
|
||||||
if (file) {
|
if (file) {
|
||||||
|
@ -489,8 +489,8 @@ export class GPXLayer {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let trackIndex = e.features[0].properties.trackIndex;
|
const trackIndex = e.features[0].properties.trackIndex;
|
||||||
let segmentIndex = e.features[0].properties.segmentIndex;
|
const segmentIndex = e.features[0].properties.segmentIndex;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
get(currentTool) === Tool.SCISSORS &&
|
get(currentTool) === Tool.SCISSORS &&
|
||||||
|
@ -505,7 +505,7 @@ export class GPXLayer {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let file = get(this.file)?.file;
|
const file = get(this.file)?.file;
|
||||||
if (!file) {
|
if (!file) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -535,7 +535,7 @@ export class GPXLayer {
|
||||||
}
|
}
|
||||||
|
|
||||||
getGeoJSON(): GeoJSON.FeatureCollection {
|
getGeoJSON(): GeoJSON.FeatureCollection {
|
||||||
let file = get(this.file)?.file;
|
const file = get(this.file)?.file;
|
||||||
if (!file) {
|
if (!file) {
|
||||||
return {
|
return {
|
||||||
type: 'FeatureCollection',
|
type: 'FeatureCollection',
|
||||||
|
@ -543,11 +543,11 @@ export class GPXLayer {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
let data = file.toGeoJSON();
|
const data = file.toGeoJSON();
|
||||||
|
|
||||||
let trackIndex = 0,
|
let trackIndex = 0,
|
||||||
segmentIndex = 0;
|
segmentIndex = 0;
|
||||||
for (let feature of data.features) {
|
for (const feature of data.features) {
|
||||||
if (!feature.properties) {
|
if (!feature.properties) {
|
||||||
feature.properties = {};
|
feature.properties = {};
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,8 +12,8 @@ export class StartEndMarkers {
|
||||||
constructor(map: mapboxgl.Map) {
|
constructor(map: mapboxgl.Map) {
|
||||||
this.map = map;
|
this.map = map;
|
||||||
|
|
||||||
let startElement = document.createElement('div');
|
const startElement = document.createElement('div');
|
||||||
let endElement = document.createElement('div');
|
const endElement = document.createElement('div');
|
||||||
startElement.className = `h-4 w-4 rounded-full bg-green-500 border-2 border-white`;
|
startElement.className = `h-4 w-4 rounded-full bg-green-500 border-2 border-white`;
|
||||||
endElement.className = `h-4 w-4 rounded-full border-2 border-white`;
|
endElement.className = `h-4 w-4 rounded-full border-2 border-white`;
|
||||||
endElement.style.background =
|
endElement.style.background =
|
||||||
|
@ -28,8 +28,8 @@ export class StartEndMarkers {
|
||||||
}
|
}
|
||||||
|
|
||||||
update() {
|
update() {
|
||||||
let tool = get(currentTool);
|
const tool = get(currentTool);
|
||||||
let statistics = get(slicedGPXStatistics)?.[0] ?? get(gpxStatistics);
|
const statistics = get(slicedGPXStatistics)?.[0] ?? get(gpxStatistics);
|
||||||
if (statistics.local.points.length > 0 && tool !== Tool.ROUTING) {
|
if (statistics.local.points.length > 0 && tool !== Tool.ROUTING) {
|
||||||
this.start.setLngLat(statistics.local.points[0].getCoordinates()).addTo(this.map);
|
this.start.setLngLat(statistics.local.points[0].getCoordinates()).addTo(this.map);
|
||||||
this.end
|
this.end
|
||||||
|
|
|
@ -12,7 +12,7 @@ const mercator = new SphericalMercator({
|
||||||
size: 256,
|
size: 256,
|
||||||
});
|
});
|
||||||
|
|
||||||
let data = writable<GeoJSON.FeatureCollection>({ type: 'FeatureCollection', features: [] });
|
const data = writable<GeoJSON.FeatureCollection>({ type: 'FeatureCollection', features: [] });
|
||||||
|
|
||||||
liveQuery(() => db.overpassdata.toArray()).subscribe((pois) => {
|
liveQuery(() => db.overpassdata.toArray()).subscribe((pois) => {
|
||||||
data.set({ type: 'FeatureCollection', features: pois.map((poi) => poi.poi) });
|
data.set({ type: 'FeatureCollection', features: pois.map((poi) => poi.poi) });
|
||||||
|
@ -68,10 +68,10 @@ export class OverpassLayer {
|
||||||
update() {
|
update() {
|
||||||
this.loadIcons();
|
this.loadIcons();
|
||||||
|
|
||||||
let d = get(data);
|
const d = get(data);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let source = this.map.getSource('overpass');
|
const source = this.map.getSource('overpass');
|
||||||
if (source) {
|
if (source) {
|
||||||
source.setData(d);
|
source.setData(d);
|
||||||
} else {
|
} else {
|
||||||
|
@ -132,13 +132,13 @@ export class OverpassLayer {
|
||||||
}
|
}
|
||||||
|
|
||||||
query(bbox: [number, number, number, number]) {
|
query(bbox: [number, number, number, number]) {
|
||||||
let queries = getCurrentQueries();
|
const queries = getCurrentQueries();
|
||||||
if (queries.length === 0) {
|
if (queries.length === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let tileLimits = mercator.xyz(bbox, this.queryZoom);
|
const tileLimits = mercator.xyz(bbox, this.queryZoom);
|
||||||
let time = Date.now();
|
const time = Date.now();
|
||||||
|
|
||||||
for (let x = tileLimits.minX; x <= tileLimits.maxX; x++) {
|
for (let x = tileLimits.minX; x <= tileLimits.maxX; x++) {
|
||||||
for (let y = tileLimits.minY; y <= tileLimits.maxY; y++) {
|
for (let y = tileLimits.minY; y <= tileLimits.maxY; y++) {
|
||||||
|
@ -151,7 +151,7 @@ export class OverpassLayer {
|
||||||
.equals([x, y])
|
.equals([x, y])
|
||||||
.toArray()
|
.toArray()
|
||||||
.then((querytiles) => {
|
.then((querytiles) => {
|
||||||
let missingQueries = queries.filter(
|
const missingQueries = queries.filter(
|
||||||
(query) =>
|
(query) =>
|
||||||
!querytiles.some(
|
!querytiles.some(
|
||||||
(querytile) =>
|
(querytile) =>
|
||||||
|
@ -191,16 +191,16 @@ export class OverpassLayer {
|
||||||
}
|
}
|
||||||
|
|
||||||
storeOverpassData(x: number, y: number, queries: string[], data: any) {
|
storeOverpassData(x: number, y: number, queries: string[], data: any) {
|
||||||
let time = Date.now();
|
const time = Date.now();
|
||||||
let queryTiles = queries.map((query) => ({ x, y, query, time }));
|
const queryTiles = queries.map((query) => ({ x, y, query, time }));
|
||||||
let pois: { query: string; id: number; poi: GeoJSON.Feature }[] = [];
|
const pois: { query: string; id: number; poi: GeoJSON.Feature }[] = [];
|
||||||
|
|
||||||
if (data.elements === undefined) {
|
if (data.elements === undefined) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let element of data.elements) {
|
for (const element of data.elements) {
|
||||||
for (let query of queries) {
|
for (const query of queries) {
|
||||||
if (belongsToQuery(element, query)) {
|
if (belongsToQuery(element, query)) {
|
||||||
pois.push({
|
pois.push({
|
||||||
query,
|
query,
|
||||||
|
@ -236,10 +236,10 @@ export class OverpassLayer {
|
||||||
}
|
}
|
||||||
|
|
||||||
loadIcons() {
|
loadIcons() {
|
||||||
let currentQueries = getCurrentQueries();
|
const currentQueries = getCurrentQueries();
|
||||||
currentQueries.forEach((query) => {
|
currentQueries.forEach((query) => {
|
||||||
if (!this.map.hasImage(`overpass-${query}`)) {
|
if (!this.map.hasImage(`overpass-${query}`)) {
|
||||||
let icon = new Image(100, 100);
|
const icon = new Image(100, 100);
|
||||||
icon.onload = () => {
|
icon.onload = () => {
|
||||||
if (!this.map.hasImage(`overpass-${query}`)) {
|
if (!this.map.hasImage(`overpass-${query}`)) {
|
||||||
this.map.addImage(`overpass-${query}`, icon);
|
this.map.addImage(`overpass-${query}`, icon);
|
||||||
|
@ -280,7 +280,7 @@ function getQuery(query: string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function getQueryItem(tags: Record<string, string | boolean | string[]>) {
|
function getQueryItem(tags: Record<string, string | boolean | string[]>) {
|
||||||
let arrayEntry = Object.entries(tags).find(([_, value]) => Array.isArray(value));
|
const arrayEntry = Object.entries(tags).find(([_, value]) => Array.isArray(value));
|
||||||
if (arrayEntry !== undefined) {
|
if (arrayEntry !== undefined) {
|
||||||
return arrayEntry[1]
|
return arrayEntry[1]
|
||||||
.map(
|
.map(
|
||||||
|
@ -312,7 +312,7 @@ function belongsToQueryItem(element: any, tags: Record<string, string | boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
function getCurrentQueries() {
|
function getCurrentQueries() {
|
||||||
let currentQueries = get(currentOverpassQueries);
|
const currentQueries = get(currentOverpassQueries);
|
||||||
if (currentQueries === undefined) {
|
if (currentQueries === undefined) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
|
@ -78,7 +78,7 @@ export class MapillaryLayer {
|
||||||
this.viewer.on('position', async () => {
|
this.viewer.on('position', async () => {
|
||||||
if (this.active) {
|
if (this.active) {
|
||||||
popupOpen.set(true);
|
popupOpen.set(true);
|
||||||
let latLng = await this.viewer.getPosition();
|
const latLng = await this.viewer.getPosition();
|
||||||
this.marker.setLngLat(latLng).addTo(this.map);
|
this.marker.setLngLat(latLng).addTo(this.map);
|
||||||
if (!this.map.getBounds()?.contains(latLng)) {
|
if (!this.map.getBounds()?.contains(latLng)) {
|
||||||
this.map.panTo(latLng);
|
this.map.panTo(latLng);
|
||||||
|
|
|
@ -59,20 +59,20 @@ async function getRoute(
|
||||||
brouterProfile: string,
|
brouterProfile: string,
|
||||||
privateRoads: boolean
|
privateRoads: boolean
|
||||||
): Promise<TrackPoint[]> {
|
): Promise<TrackPoint[]> {
|
||||||
let url = `https://routing.gpx.studio?lonlats=${points.map((point) => `${point.lon.toFixed(8)},${point.lat.toFixed(8)}`).join('|')}&profile=${brouterProfile + (privateRoads ? '-private' : '')}&format=geojson&alternativeidx=0`;
|
const url = `https://routing.gpx.studio?lonlats=${points.map((point) => `${point.lon.toFixed(8)},${point.lat.toFixed(8)}`).join('|')}&profile=${brouterProfile + (privateRoads ? '-private' : '')}&format=geojson&alternativeidx=0`;
|
||||||
|
|
||||||
let response = await fetch(url);
|
const response = await fetch(url);
|
||||||
|
|
||||||
// Check if the response is ok
|
// Check if the response is ok
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error(`${await response.text()}`);
|
throw new Error(`${await response.text()}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
let geojson = await response.json();
|
const geojson = await response.json();
|
||||||
|
|
||||||
let route: TrackPoint[] = [];
|
const route: TrackPoint[] = [];
|
||||||
let coordinates = geojson.features[0].geometry.coordinates;
|
const coordinates = geojson.features[0].geometry.coordinates;
|
||||||
let messages = geojson.features[0].properties.messages;
|
const messages = geojson.features[0].properties.messages;
|
||||||
|
|
||||||
const lngIdx = messages[0].indexOf('Longitude');
|
const lngIdx = messages[0].indexOf('Longitude');
|
||||||
const latIdx = messages[0].indexOf('Latitude');
|
const latIdx = messages[0].indexOf('Latitude');
|
||||||
|
@ -81,7 +81,7 @@ async function getRoute(
|
||||||
let tags = messageIdx < messages.length ? getTags(messages[messageIdx][tagIdx]) : {};
|
let tags = messageIdx < messages.length ? getTags(messages[messageIdx][tagIdx]) : {};
|
||||||
|
|
||||||
for (let i = 0; i < coordinates.length; i++) {
|
for (let i = 0; i < coordinates.length; i++) {
|
||||||
let coord = coordinates[i];
|
const coord = coordinates[i];
|
||||||
route.push(
|
route.push(
|
||||||
new TrackPoint({
|
new TrackPoint({
|
||||||
attributes: {
|
attributes: {
|
||||||
|
@ -111,7 +111,7 @@ async function getRoute(
|
||||||
|
|
||||||
function getTags(message: string): { [key: string]: string } {
|
function getTags(message: string): { [key: string]: string } {
|
||||||
const fields = message.split(' ');
|
const fields = message.split(' ');
|
||||||
let tags: { [key: string]: string } = {};
|
const tags: { [key: string]: string } = {};
|
||||||
for (let i = 0; i < fields.length; i++) {
|
for (let i = 0; i < fields.length; i++) {
|
||||||
let [key, value] = fields[i].split('=');
|
let [key, value] = fields[i].split('=');
|
||||||
key = key.replace(/:/g, '_');
|
key = key.replace(/:/g, '_');
|
||||||
|
@ -121,15 +121,15 @@ function getTags(message: string): { [key: string]: string } {
|
||||||
}
|
}
|
||||||
|
|
||||||
function getIntermediatePoints(points: Coordinates[]): Promise<TrackPoint[]> {
|
function getIntermediatePoints(points: Coordinates[]): Promise<TrackPoint[]> {
|
||||||
let route: TrackPoint[] = [];
|
const route: TrackPoint[] = [];
|
||||||
let step = 0.05;
|
const step = 0.05;
|
||||||
|
|
||||||
for (let i = 0; i < points.length - 1; i++) {
|
for (let i = 0; i < points.length - 1; i++) {
|
||||||
// Add intermediate points between each pair of points
|
// Add intermediate points between each pair of points
|
||||||
let dist = distance(points[i], points[i + 1]) / 1000;
|
const dist = distance(points[i], points[i + 1]) / 1000;
|
||||||
for (let d = 0; d < dist; d += step) {
|
for (let d = 0; d < dist; d += step) {
|
||||||
let lat = points[i].lat + (d / dist) * (points[i + 1].lat - points[i].lat);
|
const lat = points[i].lat + (d / dist) * (points[i + 1].lat - points[i].lat);
|
||||||
let lon = points[i].lon + (d / dist) * (points[i + 1].lon - points[i].lon);
|
const lon = points[i].lon + (d / dist) * (points[i + 1].lon - points[i].lon);
|
||||||
route.push(
|
route.push(
|
||||||
new TrackPoint({
|
new TrackPoint({
|
||||||
attributes: {
|
attributes: {
|
||||||
|
|
|
@ -54,7 +54,7 @@ export class RoutingControls {
|
||||||
this.popup = popup;
|
this.popup = popup;
|
||||||
this.popupElement = popupElement;
|
this.popupElement = popupElement;
|
||||||
|
|
||||||
let point = new TrackPoint({
|
const point = new TrackPoint({
|
||||||
attributes: {
|
attributes: {
|
||||||
lat: 0,
|
lat: 0,
|
||||||
lon: 0,
|
lon: 0,
|
||||||
|
@ -68,7 +68,7 @@ export class RoutingControls {
|
||||||
}
|
}
|
||||||
|
|
||||||
addIfNeeded() {
|
addIfNeeded() {
|
||||||
let routing = get(currentTool) === Tool.ROUTING;
|
const routing = get(currentTool) === Tool.ROUTING;
|
||||||
if (!routing) {
|
if (!routing) {
|
||||||
if (this.active) {
|
if (this.active) {
|
||||||
this.remove();
|
this.remove();
|
||||||
|
@ -76,7 +76,7 @@ export class RoutingControls {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let selected = get(selection).hasAnyChildren(new ListFileItem(this.fileId), true, [
|
const selected = get(selection).hasAnyChildren(new ListFileItem(this.fileId), true, [
|
||||||
'waypoints',
|
'waypoints',
|
||||||
]);
|
]);
|
||||||
if (selected) {
|
if (selected) {
|
||||||
|
@ -103,7 +103,7 @@ export class RoutingControls {
|
||||||
|
|
||||||
updateControls() {
|
updateControls() {
|
||||||
// Update the markers when the file changes
|
// Update the markers when the file changes
|
||||||
let file = get(this.file)?.file;
|
const file = get(this.file)?.file;
|
||||||
if (!file) {
|
if (!file) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -115,7 +115,7 @@ export class RoutingControls {
|
||||||
new ListTrackSegmentItem(this.fileId, trackIndex, segmentIndex)
|
new ListTrackSegmentItem(this.fileId, trackIndex, segmentIndex)
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
for (let point of segment.trkpt) {
|
for (const point of segment.trkpt) {
|
||||||
// Update the existing anchors (could be improved by matching the existing anchors with the new ones?)
|
// Update the existing anchors (could be improved by matching the existing anchors with the new ones?)
|
||||||
if (point._data.anchor) {
|
if (point._data.anchor) {
|
||||||
if (anchorIndex < this.anchors.length) {
|
if (anchorIndex < this.anchors.length) {
|
||||||
|
@ -146,7 +146,7 @@ export class RoutingControls {
|
||||||
remove() {
|
remove() {
|
||||||
this.active = false;
|
this.active = false;
|
||||||
|
|
||||||
for (let anchor of this.anchors) {
|
for (const anchor of this.anchors) {
|
||||||
anchor.marker.remove();
|
anchor.marker.remove();
|
||||||
}
|
}
|
||||||
this.map.off('move', this.toggleAnchorsForZoomLevelAndBoundsBinded);
|
this.map.off('move', this.toggleAnchorsForZoomLevelAndBoundsBinded);
|
||||||
|
@ -169,16 +169,16 @@ export class RoutingControls {
|
||||||
trackIndex: number,
|
trackIndex: number,
|
||||||
segmentIndex: number
|
segmentIndex: number
|
||||||
): AnchorWithMarker {
|
): AnchorWithMarker {
|
||||||
let element = document.createElement('div');
|
const element = document.createElement('div');
|
||||||
element.className = `h-5 w-5 xs:h-4 xs:w-4 md:h-3 md:w-3 rounded-full bg-white border-2 border-black cursor-pointer`;
|
element.className = `h-5 w-5 xs:h-4 xs:w-4 md:h-3 md:w-3 rounded-full bg-white border-2 border-black cursor-pointer`;
|
||||||
|
|
||||||
let marker = new mapboxgl.Marker({
|
const marker = new mapboxgl.Marker({
|
||||||
draggable: true,
|
draggable: true,
|
||||||
className: 'z-10',
|
className: 'z-10',
|
||||||
element,
|
element,
|
||||||
}).setLngLat(point.getCoordinates());
|
}).setLngLat(point.getCoordinates());
|
||||||
|
|
||||||
let anchor = {
|
const anchor = {
|
||||||
point,
|
point,
|
||||||
segment,
|
segment,
|
||||||
trackIndex,
|
trackIndex,
|
||||||
|
@ -200,7 +200,7 @@ export class RoutingControls {
|
||||||
element.classList.add('cursor-pointer');
|
element.classList.add('cursor-pointer');
|
||||||
this.moveAnchor(anchor);
|
this.moveAnchor(anchor);
|
||||||
});
|
});
|
||||||
let handleAnchorClick = this.handleClickForAnchor(anchor, marker);
|
const handleAnchorClick = this.handleClickForAnchor(anchor, marker);
|
||||||
marker.getElement().addEventListener('click', handleAnchorClick);
|
marker.getElement().addEventListener('click', handleAnchorClick);
|
||||||
marker.getElement().addEventListener('contextmenu', handleAnchorClick);
|
marker.getElement().addEventListener('contextmenu', handleAnchorClick);
|
||||||
|
|
||||||
|
@ -231,7 +231,7 @@ export class RoutingControls {
|
||||||
if (anchor.point._data.index === 0) {
|
if (anchor.point._data.index === 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
let segment = anchor.segment;
|
const segment = anchor.segment;
|
||||||
if (
|
if (
|
||||||
distance(
|
distance(
|
||||||
segment.trkpt[0].getCoordinates(),
|
segment.trkpt[0].getCoordinates(),
|
||||||
|
@ -246,9 +246,9 @@ export class RoutingControls {
|
||||||
marker.setPopup(this.popup);
|
marker.setPopup(this.popup);
|
||||||
marker.togglePopup();
|
marker.togglePopup();
|
||||||
|
|
||||||
let deleteThisAnchor = this.getDeleteAnchor(anchor);
|
const deleteThisAnchor = this.getDeleteAnchor(anchor);
|
||||||
this.popupElement.addEventListener('delete', deleteThisAnchor); // Register the delete event for this anchor
|
this.popupElement.addEventListener('delete', deleteThisAnchor); // Register the delete event for this anchor
|
||||||
let startLoopAtThisAnchor = this.getStartLoopAtAnchor(anchor);
|
const startLoopAtThisAnchor = this.getStartLoopAtAnchor(anchor);
|
||||||
this.popupElement.addEventListener('change-start', startLoopAtThisAnchor); // Register the start loop event for this anchor
|
this.popupElement.addEventListener('change-start', startLoopAtThisAnchor); // Register the start loop event for this anchor
|
||||||
this.popup.once('close', () => {
|
this.popup.once('close', () => {
|
||||||
this.popupElement.removeEventListener('delete', deleteThisAnchor);
|
this.popupElement.removeEventListener('delete', deleteThisAnchor);
|
||||||
|
@ -261,12 +261,12 @@ export class RoutingControls {
|
||||||
// Show markers only if they are in the current zoom level and bounds
|
// Show markers only if they are in the current zoom level and bounds
|
||||||
this.shownAnchors.splice(0, this.shownAnchors.length);
|
this.shownAnchors.splice(0, this.shownAnchors.length);
|
||||||
|
|
||||||
let center = this.map.getCenter();
|
const center = this.map.getCenter();
|
||||||
let bottomLeft = this.map.unproject([0, this.map.getCanvas().height]);
|
const bottomLeft = this.map.unproject([0, this.map.getCanvas().height]);
|
||||||
let topRight = this.map.unproject([this.map.getCanvas().width, 0]);
|
const topRight = this.map.unproject([this.map.getCanvas().width, 0]);
|
||||||
let diagonal = bottomLeft.distanceTo(topRight);
|
const diagonal = bottomLeft.distanceTo(topRight);
|
||||||
|
|
||||||
let zoom = this.map.getZoom();
|
const zoom = this.map.getZoom();
|
||||||
this.anchors.forEach((anchor) => {
|
this.anchors.forEach((anchor) => {
|
||||||
anchor.inZoom = anchor.point._data.zoom <= zoom;
|
anchor.inZoom = anchor.point._data.zoom <= zoom;
|
||||||
if (anchor.inZoom && center.distanceTo(anchor.marker.getLngLat()) < diagonal) {
|
if (anchor.inZoom && center.distanceTo(anchor.marker.getLngLat()) < diagonal) {
|
||||||
|
@ -334,7 +334,7 @@ export class RoutingControls {
|
||||||
}
|
}
|
||||||
|
|
||||||
temporaryAnchorCloseToOtherAnchor(e: any) {
|
temporaryAnchorCloseToOtherAnchor(e: any) {
|
||||||
for (let anchor of this.shownAnchors) {
|
for (const anchor of this.shownAnchors) {
|
||||||
if (e.point.dist(this.map.project(anchor.marker.getLngLat())) < 10) {
|
if (e.point.dist(this.map.project(anchor.marker.getLngLat())) < 10) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -344,7 +344,7 @@ export class RoutingControls {
|
||||||
|
|
||||||
async moveAnchor(anchorWithMarker: AnchorWithMarker) {
|
async moveAnchor(anchorWithMarker: AnchorWithMarker) {
|
||||||
// Move the anchor and update the route from and to the neighbouring anchors
|
// Move the anchor and update the route from and to the neighbouring anchors
|
||||||
let coordinates = {
|
const coordinates = {
|
||||||
lat: anchorWithMarker.marker.getLngLat().lat,
|
lat: anchorWithMarker.marker.getLngLat().lat,
|
||||||
lon: anchorWithMarker.marker.getLngLat().lng,
|
lon: anchorWithMarker.marker.getLngLat().lng,
|
||||||
};
|
};
|
||||||
|
@ -356,10 +356,10 @@ export class RoutingControls {
|
||||||
anchor = this.getPermanentAnchor();
|
anchor = this.getPermanentAnchor();
|
||||||
}
|
}
|
||||||
|
|
||||||
let [previousAnchor, nextAnchor] = this.getNeighbouringAnchors(anchor);
|
const [previousAnchor, nextAnchor] = this.getNeighbouringAnchors(anchor);
|
||||||
|
|
||||||
let anchors = [];
|
const anchors = [];
|
||||||
let targetCoordinates = [];
|
const targetCoordinates = [];
|
||||||
|
|
||||||
if (previousAnchor !== null) {
|
if (previousAnchor !== null) {
|
||||||
anchors.push(previousAnchor);
|
anchors.push(previousAnchor);
|
||||||
|
@ -374,7 +374,7 @@ export class RoutingControls {
|
||||||
targetCoordinates.push(nextAnchor.point.getCoordinates());
|
targetCoordinates.push(nextAnchor.point.getCoordinates());
|
||||||
}
|
}
|
||||||
|
|
||||||
let success = await this.routeBetweenAnchors(anchors, targetCoordinates);
|
const success = await this.routeBetweenAnchors(anchors, targetCoordinates);
|
||||||
|
|
||||||
if (!success) {
|
if (!success) {
|
||||||
// Route failed, revert the anchor to the previous position
|
// Route failed, revert the anchor to the previous position
|
||||||
|
@ -383,7 +383,7 @@ export class RoutingControls {
|
||||||
}
|
}
|
||||||
|
|
||||||
getPermanentAnchor(): Anchor {
|
getPermanentAnchor(): Anchor {
|
||||||
let file = get(this.file)?.file;
|
const file = get(this.file)?.file;
|
||||||
|
|
||||||
// Find the point closest to the temporary anchor
|
// Find the point closest to the temporary anchor
|
||||||
let minDetails: any = { distance: Number.MAX_VALUE };
|
let minDetails: any = { distance: Number.MAX_VALUE };
|
||||||
|
@ -394,8 +394,8 @@ export class RoutingControls {
|
||||||
new ListTrackSegmentItem(this.fileId, trackIndex, segmentIndex)
|
new ListTrackSegmentItem(this.fileId, trackIndex, segmentIndex)
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
let details: any = {};
|
const details: any = {};
|
||||||
let closest = getClosestLinePoint(
|
const closest = getClosestLinePoint(
|
||||||
segment.trkpt,
|
segment.trkpt,
|
||||||
this.temporaryAnchor.point,
|
this.temporaryAnchor.point,
|
||||||
details
|
details
|
||||||
|
@ -425,7 +425,7 @@ export class RoutingControls {
|
||||||
}
|
}
|
||||||
|
|
||||||
turnIntoPermanentAnchor() {
|
turnIntoPermanentAnchor() {
|
||||||
let file = get(this.file)?.file;
|
const file = get(this.file)?.file;
|
||||||
|
|
||||||
// Find the point closest to the temporary anchor
|
// Find the point closest to the temporary anchor
|
||||||
let minDetails: any = { distance: Number.MAX_VALUE };
|
let minDetails: any = { distance: Number.MAX_VALUE };
|
||||||
|
@ -442,22 +442,22 @@ export class RoutingControls {
|
||||||
new ListTrackSegmentItem(this.fileId, trackIndex, segmentIndex)
|
new ListTrackSegmentItem(this.fileId, trackIndex, segmentIndex)
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
let details: any = {};
|
const details: any = {};
|
||||||
getClosestLinePoint(segment.trkpt, this.temporaryAnchor.point, details);
|
getClosestLinePoint(segment.trkpt, this.temporaryAnchor.point, details);
|
||||||
if (details.distance < minDetails.distance) {
|
if (details.distance < minDetails.distance) {
|
||||||
minDetails = details;
|
minDetails = details;
|
||||||
let before = details.before ? details.index : details.index - 1;
|
const before = details.before ? details.index : details.index - 1;
|
||||||
|
|
||||||
let projectedPt = projectedPoint(
|
const projectedPt = projectedPoint(
|
||||||
segment.trkpt[before],
|
segment.trkpt[before],
|
||||||
segment.trkpt[before + 1],
|
segment.trkpt[before + 1],
|
||||||
this.temporaryAnchor.point
|
this.temporaryAnchor.point
|
||||||
);
|
);
|
||||||
let ratio =
|
const ratio =
|
||||||
distance(segment.trkpt[before], projectedPt) /
|
distance(segment.trkpt[before], projectedPt) /
|
||||||
distance(segment.trkpt[before], segment.trkpt[before + 1]);
|
distance(segment.trkpt[before], segment.trkpt[before + 1]);
|
||||||
|
|
||||||
let point = segment.trkpt[before].clone();
|
const point = segment.trkpt[before].clone();
|
||||||
point.setCoordinates(projectedPt);
|
point.setCoordinates(projectedPt);
|
||||||
point.ele =
|
point.ele =
|
||||||
(1 - ratio) * (segment.trkpt[before].ele ?? 0) +
|
(1 - ratio) * (segment.trkpt[before].ele ?? 0) +
|
||||||
|
@ -505,7 +505,7 @@ export class RoutingControls {
|
||||||
// Remove the anchor and route between the neighbouring anchors if they exist
|
// Remove the anchor and route between the neighbouring anchors if they exist
|
||||||
this.popup.remove();
|
this.popup.remove();
|
||||||
|
|
||||||
let [previousAnchor, nextAnchor] = this.getNeighbouringAnchors(anchor);
|
const [previousAnchor, nextAnchor] = this.getNeighbouringAnchors(anchor);
|
||||||
|
|
||||||
if (previousAnchor === null && nextAnchor === null) {
|
if (previousAnchor === null && nextAnchor === null) {
|
||||||
// Only one point, remove it
|
// Only one point, remove it
|
||||||
|
@ -526,7 +526,7 @@ export class RoutingControls {
|
||||||
} else if (nextAnchor === null) {
|
} else if (nextAnchor === null) {
|
||||||
// Last point, remove trackpoints from previousAnchor
|
// Last point, remove trackpoints from previousAnchor
|
||||||
dbUtils.applyToFile(this.fileId, (file) => {
|
dbUtils.applyToFile(this.fileId, (file) => {
|
||||||
let segment = file.getSegment(anchor.trackIndex, anchor.segmentIndex);
|
const segment = file.getSegment(anchor.trackIndex, anchor.segmentIndex);
|
||||||
file.replaceTrackPoints(
|
file.replaceTrackPoints(
|
||||||
anchor.trackIndex,
|
anchor.trackIndex,
|
||||||
anchor.segmentIndex,
|
anchor.segmentIndex,
|
||||||
|
@ -551,16 +551,16 @@ export class RoutingControls {
|
||||||
startLoopAtAnchor(anchor: Anchor) {
|
startLoopAtAnchor(anchor: Anchor) {
|
||||||
this.popup.remove();
|
this.popup.remove();
|
||||||
|
|
||||||
let fileWithStats = get(this.file);
|
const fileWithStats = get(this.file);
|
||||||
if (!fileWithStats) {
|
if (!fileWithStats) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let speed = fileWithStats.statistics.getStatisticsFor(
|
const speed = fileWithStats.statistics.getStatisticsFor(
|
||||||
new ListTrackSegmentItem(this.fileId, anchor.trackIndex, anchor.segmentIndex)
|
new ListTrackSegmentItem(this.fileId, anchor.trackIndex, anchor.segmentIndex)
|
||||||
).global.speed.moving;
|
).global.speed.moving;
|
||||||
|
|
||||||
let segment = anchor.segment;
|
const segment = anchor.segment;
|
||||||
dbUtils.applyToFile(this.fileId, (file) => {
|
dbUtils.applyToFile(this.fileId, (file) => {
|
||||||
file.replaceTrackPoints(
|
file.replaceTrackPoints(
|
||||||
anchor.trackIndex,
|
anchor.trackIndex,
|
||||||
|
@ -593,15 +593,15 @@ export class RoutingControls {
|
||||||
|
|
||||||
async appendAnchorWithCoordinates(coordinates: Coordinates) {
|
async appendAnchorWithCoordinates(coordinates: Coordinates) {
|
||||||
// Add a new anchor to the end of the last segment
|
// Add a new anchor to the end of the last segment
|
||||||
let selected = getOrderedSelection();
|
const selected = getOrderedSelection();
|
||||||
if (selected.length === 0 || selected[selected.length - 1].getFileId() !== this.fileId) {
|
if (selected.length === 0 || selected[selected.length - 1].getFileId() !== this.fileId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let item = selected[selected.length - 1];
|
const item = selected[selected.length - 1];
|
||||||
|
|
||||||
let lastAnchor = this.anchors[this.anchors.length - 1];
|
const lastAnchor = this.anchors[this.anchors.length - 1];
|
||||||
|
|
||||||
let newPoint = new TrackPoint({
|
const newPoint = new TrackPoint({
|
||||||
attributes: coordinates,
|
attributes: coordinates,
|
||||||
});
|
});
|
||||||
newPoint._data.anchor = true;
|
newPoint._data.anchor = true;
|
||||||
|
@ -621,11 +621,11 @@ export class RoutingControls {
|
||||||
segmentIndex = item.getSegmentIndex();
|
segmentIndex = item.getSegmentIndex();
|
||||||
}
|
}
|
||||||
if (file.trk.length === 0) {
|
if (file.trk.length === 0) {
|
||||||
let track = new Track();
|
const track = new Track();
|
||||||
track.replaceTrackPoints(0, 0, 0, [newPoint]);
|
track.replaceTrackPoints(0, 0, 0, [newPoint]);
|
||||||
file.replaceTracks(0, 0, [track]);
|
file.replaceTracks(0, 0, [track]);
|
||||||
} else if (file.trk[trackIndex].trkseg.length === 0) {
|
} else if (file.trk[trackIndex].trkseg.length === 0) {
|
||||||
let segment = new TrackSegment();
|
const segment = new TrackSegment();
|
||||||
segment.replaceTrackPoints(0, 0, [newPoint]);
|
segment.replaceTrackPoints(0, 0, [newPoint]);
|
||||||
file.replaceTrackSegments(trackIndex, 0, 0, [segment]);
|
file.replaceTrackSegments(trackIndex, 0, 0, [segment]);
|
||||||
} else {
|
} else {
|
||||||
|
@ -636,7 +636,7 @@ export class RoutingControls {
|
||||||
}
|
}
|
||||||
|
|
||||||
newPoint._data.index = lastAnchor.segment.trkpt.length - 1; // Do as if the point was the last point in the segment
|
newPoint._data.index = lastAnchor.segment.trkpt.length - 1; // Do as if the point was the last point in the segment
|
||||||
let newAnchor = {
|
const newAnchor = {
|
||||||
point: newPoint,
|
point: newPoint,
|
||||||
segment: lastAnchor.segment,
|
segment: lastAnchor.segment,
|
||||||
trackIndex: lastAnchor.trackIndex,
|
trackIndex: lastAnchor.trackIndex,
|
||||||
|
@ -680,9 +680,9 @@ export class RoutingControls {
|
||||||
anchors: Anchor[],
|
anchors: Anchor[],
|
||||||
targetCoordinates: Coordinates[]
|
targetCoordinates: Coordinates[]
|
||||||
): Promise<boolean> {
|
): Promise<boolean> {
|
||||||
let segment = anchors[0].segment;
|
const segment = anchors[0].segment;
|
||||||
|
|
||||||
let fileWithStats = get(this.file);
|
const fileWithStats = get(this.file);
|
||||||
if (!fileWithStats) {
|
if (!fileWithStats) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -753,7 +753,7 @@ export class RoutingControls {
|
||||||
anchor.point._data.zoom = 0; // Make these anchors permanent
|
anchor.point._data.zoom = 0; // Make these anchors permanent
|
||||||
});
|
});
|
||||||
|
|
||||||
let stats = fileWithStats.statistics.getStatisticsFor(
|
const stats = fileWithStats.statistics.getStatisticsFor(
|
||||||
new ListTrackSegmentItem(this.fileId, anchors[0].trackIndex, anchors[0].segmentIndex)
|
new ListTrackSegmentItem(this.fileId, anchors[0].trackIndex, anchors[0].segmentIndex)
|
||||||
);
|
);
|
||||||
let speed: number | undefined = undefined;
|
let speed: number | undefined = undefined;
|
||||||
|
@ -765,14 +765,14 @@ export class RoutingControls {
|
||||||
replacingDistance +=
|
replacingDistance +=
|
||||||
distance(response[i - 1].getCoordinates(), response[i].getCoordinates()) / 1000;
|
distance(response[i - 1].getCoordinates(), response[i].getCoordinates()) / 1000;
|
||||||
}
|
}
|
||||||
let replacedDistance =
|
const replacedDistance =
|
||||||
stats.local.distance.moving[anchors[anchors.length - 1].point._data.index] -
|
stats.local.distance.moving[anchors[anchors.length - 1].point._data.index] -
|
||||||
stats.local.distance.moving[anchors[0].point._data.index];
|
stats.local.distance.moving[anchors[0].point._data.index];
|
||||||
|
|
||||||
let newDistance = stats.global.distance.moving + replacingDistance - replacedDistance;
|
const newDistance = stats.global.distance.moving + replacingDistance - replacedDistance;
|
||||||
let newTime = (newDistance / stats.global.speed.moving) * 3600;
|
const newTime = (newDistance / stats.global.speed.moving) * 3600;
|
||||||
|
|
||||||
let remainingTime =
|
const remainingTime =
|
||||||
stats.global.time.moving -
|
stats.global.time.moving -
|
||||||
(stats.local.time.moving[anchors[anchors.length - 1].point._data.index] -
|
(stats.local.time.moving[anchors[anchors.length - 1].point._data.index] -
|
||||||
stats.local.time.moving[anchors[0].point._data.index]);
|
stats.local.time.moving[anchors[0].point._data.index]);
|
||||||
|
@ -789,7 +789,7 @@ export class RoutingControls {
|
||||||
|
|
||||||
if (startTime === undefined) {
|
if (startTime === undefined) {
|
||||||
// Replacing the first point
|
// Replacing the first point
|
||||||
let endIndex = anchors[anchors.length - 1].point._data.index;
|
const endIndex = anchors[anchors.length - 1].point._data.index;
|
||||||
startTime = new Date(
|
startTime = new Date(
|
||||||
(segment.trkpt[endIndex].time?.getTime() ?? 0) -
|
(segment.trkpt[endIndex].time?.getTime() ?? 0) -
|
||||||
(replacingTime +
|
(replacingTime +
|
||||||
|
|
|
@ -14,9 +14,9 @@ export function getZoomLevelForDistance(latitude: number, distance?: number): nu
|
||||||
}
|
}
|
||||||
|
|
||||||
export function updateAnchorPoints(file: GPXFile) {
|
export function updateAnchorPoints(file: GPXFile) {
|
||||||
let segments = file.getSegments();
|
const segments = file.getSegments();
|
||||||
|
|
||||||
for (let segment of segments) {
|
for (const segment of segments) {
|
||||||
if (!segment._data.anchors) {
|
if (!segment._data.anchors) {
|
||||||
// New segment, compute anchor points for it
|
// New segment, compute anchor points for it
|
||||||
computeAnchorPoints(segment);
|
computeAnchorPoints(segment);
|
||||||
|
@ -34,10 +34,10 @@ export function updateAnchorPoints(file: GPXFile) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function computeAnchorPoints(segment: TrackSegment) {
|
function computeAnchorPoints(segment: TrackSegment) {
|
||||||
let points = segment.trkpt;
|
const points = segment.trkpt;
|
||||||
let anchors = ramerDouglasPeucker(points, 1);
|
const anchors = ramerDouglasPeucker(points, 1);
|
||||||
anchors.forEach((anchor) => {
|
anchors.forEach((anchor) => {
|
||||||
let point = anchor.point;
|
const point = anchor.point;
|
||||||
point._data.anchor = true;
|
point._data.anchor = true;
|
||||||
point._data.zoom = getZoomLevelForDistance(point.getLatitude(), anchor.distance);
|
point._data.zoom = getZoomLevelForDistance(point.getLatitude(), anchor.distance);
|
||||||
});
|
});
|
||||||
|
|
|
@ -30,7 +30,7 @@ export class SplitControls {
|
||||||
}
|
}
|
||||||
|
|
||||||
addIfNeeded() {
|
addIfNeeded() {
|
||||||
let scissors = get(currentTool) === Tool.SCISSORS;
|
const scissors = get(currentTool) === Tool.SCISSORS;
|
||||||
if (!scissors) {
|
if (!scissors) {
|
||||||
if (this.active) {
|
if (this.active) {
|
||||||
this.remove();
|
this.remove();
|
||||||
|
@ -56,7 +56,7 @@ export class SplitControls {
|
||||||
// Update the markers when the files change
|
// Update the markers when the files change
|
||||||
let controlIndex = 0;
|
let controlIndex = 0;
|
||||||
applyToOrderedSelectedItemsFromFile((fileId, level, items) => {
|
applyToOrderedSelectedItemsFromFile((fileId, level, items) => {
|
||||||
let file = getFile(fileId);
|
const file = getFile(fileId);
|
||||||
|
|
||||||
if (file) {
|
if (file) {
|
||||||
file.forEachSegment((segment, trackIndex, segmentIndex) => {
|
file.forEachSegment((segment, trackIndex, segmentIndex) => {
|
||||||
|
@ -65,7 +65,7 @@ export class SplitControls {
|
||||||
new ListTrackSegmentItem(fileId, trackIndex, segmentIndex)
|
new ListTrackSegmentItem(fileId, trackIndex, segmentIndex)
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
for (let point of segment.trkpt.slice(1, -1)) {
|
for (const point of segment.trkpt.slice(1, -1)) {
|
||||||
// Update the existing controls (could be improved by matching the existing controls with the new ones?)
|
// Update the existing controls (could be improved by matching the existing controls with the new ones?)
|
||||||
if (point._data.anchor) {
|
if (point._data.anchor) {
|
||||||
if (controlIndex < this.controls.length) {
|
if (controlIndex < this.controls.length) {
|
||||||
|
@ -107,7 +107,7 @@ export class SplitControls {
|
||||||
remove() {
|
remove() {
|
||||||
this.active = false;
|
this.active = false;
|
||||||
|
|
||||||
for (let control of this.controls) {
|
for (const control of this.controls) {
|
||||||
control.marker.remove();
|
control.marker.remove();
|
||||||
}
|
}
|
||||||
this.map.off('zoom', this.toggleControlsForZoomLevelAndBoundsBinded);
|
this.map.off('zoom', this.toggleControlsForZoomLevelAndBoundsBinded);
|
||||||
|
@ -118,11 +118,11 @@ export class SplitControls {
|
||||||
// Show markers only if they are in the current zoom level and bounds
|
// Show markers only if they are in the current zoom level and bounds
|
||||||
this.shownControls.splice(0, this.shownControls.length);
|
this.shownControls.splice(0, this.shownControls.length);
|
||||||
|
|
||||||
let southWest = this.map.unproject([0, this.map.getCanvas().height]);
|
const southWest = this.map.unproject([0, this.map.getCanvas().height]);
|
||||||
let northEast = this.map.unproject([this.map.getCanvas().width, 0]);
|
const northEast = this.map.unproject([this.map.getCanvas().width, 0]);
|
||||||
let bounds = new mapboxgl.LngLatBounds(southWest, northEast);
|
const bounds = new mapboxgl.LngLatBounds(southWest, northEast);
|
||||||
|
|
||||||
let zoom = this.map.getZoom();
|
const zoom = this.map.getZoom();
|
||||||
this.controls.forEach((control) => {
|
this.controls.forEach((control) => {
|
||||||
control.inZoom = control.point._data.zoom <= zoom;
|
control.inZoom = control.point._data.zoom <= zoom;
|
||||||
if (control.inZoom && bounds.contains(control.marker.getLngLat())) {
|
if (control.inZoom && bounds.contains(control.marker.getLngLat())) {
|
||||||
|
@ -141,19 +141,19 @@ export class SplitControls {
|
||||||
trackIndex: number,
|
trackIndex: number,
|
||||||
segmentIndex: number
|
segmentIndex: number
|
||||||
): ControlWithMarker {
|
): ControlWithMarker {
|
||||||
let element = document.createElement('div');
|
const element = document.createElement('div');
|
||||||
element.className = `h-6 w-6 p-0.5 rounded-full bg-white border-2 border-black cursor-pointer`;
|
element.className = `h-6 w-6 p-0.5 rounded-full bg-white border-2 border-black cursor-pointer`;
|
||||||
element.innerHTML = Scissors.replace('width="24"', '')
|
element.innerHTML = Scissors.replace('width="24"', '')
|
||||||
.replace('height="24"', '')
|
.replace('height="24"', '')
|
||||||
.replace('stroke="currentColor"', 'stroke="black"');
|
.replace('stroke="currentColor"', 'stroke="black"');
|
||||||
|
|
||||||
let marker = new mapboxgl.Marker({
|
const marker = new mapboxgl.Marker({
|
||||||
draggable: true,
|
draggable: true,
|
||||||
className: 'z-10',
|
className: 'z-10',
|
||||||
element,
|
element,
|
||||||
}).setLngLat(point.getCoordinates());
|
}).setLngLat(point.getCoordinates());
|
||||||
|
|
||||||
let control = {
|
const control = {
|
||||||
point,
|
point,
|
||||||
segment,
|
segment,
|
||||||
fileId,
|
fileId,
|
||||||
|
|
|
@ -100,7 +100,7 @@ export function bidirectionalDexieStore<K, V>(
|
||||||
initialize: boolean = true
|
initialize: boolean = true
|
||||||
): Writable<V | undefined> {
|
): Writable<V | undefined> {
|
||||||
let first = true;
|
let first = true;
|
||||||
let store = writable<V | undefined>(initialize ? initial : undefined);
|
const store = writable<V | undefined>(initialize ? initial : undefined);
|
||||||
liveQuery(() => table.get(key)).subscribe((value) => {
|
liveQuery(() => table.get(key)).subscribe((value) => {
|
||||||
if (value === undefined) {
|
if (value === undefined) {
|
||||||
if (first) {
|
if (first) {
|
||||||
|
@ -123,7 +123,7 @@ export function bidirectionalDexieStore<K, V>(
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
update: (callback: (value: any) => any) => {
|
update: (callback: (value: any) => any) => {
|
||||||
let newValue = callback(get(store));
|
const newValue = callback(get(store));
|
||||||
if (typeof newValue === 'object' || newValue !== get(store)) {
|
if (typeof newValue === 'object' || newValue !== get(store)) {
|
||||||
table.put(newValue, key);
|
table.put(newValue, key);
|
||||||
}
|
}
|
||||||
|
@ -179,7 +179,7 @@ export const settings = {
|
||||||
|
|
||||||
// Wrap Dexie live queries in a Svelte store to avoid triggering the query for every subscriber
|
// Wrap Dexie live queries in a Svelte store to avoid triggering the query for every subscriber
|
||||||
function dexieStore<T>(querier: () => T | Promise<T>, initial?: T): Readable<T> {
|
function dexieStore<T>(querier: () => T | Promise<T>, initial?: T): Readable<T> {
|
||||||
let store = writable<T>(initial);
|
const store = writable<T>(initial);
|
||||||
liveQuery(querier).subscribe((value) => {
|
liveQuery(querier).subscribe((value) => {
|
||||||
if (value !== undefined) {
|
if (value !== undefined) {
|
||||||
store.set(value);
|
store.set(value);
|
||||||
|
@ -211,8 +211,8 @@ export class GPXStatisticsTree {
|
||||||
}
|
}
|
||||||
|
|
||||||
getStatisticsFor(item: ListItem): GPXStatistics {
|
getStatisticsFor(item: ListItem): GPXStatistics {
|
||||||
let statistics = new GPXStatistics();
|
const statistics = new GPXStatistics();
|
||||||
let id = item.getIdAtLevel(this.level);
|
const id = item.getIdAtLevel(this.level);
|
||||||
if (id === undefined || id === 'waypoints') {
|
if (id === undefined || id === 'waypoints') {
|
||||||
Object.keys(this.statistics).forEach((key) => {
|
Object.keys(this.statistics).forEach((key) => {
|
||||||
if (this.statistics[key] instanceof GPXStatistics) {
|
if (this.statistics[key] instanceof GPXStatistics) {
|
||||||
|
@ -222,7 +222,7 @@ export class GPXStatisticsTree {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
let child = this.statistics[id];
|
const child = this.statistics[id];
|
||||||
if (child instanceof GPXStatistics) {
|
if (child instanceof GPXStatistics) {
|
||||||
statistics.mergeWith(child);
|
statistics.mergeWith(child);
|
||||||
} else if (child !== undefined) {
|
} else if (child !== undefined) {
|
||||||
|
@ -236,13 +236,13 @@ export type GPXFileWithStatistics = { file: GPXFile; statistics: GPXStatisticsTr
|
||||||
|
|
||||||
// Wrap Dexie live queries in a Svelte store to avoid triggering the query for every subscriber, also takes care of the conversion to a GPXFile object
|
// Wrap Dexie live queries in a Svelte store to avoid triggering the query for every subscriber, also takes care of the conversion to a GPXFile object
|
||||||
function dexieGPXFileStore(id: string): Readable<GPXFileWithStatistics> & { destroy: () => void } {
|
function dexieGPXFileStore(id: string): Readable<GPXFileWithStatistics> & { destroy: () => void } {
|
||||||
let store = writable<GPXFileWithStatistics>(undefined);
|
const store = writable<GPXFileWithStatistics>(undefined);
|
||||||
let query = liveQuery(() => db.files.get(id)).subscribe((value) => {
|
const query = liveQuery(() => db.files.get(id)).subscribe((value) => {
|
||||||
if (value !== undefined) {
|
if (value !== undefined) {
|
||||||
let gpx = new GPXFile(value);
|
const gpx = new GPXFile(value);
|
||||||
updateAnchorPoints(gpx);
|
updateAnchorPoints(gpx);
|
||||||
|
|
||||||
let statistics = new GPXStatisticsTree(gpx);
|
const statistics = new GPXStatisticsTree(gpx);
|
||||||
if (!fileState.has(id)) {
|
if (!fileState.has(id)) {
|
||||||
// Update the map bounds for new files
|
// Update the map bounds for new files
|
||||||
updateTargetMapBounds(
|
updateTargetMapBounds(
|
||||||
|
@ -272,27 +272,27 @@ function dexieGPXFileStore(id: string): Readable<GPXFileWithStatistics> & { dest
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateSelection(updatedFiles: GPXFile[], deletedFileIds: string[]) {
|
function updateSelection(updatedFiles: GPXFile[], deletedFileIds: string[]) {
|
||||||
let removedItems: ListItem[] = [];
|
const removedItems: ListItem[] = [];
|
||||||
|
|
||||||
applyToOrderedItemsFromFile(get(selection).getSelected(), (fileId, level, items) => {
|
applyToOrderedItemsFromFile(get(selection).getSelected(), (fileId, level, items) => {
|
||||||
let file = updatedFiles.find((file) => file._data.id === fileId);
|
const file = updatedFiles.find((file) => file._data.id === fileId);
|
||||||
if (file) {
|
if (file) {
|
||||||
items.forEach((item) => {
|
items.forEach((item) => {
|
||||||
if (item instanceof ListTrackItem) {
|
if (item instanceof ListTrackItem) {
|
||||||
let newTrackIndex = file.trk.findIndex(
|
const newTrackIndex = file.trk.findIndex(
|
||||||
(track) => track._data.trackIndex === item.getTrackIndex()
|
(track) => track._data.trackIndex === item.getTrackIndex()
|
||||||
);
|
);
|
||||||
if (newTrackIndex === -1) {
|
if (newTrackIndex === -1) {
|
||||||
removedItems.push(item);
|
removedItems.push(item);
|
||||||
}
|
}
|
||||||
} else if (item instanceof ListTrackSegmentItem) {
|
} else if (item instanceof ListTrackSegmentItem) {
|
||||||
let newTrackIndex = file.trk.findIndex(
|
const newTrackIndex = file.trk.findIndex(
|
||||||
(track) => track._data.trackIndex === item.getTrackIndex()
|
(track) => track._data.trackIndex === item.getTrackIndex()
|
||||||
);
|
);
|
||||||
if (newTrackIndex === -1) {
|
if (newTrackIndex === -1) {
|
||||||
removedItems.push(item);
|
removedItems.push(item);
|
||||||
} else {
|
} else {
|
||||||
let newSegmentIndex = file.trk[newTrackIndex].trkseg.findIndex(
|
const newSegmentIndex = file.trk[newTrackIndex].trkseg.findIndex(
|
||||||
(segment) => segment._data.segmentIndex === item.getSegmentIndex()
|
(segment) => segment._data.segmentIndex === item.getSegmentIndex()
|
||||||
);
|
);
|
||||||
if (newSegmentIndex === -1) {
|
if (newSegmentIndex === -1) {
|
||||||
|
@ -300,7 +300,7 @@ function updateSelection(updatedFiles: GPXFile[], deletedFileIds: string[]) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (item instanceof ListWaypointItem) {
|
} else if (item instanceof ListWaypointItem) {
|
||||||
let newWaypointIndex = file.wpt.findIndex(
|
const newWaypointIndex = file.wpt.findIndex(
|
||||||
(wpt) => wpt._data.index === item.getWaypointIndex()
|
(wpt) => wpt._data.index === item.getWaypointIndex()
|
||||||
);
|
);
|
||||||
if (newWaypointIndex === -1) {
|
if (newWaypointIndex === -1) {
|
||||||
|
@ -331,7 +331,7 @@ function updateSelection(updatedFiles: GPXFile[], deletedFileIds: string[]) {
|
||||||
|
|
||||||
// Commit the changes to the file state to the database
|
// Commit the changes to the file state to the database
|
||||||
function commitFileStateChange(newFileState: ReadonlyMap<string, GPXFile>, patch: Patch[]) {
|
function commitFileStateChange(newFileState: ReadonlyMap<string, GPXFile>, patch: Patch[]) {
|
||||||
let changedFileIds = getChangedFileIds(patch);
|
const changedFileIds = getChangedFileIds(patch);
|
||||||
let updatedFileIds: string[] = [],
|
let updatedFileIds: string[] = [],
|
||||||
deletedFileIds: string[] = [];
|
deletedFileIds: string[] = [];
|
||||||
|
|
||||||
|
@ -343,7 +343,7 @@ function commitFileStateChange(newFileState: ReadonlyMap<string, GPXFile>, patch
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let updatedFiles = updatedFileIds
|
const updatedFiles = updatedFileIds
|
||||||
.map((id) => newFileState.get(id))
|
.map((id) => newFileState.get(id))
|
||||||
.filter((file) => file !== undefined) as GPXFile[];
|
.filter((file) => file !== undefined) as GPXFile[];
|
||||||
updatedFileIds = updatedFiles.map((file) => file._data.id);
|
updatedFileIds = updatedFiles.map((file) => file._data.id);
|
||||||
|
@ -378,11 +378,11 @@ export function observeFilesFromDatabase(fitBounds: boolean) {
|
||||||
initialize = false;
|
initialize = false;
|
||||||
}
|
}
|
||||||
// Find new files to observe
|
// Find new files to observe
|
||||||
let newFiles = dbFileIds
|
const newFiles = dbFileIds
|
||||||
.filter((id) => !get(fileObservers).has(id))
|
.filter((id) => !get(fileObservers).has(id))
|
||||||
.sort((a, b) => parseInt(a.split('-')[1]) - parseInt(b.split('-')[1]));
|
.sort((a, b) => parseInt(a.split('-')[1]) - parseInt(b.split('-')[1]));
|
||||||
// Find deleted files to stop observing
|
// Find deleted files to stop observing
|
||||||
let deletedFiles = Array.from(get(fileObservers).keys()).filter(
|
const deletedFiles = Array.from(get(fileObservers).keys()).filter(
|
||||||
(id) => !dbFileIds.find((fileId) => fileId === id)
|
(id) => !dbFileIds.find((fileId) => fileId === id)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -405,7 +405,7 @@ export function observeFilesFromDatabase(fitBounds: boolean) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
deletedFiles.forEach((fileId) => {
|
deletedFiles.forEach((fileId) => {
|
||||||
let index = order.indexOf(fileId);
|
const index = order.indexOf(fileId);
|
||||||
if (index !== -1) {
|
if (index !== -1) {
|
||||||
order.splice(index, 1);
|
order.splice(index, 1);
|
||||||
}
|
}
|
||||||
|
@ -417,12 +417,12 @@ export function observeFilesFromDatabase(fitBounds: boolean) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getFile(fileId: string): GPXFile | undefined {
|
export function getFile(fileId: string): GPXFile | undefined {
|
||||||
let fileStore = get(fileObservers).get(fileId);
|
const fileStore = get(fileObservers).get(fileId);
|
||||||
return fileStore ? get(fileStore)?.file : undefined;
|
return fileStore ? get(fileStore)?.file : undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getStatistics(fileId: string): GPXStatisticsTree | undefined {
|
export function getStatistics(fileId: string): GPXStatisticsTree | undefined {
|
||||||
let fileStore = get(fileObservers).get(fileId);
|
const fileStore = get(fileObservers).get(fileId);
|
||||||
return fileStore ? get(fileStore)?.statistics : undefined;
|
return fileStore ? get(fileStore)?.statistics : undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -463,7 +463,7 @@ function applyGlobal(callback: (files: Map<string, GPXFile>) => void) {
|
||||||
function applyToFiles(fileIds: string[], callback: (file: WritableDraft<GPXFile>) => void) {
|
function applyToFiles(fileIds: string[], callback: (file: WritableDraft<GPXFile>) => void) {
|
||||||
const [newFileState, patch, inversePatch] = produceWithPatches(fileState, (draft) => {
|
const [newFileState, patch, inversePatch] = produceWithPatches(fileState, (draft) => {
|
||||||
fileIds.forEach((fileId) => {
|
fileIds.forEach((fileId) => {
|
||||||
let file = draft.get(fileId);
|
const file = draft.get(fileId);
|
||||||
if (file) {
|
if (file) {
|
||||||
callback(file);
|
callback(file);
|
||||||
}
|
}
|
||||||
|
@ -484,7 +484,7 @@ function applyEachToFilesAndGlobal(
|
||||||
) {
|
) {
|
||||||
const [newFileState, patch, inversePatch] = produceWithPatches(fileState, (draft) => {
|
const [newFileState, patch, inversePatch] = produceWithPatches(fileState, (draft) => {
|
||||||
fileIds.forEach((fileId, index) => {
|
fileIds.forEach((fileId, index) => {
|
||||||
let file = draft.get(fileId);
|
const file = draft.get(fileId);
|
||||||
if (file) {
|
if (file) {
|
||||||
callbacks[index](file, context);
|
callbacks[index](file, context);
|
||||||
}
|
}
|
||||||
|
@ -502,7 +502,7 @@ const MAX_PATCHES = 100;
|
||||||
async function storePatches(patch: Patch[], inversePatch: Patch[]) {
|
async function storePatches(patch: Patch[], inversePatch: Patch[]) {
|
||||||
if (get(patchIndex) !== undefined) {
|
if (get(patchIndex) !== undefined) {
|
||||||
db.patches.where(':id').above(get(patchIndex)).delete(); // Delete all patches after the current patch to avoid redoing them
|
db.patches.where(':id').above(get(patchIndex)).delete(); // Delete all patches after the current patch to avoid redoing them
|
||||||
let minmax = get(patchMinMaxIndex);
|
const minmax = get(patchMinMaxIndex);
|
||||||
if (minmax.max - minmax.min + 1 > MAX_PATCHES) {
|
if (minmax.max - minmax.min + 1 > MAX_PATCHES) {
|
||||||
db.patches
|
db.patches
|
||||||
.where(':id')
|
.where(':id')
|
||||||
|
@ -511,7 +511,7 @@ async function storePatches(patch: Patch[], inversePatch: Patch[]) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
db.transaction('rw', db.patches, db.settings, async () => {
|
db.transaction('rw', db.patches, db.settings, async () => {
|
||||||
let index = get(patchIndex) + 1;
|
const index = get(patchIndex) + 1;
|
||||||
await db.patches.put(
|
await db.patches.put(
|
||||||
{
|
{
|
||||||
patch,
|
patch,
|
||||||
|
@ -526,14 +526,14 @@ async function storePatches(patch: Patch[], inversePatch: Patch[]) {
|
||||||
|
|
||||||
// Apply a patch to the file state
|
// Apply a patch to the file state
|
||||||
function applyPatch(patch: Patch[]) {
|
function applyPatch(patch: Patch[]) {
|
||||||
let newFileState = applyPatches(fileState, patch);
|
const newFileState = applyPatches(fileState, patch);
|
||||||
return commitFileStateChange(newFileState, patch);
|
return commitFileStateChange(newFileState, patch);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the file ids of the files that have changed in the patch
|
// Get the file ids of the files that have changed in the patch
|
||||||
function getChangedFileIds(patch: Patch[]): string[] {
|
function getChangedFileIds(patch: Patch[]): string[] {
|
||||||
let changedFileIds = new Set<string>();
|
const changedFileIds = new Set<string>();
|
||||||
for (let p of patch) {
|
for (const p of patch) {
|
||||||
changedFileIds.add(p.path[0]);
|
changedFileIds.add(p.path[0]);
|
||||||
}
|
}
|
||||||
return Array.from(changedFileIds);
|
return Array.from(changedFileIds);
|
||||||
|
@ -541,9 +541,9 @@ function getChangedFileIds(patch: Patch[]): string[] {
|
||||||
|
|
||||||
// Generate unique file ids, different from the ones in the database
|
// Generate unique file ids, different from the ones in the database
|
||||||
export function getFileIds(n: number) {
|
export function getFileIds(n: number) {
|
||||||
let ids = [];
|
const ids = [];
|
||||||
for (let index = 0; ids.length < n; index++) {
|
for (let index = 0; ids.length < n; index++) {
|
||||||
let id = `gpx-${index}`;
|
const id = `gpx-${index}`;
|
||||||
if (!get(fileObservers).has(id)) {
|
if (!get(fileObservers).has(id)) {
|
||||||
ids.push(id);
|
ids.push(id);
|
||||||
}
|
}
|
||||||
|
@ -562,7 +562,7 @@ export const dbUtils = {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
addMultiple: (files: GPXFile[]) => {
|
addMultiple: (files: GPXFile[]) => {
|
||||||
let ids = getFileIds(files.length);
|
const ids = getFileIds(files.length);
|
||||||
applyGlobal((draft) => {
|
applyGlobal((draft) => {
|
||||||
files.forEach((file, index) => {
|
files.forEach((file, index) => {
|
||||||
file._data.id = ids[index];
|
file._data.id = ids[index];
|
||||||
|
@ -590,30 +590,30 @@ export const dbUtils = {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
applyGlobal((draft) => {
|
applyGlobal((draft) => {
|
||||||
let ids = getFileIds(get(settings.fileOrder).length);
|
const ids = getFileIds(get(settings.fileOrder).length);
|
||||||
let index = 0;
|
let index = 0;
|
||||||
applyToOrderedSelectedItemsFromFile((fileId, level, items) => {
|
applyToOrderedSelectedItemsFromFile((fileId, level, items) => {
|
||||||
if (level === ListLevel.FILE) {
|
if (level === ListLevel.FILE) {
|
||||||
let file = getFile(fileId);
|
const file = getFile(fileId);
|
||||||
if (file) {
|
if (file) {
|
||||||
let newFile = file.clone();
|
const newFile = file.clone();
|
||||||
newFile._data.id = ids[index++];
|
newFile._data.id = ids[index++];
|
||||||
draft.set(newFile._data.id, freeze(newFile));
|
draft.set(newFile._data.id, freeze(newFile));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let file = draft.get(fileId);
|
const file = draft.get(fileId);
|
||||||
if (file) {
|
if (file) {
|
||||||
if (level === ListLevel.TRACK) {
|
if (level === ListLevel.TRACK) {
|
||||||
for (let item of items) {
|
for (const item of items) {
|
||||||
let trackIndex = (item as ListTrackItem).getTrackIndex();
|
const trackIndex = (item as ListTrackItem).getTrackIndex();
|
||||||
file.replaceTracks(trackIndex + 1, trackIndex, [
|
file.replaceTracks(trackIndex + 1, trackIndex, [
|
||||||
file.trk[trackIndex].clone(),
|
file.trk[trackIndex].clone(),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
} else if (level === ListLevel.SEGMENT) {
|
} else if (level === ListLevel.SEGMENT) {
|
||||||
for (let item of items) {
|
for (const item of items) {
|
||||||
let trackIndex = (item as ListTrackSegmentItem).getTrackIndex();
|
const trackIndex = (item as ListTrackSegmentItem).getTrackIndex();
|
||||||
let segmentIndex = (item as ListTrackSegmentItem).getSegmentIndex();
|
const segmentIndex = (item as ListTrackSegmentItem).getSegmentIndex();
|
||||||
file.replaceTrackSegments(
|
file.replaceTrackSegments(
|
||||||
trackIndex,
|
trackIndex,
|
||||||
segmentIndex + 1,
|
segmentIndex + 1,
|
||||||
|
@ -628,8 +628,8 @@ export const dbUtils = {
|
||||||
file.wpt.map((wpt) => wpt.clone())
|
file.wpt.map((wpt) => wpt.clone())
|
||||||
);
|
);
|
||||||
} else if (level === ListLevel.WAYPOINT) {
|
} else if (level === ListLevel.WAYPOINT) {
|
||||||
for (let item of items) {
|
for (const item of items) {
|
||||||
let waypointIndex = (item as ListWaypointItem).getWaypointIndex();
|
const waypointIndex = (item as ListWaypointItem).getWaypointIndex();
|
||||||
file.replaceWaypoints(waypointIndex + 1, waypointIndex, [
|
file.replaceWaypoints(waypointIndex + 1, waypointIndex, [
|
||||||
file.wpt[waypointIndex].clone(),
|
file.wpt[waypointIndex].clone(),
|
||||||
]);
|
]);
|
||||||
|
@ -647,7 +647,7 @@ export const dbUtils = {
|
||||||
},
|
},
|
||||||
addNewSegment: (fileId: string, trackIndex: number) => {
|
addNewSegment: (fileId: string, trackIndex: number) => {
|
||||||
dbUtils.applyToFile(fileId, (file) => {
|
dbUtils.applyToFile(fileId, (file) => {
|
||||||
let track = file.trk[trackIndex];
|
const track = file.trk[trackIndex];
|
||||||
track.replaceTrackSegments(track.trkseg.length, track.trkseg.length, [
|
track.replaceTrackSegments(track.trkseg.length, track.trkseg.length, [
|
||||||
new TrackSegment(),
|
new TrackSegment(),
|
||||||
]);
|
]);
|
||||||
|
@ -662,19 +662,19 @@ export const dbUtils = {
|
||||||
}
|
}
|
||||||
applyGlobal((draft) => {
|
applyGlobal((draft) => {
|
||||||
applyToOrderedSelectedItemsFromFile((fileId, level, items) => {
|
applyToOrderedSelectedItemsFromFile((fileId, level, items) => {
|
||||||
let file = draft.get(fileId);
|
const file = draft.get(fileId);
|
||||||
if (file) {
|
if (file) {
|
||||||
if (level === ListLevel.FILE) {
|
if (level === ListLevel.FILE) {
|
||||||
file.reverse();
|
file.reverse();
|
||||||
} else if (level === ListLevel.TRACK) {
|
} else if (level === ListLevel.TRACK) {
|
||||||
for (let item of items) {
|
for (const item of items) {
|
||||||
let trackIndex = (item as ListTrackItem).getTrackIndex();
|
const trackIndex = (item as ListTrackItem).getTrackIndex();
|
||||||
file.reverseTrack(trackIndex);
|
file.reverseTrack(trackIndex);
|
||||||
}
|
}
|
||||||
} else if (level === ListLevel.SEGMENT) {
|
} else if (level === ListLevel.SEGMENT) {
|
||||||
for (let item of items) {
|
for (const item of items) {
|
||||||
let trackIndex = (item as ListTrackSegmentItem).getTrackIndex();
|
const trackIndex = (item as ListTrackSegmentItem).getTrackIndex();
|
||||||
let segmentIndex = (item as ListTrackSegmentItem).getSegmentIndex();
|
const segmentIndex = (item as ListTrackSegmentItem).getSegmentIndex();
|
||||||
file.reverseTrackSegment(trackIndex, segmentIndex);
|
file.reverseTrackSegment(trackIndex, segmentIndex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -688,19 +688,19 @@ export const dbUtils = {
|
||||||
}
|
}
|
||||||
applyGlobal((draft) => {
|
applyGlobal((draft) => {
|
||||||
applyToOrderedSelectedItemsFromFile((fileId, level, items) => {
|
applyToOrderedSelectedItemsFromFile((fileId, level, items) => {
|
||||||
let file = draft.get(fileId);
|
const file = draft.get(fileId);
|
||||||
if (file) {
|
if (file) {
|
||||||
if (level === ListLevel.FILE) {
|
if (level === ListLevel.FILE) {
|
||||||
file.roundTrip();
|
file.roundTrip();
|
||||||
} else if (level === ListLevel.TRACK) {
|
} else if (level === ListLevel.TRACK) {
|
||||||
for (let item of items) {
|
for (const item of items) {
|
||||||
let trackIndex = (item as ListTrackItem).getTrackIndex();
|
const trackIndex = (item as ListTrackItem).getTrackIndex();
|
||||||
file.roundTripTrack(trackIndex);
|
file.roundTripTrack(trackIndex);
|
||||||
}
|
}
|
||||||
} else if (level === ListLevel.SEGMENT) {
|
} else if (level === ListLevel.SEGMENT) {
|
||||||
for (let item of items) {
|
for (const item of items) {
|
||||||
let trackIndex = (item as ListTrackSegmentItem).getTrackIndex();
|
const trackIndex = (item as ListTrackSegmentItem).getTrackIndex();
|
||||||
let segmentIndex = (item as ListTrackSegmentItem).getSegmentIndex();
|
const segmentIndex = (item as ListTrackSegmentItem).getSegmentIndex();
|
||||||
file.roundTripTrackSegment(trackIndex, segmentIndex);
|
file.roundTripTrackSegment(trackIndex, segmentIndex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -713,7 +713,7 @@ export const dbUtils = {
|
||||||
let first = true;
|
let first = true;
|
||||||
let target: ListItem = new ListRootItem();
|
let target: ListItem = new ListRootItem();
|
||||||
let targetFile: GPXFile | undefined = undefined;
|
let targetFile: GPXFile | undefined = undefined;
|
||||||
let toMerge: {
|
const toMerge: {
|
||||||
trk: Track[];
|
trk: Track[];
|
||||||
trkseg: TrackSegment[];
|
trkseg: TrackSegment[];
|
||||||
wpt: Waypoint[];
|
wpt: Waypoint[];
|
||||||
|
@ -723,8 +723,8 @@ export const dbUtils = {
|
||||||
wpt: [],
|
wpt: [],
|
||||||
};
|
};
|
||||||
applyToOrderedSelectedItemsFromFile((fileId, level, items) => {
|
applyToOrderedSelectedItemsFromFile((fileId, level, items) => {
|
||||||
let file = draft.get(fileId);
|
const file = draft.get(fileId);
|
||||||
let originalFile = getFile(fileId);
|
const originalFile = getFile(fileId);
|
||||||
if (file && originalFile) {
|
if (file && originalFile) {
|
||||||
if (level === ListLevel.FILE) {
|
if (level === ListLevel.FILE) {
|
||||||
toMerge.trk.push(...originalFile.trk.map((track) => track.clone()));
|
toMerge.trk.push(...originalFile.trk.map((track) => track.clone()));
|
||||||
|
@ -742,7 +742,7 @@ export const dbUtils = {
|
||||||
} else {
|
} else {
|
||||||
if (level === ListLevel.TRACK) {
|
if (level === ListLevel.TRACK) {
|
||||||
items.forEach((item, index) => {
|
items.forEach((item, index) => {
|
||||||
let trackIndex = (item as ListTrackItem).getTrackIndex();
|
const trackIndex = (item as ListTrackItem).getTrackIndex();
|
||||||
toMerge.trkseg.splice(
|
toMerge.trkseg.splice(
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
|
@ -760,8 +760,8 @@ export const dbUtils = {
|
||||||
});
|
});
|
||||||
} else if (level === ListLevel.SEGMENT) {
|
} else if (level === ListLevel.SEGMENT) {
|
||||||
items.forEach((item, index) => {
|
items.forEach((item, index) => {
|
||||||
let trackIndex = (item as ListTrackSegmentItem).getTrackIndex();
|
const trackIndex = (item as ListTrackSegmentItem).getTrackIndex();
|
||||||
let segmentIndex = (item as ListTrackSegmentItem).getSegmentIndex();
|
const segmentIndex = (item as ListTrackSegmentItem).getSegmentIndex();
|
||||||
if (index === items.length - 1) {
|
if (index === items.length - 1) {
|
||||||
// Order is reversed, so the last segment is the first one and the one to keep
|
// Order is reversed, so the last segment is the first one and the one to keep
|
||||||
target = item;
|
target = item;
|
||||||
|
@ -781,8 +781,8 @@ export const dbUtils = {
|
||||||
});
|
});
|
||||||
|
|
||||||
if (mergeTraces) {
|
if (mergeTraces) {
|
||||||
let statistics = get(gpxStatistics);
|
const statistics = get(gpxStatistics);
|
||||||
let speed =
|
const speed =
|
||||||
statistics.global.speed.moving > 0 ? statistics.global.speed.moving : undefined;
|
statistics.global.speed.moving > 0 ? statistics.global.speed.moving : undefined;
|
||||||
let startTime: Date | undefined = undefined;
|
let startTime: Date | undefined = undefined;
|
||||||
if (speed !== undefined) {
|
if (speed !== undefined) {
|
||||||
|
@ -792,7 +792,7 @@ export const dbUtils = {
|
||||||
) {
|
) {
|
||||||
startTime = statistics.local.points[0].time;
|
startTime = statistics.local.points[0].time;
|
||||||
} else {
|
} else {
|
||||||
let index = statistics.local.points.findIndex(
|
const index = statistics.local.points.findIndex(
|
||||||
(point) => point.time !== undefined
|
(point) => point.time !== undefined
|
||||||
);
|
);
|
||||||
if (index !== -1) {
|
if (index !== -1) {
|
||||||
|
@ -805,7 +805,7 @@ export const dbUtils = {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (toMerge.trk.length > 0 && toMerge.trk[0].trkseg.length > 0) {
|
if (toMerge.trk.length > 0 && toMerge.trk[0].trkseg.length > 0) {
|
||||||
let s = new TrackSegment();
|
const s = new TrackSegment();
|
||||||
toMerge.trk.map((track) => {
|
toMerge.trk.map((track) => {
|
||||||
track.trkseg.forEach((segment) => {
|
track.trkseg.forEach((segment) => {
|
||||||
s.replaceTrackPoints(
|
s.replaceTrackPoints(
|
||||||
|
@ -822,7 +822,7 @@ export const dbUtils = {
|
||||||
toMerge.trk[0].trkseg = [s];
|
toMerge.trk[0].trkseg = [s];
|
||||||
}
|
}
|
||||||
if (toMerge.trkseg.length > 0) {
|
if (toMerge.trkseg.length > 0) {
|
||||||
let s = new TrackSegment();
|
const s = new TrackSegment();
|
||||||
toMerge.trkseg.forEach((segment) => {
|
toMerge.trkseg.forEach((segment) => {
|
||||||
s.replaceTrackPoints(
|
s.replaceTrackPoints(
|
||||||
s.trkpt.length,
|
s.trkpt.length,
|
||||||
|
@ -842,11 +842,11 @@ export const dbUtils = {
|
||||||
targetFile.replaceTracks(0, targetFile.trk.length - 1, toMerge.trk);
|
targetFile.replaceTracks(0, targetFile.trk.length - 1, toMerge.trk);
|
||||||
targetFile.replaceWaypoints(0, targetFile.wpt.length - 1, toMerge.wpt);
|
targetFile.replaceWaypoints(0, targetFile.wpt.length - 1, toMerge.wpt);
|
||||||
} else if (target instanceof ListTrackItem) {
|
} else if (target instanceof ListTrackItem) {
|
||||||
let trackIndex = target.getTrackIndex();
|
const trackIndex = target.getTrackIndex();
|
||||||
targetFile.replaceTrackSegments(trackIndex, 0, -1, toMerge.trkseg);
|
targetFile.replaceTrackSegments(trackIndex, 0, -1, toMerge.trkseg);
|
||||||
} else if (target instanceof ListTrackSegmentItem) {
|
} else if (target instanceof ListTrackSegmentItem) {
|
||||||
let trackIndex = target.getTrackIndex();
|
const trackIndex = target.getTrackIndex();
|
||||||
let segmentIndex = target.getSegmentIndex();
|
const segmentIndex = target.getSegmentIndex();
|
||||||
targetFile.replaceTrackSegments(
|
targetFile.replaceTrackSegments(
|
||||||
trackIndex,
|
trackIndex,
|
||||||
segmentIndex,
|
segmentIndex,
|
||||||
|
@ -863,10 +863,10 @@ export const dbUtils = {
|
||||||
}
|
}
|
||||||
applyGlobal((draft) => {
|
applyGlobal((draft) => {
|
||||||
applyToOrderedSelectedItemsFromFile((fileId, level, items) => {
|
applyToOrderedSelectedItemsFromFile((fileId, level, items) => {
|
||||||
let file = draft.get(fileId);
|
const file = draft.get(fileId);
|
||||||
if (file) {
|
if (file) {
|
||||||
if (level === ListLevel.FILE) {
|
if (level === ListLevel.FILE) {
|
||||||
let length = file.getNumberOfTrackPoints();
|
const length = file.getNumberOfTrackPoints();
|
||||||
if (start >= length || end < 0) {
|
if (start >= length || end < 0) {
|
||||||
draft.delete(fileId);
|
draft.delete(fileId);
|
||||||
} else if (start > 0 || end < length - 1) {
|
} else if (start > 0 || end < length - 1) {
|
||||||
|
@ -875,13 +875,13 @@ export const dbUtils = {
|
||||||
start -= length;
|
start -= length;
|
||||||
end -= length;
|
end -= length;
|
||||||
} else if (level === ListLevel.TRACK) {
|
} else if (level === ListLevel.TRACK) {
|
||||||
let trackIndices = items.map((item) =>
|
const trackIndices = items.map((item) =>
|
||||||
(item as ListTrackItem).getTrackIndex()
|
(item as ListTrackItem).getTrackIndex()
|
||||||
);
|
);
|
||||||
file.crop(start, end, trackIndices);
|
file.crop(start, end, trackIndices);
|
||||||
} else if (level === ListLevel.SEGMENT) {
|
} else if (level === ListLevel.SEGMENT) {
|
||||||
let trackIndices = [(items[0] as ListTrackSegmentItem).getTrackIndex()];
|
const trackIndices = [(items[0] as ListTrackSegmentItem).getTrackIndex()];
|
||||||
let segmentIndices = items.map((item) =>
|
const segmentIndices = items.map((item) =>
|
||||||
(item as ListTrackSegmentItem).getSegmentIndex()
|
(item as ListTrackSegmentItem).getSegmentIndex()
|
||||||
);
|
);
|
||||||
file.crop(start, end, trackIndices, segmentIndices);
|
file.crop(start, end, trackIndices, segmentIndices);
|
||||||
|
@ -894,12 +894,12 @@ export const dbUtils = {
|
||||||
return applyGlobal((draft) => {
|
return applyGlobal((draft) => {
|
||||||
applyToOrderedSelectedItemsFromFile((fileId, level, items) => {
|
applyToOrderedSelectedItemsFromFile((fileId, level, items) => {
|
||||||
if (level === ListLevel.FILE) {
|
if (level === ListLevel.FILE) {
|
||||||
let file = getFile(fileId);
|
const file = getFile(fileId);
|
||||||
if (file) {
|
if (file) {
|
||||||
if (file.trk.length > 1) {
|
if (file.trk.length > 1) {
|
||||||
let fileIds = getFileIds(file.trk.length);
|
const fileIds = getFileIds(file.trk.length);
|
||||||
|
|
||||||
let closest = file.wpt.map((wpt, wptIndex) => {
|
const closest = file.wpt.map((wpt, wptIndex) => {
|
||||||
return {
|
return {
|
||||||
wptIndex: wptIndex,
|
wptIndex: wptIndex,
|
||||||
index: [0],
|
index: [0],
|
||||||
|
@ -910,7 +910,7 @@ export const dbUtils = {
|
||||||
track.getSegments().forEach((segment) => {
|
track.getSegments().forEach((segment) => {
|
||||||
segment.trkpt.forEach((point) => {
|
segment.trkpt.forEach((point) => {
|
||||||
file.wpt.forEach((wpt, wptIndex) => {
|
file.wpt.forEach((wpt, wptIndex) => {
|
||||||
let dist = distance(
|
const dist = distance(
|
||||||
point.getCoordinates(),
|
point.getCoordinates(),
|
||||||
wpt.getCoordinates()
|
wpt.getCoordinates()
|
||||||
);
|
);
|
||||||
|
@ -926,9 +926,9 @@ export const dbUtils = {
|
||||||
});
|
});
|
||||||
|
|
||||||
file.trk.forEach((track, index) => {
|
file.trk.forEach((track, index) => {
|
||||||
let newFile = file.clone();
|
const newFile = file.clone();
|
||||||
let tracks = track.trkseg.map((segment, segmentIndex) => {
|
const tracks = track.trkseg.map((segment, segmentIndex) => {
|
||||||
let t = track.clone();
|
const t = track.clone();
|
||||||
t.replaceTrackSegments(0, track.trkseg.length - 1, [segment]);
|
t.replaceTrackSegments(0, track.trkseg.length - 1, [segment]);
|
||||||
if (track.name) {
|
if (track.name) {
|
||||||
t.name = `${track.name} (${segmentIndex + 1})`;
|
t.name = `${track.name} (${segmentIndex + 1})`;
|
||||||
|
@ -949,9 +949,9 @@ export const dbUtils = {
|
||||||
draft.set(newFile._data.id, freeze(newFile));
|
draft.set(newFile._data.id, freeze(newFile));
|
||||||
});
|
});
|
||||||
} else if (file.trk.length === 1) {
|
} else if (file.trk.length === 1) {
|
||||||
let fileIds = getFileIds(file.trk[0].trkseg.length);
|
const fileIds = getFileIds(file.trk[0].trkseg.length);
|
||||||
|
|
||||||
let closest = file.wpt.map((wpt, wptIndex) => {
|
const closest = file.wpt.map((wpt, wptIndex) => {
|
||||||
return {
|
return {
|
||||||
wptIndex: wptIndex,
|
wptIndex: wptIndex,
|
||||||
index: [0],
|
index: [0],
|
||||||
|
@ -961,7 +961,7 @@ export const dbUtils = {
|
||||||
file.trk[0].trkseg.forEach((segment, index) => {
|
file.trk[0].trkseg.forEach((segment, index) => {
|
||||||
segment.trkpt.forEach((point) => {
|
segment.trkpt.forEach((point) => {
|
||||||
file.wpt.forEach((wpt, wptIndex) => {
|
file.wpt.forEach((wpt, wptIndex) => {
|
||||||
let dist = distance(
|
const dist = distance(
|
||||||
point.getCoordinates(),
|
point.getCoordinates(),
|
||||||
wpt.getCoordinates()
|
wpt.getCoordinates()
|
||||||
);
|
);
|
||||||
|
@ -976,7 +976,7 @@ export const dbUtils = {
|
||||||
});
|
});
|
||||||
|
|
||||||
file.trk[0].trkseg.forEach((segment, index) => {
|
file.trk[0].trkseg.forEach((segment, index) => {
|
||||||
let newFile = file.clone();
|
const newFile = file.clone();
|
||||||
newFile.replaceTrackSegments(0, 0, file.trk[0].trkseg.length - 1, [
|
newFile.replaceTrackSegments(0, 0, file.trk[0].trkseg.length - 1, [
|
||||||
segment,
|
segment,
|
||||||
]);
|
]);
|
||||||
|
@ -995,13 +995,13 @@ export const dbUtils = {
|
||||||
draft.delete(fileId);
|
draft.delete(fileId);
|
||||||
}
|
}
|
||||||
} else if (level === ListLevel.TRACK) {
|
} else if (level === ListLevel.TRACK) {
|
||||||
let file = draft.get(fileId);
|
const file = draft.get(fileId);
|
||||||
if (file) {
|
if (file) {
|
||||||
for (let item of items) {
|
for (const item of items) {
|
||||||
let trackIndex = (item as ListTrackItem).getTrackIndex();
|
const trackIndex = (item as ListTrackItem).getTrackIndex();
|
||||||
let track = file.trk[trackIndex];
|
const track = file.trk[trackIndex];
|
||||||
let tracks = track.trkseg.map((segment, segmentIndex) => {
|
const tracks = track.trkseg.map((segment, segmentIndex) => {
|
||||||
let t = track.clone();
|
const t = track.clone();
|
||||||
t.replaceTrackSegments(0, track.trkseg.length - 1, [segment]);
|
t.replaceTrackSegments(0, track.trkseg.length - 1, [segment]);
|
||||||
if (track.name) {
|
if (track.name) {
|
||||||
t.name = `${track.name} (${segmentIndex + 1})`;
|
t.name = `${track.name} (${segmentIndex + 1})`;
|
||||||
|
@ -1022,16 +1022,16 @@ export const dbUtils = {
|
||||||
coordinates: Coordinates,
|
coordinates: Coordinates,
|
||||||
trkptIndex?: number
|
trkptIndex?: number
|
||||||
) {
|
) {
|
||||||
let splitType = get(splitAs);
|
const splitType = get(splitAs);
|
||||||
return applyGlobal((draft) => {
|
return applyGlobal((draft) => {
|
||||||
let file = getFile(fileId);
|
const file = getFile(fileId);
|
||||||
if (file) {
|
if (file) {
|
||||||
let segment = file.trk[trackIndex].trkseg[segmentIndex];
|
const segment = file.trk[trackIndex].trkseg[segmentIndex];
|
||||||
|
|
||||||
let minIndex = 0;
|
let minIndex = 0;
|
||||||
if (trkptIndex === undefined) {
|
if (trkptIndex === undefined) {
|
||||||
// Find the point closest to split
|
// Find the point closest to split
|
||||||
let closest = getClosestLinePoint(segment.trkpt, coordinates);
|
const closest = getClosestLinePoint(segment.trkpt, coordinates);
|
||||||
minIndex = closest._data.index;
|
minIndex = closest._data.index;
|
||||||
} else {
|
} else {
|
||||||
minIndex = trkptIndex;
|
minIndex = trkptIndex;
|
||||||
|
@ -1048,29 +1048,29 @@ export const dbUtils = {
|
||||||
});
|
});
|
||||||
|
|
||||||
if (splitType === SplitType.FILES) {
|
if (splitType === SplitType.FILES) {
|
||||||
let newFile = draft.get(fileId);
|
const newFile = draft.get(fileId);
|
||||||
if (newFile) {
|
if (newFile) {
|
||||||
newFile.crop(0, absoluteIndex);
|
newFile.crop(0, absoluteIndex);
|
||||||
let newFile2 = file.clone();
|
const newFile2 = file.clone();
|
||||||
newFile2._data.id = getFileIds(1)[0];
|
newFile2._data.id = getFileIds(1)[0];
|
||||||
newFile2.crop(absoluteIndex, file.getNumberOfTrackPoints() - 1);
|
newFile2.crop(absoluteIndex, file.getNumberOfTrackPoints() - 1);
|
||||||
draft.set(newFile2._data.id, freeze(newFile2));
|
draft.set(newFile2._data.id, freeze(newFile2));
|
||||||
}
|
}
|
||||||
} else if (splitType === SplitType.TRACKS) {
|
} else if (splitType === SplitType.TRACKS) {
|
||||||
let newFile = draft.get(fileId);
|
const newFile = draft.get(fileId);
|
||||||
if (newFile) {
|
if (newFile) {
|
||||||
let start = file.trk[trackIndex].clone();
|
const start = file.trk[trackIndex].clone();
|
||||||
start.crop(0, absoluteIndex);
|
start.crop(0, absoluteIndex);
|
||||||
let end = file.trk[trackIndex].clone();
|
const end = file.trk[trackIndex].clone();
|
||||||
end.crop(absoluteIndex, file.trk[trackIndex].getNumberOfTrackPoints() - 1);
|
end.crop(absoluteIndex, file.trk[trackIndex].getNumberOfTrackPoints() - 1);
|
||||||
newFile.replaceTracks(trackIndex, trackIndex, [start, end]);
|
newFile.replaceTracks(trackIndex, trackIndex, [start, end]);
|
||||||
}
|
}
|
||||||
} else if (splitType === SplitType.SEGMENTS) {
|
} else if (splitType === SplitType.SEGMENTS) {
|
||||||
let newFile = draft.get(fileId);
|
const newFile = draft.get(fileId);
|
||||||
if (newFile) {
|
if (newFile) {
|
||||||
let start = segment.clone();
|
const start = segment.clone();
|
||||||
start.crop(0, minIndex);
|
start.crop(0, minIndex);
|
||||||
let end = segment.clone();
|
const end = segment.clone();
|
||||||
end.crop(minIndex, segment.trkpt.length - 1);
|
end.crop(minIndex, segment.trkpt.length - 1);
|
||||||
newFile.replaceTrackSegments(trackIndex, segmentIndex, segmentIndex, [
|
newFile.replaceTrackSegments(trackIndex, segmentIndex, segmentIndex, [
|
||||||
start,
|
start,
|
||||||
|
@ -1092,12 +1092,12 @@ export const dbUtils = {
|
||||||
}
|
}
|
||||||
applyGlobal((draft) => {
|
applyGlobal((draft) => {
|
||||||
applyToOrderedSelectedItemsFromFile((fileId, level, items) => {
|
applyToOrderedSelectedItemsFromFile((fileId, level, items) => {
|
||||||
let file = draft.get(fileId);
|
const file = draft.get(fileId);
|
||||||
if (file) {
|
if (file) {
|
||||||
if (level === ListLevel.FILE) {
|
if (level === ListLevel.FILE) {
|
||||||
file.clean(bounds, inside, deleteTrackPoints, deleteWaypoints);
|
file.clean(bounds, inside, deleteTrackPoints, deleteWaypoints);
|
||||||
} else if (level === ListLevel.TRACK) {
|
} else if (level === ListLevel.TRACK) {
|
||||||
let trackIndices = items.map((item) =>
|
const trackIndices = items.map((item) =>
|
||||||
(item as ListTrackItem).getTrackIndex()
|
(item as ListTrackItem).getTrackIndex()
|
||||||
);
|
);
|
||||||
file.clean(
|
file.clean(
|
||||||
|
@ -1108,8 +1108,8 @@ export const dbUtils = {
|
||||||
trackIndices
|
trackIndices
|
||||||
);
|
);
|
||||||
} else if (level === ListLevel.SEGMENT) {
|
} else if (level === ListLevel.SEGMENT) {
|
||||||
let trackIndices = [(items[0] as ListTrackSegmentItem).getTrackIndex()];
|
const trackIndices = [(items[0] as ListTrackSegmentItem).getTrackIndex()];
|
||||||
let segmentIndices = items.map((item) =>
|
const segmentIndices = items.map((item) =>
|
||||||
(item as ListTrackSegmentItem).getSegmentIndex()
|
(item as ListTrackSegmentItem).getSegmentIndex()
|
||||||
);
|
);
|
||||||
file.clean(
|
file.clean(
|
||||||
|
@ -1123,7 +1123,7 @@ export const dbUtils = {
|
||||||
} else if (level === ListLevel.WAYPOINTS) {
|
} else if (level === ListLevel.WAYPOINTS) {
|
||||||
file.clean(bounds, inside, false, deleteWaypoints);
|
file.clean(bounds, inside, false, deleteWaypoints);
|
||||||
} else if (level === ListLevel.WAYPOINT) {
|
} else if (level === ListLevel.WAYPOINT) {
|
||||||
let waypointIndices = items.map((item) =>
|
const waypointIndices = items.map((item) =>
|
||||||
(item as ListWaypointItem).getWaypointIndex()
|
(item as ListWaypointItem).getWaypointIndex()
|
||||||
);
|
);
|
||||||
file.clean(bounds, inside, false, deleteWaypoints, [], [], waypointIndices);
|
file.clean(bounds, inside, false, deleteWaypoints, [], [], waypointIndices);
|
||||||
|
@ -1137,15 +1137,15 @@ export const dbUtils = {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
applyGlobal((draft) => {
|
applyGlobal((draft) => {
|
||||||
let allItems = Array.from(itemsAndPoints.keys());
|
const allItems = Array.from(itemsAndPoints.keys());
|
||||||
applyToOrderedItemsFromFile(allItems, (fileId, level, items) => {
|
applyToOrderedItemsFromFile(allItems, (fileId, level, items) => {
|
||||||
let file = draft.get(fileId);
|
const file = draft.get(fileId);
|
||||||
if (file) {
|
if (file) {
|
||||||
for (let item of items) {
|
for (const item of items) {
|
||||||
if (item instanceof ListTrackSegmentItem) {
|
if (item instanceof ListTrackSegmentItem) {
|
||||||
let trackIndex = item.getTrackIndex();
|
const trackIndex = item.getTrackIndex();
|
||||||
let segmentIndex = item.getSegmentIndex();
|
const segmentIndex = item.getSegmentIndex();
|
||||||
let points = itemsAndPoints.get(item);
|
const points = itemsAndPoints.get(item);
|
||||||
if (points) {
|
if (points) {
|
||||||
file.replaceTrackPoints(
|
file.replaceTrackPoints(
|
||||||
trackIndex,
|
trackIndex,
|
||||||
|
@ -1164,14 +1164,14 @@ export const dbUtils = {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
addOrUpdateWaypoint: (waypoint: WaypointType, item?: ListWaypointItem) => {
|
addOrUpdateWaypoint: (waypoint: WaypointType, item?: ListWaypointItem) => {
|
||||||
let m = get(map);
|
const m = get(map);
|
||||||
if (m === null) {
|
if (m === null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
getElevation([waypoint.attributes]).then((elevation) => {
|
getElevation([waypoint.attributes]).then((elevation) => {
|
||||||
if (item) {
|
if (item) {
|
||||||
dbUtils.applyToFile(item.getFileId(), (file) => {
|
dbUtils.applyToFile(item.getFileId(), (file) => {
|
||||||
let wpt = file.wpt[item.getWaypointIndex()];
|
const wpt = file.wpt[item.getWaypointIndex()];
|
||||||
wpt.name = waypoint.name;
|
wpt.name = waypoint.name;
|
||||||
wpt.desc = waypoint.desc;
|
wpt.desc = waypoint.desc;
|
||||||
wpt.cmt = waypoint.cmt;
|
wpt.cmt = waypoint.cmt;
|
||||||
|
@ -1181,13 +1181,13 @@ export const dbUtils = {
|
||||||
wpt.ele = elevation[0];
|
wpt.ele = elevation[0];
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
let fileIds = new Set<string>();
|
const fileIds = new Set<string>();
|
||||||
get(selection)
|
get(selection)
|
||||||
.getSelected()
|
.getSelected()
|
||||||
.forEach((item) => {
|
.forEach((item) => {
|
||||||
fileIds.add(item.getFileId());
|
fileIds.add(item.getFileId());
|
||||||
});
|
});
|
||||||
let wpt = new Waypoint(waypoint);
|
const wpt = new Waypoint(waypoint);
|
||||||
wpt.ele = elevation[0];
|
wpt.ele = elevation[0];
|
||||||
dbUtils.applyToFiles(Array.from(fileIds), (file) =>
|
dbUtils.applyToFiles(Array.from(fileIds), (file) =>
|
||||||
file.replaceWaypoints(file.wpt.length, file.wpt.length, [wpt])
|
file.replaceWaypoints(file.wpt.length, file.wpt.length, [wpt])
|
||||||
|
@ -1201,7 +1201,7 @@ export const dbUtils = {
|
||||||
}
|
}
|
||||||
applyGlobal((draft) => {
|
applyGlobal((draft) => {
|
||||||
applyToOrderedSelectedItemsFromFile((fileId, level, items) => {
|
applyToOrderedSelectedItemsFromFile((fileId, level, items) => {
|
||||||
let file = draft.get(fileId);
|
const file = draft.get(fileId);
|
||||||
if (file && (level === ListLevel.FILE || level === ListLevel.TRACK)) {
|
if (file && (level === ListLevel.FILE || level === ListLevel.TRACK)) {
|
||||||
if (level === ListLevel.FILE) {
|
if (level === ListLevel.FILE) {
|
||||||
file.setStyle(style);
|
file.setStyle(style);
|
||||||
|
@ -1209,8 +1209,8 @@ export const dbUtils = {
|
||||||
if (items.length === file.trk.length) {
|
if (items.length === file.trk.length) {
|
||||||
file.setStyle(style);
|
file.setStyle(style);
|
||||||
} else {
|
} else {
|
||||||
for (let item of items) {
|
for (const item of items) {
|
||||||
let trackIndex = (item as ListTrackItem).getTrackIndex();
|
const trackIndex = (item as ListTrackItem).getTrackIndex();
|
||||||
file.trk[trackIndex].setStyle(style);
|
file.trk[trackIndex].setStyle(style);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1225,25 +1225,25 @@ export const dbUtils = {
|
||||||
}
|
}
|
||||||
applyGlobal((draft) => {
|
applyGlobal((draft) => {
|
||||||
applyToOrderedSelectedItemsFromFile((fileId, level, items) => {
|
applyToOrderedSelectedItemsFromFile((fileId, level, items) => {
|
||||||
let file = draft.get(fileId);
|
const file = draft.get(fileId);
|
||||||
if (file) {
|
if (file) {
|
||||||
if (level === ListLevel.FILE) {
|
if (level === ListLevel.FILE) {
|
||||||
file.setHidden(hidden);
|
file.setHidden(hidden);
|
||||||
} else if (level === ListLevel.TRACK) {
|
} else if (level === ListLevel.TRACK) {
|
||||||
let trackIndices = items.map((item) =>
|
const trackIndices = items.map((item) =>
|
||||||
(item as ListTrackItem).getTrackIndex()
|
(item as ListTrackItem).getTrackIndex()
|
||||||
);
|
);
|
||||||
file.setHidden(hidden, trackIndices);
|
file.setHidden(hidden, trackIndices);
|
||||||
} else if (level === ListLevel.SEGMENT) {
|
} else if (level === ListLevel.SEGMENT) {
|
||||||
let trackIndices = [(items[0] as ListTrackSegmentItem).getTrackIndex()];
|
const trackIndices = [(items[0] as ListTrackSegmentItem).getTrackIndex()];
|
||||||
let segmentIndices = items.map((item) =>
|
const segmentIndices = items.map((item) =>
|
||||||
(item as ListTrackSegmentItem).getSegmentIndex()
|
(item as ListTrackSegmentItem).getSegmentIndex()
|
||||||
);
|
);
|
||||||
file.setHidden(hidden, trackIndices, segmentIndices);
|
file.setHidden(hidden, trackIndices, segmentIndices);
|
||||||
} else if (level === ListLevel.WAYPOINTS) {
|
} else if (level === ListLevel.WAYPOINTS) {
|
||||||
file.setHiddenWaypoints(hidden);
|
file.setHiddenWaypoints(hidden);
|
||||||
} else if (level === ListLevel.WAYPOINT) {
|
} else if (level === ListLevel.WAYPOINT) {
|
||||||
let waypointIndices = items.map((item) =>
|
const waypointIndices = items.map((item) =>
|
||||||
(item as ListWaypointItem).getWaypointIndex()
|
(item as ListWaypointItem).getWaypointIndex()
|
||||||
);
|
);
|
||||||
file.setHiddenWaypoints(hidden, waypointIndices);
|
file.setHiddenWaypoints(hidden, waypointIndices);
|
||||||
|
@ -1261,17 +1261,17 @@ export const dbUtils = {
|
||||||
if (level === ListLevel.FILE) {
|
if (level === ListLevel.FILE) {
|
||||||
draft.delete(fileId);
|
draft.delete(fileId);
|
||||||
} else {
|
} else {
|
||||||
let file = draft.get(fileId);
|
const file = draft.get(fileId);
|
||||||
if (file) {
|
if (file) {
|
||||||
if (level === ListLevel.TRACK) {
|
if (level === ListLevel.TRACK) {
|
||||||
for (let item of items) {
|
for (const item of items) {
|
||||||
let trackIndex = (item as ListTrackItem).getTrackIndex();
|
const trackIndex = (item as ListTrackItem).getTrackIndex();
|
||||||
file.replaceTracks(trackIndex, trackIndex, []);
|
file.replaceTracks(trackIndex, trackIndex, []);
|
||||||
}
|
}
|
||||||
} else if (level === ListLevel.SEGMENT) {
|
} else if (level === ListLevel.SEGMENT) {
|
||||||
for (let item of items) {
|
for (const item of items) {
|
||||||
let trackIndex = (item as ListTrackSegmentItem).getTrackIndex();
|
const trackIndex = (item as ListTrackSegmentItem).getTrackIndex();
|
||||||
let segmentIndex = (item as ListTrackSegmentItem).getSegmentIndex();
|
const segmentIndex = (item as ListTrackSegmentItem).getSegmentIndex();
|
||||||
file.replaceTrackSegments(
|
file.replaceTrackSegments(
|
||||||
trackIndex,
|
trackIndex,
|
||||||
segmentIndex,
|
segmentIndex,
|
||||||
|
@ -1282,8 +1282,8 @@ export const dbUtils = {
|
||||||
} else if (level === ListLevel.WAYPOINTS) {
|
} else if (level === ListLevel.WAYPOINTS) {
|
||||||
file.replaceWaypoints(0, file.wpt.length - 1, []);
|
file.replaceWaypoints(0, file.wpt.length - 1, []);
|
||||||
} else if (level === ListLevel.WAYPOINT) {
|
} else if (level === ListLevel.WAYPOINT) {
|
||||||
for (let item of items) {
|
for (const item of items) {
|
||||||
let waypointIndex = (item as ListWaypointItem).getWaypointIndex();
|
const waypointIndex = (item as ListWaypointItem).getWaypointIndex();
|
||||||
file.replaceWaypoints(waypointIndex, waypointIndex, []);
|
file.replaceWaypoints(waypointIndex, waypointIndex, []);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1296,21 +1296,21 @@ export const dbUtils = {
|
||||||
if (get(selection).size === 0) {
|
if (get(selection).size === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let points: (TrackPoint | Waypoint)[] = [];
|
const points: (TrackPoint | Waypoint)[] = [];
|
||||||
applyToOrderedSelectedItemsFromFile((fileId, level, items) => {
|
applyToOrderedSelectedItemsFromFile((fileId, level, items) => {
|
||||||
let file = fileState.get(fileId);
|
const file = fileState.get(fileId);
|
||||||
if (file) {
|
if (file) {
|
||||||
if (level === ListLevel.FILE) {
|
if (level === ListLevel.FILE) {
|
||||||
points.push(...file.getTrackPoints());
|
points.push(...file.getTrackPoints());
|
||||||
points.push(...file.wpt);
|
points.push(...file.wpt);
|
||||||
} else if (level === ListLevel.TRACK) {
|
} else if (level === ListLevel.TRACK) {
|
||||||
let trackIndices = items.map((item) => (item as ListTrackItem).getTrackIndex());
|
const trackIndices = items.map((item) => (item as ListTrackItem).getTrackIndex());
|
||||||
trackIndices.forEach((trackIndex) => {
|
trackIndices.forEach((trackIndex) => {
|
||||||
points.push(...file.trk[trackIndex].getTrackPoints());
|
points.push(...file.trk[trackIndex].getTrackPoints());
|
||||||
});
|
});
|
||||||
} else if (level === ListLevel.SEGMENT) {
|
} else if (level === ListLevel.SEGMENT) {
|
||||||
let trackIndex = (items[0] as ListTrackSegmentItem).getTrackIndex();
|
const trackIndex = (items[0] as ListTrackSegmentItem).getTrackIndex();
|
||||||
let segmentIndices = items.map((item) =>
|
const segmentIndices = items.map((item) =>
|
||||||
(item as ListTrackSegmentItem).getSegmentIndex()
|
(item as ListTrackSegmentItem).getSegmentIndex()
|
||||||
);
|
);
|
||||||
segmentIndices.forEach((segmentIndex) => {
|
segmentIndices.forEach((segmentIndex) => {
|
||||||
|
@ -1319,7 +1319,7 @@ export const dbUtils = {
|
||||||
} else if (level === ListLevel.WAYPOINTS) {
|
} else if (level === ListLevel.WAYPOINTS) {
|
||||||
points.push(...file.wpt);
|
points.push(...file.wpt);
|
||||||
} else if (level === ListLevel.WAYPOINT) {
|
} else if (level === ListLevel.WAYPOINT) {
|
||||||
let waypointIndices = items.map((item) =>
|
const waypointIndices = items.map((item) =>
|
||||||
(item as ListWaypointItem).getWaypointIndex()
|
(item as ListWaypointItem).getWaypointIndex()
|
||||||
);
|
);
|
||||||
points.push(...waypointIndices.map((waypointIndex) => file.wpt[waypointIndex]));
|
points.push(...waypointIndices.map((waypointIndex) => file.wpt[waypointIndex]));
|
||||||
|
@ -1334,25 +1334,25 @@ export const dbUtils = {
|
||||||
getElevation(points).then((elevations) => {
|
getElevation(points).then((elevations) => {
|
||||||
applyGlobal((draft) => {
|
applyGlobal((draft) => {
|
||||||
applyToOrderedSelectedItemsFromFile((fileId, level, items) => {
|
applyToOrderedSelectedItemsFromFile((fileId, level, items) => {
|
||||||
let file = draft.get(fileId);
|
const file = draft.get(fileId);
|
||||||
if (file) {
|
if (file) {
|
||||||
if (level === ListLevel.FILE) {
|
if (level === ListLevel.FILE) {
|
||||||
file.addElevation(elevations);
|
file.addElevation(elevations);
|
||||||
} else if (level === ListLevel.TRACK) {
|
} else if (level === ListLevel.TRACK) {
|
||||||
let trackIndices = items.map((item) =>
|
const trackIndices = items.map((item) =>
|
||||||
(item as ListTrackItem).getTrackIndex()
|
(item as ListTrackItem).getTrackIndex()
|
||||||
);
|
);
|
||||||
file.addElevation(elevations, trackIndices, undefined, []);
|
file.addElevation(elevations, trackIndices, undefined, []);
|
||||||
} else if (level === ListLevel.SEGMENT) {
|
} else if (level === ListLevel.SEGMENT) {
|
||||||
let trackIndices = [(items[0] as ListTrackSegmentItem).getTrackIndex()];
|
const trackIndices = [(items[0] as ListTrackSegmentItem).getTrackIndex()];
|
||||||
let segmentIndices = items.map((item) =>
|
const segmentIndices = items.map((item) =>
|
||||||
(item as ListTrackSegmentItem).getSegmentIndex()
|
(item as ListTrackSegmentItem).getSegmentIndex()
|
||||||
);
|
);
|
||||||
file.addElevation(elevations, trackIndices, segmentIndices, []);
|
file.addElevation(elevations, trackIndices, segmentIndices, []);
|
||||||
} else if (level === ListLevel.WAYPOINTS) {
|
} else if (level === ListLevel.WAYPOINTS) {
|
||||||
file.addElevation(elevations, [], [], undefined);
|
file.addElevation(elevations, [], [], undefined);
|
||||||
} else if (level === ListLevel.WAYPOINT) {
|
} else if (level === ListLevel.WAYPOINT) {
|
||||||
let waypointIndices = items.map((item) =>
|
const waypointIndices = items.map((item) =>
|
||||||
(item as ListWaypointItem).getWaypointIndex()
|
(item as ListWaypointItem).getWaypointIndex()
|
||||||
);
|
);
|
||||||
file.addElevation(elevations, [], [], waypointIndices);
|
file.addElevation(elevations, [], [], waypointIndices);
|
||||||
|
@ -1380,7 +1380,7 @@ export const dbUtils = {
|
||||||
// undo-redo
|
// undo-redo
|
||||||
undo: () => {
|
undo: () => {
|
||||||
if (get(canUndo)) {
|
if (get(canUndo)) {
|
||||||
let index = get(patchIndex);
|
const index = get(patchIndex);
|
||||||
db.patches.get(index).then((patch) => {
|
db.patches.get(index).then((patch) => {
|
||||||
if (patch) {
|
if (patch) {
|
||||||
applyPatch(patch.inversePatch);
|
applyPatch(patch.inversePatch);
|
||||||
|
@ -1391,7 +1391,7 @@ export const dbUtils = {
|
||||||
},
|
},
|
||||||
redo: () => {
|
redo: () => {
|
||||||
if (get(canRedo)) {
|
if (get(canRedo)) {
|
||||||
let index = get(patchIndex) + 1;
|
const index = get(patchIndex) + 1;
|
||||||
db.patches.get(index).then((patch) => {
|
db.patches.get(index).then((patch) => {
|
||||||
if (patch) {
|
if (patch) {
|
||||||
applyPatch(patch.patch);
|
applyPatch(patch.patch);
|
||||||
|
|
|
@ -37,9 +37,9 @@ export const slicedGPXStatistics: Writable<[GPXStatistics, number, number] | und
|
||||||
writable(undefined);
|
writable(undefined);
|
||||||
|
|
||||||
export function updateGPXData() {
|
export function updateGPXData() {
|
||||||
let statistics = new GPXStatistics();
|
const statistics = new GPXStatistics();
|
||||||
applyToOrderedSelectedItemsFromFile((fileId, level, items) => {
|
applyToOrderedSelectedItemsFromFile((fileId, level, items) => {
|
||||||
let stats = getStatistics(fileId);
|
const stats = getStatistics(fileId);
|
||||||
if (stats) {
|
if (stats) {
|
||||||
let first = true;
|
let first = true;
|
||||||
items.forEach((item) => {
|
items.forEach((item) => {
|
||||||
|
@ -56,21 +56,21 @@ export function updateGPXData() {
|
||||||
gpxStatistics.set(statistics);
|
gpxStatistics.set(statistics);
|
||||||
}
|
}
|
||||||
|
|
||||||
let unsubscribes: Map<string, () => void> = new Map();
|
const unsubscribes: Map<string, () => void> = new Map();
|
||||||
selection.subscribe(($selection) => {
|
selection.subscribe(($selection) => {
|
||||||
// Maintain up-to-date statistics for the current selection
|
// Maintain up-to-date statistics for the current selection
|
||||||
updateGPXData();
|
updateGPXData();
|
||||||
|
|
||||||
while (unsubscribes.size > 0) {
|
while (unsubscribes.size > 0) {
|
||||||
let [fileId, unsubscribe] = unsubscribes.entries().next().value;
|
const [fileId, unsubscribe] = unsubscribes.entries().next().value;
|
||||||
unsubscribe();
|
unsubscribe();
|
||||||
unsubscribes.delete(fileId);
|
unsubscribes.delete(fileId);
|
||||||
}
|
}
|
||||||
|
|
||||||
$selection.forEach((item) => {
|
$selection.forEach((item) => {
|
||||||
let fileId = item.getFileId();
|
const fileId = item.getFileId();
|
||||||
if (!unsubscribes.has(fileId)) {
|
if (!unsubscribes.has(fileId)) {
|
||||||
let fileObserver = get(fileObservers).get(fileId);
|
const fileObserver = get(fileObservers).get(fileId);
|
||||||
if (fileObserver) {
|
if (fileObserver) {
|
||||||
let first = true;
|
let first = true;
|
||||||
unsubscribes.set(
|
unsubscribes.set(
|
||||||
|
@ -111,8 +111,8 @@ derived([targetMapBounds, map], (x) => x).subscribe(([bounds, $map]) => {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let currentZoom = $map.getZoom();
|
const currentZoom = $map.getZoom();
|
||||||
let currentBounds = $map.getBounds();
|
const currentBounds = $map.getBounds();
|
||||||
if (
|
if (
|
||||||
bounds.total !== get(fileObservers).size &&
|
bounds.total !== get(fileObservers).size &&
|
||||||
currentBounds &&
|
currentBounds &&
|
||||||
|
@ -167,16 +167,16 @@ export function updateTargetMapBounds(
|
||||||
}
|
}
|
||||||
|
|
||||||
export function centerMapOnSelection() {
|
export function centerMapOnSelection() {
|
||||||
let selected = get(selection).getSelected();
|
const selected = get(selection).getSelected();
|
||||||
let bounds = new mapboxgl.LngLatBounds();
|
const bounds = new mapboxgl.LngLatBounds();
|
||||||
|
|
||||||
if (selected.find((item) => item instanceof ListWaypointItem)) {
|
if (selected.find((item) => item instanceof ListWaypointItem)) {
|
||||||
applyToOrderedSelectedItemsFromFile((fileId, level, items) => {
|
applyToOrderedSelectedItemsFromFile((fileId, level, items) => {
|
||||||
let file = getFile(fileId);
|
const file = getFile(fileId);
|
||||||
if (file) {
|
if (file) {
|
||||||
items.forEach((item) => {
|
items.forEach((item) => {
|
||||||
if (item instanceof ListWaypointItem) {
|
if (item instanceof ListWaypointItem) {
|
||||||
let waypoint = file.wpt[item.getWaypointIndex()];
|
const waypoint = file.wpt[item.getWaypointIndex()];
|
||||||
if (waypoint) {
|
if (waypoint) {
|
||||||
bounds.extend([waypoint.getLongitude(), waypoint.getLatitude()]);
|
bounds.extend([waypoint.getLongitude(), waypoint.getLatitude()]);
|
||||||
}
|
}
|
||||||
|
@ -185,7 +185,7 @@ export function centerMapOnSelection() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
let selectionBounds = get(gpxStatistics).global.bounds;
|
const selectionBounds = get(gpxStatistics).global.bounds;
|
||||||
bounds.setNorthEast(selectionBounds.northEast);
|
bounds.setNorthEast(selectionBounds.northEast);
|
||||||
bounds.setSouthWest(selectionBounds.southWest);
|
bounds.setSouthWest(selectionBounds.southWest);
|
||||||
}
|
}
|
||||||
|
@ -218,13 +218,13 @@ export const streetViewEnabled = writable(false);
|
||||||
export function newGPXFile() {
|
export function newGPXFile() {
|
||||||
const newFileName = get(_)('menu.new_file');
|
const newFileName = get(_)('menu.new_file');
|
||||||
|
|
||||||
let file = new GPXFile();
|
const file = new GPXFile();
|
||||||
|
|
||||||
let maxNewFileNumber = 0;
|
let maxNewFileNumber = 0;
|
||||||
get(fileObservers).forEach((f) => {
|
get(fileObservers).forEach((f) => {
|
||||||
let file = get(f)?.file;
|
const file = get(f)?.file;
|
||||||
if (file && file.metadata.name && file.metadata.name.startsWith(newFileName)) {
|
if (file && file.metadata.name && file.metadata.name.startsWith(newFileName)) {
|
||||||
let number = parseInt(file.metadata.name.split(' ').pop() ?? '0');
|
const number = parseInt(file.metadata.name.split(' ').pop() ?? '0');
|
||||||
if (!isNaN(number) && number > maxNewFileNumber) {
|
if (!isNaN(number) && number > maxNewFileNumber) {
|
||||||
maxNewFileNumber = number;
|
maxNewFileNumber = number;
|
||||||
}
|
}
|
||||||
|
@ -237,7 +237,7 @@ export function newGPXFile() {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createFile() {
|
export function createFile() {
|
||||||
let file = newGPXFile();
|
const file = newGPXFile();
|
||||||
|
|
||||||
dbUtils.add(file);
|
dbUtils.add(file);
|
||||||
|
|
||||||
|
@ -260,27 +260,27 @@ export function triggerFileInput() {
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function loadFiles(list: FileList | File[]) {
|
export async function loadFiles(list: FileList | File[]) {
|
||||||
let files: GPXFile[] = [];
|
const files: GPXFile[] = [];
|
||||||
for (let i = 0; i < list.length; i++) {
|
for (let i = 0; i < list.length; i++) {
|
||||||
let file = await loadFile(list[i]);
|
const file = await loadFile(list[i]);
|
||||||
if (file) {
|
if (file) {
|
||||||
files.push(file);
|
files.push(file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let ids = dbUtils.addMultiple(files);
|
const ids = dbUtils.addMultiple(files);
|
||||||
|
|
||||||
initTargetMapBounds(ids);
|
initTargetMapBounds(ids);
|
||||||
selectFileWhenLoaded(ids[0]);
|
selectFileWhenLoaded(ids[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function loadFile(file: File): Promise<GPXFile | null> {
|
export async function loadFile(file: File): Promise<GPXFile | null> {
|
||||||
let result = await new Promise<GPXFile | null>((resolve) => {
|
const result = await new Promise<GPXFile | null>((resolve) => {
|
||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
reader.onload = () => {
|
reader.onload = () => {
|
||||||
let data = reader.result?.toString() ?? null;
|
const data = reader.result?.toString() ?? null;
|
||||||
if (data) {
|
if (data) {
|
||||||
let gpx = parseGPX(data);
|
const gpx = parseGPX(data);
|
||||||
if (gpx.metadata === undefined) {
|
if (gpx.metadata === undefined) {
|
||||||
gpx.metadata = {};
|
gpx.metadata = {};
|
||||||
}
|
}
|
||||||
|
@ -309,17 +309,17 @@ export function selectFileWhenLoaded(fileId: string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function updateSelectionFromKey(down: boolean, shift: boolean) {
|
export function updateSelectionFromKey(down: boolean, shift: boolean) {
|
||||||
let selected = get(selection).getSelected();
|
const selected = get(selection).getSelected();
|
||||||
if (selected.length === 0) {
|
if (selected.length === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let next: ListItem | undefined = undefined;
|
let next: ListItem | undefined = undefined;
|
||||||
if (selected[0] instanceof ListFileItem) {
|
if (selected[0] instanceof ListFileItem) {
|
||||||
let order = get(fileOrder);
|
const order = get(fileOrder);
|
||||||
let limitIndex: number | undefined = undefined;
|
let limitIndex: number | undefined = undefined;
|
||||||
selected.forEach((item) => {
|
selected.forEach((item) => {
|
||||||
let index = order.indexOf(item.getFileId());
|
const index = order.indexOf(item.getFileId());
|
||||||
if (
|
if (
|
||||||
limitIndex === undefined ||
|
limitIndex === undefined ||
|
||||||
(down && index > limitIndex) ||
|
(down && index > limitIndex) ||
|
||||||
|
@ -355,11 +355,11 @@ export function updateSelectionFromKey(down: boolean, shift: boolean) {
|
||||||
selected[0] instanceof ListTrackItem &&
|
selected[0] instanceof ListTrackItem &&
|
||||||
selected[selected.length - 1] instanceof ListTrackItem
|
selected[selected.length - 1] instanceof ListTrackItem
|
||||||
) {
|
) {
|
||||||
let fileId = selected[0].getFileId();
|
const fileId = selected[0].getFileId();
|
||||||
let file = getFile(fileId);
|
const file = getFile(fileId);
|
||||||
if (file) {
|
if (file) {
|
||||||
let numberOfTracks = file.trk.length;
|
const numberOfTracks = file.trk.length;
|
||||||
let trackIndex = down
|
const trackIndex = down
|
||||||
? selected[selected.length - 1].getTrackIndex()
|
? selected[selected.length - 1].getTrackIndex()
|
||||||
: selected[0].getTrackIndex();
|
: selected[0].getTrackIndex();
|
||||||
if (down && trackIndex < numberOfTracks - 1) {
|
if (down && trackIndex < numberOfTracks - 1) {
|
||||||
|
@ -372,12 +372,12 @@ export function updateSelectionFromKey(down: boolean, shift: boolean) {
|
||||||
selected[0] instanceof ListTrackSegmentItem &&
|
selected[0] instanceof ListTrackSegmentItem &&
|
||||||
selected[selected.length - 1] instanceof ListTrackSegmentItem
|
selected[selected.length - 1] instanceof ListTrackSegmentItem
|
||||||
) {
|
) {
|
||||||
let fileId = selected[0].getFileId();
|
const fileId = selected[0].getFileId();
|
||||||
let file = getFile(fileId);
|
const file = getFile(fileId);
|
||||||
if (file) {
|
if (file) {
|
||||||
let trackIndex = selected[0].getTrackIndex();
|
const trackIndex = selected[0].getTrackIndex();
|
||||||
let numberOfSegments = file.trk[trackIndex].trkseg.length;
|
const numberOfSegments = file.trk[trackIndex].trkseg.length;
|
||||||
let segmentIndex = down
|
const segmentIndex = down
|
||||||
? selected[selected.length - 1].getSegmentIndex()
|
? selected[selected.length - 1].getSegmentIndex()
|
||||||
: selected[0].getSegmentIndex();
|
: selected[0].getSegmentIndex();
|
||||||
if (down && segmentIndex < numberOfSegments - 1) {
|
if (down && segmentIndex < numberOfSegments - 1) {
|
||||||
|
@ -390,11 +390,11 @@ export function updateSelectionFromKey(down: boolean, shift: boolean) {
|
||||||
selected[0] instanceof ListWaypointItem &&
|
selected[0] instanceof ListWaypointItem &&
|
||||||
selected[selected.length - 1] instanceof ListWaypointItem
|
selected[selected.length - 1] instanceof ListWaypointItem
|
||||||
) {
|
) {
|
||||||
let fileId = selected[0].getFileId();
|
const fileId = selected[0].getFileId();
|
||||||
let file = getFile(fileId);
|
const file = getFile(fileId);
|
||||||
if (file) {
|
if (file) {
|
||||||
let numberOfWaypoints = file.wpt.length;
|
const numberOfWaypoints = file.wpt.length;
|
||||||
let waypointIndex = down
|
const waypointIndex = down
|
||||||
? selected[selected.length - 1].getWaypointIndex()
|
? selected[selected.length - 1].getWaypointIndex()
|
||||||
: selected[0].getWaypointIndex();
|
: selected[0].getWaypointIndex();
|
||||||
if (down && waypointIndex < numberOfWaypoints - 1) {
|
if (down && waypointIndex < numberOfWaypoints - 1) {
|
||||||
|
@ -465,9 +465,9 @@ export const allHidden = writable(false);
|
||||||
export function updateAllHidden() {
|
export function updateAllHidden() {
|
||||||
let hidden = true;
|
let hidden = true;
|
||||||
applyToOrderedSelectedItemsFromFile((fileId, level, items) => {
|
applyToOrderedSelectedItemsFromFile((fileId, level, items) => {
|
||||||
let file = getFile(fileId);
|
const file = getFile(fileId);
|
||||||
if (file) {
|
if (file) {
|
||||||
for (let item of items) {
|
for (const item of items) {
|
||||||
if (!hidden) {
|
if (!hidden) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,9 +36,9 @@ export function distancePerHourToSecondsPerDistance(value: number) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function secondsToHHMMSS(value: number) {
|
export function secondsToHHMMSS(value: number) {
|
||||||
var hours = Math.floor(value / 3600);
|
const hours = Math.floor(value / 3600);
|
||||||
var minutes = Math.floor(value / 60) % 60;
|
const minutes = Math.floor(value / 60) % 60;
|
||||||
var seconds = Math.min(59, Math.round(value % 60));
|
const seconds = Math.min(59, Math.round(value % 60));
|
||||||
|
|
||||||
return [hours, minutes, seconds]
|
return [hours, minutes, seconds]
|
||||||
.map((v) => (v < 10 ? '0' + v : v))
|
.map((v) => (v < 10 ? '0' + v : v))
|
||||||
|
|
|
@ -78,7 +78,7 @@ export function getClosestLinePoint(
|
||||||
let closest = points[0];
|
let closest = points[0];
|
||||||
let closestDist = Number.MAX_VALUE;
|
let closestDist = Number.MAX_VALUE;
|
||||||
for (let i = 0; i < points.length - 1; i++) {
|
for (let i = 0; i < points.length - 1; i++) {
|
||||||
let dist = crossarcDistance(points[i], points[i + 1], point);
|
const dist = crossarcDistance(points[i], points[i + 1], point);
|
||||||
if (dist < closestDist) {
|
if (dist < closestDist) {
|
||||||
closestDist = dist;
|
closestDist = dist;
|
||||||
if (distance(points[i], point) <= distance(points[i + 1], point)) {
|
if (distance(points[i], point) <= distance(points[i + 1], point)) {
|
||||||
|
@ -101,21 +101,21 @@ export function getElevation(
|
||||||
ELEVATION_ZOOM: number = 13,
|
ELEVATION_ZOOM: number = 13,
|
||||||
tileSize = 512
|
tileSize = 512
|
||||||
): Promise<number[]> {
|
): Promise<number[]> {
|
||||||
let coordinates = points.map((point) =>
|
const coordinates = points.map((point) =>
|
||||||
point instanceof TrackPoint || point instanceof Waypoint ? point.getCoordinates() : point
|
point instanceof TrackPoint || point instanceof Waypoint ? point.getCoordinates() : point
|
||||||
);
|
);
|
||||||
let bbox = new mapboxgl.LngLatBounds();
|
const bbox = new mapboxgl.LngLatBounds();
|
||||||
coordinates.forEach((coord) => bbox.extend(coord));
|
coordinates.forEach((coord) => bbox.extend(coord));
|
||||||
|
|
||||||
let tiles = coordinates.map((coord) =>
|
const tiles = coordinates.map((coord) =>
|
||||||
tilebelt.pointToTile(coord.lon, coord.lat, ELEVATION_ZOOM)
|
tilebelt.pointToTile(coord.lon, coord.lat, ELEVATION_ZOOM)
|
||||||
);
|
);
|
||||||
let uniqueTiles = Array.from(new Set(tiles.map((tile) => tile.join(',')))).map((tile) =>
|
const uniqueTiles = Array.from(new Set(tiles.map((tile) => tile.join(',')))).map((tile) =>
|
||||||
tile.split(',').map((x) => parseInt(x))
|
tile.split(',').map((x) => parseInt(x))
|
||||||
);
|
);
|
||||||
let pngs = new Map<string, any>();
|
const pngs = new Map<string, any>();
|
||||||
|
|
||||||
let promises = uniqueTiles.map((tile) =>
|
const promises = uniqueTiles.map((tile) =>
|
||||||
fetch(
|
fetch(
|
||||||
`https://api.mapbox.com/v4/mapbox.mapbox-terrain-dem-v1/${ELEVATION_ZOOM}/${tile[0]}/${tile[1]}@2x.pngraw?access_token=${PUBLIC_MAPBOX_TOKEN}`,
|
`https://api.mapbox.com/v4/mapbox.mapbox-terrain-dem-v1/${ELEVATION_ZOOM}/${tile[0]}/${tile[1]}@2x.pngraw?access_token=${PUBLIC_MAPBOX_TOKEN}`,
|
||||||
{ cache: 'force-cache' }
|
{ cache: 'force-cache' }
|
||||||
|
@ -124,7 +124,7 @@ export function getElevation(
|
||||||
.then(
|
.then(
|
||||||
(buffer) =>
|
(buffer) =>
|
||||||
new Promise((resolve) => {
|
new Promise((resolve) => {
|
||||||
let png = new PNGReader(new Uint8Array(buffer));
|
const png = new PNGReader(new Uint8Array(buffer));
|
||||||
png.parse((err, png) => {
|
png.parse((err, png) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
resolve(false); // Also resolve so that Promise.all doesn't fail
|
resolve(false); // Also resolve so that Promise.all doesn't fail
|
||||||
|
@ -139,20 +139,20 @@ export function getElevation(
|
||||||
|
|
||||||
return Promise.all(promises).then(() =>
|
return Promise.all(promises).then(() =>
|
||||||
coordinates.map((coord, index) => {
|
coordinates.map((coord, index) => {
|
||||||
let tile = tiles[index];
|
const tile = tiles[index];
|
||||||
let png = pngs.get(tile.join(','));
|
const png = pngs.get(tile.join(','));
|
||||||
|
|
||||||
if (!png) {
|
if (!png) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
let tf = tilebelt.pointToTileFraction(coord.lon, coord.lat, ELEVATION_ZOOM);
|
const tf = tilebelt.pointToTileFraction(coord.lon, coord.lat, ELEVATION_ZOOM);
|
||||||
let x = tileSize * (tf[0] - tile[0]);
|
const x = tileSize * (tf[0] - tile[0]);
|
||||||
let y = tileSize * (tf[1] - tile[1]);
|
const y = tileSize * (tf[1] - tile[1]);
|
||||||
let _x = Math.floor(x);
|
const _x = Math.floor(x);
|
||||||
let _y = Math.floor(y);
|
const _y = Math.floor(y);
|
||||||
let dx = x - _x;
|
const dx = x - _x;
|
||||||
let dy = y - _y;
|
const dy = y - _y;
|
||||||
|
|
||||||
const p00 = png.getPixel(_x, _y);
|
const p00 = png.getPixel(_x, _y);
|
||||||
const p01 = png.getPixel(_x, _y + (_y + 1 == tileSize ? 0 : 1));
|
const p01 = png.getPixel(_x, _y + (_y + 1 == tileSize ? 0 : 1));
|
||||||
|
@ -162,10 +162,10 @@ export function getElevation(
|
||||||
_y + (_y + 1 == tileSize ? 0 : 1)
|
_y + (_y + 1 == tileSize ? 0 : 1)
|
||||||
);
|
);
|
||||||
|
|
||||||
let ele00 = -10000 + (p00[0] * 256 * 256 + p00[1] * 256 + p00[2]) * 0.1;
|
const ele00 = -10000 + (p00[0] * 256 * 256 + p00[1] * 256 + p00[2]) * 0.1;
|
||||||
let ele01 = -10000 + (p01[0] * 256 * 256 + p01[1] * 256 + p01[2]) * 0.1;
|
const ele01 = -10000 + (p01[0] * 256 * 256 + p01[1] * 256 + p01[2]) * 0.1;
|
||||||
let ele10 = -10000 + (p10[0] * 256 * 256 + p10[1] * 256 + p10[2]) * 0.1;
|
const ele10 = -10000 + (p10[0] * 256 * 256 + p10[1] * 256 + p10[2]) * 0.1;
|
||||||
let ele11 = -10000 + (p11[0] * 256 * 256 + p11[1] * 256 + p11[2]) * 0.1;
|
const ele11 = -10000 + (p11[0] * 256 * 256 + p11[1] * 256 + p11[2]) * 0.1;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
ele00 * (1 - dx) * (1 - dy) +
|
ele00 * (1 - dx) * (1 - dy) +
|
||||||
|
@ -177,9 +177,9 @@ export function getElevation(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let previousCursors: string[] = [];
|
const previousCursors: string[] = [];
|
||||||
export function setCursor(cursor: string) {
|
export function setCursor(cursor: string) {
|
||||||
let m = get(map);
|
const m = get(map);
|
||||||
if (m) {
|
if (m) {
|
||||||
previousCursors.push(m.getCanvas().style.cursor);
|
previousCursors.push(m.getCanvas().style.cursor);
|
||||||
m.getCanvas().style.cursor = cursor;
|
m.getCanvas().style.cursor = cursor;
|
||||||
|
@ -187,7 +187,7 @@ export function setCursor(cursor: string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function resetCursor() {
|
export function resetCursor() {
|
||||||
let m = get(map);
|
const m = get(map);
|
||||||
if (m) {
|
if (m) {
|
||||||
m.getCanvas().style.cursor = previousCursors.pop() ?? '';
|
m.getCanvas().style.cursor = previousCursors.pop() ?? '';
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,9 +15,9 @@ export async function load({ params }) {
|
||||||
const { language } = params;
|
const { language } = params;
|
||||||
|
|
||||||
const guideTitles: Record<string, string> = {};
|
const guideTitles: Record<string, string> = {};
|
||||||
for (let guide of Object.keys(guides)) {
|
for (const guide of Object.keys(guides)) {
|
||||||
guideTitles[guide] = (await getModule(language, guide)).metadata.title;
|
guideTitles[guide] = (await getModule(language, guide)).metadata.title;
|
||||||
for (let subguide of guides[guide]) {
|
for (const subguide of guides[guide]) {
|
||||||
guideTitles[`${guide}/${subguide}`] = (
|
guideTitles[`${guide}/${subguide}`] = (
|
||||||
await getModule(language, `${guide}/${subguide}`)
|
await getModule(language, `${guide}/${subguide}`)
|
||||||
).metadata.title;
|
).metadata.title;
|
||||||
|
|
Loading…
Reference in a new issue