feat: fetch metadata from pmtiles
This commit is contained in:
@ -1,5 +1,8 @@
|
||||
import { map } from "$lib/components/lnv/map.svelte";
|
||||
import { lineString, pointToLineDistance } from "@turf/turf";
|
||||
import { FSSource, hasPMTiles } from "./OfflineTiles";
|
||||
import { PMTiles } from "pmtiles";
|
||||
import { VectorTile } from "@mapbox/vector-tile";
|
||||
import Protobuf from "pbf";
|
||||
|
||||
function getFeatureDistance(f: GeoJSON.Feature, point: [number, number]) {
|
||||
if (f.geometry.type === "LineString") {
|
||||
@ -16,11 +19,17 @@ function getFeatureDistance(f: GeoJSON.Feature, point: [number, number]) {
|
||||
}
|
||||
}
|
||||
|
||||
export function getTransportationMeta(coord: WorldLocation) {
|
||||
if(!map.value) return null;
|
||||
const features = map.value.querySourceFeatures("openmaptiles", {
|
||||
sourceLayer: "transportation"
|
||||
});
|
||||
export async function getMeta(coord: WorldLocation, layer: string) {
|
||||
const zxy = coordToTile(coord, 14);
|
||||
const tile = await fetchTile(zxy.z, zxy.x, zxy.y);
|
||||
const layerData = tile.layers[layer];
|
||||
if (!layerData) return null;
|
||||
|
||||
const features: GeoJSON.Feature[] = [];
|
||||
for (let i = 0; i < layerData.length; i++) {
|
||||
const feature = layerData.feature(i);
|
||||
features.push(feature.toGeoJSON(zxy.x, zxy.y, zxy.z));
|
||||
}
|
||||
const filtered = features.filter((f) => f.geometry.type === "LineString" || f.geometry.type == "MultiLineString");
|
||||
if(filtered.length === 0) return null;
|
||||
const nearest = filtered.reduce((a, b) => {
|
||||
@ -47,3 +56,74 @@ export function getSpeed(maxspeed: string): number | null {
|
||||
if(maxspeed === "walk") return 7; // https://wiki.openstreetmap.org/wiki/Proposed_features/maxspeed_walk
|
||||
return IMPLICIT_SPEEDS[maxspeed as keyof typeof IMPLICIT_SPEEDS] || null;
|
||||
}
|
||||
|
||||
function coordToTile(coord: WorldLocation, zoom: number) {
|
||||
const z = zoom;
|
||||
const x = Math.floor(((coord.lon + 180) / 360) * Math.pow(2, z));
|
||||
const y = Math.floor(
|
||||
((1 -
|
||||
Math.log(
|
||||
Math.tan((coord.lat * Math.PI) / 180) +
|
||||
1 / Math.cos((coord.lat * Math.PI) / 180)
|
||||
) /
|
||||
Math.PI) /
|
||||
2) *
|
||||
Math.pow(2, z)
|
||||
);
|
||||
return { z, x, y };
|
||||
}
|
||||
|
||||
class Cache<T> {
|
||||
data: Map<string, T>;
|
||||
size: number;
|
||||
|
||||
constructor(size: number) {
|
||||
this.data = new Map<string, T>();
|
||||
this.size = size;
|
||||
}
|
||||
|
||||
get(key: string): T | undefined {
|
||||
if (this.data.has(key)) {
|
||||
const value = this.data.get(key)!;
|
||||
this.data.delete(key);
|
||||
this.data.set(key, value);
|
||||
return value;
|
||||
}
|
||||
return this.data.get(key);
|
||||
}
|
||||
|
||||
has(key: string): boolean {
|
||||
return this.data.has(key);
|
||||
}
|
||||
|
||||
set(key: string, value: T): void {
|
||||
this.data.set(key, value);
|
||||
if (this.data.size > this.size) {
|
||||
const firstKey = this.data.keys().next().value;
|
||||
if (firstKey) {
|
||||
this.data.delete(firstKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const tileCache = new Cache<VectorTile>(5);
|
||||
|
||||
export async function fetchTile(z: number, x: number, y: number) {
|
||||
const cacheKey = `${z}/${x}/${y}`;
|
||||
if (tileCache.has(cacheKey)) {
|
||||
return tileCache.get(cacheKey)!;
|
||||
}
|
||||
const pmtiles = (await hasPMTiles("tiles"))
|
||||
? new PMTiles(new FSSource("tiles"))
|
||||
: new PMTiles("https://trafficcue-tiles.picoscratch.de/germany.pmtiles");
|
||||
const tile = (await pmtiles.getZxy(z, x, y));
|
||||
if (!tile) {
|
||||
console.log(tile);
|
||||
throw new Error(`Tile not found: z${z} x${x} y${y}`);
|
||||
}
|
||||
const data = tile.data;
|
||||
const vectorTile = new VectorTile(new Protobuf(data));
|
||||
tileCache.set(cacheKey, vectorTile);
|
||||
return vectorTile;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user