speed improvements for map (tbc)

This commit is contained in:
Christian Beutel
2025-08-02 22:02:59 +02:00
parent 396e64b5a6
commit 1c10e90db7
14 changed files with 230 additions and 113 deletions

View File

@@ -89,10 +89,7 @@
$data.distance = undefined; $data.distance = undefined;
return; return;
} }
const gpxObject = await GPX.parse(trailData); const gpxObject = GPX.parse(trailData);
if (gpxObject instanceof Error) {
throw gpxObject;
}
const totals = gpxObject.features; const totals = gpxObject.features;

View File

@@ -58,7 +58,7 @@
}; };
} }
trail = (await gpx2trail(log.expand.gpx_data!)).trail; trail = gpx2trail(log.expand.gpx_data!).trail;
trail.id = log.id; trail.id = log.id;
trail.expand!.gpx_data = log.expand.gpx_data; trail.expand!.gpx_data = log.expand.gpx_data;

View File

@@ -1,12 +1,12 @@
<script lang="ts"> <script lang="ts">
import { page } from "$app/state"; import { page } from "$app/state";
import directionCaret from "$lib/assets/svgs/caret-right-solid.svg"; import directionCaret from "$lib/assets/svgs/caret-right-solid.svg";
import GPX from "$lib/models/gpx/gpx";
import type { Trail } from "$lib/models/trail"; import type { Trail } from "$lib/models/trail";
import type { Waypoint } from "$lib/models/waypoint"; import type { Waypoint } from "$lib/models/waypoint";
import { theme } from "$lib/stores/theme_store"; import { theme } from "$lib/stores/theme_store";
import { fetchGPX } from "$lib/stores/trail_store"; import { fetchGPX } from "$lib/stores/trail_store";
import { findStartAndEndPoints } from "$lib/util/geojson_util"; import { findStartAndEndPoints } from "$lib/util/geojson_util";
import { toGeoJson } from "$lib/util/gpx_util";
import { import {
createMarkerFromWaypoint, createMarkerFromWaypoint,
createPopupFromTrail, createPopupFromTrail,
@@ -15,24 +15,17 @@
import type { ElevationProfileControl } from "$lib/vendor/maplibre-elevation-profile/elevationprofile-control"; import type { ElevationProfileControl } from "$lib/vendor/maplibre-elevation-profile/elevationprofile-control";
import { FullscreenControl } from "$lib/vendor/maplibre-fullscreen/fullscreen-control"; import { FullscreenControl } from "$lib/vendor/maplibre-fullscreen/fullscreen-control";
import MaplibreGraticule from "$lib/vendor/maplibre-graticule/maplibre-graticule"; import MaplibreGraticule from "$lib/vendor/maplibre-graticule/maplibre-graticule";
import { import { baseMapStyles } from "$lib/vendor/maplibre-layer-manager/layers";
baseMapStyles,
overlays,
pois,
} from "$lib/vendor/maplibre-layer-manager/layers";
import { LayerManager } from "$lib/vendor/maplibre-layer-manager/maplibre-layer-manager"; import { LayerManager } from "$lib/vendor/maplibre-layer-manager/maplibre-layer-manager";
import { import { StyleSwitcherControl } from "$lib/vendor/maplibre-style-switcher/style-switcher-control";
StyleSwitcherControl,
type StyleSwitcherControlOptions,
} from "$lib/vendor/maplibre-style-switcher/style-switcher-control";
import type { Feature, FeatureCollection, GeoJSON } from "geojson"; import type { Feature, FeatureCollection, GeoJSON } from "geojson";
import * as M from "maplibre-gl"; import * as M from "maplibre-gl";
import "maplibre-gl/dist/maplibre-gl.css"; import "maplibre-gl/dist/maplibre-gl.css";
import { onDestroy, onMount, untrack } from "svelte"; import { onDestroy, onMount, untrack } from "svelte";
import type { RadioItem } from "../base/radio_group.svelte";
interface Props { interface Props {
trails?: Trail[]; trails?: Trail[];
gpx?: GPX;
waypoints?: Waypoint[]; waypoints?: Waypoint[];
markers?: M.Marker[]; markers?: M.Marker[];
map?: M.Map | null; map?: M.Map | null;
@@ -160,10 +153,10 @@
toggleEpcTheme(); toggleEpcTheme();
}); });
$effect(() => { $effect(() => {
if (drawing && map) { if (drawing) {
startDrawing(); untrack(() => startDrawing());
} else if (!drawing && map) { } else {
stopDrawing(); untrack(() => stopDrawing());
} }
}); });
$effect(() => { $effect(() => {
@@ -203,9 +196,11 @@
let cD: FeatureCollection = { type: "FeatureCollection", features: [] }; let cD: FeatureCollection = { type: "FeatureCollection", features: [] };
let r: GeoJSON[] = []; let r: GeoJSON[] = [];
trails.forEach((t) => { for (const t of trails) {
if (t.expand?.gpx_data) { if (t.expand?.gpx) {
r.push(toGeoJson(t.expand.gpx_data) as GeoJSON); r.push(t.expand.gpx.toGeoJSON());
} else if (t.expand?.gpx_data) {
r.push(GPX.parse(t.expand.gpx_data).toGeoJSON());
} }
if (clusterTrails && t.lat !== null && t.lon !== null) { if (clusterTrails && t.lat !== null && t.lon !== null) {
cD.features.push({ cD.features.push({
@@ -220,7 +215,7 @@
}, },
} as Feature); } as Feature);
} }
}); }
return [r, cD]; return [r, cD];
} }
@@ -653,9 +648,9 @@
clusterPopup = createPopupFromTrail(trail); clusterPopup = createPopupFromTrail(trail);
clusterPopup.setLngLat([trail.lon!, trail.lat!]).addTo(map); clusterPopup.setLngLat([trail.lon!, trail.lat!]).addTo(map);
const geojson = await fetchGPX(trail); const gpx = await fetchGPX(trail);
addClusterHighlightLayer(toGeoJson(geojson)); addClusterHighlightLayer(GPX.parse(gpx).toGeoJSON());
clusterPopup.on("close", () => { clusterPopup.on("close", () => {
unHighlightCluster(false); unHighlightCluster(false);
@@ -814,7 +809,7 @@
} }
function showWaypoints() { function showWaypoints() {
if (!map) { if (!map || drawing) {
return; return;
} }
@@ -1030,23 +1025,23 @@
type: "hillshade", type: "hillshade",
}); });
} }
trails.forEach((t, i) => {
const layerId = t.id!;
if (map?.getLayer(layerId)) {
return;
}
addTrailLayer(t, layerId, i, data[i]);
});
if (
activeTrail !== null &&
!map?.getLayer("direction-carets")
) {
addCaretLayer(trails[activeTrail].id!);
}
} catch (e) {} } catch (e) {}
} }
trails.forEach((t, i) => {
const layerId = t.id!;
if (map?.getLayer(layerId)) {
return;
}
addTrailLayer(t, layerId, i, data[i]);
});
if (
activeTrail !== null &&
trails[activeTrail] &&
!map?.getLayer("direction-carets")
) {
addCaretLayer(trails[activeTrail].id!);
}
}); });
map.on("moveend", (e) => { map.on("moveend", (e) => {

View File

@@ -1,7 +1,8 @@
<script lang="ts"> <script lang="ts">
import * as M from "maplibre-gl"; import * as M from "maplibre-gl";
import { fromFile, toGeoJson } from "$lib/util/gpx_util"; import GPX from "$lib/models/gpx/gpx";
import { fromFile } from "$lib/util/gpx_util";
import { onMount } from "svelte"; import { onMount } from "svelte";
interface Props { interface Props {
trailFile: File | undefined | null; trailFile: File | undefined | null;
@@ -76,7 +77,12 @@
const sourceId = "trail-picker-geojson-source"; const sourceId = "trail-picker-geojson-source";
const layerId = "trail-picker-geojson-layer"; const layerId = "trail-picker-geojson-layer";
const geojson = toGeoJson(trailData); const gpx = GPX.parse(trailData)
if(gpx instanceof Error) {
throw gpx;
}
const geojson = gpx.toGeoJSON()
if (layer) { if (layer) {
map.removeLayer(layerId); map.removeLayer(layerId);

View File

@@ -10,6 +10,9 @@ import geohash from "ngeohash"
import { encodePolyline } from '$lib/util/polyline_util'; import { encodePolyline } from '$lib/util/polyline_util';
import { APIError } from '$lib/util/api_util'; import { APIError } from '$lib/util/api_util';
import type { ValhallaHeightResponse } from '../valhalla'; import type { ValhallaHeightResponse } from '../valhalla';
import { bbox, splitMultiLineStringToLineStrings } from '$lib/util/geojson_util';
import { browser } from '$app/environment';
import xmldom from 'xmldom';
const defaultAttributes = { const defaultAttributes = {
version: '1.1', version: '1.1',
@@ -210,31 +213,65 @@ export default class GPX {
this.features = this.getTotals() this.features = this.getTotals()
} }
static parse(gpxString: string): Promise<GPX | Error> { static parse(gpxString: string): GPX {
const sanitizedGPX = gpxString.replace(/\sxmlns=""/g, '').replace(/<!--[\s\S]*?-->/g, ''); const sanitizedGPX = gpxString.replace(/\sxmlns=""/g, '').replace(/<!--[\s\S]*?-->/g, '');
return new Promise<GPX | Error>((resolve, reject) => xml2js.parseString(sanitizedGPX, { return (function () {
explicitArray: false, let data = null, error = null;
attrValueProcessors: [(str: string) => { xml2js.parseString(sanitizedGPX, {
if (str.length && !isNaN(Number(str))) { explicitArray: false,
return Number.isInteger(Number(str)) ? parseInt(String(str), 10) : parseFloat(String(str)); attrValueProcessors: [(str: string) => {
if (str.length && !isNaN(Number(str))) {
return Number.isInteger(Number(str)) ? parseInt(String(str), 10) : parseFloat(String(str));
}
return str;
} }
return str; ]
} }, (err, xml) => {
] error = err;
}, async (err, xml) => { data = new GPX({
if (err) { $: xml.gpx.$,
reject(err); metadata: xml.gpx.metadata,
} wpt: xml.gpx.wpt,
const gpx = new GPX({ rte: xml.gpx.rte,
$: xml.gpx.$, trk: xml.gpx.trk
metadata: xml.gpx.metadata, });
wpt: xml.gpx.wpt,
rte: xml.gpx.rte,
trk: xml.gpx.trk
}); });
resolve(gpx) if (error) {
})); throw error
};
return data;
}()) as unknown as GPX;
}
toGeoJSON(): GeoJSON.FeatureCollection {
const features: GeoJSON.Feature[] = [];
if (this.wpt) {
for (const wpt of this.wpt) {
features.push(wpt.toGeoJSON());
}
}
if (this.rte) {
for (const rte of this.rte) {
features.push(rte.toGeoJSON());
}
}
if (this.trk) {
for (const trk of this.trk) {
features.push(...trk.toGeoJSON());
}
}
let geojson: GeoJSON.FeatureCollection = {
type: "FeatureCollection",
features
};
geojson.bbox = bbox(geojson)
return geojson
} }
toString(options?: xml2js.BuilderOptions) { toString(options?: xml2js.BuilderOptions) {

View File

@@ -42,4 +42,24 @@ export default class Route {
this.rtept = (object.rtept as Waypoint[]).map(rtept => new Waypoint(rtept)) this.rtept = (object.rtept as Waypoint[]).map(rtept => new Waypoint(rtept))
} }
} }
toGeoJSON(): GeoJSON.Feature {
const coordinates = (this.rtept || []).map(pt =>
[pt.$.lon ?? 0, pt.$.lat ?? 0, pt.ele ?? 0]
);
return {
type: "Feature",
geometry: {
type: "LineString",
coordinates
},
properties: {
name: this.name,
desc: this.desc,
type: this.type,
number: this.number
}
};
}
} }

View File

@@ -1,3 +1,4 @@
import type Track from './track';
import Waypoint from './waypoint'; import Waypoint from './waypoint';
export default class TrackSegment { export default class TrackSegment {
@@ -12,4 +13,38 @@ export default class TrackSegment {
} }
this.extensions = object.extensions; this.extensions = object.extensions;
} }
toGeoJSON(
track: Track,
segmentId: number,
featureId: number
): GeoJSON.Feature {
const coordinates = (this.trkpt || []).map(pt => [
pt.$.lon ?? 0,
pt.$.lat ?? 0,
pt.ele ?? 0,
]);
const times = (this.trkpt || []).map(pt => pt.time?.toISOString() ?? null);
return {
type: "Feature",
geometry: {
type: "LineString",
coordinates,
},
properties: {
name: track.name,
desc: track.desc,
type: track.type,
number: track.number,
featureId,
segmentId,
coordinateProperties: {
times
}
}
};
}
} }

View File

@@ -42,4 +42,16 @@ export default class Track {
this.trkseg = object.trkseg.map(trkseg => new TrackSegment(trkseg));; this.trkseg = object.trkseg.map(trkseg => new TrackSegment(trkseg));;
} }
} }
toGeoJSON(): GeoJSON.Feature[] {
const features: GeoJSON.Feature[] = [];
if (!this.trkseg) return features;
this.trkseg.forEach((segment, index) => {
const feature = segment.toGeoJSON(this, index, features.length);
features.push(feature);
});
return features;
}
} }

