Add split export: Export Route vs Export Plan

Split button with default "Export GPX" (track only) and dropdown:
- Export Route: clean track for use in any app
- Export Plan: track + waypoints + no-go areas in GPX extensions
  (trails:planning namespace) for reimporting into the planner

Also:
- Add no-go area serialization to generateGpx (GPX extensions)
- Parse no-go areas from GPX extensions on import
- Initialize no-go areas in Yjs session from imported GPX
- Save to Journal now exports track only (consistent with Export Route)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Ullrich Schäfer 2026-04-03 10:52:39 +01:00
parent 237660db5e
commit 4b711abaec
No known key found for this signature in database
GPG key ID: A32FF691A0F752D9
11 changed files with 172 additions and 60 deletions

View file

@ -1,5 +1,5 @@
import type { Waypoint } from "@trails-cool/types";
import type { GpxData, TrackPoint, ElevationProfile } from "./types.ts";
import type { GpxData, TrackPoint, ElevationProfile, NoGoArea } from "./types.ts";
/**
* Parse a GPX XML string into structured data.
@ -46,9 +46,10 @@ function parseGpxWithParser(parser: DOMParser, xml: string): GpxData {
const name = doc.querySelector("metadata > name")?.textContent ?? undefined;
const waypoints = parseWaypoints(doc);
const tracks = parseTracks(doc);
const noGoAreas = parseNoGoAreas(doc);
const { totalDistance, ...elevation } = computeElevation(tracks);
return { name, waypoints, tracks, distance: totalDistance, elevation };
return { name, waypoints, tracks, noGoAreas, distance: totalDistance, elevation };
}
function parseWaypoints(doc: Document): Waypoint[] {
@ -85,6 +86,22 @@ function parseTracks(doc: Document): TrackPoint[][] {
return tracks;
}
function parseNoGoAreas(doc: Document): NoGoArea[] {
const areas: NoGoArea[] = [];
const nogos = doc.querySelectorAll("nogo");
for (const nogo of Array.from(nogos)) {
const points: Array<{ lat: number; lon: number }> = [];
const pts = nogo.querySelectorAll("point");
for (const pt of Array.from(pts)) {
const lat = parseFloat(pt.getAttribute("lat") ?? "0");
const lon = parseFloat(pt.getAttribute("lon") ?? "0");
points.push({ lat, lon });
}
if (points.length >= 3) areas.push({ points });
}
return areas;
}
function computeElevation(tracks: TrackPoint[][]): GpxData["elevation"] & { totalDistance: number } {
let gain = 0;
let loss = 0;