@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.
Files changed (112) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/README.md +233 -1
  3. package/catalog-info.yaml +1 -1
  4. package/dist/cartesian.d.ts +2 -0
  5. package/dist/cartesian.js +2 -0
  6. package/dist/cartesian.js.map +1 -1
  7. package/dist/coordinates/coordinate.d.ts +41 -7
  8. package/dist/coordinates/coordinate.js +102 -12
  9. package/dist/coordinates/coordinate.js.map +1 -1
  10. package/dist/coordinates/latlon/decimal-degrees/formatter.d.ts +20 -0
  11. package/dist/coordinates/latlon/decimal-degrees/formatter.js +38 -0
  12. package/dist/coordinates/latlon/decimal-degrees/formatter.js.map +1 -1
  13. package/dist/coordinates/latlon/decimal-degrees/parser.d.ts +68 -2
  14. package/dist/coordinates/latlon/decimal-degrees/parser.js +66 -5
  15. package/dist/coordinates/latlon/decimal-degrees/parser.js.map +1 -1
  16. package/dist/coordinates/latlon/decimal-degrees/system.d.ts +32 -0
  17. package/dist/coordinates/latlon/decimal-degrees/system.js +31 -0
  18. package/dist/coordinates/latlon/decimal-degrees/system.js.map +1 -1
  19. package/dist/coordinates/latlon/degrees-decimal-minutes/formatter.d.ts +20 -0
  20. package/dist/coordinates/latlon/degrees-decimal-minutes/formatter.js +37 -0
  21. package/dist/coordinates/latlon/degrees-decimal-minutes/formatter.js.map +1 -1
  22. package/dist/coordinates/latlon/degrees-decimal-minutes/parser.d.ts +72 -2
  23. package/dist/coordinates/latlon/degrees-decimal-minutes/parser.js +66 -3
  24. package/dist/coordinates/latlon/degrees-decimal-minutes/parser.js.map +1 -1
  25. package/dist/coordinates/latlon/degrees-decimal-minutes/system.d.ts +32 -0
  26. package/dist/coordinates/latlon/degrees-decimal-minutes/system.js +31 -0
  27. package/dist/coordinates/latlon/degrees-decimal-minutes/system.js.map +1 -1
  28. package/dist/coordinates/latlon/degrees-minutes-seconds/formatter.d.ts +20 -0
  29. package/dist/coordinates/latlon/degrees-minutes-seconds/formatter.js +37 -0
  30. package/dist/coordinates/latlon/degrees-minutes-seconds/formatter.js.map +1 -1
  31. package/dist/coordinates/latlon/degrees-minutes-seconds/parser.d.ts +74 -2
  32. package/dist/coordinates/latlon/degrees-minutes-seconds/parser.js +66 -3
  33. package/dist/coordinates/latlon/degrees-minutes-seconds/parser.js.map +1 -1
  34. package/dist/coordinates/latlon/degrees-minutes-seconds/system.d.ts +32 -0
  35. package/dist/coordinates/latlon/degrees-minutes-seconds/system.js +31 -0
  36. package/dist/coordinates/latlon/degrees-minutes-seconds/system.js.map +1 -1
  37. package/dist/coordinates/latlon/internal/coordinate-system.d.ts +22 -0
  38. package/dist/coordinates/latlon/internal/create-cache.d.ts +17 -1
  39. package/dist/coordinates/latlon/internal/create-cache.js +19 -3
  40. package/dist/coordinates/latlon/internal/create-cache.js.map +1 -1
  41. package/dist/coordinates/latlon/internal/exhaustive-errors.d.ts +15 -0
  42. package/dist/coordinates/latlon/internal/exhaustive-errors.js +28 -0
  43. package/dist/coordinates/latlon/internal/exhaustive-errors.js.map +1 -1
  44. package/dist/coordinates/latlon/internal/format.d.ts +20 -0
  45. package/dist/coordinates/latlon/internal/format.js +20 -0
  46. package/dist/coordinates/latlon/internal/format.js.map +1 -1
  47. package/dist/coordinates/latlon/internal/in-range.d.ts +23 -0
  48. package/dist/coordinates/latlon/internal/in-range.js +24 -0
  49. package/dist/coordinates/latlon/internal/in-range.js.map +1 -1
  50. package/dist/coordinates/latlon/internal/index.d.ts +16 -1
  51. package/dist/coordinates/latlon/internal/index.js +25 -1
  52. package/dist/coordinates/latlon/internal/index.js.map +1 -1
  53. package/dist/coordinates/latlon/internal/lexer.d.ts +2 -0
  54. package/dist/coordinates/latlon/internal/lexer.js +26 -0
  55. package/dist/coordinates/latlon/internal/lexer.js.map +1 -1
  56. package/dist/coordinates/latlon/internal/normalize.d.ts +67 -0
  57. package/dist/coordinates/latlon/internal/normalize.js +87 -0
  58. package/dist/coordinates/latlon/internal/normalize.js.map +1 -0
  59. package/dist/coordinates/latlon/internal/ordinal.d.ts +25 -0
  60. package/dist/coordinates/latlon/internal/ordinal.js +25 -0
  61. package/dist/coordinates/latlon/internal/ordinal.js.map +1 -1
  62. package/dist/coordinates/latlon/internal/parse-format.d.ts +22 -0
  63. package/dist/coordinates/latlon/internal/parse-format.js +43 -1
  64. package/dist/coordinates/latlon/internal/parse-format.js.map +1 -1
  65. package/dist/coordinates/latlon/internal/parse.d.ts +2 -0
  66. package/dist/coordinates/latlon/internal/parse.js +4 -1
  67. package/dist/coordinates/latlon/internal/parse.js.map +1 -1
  68. package/dist/coordinates/latlon/internal/pipes/check-ambiguous.d.ts +17 -0
  69. package/dist/coordinates/latlon/internal/pipes/check-ambiguous.js +16 -0
  70. package/dist/coordinates/latlon/internal/pipes/check-ambiguous.js.map +1 -1
  71. package/dist/coordinates/latlon/internal/pipes/check-numbers.d.ts +25 -0
  72. package/dist/coordinates/latlon/internal/pipes/check-numbers.js +33 -0
  73. package/dist/coordinates/latlon/internal/pipes/check-numbers.js.map +1 -1
  74. package/dist/coordinates/latlon/internal/pipes/fix-bearings.d.ts +17 -0
  75. package/dist/coordinates/latlon/internal/pipes/fix-bearings.js +31 -0
  76. package/dist/coordinates/latlon/internal/pipes/fix-bearings.js.map +1 -1
  77. package/dist/coordinates/latlon/internal/pipes/fix-dividers.d.ts +17 -0
  78. package/dist/coordinates/latlon/internal/pipes/fix-dividers.js +29 -0
  79. package/dist/coordinates/latlon/internal/pipes/fix-dividers.js.map +1 -1
  80. package/dist/coordinates/latlon/internal/pipes/genome.d.ts +16 -0
  81. package/dist/coordinates/latlon/internal/pipes/genome.js +41 -0
  82. package/dist/coordinates/latlon/internal/pipes/genome.js.map +1 -1
  83. package/dist/coordinates/latlon/internal/pipes/index.d.ts +32 -2
  84. package/dist/coordinates/latlon/internal/pipes/index.js +57 -4
  85. package/dist/coordinates/latlon/internal/pipes/index.js.map +1 -1
  86. package/dist/coordinates/latlon/internal/pipes/simpler.d.ts +16 -3
  87. package/dist/coordinates/latlon/internal/pipes/simpler.js +15 -3
  88. package/dist/coordinates/latlon/internal/pipes/simpler.js.map +1 -1
  89. package/dist/coordinates/latlon/internal/validate.d.ts +75 -0
  90. package/dist/coordinates/latlon/internal/validate.js +105 -0
  91. package/dist/coordinates/latlon/internal/validate.js.map +1 -0
  92. package/dist/coordinates/latlon/internal/violation.d.ts +18 -0
  93. package/dist/coordinates/latlon/internal/violation.js +18 -0
  94. package/dist/coordinates/latlon/internal/violation.js.map +1 -1
  95. package/dist/coordinates/mgrs/parser.d.ts +24 -0
  96. package/dist/coordinates/mgrs/parser.js +56 -0
  97. package/dist/coordinates/mgrs/parser.js.map +1 -1
  98. package/dist/coordinates/mgrs/system.d.ts +31 -0
  99. package/dist/coordinates/mgrs/system.js +30 -0
  100. package/dist/coordinates/mgrs/system.js.map +1 -1
  101. package/dist/coordinates/utm/parser.d.ts +24 -0
  102. package/dist/coordinates/utm/parser.js +56 -0
  103. package/dist/coordinates/utm/parser.js.map +1 -1
  104. package/dist/coordinates/utm/system.d.ts +21 -0
  105. package/dist/coordinates/utm/system.js +20 -0
  106. package/dist/coordinates/utm/system.js.map +1 -1
  107. package/dist/index.d.ts +3 -1
  108. package/dist/index.js +3 -1
  109. package/dist/patterning.d.ts +12 -2
  110. package/dist/patterning.js +12 -2
  111. package/dist/patterning.js.map +1 -1
  112. package/package.json +3 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @accelint/geo
