import { useQuery } from '@tanstack/react-query'; import ngeohash from 'ngeohash'; import type { FoodSpot } from '@/lib/types'; const CORS_PROXY = 'https://proxy.shakespeare.diy/?url='; const OVERPASS_API = 'https://overpass-api.de/api/interpreter'; interface OverpassElement { type: string; id: number; lat?: number; lon?: number; center?: { lat: number; lon: number }; tags?: Record; } function calculateDistance(lat1: number, lon1: number, lat2: number, lon2: number): number { const R = 6371; // Earth's radius in km const dLat = ((lat2 - lat1) * Math.PI) / 180; const dLon = ((lon2 - lon1) * Math.PI) / 180; const a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.cos((lat1 * Math.PI) / 180) * Math.cos((lat2 * Math.PI) / 180) * Math.sin(dLon / 2) * Math.sin(dLon / 2); const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); return R * c; } export function useFoodSpots(geohash: string | undefined, radiusKm = 1) { return useQuery({ queryKey: ['food-spots', geohash, radiusKm], queryFn: async () => { if (!geohash) return []; // Decode geohash to get center coordinates const { latitude: centerLat, longitude: centerLon } = ngeohash.decode(geohash); // Calculate bounding box for the search area const latOffset = radiusKm / 111; // ~111km per degree of latitude const lonOffset = radiusKm / (111 * Math.cos((centerLat * Math.PI) / 180)); const south = centerLat - latOffset; const west = centerLon - lonOffset; const north = centerLat + latOffset; const east = centerLon + lonOffset; // Overpass QL query for restaurants, cafes, fast food, etc. const query = ` [out:json][timeout:25]; ( node["amenity"~"restaurant|cafe|fast_food|bar|pub|food_court"](${south},${west},${north},${east}); way["amenity"~"restaurant|cafe|fast_food|bar|pub|food_court"](${south},${west},${north},${east}); ); out center; `; const response = await fetch(CORS_PROXY + encodeURIComponent(OVERPASS_API), { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', }, body: `data=${encodeURIComponent(query)}`, }); if (!response.ok) { throw new Error('Failed to fetch food spots'); } const data = await response.json(); const foodSpots: FoodSpot[] = data.elements .map((element: OverpassElement) => { const lat = element.lat ?? element.center?.lat; const lon = element.lon ?? element.center?.lon; const tags = element.tags ?? {}; if (!lat || !lon) return null; const distance = calculateDistance(centerLat, centerLon, lat, lon); return { id: `${element.type}-${element.id}`, name: tags.name || 'Unknown', lat, lng: lon, type: tags.amenity || 'restaurant', cuisine: tags.cuisine, address: [tags['addr:housenumber'], tags['addr:street'], tags['addr:city']] .filter(Boolean) .join(' '), phone: tags.phone, website: tags.website, openingHours: tags.opening_hours, distance, }; }) .filter(Boolean) .sort((a: FoodSpot, b: FoodSpot) => (a.distance ?? Infinity) - (b.distance ?? Infinity)); return foodSpots as FoodSpot[]; }, enabled: !!geohash, staleTime: 300000, // 5 minutes }); }