diff --git a/docker-compose.yml b/docker-compose.yml index 7bcad73e..0dfd8ccb 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -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: diff --git a/web/src/hooks.server.ts b/web/src/hooks.server.ts index ff726593..b2658fe9 100644 --- a/web/src/hooks.server.ts +++ b/web/src/hooks.server.ts @@ -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 diff --git a/web/src/lib/components/trail/map_with_elevation.svelte b/web/src/lib/components/trail/map_with_elevation.svelte index 39aaeb7d..fe233fd5 100644 --- a/web/src/lib/components/trail/map_with_elevation.svelte +++ b/web/src/lib/components/trail/map_with_elevation.svelte @@ -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); diff --git a/web/src/lib/components/waypoint/waypoint_modal.svelte b/web/src/lib/components/waypoint/waypoint_modal.svelte index 8f2bfe20..bee308e6 100644 --- a/web/src/lib/components/waypoint/waypoint_modal.svelte +++ b/web/src/lib/components/waypoint/waypoint_modal.svelte @@ -20,8 +20,9 @@ const { form, errors, handleChange, handleSubmit } = createForm({ initialValues: $waypoint, validationSchema: waypointSchema, - onSubmit: async (submittedWaypoint) => { - dispatch("save", submittedWaypoint); + onSubmit: async (submittedWaypoint) => { + dispatch("save", submittedWaypoint); + closeModal!(); }, }); diff --git a/web/src/lib/util/gpx_util.ts b/web/src/lib/util/gpx_util.ts index c6d93820..2e5e5d36 100644 --- a/web/src/lib/util/gpx_util.ts +++ b/web/src/lib/util/gpx_util.ts @@ -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) { - const JSDOM = (await import("jsdom")).JSDOM - const xml = new JSDOM(gpx).window.document + let xml; + if (browser) { + const parser = new DOMParser(); + xml = parser.parseFromString(gpx, "application/xml") + } else { + const JSDOM = (await import("jsdom")).JSDOM + xml = new JSDOM(gpx).window.document + } + const trail = new Trail(""); let name = xml.getElementsByTagName('name'); diff --git a/web/src/routes/api/v1/valhalla/+server.ts b/web/src/routes/api/v1/valhalla/+server.ts new file mode 100644 index 00000000..275b5228 --- /dev/null +++ b/web/src/routes/api/v1/valhalla/+server.ts @@ -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) + } +} \ No newline at end of file diff --git a/web/src/routes/trail/edit/[id]/+page.svelte b/web/src/routes/trail/edit/[id]/+page.svelte index cd747bec..dabf307c 100644 --- a/web/src/routes/trail/edit/[id]/+page.svelte +++ b/web/src/routes/trail/edit/[id]/+page.svelte @@ -1,5 +1,4 @@