uses map with elevation for edit mode
This commit is contained in:
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
validationSchema: waypointSchema,
|
||||
onSubmit: async (submittedWaypoint) => {
|
||||
dispatch("save", submittedWaypoint);
|
||||
|
||||
closeModal!();
|
||||
},
|
||||
});
|
||||
|
||||
@@ -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');
|
||||
|
||||
16
web/src/routes/api/v1/valhalla/+server.ts
Normal file
16
web/src/routes/api/v1/valhalla/+server.ts
Normal 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)
|
||||
}
|
||||
}
|
||||
@@ -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}
|
||||
|
||||
Reference in New Issue
Block a user