View File

@@ -78,4 +78,22 @@ export default class Waypoint {
this.link = object.link.map(l => new Link(l)); this.link = object.link.map(l => new Link(l));
} }
} }
toGeoJSON(): GeoJSON.Feature {
return {
type: "Feature",
geometry: {
type: "Point",
coordinates: [this.$.lon ?? 0, this.$.lat ?? 0, this.ele ?? 0]
},
properties: {
name: this.name,
desc: this.desc,
time: this.time?.toISOString(),
type: this.type,
sym: this.sym
}
};
}
} }

View File

@@ -1,6 +1,7 @@
import type { Actor } from "./activitypub/actor"; import type { Actor } from "./activitypub/actor";
import type { Category } from "./category"; import type { Category } from "./category";
import type { Comment } from "./comment"; import type { Comment } from "./comment";
import type GPX from "./gpx/gpx";
import type { SummitLog } from "./summit_log"; import type { SummitLog } from "./summit_log";
import type { Tag } from "./tag"; import type { Tag } from "./tag";
import type { TrailLike } from "./trail_like"; import type { TrailLike } from "./trail_like";
@@ -40,6 +41,7 @@ class Trail {
author?: Actor author?: Actor
comments_via_trail?: Comment[] comments_via_trail?: Comment[]
gpx_data?: string gpx_data?: string
gpx?: GPX
trail_share_via_trail?: TrailShare[] trail_share_via_trail?: TrailShare[]
trail_like_via_trail?: TrailLike[] trail_like_via_trail?: TrailLike[]

View File

@@ -1,5 +1,5 @@
import GPX from "$lib/models/gpx/gpx"; import GPX from "$lib/models/gpx/gpx";
import type Track from "$lib/models/gpx/track"; import Track from "$lib/models/gpx/track";
import TrackSegment from "$lib/models/gpx/track-segment"; import TrackSegment from "$lib/models/gpx/track-segment";
import { haversineDistance } from "$lib/models/gpx/utils"; import { haversineDistance } from "$lib/models/gpx/utils";
import Waypoint from "$lib/models/gpx/waypoint"; import Waypoint from "$lib/models/gpx/waypoint";
@@ -11,7 +11,7 @@ import type { LngLat } from "maplibre-gl";
import { _ } from "svelte-i18n"; import { _ } from "svelte-i18n";
import { get } from "svelte/store"; import { get } from "svelte/store";
const emtpyTrack: Track = { trkseg: [] } const emtpyTrack = new Track({ trkseg: [] })
class ValhallaStore { class ValhallaStore {
route: GPX = $state(new GPX({ trk: [emtpyTrack] })); route: GPX = $state(new GPX({ trk: [emtpyTrack] }));
@@ -193,8 +193,8 @@ export function reverseRoute() {
} }
export function resetRoute() { export function resetRoute() {
const delta = diff(valhallaStore.route, new GPX({ trk: [{ ...emtpyTrack }] })); const delta = diff(valhallaStore.route, new GPX({ trk: [new Track({ ...emtpyTrack })] }));
const reverseDelta = diff(new GPX({ trk: [{ ...emtpyTrack }] }), valhallaStore.route); const reverseDelta = diff(new GPX({ trk: [new Track({ ...emtpyTrack })] }), valhallaStore.route);
valhallaStore.route = applyChangeset(valhallaStore.route, delta); valhallaStore.route = applyChangeset(valhallaStore.route, delta);
pushToUndoStack(delta, reverseDelta) pushToUndoStack(delta, reverseDelta)
@@ -214,8 +214,6 @@ export async function recalculateHeight() {
} }
export async function splitSegment(index: number, pos: LngLat) { export async function splitSegment(index: number, pos: LngLat) {
console.log(valhallaStore.route.features.duration);
let seg = valhallaStore.route.trk?.at(0)?.trkseg?.at(index); let seg = valhallaStore.route.trk?.at(0)?.trkseg?.at(index);
if (!seg || !seg.trkpt) { if (!seg || !seg.trkpt) {
return; return;

View File

@@ -18,9 +18,9 @@ import { handleFromRecordWithIRI } from "./activitypub_util";
import { Waypoint } from "$lib/models/waypoint"; import { Waypoint } from "$lib/models/waypoint";
export async function gpx2trail(gpxString: string, fallbackName?: string, f: (url: RequestInfo | URL, config?: RequestInit) => Promise<Response> = fetch) { export function gpx2trail(gpxString: string, fallbackName?: string, f: (url: RequestInfo | URL, config?: RequestInit) => Promise<Response> = fetch) {
const gpx = await GPX.parse(gpxString); const gpx = GPX.parse(gpxString);
if (gpx instanceof Error) { if (gpx instanceof Error) {
throw gpx; throw gpx;
@@ -87,7 +87,7 @@ export async function trail2gpx(trail: Trail, user?: AuthRecord) {
} }
} }
const gpx = await GPX.parse(gpxTrail.expand!.gpx_data!) as GPX; const gpx = GPX.parse(gpxTrail.expand!.gpx_data!);
if (gpx instanceof Error) { if (gpx instanceof Error) {
throw gpx; throw gpx;
@@ -108,12 +108,12 @@ export async function trail2gpx(trail: Trail, user?: AuthRecord) {
for (const wp of gpxTrail.expand!.waypoints ?? []) { for (const wp of gpxTrail.expand!.waypoints ?? []) {
const gpxWpt = gpx.wpt.find((w) => w.$.lat == wp.lat && w.$.lon == wp.lon) const gpxWpt = gpx.wpt.find((w) => w.$.lat == wp.lat && w.$.lon == wp.lon)
if (!gpxWpt) { if (!gpxWpt) {
gpx.wpt.push({ gpx.wpt.push(new GPXWaypoint({
$: { $: {
lat: wp.lat, lat: wp.lat,
lon: wp.lon lon: wp.lon
} }
}) }))
} }
} }
@@ -351,16 +351,6 @@ function isKMZFile(buffer: ArrayBuffer) {
return blob[0] === 0x50 && blob[1] === 0x4B && blob[2] === 0x03 && blob[3] === 0x04; return blob[0] === 0x50 && blob[1] === 0x4B && blob[2] === 0x03 && blob[3] === 0x04;
} }
export function toGeoJson(gpxData: string) {
const parser = browser ? new DOMParser() : new xmldom.DOMParser();
let geojson = gpx(
parser.parseFromString(gpxData, "text/xml"),
) as GeoJSON;
geojson = splitMultiLineStringToLineStrings(geojson);
geojson.bbox = bbox(geojson)
return geojson
}
export function cropGPX(start: GPXWaypoint, end: GPXWaypoint, gpx: GPX): GPX { export function cropGPX(start: GPXWaypoint, end: GPXWaypoint, gpx: GPX): GPX {
let foundStart = false; let foundStart = false;
let done = false; let done = false;
@@ -390,7 +380,7 @@ export function cropGPX(start: GPXWaypoint, end: GPXWaypoint, gpx: GPX): GPX {
} }
if (newPoints.length > 0) { if (newPoints.length > 0) {
croppedSegments.push({ trkpt: newPoints }); croppedSegments.push(new TrackSegment({ trkpt: newPoints }));
} }
}); });
@@ -400,5 +390,5 @@ export function cropGPX(start: GPXWaypoint, end: GPXWaypoint, gpx: GPX): GPX {
}; };
}).filter(track => track.trkseg.length > 0); }).filter(track => track.trkseg.length > 0);
return new GPX({ ...gpx, trk: croppedTrk ?? [], }) return new GPX({ ...gpx, trk: croppedTrk?.map(t => new Track({ ...t })) ?? [], })
} }

