/** * Privacy utilities for rMaps: location fuzzing, distance calculations, formatting. */ import type { PrecisionLevel } from "./map-sync"; /** Noise radius in meters per precision level */ const PRECISION_RADIUS: Record = { exact: 0, building: 50, area: 500, approximate: 5000, }; /** * Add random noise to coordinates based on precision level. * Returns original coords if precision is "exact". */ export function fuzzLocation(lat: number, lng: number, precision: PrecisionLevel): { latitude: number; longitude: number } { const radius = PRECISION_RADIUS[precision]; if (radius === 0) return { latitude: lat, longitude: lng }; // Random angle and distance within radius const angle = Math.random() * 2 * Math.PI; const dist = Math.random() * radius; // Approximate meters to degrees const dLat = (dist * Math.cos(angle)) / 111320; const dLng = (dist * Math.sin(angle)) / (111320 * Math.cos(lat * (Math.PI / 180))); return { latitude: lat + dLat, longitude: lng + dLng, }; } /** * Haversine distance between two points in meters. */ export function haversineDistance(lat1: number, lng1: number, lat2: number, lng2: number): number { const R = 6371000; // Earth radius in meters const toRad = (d: number) => d * (Math.PI / 180); const dLat = toRad(lat2 - lat1); const dLng = toRad(lng2 - lng1); const a = Math.sin(dLat / 2) ** 2 + Math.cos(toRad(lat1)) * Math.cos(toRad(lat2)) * Math.sin(dLng / 2) ** 2; const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); return R * c; } /** * Format a distance in meters to a human-readable string. */ export function formatDistance(meters: number): string { if (meters < 50) return "nearby"; if (meters < 1000) return `${Math.round(meters)}m`; if (meters < 10000) return `${(meters / 1000).toFixed(1)}km`; return `${Math.round(meters / 1000)}km`; } /** * Format seconds to a human-readable duration. */ export function formatTime(seconds: number): string { if (seconds < 60) return `${Math.round(seconds)}s`; const m = Math.floor(seconds / 60); if (m < 60) return `${m} min`; const h = Math.floor(m / 60); const rm = m % 60; return rm > 0 ? `${h}h ${rm}m` : `${h}h`; }