refactor map popups and add inspect trackpoint feature

This commit is contained in:
vcoppe
2024-10-08 15:49:14 +02:00
parent d823f44558
commit 711825f5a3
14 changed files with 396 additions and 289 deletions

View File

@@ -13,7 +13,6 @@
import { get, writable } from 'svelte/store';
import { customBasemapUpdate, getLayers } from './utils';
import { OverpassLayer } from './OverpassLayer';
import OverpassPopup from './OverpassPopup.svelte';
let container: HTMLDivElement;
let overpassLayer: OverpassLayer;
@@ -34,7 +33,7 @@
if ($map) {
let basemap = basemaps.hasOwnProperty($currentBasemap)
? basemaps[$currentBasemap]
: $customLayers[$currentBasemap]?.value ?? basemaps[defaultBasemap];
: ($customLayers[$currentBasemap]?.value ?? basemaps[defaultBasemap]);
$map.removeImport('basemap');
if (typeof basemap === 'string') {
$map.addImport({ id: 'basemap', url: basemap }, 'overlays');
@@ -211,8 +210,6 @@
</div>
</CustomControl>
<OverpassPopup />
<svelte:window
on:click={(e) => {
if (open && !cancelEvents && !container.contains(e.target)) {

View File

@@ -1,11 +1,10 @@
import SphericalMercator from "@mapbox/sphericalmercator";
import { getLayers } from "./utils";
import mapboxgl from "mapbox-gl";
import { get, writable } from "svelte/store";
import { liveQuery } from "dexie";
import { db, settings } from "$lib/db";
import { overpassQueryData } from "$lib/assets/layers";
import { tick } from "svelte";
import { MapPopup } from "$lib/components/MapPopup";
const {
currentOverpassQueries
@@ -15,15 +14,6 @@ const mercator = new SphericalMercator({
size: 256,
});
export const overpassPopupPOI = writable<Record<string, any> | null>(null);
export const overpassPopup = new mapboxgl.Popup({
closeButton: false,
focusAfterOpen: false,
maxWidth: undefined,
offset: 15,
});
let data = writable<GeoJSON.FeatureCollection>({ type: 'FeatureCollection', features: [] });
liveQuery(() => db.overpassdata.toArray()).subscribe((pois) => {
@@ -36,6 +26,7 @@ export class OverpassLayer {
queryZoom = 12;
expirationTime = 7 * 24 * 3600 * 1000;
map: mapboxgl.Map;
popup: MapPopup;
currentQueries: Set<string> = new Set();
nextQueries: Map<string, { x: number, y: number, queries: string[] }> = new Map();
@@ -44,10 +35,15 @@ export class OverpassLayer {
queryIfNeededBinded = this.queryIfNeeded.bind(this);
updateBinded = this.update.bind(this);
onHoverBinded = this.onHover.bind(this);
maybeHidePopupBinded = this.maybeHidePopup.bind(this);
constructor(map: mapboxgl.Map) {
this.map = map;
this.popup = new MapPopup(map, {
closeButton: false,
focusAfterOpen: false,
maxWidth: undefined,
offset: 15,
});
}
add() {
@@ -127,30 +123,12 @@ export class OverpassLayer {
}
onHover(e: any) {
overpassPopupPOI.set({
...e.features[0].properties,
sym: overpassQueryData[e.features[0].properties.query].symbol ?? ''
this.popup.setItem({
item: {
...e.features[0].properties,
sym: overpassQueryData[e.features[0].properties.query].symbol ?? ''
}
});
tick().then(() => {
// Show the popup once the content component has been rendered
overpassPopup.setLngLat(e.features[0].geometry.coordinates);
overpassPopup.addTo(this.map);
this.map.on('mousemove', this.maybeHidePopupBinded);
});
}
maybeHidePopup(e: any) {
let poi = get(overpassPopupPOI);
if (poi && this.map.project([poi.lon, poi.lat]).dist(this.map.project(e.lngLat)) > 60) {
this.hideWaypointPopup();
}
}
hideWaypointPopup() {
overpassPopupPOI.set(null);
overpassPopup.remove();
this.map.off('mousemove', this.maybeHidePopupBinded);
}
query(bbox: [number, number, number, number]) {

View File

@@ -1,102 +1,92 @@
<script lang="ts">
import * as Card from '$lib/components/ui/card';
import { Button } from '$lib/components/ui/button';
import { overpassPopup, overpassPopupPOI } from './OverpassLayer';
import { selection } from '$lib/components/file-list/Selection';
import { PencilLine, MapPin } from 'lucide-svelte';
import { onMount } from 'svelte';
import { _ } from 'svelte-i18n';
import { dbUtils } from '$lib/db';
import type { PopupItem } from '$lib/components/MapPopup';
let popupElement: HTMLDivElement;
onMount(() => {
overpassPopup.setDOMContent(popupElement);
popupElement.classList.remove('hidden');
});
export let poi: PopupItem<any>;
let tags = {};
let name = '';
$: if ($overpassPopupPOI) {
tags = JSON.parse($overpassPopupPOI.tags);
$: if (poi) {
tags = JSON.parse(poi.item.tags);
if (tags.name !== undefined && tags.name !== '') {
name = tags.name;
} else {
name = $_(`layers.label.${$overpassPopupPOI.query}`);
name = $_(`layers.label.${poi.item.query}`);
}
}
</script>
<div bind:this={popupElement} class="hidden">
{#if $overpassPopupPOI}
<Card.Root class="border-none shadow-md text-base p-2 max-w-[50dvw]">
<Card.Header class="p-0">
<Card.Title class="text-md">
<div class="flex flex-row gap-3">
<div class="flex flex-col">
{name}
<div class="text-muted-foreground text-sm font-normal">
{$overpassPopupPOI.lat.toFixed(6)}&deg; {$overpassPopupPOI.lon.toFixed(6)}&deg;
</div>
</div>
<Button
class="ml-auto p-1.5 h-8"
variant="outline"
href="https://www.openstreetmap.org/edit?editor=id&node={$overpassPopupPOI.id}"
target="_blank"
>
<PencilLine size="16" />
</Button>
<Card.Root class="border-none shadow-md text-base p-2 max-w-[50dvw]">
<Card.Header class="p-0">
<Card.Title class="text-md">
<div class="flex flex-row gap-3">
<div class="flex flex-col">
{name}
<div class="text-muted-foreground text-sm font-normal">
{poi.item.lat.toFixed(6)}&deg; {poi.item.lon.toFixed(6)}&deg;
</div>
</Card.Title>
</Card.Header>
{#if tags.image || tags['image:0']}
<div class="w-full rounded-md overflow-clip my-2 max-w-96 mx-auto">
<!-- svelte-ignore a11y-missing-attribute -->
<img src={tags.image ?? tags['image:0']} />
</div>
{/if}
<Card.Content class="flex flex-col p-0 text-sm mt-1 whitespace-normal break-all">
<div class="grid grid-cols-[auto_auto] gap-x-3">
{#each Object.entries(tags) as [key, value]}
{#if key !== 'name' && !key.includes('image')}
<span class="font-mono">{key}</span>
{#if key === 'website' || key === 'contact:website' || key === 'contact:facebook' || key === 'contact:instagram' || key === 'contact:twitter'}
<a href={value} target="_blank" class="text-link underline">{value}</a>
{:else if key === 'phone' || key === 'contact:phone'}
<a href={'tel:' + value} class="text-link underline">{value}</a>
{:else if key === 'email' || key === 'contact:email'}
<a href={'mailto:' + value} class="text-link underline">{value}</a>
{:else}
<span>{value}</span>
{/if}
{/if}
{/each}
</div>
<Button
class="mt-2"
class="ml-auto p-1.5 h-8"
variant="outline"
disabled={$selection.size === 0}
on:click={() => {
let desc = Object.entries(tags)
.map(([key, value]) => `${key}: ${value}`)
.join('\n');
dbUtils.addOrUpdateWaypoint({
attributes: {
lat: $overpassPopupPOI.lat,
lon: $overpassPopupPOI.lon
},
name: name,
desc: desc,
cmt: desc,
sym: $overpassPopupPOI.sym
});
}}
href="https://www.openstreetmap.org/edit?editor=id&node={poi.item.id}"
target="_blank"
>
<MapPin size="16" class="mr-1" />
{$_('toolbar.waypoint.add')}
<PencilLine size="16" />
</Button>
</Card.Content>
</Card.Root>
</div>
</Card.Title>
</Card.Header>
{#if tags.image || tags['image:0']}
<div class="w-full rounded-md overflow-clip my-2 max-w-96 mx-auto">
<!-- svelte-ignore a11y-missing-attribute -->
<img src={tags.image ?? tags['image:0']} />
</div>
{/if}
</div>
<Card.Content class="flex flex-col p-0 text-sm mt-1 whitespace-normal break-all">
<div class="grid grid-cols-[auto_auto] gap-x-3">
{#each Object.entries(tags) as [key, value]}
{#if key !== 'name' && !key.includes('image')}
<span class="font-mono">{key}</span>
{#if key === 'website' || key === 'contact:website' || key === 'contact:facebook' || key === 'contact:instagram' || key === 'contact:twitter'}
<a href={value} target="_blank" class="text-link underline">{value}</a>
{:else if key === 'phone' || key === 'contact:phone'}
<a href={'tel:' + value} class="text-link underline">{value}</a>
{:else if key === 'email' || key === 'contact:email'}
<a href={'mailto:' + value} class="text-link underline">{value}</a>
{:else}
<span>{value}</span>
{/if}
{/if}
{/each}
</div>
<Button
class="mt-2"
variant="outline"
disabled={$selection.size === 0}
on:click={() => {
let desc = Object.entries(tags)
.map(([key, value]) => `${key}: ${value}`)
.join('\n');
dbUtils.addOrUpdateWaypoint({
attributes: {
lat: poi.item.lat,
lon: poi.item.lon
},
name: name,
desc: desc,
cmt: desc,
sym: poi.item.sym
});
}}
>
<MapPin size="16" class="mr-1" />
{$_('toolbar.waypoint.add')}
</Button>
</Card.Content>
</Card.Root>