UNPKG

expo-osm-sdk

Version:

OpenStreetMap component for React Native with Expo

189 lines 6.86 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.formatDistance = exports.calculateDistance = exports.getSuggestions = exports.reverseGeocode = exports.searchLocations = void 0; /** * Nominatim Search Service * * Provides geocoding and reverse geocoding using OpenStreetMap's Nominatim service. * This is a free service with usage limitations - please respect the usage policy: * https://operations.osmfoundation.org/policies/nominatim/ */ const NOMINATIM_BASE_URL = 'https://nominatim.openstreetmap.org'; // Rate limiting - respect Nominatim's usage policy const REQUEST_DELAY = 1000; // 1 second between requests let lastRequestTime = 0; /** * Respect rate limiting by adding delay between requests */ const rateLimitedFetch = async (url) => { const now = Date.now(); const timeSinceLastRequest = now - lastRequestTime; if (timeSinceLastRequest < REQUEST_DELAY) { const delay = REQUEST_DELAY - timeSinceLastRequest; await new Promise(resolve => setTimeout(resolve, delay)); } lastRequestTime = Date.now(); const response = await fetch(url, { headers: { 'User-Agent': 'expo-osm-sdk/1.0 (https://github.com/mapdevsaikat/expo-osm-sdk)', 'Accept': 'application/json', }, }); if (!response.ok) { throw new Error(`Nominatim request failed: ${response.status} ${response.statusText}`); } return response; }; /** * Convert Nominatim result to SearchLocation */ const convertToSearchLocation = (result) => { const searchLocation = { coordinate: { latitude: parseFloat(result.lat), longitude: parseFloat(result.lon), }, displayName: result.display_name, boundingBox: result.boundingbox.map(parseFloat), importance: result.importance, placeId: result.place_id, category: result.class, type: result.type, }; // Only include address if it exists if (result.address) { searchLocation.address = result.address; } return searchLocation; }; /** * Search for locations using a text query * * @param query - Search text (e.g., "New York", "Starbucks near London") * @param options - Search options * @returns Promise<SearchLocation[]> */ const searchLocations = async (query, options = {}) => { if (!query.trim()) { return []; } const { limit = 5, countrycodes, bounded = false, viewbox, addressdetails = true, extratags = false, namedetails = false, } = options; const params = new URLSearchParams({ q: query.trim(), format: 'jsonv2', limit: limit.toString(), addressdetails: addressdetails ? '1' : '0', extratags: extratags ? '1' : '0', namedetails: namedetails ? '1' : '0', }); if (countrycodes) { params.append('countrycodes', countrycodes); } if (bounded && viewbox) { params.append('bounded', '1'); params.append('viewbox', viewbox.join(',')); } try { const url = `${NOMINATIM_BASE_URL}/search?${params.toString()}`; console.log('🔍 Nominatim search:', query, url); const response = await rateLimitedFetch(url); const results = await response.json(); console.log('📍 Search results:', results.length); return results.map(convertToSearchLocation); } catch (error) { console.error('❌ Nominatim search failed:', error); throw new Error(`Search failed: ${error instanceof Error ? error.message : 'Unknown error'}`); } }; exports.searchLocations = searchLocations; /** * Reverse geocoding - get location details from coordinates * * @param coordinate - Latitude and longitude * @param options - Reverse geocoding options * @returns Promise<SearchLocation | null> */ const reverseGeocode = async (coordinate, options = {}) => { const { zoom = 18, addressdetails = true, extratags = false, namedetails = false, } = options; const params = new URLSearchParams({ lat: coordinate.latitude.toString(), lon: coordinate.longitude.toString(), format: 'jsonv2', zoom: zoom.toString(), addressdetails: addressdetails ? '1' : '0', extratags: extratags ? '1' : '0', namedetails: namedetails ? '1' : '0', }); try { const url = `${NOMINATIM_BASE_URL}/reverse?${params.toString()}`; console.log('🔄 Reverse geocoding:', coordinate, url); const response = await rateLimitedFetch(url); const result = await response.json(); if (!result.place_id) { console.log('📍 No reverse geocoding result found'); return null; } console.log('📍 Reverse geocoding result:', result.display_name); return convertToSearchLocation(result); } catch (error) { console.error('❌ Reverse geocoding failed:', error); throw new Error(`Reverse geocoding failed: ${error instanceof Error ? error.message : 'Unknown error'}`); } }; exports.reverseGeocode = reverseGeocode; /** * Get suggestions for a partial query (for autocomplete) * * @param query - Partial search text * @param options - Search options * @returns Promise<SearchLocation[]> */ const getSuggestions = async (query, options = {}) => { if (query.length < 3) { return []; // Don't search for queries shorter than 3 characters } return (0, exports.searchLocations)(query, { ...options, limit: options.limit || 3, // Fewer results for suggestions }); }; exports.getSuggestions = getSuggestions; /** * Calculate distance between two coordinates (Haversine formula) * * @param coord1 - First coordinate * @param coord2 - Second coordinate * @returns Distance in kilometers */ const calculateDistance = (coord1, coord2) => { const R = 6371; // Earth's radius in kilometers const dLat = (coord2.latitude - coord1.latitude) * Math.PI / 180; const dLon = (coord2.longitude - coord1.longitude) * Math.PI / 180; const a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.cos(coord1.latitude * Math.PI / 180) * Math.cos(coord2.latitude * 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; }; exports.calculateDistance = calculateDistance; /** * Format distance for display * * @param distanceKm - Distance in kilometers * @returns Formatted distance string */ const formatDistance = (distanceKm) => { if (distanceKm < 1) { return `${Math.round(distanceKm * 1000)}m`; } else if (distanceKm < 100) { return `${distanceKm.toFixed(1)}km`; } else { return `${Math.round(distanceKm)}km`; } }; exports.formatDistance = formatDistance; //# sourceMappingURL=nominatim.js.map