@accelint/map-toolkit 1.5.0 → 3.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.
- package/CHANGELOG.md +83 -0
- package/README.md +8 -1
- package/catalog-info.yaml +9 -6
- package/dist/camera/events.js +1 -1
- package/dist/camera/index.d.ts +1 -1
- package/dist/camera/index.js +1 -1
- package/dist/camera/store.d.ts +1 -1
- package/dist/camera/store.js +3 -5
- package/dist/camera/store.js.map +1 -1
- package/dist/camera/types.d.ts +1 -1
- package/dist/camera/types.js +1 -1
- package/dist/cursor-coordinates/constants.js +1 -1
- package/dist/cursor-coordinates/index.d.ts +1 -1
- package/dist/cursor-coordinates/index.js +1 -1
- package/dist/cursor-coordinates/store.d.ts +1 -1
- package/dist/cursor-coordinates/store.js +1 -1
- package/dist/cursor-coordinates/types.d.ts +1 -1
- package/dist/cursor-coordinates/types.js +1 -1
- package/dist/cursor-coordinates/use-cursor-coordinates.d.ts +1 -1
- package/dist/cursor-coordinates/use-cursor-coordinates.js +4 -9
- package/dist/cursor-coordinates/use-cursor-coordinates.js.map +1 -1
- package/dist/deckgl/base-map/constants.js +1 -1
- package/dist/deckgl/base-map/controls.d.ts +1 -1
- package/dist/deckgl/base-map/controls.js +1 -1
- package/dist/deckgl/base-map/events.js +1 -1
- package/dist/deckgl/base-map/index.d.ts +3 -3
- package/dist/deckgl/base-map/index.js +1 -1
- package/dist/deckgl/base-map/provider.d.ts +1 -1
- package/dist/deckgl/base-map/provider.js +1 -1
- package/dist/deckgl/base-map/types.d.ts +1 -1
- package/dist/deckgl/base-map/types.js +1 -1
- package/dist/deckgl/extensions/coffin-corner/coffin-corner-extension.d.ts +144 -0
- package/dist/deckgl/extensions/coffin-corner/coffin-corner-extension.js +535 -0
- package/dist/deckgl/extensions/coffin-corner/coffin-corner-extension.js.map +1 -0
- package/dist/deckgl/extensions/coffin-corner/index.d.ts +17 -0
- package/dist/deckgl/extensions/coffin-corner/index.js +19 -0
- package/dist/deckgl/extensions/coffin-corner/store.d.ts +96 -0
- package/dist/deckgl/extensions/coffin-corner/store.js +173 -0
- package/dist/deckgl/extensions/coffin-corner/store.js.map +1 -0
- package/dist/deckgl/extensions/coffin-corner/types.d.ts +76 -0
- package/dist/deckgl/extensions/coffin-corner/types.js +27 -0
- package/dist/deckgl/extensions/coffin-corner/types.js.map +1 -0
- package/dist/deckgl/extensions/coffin-corner/use-coffin-corner.d.ts +81 -0
- package/dist/deckgl/extensions/coffin-corner/use-coffin-corner.js +75 -0
- package/dist/deckgl/extensions/coffin-corner/use-coffin-corner.js.map +1 -0
- package/dist/deckgl/extensions/index.d.ts +15 -0
- package/dist/deckgl/extensions/index.js +16 -0
- package/dist/deckgl/index.d.ts +9 -4
- package/dist/deckgl/index.js +6 -2
- package/dist/deckgl/saved-viewports/index.d.ts +1 -1
- package/dist/deckgl/saved-viewports/index.js +1 -1
- package/dist/deckgl/saved-viewports/storage.d.ts +1 -1
- package/dist/deckgl/saved-viewports/storage.js +5 -10
- package/dist/deckgl/saved-viewports/storage.js.map +1 -1
- package/dist/deckgl/shapes/display-shape-layer/constants.js +70 -26
- package/dist/deckgl/shapes/display-shape-layer/constants.js.map +1 -1
- package/dist/deckgl/shapes/display-shape-layer/fiber.d.ts +1 -1
- package/dist/deckgl/shapes/display-shape-layer/fiber.js +1 -1
- package/dist/deckgl/shapes/display-shape-layer/index.d.ts +93 -38
- package/dist/deckgl/shapes/display-shape-layer/index.js +433 -187
- package/dist/deckgl/shapes/display-shape-layer/index.js.map +1 -1
- package/dist/deckgl/shapes/display-shape-layer/shape-label-layer.js +1 -1
- package/dist/deckgl/shapes/display-shape-layer/store.js +1 -1
- package/dist/deckgl/shapes/display-shape-layer/types.d.ts +116 -19
- package/dist/deckgl/shapes/display-shape-layer/types.js +1 -1
- package/dist/deckgl/shapes/display-shape-layer/use-select-shape.d.ts +1 -1
- package/dist/deckgl/shapes/display-shape-layer/use-select-shape.js +1 -1
- package/dist/deckgl/shapes/display-shape-layer/utils/display-style.js +66 -36
- package/dist/deckgl/shapes/display-shape-layer/utils/display-style.js.map +1 -1
- package/dist/deckgl/shapes/display-shape-layer/utils/elevation.js +407 -0
- package/dist/deckgl/shapes/display-shape-layer/utils/elevation.js.map +1 -0
- package/dist/deckgl/shapes/display-shape-layer/utils/icon-config.js +106 -0
- package/dist/deckgl/shapes/display-shape-layer/utils/icon-config.js.map +1 -0
- package/dist/deckgl/shapes/display-shape-layer/utils/labels.d.ts +1 -1
- package/dist/deckgl/shapes/display-shape-layer/utils/labels.js +28 -39
- package/dist/deckgl/shapes/display-shape-layer/utils/labels.js.map +1 -1
- package/dist/deckgl/shapes/display-shape-layer/utils/radius-label.js +53 -0
- package/dist/deckgl/shapes/display-shape-layer/utils/radius-label.js.map +1 -0
- package/dist/deckgl/shapes/draw-shape-layer/constants.js +1 -1
- package/dist/deckgl/shapes/draw-shape-layer/events.d.ts +1 -1
- package/dist/deckgl/shapes/draw-shape-layer/events.js +1 -1
- package/dist/deckgl/shapes/draw-shape-layer/fiber.js +1 -1
- package/dist/deckgl/shapes/draw-shape-layer/index.d.ts +8 -4
- package/dist/deckgl/shapes/draw-shape-layer/index.js +11 -17
- package/dist/deckgl/shapes/draw-shape-layer/index.js.map +1 -1
- package/dist/deckgl/shapes/draw-shape-layer/modes/draw-circle-mode-with-tooltip.js +6 -5
- package/dist/deckgl/shapes/draw-shape-layer/modes/draw-circle-mode-with-tooltip.js.map +1 -1
- package/dist/deckgl/shapes/draw-shape-layer/modes/draw-ellipse-mode-with-tooltip.js +4 -3
- package/dist/deckgl/shapes/draw-shape-layer/modes/draw-ellipse-mode-with-tooltip.js.map +1 -1
- package/dist/deckgl/shapes/draw-shape-layer/modes/draw-line-string-mode-with-tooltip.js +6 -20
- package/dist/deckgl/shapes/draw-shape-layer/modes/draw-line-string-mode-with-tooltip.js.map +1 -1
- package/dist/deckgl/shapes/draw-shape-layer/modes/draw-polygon-mode-with-tooltip.js +6 -33
- package/dist/deckgl/shapes/draw-shape-layer/modes/draw-polygon-mode-with-tooltip.js.map +1 -1
- package/dist/deckgl/shapes/draw-shape-layer/modes/draw-rectangle-mode-with-tooltip.js +16 -12
- package/dist/deckgl/shapes/draw-shape-layer/modes/draw-rectangle-mode-with-tooltip.js.map +1 -1
- package/dist/deckgl/shapes/draw-shape-layer/modes/index.js +2 -32
- package/dist/deckgl/shapes/draw-shape-layer/modes/index.js.map +1 -1
- package/dist/deckgl/shapes/draw-shape-layer/store.js +1 -1
- package/dist/deckgl/shapes/draw-shape-layer/types.d.ts +3 -3
- package/dist/deckgl/shapes/draw-shape-layer/types.js +1 -1
- package/dist/deckgl/shapes/draw-shape-layer/use-draw-shape.d.ts +1 -1
- package/dist/deckgl/shapes/draw-shape-layer/use-draw-shape.js +1 -1
- package/dist/deckgl/shapes/draw-shape-layer/utils/feature-conversion.js +3 -8
- package/dist/deckgl/shapes/draw-shape-layer/utils/feature-conversion.js.map +1 -1
- package/dist/deckgl/shapes/edit-shape-layer/constants.js +17 -2
- package/dist/deckgl/shapes/edit-shape-layer/constants.js.map +1 -1
- package/dist/deckgl/shapes/edit-shape-layer/events.d.ts +1 -1
- package/dist/deckgl/shapes/edit-shape-layer/events.js +1 -1
- package/dist/deckgl/shapes/edit-shape-layer/fiber.d.ts +1 -1
- package/dist/deckgl/shapes/edit-shape-layer/fiber.js +1 -1
- package/dist/deckgl/shapes/edit-shape-layer/index.d.ts +12 -6
- package/dist/deckgl/shapes/edit-shape-layer/index.js +72 -27
- package/dist/deckgl/shapes/edit-shape-layer/index.js.map +1 -1
- package/dist/deckgl/shapes/edit-shape-layer/modes/base-transform-mode.js +4 -1
- package/dist/deckgl/shapes/edit-shape-layer/modes/base-transform-mode.js.map +1 -1
- package/dist/deckgl/shapes/edit-shape-layer/modes/bounding-transform-mode.js +4 -3
- package/dist/deckgl/shapes/edit-shape-layer/modes/bounding-transform-mode.js.map +1 -1
- package/dist/deckgl/shapes/edit-shape-layer/modes/circle-transform-mode.js +5 -3
- package/dist/deckgl/shapes/edit-shape-layer/modes/circle-transform-mode.js.map +1 -1
- package/dist/deckgl/shapes/edit-shape-layer/modes/index.js +1 -1
- package/dist/deckgl/shapes/edit-shape-layer/modes/point-translate-mode.js +1 -1
- package/dist/deckgl/shapes/edit-shape-layer/modes/point-translate-mode.js.map +1 -1
- package/dist/deckgl/shapes/edit-shape-layer/modes/rotate-mode-with-snap.js +1 -1
- package/dist/deckgl/shapes/edit-shape-layer/modes/rotate-mode-with-snap.js.map +1 -1
- package/dist/deckgl/shapes/edit-shape-layer/modes/scale-mode-with-free-transform.js +1 -1
- package/dist/deckgl/shapes/edit-shape-layer/modes/scale-mode-with-free-transform.js.map +1 -1
- package/dist/deckgl/shapes/edit-shape-layer/modes/vertex-transform-mode.js +1 -1
- package/dist/deckgl/shapes/edit-shape-layer/modes/vertex-transform-mode.js.map +1 -1
- package/dist/deckgl/shapes/edit-shape-layer/store.js +83 -14
- package/dist/deckgl/shapes/edit-shape-layer/store.js.map +1 -1
- package/dist/deckgl/shapes/edit-shape-layer/types.d.ts +18 -4
- package/dist/deckgl/shapes/edit-shape-layer/types.js +1 -1
- package/dist/deckgl/shapes/edit-shape-layer/use-edit-shape.d.ts +2 -2
- package/dist/deckgl/shapes/edit-shape-layer/use-edit-shape.js +8 -4
- package/dist/deckgl/shapes/edit-shape-layer/use-edit-shape.js.map +1 -1
- package/dist/deckgl/shapes/index.d.ts +5 -4
- package/dist/deckgl/shapes/index.js +3 -2
- package/dist/deckgl/shapes/shared/constants.d.ts +4 -3
- package/dist/deckgl/shapes/shared/constants.js +51 -11
- package/dist/deckgl/shapes/shared/constants.js.map +1 -1
- package/dist/deckgl/shapes/shared/events.d.ts +5 -1
- package/dist/deckgl/shapes/shared/events.js +1 -1
- package/dist/deckgl/shapes/shared/events.js.map +1 -1
- package/dist/deckgl/shapes/shared/hooks/use-shift-zoom-disable.js +19 -16
- package/dist/deckgl/shapes/shared/hooks/use-shift-zoom-disable.js.map +1 -1
- package/dist/deckgl/shapes/shared/types.d.ts +182 -54
- package/dist/deckgl/shapes/shared/types.js +155 -2
- package/dist/deckgl/shapes/shared/types.js.map +1 -1
- package/dist/deckgl/shapes/shared/utils/duplicate-shape.d.ts +56 -0
- package/dist/deckgl/shapes/shared/utils/duplicate-shape.js +131 -0
- package/dist/deckgl/shapes/shared/utils/duplicate-shape.js.map +1 -0
- package/dist/deckgl/shapes/shared/utils/geometry-measurements.js +29 -24
- package/dist/deckgl/shapes/shared/utils/geometry-measurements.js.map +1 -1
- package/dist/deckgl/shapes/shared/utils/layer-config.js +15 -9
- package/dist/deckgl/shapes/shared/utils/layer-config.js.map +1 -1
- package/dist/deckgl/shapes/shared/utils/mode-utils.js +50 -20
- package/dist/deckgl/shapes/shared/utils/mode-utils.js.map +1 -1
- package/dist/deckgl/shapes/shared/utils/pick-filtering.js +22 -15
- package/dist/deckgl/shapes/shared/utils/pick-filtering.js.map +1 -1
- package/dist/deckgl/shapes/shared/utils/style-utils.d.ts +38 -14
- package/dist/deckgl/shapes/shared/utils/style-utils.js +43 -32
- package/dist/deckgl/shapes/shared/utils/style-utils.js.map +1 -1
- package/dist/deckgl/symbol-layer/fiber.d.ts +4 -2
- package/dist/deckgl/symbol-layer/fiber.js +1 -1
- package/dist/deckgl/symbol-layer/fiber.js.map +1 -1
- package/dist/deckgl/symbol-layer/index.d.ts +1 -1
- package/dist/deckgl/symbol-layer/index.js +1 -1
- package/dist/deckgl/text-layer/character-sets.js +1 -1
- package/dist/deckgl/text-layer/default-settings.d.ts +1 -1
- package/dist/deckgl/text-layer/default-settings.js +1 -1
- package/dist/deckgl/text-layer/fiber.d.ts +1 -1
- package/dist/deckgl/text-layer/fiber.js +1 -1
- package/dist/deckgl/text-layer/index.d.ts +1 -1
- package/dist/deckgl/text-layer/index.js +1 -1
- package/dist/deckgl/text-settings.d.ts +3 -3
- package/dist/deckgl/text-settings.js +1 -1
- package/dist/map-cursor/events.js +1 -1
- package/dist/map-cursor/index.d.ts +1 -1
- package/dist/map-cursor/index.js +1 -1
- package/dist/map-cursor/store.d.ts +1 -1
- package/dist/map-cursor/store.js +1 -1
- package/dist/map-cursor/types.d.ts +1 -1
- package/dist/map-cursor/types.js +1 -1
- package/dist/map-cursor/use-map-cursor.d.ts +1 -1
- package/dist/map-cursor/use-map-cursor.js +1 -1
- package/dist/map-mode/events.js +1 -1
- package/dist/map-mode/index.d.ts +1 -1
- package/dist/map-mode/index.js +1 -1
- package/dist/map-mode/store.d.ts +1 -1
- package/dist/map-mode/store.js +3 -8
- package/dist/map-mode/store.js.map +1 -1
- package/dist/map-mode/types.d.ts +1 -1
- package/dist/map-mode/types.js +1 -1
- package/dist/map-mode/use-map-mode.d.ts +1 -1
- package/dist/map-mode/use-map-mode.js +1 -1
- package/dist/maplibre/hooks/use-maplibre.d.ts +1 -1
- package/dist/maplibre/hooks/use-maplibre.js +1 -1
- package/dist/maplibre/index.d.ts +1 -1
- package/dist/maplibre/index.js +1 -1
- package/dist/shared/cleanup.d.ts +1 -1
- package/dist/shared/cleanup.js +1 -1
- package/dist/shared/constants.js +1 -1
- package/dist/shared/create-map-store.d.ts +1 -1
- package/dist/shared/create-map-store.js +1 -1
- package/dist/shared/logger.js +31 -0
- package/dist/shared/logger.js.map +1 -0
- package/dist/shared/units.d.ts +15 -56
- package/dist/shared/units.js +2 -53
- package/dist/shared/units.js.map +1 -1
- package/dist/viewport/index.d.ts +3 -4
- package/dist/viewport/index.js +2 -3
- package/dist/viewport/store.d.ts +1 -1
- package/dist/viewport/store.js +1 -1
- package/dist/viewport/types.d.ts +9 -5
- package/dist/viewport/types.js +1 -1
- package/dist/viewport/utils.d.ts +4 -4
- package/dist/viewport/utils.js +17 -9
- package/dist/viewport/utils.js.map +1 -1
- package/dist/viewport/viewport-size.d.ts +7 -6
- package/dist/viewport/viewport-size.js +3 -3
- package/dist/viewport/viewport-size.js.map +1 -1
- package/package.json +29 -20
- package/dist/hotkey-manager/dist/react/use-hotkey.js +0 -39
- package/dist/hotkey-manager/dist/react/use-hotkey.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,88 @@
|
|
|
1
1
|
# @accelint/map-toolkit
|
|
2
2
|
|
|
3
|
+
## 3.0.0
|
|
4
|
+
|
|
5
|
+
### Major Changes
|
|
6
|
+
|
|
7
|
+
- fa05228: Fix viewport size display to use SI/ICAO-compliant unit symbols instead of uppercased abbreviations. For example, `km` instead of `KM`, `m` instead of `M`. Nautical miles changed from `nm` to `NM` per ICAO/IMO convention.
|
|
8
|
+
|
|
9
|
+
### Breaking changes
|
|
10
|
+
|
|
11
|
+
**New peer dependency:** `@accelint/constants` is now required.
|
|
12
|
+
|
|
13
|
+
**Removed exports** (from `@accelint/map-toolkit/viewport`):
|
|
14
|
+
- `DISTANCE_UNIT_ABBREVIATIONS` → use `DISTANCE_UNIT_SYMBOLS` from `@accelint/constants/units`
|
|
15
|
+
- `DistanceUnitAbbreviation` type → use `DistanceUnitSymbol` from `@accelint/constants/units`
|
|
16
|
+
- `DistanceUnit` type → use `DistanceUnit` from `@accelint/constants/units`
|
|
17
|
+
- `SupportedDistanceUnit` type → use `DistanceUnitSymbol` from `@accelint/constants/units`
|
|
18
|
+
- `getDistanceUnitFromAbbreviation()` → use `DISTANCE_UNIT_BY_SYMBOL[symbol]` from `@accelint/constants/units`
|
|
19
|
+
- `getDistanceUnitAbbreviation()` → use `DISTANCE_UNIT_SYMBOLS[unit]` from `@accelint/constants/units`
|
|
20
|
+
|
|
21
|
+
**Symbol value changes:** Unit symbols are now SI-compliant though nautical miles uses the ICAO/IMO convention of NM. If you were matching on string values, update accordingly:
|
|
22
|
+
- `'nm'` → `'NM'` (nautical miles)
|
|
23
|
+
|
|
24
|
+
### Minor Changes
|
|
25
|
+
|
|
26
|
+
- b3de1bb: Add radius label on hover for circle shapes in `DisplayShapeLayer`. When hovering a circle, the radius is displayed in the configured `unit` (defaults to nautical miles). The label positions relative to the shape's text label: below it when `showLabels` is `'always'` or `'hover'`, and in its place when `showLabels` is `'never'`. Also adds the `unit` prop to `DisplayShapeLayerProps` for configuring distance display units.
|
|
27
|
+
- 0c3f356: Add `duplicateShape()` utility for cloning shapes with a new ID. Supports optional coordinate offset and custom naming. The resolved name is also set as the clone's map label. Includes new `GeoPosition` type for 2D or 3D coordinate tuples and a `DuplicateShape` Storybook story.
|
|
28
|
+
- 1b39e64: Add CoffinCornerExtension for icon selection/hover bracket indicators
|
|
29
|
+
|
|
30
|
+
New deck.gl LayerExtension that renders bracket corner indicators around
|
|
31
|
+
selected and hovered icons via GPU shaders. Includes useCoffinCorner hook
|
|
32
|
+
for managing selection state, event bus integration, and configurable
|
|
33
|
+
highlight colors. SymbolLayer fiber type now includes CoffinCornerExtension
|
|
34
|
+
props by default.
|
|
35
|
+
|
|
36
|
+
### Patch Changes
|
|
37
|
+
|
|
38
|
+
- 75fa668: - When using icons for Point shapes, `EditShapeLayer` now displays icon markers during editing instead of plain circles.
|
|
39
|
+
- `DrawShapeLayer` and `EditShapeLayer` now self-register their fiber dependencies and no longer require manual fiber imports.
|
|
40
|
+
- `useEditShape()` now exposes `updateFeature` to allow form-based updates before saving edits
|
|
41
|
+
- 17c4b6c: Extend CoffinCornerExtension to support ScatterplotLayer (circle points without icons) alongside IconLayer, and refine DisplayShapeLayer integration.
|
|
42
|
+
- CoffinCornerExtension now supports ScatterplotLayer with quad expansion and circle-replicating fragment shaders
|
|
43
|
+
- DisplayShapeLayer skips the highlight outline layer for all Point geometries (coffin corner brackets handle hover/select feedback)
|
|
44
|
+
- DisplayShapeLayer forwards `highlightColor` as the bracket fill color via a cached tuple to avoid per-render allocations
|
|
45
|
+
- Stable `DISPLAY_EXTENSIONS` module-level constant prevents unnecessary `getShaders()` re-evaluation
|
|
46
|
+
- Removed icon-atlas-based coffin corner SVG sprites (replaced by shader SDF rendering)
|
|
47
|
+
|
|
48
|
+
- Updated dependencies [9a25205]
|
|
49
|
+
- Updated dependencies [fa05228]
|
|
50
|
+
- @accelint/logger@1.1.0
|
|
51
|
+
- @accelint/constants@0.3.0
|
|
52
|
+
|
|
53
|
+
## 2.0.0
|
|
54
|
+
|
|
55
|
+
### Major Changes
|
|
56
|
+
|
|
57
|
+
- 7b30771: Add `enableElevation` prop to DisplayShapeLayer for 3D shapes rendering.
|
|
58
|
+
|
|
59
|
+
Remove dotted border treatment on shape selection — interactions no longer modify a shape's innate styling.
|
|
60
|
+
|
|
61
|
+
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×).
|
|
62
|
+
|
|
63
|
+
Add new Selection layer for brightness effect while shape is selected.
|
|
64
|
+
|
|
65
|
+
Add optional `maxElevation` property to `StyledFeatureProperties` as the source of truth for Polygon shape elevation. Point and LineString elevation is derived from z coordinates.
|
|
66
|
+
|
|
67
|
+
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`.
|
|
68
|
+
|
|
69
|
+
### Minor Changes
|
|
70
|
+
|
|
71
|
+
- d5ac9eb: Update EditShapeLayer to include the ability to pan the map while editing a shape when the spacebar is held.
|
|
72
|
+
- 311d7b6: Upgraded @deck.gl packages to 9.2 and removed shape layer double-click workaround
|
|
73
|
+
|
|
74
|
+
### Patch Changes
|
|
75
|
+
|
|
76
|
+
- 5567348: Update logger implementation to prevent singleton pollution.
|
|
77
|
+
- 1106ced: Fix bug where Enter on Save hotkey never unregistered, causing an error on EditShapeLayer remounts.
|
|
78
|
+
- Updated dependencies [3153e74]
|
|
79
|
+
- Updated dependencies [5567348]
|
|
80
|
+
- Updated dependencies [162895c]
|
|
81
|
+
- @accelint/bus@4.0.0
|
|
82
|
+
- @accelint/logger@1.0.1
|
|
83
|
+
- @accelint/core@0.6.0
|
|
84
|
+
- @accelint/hotkey-manager@2.0.0
|
|
85
|
+
|
|
3
86
|
## 1.5.0
|
|
4
87
|
|
|
5
88
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -9,6 +9,8 @@ Map Toolkit is a comprehensive library that provides:
|
|
|
9
9
|
- **Deck.gl Components**: Pre-configured layers and components for rendering geospatial data, including symbol layers for military symbology (MIL-STD-2525)
|
|
10
10
|
- **MapLibre Integration**: Components and utilities for working with MapLibre GL JS
|
|
11
11
|
- **React Support**: React-friendly components with hooks and utilities via `@deckgl-fiber-renderer`
|
|
12
|
+
- **Shapes System**: Draw, display, and edit geometric shapes (circles, ellipses, polygons, rectangles, lines, points) on the map with built-in styling, selection, and duplication
|
|
13
|
+
- **Layer Extensions**: GPU-based visual effects like coffin corner selection/hover indicators, drawn via SDF shaders with no sprite sheet dependencies
|
|
12
14
|
- **Geospatial Utilities**: Helper functions and decorators for common mapping tasks
|
|
13
15
|
|
|
14
16
|
The package is organized by technology (e.g., `deckgl/`, `maplibre/`) with feature-specific exports, allowing you to import only what you need.
|
|
@@ -16,7 +18,7 @@ The package is organized by technology (e.g., `deckgl/`, `maplibre/`) with featu
|
|
|
16
18
|
## Installation
|
|
17
19
|
|
|
18
20
|
```sh
|
|
19
|
-
|
|
21
|
+
pnpm add @accelint/map-toolkit
|
|
20
22
|
```
|
|
21
23
|
|
|
22
24
|
### Optional Dependencies
|
|
@@ -48,6 +50,9 @@ import { SymbolLayer } from '@accelint/map-toolkit/deckgl';
|
|
|
48
50
|
// MapLibre components
|
|
49
51
|
import { /* components */ } from '@accelint/map-toolkit/maplibre';
|
|
50
52
|
|
|
53
|
+
// Layer extensions
|
|
54
|
+
import { CoffinCornerExtension, useCoffinCorner } from '@accelint/map-toolkit/deckgl';
|
|
55
|
+
|
|
51
56
|
// React/Fiber components
|
|
52
57
|
import { /* components */ } from '@accelint/map-toolkit/deckgl/fiber';
|
|
53
58
|
|
|
@@ -100,6 +105,8 @@ pnpm --filter=@accelint/map-toolkit bench
|
|
|
100
105
|
packages/map-toolkit/
|
|
101
106
|
src/
|
|
102
107
|
deckgl/ # Deck.gl layers and components
|
|
108
|
+
extensions/ # Layer extensions (coffin corners, etc.)
|
|
109
|
+
shapes/ # Shape drawing, display, editing, and utilities
|
|
103
110
|
maplibre/ # MapLibre utilities and components
|
|
104
111
|
decorators/ # Shared decorators and utilities
|
|
105
112
|
```
|
package/catalog-info.yaml
CHANGED
|
@@ -11,15 +11,16 @@ metadata:
|
|
|
11
11
|
|
|
12
12
|
Dependencies:
|
|
13
13
|
|
|
14
|
-
accelint_biome-config@1.1.0, accelint_bus@
|
|
15
|
-
|
|
16
|
-
accelint_geo@0.6.0, accelint_logger@1.
|
|
17
|
-
accelint_postcss-tailwind-css-modules@1.0.1,
|
|
18
|
-
|
|
14
|
+
accelint_biome-config@1.1.0, accelint_bus@4.0.0, accelint_constants@0.3.0,
|
|
15
|
+
accelint_core@0.6.0, accelint_design-foundation@3.0.2,
|
|
16
|
+
accelint_design-toolkit@9.9.0, accelint_geo@0.6.0, accelint_logger@1.1.0,
|
|
17
|
+
accelint_postcss-tailwind-css-modules@1.0.1, accelint_predicates@0.5.2,
|
|
18
|
+
accelint_smeegl@0.3.5, accelint_typescript-config@0.1.4,
|
|
19
|
+
accelint_vitest-config@0.1.6
|
|
19
20
|
annotations:
|
|
20
21
|
backstage.io/edit-url: https://github.com/gohypergiant/standard-toolkit/blob/main/packages/map-toolkit/catalog-info.yaml
|
|
21
22
|
backstage.io/techdocs-ref: dir:.
|
|
22
|
-
package/version:
|
|
23
|
+
package/version: 3.0.0
|
|
23
24
|
github.com/project-slug: gohypergiant/standard-toolkit
|
|
24
25
|
links:
|
|
25
26
|
- url: https://github.com/gohypergiant/standard-toolkit/tree/main/packages/map-toolkit
|
|
@@ -38,12 +39,14 @@ spec:
|
|
|
38
39
|
dependsOn:
|
|
39
40
|
- component:accelint_biome-config
|
|
40
41
|
- component:accelint_bus
|
|
42
|
+
- component:accelint_constants
|
|
41
43
|
- component:accelint_core
|
|
42
44
|
- component:accelint_design-foundation
|
|
43
45
|
- component:accelint_design-toolkit
|
|
44
46
|
- component:accelint_geo
|
|
45
47
|
- component:accelint_logger
|
|
46
48
|
- component:accelint_postcss-tailwind-css-modules
|
|
49
|
+
- component:accelint_predicates
|
|
47
50
|
- component:accelint_smeegl
|
|
48
51
|
- component:accelint_typescript-config
|
|
49
52
|
- component:accelint_vitest-config
|
package/dist/camera/events.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* Copyright
|
|
2
|
+
* Copyright 2026 Hypergiant Galactic Systems Inc. All rights reserved.
|
|
3
3
|
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
|
4
4
|
* you may not use this file except in compliance with the License. You may obtain a copy
|
|
5
5
|
* of the License at https://www.apache.org/licenses/LICENSE-2.0
|
package/dist/camera/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* Copyright
|
|
2
|
+
* Copyright 2026 Hypergiant Galactic Systems Inc. All rights reserved.
|
|
3
3
|
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
|
4
4
|
* you may not use this file except in compliance with the License. You may obtain a copy
|
|
5
5
|
* of the License at https://www.apache.org/licenses/LICENSE-2.0
|
package/dist/camera/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* Copyright
|
|
2
|
+
* Copyright 2026 Hypergiant Galactic Systems Inc. All rights reserved.
|
|
3
3
|
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
|
4
4
|
* you may not use this file except in compliance with the License. You may obtain a copy
|
|
5
5
|
* of the License at https://www.apache.org/licenses/LICENSE-2.0
|
package/dist/camera/store.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* Copyright
|
|
2
|
+
* Copyright 2026 Hypergiant Galactic Systems Inc. All rights reserved.
|
|
3
3
|
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
|
4
4
|
* you may not use this file except in compliance with the License. You may obtain a copy
|
|
5
5
|
* of the License at https://www.apache.org/licenses/LICENSE-2.0
|
package/dist/camera/store.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* Copyright
|
|
2
|
+
* Copyright 2026 Hypergiant Galactic Systems Inc. All rights reserved.
|
|
3
3
|
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
|
4
4
|
* you may not use this file except in compliance with the License. You may obtain a copy
|
|
5
5
|
* of the License at https://www.apache.org/licenses/LICENSE-2.0
|
|
@@ -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 }) => {
|
package/dist/camera/store.js.map
CHANGED
|
@@ -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 // 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
|
+
{"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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"}
|
package/dist/camera/types.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* Copyright
|
|
2
|
+
* Copyright 2026 Hypergiant Galactic Systems Inc. All rights reserved.
|
|
3
3
|
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
|
4
4
|
* you may not use this file except in compliance with the License. You may obtain a copy
|
|
5
5
|
* of the License at https://www.apache.org/licenses/LICENSE-2.0
|
package/dist/camera/types.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* Copyright
|
|
2
|
+
* Copyright 2026 Hypergiant Galactic Systems Inc. All rights reserved.
|
|
3
3
|
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
|
4
4
|
* you may not use this file except in compliance with the License. You may obtain a copy
|
|
5
5
|
* of the License at https://www.apache.org/licenses/LICENSE-2.0
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* Copyright
|
|
2
|
+
* Copyright 2026 Hypergiant Galactic Systems Inc. All rights reserved.
|
|
3
3
|
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
|
4
4
|
* you may not use this file except in compliance with the License. You may obtain a copy
|
|
5
5
|
* of the License at https://www.apache.org/licenses/LICENSE-2.0
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* Copyright
|
|
2
|
+
* Copyright 2026 Hypergiant Galactic Systems Inc. All rights reserved.
|
|
3
3
|
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
|
4
4
|
* you may not use this file except in compliance with the License. You may obtain a copy
|
|
5
5
|
* of the License at https://www.apache.org/licenses/LICENSE-2.0
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* Copyright
|
|
2
|
+
* Copyright 2026 Hypergiant Galactic Systems Inc. All rights reserved.
|
|
3
3
|
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
|
4
4
|
* you may not use this file except in compliance with the License. You may obtain a copy
|
|
5
5
|
* of the License at https://www.apache.org/licenses/LICENSE-2.0
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* Copyright
|
|
2
|
+
* Copyright 2026 Hypergiant Galactic Systems Inc. All rights reserved.
|
|
3
3
|
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
|
4
4
|
* you may not use this file except in compliance with the License. You may obtain a copy
|
|
5
5
|
* of the License at https://www.apache.org/licenses/LICENSE-2.0
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* Copyright
|
|
2
|
+
* Copyright 2026 Hypergiant Galactic Systems Inc. All rights reserved.
|
|
3
3
|
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
|
4
4
|
* you may not use this file except in compliance with the License. You may obtain a copy
|
|
5
5
|
* of the License at https://www.apache.org/licenses/LICENSE-2.0
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* Copyright
|
|
2
|
+
* Copyright 2026 Hypergiant Galactic Systems Inc. All rights reserved.
|
|
3
3
|
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
|
4
4
|
* you may not use this file except in compliance with the License. You may obtain a copy
|
|
5
5
|
* of the License at https://www.apache.org/licenses/LICENSE-2.0
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* Copyright
|
|
2
|
+
* Copyright 2026 Hypergiant Galactic Systems Inc. All rights reserved.
|
|
3
3
|
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
|
4
4
|
* you may not use this file except in compliance with the License. You may obtain a copy
|
|
5
5
|
* of the License at https://www.apache.org/licenses/LICENSE-2.0
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* Copyright
|
|
2
|
+
* Copyright 2026 Hypergiant Galactic Systems Inc. All rights reserved.
|
|
3
3
|
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
|
4
4
|
* you may not use this file except in compliance with the License. You may obtain a copy
|
|
5
5
|
* of the License at https://www.apache.org/licenses/LICENSE-2.0
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* Copyright
|
|
2
|
+
* Copyright 2026 Hypergiant Galactic Systems Inc. All rights reserved.
|
|
3
3
|
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
|
4
4
|
* you may not use this file except in compliance with the License. You may obtain a copy
|
|
5
5
|
* of the License at https://www.apache.org/licenses/LICENSE-2.0
|
|
@@ -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 =
|
|
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(
|
|
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 {\n coordinateSystems,\n createCoordinate,\n formatDecimalDegrees,\n formatDegreesDecimalMinutes,\n formatDegreesMinutesSeconds,\n} from '@accelint/geo';\nimport { getLogger } from '@accelint/logger';\nimport type { UniqueId } from '@accelint/core';\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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
14
|
+
import * as react_jsx_runtime1 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):
|
|
109
|
+
}: BaseMapProps): react_jsx_runtime1.JSX.Element;
|
|
110
110
|
//#endregion
|
|
111
111
|
export { BaseMap };
|
|
112
112
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* Copyright
|
|
2
|
+
* Copyright 2026 Hypergiant Galactic Systems Inc. All rights reserved.
|
|
3
3
|
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
|
4
4
|
* you may not use this file except in compliance with the License. You may obtain a copy
|
|
5
5
|
* of the License at https://www.apache.org/licenses/LICENSE-2.0
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* Copyright
|
|
2
|
+
* Copyright 2026 Hypergiant Galactic Systems Inc. All rights reserved.
|
|
3
3
|
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
|
4
4
|
* you may not use this file except in compliance with the License. You may obtain a copy
|
|
5
5
|
* of the License at https://www.apache.org/licenses/LICENSE-2.0
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* Copyright
|
|
2
|
+
* Copyright 2026 Hypergiant Galactic Systems Inc. All rights reserved.
|
|
3
3
|
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
|
4
4
|
* you may not use this file except in compliance with the License. You may obtain a copy
|
|
5
5
|
* of the License at https://www.apache.org/licenses/LICENSE-2.0
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* Copyright
|
|
2
|
+
* Copyright 2026 Hypergiant Galactic Systems Inc. All rights reserved.
|
|
3
3
|
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
|
4
4
|
* you may not use this file except in compliance with the License. You may obtain a copy
|
|
5
5
|
* of the License at https://www.apache.org/licenses/LICENSE-2.0
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* Copyright
|
|
2
|
+
* Copyright 2026 Hypergiant Galactic Systems Inc. All rights reserved.
|
|
3
3
|
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
|
4
4
|
* you may not use this file except in compliance with the License. You may obtain a copy
|
|
5
5
|
* of the License at https://www.apache.org/licenses/LICENSE-2.0
|