google-maps-resolver.ts
1 import { Client } from '@googlemaps/google-maps-services-js'; 2 import { PlaceInfo } from './types'; 3 4 export class GoogleMapsResolver { 5 private client: Client; 6 private apiKey: string; 7 8 constructor(apiKey: string) { 9 this.apiKey = apiKey; 10 this.client = new Client({}); 11 } 12 13 private extractPlaceIdFromUrl(url: string): string | null { 14 // Extract place ID from Google Maps URL 15 // The hex values in these URLs are not valid place IDs, so we'll return null 16 // and fall back to text search 17 return null; 18 } 19 20 private extractPlaceNameFromUrl(url: string): string | null { 21 // Extract place name from URL path 22 const match = url.match(/\/place\/([^\/]+)/); 23 if (match) { 24 return decodeURIComponent(match[1].replace(/\+/g, ' ')); 25 } 26 return null; 27 } 28 29 async resolveCoordinates(place: PlaceInfo): Promise<PlaceInfo> { 30 if (!place.url) { 31 console.warn(`No URL provided for place: ${place.name}`); 32 return place; 33 } 34 35 try { 36 // First try to extract place ID from URL 37 const placeId = this.extractPlaceIdFromUrl(place.url); 38 39 if (placeId) { 40 // Use Place Details API with place ID 41 const response = await this.client.placeDetails({ 42 params: { 43 place_id: placeId, 44 key: this.apiKey, 45 fields: ['geometry', 'name'] 46 } 47 }); 48 49 if (response.data.result?.geometry?.location) { 50 const location = response.data.result.geometry.location; 51 return { 52 ...place, 53 latitude: location.lat, 54 longitude: location.lng 55 }; 56 } 57 } 58 59 // Fallback: use text search with place name from URL or provided name 60 const searchQuery = place.name || this.extractPlaceNameFromUrl(place.url); 61 62 if (searchQuery) { 63 const response = await this.client.textSearch({ 64 params: { 65 query: searchQuery, 66 key: this.apiKey 67 } 68 }); 69 70 if (response.data.results && response.data.results.length > 0) { 71 const location = response.data.results[0].geometry?.location; 72 if (location) { 73 return { 74 ...place, 75 latitude: location.lat, 76 longitude: location.lng 77 }; 78 } 79 } 80 } 81 82 console.warn(`Could not resolve coordinates for: ${place.name || place.url}`); 83 return place; 84 85 } catch (error) { 86 console.error(`Error resolving coordinates for ${place.name}:`, error); 87 return place; 88 } 89 } 90 91 async resolveAllCoordinates(places: PlaceInfo[]): Promise<PlaceInfo[]> { 92 const results: PlaceInfo[] = []; 93 94 for (const place of places) { 95 const resolvedPlace = await this.resolveCoordinates(place); 96 results.push(resolvedPlace); 97 98 // Add small delay to avoid hitting rate limits 99 await new Promise(resolve => setTimeout(resolve, 100)); 100 } 101 102 return results; 103 } 104 }