haversine-geolocation 1.5.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.
Files changed (46) hide show
  1. package/README.md +289 -143
  2. package/dist/browser.cjs +2 -0
  3. package/dist/browser.cjs.map +1 -0
  4. package/dist/browser.mjs +11 -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 +50 -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 +8 -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 +38 -27
  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/tags.txt +0 -2
  44. package/tsconfig.json +0 -27
  45. package/webpack.config.js +0 -33
  46. /package/{src/interfaces/ILocationPoint.ts → dist/types/interfaces/ILocationPoint.d.ts} +0 -0
package/README.md CHANGED
@@ -1,170 +1,316 @@
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 important lines to distinguish:
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:** `haversine-geolocation@1.6.0` — the historical V1 API;
17
+ - **repository `master`:** the unreleased V2 development line.
24
18
 
25
- ## Formula
26
- ![haversine_2](https://user-images.githubusercontent.com/2789198/27240432-e67a0cf0-52d4-11e7-9acb-b935e1a84f47.png)
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
- ## 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
+ If you run:
35
22
 
36
- R = 6367 km OR 3956 mi
37
- ```
38
- # Installation
39
23
  ```bash
40
- npm i haversine-geolocation -S
24
+ npm install haversine-geolocation
41
25
  ```
42
- # Basic usage
43
- ## Importing:
44
- ```javascript
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
- ## 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 = [
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
- latitude: 61.5322204,
78
- longitude: 28.7515963
68
+ latitude: 61.5322204,
69
+ longitude: 28.7515963,
70
+ accuracy: 5,
79
71
  },
80
72
  {
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
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
- id: 2,
109
- title: 'Point 2',
110
- latitude: 51.9971208,
111
- longitude: 22.1455439
171
+ id: 'oslo',
172
+ latitude: 59.9138688,
173
+ longitude: 10.7522454,
112
174
  },
113
175
  {
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
- }
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
- License
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
- The MIT License (MIT)
308
+ Please read [CONTRIBUTING.md](CONTRIBUTING.md) before opening a non-trivial pull request.
155
309
 
156
- Copyright (c) 2020 Daniil Sydorenko
310
+ ## Security
157
311
 
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:
312
+ Please do not report suspected vulnerabilities through public issues. Follow [SECURITY.md](SECURITY.md).
162
313
 
163
- The above copyright notice and this permission notice shall be included in all copies or substantial
164
- portions of the Software.
314
+ ## License
165
315
 
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.
316
+ 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 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"}
@@ -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
- !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(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