haversine-geolocation 1.6.0 → 2.0.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +289 -143
- package/dist/browser.cjs +2 -0
- package/dist/browser.cjs.map +1 -0
- package/dist/browser.mjs +11 -0
- package/dist/browser.mjs.map +1 -0
- package/dist/build.js +1 -1
- package/dist/build.js.map +1 -0
- package/dist/index.cjs +2 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.mjs +50 -0
- package/dist/index.mjs.map +1 -0
- package/dist/legacy.cjs +2 -0
- package/dist/legacy.cjs.map +1 -0
- package/dist/legacy.mjs +61 -0
- package/dist/legacy.mjs.map +1 -0
- package/dist/types/browser.d.ts +1 -0
- package/dist/types/common/consts.d.ts +5 -0
- package/dist/types/common/interfaces.d.ts +13 -0
- package/dist/types/index.d.ts +3 -0
- package/dist/types/legacy.d.ts +16 -0
- package/dist/types/utils/calculateHaversine.d.ts +1 -0
- package/dist/types/utils/convertMeasurement.d.ts +1 -0
- package/dist/types/utils/getHaversineEntity.d.ts +1 -0
- package/dist/types/utils/index.d.ts +7 -0
- package/dist/types/utils/isNodeJs.d.ts +1 -0
- package/dist/types/utils/showError.d.ts +1 -0
- package/dist/types/utils/showGeolocationError.d.ts +1 -0
- package/dist/types/v2/browser.d.ts +2 -0
- package/dist/types/v2/coordinate.d.ts +6 -0
- package/dist/types/v2/distance.d.ts +8 -0
- package/dist/types/v2/index.d.ts +8 -0
- package/dist/types/v2/nearest.d.ts +7 -0
- package/dist/types/v2/units.d.ts +3 -0
- package/package.json +38 -27
- package/.babelrc +0 -27
- package/.travis.yml +0 -15
- package/karma.conf.js +0 -35
- package/spec/haversine-geolocation.spec.ts +0 -54
- package/src/index.js.map +0 -1
- package/src/index.ts +0 -100
- package/src/interfaces/ILocationPoint.js +0 -3
- package/src/interfaces/ILocationPoint.js.map +0 -1
- package/tsconfig.json +0 -27
- package/webpack.config.js +0 -33
- /package/{src/interfaces/ILocationPoint.ts → dist/types/interfaces/ILocationPoint.d.ts} +0 -0
package/README.md
CHANGED
|
@@ -1,170 +1,316 @@
|
|
|
1
|
-
Haversine
|
|
2
|
-
========
|
|
1
|
+
# Haversine Geolocation
|
|
3
2
|
|
|
4
|
-
[](https://www.npmjs.com/package/haversine-geolocation)
|
|
4
|
+
[](LICENSE.txt)
|
|
5
|
+
[](https://github.com/DaniilSydorenko/haversine-geolocation/actions/workflows/ci.yml)
|
|
6
|
+
[](https://github.com/DaniilSydorenko/haversine-geolocation/actions/workflows/consumer-fixtures.yml)
|
|
7
|
+
[](https://github.com/DaniilSydorenko/haversine-geolocation/actions/workflows/grs.yml)
|
|
8
|
+
[](https://github.com/DaniilSydorenko/haversine-geolocation/actions/workflows/secret-scan.yml)
|
|
5
9
|
|
|
6
|
-
|
|
10
|
+
A small JavaScript/TypeScript geospatial library built around spherical Haversine distance calculations, nearest-point search and browser geolocation integration.
|
|
7
11
|
|
|
8
|
-
|
|
9
|
-
- [Haversine Formula](#formula)
|
|
10
|
-
- [Pseudocode](#pseudocode)
|
|
11
|
-
- [Installation](#installation)
|
|
12
|
-
- [Basic usage](#basic-usage)
|
|
13
|
-
- [Importing](#import-module)
|
|
14
|
-
- [API](#api)
|
|
15
|
-
- [Is geolocation available](#is-geolocation-available)
|
|
16
|
-
- [Calculate distance between two points](#calculate-distance-between-two-points)
|
|
17
|
-
- [Calculate the closest position to user](#calculate-the-closest-position-to-user)
|
|
18
|
-
- [License](#license)
|
|
12
|
+
## Release status
|
|
19
13
|
|
|
20
|
-
|
|
21
|
-
If you need to calculate the distance between two points or you want to find closest from position to your current location let me introduce haversine-geolocation module. It based on the Haversine Formula:
|
|
14
|
+
There are currently two important lines to distinguish:
|
|
22
15
|
|
|
23
|
-
|
|
16
|
+
- **npm stable:** `haversine-geolocation@1.6.0` — the historical V1 API;
|
|
17
|
+
- **repository `master`:** the unreleased V2 development line.
|
|
24
18
|
|
|
25
|
-
|
|
26
|
-

|
|
19
|
+
The V2 code, package exports and examples below are already implemented on `master`, but **V2 has not yet been published to npm**.
|
|
27
20
|
|
|
28
|
-
|
|
29
|
-
```code()
|
|
30
|
-
dlon = lon2 - lon1
|
|
31
|
-
dlat = lat2 - lat1
|
|
32
|
-
a = (sin(dlat/2))^2 + cos(lat1) * cos(lat2) * (sin(dlon/2))^2
|
|
33
|
-
c = 2 * atan2( sqrt(a), sqrt(1-a) )
|
|
34
|
-
d = R * c (where R is the radius of the Earth)
|
|
21
|
+
If you run:
|
|
35
22
|
|
|
36
|
-
R = 6367 km OR 3956 mi
|
|
37
|
-
```
|
|
38
|
-
# Installation
|
|
39
23
|
```bash
|
|
40
|
-
npm
|
|
24
|
+
npm install haversine-geolocation
|
|
41
25
|
```
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
26
|
+
|
|
27
|
+
today, you receive the published V1 package.
|
|
28
|
+
|
|
29
|
+
See [Migrating from V1 to V2](docs/migration/v1-to-v2.md) for the planned upgrade path.
|
|
30
|
+
|
|
31
|
+
## Published V1 usage
|
|
32
|
+
|
|
33
|
+
### Import
|
|
34
|
+
|
|
35
|
+
```ts
|
|
45
36
|
import HaversineGeolocation from 'haversine-geolocation';
|
|
46
37
|
```
|
|
47
38
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
39
|
+
### Distance
|
|
40
|
+
|
|
41
|
+
```ts
|
|
42
|
+
const a = {
|
|
43
|
+
latitude: 61.5322204,
|
|
44
|
+
longitude: 28.7515963,
|
|
45
|
+
accuracy: 5,
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const b = {
|
|
49
|
+
latitude: 51.9971208,
|
|
50
|
+
longitude: 22.1455439,
|
|
51
|
+
accuracy: 5,
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
HaversineGeolocation.getDistanceBetween(a, b, 'km'); // 1133.1
|
|
55
|
+
HaversineGeolocation.getDistanceBetween(a, b, 'mi'); // 704.1
|
|
56
|
+
HaversineGeolocation.getDistanceBetween(a, b, 'm'); // 1133063
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
V1 applies historical presentation rounding automatically.
|
|
60
|
+
|
|
61
|
+
### Closest position
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
const closest = HaversineGeolocation.getClosestPosition(
|
|
65
|
+
a,
|
|
66
|
+
[
|
|
76
67
|
{
|
|
77
|
-
|
|
78
|
-
|
|
68
|
+
latitude: 61.5322204,
|
|
69
|
+
longitude: 28.7515963,
|
|
70
|
+
accuracy: 5,
|
|
79
71
|
},
|
|
80
72
|
{
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
];
|
|
85
|
-
|
|
86
|
-
// Distance in miles
|
|
87
|
-
HaversineGeolocation.getDistanceBetween(points[0], points[1], 'mi'); // 704.1 mi
|
|
88
|
-
|
|
89
|
-
// Distance in meters
|
|
90
|
-
HaversineGeolocation.getDistanceBetween(points[0], points[1], 'm'); // 1133062.7 m
|
|
91
|
-
|
|
92
|
-
// Distance in kilometers(default value)
|
|
93
|
-
HaversineGeolocation.getDistanceBetween(points[0], points[1]); // 1133.1 km
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
### Calculate the closest position to user:
|
|
97
|
-
Will return all existed properties with a small nested object haversine: { distance: val, measurement: 'val' }
|
|
98
|
-
```javascript
|
|
99
|
-
|
|
100
|
-
const locationPoints = [
|
|
101
|
-
{
|
|
102
|
-
id: 1,
|
|
103
|
-
title: 'Point 1',
|
|
104
|
-
latitude: 61.5322204,
|
|
105
|
-
longitude: 28.7515963
|
|
73
|
+
latitude: 51.9971208,
|
|
74
|
+
longitude: 22.1455439,
|
|
75
|
+
accuracy: 5,
|
|
106
76
|
},
|
|
77
|
+
],
|
|
78
|
+
'km',
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
console.log(closest.haversine.distance);
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Browser geolocation
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
const position = await HaversineGeolocation.isGeolocationAvailable();
|
|
88
|
+
|
|
89
|
+
console.log(position.coords.latitude);
|
|
90
|
+
console.log(position.coords.longitude);
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
V1 couples browser geolocation to the historical singleton. That package design is one of the areas V2 changes.
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## V2 preview — unreleased
|
|
98
|
+
|
|
99
|
+
The canonical V2 API is functional and uses named exports.
|
|
100
|
+
|
|
101
|
+
### Distance
|
|
102
|
+
|
|
103
|
+
```ts
|
|
104
|
+
import { distance } from 'haversine-geolocation';
|
|
105
|
+
|
|
106
|
+
const kilometres = distance(
|
|
107
|
+
{ latitude: 52.2296756, longitude: 21.0122287 },
|
|
108
|
+
{ latitude: 59.9138688, longitude: 10.7522454 },
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
const metres = distance(
|
|
112
|
+
{ latitude: 52.2296756, longitude: 21.0122287 },
|
|
113
|
+
{ latitude: 59.9138688, longitude: 10.7522454 },
|
|
114
|
+
{ unit: 'm' },
|
|
115
|
+
);
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
V2:
|
|
119
|
+
|
|
120
|
+
- validates coordinates;
|
|
121
|
+
- keeps full calculation precision;
|
|
122
|
+
- supports `m`, `km` and `mi`;
|
|
123
|
+
- uses a documented spherical mean-Earth-radius default;
|
|
124
|
+
- supports an explicit custom spherical radius;
|
|
125
|
+
- guards the Haversine intermediate against floating-point domain drift.
|
|
126
|
+
|
|
127
|
+
### Coordinate validation
|
|
128
|
+
|
|
129
|
+
```ts
|
|
130
|
+
import {
|
|
131
|
+
assertCoordinate,
|
|
132
|
+
isCoordinate,
|
|
133
|
+
} from 'haversine-geolocation';
|
|
134
|
+
|
|
135
|
+
isCoordinate({
|
|
136
|
+
latitude: 52.2296756,
|
|
137
|
+
longitude: 21.0122287,
|
|
138
|
+
}); // true
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Canonical V2 coordinates use:
|
|
142
|
+
|
|
143
|
+
```ts
|
|
144
|
+
interface Coordinate {
|
|
145
|
+
latitude: number;
|
|
146
|
+
longitude: number;
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
Latitude must be in `[-90, 90]`, longitude in `[-180, 180]`, and both values must be finite numbers.
|
|
151
|
+
|
|
152
|
+
### Unit conversion
|
|
153
|
+
|
|
154
|
+
```ts
|
|
155
|
+
import { convertDistance } from 'haversine-geolocation';
|
|
156
|
+
|
|
157
|
+
convertDistance(10, 'km', 'mi');
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Conversion does not apply display rounding.
|
|
161
|
+
|
|
162
|
+
### Nearest point
|
|
163
|
+
|
|
164
|
+
```ts
|
|
165
|
+
import { nearest } from 'haversine-geolocation';
|
|
166
|
+
|
|
167
|
+
const result = nearest(
|
|
168
|
+
{ latitude: 52.2296756, longitude: 21.0122287 },
|
|
169
|
+
[
|
|
107
170
|
{
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
longitude: 22.1455439
|
|
171
|
+
id: 'oslo',
|
|
172
|
+
latitude: 59.9138688,
|
|
173
|
+
longitude: 10.7522454,
|
|
112
174
|
},
|
|
113
175
|
{
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
176
|
+
id: 'nearby',
|
|
177
|
+
latitude: 52.23,
|
|
178
|
+
longitude: 21.012,
|
|
179
|
+
},
|
|
180
|
+
],
|
|
181
|
+
);
|
|
182
|
+
|
|
183
|
+
if (result) {
|
|
184
|
+
console.log(result.point);
|
|
185
|
+
console.log(result.distance);
|
|
186
|
+
}
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
`nearest()` is single-pass, does not mutate candidates, preserves candidate metadata, returns the first candidate on an exact tie, and returns `undefined` for an empty collection.
|
|
190
|
+
|
|
191
|
+
### Browser geolocation
|
|
192
|
+
|
|
193
|
+
Browser functionality is isolated from the pure package root:
|
|
194
|
+
|
|
195
|
+
```ts
|
|
196
|
+
import {
|
|
197
|
+
getCurrentPosition,
|
|
198
|
+
isGeolocationSupported,
|
|
199
|
+
} from 'haversine-geolocation/browser';
|
|
200
|
+
|
|
201
|
+
if (isGeolocationSupported()) {
|
|
202
|
+
const position = await getCurrentPosition();
|
|
203
|
+
console.log(position.coords);
|
|
204
|
+
}
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
The browser module performs no top-level access to `window`, `document` or `navigator`, so the pure package root remains safe for Node and SSR imports.
|
|
208
|
+
|
|
209
|
+
### Legacy compatibility
|
|
210
|
+
|
|
211
|
+
During the V2 2.x migration line, the historical singleton remains available as a deprecated compatibility facade.
|
|
212
|
+
|
|
213
|
+
```ts
|
|
214
|
+
import HaversineGeolocation from 'haversine-geolocation';
|
|
149
215
|
```
|
|
150
216
|
|
|
151
|
-
|
|
152
|
-
|
|
217
|
+
An explicit legacy subpath is also part of the V2 package contract:
|
|
218
|
+
|
|
219
|
+
```ts
|
|
220
|
+
import HaversineGeolocation from 'haversine-geolocation/legacy';
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
The legacy facade preserves historical V1 behavior where compatibility matters, including the old radius and rounding semantics.
|
|
224
|
+
|
|
225
|
+
## V2 package architecture
|
|
226
|
+
|
|
227
|
+
The unreleased V2 package on `master` is designed around explicit entry points:
|
|
228
|
+
|
|
229
|
+
```text
|
|
230
|
+
haversine-geolocation
|
|
231
|
+
├── . pure core + deprecated legacy default
|
|
232
|
+
├── ./browser browser Geolocation API adapter
|
|
233
|
+
└── ./legacy explicit V1 compatibility facade
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
The package build produces:
|
|
237
|
+
|
|
238
|
+
- ESM artifacts;
|
|
239
|
+
- CommonJS artifacts;
|
|
240
|
+
- TypeScript declarations;
|
|
241
|
+
- the historical UMD compatibility artifact.
|
|
242
|
+
|
|
243
|
+
CI validates the actual `npm pack` tarball rather than only importing repository source files.
|
|
244
|
+
|
|
245
|
+
## Mathematical model
|
|
246
|
+
|
|
247
|
+
Haversine calculates great-circle distance on a **sphere**.
|
|
248
|
+
|
|
249
|
+
V2 uses the documented mean Earth radius:
|
|
250
|
+
|
|
251
|
+
```text
|
|
252
|
+
6,371,008.8 m
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
The historical V1 compatibility facade keeps its original:
|
|
256
|
+
|
|
257
|
+
```text
|
|
258
|
+
6,372.8 km
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
Earth is not a perfect sphere. Haversine is therefore not a replacement for an ellipsoidal WGS84 geodesic algorithm when survey-grade or high-precision geodesy is required.
|
|
262
|
+
|
|
263
|
+
The core calculation is conceptually:
|
|
264
|
+
|
|
265
|
+
```text
|
|
266
|
+
dlat = lat2 - lat1
|
|
267
|
+
dlon = lon2 - lon1
|
|
268
|
+
|
|
269
|
+
a = sin²(dlat / 2)
|
|
270
|
+
+ cos(lat1) * cos(lat2) * sin²(dlon / 2)
|
|
271
|
+
|
|
272
|
+
a = clamp(a, 0, 1)
|
|
273
|
+
c = 2 * asin(sqrt(a))
|
|
274
|
+
d = R * c
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
## Architecture and migration
|
|
278
|
+
|
|
279
|
+
The V2 contract is documented in the accepted ADRs under [docs/architecture](docs/architecture/README.md).
|
|
280
|
+
|
|
281
|
+
The detailed migration and release guides are:
|
|
282
|
+
|
|
283
|
+
- [Migrating from Haversine V1 to V2](docs/migration/v1-to-v2.md)
|
|
284
|
+
- [Haversine V2 release runbook](docs/release/v2-release-runbook.md)
|
|
285
|
+
|
|
286
|
+
Historical behavior and compatibility findings are preserved in:
|
|
287
|
+
|
|
288
|
+
- [Haversine V1 Forensic Audit](docs/research/v1-forensic-audit.md)
|
|
289
|
+
|
|
290
|
+
## Development quality gates
|
|
291
|
+
|
|
292
|
+
The repository currently validates:
|
|
293
|
+
|
|
294
|
+
- TypeScript source typechecking;
|
|
295
|
+
- browser-based tests;
|
|
296
|
+
- production builds;
|
|
297
|
+
- Node runtime smoke tests;
|
|
298
|
+
- CommonJS and ESM package consumers;
|
|
299
|
+
- npm tarball contents;
|
|
300
|
+
- secret scanning.
|
|
301
|
+
|
|
302
|
+
The packed package is also validated through production builds in Vite, Next.js and Nuxt.
|
|
303
|
+
|
|
304
|
+
## Contributing
|
|
305
|
+
|
|
306
|
+
Bug fixes, tests, documentation improvements, compatibility fixes and focused maintenance improvements are welcome.
|
|
153
307
|
|
|
154
|
-
|
|
308
|
+
Please read [CONTRIBUTING.md](CONTRIBUTING.md) before opening a non-trivial pull request.
|
|
155
309
|
|
|
156
|
-
|
|
310
|
+
## Security
|
|
157
311
|
|
|
158
|
-
|
|
159
|
-
documentation files (the "Software"), to deal in the Software without restriction, including without limitation
|
|
160
|
-
the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
|
|
161
|
-
and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
312
|
+
Please do not report suspected vulnerabilities through public issues. Follow [SECURITY.md](SECURITY.md).
|
|
162
313
|
|
|
163
|
-
|
|
164
|
-
portions of the Software.
|
|
314
|
+
## License
|
|
165
315
|
|
|
166
|
-
|
|
167
|
-
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
168
|
-
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
|
169
|
-
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
170
|
-
DEALINGS IN THE SOFTWARE.
|
|
316
|
+
MIT. See [LICENSE.txt](LICENSE.txt).
|
package/dist/browser.cjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=()=>{if(typeof navigator<`u`)return navigator.geolocation},t=()=>e()!==void 0,n=t=>new Promise((n,r)=>{let i=e();i===void 0?r(Error(`Geolocation API is not available in this environment`)):i.getCurrentPosition(n,r,t)});exports.getCurrentPosition=n,exports.isGeolocationSupported=t;
|
|
2
|
+
//# sourceMappingURL=browser.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser.cjs","names":["getGeolocation","isGeolocationSupported","getCurrentPosition","geolocation","reject","resolve","options"],"sources":["../src/v2/browser.ts"],"sourcesContent":["const getGeolocation = (): Geolocation | undefined => {\n if (typeof navigator === 'undefined') {\n return undefined;\n }\n\n return navigator.geolocation;\n};\n\nexport const isGeolocationSupported = (): boolean =>\n getGeolocation() !== undefined;\n\nexport const getCurrentPosition = (\n options?: PositionOptions,\n): Promise<GeolocationPosition> =>\n new Promise((resolve, reject) => {\n const geolocation = getGeolocation();\n\n if (geolocation === undefined) {\n reject(new Error('Geolocation API is not available in this environment'));\n return;\n }\n\n geolocation.getCurrentPosition(\n resolve,\n reject,\n options,\n );\n });\n"],"mappings":"mEAAA,IAAMA,MAAgD,CAClD,GAAI,OAAO,UAAc,IAIzB,OAAO,UAAU,WACrB,EAEaC,MACTD,EAAe,IAAM,IAAA,GAEZE,EACT,GAEA,IAAI,SAAS,EAAS,IAAW,CAC7B,IAAMC,EAAcH,EAAe,EAE/BG,IAAgB,IAAA,GAChBC,EAAW,MAAM,sDAAsD,CAAC,EAI5ED,EAAY,mBACRE,EACAD,EACAE,CACJ,CACJ,CAAC"}
|
package/dist/browser.mjs
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
//#region src/v2/browser.ts
|
|
2
|
+
var e = () => {
|
|
3
|
+
if (typeof navigator < "u") return navigator.geolocation;
|
|
4
|
+
}, t = () => e() !== void 0, n = (t) => new Promise((n, r) => {
|
|
5
|
+
let i = e();
|
|
6
|
+
i === void 0 ? r(/* @__PURE__ */ Error("Geolocation API is not available in this environment")) : i.getCurrentPosition(n, r, t);
|
|
7
|
+
});
|
|
8
|
+
//#endregion
|
|
9
|
+
export { n as getCurrentPosition, t as isGeolocationSupported };
|
|
10
|
+
|
|
11
|
+
//# sourceMappingURL=browser.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser.mjs","names":["getGeolocation","isGeolocationSupported","getCurrentPosition","geolocation","reject","resolve","options"],"sources":["../src/v2/browser.ts"],"sourcesContent":["const getGeolocation = (): Geolocation | undefined => {\n if (typeof navigator === 'undefined') {\n return undefined;\n }\n\n return navigator.geolocation;\n};\n\nexport const isGeolocationSupported = (): boolean =>\n getGeolocation() !== undefined;\n\nexport const getCurrentPosition = (\n options?: PositionOptions,\n): Promise<GeolocationPosition> =>\n new Promise((resolve, reject) => {\n const geolocation = getGeolocation();\n\n if (geolocation === undefined) {\n reject(new Error('Geolocation API is not available in this environment'));\n return;\n }\n\n geolocation.getCurrentPosition(\n resolve,\n reject,\n options,\n );\n });\n"],"mappings":";AAAA,IAAMA,UAAgD;CAClD,IAAI,OAAO,YAAc,KAIzB,OAAO,UAAU;AACrB,GAEaC,UACTD,EAAe,MAAM,KAAA,GAEZE,KACT,MAEA,IAAI,SAAS,GAAS,MAAW;CAC7B,IAAMC,IAAcH,EAAe;CAEnC,AAAIG,MAAgB,KAAA,IAChBC,EAAO,gBAAI,MAAM,sDAAsD,CAAC,IAI5ED,EAAY,mBACRE,GACAD,GACAE,CACJ;AACJ,CAAC"}
|
package/dist/build.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
|
|
1
|
+
(function(e,t){typeof exports==`object`&&typeof module<`u`?t(exports):typeof define==`function`&&define.amd?define([`exports`],t):(e=typeof globalThis<`u`?globalThis:e||self,t(e.HaversineGeolocation={}))})(this,function(e){Object.defineProperties(e,{__esModule:{value:!0},[Symbol.toStringTag]:{value:`Module`}});var t=new class{_convertMeasurements(e,t=`km`){let n=``;switch(t.toLowerCase()){case`mi`:n=(e*.62137).toFixed(1);break;case`km`:n=e.toFixed(1);break;case`m`:n=(e*1e3).toFixed();break;default:n=e.toFixed(1)}return parseFloat(n)}_haversine(...e){let t=e.map(e=>e/180*Math.PI),n=t[0],r=t[1],i=t[2],a=t[3],o=i-n,s=a-r,c=Math.sin(o/2)*Math.sin(o/2)+Math.sin(s/2)*Math.sin(s/2)*Math.cos(n)*Math.cos(i);return 2*Math.asin(Math.sqrt(Math.min(1,Math.max(0,c))))*6372.8}isGeolocationAvailable(){return new Promise((e,t)=>{`geolocation`in navigator?navigator.geolocation.getCurrentPosition(t=>{e(t)},e=>{switch(e.code){case e.PERMISSION_DENIED:t(Error(`Error: Permission denied`));break;case e.POSITION_UNAVAILABLE:t(Error(`Error: Position unavailable`));break;case e.TIMEOUT:t(Error(`Error: Timeout`))}}):t(Error(`Error: Geolocation disabled in your browser`))})}getDistanceBetween(e,t,n){if(e.hasOwnProperty(`latitude`)&&e.hasOwnProperty(`longitude`)&&t.hasOwnProperty(`latitude`)&&t.hasOwnProperty(`longitude`)){let r=this._haversine(e.latitude,e.longitude,t.latitude,t.longitude);return this._convertMeasurements(r,n)}throw Error(`Error: Position latitude or longitude is not correct`)}getClosestPosition(e,t,n){let r=t.map(t=>this.getDistanceBetween(e,t,n)),i=r.indexOf(Math.min(...r));return{...t[i],haversine:{distance:r[i],measurement:n,accuracy:e.accuracy}}}},n=e=>typeof e==`number`&&Number.isFinite(e),r=e=>typeof e==`object`&&!!e&&`latitude`in e&&`longitude`in e;function i(e){if(!r(e))throw TypeError(`Coordinate must provide latitude and longitude`);if(!n(e.latitude)||!n(e.longitude))throw TypeError(`Coordinate latitude and longitude must be finite numbers`);if(e.latitude<-90||e.latitude>90)throw RangeError(`Coordinate latitude must be between -90 and 90`);if(e.longitude<-180||e.longitude>180)throw RangeError(`Coordinate longitude must be between -180 and 180`)}var a=e=>{try{return i(e),!0}catch{return!1}},o={m:1,km:1e3,mi:1609.344};function s(e){if(e!==`m`&&e!==`km`&&e!==`mi`)throw TypeError(`Distance unit must be one of: m, km, mi`)}var c=(e,t,n)=>{if(!Number.isFinite(e))throw TypeError(`Distance value must be a finite number`);return s(t),s(n),e*o[t]/o[n]},l=6371008.8,u=e=>e*Math.PI/180,d=e=>{if(!Number.isFinite(e))throw TypeError(`Radius must be a finite number`);if(e<=0)throw RangeError(`Radius must be greater than zero`)},f=(e,t,n={})=>{i(e),i(t);let r=n.unit??`km`,a=n.radius??6371008.8;d(a);let o=u(e.latitude),s=u(e.longitude),l=u(t.latitude),f=u(t.longitude),p=l-o,m=f-s,h=Math.sin(p/2),g=Math.sin(m/2),_=h*h+g*g*Math.cos(o)*Math.cos(l);return c(2*Math.asin(Math.sqrt(Math.min(1,Math.max(0,_))))*a,`m`,r)};e.EARTH_MEAN_RADIUS_M=l,e.assertCoordinate=i,e.assertDistanceUnit=s,e.convertDistance=c,e.default=t,e.distance=f,e.isCoordinate=a,e.nearest=(e,t,n={})=>{let r,i=1/0;for(let a of t){let t=f(e,a,n);t<i&&(r=a,i=t)}if(r!==void 0)return{point:r,distance:i}}});
|
|
2
2
|
//# sourceMappingURL=build.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"build.js","names":["factory","global","exports","legacy_default","rawValue","measurement","distance","args","rad","deg","lat1","lon1","lat2","lon2","dLat","dLon","a","resolve","data","error","reject","p1","p2","distances","otherPoints","current","value","indexOfSmallest","isFiniteNumber","hasCoordinateShape","assertCoordinate","isCoordinate","METRES_PER_UNIT","assertDistanceUnit","convertDistance","from","to","EARTH_MEAN_RADIUS_M","degreesToRadians","degrees","assertRadius","radius","unit","options","latitude1","longitude1","latitude2","longitude2","latitudeDelta","longitudeDelta","latitudeHaversine","longitudeHaversine","closestPoint","closestDistance","candidate","candidates","candidateDistance","origin"],"sources":["../src/legacy.ts","../src/v2/coordinate.ts","../src/v2/units.ts","../src/v2/distance.ts","../src/v2/nearest.ts"],"sourcesContent":["import type { ILocationPoint } from \"./interfaces/ILocationPoint.js\";\n\nclass HaversineGeolocation {\n _convertMeasurements(distance: number, measurement: string = 'km'): number {\n let rawValue: string = '';\n switch (measurement.toLowerCase()) {\n case 'mi' :\n rawValue = (distance * 0.62137).toFixed(1);\n break;\n case 'km' :\n rawValue = distance.toFixed(1);\n break;\n case 'm' :\n rawValue = (distance * 1000).toFixed();\n break;\n default :\n rawValue = distance.toFixed(1);\n }\n return parseFloat(rawValue);\n }\n\n _haversine(...args: number[]): number {\n const rad: number[] = args.map((deg: number) => deg / 180.0 * Math.PI);\n const lat1: number = rad[0];\n const lon1: number = rad[1];\n const lat2: number = rad[2];\n const lon2: number = rad[3];\n const R: number = 6372.8;\n const dLat: number = lat2 - lat1;\n const dLon: number = lon2 - lon1;\n\n const a: number = Math.sin(dLat / 2) *\n Math.sin(dLat / 2) +\n Math.sin(dLon / 2) *\n Math.sin(dLon / 2) *\n Math.cos(lat1) *\n Math.cos(lat2);\n\n const safeA: number = Math.min(1, Math.max(0, a));\n const c: number = 2 * Math.asin(Math.sqrt(safeA));\n\n return R * c;\n };\n\n isGeolocationAvailable(): Promise<GeolocationPosition> {\n return new Promise((resolve, reject) => {\n if ('geolocation' in navigator) {\n navigator.geolocation.getCurrentPosition((data: GeolocationPosition) => {\n resolve(data);\n }, (error: GeolocationPositionError) => {\n switch (error.code) {\n case error.PERMISSION_DENIED:\n reject(new Error(\"Error: Permission denied\"));\n break;\n case error.POSITION_UNAVAILABLE:\n reject(new Error(\"Error: Position unavailable\"));\n break;\n case error.TIMEOUT:\n reject(new Error(\"Error: Timeout\"));\n break;\n }\n });\n } else {\n reject(new Error(\"Error: Geolocation disabled in your browser\"));\n }\n });\n };\n\n getDistanceBetween(p1: ILocationPoint, p2: ILocationPoint, measurement: string): number {\n if (p1.hasOwnProperty('latitude') && p1.hasOwnProperty('longitude') &&\n p2.hasOwnProperty('latitude') && p2.hasOwnProperty('longitude')) {\n const distance: number = this._haversine(\n p1.latitude,\n p1.longitude,\n p2.latitude,\n p2.longitude\n );\n\n return this._convertMeasurements(distance, measurement)\n\n } else {\n throw new Error(\"Error: Position latitude or longitude is not correct\");\n }\n }\n\n getClosestPosition(\n current: ILocationPoint,\n otherPoints: ILocationPoint[],\n measurement: string,\n ): ILocationPoint & {\n haversine: {\n distance: number;\n measurement: string;\n accuracy: number;\n };\n } {\n const distances: number[] = otherPoints.map((value: any) => this.getDistanceBetween(current, value, measurement));\n const indexOfSmallest: number = distances.indexOf(Math.min(...distances));\n\n return {\n ...otherPoints[indexOfSmallest],\n haversine: {\n distance: distances[indexOfSmallest],\n measurement,\n accuracy: current.accuracy\n },\n };\n };\n}\n\nexport default new HaversineGeolocation();\n","export interface Coordinate {\n latitude: number;\n longitude: number;\n}\n\nconst isFiniteNumber = (value: unknown): value is number =>\n typeof value === 'number' && Number.isFinite(value);\n\nconst hasCoordinateShape = (\n value: unknown,\n): value is { latitude: unknown; longitude: unknown } =>\n typeof value === 'object' &&\n value !== null &&\n 'latitude' in value &&\n 'longitude' in value;\n\nexport function assertCoordinate(\n value: unknown,\n): asserts value is Coordinate {\n if (!hasCoordinateShape(value)) {\n throw new TypeError('Coordinate must provide latitude and longitude');\n }\n\n if (!isFiniteNumber(value.latitude) || !isFiniteNumber(value.longitude)) {\n throw new TypeError('Coordinate latitude and longitude must be finite numbers');\n }\n\n if (value.latitude < -90 || value.latitude > 90) {\n throw new RangeError('Coordinate latitude must be between -90 and 90');\n }\n\n if (value.longitude < -180 || value.longitude > 180) {\n throw new RangeError('Coordinate longitude must be between -180 and 180');\n }\n}\n\nexport const isCoordinate = (value: unknown): value is Coordinate => {\n try {\n assertCoordinate(value);\n return true;\n } catch {\n return false;\n }\n};\n","export type DistanceUnit = 'm' | 'km' | 'mi';\n\nconst METRES_PER_UNIT: Record<DistanceUnit, number> = {\n m: 1,\n km: 1000,\n mi: 1609.344,\n};\n\nexport function assertDistanceUnit(\n value: unknown,\n): asserts value is DistanceUnit {\n if (value !== 'm' && value !== 'km' && value !== 'mi') {\n throw new TypeError('Distance unit must be one of: m, km, mi');\n }\n}\n\nexport const convertDistance = (\n value: number,\n from: DistanceUnit,\n to: DistanceUnit,\n): number => {\n if (!Number.isFinite(value)) {\n throw new TypeError('Distance value must be a finite number');\n }\n\n assertDistanceUnit(from);\n assertDistanceUnit(to);\n\n const metres = value * METRES_PER_UNIT[from];\n\n return metres / METRES_PER_UNIT[to];\n};\n","import { assertCoordinate } from './coordinate.js';\nimport type { Coordinate } from './coordinate.js';\nimport { convertDistance } from './units.js';\nimport type { DistanceUnit } from './units.js';\n\nexport const EARTH_MEAN_RADIUS_M = 6_371_008.8;\n\nexport interface DistanceOptions {\n unit?: DistanceUnit;\n radius?: number;\n}\n\nconst degreesToRadians = (degrees: number): number =>\n degrees * Math.PI / 180;\n\nconst assertRadius = (radius: number): void => {\n if (!Number.isFinite(radius)) {\n throw new TypeError('Radius must be a finite number');\n }\n\n if (radius <= 0) {\n throw new RangeError('Radius must be greater than zero');\n }\n};\n\nexport const distance = (\n from: Coordinate,\n to: Coordinate,\n options: DistanceOptions = {},\n): number => {\n assertCoordinate(from);\n assertCoordinate(to);\n\n const unit = options.unit ?? 'km';\n const radius = options.radius ?? EARTH_MEAN_RADIUS_M;\n\n assertRadius(radius);\n\n const latitude1 = degreesToRadians(from.latitude);\n const longitude1 = degreesToRadians(from.longitude);\n const latitude2 = degreesToRadians(to.latitude);\n const longitude2 = degreesToRadians(to.longitude);\n\n const latitudeDelta = latitude2 - latitude1;\n const longitudeDelta = longitude2 - longitude1;\n\n const latitudeHaversine = Math.sin(latitudeDelta / 2);\n const longitudeHaversine = Math.sin(longitudeDelta / 2);\n\n const a =\n latitudeHaversine * latitudeHaversine +\n longitudeHaversine * longitudeHaversine *\n Math.cos(latitude1) *\n Math.cos(latitude2);\n\n const safeA = Math.min(1, Math.max(0, a));\n const centralAngle = 2 * Math.asin(Math.sqrt(safeA));\n const metres = radius * centralAngle;\n\n return convertDistance(metres, 'm', unit);\n};\n","import type { Coordinate } from './coordinate.js';\nimport { distance } from './distance.js';\nimport type { DistanceOptions } from './distance.js';\n\nexport interface NearestResult<T> {\n point: T;\n distance: number;\n}\n\nexport const nearest = <T extends Coordinate>(\n origin: Coordinate,\n candidates: readonly T[],\n options: DistanceOptions = {},\n): NearestResult<T> | undefined => {\n let closestPoint: T | undefined;\n let closestDistance = Infinity;\n\n for (const candidate of candidates) {\n const candidateDistance = distance(origin, candidate, options);\n\n if (candidateDistance < closestDistance) {\n closestPoint = candidate;\n closestDistance = candidateDistance;\n }\n }\n\n if (closestPoint === undefined) {\n return undefined;\n }\n\n return {\n point: closestPoint,\n distance: closestDistance,\n };\n};\n"],"mappings":"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"}
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:`Module`}});const e=require("./legacy.cjs");var t=e=>typeof e==`number`&&Number.isFinite(e),n=e=>typeof e==`object`&&!!e&&`latitude`in e&&`longitude`in e;function r(e){if(!n(e))throw TypeError(`Coordinate must provide latitude and longitude`);if(!t(e.latitude)||!t(e.longitude))throw TypeError(`Coordinate latitude and longitude must be finite numbers`);if(e.latitude<-90||e.latitude>90)throw RangeError(`Coordinate latitude must be between -90 and 90`);if(e.longitude<-180||e.longitude>180)throw RangeError(`Coordinate longitude must be between -180 and 180`)}var i=e=>{try{return r(e),!0}catch{return!1}},a={m:1,km:1e3,mi:1609.344};function o(e){if(e!==`m`&&e!==`km`&&e!==`mi`)throw TypeError(`Distance unit must be one of: m, km, mi`)}var s=(e,t,n)=>{if(!Number.isFinite(e))throw TypeError(`Distance value must be a finite number`);return o(t),o(n),e*a[t]/a[n]},c=6371008.8,l=e=>e*Math.PI/180,u=e=>{if(!Number.isFinite(e))throw TypeError(`Radius must be a finite number`);if(e<=0)throw RangeError(`Radius must be greater than zero`)},d=(e,t,n={})=>{r(e),r(t);let i=n.unit??`km`,a=n.radius??6371008.8;u(a);let o=l(e.latitude),c=l(e.longitude),d=l(t.latitude),f=l(t.longitude),p=d-o,m=f-c,h=Math.sin(p/2),g=Math.sin(m/2),_=h*h+g*g*Math.cos(o)*Math.cos(d);return s(2*Math.asin(Math.sqrt(Math.min(1,Math.max(0,_))))*a,`m`,i)},f=(e,t,n={})=>{let r,i=1/0;for(let a of t){let t=d(e,a,n);t<i&&(r=a,i=t)}if(r!==void 0)return{point:r,distance:i}};exports.EARTH_MEAN_RADIUS_M=c,exports.assertCoordinate=r,exports.assertDistanceUnit=o,exports.convertDistance=s,exports.default=e.default,exports.distance=d,exports.isCoordinate=i,exports.nearest=f;
|
|
2
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["require_legacy","isFiniteNumber","value","hasCoordinateShape","assertCoordinate","isCoordinate","METRES_PER_UNIT","assertDistanceUnit","convertDistance","from","to","EARTH_MEAN_RADIUS_M","degreesToRadians","degrees","assertRadius","radius","distance","unit","options","latitude1","longitude1","latitude2","longitude2","latitudeDelta","longitudeDelta","latitudeHaversine","longitudeHaversine","a","nearest","closestPoint","closestDistance","candidate","candidates","candidateDistance","origin"],"sources":["../src/v2/coordinate.ts","../src/v2/units.ts","../src/v2/distance.ts","../src/v2/nearest.ts"],"sourcesContent":["export interface Coordinate {\n latitude: number;\n longitude: number;\n}\n\nconst isFiniteNumber = (value: unknown): value is number =>\n typeof value === 'number' && Number.isFinite(value);\n\nconst hasCoordinateShape = (\n value: unknown,\n): value is { latitude: unknown; longitude: unknown } =>\n typeof value === 'object' &&\n value !== null &&\n 'latitude' in value &&\n 'longitude' in value;\n\nexport function assertCoordinate(\n value: unknown,\n): asserts value is Coordinate {\n if (!hasCoordinateShape(value)) {\n throw new TypeError('Coordinate must provide latitude and longitude');\n }\n\n if (!isFiniteNumber(value.latitude) || !isFiniteNumber(value.longitude)) {\n throw new TypeError('Coordinate latitude and longitude must be finite numbers');\n }\n\n if (value.latitude < -90 || value.latitude > 90) {\n throw new RangeError('Coordinate latitude must be between -90 and 90');\n }\n\n if (value.longitude < -180 || value.longitude > 180) {\n throw new RangeError('Coordinate longitude must be between -180 and 180');\n }\n}\n\nexport const isCoordinate = (value: unknown): value is Coordinate => {\n try {\n assertCoordinate(value);\n return true;\n } catch {\n return false;\n }\n};\n","export type DistanceUnit = 'm' | 'km' | 'mi';\n\nconst METRES_PER_UNIT: Record<DistanceUnit, number> = {\n m: 1,\n km: 1000,\n mi: 1609.344,\n};\n\nexport function assertDistanceUnit(\n value: unknown,\n): asserts value is DistanceUnit {\n if (value !== 'm' && value !== 'km' && value !== 'mi') {\n throw new TypeError('Distance unit must be one of: m, km, mi');\n }\n}\n\nexport const convertDistance = (\n value: number,\n from: DistanceUnit,\n to: DistanceUnit,\n): number => {\n if (!Number.isFinite(value)) {\n throw new TypeError('Distance value must be a finite number');\n }\n\n assertDistanceUnit(from);\n assertDistanceUnit(to);\n\n const metres = value * METRES_PER_UNIT[from];\n\n return metres / METRES_PER_UNIT[to];\n};\n","import { assertCoordinate } from './coordinate.js';\nimport type { Coordinate } from './coordinate.js';\nimport { convertDistance } from './units.js';\nimport type { DistanceUnit } from './units.js';\n\nexport const EARTH_MEAN_RADIUS_M = 6_371_008.8;\n\nexport interface DistanceOptions {\n unit?: DistanceUnit;\n radius?: number;\n}\n\nconst degreesToRadians = (degrees: number): number =>\n degrees * Math.PI / 180;\n\nconst assertRadius = (radius: number): void => {\n if (!Number.isFinite(radius)) {\n throw new TypeError('Radius must be a finite number');\n }\n\n if (radius <= 0) {\n throw new RangeError('Radius must be greater than zero');\n }\n};\n\nexport const distance = (\n from: Coordinate,\n to: Coordinate,\n options: DistanceOptions = {},\n): number => {\n assertCoordinate(from);\n assertCoordinate(to);\n\n const unit = options.unit ?? 'km';\n const radius = options.radius ?? EARTH_MEAN_RADIUS_M;\n\n assertRadius(radius);\n\n const latitude1 = degreesToRadians(from.latitude);\n const longitude1 = degreesToRadians(from.longitude);\n const latitude2 = degreesToRadians(to.latitude);\n const longitude2 = degreesToRadians(to.longitude);\n\n const latitudeDelta = latitude2 - latitude1;\n const longitudeDelta = longitude2 - longitude1;\n\n const latitudeHaversine = Math.sin(latitudeDelta / 2);\n const longitudeHaversine = Math.sin(longitudeDelta / 2);\n\n const a =\n latitudeHaversine * latitudeHaversine +\n longitudeHaversine * longitudeHaversine *\n Math.cos(latitude1) *\n Math.cos(latitude2);\n\n const safeA = Math.min(1, Math.max(0, a));\n const centralAngle = 2 * Math.asin(Math.sqrt(safeA));\n const metres = radius * centralAngle;\n\n return convertDistance(metres, 'm', unit);\n};\n","import type { Coordinate } from './coordinate.js';\nimport { distance } from './distance.js';\nimport type { DistanceOptions } from './distance.js';\n\nexport interface NearestResult<T> {\n point: T;\n distance: number;\n}\n\nexport const nearest = <T extends Coordinate>(\n origin: Coordinate,\n candidates: readonly T[],\n options: DistanceOptions = {},\n): NearestResult<T> | undefined => {\n let closestPoint: T | undefined;\n let closestDistance = Infinity;\n\n for (const candidate of candidates) {\n const candidateDistance = distance(origin, candidate, options);\n\n if (candidateDistance < closestDistance) {\n closestPoint = candidate;\n closestDistance = candidateDistance;\n }\n }\n\n if (closestPoint === undefined) {\n return undefined;\n }\n\n return {\n point: closestPoint,\n distance: closestDistance,\n };\n};\n"],"mappings":"+HAKA,IAAMC,EAAkB,GACpB,OAAOC,GAAU,UAAY,OAAO,SAASA,CAAK,EAEhDC,EACF,GAEA,OAAOD,GAAU,YACjBA,GACA,aAAcA,GACd,cAAeA,EAEnB,SAAgBE,EACZ,EAC2B,CAC3B,GAAI,CAACD,EAAmBD,CAAK,EACzB,MAAU,UAAU,gDAAgD,EAGxE,GAAI,CAACD,EAAeC,EAAM,QAAQ,GAAK,CAACD,EAAeC,EAAM,SAAS,EAClE,MAAU,UAAU,0DAA0D,EAGlF,GAAIA,EAAM,SAAW,KAAOA,EAAM,SAAW,GACzC,MAAU,WAAW,gDAAgD,EAGzE,GAAIA,EAAM,UAAY,MAAQA,EAAM,UAAY,IAC5C,MAAU,WAAW,mDAAmD,CAEhF,CAEA,IAAaG,EAAgB,GAAwC,CACjE,GAAI,CAEA,OADAD,EAAiBF,CAAK,EACf,EACX,MAAQ,CACJ,MAAO,EACX,CACJ,ECzCMI,EAAgD,CAClD,EAAG,EACH,GAAI,IACJ,GAAI,QACR,EAEA,SAAgBC,EACZ,EAC6B,CAC7B,GAAIL,IAAU,KAAOA,IAAU,MAAQA,IAAU,KAC7C,MAAU,UAAU,yCAAyC,CAErE,CAEA,IAAaM,GACT,EACA,EACA,IACS,CACT,GAAI,CAAC,OAAO,SAASN,CAAK,EACtB,MAAU,UAAU,wCAAwC,EAQhE,OALAK,EAAmBE,CAAI,EACvBF,EAAmBG,CAAE,EAENR,EAAQI,EAAgBG,GAEvBH,EAAgBI,EACpC,EC1BaC,EAAsB,UAO7BC,EAAoB,GACtBC,EAAU,KAAK,GAAK,IAElBC,EAAgB,GAAyB,CAC3C,GAAI,CAAC,OAAO,SAASC,CAAM,EACvB,MAAU,UAAU,gCAAgC,EAGxD,GAAIA,GAAU,EACV,MAAU,WAAW,kCAAkC,CAE/D,EAEaC,GACT,EACA,EACA,EAA2B,CAAC,IACnB,CACTZ,EAAiBK,CAAI,EACrBL,EAAiBM,CAAE,EAEnB,IAAMO,EAAOC,EAAQ,MAAQ,KACvBH,EAASG,EAAQ,QAAA,UAEvBJ,EAAaC,CAAM,EAEnB,IAAMI,EAAYP,EAAiBH,EAAK,QAAQ,EAC1CW,EAAaR,EAAiBH,EAAK,SAAS,EAC5CY,EAAYT,EAAiBF,EAAG,QAAQ,EACxCY,EAAaV,EAAiBF,EAAG,SAAS,EAE1Ca,EAAgBF,EAAYF,EAC5BK,EAAiBF,EAAaF,EAE9BK,EAAoB,KAAK,IAAIF,EAAgB,CAAC,EAC9CG,EAAqB,KAAK,IAAIF,EAAiB,CAAC,EAEhDG,EACFF,EAAoBA,EACpBC,EAAqBA,EACrB,KAAK,IAAIP,CAAS,EAClB,KAAK,IAAIE,CAAS,EAMtB,OAAOb,EAHc,EAAI,KAAK,KAAK,KAAK,KAD1B,KAAK,IAAI,EAAG,KAAK,IAAI,EAAGmB,CAAC,CACM,CAAK,CAAC,EACpCZ,EAEgB,IAAKE,CAAI,CAC5C,ECnDaW,GACT,EACA,EACA,EAA2B,CAAC,IACG,CAC/B,IAAIC,EACAC,EAAkB,IAEtB,IAAK,IAAMC,KAAaC,EAAY,CAChC,IAAMC,EAAoBjB,EAASkB,EAAQH,EAAWb,CAAO,EAEzDe,EAAoBH,IACpB,EAAeC,EACf,EAAkBE,EAE1B,CAEA,GAAIJ,IAAiB,IAAA,GAIrB,MAAO,CACH,MAAOA,EACP,SAAUC,CACd,CACJ"}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import e from "./legacy.mjs";
|
|
2
|
+
//#region src/v2/coordinate.ts
|
|
3
|
+
var t = (e) => typeof e == "number" && Number.isFinite(e), n = (e) => typeof e == "object" && !!e && "latitude" in e && "longitude" in e;
|
|
4
|
+
function r(e) {
|
|
5
|
+
if (!n(e)) throw TypeError("Coordinate must provide latitude and longitude");
|
|
6
|
+
if (!t(e.latitude) || !t(e.longitude)) throw TypeError("Coordinate latitude and longitude must be finite numbers");
|
|
7
|
+
if (e.latitude < -90 || e.latitude > 90) throw RangeError("Coordinate latitude must be between -90 and 90");
|
|
8
|
+
if (e.longitude < -180 || e.longitude > 180) throw RangeError("Coordinate longitude must be between -180 and 180");
|
|
9
|
+
}
|
|
10
|
+
var i = (e) => {
|
|
11
|
+
try {
|
|
12
|
+
return r(e), !0;
|
|
13
|
+
} catch {
|
|
14
|
+
return !1;
|
|
15
|
+
}
|
|
16
|
+
}, a = {
|
|
17
|
+
m: 1,
|
|
18
|
+
km: 1e3,
|
|
19
|
+
mi: 1609.344
|
|
20
|
+
};
|
|
21
|
+
function o(e) {
|
|
22
|
+
if (e !== "m" && e !== "km" && e !== "mi") throw TypeError("Distance unit must be one of: m, km, mi");
|
|
23
|
+
}
|
|
24
|
+
var s = (e, t, n) => {
|
|
25
|
+
if (!Number.isFinite(e)) throw TypeError("Distance value must be a finite number");
|
|
26
|
+
return o(t), o(n), e * a[t] / a[n];
|
|
27
|
+
}, c = 6371008.8, l = (e) => e * Math.PI / 180, u = (e) => {
|
|
28
|
+
if (!Number.isFinite(e)) throw TypeError("Radius must be a finite number");
|
|
29
|
+
if (e <= 0) throw RangeError("Radius must be greater than zero");
|
|
30
|
+
}, d = (e, t, n = {}) => {
|
|
31
|
+
r(e), r(t);
|
|
32
|
+
let i = n.unit ?? "km", a = n.radius ?? 6371008.8;
|
|
33
|
+
u(a);
|
|
34
|
+
let o = l(e.latitude), c = l(e.longitude), d = l(t.latitude), f = l(t.longitude), p = d - o, m = f - c, h = Math.sin(p / 2), g = Math.sin(m / 2), _ = h * h + g * g * Math.cos(o) * Math.cos(d);
|
|
35
|
+
return s(2 * Math.asin(Math.sqrt(Math.min(1, Math.max(0, _)))) * a, "m", i);
|
|
36
|
+
}, f = (e, t, n = {}) => {
|
|
37
|
+
let r, i = Infinity;
|
|
38
|
+
for (let a of t) {
|
|
39
|
+
let t = d(e, a, n);
|
|
40
|
+
t < i && (r = a, i = t);
|
|
41
|
+
}
|
|
42
|
+
if (r !== void 0) return {
|
|
43
|
+
point: r,
|
|
44
|
+
distance: i
|
|
45
|
+
};
|
|
46
|
+
};
|
|
47
|
+
//#endregion
|
|
48
|
+
export { c as EARTH_MEAN_RADIUS_M, r as assertCoordinate, o as assertDistanceUnit, s as convertDistance, e as default, d as distance, i as isCoordinate, f as nearest };
|
|
49
|
+
|
|
50
|
+
//# sourceMappingURL=index.mjs.map
|