feat: add speed limit display
This commit is contained in:
@@ -14,6 +14,7 @@
|
||||
import RequiresCapability from "./RequiresCapability.svelte";
|
||||
import {
|
||||
advertiseRemoteLocation,
|
||||
getRoadMetadata,
|
||||
location,
|
||||
remoteLocation,
|
||||
} from "./location.svelte";
|
||||
@@ -27,6 +28,7 @@
|
||||
import Progressbar from "../Progressbar.svelte";
|
||||
import { postHazard } from "$lib/services/lnv";
|
||||
import { fetchHazards } from "./hazards.svelte";
|
||||
import { getSpeed } from "$lib/services/TileMeta";
|
||||
|
||||
const views: Record<string, string> = {
|
||||
main: "MainSidebar",
|
||||
@@ -180,6 +182,17 @@
|
||||
{(location.speed * 3.6 | 0).toFixed(0)}
|
||||
</div>
|
||||
{/if}
|
||||
{#if getRoadMetadata()}
|
||||
{@const meta = getRoadMetadata()!}
|
||||
{#if meta.maxspeed}
|
||||
{@const maxspeed = getSpeed(meta.maxspeed)}
|
||||
{#if maxspeed && maxspeed < 100}
|
||||
<div id="max-speed" style="position: fixed; {mobileView ? `bottom: calc(50px + ${sidebarHeight.current}px + 10px + 2ch + 20px + 10px); right: 10px;` : "bottom: calc(10px + 2ch + 20px + 10px); right: 10px;"}">
|
||||
{meta.maxspeed}
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
<div
|
||||
id="sidebar"
|
||||
@@ -406,7 +419,8 @@
|
||||
padding-bottom: calc(40px + env(safe-area-inset-bottom));
|
||||
}
|
||||
|
||||
#speedometer {
|
||||
#speedometer,
|
||||
#max-speed {
|
||||
z-index: 30;
|
||||
background-color: hsla(0, 0%, 5%, 0.6);
|
||||
backdrop-filter: blur(5px);
|
||||
@@ -417,5 +431,12 @@
|
||||
border-radius: 10px;
|
||||
text-align: center;
|
||||
width: calc(2ch + 20px);
|
||||
height: calc(2ch + 20px);
|
||||
}
|
||||
|
||||
#max-speed {
|
||||
border-radius: 50%;
|
||||
border: 5px solid red;
|
||||
padding: 5px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { LNV_SERVER } from "$lib/services/hosts";
|
||||
import { routing } from "$lib/services/navigation/routing.svelte";
|
||||
import { getTransportationMeta } from "$lib/services/TileMeta";
|
||||
import { map } from "./map.svelte";
|
||||
|
||||
export const location = $state({
|
||||
@@ -39,6 +40,12 @@ export function isDriving() {
|
||||
return _isDriving;
|
||||
}
|
||||
|
||||
const roadMetadata = $derived(getTransportationMeta({ lat: location.lat, lon: location.lng }));
|
||||
|
||||
export function getRoadMetadata() {
|
||||
return roadMetadata;
|
||||
}
|
||||
|
||||
export function watchLocation() {
|
||||
if (navigator.geolocation) {
|
||||
navigator.geolocation.watchPosition(
|
||||
|
||||
49
src/lib/services/TileMeta.ts
Normal file
49
src/lib/services/TileMeta.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import { map } from "$lib/components/lnv/map.svelte";
|
||||
import { lineString, pointToLineDistance } from "@turf/turf";
|
||||
|
||||
function getFeatureDistance(f: GeoJSON.Feature, point: [number, number]) {
|
||||
if (f.geometry.type === "LineString") {
|
||||
return pointToLineDistance(point, lineString(f.geometry.coordinates));
|
||||
} else if (f.geometry.type === "MultiLineString") {
|
||||
// Compute the min distance across all parts
|
||||
return Math.min(
|
||||
...f.geometry.coordinates.map((coords) =>
|
||||
pointToLineDistance(point, lineString(coords))
|
||||
)
|
||||
);
|
||||
} else {
|
||||
return Infinity;
|
||||
}
|
||||
}
|
||||
|
||||
export function getTransportationMeta(coord: WorldLocation) {
|
||||
if(!map.value) return null;
|
||||
const features = map.value.querySourceFeatures("openmaptiles", {
|
||||
sourceLayer: "transportation"
|
||||
});
|
||||
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) => {
|
||||
const distA = getFeatureDistance(a, [coord.lon, coord.lat]);
|
||||
const distB = getFeatureDistance(b, [coord.lon, coord.lat]);
|
||||
return distA < distB ? a : b;
|
||||
});
|
||||
return nearest ? nearest.properties : null;
|
||||
}
|
||||
|
||||
const IMPLICIT_SPEEDS: Record<string, number> = {
|
||||
"DE:urban": 50,
|
||||
"DE:rural": 100, // TODO: 80 (hgv weight > 3.5t, or trailer), 60 (weight > 7.5t)
|
||||
"DE:living_street": 7,
|
||||
"DE:bicycle_road": 30
|
||||
}
|
||||
|
||||
export function getSpeed(maxspeed: string): number | null {
|
||||
if(!isNaN(parseInt(maxspeed))) return parseInt(maxspeed);
|
||||
if(maxspeed.endsWith(" mph")) {
|
||||
const val = parseInt(maxspeed.replace(" mph", ""));
|
||||
if(!isNaN(val)) return Math.round(val * 1.60934); // Convert to km/h
|
||||
}
|
||||
if(maxspeed === "walk") return 7; // https://wiki.openstreetmap.org/wiki/Proposed_features/maxspeed_walk
|
||||
return IMPLICIT_SPEEDS[maxspeed as keyof typeof IMPLICIT_SPEEDS] || null;
|
||||
}
|
||||
Reference in New Issue
Block a user