interface Coordinates { lat: number; lon: number; } interface DistanceResult { distance: number; duration: number; } export class DistanceCalculator { private static readonly OSRM_API = 'https://router.project-osrm.org/route/v1/driving'; static async geocodeAddress(address: string): Promise { try { const nominatimUrl = `https://nominatim.openstreetmap.org/search?format=json&q=${encodeURIComponent(address)}&limit=1`; const response = await fetch(nominatimUrl, { headers: { 'User-Agent': 'SaveTheMoment-Atlas/1.0', }, }); if (!response.ok) { throw new Error(`Geocoding failed: ${response.statusText}`); } const data = await response.json(); if (!data || data.length === 0) { console.warn(`No results found for address: ${address}`); return null; } return { lat: parseFloat(data[0].lat), lon: parseFloat(data[0].lon), }; } catch (error) { console.error('Geocoding error:', error); return null; } } static async calculateDistance( fromAddress: string, toAddress: string ): Promise { try { const fromCoords = await this.geocodeAddress(fromAddress); const toCoords = await this.geocodeAddress(toAddress); if (!fromCoords || !toCoords) { console.error('Failed to geocode one or both addresses'); return null; } const url = `${this.OSRM_API}/${fromCoords.lon},${fromCoords.lat};${toCoords.lon},${toCoords.lat}?overview=false`; const response = await fetch(url); if (!response.ok) { throw new Error(`OSRM API failed: ${response.statusText}`); } const data = await response.json(); if (!data.routes || data.routes.length === 0) { console.error('No route found'); return null; } const route = data.routes[0]; return { distance: Math.round(route.distance / 1000 * 100) / 100, duration: Math.round(route.duration / 60), }; } catch (error) { console.error('Distance calculation error:', error); return null; } } static formatAddress(address: string, zip: string, city: string): string { return `${address}, ${zip} ${city}`.trim(); } }