@accelint/map-toolkit 1.4.0 → 2.0.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 (204) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/catalog-info.yaml +4 -4
  3. package/dist/camera/events.js +1 -1
  4. package/dist/camera/index.d.ts +1 -1
  5. package/dist/camera/index.js +1 -1
  6. package/dist/camera/store.d.ts +1 -1
  7. package/dist/camera/store.js +4 -6
  8. package/dist/camera/store.js.map +1 -1
  9. package/dist/camera/types.d.ts +1 -1
  10. package/dist/camera/types.js +1 -1
  11. package/dist/cursor-coordinates/constants.js +1 -1
  12. package/dist/cursor-coordinates/index.d.ts +1 -1
  13. package/dist/cursor-coordinates/index.js +1 -1
  14. package/dist/cursor-coordinates/store.d.ts +1 -1
  15. package/dist/cursor-coordinates/store.js +1 -1
  16. package/dist/cursor-coordinates/types.d.ts +1 -1
  17. package/dist/cursor-coordinates/types.js +1 -1
  18. package/dist/cursor-coordinates/use-cursor-coordinates.d.ts +1 -1
  19. package/dist/cursor-coordinates/use-cursor-coordinates.js +4 -9
  20. package/dist/cursor-coordinates/use-cursor-coordinates.js.map +1 -1
  21. package/dist/deckgl/base-map/constants.js +1 -1
  22. package/dist/deckgl/base-map/controls.d.ts +1 -1
  23. package/dist/deckgl/base-map/controls.js +1 -1
  24. package/dist/deckgl/base-map/events.js +1 -1
  25. package/dist/deckgl/base-map/index.d.ts +3 -3
  26. package/dist/deckgl/base-map/index.js +15 -7
  27. package/dist/deckgl/base-map/index.js.map +1 -1
  28. package/dist/deckgl/base-map/provider.d.ts +3 -3
  29. package/dist/deckgl/base-map/provider.js +3 -5
  30. package/dist/deckgl/base-map/provider.js.map +1 -1
  31. package/dist/deckgl/base-map/types.d.ts +1 -1
  32. package/dist/deckgl/base-map/types.js +1 -1
  33. package/dist/deckgl/index.d.ts +4 -4
  34. package/dist/deckgl/index.js +4 -4
  35. package/dist/deckgl/saved-viewports/index.d.ts +1 -1
  36. package/dist/deckgl/saved-viewports/index.js +1 -1
  37. package/dist/deckgl/saved-viewports/storage.d.ts +1 -1
  38. package/dist/deckgl/saved-viewports/storage.js +5 -10
  39. package/dist/deckgl/saved-viewports/storage.js.map +1 -1
  40. package/dist/deckgl/shapes/display-shape-layer/constants.js +66 -13
  41. package/dist/deckgl/shapes/display-shape-layer/constants.js.map +1 -1
  42. package/dist/deckgl/shapes/display-shape-layer/fiber.d.ts +1 -1
  43. package/dist/deckgl/shapes/display-shape-layer/fiber.js +1 -1
  44. package/dist/deckgl/shapes/display-shape-layer/index.d.ts +74 -35
  45. package/dist/deckgl/shapes/display-shape-layer/index.js +381 -154
  46. package/dist/deckgl/shapes/display-shape-layer/index.js.map +1 -1
  47. package/dist/deckgl/shapes/display-shape-layer/shape-label-layer.js +1 -1
  48. package/dist/deckgl/shapes/display-shape-layer/store.js +8 -2
  49. package/dist/deckgl/shapes/display-shape-layer/store.js.map +1 -1
  50. package/dist/deckgl/shapes/display-shape-layer/types.d.ts +108 -19
  51. package/dist/deckgl/shapes/display-shape-layer/types.js +1 -1
  52. package/dist/deckgl/shapes/display-shape-layer/use-select-shape.d.ts +1 -1
  53. package/dist/deckgl/shapes/display-shape-layer/use-select-shape.js +1 -1
  54. package/dist/deckgl/shapes/display-shape-layer/utils/display-style.js +66 -36
  55. package/dist/deckgl/shapes/display-shape-layer/utils/display-style.js.map +1 -1
  56. package/dist/deckgl/shapes/display-shape-layer/utils/elevation.js +407 -0
  57. package/dist/deckgl/shapes/display-shape-layer/utils/elevation.js.map +1 -0
  58. package/dist/deckgl/shapes/display-shape-layer/utils/icon-config.js +151 -0
  59. package/dist/deckgl/shapes/display-shape-layer/utils/icon-config.js.map +1 -0
  60. package/dist/deckgl/shapes/display-shape-layer/utils/interaction.js +50 -0
  61. package/dist/deckgl/shapes/display-shape-layer/utils/interaction.js.map +1 -0
  62. package/dist/deckgl/shapes/display-shape-layer/utils/labels.d.ts +1 -1
  63. package/dist/deckgl/shapes/display-shape-layer/utils/labels.js +28 -39
  64. package/dist/deckgl/shapes/display-shape-layer/utils/labels.js.map +1 -1
  65. package/dist/deckgl/shapes/draw-shape-layer/constants.js +1 -1
  66. package/dist/deckgl/shapes/draw-shape-layer/events.d.ts +1 -1
  67. package/dist/deckgl/shapes/draw-shape-layer/events.js +1 -1
  68. package/dist/deckgl/shapes/draw-shape-layer/fiber.js +1 -1
  69. package/dist/deckgl/shapes/draw-shape-layer/index.d.ts +3 -3
  70. package/dist/deckgl/shapes/draw-shape-layer/index.js +7 -17
  71. package/dist/deckgl/shapes/draw-shape-layer/index.js.map +1 -1
  72. package/dist/deckgl/shapes/draw-shape-layer/modes/draw-circle-mode-with-tooltip.js +8 -9
  73. package/dist/deckgl/shapes/draw-shape-layer/modes/draw-circle-mode-with-tooltip.js.map +1 -1
  74. package/dist/deckgl/shapes/draw-shape-layer/modes/draw-ellipse-mode-with-tooltip.js +3 -3
  75. package/dist/deckgl/shapes/draw-shape-layer/modes/draw-line-string-mode-with-tooltip.js +4 -21
  76. package/dist/deckgl/shapes/draw-shape-layer/modes/draw-line-string-mode-with-tooltip.js.map +1 -1
  77. package/dist/deckgl/shapes/draw-shape-layer/modes/draw-polygon-mode-with-tooltip.js +4 -34
  78. package/dist/deckgl/shapes/draw-shape-layer/modes/draw-polygon-mode-with-tooltip.js.map +1 -1
  79. package/dist/deckgl/shapes/draw-shape-layer/modes/draw-rectangle-mode-with-tooltip.js +11 -12
  80. package/dist/deckgl/shapes/draw-shape-layer/modes/draw-rectangle-mode-with-tooltip.js.map +1 -1
  81. package/dist/deckgl/shapes/draw-shape-layer/modes/index.js +2 -32
  82. package/dist/deckgl/shapes/draw-shape-layer/modes/index.js.map +1 -1
  83. package/dist/deckgl/shapes/draw-shape-layer/store.js +38 -4
  84. package/dist/deckgl/shapes/draw-shape-layer/store.js.map +1 -1
  85. package/dist/deckgl/shapes/draw-shape-layer/types.d.ts +1 -1
  86. package/dist/deckgl/shapes/draw-shape-layer/types.js +1 -1
  87. package/dist/deckgl/shapes/draw-shape-layer/use-draw-shape.d.ts +1 -1
  88. package/dist/deckgl/shapes/draw-shape-layer/use-draw-shape.js +2 -2
  89. package/dist/deckgl/shapes/draw-shape-layer/utils/feature-conversion.js +4 -9
  90. package/dist/deckgl/shapes/draw-shape-layer/utils/feature-conversion.js.map +1 -1
  91. package/dist/deckgl/shapes/edit-shape-layer/constants.js +17 -2
  92. package/dist/deckgl/shapes/edit-shape-layer/constants.js.map +1 -1
  93. package/dist/deckgl/shapes/edit-shape-layer/events.d.ts +1 -1
  94. package/dist/deckgl/shapes/edit-shape-layer/events.js +1 -1
  95. package/dist/deckgl/shapes/edit-shape-layer/fiber.d.ts +1 -1
  96. package/dist/deckgl/shapes/edit-shape-layer/fiber.js +1 -1
  97. package/dist/deckgl/shapes/edit-shape-layer/index.d.ts +7 -4
  98. package/dist/deckgl/shapes/edit-shape-layer/index.js +52 -21
  99. package/dist/deckgl/shapes/edit-shape-layer/index.js.map +1 -1
  100. package/dist/deckgl/shapes/edit-shape-layer/modes/base-transform-mode.js +4 -1
  101. package/dist/deckgl/shapes/edit-shape-layer/modes/base-transform-mode.js.map +1 -1
  102. package/dist/deckgl/shapes/edit-shape-layer/modes/bounding-transform-mode.js +2 -2
  103. package/dist/deckgl/shapes/edit-shape-layer/modes/bounding-transform-mode.js.map +1 -1
  104. package/dist/deckgl/shapes/edit-shape-layer/modes/circle-transform-mode.js +7 -7
  105. package/dist/deckgl/shapes/edit-shape-layer/modes/circle-transform-mode.js.map +1 -1
  106. package/dist/deckgl/shapes/edit-shape-layer/modes/index.js +2 -2
  107. package/dist/deckgl/shapes/edit-shape-layer/modes/index.js.map +1 -1
  108. package/dist/deckgl/shapes/edit-shape-layer/modes/point-translate-mode.js +1 -1
  109. package/dist/deckgl/shapes/edit-shape-layer/modes/point-translate-mode.js.map +1 -1
  110. package/dist/deckgl/shapes/edit-shape-layer/modes/rotate-mode-with-snap.js +2 -2
  111. package/dist/deckgl/shapes/edit-shape-layer/modes/rotate-mode-with-snap.js.map +1 -1
  112. package/dist/deckgl/shapes/edit-shape-layer/modes/scale-mode-with-free-transform.js +1 -1
  113. package/dist/deckgl/shapes/edit-shape-layer/modes/scale-mode-with-free-transform.js.map +1 -1
  114. package/dist/deckgl/shapes/edit-shape-layer/modes/vertex-transform-mode.js +1 -1
  115. package/dist/deckgl/shapes/edit-shape-layer/modes/vertex-transform-mode.js.map +1 -1
  116. package/dist/deckgl/shapes/edit-shape-layer/store.js +78 -14
  117. package/dist/deckgl/shapes/edit-shape-layer/store.js.map +1 -1
  118. package/dist/deckgl/shapes/edit-shape-layer/types.d.ts +14 -2
  119. package/dist/deckgl/shapes/edit-shape-layer/types.js +1 -1
  120. package/dist/deckgl/shapes/edit-shape-layer/use-edit-shape.d.ts +1 -1
  121. package/dist/deckgl/shapes/edit-shape-layer/use-edit-shape.js +2 -2
  122. package/dist/deckgl/shapes/index.d.ts +4 -4
  123. package/dist/deckgl/shapes/index.js +5 -5
  124. package/dist/deckgl/shapes/shared/constants.d.ts +4 -3
  125. package/dist/deckgl/shapes/shared/constants.js +55 -15
  126. package/dist/deckgl/shapes/shared/constants.js.map +1 -1
  127. package/dist/deckgl/shapes/shared/events.d.ts +5 -1
  128. package/dist/deckgl/shapes/shared/events.js +1 -1
  129. package/dist/deckgl/shapes/shared/events.js.map +1 -1
  130. package/dist/deckgl/shapes/shared/hooks/use-shift-zoom-disable.js +19 -16
  131. package/dist/deckgl/shapes/shared/hooks/use-shift-zoom-disable.js.map +1 -1
  132. package/dist/deckgl/shapes/shared/types.d.ts +174 -53
  133. package/dist/deckgl/shapes/shared/types.js +155 -2
  134. package/dist/deckgl/shapes/shared/types.js.map +1 -1
  135. package/dist/deckgl/shapes/shared/utils/geometry-measurements.js +29 -24
  136. package/dist/deckgl/shapes/shared/utils/geometry-measurements.js.map +1 -1
  137. package/dist/deckgl/shapes/shared/utils/layer-config.js +9 -6
  138. package/dist/deckgl/shapes/shared/utils/layer-config.js.map +1 -1
  139. package/dist/deckgl/shapes/shared/utils/mode-utils.js +50 -20
  140. package/dist/deckgl/shapes/shared/utils/mode-utils.js.map +1 -1
  141. package/dist/deckgl/shapes/shared/utils/pick-filtering.js +22 -15
  142. package/dist/deckgl/shapes/shared/utils/pick-filtering.js.map +1 -1
  143. package/dist/deckgl/shapes/shared/utils/style-utils.d.ts +38 -14
  144. package/dist/deckgl/shapes/shared/utils/style-utils.js +43 -32
  145. package/dist/deckgl/shapes/shared/utils/style-utils.js.map +1 -1
  146. package/dist/deckgl/symbol-layer/fiber.d.ts +1 -1
  147. package/dist/deckgl/symbol-layer/fiber.js +1 -1
  148. package/dist/deckgl/symbol-layer/index.d.ts +1 -1
  149. package/dist/deckgl/symbol-layer/index.js +1 -1
  150. package/dist/deckgl/text-layer/character-sets.js +1 -1
  151. package/dist/deckgl/text-layer/default-settings.d.ts +1 -1
  152. package/dist/deckgl/text-layer/default-settings.js +1 -1
  153. package/dist/deckgl/text-layer/fiber.d.ts +1 -1
  154. package/dist/deckgl/text-layer/fiber.js +1 -1
  155. package/dist/deckgl/text-layer/index.d.ts +1 -1
  156. package/dist/deckgl/text-layer/index.js +1 -1
  157. package/dist/deckgl/text-settings.d.ts +3 -3
  158. package/dist/deckgl/text-settings.js +1 -1
  159. package/dist/map-cursor/events.js +1 -1
  160. package/dist/map-cursor/index.d.ts +1 -1
  161. package/dist/map-cursor/index.js +1 -1
  162. package/dist/map-cursor/store.d.ts +1 -1
  163. package/dist/map-cursor/store.js +1 -1
  164. package/dist/map-cursor/types.d.ts +1 -1
  165. package/dist/map-cursor/types.js +1 -1
  166. package/dist/map-cursor/use-map-cursor.d.ts +1 -1
  167. package/dist/map-cursor/use-map-cursor.js +1 -1
  168. package/dist/map-mode/events.js +1 -1
  169. package/dist/map-mode/index.d.ts +1 -1
  170. package/dist/map-mode/index.js +1 -1
  171. package/dist/map-mode/store.d.ts +1 -1
  172. package/dist/map-mode/store.js +3 -8
  173. package/dist/map-mode/store.js.map +1 -1
  174. package/dist/map-mode/types.d.ts +1 -1
  175. package/dist/map-mode/types.js +1 -1
  176. package/dist/map-mode/use-map-mode.d.ts +1 -1
  177. package/dist/map-mode/use-map-mode.js +1 -1
  178. package/dist/maplibre/hooks/use-maplibre.d.ts +1 -1
  179. package/dist/maplibre/hooks/use-maplibre.js +1 -1
  180. package/dist/maplibre/index.d.ts +1 -1
  181. package/dist/maplibre/index.js +1 -1
  182. package/dist/shared/cleanup.d.ts +58 -0
  183. package/dist/shared/cleanup.js +93 -0
  184. package/dist/shared/cleanup.js.map +1 -0
  185. package/dist/shared/constants.js +1 -1
  186. package/dist/shared/create-map-store.d.ts +13 -1
  187. package/dist/shared/create-map-store.js +9 -4
  188. package/dist/shared/create-map-store.js.map +1 -1
  189. package/dist/shared/logger.js +31 -0
  190. package/dist/shared/logger.js.map +1 -0
  191. package/dist/shared/units.js +1 -1
  192. package/dist/viewport/index.d.ts +1 -1
  193. package/dist/viewport/index.js +1 -1
  194. package/dist/viewport/store.d.ts +1 -1
  195. package/dist/viewport/store.js +1 -1
  196. package/dist/viewport/types.d.ts +1 -1
  197. package/dist/viewport/types.js +1 -1
  198. package/dist/viewport/utils.d.ts +1 -1
  199. package/dist/viewport/utils.js +1 -1
  200. package/dist/viewport/viewport-size.d.ts +3 -3
  201. package/dist/viewport/viewport-size.js +1 -1
  202. package/package.json +22 -19
  203. package/dist/hotkey-manager/dist/react/use-hotkey.js +0 -39
  204. package/dist/hotkey-manager/dist/react/use-hotkey.js.map +0 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,50 @@
