|
1 | 1 | const kGeonamesUser = "codap"; |
2 | 2 | const kGeolocService = "https://secure.geonames.org/findNearbyPlaceNameJSON"; |
| 3 | +const kGeolocCitiesService = "https://secure.geonames.org/citiesJSON"; |
| 4 | + |
| 5 | + |
| 6 | +interface IBounds { |
| 7 | + north: number; |
| 8 | + south: number; |
| 9 | + east: number; |
| 10 | + west: number; |
| 11 | +} |
| 12 | +export interface MapComponentInfo { |
| 13 | + id: number; |
| 14 | + name: string; |
| 15 | + dimensions: {width: number, height: number}; |
| 16 | + position: {top: number, left: number}; |
| 17 | + title: string; |
| 18 | + type: string; |
| 19 | + zoom: number; |
| 20 | + center: [number, number]; |
| 21 | + bounds:{north: number, south: number, east: number, west: number}; |
| 22 | +} |
| 23 | + |
| 24 | +const toRadians = (degrees: number) =>{ |
| 25 | + return degrees * Math.PI / 180; |
| 26 | +} |
| 27 | + |
| 28 | +const haversineDistance = (lat1: number, lon1: number, lat2: number, lon2: number) => { |
| 29 | + const R = 6371; // Earth radius in km |
| 30 | + const dLat = toRadians(lat2 - lat1); |
| 31 | + const dLon = toRadians(lon2 - lon1); |
| 32 | + |
| 33 | + const a = Math.sin(dLat / 2) ** 2 + |
| 34 | + Math.cos(toRadians(lat1)) * Math.cos(toRadians(lat2)) * |
| 35 | + Math.sin(dLon / 2) ** 2; |
| 36 | + |
| 37 | + const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); |
| 38 | + return R * c; |
| 39 | +} |
| 40 | + |
| 41 | +const findNearestCity = (targetLat: number, targetLon: number, citiesArray: any) => { |
| 42 | + let nearestCity = null; |
| 43 | + let minDistance = Infinity; |
| 44 | + |
| 45 | + for (const city of citiesArray) { |
| 46 | + const dist = haversineDistance(targetLat, targetLon, parseFloat(city.lat), parseFloat(city.lng)); |
| 47 | + if (dist < minDistance) { |
| 48 | + minDistance = dist; |
| 49 | + nearestCity = city; |
| 50 | + } |
| 51 | + } |
| 52 | + |
| 53 | + return nearestCity; |
| 54 | +} |
3 | 55 |
|
4 | 56 | // Finds a geo name from lat/long |
5 | | -export const geoLocSearch = async (lat: number, long: number) => { |
| 57 | +export const geoLocSearch = async (lat: number, long: number, bounds: IBounds) => { |
6 | 58 | const userClause = `username=${kGeonamesUser}`; |
7 | 59 | const locClause = `lat=${lat}&lng=${long}`; |
8 | | - const url = `${kGeolocService}?${[locClause, userClause].join("&")}`; |
| 60 | + const boundsClause = `north=${bounds.north}&south=${bounds.south}&east=${bounds.east}&west=${bounds.west}`; |
| 61 | + const url = `${kGeolocCitiesService}?${[locClause, userClause, boundsClause].join("&")}`; |
9 | 62 | try { |
10 | 63 | const response = await fetch(url); |
11 | 64 | if (response.ok) { |
12 | 65 | const data = await response.json(); |
13 | | - return data?.geonames?.[0] |
14 | | - ? {success: true, values: {location:`${data.geonames[0].name}, ${data.geonames[0].adminCode1}`}} |
| 66 | + // find the nearest location nearest the lat/long |
| 67 | + const nearest = findNearestCity(lat, long, data.geonames); |
| 68 | + return nearest |
| 69 | + ? {success: true, values: {location:`${nearest.name}, ${nearest.countrycode}`}} |
15 | 70 | : {success: false, values: {location: "Unknown Location"}}; |
16 | 71 | } else { |
17 | 72 | return Promise.reject(response.statusText); |
|
0 commit comments