adds undo/redo to route edit
This commit is contained in:
17
web/package-lock.json
generated
17
web/package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "wanderer",
|
"name": "wanderer",
|
||||||
"version": "0.17.1",
|
"version": "0.17.2",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "wanderer",
|
"name": "wanderer",
|
||||||
"version": "0.17.1",
|
"version": "0.17.2",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@felte/validator-zod": "^1.0.18",
|
"@felte/validator-zod": "^1.0.18",
|
||||||
"@fortawesome/fontawesome-free": "^6.5.1",
|
"@fortawesome/fontawesome-free": "^6.5.1",
|
||||||
@@ -38,6 +38,7 @@
|
|||||||
"heic2any": "^0.0.4",
|
"heic2any": "^0.0.4",
|
||||||
"instead": "^1.0.3",
|
"instead": "^1.0.3",
|
||||||
"isomorphic-xml2js": "^0.1.3",
|
"isomorphic-xml2js": "^0.1.3",
|
||||||
|
"json-diff-ts": "^4.8.1",
|
||||||
"jspdf": "^2.5.1",
|
"jspdf": "^2.5.1",
|
||||||
"jszip": "^3.10.1",
|
"jszip": "^3.10.1",
|
||||||
"maplibre-gl": "^4.7.1",
|
"maplibre-gl": "^4.7.1",
|
||||||
@@ -2665,9 +2666,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/caniuse-lite": {
|
"node_modules/caniuse-lite": {
|
||||||
"version": "1.0.30001695",
|
"version": "1.0.30001727",
|
||||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001695.tgz",
|
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001727.tgz",
|
||||||
"integrity": "sha512-vHyLade6wTgI2u1ec3WQBxv+2BrTERV28UXQu9LO6lZ9pYeMk34vjXFLOxo1A4UBA8XTL4njRQZdno/yYaSmWw==",
|
"integrity": "sha512-pB68nIHmbN6L/4C6MH1DokyR3bYqFwjaSs/sWDHGj4CTcFtQUQMuJftVwWkXq7mNWOybD3KhUv3oWHoGxgP14Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -4303,6 +4304,12 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/json-diff-ts": {
|
||||||
|
"version": "4.8.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/json-diff-ts/-/json-diff-ts-4.8.1.tgz",
|
||||||
|
"integrity": "sha512-Bjs+7bgxkolosAL1n/29XJXCXByAVMdhARkRk32HpJ2IuCvZ/KpqT79ljlYZZRlcN6JE0ygNxRPR+mEMPQBshw==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/json-stringify-pretty-compact": {
|
"node_modules/json-stringify-pretty-compact": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/json-stringify-pretty-compact/-/json-stringify-pretty-compact-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/json-stringify-pretty-compact/-/json-stringify-pretty-compact-4.0.0.tgz",
|
||||||
|
|||||||
@@ -63,6 +63,7 @@
|
|||||||
"heic2any": "^0.0.4",
|
"heic2any": "^0.0.4",
|
||||||
"instead": "^1.0.3",
|
"instead": "^1.0.3",
|
||||||
"isomorphic-xml2js": "^0.1.3",
|
"isomorphic-xml2js": "^0.1.3",
|
||||||
|
"json-diff-ts": "^4.8.1",
|
||||||
"jspdf": "^2.5.1",
|
"jspdf": "^2.5.1",
|
||||||
"jszip": "^3.10.1",
|
"jszip": "^3.10.1",
|
||||||
"maplibre-gl": "^4.7.1",
|
"maplibre-gl": "^4.7.1",
|
||||||
|
|||||||
@@ -3,14 +3,15 @@
|
|||||||
RoutingOptions,
|
RoutingOptions,
|
||||||
ValhallaBicycleCostingOptions,
|
ValhallaBicycleCostingOptions,
|
||||||
} from "$lib/models/valhalla";
|
} from "$lib/models/valhalla";
|
||||||
|
import { valhallaStore } from "$lib/stores/valhalla_store.svelte";
|
||||||
import { formatSpeed } from "$lib/util/format_util";
|
import { formatSpeed } from "$lib/util/format_util";
|
||||||
import { _ } from "svelte-i18n";
|
import { _ } from "svelte-i18n";
|
||||||
import { slide } from "svelte/transition";
|
import { slide } from "svelte/transition";
|
||||||
|
import Button from "../base/button.svelte";
|
||||||
|
import DoubleSlider from "../base/double_slider.svelte";
|
||||||
import Select, { type SelectItem } from "../base/select.svelte";
|
import Select, { type SelectItem } from "../base/select.svelte";
|
||||||
import Slider from "../base/slider.svelte";
|
import Slider from "../base/slider.svelte";
|
||||||
import Toggle from "../base/toggle.svelte";
|
import Toggle from "../base/toggle.svelte";
|
||||||
import Button from "../base/button.svelte";
|
|
||||||
import DoubleSlider from "../base/double_slider.svelte";
|
|
||||||
interface Props {
|
interface Props {
|
||||||
options: RoutingOptions;
|
options: RoutingOptions;
|
||||||
onReverse: () => void;
|
onReverse: () => void;
|
||||||
@@ -19,6 +20,8 @@
|
|||||||
onUpdateCropRange: (data: [number, number]) => void;
|
onUpdateCropRange: (data: [number, number]) => void;
|
||||||
onCrop: () => void;
|
onCrop: () => void;
|
||||||
onRecalculateElevationData: () => void;
|
onRecalculateElevationData: () => void;
|
||||||
|
onUndo: () => void;
|
||||||
|
onRedo: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
let {
|
let {
|
||||||
@@ -29,6 +32,8 @@
|
|||||||
onUpdateCropRange,
|
onUpdateCropRange,
|
||||||
onCrop,
|
onCrop,
|
||||||
onRecalculateElevationData,
|
onRecalculateElevationData,
|
||||||
|
onUndo,
|
||||||
|
onRedo,
|
||||||
}: Props = $props();
|
}: Props = $props();
|
||||||
|
|
||||||
const modesOfTransport: SelectItem[] = [
|
const modesOfTransport: SelectItem[] = [
|
||||||
@@ -121,6 +126,7 @@
|
|||||||
recalculateElevationData = false;
|
recalculateElevationData = false;
|
||||||
crop = false;
|
crop = false;
|
||||||
editRoute = !editRoute;
|
editRoute = !editRoute;
|
||||||
|
onCropToggle(false);
|
||||||
}}><i class="fa fa-route text-sm"></i></button
|
}}><i class="fa fa-route text-sm"></i></button
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
@@ -142,8 +148,23 @@
|
|||||||
recalculateElevationData = !recalculateElevationData;
|
recalculateElevationData = !recalculateElevationData;
|
||||||
crop = false;
|
crop = false;
|
||||||
editRoute = false;
|
editRoute = false;
|
||||||
|
onCropToggle(false);
|
||||||
}}><i class="fa fa-mountain text-sm"></i></button
|
}}><i class="fa fa-mountain text-sm"></i></button
|
||||||
>
|
>
|
||||||
|
<button
|
||||||
|
class="btn-icon"
|
||||||
|
class:text-gray-500={valhallaStore.undoStack.length == 0}
|
||||||
|
disabled={valhallaStore.undoStack.length == 0}
|
||||||
|
aria-label="undo route action"
|
||||||
|
onclick={onUndo}><i class="fa fa-undo text-sm"></i></button
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
class="btn-icon"
|
||||||
|
class:text-gray-500={valhallaStore.redoStack.length == 0}
|
||||||
|
disabled={valhallaStore.redoStack.length == 0}
|
||||||
|
aria-label="redo route action"
|
||||||
|
onclick={onRedo}><i class="fa fa-redo text-sm"></i></button
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{#if editRoute}
|
{#if editRoute}
|
||||||
|
|||||||
@@ -6,21 +6,22 @@ import Waypoint from "$lib/models/gpx/waypoint";
|
|||||||
import { type RoutingOptions, type ValhallaAnchor, type ValhallaHeightResponse, type ValhallaRouteResponse } from "$lib/models/valhalla";
|
import { type RoutingOptions, type ValhallaAnchor, type ValhallaHeightResponse, type ValhallaRouteResponse } from "$lib/models/valhalla";
|
||||||
import { APIError } from "$lib/util/api_util";
|
import { APIError } from "$lib/util/api_util";
|
||||||
import { decodePolyline, encodePolyline } from "$lib/util/polyline_util";
|
import { decodePolyline, encodePolyline } from "$lib/util/polyline_util";
|
||||||
|
import { applyChangeset, diff, revertChangeset, type Changeset } from 'json-diff-ts';
|
||||||
import type { LngLat } from "maplibre-gl";
|
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: Track = { trkseg: [] }
|
||||||
|
|
||||||
class ValhallaStore {
|
class ValhallaStore {
|
||||||
route: GPX = $state(new GPX({ trk: [emtpyTrack] }));
|
route: GPX = $state(new GPX({ trk: [emtpyTrack] }));
|
||||||
anchors: ValhallaAnchor[] = $state([]);
|
anchors: ValhallaAnchor[] = $state([]);
|
||||||
|
undoStack: { delta: Changeset, reverseDelta: Changeset }[] = $state([]);
|
||||||
|
redoStack: { delta: Changeset, reverseDelta: Changeset }[] = $state([]);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const valhallaStore = new ValhallaStore();
|
export const valhallaStore = new ValhallaStore();
|
||||||
|
|
||||||
|
|
||||||
export function clearRoute() {
|
export function clearRoute() {
|
||||||
valhallaStore.route = new GPX({ trk: [emtpyTrack] });
|
valhallaStore.route = new GPX({ trk: [emtpyTrack] });
|
||||||
}
|
}
|
||||||
@@ -32,8 +33,25 @@ export function clearAnchors() {
|
|||||||
valhallaStore.anchors = [];
|
valhallaStore.anchors = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setRoute(newRoute: GPX) {
|
export function clearUndoRedoStack() {
|
||||||
valhallaStore.route = newRoute
|
valhallaStore.undoStack = []
|
||||||
|
valhallaStore.redoStack = []
|
||||||
|
}
|
||||||
|
|
||||||
|
function pushToUndoStack(delta: Changeset, reverseDelta: Changeset) {
|
||||||
|
valhallaStore.undoStack.push({ delta, reverseDelta })
|
||||||
|
valhallaStore.redoStack = []
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function setRoute(newRoute: GPX, undoable: boolean = false) {
|
||||||
|
const delta = diff(valhallaStore.route, newRoute);
|
||||||
|
const reverseDelta = diff(newRoute, valhallaStore.route);
|
||||||
|
valhallaStore.route = applyChangeset(valhallaStore.route, delta);
|
||||||
|
if (undoable) {
|
||||||
|
pushToUndoStack(delta, reverseDelta)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function calculateRouteBetween(startLat: number, startLon: number, endLat: number, endLon: number, options: RoutingOptions) {
|
export async function calculateRouteBetween(startLat: number, startLon: number, endLat: number, endLon: number, options: RoutingOptions) {
|
||||||
@@ -92,38 +110,67 @@ export async function calculateRouteBetween(startLat: number, startLon: number,
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function insertIntoRoute(waypoints: Waypoint[], index?: number) {
|
export async function insertIntoRoute(waypoints: Waypoint[], index?: number) {
|
||||||
|
const snapshot = new GPX({ ...valhallaStore.route })
|
||||||
const segment = new TrackSegment({ trkpt: waypoints })
|
const segment = new TrackSegment({ trkpt: waypoints })
|
||||||
|
|
||||||
if (index) {
|
if (index) {
|
||||||
valhallaStore.route.trk?.at(0)?.trkseg?.splice(index, 0, segment);
|
snapshot.trk?.at(0)?.trkseg?.splice(index, 0, segment);
|
||||||
} else {
|
} else {
|
||||||
valhallaStore.route.trk?.at(0)?.trkseg?.push(segment);
|
snapshot.trk?.at(0)?.trkseg?.push(segment);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const delta = diff(valhallaStore.route, snapshot);
|
||||||
|
const reverseDelta = diff(snapshot, valhallaStore.route);
|
||||||
|
valhallaStore.route = applyChangeset(valhallaStore.route, delta);
|
||||||
|
pushToUndoStack(delta, reverseDelta)
|
||||||
|
|
||||||
valhallaStore.route.features = valhallaStore.route.getTotals();
|
valhallaStore.route.features = valhallaStore.route.getTotals();
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function editRoute(index: number, waypoints: Waypoint[]) {
|
export async function editRoute(index: number, waypoints: Waypoint[]) {
|
||||||
const segment = valhallaStore.route.trk?.at(0)?.trkseg?.at(index)
|
const snapshot = new GPX({ ...valhallaStore.route })
|
||||||
|
|
||||||
|
const segment = snapshot.trk?.at(0)?.trkseg?.at(index)
|
||||||
if (segment) {
|
if (segment) {
|
||||||
segment.trkpt = waypoints
|
segment.trkpt = waypoints
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const delta = diff(valhallaStore.route, snapshot);
|
||||||
|
const reverseDelta = diff(snapshot, valhallaStore.route)
|
||||||
|
valhallaStore.route = applyChangeset(valhallaStore.route, delta);
|
||||||
|
pushToUndoStack(delta, reverseDelta)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
valhallaStore.route.features = valhallaStore.route.getTotals();
|
valhallaStore.route.features = valhallaStore.route.getTotals();
|
||||||
}
|
}
|
||||||
|
|
||||||
export function deleteFromRoute(index: number) {
|
export function deleteFromRoute(index: number) {
|
||||||
valhallaStore.route.trk?.at(0)?.trkseg?.splice(index, 1);
|
const snapshot = new GPX({ ...valhallaStore.route })
|
||||||
valhallaStore.route.features = valhallaStore.route.getTotals();
|
|
||||||
|
snapshot.trk?.at(0)?.trkseg?.splice(index, 1);
|
||||||
|
snapshot.features = valhallaStore.route.getTotals();
|
||||||
|
|
||||||
|
const delta = diff(valhallaStore.route, snapshot);
|
||||||
|
const reverseDelta = diff(snapshot, valhallaStore.route)
|
||||||
|
valhallaStore.route = applyChangeset(valhallaStore.route, delta);
|
||||||
|
pushToUndoStack(delta, reverseDelta)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function reverseRoute() {
|
export function reverseRoute() {
|
||||||
for (const trk of valhallaStore.route.trk ?? []) {
|
const snapshot = new GPX({ ...valhallaStore.route })
|
||||||
|
for (const trk of snapshot.trk ?? []) {
|
||||||
for (const seg of trk.trkseg ?? []) {
|
for (const seg of trk.trkseg ?? []) {
|
||||||
seg.trkpt?.reverse()
|
seg.trkpt?.reverse()
|
||||||
}
|
}
|
||||||
trk.trkseg?.reverse()
|
trk.trkseg?.reverse()
|
||||||
}
|
}
|
||||||
valhallaStore.route.trk?.reverse()
|
snapshot.trk?.reverse()
|
||||||
|
|
||||||
|
const delta = diff(valhallaStore.route, snapshot);
|
||||||
|
const reverseDelta = diff(snapshot, valhallaStore.route);
|
||||||
|
valhallaStore.route = applyChangeset(valhallaStore.route, delta);
|
||||||
|
pushToUndoStack(delta, reverseDelta)
|
||||||
|
|
||||||
valhallaStore.route.features = valhallaStore.route.getTotals();
|
valhallaStore.route.features = valhallaStore.route.getTotals();
|
||||||
|
|
||||||
@@ -146,7 +193,10 @@ export function reverseRoute() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function resetRoute() {
|
export function resetRoute() {
|
||||||
valhallaStore.route = new GPX({ trk: [{ ...emtpyTrack }] });
|
const delta = diff(valhallaStore.route, new GPX({ trk: [{ ...emtpyTrack }] }));
|
||||||
|
const reverseDelta = diff(new GPX({ trk: [{ ...emtpyTrack }] }), valhallaStore.route);
|
||||||
|
valhallaStore.route = applyChangeset(valhallaStore.route, delta);
|
||||||
|
pushToUndoStack(delta, reverseDelta)
|
||||||
|
|
||||||
valhallaStore.anchors.forEach((a) => {
|
valhallaStore.anchors.forEach((a) => {
|
||||||
if (!a.marker) {
|
if (!a.marker) {
|
||||||
@@ -209,3 +259,25 @@ export function normalizeRouteTime() {
|
|||||||
currentTime = new Date(seg.trkpt[seg.trkpt.length - 1].time!.getTime());
|
currentTime = new Date(seg.trkpt[seg.trkpt.length - 1].time!.getTime());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function undo() {
|
||||||
|
const historyItem = valhallaStore.undoStack.pop()
|
||||||
|
if (!historyItem) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
valhallaStore.redoStack.push(historyItem)
|
||||||
|
|
||||||
|
valhallaStore.route = applyChangeset(valhallaStore.route, historyItem.reverseDelta);
|
||||||
|
valhallaStore.route.features = valhallaStore.route.getTotals();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function redo() {
|
||||||
|
const historyItem = valhallaStore.redoStack.pop()
|
||||||
|
if (!historyItem) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
valhallaStore.undoStack.push(historyItem)
|
||||||
|
|
||||||
|
valhallaStore.route = applyChangeset(valhallaStore.route, historyItem.delta);
|
||||||
|
valhallaStore.route.features = valhallaStore.route.getTotals();
|
||||||
|
}
|
||||||
|
|||||||
@@ -48,6 +48,9 @@
|
|||||||
reverseRoute,
|
reverseRoute,
|
||||||
setRoute,
|
setRoute,
|
||||||
splitSegment,
|
splitSegment,
|
||||||
|
undo,
|
||||||
|
redo,
|
||||||
|
clearUndoRedoStack,
|
||||||
} from "$lib/stores/valhalla_store.svelte.js";
|
} from "$lib/stores/valhalla_store.svelte.js";
|
||||||
import { waypoint } from "$lib/stores/waypoint_store";
|
import { waypoint } from "$lib/stores/waypoint_store";
|
||||||
import { getFileURL } from "$lib/util/file_util";
|
import { getFileURL } from "$lib/util/file_util";
|
||||||
@@ -125,8 +128,6 @@
|
|||||||
let cropStartMarker: FontawesomeMarker;
|
let cropStartMarker: FontawesomeMarker;
|
||||||
let cropEndMarker: FontawesomeMarker;
|
let cropEndMarker: FontawesomeMarker;
|
||||||
|
|
||||||
let flatRoute: GPXWaypoint[] = $derived(valhallaStore.route.flatten());
|
|
||||||
|
|
||||||
let croppedGPX: GPX | null = null;
|
let croppedGPX: GPX | null = null;
|
||||||
|
|
||||||
const ClientTrailCreateSchema = TrailCreateSchema.extend({
|
const ClientTrailCreateSchema = TrailCreateSchema.extend({
|
||||||
@@ -261,8 +262,9 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
clearAnchorMarker();
|
clearAnchors();
|
||||||
clearRoute();
|
clearRoute();
|
||||||
|
clearUndoRedoStack();
|
||||||
|
|
||||||
if ($formData.expand!.gpx_data) {
|
if ($formData.expand!.gpx_data) {
|
||||||
const gpx = await GPX.parse($formData.expand!.gpx_data);
|
const gpx = await GPX.parse($formData.expand!.gpx_data);
|
||||||
@@ -300,7 +302,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
clearWaypoints();
|
clearWaypoints();
|
||||||
clearAnchorMarker();
|
clearAnchors();
|
||||||
|
clearUndoRedoStack();
|
||||||
clearRoute();
|
clearRoute();
|
||||||
drawingActive = false;
|
drawingActive = false;
|
||||||
overwriteGPX = false;
|
overwriteGPX = false;
|
||||||
@@ -354,7 +357,6 @@
|
|||||||
}
|
}
|
||||||
setRoute(parseResult.gpx);
|
setRoute(parseResult.gpx);
|
||||||
initRouteAnchors(parseResult.gpx);
|
initRouteAnchors(parseResult.gpx);
|
||||||
initCropMarkers();
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
|
|
||||||
@@ -380,10 +382,6 @@
|
|||||||
$formData.waypoints = [];
|
$formData.waypoints = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
function clearAnchorMarker() {
|
|
||||||
clearAnchors();
|
|
||||||
}
|
|
||||||
|
|
||||||
function initRouteAnchors(gpx: GPX, addToMap: boolean = false) {
|
function initRouteAnchors(gpx: GPX, addToMap: boolean = false) {
|
||||||
const segments = gpx.trk?.at(0)?.trkseg ?? [];
|
const segments = gpx.trk?.at(0)?.trkseg ?? [];
|
||||||
|
|
||||||
@@ -410,56 +408,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function initCropMarkers() {
|
|
||||||
const routeStartPoint: M.LngLatLike = [
|
|
||||||
valhallaStore.route.trk?.at(0)?.trkseg?.at(0)?.trkpt?.at(0)?.$
|
|
||||||
.lon ?? 0,
|
|
||||||
valhallaStore.route.trk?.at(0)?.trkseg?.at(0)?.trkpt?.at(0)?.$
|
|
||||||
.lat ?? 0,
|
|
||||||
];
|
|
||||||
const routeEndPoint: M.LngLatLike = [
|
|
||||||
valhallaStore.route.trk?.at(-1)?.trkseg?.at(-1)?.trkpt?.at(-1)?.$
|
|
||||||
.lon ?? 0,
|
|
||||||
valhallaStore.route.trk?.at(-1)?.trkseg?.at(-1)?.trkpt?.at(-1)?.$
|
|
||||||
.lat ?? 0,
|
|
||||||
];
|
|
||||||
if (!cropStartMarker || !cropEndMarker) {
|
|
||||||
cropStartMarker = new FontawesomeMarker(
|
|
||||||
{
|
|
||||||
id: "crop-start-marker",
|
|
||||||
icon: "fa-regular fa-circle",
|
|
||||||
fontSize: "xs",
|
|
||||||
style: "w-6",
|
|
||||||
width: 4,
|
|
||||||
backgroundColor: "bg-primary",
|
|
||||||
fontColor: "white",
|
|
||||||
},
|
|
||||||
{},
|
|
||||||
);
|
|
||||||
cropEndMarker = new FontawesomeMarker(
|
|
||||||
{
|
|
||||||
id: "crop-end-marker",
|
|
||||||
icon: "fa fa-flag-checkered",
|
|
||||||
fontSize: "xs",
|
|
||||||
style: "w-6",
|
|
||||||
width: 4,
|
|
||||||
backgroundColor: "bg-primary",
|
|
||||||
fontColor: "white",
|
|
||||||
},
|
|
||||||
{},
|
|
||||||
);
|
|
||||||
|
|
||||||
cropStartMarker
|
|
||||||
.setOpacity("0")
|
|
||||||
.setLngLat(routeStartPoint)
|
|
||||||
.addTo(map!);
|
|
||||||
cropEndMarker.setOpacity("0").setLngLat(routeEndPoint).addTo(map!);
|
|
||||||
} else {
|
|
||||||
cropStartMarker.setLngLat(routeStartPoint);
|
|
||||||
cropEndMarker.setLngLat(routeEndPoint);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function openMarkerPopup(waypoint: Waypoint) {
|
function openMarkerPopup(waypoint: Waypoint) {
|
||||||
waypoint.marker?.togglePopup();
|
waypoint.marker?.togglePopup();
|
||||||
}
|
}
|
||||||
@@ -600,6 +548,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function startDrawing() {
|
function startDrawing() {
|
||||||
|
console.log("starting drawing...");
|
||||||
if (!map) {
|
if (!map) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -612,28 +561,32 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function stopDrawing() {
|
async function stopDrawing() {
|
||||||
|
console.log("stopping drawing...");
|
||||||
|
|
||||||
drawingActive = false;
|
drawingActive = false;
|
||||||
for (const anchor of valhallaStore.anchors) {
|
// for (const anchor of valhallaStore.anchors) {
|
||||||
anchor.marker?.remove();
|
// anchor.marker?.remove();
|
||||||
}
|
// }
|
||||||
toggleCropMarkers(false);
|
// toggleCropMarkers(false);
|
||||||
|
// clearUndoRedoStack();
|
||||||
|
|
||||||
if (valhallaStore.route.trk?.at(0)?.trkseg?.at(0)?.trkpt?.at(0)) {
|
// if (valhallaStore.route.trk?.at(0)?.trkseg?.at(0)?.trkpt?.at(0)) {
|
||||||
$formData.lat = valhallaStore.route.trk
|
// $formData.lat = valhallaStore.route.trk
|
||||||
?.at(0)
|
// ?.at(0)
|
||||||
?.trkseg?.at(0)
|
// ?.trkseg?.at(0)
|
||||||
?.trkpt?.at(0)?.$.lat;
|
// ?.trkpt?.at(0)?.$.lat;
|
||||||
$formData.lon = valhallaStore.route.trk
|
// $formData.lon = valhallaStore.route.trk
|
||||||
?.at(0)
|
// ?.at(0)
|
||||||
?.trkseg?.at(0)
|
// ?.trkseg?.at(0)
|
||||||
?.trkpt?.at(0)?.$.lon;
|
// ?.trkpt?.at(0)?.$.lon;
|
||||||
}
|
// }
|
||||||
|
|
||||||
const r = await searchLocationReverse($formData.lat!, $formData.lon!);
|
// if ($formData.lat && $formData.lon) {
|
||||||
|
// const r = await searchLocationReverse($formData.lat, $formData.lon);
|
||||||
if (r) {
|
// if (r) {
|
||||||
setFields("location", r);
|
// setFields("location", r);
|
||||||
}
|
// }
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleMapClick(e: M.MapMouseEvent) {
|
async function handleMapClick(e: M.MapMouseEvent) {
|
||||||
@@ -984,8 +937,40 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function updateCropMarkers(range: [start: number, end: number]) {
|
function updateCropMarkers(range: [start: number, end: number]) {
|
||||||
|
if (!cropStartMarker || !cropEndMarker) {
|
||||||
|
cropStartMarker = new FontawesomeMarker(
|
||||||
|
{
|
||||||
|
id: "crop-start-marker",
|
||||||
|
icon: "fa-regular fa-circle",
|
||||||
|
fontSize: "xs",
|
||||||
|
style: "w-6",
|
||||||
|
width: 4,
|
||||||
|
backgroundColor: "bg-primary",
|
||||||
|
fontColor: "white",
|
||||||
|
},
|
||||||
|
{},
|
||||||
|
);
|
||||||
|
cropEndMarker = new FontawesomeMarker(
|
||||||
|
{
|
||||||
|
id: "crop-end-marker",
|
||||||
|
icon: "fa fa-flag-checkered",
|
||||||
|
fontSize: "xs",
|
||||||
|
style: "w-6",
|
||||||
|
width: 4,
|
||||||
|
backgroundColor: "bg-primary",
|
||||||
|
fontColor: "white",
|
||||||
|
},
|
||||||
|
{},
|
||||||
|
);
|
||||||
|
|
||||||
|
cropStartMarker.setLngLat([0, 0]).addTo(map!);
|
||||||
|
cropEndMarker.setLngLat([0, 0]).addTo(map!);
|
||||||
|
}
|
||||||
|
|
||||||
const [start, end] = range;
|
const [start, end] = range;
|
||||||
|
|
||||||
|
const flatRoute = valhallaStore.route.flatten();
|
||||||
|
|
||||||
const targetStartDistance =
|
const targetStartDistance =
|
||||||
valhallaStore.route.features.distance * (start / 100);
|
valhallaStore.route.features.distance * (start / 100);
|
||||||
const [startLon, startLat, startIndex] = getCoordinateAtDistance(
|
const [startLon, startLat, startIndex] = getCoordinateAtDistance(
|
||||||
@@ -1021,9 +1006,9 @@
|
|||||||
if (!croppedGPX) {
|
if (!croppedGPX) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setRoute(croppedGPX);
|
setRoute(croppedGPX, true);
|
||||||
updateTrailWithRouteData();
|
updateTrailWithRouteData();
|
||||||
clearAnchorMarker();
|
clearAnchors();
|
||||||
initRouteAnchors(croppedGPX, true);
|
initRouteAnchors(croppedGPX, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1167,6 +1152,20 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function undoRouteEdit() {
|
||||||
|
undo();
|
||||||
|
updateTrailWithRouteData();
|
||||||
|
clearAnchors();
|
||||||
|
initRouteAnchors(valhallaStore.route, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function redoRouteEdit() {
|
||||||
|
redo();
|
||||||
|
updateTrailWithRouteData();
|
||||||
|
clearAnchors();
|
||||||
|
initRouteAnchors(valhallaStore.route, true);
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
@@ -1488,6 +1487,8 @@
|
|||||||
onCrop={confirmCrop}
|
onCrop={confirmCrop}
|
||||||
onUpdateCropRange={updateCropMarkers}
|
onUpdateCropRange={updateCropMarkers}
|
||||||
onRecalculateElevationData={recalculateElevationData}
|
onRecalculateElevationData={recalculateElevationData}
|
||||||
|
onUndo={undoRouteEdit}
|
||||||
|
onRedo={redoRouteEdit}
|
||||||
></RouteEditor>
|
></RouteEditor>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|||||||
Reference in New Issue
Block a user