1
1
  # @accelint/map-toolkit
2
2
 
3
+ ## 2.0.0
4
+
5
+ ### Major Changes
6
+
7
+ - 7b30771: Add `enableElevation` prop to DisplayShapeLayer for 3D shapes rendering.
8
+
9
+ Remove dotted border treatment on shape selection — interactions no longer modify a shape's innate styling.
10
+
11
+ Apply material-based brightness effect for hover and selection on all polygon shapes. All shapes brighten their outline color on hover or select (1.4×), with an even brighter combined state when both active (1.7×).
12
+
13
+ Add new Selection layer for brightness effect while shape is selected.
14
+
15
+ Add optional `maxElevation` property to `StyledFeatureProperties` as the source of truth for Polygon shape elevation. Point and LineString elevation is derived from z coordinates.
16
+
17
+ Note: the only breaking change is removal of a redundant type. If you were using `type ShapeFeatureTypeValues` from map-toolkit, replace that with `type ShapeFeatureType`.
18
+
19
+ ### Minor Changes
20
+
21
+ - d5ac9eb: Update EditShapeLayer to include the ability to pan the map while editing a shape when the spacebar is held.
22
+ - 311d7b6: Upgraded @deck.gl packages to 9.2 and removed shape layer double-click workaround
23
+
24
+ ### Patch Changes
25
+
26
+ - 5567348: Update logger implementation to prevent singleton pollution.
27
+ - 1106ced: Fix bug where Enter on Save hotkey never unregistered, causing an error on EditShapeLayer remounts.
28
+ - Updated dependencies [3153e74]
29
+ - Updated dependencies [5567348]
30
+ - Updated dependencies [162895c]
31
+ - @accelint/bus@4.0.0
32
+ - @accelint/logger@1.0.1
33
+ - @accelint/core@0.6.0
34
+ - @accelint/hotkey-manager@2.0.0
35
+
36
+ ## 1.5.0
37
+
38
+ ### Minor Changes
39
+
40
+ - cae932c: Update circle tooltip to show radius instead of diameter in draw and edit modes.
41
+
42
+ ### Patch Changes
43
+
44
+ - 9419d41: Fix infinite render loop in BaseMap when navigating in React Strict Mode. The camera store's `createMapStore` now properly handles initial state timing by directly updating existing instances when `setInitialState` is called after the instance already exists, which can occur during React Strict Mode's double-mount behavior.
45
+ - Updated dependencies [ed09ea6]
46
+ - @accelint/logger@1.0.0
47
+
3
48
  ## 1.4.0
4
49
 
5
50
  ### Minor Changes
package/catalog-info.yaml CHANGED
@@ -11,15 +11,15 @@ metadata:
11
11
 
12
12
  Dependencies:
13
13
 
14
- accelint_biome-config@1.1.0, accelint_bus@3.0.2, accelint_core@0.5.2,
15
- accelint_design-foundation@3.0.0, accelint_design-toolkit@9.6.0,
16
- accelint_geo@0.6.0, accelint_logger@0.1.5,
14
+ accelint_biome-config@1.1.0, accelint_bus@4.0.0, accelint_core@0.6.0,
15
+ accelint_design-foundation@3.0.1, accelint_design-toolkit@9.8.0,
16
+ accelint_geo@0.6.0, accelint_logger@1.0.1,
17
17
  accelint_postcss-tailwind-css-modules@1.0.1, accelint_smeegl@0.3.5,
18
18
  accelint_typescript-config@0.1.4, accelint_vitest-config@0.1.6
19
19
  annotations:
20
20
  backstage.io/edit-url: https://github.com/gohypergiant/standard-toolkit/blob/main/packages/map-toolkit/catalog-info.yaml
21
21
  backstage.io/techdocs-ref: dir:.
22
- package/version: 1.4.0
22
+ package/version: 2.0.0
23
23
  github.com/project-slug: gohypergiant/standard-toolkit
24
24
  links:
25
25
  - url: https://github.com/gohypergiant/standard-toolkit/tree/main/packages/map-toolkit
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright 2025 Hypergiant Galactic Systems Inc. All rights reserved.
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
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright 2025 Hypergiant Galactic Systems Inc. All rights reserved.
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
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright 2025 Hypergiant Galactic Systems Inc. All rights reserved.
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
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright 2025 Hypergiant Galactic Systems Inc. All rights reserved.
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
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright 2025 Hypergiant Galactic Systems Inc. All rights reserved.
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
@@ -199,11 +199,9 @@ const cameraStore = createMapStore({
199
199
  if (payload.id !== mapId) return;
200
200
  const newState = { ...get() };
201
201
  newState.projection = payload.projection;
202
+ newState.pitch = 0;
202
203
  if (payload.projection === "globe") newState.view = "3D";
203
- else {
204
- newState.view = "2D";
205
- newState.pitch = 0;
206
- }
204
+ else newState.view = "2D";
207
205
  replace(newState);
208
206
  });
