adds mapwithelevationmultiple to map page
This commit is contained in:
@@ -135,7 +135,7 @@
|
||||
{/each}
|
||||
</ul>
|
||||
<div class:hidden={!showMap}>
|
||||
<MapWithElevationMultiple trails={$list.expand?.trails ?? []} bind:map
|
||||
<MapWithElevationMultiple trails={$list.expand?.trails ?? []} bind:map options={{itinerary: true, flyToBounds: true}}
|
||||
></MapWithElevationMultiple>
|
||||
</div>
|
||||
<div class="min-w-0" class:hidden={showMap}>
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
type SearchItem,
|
||||
} from "$lib/components/base/search.svelte";
|
||||
import EmptyStateSearch from "$lib/components/empty_states/empty_state_search.svelte";
|
||||
import MapWithElevationMultiple from "$lib/components/trail/map_with_elevation_multiple.svelte";
|
||||
import TrailCard from "$lib/components/trail/trail_card.svelte";
|
||||
import TrailFilterPanel from "$lib/components/trail/trail_filter_panel.svelte";
|
||||
import type { Settings } from "$lib/models/settings";
|
||||
@@ -19,21 +20,12 @@
|
||||
trails,
|
||||
trails_search_bounding_box,
|
||||
} from "$lib/stores/trail_store";
|
||||
import { currentUser } from "$lib/stores/user_store";
|
||||
import { country_codes } from "$lib/util/country_code_util";
|
||||
import { getFileURL } from "$lib/util/file_util";
|
||||
import {
|
||||
formatDistance,
|
||||
formatElevation,
|
||||
formatTimeHHMM,
|
||||
} from "$lib/util/format_util";
|
||||
import "$lib/vendor/leaflet-elevation/src/index.css";
|
||||
import type {
|
||||
GPX,
|
||||
Icon,
|
||||
LatLng,
|
||||
LatLngBoundsExpression,
|
||||
LeafletEvent,
|
||||
Map,
|
||||
Marker,
|
||||
} from "leaflet";
|
||||
@@ -42,11 +34,8 @@
|
||||
import { onMount } from "svelte";
|
||||
import { _ } from "svelte-i18n";
|
||||
import { slide } from "svelte/transition";
|
||||
let L: any;
|
||||
let map: Map;
|
||||
let gpxLayers: Record<string, GPX> = {};
|
||||
let startMarkers: Record<string, Marker> = {};
|
||||
|
||||
let mapWithElevation: MapWithElevationMultiple;
|
||||
let searchDropdownItems: SearchItem[] = [];
|
||||
|
||||
let showFilter: boolean = false;
|
||||
@@ -56,103 +45,7 @@
|
||||
const maxBoundingBox: TrailBoundingBox = $page.data.boundingBox;
|
||||
const settings: Settings = $page.data.settings;
|
||||
|
||||
onMount(async () => {
|
||||
L = (await import("leaflet")).default;
|
||||
await import("leaflet-gpx");
|
||||
await import("leaflet.awesome-markers");
|
||||
|
||||
map = L.map("map").setView([0, 0], 4);
|
||||
map.attributionControl.setPrefix(false);
|
||||
|
||||
const baseLayer = L.tileLayer(
|
||||
"https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
|
||||
{
|
||||
attribution: "© OpenStreetMap contributors",
|
||||
},
|
||||
);
|
||||
|
||||
const topoLayer = L.tileLayer(
|
||||
"https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png ",
|
||||
{
|
||||
attribution: "© OpenStreetMap contributors",
|
||||
},
|
||||
);
|
||||
|
||||
const baseMaps = {
|
||||
OpenStreetMaps: baseLayer,
|
||||
OpenTopoMaps: topoLayer,
|
||||
};
|
||||
|
||||
L.control.layers(baseMaps).addTo(map);
|
||||
|
||||
switch (localStorage.getItem("layer")) {
|
||||
case "OpenTopoMaps":
|
||||
topoLayer.addTo(map);
|
||||
break;
|
||||
default:
|
||||
baseLayer.addTo(map);
|
||||
break;
|
||||
}
|
||||
|
||||
map.on("baselayerchange", function (e) {
|
||||
localStorage.setItem("layer", e.name);
|
||||
});
|
||||
|
||||
map.on("moveend", async (e) => {
|
||||
const bounds = map.getBounds();
|
||||
await searchTrails(bounds.getNorthEast(), bounds.getSouthWest());
|
||||
|
||||
$page.url.searchParams.set("tl_lat", bounds.getNorth().toString());
|
||||
$page.url.searchParams.set("tl_lon", bounds.getEast().toString());
|
||||
$page.url.searchParams.set("br_lat", bounds.getSouth().toString());
|
||||
$page.url.searchParams.set("br_lon", bounds.getWest().toString());
|
||||
|
||||
goto(`?${$page.url.searchParams.toString()}`);
|
||||
});
|
||||
|
||||
const lat = $page.url.searchParams.get("lat");
|
||||
const lon = $page.url.searchParams.get("lon");
|
||||
|
||||
const tl_lat = $page.url.searchParams.get("tl_lat");
|
||||
const tl_lon = $page.url.searchParams.get("tl_lon");
|
||||
const br_lat = $page.url.searchParams.get("br_lat");
|
||||
const br_lon = $page.url.searchParams.get("br_lon");
|
||||
|
||||
if (lat && lon) {
|
||||
map.setView([parseFloat(lat), parseFloat(lon)], 12);
|
||||
} else if (tl_lat && tl_lon && br_lat && br_lon) {
|
||||
const boundingBox: LatLngBoundsExpression = [
|
||||
[parseFloat(tl_lat), parseFloat(tl_lon)],
|
||||
[parseFloat(br_lat), parseFloat(br_lon)],
|
||||
];
|
||||
|
||||
map.fitBounds(boundingBox);
|
||||
} else if (settings && settings.mapFocus == "trails") {
|
||||
const boundingBox: LatLngBoundsExpression = [
|
||||
[maxBoundingBox.max_lat, maxBoundingBox.min_lon],
|
||||
[maxBoundingBox.min_lat, maxBoundingBox.max_lon],
|
||||
];
|
||||
map.fitBounds(boundingBox);
|
||||
} else if (
|
||||
settings &&
|
||||
settings.mapFocus == "location" &&
|
||||
settings.location
|
||||
) {
|
||||
map.setView([settings.location.lat, settings.location.lon], 12);
|
||||
} else {
|
||||
navigator.geolocation.getCurrentPosition(
|
||||
(position) => {
|
||||
const lat = position.coords.latitude;
|
||||
const lon = position.coords.longitude;
|
||||
|
||||
map.setView([lat, lon], 13);
|
||||
},
|
||||
(error) => {
|
||||
console.error("Error getting user location:", error);
|
||||
},
|
||||
);
|
||||
}
|
||||
});
|
||||
onMount(async () => {});
|
||||
|
||||
async function search(q: string) {
|
||||
const r = await fetch("/api/v1/search/multi", {
|
||||
@@ -209,103 +102,59 @@
|
||||
southWest,
|
||||
filter,
|
||||
);
|
||||
|
||||
for (const newTrail of changes.added) {
|
||||
const gpxLayer = await addGPXLayer(newTrail);
|
||||
|
||||
if (gpxLayer) {
|
||||
gpxLayers[newTrail.id!] = gpxLayer;
|
||||
}
|
||||
}
|
||||
|
||||
for (const deletedTrail of changes.deleted) {
|
||||
map.removeLayer(gpxLayers[deletedTrail.id!]);
|
||||
delete gpxLayers[deletedTrail.id!];
|
||||
delete startMarkers[deletedTrail.id!];
|
||||
}
|
||||
}
|
||||
|
||||
function addGPXLayer(trail: Trail) {
|
||||
return new Promise<GPX>(function (resolve, reject) {
|
||||
if (!trail.expand.gpx_data) {
|
||||
reject();
|
||||
}
|
||||
|
||||
const thumbnail = trail.photos.length
|
||||
? getFileURL(trail, trail.photos[trail.thumbnail])
|
||||
: "/imgs/default_thumbnail.webp";
|
||||
const gpxLayer = new L.GPX(trail.expand.gpx_data!, {
|
||||
async: true,
|
||||
polyline_options: {
|
||||
className: "lightblue-theme elevation-polyline",
|
||||
weight: 5,
|
||||
},
|
||||
gpx_options: {
|
||||
parseElements: ["track", "route"],
|
||||
},
|
||||
marker_options: {
|
||||
startIcon: L.AwesomeMarkers.icon({
|
||||
icon: "circle-half-stroke",
|
||||
prefix: "fa",
|
||||
markerColor: "cadetblue",
|
||||
iconColor: "white",
|
||||
}) as Icon,
|
||||
startIconUrl: "",
|
||||
endIconUrl: "",
|
||||
shadowUrl: "",
|
||||
},
|
||||
})
|
||||
.on("addpoint", function (e: any) {
|
||||
if (e.point_type === "start") {
|
||||
const marker: Marker = e.point as Marker;
|
||||
startMarkers[trail.id!] = marker;
|
||||
marker.bindPopup(
|
||||
`<a href="map/trail/${trail.id}">
|
||||
<li class="flex items-center gap-4 cursor-pointer text-black">
|
||||
<div class="shrink-0"><img class="h-14 w-14 object-cover rounded-xl" src="${thumbnail}" alt="">
|
||||
</div>
|
||||
<div>
|
||||
<h4 class="font-semibold text-lg">${trail.name}</h4>
|
||||
<div class="flex gap-x-4">
|
||||
${trail.location ? `<h5><i class="fa fa-location-dot mr-2"></i>${trail.location}</h5>` : ""}
|
||||
<h5><i class="fa fa-gauge mr-2"></i>${$_(trail.difficulty as string)}</h5>
|
||||
</div>
|
||||
<div class="flex mt-2 gap-4 text-sm text-gray-500"><span class="shrink-0"><i
|
||||
class="fa fa-left-right mr-2"></i>${formatDistance(
|
||||
trail.distance,
|
||||
)}</span> <span class="shrink-0"><i class="fa fa-up-down mr-2"></i>${formatElevation(
|
||||
trail.elevation_gain,
|
||||
)}</span> <span class="shrink-0"><i class="fa fa-clock mr-2"></i>${formatTimeHHMM(
|
||||
trail.duration,
|
||||
)}</span></div>
|
||||
</div>
|
||||
</li>
|
||||
</a>`,
|
||||
);
|
||||
}
|
||||
})
|
||||
.on("loaded", function (e: LeafletEvent) {
|
||||
resolve(gpxLayer);
|
||||
})
|
||||
.on("error", reject)
|
||||
.addTo(map);
|
||||
});
|
||||
function handleTrailCardMouseEnter(index: number) {
|
||||
mapWithElevation.openPopup(index)
|
||||
}
|
||||
|
||||
function handleTrailCardMouseEnter(trail: Trail) {
|
||||
const marker: Marker = startMarkers[trail.id!];
|
||||
marker?.openPopup();
|
||||
}
|
||||
|
||||
function handleTrailCardMouseLeave(trail: Trail) {
|
||||
const marker: Marker = startMarkers[trail.id!];
|
||||
marker?.closePopup();
|
||||
function handleTrailCardMouseLeave(index: number) {
|
||||
mapWithElevation.closePopup(index)
|
||||
}
|
||||
|
||||
async function handleFilterUpdate(filter: TrailFilter) {
|
||||
const bounds = map.getBounds();
|
||||
await searchTrails(bounds.getNorthEast(), bounds.getSouthWest());
|
||||
}
|
||||
|
||||
async function handleMapMove() {
|
||||
const bounds = map.getBounds();
|
||||
await searchTrails(bounds.getNorthEast(), bounds.getSouthWest());
|
||||
|
||||
$page.url.searchParams.set("tl_lat", bounds.getNorth().toString());
|
||||
$page.url.searchParams.set("tl_lon", bounds.getEast().toString());
|
||||
$page.url.searchParams.set("br_lat", bounds.getSouth().toString());
|
||||
$page.url.searchParams.set("br_lon", bounds.getWest().toString());
|
||||
|
||||
goto(`?${$page.url.searchParams.toString()}`);
|
||||
}
|
||||
|
||||
function handleMapInit() {
|
||||
if (settings && settings.mapFocus == "trails") {
|
||||
const boundingBox: LatLngBoundsExpression = [
|
||||
[maxBoundingBox.max_lat, maxBoundingBox.min_lon],
|
||||
[maxBoundingBox.min_lat, maxBoundingBox.max_lon],
|
||||
];
|
||||
map.fitBounds(boundingBox);
|
||||
} else if (
|
||||
settings &&
|
||||
settings.mapFocus == "location" &&
|
||||
settings.location
|
||||
) {
|
||||
map.setView([settings.location.lat, settings.location.lon], 12);
|
||||
} else {
|
||||
navigator.geolocation.getCurrentPosition(
|
||||
(position) => {
|
||||
const lat = position.coords.latitude;
|
||||
const lon = position.coords.longitude;
|
||||
map.setView([lat, lon], 13);
|
||||
},
|
||||
(error) => {
|
||||
console.error("Error getting user location:", error);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
@@ -357,22 +206,27 @@
|
||||
{#if $trails.length == 0}
|
||||
<EmptyStateSearch></EmptyStateSearch>
|
||||
{/if}
|
||||
{#each $trails as trail}
|
||||
{#each $trails as trail,i}
|
||||
<a href="map/trail/{trail.id}">
|
||||
<TrailCard
|
||||
{trail}
|
||||
on:mouseenter={() => handleTrailCardMouseEnter(trail)}
|
||||
on:mouseleave={() => handleTrailCardMouseLeave(trail)}
|
||||
on:mouseenter={() => handleTrailCardMouseEnter(i)}
|
||||
on:mouseleave={() => handleTrailCardMouseLeave(i)}
|
||||
></TrailCard>
|
||||
</a>
|
||||
{/each}
|
||||
{/if}
|
||||
</div>
|
||||
<div
|
||||
id="map"
|
||||
class="rounded-xl z-0"
|
||||
class:hidden={!showMap && browser && window.innerWidth < 768}
|
||||
></div>
|
||||
<div class:hidden={!showMap && browser && window.innerWidth < 768}>
|
||||
<MapWithElevationMultiple
|
||||
on:moveend={handleMapMove}
|
||||
on:init={handleMapInit}
|
||||
trails={$trails}
|
||||
options={{ flyToBounds: false }}
|
||||
bind:map
|
||||
bind:this={mapWithElevation}
|
||||
></MapWithElevationMultiple>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<style>
|
||||
|
||||
Reference in New Issue
Block a user