2
2
 
3
+ ## 0.6.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 58bc0db: Extends `createCoordinate` to accept numeric input formats in addition to strings
8
+
3
9
  ## 0.5.1
4
10
 
5
11
  ### Patch Changes
package/README.md CHANGED
@@ -1,9 +1,241 @@
1
1
  # @accelint/geo
2
2
 
3
- A collection of JavaScript functions for working with coordinates and geospatial data.
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.5.1
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
@@ -14,8 +14,10 @@
14
14
  * pure function
15
15
  *
16
16
  * @example
17
+ * ```typescript
17
18
  * const result = cartesian([1, 2], ['a', 'b']);
18
19
  * // result: [[1, 'a'], [1, 'b'], [2, 'a'], [2, 'b']]
20
+ * ```
19
21
  */
20
22
  declare function cartesian<T>(...all: T[][]): T[][];
21
23
  //#endregion
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), []), [[]]);
@@ -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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+BA,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"}
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 of coordinateSystems
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: string) => Readonly<Coordinate>;
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 of coordinateSystems
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
- return (input) => {
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 freezeCoordinate(errors$1, () => "", {}, false);
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
- const cachedValues = { [initSystem.name]: createCache(initFormat, initSystem.name === systemMGRS.name ? input : tokens.join(" ")) };
66
- const raw = Object.fromEntries([[initFormat.slice(0, 3), initSystem.toFloat(tokens.slice(0, tokens.indexOf(SYMBOLS.DIVIDER)))], [initFormat.slice(3), initSystem.toFloat(tokens.slice(1 + tokens.indexOf(SYMBOLS.DIVIDER)))]]);
67
- function to(system = initSystem, format = initFormat) {
68
- const key = system.name;
69
- if (!cachedValues[key]?.[format]) cachedValues[key] = {
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 cachedValues[key][format];
155
+ return normalizeNumeric(result.lat, result.lon);
74
156
  }
75
- return freezeCoordinate([], to, raw, true);
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