209
207
  const unsubSetView = cameraBus.on(CameraEventTypes.setView, ({ payload }) => {
@@ -289,7 +287,7 @@ function initializeCameraState(mapId, initialState) {
289
287
  initializedInstances.add(mapId);
290
288
  if (initialState) initialStateCache.set(mapId, initialState);
291
289
  const builtState = buildCameraState(initialState);
292
- cameraStore.set(mapId, builtState);
290
+ cameraStore.setInitialState(mapId, builtState);
293
291
  }
294
292
  /**
295
293
  * Hook to subscribe to camera state changes for a specific map.
@@ -1 +1 @@
1
- {"version":3,"file":"store.js","names":["DEFAULT_CAMERA_STATE: CameraState"],"sources":["../../src/camera/store.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/**\n * Camera Store\n *\n * Manages camera state (position, zoom, pitch, rotation, projection, view) per map instance.\n * State is updated via bus events or direct actions.\n *\n * @example\n * ```tsx\n * import { cameraStore } from '@accelint/map-toolkit/camera';\n *\n * function MapInfo({ mapId }) {\n * const { state } = cameraStore.use(mapId);\n * return (\n * <div>\n * Lat: {state.latitude.toFixed(2)}, Lon: {state.longitude.toFixed(2)}\n * </div>\n * );\n * }\n * ```\n */\n\nimport { Broadcast } from '@accelint/bus';\nimport { fitBounds } from '@math.gl/web-mercator';\nimport { createMapStore } from '../shared/create-map-store';\nimport { CameraEventTypes } from './events';\nimport type { UniqueId } from '@accelint/core';\nimport type { CameraEvent, ProjectionType, ViewType } from './types';\n\nconst cameraBus = Broadcast.getInstance<CameraEvent>();\n\n/**\n * Camera state for 2D view\n */\ntype CameraState2D = {\n latitude: number;\n longitude: number;\n zoom: number;\n pitch: 0;\n rotation: number;\n projection: 'mercator';\n view: '2D';\n};\n\n/**\n * Camera state for 3D view\n */\ntype CameraState3D = {\n latitude: number;\n longitude: number;\n zoom: number;\n pitch: 0;\n rotation: number;\n projection: 'globe';\n view: '3D';\n};\n\n/**\n * Camera state for 2.5D view\n */\ntype CameraState2Point5D = {\n latitude: number;\n longitude: number;\n zoom: number;\n pitch: number;\n rotation: number;\n projection: 'mercator';\n view: '2.5D';\n};\n\n/**\n * Union type for all camera states\n */\nexport type CameraState = CameraState2D | CameraState3D | CameraState2Point5D;\n\n/**\n * Actions for camera management\n */\ntype CameraActions = {\n /** Update camera state directly */\n setCameraState: (state: Partial<CameraState>) => void;\n};\n\n/**\n * Storage for initial camera state per instance.\n * Used for reset operations to restore to initial values.\n *\n * @internal These caches live outside the store factory because reset operations\n * need access to the original initial values. They are cleaned up via `onCleanup`\n * hook when the store instance is destroyed, and via `clearCameraState()` for\n * manual cleanup. Do NOT use `cameraStore.clear()` directly in tests - use\n * `clearCameraState()` instead to ensure proper cleanup.\n */\nconst initialStateCache = new Map<UniqueId, CameraStateInput>();\n\n/**\n * Track which instances have been initialized.\n * @internal See note on initialStateCache about cleanup.\n */\nconst initializedInstances = new Set<UniqueId>();\n\n/**\n * Input type for building camera state - simpler than union type\n */\ntype CameraStateInput = {\n latitude?: number;\n longitude?: number;\n zoom?: number;\n pitch?: number;\n rotation?: number;\n projection?: ProjectionType;\n view?: ViewType;\n};\n\n/**\n * Build a complete camera state from partial input.\n * Returns the appropriate discriminated union variant based on view/projection.\n *\n * This function ensures type-safe camera states by constructing the correct\n * discriminated union variant (2D, 2.5D, or 3D) based on view and projection\n * settings, applying appropriate defaults and constraints.\n *\n * @param partial - Optional partial camera state input\n * @returns Complete camera state matching one of the discriminated union variants\n *\n * @example\n * ```typescript\n * // Build 2D state (default)\n * const state2D = buildCameraState({\n * latitude: 40.7128,\n * longitude: -74.0060,\n * zoom: 10,\n * });\n * // Result: { ..., view: '2D', projection: 'mercator', pitch: 0 }\n *\n * // Build 2.5D state\n * const state2Point5D = buildCameraState({\n * latitude: 37.7749,\n * longitude: -122.4194,\n * zoom: 12,\n * view: '2.5D',\n * });\n * // Result: { ..., view: '2.5D', projection: 'mercator', pitch: 45 }\n *\n * // Build 3D state\n * const state3D = buildCameraState({\n * view: '3D',\n * zoom: 2,\n * });\n * // Result: { ..., view: '3D', projection: 'globe', pitch: 0, rotation: 0 }\n * ```\n */\nfunction buildCameraState(partial?: CameraStateInput): CameraState {\n const latitude = partial?.latitude ?? 0;\n const longitude = partial?.longitude ?? 0;\n const zoom = partial?.zoom ?? 0;\n const rotation = partial?.rotation ?? 0;\n\n // Determine which variant to build based on view/projection\n const is3D = partial?.view === '3D' || partial?.projection === 'globe';\n const is2Point5D = partial?.view === '2.5D';\n\n if (is3D) {\n // 3D view: globe projection, no pitch, no rotation\n return {\n latitude,\n longitude,\n zoom,\n pitch: 0,\n rotation: 0,\n projection: 'globe',\n view: '3D',\n } satisfies CameraState3D;\n }\n\n if (is2Point5D) {\n // 2.5D view: mercator projection, variable pitch\n return {\n latitude,\n longitude,\n zoom,\n pitch: partial?.pitch ?? 45,\n rotation,\n projection: 'mercator',\n view: '2.5D',\n } satisfies CameraState2Point5D;\n }\n\n // Default: 2D view, mercator projection, no pitch\n return {\n latitude,\n longitude,\n zoom,\n pitch: 0,\n rotation,\n projection: 'mercator',\n view: '2D',\n } satisfies CameraState2D;\n}\n\n/**\n * Default camera state\n */\nconst DEFAULT_CAMERA_STATE: CameraState = {\n latitude: 0,\n longitude: 0,\n zoom: 0,\n pitch: 0,\n rotation: 0,\n projection: 'mercator',\n view: '2D',\n};\n\n/**\n * Camera store instance\n */\nexport const cameraStore = createMapStore<CameraState, CameraActions>({\n defaultState: DEFAULT_CAMERA_STATE,\n\n actions: (_mapId, { get, replace }) => ({\n setCameraState: (updates: Partial<CameraState>) => {\n const currentState = get();\n // Use buildCameraState to ensure proper discriminated union type\n replace(buildCameraState({ ...currentState, ...updates }));\n },\n }),\n\n bus: (mapId, { get, replace }) => {\n const unsubReset = cameraBus.on(CameraEventTypes.reset, ({ payload }) => {\n if (payload.id !== mapId) {\n return;\n }\n\n const state = get();\n const initialState = initialStateCache.get(mapId);\n const newState = buildCameraState({\n latitude: state.latitude,\n longitude: state.longitude,\n projection: state.projection,\n view: state.view,\n zoom: payload.zoom === false ? state.zoom : (initialState?.zoom ?? 0),\n pitch:\n payload.pitch === false ? state.pitch : (initialState?.pitch ?? 0),\n rotation:\n payload.rotation === false\n ? state.rotation\n : (initialState?.rotation ?? 0),\n });\n\n replace(newState);\n });\n\n const unsubSetCenter = cameraBus.on(\n CameraEventTypes.setCenter,\n ({ payload }) => {\n if (payload.id !== mapId) {\n return;\n }\n\n const state = get();\n replace(\n buildCameraState({\n ...state,\n latitude: payload.latitude,\n longitude: payload.longitude,\n zoom: payload.zoom ?? state.zoom,\n rotation: payload.heading ?? state.rotation,\n pitch: payload.pitch ?? state.pitch,\n }),\n );\n },\n );\n\n const unsubFitBounds = cameraBus.on(\n CameraEventTypes.fitBounds,\n ({ payload }) => {\n if (payload.id !== mapId) {\n return;\n }\n\n const state = get();\n const { longitude, latitude, zoom } = fitBounds({\n width: payload.width,\n height: payload.height,\n bounds: [\n [payload.bounds[0], payload.bounds[1]],\n [payload.bounds[2], payload.bounds[3]],\n ],\n padding: payload.padding,\n });\n\n replace(\n buildCameraState({\n ...state,\n latitude,\n longitude,\n zoom,\n rotation: payload.heading ?? state.rotation,\n pitch: payload.pitch ?? state.pitch,\n }),\n );\n },\n );\n\n const unsubSetProjection = cameraBus.on(\n CameraEventTypes.setProjection,\n ({ payload }) => {\n if (payload.id !== mapId) {\n return;\n }\n\n const state = get();\n const newState = { ...state };\n newState.projection = payload.projection;\n if (payload.projection === 'globe') {\n newState.view = '3D';\n } else {\n newState.view = '2D';\n newState.pitch = 0;\n }\n replace(newState);\n },\n );\n\n const unsubSetView = cameraBus.on(\n CameraEventTypes.setView,\n ({ payload }) => {\n if (payload.id !== mapId) {\n return;\n }\n\n const state = get();\n const newState = { ...state };\n newState.view = payload.view;\n if (payload.view === '3D') {\n newState.projection = 'globe';\n newState.pitch = 0;\n } else {\n newState.projection = 'mercator';\n }\n\n if (payload.view === '2.5D') {\n newState.pitch = 45;\n }\n replace(newState);\n },\n );\n\n const unsubSetZoom = cameraBus.on(\n CameraEventTypes.setZoom,\n ({ payload }) => {\n if (payload.id !== mapId) {\n return;\n }\n\n const state = get();\n replace({ ...state, zoom: payload.zoom });\n },\n );\n\n const unsubSetRotation = cameraBus.on(\n CameraEventTypes.setRotation,\n ({ payload }) => {\n if (payload.id !== mapId) {\n return;\n }\n\n const state = get();\n if (state.view !== '3D') {\n replace({ ...state, rotation: payload.rotation });\n }\n },\n );\n\n const unsubSetPitch = cameraBus.on(\n CameraEventTypes.setPitch,\n ({ payload }) => {\n if (payload.id !== mapId) {\n return;\n }\n\n const state = get();\n if (state.view === '2.5D') {\n replace({ ...state, pitch: payload.pitch });\n }\n },\n );\n\n return () => {\n unsubReset();\n unsubSetCenter();\n unsubFitBounds();\n unsubSetProjection();\n unsubSetView();\n unsubSetZoom();\n unsubSetRotation();\n unsubSetPitch();\n };\n },\n\n onCleanup: (mapId) => {\n initializedInstances.delete(mapId);\n initialStateCache.delete(mapId);\n },\n});\n\n// =============================================================================\n// Convenience exports\n// =============================================================================\n\n/**\n * Initialize camera state for a map instance with optional initial values.\n * Should be called before using the camera store for a given mapId.\n *\n * @param mapId - Unique identifier for the map instance\n * @param initialState - Optional initial camera state\n * @returns void\n *\n * @example\n * ```typescript\n * import { uuid } from '@accelint/core';\n * import { initializeCameraState } from '@accelint/map-toolkit/camera';\n *\n * const mapId = uuid();\n *\n * // Initialize with default state\n * initializeCameraState(mapId);\n *\n * // Initialize with custom state\n * initializeCameraState(mapId, {\n * latitude: 37.7749,\n * longitude: -122.4194,\n * zoom: 10,\n * view: '2.5D',\n * pitch: 45,\n * });\n * ```\n */\nexport function initializeCameraState(\n mapId: UniqueId,\n initialState?: CameraStateInput,\n): void {\n if (initializedInstances.has(mapId)) {\n return; // Already initialized\n }\n\n initializedInstances.add(mapId);\n if (initialState) {\n initialStateCache.set(mapId, initialState);\n }\n const builtState = buildCameraState(initialState);\n cameraStore.set(mapId, builtState);\n}\n\n/**\n * Hook to subscribe to camera state changes for a specific map.\n *\n * @param mapId - Unique identifier for the map instance\n * @param initialCameraState - Optional initial camera state (only used on first call)\n * @returns Camera state and setCameraState action\n *\n * @example\n * ```tsx\n * function MapInfo({ mapId }) {\n * const { cameraState, setCameraState } = useMapCamera(mapId);\n * return (\n * <div>\n * Lat: {cameraState.latitude.toFixed(2)}, Lon: {cameraState.longitude.toFixed(2)}\n * </div>\n * );\n * }\n * ```\n */\nexport function useMapCamera(\n mapId: UniqueId,\n initialCameraState?: CameraStateInput,\n): {\n cameraState: CameraState;\n setCameraState: (state: Partial<CameraState>) => void;\n} {\n // Initialize on first use if initial state provided\n if (initialCameraState && !initializedInstances.has(mapId)) {\n initializeCameraState(mapId, initialCameraState);\n }\n\n const { state, setCameraState } = cameraStore.use(mapId);\n\n return { cameraState: state, setCameraState };\n}\n\n/**\n * Manually clear camera state for a specific map instance.\n *\n * Removes all cached state including initial values and subscription tracking.\n * Useful for cleanup when dynamically destroying map instances.\n *\n * @param mapId - The unique identifier for the map instance to clear\n * @returns void\n *\n * @example\n * ```typescript\n * import { clearCameraState } from '@accelint/map-toolkit/camera';\n *\n * // Clean up when removing a map\n * function removeMap(mapId: UniqueId) {\n * clearCameraState(mapId);\n * // ... remove map component\n * }\n * ```\n *\n * @example\n * ```typescript\n * import { clearCameraState } from '@accelint/map-toolkit/camera';\n * import { afterEach } from 'vitest';\n *\n * // Clean up in tests\n * afterEach(() => {\n * clearCameraState('test-map-id');\n * });\n * ```\n */\nexport function clearCameraState(mapId: UniqueId): void {\n initializedInstances.delete(mapId);\n initialStateCache.delete(mapId);\n cameraStore.clear(mapId);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"store.js","names":["DEFAULT_CAMERA_STATE: CameraState"],"sources":["../../src/camera/store.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/**\n * Camera Store\n *\n * Manages camera state (position, zoom, pitch, rotation, projection, view) per map instance.\n * State is updated via bus events or direct actions.\n *\n * @example\n * ```tsx\n * import { cameraStore } from '@accelint/map-toolkit/camera';\n *\n * function MapInfo({ mapId }) {\n * const { state } = cameraStore.use(mapId);\n * return (\n * <div>\n * Lat: {state.latitude.toFixed(2)}, Lon: {state.longitude.toFixed(2)}\n * </div>\n * );\n * }\n * ```\n */\n\nimport { Broadcast } from '@accelint/bus';\nimport { fitBounds } from '@math.gl/web-mercator';\nimport { createMapStore } from '../shared/create-map-store';\nimport { CameraEventTypes } from './events';\nimport type { UniqueId } from '@accelint/core';\nimport type { CameraEvent, ProjectionType, ViewType } from './types';\n\nconst cameraBus = Broadcast.getInstance<CameraEvent>();\n\n/**\n * Camera state for 2D view\n */\ntype CameraState2D = {\n latitude: number;\n longitude: number;\n zoom: number;\n pitch: 0;\n rotation: number;\n projection: 'mercator';\n view: '2D';\n};\n\n/**\n * Camera state for 3D view\n */\ntype CameraState3D = {\n latitude: number;\n longitude: number;\n zoom: number;\n pitch: 0;\n rotation: number;\n projection: 'globe';\n view: '3D';\n};\n\n/**\n * Camera state for 2.5D view\n */\ntype CameraState2Point5D = {\n latitude: number;\n longitude: number;\n zoom: number;\n pitch: number;\n rotation: number;\n projection: 'mercator';\n view: '2.5D';\n};\n\n/**\n * Union type for all camera states\n */\nexport type CameraState = CameraState2D | CameraState3D | CameraState2Point5D;\n\n/**\n * Actions for camera management\n */\ntype CameraActions = {\n /** Update camera state directly */\n setCameraState: (state: Partial<CameraState>) => void;\n};\n\n/**\n * Storage for initial camera state per instance.\n * Used for reset operations to restore to initial values.\n *\n * @internal These caches live outside the store factory because reset operations\n * need access to the original initial values. They are cleaned up via `onCleanup`\n * hook when the store instance is destroyed, and via `clearCameraState()` for\n * manual cleanup. Do NOT use `cameraStore.clear()` directly in tests - use\n * `clearCameraState()` instead to ensure proper cleanup.\n */\nconst initialStateCache = new Map<UniqueId, CameraStateInput>();\n\n/**\n * Track which instances have been initialized.\n * @internal See note on initialStateCache about cleanup.\n */\nconst initializedInstances = new Set<UniqueId>();\n\n/**\n * Input type for building camera state - simpler than union type\n */\ntype CameraStateInput = {\n latitude?: number;\n longitude?: number;\n zoom?: number;\n pitch?: number;\n rotation?: number;\n projection?: ProjectionType;\n view?: ViewType;\n};\n\n/**\n * Build a complete camera state from partial input.\n * Returns the appropriate discriminated union variant based on view/projection.\n *\n * This function ensures type-safe camera states by constructing the correct\n * discriminated union variant (2D, 2.5D, or 3D) based on view and projection\n * settings, applying appropriate defaults and constraints.\n *\n * @param partial - Optional partial camera state input\n * @returns Complete camera state matching one of the discriminated union variants\n *\n * @example\n * ```typescript\n * // Build 2D state (default)\n * const state2D = buildCameraState({\n * latitude: 40.7128,\n * longitude: -74.0060,\n * zoom: 10,\n * });\n * // Result: { ..., view: '2D', projection: 'mercator', pitch: 0 }\n *\n * // Build 2.5D state\n * const state2Point5D = buildCameraState({\n * latitude: 37.7749,\n * longitude: -122.4194,\n * zoom: 12,\n * view: '2.5D',\n * });\n * // Result: { ..., view: '2.5D', projection: 'mercator', pitch: 45 }\n *\n * // Build 3D state\n * const state3D = buildCameraState({\n * view: '3D',\n * zoom: 2,\n * });\n * // Result: { ..., view: '3D', projection: 'globe', pitch: 0, rotation: 0 }\n * ```\n */\nfunction buildCameraState(partial?: CameraStateInput): CameraState {\n const latitude = partial?.latitude ?? 0;\n const longitude = partial?.longitude ?? 0;\n const zoom = partial?.zoom ?? 0;\n const rotation = partial?.rotation ?? 0;\n\n // Determine which variant to build based on view/projection\n const is3D = partial?.view === '3D' || partial?.projection === 'globe';\n const is2Point5D = partial?.view === '2.5D';\n\n if (is3D) {\n // 3D view: globe projection, no pitch, no rotation\n return {\n latitude,\n longitude,\n zoom,\n pitch: 0,\n rotation: 0,\n projection: 'globe',\n view: '3D',\n } satisfies CameraState3D;\n }\n\n if (is2Point5D) {\n // 2.5D view: mercator projection, variable pitch\n return {\n latitude,\n longitude,\n zoom,\n pitch: partial?.pitch ?? 45,\n rotation,\n projection: 'mercator',\n view: '2.5D',\n } satisfies CameraState2Point5D;\n }\n\n // Default: 2D view, mercator projection, no pitch\n return {\n latitude,\n longitude,\n zoom,\n pitch: 0,\n rotation,\n projection: 'mercator',\n view: '2D',\n } satisfies CameraState2D;\n}\n\n/**\n * Default camera state\n */\nconst DEFAULT_CAMERA_STATE: CameraState = {\n latitude: 0,\n longitude: 0,\n zoom: 0,\n pitch: 0,\n rotation: 0,\n projection: 'mercator',\n view: '2D',\n};\n\n/**\n * Camera store instance\n */\nexport const cameraStore = createMapStore<CameraState, CameraActions>({\n defaultState: DEFAULT_CAMERA_STATE,\n\n actions: (_mapId, { get, replace }) => ({\n setCameraState: (updates: Partial<CameraState>) => {\n const currentState = get();\n // Use buildCameraState to ensure proper discriminated union type\n replace(buildCameraState({ ...currentState, ...updates }));\n },\n }),\n\n bus: (mapId, { get, replace }) => {\n const unsubReset = cameraBus.on(CameraEventTypes.reset, ({ payload }) => {\n if (payload.id !== mapId) {\n return;\n }\n\n const state = get();\n const initialState = initialStateCache.get(mapId);\n const newState = buildCameraState({\n latitude: state.latitude,\n longitude: state.longitude,\n projection: state.projection,\n view: state.view,\n zoom: payload.zoom === false ? state.zoom : (initialState?.zoom ?? 0),\n pitch:\n payload.pitch === false ? state.pitch : (initialState?.pitch ?? 0),\n rotation:\n payload.rotation === false\n ? state.rotation\n : (initialState?.rotation ?? 0),\n });\n\n replace(newState);\n });\n\n const unsubSetCenter = cameraBus.on(\n CameraEventTypes.setCenter,\n ({ payload }) => {\n if (payload.id !== mapId) {\n return;\n }\n\n const state = get();\n replace(\n buildCameraState({\n ...state,\n latitude: payload.latitude,\n longitude: payload.longitude,\n zoom: payload.zoom ?? state.zoom,\n rotation: payload.heading ?? state.rotation,\n pitch: payload.pitch ?? state.pitch,\n }),\n );\n },\n );\n\n const unsubFitBounds = cameraBus.on(\n CameraEventTypes.fitBounds,\n ({ payload }) => {\n if (payload.id !== mapId) {\n return;\n }\n\n const state = get();\n const { longitude, latitude, zoom } = fitBounds({\n width: payload.width,\n height: payload.height,\n bounds: [\n [payload.bounds[0], payload.bounds[1]],\n [payload.bounds[2], payload.bounds[3]],\n ],\n padding: payload.padding,\n });\n\n replace(\n buildCameraState({\n ...state,\n latitude,\n longitude,\n zoom,\n rotation: payload.heading ?? state.rotation,\n pitch: payload.pitch ?? state.pitch,\n }),\n );\n },\n );\n\n const unsubSetProjection = cameraBus.on(\n CameraEventTypes.setProjection,\n ({ payload }) => {\n if (payload.id !== mapId) {\n return;\n }\n\n const state = get();\n const newState = { ...state };\n newState.projection = payload.projection;\n newState.pitch = 0;\n if (payload.projection === 'globe') {\n newState.view = '3D';\n } else {\n newState.view = '2D';\n }\n replace(newState);\n },\n );\n\n const unsubSetView = cameraBus.on(\n CameraEventTypes.setView,\n ({ payload }) => {\n if (payload.id !== mapId) {\n return;\n }\n\n const state = get();\n const newState = { ...state };\n newState.view = payload.view;\n if (payload.view === '3D') {\n newState.projection = 'globe';\n newState.pitch = 0;\n } else {\n newState.projection = 'mercator';\n }\n\n if (payload.view === '2.5D') {\n newState.pitch = 45;\n }\n replace(newState);\n },\n );\n\n const unsubSetZoom = cameraBus.on(\n CameraEventTypes.setZoom,\n ({ payload }) => {\n if (payload.id !== mapId) {\n return;\n }\n\n const state = get();\n replace({ ...state, zoom: payload.zoom });\n },\n );\n\n const unsubSetRotation = cameraBus.on(\n CameraEventTypes.setRotation,\n ({ payload }) => {\n if (payload.id !== mapId) {\n return;\n }\n\n const state = get();\n if (state.view !== '3D') {\n replace({ ...state, rotation: payload.rotation });\n }\n },\n );\n\n const unsubSetPitch = cameraBus.on(\n CameraEventTypes.setPitch,\n ({ payload }) => {\n if (payload.id !== mapId) {\n return;\n }\n\n const state = get();\n if (state.view === '2.5D') {\n replace({ ...state, pitch: payload.pitch });\n }\n },\n );\n\n return () => {\n unsubReset();\n unsubSetCenter();\n unsubFitBounds();\n unsubSetProjection();\n unsubSetView();\n unsubSetZoom();\n unsubSetRotation();\n unsubSetPitch();\n };\n },\n\n onCleanup: (mapId) => {\n initializedInstances.delete(mapId);\n initialStateCache.delete(mapId);\n },\n});\n\n// =============================================================================\n// Convenience exports\n// =============================================================================\n\n/**\n * Initialize camera state for a map instance with optional initial values.\n * Should be called before using the camera store for a given mapId.\n *\n * @param mapId - Unique identifier for the map instance\n * @param initialState - Optional initial camera state\n * @returns void\n *\n * @example\n * ```typescript\n * import { uuid } from '@accelint/core';\n * import { initializeCameraState } from '@accelint/map-toolkit/camera';\n *\n * const mapId = uuid();\n *\n * // Initialize with default state\n * initializeCameraState(mapId);\n *\n * // Initialize with custom state\n * initializeCameraState(mapId, {\n * latitude: 37.7749,\n * longitude: -122.4194,\n * zoom: 10,\n * view: '2.5D',\n * pitch: 45,\n * });\n * ```\n */\nexport function initializeCameraState(\n mapId: UniqueId,\n initialState?: CameraStateInput,\n): void {\n if (initializedInstances.has(mapId)) {\n return; // Already initialized\n }\n\n initializedInstances.add(mapId);\n if (initialState) {\n initialStateCache.set(mapId, initialState);\n }\n const builtState = buildCameraState(initialState);\n // Set initial state BEFORE getInstance is called by useSyncExternalStore\n // This ensures any code path that creates the instance uses this state\n cameraStore.setInitialState(mapId, builtState);\n}\n\n/**\n * Hook to subscribe to camera state changes for a specific map.\n *\n * @param mapId - Unique identifier for the map instance\n * @param initialCameraState - Optional initial camera state (only used on first call)\n * @returns Camera state and setCameraState action\n *\n * @example\n * ```tsx\n * function MapInfo({ mapId }) {\n * const { cameraState, setCameraState } = useMapCamera(mapId);\n * return (\n * <div>\n * Lat: {cameraState.latitude.toFixed(2)}, Lon: {cameraState.longitude.toFixed(2)}\n * </div>\n * );\n * }\n * ```\n */\nexport function useMapCamera(\n mapId: UniqueId,\n initialCameraState?: CameraStateInput,\n): {\n cameraState: CameraState;\n setCameraState: (state: Partial<CameraState>) => void;\n} {\n // Initialize BEFORE subscribing to ensure first render has correct state.\n // This prevents MapLibre from rendering at default (0,0,0) and firing onMove\n // events that would overwrite the initialized state.\n // This is safe because initializeCameraState is idempotent (checks initializedInstances first).\n if (initialCameraState && !initializedInstances.has(mapId)) {\n initializeCameraState(mapId, initialCameraState);\n }\n\n const { state, setCameraState } = cameraStore.use(mapId);\n\n return { cameraState: state, setCameraState };\n}\n\n/**\n * Manually clear camera state for a specific map instance.\n *\n * Removes all cached state including initial values and subscription tracking.\n * Useful for cleanup when dynamically destroying map instances.\n *\n * @param mapId - The unique identifier for the map instance to clear\n * @returns void\n *\n * @example\n * ```typescript\n * import { clearCameraState } from '@accelint/map-toolkit/camera';\n *\n * // Clean up when removing a map\n * function removeMap(mapId: UniqueId) {\n * clearCameraState(mapId);\n * // ... remove map component\n * }\n * ```\n *\n * @example\n * ```typescript\n * import { clearCameraState } from '@accelint/map-toolkit/camera';\n * import { afterEach } from 'vitest';\n *\n * // Clean up in tests\n * afterEach(() => {\n * clearCameraState('test-map-id');\n * });\n * ```\n */\nexport function clearCameraState(mapId: UniqueId): void {\n initializedInstances.delete(mapId);\n initialStateCache.delete(mapId);\n cameraStore.clear(mapId);\n}\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright 2025 Hypergiant Galactic Systems Inc. All rights reserved.
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
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright 2025 Hypergiant Galactic Systems Inc. All rights reserved.
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
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright 2025 Hypergiant Galactic Systems Inc. All rights reserved.
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
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright 2025 Hypergiant Galactic Systems Inc. All rights reserved.
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
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright 2025 Hypergiant Galactic Systems Inc. All rights reserved.
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
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright 2025 Hypergiant Galactic Systems Inc. All rights reserved.
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
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright 2025 Hypergiant Galactic Systems Inc. All rights reserved.
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
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright 2025 Hypergiant Galactic Systems Inc. All rights reserved.
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
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright 2025 Hypergiant Galactic Systems Inc. All rights reserved.
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
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright 2025 Hypergiant Galactic Systems Inc. All rights reserved.
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
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright 2025 Hypergiant Galactic Systems Inc. All rights reserved.
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
@@ -15,18 +15,13 @@
15
15
 
16
16
  import { DEFAULT_LATLON_COORDS, DEFAULT_MGRS_UTM_COORDS, LONGITUDE_RANGE, MAX_LONGITUDE } from "./constants.js";
17
17
  import { cursorCoordinateStore } from "./store.js";
18
+ import { createLoggerDomain } from "../shared/logger.js";
18
19
  import { MapContext } from "../deckgl/base-map/provider.js";
19
20
  import { useContext, useMemo } from "react";
20
21
  import { coordinateSystems, createCoordinate, formatDecimalDegrees, formatDegreesDecimalMinutes, formatDegreesMinutesSeconds } from "@accelint/geo";
21
- import { getLogger } from "@accelint/logger";
22
22
 
23
23
  //#region src/cursor-coordinates/use-cursor-coordinates.ts
24
- const logger = getLogger({
25
- enabled: process.env.NODE_ENV !== "production" && process.env.NODE_ENV !== "test",
26
- level: "warn",
27
- prefix: "[CursorCoordinates]",
28
- pretty: true
29
- });
24
+ const logger = createLoggerDomain("[CursorCoordinates]");
30
25
  /**
31
26
  * Normalizes longitude to -180 to 180 range.
32
27
  * Handles wraparound including multi-revolution values.
@@ -196,7 +191,7 @@ function useCursorCoordinates(id, options) {
196
191
  if (customFormatter) try {
197
192
  return customFormatter(rawCoord);
198
193
  } catch (error) {
199
- logger.error(`Custom formatter failed: ${error instanceof Error ? error.message : String(error)}`);
194
+ logger.withError(error).error("Custom formatter failed");
200
195
  return getDefaultCoords();
201
196
  }
202
197
  return formatCoordinate(state.coordinate, state.format);
@@ -1 +1 @@
1
- {"version":3,"file":"use-cursor-coordinates.js","names":["latLon: [number, number]"],"sources":["../../src/cursor-coordinates/use-cursor-coordinates.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'use client';\n\nimport type { UniqueId } from '@accelint/core';\nimport {\n coordinateSystems,\n createCoordinate,\n formatDecimalDegrees,\n formatDegreesDecimalMinutes,\n formatDegreesMinutesSeconds,\n} from '@accelint/geo';\nimport { getLogger } from '@accelint/logger';\nimport 'client-only';\nimport { useContext, useMemo } from 'react';\nimport { MapContext } from '../deckgl/base-map/provider';\nimport {\n DEFAULT_LATLON_COORDS,\n DEFAULT_MGRS_UTM_COORDS,\n LONGITUDE_RANGE,\n MAX_LONGITUDE,\n} from './constants';\nimport { cursorCoordinateStore } from './store';\nimport type {\n CoordinateFormatTypes,\n RawCoordinate,\n UseCursorCoordinatesOptions,\n UseCursorCoordinatesReturn,\n} from './types';\n\nconst logger = getLogger({\n enabled:\n process.env.NODE_ENV !== 'production' && process.env.NODE_ENV !== 'test',\n level: 'warn',\n prefix: '[CursorCoordinates]',\n pretty: true,\n});\n\n/**\n * Normalizes longitude to -180 to 180 range.\n * Handles wraparound including multi-revolution values.\n *\n * @param lon - Longitude value in degrees\n * @returns Normalized longitude between -180 and 180\n */\nfunction normalizeLongitude(lon: number): number {\n return (\n ((((lon + MAX_LONGITUDE) % LONGITUDE_RANGE) + LONGITUDE_RANGE) %\n LONGITUDE_RANGE) -\n MAX_LONGITUDE\n );\n}\n\n/**\n * Builds a RawCoordinate object from a coordinate tuple.\n *\n * @param coord - Coordinate tuple [longitude, latitude] or null\n * @returns RawCoordinate object or null\n */\nfunction buildRawCoordinate(coord: [number, number] | null): RawCoordinate {\n if (!coord) {\n return null;\n }\n\n const normalizedLon = normalizeLongitude(coord[0]);\n\n return {\n longitude: normalizedLon,\n latitude: coord[1],\n };\n}\n\n/**\n * Formats a coordinate using the specified format.\n * Uses @accelint/geo formatters which match CoordinateField precision:\n * - DD: 6 decimal places\n * - DDM: 4 decimal places for minutes\n * - DMS: 2 decimal places for seconds\n *\n * @param coord - Coordinate tuple [longitude, latitude]\n * @param format - Coordinate format type\n * @returns Formatted coordinate string\n * *\n * @remarks\n * **UTM/MGRS Limitations:** UTM and MGRS coordinate systems are only valid between 80°S and 84°N.\n * Coordinates outside this range (e.g., polar regions) will return the default placeholder `--, --`.\n * Other formats (DD, DDM, DMS) work correctly at all latitudes.\n */\nfunction formatCoordinate(\n coord: [number, number],\n format: CoordinateFormatTypes,\n): string {\n // Normalize longitude and convert to [lat, lon] for geo formatters\n const normalizedLon = normalizeLongitude(coord[0]);\n const latLon: [number, number] = [coord[1], normalizedLon];\n\n switch (format) {\n case 'dd':\n return formatDecimalDegrees(latLon, {\n withOrdinal: true,\n separator: ' / ',\n prefix: '',\n suffix: '',\n });\n case 'ddm':\n return formatDegreesDecimalMinutes(latLon, {\n withOrdinal: true,\n separator: ' / ',\n prefix: '',\n suffix: '',\n });\n case 'dms':\n return formatDegreesMinutesSeconds(latLon, {\n withOrdinal: true,\n separator: ' / ',\n prefix: '',\n suffix: '',\n });\n case 'mgrs':\n case 'utm': {\n // UTM and MGRS are only valid between 80°S and 84°N\n // Use createCoordinate for grid-based formats\n // Input format: \"lon E / lat N\" for LONLAT (matching geo package DD tests)\n // Limit to 10 decimal places (geo parser max) and avoid floating point precision issues\n const lat = latLon[0];\n const lon = latLon[1];\n\n // Check if coordinate is within valid UTM/MGRS range\n if (lat < -80 || lat > 84) {\n return DEFAULT_MGRS_UTM_COORDS;\n }\n\n const latOrdinal = lat >= 0 ? 'N' : 'S';\n const lonOrdinal = lon >= 0 ? 'E' : 'W';\n // Use LONLAT format: longitude first, then latitude\n // toFixed(10) ensures we stay within the parser's regex limits\n const formattedInput = `${Math.abs(lon).toFixed(10)} ${lonOrdinal} / ${Math.abs(lat).toFixed(10)} ${latOrdinal}`;\n\n const geoCoord = createCoordinate(\n coordinateSystems.dd,\n 'LONLAT',\n )(formattedInput);\n\n // Validate the coordinate was created successfully\n if (!geoCoord.valid) {\n logger.error(\n `Failed to create coordinate for ${format}: ${geoCoord.errors.join(', ')}`,\n );\n return DEFAULT_MGRS_UTM_COORDS;\n }\n\n return geoCoord[format]();\n }\n }\n}\n\n/**\n * React hook that tracks and formats the cursor hover position coordinates on a map.\n *\n * Subscribes to map hover events via the event bus and converts coordinates to various\n * geographic formats (Decimal Degrees, DMS, MGRS, UTM, etc.). The hook automatically\n * filters events to only process those from the specified map instance.\n *\n * Uses the shared store factory for efficient state management and automatic cleanup.\n *\n * @param id - Optional map instance ID. If not provided, attempts to use the ID from MapProvider context.\n * @param options - Optional configuration options\n * @returns Object containing the formatted coordinate string, raw coordinate, format setter, and current format\n * @throws {Error} When no id is provided and hook is used outside MapProvider context\n *\n * @remarks\n * **UTM/MGRS Limitations:** UTM and MGRS coordinate systems are only valid between 80°S and 84°N.\n * Coordinates outside this range (e.g., polar regions) will display the default placeholder `--, --`.\n * Other formats (DD, DDM, DMS) work correctly at all latitudes.\n *\n * @example\n * Basic usage:\n * ```tsx\n * import { uuid } from '@accelint/core';\n * import { useCursorCoordinates } from '@accelint/map-toolkit/cursor-coordinates';\n *\n * const MAP_ID = uuid();\n *\n * function CoordinateDisplay() {\n * const { formattedCoord, setFormat } = useCursorCoordinates(MAP_ID);\n *\n * return (\n * <div>\n * <select onChange={(e) => setFormat(e.target.value as CoordinateFormatTypes)}>\n * <option value=\"dd\">Decimal Degrees</option>\n * <option value=\"ddm\">Degrees Decimal Minutes</option>\n * <option value=\"dms\">Degrees Minutes Seconds</option>\n * <option value=\"mgrs\">MGRS</option>\n * <option value=\"utm\">UTM</option>\n * </select>\n * <div>{formattedCoord}</div>\n * </div>\n * );\n * }\n * ```\n *\n * @example\n * With custom formatter:\n * ```tsx\n * function CustomCoordinateDisplay() {\n * const { formattedCoord, rawCoord } = useCursorCoordinates(MAP_ID, {\n * formatter: (coord) =>\n * `Lat: ${coord.latitude.toFixed(6)}° Lng: ${coord.longitude.toFixed(6)}°`,\n * });\n *\n * return <div>{formattedCoord}</div>;\n * }\n * ```\n *\n * @example\n * Accessing raw coordinates:\n * ```tsx\n * function RawCoordinateDisplay() {\n * const { rawCoord, currentFormat } = useCursorCoordinates(MAP_ID);\n *\n * if (!rawCoord) {\n * return <div>Move cursor over map</div>;\n * }\n *\n * return (\n * <div>\n * <div>Longitude: {rawCoord.longitude}</div>\n * <div>Latitude: {rawCoord.latitude}</div>\n * <div>Format: {currentFormat}</div>\n * </div>\n * );\n * }\n * ```\n */\nexport function useCursorCoordinates(\n id?: UniqueId,\n options?: UseCursorCoordinatesOptions,\n): UseCursorCoordinatesReturn {\n const contextId = useContext(MapContext);\n const actualId = id ?? contextId;\n\n if (!actualId) {\n throw new Error(\n 'useCursorCoordinates requires either an id parameter or to be used within a MapProvider',\n );\n }\n\n const customFormatter = options?.formatter;\n\n // Use the store hook to get state and actions\n const { state, setFormat } = cursorCoordinateStore.use(actualId);\n\n // Build raw coordinate object\n const rawCoord = useMemo(\n () => buildRawCoordinate(state.coordinate),\n [state.coordinate],\n );\n\n // Compute formatted coordinate string\n const formattedCoord = useMemo(() => {\n // Return default coords based on current format.\n const getDefaultCoords = () =>\n state.format === 'mgrs' || state.format === 'utm'\n ? DEFAULT_MGRS_UTM_COORDS\n : DEFAULT_LATLON_COORDS;\n\n if (!(rawCoord && state.coordinate)) {\n return getDefaultCoords();\n }\n\n // Use custom formatter if provided\n if (customFormatter) {\n try {\n return customFormatter(rawCoord);\n } catch (error) {\n logger.error(\n `Custom formatter failed: ${error instanceof Error ? error.message : String(error)}`,\n );\n return getDefaultCoords();\n }\n }\n\n // Use built-in formatter\n return formatCoordinate(state.coordinate, state.format);\n }, [rawCoord, customFormatter, state.format, state.coordinate]);\n\n // Memoize the return value to prevent unnecessary re-renders\n return useMemo(\n () => ({\n formattedCoord,\n setFormat,\n rawCoord,\n currentFormat: state.format,\n }),\n [formattedCoord, setFormat, rawCoord, state.format],\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAuCA,MAAM,SAAS,UAAU;CACvB,SACE,QAAQ,IAAI,aAAa,gBAAgB,QAAQ,IAAI,aAAa;CACpE,OAAO;CACP,QAAQ;CACR,QAAQ;CACT,CAAC;;;;;;;;AASF,SAAS,mBAAmB,KAAqB;AAC/C,UACM,MAAM,iBAAiB,kBAAmB,mBAC5C,kBACF;;;;;;;;AAUJ,SAAS,mBAAmB,OAA+C;AACzE,KAAI,CAAC,MACH,QAAO;AAKT,QAAO;EACL,WAHoB,mBAAmB,MAAM,GAAG;EAIhD,UAAU,MAAM;EACjB;;;;;;;;;;;;;;;;;;AAmBH,SAAS,iBACP,OACA,QACQ;CAER,MAAM,gBAAgB,mBAAmB,MAAM,GAAG;CAClD,MAAMA,SAA2B,CAAC,MAAM,IAAI,cAAc;AAE1D,SAAQ,QAAR;EACE,KAAK,KACH,QAAO,qBAAqB,QAAQ;GAClC,aAAa;GACb,WAAW;GACX,QAAQ;GACR,QAAQ;GACT,CAAC;EACJ,KAAK,MACH,QAAO,4BAA4B,QAAQ;GACzC,aAAa;GACb,WAAW;GACX,QAAQ;GACR,QAAQ;GACT,CAAC;EACJ,KAAK,MACH,QAAO,4BAA4B,QAAQ;GACzC,aAAa;GACb,WAAW;GACX,QAAQ;GACR,QAAQ;GACT,CAAC;EACJ,KAAK;EACL,KAAK,OAAO;GAKV,MAAM,MAAM,OAAO;GACnB,MAAM,MAAM,OAAO;AAGnB,OAAI,MAAM,OAAO,MAAM,GACrB,QAAO;GAGT,MAAM,aAAa,OAAO,IAAI,MAAM;GACpC,MAAM,aAAa,OAAO,IAAI,MAAM;GAGpC,MAAM,iBAAiB,GAAG,KAAK,IAAI,IAAI,CAAC,QAAQ,GAAG,CAAC,GAAG,WAAW,KAAK,KAAK,IAAI,IAAI,CAAC,QAAQ,GAAG,CAAC,GAAG;GAEpG,MAAM,WAAW,iBACf,kBAAkB,IAClB,SACD,CAAC,eAAe;AAGjB,OAAI,CAAC,SAAS,OAAO;AACnB,WAAO,MACL,mCAAmC,OAAO,IAAI,SAAS,OAAO,KAAK,KAAK,GACzE;AACD,WAAO;;AAGT,UAAO,SAAS,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmF/B,SAAgB,qBACd,IACA,SAC4B;CAC5B,MAAM,YAAY,WAAW,WAAW;CACxC,MAAM,WAAW,MAAM;AAEvB,KAAI,CAAC,SACH,OAAM,IAAI,MACR,0FACD;CAGH,MAAM,kBAAkB,SAAS;CAGjC,MAAM,EAAE,OAAO,cAAc,sBAAsB,IAAI,SAAS;CAGhE,MAAM,WAAW,cACT,mBAAmB,MAAM,WAAW,EAC1C,CAAC,MAAM,WAAW,CACnB;CAGD,MAAM,iBAAiB,cAAc;EAEnC,MAAM,yBACJ,MAAM,WAAW,UAAU,MAAM,WAAW,QACxC,0BACA;AAEN,MAAI,EAAE,YAAY,MAAM,YACtB,QAAO,kBAAkB;AAI3B,MAAI,gBACF,KAAI;AACF,UAAO,gBAAgB,SAAS;WACzB,OAAO;AACd,UAAO,MACL,4BAA4B,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM,GACnF;AACD,UAAO,kBAAkB;;AAK7B,SAAO,iBAAiB,MAAM,YAAY,MAAM,OAAO;IACtD;EAAC;EAAU;EAAiB,MAAM;EAAQ,MAAM;EAAW,CAAC;AAG/D,QAAO,eACE;EACL;EACA;EACA;EACA,eAAe,MAAM;EACtB,GACD;EAAC;EAAgB;EAAW;EAAU,MAAM;EAAO,CACpD"}
1
+ {"version":3,"file":"use-cursor-coordinates.js","names":["latLon: [number, number]"],"sources":["../../src/cursor-coordinates/use-cursor-coordinates.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'use client';\n\nimport {\n coordinateSystems,\n createCoordinate,\n formatDecimalDegrees,\n formatDegreesDecimalMinutes,\n formatDegreesMinutesSeconds,\n} from '@accelint/geo';\nimport type { UniqueId } from '@accelint/core';\nimport 'client-only';\nimport { useContext, useMemo } from 'react';\nimport { createLoggerDomain } from '@/shared/logger';\nimport { MapContext } from '../deckgl/base-map/provider';\nimport {\n DEFAULT_LATLON_COORDS,\n DEFAULT_MGRS_UTM_COORDS,\n LONGITUDE_RANGE,\n MAX_LONGITUDE,\n} from './constants';\nimport { cursorCoordinateStore } from './store';\nimport type {\n CoordinateFormatTypes,\n RawCoordinate,\n UseCursorCoordinatesOptions,\n UseCursorCoordinatesReturn,\n} from './types';\n\nconst logger = createLoggerDomain('[CursorCoordinates]');\n\n/**\n * Normalizes longitude to -180 to 180 range.\n * Handles wraparound including multi-revolution values.\n *\n * @param lon - Longitude value in degrees\n * @returns Normalized longitude between -180 and 180\n */\nfunction normalizeLongitude(lon: number): number {\n return (\n ((((lon + MAX_LONGITUDE) % LONGITUDE_RANGE) + LONGITUDE_RANGE) %\n LONGITUDE_RANGE) -\n MAX_LONGITUDE\n );\n}\n\n/**\n * Builds a RawCoordinate object from a coordinate tuple.\n *\n * @param coord - Coordinate tuple [longitude, latitude] or null\n * @returns RawCoordinate object or null\n */\nfunction buildRawCoordinate(coord: [number, number] | null): RawCoordinate {\n if (!coord) {\n return null;\n }\n\n const normalizedLon = normalizeLongitude(coord[0]);\n\n return {\n longitude: normalizedLon,\n latitude: coord[1],\n };\n}\n\n/**\n * Formats a coordinate using the specified format.\n * Uses @accelint/geo formatters which match CoordinateField precision:\n * - DD: 6 decimal places\n * - DDM: 4 decimal places for minutes\n * - DMS: 2 decimal places for seconds\n *\n * @param coord - Coordinate tuple [longitude, latitude]\n * @param format - Coordinate format type\n * @returns Formatted coordinate string\n * *\n * @remarks\n * **UTM/MGRS Limitations:** UTM and MGRS coordinate systems are only valid between 80°S and 84°N.\n * Coordinates outside this range (e.g., polar regions) will return the default placeholder `--, --`.\n * Other formats (DD, DDM, DMS) work correctly at all latitudes.\n */\nfunction formatCoordinate(\n coord: [number, number],\n format: CoordinateFormatTypes,\n): string {\n // Normalize longitude and convert to [lat, lon] for geo formatters\n const normalizedLon = normalizeLongitude(coord[0]);\n const latLon: [number, number] = [coord[1], normalizedLon];\n\n switch (format) {\n case 'dd':\n return formatDecimalDegrees(latLon, {\n withOrdinal: true,\n separator: ' / ',\n prefix: '',\n suffix: '',\n });\n case 'ddm':\n return formatDegreesDecimalMinutes(latLon, {\n withOrdinal: true,\n separator: ' / ',\n prefix: '',\n suffix: '',\n });\n case 'dms':\n return formatDegreesMinutesSeconds(latLon, {\n withOrdinal: true,\n separator: ' / ',\n prefix: '',\n suffix: '',\n });\n case 'mgrs':\n case 'utm': {\n // UTM and MGRS are only valid between 80°S and 84°N\n // Use createCoordinate for grid-based formats\n // Input format: \"lon E / lat N\" for LONLAT (matching geo package DD tests)\n // Limit to 10 decimal places (geo parser max) and avoid floating point precision issues\n const lat = latLon[0];\n const lon = latLon[1];\n\n // Check if coordinate is within valid UTM/MGRS range\n if (lat < -80 || lat > 84) {\n return DEFAULT_MGRS_UTM_COORDS;\n }\n\n const latOrdinal = lat >= 0 ? 'N' : 'S';\n const lonOrdinal = lon >= 0 ? 'E' : 'W';\n // Use LONLAT format: longitude first, then latitude\n // toFixed(10) ensures we stay within the parser's regex limits\n const formattedInput = `${Math.abs(lon).toFixed(10)} ${lonOrdinal} / ${Math.abs(lat).toFixed(10)} ${latOrdinal}`;\n\n const geoCoord = createCoordinate(\n coordinateSystems.dd,\n 'LONLAT',\n )(formattedInput);\n\n // Validate the coordinate was created successfully\n if (!geoCoord.valid) {\n logger.error(\n `Failed to create coordinate for ${format}: ${geoCoord.errors.join(', ')}`,\n );\n return DEFAULT_MGRS_UTM_COORDS;\n }\n\n return geoCoord[format]();\n }\n }\n}\n\n/**\n * React hook that tracks and formats the cursor hover position coordinates on a map.\n *\n * Subscribes to map hover events via the event bus and converts coordinates to various\n * geographic formats (Decimal Degrees, DMS, MGRS, UTM, etc.). The hook automatically\n * filters events to only process those from the specified map instance.\n *\n * Uses the shared store factory for efficient state management and automatic cleanup.\n *\n * @param id - Optional map instance ID. If not provided, attempts to use the ID from MapProvider context.\n * @param options - Optional configuration options\n * @returns Object containing the formatted coordinate string, raw coordinate, format setter, and current format\n * @throws {Error} When no id is provided and hook is used outside MapProvider context\n *\n * @remarks\n * **UTM/MGRS Limitations:** UTM and MGRS coordinate systems are only valid between 80°S and 84°N.\n * Coordinates outside this range (e.g., polar regions) will display the default placeholder `--, --`.\n * Other formats (DD, DDM, DMS) work correctly at all latitudes.\n *\n * @example\n * Basic usage:\n * ```tsx\n * import { uuid } from '@accelint/core';\n * import { useCursorCoordinates } from '@accelint/map-toolkit/cursor-coordinates';\n *\n * const MAP_ID = uuid();\n *\n * function CoordinateDisplay() {\n * const { formattedCoord, setFormat } = useCursorCoordinates(MAP_ID);\n *\n * return (\n * <div>\n * <select onChange={(e) => setFormat(e.target.value as CoordinateFormatTypes)}>\n * <option value=\"dd\">Decimal Degrees</option>\n * <option value=\"ddm\">Degrees Decimal Minutes</option>\n * <option value=\"dms\">Degrees Minutes Seconds</option>\n * <option value=\"mgrs\">MGRS</option>\n * <option value=\"utm\">UTM</option>\n * </select>\n * <div>{formattedCoord}</div>\n * </div>\n * );\n * }\n * ```\n *\n * @example\n * With custom formatter:\n * ```tsx\n * function CustomCoordinateDisplay() {\n * const { formattedCoord, rawCoord } = useCursorCoordinates(MAP_ID, {\n * formatter: (coord) =>\n * `Lat: ${coord.latitude.toFixed(6)}° Lng: ${coord.longitude.toFixed(6)}°`,\n * });\n *\n * return <div>{formattedCoord}</div>;\n * }\n * ```\n *\n * @example\n * Accessing raw coordinates:\n * ```tsx\n * function RawCoordinateDisplay() {\n * const { rawCoord, currentFormat } = useCursorCoordinates(MAP_ID);\n *\n * if (!rawCoord) {\n * return <div>Move cursor over map</div>;\n * }\n *\n * return (\n * <div>\n * <div>Longitude: {rawCoord.longitude}</div>\n * <div>Latitude: {rawCoord.latitude}</div>\n * <div>Format: {currentFormat}</div>\n * </div>\n * );\n * }\n * ```\n */\nexport function useCursorCoordinates(\n id?: UniqueId,\n options?: UseCursorCoordinatesOptions,\n): UseCursorCoordinatesReturn {\n const contextId = useContext(MapContext);\n const actualId = id ?? contextId;\n\n if (!actualId) {\n throw new Error(\n 'useCursorCoordinates requires either an id parameter or to be used within a MapProvider',\n );\n }\n\n const customFormatter = options?.formatter;\n\n // Use the store hook to get state and actions\n const { state, setFormat } = cursorCoordinateStore.use(actualId);\n\n // Build raw coordinate object\n const rawCoord = useMemo(\n () => buildRawCoordinate(state.coordinate),\n [state.coordinate],\n );\n\n // Compute formatted coordinate string\n const formattedCoord = useMemo(() => {\n // Return default coords based on current format.\n const getDefaultCoords = () =>\n state.format === 'mgrs' || state.format === 'utm'\n ? DEFAULT_MGRS_UTM_COORDS\n : DEFAULT_LATLON_COORDS;\n\n if (!(rawCoord && state.coordinate)) {\n return getDefaultCoords();\n }\n\n // Use custom formatter if provided\n if (customFormatter) {\n try {\n return customFormatter(rawCoord);\n } catch (error) {\n logger.withError(error).error('Custom formatter failed');\n return getDefaultCoords();\n }\n }\n\n // Use built-in formatter\n return formatCoordinate(state.coordinate, state.format);\n }, [rawCoord, customFormatter, state.format, state.coordinate]);\n\n // Memoize the return value to prevent unnecessary re-renders\n return useMemo(\n () => ({\n formattedCoord,\n setFormat,\n rawCoord,\n currentFormat: state.format,\n }),\n [formattedCoord, setFormat, rawCoord, state.format],\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAuCA,MAAM,SAAS,mBAAmB,sBAAsB;;;;;;;;AASxD,SAAS,mBAAmB,KAAqB;AAC/C,UACM,MAAM,iBAAiB,kBAAmB,mBAC5C,kBACF;;;;;;;;AAUJ,SAAS,mBAAmB,OAA+C;AACzE,KAAI,CAAC,MACH,QAAO;AAKT,QAAO;EACL,WAHoB,mBAAmB,MAAM,GAAG;EAIhD,UAAU,MAAM;EACjB;;;;;;;;;;;;;;;;;;AAmBH,SAAS,iBACP,OACA,QACQ;CAER,MAAM,gBAAgB,mBAAmB,MAAM,GAAG;CAClD,MAAMA,SAA2B,CAAC,MAAM,IAAI,cAAc;AAE1D,SAAQ,QAAR;EACE,KAAK,KACH,QAAO,qBAAqB,QAAQ;GAClC,aAAa;GACb,WAAW;GACX,QAAQ;GACR,QAAQ;GACT,CAAC;EACJ,KAAK,MACH,QAAO,4BAA4B,QAAQ;GACzC,aAAa;GACb,WAAW;GACX,QAAQ;GACR,QAAQ;GACT,CAAC;EACJ,KAAK,MACH,QAAO,4BAA4B,QAAQ;GACzC,aAAa;GACb,WAAW;GACX,QAAQ;GACR,QAAQ;GACT,CAAC;EACJ,KAAK;EACL,KAAK,OAAO;GAKV,MAAM,MAAM,OAAO;GACnB,MAAM,MAAM,OAAO;AAGnB,OAAI,MAAM,OAAO,MAAM,GACrB,QAAO;GAGT,MAAM,aAAa,OAAO,IAAI,MAAM;GACpC,MAAM,aAAa,OAAO,IAAI,MAAM;GAGpC,MAAM,iBAAiB,GAAG,KAAK,IAAI,IAAI,CAAC,QAAQ,GAAG,CAAC,GAAG,WAAW,KAAK,KAAK,IAAI,IAAI,CAAC,QAAQ,GAAG,CAAC,GAAG;GAEpG,MAAM,WAAW,iBACf,kBAAkB,IAClB,SACD,CAAC,eAAe;AAGjB,OAAI,CAAC,SAAS,OAAO;AACnB,WAAO,MACL,mCAAmC,OAAO,IAAI,SAAS,OAAO,KAAK,KAAK,GACzE;AACD,WAAO;;AAGT,UAAO,SAAS,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmF/B,SAAgB,qBACd,IACA,SAC4B;CAC5B,MAAM,YAAY,WAAW,WAAW;CACxC,MAAM,WAAW,MAAM;AAEvB,KAAI,CAAC,SACH,OAAM,IAAI,MACR,0FACD;CAGH,MAAM,kBAAkB,SAAS;CAGjC,MAAM,EAAE,OAAO,cAAc,sBAAsB,IAAI,SAAS;CAGhE,MAAM,WAAW,cACT,mBAAmB,MAAM,WAAW,EAC1C,CAAC,MAAM,WAAW,CACnB;CAGD,MAAM,iBAAiB,cAAc;EAEnC,MAAM,yBACJ,MAAM,WAAW,UAAU,MAAM,WAAW,QACxC,0BACA;AAEN,MAAI,EAAE,YAAY,MAAM,YACtB,QAAO,kBAAkB;AAI3B,MAAI,gBACF,KAAI;AACF,UAAO,gBAAgB,SAAS;WACzB,OAAO;AACd,UAAO,UAAU,MAAM,CAAC,MAAM,0BAA0B;AACxD,UAAO,kBAAkB;;AAK7B,SAAO,iBAAiB,MAAM,YAAY,MAAM,OAAO;IACtD;EAAC;EAAU;EAAiB,MAAM;EAAQ,MAAM;EAAW,CAAC;AAG/D,QAAO,eACE;EACL;EACA;EACA;EACA,eAAe,MAAM;EACtB,GACD;EAAC;EAAgB;EAAW;EAAU,MAAM;EAAO,CACpD"}
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright 2025 Hypergiant Galactic Systems Inc. All rights reserved.
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
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright 2025 Hypergiant Galactic Systems Inc. All rights reserved.
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
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright 2025 Hypergiant Galactic Systems Inc. All rights reserved.
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
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright 2025 Hypergiant Galactic Systems Inc. All rights reserved.
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
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright 2025 Hypergiant Galactic Systems Inc. All rights reserved.
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,7 +11,7 @@
11
11
  */
12
12
 
13
13
  import { BaseMapProps } from "./types.js";
14
- import * as react_jsx_runtime2 from "react/jsx-runtime";
14
+ import * as react_jsx_runtime0 from "react/jsx-runtime";
15
15
 
16
16
  //#region src/deckgl/base-map/index.d.ts
17
17
 
@@ -106,7 +106,7 @@ declare function BaseMap({
106
106
  onViewStateChange,
107
107
  pickingRadius,
108
108
  ...rest
109
- }: BaseMapProps): react_jsx_runtime2.JSX.Element;
109
+ }: BaseMapProps): react_jsx_runtime0.JSX.Element;
110
110
  //#endregion
111
111
  export { BaseMap };
112
112
  //# sourceMappingURL=index.d.ts.map
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright 2025 Hypergiant Galactic Systems Inc. All rights reserved.
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
@@ -16,6 +16,7 @@
16
16
  import { useMapCamera } from "../../camera/store.js";
17
17
  import { MapEvents } from "./events.js";
18
18
  import { getCursor } from "../../map-cursor/store.js";
19
+ import { getMapGeneration } from "../../shared/cleanup.js";
19
20
  import { MapProvider } from "./provider.js";
20
21
  import { DEFAULT_VIEW_STATE } from "../../shared/constants.js";
21
22
  import { DARK_BASE_MAP_STYLE, PARAMETERS, PICKING_RADIUS } from "./constants.js";
@@ -143,6 +144,7 @@ function AddDeckglControl() {
143
144
  * ```
144
145
  */
145
146
  function BaseMap({ id, className, children, controller = true, enableControlEvents = true, interleaved = true, parameters = {}, styleUrl = DARK_BASE_MAP_STYLE, useDevicePixels = false, widgets: widgetsProp = [], defaultView = "2D", initialViewState, onClick, onHover, onViewStateChange, pickingRadius, ...rest }) {
147
+ const mapGeneration = getMapGeneration(id);
146
148
  const deckglInstance = useDeckgl();
147
149
  const container = useId();
148
150
  const mapRef = useRef(null);
@@ -156,7 +158,7 @@ function BaseMap({ id, className, children, controller = true, enableControlEven
156
158
  ...deckglInstance?._deck?._getViewState(),
157
159
  ...cameraState,
158
160
  bearing: cameraState.rotation
159
- }), [cameraState, deckglInstance?._deck?._getViewState]);
161
+ }), [cameraState]);
160
162
  const mapOptions = useMemo(() => ({
161
163
  container,
162
164
  zoom: viewState.zoom,
@@ -212,7 +214,9 @@ function BaseMap({ id, className, children, controller = true, enableControlEven
212
214
  const handleViewStateChange = useEffectEvent((params) => {
213
215
  onViewStateChange?.(params);
214
216
  const { viewId, viewState: { latitude, longitude, zoom } } = params;
215
- const viewport = deckglInstance._deck.getViewports()?.find((vp) => vp.id === viewId);
217
+ const viewports = deckglInstance._deck?.getViewports();
218
+ if (!viewports) return;
219
+ const viewport = viewports.find((vp) => vp.id === viewId);
216
220
  if (!viewport) return;
217
221
  emitViewport({
218
222
  id,
@@ -227,7 +231,8 @@ function BaseMap({ id, className, children, controller = true, enableControlEven
227
231
  const handleResize = useEffectEvent((params) => {
228
232
  if (resizeTimeoutRef.current) clearTimeout(resizeTimeoutRef.current);
229
233
  resizeTimeoutRef.current = setTimeout(() => {
230
- const viewports = deckglInstance._deck.getViewports() ?? [];
234
+ const viewports = deckglInstance._deck?.getViewports();
235
+ if (!viewports) return;
231
236
  for (const vp of viewports) handleViewStateChange({
232
237
  viewId: vp.id,
233
238
  viewState: {
@@ -243,7 +248,8 @@ function BaseMap({ id, className, children, controller = true, enableControlEven
243
248
  }, 200);
244
249
  });
245
250
  const handleLoad = useEffectEvent(() => {
246
- const viewports = deckglInstance._deck.getViewports() ?? [];
251
+ const viewports = deckglInstance._deck?.getViewports();
252
+ if (!viewports) return;
247
253
  for (const vp of viewports) handleViewStateChange({
248
254
  viewId: vp.id,
249
255
  viewState: {
@@ -266,7 +272,9 @@ function BaseMap({ id, className, children, controller = true, enableControlEven
266
272
  }), /* @__PURE__ */ jsx(MapProvider, {
267
273
  id,
268
274
  children: /* @__PURE__ */ jsx(Map, {
269
- onMove: (evt) => setCameraState(evt.viewState),
275
+ onMove: (evt) => {
276
+ setCameraState(evt.viewState);
277
+ },
270
278
  mapStyle: styleUrl,
271
279
  ref: mapRef,
272
280
  ...mapOptions,
@@ -288,7 +296,7 @@ function BaseMap({ id, className, children, controller = true, enableControlEven
288
296
  },
289
297
  children: [/* @__PURE__ */ jsx(AddDeckglControl, {}), children]
290
298
  })
291
- })
299
+ }, mapGeneration)
292
300
  })]
293
301
  });
294
302
  }