mirror of
https://github.com/dpup/meshstream.git
synced 2026-03-28 17:42:37 +01:00
- Migrate all three map components (Map, GoogleMap, NetworkMap) to MapLibre GL JS - Extract shared CARTO_DARK_STYLE constants into lib/mapStyle.ts - Move buildCircleCoords to lib/mapUtils.ts (was duplicated across components) - Rename exports: Map → LocationMap, GoogleMap → NodeLocationMap - Remove dead props (width, height, nightMode) from LocationMap interface - Lazy-mount GL contexts via IntersectionObserver to prevent WebGL exhaustion - Fix Math.spread RangeError in NetworkMap bounds calculation - Remove showLinks conditional render in favour of visibility layout property - Remove cursor state; set canvas cursor style directly on map interactions - Remove Google Maps API key env vars from .env.example and .env.local - Move Vite dev server to port 5747 (avoids cached redirect on 3000) - Fix CORS/404: set VITE_API_BASE_URL="" so browser uses Vite proxy Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
85 lines
2.5 KiB
TypeScript
85 lines
2.5 KiB
TypeScript
/**
|
|
* Utility functions for working with maps and coordinates
|
|
*/
|
|
|
|
/**
|
|
* Calculate the position accuracy in meters using precision bits
|
|
* @param precisionBits Number of precision bits used in position encoding
|
|
* @returns Accuracy radius in meters
|
|
*/
|
|
export const calculateAccuracyFromPrecisionBits = (
|
|
precisionBits?: number
|
|
): number => {
|
|
if (!precisionBits) return 300; // Default accuracy of 300m
|
|
|
|
// Each precision bit halves the accuracy radius
|
|
// Starting with Earth's circumference (~40075km), calculate the precision
|
|
// For reference: 24 bits = ~2.4m accuracy, 21 bits = ~19m accuracy
|
|
const earthCircumference = 40075000; // in meters
|
|
const accuracy = earthCircumference / 2 ** precisionBits / 2;
|
|
|
|
// Limit to reasonable values
|
|
return Math.max(1, Math.min(accuracy, 10000));
|
|
};
|
|
|
|
/**
|
|
* Calculate appropriate zoom level based on accuracy
|
|
* @param accuracyMeters Accuracy in meters
|
|
* @returns Zoom level (1-20)
|
|
*/
|
|
export const calculateZoomFromAccuracy = (accuracyMeters: number): number => {
|
|
// Roughly map accuracy to zoom level (higher accuracy = higher zoom)
|
|
// < 10m: zoom 18
|
|
// < 50m: zoom 16
|
|
// < 100m: zoom 15
|
|
// < 500m: zoom 14
|
|
// < 1km: zoom 13
|
|
// < 5km: zoom 11
|
|
// >= 5km: zoom 10
|
|
if (accuracyMeters < 10) return 18;
|
|
if (accuracyMeters < 50) return 16;
|
|
if (accuracyMeters < 100) return 15;
|
|
if (accuracyMeters < 500) return 14;
|
|
if (accuracyMeters < 1000) return 13;
|
|
if (accuracyMeters < 5000) return 11;
|
|
return 10;
|
|
};
|
|
|
|
|
|
/**
|
|
* Approximate a geographic circle as a GeoJSON polygon ring.
|
|
* @param lng Center longitude
|
|
* @param lat Center latitude
|
|
* @param radiusMeters Radius in meters
|
|
* @param points Number of polygon vertices (more = smoother)
|
|
*/
|
|
export function buildCircleCoords(
|
|
lng: number,
|
|
lat: number,
|
|
radiusMeters: number,
|
|
points = 64
|
|
): [number, number][] {
|
|
const earthRadius = 6371000;
|
|
const coords: [number, number][] = [];
|
|
for (let i = 0; i <= points; i++) {
|
|
const angle = (i / points) * 2 * Math.PI;
|
|
const dx = radiusMeters * Math.cos(angle);
|
|
const dy = radiusMeters * Math.sin(angle);
|
|
const pLat = lat + (dy / earthRadius) * (180 / Math.PI);
|
|
const pLng = lng + (dx / (earthRadius * Math.cos((lat * Math.PI) / 180))) * (180 / Math.PI);
|
|
coords.push([pLng, pLat]);
|
|
}
|
|
coords.push(coords[0]); // close the ring
|
|
return coords;
|
|
}
|
|
|
|
/**
|
|
* Create a Google Maps URL to open the location in Google Maps
|
|
*/
|
|
export const getGoogleMapsUrl = (
|
|
latitude: number,
|
|
longitude: number
|
|
): string => {
|
|
return `https://www.google.com/maps?q=${latitude},${longitude}`;
|
|
};
|