@accelint/geo 0.5.1 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/README.md +233 -1
- package/catalog-info.yaml +1 -1
- package/dist/cartesian.d.ts +2 -0
- package/dist/cartesian.js +3 -1
- package/dist/cartesian.js.map +1 -1
- package/dist/coordinates/coordinate.d.ts +42 -8
- package/dist/coordinates/coordinate.js +103 -13
- package/dist/coordinates/coordinate.js.map +1 -1
- package/dist/coordinates/latlon/decimal-degrees/formatter.d.ts +21 -1
- package/dist/coordinates/latlon/decimal-degrees/formatter.js +39 -1
- package/dist/coordinates/latlon/decimal-degrees/formatter.js.map +1 -1
- package/dist/coordinates/latlon/decimal-degrees/parser.d.ts +69 -3
- package/dist/coordinates/latlon/decimal-degrees/parser.js +67 -6
- package/dist/coordinates/latlon/decimal-degrees/parser.js.map +1 -1
- package/dist/coordinates/latlon/decimal-degrees/system.d.ts +33 -1
- package/dist/coordinates/latlon/decimal-degrees/system.js +32 -1
- package/dist/coordinates/latlon/decimal-degrees/system.js.map +1 -1
- package/dist/coordinates/latlon/degrees-decimal-minutes/formatter.d.ts +21 -1
- package/dist/coordinates/latlon/degrees-decimal-minutes/formatter.js +43 -3
- package/dist/coordinates/latlon/degrees-decimal-minutes/formatter.js.map +1 -1
- package/dist/coordinates/latlon/degrees-decimal-minutes/parser.d.ts +73 -3
- package/dist/coordinates/latlon/degrees-decimal-minutes/parser.js +67 -4
- package/dist/coordinates/latlon/degrees-decimal-minutes/parser.js.map +1 -1
- package/dist/coordinates/latlon/degrees-decimal-minutes/system.d.ts +33 -1
- package/dist/coordinates/latlon/degrees-decimal-minutes/system.js +32 -1
- package/dist/coordinates/latlon/degrees-decimal-minutes/system.js.map +1 -1
- package/dist/coordinates/latlon/degrees-minutes-seconds/formatter.d.ts +21 -1
- package/dist/coordinates/latlon/degrees-minutes-seconds/formatter.js +46 -4
- package/dist/coordinates/latlon/degrees-minutes-seconds/formatter.js.map +1 -1
- package/dist/coordinates/latlon/degrees-minutes-seconds/parser.d.ts +75 -3
- package/dist/coordinates/latlon/degrees-minutes-seconds/parser.js +67 -4
- package/dist/coordinates/latlon/degrees-minutes-seconds/parser.js.map +1 -1
- package/dist/coordinates/latlon/degrees-minutes-seconds/system.d.ts +33 -1
- package/dist/coordinates/latlon/degrees-minutes-seconds/system.js +32 -1
- package/dist/coordinates/latlon/degrees-minutes-seconds/system.js.map +1 -1
- package/dist/coordinates/latlon/internal/coordinate-system.d.ts +23 -1
- package/dist/coordinates/latlon/internal/coordinate-system.js +1 -1
- package/dist/coordinates/latlon/internal/create-cache.d.ts +18 -2
- package/dist/coordinates/latlon/internal/create-cache.js +20 -4
- 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 +29 -1
- 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 +21 -1
- 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 +25 -1
- 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 +26 -2
- 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 +27 -1
- 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 +26 -1
- package/dist/coordinates/latlon/internal/ordinal.js.map +1 -1
- package/dist/coordinates/latlon/internal/parse-format.d.ts +23 -1
- package/dist/coordinates/latlon/internal/parse-format.js +44 -2
- package/dist/coordinates/latlon/internal/parse-format.js.map +1 -1
- package/dist/coordinates/latlon/internal/parse.d.ts +3 -1
- package/dist/coordinates/latlon/internal/parse.js +5 -2
- package/dist/coordinates/latlon/internal/parse.js.map +1 -1
- package/dist/coordinates/latlon/internal/pipes/check-ambiguous.d.ts +18 -1
- package/dist/coordinates/latlon/internal/pipes/check-ambiguous.js +17 -1
- package/dist/coordinates/latlon/internal/pipes/check-ambiguous.js.map +1 -1
- package/dist/coordinates/latlon/internal/pipes/check-numbers.d.ts +26 -1
- package/dist/coordinates/latlon/internal/pipes/check-numbers.js +34 -1
- package/dist/coordinates/latlon/internal/pipes/check-numbers.js.map +1 -1
- package/dist/coordinates/latlon/internal/pipes/fix-bearings.d.ts +18 -1
- package/dist/coordinates/latlon/internal/pipes/fix-bearings.js +32 -1
- package/dist/coordinates/latlon/internal/pipes/fix-bearings.js.map +1 -1
- package/dist/coordinates/latlon/internal/pipes/fix-dividers.d.ts +18 -1
- package/dist/coordinates/latlon/internal/pipes/fix-dividers.js +30 -1
- package/dist/coordinates/latlon/internal/pipes/fix-dividers.js.map +1 -1
- package/dist/coordinates/latlon/internal/pipes/genome.d.ts +17 -1
- package/dist/coordinates/latlon/internal/pipes/genome.js +42 -1
- package/dist/coordinates/latlon/internal/pipes/genome.js.map +1 -1
- package/dist/coordinates/latlon/internal/pipes/index.d.ts +33 -3
- package/dist/coordinates/latlon/internal/pipes/index.js +58 -5
- package/dist/coordinates/latlon/internal/pipes/index.js.map +1 -1
- package/dist/coordinates/latlon/internal/pipes/simpler.d.ts +17 -4
- package/dist/coordinates/latlon/internal/pipes/simpler.js +16 -4
- 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 +19 -1
- package/dist/coordinates/latlon/internal/violation.js.map +1 -1
- package/dist/coordinates/mgrs/parser.d.ts +25 -1
- package/dist/coordinates/mgrs/parser.js +57 -1
- package/dist/coordinates/mgrs/parser.js.map +1 -1
- package/dist/coordinates/mgrs/system.d.ts +32 -1
- package/dist/coordinates/mgrs/system.js +31 -1
- package/dist/coordinates/mgrs/system.js.map +1 -1
- package/dist/coordinates/utm/parser.d.ts +25 -1
- package/dist/coordinates/utm/parser.js +57 -1
- package/dist/coordinates/utm/parser.js.map +1 -1
- package/dist/coordinates/utm/system.d.ts +22 -1
- package/dist/coordinates/utm/system.js +21 -1
- package/dist/coordinates/utm/system.js.map +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.js +4 -2
- package/dist/patterning.d.ts +12 -2
- package/dist/patterning.js +13 -3
- package/dist/patterning.js.map +1 -1
- package/package.json +5 -3
|
@@ -13,8 +13,10 @@ type Tokens = ReturnType<typeof lexer>;
|
|
|
13
13
|
* pure function
|
|
14
14
|
*
|
|
15
15
|
* @example
|
|
16
|
+
* ```typescript
|
|
16
17
|
* lexer('N 55,E 44') === ['N' '55', '/', 'E', '44']
|
|
17
18
|
* lexer(` + 89 ° 59 59.999 " N, 179° 59 59.999" `) === ['89', '59', '59.999', 'N', '/', '179', '59', '59.999', 'E']
|
|
19
|
+
* ```
|
|
18
20
|
*/
|
|
19
21
|
declare function lexer(input: string): string[];
|
|
20
22
|
//#endregion
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* Copyright
|
|
2
|
+
* Copyright 2026 Hypergiant Galactic Systems Inc. All rights reserved.
|
|
3
3
|
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
|
4
4
|
* you may not use this file except in compliance with the License. You may obtain a copy
|
|
5
5
|
* of the License at https://www.apache.org/licenses/LICENSE-2.0
|
|
@@ -36,6 +36,30 @@ const SIGNS = /([-+])\s*/g;
|
|
|
36
36
|
* - [Nodexr](https://www.nodexr.net/?parse=%2F%5B,%2F%5D%7C%5BNSEW%5D%7C(%3F%3A%5B-%2B%5D%3F(%3F%3A(%3F%3A%5Cd%2B(%3F%3A%5C.%5Cd*)%3F)%7C(%3F%3A%5C.%5Cd%2B))%5B%C2%B0%27%22%5D%3F)%2Fgi)
|
|
37
37
|
*/
|
|
38
38
|
const TOKENS = new RegExp(merge(DIVIDERS, /|/, SYMBOL_PATTERNS.NSEW, /|/, group(/[-+]?/, group(/(?:\d+(?:\.\d*)?)|/, /(?:\.\d+)/), optional(SYMBOL_PATTERNS.DMS))), "gi");
|
|
39
|
+
/**
|
|
40
|
+
* Remove trailing zeros '?.0' and ensure leading zero '0.?' in numbers.
|
|
41
|
+
*
|
|
42
|
+
* @param t - Token string to normalize.
|
|
43
|
+
* @returns Normalized token with cleaned numeric formatting.
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* ```typescript
|
|
47
|
+
* fixLeadingAndTrailing('45.0°');
|
|
48
|
+
* // '45°'
|
|
49
|
+
* ```
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* ```typescript
|
|
53
|
+
* fixLeadingAndTrailing('.5'');
|
|
54
|
+
* // '0.5''
|
|
55
|
+
* ```
|
|
56
|
+
*
|
|
57
|
+
* @example
|
|
58
|
+
* ```typescript
|
|
59
|
+
* fixLeadingAndTrailing('-122.00');
|
|
60
|
+
* // '-122'
|
|
61
|
+
* ```
|
|
62
|
+
*/
|
|
39
63
|
function fixLeadingAndTrailing(t) {
|
|
40
64
|
const [sign, num, pos] = (FLOATS.exec(t) ?? []).slice(1);
|
|
41
65
|
if (num) return `${sign}${Number.parseFloat(num)}${pos}`;
|
|
@@ -54,8 +78,10 @@ function fixLeadingAndTrailing(t) {
|
|
|
54
78
|
* pure function
|
|
55
79
|
*
|
|
56
80
|
* @example
|
|
81
|
+
* ```typescript
|
|
57
82
|
* lexer('N 55,E 44') === ['N' '55', '/', 'E', '44']
|
|
58
83
|
* lexer(` + 89 ° 59 59.999 " N, 179° 59 59.999" `) === ['89', '59', '59.999', 'N', '/', '179', '59', '59.999', 'E']
|
|
84
|
+
* ```
|
|
59
85
|
*/
|
|
60
86
|
function lexer(input) {
|
|
61
87
|
return input.trim().toUpperCase().replace(POSITIVE, "").replace(POSITIONAL, "$1 ").replace(SIGNS, "$1").replace(DIVIDERS, SYMBOLS.DIVIDER).match(TOKENS)?.map(fixLeadingAndTrailing)?.slice() ?? [];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lexer.js","names":["Patterning.merge","Patterning.capture","Patterning.group","Patterning.optional"],"sources":["../../../../src/coordinates/latlon/internal/lexer.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\nimport * as Patterning from '@/patterning';\nimport { SYMBOL_PATTERNS, SYMBOLS } from '.';\n\nexport type Tokens = ReturnType<typeof lexer>;\n\n/**\n * Separating latitude from longitude portions of a coordinate. At this level\n * of pattern matching this list can not include the \"space\" character since\n * that is valid between components of either side of a divider; higher level\n * parsers will be able to make up for this shortcoming and be more intelligent\n * about deducing where a divider could be added or would not be valid.\n */\nconst DIVIDERS = /[,/]/g;\nconst FLOATS = /^(-?)([\\d.]+)([^.\\d]?)$/;\n/** Positional indicators for: degrees, minutes, and seconds */\nconst POSITIONAL = new RegExp(\n Patterning.merge(/\\s*/, Patterning.capture(SYMBOL_PATTERNS.DMS), /\\s*/),\n 'g',\n);\nconst POSITIVE = /\\+/g;\nconst SIGNS = /([-+])\\s*/g;\n/**\n * Any recognizably significant tokens anywhere (non-positional-y) within a\n * string; because at this level (lexing) actual position is not important.\n *\n * - [Regex Vis](https://regex-vis.com/?r=%2F%5B%2C%2F%5D%7C%5BNSEW%5D%7C%28%3F%3A%5B-%2B%5D%3F%28%3F%3A%28%3F%3A%5Cd%2B%28%3F%3A%5C.%5Cd*%29%3F%29%7C%28%3F%3A%5C.%5Cd%2B%29%29%28%3F%3A%5B%C2%B0%27%22%5D%29%3F%29%2Fgi)\n * - [Nodexr](https://www.nodexr.net/?parse=%2F%5B,%2F%5D%7C%5BNSEW%5D%7C(%3F%3A%5B-%2B%5D%3F(%3F%3A(%3F%3A%5Cd%2B(%3F%3A%5C.%5Cd*)%3F)%7C(%3F%3A%5C.%5Cd%2B))%5B%C2%B0%27%22%5D%3F)%2Fgi)\n */\n// NOTE: the links (above) for \"Regex Vis\" and \"Nodexr\" would need to be updated if/when the pattern is changed.\nconst TOKENS = new RegExp(\n Patterning.merge(\n DIVIDERS,\n /|/,\n SYMBOL_PATTERNS.NSEW,\n /|/,\n Patterning.group(\n /[-+]?/,\n Patterning.group(\n // left of decimal REQUIRED, right of decimal optional\n /(?:\\d+(?:\\.\\d*)?)|/,\n // left of decimal omitted, right of decimal REQUIRED\n /(?:\\.\\d+)/,\n ),\n Patterning.optional(SYMBOL_PATTERNS.DMS),\n ),\n ),\n 'gi',\n);\n\n
|
|
1
|
+
{"version":3,"file":"lexer.js","names":["Patterning.merge","Patterning.capture","Patterning.group","Patterning.optional"],"sources":["../../../../src/coordinates/latlon/internal/lexer.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\nimport * as Patterning from '@/patterning';\nimport { SYMBOL_PATTERNS, SYMBOLS } from '.';\n\nexport type Tokens = ReturnType<typeof lexer>;\n\n/**\n * Separating latitude from longitude portions of a coordinate. At this level\n * of pattern matching this list can not include the \"space\" character since\n * that is valid between components of either side of a divider; higher level\n * parsers will be able to make up for this shortcoming and be more intelligent\n * about deducing where a divider could be added or would not be valid.\n */\nconst DIVIDERS = /[,/]/g;\nconst FLOATS = /^(-?)([\\d.]+)([^.\\d]?)$/;\n/** Positional indicators for: degrees, minutes, and seconds */\nconst POSITIONAL = new RegExp(\n Patterning.merge(/\\s*/, Patterning.capture(SYMBOL_PATTERNS.DMS), /\\s*/),\n 'g',\n);\nconst POSITIVE = /\\+/g;\nconst SIGNS = /([-+])\\s*/g;\n/**\n * Any recognizably significant tokens anywhere (non-positional-y) within a\n * string; because at this level (lexing) actual position is not important.\n *\n * - [Regex Vis](https://regex-vis.com/?r=%2F%5B%2C%2F%5D%7C%5BNSEW%5D%7C%28%3F%3A%5B-%2B%5D%3F%28%3F%3A%28%3F%3A%5Cd%2B%28%3F%3A%5C.%5Cd*%29%3F%29%7C%28%3F%3A%5C.%5Cd%2B%29%29%28%3F%3A%5B%C2%B0%27%22%5D%29%3F%29%2Fgi)\n * - [Nodexr](https://www.nodexr.net/?parse=%2F%5B,%2F%5D%7C%5BNSEW%5D%7C(%3F%3A%5B-%2B%5D%3F(%3F%3A(%3F%3A%5Cd%2B(%3F%3A%5C.%5Cd*)%3F)%7C(%3F%3A%5C.%5Cd%2B))%5B%C2%B0%27%22%5D%3F)%2Fgi)\n */\n// NOTE: the links (above) for \"Regex Vis\" and \"Nodexr\" would need to be updated if/when the pattern is changed.\nconst TOKENS = new RegExp(\n Patterning.merge(\n DIVIDERS,\n /|/,\n SYMBOL_PATTERNS.NSEW,\n /|/,\n Patterning.group(\n /[-+]?/,\n Patterning.group(\n // left of decimal REQUIRED, right of decimal optional\n /(?:\\d+(?:\\.\\d*)?)|/,\n // left of decimal omitted, right of decimal REQUIRED\n /(?:\\.\\d+)/,\n ),\n Patterning.optional(SYMBOL_PATTERNS.DMS),\n ),\n ),\n 'gi',\n);\n\n/**\n * Remove trailing zeros '?.0' and ensure leading zero '0.?' in numbers.\n *\n * @param t - Token string to normalize.\n * @returns Normalized token with cleaned numeric formatting.\n *\n * @example\n * ```typescript\n * fixLeadingAndTrailing('45.0°');\n * // '45°'\n * ```\n *\n * @example\n * ```typescript\n * fixLeadingAndTrailing('.5'');\n * // '0.5''\n * ```\n *\n * @example\n * ```typescript\n * fixLeadingAndTrailing('-122.00');\n * // '-122'\n * ```\n */\nfunction fixLeadingAndTrailing(t: string) {\n const [sign, num, pos] = (FLOATS.exec(t) ?? []).slice(1);\n\n if (num) {\n return `${sign}${Number.parseFloat(num)}${pos}`;\n }\n\n return t;\n}\n\n/**\n * Take an input string - possibly from user input - and clean it up enough to\n * be something to work with at a higher level of processing (with more\n * information) than is available at this level. Generating a list of \"tokens\"\n * that are potentially valid parts of a coordinate. The values being looked\n * for are: numbers (with positional indicators) and axes (NSEW).\n *\n * NOTE: No validation is done at this level to keep it simple as agnostic.\n *\n * @remarks\n * pure function\n *\n * @example\n * ```typescript\n * lexer('N 55,E 44') === ['N' '55', '/', 'E', '44']\n * lexer(` + 89 ° 59 59.999 \" N, 179° 59 59.999\" `) === ['89', '59', '59.999', 'N', '/', '179', '59', '59.999', 'E']\n * ```\n */\nexport function lexer(input: string) {\n const tokens =\n input\n .trim()\n .toUpperCase()\n .replace(POSITIVE, '') // positive signs are redundant\n .replace(POSITIONAL, '$1 ') // group positional indicators with numbers\n .replace(SIGNS, '$1') // group signs with numbers\n .replace(DIVIDERS, SYMBOLS.DIVIDER) // standardize the divider\n .match(TOKENS)\n ?.map(fixLeadingAndTrailing)\n ?.slice() ?? [];\n\n return tokens;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAyBA,MAAM,WAAW;AACjB,MAAM,SAAS;;AAEf,MAAM,aAAa,IAAI,OACrBA,MAAiB,OAAOC,QAAmB,gBAAgB,IAAI,EAAE,MAAM,EACvE,IACD;AACD,MAAM,WAAW;AACjB,MAAM,QAAQ;;;;;;;;AASd,MAAM,SAAS,IAAI,OACjBD,MACE,UACA,KACA,gBAAgB,MAChB,KACAE,MACE,SACAA,MAEE,sBAEA,YACD,EACDC,SAAoB,gBAAgB,IAAI,CACzC,CACF,EACD,KACD;;;;;;;;;;;;;;;;;;;;;;;;;AA0BD,SAAS,sBAAsB,GAAW;CACxC,MAAM,CAAC,MAAM,KAAK,QAAQ,OAAO,KAAK,EAAE,IAAI,EAAE,EAAE,MAAM,EAAE;AAExD,KAAI,IACF,QAAO,GAAG,OAAO,OAAO,WAAW,IAAI,GAAG;AAG5C,QAAO;;;;;;;;;;;;;;;;;;;;AAqBT,SAAgB,MAAM,OAAe;AAanC,QAXE,MACG,MAAM,CACN,aAAa,CACb,QAAQ,UAAU,GAAG,CACrB,QAAQ,YAAY,MAAM,CAC1B,QAAQ,OAAO,KAAK,CACpB,QAAQ,UAAU,QAAQ,QAAQ,CAClC,MAAM,OAAO,EACZ,IAAI,sBAAsB,EAC1B,OAAO,IAAI,EAAE"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
//#region src/coordinates/latlon/internal/normalize.d.ts
|
|
2
|
+
type Format = 'LATLON' | 'LONLAT';
|
|
3
|
+
type CoordinateInternalValue = {
|
|
4
|
+
LAT: number;
|
|
5
|
+
LON: number;
|
|
6
|
+
};
|
|
7
|
+
/** Tuple in `[latitude, longitude]` order. */
|
|
8
|
+
type LatLonTuple = readonly [latitude: number, longitude: number];
|
|
9
|
+
/** Tuple in `[longitude, latitude]` order (GeoJSON convention). */
|
|
10
|
+
type LonLatTuple = readonly [longitude: number, latitude: number];
|
|
11
|
+
/** A coordinate tuple in either lat/lon or lon/lat order. */
|
|
12
|
+
type CoordinateTuple = LatLonTuple | LonLatTuple;
|
|
13
|
+
/**
|
|
14
|
+
* Object representation of a coordinate with explicit lat/lon keys.
|
|
15
|
+
* Accepts objects with case-insensitive variations: lat/LAT/latitude/LATITUDE and lon/LON/longitude/LONGITUDE.
|
|
16
|
+
* Runtime validation via `isCoordinateObject` ensures proper keys are present.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```typescript
|
|
20
|
+
* { lat: 40.7128, lon: -74.0060 }
|
|
21
|
+
* { latitude: 40.7128, longitude: -74.0060 }
|
|
22
|
+
* { LAT: 40.7128, LON: -74.0060 }
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
type CoordinateObject = Record<string, number>;
|
|
26
|
+
type CoordinateInput = string | CoordinateTuple | CoordinateObject;
|
|
27
|
+
declare function isCoordinateTuple(input: CoordinateInput): input is CoordinateTuple;
|
|
28
|
+
declare function isCoordinateObject(input: CoordinateInput): input is CoordinateObject;
|
|
29
|
+
/**
|
|
30
|
+
* Normalizes a coordinate object to a consistent lat/lon format.
|
|
31
|
+
*
|
|
32
|
+
* Accepts coordinate objects with case-insensitive key variations (lat/LAT/latitude/LATITUDE
|
|
33
|
+
* and lon/LON/longitude/LONGITUDE) and returns a normalized object with lowercase 'lat' and 'lon' keys.
|
|
34
|
+
* Returns null if required keys are not found.
|
|
35
|
+
*
|
|
36
|
+
* @param obj - Coordinate object with lat/lon keys (case-insensitive)
|
|
37
|
+
* @returns Normalized object with lat and lon keys, or null if invalid
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* ```typescript
|
|
41
|
+
* normalizeObjectToLatLon({ lat: 40.7128, lon: -74.0060 });
|
|
42
|
+
* // => { lat: 40.7128, lon: -74.0060 }
|
|
43
|
+
* ```
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* ```typescript
|
|
47
|
+
* normalizeObjectToLatLon({ LATITUDE: 40.7128, LONGITUDE: -74.0060 });
|
|
48
|
+
* // => { lat: 40.7128, lon: -74.0060 }
|
|
49
|
+
* ```
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* ```typescript
|
|
53
|
+
* normalizeObjectToLatLon({ x: 10, y: 20 });
|
|
54
|
+
* // => null (missing required keys)
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
57
|
+
declare function normalizeObjectToLatLon(obj: CoordinateObject): {
|
|
58
|
+
lat: number;
|
|
59
|
+
lon: number;
|
|
60
|
+
} | null;
|
|
61
|
+
declare function tupleToLatLon(format: Format, tuple: CoordinateTuple): {
|
|
62
|
+
lat: number;
|
|
63
|
+
lon: number;
|
|
64
|
+
};
|
|
65
|
+
//#endregion
|
|
66
|
+
export { CoordinateInput, CoordinateInternalValue, CoordinateObject, CoordinateTuple, LatLonTuple, LonLatTuple, isCoordinateObject, isCoordinateTuple, normalizeObjectToLatLon, tupleToLatLon };
|
|
67
|
+
//# sourceMappingURL=normalize.d.ts.map
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2026 Hypergiant Galactic Systems Inc. All rights reserved.
|
|
3
|
+
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
* you may not use this file except in compliance with the License. You may obtain a copy
|
|
5
|
+
* of the License at https://www.apache.org/licenses/LICENSE-2.0
|
|
6
|
+
*
|
|
7
|
+
* Unless required by applicable law or agreed to in writing, software distributed under
|
|
8
|
+
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
|
|
9
|
+
* OF ANY KIND, either express or implied. See the License for the specific language
|
|
10
|
+
* governing permissions and limitations under the License.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
//#region src/coordinates/latlon/internal/normalize.ts
|
|
15
|
+
const LAT_KEYS = ["lat", "latitude"];
|
|
16
|
+
const LON_KEYS = ["lon", "longitude"];
|
|
17
|
+
function isCoordinateTuple(input) {
|
|
18
|
+
return Array.isArray(input) && input.length === 2 && typeof input[0] === "number" && typeof input[1] === "number";
|
|
19
|
+
}
|
|
20
|
+
function isCoordinateObject(input) {
|
|
21
|
+
if (input === null || typeof input !== "object" || Array.isArray(input)) return false;
|
|
22
|
+
const keys = Object.keys(input).map((k) => k.toLowerCase());
|
|
23
|
+
const hasLat = LAT_KEYS.some((k) => keys.includes(k));
|
|
24
|
+
const hasLon = LON_KEYS.some((k) => keys.includes(k));
|
|
25
|
+
return hasLat && hasLon;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Normalizes a coordinate object to a consistent lat/lon format.
|
|
29
|
+
*
|
|
30
|
+
* Accepts coordinate objects with case-insensitive key variations (lat/LAT/latitude/LATITUDE
|
|
31
|
+
* and lon/LON/longitude/LONGITUDE) and returns a normalized object with lowercase 'lat' and 'lon' keys.
|
|
32
|
+
* Returns null if required keys are not found.
|
|
33
|
+
*
|
|
34
|
+
* @param obj - Coordinate object with lat/lon keys (case-insensitive)
|
|
35
|
+
* @returns Normalized object with lat and lon keys, or null if invalid
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* ```typescript
|
|
39
|
+
* normalizeObjectToLatLon({ lat: 40.7128, lon: -74.0060 });
|
|
40
|
+
* // => { lat: 40.7128, lon: -74.0060 }
|
|
41
|
+
* ```
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* ```typescript
|
|
45
|
+
* normalizeObjectToLatLon({ LATITUDE: 40.7128, LONGITUDE: -74.0060 });
|
|
46
|
+
* // => { lat: 40.7128, lon: -74.0060 }
|
|
47
|
+
* ```
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* ```typescript
|
|
51
|
+
* normalizeObjectToLatLon({ x: 10, y: 20 });
|
|
52
|
+
* // => null (missing required keys)
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
function normalizeObjectToLatLon(obj) {
|
|
56
|
+
const lower = /* @__PURE__ */ new Map();
|
|
57
|
+
for (const [key, value] of Object.entries(obj)) lower.set(key.toLowerCase(), value);
|
|
58
|
+
let lat;
|
|
59
|
+
let lon;
|
|
60
|
+
for (const k of LAT_KEYS) if (lower.has(k)) {
|
|
61
|
+
lat = lower.get(k);
|
|
62
|
+
break;
|
|
63
|
+
}
|
|
64
|
+
for (const k of LON_KEYS) if (lower.has(k)) {
|
|
65
|
+
lon = lower.get(k);
|
|
66
|
+
break;
|
|
67
|
+
}
|
|
68
|
+
if (lat === void 0 || lon === void 0) return null;
|
|
69
|
+
return {
|
|
70
|
+
lat,
|
|
71
|
+
lon
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
function tupleToLatLon(format, tuple) {
|
|
75
|
+
if (format === "LATLON") return {
|
|
76
|
+
lat: tuple[0],
|
|
77
|
+
lon: tuple[1]
|
|
78
|
+
};
|
|
79
|
+
return {
|
|
80
|
+
lat: tuple[1],
|
|
81
|
+
lon: tuple[0]
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
//#endregion
|
|
86
|
+
export { isCoordinateObject, isCoordinateTuple, normalizeObjectToLatLon, tupleToLatLon };
|
|
87
|
+
//# sourceMappingURL=normalize.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"normalize.js","names":["lat: number | undefined","lon: number | undefined"],"sources":["../../../../src/coordinates/latlon/internal/normalize.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\ntype Format = 'LATLON' | 'LONLAT';\n\n// biome-ignore lint/style/useNamingConvention: consistency with Axes type\nexport type CoordinateInternalValue = { LAT: number; LON: number };\n\n/** Tuple in `[latitude, longitude]` order. */\nexport type LatLonTuple = readonly [latitude: number, longitude: number];\n\n/** Tuple in `[longitude, latitude]` order (GeoJSON convention). */\nexport type LonLatTuple = readonly [longitude: number, latitude: number];\n\n/** A coordinate tuple in either lat/lon or lon/lat order. */\nexport type CoordinateTuple = LatLonTuple | LonLatTuple;\n\n/**\n * Object representation of a coordinate with explicit lat/lon keys.\n * Accepts objects with case-insensitive variations: lat/LAT/latitude/LATITUDE and lon/LON/longitude/LONGITUDE.\n * Runtime validation via `isCoordinateObject` ensures proper keys are present.\n *\n * @example\n * ```typescript\n * { lat: 40.7128, lon: -74.0060 }\n * { latitude: 40.7128, longitude: -74.0060 }\n * { LAT: 40.7128, LON: -74.0060 }\n * ```\n */\nexport type CoordinateObject = Record<string, number>;\n\nexport type CoordinateInput = string | CoordinateTuple | CoordinateObject;\n\nconst LAT_KEYS = ['lat', 'latitude'];\nconst LON_KEYS = ['lon', 'longitude'];\n\nexport function isCoordinateTuple(\n input: CoordinateInput,\n): input is CoordinateTuple {\n return (\n Array.isArray(input) &&\n input.length === 2 &&\n typeof input[0] === 'number' &&\n typeof input[1] === 'number'\n );\n}\n\nexport function isCoordinateObject(\n input: CoordinateInput,\n): input is CoordinateObject {\n if (input === null || typeof input !== 'object' || Array.isArray(input)) {\n return false;\n }\n const keys = Object.keys(input).map((k) => k.toLowerCase());\n const hasLat = LAT_KEYS.some((k) => keys.includes(k));\n const hasLon = LON_KEYS.some((k) => keys.includes(k));\n return hasLat && hasLon;\n}\n\n/**\n * Normalizes a coordinate object to a consistent lat/lon format.\n *\n * Accepts coordinate objects with case-insensitive key variations (lat/LAT/latitude/LATITUDE\n * and lon/LON/longitude/LONGITUDE) and returns a normalized object with lowercase 'lat' and 'lon' keys.\n * Returns null if required keys are not found.\n *\n * @param obj - Coordinate object with lat/lon keys (case-insensitive)\n * @returns Normalized object with lat and lon keys, or null if invalid\n *\n * @example\n * ```typescript\n * normalizeObjectToLatLon({ lat: 40.7128, lon: -74.0060 });\n * // => { lat: 40.7128, lon: -74.0060 }\n * ```\n *\n * @example\n * ```typescript\n * normalizeObjectToLatLon({ LATITUDE: 40.7128, LONGITUDE: -74.0060 });\n * // => { lat: 40.7128, lon: -74.0060 }\n * ```\n *\n * @example\n * ```typescript\n * normalizeObjectToLatLon({ x: 10, y: 20 });\n * // => null (missing required keys)\n * ```\n */\nexport function normalizeObjectToLatLon(obj: CoordinateObject): {\n lat: number;\n lon: number;\n} | null {\n const lower = new Map<string, number>();\n for (const [key, value] of Object.entries(obj)) {\n lower.set(key.toLowerCase(), value);\n }\n\n let lat: number | undefined;\n let lon: number | undefined;\n\n for (const k of LAT_KEYS) {\n if (lower.has(k)) {\n lat = lower.get(k);\n break;\n }\n }\n\n for (const k of LON_KEYS) {\n if (lower.has(k)) {\n lon = lower.get(k);\n break;\n }\n }\n\n if (lat === undefined || lon === undefined) {\n return null;\n }\n\n return { lat, lon };\n}\n\nexport function tupleToLatLon(\n format: Format,\n tuple: CoordinateTuple,\n): { lat: number; lon: number } {\n // LATLON = [lat, lon], LONLAT = [lon, lat]\n if (format === 'LATLON') {\n return { lat: tuple[0], lon: tuple[1] };\n }\n return { lat: tuple[1], lon: tuple[0] };\n}\n"],"mappings":";;;;;;;;;;;;;;AA0CA,MAAM,WAAW,CAAC,OAAO,WAAW;AACpC,MAAM,WAAW,CAAC,OAAO,YAAY;AAErC,SAAgB,kBACd,OAC0B;AAC1B,QACE,MAAM,QAAQ,MAAM,IACpB,MAAM,WAAW,KACjB,OAAO,MAAM,OAAO,YACpB,OAAO,MAAM,OAAO;;AAIxB,SAAgB,mBACd,OAC2B;AAC3B,KAAI,UAAU,QAAQ,OAAO,UAAU,YAAY,MAAM,QAAQ,MAAM,CACrE,QAAO;CAET,MAAM,OAAO,OAAO,KAAK,MAAM,CAAC,KAAK,MAAM,EAAE,aAAa,CAAC;CAC3D,MAAM,SAAS,SAAS,MAAM,MAAM,KAAK,SAAS,EAAE,CAAC;CACrD,MAAM,SAAS,SAAS,MAAM,MAAM,KAAK,SAAS,EAAE,CAAC;AACrD,QAAO,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+BnB,SAAgB,wBAAwB,KAG/B;CACP,MAAM,wBAAQ,IAAI,KAAqB;AACvC,MAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,IAAI,CAC5C,OAAM,IAAI,IAAI,aAAa,EAAE,MAAM;CAGrC,IAAIA;CACJ,IAAIC;AAEJ,MAAK,MAAM,KAAK,SACd,KAAI,MAAM,IAAI,EAAE,EAAE;AAChB,QAAM,MAAM,IAAI,EAAE;AAClB;;AAIJ,MAAK,MAAM,KAAK,SACd,KAAI,MAAM,IAAI,EAAE,EAAE;AAChB,QAAM,MAAM,IAAI,EAAE;AAClB;;AAIJ,KAAI,QAAQ,UAAa,QAAQ,OAC/B,QAAO;AAGT,QAAO;EAAE;EAAK;EAAK;;AAGrB,SAAgB,cACd,QACA,OAC8B;AAE9B,KAAI,WAAW,SACb,QAAO;EAAE,KAAK,MAAM;EAAI,KAAK,MAAM;EAAI;AAEzC,QAAO;EAAE,KAAK,MAAM;EAAI,KAAK,MAAM;EAAI"}
|
|
@@ -1,4 +1,29 @@
|
|
|
1
1
|
//#region src/coordinates/latlon/internal/ordinal.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Gets the ordinal direction (N/S/E/W) for a coordinate value.
|
|
4
|
+
*
|
|
5
|
+
* @param num - The coordinate value (positive or negative).
|
|
6
|
+
* @param isLatitude - Whether this is a latitude coordinate (true) or longitude (false).
|
|
7
|
+
* @returns Ordinal direction character: 'N', 'S', 'E', or 'W'.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* getOrdinal(37.7749, true);
|
|
12
|
+
* // 'N'
|
|
13
|
+
* ```
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```typescript
|
|
17
|
+
* getOrdinal(-122.4194, false);
|
|
18
|
+
* // 'W'
|
|
19
|
+
* ```
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```typescript
|
|
23
|
+
* getOrdinal(-45, true);
|
|
24
|
+
* // 'S'
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
2
27
|
declare const getOrdinal: (num: number, isLatitude: boolean) => string;
|
|
3
28
|
//#endregion
|
|
4
29
|
export { getOrdinal };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* Copyright
|
|
2
|
+
* Copyright 2026 Hypergiant Galactic Systems Inc. All rights reserved.
|
|
3
3
|
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
|
4
4
|
* you may not use this file except in compliance with the License. You may obtain a copy
|
|
5
5
|
* of the License at https://www.apache.org/licenses/LICENSE-2.0
|
|
@@ -12,6 +12,31 @@
|
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
//#region src/coordinates/latlon/internal/ordinal.ts
|
|
15
|
+
/**
|
|
16
|
+
* Gets the ordinal direction (N/S/E/W) for a coordinate value.
|
|
17
|
+
*
|
|
18
|
+
* @param num - The coordinate value (positive or negative).
|
|
19
|
+
* @param isLatitude - Whether this is a latitude coordinate (true) or longitude (false).
|
|
20
|
+
* @returns Ordinal direction character: 'N', 'S', 'E', or 'W'.
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```typescript
|
|
24
|
+
* getOrdinal(37.7749, true);
|
|
25
|
+
* // 'N'
|
|
26
|
+
* ```
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```typescript
|
|
30
|
+
* getOrdinal(-122.4194, false);
|
|
31
|
+
* // 'W'
|
|
32
|
+
* ```
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* ```typescript
|
|
36
|
+
* getOrdinal(-45, true);
|
|
37
|
+
* // 'S'
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
15
40
|
const getOrdinal = (num, isLatitude) => {
|
|
16
41
|
if (isLatitude) return num >= 0 ? "N" : "S";
|
|
17
42
|
return num >= 0 ? "E" : "W";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ordinal.js","names":[],"sources":["../../../../src/coordinates/latlon/internal/ordinal.ts"],"sourcesContent":["/*\n * Copyright
|
|
1
|
+
{"version":3,"file":"ordinal.js","names":[],"sources":["../../../../src/coordinates/latlon/internal/ordinal.ts"],"sourcesContent":["/*\n * Copyright 2026 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// __private-exports\n\n/**\n * Gets the ordinal direction (N/S/E/W) for a coordinate value.\n *\n * @param num - The coordinate value (positive or negative).\n * @param isLatitude - Whether this is a latitude coordinate (true) or longitude (false).\n * @returns Ordinal direction character: 'N', 'S', 'E', or 'W'.\n *\n * @example\n * ```typescript\n * getOrdinal(37.7749, true);\n * // 'N'\n * ```\n *\n * @example\n * ```typescript\n * getOrdinal(-122.4194, false);\n * // 'W'\n * ```\n *\n * @example\n * ```typescript\n * getOrdinal(-45, true);\n * // 'S'\n * ```\n */\nexport const getOrdinal = (num: number, isLatitude: boolean): string => {\n if (isLatitude) {\n return num >= 0 ? 'N' : 'S';\n }\n return num >= 0 ? 'E' : 'W';\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAuCA,MAAa,cAAc,KAAa,eAAgC;AACtE,KAAI,WACF,QAAO,OAAO,IAAI,MAAM;AAE1B,QAAO,OAAO,IAAI,MAAM"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* Copyright
|
|
2
|
+
* Copyright 2026 Hypergiant Galactic Systems Inc. All rights reserved.
|
|
3
3
|
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
|
4
4
|
* you may not use this file except in compliance with the License. You may obtain a copy
|
|
5
5
|
* of the License at https://www.apache.org/licenses/LICENSE-2.0
|
|
@@ -22,6 +22,28 @@ type FormatParserConfig<T> = {
|
|
|
22
22
|
identifyErrors: (format: Format) => (arg: T, i: number) => ParseResults;
|
|
23
23
|
identifyPieces: (half: string[]) => T;
|
|
24
24
|
};
|
|
25
|
+
/**
|
|
26
|
+
* Creates a coordinate parser function from a format parser configuration.
|
|
27
|
+
*
|
|
28
|
+
* @param config - Parser configuration with format patterns, error identification, and piece identification.
|
|
29
|
+
* @returns Parser function that takes format and input string, returns ParseResults.
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```typescript
|
|
33
|
+
* const parseDD = createParser({
|
|
34
|
+
* formats: { LATLON: /pattern/, LONLAT: /pattern/ },
|
|
35
|
+
* identifyErrors: (format) => (arg, i) => [tokens, errors],
|
|
36
|
+
* identifyPieces: (half) => ({ deg, bear })
|
|
37
|
+
* });
|
|
38
|
+
* ```
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* ```typescript
|
|
42
|
+
* const parser = createParser(config);
|
|
43
|
+
* parser('LATLON', '37.7749° N / 122.4194° W');
|
|
44
|
+
* // [[37.7749, -122.4194], []]
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
25
47
|
declare const createParser: <T>(config: FormatParserConfig<T | undefined>) => (format: Format, input: string) => ParseResults;
|
|
26
48
|
//#endregion
|
|
27
49
|
export { createParser };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* Copyright
|
|
2
|
+
* Copyright 2026 Hypergiant Galactic Systems Inc. All rights reserved.
|
|
3
3
|
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
|
4
4
|
* you may not use this file except in compliance with the License. You may obtain a copy
|
|
5
5
|
* of the License at https://www.apache.org/licenses/LICENSE-2.0
|
|
@@ -11,13 +11,55 @@
|
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
|
|
14
|
-
import { SYMBOLS, SYMBOL_PATTERNS } from "./index.js";
|
|
15
14
|
import { violation } from "./violation.js";
|
|
15
|
+
import { SYMBOLS, SYMBOL_PATTERNS } from "./index.js";
|
|
16
16
|
import { parse } from "./parse.js";
|
|
17
17
|
|
|
18
18
|
//#region src/coordinates/latlon/internal/parse-format.ts
|
|
19
19
|
const axisTypeTest = (k, t) => SYMBOL_PATTERNS[k].test(t) && k;
|
|
20
|
+
/**
|
|
21
|
+
* Creates a coordinate parser function from a format parser configuration.
|
|
22
|
+
*
|
|
23
|
+
* @param config - Parser configuration with format patterns, error identification, and piece identification.
|
|
24
|
+
* @returns Parser function that takes format and input string, returns ParseResults.
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```typescript
|
|
28
|
+
* const parseDD = createParser({
|
|
29
|
+
* formats: { LATLON: /pattern/, LONLAT: /pattern/ },
|
|
30
|
+
* identifyErrors: (format) => (arg, i) => [tokens, errors],
|
|
31
|
+
* identifyPieces: (half) => ({ deg, bear })
|
|
32
|
+
* });
|
|
33
|
+
* ```
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* ```typescript
|
|
37
|
+
* const parser = createParser(config);
|
|
38
|
+
* parser('LATLON', '37.7749° N / 122.4194° W');
|
|
39
|
+
* // [[37.7749, -122.4194], []]
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
20
42
|
const createParser = (config) => (format, input) => parseWithConfig(config, format, input);
|
|
43
|
+
/**
|
|
44
|
+
* Parses a coordinate string using the provided configuration.
|
|
45
|
+
*
|
|
46
|
+
* @param config - Parser configuration with format patterns and validation functions.
|
|
47
|
+
* @param format - Expected coordinate format (LATLON or LONLAT).
|
|
48
|
+
* @param input - Raw coordinate string to parse.
|
|
49
|
+
* @returns Parse results with coordinate values or error messages.
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* ```typescript
|
|
53
|
+
* parseWithConfig(config, 'LATLON', '37° N / 122° W');
|
|
54
|
+
* // [['37', 'N', '122', 'W'], []]
|
|
55
|
+
* ```
|
|
56
|
+
*
|
|
57
|
+
* @example
|
|
58
|
+
* ```typescript
|
|
59
|
+
* parseWithConfig(config, 'LATLON', '91° N / 122° W');
|
|
60
|
+
* // [[], ['[ERROR] Degrees value (91) exceeds max value (90).']]
|
|
61
|
+
* ```
|
|
62
|
+
*/
|
|
21
63
|
function parseWithConfig(config, format, input) {
|
|
22
64
|
const [parseTokens, violations] = parse(input, format);
|
|
23
65
|
const foundFormat = parseTokens.reduce((acc, t) => acc + (axisTypeTest("LAT", t) || axisTypeTest("LON", t) || ""), "");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parse-format.js","names":[],"sources":["../../../../src/coordinates/latlon/internal/parse-format.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\nimport { type Format, SYMBOL_PATTERNS, SYMBOLS } from '.';\nimport { type ParseResults, parse } from './parse';\nimport { violation } from './violation';\n\ntype FormatParserConfig<T> = {\n formats: {\n // biome-ignore lint/style/useNamingConvention: Name is chosen to stand out amongst other formats\n LATLON: RegExp;\n // biome-ignore lint/style/useNamingConvention: Name is chosen to stand out amongst other formats\n LONLAT: RegExp;\n };\n identifyErrors: (format: Format) => (arg: T, i: number) => ParseResults;\n identifyPieces: (half: string[]) => T;\n};\n\nconst axisTypeTest = (k: keyof typeof SYMBOL_PATTERNS, t: string) =>\n SYMBOL_PATTERNS[k].test(t) && k;\n\nexport const createParser =\n <T>(config: FormatParserConfig<T | undefined>) =>\n (format: Format, input: string) =>\n parseWithConfig(config, format, input);\n\nfunction parseWithConfig<T>(\n config: FormatParserConfig<T>,\n format: Format,\n input: string,\n): ParseResults {\n const [parseTokens, violations] = parse(input, format);\n\n const foundFormat = parseTokens.reduce(\n (acc, t) => acc + (axisTypeTest('LAT', t) || axisTypeTest('LON', t) || ''),\n '',\n );\n\n if (!violations.length && foundFormat && foundFormat !== format) {\n return [\n [],\n [\n violation(\n `Mismatched formats: \"${format}\" expected, \"${foundFormat}\" found.`,\n ),\n ],\n ];\n }\n\n const hasErrors = !parseTokens.length && violations.length;\n\n if (hasErrors) {\n return [[], violations];\n }\n\n const [tokens, errors] = [\n parseTokens.slice(0, parseTokens.indexOf(SYMBOLS.DIVIDER)), // left of divider\n parseTokens.slice(1 + parseTokens.indexOf(SYMBOLS.DIVIDER)), // right of divider\n ]\n .map(config.identifyPieces)\n .map(config.identifyErrors(format))\n .reduce((a, b) => [\n [...a[0], SYMBOLS.DIVIDER, ...b[0]],\n [...a[1], ...b[1]].map(violation),\n ]);\n\n if (errors.length) {\n return [\n [],\n // dedupe errors\n Array.from(new Set(errors)),\n ];\n }\n\n const matches = (config.formats[format].exec(tokens.join(' ')) || []).slice(\n 1,\n );\n\n if (!matches?.length) {\n throw new Error(\n [\n 'A validation check seems to be missing because the cleaned and normalized input value (',\n tokens.join(' '),\n ') is not matching the pattern (',\n config.formats[format],\n ').',\n ].join(''),\n );\n }\n\n return [matches, []];\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AA4BA,MAAM,gBAAgB,GAAiC,MACrD,gBAAgB,GAAG,KAAK,EAAE,IAAI
|
|
1
|
+
{"version":3,"file":"parse-format.js","names":[],"sources":["../../../../src/coordinates/latlon/internal/parse-format.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\nimport { type Format, SYMBOL_PATTERNS, SYMBOLS } from '.';\nimport { type ParseResults, parse } from './parse';\nimport { violation } from './violation';\n\ntype FormatParserConfig<T> = {\n formats: {\n // biome-ignore lint/style/useNamingConvention: Name is chosen to stand out amongst other formats\n LATLON: RegExp;\n // biome-ignore lint/style/useNamingConvention: Name is chosen to stand out amongst other formats\n LONLAT: RegExp;\n };\n identifyErrors: (format: Format) => (arg: T, i: number) => ParseResults;\n identifyPieces: (half: string[]) => T;\n};\n\nconst axisTypeTest = (k: keyof typeof SYMBOL_PATTERNS, t: string) =>\n SYMBOL_PATTERNS[k].test(t) && k;\n\n/**\n * Creates a coordinate parser function from a format parser configuration.\n *\n * @param config - Parser configuration with format patterns, error identification, and piece identification.\n * @returns Parser function that takes format and input string, returns ParseResults.\n *\n * @example\n * ```typescript\n * const parseDD = createParser({\n * formats: { LATLON: /pattern/, LONLAT: /pattern/ },\n * identifyErrors: (format) => (arg, i) => [tokens, errors],\n * identifyPieces: (half) => ({ deg, bear })\n * });\n * ```\n *\n * @example\n * ```typescript\n * const parser = createParser(config);\n * parser('LATLON', '37.7749° N / 122.4194° W');\n * // [[37.7749, -122.4194], []]\n * ```\n */\nexport const createParser =\n <T>(config: FormatParserConfig<T | undefined>) =>\n (format: Format, input: string) =>\n parseWithConfig(config, format, input);\n\n/**\n * Parses a coordinate string using the provided configuration.\n *\n * @param config - Parser configuration with format patterns and validation functions.\n * @param format - Expected coordinate format (LATLON or LONLAT).\n * @param input - Raw coordinate string to parse.\n * @returns Parse results with coordinate values or error messages.\n *\n * @example\n * ```typescript\n * parseWithConfig(config, 'LATLON', '37° N / 122° W');\n * // [['37', 'N', '122', 'W'], []]\n * ```\n *\n * @example\n * ```typescript\n * parseWithConfig(config, 'LATLON', '91° N / 122° W');\n * // [[], ['[ERROR] Degrees value (91) exceeds max value (90).']]\n * ```\n */\nfunction parseWithConfig<T>(\n config: FormatParserConfig<T>,\n format: Format,\n input: string,\n): ParseResults {\n const [parseTokens, violations] = parse(input, format);\n\n const foundFormat = parseTokens.reduce(\n (acc, t) => acc + (axisTypeTest('LAT', t) || axisTypeTest('LON', t) || ''),\n '',\n );\n\n if (!violations.length && foundFormat && foundFormat !== format) {\n return [\n [],\n [\n violation(\n `Mismatched formats: \"${format}\" expected, \"${foundFormat}\" found.`,\n ),\n ],\n ];\n }\n\n const hasErrors = !parseTokens.length && violations.length;\n\n if (hasErrors) {\n return [[], violations];\n }\n\n const [tokens, errors] = [\n parseTokens.slice(0, parseTokens.indexOf(SYMBOLS.DIVIDER)), // left of divider\n parseTokens.slice(1 + parseTokens.indexOf(SYMBOLS.DIVIDER)), // right of divider\n ]\n .map(config.identifyPieces)\n .map(config.identifyErrors(format))\n .reduce((a, b) => [\n [...a[0], SYMBOLS.DIVIDER, ...b[0]],\n [...a[1], ...b[1]].map(violation),\n ]);\n\n if (errors.length) {\n return [\n [],\n // dedupe errors\n Array.from(new Set(errors)),\n ];\n }\n\n const matches = (config.formats[format].exec(tokens.join(' ')) || []).slice(\n 1,\n );\n\n if (!matches?.length) {\n throw new Error(\n [\n 'A validation check seems to be missing because the cleaned and normalized input value (',\n tokens.join(' '),\n ') is not matching the pattern (',\n config.formats[format],\n ').',\n ].join(''),\n );\n }\n\n return [matches, []];\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AA4BA,MAAM,gBAAgB,GAAiC,MACrD,gBAAgB,GAAG,KAAK,EAAE,IAAI;;;;;;;;;;;;;;;;;;;;;;;AAwBhC,MAAa,gBACP,YACH,QAAgB,UACf,gBAAgB,QAAQ,QAAQ,MAAM;;;;;;;;;;;;;;;;;;;;;AAsB1C,SAAS,gBACP,QACA,QACA,OACc;CACd,MAAM,CAAC,aAAa,cAAc,MAAM,OAAO,OAAO;CAEtD,MAAM,cAAc,YAAY,QAC7B,KAAK,MAAM,OAAO,aAAa,OAAO,EAAE,IAAI,aAAa,OAAO,EAAE,IAAI,KACvE,GACD;AAED,KAAI,CAAC,WAAW,UAAU,eAAe,gBAAgB,OACvD,QAAO,CACL,EAAE,EACF,CACE,UACE,wBAAwB,OAAO,eAAe,YAAY,UAC3D,CACF,CACF;AAKH,KAFkB,CAAC,YAAY,UAAU,WAAW,OAGlD,QAAO,CAAC,EAAE,EAAE,WAAW;CAGzB,MAAM,CAAC,QAAQ,UAAU,CACvB,YAAY,MAAM,GAAG,YAAY,QAAQ,QAAQ,QAAQ,CAAC,EAC1D,YAAY,MAAM,IAAI,YAAY,QAAQ,QAAQ,QAAQ,CAAC,CAC5D,CACE,IAAI,OAAO,eAAe,CAC1B,IAAI,OAAO,eAAe,OAAO,CAAC,CAClC,QAAQ,GAAG,MAAM,CAChB;EAAC,GAAG,EAAE;EAAI,QAAQ;EAAS,GAAG,EAAE;EAAG,EACnC,CAAC,GAAG,EAAE,IAAI,GAAG,EAAE,GAAG,CAAC,IAAI,UAAU,CAClC,CAAC;AAEJ,KAAI,OAAO,OACT,QAAO,CACL,EAAE,EAEF,MAAM,KAAK,IAAI,IAAI,OAAO,CAAC,CAC5B;CAGH,MAAM,WAAW,OAAO,QAAQ,QAAQ,KAAK,OAAO,KAAK,IAAI,CAAC,IAAI,EAAE,EAAE,MACpE,EACD;AAED,KAAI,CAAC,SAAS,OACZ,OAAM,IAAI,MACR;EACE;EACA,OAAO,KAAK,IAAI;EAChB;EACA,OAAO,QAAQ;EACf;EACD,CAAC,KAAK,GAAG,CACX;AAGH,QAAO,CAAC,SAAS,EAAE,CAAC"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* Copyright
|
|
2
|
+
* Copyright 2026 Hypergiant Galactic Systems Inc. All rights reserved.
|
|
3
3
|
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
|
4
4
|
* you may not use this file except in compliance with the License. You may obtain a copy
|
|
5
5
|
* of the License at https://www.apache.org/licenses/LICENSE-2.0
|
|
@@ -32,8 +32,10 @@ type ParseResults = [Tokens, Errors];
|
|
|
32
32
|
* pure function
|
|
33
33
|
*
|
|
34
34
|
* @example
|
|
35
|
+
* ```typescript
|
|
35
36
|
* const input = '1 2 3 / 4 5 6'
|
|
36
37
|
* parse(input); // [['1', '2', '3', '/', '4', '5', '6'], []]
|
|
38
|
+
* ```
|
|
37
39
|
*
|
|
38
40
|
* @description
|
|
39
41
|
* __Assumptions/Specification__
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* Copyright
|
|
2
|
+
* Copyright 2026 Hypergiant Galactic Systems Inc. All rights reserved.
|
|
3
3
|
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
|
4
4
|
* you may not use this file except in compliance with the License. You may obtain a copy
|
|
5
5
|
* of the License at https://www.apache.org/licenses/LICENSE-2.0
|
|
@@ -11,9 +11,9 @@
|
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
|
|
14
|
+
import { violation } from "./violation.js";
|
|
14
15
|
import { lexer } from "./lexer.js";
|
|
15
16
|
import { pipesRunner } from "./pipes/index.js";
|
|
16
|
-
import { violation } from "./violation.js";
|
|
17
17
|
|
|
18
18
|
//#region src/coordinates/latlon/internal/parse.ts
|
|
19
19
|
/**
|
|
@@ -33,8 +33,10 @@ import { violation } from "./violation.js";
|
|
|
33
33
|
* pure function
|
|
34
34
|
*
|
|
35
35
|
* @example
|
|
36
|
+
* ```typescript
|
|
36
37
|
* const input = '1 2 3 / 4 5 6'
|
|
37
38
|
* parse(input); // [['1', '2', '3', '/', '4', '5', '6'], []]
|
|
39
|
+
* ```
|
|
38
40
|
*
|
|
39
41
|
* @description
|
|
40
42
|
* __Assumptions/Specification__
|
|
@@ -52,6 +54,7 @@ import { violation } from "./violation.js";
|
|
|
52
54
|
*/
|
|
53
55
|
function parse(input, format) {
|
|
54
56
|
if (!input?.length) return [[], [violation("No input.")]];
|
|
57
|
+
if (input.match(/(?<![a-z])[a-z](?![a-z])/gi)?.some((ch) => !/^[nsew]$/i.test(ch))) return [[], [violation("Unrecognized characters in input.")]];
|
|
55
58
|
const [tokens, errors] = pipesRunner(lexer(input), format);
|
|
56
59
|
return [tokens, errors?.length ? errors.map(violation) : []];
|
|
57
60
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parse.js","names":[],"sources":["../../../../src/coordinates/latlon/internal/parse.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\nimport { lexer, type Tokens } from './lexer';\nimport { pipesRunner } from './pipes';\nimport { violation } from './violation';\nimport type { Errors, Format } from '.';\n\nexport type ParseResults = [Tokens, Errors];\n\n/**\n * Parse a raw input string into a validated and normalized coordinate\n * primitive ready for further refinement/validation by a more specific parser.\n *\n * @param input\n * raw input, from a user or external system, of unknown validity\n *\n * @param format\n * the expected format - LATLON or LONLAT - the coordinate should conform to\n *\n * @returns\n * tuple with two values: tokens, and errors\n *\n * @remarks\n * pure function\n *\n * @example\n * const input = '1 2 3 / 4 5 6'\n * parse(input); // [['1', '2', '3', '/', '4', '5', '6'], []]\n *\n * @description\n * __Assumptions/Specification__\n * 1. Decimals are indicated by a \".\" and not \",\"\n * 2. A degrees indicator is \"°\"\n * 3. A minutes indicator is \"'\"\n * 4. A seconds indicator is '\"'\n * 5. Each indicator is expected to follow the number it is annotating\n * 6. Numeric parts - degrees, minutes, and seconds - are positional and can\n * not be arranged in alternative orders and be considered valid\n * 7. Output will have explicit bearings characters (NSEW) and not rely on\n * positive and negative values; when bearings are available. Negative\n * values will only be replaced with absolute values if bearings are available.\n *\n */\nexport function parse(input: string, format?: Format): ParseResults {\n if (!input?.length) {\n return [[], [violation('No input.')]];\n }\n\n const [tokens, errors] = pipesRunner(lexer(input), format);\n\n return [tokens, errors?.length ? errors.map(violation) : []];\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"parse.js","names":[],"sources":["../../../../src/coordinates/latlon/internal/parse.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\nimport { lexer, type Tokens } from './lexer';\nimport { pipesRunner } from './pipes';\nimport { violation } from './violation';\nimport type { Errors, Format } from '.';\n\nexport type ParseResults = [Tokens, Errors];\n\n/**\n * Parse a raw input string into a validated and normalized coordinate\n * primitive ready for further refinement/validation by a more specific parser.\n *\n * @param input\n * raw input, from a user or external system, of unknown validity\n *\n * @param format\n * the expected format - LATLON or LONLAT - the coordinate should conform to\n *\n * @returns\n * tuple with two values: tokens, and errors\n *\n * @remarks\n * pure function\n *\n * @example\n * ```typescript\n * const input = '1 2 3 / 4 5 6'\n * parse(input); // [['1', '2', '3', '/', '4', '5', '6'], []]\n * ```\n *\n * @description\n * __Assumptions/Specification__\n * 1. Decimals are indicated by a \".\" and not \",\"\n * 2. A degrees indicator is \"°\"\n * 3. A minutes indicator is \"'\"\n * 4. A seconds indicator is '\"'\n * 5. Each indicator is expected to follow the number it is annotating\n * 6. Numeric parts - degrees, minutes, and seconds - are positional and can\n * not be arranged in alternative orders and be considered valid\n * 7. Output will have explicit bearings characters (NSEW) and not rely on\n * positive and negative values; when bearings are available. Negative\n * values will only be replaced with absolute values if bearings are available.\n *\n */\nexport function parse(input: string, format?: Format): ParseResults {\n if (!input?.length) {\n return [[], [violation('No input.')]];\n }\n\n // Find isolated letters (not part of a larger word)\n const isolatedLetters = input.match(/(?<![a-z])[a-z](?![a-z])/gi);\n\n // Reject if any isolated letter is not a valid cardinal direction (N, S, E, W)\n if (isolatedLetters?.some((ch) => !/^[nsew]$/i.test(ch))) {\n return [[], [violation('Unrecognized characters in input.')]];\n }\n\n const [tokens, errors] = pipesRunner(lexer(input), format);\n\n return [tokens, errors?.length ? errors.map(violation) : []];\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwDA,SAAgB,MAAM,OAAe,QAA+B;AAClE,KAAI,CAAC,OAAO,OACV,QAAO,CAAC,EAAE,EAAE,CAAC,UAAU,YAAY,CAAC,CAAC;AAOvC,KAHwB,MAAM,MAAM,6BAA6B,EAG5C,MAAM,OAAO,CAAC,YAAY,KAAK,GAAG,CAAC,CACtD,QAAO,CAAC,EAAE,EAAE,CAAC,UAAU,oCAAoC,CAAC,CAAC;CAG/D,MAAM,CAAC,QAAQ,UAAU,YAAY,MAAM,MAAM,EAAE,OAAO;AAE1D,QAAO,CAAC,QAAQ,QAAQ,SAAS,OAAO,IAAI,UAAU,GAAG,EAAE,CAAC"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* Copyright
|
|
2
|
+
* Copyright 2026 Hypergiant Galactic Systems Inc. All rights reserved.
|
|
3
3
|
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
|
4
4
|
* you may not use this file except in compliance with the License. You may obtain a copy
|
|
5
5
|
* of the License at https://www.apache.org/licenses/LICENSE-2.0
|
|
@@ -14,9 +14,26 @@ import { Format } from "../index.js";
|
|
|
14
14
|
import { Tokens } from "../lexer.js";
|
|
15
15
|
|
|
16
16
|
//#region src/coordinates/latlon/internal/pipes/check-ambiguous.d.ts
|
|
17
|
+
|
|
17
18
|
/**
|
|
18
19
|
* Look for groupings of numbers that are ambiguous; no indicators, or no
|
|
19
20
|
* dividers and not possibility of deducing where a divider should be inserted.
|
|
21
|
+
*
|
|
22
|
+
* @param tokens - Array of parsed coordinate tokens.
|
|
23
|
+
* @param _format - Optional coordinate format (LATLON or LONLAT), currently unused.
|
|
24
|
+
* @returns Pipe result with tokens and error status (true if ambiguous, false otherwise).
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```typescript
|
|
28
|
+
* checkAmbiguousGrouping(['45', '30', '15', 'N'], 'LATLON');
|
|
29
|
+
* // Returns tokens with error=true if grouping is ambiguous
|
|
30
|
+
* ```
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* ```typescript
|
|
34
|
+
* checkAmbiguousGrouping(['45', '30', 'N', '/', '122', '15', 'W'], 'LATLON');
|
|
35
|
+
* // Returns tokens with error=false (divider present, not ambiguous)
|
|
36
|
+
* ```
|
|
20
37
|
*/
|
|
21
38
|
declare function checkAmbiguousGrouping(tokens: Tokens, _format?: Format): [string[], string | boolean];
|
|
22
39
|
//#endregion
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* Copyright
|
|
2
|
+
* Copyright 2026 Hypergiant Galactic Systems Inc. All rights reserved.
|
|
3
3
|
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
|
4
4
|
* you may not use this file except in compliance with the License. You may obtain a copy
|
|
5
5
|
* of the License at https://www.apache.org/licenses/LICENSE-2.0
|
|
@@ -19,6 +19,22 @@ import { pipesResult } from "./index.js";
|
|
|
19
19
|
/**
|
|
20
20
|
* Look for groupings of numbers that are ambiguous; no indicators, or no
|
|
21
21
|
* dividers and not possibility of deducing where a divider should be inserted.
|
|
22
|
+
*
|
|
23
|
+
* @param tokens - Array of parsed coordinate tokens.
|
|
24
|
+
* @param _format - Optional coordinate format (LATLON or LONLAT), currently unused.
|
|
25
|
+
* @returns Pipe result with tokens and error status (true if ambiguous, false otherwise).
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```typescript
|
|
29
|
+
* checkAmbiguousGrouping(['45', '30', '15', 'N'], 'LATLON');
|
|
30
|
+
* // Returns tokens with error=true if grouping is ambiguous
|
|
31
|
+
* ```
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* ```typescript
|
|
35
|
+
* checkAmbiguousGrouping(['45', '30', 'N', '/', '122', '15', 'W'], 'LATLON');
|
|
36
|
+
* // Returns tokens with error=false (divider present, not ambiguous)
|
|
37
|
+
* ```
|
|
22
38
|
*/
|
|
23
39
|
function checkAmbiguousGrouping(tokens, _format) {
|
|
24
40
|
if (tokens.includes(SYMBOLS.DIVIDER)) return pipesResult(tokens, false);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"check-ambiguous.js","names":[],"sources":["../../../../../src/coordinates/latlon/internal/pipes/check-ambiguous.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\nimport { type Format, SYMBOL_PATTERNS, SYMBOLS } from '..';\nimport { pipesResult } from '../pipes';\nimport { simpler } from './simpler';\nimport type { Tokens } from '../lexer';\n\n/**\n * Look for groupings of numbers that are ambiguous; no indicators, or no\n * dividers and not possibility of deducing where a divider should be inserted.\n */\nexport function checkAmbiguousGrouping(tokens: Tokens, _format?: Format) {\n if (tokens.includes(SYMBOLS.DIVIDER)) {\n return pipesResult(tokens, false);\n }\n\n const simple = simpler(tokens);\n\n // 3-5 numbers after or before a single bearing indicator BNNN, NNNB\n const ambiguous = /^(?:(?:BN{3,5})|(?:N{3,5}B))$/.test(simple);\n // 3-5 numbers between 2 bearings indicators BNNNB\n const bookends = /^BN{3,5}B$/.test(simple);\n // \"helpful\" indicators:\n // 1. degrees number in the right-of-divider position e.g. # #°\n // 2. seconds number in the left-of-divider position e.g. # #\" #\n // 3. trailing bearings in the left-of-divider position e.g. # N #\n // 4. leading bearings in the right-of-divider position e.g. N # E #\n const helpful = tokens\n // helpful tokens are only helpful in the middle of the token list\n // not the beginning or end of the list\n .slice(1, -1)\n .reduce((acc, t) => {\n const helps =\n t.includes(SYMBOLS.DEGREES) ||\n t.includes(SYMBOLS.SECONDS) ||\n SYMBOL_PATTERNS.NSEW.test(t);\n\n return `${acc}${helps ? 'H' : '_'}`;\n }, '')\n .includes('H');\n // 3-5 numbers with no bearings indicators, and no helpful indicators\n const hopeless = /^N{3,5}$/.test(simple) && !helpful;\n\n return pipesResult(tokens, ambiguous || bookends || hopeless);\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"check-ambiguous.js","names":[],"sources":["../../../../../src/coordinates/latlon/internal/pipes/check-ambiguous.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\nimport { type Format, SYMBOL_PATTERNS, SYMBOLS } from '..';\nimport { pipesResult } from '../pipes';\nimport { simpler } from './simpler';\nimport type { Tokens } from '../lexer';\n\n/**\n * Look for groupings of numbers that are ambiguous; no indicators, or no\n * dividers and not possibility of deducing where a divider should be inserted.\n *\n * @param tokens - Array of parsed coordinate tokens.\n * @param _format - Optional coordinate format (LATLON or LONLAT), currently unused.\n * @returns Pipe result with tokens and error status (true if ambiguous, false otherwise).\n *\n * @example\n * ```typescript\n * checkAmbiguousGrouping(['45', '30', '15', 'N'], 'LATLON');\n * // Returns tokens with error=true if grouping is ambiguous\n * ```\n *\n * @example\n * ```typescript\n * checkAmbiguousGrouping(['45', '30', 'N', '/', '122', '15', 'W'], 'LATLON');\n * // Returns tokens with error=false (divider present, not ambiguous)\n * ```\n */\nexport function checkAmbiguousGrouping(tokens: Tokens, _format?: Format) {\n if (tokens.includes(SYMBOLS.DIVIDER)) {\n return pipesResult(tokens, false);\n }\n\n const simple = simpler(tokens);\n\n // 3-5 numbers after or before a single bearing indicator BNNN, NNNB\n const ambiguous = /^(?:(?:BN{3,5})|(?:N{3,5}B))$/.test(simple);\n // 3-5 numbers between 2 bearings indicators BNNNB\n const bookends = /^BN{3,5}B$/.test(simple);\n // \"helpful\" indicators:\n // 1. degrees number in the right-of-divider position e.g. # #°\n // 2. seconds number in the left-of-divider position e.g. # #\" #\n // 3. trailing bearings in the left-of-divider position e.g. # N #\n // 4. leading bearings in the right-of-divider position e.g. N # E #\n const helpful = tokens\n // helpful tokens are only helpful in the middle of the token list\n // not the beginning or end of the list\n .slice(1, -1)\n .reduce((acc, t) => {\n const helps =\n t.includes(SYMBOLS.DEGREES) ||\n t.includes(SYMBOLS.SECONDS) ||\n SYMBOL_PATTERNS.NSEW.test(t);\n\n return `${acc}${helps ? 'H' : '_'}`;\n }, '')\n .includes('H');\n // 3-5 numbers with no bearings indicators, and no helpful indicators\n const hopeless = /^N{3,5}$/.test(simple) && !helpful;\n\n return pipesResult(tokens, ambiguous || bookends || hopeless);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsCA,SAAgB,uBAAuB,QAAgB,SAAkB;AACvE,KAAI,OAAO,SAAS,QAAQ,QAAQ,CAClC,QAAO,YAAY,QAAQ,MAAM;CAGnC,MAAM,SAAS,QAAQ,OAAO;CAG9B,MAAM,YAAY,gCAAgC,KAAK,OAAO;CAE9D,MAAM,WAAW,aAAa,KAAK,OAAO;CAM1C,MAAM,UAAU,OAGb,MAAM,GAAG,GAAG,CACZ,QAAQ,KAAK,MAAM;AAMlB,SAAO,GAAG,MAJR,EAAE,SAAS,QAAQ,QAAQ,IAC3B,EAAE,SAAS,QAAQ,QAAQ,IAC3B,gBAAgB,KAAK,KAAK,EAAE,GAEN,MAAM;IAC7B,GAAG,CACL,SAAS,IAAI;CAEhB,MAAM,WAAW,WAAW,KAAK,OAAO,IAAI,CAAC;AAE7C,QAAO,YAAY,QAAQ,aAAa,YAAY,SAAS"}
|