@accelint/geo 0.5.1 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/README.md +233 -1
- package/catalog-info.yaml +1 -1
- package/dist/cartesian.d.ts +2 -0
- package/dist/cartesian.js +2 -0
- package/dist/cartesian.js.map +1 -1
- package/dist/coordinates/coordinate.d.ts +41 -7
- package/dist/coordinates/coordinate.js +102 -12
- package/dist/coordinates/coordinate.js.map +1 -1
- package/dist/coordinates/latlon/decimal-degrees/formatter.d.ts +20 -0
- package/dist/coordinates/latlon/decimal-degrees/formatter.js +38 -0
- package/dist/coordinates/latlon/decimal-degrees/formatter.js.map +1 -1
- package/dist/coordinates/latlon/decimal-degrees/parser.d.ts +68 -2
- package/dist/coordinates/latlon/decimal-degrees/parser.js +66 -5
- package/dist/coordinates/latlon/decimal-degrees/parser.js.map +1 -1
- package/dist/coordinates/latlon/decimal-degrees/system.d.ts +32 -0
- package/dist/coordinates/latlon/decimal-degrees/system.js +31 -0
- package/dist/coordinates/latlon/decimal-degrees/system.js.map +1 -1
- package/dist/coordinates/latlon/degrees-decimal-minutes/formatter.d.ts +20 -0
- package/dist/coordinates/latlon/degrees-decimal-minutes/formatter.js +37 -0
- package/dist/coordinates/latlon/degrees-decimal-minutes/formatter.js.map +1 -1
- package/dist/coordinates/latlon/degrees-decimal-minutes/parser.d.ts +72 -2
- package/dist/coordinates/latlon/degrees-decimal-minutes/parser.js +66 -3
- package/dist/coordinates/latlon/degrees-decimal-minutes/parser.js.map +1 -1
- package/dist/coordinates/latlon/degrees-decimal-minutes/system.d.ts +32 -0
- package/dist/coordinates/latlon/degrees-decimal-minutes/system.js +31 -0
- package/dist/coordinates/latlon/degrees-decimal-minutes/system.js.map +1 -1
- package/dist/coordinates/latlon/degrees-minutes-seconds/formatter.d.ts +20 -0
- package/dist/coordinates/latlon/degrees-minutes-seconds/formatter.js +37 -0
- package/dist/coordinates/latlon/degrees-minutes-seconds/formatter.js.map +1 -1
- package/dist/coordinates/latlon/degrees-minutes-seconds/parser.d.ts +74 -2
- package/dist/coordinates/latlon/degrees-minutes-seconds/parser.js +66 -3
- package/dist/coordinates/latlon/degrees-minutes-seconds/parser.js.map +1 -1
- package/dist/coordinates/latlon/degrees-minutes-seconds/system.d.ts +32 -0
- package/dist/coordinates/latlon/degrees-minutes-seconds/system.js +31 -0
- package/dist/coordinates/latlon/degrees-minutes-seconds/system.js.map +1 -1
- package/dist/coordinates/latlon/internal/coordinate-system.d.ts +22 -0
- package/dist/coordinates/latlon/internal/create-cache.d.ts +17 -1
- package/dist/coordinates/latlon/internal/create-cache.js +19 -3
- package/dist/coordinates/latlon/internal/create-cache.js.map +1 -1
- package/dist/coordinates/latlon/internal/exhaustive-errors.d.ts +15 -0
- package/dist/coordinates/latlon/internal/exhaustive-errors.js +28 -0
- package/dist/coordinates/latlon/internal/exhaustive-errors.js.map +1 -1
- package/dist/coordinates/latlon/internal/format.d.ts +20 -0
- package/dist/coordinates/latlon/internal/format.js +20 -0
- package/dist/coordinates/latlon/internal/format.js.map +1 -1
- package/dist/coordinates/latlon/internal/in-range.d.ts +23 -0
- package/dist/coordinates/latlon/internal/in-range.js +24 -0
- package/dist/coordinates/latlon/internal/in-range.js.map +1 -1
- package/dist/coordinates/latlon/internal/index.d.ts +16 -1
- package/dist/coordinates/latlon/internal/index.js +25 -1
- package/dist/coordinates/latlon/internal/index.js.map +1 -1
- package/dist/coordinates/latlon/internal/lexer.d.ts +2 -0
- package/dist/coordinates/latlon/internal/lexer.js +26 -0
- package/dist/coordinates/latlon/internal/lexer.js.map +1 -1
- package/dist/coordinates/latlon/internal/normalize.d.ts +67 -0
- package/dist/coordinates/latlon/internal/normalize.js +87 -0
- package/dist/coordinates/latlon/internal/normalize.js.map +1 -0
- package/dist/coordinates/latlon/internal/ordinal.d.ts +25 -0
- package/dist/coordinates/latlon/internal/ordinal.js +25 -0
- package/dist/coordinates/latlon/internal/ordinal.js.map +1 -1
- package/dist/coordinates/latlon/internal/parse-format.d.ts +22 -0
- package/dist/coordinates/latlon/internal/parse-format.js +43 -1
- package/dist/coordinates/latlon/internal/parse-format.js.map +1 -1
- package/dist/coordinates/latlon/internal/parse.d.ts +2 -0
- package/dist/coordinates/latlon/internal/parse.js +4 -1
- package/dist/coordinates/latlon/internal/parse.js.map +1 -1
- package/dist/coordinates/latlon/internal/pipes/check-ambiguous.d.ts +17 -0
- package/dist/coordinates/latlon/internal/pipes/check-ambiguous.js +16 -0
- package/dist/coordinates/latlon/internal/pipes/check-ambiguous.js.map +1 -1
- package/dist/coordinates/latlon/internal/pipes/check-numbers.d.ts +25 -0
- package/dist/coordinates/latlon/internal/pipes/check-numbers.js +33 -0
- package/dist/coordinates/latlon/internal/pipes/check-numbers.js.map +1 -1
- package/dist/coordinates/latlon/internal/pipes/fix-bearings.d.ts +17 -0
- package/dist/coordinates/latlon/internal/pipes/fix-bearings.js +31 -0
- package/dist/coordinates/latlon/internal/pipes/fix-bearings.js.map +1 -1
- package/dist/coordinates/latlon/internal/pipes/fix-dividers.d.ts +17 -0
- package/dist/coordinates/latlon/internal/pipes/fix-dividers.js +29 -0
- package/dist/coordinates/latlon/internal/pipes/fix-dividers.js.map +1 -1
- package/dist/coordinates/latlon/internal/pipes/genome.d.ts +16 -0
- package/dist/coordinates/latlon/internal/pipes/genome.js +41 -0
- package/dist/coordinates/latlon/internal/pipes/genome.js.map +1 -1
- package/dist/coordinates/latlon/internal/pipes/index.d.ts +32 -2
- package/dist/coordinates/latlon/internal/pipes/index.js +57 -4
- package/dist/coordinates/latlon/internal/pipes/index.js.map +1 -1
- package/dist/coordinates/latlon/internal/pipes/simpler.d.ts +16 -3
- package/dist/coordinates/latlon/internal/pipes/simpler.js +15 -3
- package/dist/coordinates/latlon/internal/pipes/simpler.js.map +1 -1
- package/dist/coordinates/latlon/internal/validate.d.ts +75 -0
- package/dist/coordinates/latlon/internal/validate.js +105 -0
- package/dist/coordinates/latlon/internal/validate.js.map +1 -0
- package/dist/coordinates/latlon/internal/violation.d.ts +18 -0
- package/dist/coordinates/latlon/internal/violation.js +18 -0
- package/dist/coordinates/latlon/internal/violation.js.map +1 -1
- package/dist/coordinates/mgrs/parser.d.ts +24 -0
- package/dist/coordinates/mgrs/parser.js +56 -0
- package/dist/coordinates/mgrs/parser.js.map +1 -1
- package/dist/coordinates/mgrs/system.d.ts +31 -0
- package/dist/coordinates/mgrs/system.js +30 -0
- package/dist/coordinates/mgrs/system.js.map +1 -1
- package/dist/coordinates/utm/parser.d.ts +24 -0
- package/dist/coordinates/utm/parser.js +56 -0
- package/dist/coordinates/utm/parser.js.map +1 -1
- package/dist/coordinates/utm/system.d.ts +21 -0
- package/dist/coordinates/utm/system.js +20 -0
- package/dist/coordinates/utm/system.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +3 -1
- package/dist/patterning.d.ts +12 -2
- package/dist/patterning.js +12 -2
- package/dist/patterning.js.map +1 -1
- package/package.json +3 -1
package/CHANGELOG.md
CHANGED
package/README.md
CHANGED
|
@@ -1,9 +1,241 @@
|
|
|
1
1
|
# @accelint/geo
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Geographic coordinate parsing, conversion, and formatting for multiple coordinate systems.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **5 Coordinate Systems**: Parse and format coordinates in multiple notation systems
|
|
8
|
+
- Decimal Degrees (DD): `40.7128° N / 74.0060° W`
|
|
9
|
+
- Degrees Decimal Minutes (DDM): `40° 42.768' N / 74° 0.36' W`
|
|
10
|
+
- Degrees Minutes Seconds (DMS): `40° 42' 46.08" N / 74° 0' 21.6" W`
|
|
11
|
+
- Military Grid Reference System (MGRS): `18T WL 80000 00000`
|
|
12
|
+
- Universal Transverse Mercator (UTM): `18N 585628 4511644`
|
|
13
|
+
|
|
14
|
+
- **Flexible Format Ordering**: Convert between LATLON and LONLAT formats
|
|
15
|
+
- **Input Validation**: Detailed error messages for invalid coordinates
|
|
16
|
+
- **Performance Optimized**: Immutable coordinate objects with intelligent caching
|
|
17
|
+
- **Type Safe**: Full TypeScript support with complete type definitions
|
|
4
18
|
|
|
5
19
|
## Installation
|
|
6
20
|
|
|
7
21
|
```sh
|
|
8
22
|
npm install @accelint/geo
|
|
9
23
|
```
|
|
24
|
+
|
|
25
|
+
## Quick Start
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
import { createCoordinate, coordinateSystems } from '@accelint/geo';
|
|
29
|
+
|
|
30
|
+
// Create a coordinate parser for Decimal Degrees
|
|
31
|
+
const create = createCoordinate(coordinateSystems.dd, 'LATLON');
|
|
32
|
+
const coord = create('40.7128 / -74.0060');
|
|
33
|
+
|
|
34
|
+
// Or use numeric input
|
|
35
|
+
const coord2 = create([40.7128, -74.0060]);
|
|
36
|
+
|
|
37
|
+
// Access different formats
|
|
38
|
+
coord.dd(); // '40.7128 N / 74.006 W'
|
|
39
|
+
coord.ddm(); // '40 42.768 N / 74 0.36 W'
|
|
40
|
+
coord.dms(); // '40 42 46.08 N / 74 0 21.6 W'
|
|
41
|
+
coord.mgrs(); // '18T WL 80000 00000'
|
|
42
|
+
coord.utm(); // '18N 585628 4511644'
|
|
43
|
+
|
|
44
|
+
// Change format order
|
|
45
|
+
coord.dms('LONLAT'); // '74 0 21.6 W / 40 42 46.08 N'
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Usage Examples
|
|
49
|
+
|
|
50
|
+
### Parsing Different Coordinate Systems
|
|
51
|
+
|
|
52
|
+
```typescript
|
|
53
|
+
import { createCoordinate, coordinateSystems } from '@accelint/geo';
|
|
54
|
+
|
|
55
|
+
// Parse Decimal Degrees
|
|
56
|
+
const parseDD = createCoordinate(coordinateSystems.dd);
|
|
57
|
+
const coordDD = parseDD('40.7128 / -74.0060');
|
|
58
|
+
|
|
59
|
+
// Parse Degrees Minutes Seconds
|
|
60
|
+
const parseDMS = createCoordinate(coordinateSystems.dms);
|
|
61
|
+
const coordDMS = parseDMS('40° 42\' 46.08" N / 74° 0\' 21.6" W');
|
|
62
|
+
|
|
63
|
+
// Parse UTM coordinates
|
|
64
|
+
const parseUTM = createCoordinate(coordinateSystems.utm);
|
|
65
|
+
const coordUTM = parseUTM('18N 585628 4511644');
|
|
66
|
+
|
|
67
|
+
// Parse MGRS coordinates
|
|
68
|
+
const parseMGRS = createCoordinate(coordinateSystems.mgrs);
|
|
69
|
+
const coordMGRS = parseMGRS('18T WL 80000 00000');
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Numeric Input
|
|
73
|
+
|
|
74
|
+
```typescript
|
|
75
|
+
import { createCoordinate, coordinateSystems } from '@accelint/geo';
|
|
76
|
+
import type { LatLonTuple, LonLatTuple } from '@accelint/geo';
|
|
77
|
+
|
|
78
|
+
// Using typed tuple input (order follows format parameter)
|
|
79
|
+
const create = createCoordinate(coordinateSystems.dd, 'LATLON');
|
|
80
|
+
const latlon: LatLonTuple = [40.7128, -74.0060];
|
|
81
|
+
const coordTuple = create(latlon);
|
|
82
|
+
|
|
83
|
+
// LONLAT order (GeoJSON convention)
|
|
84
|
+
const createLonLat = createCoordinate(coordinateSystems.dd, 'LONLAT');
|
|
85
|
+
const lonlat: LonLatTuple = [-74.0060, 40.7128];
|
|
86
|
+
const coordLonLat = createLonLat(lonlat);
|
|
87
|
+
|
|
88
|
+
// Using object input
|
|
89
|
+
const coordObj = create({ lat: 40.7128, lon: -74.0060 });
|
|
90
|
+
|
|
91
|
+
// Object aliases also work
|
|
92
|
+
const coordAlt = create({ latitude: 40.7128, longitude: -74.0060 });
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Converting Between Formats
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
const create = createCoordinate(coordinateSystems.dd, 'LATLON');
|
|
99
|
+
const coord = create('40.7128 / -74.0060');
|
|
100
|
+
|
|
101
|
+
// Convert to different coordinate systems
|
|
102
|
+
coord.dd(); // '40.7128 N / 74.006 W'
|
|
103
|
+
coord.ddm(); // '40 42.768 N / 74 0.36 W'
|
|
104
|
+
coord.dms(); // '40 42 46.08 N / 74 0 21.6 W'
|
|
105
|
+
coord.mgrs(); // '18T WL 80000 00000'
|
|
106
|
+
coord.utm(); // '18N 585628 4511644'
|
|
107
|
+
|
|
108
|
+
// Change coordinate order (LATLON to LONLAT)
|
|
109
|
+
coord.dd('LONLAT'); // '74.006 W / 40.7128 N'
|
|
110
|
+
coord.dms('LONLAT'); // '74 0 21.6 W / 40 42 46.08 N'
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### Error Handling
|
|
114
|
+
|
|
115
|
+
```typescript
|
|
116
|
+
const create = createCoordinate(coordinateSystems.dd);
|
|
117
|
+
const coord = create('invalid input');
|
|
118
|
+
|
|
119
|
+
if (!coord.valid) {
|
|
120
|
+
console.log(coord.errors);
|
|
121
|
+
// ['[ERROR] No input.']
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Access raw coordinate values
|
|
125
|
+
if (coord.valid) {
|
|
126
|
+
console.log(coord.raw.LAT); // 40.7128
|
|
127
|
+
console.log(coord.raw.LON); // -74.0060
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## API Reference
|
|
132
|
+
|
|
133
|
+
### `createCoordinate(system?, format?)`
|
|
134
|
+
|
|
135
|
+
Creates a coordinate parser function.
|
|
136
|
+
|
|
137
|
+
**Parameters:**
|
|
138
|
+
|
|
139
|
+
- `system` (CoordinateSystem, optional): Coordinate system to use for parsing. Defaults to `coordinateSystems.dd`
|
|
140
|
+
- `format` (Format, optional): Coordinate order format (`'LATLON'` or `'LONLAT'`). Defaults to `'LATLON'`
|
|
141
|
+
|
|
142
|
+
**Returns:** Function that accepts a coordinate input and returns a Coordinate object
|
|
143
|
+
|
|
144
|
+
**Input types:**
|
|
145
|
+
|
|
146
|
+
- **String**: Coordinate string in the specified system's format (e.g., `'40.7128 / -74.0060'`)
|
|
147
|
+
- **Tuple**: `LatLonTuple` or `LonLatTuple` — a `readonly [number, number]` with named elements, where order follows the `format` parameter (e.g., `[40.7128, -74.0060]` for LATLON)
|
|
148
|
+
- **Object**: Object with lat/lon properties. Accepts case-insensitive keys:
|
|
149
|
+
- `lat` / `lon`
|
|
150
|
+
- `latitude` / `longitude`
|
|
151
|
+
|
|
152
|
+
**Example:**
|
|
153
|
+
|
|
154
|
+
```typescript
|
|
155
|
+
const create = createCoordinate(coordinateSystems.dd, 'LATLON');
|
|
156
|
+
const coord = create('40.7128 / -74.0060');
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### `coordinateSystems`
|
|
160
|
+
|
|
161
|
+
Object containing available coordinate system implementations.
|
|
162
|
+
|
|
163
|
+
**Properties:**
|
|
164
|
+
|
|
165
|
+
- `dd` - Decimal Degrees system
|
|
166
|
+
- `ddm` - Degrees Decimal Minutes system
|
|
167
|
+
- `dms` - Degrees Minutes Seconds system
|
|
168
|
+
- `mgrs` - Military Grid Reference System
|
|
169
|
+
- `utm` - Universal Transverse Mercator system
|
|
170
|
+
|
|
171
|
+
### Coordinate Object
|
|
172
|
+
|
|
173
|
+
The object returned by calling the function from `createCoordinate()`.
|
|
174
|
+
|
|
175
|
+
**Properties:**
|
|
176
|
+
|
|
177
|
+
- `valid` (boolean): Whether the coordinate was successfully parsed
|
|
178
|
+
- `errors` (string[]): Array of error messages (empty if valid)
|
|
179
|
+
- `raw` (object): Raw coordinate values with `LAT` and `LON` properties
|
|
180
|
+
|
|
181
|
+
**Methods:**
|
|
182
|
+
|
|
183
|
+
- `dd(format?)`: Format as Decimal Degrees
|
|
184
|
+
- `ddm(format?)`: Format as Degrees Decimal Minutes
|
|
185
|
+
- `dms(format?)`: Format as Degrees Minutes Seconds
|
|
186
|
+
- `mgrs(format?)`: Format as MGRS
|
|
187
|
+
- `utm(format?)`: Format as UTM
|
|
188
|
+
|
|
189
|
+
Each method accepts an optional `format` parameter (`'LATLON'` or `'LONLAT'`) to override the default format order.
|
|
190
|
+
|
|
191
|
+
### TypeScript Types
|
|
192
|
+
|
|
193
|
+
The package exports named tuple types for type-safe coordinate handling:
|
|
194
|
+
|
|
195
|
+
- **`LatLonTuple`** — `readonly [latitude: number, longitude: number]` — tuple in lat/lon order
|
|
196
|
+
- **`LonLatTuple`** — `readonly [longitude: number, latitude: number]` — tuple in lon/lat order (GeoJSON convention)
|
|
197
|
+
- **`CoordinateTuple`** — `LatLonTuple | LonLatTuple` — union of both orderings
|
|
198
|
+
- **`CoordinateObject`** — `Record<string, number>` — object with string keys (e.g., `{ lat, lon }`)
|
|
199
|
+
- **`CoordinateInput`** — `string | CoordinateTuple | CoordinateObject` — all accepted input types
|
|
200
|
+
|
|
201
|
+
Named tuple elements provide IDE hints for element order, preventing lat/lon mix-ups:
|
|
202
|
+
|
|
203
|
+
```typescript
|
|
204
|
+
import type { LatLonTuple, LonLatTuple } from '@accelint/geo';
|
|
205
|
+
|
|
206
|
+
const latlon: LatLonTuple = [40.7128, -74.0060]; // [latitude, longitude]
|
|
207
|
+
const lonlat: LonLatTuple = [-74.0060, 40.7128]; // [longitude, latitude]
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
## Coordinate System Formats
|
|
211
|
+
|
|
212
|
+
### Decimal Degrees (DD)
|
|
213
|
+
|
|
214
|
+
- Format: `±DD.DDDD°`
|
|
215
|
+
- Example: `40.7128° N / 74.0060° W`
|
|
216
|
+
|
|
217
|
+
### Degrees Decimal Minutes (DDM)
|
|
218
|
+
|
|
219
|
+
- Format: `±DD° MM.MMMM'`
|
|
220
|
+
- Example: `40° 42.768' N / 74° 0.36' W`
|
|
221
|
+
|
|
222
|
+
### Degrees Minutes Seconds (DMS)
|
|
223
|
+
|
|
224
|
+
- Format: `±DD° MM' SS.SSSS"`
|
|
225
|
+
- Example: `40° 42' 46.08" N / 74° 0' 21.6" W`
|
|
226
|
+
|
|
227
|
+
### Military Grid Reference System (MGRS)
|
|
228
|
+
|
|
229
|
+
- Format: `ZZ[A-Z] [A-Z][A-Z] EEEEE NNNNN`
|
|
230
|
+
- Example: `18T WL 80000 00000`
|
|
231
|
+
|
|
232
|
+
### Universal Transverse Mercator (UTM)
|
|
233
|
+
|
|
234
|
+
- Format: `ZZ[N|S] EEEEEE NNNNNNN`
|
|
235
|
+
- Example: `18N 585628 4511644`
|
|
236
|
+
|
|
237
|
+
## License
|
|
238
|
+
|
|
239
|
+
Apache-2.0
|
|
240
|
+
|
|
241
|
+
Copyright 2024-2026 Hypergiant Galactic Systems Inc.
|
package/catalog-info.yaml
CHANGED
|
@@ -15,7 +15,7 @@ metadata:
|
|
|
15
15
|
annotations:
|
|
16
16
|
backstage.io/edit-url: https://github.com/gohypergiant/standard-toolkit/blob/main/packages/geo/catalog-info.yaml
|
|
17
17
|
backstage.io/techdocs-ref: dir:.
|
|
18
|
-
package/version: 0.
|
|
18
|
+
package/version: 0.6.0
|
|
19
19
|
github.com/project-slug: gohypergiant/standard-toolkit
|
|
20
20
|
links:
|
|
21
21
|
- url: https://github.com/gohypergiant/standard-toolkit/tree/main/packages/geo
|
package/dist/cartesian.d.ts
CHANGED
package/dist/cartesian.js
CHANGED
|
@@ -27,8 +27,10 @@
|
|
|
27
27
|
* pure function
|
|
28
28
|
*
|
|
29
29
|
* @example
|
|
30
|
+
* ```typescript
|
|
30
31
|
* const result = cartesian([1, 2], ['a', 'b']);
|
|
31
32
|
* // result: [[1, 'a'], [1, 'b'], [2, 'a'], [2, 'b']]
|
|
33
|
+
* ```
|
|
32
34
|
*/
|
|
33
35
|
function cartesian(...all) {
|
|
34
36
|
return all.reduce((results, entries) => results.map((result) => entries.map((entry) => result.concat([entry]))).reduce((sub, res) => sub.concat(res), []), [[]]);
|
package/dist/cartesian.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cartesian.js","names":[],"sources":["../src/cartesian.ts"],"sourcesContent":["// __private-exports\n/*\n * Copyright 2024 Hypergiant Galactic Systems Inc. All rights reserved.\n * This file is licensed to you under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License. You may obtain a copy\n * of the License at https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under\n * the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\n * OF ANY KIND, either express or implied. See the License for the specific language\n * governing permissions and limitations under the License.\n */\n\n/**\n * Computes the Cartesian product of multiple arrays.\n *\n * @template T\n * the type of elements in the input arrays.\n * @param {...T[][]} all\n * a variadic number of arrays to compute the Cartesian product of.\n * @returns {T[][]}\n * An array containing all possible combinations of elements from the input\n * arrays, where each combination is represented as an array.\n *\n * @remarks\n * pure function\n *\n * @example\n * const result = cartesian([1, 2], ['a', 'b']);\n * // result: [[1, 'a'], [1, 'b'], [2, 'a'], [2, 'b']]\n */\nexport function cartesian<T>(...all: T[][]): T[][] {\n return all.reduce<T[][]>(\n (results, entries) =>\n results\n .map((result) => entries.map((entry) => result.concat([entry])))\n .reduce((sub, res) => sub.concat(res), []),\n [[]],\n );\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"cartesian.js","names":[],"sources":["../src/cartesian.ts"],"sourcesContent":["// __private-exports\n/*\n * Copyright 2024 Hypergiant Galactic Systems Inc. All rights reserved.\n * This file is licensed to you under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License. You may obtain a copy\n * of the License at https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under\n * the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\n * OF ANY KIND, either express or implied. See the License for the specific language\n * governing permissions and limitations under the License.\n */\n\n/**\n * Computes the Cartesian product of multiple arrays.\n *\n * @template T\n * the type of elements in the input arrays.\n * @param {...T[][]} all\n * a variadic number of arrays to compute the Cartesian product of.\n * @returns {T[][]}\n * An array containing all possible combinations of elements from the input\n * arrays, where each combination is represented as an array.\n *\n * @remarks\n * pure function\n *\n * @example\n * ```typescript\n * const result = cartesian([1, 2], ['a', 'b']);\n * // result: [[1, 'a'], [1, 'b'], [2, 'a'], [2, 'b']]\n * ```\n */\nexport function cartesian<T>(...all: T[][]): T[][] {\n return all.reduce<T[][]>(\n (results, entries) =>\n results\n .map((result) => entries.map((entry) => result.concat([entry])))\n .reduce((sub, res) => sub.concat(res), []),\n [[]],\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiCA,SAAgB,UAAa,GAAG,KAAmB;AACjD,QAAO,IAAI,QACR,SAAS,YACR,QACG,KAAK,WAAW,QAAQ,KAAK,UAAU,OAAO,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAC/D,QAAQ,KAAK,QAAQ,IAAI,OAAO,IAAI,EAAE,EAAE,CAAC,EAC9C,CAAC,EAAE,CAAC,CACL"}
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
* governing permissions and limitations under the License.
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
+
import { CoordinateInput, CoordinateInternalValue, CoordinateObject, CoordinateTuple, LatLonTuple, LonLatTuple } from "./latlon/internal/normalize.js";
|
|
13
14
|
import { Format } from "./latlon/internal/index.js";
|
|
14
15
|
import { CoordinateSystem } from "./latlon/internal/coordinate-system.js";
|
|
15
16
|
|
|
@@ -29,10 +30,6 @@ type Coordinate = {
|
|
|
29
30
|
raw: CoordinateInternalValue;
|
|
30
31
|
valid: boolean;
|
|
31
32
|
};
|
|
32
|
-
type CoordinateInternalValue = {
|
|
33
|
-
LAT: number;
|
|
34
|
-
LON: number;
|
|
35
|
-
};
|
|
36
33
|
/**
|
|
37
34
|
* Output a string value of a coordinate using an available system. The
|
|
38
35
|
* original value is preserved without conversion to an internal
|
|
@@ -46,6 +43,7 @@ type CoordinateInternalValue = {
|
|
|
46
43
|
* pure function
|
|
47
44
|
*
|
|
48
45
|
* @example
|
|
46
|
+
* ```typescript
|
|
49
47
|
* const create = createCoordinate(coordinateSystems.dd, 'LATLON')
|
|
50
48
|
* const coord = create('89.765432109 / 123.456789012')
|
|
51
49
|
*
|
|
@@ -56,8 +54,31 @@ type CoordinateInternalValue = {
|
|
|
56
54
|
*
|
|
57
55
|
* // change format to 'LONLAT'
|
|
58
56
|
* coord.dms('LONLAT') === '123 27 24.4404432 E / 89 45 55.5555924 N'
|
|
57
|
+
* ```
|
|
59
58
|
*/
|
|
60
59
|
type Formatter = (f?: Format) => string;
|
|
60
|
+
/**
|
|
61
|
+
* Available coordinate systems for parsing, converting, and formatting geographic coordinates.
|
|
62
|
+
*
|
|
63
|
+
* Provides five coordinate notation systems:
|
|
64
|
+
* - dd: Decimal Degrees
|
|
65
|
+
* - ddm: Degrees Decimal Minutes
|
|
66
|
+
* - dms: Degrees Minutes Seconds
|
|
67
|
+
* - mgrs: Military Grid Reference System
|
|
68
|
+
* - utm: Universal Transverse Mercator
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
* ```typescript
|
|
72
|
+
* const create = createCoordinate(coordinateSystems.dd, 'LATLON');
|
|
73
|
+
* const coord = create('40.7128 / -74.0060');
|
|
74
|
+
* ```
|
|
75
|
+
*
|
|
76
|
+
* @example
|
|
77
|
+
* ```typescript
|
|
78
|
+
* const createMGRS = createCoordinate(coordinateSystems.mgrs);
|
|
79
|
+
* const coord = createMGRS('18T WL 80000 00000');
|
|
80
|
+
* ```
|
|
81
|
+
*/
|
|
61
82
|
declare const coordinateSystems: Readonly<{
|
|
62
83
|
readonly dd: CoordinateSystem;
|
|
63
84
|
readonly ddm: CoordinateSystem;
|
|
@@ -71,16 +92,29 @@ declare const coordinateSystems: Readonly<{
|
|
|
71
92
|
* used for validation and eventually for output as defaults if no alternatives
|
|
72
93
|
* are provided.
|
|
73
94
|
*
|
|
74
|
-
* @param initSystem dd, ddm, dms, mgrs, or utm
|
|
95
|
+
* @param initSystem - Coordinate system to use for parsing (dd, ddm, dms, mgrs, or utm from coordinateSystems). Defaults to Decimal Degrees.
|
|
96
|
+
* @param initFormat - Coordinate format ordering (LATLON or LONLAT). Defaults to LATLON.
|
|
97
|
+
* @returns Function that accepts coordinate string and returns Coordinate object with formatters.
|
|
75
98
|
*
|
|
76
99
|
* @remarks
|
|
77
100
|
* pure function
|
|
78
101
|
*
|
|
79
102
|
* @example
|
|
103
|
+
* ```typescript
|
|
80
104
|
* const create = createCoordinate(coordinateSystems.dd, 'LATLON')
|
|
105
|
+
* const coord = create('40.7128 / -74.0060')
|
|
106
|
+
* coord.dd() // '40.7128 N / 74.006 W'
|
|
107
|
+
* coord.dms() // '40 42 46.08 N / 74 0 21.6 W'
|
|
108
|
+
* ```
|
|
109
|
+
*
|
|
110
|
+
* @example
|
|
111
|
+
* ```typescript
|
|
81
112
|
* const create = createCoordinate(coordinateSystems.ddm, 'LONLAT')
|
|
113
|
+
* const coord = create('-74 0.36 / 40 42.768')
|
|
114
|
+
* coord.ddm('LATLON') // '40 42.768 N / 74 0.36 W'
|
|
115
|
+
* ```
|
|
82
116
|
*/
|
|
83
|
-
declare function createCoordinate(initSystem?: CoordinateSystem, initFormat?: Format): (input:
|
|
117
|
+
declare function createCoordinate(initSystem?: CoordinateSystem, initFormat?: Format): (input: CoordinateInput) => Coordinate;
|
|
84
118
|
//#endregion
|
|
85
|
-
export { coordinateSystems, createCoordinate };
|
|
119
|
+
export { type CoordinateInput, type CoordinateObject, type CoordinateTuple, type LatLonTuple, type LonLatTuple, coordinateSystems, createCoordinate };
|
|
86
120
|
//# sourceMappingURL=coordinate.d.ts.map
|
|
@@ -11,6 +11,8 @@
|
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
|
|
14
|
+
import { isCoordinateTuple, normalizeObjectToLatLon, tupleToLatLon } from "./latlon/internal/normalize.js";
|
|
15
|
+
import { validateNumericCoordinate } from "./latlon/internal/validate.js";
|
|
14
16
|
import { FORMATS_DEFAULT, SYMBOLS } from "./latlon/internal/index.js";
|
|
15
17
|
import { systemDecimalDegrees } from "./latlon/decimal-degrees/system.js";
|
|
16
18
|
import { systemDegreesDecimalMinutes } from "./latlon/degrees-decimal-minutes/system.js";
|
|
@@ -20,6 +22,28 @@ import { systemMGRS } from "./mgrs/system.js";
|
|
|
20
22
|
import { systemUTM } from "./utm/system.js";
|
|
21
23
|
|
|
22
24
|
//#region src/coordinates/coordinate.ts
|
|
25
|
+
/**
|
|
26
|
+
* Available coordinate systems for parsing, converting, and formatting geographic coordinates.
|
|
27
|
+
*
|
|
28
|
+
* Provides five coordinate notation systems:
|
|
29
|
+
* - dd: Decimal Degrees
|
|
30
|
+
* - ddm: Degrees Decimal Minutes
|
|
31
|
+
* - dms: Degrees Minutes Seconds
|
|
32
|
+
* - mgrs: Military Grid Reference System
|
|
33
|
+
* - utm: Universal Transverse Mercator
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* ```typescript
|
|
37
|
+
* const create = createCoordinate(coordinateSystems.dd, 'LATLON');
|
|
38
|
+
* const coord = create('40.7128 / -74.0060');
|
|
39
|
+
* ```
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* ```typescript
|
|
43
|
+
* const createMGRS = createCoordinate(coordinateSystems.mgrs);
|
|
44
|
+
* const coord = createMGRS('18T WL 80000 00000');
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
23
47
|
const coordinateSystems = Object.freeze({
|
|
24
48
|
dd: systemDecimalDegrees,
|
|
25
49
|
ddm: systemDegreesDecimalMinutes,
|
|
@@ -37,42 +61,108 @@ const freezeCoordinate = (errors, to, raw, valid) => Object.freeze({
|
|
|
37
61
|
raw,
|
|
38
62
|
valid
|
|
39
63
|
});
|
|
64
|
+
const errorCoordinate = (errors) => freezeCoordinate(errors, () => "", {}, false);
|
|
65
|
+
const createFormatter = (raw, cachedValues, initSystem, initFormat) => (system = initSystem, format = initFormat) => {
|
|
66
|
+
let cache = cachedValues.get(system);
|
|
67
|
+
if (!cache?.[format]) {
|
|
68
|
+
if (!cache) {
|
|
69
|
+
cache = {};
|
|
70
|
+
cachedValues.set(system, cache);
|
|
71
|
+
}
|
|
72
|
+
cache[format] = system.toFormat(format, [raw[format.slice(0, 3)], raw[format.slice(3)]]);
|
|
73
|
+
}
|
|
74
|
+
return cache[format];
|
|
75
|
+
};
|
|
40
76
|
/**
|
|
41
77
|
* Create a coordinate object enabling: lexing, parsing, validation, and
|
|
42
78
|
* formatting in alternative systems and formats. The system and format will be
|
|
43
79
|
* used for validation and eventually for output as defaults if no alternatives
|
|
44
80
|
* are provided.
|
|
45
81
|
*
|
|
46
|
-
* @param initSystem dd, ddm, dms, mgrs, or utm
|
|
82
|
+
* @param initSystem - Coordinate system to use for parsing (dd, ddm, dms, mgrs, or utm from coordinateSystems). Defaults to Decimal Degrees.
|
|
83
|
+
* @param initFormat - Coordinate format ordering (LATLON or LONLAT). Defaults to LATLON.
|
|
84
|
+
* @returns Function that accepts coordinate string and returns Coordinate object with formatters.
|
|
47
85
|
*
|
|
48
86
|
* @remarks
|
|
49
87
|
* pure function
|
|
50
88
|
*
|
|
51
89
|
* @example
|
|
90
|
+
* ```typescript
|
|
52
91
|
* const create = createCoordinate(coordinateSystems.dd, 'LATLON')
|
|
92
|
+
* const coord = create('40.7128 / -74.0060')
|
|
93
|
+
* coord.dd() // '40.7128 N / 74.006 W'
|
|
94
|
+
* coord.dms() // '40 42 46.08 N / 74 0 21.6 W'
|
|
95
|
+
* ```
|
|
96
|
+
*
|
|
97
|
+
* @example
|
|
98
|
+
* ```typescript
|
|
53
99
|
* const create = createCoordinate(coordinateSystems.ddm, 'LONLAT')
|
|
100
|
+
* const coord = create('-74 0.36 / 40 42.768')
|
|
101
|
+
* coord.ddm('LATLON') // '40 42.768 N / 74 0.36 W'
|
|
102
|
+
* ```
|
|
54
103
|
*/
|
|
55
104
|
function createCoordinate(initSystem = coordinateSystems.dd, initFormat = FORMATS_DEFAULT) {
|
|
56
|
-
|
|
105
|
+
function normalizeString(input) {
|
|
57
106
|
let tokens;
|
|
58
107
|
let errors;
|
|
59
108
|
try {
|
|
60
109
|
[tokens, errors] = initSystem.parse(initFormat, input);
|
|
61
110
|
if (errors.length) throw errors;
|
|
62
111
|
} catch (errors$1) {
|
|
63
|
-
return
|
|
112
|
+
return {
|
|
113
|
+
valid: false,
|
|
114
|
+
errors: errors$1
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
const cachedValues = new Map([[initSystem, createCache(initFormat, initSystem === systemMGRS ? input : tokens.join(" "))]]);
|
|
118
|
+
const dividerIndex = tokens.indexOf(SYMBOLS.DIVIDER);
|
|
119
|
+
return {
|
|
120
|
+
valid: true,
|
|
121
|
+
raw: {
|
|
122
|
+
[initFormat.slice(0, 3)]: initSystem.toFloat(tokens.slice(0, dividerIndex)),
|
|
123
|
+
[initFormat.slice(3)]: initSystem.toFloat(tokens.slice(dividerIndex + 1))
|
|
124
|
+
},
|
|
125
|
+
cachedValues
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
function normalizeNumeric(lat, lon) {
|
|
129
|
+
const errors = validateNumericCoordinate(lat, lon);
|
|
130
|
+
if (errors.length) return {
|
|
131
|
+
valid: false,
|
|
132
|
+
errors
|
|
133
|
+
};
|
|
134
|
+
return {
|
|
135
|
+
valid: true,
|
|
136
|
+
raw: {
|
|
137
|
+
LAT: lat,
|
|
138
|
+
LON: lon
|
|
139
|
+
},
|
|
140
|
+
cachedValues: /* @__PURE__ */ new Map()
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
function normalize(input) {
|
|
144
|
+
if (typeof input === "string") return normalizeString(input);
|
|
145
|
+
if (isCoordinateTuple(input)) {
|
|
146
|
+
const { lat, lon } = tupleToLatLon(initFormat, input);
|
|
147
|
+
return normalizeNumeric(lat, lon);
|
|
64
148
|
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
...cachedValues[key],
|
|
71
|
-
[format]: system.toFormat(format, [format.slice(0, 3), format.slice(3)].map((key$1) => raw[key$1]))
|
|
149
|
+
if (typeof input === "object" && input !== null && !Array.isArray(input)) {
|
|
150
|
+
const result = normalizeObjectToLatLon(input);
|
|
151
|
+
if (result === null) return {
|
|
152
|
+
valid: false,
|
|
153
|
+
errors: ["[ERROR] Invalid coordinate object; object must contain valid latitude and longitude properties."]
|
|
72
154
|
};
|
|
73
|
-
return
|
|
155
|
+
return normalizeNumeric(result.lat, result.lon);
|
|
74
156
|
}
|
|
75
|
-
return
|
|
157
|
+
return {
|
|
158
|
+
valid: false,
|
|
159
|
+
errors: ["[ERROR] Invalid coordinate input; expected a string, [lat, lon] tuple, or { lat, lon } object."]
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
return (input) => {
|
|
163
|
+
const result = normalize(input);
|
|
164
|
+
if (!result.valid) return errorCoordinate(result.errors);
|
|
165
|
+
return freezeCoordinate([], createFormatter(result.raw, result.cachedValues, initSystem, initFormat), result.raw, true);
|
|
76
166
|
};
|
|
77
167
|
}
|
|
78
168
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"coordinate.js","names":["tokens: Tokens","errors: Errors","errors","key"],"sources":["../../src/coordinates/coordinate.ts"],"sourcesContent":["/*\n * Copyright 2024 Hypergiant Galactic Systems Inc. All rights reserved.\n * This file is licensed to you under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License. You may obtain a copy\n * of the License at https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under\n * the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\n * OF ANY KIND, either express or implied. See the License for the specific language\n * governing permissions and limitations under the License.\n */\n\nimport { systemDecimalDegrees } from './latlon/decimal-degrees/system';\nimport { systemDegreesDecimalMinutes } from './latlon/degrees-decimal-minutes/system';\nimport { systemDegreesMinutesSeconds } from './latlon/degrees-minutes-seconds/system';\nimport {\n type Axes,\n type Errors,\n FORMATS_DEFAULT,\n type Format,\n SYMBOLS,\n} from './latlon/internal';\nimport {\n type CoordinateCache,\n createCache,\n} from './latlon/internal/create-cache';\nimport { systemMGRS } from './mgrs/system';\nimport { systemUTM } from './utm/system';\nimport type { CoordinateSystem } from './latlon/internal/coordinate-system';\nimport type { Tokens } from './latlon/internal/lexer';\n\ntype Coordinate = {\n /** {@interitDoc Formatter} */\n dd: Formatter;\n /** {@interitDoc Formatter} */\n ddm: Formatter;\n /** {@interitDoc Formatter} */\n dms: Formatter;\n /** {@interitDoc Formatter} */\n mgrs: Formatter;\n /** {@interitDoc Formatter} */\n utm: Formatter;\n errors: string[];\n raw: CoordinateInternalValue;\n valid: boolean;\n};\n\n// biome-ignore lint/style/useNamingConvention: consistency with Axes type\ntype CoordinateInternalValue = { LAT: number; LON: number };\n\n/**\n * Output a string value of a coordinate using an available system. The\n * original value is preserved without conversion to an internal\n * representation - Decimal Degrees - to prevent the possibility of\n * rounding errors. All alternative values are computed from a common\n * internal value to reduce complexity.\n *\n * @link https://en.wikipedia.org/wiki/Coordinate_system\n *\n * @remarks\n * pure function\n *\n * @example\n * const create = createCoordinate(coordinateSystems.dd, 'LATLON')\n * const coord = create('89.765432109 / 123.456789012')\n *\n * // honors the instantiation format 'LATLON'\n * coord.dd() === '89.765432109 N / 123.456789012 E'\n * coord.ddm() === '89 45.92592654 N / 123 27.40734072 E'\n * coord.dms() === '89 45 55.5555924 N / 123 27 24.4404432 E'\n *\n * // change format to 'LONLAT'\n * coord.dms('LONLAT') === '123 27 24.4404432 E / 89 45 55.5555924 N'\n */\ntype Formatter = (f?: Format) => string;\n\ntype ToFloatArg = Parameters<CoordinateSystem['toFloat']>[0];\n\ntype OutputCache = Record<keyof typeof coordinateSystems, CoordinateCache>;\n\nexport const coordinateSystems = Object.freeze({\n dd: systemDecimalDegrees,\n ddm: systemDegreesDecimalMinutes,\n dms: systemDegreesMinutesSeconds,\n mgrs: systemMGRS,\n utm: systemUTM,\n} as const);\n\nconst freezeCoordinate = (\n errors: Coordinate['errors'],\n to: (s?: CoordinateSystem, f?: Format) => string,\n raw: CoordinateInternalValue,\n valid: Coordinate['valid'],\n) =>\n Object.freeze({\n dd: (format?: Format) => to(systemDecimalDegrees, format),\n ddm: (format?: Format) => to(systemDegreesDecimalMinutes, format),\n dms: (format?: Format) => to(systemDegreesMinutesSeconds, format),\n mgrs: (format?: Format) => to(systemMGRS, format),\n utm: (format?: Format) => to(systemUTM, format),\n errors,\n raw,\n valid,\n } as Coordinate);\n\n/**\n * Create a coordinate object enabling: lexing, parsing, validation, and\n * formatting in alternative systems and formats. The system and format will be\n * used for validation and eventually for output as defaults if no alternatives\n * are provided.\n *\n * @param initSystem dd, ddm, dms, mgrs, or utm of coordinateSystems\n *\n * @remarks\n * pure function\n *\n * @example\n * const create = createCoordinate(coordinateSystems.dd, 'LATLON')\n * const create = createCoordinate(coordinateSystems.ddm, 'LONLAT')\n */\nexport function createCoordinate(\n initSystem: CoordinateSystem = coordinateSystems.dd,\n initFormat: Format = FORMATS_DEFAULT,\n) {\n return (input: string) => {\n let tokens: Tokens;\n let errors: Errors;\n\n try {\n [tokens, errors] = initSystem.parse(initFormat, input);\n\n if (errors.length) {\n throw errors;\n }\n } catch (errors) {\n return freezeCoordinate(\n errors as Coordinate['errors'],\n () => '',\n {} as CoordinateInternalValue,\n false,\n );\n }\n\n // start with the original value for the original system in the original format\n // other values will be computed as needed and cached per request\n const cachedValues = {\n [initSystem.name]: createCache(\n initFormat,\n // because mgrs doesn't have two formats: LATLON v LONLAT\n initSystem.name === systemMGRS.name ? input : tokens.join(' '),\n ),\n } as OutputCache;\n\n // Create the \"internal\" representation - Decimal Degrees - for\n // consistency and ease of computation; all systems expect to\n // start from a common starting point to reduce complexity.\n const raw = Object.fromEntries([\n [\n initFormat.slice(0, 3),\n initSystem.toFloat(\n tokens.slice(0, tokens.indexOf(SYMBOLS.DIVIDER)) as ToFloatArg,\n ),\n ],\n [\n initFormat.slice(3),\n initSystem.toFloat(\n tokens.slice(1 + tokens.indexOf(SYMBOLS.DIVIDER)) as ToFloatArg,\n ),\n ],\n ]) as CoordinateInternalValue;\n\n function to(\n system: CoordinateSystem = initSystem,\n format: Format = initFormat,\n ) {\n const key = system.name as keyof typeof coordinateSystems;\n\n if (!cachedValues[key]?.[format]) {\n // cache \"miss\" - fill the missing value in the cache before returning it\n\n // update the cache to include the newly computed value\n cachedValues[key] = {\n ...cachedValues[key],\n // use the Format to build the object, correctly pairing the halves of\n // the coordinate value with their labels\n [format]: system.toFormat(\n format,\n ([format.slice(0, 3), format.slice(3)] as Axes[]).map(\n (key) => raw[key],\n ) as [number, number],\n ),\n };\n }\n\n return cachedValues[key][format];\n }\n\n return freezeCoordinate([] as Coordinate['errors'], to, raw, true);\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAgFA,MAAa,oBAAoB,OAAO,OAAO;CAC7C,IAAI;CACJ,KAAK;CACL,KAAK;CACL,MAAM;CACN,KAAK;CACN,CAAU;AAEX,MAAM,oBACJ,QACA,IACA,KACA,UAEA,OAAO,OAAO;CACZ,KAAK,WAAoB,GAAG,sBAAsB,OAAO;CACzD,MAAM,WAAoB,GAAG,6BAA6B,OAAO;CACjE,MAAM,WAAoB,GAAG,6BAA6B,OAAO;CACjE,OAAO,WAAoB,GAAG,YAAY,OAAO;CACjD,MAAM,WAAoB,GAAG,WAAW,OAAO;CAC/C;CACA;CACA;CACD,CAAe;;;;;;;;;;;;;;;;AAiBlB,SAAgB,iBACd,aAA+B,kBAAkB,IACjD,aAAqB,iBACrB;AACA,SAAQ,UAAkB;EACxB,IAAIA;EACJ,IAAIC;AAEJ,MAAI;AACF,IAAC,QAAQ,UAAU,WAAW,MAAM,YAAY,MAAM;AAEtD,OAAI,OAAO,OACT,OAAM;WAEDC,UAAQ;AACf,UAAO,iBACLA,gBACM,IACN,EAAE,EACF,MACD;;EAKH,MAAM,eAAe,GAClB,WAAW,OAAO,YACjB,YAEA,WAAW,SAAS,WAAW,OAAO,QAAQ,OAAO,KAAK,IAAI,CAC/D,EACF;EAKD,MAAM,MAAM,OAAO,YAAY,CAC7B,CACE,WAAW,MAAM,GAAG,EAAE,EACtB,WAAW,QACT,OAAO,MAAM,GAAG,OAAO,QAAQ,QAAQ,QAAQ,CAAC,CACjD,CACF,EACD,CACE,WAAW,MAAM,EAAE,EACnB,WAAW,QACT,OAAO,MAAM,IAAI,OAAO,QAAQ,QAAQ,QAAQ,CAAC,CAClD,CACF,CACF,CAAC;EAEF,SAAS,GACP,SAA2B,YAC3B,SAAiB,YACjB;GACA,MAAM,MAAM,OAAO;AAEnB,OAAI,CAAC,aAAa,OAAO,QAIvB,cAAa,OAAO;IAClB,GAAG,aAAa;KAGf,SAAS,OAAO,SACf,QACC,CAAC,OAAO,MAAM,GAAG,EAAE,EAAE,OAAO,MAAM,EAAE,CAAC,CAAY,KAC/C,UAAQ,IAAIC,OACd,CACF;IACF;AAGH,UAAO,aAAa,KAAK;;AAG3B,SAAO,iBAAiB,EAAE,EAA0B,IAAI,KAAK,KAAK"}
|
|
1
|
+
{"version":3,"file":"coordinate.js","names":["tokens: Tokens","errors: Errors","errors","cachedValues: OutputCache"],"sources":["../../src/coordinates/coordinate.ts"],"sourcesContent":["/*\n * Copyright 2024 Hypergiant Galactic Systems Inc. All rights reserved.\n * This file is licensed to you under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License. You may obtain a copy\n * of the License at https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under\n * the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\n * OF ANY KIND, either express or implied. See the License for the specific language\n * governing permissions and limitations under the License.\n */\n\nimport { systemDecimalDegrees } from './latlon/decimal-degrees/system';\nimport { systemDegreesDecimalMinutes } from './latlon/degrees-decimal-minutes/system';\nimport { systemDegreesMinutesSeconds } from './latlon/degrees-minutes-seconds/system';\nimport {\n type Axes,\n type CoordinateInput,\n type CoordinateInternalValue,\n type CoordinateObject,\n type Errors,\n FORMATS_DEFAULT,\n type Format,\n isCoordinateTuple,\n normalizeObjectToLatLon,\n SYMBOLS,\n tupleToLatLon,\n validateNumericCoordinate,\n} from './latlon/internal';\n\nexport type {\n CoordinateInput,\n CoordinateObject,\n CoordinateTuple,\n LatLonTuple,\n LonLatTuple,\n} from './latlon/internal';\n\nimport {\n type CoordinateCache,\n createCache,\n} from './latlon/internal/create-cache';\nimport { systemMGRS } from './mgrs/system';\nimport { systemUTM } from './utm/system';\nimport type { CoordinateSystem } from './latlon/internal/coordinate-system';\nimport type { Tokens } from './latlon/internal/lexer';\n\ntype Coordinate = {\n /** {@interitDoc Formatter} */\n dd: Formatter;\n /** {@interitDoc Formatter} */\n ddm: Formatter;\n /** {@interitDoc Formatter} */\n dms: Formatter;\n /** {@interitDoc Formatter} */\n mgrs: Formatter;\n /** {@interitDoc Formatter} */\n utm: Formatter;\n errors: string[];\n raw: CoordinateInternalValue;\n valid: boolean;\n};\n\n/**\n * Output a string value of a coordinate using an available system. The\n * original value is preserved without conversion to an internal\n * representation - Decimal Degrees - to prevent the possibility of\n * rounding errors. All alternative values are computed from a common\n * internal value to reduce complexity.\n *\n * @link https://en.wikipedia.org/wiki/Coordinate_system\n *\n * @remarks\n * pure function\n *\n * @example\n * ```typescript\n * const create = createCoordinate(coordinateSystems.dd, 'LATLON')\n * const coord = create('89.765432109 / 123.456789012')\n *\n * // honors the instantiation format 'LATLON'\n * coord.dd() === '89.765432109 N / 123.456789012 E'\n * coord.ddm() === '89 45.92592654 N / 123 27.40734072 E'\n * coord.dms() === '89 45 55.5555924 N / 123 27 24.4404432 E'\n *\n * // change format to 'LONLAT'\n * coord.dms('LONLAT') === '123 27 24.4404432 E / 89 45 55.5555924 N'\n * ```\n */\ntype Formatter = (f?: Format) => string;\n\ntype ToFloatArg = Parameters<CoordinateSystem['toFloat']>[0];\n\ntype OutputCache = Map<CoordinateSystem, CoordinateCache>;\n\n/**\n * Available coordinate systems for parsing, converting, and formatting geographic coordinates.\n *\n * Provides five coordinate notation systems:\n * - dd: Decimal Degrees\n * - ddm: Degrees Decimal Minutes\n * - dms: Degrees Minutes Seconds\n * - mgrs: Military Grid Reference System\n * - utm: Universal Transverse Mercator\n *\n * @example\n * ```typescript\n * const create = createCoordinate(coordinateSystems.dd, 'LATLON');\n * const coord = create('40.7128 / -74.0060');\n * ```\n *\n * @example\n * ```typescript\n * const createMGRS = createCoordinate(coordinateSystems.mgrs);\n * const coord = createMGRS('18T WL 80000 00000');\n * ```\n */\nexport const coordinateSystems = Object.freeze({\n dd: systemDecimalDegrees,\n ddm: systemDegreesDecimalMinutes,\n dms: systemDegreesMinutesSeconds,\n mgrs: systemMGRS,\n utm: systemUTM,\n} as const);\n\nconst freezeCoordinate = (\n errors: Coordinate['errors'],\n to: (s?: CoordinateSystem, f?: Format) => string,\n raw: CoordinateInternalValue,\n valid: Coordinate['valid'],\n) =>\n Object.freeze({\n dd: (format?: Format) => to(systemDecimalDegrees, format),\n ddm: (format?: Format) => to(systemDegreesDecimalMinutes, format),\n dms: (format?: Format) => to(systemDegreesMinutesSeconds, format),\n mgrs: (format?: Format) => to(systemMGRS, format),\n utm: (format?: Format) => to(systemUTM, format),\n errors,\n raw,\n valid,\n } as Coordinate);\n\nconst errorCoordinate = (errors: string[]) =>\n freezeCoordinate(errors, () => '', {} as CoordinateInternalValue, false);\n\nconst createFormatter =\n (\n raw: CoordinateInternalValue,\n cachedValues: OutputCache,\n initSystem: CoordinateSystem,\n initFormat: Format,\n ) =>\n (system: CoordinateSystem = initSystem, format: Format = initFormat) => {\n let cache = cachedValues.get(system);\n\n if (!cache?.[format]) {\n if (!cache) {\n cache = {} as CoordinateCache;\n cachedValues.set(system, cache);\n }\n\n cache[format] = system.toFormat(format, [\n raw[format.slice(0, 3) as Axes],\n raw[format.slice(3) as Axes],\n ] as [number, number]);\n }\n\n return cache[format];\n };\n\ntype NormalizedResult =\n | { valid: true; raw: CoordinateInternalValue; cachedValues: OutputCache }\n | { valid: false; errors: string[] };\n\n/**\n * Create a coordinate object enabling: lexing, parsing, validation, and\n * formatting in alternative systems and formats. The system and format will be\n * used for validation and eventually for output as defaults if no alternatives\n * are provided.\n *\n * @param initSystem - Coordinate system to use for parsing (dd, ddm, dms, mgrs, or utm from coordinateSystems). Defaults to Decimal Degrees.\n * @param initFormat - Coordinate format ordering (LATLON or LONLAT). Defaults to LATLON.\n * @returns Function that accepts coordinate string and returns Coordinate object with formatters.\n *\n * @remarks\n * pure function\n *\n * @example\n * ```typescript\n * const create = createCoordinate(coordinateSystems.dd, 'LATLON')\n * const coord = create('40.7128 / -74.0060')\n * coord.dd() // '40.7128 N / 74.006 W'\n * coord.dms() // '40 42 46.08 N / 74 0 21.6 W'\n * ```\n *\n * @example\n * ```typescript\n * const create = createCoordinate(coordinateSystems.ddm, 'LONLAT')\n * const coord = create('-74 0.36 / 40 42.768')\n * coord.ddm('LATLON') // '40 42.768 N / 74 0.36 W'\n * ```\n */\nexport function createCoordinate(\n initSystem: CoordinateSystem = coordinateSystems.dd,\n initFormat: Format = FORMATS_DEFAULT,\n) {\n function normalizeString(input: string): NormalizedResult {\n let tokens: Tokens;\n let errors: Errors;\n\n try {\n [tokens, errors] = initSystem.parse(initFormat, input);\n\n if (errors.length) {\n throw errors;\n }\n } catch (errors) {\n return { valid: false, errors: errors as string[] };\n }\n\n const cachedValues: OutputCache = new Map([\n [\n initSystem,\n createCache(\n initFormat,\n initSystem === systemMGRS ? input : tokens.join(' '),\n ),\n ],\n ]);\n\n const dividerIndex = tokens.indexOf(SYMBOLS.DIVIDER);\n const raw = {\n [initFormat.slice(0, 3)]: initSystem.toFloat(\n tokens.slice(0, dividerIndex) as ToFloatArg,\n ),\n [initFormat.slice(3)]: initSystem.toFloat(\n tokens.slice(dividerIndex + 1) as ToFloatArg,\n ),\n } as CoordinateInternalValue;\n\n return { valid: true, raw, cachedValues };\n }\n\n function normalizeNumeric(lat: number, lon: number): NormalizedResult {\n const errors = validateNumericCoordinate(lat, lon);\n\n if (errors.length) {\n return { valid: false, errors };\n }\n\n return {\n valid: true,\n raw: { LAT: lat, LON: lon },\n cachedValues: new Map(),\n };\n }\n\n function normalize(input: CoordinateInput): NormalizedResult {\n if (typeof input === 'string') {\n return normalizeString(input);\n }\n\n if (isCoordinateTuple(input)) {\n const { lat, lon } = tupleToLatLon(initFormat, input);\n return normalizeNumeric(lat, lon);\n }\n\n if (typeof input === 'object' && input !== null && !Array.isArray(input)) {\n const result = normalizeObjectToLatLon(input as CoordinateObject);\n\n if (result === null) {\n return {\n valid: false,\n errors: [\n '[ERROR] Invalid coordinate object; object must contain valid latitude and longitude properties.',\n ],\n };\n }\n\n return normalizeNumeric(result.lat, result.lon);\n }\n\n return {\n valid: false,\n errors: [\n '[ERROR] Invalid coordinate input; expected a string, [lat, lon] tuple, or { lat, lon } object.',\n ],\n };\n }\n\n return (input: CoordinateInput): Coordinate => {\n const result = normalize(input);\n\n if (!result.valid) {\n return errorCoordinate(result.errors);\n }\n\n const to = createFormatter(\n result.raw,\n result.cachedValues,\n initSystem,\n initFormat,\n );\n\n return freezeCoordinate([], to, result.raw, true);\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqHA,MAAa,oBAAoB,OAAO,OAAO;CAC7C,IAAI;CACJ,KAAK;CACL,KAAK;CACL,MAAM;CACN,KAAK;CACN,CAAU;AAEX,MAAM,oBACJ,QACA,IACA,KACA,UAEA,OAAO,OAAO;CACZ,KAAK,WAAoB,GAAG,sBAAsB,OAAO;CACzD,MAAM,WAAoB,GAAG,6BAA6B,OAAO;CACjE,MAAM,WAAoB,GAAG,6BAA6B,OAAO;CACjE,OAAO,WAAoB,GAAG,YAAY,OAAO;CACjD,MAAM,WAAoB,GAAG,WAAW,OAAO;CAC/C;CACA;CACA;CACD,CAAe;AAElB,MAAM,mBAAmB,WACvB,iBAAiB,cAAc,IAAI,EAAE,EAA6B,MAAM;AAE1E,MAAM,mBAEF,KACA,cACA,YACA,gBAED,SAA2B,YAAY,SAAiB,eAAe;CACtE,IAAI,QAAQ,aAAa,IAAI,OAAO;AAEpC,KAAI,CAAC,QAAQ,SAAS;AACpB,MAAI,CAAC,OAAO;AACV,WAAQ,EAAE;AACV,gBAAa,IAAI,QAAQ,MAAM;;AAGjC,QAAM,UAAU,OAAO,SAAS,QAAQ,CACtC,IAAI,OAAO,MAAM,GAAG,EAAE,GACtB,IAAI,OAAO,MAAM,EAAE,EACpB,CAAqB;;AAGxB,QAAO,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmCjB,SAAgB,iBACd,aAA+B,kBAAkB,IACjD,aAAqB,iBACrB;CACA,SAAS,gBAAgB,OAAiC;EACxD,IAAIA;EACJ,IAAIC;AAEJ,MAAI;AACF,IAAC,QAAQ,UAAU,WAAW,MAAM,YAAY,MAAM;AAEtD,OAAI,OAAO,OACT,OAAM;WAEDC,UAAQ;AACf,UAAO;IAAE,OAAO;IAAO,QAAQA;IAAoB;;EAGrD,MAAMC,eAA4B,IAAI,IAAI,CACxC,CACE,YACA,YACE,YACA,eAAe,aAAa,QAAQ,OAAO,KAAK,IAAI,CACrD,CACF,CACF,CAAC;EAEF,MAAM,eAAe,OAAO,QAAQ,QAAQ,QAAQ;AAUpD,SAAO;GAAE,OAAO;GAAM,KATV;KACT,WAAW,MAAM,GAAG,EAAE,GAAG,WAAW,QACnC,OAAO,MAAM,GAAG,aAAa,CAC9B;KACA,WAAW,MAAM,EAAE,GAAG,WAAW,QAChC,OAAO,MAAM,eAAe,EAAE,CAC/B;IACF;GAE0B;GAAc;;CAG3C,SAAS,iBAAiB,KAAa,KAA+B;EACpE,MAAM,SAAS,0BAA0B,KAAK,IAAI;AAElD,MAAI,OAAO,OACT,QAAO;GAAE,OAAO;GAAO;GAAQ;AAGjC,SAAO;GACL,OAAO;GACP,KAAK;IAAE,KAAK;IAAK,KAAK;IAAK;GAC3B,8BAAc,IAAI,KAAK;GACxB;;CAGH,SAAS,UAAU,OAA0C;AAC3D,MAAI,OAAO,UAAU,SACnB,QAAO,gBAAgB,MAAM;AAG/B,MAAI,kBAAkB,MAAM,EAAE;GAC5B,MAAM,EAAE,KAAK,QAAQ,cAAc,YAAY,MAAM;AACrD,UAAO,iBAAiB,KAAK,IAAI;;AAGnC,MAAI,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,MAAM,EAAE;GACxE,MAAM,SAAS,wBAAwB,MAA0B;AAEjE,OAAI,WAAW,KACb,QAAO;IACL,OAAO;IACP,QAAQ,CACN,kGACD;IACF;AAGH,UAAO,iBAAiB,OAAO,KAAK,OAAO,IAAI;;AAGjD,SAAO;GACL,OAAO;GACP,QAAQ,CACN,iGACD;GACF;;AAGH,SAAQ,UAAuC;EAC7C,MAAM,SAAS,UAAU,MAAM;AAE/B,MAAI,CAAC,OAAO,MACV,QAAO,gBAAgB,OAAO,OAAO;AAUvC,SAAO,iBAAiB,EAAE,EAPf,gBACT,OAAO,KACP,OAAO,cACP,YACA,WACD,EAE+B,OAAO,KAAK,KAAK"}
|
|
@@ -13,6 +13,26 @@
|
|
|
13
13
|
import { FormatOptions } from "../internal/format.js";
|
|
14
14
|
|
|
15
15
|
//#region src/coordinates/latlon/decimal-degrees/formatter.d.ts
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Formats latitude/longitude coordinates in decimal degrees notation.
|
|
19
|
+
*
|
|
20
|
+
* @param coordinates - Tuple of [latitude, longitude] values.
|
|
21
|
+
* @param config - Optional formatting configuration.
|
|
22
|
+
* @returns Formatted coordinate string in decimal degrees format.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```typescript
|
|
26
|
+
* formatDecimalDegrees([37.7749, -122.4194]);
|
|
27
|
+
* // '37.774900° N, 122.419400° W'
|
|
28
|
+
* ```
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ```typescript
|
|
32
|
+
* formatDecimalDegrees([37.7749, -122.4194], { separator: ' / ' });
|
|
33
|
+
* // '37.774900° N / 122.419400° W'
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
16
36
|
declare const formatDecimalDegrees: (coordinates: [number, number], config?: FormatOptions) => string;
|
|
17
37
|
//#endregion
|
|
18
38
|
export { formatDecimalDegrees };
|
|
@@ -14,9 +14,47 @@
|
|
|
14
14
|
import { createFormatter } from "../internal/format.js";
|
|
15
15
|
|
|
16
16
|
//#region src/coordinates/latlon/decimal-degrees/formatter.ts
|
|
17
|
+
/**
|
|
18
|
+
* Converts a coordinate value to decimal degrees format.
|
|
19
|
+
*
|
|
20
|
+
* @param num - The coordinate value to format.
|
|
21
|
+
* @param withOrdinal - Whether to use absolute value (when ordinal directions are shown separately).
|
|
22
|
+
* @returns Formatted coordinate string with degree symbol and 6 decimal places.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```typescript
|
|
26
|
+
* toDecimalDegrees(45.123456);
|
|
27
|
+
* // '45.123456°'
|
|
28
|
+
* ```
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ```typescript
|
|
32
|
+
* toDecimalDegrees(-122.4194, true);
|
|
33
|
+
* // '122.419400°'
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
17
36
|
const toDecimalDegrees = (num, withOrdinal) => {
|
|
18
37
|
return `${(withOrdinal ? Math.abs(num) : num).toFixed(6)}°`;
|
|
19
38
|
};
|
|
39
|
+
/**
|
|
40
|
+
* Formats latitude/longitude coordinates in decimal degrees notation.
|
|
41
|
+
*
|
|
42
|
+
* @param coordinates - Tuple of [latitude, longitude] values.
|
|
43
|
+
* @param config - Optional formatting configuration.
|
|
44
|
+
* @returns Formatted coordinate string in decimal degrees format.
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* ```typescript
|
|
48
|
+
* formatDecimalDegrees([37.7749, -122.4194]);
|
|
49
|
+
* // '37.774900° N, 122.419400° W'
|
|
50
|
+
* ```
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* ```typescript
|
|
54
|
+
* formatDecimalDegrees([37.7749, -122.4194], { separator: ' / ' });
|
|
55
|
+
* // '37.774900° N / 122.419400° W'
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
20
58
|
const formatDecimalDegrees = createFormatter(toDecimalDegrees);
|
|
21
59
|
|
|
22
60
|
//#endregion
|