mirror of
https://github.com/gpxstudio/gpx.studio.git
synced 2026-02-06 00:13:09 +00:00
switch to maplibre, but laggy
This commit is contained in:
@@ -42,13 +42,8 @@
|
||||
let maxZoom: number = $state(20);
|
||||
let layerType: 'basemap' | 'overlay' = $state('basemap');
|
||||
let resourceType: 'raster' | 'vector' = $derived.by(() => {
|
||||
if (tileUrls[0].length > 0) {
|
||||
if (
|
||||
tileUrls[0].includes('.json') ||
|
||||
(tileUrls[0].includes('api.mapbox.com/styles') && !tileUrls[0].includes('tiles'))
|
||||
) {
|
||||
return 'vector';
|
||||
}
|
||||
if (tileUrls[0].length > 0 && tileUrls[0].includes('.json')) {
|
||||
return 'vector';
|
||||
}
|
||||
return 'raster';
|
||||
});
|
||||
|
||||
@@ -5,12 +5,8 @@
|
||||
import { Separator } from '$lib/components/ui/separator';
|
||||
import { ScrollArea } from '$lib/components/ui/scroll-area/index.js';
|
||||
import { Layers } from '@lucide/svelte';
|
||||
import { basemaps, defaultBasemap, overlays } from '$lib/assets/layers';
|
||||
import { settings } from '$lib/logic/settings';
|
||||
import { map } from '$lib/components/map/map';
|
||||
import { customBasemapUpdate, getLayers } from './utils';
|
||||
import type { ImportSpecification, StyleSpecification } from 'mapbox-gl';
|
||||
import { untrack } from 'svelte';
|
||||
|
||||
let container: HTMLDivElement;
|
||||
let overpassLayer: OverpassLayer;
|
||||
@@ -23,125 +19,14 @@
|
||||
selectedBasemapTree,
|
||||
selectedOverlayTree,
|
||||
selectedOverpassTree,
|
||||
customLayers,
|
||||
opacities,
|
||||
} = settings;
|
||||
|
||||
function setStyle() {
|
||||
if (!$map) {
|
||||
return;
|
||||
}
|
||||
let basemap = basemaps.hasOwnProperty($currentBasemap)
|
||||
? basemaps[$currentBasemap]
|
||||
: ($customLayers[$currentBasemap]?.value ?? basemaps[defaultBasemap]);
|
||||
$map.removeImport('basemap');
|
||||
if (typeof basemap === 'string') {
|
||||
$map.addImport({ id: 'basemap', url: basemap }, 'overlays');
|
||||
} else {
|
||||
$map.addImport(
|
||||
{
|
||||
id: 'basemap',
|
||||
url: '',
|
||||
data: basemap as StyleSpecification,
|
||||
},
|
||||
'overlays'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$effect(() => {
|
||||
if ($map && ($currentBasemap || $customBasemapUpdate)) {
|
||||
untrack(() => setStyle());
|
||||
}
|
||||
});
|
||||
|
||||
function addOverlay(id: string) {
|
||||
if (!$map) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
let overlay = $customLayers.hasOwnProperty(id) ? $customLayers[id].value : overlays[id];
|
||||
if (typeof overlay === 'string') {
|
||||
$map.addImport({ id, url: overlay });
|
||||
} else {
|
||||
if ($opacities.hasOwnProperty(id)) {
|
||||
overlay = {
|
||||
...overlay,
|
||||
layers: (overlay as StyleSpecification).layers.map((layer) => {
|
||||
if (layer.type === 'raster') {
|
||||
if (!layer.paint) {
|
||||
layer.paint = {};
|
||||
}
|
||||
layer.paint['raster-opacity'] = $opacities[id];
|
||||
}
|
||||
return layer;
|
||||
}),
|
||||
};
|
||||
}
|
||||
$map.addImport({
|
||||
id,
|
||||
url: '',
|
||||
data: overlay as StyleSpecification,
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
// No reliable way to check if the map is ready to add sources and layers
|
||||
}
|
||||
}
|
||||
|
||||
function updateOverlays() {
|
||||
if ($map && $currentOverlays && $opacities) {
|
||||
let overlayLayers = getLayers($currentOverlays);
|
||||
try {
|
||||
let activeOverlays =
|
||||
$map
|
||||
.getStyle()
|
||||
.imports?.reduce(
|
||||
(
|
||||
acc: Record<string, ImportSpecification>,
|
||||
imprt: ImportSpecification
|
||||
) => {
|
||||
if (!['basemap', 'overlays'].includes(imprt.id)) {
|
||||
acc[imprt.id] = imprt;
|
||||
}
|
||||
return acc;
|
||||
},
|
||||
{}
|
||||
) || {};
|
||||
let toRemove = Object.keys(activeOverlays).filter((id) => !overlayLayers[id]);
|
||||
toRemove.forEach((id) => {
|
||||
$map?.removeImport(id);
|
||||
});
|
||||
let toAdd = Object.entries(overlayLayers)
|
||||
.filter(([id, selected]) => selected && !activeOverlays.hasOwnProperty(id))
|
||||
.map(([id]) => id);
|
||||
toAdd.forEach((id) => {
|
||||
addOverlay(id);
|
||||
});
|
||||
} catch (e) {
|
||||
// No reliable way to check if the map is ready to add sources and layers
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$effect(() => {
|
||||
if ($map && $currentOverlays && $opacities) {
|
||||
untrack(() => updateOverlays());
|
||||
}
|
||||
});
|
||||
|
||||
map.onLoad((_map: mapboxgl.Map) => {
|
||||
map.onLoad((_map: maplibregl.Map) => {
|
||||
if (overpassLayer) {
|
||||
overpassLayer.remove();
|
||||
}
|
||||
overpassLayer = new OverpassLayer(_map);
|
||||
overpassLayer.add();
|
||||
let first = true;
|
||||
_map.on('style.import.load', () => {
|
||||
if (!first) return;
|
||||
first = false;
|
||||
updateOverlays();
|
||||
});
|
||||
});
|
||||
|
||||
let open = $state(false);
|
||||
|
||||
@@ -213,7 +213,9 @@
|
||||
isSelected($currentOverlays, selectedOverlay)
|
||||
) {
|
||||
try {
|
||||
$map.removeImport(selectedOverlay);
|
||||
if ($map.getLayer(selectedOverlay)) {
|
||||
$map.removeLayer(selectedOverlay);
|
||||
}
|
||||
} catch (e) {
|
||||
// No reliable way to check if the map is ready to remove sources and layers
|
||||
}
|
||||
|
||||
@@ -54,28 +54,27 @@
|
||||
|
||||
<Card.Root class="border-none shadow-md text-base p-2 max-w-[50dvw] gap-0">
|
||||
<Card.Header class="p-0 gap-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-xs font-normal">
|
||||
{poi.item.lat.toFixed(6)}° {poi.item.lon.toFixed(6)}°
|
||||
</div>
|
||||
<Card.Title class="text-md flex flex-row">
|
||||
<div class="flex flex-col">
|
||||
<p>{name}</p>
|
||||
<div class="text-muted-foreground text-xs font-normal">
|
||||
{poi.item.lat.toFixed(6)}° {poi.item.lon.toFixed(6)}°
|
||||
</div>
|
||||
<Button
|
||||
class="ml-auto"
|
||||
variant="outline"
|
||||
size="icon"
|
||||
href="https://www.openstreetmap.org/edit?editor=id&{poi.item.type ??
|
||||
'node'}={poi.item.id}"
|
||||
target="_blank"
|
||||
>
|
||||
<PencilLine size="16" />
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<Button
|
||||
class="ml-auto"
|
||||
variant="outline"
|
||||
size="icon-sm"
|
||||
href="https://www.openstreetmap.org/edit?editor=id&{poi.item.type ?? 'node'}={poi
|
||||
.item.id}"
|
||||
target="_blank"
|
||||
>
|
||||
<PencilLine size="16" />
|
||||
</Button>
|
||||
</Card.Title>
|
||||
</Card.Header>
|
||||
<Card.Content class="flex flex-col p-0 text-sm mt-1 whitespace-normal break-all">
|
||||
<Card.Content class="flex flex-col gap-1 p-0 text-sm whitespace-normal break-all">
|
||||
<ScrollArea class="flex flex-col max-h-[30dvh]">
|
||||
{#if tags.image || tags['image:0']}
|
||||
<div class="w-full rounded-md overflow-clip my-2 max-w-96 mx-auto">
|
||||
@@ -100,8 +99,14 @@
|
||||
{/each}
|
||||
</div>
|
||||
</ScrollArea>
|
||||
<Button class="mt-2" variant="outline" disabled={$selection.size === 0} onclick={addToFile}>
|
||||
<MapPin size="16" />
|
||||
<Button
|
||||
size="sm"
|
||||
class="mt-1 justify-start"
|
||||
variant="outline"
|
||||
disabled={$selection.size === 0}
|
||||
onclick={addToFile}
|
||||
>
|
||||
<MapPin size="14" />
|
||||
{i18n._('toolbar.waypoint.add')}
|
||||
</Button>
|
||||
</Card.Content>
|
||||
|
||||
@@ -103,7 +103,7 @@ export class ExtensionAPI {
|
||||
if (current && isSelected(current, overlay.id)) {
|
||||
show = true;
|
||||
try {
|
||||
get(map)?.removeImport(overlay.id);
|
||||
get(map)?.removeLayer(overlay.id);
|
||||
} catch (e) {
|
||||
// No reliable way to check if the map is ready to remove sources and layers
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@ import { overpassQueryData } from '$lib/assets/layers';
|
||||
import { MapPopup } from '$lib/components/map/map-popup';
|
||||
import { settings } from '$lib/logic/settings';
|
||||
import { db } from '$lib/db';
|
||||
import type { GeoJSONSource } from 'maplibre-gl';
|
||||
import { ANCHOR_LAYER_KEY } from '../style';
|
||||
|
||||
const { currentOverpassQueries } = settings;
|
||||
|
||||
@@ -20,11 +22,11 @@ liveQuery(() => db.overpassdata.toArray()).subscribe((pois) => {
|
||||
});
|
||||
|
||||
export class OverpassLayer {
|
||||
overpassUrl = 'https://overpass.private.coffee/api/interpreter';
|
||||
overpassUrl = 'https://maps.mail.ru/osm/tools/overpass/api/interpreter';
|
||||
minZoom = 12;
|
||||
queryZoom = 12;
|
||||
expirationTime = 7 * 24 * 3600 * 1000;
|
||||
map: mapboxgl.Map;
|
||||
map: maplibregl.Map;
|
||||
popup: MapPopup;
|
||||
|
||||
currentQueries: Set<string> = new Set();
|
||||
@@ -35,7 +37,7 @@ export class OverpassLayer {
|
||||
updateBinded = this.update.bind(this);
|
||||
onHoverBinded = this.onHover.bind(this);
|
||||
|
||||
constructor(map: mapboxgl.Map) {
|
||||
constructor(map: maplibregl.Map) {
|
||||
this.map = map;
|
||||
this.popup = new MapPopup(map, {
|
||||
closeButton: false,
|
||||
@@ -47,7 +49,7 @@ export class OverpassLayer {
|
||||
|
||||
add() {
|
||||
this.map.on('moveend', this.queryIfNeededBinded);
|
||||
this.map.on('style.import.load', this.updateBinded);
|
||||
this.map.on('style.load', this.updateBinded);
|
||||
this.unsubscribes.push(data.subscribe(this.updateBinded));
|
||||
this.unsubscribes.push(
|
||||
currentOverpassQueries.subscribe(() => {
|
||||
@@ -74,7 +76,7 @@ export class OverpassLayer {
|
||||
let d = get(data);
|
||||
|
||||
try {
|
||||
let source = this.map.getSource('overpass') as mapboxgl.GeoJSONSource | undefined;
|
||||
let source = this.map.getSource('overpass') as GeoJSONSource | undefined;
|
||||
if (source) {
|
||||
source.setData(d);
|
||||
} else {
|
||||
@@ -85,17 +87,20 @@ export class OverpassLayer {
|
||||
}
|
||||
|
||||
if (!this.map.getLayer('overpass')) {
|
||||
this.map.addLayer({
|
||||
id: 'overpass',
|
||||
type: 'symbol',
|
||||
source: 'overpass',
|
||||
layout: {
|
||||
'icon-image': ['get', 'icon'],
|
||||
'icon-size': 0.25,
|
||||
'icon-padding': 0,
|
||||
'icon-allow-overlap': ['step', ['zoom'], false, 14, true],
|
||||
this.map.addLayer(
|
||||
{
|
||||
id: 'overpass',
|
||||
type: 'symbol',
|
||||
source: 'overpass',
|
||||
layout: {
|
||||
'icon-image': ['get', 'icon'],
|
||||
'icon-size': 0.25,
|
||||
'icon-padding': 0,
|
||||
'icon-allow-overlap': ['step', ['zoom'], false, 14, true],
|
||||
},
|
||||
},
|
||||
});
|
||||
ANCHOR_LAYER_KEY.overpass
|
||||
);
|
||||
|
||||
this.map.on('mouseenter', 'overpass', this.onHoverBinded);
|
||||
this.map.on('click', 'overpass', this.onHoverBinded);
|
||||
@@ -111,7 +116,7 @@ export class OverpassLayer {
|
||||
|
||||
remove() {
|
||||
this.map.off('moveend', this.queryIfNeededBinded);
|
||||
this.map.off('style.import.load', this.updateBinded);
|
||||
this.map.off('style.load', this.updateBinded);
|
||||
this.unsubscribes.forEach((unsubscribe) => unsubscribe());
|
||||
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user