Skip to content

Commit 9528f2c

Browse files
committed
✨ added geo functions
1 parent 60e05bf commit 9528f2c

File tree

3 files changed

+101
-0
lines changed

3 files changed

+101
-0
lines changed

src/geo.spec.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { haversine, bearing } from "./geo";
2+
3+
test("haversine", () => {
4+
const lat1 = 17.408992;
5+
const lon1 = 78.490229;
6+
const lat2 = 17.41583;
7+
const lon2 = 78.498372;
8+
expect(haversine(lat1, lon1, lat2, lon2)).toBe(1150.9015135733925);
9+
});
10+
11+
test("bearing", () => {
12+
expect((bearing(52.205, 0.119, 48.857, 2.351) * 180) / Math.PI).toBe(
13+
156.16658258153174
14+
);
15+
});

src/geo.ts

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
const { sin, cos, atan2, sqrt, PI } = Math;
2+
3+
const PIBy180 = PI / 180;
4+
const _180ByPI = 180 / PI;
5+
6+
export const convToRad = (degrees: number) => degrees * PIBy180;
7+
export const convToDeg = (rads: number) => rads * _180ByPI;
8+
const sq = (num: number) => num * num;
9+
10+
export const R = 6371e3; // Radius of earth in metres
11+
12+
export const validate = (lat: number, lon: number) => {
13+
if (!Number.isFinite(lat)) {
14+
return false;
15+
}
16+
if (!Number.isFinite(lon)) {
17+
return false;
18+
}
19+
if (lat > 90 || lat < -90) {
20+
return false;
21+
}
22+
if (lon > 180 || lon < -180) {
23+
return false;
24+
}
25+
// pathological case. (0, 0) is water
26+
if (lon === 0 && lat === 0) {
27+
return false;
28+
}
29+
return true;
30+
};
31+
32+
export const haversine = (
33+
lat1: number,
34+
lon1: number,
35+
lat2: number,
36+
lon2: number
37+
) => {
38+
// in degrees
39+
// convert to radians
40+
lat1 = convToRad(lat1);
41+
lon1 = convToRad(lon1);
42+
lat2 = convToRad(lat2);
43+
lon2 = convToRad(lon2);
44+
45+
const dLat = lat2 - lat1;
46+
const dLon = lon2 - lon1;
47+
48+
const a = sq(sin(dLat / 2)) + cos(lat1) * cos(lat2) * sq(sin(dLon / 2));
49+
50+
const c = 2 * atan2(sqrt(a), sqrt(1 - a));
51+
52+
return R * c;
53+
};
54+
55+
export const bearing = (
56+
lat1: number,
57+
lon1: number,
58+
lat2: number,
59+
lon2: number
60+
) => {
61+
// convert to radians
62+
lat1 = convToRad(lat1);
63+
lon1 = convToRad(lon1);
64+
lat2 = convToRad(lat2);
65+
lon2 = convToRad(lon2);
66+
67+
const dLon = lon2 - lon1;
68+
return atan2(
69+
sin(dLon) * cos(lat2),
70+
cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(dLon)
71+
);
72+
};
73+
74+
export const normaliseDegs = (degs: number) => (degs + 360) % 360;
75+
76+
export const bearingInDegs = (
77+
lat1: number,
78+
lon1: number,
79+
lat2: number,
80+
lon2: number
81+
) => {
82+
const rads = bearing(lat1, lon1, lat2, lon2);
83+
const degs = convToDeg(rads);
84+
return normaliseDegs(degs);
85+
};

src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
export * as Lang from "./lang";
22
export * as Graph from "./graph";
33
export * as BinPack from "./binpack";
4+
export * as Geo from "./geo";

0 commit comments

Comments
 (0)