uses map with elevation for edit mode

This commit is contained in:
Christian Beutel
2024-04-20 22:04:06 +02:00
parent 90e482dc59
commit 13f0f5ca8a
7 changed files with 62 additions and 141 deletions

View File

@@ -56,6 +56,7 @@ services:
UPLOAD_FOLDER: /app/uploads
UPLOAD_USER:
UPLOAD_PASSWORD:
PUBLIC_VALHALLA_URL: http://localhost:8002
volumes:
- ./data/uploads:/app/uploads
ports:
@@ -63,6 +64,20 @@ services:
networks:
- wanderer
restart: unless-stopped
valhalla:
image: ghcr.io/gis-ops/docker-valhalla/valhalla:latest
ports:
- "8002:8002"
volumes:
- ./data/valhalla:/custom_files
environment:
- tile_urls=https://download.geofabrik.de/europe/germany-latest.osm.pbf
- use_tiles_ignore_pbf=True
- force_rebuild=False
- force_rebuild_elevation=False
- build_elevation=True
- build_admins=True
- build_time_zones=True
networks:
wanderer:

View File

@@ -7,7 +7,6 @@ import { isRouteProtected } from '$lib/util/authorization_util'
import { redirect, type Handle } from '@sveltejs/kit'
import { MeiliSearch } from 'meilisearch'
import { locale } from 'svelte-i18n'
import { get } from 'svelte/store'
export const handle: Handle = async ({ event, resolve }) => {
// load the store data from the request cookie string

View File

@@ -2,11 +2,14 @@
import { page } from "$app/stores";
import type { Trail } from "$lib/models/trail";
import { createMarkerFromWaypoint } from "$lib/util/leaflet_util";
import "$lib/vendor/leaflet-elevation/src/index.css";
import type AutoGraticule from "$lib/vendor/leaflet-graticule/leaflet-auto-graticule";
import type { Map, Marker } from "leaflet";
import "leaflet.awesome-markers/dist/leaflet.awesome-markers.css";
import "leaflet/dist/leaflet.css";
import { createEventDispatcher, onMount } from "svelte";
export let trail: Trail;
export let trail: Trail | null;
export let markers: Marker[] = [];
export let map: Map | null = null;
export let options = {};
@@ -17,7 +20,8 @@
let L: any;
let controlElevation: any;
$: if (trail.expand.gpx_data && controlElevation) {
$: if (trail?.expand.gpx_data && controlElevation) {
controlElevation.clear();
controlElevation.load(trail.expand.gpx_data);
}
@@ -30,8 +34,8 @@
const AutoGraticule = (await import("$lib/vendor/leaflet-graticule/leaflet-auto-graticule")).default;
map = L.map("map", { preferCanvas: true }).setView(
[trail.lat ?? 0, trail.lon ?? 0],
14,
[trail?.lat ?? 0, trail?.lon ?? 0],
3,
);
map!.attributionControl.setPrefix(false);
@@ -105,7 +109,7 @@
controlElevation = L.control.elevation(elevation_options).addTo(map);
for (const waypoint of trail.expand.waypoints) {
for (const waypoint of trail?.expand.waypoints ?? []) {
const marker = createMarkerFromWaypoint(L, waypoint);
marker.addTo(map!);
markers.push(marker);

View File

@@ -22,6 +22,7 @@
validationSchema: waypointSchema,
onSubmit: async (submittedWaypoint) => {
dispatch("save", submittedWaypoint);
closeModal!();
},
});

View File

@@ -2,6 +2,7 @@ import { Trail } from "$lib/models/trail";
import { Waypoint } from "$lib/models/waypoint";
import { kml, tcx } from "$lib/vendor/toGeoJSON/toGeoJSON"
import GeoJsonToGpx from "$lib/vendor/geoJSONToGPX"
import { browser } from "$app/environment";
function calculateDistance(lat1: number, lon1: number, lat2: number, lon2: number): number {
const R = 6371; // Radius of the Earth in km
@@ -17,8 +18,15 @@ function calculateDistance(lat1: number, lon1: number, lat2: number, lon2: numbe
}
export async function gpx2trail(gpx: string) {
let xml;
if (browser) {
const parser = new DOMParser();
xml = parser.parseFromString(gpx, "application/xml")
} else {
const JSDOM = (await import("jsdom")).JSDOM
const xml = new JSDOM(gpx).window.document
xml = new JSDOM(gpx).window.document
}
const trail = new Trail("");
let name = xml.getElementsByTagName('name');

View File

@@ -0,0 +1,16 @@
import { error, json, type RequestEvent } from "@sveltejs/kit";
import { env } from '$env/dynamic/public'
export async function POST(event: RequestEvent) {
const data = await event.request.json()
if(!env.PUBLIC_VALHALLA_URL) {
return error(400, "PUBLIC_VALHALLA_URL not set")
}
try {
const r = await event.fetch(env.PUBLIC_VALHALLA_URL + '/route', data);
return json(r);
} catch (e: any) {
throw error(e.status, e)
}
}

View File

@@ -1,5 +1,4 @@
<script lang="ts">
import { page } from "$app/stores";
import Button from "$lib/components/base/button.svelte";
import Datepicker from "$lib/components/base/datepicker.svelte";
import Select from "$lib/components/base/select.svelte";
@@ -9,6 +8,7 @@
import ListSelectModal from "$lib/components/list/list_select_modal.svelte";
import SummitLogCard from "$lib/components/summit_log/summit_log_card.svelte";
import SummitLogModal from "$lib/components/summit_log/summit_log_modal.svelte";
import MapWithElevation from "$lib/components/trail/map_with_elevation.svelte";
import PhotoPicker from "$lib/components/trail/photo_picker.svelte";
import WaypointCard from "$lib/components/waypoint/waypoint_card.svelte";
import WaypointModal from "$lib/components/waypoint/waypoint_modal.svelte";
@@ -37,14 +37,11 @@
formatElevation,
formatTimeHHMM,
} from "$lib/util/format_util";
import { fromKML, fromTCX } from "$lib/util/gpx_util";
import { fromKML, fromTCX, gpx2trail } from "$lib/util/gpx_util";
import { createMarkerFromWaypoint } from "$lib/util/leaflet_util";
import "$lib/vendor/leaflet-elevation/src/index.css";
import { createForm } from "$lib/vendor/svelte-form-lib";
import cryptoRandomString from "crypto-random-string";
import type { GPX, Icon, LeafletEvent, Map } from "leaflet";
import "leaflet.awesome-markers/dist/leaflet.awesome-markers.css";
import "leaflet/dist/leaflet.css";
import type { Map } from "leaflet";
import { onMount } from "svelte";
import { _ } from "svelte-i18n";
import { array, number, object, string } from "yup";
@@ -54,8 +51,6 @@
let L: any;
let map: Map;
let gpxLayer: GPX;
let openWaypointModal: () => void;
let openSummitLogModal: () => void;
let openListSelectModal: () => void;
@@ -157,129 +152,9 @@
onMount(async () => {
L = (await import("leaflet")).default;
await import("leaflet-gpx");
await import("leaflet.awesome-markers");
map = L.map("map").setView([0, 0], 2);
map.attributionControl.setPrefix(false);
L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
attribution: "© OpenStreetMap contributors",
}).addTo(map);
if (
data.trail.expand.gpx_data &&
data.trail.expand.gpx_data.length > 0
) {
addGPXLayer(data.trail.expand.gpx_data, false);
}
if (data.trail.expand.waypoints?.length > 0) {
for (const waypoint of data.trail.expand.waypoints) {
const marker = createMarkerFromWaypoint(
L,
waypoint,
(event) => {
var marker = event.target;
var position = marker.getLatLng();
const editableWaypoint = $form.expand.waypoints.find(
(w) => w.id == waypoint.id,
);
editableWaypoint!.lat = position.lat;
editableWaypoint!.lon = position.lng;
$form.expand.waypoints = [...$form.expand.waypoints];
},
);
marker.addTo(map);
}
}
});
function addGPXLayer(gpx: string, addWaypoints: boolean = true) {
return new Promise<void>(function (resolve, reject) {
gpxLayer?.remove();
gpxLayer = new L.GPX(gpx, {
async: true,
polyline_options: {
className: "lightblue-theme elevation-polyline",
opacity: 0.75,
weight: 5,
},
gpx_options: {
parseElements: [
"track",
"route",
...(addWaypoints ? ["waypoint"] : []),
],
},
marker_options: {
wptIcons: {
"": L.AwesomeMarkers.icon({
icon: "circle",
prefix: "fa",
markerColor: "cadetblue",
iconColor: "white",
}) as Icon,
},
startIcon: L.AwesomeMarkers.icon({
icon: "circle-half-stroke",
prefix: "fa",
markerColor: "cadetblue",
iconColor: "white",
}) as Icon,
endIcon: L.AwesomeMarkers.icon({
icon: "flag-checkered",
prefix: "fa",
markerColor: "cadetblue",
iconColor: "white",
}) as Icon,
startIconUrl: "",
endIconUrl: "",
shadowUrl: "",
},
})
.on("addpoint", function (e: any) {
if (e.point_type === "start") {
e.point.setZIndexOffset(1000);
$form.lat = e.point._latlng.lat;
$form.lon = e.point._latlng.lng;
} else if (e.point_type === "waypoint") {
const waypoint = new Waypoint(
e.point._latlng.lat,
e.point._latlng.lng,
{ name: e.point.options.title, marker: e.point },
);
$form.expand.waypoints.push(waypoint);
}
})
.on("loaded", function (e: LeafletEvent) {
map.fitBounds(e.target.getBounds());
if (!$form.id) {
$form.name = e.target._info.name;
}
$form.distance = Math.round(e.target.get_distance());
$form.elevation_gain = Math.round(
e.target.get_elevation_gain(),
);
$form.duration = Math.round(
e.target.get_total_time() / 1000 / 60,
);
if ($form.duration && !$form.date) {
$form.date = e.target
.get_start_time()
.toISOString()
.substring(0, 10);
} else if (!$form.date) {
$form.date = new Date().toISOString().substring(0, 10);
}
resolve();
})
.on("error", reject)
.addTo(map);
});
}
function openFileBrowser() {
document.getElementById("fileInput")!.click();
}
@@ -314,8 +189,11 @@
gpxFile = selectedFile;
}
try {
await addGPXLayer(gpxData);
$form = await gpx2trail(gpxData);
$form.expand.gpx_data = gpxData;
} catch (e) {
console.log(e);
show_toast({
icon: "close",
type: "error",
@@ -671,7 +549,7 @@
{loading}>{$_("save-trail")}</Button
>
</form>
<div class="rounded-xl" id="map"></div>
<MapWithElevation trail={$form} bind:map></MapWithElevation>
</main>
<WaypointModal
bind:openModal={openWaypointModal}