View File

@@ -21,7 +21,7 @@ export async function PUT(event: RequestEvent) {
} }
let parseResult: { trail: Trail, gpx: GPX }; let parseResult: { trail: Trail, gpx: GPX };
try { try {
parseResult = (await gpx2trail(gpxData, data.get("name") as string | undefined, event.fetch)); parseResult = gpx2trail(gpxData, data.get("name") as string | undefined, event.fetch);
} catch (e: any) { } catch (e: any) {
console.error(e) console.error(e)
throw new ClientResponseError({ status: 400, response: { message: "Invalid file" } }) throw new ClientResponseError({ status: 400, response: { message: "Invalid file" } })

View File

@@ -99,6 +99,8 @@
import { backInOut } from "svelte/easing"; import { backInOut } from "svelte/easing";
import { fly, slide } from "svelte/transition"; import { fly, slide } from "svelte/transition";
import { z } from "zod"; import { z } from "zod";
import Track from "$lib/models/gpx/track.js";
import TrackSegment from "$lib/models/gpx/track-segment.js";
let { data } = $props(); let { data } = $props();
@@ -202,6 +204,7 @@
photoFiles = [new File([blob], "route")]; photoFiles = [new File([blob], "route")];
} }
form.expand!.gpx_data = valhallaStore.route.toString();
if (form.expand!.gpx_data && overwriteGPX) { if (form.expand!.gpx_data && overwriteGPX) {
gpxFile = new Blob([form.expand!.gpx_data], { gpxFile = new Blob([form.expand!.gpx_data], {
type: "text/xml", type: "text/xml",
@@ -267,23 +270,25 @@
clearUndoRedoStack(); clearUndoRedoStack();
if ($formData.expand!.gpx_data) { if ($formData.expand!.gpx_data) {
const gpx = await GPX.parse($formData.expand!.gpx_data); const gpx = GPX.parse($formData.expand!.gpx_data);
if (!(gpx instanceof Error)) { if (!(gpx instanceof Error)) {
if (gpx.rte && !gpx.trk) { if (gpx.rte && !gpx.trk) {
gpx.trk = [ gpx.trk = [
{ new Track({
trkseg: [ trkseg: [
{ new TrackSegment({
trkpt: gpx.rte?.at(0)?.rtept, trkpt: gpx.rte?.at(0)?.rtept,
}, }),
], ],
}, }),
]; ];
gpx.rte = undefined; gpx.rte = undefined;
} }
setRoute(gpx); setRoute(gpx);
initRouteAnchors(gpx); initRouteAnchors(gpx);
updateTrailOnMap();
} }
} }
}); });
@@ -305,6 +310,7 @@
clearAnchors(); clearAnchors();
clearUndoRedoStack(); clearUndoRedoStack();
clearRoute(); clearRoute();
mapTrail = [];
drawingActive = false; drawingActive = false;
overwriteGPX = false; overwriteGPX = false;
@@ -313,10 +319,11 @@
try { try {
const prevId = $formData.id; const prevId = $formData.id;
const parseResult = await gpx2trail(gpxData, selectedFile.name); const parseResult = gpx2trail(gpxData, selectedFile.name);
setFields(parseResult.trail); setFields(parseResult.trail);
$formData.id = prevId ?? cryptoRandomString({ length: 15 }); $formData.id = prevId ?? cryptoRandomString({ length: 15 });
$formData.expand!.gpx_data = gpxData; $formData.expand!.gpx_data = gpxData;
setFields( setFields(
"category", "category",
page.data.settings.category || $categories[0].id, page.data.settings.category || $categories[0].id,
@@ -345,18 +352,20 @@
if (parseResult.gpx.rte?.length && !parseResult.gpx.trk) { if (parseResult.gpx.rte?.length && !parseResult.gpx.trk) {
parseResult.gpx.trk = [ parseResult.gpx.trk = [
{ new Track({
trkseg: [ trkseg: [
{ new TrackSegment({
trkpt: parseResult.gpx.rte?.at(0)?.rtept, trkpt: parseResult.gpx.rte?.at(0)?.rtept,
}, }),
], ],
}, }),
]; ];
parseResult.gpx.rte = undefined; parseResult.gpx.rte = undefined;
} }
setRoute(parseResult.gpx); setRoute(parseResult.gpx);
initRouteAnchors(parseResult.gpx); initRouteAnchors(parseResult.gpx);
updateTrailOnMap();
} catch (e) { } catch (e) {
console.error(e); console.error(e);
@@ -919,7 +928,6 @@
} }
function toggleCropMarkers(active: boolean) { function toggleCropMarkers(active: boolean) {
console.log("toggleCropMarkers", active);
if (active) { if (active) {
cropStartMarker?.setOpacity("1"); cropStartMarker?.setOpacity("1");
cropEndMarker?.setOpacity("1"); cropEndMarker?.setOpacity("1");
@@ -1035,8 +1043,8 @@
function updateTrailWithRouteData() { function updateTrailWithRouteData() {
overwriteGPX = true; overwriteGPX = true;
updateTotals(valhallaStore.route); updateTotals(valhallaStore.route);
$formData.expand!.gpx_data = valhallaStore.route.toString();
updateTrailOnMap();
if (!$formData.id) { if (!$formData.id) {
$formData.id = cryptoRandomString({ length: 15 }); $formData.id = cryptoRandomString({ length: 15 });
} }
@@ -1044,14 +1052,19 @@
function updateTotals(gpx: GPX) { function updateTotals(gpx: GPX) {
const totals = gpx.features; const totals = gpx.features;
$formData.distance = totals.distance; formData.set({
$formData.duration = totals.duration / 1000; ...$formData,
$formData.elevation_gain = totals.elevationGain; distance: totals.distance,
$formData.elevation_loss = totals.elevationLoss; duration: totals.duration / 1000,
elevation_gain: totals.elevationGain,
elevation_loss: totals.elevationLoss,
});
} }
function updateTrailOnMap() { function updateTrailOnMap() {
mapTrail = [$formData as Trail]; const t = { ...$formData } as Trail;
t.expand!.gpx = valhallaStore.route;
mapTrail = [t];
} }
function handleSearchClick(item: SearchItem) { function handleSearchClick(item: SearchItem) {
@@ -1071,12 +1084,6 @@
icon: getIconForLocation(h), icon: getIconForLocation(h),
})); }));
} }
let gpxData = $derived($formData.expand?.gpx_data);
$effect(() => {
if (gpxData) {
untrack(() => updateTrailOnMap());
}
});
function getTrailTags() { function getTrailTags() {
return ( return (