haversine-geolocation 1.6.0 → 2.0.0-beta.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.
Files changed (45) hide show
  1. package/README.md +299 -142
  2. package/dist/browser.cjs +2 -0
  3. package/dist/browser.cjs.map +1 -0
  4. package/dist/browser.mjs +13 -0
  5. package/dist/browser.mjs.map +1 -0
  6. package/dist/build.js +1 -1
  7. package/dist/build.js.map +1 -0
  8. package/dist/index.cjs +2 -0
  9. package/dist/index.cjs.map +1 -0
  10. package/dist/index.mjs +56 -0
  11. package/dist/index.mjs.map +1 -0
  12. package/dist/legacy.cjs +2 -0
  13. package/dist/legacy.cjs.map +1 -0
  14. package/dist/legacy.mjs +61 -0
  15. package/dist/legacy.mjs.map +1 -0
  16. package/dist/types/browser.d.ts +1 -0
  17. package/dist/types/common/consts.d.ts +5 -0
  18. package/dist/types/common/interfaces.d.ts +13 -0
  19. package/dist/types/index.d.ts +3 -0
  20. package/dist/types/legacy.d.ts +16 -0
  21. package/dist/types/utils/calculateHaversine.d.ts +1 -0
  22. package/dist/types/utils/convertMeasurement.d.ts +1 -0
  23. package/dist/types/utils/getHaversineEntity.d.ts +1 -0
  24. package/dist/types/utils/index.d.ts +7 -0
  25. package/dist/types/utils/isNodeJs.d.ts +1 -0
  26. package/dist/types/utils/showError.d.ts +1 -0
  27. package/dist/types/utils/showGeolocationError.d.ts +1 -0
  28. package/dist/types/v2/browser.d.ts +2 -0
  29. package/dist/types/v2/coordinate.d.ts +6 -0
  30. package/dist/types/v2/distance.d.ts +9 -0
  31. package/dist/types/v2/index.d.ts +8 -0
  32. package/dist/types/v2/nearest.d.ts +7 -0
  33. package/dist/types/v2/units.d.ts +3 -0
  34. package/package.json +43 -28
  35. package/.babelrc +0 -27
  36. package/.travis.yml +0 -15
  37. package/karma.conf.js +0 -35
  38. package/spec/haversine-geolocation.spec.ts +0 -54
  39. package/src/index.js.map +0 -1
  40. package/src/index.ts +0 -100
  41. package/src/interfaces/ILocationPoint.js +0 -3
  42. package/src/interfaces/ILocationPoint.js.map +0 -1
  43. package/tsconfig.json +0 -27
  44. package/webpack.config.js +0 -33
  45. /package/{src/interfaces/ILocationPoint.ts → dist/types/interfaces/ILocationPoint.d.ts} +0 -0
package/README.md CHANGED
@@ -1,170 +1,327 @@
1
- Haversine-Geolocation
2
- ========
1
+ # Haversine Geolocation
3
2
 
