@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
@@ -95,11 +95,39 @@ const systems = [
95
95
  /**
96
96
  * A collection of input strings each with exactly one error in a unique
97
97
  * position for each format (LATLON and LONLAT) in each system (DD, DDM, DMS).
98
+ *
99
+ * Used for comprehensive error validation testing. Each entry contains coordinate
100
+ * strings with systematic errors across different notation systems.
101
+ *
102
+ * @example
103
+ * ```typescript
104
+ * EXHAUSTIVE_ERRORS.DD.LATLON;
105
+ * // Array of decimal degrees strings with errors like '91 N / 179 E'
106
+ * ```
107
+ *
108
+ * @example
109
+ * ```typescript
110
+ * EXHAUSTIVE_ERRORS.DMS.LONLAT;
111
+ * // Array of degrees-minutes-seconds strings with errors
112
+ * ```
98
113
  */
99
114
  const EXHAUSTIVE_ERRORS = Object.fromEntries(systems.map(({ designation, ...system }) => {
100
115
  const options = ["LAT LON", "LON LAT"].map((format) => [format.replace(" ", ""), cartesian(...format.split(" ").map((key) => system[key])).map((pair) => pair.join(" / ")).flatMap((t) => fillTemplate(t, values))]);
101
116
  return [designation, Object.fromEntries(options)];
102
117
  }));
118
+ /**
119
+ * Fills a coordinate template with test values, generating variations with errors.
120
+ *
121
+ * @param template - Template string with placeholders like 'DLAT MM BLAT / DLON MM BLON'.
122
+ * @param values - Object containing valid and invalid test values for each placeholder.
123
+ * @returns Array of coordinate strings with systematic errors injected.
124
+ *
125
+ * @example
126
+ * ```typescript
127
+ * fillTemplate('DLAT MM BLAT / DLON MM BLON', values);
128
+ * // ['91 30 N / 179 30 E', 'nope 30 N / 179 30 E', ...]
129
+ * ```
130
+ */
103
131
  function fillTemplate(template, values$1) {
104
132
  return template.split(" ").flatMap((key, i, original) => {
105
133
  if (!values$1.invalid[key]) return "";
@@ -1 +1 @@
1
- {"version":3,"file":"exhaustive-errors.js","names":["values: Values","values"],"sources":["../../../../src/coordinates/latlon/internal/exhaustive-errors.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 { cartesian } from '@/cartesian';\n\ntype Values = {\n invalid: Record<string, string[]>;\n valid: Record<string, string>;\n};\n\nconst values: Values = {\n invalid: {\n BLAT: ['X', 'random garbage'],\n BLON: ['X', 'random garbage'],\n DLAT: ['91', 'nope'],\n DDLAT: ['90.1', 'nope'],\n DLON: ['181', 'nope'],\n DDLON: ['180.1', 'nope'],\n M: ['-1', '61', 'nope'],\n MM: ['-0.1', '60.1', 'nope'],\n SS: ['-0.1', '60.1', 'nope'],\n },\n valid: {\n '/': '/',\n BLAT: 'N',\n BLON: 'E',\n DLAT: '89',\n DDLAT: '89.999999999',\n DLON: '179',\n DDLON: '179.999999999',\n M: '59',\n MM: '59.999999999',\n SS: '59.999999999',\n },\n};\n\nconst systems = [\n {\n designation: 'DD',\n LAT: ['DDLAT', 'BLAT DDLAT', 'DDLAT BLAT'],\n LON: ['DDLON', 'BLON DDLON', 'DDLON BLON'],\n },\n {\n designation: 'DDM',\n LAT: ['DLAT MM', 'BLAT DLAT MM', 'DLAT MM BLAT'],\n LON: ['DLON MM', 'BLON DLON MM', 'DLON MM BLON'],\n },\n {\n designation: 'DMS',\n LAT: ['DLAT M SS', 'BLAT DLAT M SS', 'DLAT M SS BLAT'],\n LON: ['DLON M SS', 'BLON DLON M SS', 'DLON M SS BLON'],\n },\n];\n\n/**\n * A collection of input strings each with exactly one error in a unique\n * position for each format (LATLON and LONLAT) in each system (DD, DDM, DMS).\n */\nexport const EXHAUSTIVE_ERRORS = Object.fromEntries(\n systems.map(({ designation, ...system }) => {\n // for both format options\n const options = ['LAT LON', 'LON LAT'].map((format) => [\n // create object key: 'LATLON' or 'LONLAT'\n format.replace(' ', ''),\n\n // cross-join each variation of LAT with each variation of LON in the system\n cartesian(\n ...format.split(' ').map((key) => system[key as keyof typeof system]),\n )\n // input not including this isn't an error so no need for variation\n .map((pair) => pair.join(' / '))\n // fill the generated template with actual values\n .flatMap((t) => fillTemplate(t, values)),\n ]);\n\n return [designation, Object.fromEntries(options)];\n }),\n);\n\nfunction fillTemplate(template: string, values: Values) {\n return template\n .split(' ')\n .flatMap((key, i, original) => {\n if (!values.invalid[key]) {\n return '';\n }\n\n return (values.invalid[key] as string[]).map((opt) =>\n [...original.slice(0, i), opt, ...original.slice(i + 1)]\n .map((token) => (token in values.valid ? values.valid[token] : token))\n .join(' '),\n );\n })\n .filter(Boolean);\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAoBA,MAAMA,SAAiB;CACrB,SAAS;EACP,MAAM,CAAC,KAAK,iBAAiB;EAC7B,MAAM,CAAC,KAAK,iBAAiB;EAC7B,MAAM,CAAC,MAAM,OAAO;EACpB,OAAO,CAAC,QAAQ,OAAO;EACvB,MAAM,CAAC,OAAO,OAAO;EACrB,OAAO,CAAC,SAAS,OAAO;EACxB,GAAG;GAAC;GAAM;GAAM;GAAO;EACvB,IAAI;GAAC;GAAQ;GAAQ;GAAO;EAC5B,IAAI;GAAC;GAAQ;GAAQ;GAAO;EAC7B;CACD,OAAO;EACL,KAAK;EACL,MAAM;EACN,MAAM;EACN,MAAM;EACN,OAAO;EACP,MAAM;EACN,OAAO;EACP,GAAG;EACH,IAAI;EACJ,IAAI;EACL;CACF;AAED,MAAM,UAAU;CACd;EACE,aAAa;EACb,KAAK;GAAC;GAAS;GAAc;GAAa;EAC1C,KAAK;GAAC;GAAS;GAAc;GAAa;EAC3C;CACD;EACE,aAAa;EACb,KAAK;GAAC;GAAW;GAAgB;GAAe;EAChD,KAAK;GAAC;GAAW;GAAgB;GAAe;EACjD;CACD;EACE,aAAa;EACb,KAAK;GAAC;GAAa;GAAkB;GAAiB;EACtD,KAAK;GAAC;GAAa;GAAkB;GAAiB;EACvD;CACF;;;;;AAMD,MAAa,oBAAoB,OAAO,YACtC,QAAQ,KAAK,EAAE,aAAa,GAAG,aAAa;CAE1C,MAAM,UAAU,CAAC,WAAW,UAAU,CAAC,KAAK,WAAW,CAErD,OAAO,QAAQ,KAAK,GAAG,EAGvB,UACE,GAAG,OAAO,MAAM,IAAI,CAAC,KAAK,QAAQ,OAAO,KAA4B,CACtE,CAEE,KAAK,SAAS,KAAK,KAAK,MAAM,CAAC,CAE/B,SAAS,MAAM,aAAa,GAAG,OAAO,CAAC,CAC3C,CAAC;AAEF,QAAO,CAAC,aAAa,OAAO,YAAY,QAAQ,CAAC;EACjD,CACH;AAED,SAAS,aAAa,UAAkB,UAAgB;AACtD,QAAO,SACJ,MAAM,IAAI,CACV,SAAS,KAAK,GAAG,aAAa;AAC7B,MAAI,CAACC,SAAO,QAAQ,KAClB,QAAO;AAGT,SAAQA,SAAO,QAAQ,KAAkB,KAAK,QAC5C;GAAC,GAAG,SAAS,MAAM,GAAG,EAAE;GAAE;GAAK,GAAG,SAAS,MAAM,IAAI,EAAE;GAAC,CACrD,KAAK,UAAW,SAASA,SAAO,QAAQA,SAAO,MAAM,SAAS,MAAO,CACrE,KAAK,IAAI,CACb;GACD,CACD,OAAO,QAAQ"}
1
+ {"version":3,"file":"exhaustive-errors.js","names":["values: Values","values"],"sources":["../../../../src/coordinates/latlon/internal/exhaustive-errors.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 { cartesian } from '@/cartesian';\n\ntype Values = {\n invalid: Record<string, string[]>;\n valid: Record<string, string>;\n};\n\nconst values: Values = {\n invalid: {\n BLAT: ['X', 'random garbage'],\n BLON: ['X', 'random garbage'],\n DLAT: ['91', 'nope'],\n DDLAT: ['90.1', 'nope'],\n DLON: ['181', 'nope'],\n DDLON: ['180.1', 'nope'],\n M: ['-1', '61', 'nope'],\n MM: ['-0.1', '60.1', 'nope'],\n SS: ['-0.1', '60.1', 'nope'],\n },\n valid: {\n '/': '/',\n BLAT: 'N',\n BLON: 'E',\n DLAT: '89',\n DDLAT: '89.999999999',\n DLON: '179',\n DDLON: '179.999999999',\n M: '59',\n MM: '59.999999999',\n SS: '59.999999999',\n },\n};\n\nconst systems = [\n {\n designation: 'DD',\n LAT: ['DDLAT', 'BLAT DDLAT', 'DDLAT BLAT'],\n LON: ['DDLON', 'BLON DDLON', 'DDLON BLON'],\n },\n {\n designation: 'DDM',\n LAT: ['DLAT MM', 'BLAT DLAT MM', 'DLAT MM BLAT'],\n LON: ['DLON MM', 'BLON DLON MM', 'DLON MM BLON'],\n },\n {\n designation: 'DMS',\n LAT: ['DLAT M SS', 'BLAT DLAT M SS', 'DLAT M SS BLAT'],\n LON: ['DLON M SS', 'BLON DLON M SS', 'DLON M SS BLON'],\n },\n];\n\n/**\n * A collection of input strings each with exactly one error in a unique\n * position for each format (LATLON and LONLAT) in each system (DD, DDM, DMS).\n *\n * Used for comprehensive error validation testing. Each entry contains coordinate\n * strings with systematic errors across different notation systems.\n *\n * @example\n * ```typescript\n * EXHAUSTIVE_ERRORS.DD.LATLON;\n * // Array of decimal degrees strings with errors like '91 N / 179 E'\n * ```\n *\n * @example\n * ```typescript\n * EXHAUSTIVE_ERRORS.DMS.LONLAT;\n * // Array of degrees-minutes-seconds strings with errors\n * ```\n */\nexport const EXHAUSTIVE_ERRORS = Object.fromEntries(\n systems.map(({ designation, ...system }) => {\n // for both format options\n const options = ['LAT LON', 'LON LAT'].map((format) => [\n // create object key: 'LATLON' or 'LONLAT'\n format.replace(' ', ''),\n\n // cross-join each variation of LAT with each variation of LON in the system\n cartesian(\n ...format.split(' ').map((key) => system[key as keyof typeof system]),\n )\n // input not including this isn't an error so no need for variation\n .map((pair) => pair.join(' / '))\n // fill the generated template with actual values\n .flatMap((t) => fillTemplate(t, values)),\n ]);\n\n return [designation, Object.fromEntries(options)];\n }),\n);\n\n/**\n * Fills a coordinate template with test values, generating variations with errors.\n *\n * @param template - Template string with placeholders like 'DLAT MM BLAT / DLON MM BLON'.\n * @param values - Object containing valid and invalid test values for each placeholder.\n * @returns Array of coordinate strings with systematic errors injected.\n *\n * @example\n * ```typescript\n * fillTemplate('DLAT MM BLAT / DLON MM BLON', values);\n * // ['91 30 N / 179 30 E', 'nope 30 N / 179 30 E', ...]\n * ```\n */\nfunction fillTemplate(template: string, values: Values) {\n return template\n .split(' ')\n .flatMap((key, i, original) => {\n if (!values.invalid[key]) {\n return '';\n }\n\n return (values.invalid[key] as string[]).map((opt) =>\n [...original.slice(0, i), opt, ...original.slice(i + 1)]\n .map((token) => (token in values.valid ? values.valid[token] : token))\n .join(' '),\n );\n })\n .filter(Boolean);\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAoBA,MAAMA,SAAiB;CACrB,SAAS;EACP,MAAM,CAAC,KAAK,iBAAiB;EAC7B,MAAM,CAAC,KAAK,iBAAiB;EAC7B,MAAM,CAAC,MAAM,OAAO;EACpB,OAAO,CAAC,QAAQ,OAAO;EACvB,MAAM,CAAC,OAAO,OAAO;EACrB,OAAO,CAAC,SAAS,OAAO;EACxB,GAAG;GAAC;GAAM;GAAM;GAAO;EACvB,IAAI;GAAC;GAAQ;GAAQ;GAAO;EAC5B,IAAI;GAAC;GAAQ;GAAQ;GAAO;EAC7B;CACD,OAAO;EACL,KAAK;EACL,MAAM;EACN,MAAM;EACN,MAAM;EACN,OAAO;EACP,MAAM;EACN,OAAO;EACP,GAAG;EACH,IAAI;EACJ,IAAI;EACL;CACF;AAED,MAAM,UAAU;CACd;EACE,aAAa;EACb,KAAK;GAAC;GAAS;GAAc;GAAa;EAC1C,KAAK;GAAC;GAAS;GAAc;GAAa;EAC3C;CACD;EACE,aAAa;EACb,KAAK;GAAC;GAAW;GAAgB;GAAe;EAChD,KAAK;GAAC;GAAW;GAAgB;GAAe;EACjD;CACD;EACE,aAAa;EACb,KAAK;GAAC;GAAa;GAAkB;GAAiB;EACtD,KAAK;GAAC;GAAa;GAAkB;GAAiB;EACvD;CACF;;;;;;;;;;;;;;;;;;;;AAqBD,MAAa,oBAAoB,OAAO,YACtC,QAAQ,KAAK,EAAE,aAAa,GAAG,aAAa;CAE1C,MAAM,UAAU,CAAC,WAAW,UAAU,CAAC,KAAK,WAAW,CAErD,OAAO,QAAQ,KAAK,GAAG,EAGvB,UACE,GAAG,OAAO,MAAM,IAAI,CAAC,KAAK,QAAQ,OAAO,KAA4B,CACtE,CAEE,KAAK,SAAS,KAAK,KAAK,MAAM,CAAC,CAE/B,SAAS,MAAM,aAAa,GAAG,OAAO,CAAC,CAC3C,CAAC;AAEF,QAAO,CAAC,aAAa,OAAO,YAAY,QAAQ,CAAC;EACjD,CACH;;;;;;;;;;;;;;AAeD,SAAS,aAAa,UAAkB,UAAgB;AACtD,QAAO,SACJ,MAAM,IAAI,CACV,SAAS,KAAK,GAAG,aAAa;AAC7B,MAAI,CAACC,SAAO,QAAQ,KAClB,QAAO;AAGT,SAAQA,SAAO,QAAQ,KAAkB,KAAK,QAC5C;GAAC,GAAG,SAAS,MAAM,GAAG,EAAE;GAAE;GAAK,GAAG,SAAS,MAAM,IAAI,EAAE;GAAC,CACrD,KAAK,UAAW,SAASA,SAAO,QAAQA,SAAO,MAAM,SAAS,MAAO,CACrE,KAAK,IAAI,CACb;GACD,CACD,OAAO,QAAQ"}
@@ -5,6 +5,26 @@ interface FormatOptions {
5
5
  separator: string;
6
6
  withOrdinal?: boolean;
7
7
  }
8
+ /**
9
+ * Creates a coordinate formatter function from a coordinate conversion function.
10
+ *
11
+ * @param fn - Function that converts a single coordinate value to a formatted string.
12
+ * @returns Formatter function that takes coordinate pair and optional config.
13
+ *
14
+ * @example
15
+ * ```typescript
16
+ * const formatDD = createFormatter((num) => `${num.toFixed(6)}°`);
17
+ * formatDD([37.7749, -122.4194]);
18
+ * // '37.774900° N, 122.419400° W'
19
+ * ```
20
+ *
21
+ * @example
22
+ * ```typescript
23
+ * const formatDMS = createFormatter(toDegreesMinutesSeconds);
24
+ * formatDMS([37.7749, -122.4194], { separator: ' / ', withOrdinal: true });
25
+ * // '37° 46' 29.64″ N / 122° 25' 9.84″ W'
26
+ * ```
27
+ */
8
28
  declare const createFormatter: (fn: (coord: number, withOrdinal?: boolean) => string) => (coordinates: [number, number], config?: FormatOptions) => string;
9
29
  //#endregion
10
30
  export { FormatOptions, createFormatter };
@@ -14,6 +14,26 @@
14
14
  import { getOrdinal } from "./ordinal.js";
15
15
 
16
16
  //#region src/coordinates/latlon/internal/format.ts
17
+ /**
18
+ * Creates a coordinate formatter function from a coordinate conversion function.
19
+ *
20
+ * @param fn - Function that converts a single coordinate value to a formatted string.
21
+ * @returns Formatter function that takes coordinate pair and optional config.
22
+ *
23
+ * @example
24
+ * ```typescript
25
+ * const formatDD = createFormatter((num) => `${num.toFixed(6)}°`);
26
+ * formatDD([37.7749, -122.4194]);
27
+ * // '37.774900° N, 122.419400° W'
28
+ * ```
29
+ *
30
+ * @example
31
+ * ```typescript
32
+ * const formatDMS = createFormatter(toDegreesMinutesSeconds);
33
+ * formatDMS([37.7749, -122.4194], { separator: ' / ', withOrdinal: true });
34
+ * // '37° 46' 29.64″ N / 122° 25' 9.84″ W'
35
+ * ```
36
+ */
17
37
  const createFormatter = (fn) => (coordinates, config) => {
18
38
  const [latitude, longitude] = coordinates;
19
39
  const latOrdinal = `${config?.withOrdinal ? ` ${getOrdinal(latitude, true)}` : ""}`;
@@ -1 +1 @@
1
- {"version":3,"file":"format.js","names":[],"sources":["../../../../src/coordinates/latlon/internal/format.ts"],"sourcesContent":["/*\n * Copyright 2025 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\nimport { getOrdinal } from './ordinal';\n\nexport interface FormatOptions {\n prefix: string;\n suffix: string;\n separator: string;\n withOrdinal?: boolean;\n}\n\nexport const createFormatter =\n (fn: (coord: number, withOrdinal?: boolean) => string) =>\n (coordinates: [number, number], config?: FormatOptions): string => {\n const [latitude, longitude] = coordinates;\n const latOrdinal = `${config?.withOrdinal ? ` ${getOrdinal(latitude, true)}` : ''}`;\n const lonOrdinal = `${config?.withOrdinal ? ` ${getOrdinal(longitude, false)}` : ''}`;\n const lat = fn(latitude, config?.withOrdinal);\n const lon = fn(longitude, config?.withOrdinal);\n const prefix = config?.prefix ?? '';\n const suffix = config?.suffix ?? '';\n const separator = config?.separator ?? ', ';\n\n return `${prefix}${lat}${latOrdinal}${separator}${lon}${lonOrdinal}${suffix}`;\n };\n"],"mappings":";;;;;;;;;;;;;;;;AAuBA,MAAa,mBACV,QACA,aAA+B,WAAmC;CACjE,MAAM,CAAC,UAAU,aAAa;CAC9B,MAAM,aAAa,GAAG,QAAQ,cAAc,IAAI,WAAW,UAAU,KAAK,KAAK;CAC/E,MAAM,aAAa,GAAG,QAAQ,cAAc,IAAI,WAAW,WAAW,MAAM,KAAK;CACjF,MAAM,MAAM,GAAG,UAAU,QAAQ,YAAY;CAC7C,MAAM,MAAM,GAAG,WAAW,QAAQ,YAAY;CAC9C,MAAM,SAAS,QAAQ,UAAU;CACjC,MAAM,SAAS,QAAQ,UAAU;AAGjC,QAAO,GAAG,SAAS,MAAM,aAFP,QAAQ,aAAa,OAEW,MAAM,aAAa"}
1
+ {"version":3,"file":"format.js","names":[],"sources":["../../../../src/coordinates/latlon/internal/format.ts"],"sourcesContent":["/*\n * Copyright 2025 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\nimport { getOrdinal } from './ordinal';\n\nexport interface FormatOptions {\n prefix: string;\n suffix: string;\n separator: string;\n withOrdinal?: boolean;\n}\n\n/**\n * Creates a coordinate formatter function from a coordinate conversion function.\n *\n * @param fn - Function that converts a single coordinate value to a formatted string.\n * @returns Formatter function that takes coordinate pair and optional config.\n *\n * @example\n * ```typescript\n * const formatDD = createFormatter((num) => `${num.toFixed(6)}°`);\n * formatDD([37.7749, -122.4194]);\n * // '37.774900° N, 122.419400° W'\n * ```\n *\n * @example\n * ```typescript\n * const formatDMS = createFormatter(toDegreesMinutesSeconds);\n * formatDMS([37.7749, -122.4194], { separator: ' / ', withOrdinal: true });\n * // '37° 46' 29.64″ N / 122° 25' 9.84″ W'\n * ```\n */\nexport const createFormatter =\n (fn: (coord: number, withOrdinal?: boolean) => string) =>\n (coordinates: [number, number], config?: FormatOptions): string => {\n const [latitude, longitude] = coordinates;\n const latOrdinal = `${config?.withOrdinal ? ` ${getOrdinal(latitude, true)}` : ''}`;\n const lonOrdinal = `${config?.withOrdinal ? ` ${getOrdinal(longitude, false)}` : ''}`;\n const lat = fn(latitude, config?.withOrdinal);\n const lon = fn(longitude, config?.withOrdinal);\n const prefix = config?.prefix ?? '';\n const suffix = config?.suffix ?? '';\n const separator = config?.separator ?? ', ';\n\n return `${prefix}${lat}${latOrdinal}${separator}${lon}${lonOrdinal}${suffix}`;\n };\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2CA,MAAa,mBACV,QACA,aAA+B,WAAmC;CACjE,MAAM,CAAC,UAAU,aAAa;CAC9B,MAAM,aAAa,GAAG,QAAQ,cAAc,IAAI,WAAW,UAAU,KAAK,KAAK;CAC/E,MAAM,aAAa,GAAG,QAAQ,cAAc,IAAI,WAAW,WAAW,MAAM,KAAK;CACjF,MAAM,MAAM,GAAG,UAAU,QAAQ,YAAY;CAC7C,MAAM,MAAM,GAAG,WAAW,QAAQ,YAAY;CAC9C,MAAM,SAAS,QAAQ,UAAU;CACjC,MAAM,SAAS,QAAQ,UAAU;AAGjC,QAAO,GAAG,SAAS,MAAM,aAFP,QAAQ,aAAa,OAEW,MAAM,aAAa"}
@@ -2,6 +2,29 @@
2
2
  /**
3
3
  * Return an error string if the value is outside the range where the limits
4
4
  * are 0-limit.
5
+ *
6
+ * @param label - Descriptive label for the value being validated (e.g., "Minutes", "Seconds").
7
+ * @param value - String representation of the numeric value to validate.
8
+ * @param limit - Maximum allowed value (minimum is always 0).
9
+ * @returns Error message string if out of range, undefined if valid.
10
+ *
11
+ * @example
12
+ * ```typescript
13
+ * inRange('Minutes', '45', 59);
14
+ * // undefined (valid)
15
+ * ```
16
+ *
17
+ * @example
18
+ * ```typescript
19
+ * inRange('Minutes', '61', 59);
20
+ * // 'Minutes value (61) exceeds max value (59).'
21
+ * ```
22
+ *
23
+ * @example
24
+ * ```typescript
25
+ * inRange('Seconds', '-5', 59);
26
+ * // 'Seconds value (-5) must be positive.'
27
+ * ```
5
28
  */
6
29
  declare const inRange: (label: string, value: string, limit: number) => string | undefined;
7
30
  //#endregion
@@ -15,9 +15,33 @@
15
15
  /**
16
16
  * Return an error string if the value is outside the range where the limits
17
17
  * are 0-limit.
18
+ *
19
+ * @param label - Descriptive label for the value being validated (e.g., "Minutes", "Seconds").
20
+ * @param value - String representation of the numeric value to validate.
21
+ * @param limit - Maximum allowed value (minimum is always 0).
22
+ * @returns Error message string if out of range, undefined if valid.
23
+ *
24
+ * @example
25
+ * ```typescript
26
+ * inRange('Minutes', '45', 59);
27
+ * // undefined (valid)
28
+ * ```
29
+ *
30
+ * @example
31
+ * ```typescript
32
+ * inRange('Minutes', '61', 59);
33
+ * // 'Minutes value (61) exceeds max value (59).'
34
+ * ```
35
+ *
36
+ * @example
37
+ * ```typescript
38
+ * inRange('Seconds', '-5', 59);
39
+ * // 'Seconds value (-5) must be positive.'
40
+ * ```
18
41
  */
19
42
  const inRange = (label, value, limit) => {
20
43
  const num = Number.parseFloat(value);
44
+ if (value !== "" && Number.isNaN(num)) return `${label} value (${value}) is not a valid number.`;
21
45
  if (limit < num) return `${label} value (${value}) exceeds max value (${limit}).`;
22
46
  if (num < 0) return `${label} value (${value}) must be positive.`;
23
47
  };
@@ -1 +1 @@
1
- {"version":3,"file":"in-range.js","names":[],"sources":["../../../../src/coordinates/latlon/internal/in-range.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 * Return an error string if the value is outside the range where the limits\n * are 0-limit.\n */\nexport const inRange = (label: string, value: string, limit: number) => {\n const num = Number.parseFloat(value);\n\n if (limit < num) {\n return `${label} value (${value}) exceeds max value (${limit}).`;\n }\n\n if (num < 0) {\n return `${label} value (${value}) must be positive.`;\n }\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;AAiBA,MAAa,WAAW,OAAe,OAAe,UAAkB;CACtE,MAAM,MAAM,OAAO,WAAW,MAAM;AAEpC,KAAI,QAAQ,IACV,QAAO,GAAG,MAAM,UAAU,MAAM,uBAAuB,MAAM;AAG/D,KAAI,MAAM,EACR,QAAO,GAAG,MAAM,UAAU,MAAM"}
1
+ {"version":3,"file":"in-range.js","names":[],"sources":["../../../../src/coordinates/latlon/internal/in-range.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 * Return an error string if the value is outside the range where the limits\n * are 0-limit.\n *\n * @param label - Descriptive label for the value being validated (e.g., \"Minutes\", \"Seconds\").\n * @param value - String representation of the numeric value to validate.\n * @param limit - Maximum allowed value (minimum is always 0).\n * @returns Error message string if out of range, undefined if valid.\n *\n * @example\n * ```typescript\n * inRange('Minutes', '45', 59);\n * // undefined (valid)\n * ```\n *\n * @example\n * ```typescript\n * inRange('Minutes', '61', 59);\n * // 'Minutes value (61) exceeds max value (59).'\n * ```\n *\n * @example\n * ```typescript\n * inRange('Seconds', '-5', 59);\n * // 'Seconds value (-5) must be positive.'\n * ```\n */\nexport const inRange = (label: string, value: string, limit: number) => {\n const num = Number.parseFloat(value);\n\n if (value !== '' && Number.isNaN(num)) {\n return `${label} value (${value}) is not a valid number.`;\n }\n\n if (limit < num) {\n return `${label} value (${value}) exceeds max value (${limit}).`;\n }\n\n if (num < 0) {\n return `${label} value (${value}) must be positive.`;\n }\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwCA,MAAa,WAAW,OAAe,OAAe,UAAkB;CACtE,MAAM,MAAM,OAAO,WAAW,MAAM;AAEpC,KAAI,UAAU,MAAM,OAAO,MAAM,IAAI,CACnC,QAAO,GAAG,MAAM,UAAU,MAAM;AAGlC,KAAI,QAAQ,IACV,QAAO,GAAG,MAAM,UAAU,MAAM,uBAAuB,MAAM;AAG/D,KAAI,MAAM,EACR,QAAO,GAAG,MAAM,UAAU,MAAM"}
@@ -1,3 +1,18 @@
1
+ /*
2
+ * Copyright 2025 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
+ import { CoordinateInput, CoordinateInternalValue, CoordinateObject, CoordinateTuple, LatLonTuple, LonLatTuple, isCoordinateObject, isCoordinateTuple, normalizeObjectToLatLon, tupleToLatLon } from "./normalize.js";
14
+ import { isFiniteNumber, validateNumericCoordinate, validateSignedRange } from "./validate.js";
15
+
1
16
  //#region src/coordinates/latlon/internal/index.d.ts
2
17
  type Axes = 'LAT' | 'LON';
3
18
  type Compass = 'N' | 'S' | 'E' | 'W';
@@ -57,5 +72,5 @@ declare const PARTIAL_PATTERNS: {
57
72
  readonly secDec: RegExp;
58
73
  };
59
74
  //#endregion
60
- export { Axes, BEARINGS, Compass, Errors, FORMATS, FORMATS_DEFAULT, Format, LIMITS, PARTIAL_PATTERNS, SYMBOLS, SYMBOL_PATTERNS };
75
+ export { Axes, BEARINGS, Compass, type CoordinateInput, type CoordinateInternalValue, type CoordinateObject, type CoordinateTuple, Errors, FORMATS, FORMATS_DEFAULT, Format, LIMITS, type LatLonTuple, type LonLatTuple, PARTIAL_PATTERNS, SYMBOLS, SYMBOL_PATTERNS, isCoordinateObject, isCoordinateTuple, isFiniteNumber, normalizeObjectToLatLon, tupleToLatLon, validateNumericCoordinate, validateSignedRange };
61
76
  //# sourceMappingURL=index.d.ts.map
@@ -12,6 +12,8 @@
12
12
 
13
13
 
14
14
  import { capture, merge, optional } from "../../../patterning.js";
15
+ import { isCoordinateObject, isCoordinateTuple, normalizeObjectToLatLon, tupleToLatLon } from "./normalize.js";
16
+ import { isFiniteNumber, validateNumericCoordinate, validateSignedRange } from "./validate.js";
15
17
 
16
18
  //#region src/coordinates/latlon/internal/index.ts
17
19
  /**
@@ -58,6 +60,28 @@ const SYMBOL_PATTERNS = {
58
60
  SYMBOLS.SECONDS
59
61
  ].join("")}]`)
60
62
  };
63
+ /**
64
+ * Creates a regex pattern for matching decimal minutes or seconds values.
65
+ *
66
+ * Generates a pattern that matches numeric values in the range 0-59.999... with optional
67
+ * leading zeros, decimal points, and symbol indicators, using lookbehind and lookahead
68
+ * to prevent partial matches within larger numbers.
69
+ *
70
+ * @param symbol - Regular expression for the symbol (minutes ' or seconds " indicator).
71
+ * @returns Combined regex pattern with precise boundary matching.
72
+ *
73
+ * @example
74
+ * ```typescript
75
+ * const minutesPattern = decimalSecAndMin(SYMBOL_PATTERNS.MINUTES);
76
+ * // Matches: "30.5'", "59.999999999", ".5'", "001'", etc.
77
+ * ```
78
+ *
79
+ * @example
80
+ * ```typescript
81
+ * const secondsPattern = decimalSecAndMin(SYMBOL_PATTERNS.SECONDS);
82
+ * // Matches: '45.23"', '0.5', '59.9999999999"', etc.
83
+ * ```
84
+ */
61
85
  const decimalSecAndMin = (symbol) => optional(/(?<!\d)/, /([-+]?0*(?:[0-5]?\d|\.\d{1,10})(?:\.\d{1,10})?)/, optional(symbol), /(?!\d)/);
62
86
  const PARTIAL_PATTERNS = {
63
87
  " ": /\s*/,
@@ -74,5 +98,5 @@ const PARTIAL_PATTERNS = {
74
98
  };
75
99
 
76
100
  //#endregion
77
- export { BEARINGS, FORMATS, FORMATS_DEFAULT, LIMITS, PARTIAL_PATTERNS, SYMBOLS, SYMBOL_PATTERNS };
101
+ export { BEARINGS, FORMATS, FORMATS_DEFAULT, LIMITS, PARTIAL_PATTERNS, SYMBOLS, SYMBOL_PATTERNS, isCoordinateObject, isCoordinateTuple, isFiniteNumber, normalizeObjectToLatLon, tupleToLatLon, validateNumericCoordinate, validateSignedRange };
78
102
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["Patterning.optional","Patterning.capture","Patterning.merge"],"sources":["../../../../src/coordinates/latlon/internal/index.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';\n\nexport type Axes = 'LAT' | 'LON';\nexport type Compass = 'N' | 'S' | 'E' | 'W';\nexport type Errors = string[];\nexport type Format = (typeof FORMATS)[number];\n\n/**\n * Bearings are the consistent/explicit identifiers of directionality of a\n * coordinate component; this library has opted for these over implicit\n * indication by number sign not because there is an inherent superiority\n * but because something had to be chosen.\n *\n * NOTE: these arrays are position-important; negative values are [1] and\n * positive values are [0] so that they can be consistently indexed using\n * an `isNegative` boolean to reference the negative bearing of each axis\n */\nexport const BEARINGS = {\n LAT: ['N', 'S'],\n LON: ['E', 'W'],\n LATLON: [\n ['N', 'S'],\n ['E', 'W'],\n ],\n LONLAT: [\n ['E', 'W'],\n ['N', 'S'],\n ],\n} as const;\n\nexport const FORMATS = ['LATLON', 'LONLAT'] as const;\nexport const FORMATS_DEFAULT = FORMATS[0];\n\nexport const LIMITS = { LATLON: [90, 180], LONLAT: [180, 90] } as const;\n\nexport const SYMBOLS = {\n DEGREES: '°',\n MINUTES: \"'\",\n SECONDS: '\"',\n DIVIDER: '/',\n};\n\nexport const SYMBOL_PATTERNS = {\n LAT: new RegExp(`[${BEARINGS.LAT.join('')}]`),\n LON: new RegExp(`[${BEARINGS.LON.join('')}]`),\n NSEW: new RegExp(`[${[...BEARINGS.LAT, ...BEARINGS.LON].join('')}]`),\n NEGATIVE_BEARINGS: /[SW]/i,\n NEGATIVE_SIGN: /-/,\n\n DEGREES: new RegExp(SYMBOLS.DEGREES),\n MINUTES: new RegExp(SYMBOLS.MINUTES),\n SECONDS: new RegExp(SYMBOLS.SECONDS),\n\n DIVIDER: new RegExp(SYMBOLS.DIVIDER),\n\n DMS: new RegExp(\n `[${[SYMBOLS.DEGREES, SYMBOLS.MINUTES, SYMBOLS.SECONDS].join('')}]`,\n ),\n\n // divider: {\n // first: /(?<NAMED_SEPARATOR>:?)/,\n // follow: new RegExp(`\\\\s?\\\\k<${'NAMED_SEPARATOR'}>\\\\s?`),\n // },\n} as const;\n\nconst decimalSecAndMin = (symbol: RegExp) =>\n Patterning.optional(\n // Negative lookbehind\n // to ensure that the match is not preceded by a digit,\n // avoiding partial matches within larger numbers.\n /(?<!\\d)/,\n\n // 0-59 including 10 decimal places and leading zeros or no number before\n // acceptable values: 0, 0.1234567890, .9876543210, 001, 59.9999999999\n /([-+]?0*(?:[0-5]?\\d|\\.\\d{1,10})(?:\\.\\d{1,10})?)/,\n\n Patterning.optional(symbol),\n\n // Negative lookahead\n // to ensure that the match is not followed by a digit,\n // avoiding partial matches within larger numbers.\n /(?!\\d)/,\n );\n\nexport const PARTIAL_PATTERNS = {\n ' ': /\\s*/,\n '/': Patterning.capture(SYMBOL_PATTERNS.DIVIDER),\n NS: Patterning.optional(Patterning.capture(SYMBOL_PATTERNS.LAT)),\n EW: Patterning.optional(Patterning.capture(SYMBOL_PATTERNS.LON)),\n\n degLatDec: Patterning.merge(\n Patterning.capture(\n /0*(?:90(?:\\.0{1,10})?)/, // 90[.0]\n /|/,\n /(?:0?[0-8]?\\d(?:\\.\\d{1,10})?)/, // [0]0[.0]-89[.9]\n ),\n Patterning.optional(SYMBOL_PATTERNS.DEGREES),\n ),\n degLonDec: Patterning.merge(\n Patterning.capture(\n /(?:180(?:\\.0{1,10})?)/, // 180[.0]\n /|/,\n /(?:0*(?:\\d{1,2}|1[0-7]\\d)(?:\\.\\d{1,10})?)/, // [00]0[.0]-179[.9]\n ),\n Patterning.optional(SYMBOL_PATTERNS.DEGREES),\n ),\n degLat: Patterning.merge(\n Patterning.capture(\n /(?:0?90)/, // 90\n /|/,\n /(?:0?[0-8]?\\d)/, // [0]0-89\n ),\n Patterning.optional(SYMBOL_PATTERNS.DEGREES),\n ),\n degLon: Patterning.merge(\n Patterning.capture(\n /(?:180)/, // 180\n /|/,\n /(?:0*(?:\\d{1,2}|1[0-7]\\d))/, // [00]0-179\n ),\n Patterning.optional(SYMBOL_PATTERNS.DEGREES),\n ),\n min: Patterning.merge(\n Patterning.optional(\n Patterning.capture(\n /(?:0?[0-5]?\\d)?/, // [0]0-59\n ),\n Patterning.optional(SYMBOL_PATTERNS.MINUTES),\n ),\n ),\n minDec: decimalSecAndMin(SYMBOL_PATTERNS.MINUTES),\n secDec: decimalSecAndMin(SYMBOL_PATTERNS.SECONDS),\n} as const;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AA8BA,MAAa,WAAW;CACtB,KAAK,CAAC,KAAK,IAAI;CACf,KAAK,CAAC,KAAK,IAAI;CACf,QAAQ,CACN,CAAC,KAAK,IAAI,EACV,CAAC,KAAK,IAAI,CACX;CACD,QAAQ,CACN,CAAC,KAAK,IAAI,EACV,CAAC,KAAK,IAAI,CACX;CACF;AAED,MAAa,UAAU,CAAC,UAAU,SAAS;AAC3C,MAAa,kBAAkB,QAAQ;AAEvC,MAAa,SAAS;CAAE,QAAQ,CAAC,IAAI,IAAI;CAAE,QAAQ,CAAC,KAAK,GAAG;CAAE;AAE9D,MAAa,UAAU;CACrB,SAAS;CACT,SAAS;CACT,SAAS;CACT,SAAS;CACV;AAED,MAAa,kBAAkB;CAC7B,qBAAK,IAAI,OAAO,IAAI,SAAS,IAAI,KAAK,GAAG,CAAC,GAAG;CAC7C,qBAAK,IAAI,OAAO,IAAI,SAAS,IAAI,KAAK,GAAG,CAAC,GAAG;CAC7C,sBAAM,IAAI,OAAO,IAAI,CAAC,GAAG,SAAS,KAAK,GAAG,SAAS,IAAI,CAAC,KAAK,GAAG,CAAC,GAAG;CACpE,mBAAmB;CACnB,eAAe;CAEf,SAAS,IAAI,OAAO,QAAQ,QAAQ;CACpC,SAAS,IAAI,OAAO,QAAQ,QAAQ;CACpC,SAAS,IAAI,OAAO,QAAQ,QAAQ;CAEpC,SAAS,IAAI,OAAO,QAAQ,QAAQ;CAEpC,qBAAK,IAAI,OACP,IAAI;EAAC,QAAQ;EAAS,QAAQ;EAAS,QAAQ;EAAQ,CAAC,KAAK,GAAG,CAAC,GAClE;CAMF;AAED,MAAM,oBAAoB,WACxBA,SAIE,WAIA,mDAEAA,SAAoB,OAAO,EAK3B,SACD;AAEH,MAAa,mBAAmB;CAC9B,KAAK;CACL,KAAKC,QAAmB,gBAAgB,QAAQ;CAChD,IAAID,SAAoBC,QAAmB,gBAAgB,IAAI,CAAC;CAChE,IAAID,SAAoBC,QAAmB,gBAAgB,IAAI,CAAC;CAEhE,WAAWC,MACTD,QACE,0BACA,KACA,gCACD,EACDD,SAAoB,gBAAgB,QAAQ,CAC7C;CACD,WAAWE,MACTD,QACE,yBACA,KACA,4CACD,EACDD,SAAoB,gBAAgB,QAAQ,CAC7C;CACD,QAAQE,MACND,QACE,YACA,KACA,iBACD,EACDD,SAAoB,gBAAgB,QAAQ,CAC7C;CACD,QAAQE,MACND,QACE,WACA,KACA,6BACD,EACDD,SAAoB,gBAAgB,QAAQ,CAC7C;CACD,KAAKE,MACHF,SACEC,QACE,kBACD,EACDD,SAAoB,gBAAgB,QAAQ,CAC7C,CACF;CACD,QAAQ,iBAAiB,gBAAgB,QAAQ;CACjD,QAAQ,iBAAiB,gBAAgB,QAAQ;CAClD"}
1
+ {"version":3,"file":"index.js","names":["Patterning.optional","Patterning.capture","Patterning.merge"],"sources":["../../../../src/coordinates/latlon/internal/index.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';\n\nexport type Axes = 'LAT' | 'LON';\nexport type Compass = 'N' | 'S' | 'E' | 'W';\nexport type Errors = string[];\nexport type Format = (typeof FORMATS)[number];\n\n/**\n * Bearings are the consistent/explicit identifiers of directionality of a\n * coordinate component; this library has opted for these over implicit\n * indication by number sign not because there is an inherent superiority\n * but because something had to be chosen.\n *\n * NOTE: these arrays are position-important; negative values are [1] and\n * positive values are [0] so that they can be consistently indexed using\n * an `isNegative` boolean to reference the negative bearing of each axis\n */\nexport const BEARINGS = {\n LAT: ['N', 'S'],\n LON: ['E', 'W'],\n LATLON: [\n ['N', 'S'],\n ['E', 'W'],\n ],\n LONLAT: [\n ['E', 'W'],\n ['N', 'S'],\n ],\n} as const;\n\nexport const FORMATS = ['LATLON', 'LONLAT'] as const;\nexport const FORMATS_DEFAULT = FORMATS[0];\n\nexport const LIMITS = { LATLON: [90, 180], LONLAT: [180, 90] } as const;\n\nexport const SYMBOLS = {\n DEGREES: '°',\n MINUTES: \"'\",\n SECONDS: '\"',\n DIVIDER: '/',\n};\n\nexport const SYMBOL_PATTERNS = {\n LAT: new RegExp(`[${BEARINGS.LAT.join('')}]`),\n LON: new RegExp(`[${BEARINGS.LON.join('')}]`),\n NSEW: new RegExp(`[${[...BEARINGS.LAT, ...BEARINGS.LON].join('')}]`),\n NEGATIVE_BEARINGS: /[SW]/i,\n NEGATIVE_SIGN: /-/,\n\n DEGREES: new RegExp(SYMBOLS.DEGREES),\n MINUTES: new RegExp(SYMBOLS.MINUTES),\n SECONDS: new RegExp(SYMBOLS.SECONDS),\n\n DIVIDER: new RegExp(SYMBOLS.DIVIDER),\n\n DMS: new RegExp(\n `[${[SYMBOLS.DEGREES, SYMBOLS.MINUTES, SYMBOLS.SECONDS].join('')}]`,\n ),\n\n // divider: {\n // first: /(?<NAMED_SEPARATOR>:?)/,\n // follow: new RegExp(`\\\\s?\\\\k<${'NAMED_SEPARATOR'}>\\\\s?`),\n // },\n} as const;\n\n/**\n * Creates a regex pattern for matching decimal minutes or seconds values.\n *\n * Generates a pattern that matches numeric values in the range 0-59.999... with optional\n * leading zeros, decimal points, and symbol indicators, using lookbehind and lookahead\n * to prevent partial matches within larger numbers.\n *\n * @param symbol - Regular expression for the symbol (minutes ' or seconds \" indicator).\n * @returns Combined regex pattern with precise boundary matching.\n *\n * @example\n * ```typescript\n * const minutesPattern = decimalSecAndMin(SYMBOL_PATTERNS.MINUTES);\n * // Matches: \"30.5'\", \"59.999999999\", \".5'\", \"001'\", etc.\n * ```\n *\n * @example\n * ```typescript\n * const secondsPattern = decimalSecAndMin(SYMBOL_PATTERNS.SECONDS);\n * // Matches: '45.23\"', '0.5', '59.9999999999\"', etc.\n * ```\n */\nconst decimalSecAndMin = (symbol: RegExp) =>\n Patterning.optional(\n // Negative lookbehind\n // to ensure that the match is not preceded by a digit,\n // avoiding partial matches within larger numbers.\n /(?<!\\d)/,\n\n // 0-59 including 10 decimal places and leading zeros or no number before\n // acceptable values: 0, 0.1234567890, .9876543210, 001, 59.9999999999\n /([-+]?0*(?:[0-5]?\\d|\\.\\d{1,10})(?:\\.\\d{1,10})?)/,\n\n Patterning.optional(symbol),\n\n // Negative lookahead\n // to ensure that the match is not followed by a digit,\n // avoiding partial matches within larger numbers.\n /(?!\\d)/,\n );\n\nexport const PARTIAL_PATTERNS = {\n ' ': /\\s*/,\n '/': Patterning.capture(SYMBOL_PATTERNS.DIVIDER),\n NS: Patterning.optional(Patterning.capture(SYMBOL_PATTERNS.LAT)),\n EW: Patterning.optional(Patterning.capture(SYMBOL_PATTERNS.LON)),\n\n degLatDec: Patterning.merge(\n Patterning.capture(\n /0*(?:90(?:\\.0{1,10})?)/, // 90[.0]\n /|/,\n /(?:0?[0-8]?\\d(?:\\.\\d{1,10})?)/, // [0]0[.0]-89[.9]\n ),\n Patterning.optional(SYMBOL_PATTERNS.DEGREES),\n ),\n degLonDec: Patterning.merge(\n Patterning.capture(\n /(?:180(?:\\.0{1,10})?)/, // 180[.0]\n /|/,\n /(?:0*(?:\\d{1,2}|1[0-7]\\d)(?:\\.\\d{1,10})?)/, // [00]0[.0]-179[.9]\n ),\n Patterning.optional(SYMBOL_PATTERNS.DEGREES),\n ),\n degLat: Patterning.merge(\n Patterning.capture(\n /(?:0?90)/, // 90\n /|/,\n /(?:0?[0-8]?\\d)/, // [0]0-89\n ),\n Patterning.optional(SYMBOL_PATTERNS.DEGREES),\n ),\n degLon: Patterning.merge(\n Patterning.capture(\n /(?:180)/, // 180\n /|/,\n /(?:0*(?:\\d{1,2}|1[0-7]\\d))/, // [00]0-179\n ),\n Patterning.optional(SYMBOL_PATTERNS.DEGREES),\n ),\n min: Patterning.merge(\n Patterning.optional(\n Patterning.capture(\n /(?:0?[0-5]?\\d)?/, // [0]0-59\n ),\n Patterning.optional(SYMBOL_PATTERNS.MINUTES),\n ),\n ),\n minDec: decimalSecAndMin(SYMBOL_PATTERNS.MINUTES),\n secDec: decimalSecAndMin(SYMBOL_PATTERNS.SECONDS),\n} as const;\n\nexport {\n type CoordinateInput,\n type CoordinateInternalValue,\n type CoordinateObject,\n type CoordinateTuple,\n type LatLonTuple,\n type LonLatTuple,\n isCoordinateObject,\n isCoordinateTuple,\n normalizeObjectToLatLon,\n tupleToLatLon,\n} from './normalize';\nexport {\n isFiniteNumber,\n validateNumericCoordinate,\n validateSignedRange,\n} from './validate';\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8BA,MAAa,WAAW;CACtB,KAAK,CAAC,KAAK,IAAI;CACf,KAAK,CAAC,KAAK,IAAI;CACf,QAAQ,CACN,CAAC,KAAK,IAAI,EACV,CAAC,KAAK,IAAI,CACX;CACD,QAAQ,CACN,CAAC,KAAK,IAAI,EACV,CAAC,KAAK,IAAI,CACX;CACF;AAED,MAAa,UAAU,CAAC,UAAU,SAAS;AAC3C,MAAa,kBAAkB,QAAQ;AAEvC,MAAa,SAAS;CAAE,QAAQ,CAAC,IAAI,IAAI;CAAE,QAAQ,CAAC,KAAK,GAAG;CAAE;AAE9D,MAAa,UAAU;CACrB,SAAS;CACT,SAAS;CACT,SAAS;CACT,SAAS;CACV;AAED,MAAa,kBAAkB;CAC7B,qBAAK,IAAI,OAAO,IAAI,SAAS,IAAI,KAAK,GAAG,CAAC,GAAG;CAC7C,qBAAK,IAAI,OAAO,IAAI,SAAS,IAAI,KAAK,GAAG,CAAC,GAAG;CAC7C,sBAAM,IAAI,OAAO,IAAI,CAAC,GAAG,SAAS,KAAK,GAAG,SAAS,IAAI,CAAC,KAAK,GAAG,CAAC,GAAG;CACpE,mBAAmB;CACnB,eAAe;CAEf,SAAS,IAAI,OAAO,QAAQ,QAAQ;CACpC,SAAS,IAAI,OAAO,QAAQ,QAAQ;CACpC,SAAS,IAAI,OAAO,QAAQ,QAAQ;CAEpC,SAAS,IAAI,OAAO,QAAQ,QAAQ;CAEpC,qBAAK,IAAI,OACP,IAAI;EAAC,QAAQ;EAAS,QAAQ;EAAS,QAAQ;EAAQ,CAAC,KAAK,GAAG,CAAC,GAClE;CAMF;;;;;;;;;;;;;;;;;;;;;;;AAwBD,MAAM,oBAAoB,WACxBA,SAIE,WAIA,mDAEAA,SAAoB,OAAO,EAK3B,SACD;AAEH,MAAa,mBAAmB;CAC9B,KAAK;CACL,KAAKC,QAAmB,gBAAgB,QAAQ;CAChD,IAAID,SAAoBC,QAAmB,gBAAgB,IAAI,CAAC;CAChE,IAAID,SAAoBC,QAAmB,gBAAgB,IAAI,CAAC;CAEhE,WAAWC,MACTD,QACE,0BACA,KACA,gCACD,EACDD,SAAoB,gBAAgB,QAAQ,CAC7C;CACD,WAAWE,MACTD,QACE,yBACA,KACA,4CACD,EACDD,SAAoB,gBAAgB,QAAQ,CAC7C;CACD,QAAQE,MACND,QACE,YACA,KACA,iBACD,EACDD,SAAoB,gBAAgB,QAAQ,CAC7C;CACD,QAAQE,MACND,QACE,WACA,KACA,6BACD,EACDD,SAAoB,gBAAgB,QAAQ,CAC7C;CACD,KAAKE,MACHF,SACEC,QACE,kBACD,EACDD,SAAoB,gBAAgB,QAAQ,CAC7C,CACF;CACD,QAAQ,iBAAiB,gBAAgB,QAAQ;CACjD,QAAQ,iBAAiB,gBAAgB,QAAQ;CAClD"}
@@ -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
@@ -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// remove trailing zeros '?.0' and ensure leading zero '0.?' in numbers\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 * 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 */\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;AAGD,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;;;;;;;;;;;;;;;;;;AAmBT,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"}
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 2025 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 };