adds route edit menu
This commit is contained in:
@@ -9,6 +9,7 @@
|
|||||||
currentMin?: any;
|
currentMin?: any;
|
||||||
currentMax?: any;
|
currentMax?: any;
|
||||||
onset?: (data: [number, number]) => void;
|
onset?: (data: [number, number]) => void;
|
||||||
|
onupdate?: (data: [number, number]) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
let {
|
let {
|
||||||
@@ -17,6 +18,7 @@
|
|||||||
currentMin = $bindable(minValue),
|
currentMin = $bindable(minValue),
|
||||||
currentMax = $bindable(maxValue),
|
currentMax = $bindable(maxValue),
|
||||||
onset,
|
onset,
|
||||||
|
onupdate,
|
||||||
}: Props = $props();
|
}: Props = $props();
|
||||||
|
|
||||||
let sliderContainer: any = $state();
|
let sliderContainer: any = $state();
|
||||||
@@ -25,6 +27,7 @@
|
|||||||
const updateValues = (values: string[]) => {
|
const updateValues = (values: string[]) => {
|
||||||
currentMin = parseFloat(values[0]);
|
currentMin = parseFloat(values[0]);
|
||||||
currentMax = parseFloat(values[1]);
|
currentMax = parseFloat(values[1]);
|
||||||
|
onupdate?.([currentMin, currentMax]);
|
||||||
};
|
};
|
||||||
|
|
||||||
noUiSlider.create(sliderContainer, {
|
noUiSlider.create(sliderContainer, {
|
||||||
|
|||||||
@@ -716,6 +716,7 @@
|
|||||||
if (!map) {
|
if (!map) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
hideWaypoints();
|
||||||
activeTrail ??= 0;
|
activeTrail ??= 0;
|
||||||
map.getCanvas().style.cursor = "crosshair";
|
map.getCanvas().style.cursor = "crosshair";
|
||||||
if (trails[activeTrail]) {
|
if (trails[activeTrail]) {
|
||||||
@@ -727,6 +728,7 @@
|
|||||||
if (!map) {
|
if (!map) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
showWaypoints();
|
||||||
map.getCanvas().style.cursor = "inherit";
|
map.getCanvas().style.cursor = "inherit";
|
||||||
|
|
||||||
if (activeTrail !== null && trails[activeTrail] && !clusterTrails) {
|
if (activeTrail !== null && trails[activeTrail] && !clusterTrails) {
|
||||||
|
|||||||
411
web/src/lib/components/trail/route_editor.svelte
Normal file
411
web/src/lib/components/trail/route_editor.svelte
Normal file
@@ -0,0 +1,411 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import type {
|
||||||
|
RoutingOptions,
|
||||||
|
ValhallaBicycleCostingOptions,
|
||||||
|
} from "$lib/models/valhalla";
|
||||||
|
import { formatSpeed } from "$lib/util/format_util";
|
||||||
|
import { _ } from "svelte-i18n";
|
||||||
|
import { slide } from "svelte/transition";
|
||||||
|
import Select, { type SelectItem } from "../base/select.svelte";
|
||||||
|
import Slider from "../base/slider.svelte";
|
||||||
|
import Toggle from "../base/toggle.svelte";
|
||||||
|
import Button from "../base/button.svelte";
|
||||||
|
import DoubleSlider from "../base/double_slider.svelte";
|
||||||
|
interface Props {
|
||||||
|
options: RoutingOptions;
|
||||||
|
onReverse: () => void;
|
||||||
|
onReset: () => void;
|
||||||
|
onCropToggle: (active: boolean) => void;
|
||||||
|
onUpdateCropRange: (data: [number, number]) => void;
|
||||||
|
onCrop: () => void;
|
||||||
|
onRecalculateElevationData: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
let {
|
||||||
|
options = $bindable(),
|
||||||
|
onReverse,
|
||||||
|
onReset,
|
||||||
|
onCropToggle,
|
||||||
|
onUpdateCropRange,
|
||||||
|
onCrop,
|
||||||
|
onRecalculateElevationData,
|
||||||
|
}: Props = $props();
|
||||||
|
|
||||||
|
const modesOfTransport: SelectItem[] = [
|
||||||
|
{ text: $_("hiking"), value: "pedestrian" },
|
||||||
|
{ text: $_("cycling"), value: "bicycle" },
|
||||||
|
{ text: $_("driving"), value: "auto" },
|
||||||
|
];
|
||||||
|
|
||||||
|
const bikeTypes: SelectItem[] = [
|
||||||
|
{ text: $_("hybrid"), value: "Hybrid" },
|
||||||
|
{ text: $_("road"), value: "Road" },
|
||||||
|
{ text: $_("cross"), value: "Cross" },
|
||||||
|
{ text: $_("mountain"), value: "Mountain" },
|
||||||
|
];
|
||||||
|
|
||||||
|
if (!options.pedestrianOptions) {
|
||||||
|
options.pedestrianOptions = {
|
||||||
|
max_hiking_difficulty: 6,
|
||||||
|
walking_speed: 5.1,
|
||||||
|
use_hills: 1,
|
||||||
|
shortest: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!options.bicycleOptions) {
|
||||||
|
options.bicycleOptions = {
|
||||||
|
bicycle_type: "Hybrid",
|
||||||
|
cycling_speed: 20,
|
||||||
|
use_roads: 0.5,
|
||||||
|
use_hills: 0.5,
|
||||||
|
avoid_bad_surfaces: 0.25,
|
||||||
|
shortest: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!options.autoOptions) {
|
||||||
|
options.autoOptions = {
|
||||||
|
width: 1.6,
|
||||||
|
height: 1.9,
|
||||||
|
top_speed: 140,
|
||||||
|
fixed_speed: 0,
|
||||||
|
shortest: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
$effect(() => {
|
||||||
|
if (!options.autoRouting) {
|
||||||
|
showSettings = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let showSettings = $state(false);
|
||||||
|
let editRoute = $state(false);
|
||||||
|
let crop = $state(false);
|
||||||
|
let recalculateElevationData = $state(false);
|
||||||
|
|
||||||
|
// svelte-ignore non_reactive_update
|
||||||
|
let cycleSpeedSlider: Slider;
|
||||||
|
|
||||||
|
function adjustSpeeddependingOnBikeType(
|
||||||
|
type: ValhallaBicycleCostingOptions["bicycle_type"],
|
||||||
|
) {
|
||||||
|
switch (type) {
|
||||||
|
case "City":
|
||||||
|
case "Hybrid":
|
||||||
|
cycleSpeedSlider?.set(18);
|
||||||
|
break;
|
||||||
|
case "Road":
|
||||||
|
cycleSpeedSlider?.set(25);
|
||||||
|
break;
|
||||||
|
case "Cross":
|
||||||
|
cycleSpeedSlider?.set(20);
|
||||||
|
break;
|
||||||
|
case "Mountain":
|
||||||
|
cycleSpeedSlider?.set(16);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="flex gap-x-2 items-start">
|
||||||
|
<div class="flex flex-col gap-y-1 p-1 bg-background rounded-md my-2">
|
||||||
|
<button
|
||||||
|
class="btn-icon"
|
||||||
|
class:bg-secondary-hover={editRoute}
|
||||||
|
aria-label="edit route"
|
||||||
|
onclick={() => {
|
||||||
|
recalculateElevationData = false;
|
||||||
|
crop = false;
|
||||||
|
editRoute = !editRoute;
|
||||||
|
}}><i class="fa fa-pen text-sm"></i></button
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
class="btn-icon"
|
||||||
|
class:bg-secondary-hover={crop}
|
||||||
|
aria-label="crop route"
|
||||||
|
onclick={() => {
|
||||||
|
recalculateElevationData = false;
|
||||||
|
crop = !crop;
|
||||||
|
editRoute = false;
|
||||||
|
onCropToggle(crop);
|
||||||
|
}}><i class="fa fa-scissors text-sm"></i></button
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
class="btn-icon"
|
||||||
|
class:bg-secondary-hover={recalculateElevationData}
|
||||||
|
aria-label="recalculate elevation data"
|
||||||
|
onclick={() => {
|
||||||
|
recalculateElevationData = !recalculateElevationData;
|
||||||
|
crop = false;
|
||||||
|
editRoute = false;
|
||||||
|
}}><i class="fa fa-mountain text-sm"></i></button
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{#if editRoute}
|
||||||
|
<div class=" pt-2 pb-3 px-4 my-2 rounded-xl bg-background shadow-xl">
|
||||||
|
<Toggle
|
||||||
|
bind:value={options.autoRouting}
|
||||||
|
label={$_("enable-auto-routing")}
|
||||||
|
></Toggle>
|
||||||
|
<Select
|
||||||
|
items={modesOfTransport}
|
||||||
|
bind:value={options.modeOfTransport}
|
||||||
|
disabled={!options.autoRouting}
|
||||||
|
label={$_("activity", { values: { n: 1 } })}
|
||||||
|
></Select>
|
||||||
|
<div class="flex items-center gap-4 mt-4">
|
||||||
|
<button
|
||||||
|
class="btn-icon tooltip"
|
||||||
|
type="button"
|
||||||
|
onclick={() => onReverse()}
|
||||||
|
aria-label="Reverse trail direction"
|
||||||
|
data-title={$_("reverse-direction")}
|
||||||
|
><i class="fa fa-arrow-right-arrow-left"></i></button
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
class="btn-icon tooltip"
|
||||||
|
type="button"
|
||||||
|
onclick={() => onReset()}
|
||||||
|
aria-label="Reset route"
|
||||||
|
data-title={$_("reset")}><i class="fa fa-trash"></i></button
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
class="btn-icon tooltip"
|
||||||
|
type="button"
|
||||||
|
disabled={!options.autoRouting}
|
||||||
|
onclick={() => (showSettings = !showSettings)}
|
||||||
|
data-title={$_("more-route-settings")}
|
||||||
|
aria-label="Toggle routing settings"
|
||||||
|
><i
|
||||||
|
class="fa fa-cogs"
|
||||||
|
class:text-gray-500={!options.autoRouting}
|
||||||
|
></i></button
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
{#if showSettings}
|
||||||
|
<div in:slide out:slide>
|
||||||
|
{#if options.modeOfTransport === "pedestrian" && options.pedestrianOptions}
|
||||||
|
<p class="text-sm font-medium pb-1">
|
||||||
|
{$_("walking-speed")}
|
||||||
|
</p>
|
||||||
|
<Slider
|
||||||
|
minValue={0.5}
|
||||||
|
maxValue={25}
|
||||||
|
bind:currentValue={
|
||||||
|
options.pedestrianOptions.walking_speed
|
||||||
|
}
|
||||||
|
></Slider>
|
||||||
|
<p class="text-sm text-end">
|
||||||
|
{formatSpeed(
|
||||||
|
options.pedestrianOptions.walking_speed! / 3.6,
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
|
<hr class="border-input-border my-3" />
|
||||||
|
<p class="text-sm font-medium">{$_("use-hills")}</p>
|
||||||
|
<Slider
|
||||||
|
minValue={0}
|
||||||
|
maxValue={1}
|
||||||
|
step={0.1}
|
||||||
|
bind:currentValue={
|
||||||
|
options.pedestrianOptions!.use_hills
|
||||||
|
}
|
||||||
|
></Slider>
|
||||||
|
<p class="text-sm text-end">
|
||||||
|
{options.pedestrianOptions.use_hills?.toFixed(2)}
|
||||||
|
</p>
|
||||||
|
<hr class="border-input-border my-3" />
|
||||||
|
<p class="text-sm font-medium">
|
||||||
|
{$_("max-hiking-difficulty")}
|
||||||
|
</p>
|
||||||
|
<Slider
|
||||||
|
minValue={0}
|
||||||
|
maxValue={6}
|
||||||
|
step={1}
|
||||||
|
bind:currentValue={
|
||||||
|
options.pedestrianOptions!.max_hiking_difficulty
|
||||||
|
}
|
||||||
|
></Slider>
|
||||||
|
<p class="text-sm text-end">
|
||||||
|
{options.pedestrianOptions.max_hiking_difficulty?.toFixed(
|
||||||
|
0,
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
|
<hr class="border-input-border my-3" />
|
||||||
|
<Toggle
|
||||||
|
label={$_("shortest")}
|
||||||
|
bind:value={options.pedestrianOptions.shortest}
|
||||||
|
></Toggle>
|
||||||
|
{:else if options.modeOfTransport === "bicycle" && options.bicycleOptions}
|
||||||
|
<Select
|
||||||
|
items={bikeTypes}
|
||||||
|
label={$_("bike-type")}
|
||||||
|
onchange={(v) => adjustSpeeddependingOnBikeType(v)}
|
||||||
|
bind:value={options.bicycleOptions.bicycle_type}
|
||||||
|
></Select>
|
||||||
|
<hr class="border-input-border my-3" />
|
||||||
|
<p class="text-sm font-medium pb-1">
|
||||||
|
{$_("cycling-speed")}
|
||||||
|
</p>
|
||||||
|
<Slider
|
||||||
|
minValue={5}
|
||||||
|
maxValue={50}
|
||||||
|
bind:currentValue={
|
||||||
|
options.bicycleOptions.cycling_speed
|
||||||
|
}
|
||||||
|
bind:this={cycleSpeedSlider}
|
||||||
|
></Slider>
|
||||||
|
<p class="text-sm text-end">
|
||||||
|
{formatSpeed(
|
||||||
|
options.bicycleOptions.cycling_speed! / 3.6,
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
|
<hr class="border-input-border my-3" />
|
||||||
|
<p class="text-sm font-medium">{$_("use-hills")}</p>
|
||||||
|
<Slider
|
||||||
|
minValue={0}
|
||||||
|
maxValue={1}
|
||||||
|
step={0.05}
|
||||||
|
bind:currentValue={options.bicycleOptions.use_hills}
|
||||||
|
></Slider>
|
||||||
|
<p class="text-sm text-end">
|
||||||
|
{options.bicycleOptions.use_hills?.toFixed(2)}
|
||||||
|
</p>
|
||||||
|
<hr class="border-input-border my-3" />
|
||||||
|
<p class="text-sm font-medium">{$_("use-roads")}</p>
|
||||||
|
<Slider
|
||||||
|
minValue={0}
|
||||||
|
maxValue={1}
|
||||||
|
step={0.05}
|
||||||
|
bind:currentValue={options.bicycleOptions.use_roads}
|
||||||
|
></Slider>
|
||||||
|
<p class="text-sm text-end">
|
||||||
|
{options.bicycleOptions.use_roads?.toFixed(2)}
|
||||||
|
</p>
|
||||||
|
<hr class="border-input-border my-3" />
|
||||||
|
<p class="text-sm font-medium">
|
||||||
|
{$_("avoid-bad-surfaces")}
|
||||||
|
</p>
|
||||||
|
<Slider
|
||||||
|
minValue={0}
|
||||||
|
maxValue={1}
|
||||||
|
step={0.05}
|
||||||
|
bind:currentValue={
|
||||||
|
options.bicycleOptions.avoid_bad_surfaces
|
||||||
|
}
|
||||||
|
></Slider>
|
||||||
|
<p class="text-sm text-end">
|
||||||
|
{options.bicycleOptions.avoid_bad_surfaces?.toFixed(
|
||||||
|
2,
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
|
<hr class="border-input-border my-3" />
|
||||||
|
<Toggle
|
||||||
|
label={$_("shortest")}
|
||||||
|
bind:value={options.bicycleOptions.shortest}
|
||||||
|
></Toggle>
|
||||||
|
{:else if options.modeOfTransport === "auto" && options.autoOptions}
|
||||||
|
<p class="text-sm font-medium pb-1">
|
||||||
|
{$_("fixed-speed")}
|
||||||
|
</p>
|
||||||
|
<Slider
|
||||||
|
minValue={0}
|
||||||
|
maxValue={252}
|
||||||
|
step={1}
|
||||||
|
bind:currentValue={options.autoOptions.fixed_speed}
|
||||||
|
></Slider>
|
||||||
|
<p class="text-sm text-end">
|
||||||
|
{formatSpeed(
|
||||||
|
options.autoOptions.fixed_speed! / 3.6,
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
|
<hr class="border-input-border my-3" />
|
||||||
|
<p class="text-sm font-medium">{$_("top-speed")}</p>
|
||||||
|
<Slider
|
||||||
|
minValue={10}
|
||||||
|
maxValue={252}
|
||||||
|
step={1}
|
||||||
|
bind:currentValue={options.autoOptions.top_speed}
|
||||||
|
></Slider>
|
||||||
|
<p class="text-sm text-end">
|
||||||
|
{formatSpeed(options.autoOptions.top_speed! / 3.6)}
|
||||||
|
</p>
|
||||||
|
<hr class="border-input-border my-3" />
|
||||||
|
<p class="text-sm font-medium">
|
||||||
|
{$_("car")}
|
||||||
|
{$_("width")}
|
||||||
|
</p>
|
||||||
|
<Slider
|
||||||
|
minValue={1}
|
||||||
|
maxValue={10}
|
||||||
|
step={0.1}
|
||||||
|
bind:currentValue={options.autoOptions.width}
|
||||||
|
></Slider>
|
||||||
|
<p class="text-sm text-end">
|
||||||
|
{options.autoOptions.width?.toFixed(1)}
|
||||||
|
</p>
|
||||||
|
<hr class="border-input-border my-3" />
|
||||||
|
<p class="text-sm font-medium">
|
||||||
|
{$_("car")}
|
||||||
|
{$_("height")}
|
||||||
|
</p>
|
||||||
|
<Slider
|
||||||
|
minValue={1}
|
||||||
|
maxValue={10}
|
||||||
|
step={0.1}
|
||||||
|
bind:currentValue={options.autoOptions.height}
|
||||||
|
></Slider>
|
||||||
|
<p class="text-sm text-end">
|
||||||
|
{options.autoOptions.height?.toFixed(1)}
|
||||||
|
</p>
|
||||||
|
<hr class="border-input-border my-3" />
|
||||||
|
<Toggle
|
||||||
|
label={$_("shortest")}
|
||||||
|
bind:value={options.autoOptions.shortest}
|
||||||
|
></Toggle>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
{#if crop}
|
||||||
|
<div
|
||||||
|
class="p-4 my-2 rounded-xl bg-background shadow-xl min-w-72 flex flex-col"
|
||||||
|
>
|
||||||
|
<DoubleSlider onupdate={onUpdateCropRange}></DoubleSlider>
|
||||||
|
<button
|
||||||
|
class="btn-secondary mb-2"
|
||||||
|
onclick={() => {
|
||||||
|
crop = false;
|
||||||
|
onCrop();
|
||||||
|
onCropToggle(false);
|
||||||
|
}}>{$_("crop")}</button
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
class="btn-secondary"
|
||||||
|
onclick={() => {
|
||||||
|
crop = false;
|
||||||
|
onCropToggle(false);
|
||||||
|
}}>{$_("cancel")}</button
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
{#if recalculateElevationData}
|
||||||
|
<div class="p-4 my-2 rounded-xl bg-background shadow-xl">
|
||||||
|
<Button secondary onclick={onRecalculateElevationData}
|
||||||
|
>{$_("recalculate-elevation-data")}</Button
|
||||||
|
>
|
||||||
|
<p
|
||||||
|
class="bg-background/50 rounded-xl text-sm text-gray-500 mt-2 max-w-72"
|
||||||
|
>
|
||||||
|
{$_("recalculating-elevation-data-hint")}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
@@ -1,288 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import type {
|
|
||||||
RoutingOptions,
|
|
||||||
ValhallaBicycleCostingOptions,
|
|
||||||
} from "$lib/models/valhalla";
|
|
||||||
import { formatSpeed } from "$lib/util/format_util";
|
|
||||||
import { _ } from "svelte-i18n";
|
|
||||||
import { slide } from "svelte/transition";
|
|
||||||
import Select, { type SelectItem } from "../base/select.svelte";
|
|
||||||
import Slider from "../base/slider.svelte";
|
|
||||||
import Toggle from "../base/toggle.svelte";
|
|
||||||
interface Props {
|
|
||||||
options: RoutingOptions;
|
|
||||||
onReverse: () => void;
|
|
||||||
onReset: () => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
let { options = $bindable(), onReverse, onReset }: Props = $props();
|
|
||||||
|
|
||||||
const modesOfTransport: SelectItem[] = [
|
|
||||||
{ text: $_("hiking"), value: "pedestrian" },
|
|
||||||
{ text: $_("cycling"), value: "bicycle" },
|
|
||||||
{ text: $_("driving"), value: "auto" },
|
|
||||||
];
|
|
||||||
|
|
||||||
const bikeTypes: SelectItem[] = [
|
|
||||||
{ text: $_("hybrid"), value: "Hybrid" },
|
|
||||||
{ text: $_("road"), value: "Road" },
|
|
||||||
{ text: $_("cross"), value: "Cross" },
|
|
||||||
{ text: $_("mountain"), value: "Mountain" },
|
|
||||||
];
|
|
||||||
|
|
||||||
if (!options.pedestrianOptions) {
|
|
||||||
options.pedestrianOptions = {
|
|
||||||
max_hiking_difficulty: 6,
|
|
||||||
walking_speed: 5.1,
|
|
||||||
use_hills: 1,
|
|
||||||
shortest: false,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!options.bicycleOptions) {
|
|
||||||
options.bicycleOptions = {
|
|
||||||
bicycle_type: "Hybrid",
|
|
||||||
cycling_speed: 20,
|
|
||||||
use_roads: 0.5,
|
|
||||||
use_hills: 0.5,
|
|
||||||
avoid_bad_surfaces: 0.25,
|
|
||||||
shortest: false,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!options.autoOptions) {
|
|
||||||
options.autoOptions = {
|
|
||||||
width: 1.6,
|
|
||||||
height: 1.9,
|
|
||||||
top_speed: 140,
|
|
||||||
fixed_speed: 0,
|
|
||||||
shortest: false,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
$effect(() => {
|
|
||||||
if (!options.autoRouting) {
|
|
||||||
showSettings = false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
let showSettings = $state(false);
|
|
||||||
|
|
||||||
// svelte-ignore non_reactive_update
|
|
||||||
let cycleSpeedSlider: Slider;
|
|
||||||
|
|
||||||
function adjustSpeeddependingOnBikeType(
|
|
||||||
type: ValhallaBicycleCostingOptions["bicycle_type"],
|
|
||||||
) {
|
|
||||||
switch (type) {
|
|
||||||
case "City":
|
|
||||||
case "Hybrid":
|
|
||||||
cycleSpeedSlider?.set(18);
|
|
||||||
break;
|
|
||||||
case "Road":
|
|
||||||
cycleSpeedSlider?.set(25);
|
|
||||||
break;
|
|
||||||
case "Cross":
|
|
||||||
cycleSpeedSlider?.set(20);
|
|
||||||
break;
|
|
||||||
case "Mountain":
|
|
||||||
cycleSpeedSlider?.set(16);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div class="px-6 py-4 my-2 rounded-xl bg-background space-y-4">
|
|
||||||
<Toggle bind:value={options.autoRouting} label={$_("enable-auto-routing")}
|
|
||||||
></Toggle>
|
|
||||||
<div class="flex items-center gap-4">
|
|
||||||
<Select
|
|
||||||
items={modesOfTransport}
|
|
||||||
bind:value={options.modeOfTransport}
|
|
||||||
disabled={!options.autoRouting}
|
|
||||||
></Select>
|
|
||||||
<button
|
|
||||||
class="btn-icon"
|
|
||||||
type="button"
|
|
||||||
disabled={!options.autoRouting}
|
|
||||||
onclick={() => (showSettings = !showSettings)}
|
|
||||||
aria-label="Toggle routing settings"
|
|
||||||
><i class="fa fa-cogs" class:text-gray-500={!options.autoRouting}
|
|
||||||
></i></button
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
<div class="flex items-center gap-4">
|
|
||||||
<button
|
|
||||||
class="btn-icon tooltip"
|
|
||||||
type="button"
|
|
||||||
onclick={() => onReverse()}
|
|
||||||
aria-label="Reverse trail direction"
|
|
||||||
data-title={$_("reverse-direction")}
|
|
||||||
><i class="fa fa-arrow-right-arrow-left"></i></button
|
|
||||||
>
|
|
||||||
<button
|
|
||||||
class="btn-icon tooltip"
|
|
||||||
type="button"
|
|
||||||
onclick={() => onReset()}
|
|
||||||
aria-label="Reset route"
|
|
||||||
data-title={$_("reset")}><i class="fa fa-trash"></i></button
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
{#if showSettings}
|
|
||||||
<div in:slide out:slide>
|
|
||||||
{#if options.modeOfTransport === "pedestrian" && options.pedestrianOptions}
|
|
||||||
<p class="text-sm font-medium pb-1">{$_("walking-speed")}</p>
|
|
||||||
<Slider
|
|
||||||
minValue={0.5}
|
|
||||||
maxValue={25}
|
|
||||||
bind:currentValue={options.pedestrianOptions.walking_speed}
|
|
||||||
></Slider>
|
|
||||||
<p class="text-sm text-end">
|
|
||||||
{formatSpeed(
|
|
||||||
options.pedestrianOptions.walking_speed! / 3.6,
|
|
||||||
)}
|
|
||||||
</p>
|
|
||||||
<hr class="border-input-border my-3" />
|
|
||||||
<p class="text-sm font-medium">{$_("use-hills")}</p>
|
|
||||||
<Slider
|
|
||||||
minValue={0}
|
|
||||||
maxValue={1}
|
|
||||||
step={0.1}
|
|
||||||
bind:currentValue={options.pedestrianOptions!.use_hills}
|
|
||||||
></Slider>
|
|
||||||
<p class="text-sm text-end">
|
|
||||||
{options.pedestrianOptions.use_hills?.toFixed(2)}
|
|
||||||
</p>
|
|
||||||
<hr class="border-input-border my-3" />
|
|
||||||
<p class="text-sm font-medium">{$_("max-hiking-difficulty")}</p>
|
|
||||||
<Slider
|
|
||||||
minValue={0}
|
|
||||||
maxValue={6}
|
|
||||||
step={1}
|
|
||||||
bind:currentValue={
|
|
||||||
options.pedestrianOptions!.max_hiking_difficulty
|
|
||||||
}
|
|
||||||
></Slider>
|
|
||||||
<p class="text-sm text-end">
|
|
||||||
{options.pedestrianOptions.max_hiking_difficulty?.toFixed(
|
|
||||||
0,
|
|
||||||
)}
|
|
||||||
</p>
|
|
||||||
<hr class="border-input-border my-3" />
|
|
||||||
<Toggle
|
|
||||||
label={$_("shortest")}
|
|
||||||
bind:value={options.pedestrianOptions.shortest}
|
|
||||||
></Toggle>
|
|
||||||
{:else if options.modeOfTransport === "bicycle" && options.bicycleOptions}
|
|
||||||
<Select
|
|
||||||
items={bikeTypes}
|
|
||||||
label={$_("bike-type")}
|
|
||||||
onchange={(v) => adjustSpeeddependingOnBikeType(v)}
|
|
||||||
bind:value={options.bicycleOptions.bicycle_type}
|
|
||||||
></Select>
|
|
||||||
<hr class="border-input-border my-3" />
|
|
||||||
<p class="text-sm font-medium pb-1">{$_("cycling-speed")}</p>
|
|
||||||
<Slider
|
|
||||||
minValue={5}
|
|
||||||
maxValue={50}
|
|
||||||
bind:currentValue={options.bicycleOptions.cycling_speed}
|
|
||||||
bind:this={cycleSpeedSlider}
|
|
||||||
></Slider>
|
|
||||||
<p class="text-sm text-end">
|
|
||||||
{formatSpeed(options.bicycleOptions.cycling_speed! / 3.6)}
|
|
||||||
</p>
|
|
||||||
<hr class="border-input-border my-3" />
|
|
||||||
<p class="text-sm font-medium">{$_("use-hills")}</p>
|
|
||||||
<Slider
|
|
||||||
minValue={0}
|
|
||||||
maxValue={1}
|
|
||||||
step={0.05}
|
|
||||||
bind:currentValue={options.bicycleOptions.use_hills}
|
|
||||||
></Slider>
|
|
||||||
<p class="text-sm text-end">
|
|
||||||
{options.bicycleOptions.use_hills?.toFixed(2)}
|
|
||||||
</p>
|
|
||||||
<hr class="border-input-border my-3" />
|
|
||||||
<p class="text-sm font-medium">{$_("use-roads")}</p>
|
|
||||||
<Slider
|
|
||||||
minValue={0}
|
|
||||||
maxValue={1}
|
|
||||||
step={0.05}
|
|
||||||
bind:currentValue={options.bicycleOptions.use_roads}
|
|
||||||
></Slider>
|
|
||||||
<p class="text-sm text-end">
|
|
||||||
{options.bicycleOptions.use_roads?.toFixed(2)}
|
|
||||||
</p>
|
|
||||||
<hr class="border-input-border my-3" />
|
|
||||||
<p class="text-sm font-medium">{$_("avoid-bad-surfaces")}</p>
|
|
||||||
<Slider
|
|
||||||
minValue={0}
|
|
||||||
maxValue={1}
|
|
||||||
step={0.05}
|
|
||||||
bind:currentValue={
|
|
||||||
options.bicycleOptions.avoid_bad_surfaces
|
|
||||||
}
|
|
||||||
></Slider>
|
|
||||||
<p class="text-sm text-end">
|
|
||||||
{options.bicycleOptions.avoid_bad_surfaces?.toFixed(2)}
|
|
||||||
</p>
|
|
||||||
<hr class="border-input-border my-3" />
|
|
||||||
<Toggle
|
|
||||||
label={$_("shortest")}
|
|
||||||
bind:value={options.bicycleOptions.shortest}
|
|
||||||
></Toggle>
|
|
||||||
{:else if options.modeOfTransport === "auto" && options.autoOptions}
|
|
||||||
<p class="text-sm font-medium pb-1">{$_("fixed-speed")}</p>
|
|
||||||
<Slider
|
|
||||||
minValue={0}
|
|
||||||
maxValue={252}
|
|
||||||
step={1}
|
|
||||||
bind:currentValue={options.autoOptions.fixed_speed}
|
|
||||||
></Slider>
|
|
||||||
<p class="text-sm text-end">
|
|
||||||
{formatSpeed(options.autoOptions.fixed_speed! / 3.6)}
|
|
||||||
</p>
|
|
||||||
<hr class="border-input-border my-3" />
|
|
||||||
<p class="text-sm font-medium">{$_("top-speed")}</p>
|
|
||||||
<Slider
|
|
||||||
minValue={10}
|
|
||||||
maxValue={252}
|
|
||||||
step={1}
|
|
||||||
bind:currentValue={options.autoOptions.top_speed}
|
|
||||||
></Slider>
|
|
||||||
<p class="text-sm text-end">
|
|
||||||
{formatSpeed(options.autoOptions.top_speed! / 3.6)}
|
|
||||||
</p>
|
|
||||||
<hr class="border-input-border my-3" />
|
|
||||||
<p class="text-sm font-medium">{$_("car")} {$_("width")}</p>
|
|
||||||
<Slider
|
|
||||||
minValue={1}
|
|
||||||
maxValue={10}
|
|
||||||
step={0.1}
|
|
||||||
bind:currentValue={options.autoOptions.width}
|
|
||||||
></Slider>
|
|
||||||
<p class="text-sm text-end">
|
|
||||||
{options.autoOptions.width?.toFixed(1)}
|
|
||||||
</p>
|
|
||||||
<hr class="border-input-border my-3" />
|
|
||||||
<p class="text-sm font-medium">{$_("car")} {$_("height")}</p>
|
|
||||||
<Slider
|
|
||||||
minValue={1}
|
|
||||||
maxValue={10}
|
|
||||||
step={0.1}
|
|
||||||
bind:currentValue={options.autoOptions.height}
|
|
||||||
></Slider>
|
|
||||||
<p class="text-sm text-end">
|
|
||||||
{options.autoOptions.height?.toFixed(1)}
|
|
||||||
</p>
|
|
||||||
<hr class="border-input-border my-3" />
|
|
||||||
<Toggle
|
|
||||||
label={$_("shortest")}
|
|
||||||
bind:value={options.autoOptions.shortest}
|
|
||||||
></Toggle>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
@@ -81,6 +81,7 @@
|
|||||||
"create-new-list": "Neue Liste erstellen",
|
"create-new-list": "Neue Liste erstellen",
|
||||||
"create-waypoint": "Wegpunkt erstellen",
|
"create-waypoint": "Wegpunkt erstellen",
|
||||||
"creation-date": "Erstellungsdatum",
|
"creation-date": "Erstellungsdatum",
|
||||||
|
"crop": "",
|
||||||
"cross": "Querfeldein",
|
"cross": "Querfeldein",
|
||||||
"current-password": "Aktuelles Passwort",
|
"current-password": "Aktuelles Passwort",
|
||||||
"cycling": "Radfahren",
|
"cycling": "Radfahren",
|
||||||
@@ -235,6 +236,7 @@
|
|||||||
"metric": "Metrisch",
|
"metric": "Metrisch",
|
||||||
"moderate": "Mittel",
|
"moderate": "Mittel",
|
||||||
"more": "More",
|
"more": "More",
|
||||||
|
"more-route-settings": "",
|
||||||
"mountain": "Berg",
|
"mountain": "Berg",
|
||||||
"mountain-pass": "",
|
"mountain-pass": "",
|
||||||
"must-be-at-least-n-characters-long": "Muss mindestens {n} Zeichen lang sein",
|
"must-be-at-least-n-characters-long": "Muss mindestens {n} Zeichen lang sein",
|
||||||
@@ -313,6 +315,8 @@
|
|||||||
"radius": "Radius",
|
"radius": "Radius",
|
||||||
"railway-station": "",
|
"railway-station": "",
|
||||||
"read-more": "Mehr",
|
"read-more": "Mehr",
|
||||||
|
"recalculate-elevation-data": "",
|
||||||
|
"recalculating-elevation-data-hint": "",
|
||||||
"register": "Registrieren",
|
"register": "Registrieren",
|
||||||
"remote-users-cannot-edit": "Remote users cannot edit",
|
"remote-users-cannot-edit": "Remote users cannot edit",
|
||||||
"removed-trail-from": "Route entfernt aus",
|
"removed-trail-from": "Route entfernt aus",
|
||||||
@@ -366,6 +370,7 @@
|
|||||||
"show-less": "Show less",
|
"show-less": "Show less",
|
||||||
"show-on-map": "Auf der Karte anzeigen",
|
"show-on-map": "Auf der Karte anzeigen",
|
||||||
"shower": "",
|
"shower": "",
|
||||||
|
"skiing": "",
|
||||||
"slogan": "Speichere deine Abenteuer!",
|
"slogan": "Speichere deine Abenteuer!",
|
||||||
"slope": "Steigung",
|
"slope": "Steigung",
|
||||||
"someone": "Jemand",
|
"someone": "Jemand",
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
"Canoeing": "Canoeing",
|
"Canoeing": "Canoeing",
|
||||||
"Climbing": "Climbing",
|
"Climbing": "Climbing",
|
||||||
"Hiking": "Hiking",
|
"Hiking": "Hiking",
|
||||||
"skiing": "Skiing",
|
"Skiing": "",
|
||||||
"Walking": "Walking",
|
"Walking": "Walking",
|
||||||
"about": "About",
|
"about": "About",
|
||||||
"account-delete-confirm": "You are about to delete your account. All your trails will also be deleted. Do you want to proceed?",
|
"account-delete-confirm": "You are about to delete your account. All your trails will also be deleted. Do you want to proceed?",
|
||||||
@@ -81,6 +81,7 @@
|
|||||||
"create-new-list": "Create new list",
|
"create-new-list": "Create new list",
|
||||||
"create-waypoint": "Create waypoint",
|
"create-waypoint": "Create waypoint",
|
||||||
"creation-date": "Creation date",
|
"creation-date": "Creation date",
|
||||||
|
"crop": "Crop",
|
||||||
"cross": "Cross",
|
"cross": "Cross",
|
||||||
"current-password": "Current password",
|
"current-password": "Current password",
|
||||||
"cycling": "Cycling",
|
"cycling": "Cycling",
|
||||||
@@ -235,6 +236,7 @@
|
|||||||
"metric": "Metric",
|
"metric": "Metric",
|
||||||
"moderate": "Moderate",
|
"moderate": "Moderate",
|
||||||
"more": "More",
|
"more": "More",
|
||||||
|
"more-route-settings": "More route settings",
|
||||||
"mountain": "Mountain",
|
"mountain": "Mountain",
|
||||||
"mountain-pass": "Mountain pass",
|
"mountain-pass": "Mountain pass",
|
||||||
"must-be-at-least-n-characters-long": "Must be at least {n} characters long",
|
"must-be-at-least-n-characters-long": "Must be at least {n} characters long",
|
||||||
@@ -313,6 +315,8 @@
|
|||||||
"radius": "Radius",
|
"radius": "Radius",
|
||||||
"railway-station": "Railway station",
|
"railway-station": "Railway station",
|
||||||
"read-more": "Read more",
|
"read-more": "Read more",
|
||||||
|
"recalculate-elevation-data": "Recalculate elevation data",
|
||||||
|
"recalculating-elevation-data-hint": "Recalculating elevation data will erase the existing elevation data, if any, and replace it with data from Valhalla.",
|
||||||
"register": "Register",
|
"register": "Register",
|
||||||
"remote-users-cannot-edit": "Remote users cannot edit",
|
"remote-users-cannot-edit": "Remote users cannot edit",
|
||||||
"removed-trail-from": "Removed trail from",
|
"removed-trail-from": "Removed trail from",
|
||||||
@@ -366,6 +370,7 @@
|
|||||||
"show-less": "Show less",
|
"show-less": "Show less",
|
||||||
"show-on-map": "Show on map",
|
"show-on-map": "Show on map",
|
||||||
"shower": "Shower",
|
"shower": "Shower",
|
||||||
|
"skiing": "Skiing",
|
||||||
"slogan": "Save your adventures!",
|
"slogan": "Save your adventures!",
|
||||||
"slope": "Slope",
|
"slope": "Slope",
|
||||||
"someone": "Someone",
|
"someone": "Someone",
|
||||||
|
|||||||
@@ -81,6 +81,7 @@
|
|||||||
"create-new-list": "Crear una nueva lista",
|
"create-new-list": "Crear una nueva lista",
|
||||||
"create-waypoint": "Crear punto de ruta",
|
"create-waypoint": "Crear punto de ruta",
|
||||||
"creation-date": "Fecha de creación",
|
"creation-date": "Fecha de creación",
|
||||||
|
"crop": "",
|
||||||
"cross": "Cruzar",
|
"cross": "Cruzar",
|
||||||
"current-password": "Contraseña actual",
|
"current-password": "Contraseña actual",
|
||||||
"cycling": "Ciclismo",
|
"cycling": "Ciclismo",
|
||||||
@@ -235,6 +236,7 @@
|
|||||||
"metric": "Métrica",
|
"metric": "Métrica",
|
||||||
"moderate": "Medio",
|
"moderate": "Medio",
|
||||||
"more": "More",
|
"more": "More",
|
||||||
|
"more-route-settings": "",
|
||||||
"mountain": "Montaña",
|
"mountain": "Montaña",
|
||||||
"mountain-pass": "",
|
"mountain-pass": "",
|
||||||
"must-be-at-least-n-characters-long": "Tiene que tener por lo menos {n} caracteres",
|
"must-be-at-least-n-characters-long": "Tiene que tener por lo menos {n} caracteres",
|
||||||
@@ -313,6 +315,8 @@
|
|||||||
"radius": "Radio",
|
"radius": "Radio",
|
||||||
"railway-station": "",
|
"railway-station": "",
|
||||||
"read-more": "Leer más",
|
"read-more": "Leer más",
|
||||||
|
"recalculate-elevation-data": "",
|
||||||
|
"recalculating-elevation-data-hint": "",
|
||||||
"register": "Registrar",
|
"register": "Registrar",
|
||||||
"remote-users-cannot-edit": "Remote users cannot edit",
|
"remote-users-cannot-edit": "Remote users cannot edit",
|
||||||
"removed-trail-from": "Ruta borrada de",
|
"removed-trail-from": "Ruta borrada de",
|
||||||
@@ -366,6 +370,7 @@
|
|||||||
"show-less": "Show less",
|
"show-less": "Show less",
|
||||||
"show-on-map": "Mostrar en mapa",
|
"show-on-map": "Mostrar en mapa",
|
||||||
"shower": "",
|
"shower": "",
|
||||||
|
"skiing": "",
|
||||||
"slogan": "¡Guarda tus aventuras!",
|
"slogan": "¡Guarda tus aventuras!",
|
||||||
"slope": "Pendiente",
|
"slope": "Pendiente",
|
||||||
"someone": "Alguien",
|
"someone": "Alguien",
|
||||||
|
|||||||
@@ -81,6 +81,7 @@
|
|||||||
"create-new-list": "Create new list",
|
"create-new-list": "Create new list",
|
||||||
"create-waypoint": "Create waypoint",
|
"create-waypoint": "Create waypoint",
|
||||||
"creation-date": "Creation date",
|
"creation-date": "Creation date",
|
||||||
|
"crop": "",
|
||||||
"cross": "Cross",
|
"cross": "Cross",
|
||||||
"current-password": "Current password",
|
"current-password": "Current password",
|
||||||
"cycling": "Cycling",
|
"cycling": "Cycling",
|
||||||
@@ -235,6 +236,7 @@
|
|||||||
"metric": "Metric",
|
"metric": "Metric",
|
||||||
"moderate": "Moderate",
|
"moderate": "Moderate",
|
||||||
"more": "More",
|
"more": "More",
|
||||||
|
"more-route-settings": "",
|
||||||
"mountain": "Mountain",
|
"mountain": "Mountain",
|
||||||
"mountain-pass": "",
|
"mountain-pass": "",
|
||||||
"must-be-at-least-n-characters-long": "Must be at least {n} characters long",
|
"must-be-at-least-n-characters-long": "Must be at least {n} characters long",
|
||||||
@@ -313,6 +315,8 @@
|
|||||||
"radius": "Radius",
|
"radius": "Radius",
|
||||||
"railway-station": "",
|
"railway-station": "",
|
||||||
"read-more": "Read more",
|
"read-more": "Read more",
|
||||||
|
"recalculate-elevation-data": "",
|
||||||
|
"recalculating-elevation-data-hint": "",
|
||||||
"register": "Register",
|
"register": "Register",
|
||||||
"remote-users-cannot-edit": "Remote users cannot edit",
|
"remote-users-cannot-edit": "Remote users cannot edit",
|
||||||
"removed-trail-from": "Removed trail from",
|
"removed-trail-from": "Removed trail from",
|
||||||
@@ -366,6 +370,7 @@
|
|||||||
"show-less": "Show less",
|
"show-less": "Show less",
|
||||||
"show-on-map": "Show on map",
|
"show-on-map": "Show on map",
|
||||||
"shower": "",
|
"shower": "",
|
||||||
|
"skiing": "",
|
||||||
"slogan": "Save your adventures!",
|
"slogan": "Save your adventures!",
|
||||||
"slope": "Slope",
|
"slope": "Slope",
|
||||||
"someone": "Someone",
|
"someone": "Someone",
|
||||||
|
|||||||
@@ -81,6 +81,7 @@
|
|||||||
"create-new-list": "Créer une nouvelle liste",
|
"create-new-list": "Créer une nouvelle liste",
|
||||||
"create-waypoint": "Créer un point de passage",
|
"create-waypoint": "Créer un point de passage",
|
||||||
"creation-date": "Date de création",
|
"creation-date": "Date de création",
|
||||||
|
"crop": "",
|
||||||
"cross": "Cross",
|
"cross": "Cross",
|
||||||
"current-password": "Mot de passe actuel",
|
"current-password": "Mot de passe actuel",
|
||||||
"cycling": "Vélo",
|
"cycling": "Vélo",
|
||||||
@@ -235,6 +236,7 @@
|
|||||||
"metric": "Métrique",
|
"metric": "Métrique",
|
||||||
"moderate": "Moyenne",
|
"moderate": "Moyenne",
|
||||||
"more": "More",
|
"more": "More",
|
||||||
|
"more-route-settings": "",
|
||||||
"mountain": "Montagne",
|
"mountain": "Montagne",
|
||||||
"mountain-pass": "",
|
"mountain-pass": "",
|
||||||
"must-be-at-least-n-characters-long": "Doit être composé d'au moins {n} caractères",
|
"must-be-at-least-n-characters-long": "Doit être composé d'au moins {n} caractères",
|
||||||
@@ -313,6 +315,8 @@
|
|||||||
"radius": "Rayon",
|
"radius": "Rayon",
|
||||||
"railway-station": "",
|
"railway-station": "",
|
||||||
"read-more": "Voir plus",
|
"read-more": "Voir plus",
|
||||||
|
"recalculate-elevation-data": "",
|
||||||
|
"recalculating-elevation-data-hint": "",
|
||||||
"register": "Créer un compte",
|
"register": "Créer un compte",
|
||||||
"remote-users-cannot-edit": "Remote users cannot edit",
|
"remote-users-cannot-edit": "Remote users cannot edit",
|
||||||
"removed-trail-from": "Enlever l'itinéraire de",
|
"removed-trail-from": "Enlever l'itinéraire de",
|
||||||
@@ -366,6 +370,7 @@
|
|||||||
"show-less": "Show less",
|
"show-less": "Show less",
|
||||||
"show-on-map": "Voir sur la carte",
|
"show-on-map": "Voir sur la carte",
|
||||||
"shower": "",
|
"shower": "",
|
||||||
|
"skiing": "",
|
||||||
"slogan": "Sauvegarder vos aventures !",
|
"slogan": "Sauvegarder vos aventures !",
|
||||||
"slope": "Pente",
|
"slope": "Pente",
|
||||||
"someone": "Quelqu'un",
|
"someone": "Quelqu'un",
|
||||||
|
|||||||
@@ -81,6 +81,7 @@
|
|||||||
"create-new-list": "Új lista létrehozása",
|
"create-new-list": "Új lista létrehozása",
|
||||||
"create-waypoint": "Create waypoint",
|
"create-waypoint": "Create waypoint",
|
||||||
"creation-date": "létrehozás dátuma",
|
"creation-date": "létrehozás dátuma",
|
||||||
|
"crop": "",
|
||||||
"cross": "Cross",
|
"cross": "Cross",
|
||||||
"current-password": "Current password",
|
"current-password": "Current password",
|
||||||
"cycling": "Cycling",
|
"cycling": "Cycling",
|
||||||
@@ -235,6 +236,7 @@
|
|||||||
"metric": "Metrikus",
|
"metric": "Metrikus",
|
||||||
"moderate": "Mérsékelt",
|
"moderate": "Mérsékelt",
|
||||||
"more": "More",
|
"more": "More",
|
||||||
|
"more-route-settings": "",
|
||||||
"mountain": "Mountain",
|
"mountain": "Mountain",
|
||||||
"mountain-pass": "",
|
"mountain-pass": "",
|
||||||
"must-be-at-least-n-characters-long": "Legalább {n} karakter hosszúnak kell lennie",
|
"must-be-at-least-n-characters-long": "Legalább {n} karakter hosszúnak kell lennie",
|
||||||
@@ -313,6 +315,8 @@
|
|||||||
"radius": "Átmérő",
|
"radius": "Átmérő",
|
||||||
"railway-station": "",
|
"railway-station": "",
|
||||||
"read-more": "Read more",
|
"read-more": "Read more",
|
||||||
|
"recalculate-elevation-data": "",
|
||||||
|
"recalculating-elevation-data-hint": "",
|
||||||
"register": "Regisztráció",
|
"register": "Regisztráció",
|
||||||
"remote-users-cannot-edit": "Remote users cannot edit",
|
"remote-users-cannot-edit": "Remote users cannot edit",
|
||||||
"removed-trail-from": "Eltávolított nyomvonal a",
|
"removed-trail-from": "Eltávolított nyomvonal a",
|
||||||
@@ -366,6 +370,7 @@
|
|||||||
"show-less": "Show less",
|
"show-less": "Show less",
|
||||||
"show-on-map": "Mutatás térképen",
|
"show-on-map": "Mutatás térképen",
|
||||||
"shower": "",
|
"shower": "",
|
||||||
|
"skiing": "",
|
||||||
"slogan": "Mentse el a kalandjait!",
|
"slogan": "Mentse el a kalandjait!",
|
||||||
"slope": "Slope",
|
"slope": "Slope",
|
||||||
"someone": "Someone",
|
"someone": "Someone",
|
||||||
|
|||||||
@@ -81,6 +81,7 @@
|
|||||||
"create-new-list": "Crea nuova lista",
|
"create-new-list": "Crea nuova lista",
|
||||||
"create-waypoint": "Create waypoint",
|
"create-waypoint": "Create waypoint",
|
||||||
"creation-date": "Data di creazione",
|
"creation-date": "Data di creazione",
|
||||||
|
"crop": "",
|
||||||
"cross": "Cross",
|
"cross": "Cross",
|
||||||
"current-password": "Password attuale",
|
"current-password": "Password attuale",
|
||||||
"cycling": "Ciclismo",
|
"cycling": "Ciclismo",
|
||||||
@@ -235,6 +236,7 @@
|
|||||||
"metric": "Metrico",
|
"metric": "Metrico",
|
||||||
"moderate": "Moderato",
|
"moderate": "Moderato",
|
||||||
"more": "More",
|
"more": "More",
|
||||||
|
"more-route-settings": "",
|
||||||
"mountain": "Mountain",
|
"mountain": "Mountain",
|
||||||
"mountain-pass": "",
|
"mountain-pass": "",
|
||||||
"must-be-at-least-n-characters-long": "Deve essere lungo almeno {n} caratteri",
|
"must-be-at-least-n-characters-long": "Deve essere lungo almeno {n} caratteri",
|
||||||
@@ -313,6 +315,8 @@
|
|||||||
"radius": "Raggio",
|
"radius": "Raggio",
|
||||||
"railway-station": "",
|
"railway-station": "",
|
||||||
"read-more": "Per saperne di più",
|
"read-more": "Per saperne di più",
|
||||||
|
"recalculate-elevation-data": "",
|
||||||
|
"recalculating-elevation-data-hint": "",
|
||||||
"register": "Registrati",
|
"register": "Registrati",
|
||||||
"remote-users-cannot-edit": "Remote users cannot edit",
|
"remote-users-cannot-edit": "Remote users cannot edit",
|
||||||
"removed-trail-from": "Percorso rimosso da",
|
"removed-trail-from": "Percorso rimosso da",
|
||||||
@@ -366,6 +370,7 @@
|
|||||||
"show-less": "Show less",
|
"show-less": "Show less",
|
||||||
"show-on-map": "Mostra sulla mappa",
|
"show-on-map": "Mostra sulla mappa",
|
||||||
"shower": "",
|
"shower": "",
|
||||||
|
"skiing": "",
|
||||||
"slogan": "Salva le tue avventure!",
|
"slogan": "Salva le tue avventure!",
|
||||||
"slope": "Pendenza",
|
"slope": "Pendenza",
|
||||||
"someone": "Qualcuno",
|
"someone": "Qualcuno",
|
||||||
|
|||||||
@@ -81,6 +81,7 @@
|
|||||||
"create-new-list": "Nieuwe lijst",
|
"create-new-list": "Nieuwe lijst",
|
||||||
"create-waypoint": "Nieuw routepunt",
|
"create-waypoint": "Nieuw routepunt",
|
||||||
"creation-date": "Aanmaakdatum",
|
"creation-date": "Aanmaakdatum",
|
||||||
|
"crop": "",
|
||||||
"cross": "Oversteken",
|
"cross": "Oversteken",
|
||||||
"current-password": "Huidig wachtwoord",
|
"current-password": "Huidig wachtwoord",
|
||||||
"cycling": "Fietsen",
|
"cycling": "Fietsen",
|
||||||
@@ -235,6 +236,7 @@
|
|||||||
"metric": "Metrisch",
|
"metric": "Metrisch",
|
||||||
"moderate": "Gemiddeld",
|
"moderate": "Gemiddeld",
|
||||||
"more": "More",
|
"more": "More",
|
||||||
|
"more-route-settings": "",
|
||||||
"mountain": "Berg",
|
"mountain": "Berg",
|
||||||
"mountain-pass": "",
|
"mountain-pass": "",
|
||||||
"must-be-at-least-n-characters-long": "Minimaal {n} tekens",
|
"must-be-at-least-n-characters-long": "Minimaal {n} tekens",
|
||||||
@@ -313,6 +315,8 @@
|
|||||||
"radius": "Straal",
|
"radius": "Straal",
|
||||||
"railway-station": "",
|
"railway-station": "",
|
||||||
"read-more": "Lees meer",
|
"read-more": "Lees meer",
|
||||||
|
"recalculate-elevation-data": "",
|
||||||
|
"recalculating-elevation-data-hint": "",
|
||||||
"register": "Registreren",
|
"register": "Registreren",
|
||||||
"remote-users-cannot-edit": "Remote users cannot edit",
|
"remote-users-cannot-edit": "Remote users cannot edit",
|
||||||
"removed-trail-from": "Route verwijderd van",
|
"removed-trail-from": "Route verwijderd van",
|
||||||
@@ -366,6 +370,7 @@
|
|||||||
"show-less": "Show less",
|
"show-less": "Show less",
|
||||||
"show-on-map": "Tonen op kaart",
|
"show-on-map": "Tonen op kaart",
|
||||||
"shower": "",
|
"shower": "",
|
||||||
|
"skiing": "",
|
||||||
"slogan": "Bewaar je avonturen!",
|
"slogan": "Bewaar je avonturen!",
|
||||||
"slope": "helling",
|
"slope": "helling",
|
||||||
"someone": "Iemand",
|
"someone": "Iemand",
|
||||||
|
|||||||
@@ -81,6 +81,7 @@
|
|||||||
"create-new-list": "Stwórz nową listę",
|
"create-new-list": "Stwórz nową listę",
|
||||||
"create-waypoint": "Utwórz punkt trasy",
|
"create-waypoint": "Utwórz punkt trasy",
|
||||||
"creation-date": "Data dodania",
|
"creation-date": "Data dodania",
|
||||||
|
"crop": "",
|
||||||
"cross": "Krzyż",
|
"cross": "Krzyż",
|
||||||
"current-password": "Obecne hasło",
|
"current-password": "Obecne hasło",
|
||||||
"cycling": "Rower",
|
"cycling": "Rower",
|
||||||
@@ -235,6 +236,7 @@
|
|||||||
"metric": "Metryczne",
|
"metric": "Metryczne",
|
||||||
"moderate": "Średni",
|
"moderate": "Średni",
|
||||||
"more": "More",
|
"more": "More",
|
||||||
|
"more-route-settings": "",
|
||||||
"mountain": "Góra",
|
"mountain": "Góra",
|
||||||
"mountain-pass": "",
|
"mountain-pass": "",
|
||||||
"must-be-at-least-n-characters-long": "Długość musi wynosić przynajmniej {n} znaków",
|
"must-be-at-least-n-characters-long": "Długość musi wynosić przynajmniej {n} znaków",
|
||||||
@@ -313,6 +315,8 @@
|
|||||||
"radius": "Promień",
|
"radius": "Promień",
|
||||||
"railway-station": "",
|
"railway-station": "",
|
||||||
"read-more": "Czytaj dalej",
|
"read-more": "Czytaj dalej",
|
||||||
|
"recalculate-elevation-data": "",
|
||||||
|
"recalculating-elevation-data-hint": "",
|
||||||
"register": "Zarejestruj",
|
"register": "Zarejestruj",
|
||||||
"remote-users-cannot-edit": "Remote users cannot edit",
|
"remote-users-cannot-edit": "Remote users cannot edit",
|
||||||
"removed-trail-from": "Usunięto szlak z",
|
"removed-trail-from": "Usunięto szlak z",
|
||||||
@@ -366,6 +370,7 @@
|
|||||||
"show-less": "Show less",
|
"show-less": "Show less",
|
||||||
"show-on-map": "Pokaż na mapie",
|
"show-on-map": "Pokaż na mapie",
|
||||||
"shower": "",
|
"shower": "",
|
||||||
|
"skiing": "",
|
||||||
"slogan": "Zapisz swoją wyprawę!",
|
"slogan": "Zapisz swoją wyprawę!",
|
||||||
"slope": "Nachylenie",
|
"slope": "Nachylenie",
|
||||||
"someone": "Ktoś",
|
"someone": "Ktoś",
|
||||||
|
|||||||
@@ -81,6 +81,7 @@
|
|||||||
"create-new-list": "Criar nova lista",
|
"create-new-list": "Criar nova lista",
|
||||||
"create-waypoint": "Create waypoint",
|
"create-waypoint": "Create waypoint",
|
||||||
"creation-date": "Data de criação",
|
"creation-date": "Data de criação",
|
||||||
|
"crop": "",
|
||||||
"cross": "Cross",
|
"cross": "Cross",
|
||||||
"current-password": "Senha atual",
|
"current-password": "Senha atual",
|
||||||
"cycling": "Ciclismo",
|
"cycling": "Ciclismo",
|
||||||
@@ -235,6 +236,7 @@
|
|||||||
"metric": "Métrica",
|
"metric": "Métrica",
|
||||||
"moderate": "Moderado",
|
"moderate": "Moderado",
|
||||||
"more": "More",
|
"more": "More",
|
||||||
|
"more-route-settings": "",
|
||||||
"mountain": "Mountain",
|
"mountain": "Mountain",
|
||||||
"mountain-pass": "",
|
"mountain-pass": "",
|
||||||
"must-be-at-least-n-characters-long": "Deve ter pelo menos {n} caracteres",
|
"must-be-at-least-n-characters-long": "Deve ter pelo menos {n} caracteres",
|
||||||
@@ -313,6 +315,8 @@
|
|||||||
"radius": "Raio",
|
"radius": "Raio",
|
||||||
"railway-station": "",
|
"railway-station": "",
|
||||||
"read-more": "Ler mais",
|
"read-more": "Ler mais",
|
||||||
|
"recalculate-elevation-data": "",
|
||||||
|
"recalculating-elevation-data-hint": "",
|
||||||
"register": "Registo",
|
"register": "Registo",
|
||||||
"remote-users-cannot-edit": "Remote users cannot edit",
|
"remote-users-cannot-edit": "Remote users cannot edit",
|
||||||
"removed-trail-from": "Trilha removida de",
|
"removed-trail-from": "Trilha removida de",
|
||||||
@@ -366,6 +370,7 @@
|
|||||||
"show-less": "Show less",
|
"show-less": "Show less",
|
||||||
"show-on-map": "Mostrar no mapa",
|
"show-on-map": "Mostrar no mapa",
|
||||||
"shower": "",
|
"shower": "",
|
||||||
|
"skiing": "",
|
||||||
"slogan": "Guarde as suas aventuras!",
|
"slogan": "Guarde as suas aventuras!",
|
||||||
"slope": "Inclinação",
|
"slope": "Inclinação",
|
||||||
"someone": "Someone",
|
"someone": "Someone",
|
||||||
|
|||||||
@@ -81,6 +81,7 @@
|
|||||||
"create-new-list": "Создать новый список",
|
"create-new-list": "Создать новый список",
|
||||||
"create-waypoint": "Создать путевую точку",
|
"create-waypoint": "Создать путевую точку",
|
||||||
"creation-date": "Дата создания",
|
"creation-date": "Дата создания",
|
||||||
|
"crop": "",
|
||||||
"cross": "Циклокросс",
|
"cross": "Циклокросс",
|
||||||
"current-password": "Текущий пароль",
|
"current-password": "Текущий пароль",
|
||||||
"cycling": "Велосипед",
|
"cycling": "Велосипед",
|
||||||
@@ -235,6 +236,7 @@
|
|||||||
"metric": "Метрическая",
|
"metric": "Метрическая",
|
||||||
"moderate": "Средний",
|
"moderate": "Средний",
|
||||||
"more": "More",
|
"more": "More",
|
||||||
|
"more-route-settings": "",
|
||||||
"mountain": "Горный",
|
"mountain": "Горный",
|
||||||
"mountain-pass": "",
|
"mountain-pass": "",
|
||||||
"must-be-at-least-n-characters-long": "Минимум {n} символов",
|
"must-be-at-least-n-characters-long": "Минимум {n} символов",
|
||||||
@@ -313,6 +315,8 @@
|
|||||||
"radius": "Радиус",
|
"radius": "Радиус",
|
||||||
"railway-station": "",
|
"railway-station": "",
|
||||||
"read-more": "Подробнее",
|
"read-more": "Подробнее",
|
||||||
|
"recalculate-elevation-data": "",
|
||||||
|
"recalculating-elevation-data-hint": "",
|
||||||
"register": "Регистрация",
|
"register": "Регистрация",
|
||||||
"remote-users-cannot-edit": "Remote users cannot edit",
|
"remote-users-cannot-edit": "Remote users cannot edit",
|
||||||
"removed-trail-from": "Трек удалён из",
|
"removed-trail-from": "Трек удалён из",
|
||||||
@@ -366,6 +370,7 @@
|
|||||||
"show-less": "Show less",
|
"show-less": "Show less",
|
||||||
"show-on-map": "Показать на карте",
|
"show-on-map": "Показать на карте",
|
||||||
"shower": "",
|
"shower": "",
|
||||||
|
"skiing": "",
|
||||||
"slogan": "Сохраняйте ваши приключения!",
|
"slogan": "Сохраняйте ваши приключения!",
|
||||||
"slope": "Уклон",
|
"slope": "Уклон",
|
||||||
"someone": "Кто-то",
|
"someone": "Кто-то",
|
||||||
|
|||||||
@@ -81,6 +81,7 @@
|
|||||||
"create-new-list": "创建新列表",
|
"create-new-list": "创建新列表",
|
||||||
"create-waypoint": "Create waypoint",
|
"create-waypoint": "Create waypoint",
|
||||||
"creation-date": "创建日期",
|
"creation-date": "创建日期",
|
||||||
|
"crop": "",
|
||||||
"cross": "Cross",
|
"cross": "Cross",
|
||||||
"current-password": "当前密码",
|
"current-password": "当前密码",
|
||||||
"cycling": "骑行",
|
"cycling": "骑行",
|
||||||
@@ -235,6 +236,7 @@
|
|||||||
"metric": "公制",
|
"metric": "公制",
|
||||||
"moderate": "中等",
|
"moderate": "中等",
|
||||||
"more": "More",
|
"more": "More",
|
||||||
|
"more-route-settings": "",
|
||||||
"mountain": "Mountain",
|
"mountain": "Mountain",
|
||||||
"mountain-pass": "",
|
"mountain-pass": "",
|
||||||
"must-be-at-least-n-characters-long": "长度至少 {n} 字符",
|
"must-be-at-least-n-characters-long": "长度至少 {n} 字符",
|
||||||
@@ -313,6 +315,8 @@
|
|||||||
"radius": "半径",
|
"radius": "半径",
|
||||||
"railway-station": "",
|
"railway-station": "",
|
||||||
"read-more": "阅读更多",
|
"read-more": "阅读更多",
|
||||||
|
"recalculate-elevation-data": "",
|
||||||
|
"recalculating-elevation-data-hint": "",
|
||||||
"register": "注册",
|
"register": "注册",
|
||||||
"remote-users-cannot-edit": "Remote users cannot edit",
|
"remote-users-cannot-edit": "Remote users cannot edit",
|
||||||
"removed-trail-from": "路线已删除自",
|
"removed-trail-from": "路线已删除自",
|
||||||
@@ -366,6 +370,7 @@
|
|||||||
"show-less": "Show less",
|
"show-less": "Show less",
|
||||||
"show-on-map": "地图中展示",
|
"show-on-map": "地图中展示",
|
||||||
"shower": "",
|
"shower": "",
|
||||||
|
"skiing": "",
|
||||||
"slogan": "保存你的冒险!",
|
"slogan": "保存你的冒险!",
|
||||||
"slope": "坡度",
|
"slope": "坡度",
|
||||||
"someone": "Someone",
|
"someone": "Someone",
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ class GpxMetricsComputation {
|
|||||||
totalElevationLossSmoothed = 0;
|
totalElevationLossSmoothed = 0;
|
||||||
totalDistance = 0;
|
totalDistance = 0;
|
||||||
totalDistanceSmoothed = 0;
|
totalDistanceSmoothed = 0;
|
||||||
|
cumulativeDistance: number[] = []
|
||||||
|
|
||||||
constructor(thresholdXY_m: number, thresholdZ_m: number) {
|
constructor(thresholdXY_m: number, thresholdZ_m: number) {
|
||||||
this.thresholdXY_m = thresholdXY_m;
|
this.thresholdXY_m = thresholdXY_m;
|
||||||
@@ -35,6 +36,7 @@ class GpxMetricsComputation {
|
|||||||
);
|
);
|
||||||
|
|
||||||
this.totalDistance += distance;
|
this.totalDistance += distance;
|
||||||
|
this.cumulativeDistance.push(this.totalDistance)
|
||||||
|
|
||||||
this.lastFilteredPointXY = point;
|
this.lastFilteredPointXY = point;
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ type GPXFeature = {
|
|||||||
centroid: { lat: number; lon: number };
|
centroid: { lat: number; lon: number };
|
||||||
boundingBox: { minLat: number; maxLat: number; minLon: number; maxLon: number };
|
boundingBox: { minLat: number; maxLat: number; minLon: number; maxLon: number };
|
||||||
distance: number;
|
distance: number;
|
||||||
|
cumulativeDistance: number[]
|
||||||
elevationGain?: number;
|
elevationGain?: number;
|
||||||
elevationLoss?: number;
|
elevationLoss?: number;
|
||||||
duration: number;
|
duration: number;
|
||||||
@@ -146,6 +147,7 @@ export default class GPX {
|
|||||||
centroid,
|
centroid,
|
||||||
boundingBox,
|
boundingBox,
|
||||||
distance: totalDistance,
|
distance: totalDistance,
|
||||||
|
cumulativeDistance: metrics.cumulativeDistance,
|
||||||
elevationGain: totalElevationGain,
|
elevationGain: totalElevationGain,
|
||||||
elevationLoss: totalElevationLoss,
|
elevationLoss: totalElevationLoss,
|
||||||
duration: Math.abs(totalDuration),
|
duration: Math.abs(totalDuration),
|
||||||
@@ -153,6 +155,20 @@ export default class GPX {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
flatten() {
|
||||||
|
const points: Waypoint[] = [];
|
||||||
|
|
||||||
|
this.trk?.forEach(track => {
|
||||||
|
track.trkseg?.forEach(segment => {
|
||||||
|
segment.trkpt?.forEach(pt => {
|
||||||
|
points.push(pt);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return points;
|
||||||
|
}
|
||||||
|
|
||||||
private generateMinHash(points: Waypoint[]): string {
|
private generateMinHash(points: Waypoint[]): string {
|
||||||
const hashes = points.map(pt => geohash.encode(pt.$.lat, pt.$.lon));
|
const hashes = points.map(pt => geohash.encode(pt.$.lat, pt.$.lon));
|
||||||
return hashes.sort().join('').slice(0, 10);
|
return hashes.sort().join('').slice(0, 10);
|
||||||
|
|||||||
@@ -10,16 +10,28 @@ import { _ } from "svelte-i18n";
|
|||||||
|
|
||||||
|
|
||||||
const emtpyTrack: Track = { trkseg: [] }
|
const emtpyTrack: Track = { trkseg: [] }
|
||||||
export let route: GPX = new GPX({ trk: [emtpyTrack] });
|
|
||||||
export let anchors: ValhallaAnchor[] = [];
|
class ValhallaStore {
|
||||||
|
route: GPX = $state(new GPX({ trk: [emtpyTrack] }));
|
||||||
|
anchors: ValhallaAnchor[] = $state([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const valhallaStore = new ValhallaStore();
|
||||||
|
|
||||||
|
|
||||||
export function clearRoute() {
|
export function clearRoute() {
|
||||||
route = new GPX({ trk: [emtpyTrack] });
|
valhallaStore.route = new GPX({ trk: [emtpyTrack] });
|
||||||
anchors = [];
|
}
|
||||||
|
|
||||||
|
export function clearAnchors() {
|
||||||
|
for (const anchor of valhallaStore.anchors) {
|
||||||
|
anchor.marker?.remove();
|
||||||
|
}
|
||||||
|
valhallaStore.anchors = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setRoute(newRoute: GPX) {
|
export function setRoute(newRoute: GPX) {
|
||||||
route = newRoute
|
valhallaStore.route = newRoute
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
||||||
@@ -81,41 +93,41 @@ export async function insertIntoRoute(waypoints: Waypoint[], index?: number) {
|
|||||||
const segment = new TrackSegment({ trkpt: waypoints })
|
const segment = new TrackSegment({ trkpt: waypoints })
|
||||||
|
|
||||||
if (index) {
|
if (index) {
|
||||||
route.trk?.at(0)?.trkseg?.splice(index, 0, segment);
|
valhallaStore.route.trk?.at(0)?.trkseg?.splice(index, 0, segment);
|
||||||
} else {
|
} else {
|
||||||
route.trk?.at(0)?.trkseg?.push(segment);
|
valhallaStore.route.trk?.at(0)?.trkseg?.push(segment);
|
||||||
}
|
}
|
||||||
|
|
||||||
route.features = 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 = route.trk?.at(0)?.trkseg?.at(index)
|
const segment = valhallaStore.route.trk?.at(0)?.trkseg?.at(index)
|
||||||
if (segment) {
|
if (segment) {
|
||||||
segment.trkpt = waypoints
|
segment.trkpt = waypoints
|
||||||
}
|
}
|
||||||
route.features = route.getTotals();
|
valhallaStore.route.features = valhallaStore.route.getTotals();
|
||||||
}
|
}
|
||||||
|
|
||||||
export function deleteFromRoute(index: number) {
|
export function deleteFromRoute(index: number) {
|
||||||
route.trk?.at(0)?.trkseg?.splice(index, 1);
|
valhallaStore.route.trk?.at(0)?.trkseg?.splice(index, 1);
|
||||||
route.features = route.getTotals();
|
valhallaStore.route.features = valhallaStore.route.getTotals();
|
||||||
}
|
}
|
||||||
|
|
||||||
export function reverseRoute() {
|
export function reverseRoute() {
|
||||||
for (const trk of route.trk ?? []) {
|
for (const trk of valhallaStore.route.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()
|
||||||
}
|
}
|
||||||
route.trk?.reverse()
|
valhallaStore.route.trk?.reverse()
|
||||||
|
|
||||||
route.features = route.getTotals();
|
valhallaStore.route.features = valhallaStore.route.getTotals();
|
||||||
|
|
||||||
anchors.reverse();
|
valhallaStore.anchors.reverse();
|
||||||
|
|
||||||
anchors.forEach((a, i) => {
|
valhallaStore.anchors.forEach((a, i) => {
|
||||||
if (!a.marker) {
|
if (!a.marker) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -126,15 +138,15 @@ export function reverseRoute() {
|
|||||||
._content.getElementsByTagName("h5")[0];
|
._content.getElementsByTagName("h5")[0];
|
||||||
if (anchorPopupHeading) {
|
if (anchorPopupHeading) {
|
||||||
anchorPopupHeading.textContent =
|
anchorPopupHeading.textContent =
|
||||||
get(_)("route-point") + " #" + (i + 1);
|
get(_)("valhallaStore.route-point") + " #" + (i + 1);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function resetRoute() {
|
export function resetRoute() {
|
||||||
route = new GPX({ trk: [emtpyTrack] });
|
valhallaStore.route = new GPX({ trk: [{ ...emtpyTrack }] });
|
||||||
|
|
||||||
anchors.forEach((a) => {
|
valhallaStore.anchors.forEach((a) => {
|
||||||
if (!a.marker) {
|
if (!a.marker) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -142,13 +154,17 @@ export function resetRoute() {
|
|||||||
a.marker.remove();
|
a.marker.remove();
|
||||||
})
|
})
|
||||||
|
|
||||||
anchors = []
|
valhallaStore.anchors = []
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function recalculateHeight() {
|
||||||
|
await valhallaStore.route.correctElevation();
|
||||||
}
|
}
|
||||||
|
|
||||||
export function normalizeRouteTime() {
|
export function normalizeRouteTime() {
|
||||||
let currentTime = new Date();
|
let currentTime = new Date();
|
||||||
|
|
||||||
for (const seg of route.trk?.at(0)?.trkseg ?? []) {
|
for (const seg of valhallaStore.route.trk?.at(0)?.trkseg ?? []) {
|
||||||
|
|
||||||
if (!seg.trkpt?.length) {
|
if (!seg.trkpt?.length) {
|
||||||
continue
|
continue
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
import GPX from "$lib/models/gpx/gpx";
|
import GPX from "$lib/models/gpx/gpx";
|
||||||
import { Trail } from "$lib/models/trail";
|
import { Trail } from "$lib/models/trail";
|
||||||
import { Waypoint } from "$lib/models/waypoint";
|
|
||||||
import { gpx, kml, tcx } from "$lib/vendor/toGeoJSON/toGeoJSON";
|
import { gpx, kml, tcx } from "$lib/vendor/toGeoJSON/toGeoJSON";
|
||||||
import cryptoRandomString from "crypto-random-string";
|
import cryptoRandomString from "crypto-random-string";
|
||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
@@ -16,6 +15,7 @@ import * as xmldom from 'xmldom';
|
|||||||
import { bbox, splitMultiLineStringToLineStrings } from "./geojson_util";
|
import { bbox, splitMultiLineStringToLineStrings } from "./geojson_util";
|
||||||
import { trails_show } from "$lib/stores/trail_store";
|
import { trails_show } from "$lib/stores/trail_store";
|
||||||
import { handleFromRecordWithIRI } from "./activitypub_util";
|
import { handleFromRecordWithIRI } from "./activitypub_util";
|
||||||
|
import { Waypoint } from "$lib/models/waypoint";
|
||||||
|
|
||||||
|
|
||||||
export async function gpx2trail(gpxString: string, fallbackName?: string, f: (url: RequestInfo | URL, config?: RequestInit) => Promise<Response> = fetch) {
|
export async function gpx2trail(gpxString: string, fallbackName?: string, f: (url: RequestInfo | URL, config?: RequestInit) => Promise<Response> = fetch) {
|
||||||
@@ -25,11 +25,11 @@ export async function gpx2trail(gpxString: string, fallbackName?: string, f: (ur
|
|||||||
if (gpx instanceof Error) {
|
if (gpx instanceof Error) {
|
||||||
throw gpx;
|
throw gpx;
|
||||||
}
|
}
|
||||||
try {
|
// try {
|
||||||
await gpx.correctElevation(f)
|
// await gpx.correctElevation(f)
|
||||||
} catch(e) {
|
// } catch(e) {
|
||||||
console.warn("Unable to correct elevation: " + e)
|
// console.warn("Unable to correct elevation: " + e)
|
||||||
}
|
// }
|
||||||
|
|
||||||
const trail = new Trail("");
|
const trail = new Trail("");
|
||||||
|
|
||||||
@@ -360,3 +360,45 @@ export function toGeoJson(gpxData: string) {
|
|||||||
geojson.bbox = bbox(geojson)
|
geojson.bbox = bbox(geojson)
|
||||||
return geojson
|
return geojson
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function cropGPX(start: GPXWaypoint, end: GPXWaypoint, gpx: GPX): GPX {
|
||||||
|
let foundStart = false;
|
||||||
|
let done = false;
|
||||||
|
|
||||||
|
const croppedTrk = gpx.trk?.map(track => {
|
||||||
|
const croppedSegments: TrackSegment[] = [];
|
||||||
|
|
||||||
|
track.trkseg?.forEach(seg => {
|
||||||
|
const newPoints: GPXWaypoint[] = [];
|
||||||
|
|
||||||
|
for (const pt of seg.trkpt ?? []) {
|
||||||
|
if (!foundStart) {
|
||||||
|
if (pt === start) {
|
||||||
|
foundStart = true;
|
||||||
|
newPoints.push(pt);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (foundStart && !done) {
|
||||||
|
newPoints.push(pt);
|
||||||
|
if (pt === end) {
|
||||||
|
done = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newPoints.length > 0) {
|
||||||
|
croppedSegments.push({ trkpt: newPoints });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
...track,
|
||||||
|
trkseg: croppedSegments,
|
||||||
|
};
|
||||||
|
}).filter(track => track.trkseg.length > 0);
|
||||||
|
|
||||||
|
return new GPX({ ...gpx, trk: croppedTrk ?? [], })
|
||||||
|
}
|
||||||
@@ -13,9 +13,9 @@ import { formatDistance, formatElevation, formatTimeHHMM } from "./format_util";
|
|||||||
import { icons } from "./icon_util";
|
import { icons } from "./icon_util";
|
||||||
|
|
||||||
export class FontawesomeMarker extends M.Marker {
|
export class FontawesomeMarker extends M.Marker {
|
||||||
constructor(options: { icon: string, fontSize?: string, width?: number, backgroundColor?: string, fontColor?: string, id?: string }, markerOptions?: M.MarkerOptions) {
|
constructor(options: { icon: string, fontSize?: string, width?: number, backgroundColor?: string, fontColor?: string, style?: string, id?: string }, markerOptions?: M.MarkerOptions) {
|
||||||
const element = document.createElement('div')
|
const element = document.createElement('div')
|
||||||
element.className = `cursor-pointer flex items-center justify-center w-${options.width ?? 7} aspect-square ${options.backgroundColor ?? "bg-gray-500"} rounded-full text-${options.fontSize ?? "normal"}`
|
element.className = `cursor-pointer flex items-center justify-center w-${options.width ?? 7} aspect-square ${options.backgroundColor ?? "bg-gray-500"} rounded-full text-${options.fontSize ?? "normal"} ${options.style ?? ""}`
|
||||||
element.id = options.id ?? "";
|
element.id = options.id ?? "";
|
||||||
super({ element: element, ...markerOptions });
|
super({ element: element, ...markerOptions });
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
import Datepicker from "$lib/components/base/datepicker.svelte";
|
import Datepicker from "$lib/components/base/datepicker.svelte";
|
||||||
import Select from "$lib/components/base/select.svelte";
|
import Select from "$lib/components/base/select.svelte";
|
||||||
import TextField from "$lib/components/base/text_field.svelte";
|
import TextField from "$lib/components/base/text_field.svelte";
|
||||||
import Textarea from "$lib/components/base/textarea.svelte";
|
|
||||||
import Toggle from "$lib/components/base/toggle.svelte";
|
import Toggle from "$lib/components/base/toggle.svelte";
|
||||||
import ListSelectModal from "$lib/components/list/list_select_modal.svelte";
|
import ListSelectModal from "$lib/components/list/list_select_modal.svelte";
|
||||||
import SummitLogCard from "$lib/components/summit_log/summit_log_card.svelte";
|
import SummitLogCard from "$lib/components/summit_log/summit_log_card.svelte";
|
||||||
@@ -17,6 +16,7 @@
|
|||||||
import { TrailCreateSchema } from "$lib/models/api/trail_schema.js";
|
import { TrailCreateSchema } from "$lib/models/api/trail_schema.js";
|
||||||
import { WaypointCreateSchema } from "$lib/models/api/waypoint_schema.js";
|
import { WaypointCreateSchema } from "$lib/models/api/waypoint_schema.js";
|
||||||
import GPX from "$lib/models/gpx/gpx";
|
import GPX from "$lib/models/gpx/gpx";
|
||||||
|
import GPXWaypoint from "$lib/models/gpx/waypoint";
|
||||||
import type { List } from "$lib/models/list";
|
import type { List } from "$lib/models/list";
|
||||||
import { SummitLog } from "$lib/models/summit_log";
|
import { SummitLog } from "$lib/models/summit_log";
|
||||||
import { Trail } from "$lib/models/trail";
|
import { Trail } from "$lib/models/trail";
|
||||||
@@ -35,26 +35,27 @@
|
|||||||
trails_update,
|
trails_update,
|
||||||
} from "$lib/stores/trail_store.js";
|
} from "$lib/stores/trail_store.js";
|
||||||
import {
|
import {
|
||||||
anchors,
|
valhallaStore,
|
||||||
calculateRouteBetween,
|
calculateRouteBetween,
|
||||||
|
clearAnchors,
|
||||||
clearRoute,
|
clearRoute,
|
||||||
deleteFromRoute,
|
deleteFromRoute,
|
||||||
editRoute,
|
editRoute,
|
||||||
insertIntoRoute,
|
insertIntoRoute,
|
||||||
normalizeRouteTime,
|
normalizeRouteTime,
|
||||||
|
recalculateHeight,
|
||||||
resetRoute,
|
resetRoute,
|
||||||
reverseRoute,
|
reverseRoute,
|
||||||
route,
|
|
||||||
setRoute,
|
setRoute,
|
||||||
} from "$lib/stores/valhalla_store";
|
} from "$lib/stores/valhalla_store.svelte.js";
|
||||||
import { waypoint } from "$lib/stores/waypoint_store";
|
import { waypoint } from "$lib/stores/waypoint_store";
|
||||||
import { getFileURL, readAsDataURLAsync } from "$lib/util/file_util";
|
import { getFileURL } from "$lib/util/file_util";
|
||||||
import {
|
import {
|
||||||
formatDistance,
|
formatDistance,
|
||||||
formatElevation,
|
formatElevation,
|
||||||
formatTimeHHMM,
|
formatTimeHHMM,
|
||||||
} from "$lib/util/format_util";
|
} from "$lib/util/format_util";
|
||||||
import { fromFile, gpx2trail } from "$lib/util/gpx_util";
|
import { cropGPX, fromFile, gpx2trail } from "$lib/util/gpx_util";
|
||||||
|
|
||||||
import { page } from "$app/state";
|
import { page } from "$app/state";
|
||||||
import emptyStateTrailDark from "$lib/assets/svgs/empty_states/empty_state_trail_dark.svg";
|
import emptyStateTrailDark from "$lib/assets/svgs/empty_states/empty_state_trail_dark.svg";
|
||||||
@@ -63,10 +64,11 @@
|
|||||||
type ComboboxItem,
|
type ComboboxItem,
|
||||||
} from "$lib/components/base/combobox.svelte";
|
} from "$lib/components/base/combobox.svelte";
|
||||||
import type { DropdownItem } from "$lib/components/base/dropdown.svelte";
|
import type { DropdownItem } from "$lib/components/base/dropdown.svelte";
|
||||||
|
import Editor from "$lib/components/base/editor.svelte";
|
||||||
import Search, {
|
import Search, {
|
||||||
type SearchItem,
|
type SearchItem,
|
||||||
} from "$lib/components/base/search.svelte";
|
} from "$lib/components/base/search.svelte";
|
||||||
import RoutingOptionsPopup from "$lib/components/trail/routing_options_popup.svelte";
|
import RouteEditor from "$lib/components/trail/route_editor.svelte";
|
||||||
import { TagCreateSchema } from "$lib/models/api/tag_schema.js";
|
import { TagCreateSchema } from "$lib/models/api/tag_schema.js";
|
||||||
import { convertDMSToDD } from "$lib/models/gpx/utils.js";
|
import { convertDMSToDD } from "$lib/models/gpx/utils.js";
|
||||||
import { Tag } from "$lib/models/tag.js";
|
import { Tag } from "$lib/models/tag.js";
|
||||||
@@ -76,10 +78,12 @@
|
|||||||
} from "$lib/stores/search_store.js";
|
} from "$lib/stores/search_store.js";
|
||||||
import { tags_index } from "$lib/stores/tag_store.js";
|
import { tags_index } from "$lib/stores/tag_store.js";
|
||||||
import { theme } from "$lib/stores/theme_store.js";
|
import { theme } from "$lib/stores/theme_store.js";
|
||||||
|
import { currentUser } from "$lib/stores/user_store.js";
|
||||||
import { getIconForLocation } from "$lib/util/icon_util.js";
|
import { getIconForLocation } from "$lib/util/icon_util.js";
|
||||||
import {
|
import {
|
||||||
createAnchorMarker,
|
createAnchorMarker,
|
||||||
createEditTrailMapPopup,
|
createEditTrailMapPopup,
|
||||||
|
FontawesomeMarker,
|
||||||
} from "$lib/util/maplibre_util";
|
} from "$lib/util/maplibre_util";
|
||||||
import EXIF from "$lib/vendor/exif-js/exif.js";
|
import EXIF from "$lib/vendor/exif-js/exif.js";
|
||||||
import { validator } from "@felte/validator-zod";
|
import { validator } from "@felte/validator-zod";
|
||||||
@@ -89,10 +93,8 @@
|
|||||||
import { onMount, untrack } from "svelte";
|
import { onMount, untrack } from "svelte";
|
||||||
import { _ } from "svelte-i18n";
|
import { _ } from "svelte-i18n";
|
||||||
import { backInOut } from "svelte/easing";
|
import { backInOut } from "svelte/easing";
|
||||||
import { scale } from "svelte/transition";
|
import { slide } from "svelte/transition";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { currentUser } from "$lib/stores/user_store.js";
|
|
||||||
import Editor from "$lib/components/base/editor.svelte";
|
|
||||||
|
|
||||||
let { data } = $props();
|
let { data } = $props();
|
||||||
|
|
||||||
@@ -119,6 +121,13 @@
|
|||||||
|
|
||||||
let searchDropdownItems: SearchItem[] = $state([]);
|
let searchDropdownItems: SearchItem[] = $state([]);
|
||||||
|
|
||||||
|
let cropStartMarker: FontawesomeMarker;
|
||||||
|
let cropEndMarker: FontawesomeMarker;
|
||||||
|
|
||||||
|
let flatRoute: GPXWaypoint[] = $derived(valhallaStore.route.flatten())
|
||||||
|
|
||||||
|
let croppedGPX: GPX | null = null;
|
||||||
|
|
||||||
const ClientTrailCreateSchema = TrailCreateSchema.extend({
|
const ClientTrailCreateSchema = TrailCreateSchema.extend({
|
||||||
expand: z
|
expand: z
|
||||||
.object({
|
.object({
|
||||||
@@ -199,13 +208,13 @@
|
|||||||
|
|
||||||
if (
|
if (
|
||||||
(!form.lat || !form.lon) &&
|
(!form.lat || !form.lon) &&
|
||||||
route.trk?.at(0)?.trkseg?.at(0)?.trkpt?.at(0)
|
valhallaStore.route.trk?.at(0)?.trkseg?.at(0)?.trkpt?.at(0)
|
||||||
) {
|
) {
|
||||||
form.lat = route.trk
|
form.lat = valhallaStore.route.trk
|
||||||
?.at(0)
|
?.at(0)
|
||||||
?.trkseg?.at(0)
|
?.trkseg?.at(0)
|
||||||
?.trkpt?.at(0)?.$.lat;
|
?.trkpt?.at(0)?.$.lat;
|
||||||
form.lon = route.trk
|
form.lon = valhallaStore.route.trk
|
||||||
?.at(0)
|
?.at(0)
|
||||||
?.trkseg?.at(0)
|
?.trkseg?.at(0)
|
||||||
?.trkpt?.at(0)?.$.lon;
|
?.trkpt?.at(0)?.$.lon;
|
||||||
@@ -344,6 +353,7 @@
|
|||||||
}
|
}
|
||||||
setRoute(parseResult.gpx);
|
setRoute(parseResult.gpx);
|
||||||
initRouteAnchors(parseResult.gpx);
|
initRouteAnchors(parseResult.gpx);
|
||||||
|
initCropMarkers();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
|
|
||||||
@@ -370,12 +380,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function clearAnchorMarker() {
|
function clearAnchorMarker() {
|
||||||
for (const anchor of anchors) {
|
clearAnchors();
|
||||||
anchor.marker?.remove();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function initRouteAnchors(gpx: GPX) {
|
function initRouteAnchors(gpx: GPX, addToMap: boolean = false) {
|
||||||
const segments = gpx.trk?.at(0)?.trkseg ?? [];
|
const segments = gpx.trk?.at(0)?.trkseg ?? [];
|
||||||
|
|
||||||
for (let i = 0; i < segments.length; i++) {
|
for (let i = 0; i < segments.length; i++) {
|
||||||
@@ -386,21 +394,67 @@
|
|||||||
addAnchor(
|
addAnchor(
|
||||||
points[0].$.lat!,
|
points[0].$.lat!,
|
||||||
points[0].$.lon!,
|
points[0].$.lon!,
|
||||||
anchors.length,
|
valhallaStore.anchors.length,
|
||||||
false,
|
addToMap,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (i == segments.length - 1) {
|
if (i == segments.length - 1) {
|
||||||
addAnchor(
|
addAnchor(
|
||||||
points[points.length - 1].$.lat!,
|
points[points.length - 1].$.lat!,
|
||||||
points[points.length - 1].$.lon!,
|
points[points.length - 1].$.lon!,
|
||||||
anchors.length,
|
valhallaStore.anchors.length,
|
||||||
false,
|
addToMap,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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: "z-10",
|
||||||
|
width: 4,
|
||||||
|
backgroundColor: "bg-primary",
|
||||||
|
fontColor: "white",
|
||||||
|
},
|
||||||
|
{},
|
||||||
|
);
|
||||||
|
cropEndMarker = new FontawesomeMarker(
|
||||||
|
{
|
||||||
|
id: "crop-end-marker",
|
||||||
|
icon: "fa fa-flag-checkered",
|
||||||
|
fontSize: "xs",
|
||||||
|
style: "z-10",
|
||||||
|
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();
|
||||||
}
|
}
|
||||||
@@ -545,25 +599,25 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
drawingActive = true;
|
drawingActive = true;
|
||||||
if (!route.trk?.at(0)?.trkseg?.at(0)?.trkpt?.length) {
|
if (!valhallaStore.route.trk?.at(0)?.trkseg?.at(0)?.trkpt?.length) {
|
||||||
}
|
}
|
||||||
for (const anchor of anchors) {
|
for (const anchor of valhallaStore.anchors) {
|
||||||
anchor.marker?.addTo(map);
|
anchor.marker?.addTo(map);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function stopDrawing() {
|
async function stopDrawing() {
|
||||||
drawingActive = false;
|
drawingActive = false;
|
||||||
for (const anchor of anchors) {
|
for (const anchor of valhallaStore.anchors) {
|
||||||
anchor.marker?.remove();
|
anchor.marker?.remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (route.trk?.at(0)?.trkseg?.at(0)?.trkpt?.at(0)) {
|
if (valhallaStore.route.trk?.at(0)?.trkseg?.at(0)?.trkpt?.at(0)) {
|
||||||
$formData.lat = 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 = 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;
|
||||||
@@ -593,9 +647,9 @@
|
|||||||
});
|
});
|
||||||
mapPopup.addTo(map!);
|
mapPopup.addTo(map!);
|
||||||
} else {
|
} else {
|
||||||
const anchorCount = anchors.length;
|
const anchorCount = valhallaStore.anchors.length;
|
||||||
if (anchorCount == 0) {
|
if (anchorCount == 0) {
|
||||||
addAnchor(e.lngLat.lat, e.lngLat.lng, anchors.length);
|
addAnchor(e.lngLat.lat, e.lngLat.lng, valhallaStore.anchors.length);
|
||||||
} else {
|
} else {
|
||||||
await addAnchorAndRecalculate(e.lngLat.lat, e.lngLat.lng);
|
await addAnchorAndRecalculate(e.lngLat.lat, e.lngLat.lng);
|
||||||
}
|
}
|
||||||
@@ -603,8 +657,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function addAnchorAndRecalculate(lat: number, lon: number) {
|
async function addAnchorAndRecalculate(lat: number, lon: number) {
|
||||||
const previousAnchor = anchors[anchors.length - 1];
|
const previousAnchor = valhallaStore.anchors[valhallaStore.anchors.length - 1];
|
||||||
const anchor = addAnchor(lat, lon, anchors.length);
|
const anchor = addAnchor(lat, lon, valhallaStore.anchors.length);
|
||||||
const markerText = startAnchorLoading(anchor);
|
const markerText = startAnchorLoading(anchor);
|
||||||
try {
|
try {
|
||||||
const routeWaypoints = await calculateRouteBetween(
|
const routeWaypoints = await calculateRouteBetween(
|
||||||
@@ -645,10 +699,10 @@
|
|||||||
lon,
|
lon,
|
||||||
index + 1,
|
index + 1,
|
||||||
() => {
|
() => {
|
||||||
removeAnchor(anchors.findIndex((a) => a.id == anchor.id));
|
removeAnchor(valhallaStore.anchors.findIndex((a) => a.id == anchor.id));
|
||||||
},
|
},
|
||||||
() => {
|
() => {
|
||||||
const thisAnchor = anchors.find((a) => a.id == anchor.id);
|
const thisAnchor = valhallaStore.anchors.find((a) => a.id == anchor.id);
|
||||||
addAnchorAndRecalculate(
|
addAnchorAndRecalculate(
|
||||||
thisAnchor?.lat ?? lat,
|
thisAnchor?.lat ?? lat,
|
||||||
thisAnchor?.lon ?? lon,
|
thisAnchor?.lon ?? lon,
|
||||||
@@ -666,7 +720,7 @@
|
|||||||
anchor.lat = position.lat;
|
anchor.lat = position.lat;
|
||||||
anchor.lon = position.lng;
|
anchor.lon = position.lng;
|
||||||
await recalculateRoute(
|
await recalculateRoute(
|
||||||
anchors.findIndex((a) => a.id == anchor.id),
|
valhallaStore.anchors.findIndex((a) => a.id == anchor.id),
|
||||||
);
|
);
|
||||||
draggingMarker = false;
|
draggingMarker = false;
|
||||||
},
|
},
|
||||||
@@ -675,7 +729,7 @@
|
|||||||
marker.addTo(map);
|
marker.addTo(map);
|
||||||
}
|
}
|
||||||
anchor.marker = marker;
|
anchor.marker = marker;
|
||||||
anchors.splice(index, 0, anchor);
|
valhallaStore.anchors.splice(index, 0, anchor);
|
||||||
|
|
||||||
return anchor;
|
return anchor;
|
||||||
}
|
}
|
||||||
@@ -709,10 +763,10 @@
|
|||||||
if (!drawingActive) {
|
if (!drawingActive) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
anchors[anchorIndex]?.marker?.remove();
|
valhallaStore.anchors[anchorIndex]?.marker?.remove();
|
||||||
anchors.splice(anchorIndex, 1);
|
valhallaStore.anchors.splice(anchorIndex, 1);
|
||||||
for (let i = anchorIndex; i < anchors.length; i++) {
|
for (let i = anchorIndex; i < valhallaStore.anchors.length; i++) {
|
||||||
const anchor = anchors[i];
|
const anchor = valhallaStore.anchors[i];
|
||||||
const markerIcon = anchor.marker?.getElement();
|
const markerIcon = anchor.marker?.getElement();
|
||||||
if (markerIcon) {
|
if (markerIcon) {
|
||||||
const markerText = markerIcon.textContent ?? "0";
|
const markerText = markerIcon.textContent ?? "0";
|
||||||
@@ -730,7 +784,7 @@
|
|||||||
if ($formData.expand?.gpx_data) {
|
if ($formData.expand?.gpx_data) {
|
||||||
updateTrailWithRouteData();
|
updateTrailWithRouteData();
|
||||||
}
|
}
|
||||||
} else if (anchorIndex == anchors.length) {
|
} else if (anchorIndex == valhallaStore.anchors.length) {
|
||||||
deleteFromRoute(anchorIndex - 1);
|
deleteFromRoute(anchorIndex - 1);
|
||||||
updateTrailWithRouteData();
|
updateTrailWithRouteData();
|
||||||
} else {
|
} else {
|
||||||
@@ -740,17 +794,17 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function recalculateRoute(anchorIndex: number) {
|
async function recalculateRoute(anchorIndex: number) {
|
||||||
const markerText = startAnchorLoading(anchors[anchorIndex]);
|
const markerText = startAnchorLoading(valhallaStore.anchors[anchorIndex]);
|
||||||
|
|
||||||
const anchor = anchors[anchorIndex];
|
const anchor = valhallaStore.anchors[anchorIndex];
|
||||||
if (!anchor) {
|
if (!anchor) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let nextRouteSegment;
|
let nextRouteSegment;
|
||||||
let previousRouteSegment;
|
let previousRouteSegment;
|
||||||
try {
|
try {
|
||||||
if (anchorIndex < anchors.length - 1) {
|
if (anchorIndex < valhallaStore.anchors.length - 1) {
|
||||||
const nextAnchor = anchors[anchorIndex + 1];
|
const nextAnchor = valhallaStore.anchors[anchorIndex + 1];
|
||||||
|
|
||||||
nextRouteSegment = await calculateRouteBetween(
|
nextRouteSegment = await calculateRouteBetween(
|
||||||
anchor.lat,
|
anchor.lat,
|
||||||
@@ -761,7 +815,7 @@
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (anchorIndex > 0) {
|
if (anchorIndex > 0) {
|
||||||
const previousAnchor = anchors[anchorIndex - 1];
|
const previousAnchor = valhallaStore.anchors[anchorIndex - 1];
|
||||||
previousRouteSegment = await calculateRouteBetween(
|
previousRouteSegment = await calculateRouteBetween(
|
||||||
previousAnchor.lat,
|
previousAnchor.lat,
|
||||||
previousAnchor.lon,
|
previousAnchor.lon,
|
||||||
@@ -789,7 +843,7 @@
|
|||||||
type: "error",
|
type: "error",
|
||||||
});
|
});
|
||||||
} finally {
|
} finally {
|
||||||
stopAnchorLoading(anchors[anchorIndex], markerText);
|
stopAnchorLoading(valhallaStore.anchors[anchorIndex], markerText);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -807,8 +861,8 @@
|
|||||||
);
|
);
|
||||||
const markerText = startAnchorLoading(anchor);
|
const markerText = startAnchorLoading(anchor);
|
||||||
|
|
||||||
for (let i = data.segment + 2; i < anchors.length; i++) {
|
for (let i = data.segment + 2; i < valhallaStore.anchors.length; i++) {
|
||||||
const anchor = anchors[i];
|
const anchor = valhallaStore.anchors[i];
|
||||||
const markerIcon = anchor.marker?.getElement();
|
const markerIcon = anchor.marker?.getElement();
|
||||||
if (markerIcon) {
|
if (markerIcon) {
|
||||||
const markerText = markerIcon.textContent ?? "0";
|
const markerText = markerIcon.textContent ?? "0";
|
||||||
@@ -821,8 +875,8 @@
|
|||||||
$_("route-point") + " #" + newIndex;
|
$_("route-point") + " #" + newIndex;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const previousAnchor = anchors[data.segment];
|
const previousAnchor = valhallaStore.anchors[data.segment];
|
||||||
const nextAnchor = anchors[data.segment + 2];
|
const nextAnchor = valhallaStore.anchors[data.segment + 2];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const previousRouteSegment = await calculateRouteBetween(
|
const previousRouteSegment = await calculateRouteBetween(
|
||||||
@@ -868,14 +922,97 @@
|
|||||||
updateTrailWithRouteData();
|
updateTrailWithRouteData();
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateTrailWithRouteData() {
|
async function recalculateElevationData() {
|
||||||
overwriteGPX = true;
|
await recalculateHeight();
|
||||||
const totals = route.features;
|
|
||||||
|
updateTrailWithRouteData();
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleCropMarkers(active: boolean) {
|
||||||
|
if (active) {
|
||||||
|
cropStartMarker?.setOpacity("1");
|
||||||
|
cropEndMarker?.setOpacity("1");
|
||||||
|
} else {
|
||||||
|
cropStartMarker?.setOpacity("0");
|
||||||
|
cropEndMarker?.setOpacity("0");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateCropMarkers(range: [start: number, end: number]) {
|
||||||
|
const [start, end] = range;
|
||||||
|
|
||||||
|
const targetStartDistance = valhallaStore.route.features.distance * (start / 100);
|
||||||
|
const [startLon, startLat, startIndex] = getCoordinateAtDistance(
|
||||||
|
flatRoute,
|
||||||
|
valhallaStore.route.features.cumulativeDistance,
|
||||||
|
targetStartDistance,
|
||||||
|
);
|
||||||
|
|
||||||
|
const targetEndDistance = valhallaStore.route.features.distance * (end / 100);
|
||||||
|
const [endLon, endLat, endIndex] = getCoordinateAtDistance(
|
||||||
|
flatRoute,
|
||||||
|
valhallaStore.route.features.cumulativeDistance,
|
||||||
|
targetEndDistance,
|
||||||
|
);
|
||||||
|
|
||||||
|
cropStartMarker.setLngLat([startLon, startLat]);
|
||||||
|
cropEndMarker.setLngLat([endLon, endLat]);
|
||||||
|
|
||||||
|
croppedGPX = cropGPX(flatRoute[startIndex], flatRoute[endIndex], valhallaStore.route);
|
||||||
|
const totals = croppedGPX.features;
|
||||||
$formData.distance = totals.distance;
|
$formData.distance = totals.distance;
|
||||||
$formData.duration = totals.duration / 1000;
|
$formData.duration = totals.duration / 1000;
|
||||||
$formData.elevation_gain = totals.elevationGain;
|
$formData.elevation_gain = totals.elevationGain;
|
||||||
$formData.elevation_loss = totals.elevationLoss;
|
$formData.elevation_loss = totals.elevationLoss;
|
||||||
$formData.expand!.gpx_data = route.toString();
|
}
|
||||||
|
|
||||||
|
function confirmCrop() {
|
||||||
|
if (!croppedGPX) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setRoute(croppedGPX);
|
||||||
|
updateTrailWithRouteData();
|
||||||
|
clearAnchorMarker();
|
||||||
|
initRouteAnchors(croppedGPX, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCoordinateAtDistance(
|
||||||
|
points: GPXWaypoint[],
|
||||||
|
cumulative: number[],
|
||||||
|
target: number,
|
||||||
|
) {
|
||||||
|
let low = 0,
|
||||||
|
high = cumulative.length - 1;
|
||||||
|
|
||||||
|
while (low < high) {
|
||||||
|
const mid = Math.floor((low + high) / 2);
|
||||||
|
if (cumulative[mid] < target) low = mid + 1;
|
||||||
|
else high = mid;
|
||||||
|
}
|
||||||
|
|
||||||
|
const i = Math.max(1, low);
|
||||||
|
const prevDist = cumulative[i - 1];
|
||||||
|
const nextDist = cumulative[i];
|
||||||
|
const ratio = (target - prevDist) / (nextDist - prevDist);
|
||||||
|
|
||||||
|
const prev = points[i - 1];
|
||||||
|
const next = points[i];
|
||||||
|
|
||||||
|
return [
|
||||||
|
prev.$.lon! + (next.$.lon! - prev.$.lon!) * ratio,
|
||||||
|
prev.$.lat! + (next.$.lat! - prev.$.lat!) * ratio,
|
||||||
|
i,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateTrailWithRouteData() {
|
||||||
|
overwriteGPX = true;
|
||||||
|
const totals = valhallaStore.route.features;
|
||||||
|
$formData.distance = totals.distance;
|
||||||
|
$formData.duration = totals.duration / 1000;
|
||||||
|
$formData.elevation_gain = totals.elevationGain;
|
||||||
|
$formData.elevation_loss = totals.elevationLoss;
|
||||||
|
$formData.expand!.gpx_data = valhallaStore.route.toString();
|
||||||
|
|
||||||
if (!$formData.id) {
|
if (!$formData.id) {
|
||||||
$formData.id = cryptoRandomString({ length: 15 });
|
$formData.id = cryptoRandomString({ length: 15 });
|
||||||
@@ -1288,15 +1425,19 @@
|
|||||||
<div class="relative">
|
<div class="relative">
|
||||||
{#if drawingActive}
|
{#if drawingActive}
|
||||||
<div
|
<div
|
||||||
in:scale={{ easing: backInOut }}
|
in:slide={{ easing: backInOut, axis: "x" }}
|
||||||
out:scale={{ easing: backInOut }}
|
out:slide={{ easing: backInOut, axis: "x" }}
|
||||||
class="absolute top-8 left-2 z-50"
|
class="absolute top-8 left-2 z-50"
|
||||||
>
|
>
|
||||||
<RoutingOptionsPopup
|
<RouteEditor
|
||||||
bind:options={routingOptions}
|
bind:options={routingOptions}
|
||||||
onReverse={reverseTrail}
|
onReverse={reverseTrail}
|
||||||
onReset={resetTrail}
|
onReset={resetTrail}
|
||||||
></RoutingOptionsPopup>
|
onCropToggle={toggleCropMarkers}
|
||||||
|
onCrop={confirmCrop}
|
||||||
|
onUpdateCropRange={updateCropMarkers}
|
||||||
|
onRecalculateElevationData={recalculateElevationData}
|
||||||
|
></RouteEditor>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
<div id="trail-map">
|
<div id="trail-map">
|
||||||
|
|||||||
@@ -9,12 +9,12 @@ export default defineConfig({
|
|||||||
ssr: { noExternal: ['three'] },
|
ssr: { noExternal: ['three'] },
|
||||||
...(process.env.WANDERER_ENV == "dev" ? {
|
...(process.env.WANDERER_ENV == "dev" ? {
|
||||||
server: {
|
server: {
|
||||||
https: {
|
// https: {
|
||||||
key: fs.readFileSync('.svelte-kit/key.pem'),
|
// key: fs.readFileSync('.svelte-kit/key.pem'),
|
||||||
cert: fs.readFileSync('.svelte-kit/cert.pem')
|
// cert: fs.readFileSync('.svelte-kit/cert.pem')
|
||||||
},
|
// },
|
||||||
host: true, // true
|
// host: true, // true
|
||||||
port: 443 // 443
|
// port: 443 // 443
|
||||||
}
|
}
|
||||||
} : {})
|
} : {})
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user