Files
meshstream/web/src/lib/mapUtils.ts
Daniel Pupius d95a74c1d7 refactor(web): replace Google Maps with MapLibre, clean up map components
- 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>
2026-03-15 20:22:12 +00:00

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}`;
};