This commit is contained in:
vcoppe committed 2026-10-08 18:34:19 +02:00
1 parent 606a0e974a
commit 67a4c090bd
107 files changed
+3474 -3948

No files matched your search

@@ -33,7 +33,7 @@
const desc = Object.entries(tags)
.map(([key, value]) => `${key}: ${value}`)
.join('\n');
const [ele] = await getElevation([{ lat: poi.item.lat, lon: poi.item.lng }]);
const [ele] = await getElevation([{ lat: poi.item.lat, lng: poi.item.lng }]);
await engine.newWaypoint({
lng: poi.item.lng,
lat: poi.item.lat,
@@ -1,39 +1,38 @@
import { SphericalMercator } from '@mapbox/sphericalmercator';
import { getLayers } from './utils';
import { get, writable } from 'svelte/store';
import { liveQuery } from 'dexie';
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 '$lib/components/map/style';
import type { MapLayerEventManager } from '$lib/components/map/map-layer-event-manager';
import { loadSVGIcon } from '$lib/utils';
import { cachedFetch } from '$lib/cached-fetch';
const { currentOverpassQueries } = settings;
/** The responses of Overpass are kept in the browser for a week. */
const CACHE = { name: 'overpass', maxAge: 7 * 24 * 3600 * 1000 };
const mercator = new SphericalMercator({
size: 256,
});
let data = writable<GeoJSON.FeatureCollection>({ type: 'FeatureCollection', features: [] });
liveQuery(() => db.overpassdata.toArray()).subscribe((pois) => {
data.set({ type: 'FeatureCollection', features: pois.map((poi) => poi.poi) });
});
export class OverpassLayer {
overpassUrl = 'https://overpass.gpx.studio/api/interpreter';
minZoom = 12;
queryZoom = 12;
expirationTime = 7 * 24 * 3600 * 1000;
map: maplibregl.Map;
layerEventManager: MapLayerEventManager;
popup: MapPopup;
data = writable<GeoJSON.FeatureCollection>({ type: 'FeatureCollection', features: [] });
// What was queried this session: the queries that were made for each tile. The responses are
// kept longer, see `cachedFetch`.
queriedTiles = new Map<string, Set<string>>();
currentQueries: Set<string> = new Set();
nextQueries: Map<string, { x: number; y: number; queries: string[] }> = new Map();
unsubscribes: (() => void)[] = [];
queryIfNeededBinded = this.queryIfNeeded.bind(this);
@@ -54,7 +53,7 @@ export class OverpassLayer {
add() {
this.map.on('moveend', this.queryIfNeededBinded);
this.map.on('style.load', this.updateBinded);
this.unsubscribes.push(data.subscribe(this.updateBinded));
this.unsubscribes.push(this.data.subscribe(this.updateBinded));
this.unsubscribes.push(
currentOverpassQueries.subscribe(() => {
this.updateBinded();
@@ -77,7 +76,7 @@ export class OverpassLayer {
update() {
this.loadIcons();
const fullData = get(data);
const fullData = get(this.data);
const queries = getCurrentQueries();
const d: GeoJSON.FeatureCollection = {
type: 'FeatureCollection',
@@ -158,7 +157,6 @@ export class OverpassLayer {
}
let tileLimits = mercator.xyz(bbox, this.queryZoom);
let time = Date.now();
for (let x = tileLimits.minX; x <= tileLimits.maxX; x++) {
for (let y = tileLimits.minY; y <= tileLimits.maxY; y++) {
@@ -166,66 +164,35 @@ export class OverpassLayer {
continue;
}
db.overpasstiles
.where('[x+y]')
.equals([x, y])
.toArray()
.then((querytiles) => {
let missingQueries = queries.filter(
(query) =>
!querytiles.some(
(querytile) =>
querytile.query === query &&
time - querytile.time < this.expirationTime
)
);
if (missingQueries.length > 0) {
this.queryTile(x, y, missingQueries);
}
});
const queried = this.queriedTiles.get(`${x},${y}`);
const missingQueries = queries.filter((query) => !queried?.has(query));
if (missingQueries.length > 0) {
this.queryTile(x, y, missingQueries);
}
}
}
}
queryTile(x: number, y: number, queries: string[]) {
if (this.currentQueries.size > 5) {
return;
}
this.currentQueries.add(`${x},${y}`);
const bounds = mercator.bbox(x, y, this.queryZoom);
fetch(`${this.overpassUrl}?data=${getQueryForBounds(bounds, queries)}`)
.then(
(response) => {
if (response.ok) {
return response.json();
}
this.currentQueries.delete(`${x},${y}`);
return Promise.reject();
},
() => this.currentQueries.delete(`${x},${y}`)
)
cachedFetch(`${this.overpassUrl}?data=${getQueryForBounds(bounds, queries)}`, CACHE)
.then((response) => (response.ok ? response.json() : Promise.reject()))
.then((data) => this.storeOverpassData(x, y, queries, data))
.catch(() => this.currentQueries.delete(`${x},${y}`));
}
storeOverpassData(x: number, y: number, queries: string[], data: any) {
let time = Date.now();
let queryTiles = queries.map((query) => ({ x, y, query, time }));
let pois: { query: string; id: number; poi: GeoJSON.Feature }[] = [];
if (data.elements === undefined) {
return;
}
for (let element of data.elements) {
for (let query of queries) {
if (belongsToQuery(element, query)) {
pois.push({
query,
id: element.id,
poi: {
this.data.update((updatedData) => {
for (let element of data.elements) {
for (let query of queries) {
if (belongsToQuery(element, query)) {
updatedData.features.push({
type: 'Feature',
geometry: {
type: 'Point',
@@ -242,17 +209,17 @@ export class OverpassLayer {
tags: element.tags,
type: element.type,
},
},
});
});
}
}
}
}
db.transaction('rw', db.overpasstiles, db.overpassdata, async () => {
await db.overpasstiles.bulkPut(queryTiles);
await db.overpassdata.bulkPut(pois);
return updatedData;
});
const queried = this.queriedTiles.get(`${x},${y}`) ?? new Set<string>();
queries.forEach((query) => queried.add(query));
this.queriedTiles.set(`${x},${y}`, queried);
this.currentQueries.delete(`${x},${y}`);
}