This commit is contained in:
@ -7,7 +7,7 @@
|
||||
decodePolyline,
|
||||
routing,
|
||||
} from "$lib/services/navigation/routing.svelte";
|
||||
import { location } from "./location.svelte";
|
||||
import { getSnappedLocation, location } from "./location.svelte";
|
||||
import RoutingLayers from "$lib/services/navigation/RoutingLayers.svelte";
|
||||
import {
|
||||
getPMTilesURL,
|
||||
@ -137,6 +137,15 @@
|
||||
pitchAlignment="map"
|
||||
/>
|
||||
{/if}
|
||||
{#if getSnappedLocation().current}
|
||||
<Marker
|
||||
lnglat={{
|
||||
lat: getSnappedLocation().current!.lat,
|
||||
lng: getSnappedLocation().current!.lon,
|
||||
}}
|
||||
color="blue"
|
||||
/>
|
||||
{/if}
|
||||
|
||||
<MapLocationMarkers />
|
||||
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
import { LNV_SERVER } from "$lib/services/hosts";
|
||||
import { routing } from "$lib/services/navigation/routing.svelte";
|
||||
import type { WrappedValue } from "$lib/services/stores.svelte";
|
||||
import { getMeta } from "$lib/services/TileMeta";
|
||||
import { getFeature, getMeta } from "$lib/services/TileMeta";
|
||||
import { lineString, nearestPointOnLine, point } from "@turf/turf";
|
||||
import { map } from "./map.svelte";
|
||||
|
||||
export const location = $state({
|
||||
@ -44,11 +45,59 @@ export function isDriving() {
|
||||
const roadMetadata: WrappedValue<GeoJSON.GeoJsonProperties> = $state({
|
||||
current: null,
|
||||
});
|
||||
const roadFeature: WrappedValue<GeoJSON.Feature | null> = $state({
|
||||
current: null,
|
||||
});
|
||||
const snappedLocation: WrappedValue<WorldLocation | null> = $state({
|
||||
current: null,
|
||||
});
|
||||
|
||||
export function getRoadMetadata() {
|
||||
return roadMetadata;
|
||||
}
|
||||
|
||||
export function getRoadFeature() {
|
||||
return roadFeature;
|
||||
}
|
||||
|
||||
export function getSnappedLocation() {
|
||||
return snappedLocation;
|
||||
}
|
||||
|
||||
function snapLocation() {
|
||||
const feature = roadFeature.current;
|
||||
console.log("Snapping location to road feature:", feature);
|
||||
if (!feature) return;
|
||||
if (feature.geometry.type === "LineString") {
|
||||
const loc = nearestPointOnLine(lineString(feature.geometry.coordinates), point([location.lng, location.lat]));
|
||||
snappedLocation.current = {
|
||||
lat: loc.geometry.coordinates[1],
|
||||
lon: loc.geometry.coordinates[0],
|
||||
};
|
||||
} else if (feature.geometry.type === "MultiLineString") {
|
||||
// Find nearest point across all parts
|
||||
let nearestLoc: GeoJSON.Feature<GeoJSON.Point> | null = null;
|
||||
let minDist = Infinity;
|
||||
for (const coords of feature.geometry.coordinates) {
|
||||
const loc = nearestPointOnLine(lineString(coords), point([location.lng, location.lat]));
|
||||
const dist = Math.hypot(
|
||||
loc.geometry.coordinates[0] - location.lng,
|
||||
loc.geometry.coordinates[1] - location.lat,
|
||||
);
|
||||
if (dist < minDist) {
|
||||
minDist = dist;
|
||||
nearestLoc = loc;
|
||||
}
|
||||
}
|
||||
if (nearestLoc) {
|
||||
snappedLocation.current = {
|
||||
lat: nearestLoc.geometry.coordinates[1],
|
||||
lon: nearestLoc.geometry.coordinates[0],
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function watchLocation() {
|
||||
if (navigator.geolocation) {
|
||||
navigator.geolocation.watchPosition(
|
||||
@ -63,12 +112,32 @@ export function watchLocation() {
|
||||
location.heading = pos.coords.heading;
|
||||
location.lastUpdate = new Date();
|
||||
|
||||
const blacklist = ["footway", "platform"];
|
||||
getMeta(
|
||||
{ lat: location.lat, lon: location.lng },
|
||||
"transportation",
|
||||
(f) => {
|
||||
if(f.properties) {
|
||||
return !blacklist.includes(f.properties["subclass"]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
).then((meta) => {
|
||||
roadMetadata.current = meta;
|
||||
});
|
||||
getFeature(
|
||||
{ lat: location.lat, lon: location.lng },
|
||||
"transportation",
|
||||
(f) => {
|
||||
if(f.properties) {
|
||||
return !blacklist.includes(f.properties["subclass"]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
).then((feature) => {
|
||||
roadFeature.current = feature;
|
||||
snapLocation();
|
||||
});
|
||||
|
||||
if (location.locked) {
|
||||
map.value?.flyTo(
|
||||
|
||||
@ -19,7 +19,7 @@ function getFeatureDistance(f: GeoJSON.Feature, point: [number, number]) {
|
||||
}
|
||||
}
|
||||
|
||||
export async function getMeta(coord: WorldLocation, layer: string) {
|
||||
export async function getFeature(coord: WorldLocation, layer: string, filter?: (feature: GeoJSON.Feature) => boolean) {
|
||||
const zxy = coordToTile(coord, 14);
|
||||
const tile = await fetchTile(zxy.z, zxy.x, zxy.y);
|
||||
const layerData = tile.layers[layer];
|
||||
@ -33,13 +33,18 @@ export async function getMeta(coord: WorldLocation, layer: string) {
|
||||
const filtered = features.filter(
|
||||
(f) =>
|
||||
f.geometry.type === "LineString" || f.geometry.type == "MultiLineString",
|
||||
);
|
||||
).filter((f) => (filter ? filter(f) : true));
|
||||
if (filtered.length === 0) return null;
|
||||
const nearest = filtered.reduce((a, b) => {
|
||||
const distA = getFeatureDistance(a, [coord.lon, coord.lat]);
|
||||
const distB = getFeatureDistance(b, [coord.lon, coord.lat]);
|
||||
return distA < distB ? a : b;
|
||||
});
|
||||
return nearest;
|
||||
}
|
||||
|
||||
export async function getMeta(coord: WorldLocation, layer: string, filter?: (feature: GeoJSON.Feature) => boolean) {
|
||||
const nearest = await getFeature(coord, layer, filter);
|
||||
return nearest ? nearest.properties : null;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user