4
- [![NPM](https://nodei.co/npm/haversine-geolocation.png)](https://nodei.co/npm/haversine-geolocation/)
3
+ [![npm version](https://img.shields.io/npm/v/haversine-geolocation.svg)](https://www.npmjs.com/package/haversine-geolocation)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE.txt)
5
+ [![CI](https://github.com/DaniilSydorenko/haversine-geolocation/actions/workflows/ci.yml/badge.svg)](https://github.com/DaniilSydorenko/haversine-geolocation/actions/workflows/ci.yml)
6
+ [![Consumer Fixtures](https://github.com/DaniilSydorenko/haversine-geolocation/actions/workflows/consumer-fixtures.yml/badge.svg)](https://github.com/DaniilSydorenko/haversine-geolocation/actions/workflows/consumer-fixtures.yml)
7
+ [![GRS Governance](https://github.com/DaniilSydorenko/haversine-geolocation/actions/workflows/grs.yml/badge.svg)](https://github.com/DaniilSydorenko/haversine-geolocation/actions/workflows/grs.yml)
8
+ [![Secret Scan](https://github.com/DaniilSydorenko/haversine-geolocation/actions/workflows/secret-scan.yml/badge.svg)](https://github.com/DaniilSydorenko/haversine-geolocation/actions/workflows/secret-scan.yml)
5
9
 
6
- [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![Travis CI](https://travis-ci.org/DaniilSydorenko/haversine-geolocation.svg?branch=master)](https://travis-ci.org/DaniilSydorenko/haversine-geolocation)
10
+ A small JavaScript/TypeScript geospatial library built around spherical Haversine distance calculations, nearest-point search and browser geolocation integration.
7
11
 
8
- - [Introduction](#introduction)
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
- # Introduction
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 public release lines:
22
15
 
23
- <img width="439" alt="haversine" src="https://user-images.githubusercontent.com/2789198/27240436-e9a459da-52d4-11e7-8f84-f96d0b312859.png">
16
+ - **npm stable / `latest`:** `haversine-geolocation@1.6.0` — the historical V1 API;
17
+ - **npm prerelease / `next`:** `haversine-geolocation@2.0.0-alpha.1` — the first public V2 alpha.
24
18
 
25
- ## Formula
26
- ![haversine_2](https://user-images.githubusercontent.com/2789198/27240432-e67a0cf0-52d4-11e7-9acb-b935e1a84f47.png)
19
+ A normal install still receives stable V1:
27
20
 
28
- ## Pseudocode
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
+ ```bash
22
+ npm install haversine-geolocation
23
+ ```
35
24
 
36
- R = 6367 km OR 3956 mi
25
+ To test V2 explicitly:
26
+
27
+ ```bash
28
+ npm install haversine-geolocation@2.0.0-alpha.1
37
29
  ```
38
- # Installation
30
+
31
+ or:
32
+
39
33
  ```bash
40
- npm i haversine-geolocation -S
34
+ npm install haversine-geolocation@next
41
35
  ```
42
- # Basic usage
43
- ## Importing:
44
- ```javascript
36
+
37
+ V2 is public, but remains prerelease until stable `2.0.0`.
38
+
39
+ See [Migrating from V1 to V2](docs/migration/v1-to-v2.md) for the upgrade path and the [Changelog](CHANGELOG.md) for release history.
40
+
41
+ ## Published V1 usage
42
+
43
+ ### Import
44
+
45
+ ```ts
45
46
  import HaversineGeolocation from 'haversine-geolocation';
46
47
  ```
47
48
 
48
- ## API:
49
- ### Is geolocation available:
50
- #### Promise
51
- ```javascript
52
- HaversineGeolocation.isGeolocationAvailable()
53
- .then(data => {
54
- const currentPoint = {
55
- latitude: data.coords.latitude,
56
- longitude: data.coords.longitude,
57
- accuracy: data.coords.accuracy
58
- };
59
- });
60
- ```
61
- #### Async await
62
- ```javascript
63
- (async () => {
64
- const data = await HaversineGeolocation.isGeolocationAvailable();
65
- const currentPoint = {
66
- latitude: data.coords.latitude,
67
- longitude: data.coords.longitude,
68
- accuracy: data.coords.accuracy
69
- };
70
- })();
71
- ```
72
-
73
- ### Calculate distance between two points:
74
- ```javascript
75
- const points = [
49
+ ### Distance
50
+
51
+ ```ts
52
+ const a = {
53
+ latitude: 61.5322204,
54
+ longitude: 28.7515963,
55
+ accuracy: 5,
56
+ };
57
+
58
+ const b = {
59
+ latitude: 51.9971208,
60
+ longitude: 22.1455439,
61
+ accuracy: 5,
62
+ };
63
+
64
+ HaversineGeolocation.getDistanceBetween(a, b, 'km'); // 1133.1
65
+ HaversineGeolocation.getDistanceBetween(a, b, 'mi'); // 704.1
66
+ HaversineGeolocation.getDistanceBetween(a, b, 'm'); // 1133063
67
+ ```
68
+
69
+ V1 applies historical presentation rounding automatically.
70
+
71
+ ### Closest position
72
+
73
+ ```ts
74
+ const closest = HaversineGeolocation.getClosestPosition(
75
+ a,
76
+ [
76
77
  {
77
- latitude: 61.5322204,
78
- longitude: 28.7515963
78
+ latitude: 61.5322204,
79
+ longitude: 28.7515963,
80
+ accuracy: 5,
79
81
  },
80
82
  {
81
- latitude: 51.9971208,
82
- longitude: 22.1455439
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
83
+ latitude: 51.9971208,
84
+ longitude: 22.1455439,
85
+ accuracy: 5,
106
86
  },
87
+ ],
88
+ 'km',
89
+ );
90
+
91
+ console.log(closest.haversine.distance);
92
+ ```
93
+
94
+ ### Browser geolocation
95
+
96
+ ```ts
97
+ const position = await HaversineGeolocation.isGeolocationAvailable();
98
+
99
+ console.log(position.coords.latitude);
100
+ console.log(position.coords.longitude);
101
+ ```
102
+
103
+ V1 couples browser geolocation to the historical singleton. That package design is one of the areas V2 changes.
104
+
105
+ ---
106
+
107
+ ## V2 alpha — public prerelease
108
+
109
+ The canonical V2 API is available in the public `2.0.0-alpha.1` prerelease and uses named exports.
110
+
111
+ ### Distance
112
+
113
+ ```ts
114
+ import { distance } from 'haversine-geolocation';
115
+
116
+ const kilometres = distance(
117
+ { latitude: 52.2296756, longitude: 21.0122287 },
118
+ { latitude: 59.9138688, longitude: 10.7522454 },
119
+ );
120
+
121
+ const metres = distance(
122
+ { latitude: 52.2296756, longitude: 21.0122287 },
123
+ { latitude: 59.9138688, longitude: 10.7522454 },
124
+ { unit: 'm' },
125
+ );
126
+ ```
127
+
128
+ V2:
129
+
130
+ - validates coordinates;
131
+ - keeps full calculation precision;
132
+ - supports `m`, `km` and `mi`;
133
+ - uses a documented spherical mean-Earth-radius default;
134
+ - supports an explicit custom spherical radius;
135
+ - guards the Haversine intermediate against floating-point domain drift.
136
+
137
+ ### Coordinate validation
138
+
139
+ ```ts
140
+ import {
141
+ assertCoordinate,
142
+ isCoordinate,
143
+ } from 'haversine-geolocation';
144
+
145
+ isCoordinate({
146
+ latitude: 52.2296756,
147
+ longitude: 21.0122287,
148
+ }); // true
149
+ ```
150
+
151
+ Canonical V2 coordinates use:
152
+
153
+ ```ts
154
+ interface Coordinate {
155
+ latitude: number;
156
+ longitude: number;
157
+ }
158
+ ```
159
+
160
+ Latitude must be in `[-90, 90]`, longitude in `[-180, 180]`, and both values must be finite numbers.
161
+
162
+ ### Unit conversion
163
+
164
+ ```ts
165
+ import { convertDistance } from 'haversine-geolocation';
166
+
167
+ convertDistance(10, 'km', 'mi');
168
+ ```
169
+
170
+ Conversion does not apply display rounding.
171
+
172
+ ### Nearest point
173
+
174
+ ```ts
175
+ import { nearest } from 'haversine-geolocation';
176
+
177
+ const result = nearest(
178
+ { latitude: 52.2296756, longitude: 21.0122287 },
179
+ [
107
180
  {
108
- id: 2,
109
- title: 'Point 2',
110
- latitude: 51.9971208,
111
- longitude: 22.1455439
181
+ id: 'oslo',
182
+ latitude: 59.9138688,
183
+ longitude: 10.7522454,
112
184
  },
113
185
  {
114
- id: 3,
115
- title: 'Point 3',
116
- latitude: 45.3571207,
117
- longitude: 30.3435456
118
- }
119
- ];
120
-
121
- HaversineGeolocation.isGeolocationAvailable()
122
- .then(data => {
123
- const currentPoint = {
124
- latitude: data.coords.latitude,
125
- longitude: data.coords.longitude,
126
- accuracy: data.coords.accuracy
127
- };
128
-
129
- HaversineGeolocation.getClosestPosition(
130
- currentPoint,
131
- locationPoints,
132
- 'mi'
133
- );
134
- });
135
- ```
136
-
137
- #### Expected response:
138
- ```json
139
- {
140
- "id": 3,
141
- "title": "Point 3",
142
- "latitude": 45.3571207,
143
- "longitude": 30.3435456,
144
- "haversine": {
145
- "distance": 49,
146
- "measurement": "mi"
147
- }
148
- }
186
+ id: 'nearby',
187
+ latitude: 52.23,
188
+ longitude: 21.012,
189
+ },
190
+ ],
191
+ );
192
+
193
+ if (result) {
194
+ console.log(result.point);
195
+ console.log(result.distance);
196
+ }
197
+ ```
198
+
199
+ `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.
200
+
201
+ ### Browser geolocation
202
+
203
+ Browser functionality is isolated from the pure package root:
204
+
205
+ ```ts
206
+ import {
207
+ getCurrentPosition,
208
+ isGeolocationSupported,
209
+ } from 'haversine-geolocation/browser';
210
+
211
+ if (isGeolocationSupported()) {
212
+ const position = await getCurrentPosition();
213
+ console.log(position.coords);
214
+ }
215
+ ```
216
+
217
+ 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.
218
+
219
+ ### Legacy compatibility
220
+
221
+ During the V2 2.x migration line, the historical singleton remains available as a deprecated compatibility facade.
222
+
223
+ ```ts
224
+ import HaversineGeolocation from 'haversine-geolocation';
149
225
  ```
150
226
 
151
- License
152
- -------
227
+ An explicit legacy subpath is also part of the V2 package contract:
228
+
229
+ ```ts
230
+ import HaversineGeolocation from 'haversine-geolocation/legacy';
231
+ ```
232
+
233
+ The legacy facade preserves historical V1 behavior where compatibility matters, including the old radius and rounding semantics.
234
+
235
+ ## V2 package architecture
236
+
237
+ The V2 package is designed around explicit entry points:
238
+
239
+ ```text
240
+ haversine-geolocation
241
+ ├── . pure core + deprecated legacy default
242
+ ├── ./browser browser Geolocation API adapter
243
+ └── ./legacy explicit V1 compatibility facade
244
+ ```
245
+
246
+ The package build produces:
247
+
248
+ - ESM artifacts;
249
+ - CommonJS artifacts;
250
+ - TypeScript declarations;
251
+ - the historical UMD compatibility artifact.
252
+
253
+ CI validates the actual `npm pack` tarball rather than only importing repository source files.
254
+
255
+ ## Mathematical model
256
+
257
+ Haversine calculates great-circle distance on a **sphere**.
258
+
259
+ V2 uses the documented mean Earth radius:
260
+
261
+ ```text
262
+ 6,371,008.8 m
263
+ ```
264
+
265
+ The historical V1 compatibility facade keeps its original:
266
+
267
+ ```text
268
+ 6,372.8 km
269
+ ```
270
+
271
+ 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.
272
+
273
+ The core calculation is conceptually:
274
+
275
+ ```text
276
+ dlat = lat2 - lat1
277
+ dlon = lon2 - lon1
278
+
279
+ a = sin²(dlat / 2)
280
+ + cos(lat1) * cos(lat2) * sin²(dlon / 2)
281
+
282
+ a = clamp(a, 0, 1)
283
+ c = 2 * asin(sqrt(a))
284
+ d = R * c
285
+ ```
286
+
287
+ ## Architecture and migration
288
+
289
+ The V2 contract is documented in the accepted ADRs under [docs/architecture](docs/architecture/README.md).
290
+
291
+ The detailed API, migration and release guides are:
292
+
293
+ - [V2 API Reference](docs/api-reference.md)
294
+ - [Migrating from Haversine V1 to V2](docs/migration/v1-to-v2.md)
295
+ - [Haversine V2 release runbook](docs/release/v2-release-runbook.md)
296
+
297
+ Historical behavior and compatibility findings are preserved in:
298
+
299
+ - [Haversine V1 Forensic Audit](docs/research/v1-forensic-audit.md)
300
+
301
+ ## Development quality gates
302
+
303
+ The repository currently validates:
304
+
305
+ - TypeScript source typechecking;
306
+ - Vitest unit and characterization tests;
307
+ - production builds through Vite library mode;
308
+ - Node runtime smoke tests;
309
+ - CommonJS and ESM package consumers;
310
+ - npm tarball contents;
311
+ - secret scanning.
312
+
313
+ The packed package is also validated through production builds in Vite, Next.js and Nuxt.
314
+
315
+ ## Contributing
316
+
317
+ Bug fixes, tests, documentation improvements, compatibility fixes and focused maintenance improvements are welcome.
153
318
 
154
- The MIT License (MIT)
319
+ Please read [CONTRIBUTING.md](CONTRIBUTING.md) before opening a non-trivial pull request.
155
320
 
156
- Copyright (c) 2020 Daniil Sydorenko
321
+ ## Security
157
322
 
158
- Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
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:
323
+ Please do not report suspected vulnerabilities through public issues. Follow [SECURITY.md](SECURITY.md).
162
324
 
163
- The above copyright notice and this permission notice shall be included in all copies or substantial
164
- portions of the Software.
325
+ ## License
165
326
 
166
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
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.
327
+ MIT. See [LICENSE.txt](LICENSE.txt).
@@ -0,0 +1,2 @@
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=()=>{if(typeof navigator>`u`)return;let e=navigator.geolocation;return typeof e?.getCurrentPosition==`function`?e:void 0},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","geolocation","isGeolocationSupported","getCurrentPosition","reject","resolve","options"],"sources":["../src/v2/browser.ts"],"sourcesContent":["const getGeolocation = (): Geolocation | undefined => {\n if (typeof navigator === 'undefined') {\n return undefined;\n }\n\n const geolocation = navigator.geolocation;\n return typeof geolocation?.getCurrentPosition === 'function' ? geolocation : undefined;\n};\n\nexport const isGeolocationSupported = (): boolean => getGeolocation() !== undefined;\n\nexport const getCurrentPosition = (options?: PositionOptions): 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(resolve, reject, options);\n });\n"],"mappings":"mEAAA,IAAMA,MAAgD,CAClD,GAAI,OAAO,UAAc,IACrB,OAGJ,IAAMC,EAAc,UAAU,YAC9B,OAAO,OAAOA,GAAa,oBAAuB,WAAaA,EAAc,IAAA,EACjF,EAEaC,MAAwCF,EAAe,IAAM,IAAA,GAE7DG,EAAsB,GAC/B,IAAI,SAAS,EAAS,IAAW,CAC7B,IAAMF,EAAcD,EAAe,EAE/BC,IAAgB,IAAA,GAChBG,EAAW,MAAM,sDAAsD,CAAC,EAI5EH,EAAY,mBAAmBI,EAASD,EAAQE,CAAO,CAC3D,CAAC"}
@@ -0,0 +1,13 @@
1
+ //#region src/v2/browser.ts
2
+ var e = () => {
3
+ if (typeof navigator > "u") return;
4
+ let e = navigator.geolocation;
5
+ return typeof e?.getCurrentPosition == "function" ? e : void 0;
6
+ }, t = () => e() !== void 0, n = (t) => new Promise((n, r) => {
7
+ let i = e();
8
+ i === void 0 ? r(/* @__PURE__ */ Error("Geolocation API is not available in this environment")) : i.getCurrentPosition(n, r, t);
9
+ });
10
+ //#endregion
11
+ export { n as getCurrentPosition, t as isGeolocationSupported };
12
+
13
+ //# sourceMappingURL=browser.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser.mjs","names":["getGeolocation","geolocation","isGeolocationSupported","getCurrentPosition","reject","resolve","options"],"sources":["../src/v2/browser.ts"],"sourcesContent":["const getGeolocation = (): Geolocation | undefined => {\n if (typeof navigator === 'undefined') {\n return undefined;\n }\n\n const geolocation = navigator.geolocation;\n return typeof geolocation?.getCurrentPosition === 'function' ? geolocation : undefined;\n};\n\nexport const isGeolocationSupported = (): boolean => getGeolocation() !== undefined;\n\nexport const getCurrentPosition = (options?: PositionOptions): 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(resolve, reject, options);\n });\n"],"mappings":";AAAA,IAAMA,UAAgD;CAClD,IAAI,OAAO,YAAc,KACrB;CAGJ,IAAMC,IAAc,UAAU;CAC9B,OAAO,OAAOA,GAAa,sBAAuB,aAAaA,IAAc,KAAA;AACjF,GAEaC,UAAwCF,EAAe,MAAM,KAAA,GAE7DG,KAAsB,MAC/B,IAAI,SAAS,GAAS,MAAW;CAC7B,IAAMF,IAAcD,EAAe;CAEnC,AAAIC,MAAgB,KAAA,IAChBG,EAAO,gBAAI,MAAM,sDAAsD,CAAC,IAI5EH,EAAY,mBAAmBI,GAASD,GAAQE,CAAO;AAC3D,CAAC"}
package/dist/build.js CHANGED
@@ -1,2 +1,2 @@
1
- !function(e,t){if("object"==typeof exports&&"object"==typeof module)module.exports=t();else if("function"==typeof define&&define.amd)define([],t);else{var r=t();for(var n in r)("object"==typeof exports?exports:e)[n]=r[n]}}(window,(function(){return function(e){var t={};function r(n){if(t[n])return t[n].exports;var o=t[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}return r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)r.d(n,o,function(t){return e[t]}.bind(null,o));return n},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="",r(r.s=5)}([function(e,t){e.exports=function(e,t){(null==t||t>e.length)&&(t=e.length);for(var r=0,n=new Array(t);r<t;r++)n[r]=e[r];return n}},function(e,t){e.exports=function(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}},function(e,t,r){var n=r(6),o=r(7),i=r(8),a=r(9);e.exports=function(e){return n(e)||o(e)||i(e)||a()}},function(e,t){e.exports=function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}},function(e,t){function r(e,t){for(var r=0;r<t.length;r++){var n=t[r];n.enumerable=n.enumerable||!1,n.configurable=!0,"value"in n&&(n.writable=!0),Object.defineProperty(e,n.key,n)}}e.exports=function(e,t,n){return t&&r(e.prototype,t),n&&r(e,n),e}},function(e,t,r){"use strict";r.r(t);var n=r(1),o=r.n(n),i=r(2),a=r.n(i),u=r(3),c=r.n(u),s=r(4),f=r.n(s);function l(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function p(e){for(var t=1;t<arguments.length;t++){var r=null!=arguments[t]?arguments[t]:{};t%2?l(Object(r),!0).forEach((function(t){o()(e,t,r[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(r)):l(Object(r)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(r,t))}))}return e}var y=function(){function e(){c()(this,e)}return f()(e,[{key:"_convertMeasurements",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"km",r="";switch(t.toLowerCase()){case"mi":r=(.62137*e).toFixed(1);break;case"km":r=e.toFixed(1);break;case"m":r=(1e3*e).toFixed();break;default:r=e.toFixed(1)}return parseFloat(r)}},{key:"_haversine",value:function(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];var n=t.map((function(e){return e/180*Math.PI})),o=n[0],i=n[1],a=n[2],u=n[3],c=6372.8,s=a-o,f=u-i,l=Math.sin(s/2)*Math.sin(s/2)+Math.sin(f/2)*Math.sin(f/2)*Math.cos(o)*Math.cos(a),p=2*Math.asin(Math.sqrt(l));return c*p}},{key:"isGeolocationAvailable",value:function(){return new Promise((function(e,t){"geolocation"in navigator?navigator.geolocation.getCurrentPosition((function(t){e(t)}),(function(e){switch(e.code){case e.PERMISSION_DENIED:t(new Error("Error: Permission denied"));break;case e.POSITION_UNAVAILABLE:t(new Error("Error: Position unavailable"));break;case e.TIMEOUT:t(new Error("Error: Timeout"))}})):t(new Error("Error: Geolocation disabled in your browser"))}))}},{key:"getDistanceBetween",value:function(e,t,r){if(e.hasOwnProperty("latitude")&&e.hasOwnProperty("longitude")&&t.hasOwnProperty("latitude")&&t.hasOwnProperty("longitude")){var n=this._haversine(e.latitude,e.longitude,t.latitude,t.longitude);return this._convertMeasurements(n,r)}throw new Error("Error: Position latitude or longitude is not correct")}},{key:"getClosestPosition",value:function(e,t,r){var n=this,o=t.map((function(t){return n.getDistanceBetween(e,t,r)})),i=o.indexOf(Math.min.apply(Math,a()(o)));return console.log("getClosestPosition: closestPosition: ",p(p({},t[i]),{},{haversine:{distance:o[i],measurement:r,accuracy:e.accuracy}})),p(p({},t[i]),{},{haversine:{distance:o[i],measurement:r,accuracy:e.accuracy}})}}]),e}();t.default=new y},function(e,t,r){var n=r(0);e.exports=function(e){if(Array.isArray(e))return n(e)}},function(e,t){e.exports=function(e){if("undefined"!=typeof Symbol&&Symbol.iterator in Object(e))return Array.from(e)}},function(e,t,r){var n=r(0);e.exports=function(e,t){if(e){if("string"==typeof e)return n(e,t);var r=Object.prototype.toString.call(e).slice(8,-1);return"Object"===r&&e.constructor&&(r=e.constructor.name),"Map"===r||"Set"===r?Array.from(e):"Arguments"===r||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r)?n(e,t):void 0}}},function(e,t){e.exports=function(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}}])}));
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(Object.prototype.hasOwnProperty.call(e,`latitude`)&&Object.prototype.hasOwnProperty.call(e,`longitude`)&&Object.prototype.hasOwnProperty.call(t,`latitude`)&&Object.prototype.hasOwnProperty.call(t,`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={})=>{d(e.radius??6371008.8),s(e.unit??`km`)},p=(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=p,e.isCoordinate=a,e.nearest=(e,t,n={})=>{if(i(e),t.length===0){f(n);return}let r,a=1/0;for(let i of t){let t=p(e,i,n);t<a&&(r=i,a=t)}if(r!==void 0)return{point:r,distance:a}}});
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","assertDistanceOptions","options","unit","latitude1","longitude1","latitude2","longitude2","latitudeDelta","longitudeDelta","latitudeHaversine","longitudeHaversine","origin","candidates","closestPoint","closestDistance","candidate","candidateDistance"],"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 =\n Math.sin(dLat / 2) * Math.sin(dLat / 2) +\n Math.sin(dLon / 2) * Math.sin(dLon / 2) * Math.cos(lat1) * 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(\n (data: GeolocationPosition) => {\n resolve(data);\n },\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 );\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 (\n // biome-ignore lint/suspicious/noPrototypeBuiltins: Preserve ES2020-compatible V1 own-property checks without Object.hasOwn (ES2022).\n Object.prototype.hasOwnProperty.call(p1, 'latitude') &&\n // biome-ignore lint/suspicious/noPrototypeBuiltins: Preserve ES2020-compatible V1 own-property checks without Object.hasOwn (ES2022).\n Object.prototype.hasOwnProperty.call(p1, 'longitude') &&\n // biome-ignore lint/suspicious/noPrototypeBuiltins: Preserve ES2020-compatible V1 own-property checks without Object.hasOwn (ES2022).\n Object.prototype.hasOwnProperty.call(p2, 'latitude') &&\n // biome-ignore lint/suspicious/noPrototypeBuiltins: Preserve ES2020-compatible V1 own-property checks without Object.hasOwn (ES2022).\n Object.prototype.hasOwnProperty.call(p2, 'longitude')\n ) {\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 } 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) =>\n this.getDistanceBetween(current, value, measurement),\n );\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 = (value: unknown): value is { latitude: unknown; longitude: unknown } =>\n typeof value === 'object' && value !== null && 'latitude' in value && 'longitude' in value;\n\nexport function assertCoordinate(value: unknown): 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(value: unknown): 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 = (value: number, from: DistanceUnit, to: DistanceUnit): 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 { assertDistanceUnit, 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 => (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\n// Validate options even when nearest() receives no candidates to measure.\nexport const assertDistanceOptions = (options: DistanceOptions = {}): void => {\n assertRadius(options.radius ?? EARTH_MEAN_RADIUS_M);\n assertDistanceUnit(options.unit ?? 'km');\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 * Math.cos(latitude1) * 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 { assertCoordinate } from './coordinate.js';\nimport type { Coordinate } from './coordinate.js';\nimport { assertDistanceOptions, 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 assertCoordinate(origin);\n\n if (candidates.length === 0) {\n assertDistanceOptions(options);\n return undefined;\n }\n\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={})=>{u(e.radius??6371008.8),o(e.unit??`km`)},f=(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)},p=(e,t,n={})=>{if(r(e),t.length===0){d(n);return}let i,a=1/0;for(let r of t){let t=f(e,r,n);t<a&&(i=r,a=t)}if(i!==void 0)return{point:i,distance:a}};exports.EARTH_MEAN_RADIUS_M=c,exports.assertCoordinate=r,exports.assertDistanceUnit=o,exports.convertDistance=s,exports.default=e.default,exports.distance=f,exports.isCoordinate=i,exports.nearest=p;
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","assertDistanceOptions","options","distance","unit","latitude1","longitude1","latitude2","longitude2","latitudeDelta","longitudeDelta","latitudeHaversine","longitudeHaversine","a","nearest","origin","candidates","closestPoint","closestDistance","candidate","candidateDistance"],"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 = (value: unknown): value is { latitude: unknown; longitude: unknown } =>\n typeof value === 'object' && value !== null && 'latitude' in value && 'longitude' in value;\n\nexport function assertCoordinate(value: unknown): 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(value: unknown): 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 = (value: number, from: DistanceUnit, to: DistanceUnit): 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 { assertDistanceUnit, 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 => (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\n// Validate options even when nearest() receives no candidates to measure.\nexport const assertDistanceOptions = (options: DistanceOptions = {}): void => {\n assertRadius(options.radius ?? EARTH_MEAN_RADIUS_M);\n assertDistanceUnit(options.unit ?? 'km');\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 * Math.cos(latitude1) * 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 { assertCoordinate } from './coordinate.js';\nimport type { Coordinate } from './coordinate.js';\nimport { assertDistanceOptions, 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 assertCoordinate(origin);\n\n if (candidates.length === 0) {\n assertDistanceOptions(options);\n return undefined;\n }\n\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,EAAsB,GACxB,OAAOD,GAAU,YAAYA,GAAkB,aAAcA,GAAS,cAAeA,EAEzF,SAAgBE,EAAiB,EAA6C,CAC1E,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,EClCMI,EAAgD,CAClD,EAAG,EACH,GAAI,IACJ,GAAI,QACR,EAEA,SAAgBC,EAAmB,EAA+C,CAC9E,GAAIL,IAAU,KAAOA,IAAU,MAAQA,IAAU,KAC7C,MAAU,UAAU,yCAAyC,CAErE,CAEA,IAAaM,GAAmB,EAAe,EAAoB,IAA6B,CAC5F,GAAI,CAAC,OAAO,SAASN,CAAK,EACtB,MAAU,UAAU,wCAAwC,EAQhE,OALAK,EAAmBE,CAAI,EACvBF,EAAmBG,CAAE,EAENR,EAAQI,EAAgBG,GAEvBH,EAAgBI,EACpC,ECpBaC,EAAsB,UAO7BC,EAAoB,GAA6BC,EAAU,KAAK,GAAM,IAEtEC,EAAgB,GAAyB,CAC3C,GAAI,CAAC,OAAO,SAASC,CAAM,EACvB,MAAU,UAAU,gCAAgC,EAGxD,GAAIA,GAAU,EACV,MAAU,WAAW,kCAAkC,CAE/D,EAGaC,GAAyB,EAA2B,CAAC,IAAY,CAC1EF,EAAaG,EAAQ,QAAA,SAA6B,EAClDV,EAAmBU,EAAQ,MAAQ,IAAI,CAC3C,EAEaC,GACT,EACA,EACA,EAA2B,CAAC,IACnB,CACTd,EAAiBK,CAAI,EACrBL,EAAiBM,CAAE,EAEnB,IAAMS,EAAOF,EAAQ,MAAQ,KACvBF,EAASE,EAAQ,QAAA,UAEvBH,EAAaC,CAAM,EAEnB,IAAMK,EAAYR,EAAiBH,EAAK,QAAQ,EAC1CY,EAAaT,EAAiBH,EAAK,SAAS,EAC5Ca,EAAYV,EAAiBF,EAAG,QAAQ,EACxCa,EAAaX,EAAiBF,EAAG,SAAS,EAE1Cc,EAAgBF,EAAYF,EAC5BK,EAAiBF,EAAaF,EAE9BK,EAAoB,KAAK,IAAIF,EAAgB,CAAC,EAC9CG,EAAqB,KAAK,IAAIF,EAAiB,CAAC,EAEhDG,EACFF,EAAoBA,EACpBC,EAAqBA,EAAqB,KAAK,IAAIP,CAAS,EAAI,KAAK,IAAIE,CAAS,EAMtF,OAAOd,EAHc,EAAI,KAAK,KAAK,KAAK,KAD1B,KAAK,IAAI,EAAG,KAAK,IAAI,EAAGoB,CAAC,CACM,CAAK,CAAC,EACpCb,EAEgB,IAAKI,CAAI,CAC5C,ECrDaU,GACT,EACA,EACA,EAA2B,CAAC,IACG,CAG/B,GAFAzB,EAAiB0B,CAAM,EAEnBC,EAAW,SAAW,EAAG,CACzBf,EAAsBC,CAAO,EAC7B,MACJ,CAEA,IAAIe,EACAC,EAAkB,IAEtB,IAAK,IAAMC,KAAaH,EAAY,CAChC,IAAMI,EAAoBjB,EAASY,EAAQI,EAAWjB,CAAO,EAEzDkB,EAAoBF,IACpB,EAAeC,EACf,EAAkBC,EAE1B,CAEA,GAAIH,IAAiB,IAAA,GAIrB,MAAO,CACH,MAAOA,EACP,SAAUC,CACd,CACJ"}