Skip to content

Commit fb935a0

Browse files
committed
Gets the map area to determine the cities in the area.
Gets the nearest city to the lat/long pin
1 parent 38830ea commit fb935a0

File tree

3 files changed

+74
-6
lines changed

3 files changed

+74
-6
lines changed

src/models/plugin-state.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ import {
44
kPinColorAttributeName, kPinDataContextName, kPinLatAttributeName, kPinLongAttributeName
55
} from "../data/constants";
66
import { NeoDataset } from "./neo-types";
7-
import { geoLocSearch } from "../utils/location-utils";
7+
import { getMapComponentInfo } from "../utils/codap-utils";
8+
import { geoLocSearch, MapComponentInfo } from "../utils/location-utils";
89

910
interface IMapPin {
1011
color: string;
@@ -34,13 +35,16 @@ class PluginState {
3435

3536
*updatePins(): Generator<Promise<IResult>, void, IResult> {
3637
const pinResult = yield(getAllItems(kPinDataContextName));
38+
const map = yield getMapComponentInfo();
39+
const mapInfo = map.values as MapComponentInfo;
40+
const bounds = mapInfo?.bounds ?? {north: 90, south: -90, east: 180,west: -180};
3741
if (pinResult.success) {
3842
const pinData = pinResult.values as any;
3943
const labels: string[] = [];
4044
for (const pin of pinData) {
4145
const lat = pin.values[kPinLatAttributeName];
4246
const long = pin.values[kPinLongAttributeName];
43-
const locationResult = yield geoLocSearch(lat, long);
47+
const locationResult = yield geoLocSearch(lat, long, bounds);
4448
const label = locationResult.values.location;
4549
labels.push(label);
4650
}

src/utils/codap-utils.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,3 +209,12 @@ export const updateLocationColorMap = async (colorMap: Record<string,string>) =>
209209
{ colormap: colorMap });
210210
return updateColorMap;
211211
};
212+
213+
export const getMapComponentInfo = async () => {
214+
const mapInfo = await sendMessage("get", `component[${kMapName}]`);
215+
if (mapInfo.success) {
216+
return mapInfo;
217+
} else {
218+
return Promise.reject(mapInfo);
219+
}
220+
}

src/utils/location-utils.ts

Lines changed: 59 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,72 @@
11
const kGeonamesUser = "codap";
22
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+
}
355

456
// 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) => {
658
const userClause = `username=${kGeonamesUser}`;
759
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("&")}`;
962
try {
1063
const response = await fetch(url);
1164
if (response.ok) {
1265
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}`}}
1570
: {success: false, values: {location: "Unknown Location"}};
1671
} else {
1772
return Promise.reject(response.statusText);

0 commit comments

Comments
 (0)