@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
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interaction.js","names":[],"sources":["../../../../../src/deckgl/shapes/display-shape-layer/utils/interaction.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'use client';\n\nimport type { Shape, ShapeId } from '../../shared/types';\n\n/**\n * Compute hover/selection interaction state for a feature by index.\n *\n * @param feature - The feature to check\n * @param selectedShapeId - Currently selected shape ID\n * @param hoverIndex - Currently hovered feature index\n * @param shapeIdToIndex - Map of shapeId to feature index for O(1) lookup\n * @returns `{ isSelected, isHovered }`\n * @example\n * ```typescript\n * const { isSelected, isHovered } = getPointInteractionState(\n * feature,\n * selectedShapeId,\n * hoverIndex,\n * shapeIdToIndex, // Map<ShapeId, number> from getFeaturesWithId()\n * );\n * if (isSelected || isHovered) {\n * // render interaction feedback\n * }\n * ```\n */\nexport function getPointInteractionState(\n feature: Shape['feature'],\n selectedShapeId: ShapeId | undefined,\n hoverIndex: number | undefined,\n shapeIdToIndex: Map<ShapeId, number>,\n): { isSelected: boolean; isHovered: boolean } {\n const shapeId = feature.properties?.shapeId;\n const isSelected = shapeId != null && shapeId === selectedShapeId;\n const featureIndex = shapeId ? shapeIdToIndex.get(shapeId) : undefined;\n const isHovered = hoverIndex !== undefined && featureIndex === hoverIndex;\n return { isSelected, isHovered };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqCA,SAAgB,yBACd,SACA,iBACA,YACA,gBAC6C;CAC7C,MAAM,UAAU,QAAQ,YAAY;CACpC,MAAM,aAAa,WAAW,QAAQ,YAAY;CAClD,MAAM,eAAe,UAAU,eAAe,IAAI,QAAQ,GAAG;AAE7D,QAAO;EAAE;EAAY,WADH,eAAe,UAAa,iBAAiB;EAC/B"}
@@ -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
@@ -58,21 +58,27 @@ import { isCircleShape } from "../../shared/types.js";
58
58
  * };
59
59
  * ```
60
60
  */
61
+ const DEFAULT_LABEL_OFFSET = [0, 10];
62
+ const DEFAULT_LABEL_VERTICAL = "top";
63
+ const DEFAULT_LABEL_HORIZONTAL = "center";
64
+ const DEFAULT_COORDINATE_ANCHOR = "bottom";
65
+ const TEXT_ANCHOR_MAP = {
66
+ left: "start",
67
+ center: "middle",
68
+ right: "end"
69
+ };
70
+ const ALIGNMENT_BASELINE_MAP = {
71
+ top: "top",
72
+ middle: "center",
73
+ bottom: "bottom"
74
+ };
61
75
  /**
62
76
  * Convert vertical/horizontal position to deck.gl textAnchor and alignmentBaseline
63
77
  */
64
78
  function convertPositionToAnchors(vertical, horizontal) {
65
79
  return {
66
- textAnchor: {
67
- left: "start",
68
- center: "middle",
69
- right: "end"
70
- }[horizontal],
71
- alignmentBaseline: {
72
- top: "top",
73
- middle: "center",
74
- bottom: "bottom"
75
- }[vertical]
80
+ textAnchor: TEXT_ANCHOR_MAP[horizontal],
81
+ alignmentBaseline: ALIGNMENT_BASELINE_MAP[vertical]
76
82
  };
77
83
  }
78
84
  /**
@@ -88,7 +94,7 @@ function resolveLabelProperties(shapeOffset, shapeVertical, shapeHorizontal, def
88
94
  * Get position for Point geometry
89
95
  */
90
96
  function getPointPosition(geometry, shapeOffset, shapeVertical, shapeHorizontal, options) {
91
- const resolved = resolveLabelProperties(shapeOffset, shapeVertical, shapeHorizontal, [0, 10], "top", "center", options?.pointLabelOffset, options?.pointLabelVerticalAnchor, options?.pointLabelHorizontalAnchor);
97
+ const resolved = resolveLabelProperties(shapeOffset, shapeVertical, shapeHorizontal, DEFAULT_LABEL_OFFSET, DEFAULT_LABEL_VERTICAL, DEFAULT_LABEL_HORIZONTAL, options?.pointLabelOffset, options?.pointLabelVerticalAnchor, options?.pointLabelHorizontalAnchor);
92
98
  return {
93
99
  coordinates: [geometry.coordinates[0] ?? 0, geometry.coordinates[1] ?? 0],
94
100
  ...resolved
@@ -99,12 +105,8 @@ function getPointPosition(geometry, shapeOffset, shapeVertical, shapeHorizontal,
99
105
  * Uses cardinal direction positioning to find the edge point
100
106
  */
101
107
  function getLineStringPosition(geometry, shapeOffset, shapeVertical, shapeHorizontal, shapeCoordinateAnchor, options) {
102
- const defaultOffset = [0, 10];
103
- const defaultVertical = "top";
104
- const defaultHorizontal = "center";
105
- const defaultCoordinateAnchor = "bottom";
106
- const resolved = resolveLabelProperties(shapeOffset, shapeVertical, shapeHorizontal, defaultOffset, defaultVertical, defaultHorizontal, options?.lineStringLabelOffset, options?.lineStringLabelVerticalAnchor, options?.lineStringLabelHorizontalAnchor);
107
- const coordinateAnchor = shapeCoordinateAnchor ?? options?.lineStringLabelCoordinateAnchor ?? defaultCoordinateAnchor;
108
+ const resolved = resolveLabelProperties(shapeOffset, shapeVertical, shapeHorizontal, DEFAULT_LABEL_OFFSET, DEFAULT_LABEL_VERTICAL, DEFAULT_LABEL_HORIZONTAL, options?.lineStringLabelOffset, options?.lineStringLabelVerticalAnchor, options?.lineStringLabelHorizontalAnchor);
109
+ const coordinateAnchor = shapeCoordinateAnchor ?? options?.lineStringLabelCoordinateAnchor ?? DEFAULT_COORDINATE_ANCHOR;
108
110
  const coordinates = findEdgePoint(geometry.coordinates, coordinateAnchor);
109
111
  if (!coordinates) return null;
110
112
  return {
@@ -120,12 +122,6 @@ function getVertexCoordinate(vertex) {
120
122
  return [vertex[0], vertex[1]];
121
123
  }
122
124
  /**
123
- * Check if a vertex should replace the current target based on edge position
124
- */
125
- function shouldUpdateEdgeVertex(vertexValue, targetValue, position) {
126
- return position === "top" || position === "right" ? vertexValue > targetValue : vertexValue < targetValue;
127
- }
128
- /**
129
125
  * Get the coordinate index based on edge position (0 for x/longitude, 1 for y/latitude)
130
126
  */
131
127
  function getCoordinateIndexForEdgePosition(position) {
@@ -160,14 +156,15 @@ function findEdgePoint(coordinates, position) {
160
156
  if (!coordinates || coordinates.length === 0) return null;
161
157
  if (position === "center") return calculateCentroid(coordinates);
162
158
  let targetVertex = coordinates[0];
159
+ if (!targetVertex) return null;
163
160
  const coordinateIndex = getCoordinateIndexForEdgePosition(position);
161
+ const findMax = position === "top" || position === "right";
164
162
  for (const vertex of coordinates) {
165
163
  if (!vertex) continue;
166
- if (!targetVertex) continue;
167
164
  const vertexValue = vertex[coordinateIndex];
168
165
  const targetValue = targetVertex[coordinateIndex];
169
166
  if (vertexValue === void 0 || targetValue === void 0) continue;
170
- if (shouldUpdateEdgeVertex(vertexValue, targetValue, position)) targetVertex = vertex;
167
+ if (findMax ? vertexValue > targetValue : vertexValue < targetValue) targetVertex = vertex;
171
168
  }
172
169
  return getVertexCoordinate(targetVertex);
173
170
  }
@@ -175,12 +172,8 @@ function findEdgePoint(coordinates, position) {
175
172
  * Get position for Circle geometry (special case of Polygon)
176
173
  */
177
174
  function getCirclePosition(ring, shapeOffset, shapeVertical, shapeHorizontal, shapeCoordinateAnchor, options) {
178
- const defaultOffset = [0, 10];
179
- const defaultVertical = "top";
180
- const defaultHorizontal = "center";
181
- const defaultCoordinateAnchor = "bottom";
182
- const resolved = resolveLabelProperties(shapeOffset, shapeVertical, shapeHorizontal, defaultOffset, defaultVertical, defaultHorizontal, options?.circleLabelOffset, options?.circleLabelVerticalAnchor, options?.circleLabelHorizontalAnchor);
183
- const coordinates = findEdgePoint(ring, shapeCoordinateAnchor ?? options?.circleLabelCoordinateAnchor ?? defaultCoordinateAnchor);
175
+ const resolved = resolveLabelProperties(shapeOffset, shapeVertical, shapeHorizontal, DEFAULT_LABEL_OFFSET, DEFAULT_LABEL_VERTICAL, DEFAULT_LABEL_HORIZONTAL, options?.circleLabelOffset, options?.circleLabelVerticalAnchor, options?.circleLabelHorizontalAnchor);
176
+ const coordinates = findEdgePoint(ring, shapeCoordinateAnchor ?? options?.circleLabelCoordinateAnchor ?? DEFAULT_COORDINATE_ANCHOR);
184
177
  if (!coordinates) return null;
185
178
  return {
186
179
  coordinates,
@@ -194,12 +187,8 @@ function getCirclePosition(ring, shapeOffset, shapeVertical, shapeHorizontal, sh
194
187
  function getPolygonPosition(geometry, shape, shapeOffset, shapeVertical, shapeHorizontal, shapeCoordinateAnchor, options) {
195
188
  const ring = geometry.coordinates[0];
196
189
  if (isCircleShape(shape)) return getCirclePosition(ring, shapeOffset, shapeVertical, shapeHorizontal, shapeCoordinateAnchor, options);
197
- const defaultOffset = [0, 10];
198
- const defaultVertical = "top";
199
- const defaultHorizontal = "center";
200
- const defaultCoordinateAnchor = "bottom";
201
- const resolved = resolveLabelProperties(shapeOffset, shapeVertical, shapeHorizontal, defaultOffset, defaultVertical, defaultHorizontal, options?.polygonLabelOffset, options?.polygonLabelVerticalAnchor, options?.polygonLabelHorizontalAnchor);
202
- const coordinates = findEdgePoint(ring, shapeCoordinateAnchor ?? options?.polygonLabelCoordinateAnchor ?? defaultCoordinateAnchor);
190
+ const resolved = resolveLabelProperties(shapeOffset, shapeVertical, shapeHorizontal, DEFAULT_LABEL_OFFSET, DEFAULT_LABEL_VERTICAL, DEFAULT_LABEL_HORIZONTAL, options?.polygonLabelOffset, options?.polygonLabelVerticalAnchor, options?.polygonLabelHorizontalAnchor);
191
+ const coordinates = findEdgePoint(ring, shapeCoordinateAnchor ?? options?.polygonLabelCoordinateAnchor ?? DEFAULT_COORDINATE_ANCHOR);
203
192
  if (!coordinates) return null;
204
193
  return {
205
194
  coordinates,
@@ -313,7 +302,7 @@ function getLabelPosition2d(shape, options) {
313
302
  * ```
314
303
  */
315
304
  function getLabelText(shape) {
316
- return (shape.label || shape.name).toUpperCase();
305
+ return (shape.label ?? shape.name).toUpperCase();
317
306
  }
318
307
 
319
308
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"labels.js","names":["defaultOffset: [number, number]","defaultVertical: LabelVerticalPosition","defaultHorizontal: LabelHorizontalPosition","defaultCoordinateAnchor: CardinalLabelCoordinateAnchor"],"sources":["../../../../../src/deckgl/shapes/display-shape-layer/utils/labels.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 * Label positioning utilities for shapes\n *\n * This module provides utilities for positioning labels on shapes and calculating\n * points along line segments. Use these to customize label placement in your shape data.\n *\n * ## Label Styling\n *\n * Labels use a text-only style for legibility across different map backgrounds:\n * - **Text**: White uppercase with bold Roboto Mono font at 10px\n * - **Outline**: Black 2px outline around text for contrast\n * - **No background or border**: Clean text-only appearance\n *\n * ### Calculating Offsets\n *\n * When positioning labels, the text height is approximately 10px.\n * For example, to position a label exactly 5px above a point:\n * - Label height ≈ text height (10px)\n * - Offset needed: [0, -(10 + 5)] = [0, -15]\n *\n * @example Position label at the middle of a LineString\n * ```typescript\n * import { getLineStringMidpoint } from '@accelint/map-toolkit/deckgl/shapes/display-shape-layer/utils/labels';\n *\n * // Calculate midpoint for custom positioning\n * const coordinates = [[-122.4, 37.8], [-122.3, 37.9], [-122.2, 38.0]];\n * const midpoint = getLineStringMidpoint(coordinates);\n *\n * // Use in shape data with custom label properties\n * const shape = {\n * feature: {\n * properties: {\n * styleProperties: {\n * labelVerticalAnchor: 'top',\n * labelHorizontalAnchor: 'center',\n * labelOffset: [0, -10], // Small offset above the line\n * }\n * }\n * }\n * };\n * ```\n */\n\n'use client';\n\nimport { isCircleShape } from '../../shared/types';\nimport type { LineString, Point, Polygon } from 'geojson';\nimport type { Shape } from '../../shared/types';\n\n/**\n * Label positioning information including coordinates and screen-space offsets.\n *\n * Defines where a label should be positioned on the map, combining geographic coordinates\n * with text alignment and pixel-level offsets for precise placement.\n */\nexport interface LabelPosition2d {\n /** Geographic coordinates [longitude, latitude] */\n coordinates: [number, number];\n /** Horizontal text anchor point */\n textAnchor: 'start' | 'middle' | 'end';\n /** Vertical text alignment */\n alignmentBaseline: 'top' | 'center' | 'bottom';\n /** Pixel offset from coordinates [x, y] */\n pixelOffset: [number, number];\n}\n\n/**\n * Calculate a point along a line segment\n * @param start - Start coordinate [lon, lat]\n * @param end - End coordinate [lon, lat]\n * @param ratio - Position along segment (0 = start, 0.5 = middle, 1 = end)\n * @returns Interpolated coordinate [lon, lat]\n *\n * @example\n * // Get a point 25% along a line segment\n * const start: [number, number] = [-122.4, 37.8];\n * const end: [number, number] = [-122.3, 37.9];\n * const point = interpolatePoint(start, end, 0.25);\n */\nexport function interpolatePoint(\n start: [number, number],\n end: [number, number],\n ratio: number,\n): [number, number] {\n const clampedRatio = Math.max(0, Math.min(1, ratio));\n return [\n start[0] + (end[0] - start[0]) * clampedRatio,\n start[1] + (end[1] - start[1]) * clampedRatio,\n ];\n}\n\n/**\n * Calculate segment lengths for a line\n */\nfunction calculateSegmentLengths(coordinates: [number, number][]): {\n lengths: number[];\n total: number;\n} {\n let total = 0;\n const lengths: number[] = [];\n\n for (let i = 0; i < coordinates.length - 1; i++) {\n const start = coordinates[i];\n const end = coordinates[i + 1];\n if (start && end) {\n const length = Math.sqrt(\n (end[0] - start[0]) ** 2 + (end[1] - start[1]) ** 2,\n );\n lengths.push(length);\n total += length;\n }\n }\n\n return { lengths, total };\n}\n\n/**\n * Find point at specific distance along line\n */\nfunction findPointAtDistance(\n coordinates: [number, number][],\n segmentLengths: number[],\n targetDistance: number,\n): [number, number] {\n let accumulatedLength = 0;\n\n for (let i = 0; i < segmentLengths.length; i++) {\n const segmentLength = segmentLengths[i];\n if (!segmentLength) {\n continue;\n }\n\n if (accumulatedLength + segmentLength >= targetDistance) {\n const start = coordinates[i];\n const end = coordinates[i + 1];\n if (start && end) {\n const ratio = (targetDistance - accumulatedLength) / segmentLength;\n return interpolatePoint(start, end, ratio);\n }\n }\n accumulatedLength += segmentLength;\n }\n\n return coordinates[coordinates.length - 1] ?? [0, 0];\n}\n\n/**\n * Get the midpoint of a LineString.\n *\n * Calculates the geometric center point along a LineString by measuring distances\n * along each segment. This is more accurate than the bounding box center for curved lines.\n *\n * @param coordinates - LineString coordinates array\n * @returns Midpoint coordinate [lon, lat]\n *\n * @example\n * ```typescript\n * import { getLineStringMidpoint } from '@accelint/map-toolkit/deckgl/shapes/display-shape-layer/utils/labels';\n *\n * const lineCoordinates: [number, number][] = [\n * [-122.4, 37.8],\n * [-122.3, 37.85],\n * [-122.2, 37.9],\n * ];\n *\n * const midpoint = getLineStringMidpoint(lineCoordinates);\n * // Returns the coordinate at the middle of the line's total length\n * ```\n */\nexport function getLineStringMidpoint(\n coordinates: [number, number][],\n): [number, number] {\n if (coordinates.length === 0) {\n return [0, 0];\n }\n if (coordinates.length === 1) {\n return coordinates[0] ?? [0, 0];\n }\n\n const { lengths, total } = calculateSegmentLengths(coordinates);\n const halfLength = total / 2;\n\n return findPointAtDistance(coordinates, lengths, halfLength);\n}\n\n/**\n * Get the end point of a LineString.\n *\n * Returns the last coordinate in the LineString, useful for positioning labels\n * at the end of lines (e.g., directional arrows, route endpoints).\n *\n * @param coordinates - LineString coordinates array\n * @returns End coordinate [lon, lat]\n *\n * @example\n * ```typescript\n * import { getLineStringEndpoint } from '@accelint/map-toolkit/deckgl/shapes/display-shape-layer/utils/labels';\n *\n * const lineCoordinates: [number, number][] = [\n * [-122.4, 37.8],\n * [-122.3, 37.85],\n * [-122.2, 37.9],\n * ];\n *\n * const endpoint = getLineStringEndpoint(lineCoordinates);\n * // Returns: [-122.2, 37.9]\n * ```\n */\nexport function getLineStringEndpoint(\n coordinates: [number, number][],\n): [number, number] {\n if (coordinates.length === 0) {\n return [0, 0];\n }\n return coordinates[coordinates.length - 1] ?? [0, 0];\n}\n\n/**\n * Get the midpoint of a Polygon's outer ring.\n *\n * Calculates the geometric center point along the polygon's perimeter by using\n * the midpoint of the outer ring. This is useful for positioning labels on polygons.\n *\n * @param coordinates - Polygon coordinates array (rings)\n * @returns Midpoint of outer ring [lon, lat]\n *\n * @example\n * ```typescript\n * import { getPolygonMidpoint } from '@accelint/map-toolkit/deckgl/shapes/display-shape-layer/utils/labels';\n *\n * const polygonCoordinates: [number, number][][] = [\n * [\n * [-122.4, 37.8],\n * [-122.3, 37.8],\n * [-122.3, 37.9],\n * [-122.4, 37.9],\n * [-122.4, 37.8], // Closing point\n * ]\n * ];\n *\n * const midpoint = getPolygonMidpoint(polygonCoordinates);\n * // Returns the coordinate at the middle of the outer ring's perimeter\n * ```\n */\nexport function getPolygonMidpoint(\n coordinates: [number, number][][],\n): [number, number] {\n const outerRing = coordinates[0];\n if (!outerRing || outerRing.length === 0) {\n return [0, 0];\n }\n // Use the outer ring (first ring)\n return getLineStringMidpoint(outerRing);\n}\n\n/**\n * Vertical label position relative to anchor point.\n *\n * Controls how the label aligns vertically with its anchor coordinate:\n * - `'top'`: Label text appears below the anchor point\n * - `'middle'`: Label text is vertically centered on the anchor point\n * - `'bottom'`: Label text appears above the anchor point\n */\nexport type LabelVerticalPosition = 'top' | 'middle' | 'bottom';\n\n/**\n * Horizontal label position relative to anchor point.\n *\n * Controls how the label aligns horizontally with its anchor coordinate:\n * - `'left'`: Label text appears to the right of the anchor point\n * - `'center'`: Label text is horizontally centered on the anchor point\n * - `'right'`: Label text appears to the left of the anchor point\n */\nexport type LabelHorizontalPosition = 'left' | 'center' | 'right';\n\n/**\n * Cardinal direction anchor for positioning labels on geometry edges.\n *\n * Uses edge positions relative to the geometry's bounding box.\n * Works for LineString, Polygon, and Circle geometries:\n * - `'center'`: centroid/midpoint of the geometry\n * - `'top'`/`'right'`/`'bottom'`/`'left'`: edge positions based on bounding box\n */\nexport type CardinalLabelCoordinateAnchor =\n | 'center'\n | 'top'\n | 'right'\n | 'bottom'\n | 'left';\n\n/**\n * Global label positioning options for DisplayShapeLayer\n *\n * ## Priority System\n * Label positioning follows a three-tier priority system:\n * 1. **Per-shape properties** in `styleProperties` (highest priority)\n * 2. **Global options** via this interface\n * 3. **Default values** (geometry-specific fallbacks)\n *\n * ## Label Appearance\n *\n * Labels use a clean text-only style:\n * - **Text**: White uppercase, bold Roboto Mono, 10px\n * - **Outline**: Black 2px outline for contrast\n * - **No background or border**\n *\n * Text height ≈ 10px\n *\n * ## Positioning Concepts\n *\n * ### Coordinate Anchor\n * Determines *where* on the geometry to place the label:\n * - **Point**: Label is always at the point coordinate\n * - **LineString/Polygon**: 'start', 'middle', or 'end' along the geometry\n * - **Circle**: 'top', 'right', 'bottom', or 'left' on the perimeter\n *\n * ### Vertical/Horizontal Anchor\n * Determines how the label aligns *relative to* the anchor point:\n * - **Vertical**: 'top' (label text below point), 'middle' (centered), 'bottom' (label text above point)\n * - **Horizontal**: 'left' (label text right of point), 'center' (centered), 'right' (label text left of point)\n *\n * ### Pixel Offset\n * Fine-tune label position with [x, y] pixel offsets:\n * - Positive x moves right, negative moves left\n * - Positive y moves down, negative moves up\n *\n * @example Position circle labels at the top with 5px clearance\n * ```tsx\n * const labelOptions: LabelPositionOptions = {\n * circleLabelCoordinateAnchor: 'top',\n * circleLabelVerticalAnchor: 'bottom', // Label above the top point\n * circleLabelOffset: [0, -15], // -(10px text height + 5px clearance)\n * };\n * ```\n *\n * @example Position line labels at middle with offset to the right\n * ```tsx\n * const labelOptions: LabelPositionOptions = {\n * lineStringLabelCoordinateAnchor: 'middle',\n * lineStringLabelHorizontalAnchor: 'left',\n * lineStringLabelOffset: [10, 0], // 10px to the right\n * };\n * ```\n */\nexport interface LabelPositionOptions {\n // Point geometry options\n /** Vertical anchor for Point labels @default 'top' */\n pointLabelVerticalAnchor?: LabelVerticalPosition;\n /** Horizontal anchor for Point labels @default 'center' */\n pointLabelHorizontalAnchor?: LabelHorizontalPosition;\n /** Pixel offset for Point labels [x, y] @default [0, 10] */\n pointLabelOffset?: [number, number];\n\n // LineString geometry options\n /** Position on LineString edge (top/right/bottom/left) @default 'bottom' */\n lineStringLabelCoordinateAnchor?: CardinalLabelCoordinateAnchor;\n /** Vertical anchor for LineString labels @default 'top' */\n lineStringLabelVerticalAnchor?: LabelVerticalPosition;\n /** Horizontal anchor for LineString labels @default 'center' */\n lineStringLabelHorizontalAnchor?: LabelHorizontalPosition;\n /** Pixel offset for LineString labels [x, y] @default [0, 10] */\n lineStringLabelOffset?: [number, number];\n\n // Polygon geometry options\n /** Position on Polygon edge (top/right/bottom/left) @default 'bottom' */\n polygonLabelCoordinateAnchor?: CardinalLabelCoordinateAnchor;\n /** Vertical anchor for Polygon labels @default 'top' */\n polygonLabelVerticalAnchor?: LabelVerticalPosition;\n /** Horizontal anchor for Polygon labels @default 'center' */\n polygonLabelHorizontalAnchor?: LabelHorizontalPosition;\n /** Pixel offset for Polygon labels [x, y] @default [0, 10] */\n polygonLabelOffset?: [number, number];\n\n // Circle geometry options\n /** Position on Circle perimeter (top/right/bottom/left) @default 'bottom' */\n circleLabelCoordinateAnchor?: CardinalLabelCoordinateAnchor;\n /** Vertical anchor for Circle labels @default 'top' */\n circleLabelVerticalAnchor?: LabelVerticalPosition;\n /** Horizontal anchor for Circle labels @default 'center' */\n circleLabelHorizontalAnchor?: LabelHorizontalPosition;\n /** Pixel offset for Circle labels [x, y] @default [0, 10] */\n circleLabelOffset?: [number, number];\n}\n\n/**\n * Convert vertical/horizontal position to deck.gl textAnchor and alignmentBaseline\n */\nfunction convertPositionToAnchors(\n vertical: LabelVerticalPosition,\n horizontal: LabelHorizontalPosition,\n): {\n textAnchor: 'start' | 'middle' | 'end';\n alignmentBaseline: 'top' | 'center' | 'bottom';\n} {\n // Map horizontal to textAnchor\n const textAnchorMap: Record<\n LabelHorizontalPosition,\n 'start' | 'middle' | 'end'\n > = {\n left: 'start',\n center: 'middle',\n right: 'end',\n };\n\n // Map vertical to alignmentBaseline\n const alignmentBaselineMap: Record<\n LabelVerticalPosition,\n 'top' | 'center' | 'bottom'\n > = {\n top: 'top',\n middle: 'center',\n bottom: 'bottom',\n };\n\n return {\n textAnchor: textAnchorMap[horizontal],\n alignmentBaseline: alignmentBaselineMap[vertical],\n };\n}\n\n/**\n * Helper to resolve label properties with priority: shape > options > default\n */\nfunction resolveLabelProperties(\n shapeOffset: [number, number] | undefined,\n shapeVertical: string | undefined,\n shapeHorizontal: string | undefined,\n defaultOffset: [number, number],\n defaultVertical: LabelVerticalPosition,\n defaultHorizontal: LabelHorizontalPosition,\n optionsOffset?: [number, number],\n optionsVertical?: LabelVerticalPosition,\n optionsHorizontal?: LabelHorizontalPosition,\n) {\n const vertical = (shapeVertical ??\n optionsVertical ??\n defaultVertical) as LabelVerticalPosition;\n const horizontal = (shapeHorizontal ??\n optionsHorizontal ??\n defaultHorizontal) as LabelHorizontalPosition;\n const pixelOffset = shapeOffset ?? optionsOffset ?? defaultOffset;\n\n const anchors = convertPositionToAnchors(vertical, horizontal);\n\n return {\n pixelOffset,\n ...anchors,\n };\n}\n\n/**\n * Get position for Point geometry\n */\nfunction getPointPosition(\n geometry: Point,\n shapeOffset: [number, number] | undefined,\n shapeVertical: string | undefined,\n shapeHorizontal: string | undefined,\n options?: LabelPositionOptions,\n): LabelPosition2d {\n const defaultOffset: [number, number] = [0, 10];\n const defaultVertical: LabelVerticalPosition = 'top';\n const defaultHorizontal: LabelHorizontalPosition = 'center';\n\n const resolved = resolveLabelProperties(\n shapeOffset,\n shapeVertical,\n shapeHorizontal,\n defaultOffset,\n defaultVertical,\n defaultHorizontal,\n options?.pointLabelOffset,\n options?.pointLabelVerticalAnchor,\n options?.pointLabelHorizontalAnchor,\n );\n\n return {\n coordinates: [\n geometry.coordinates[0] ?? 0,\n geometry.coordinates[1] ?? 0,\n ] as [number, number],\n ...resolved,\n };\n}\n\n/**\n * Get position for LineString geometry\n * Uses cardinal direction positioning to find the edge point\n */\nfunction getLineStringPosition(\n geometry: LineString,\n shapeOffset: [number, number] | undefined,\n shapeVertical: string | undefined,\n shapeHorizontal: string | undefined,\n shapeCoordinateAnchor: string | undefined,\n options?: LabelPositionOptions,\n): LabelPosition2d | null {\n const defaultOffset: [number, number] = [0, 10];\n const defaultVertical: LabelVerticalPosition = 'top';\n const defaultHorizontal: LabelHorizontalPosition = 'center';\n const defaultCoordinateAnchor: CardinalLabelCoordinateAnchor = 'bottom';\n\n const resolved = resolveLabelProperties(\n shapeOffset,\n shapeVertical,\n shapeHorizontal,\n defaultOffset,\n defaultVertical,\n defaultHorizontal,\n options?.lineStringLabelOffset,\n options?.lineStringLabelVerticalAnchor,\n options?.lineStringLabelHorizontalAnchor,\n );\n\n // Determine coordinate anchor (priority: shape > options > default)\n const coordinateAnchor = (shapeCoordinateAnchor ??\n options?.lineStringLabelCoordinateAnchor ??\n defaultCoordinateAnchor) as CardinalLabelCoordinateAnchor;\n\n // Calculate position based on cardinal direction\n const coordinates = findEdgePoint(\n geometry.coordinates as number[][],\n coordinateAnchor,\n );\n\n if (!coordinates) {\n return null;\n }\n\n return {\n coordinates,\n ...resolved,\n };\n}\n\n/**\n * Get vertex coordinate from ring\n */\nfunction getVertexCoordinate(\n vertex: number[] | undefined,\n): [number, number] | null {\n if (!vertex || vertex[0] === undefined || vertex[1] === undefined) {\n return null;\n }\n return [vertex[0], vertex[1]];\n}\n\n/**\n * Check if a vertex should replace the current target based on edge position\n */\nfunction shouldUpdateEdgeVertex(\n vertexValue: number,\n targetValue: number,\n position: CardinalLabelCoordinateAnchor,\n): boolean {\n // For top and right, find maximum value\n // For bottom and left, find minimum value\n return position === 'top' || position === 'right'\n ? vertexValue > targetValue\n : vertexValue < targetValue;\n}\n\n/**\n * Get the coordinate index based on edge position (0 for x/longitude, 1 for y/latitude)\n */\nfunction getCoordinateIndexForEdgePosition(\n position: CardinalLabelCoordinateAnchor,\n): number {\n return position === 'top' || position === 'bottom' ? 1 : 0;\n}\n\n/**\n * Calculate the centroid (center point) of a set of coordinates\n * For polygons, this calculates the geometric center\n * For lines, this calculates the midpoint of the bounding box\n * Returns null if no valid coordinates exist\n */\nfunction calculateCentroid(coordinates: number[][]): [number, number] | null {\n if (coordinates.length === 0) {\n return null;\n }\n\n let sumX = 0;\n let sumY = 0;\n let count = 0;\n\n for (const coord of coordinates) {\n if (coord && coord[0] !== undefined && coord[1] !== undefined) {\n sumX += coord[0];\n sumY += coord[1];\n count++;\n }\n }\n\n if (count === 0) {\n return null;\n }\n\n return [sumX / count, sumY / count];\n}\n\n/**\n * Find the point on a geometry's perimeter at the specified edge position\n * @param coordinates - Array of coordinates (ring or line)\n * @param position - Edge position (center/top/right/bottom/left) relative to bounding box\n * @returns Coordinate at the specified edge position, or null if no valid coordinates\n */\nfunction findEdgePoint(\n coordinates: number[][] | undefined,\n position: CardinalLabelCoordinateAnchor,\n): [number, number] | null {\n if (!coordinates || coordinates.length === 0) {\n return null;\n }\n\n // Handle center positioning\n if (position === 'center') {\n return calculateCentroid(coordinates);\n }\n\n // Find the vertex with max/min latitude or longitude\n let targetVertex = coordinates[0];\n const coordinateIndex = getCoordinateIndexForEdgePosition(position);\n\n for (const vertex of coordinates) {\n if (!vertex) {\n continue;\n }\n if (!targetVertex) {\n continue;\n }\n\n const vertexValue = vertex[coordinateIndex];\n const targetValue = targetVertex[coordinateIndex];\n\n if (vertexValue === undefined || targetValue === undefined) {\n continue;\n }\n\n if (shouldUpdateEdgeVertex(vertexValue, targetValue, position)) {\n targetVertex = vertex;\n }\n }\n\n return getVertexCoordinate(targetVertex);\n}\n\n/**\n * Get position for Circle geometry (special case of Polygon)\n */\nfunction getCirclePosition(\n ring: number[][] | undefined,\n shapeOffset: [number, number] | undefined,\n shapeVertical: string | undefined,\n shapeHorizontal: string | undefined,\n shapeCoordinateAnchor: string | undefined,\n options?: LabelPositionOptions,\n): LabelPosition2d | null {\n const defaultOffset: [number, number] = [0, 10];\n const defaultVertical: LabelVerticalPosition = 'top';\n const defaultHorizontal: LabelHorizontalPosition = 'center';\n const defaultCoordinateAnchor: CardinalLabelCoordinateAnchor = 'bottom';\n\n const resolved = resolveLabelProperties(\n shapeOffset,\n shapeVertical,\n shapeHorizontal,\n defaultOffset,\n defaultVertical,\n defaultHorizontal,\n options?.circleLabelOffset,\n options?.circleLabelVerticalAnchor,\n options?.circleLabelHorizontalAnchor,\n );\n\n // Determine coordinate anchor (priority: shape > options > default)\n const coordinateAnchor = (shapeCoordinateAnchor ??\n options?.circleLabelCoordinateAnchor ??\n defaultCoordinateAnchor) as CardinalLabelCoordinateAnchor;\n\n // Calculate position based on coordinate anchor\n const coordinates = findEdgePoint(ring, coordinateAnchor);\n\n if (!coordinates) {\n return null;\n }\n\n return {\n coordinates,\n ...resolved,\n };\n}\n\n/**\n * Get position for Polygon geometry\n * Uses cardinal direction positioning to find the edge point\n */\nfunction getPolygonPosition(\n geometry: Polygon,\n shape: Shape,\n shapeOffset: [number, number] | undefined,\n shapeVertical: string | undefined,\n shapeHorizontal: string | undefined,\n shapeCoordinateAnchor: string | undefined,\n options?: LabelPositionOptions,\n): LabelPosition2d | null {\n const ring = geometry.coordinates[0];\n\n // Circle shapes use circle-specific options\n if (isCircleShape(shape)) {\n return getCirclePosition(\n ring,\n shapeOffset,\n shapeVertical,\n shapeHorizontal,\n shapeCoordinateAnchor,\n options,\n );\n }\n\n // Regular polygons use cardinal direction positioning\n const defaultOffset: [number, number] = [0, 10];\n const defaultVertical: LabelVerticalPosition = 'top';\n const defaultHorizontal: LabelHorizontalPosition = 'center';\n const defaultCoordinateAnchor: CardinalLabelCoordinateAnchor = 'bottom';\n\n const resolved = resolveLabelProperties(\n shapeOffset,\n shapeVertical,\n shapeHorizontal,\n defaultOffset,\n defaultVertical,\n defaultHorizontal,\n options?.polygonLabelOffset,\n options?.polygonLabelVerticalAnchor,\n options?.polygonLabelHorizontalAnchor,\n );\n\n // Determine coordinate anchor (priority: shape > options > default)\n const coordinateAnchor = (shapeCoordinateAnchor ??\n options?.polygonLabelCoordinateAnchor ??\n defaultCoordinateAnchor) as CardinalLabelCoordinateAnchor;\n\n // Calculate position based on cardinal direction\n const coordinates = findEdgePoint(ring, coordinateAnchor);\n\n if (!coordinates) {\n return null;\n }\n\n return {\n coordinates,\n ...resolved,\n };\n}\n\n/**\n * Get 2D position for label based on geometry type.\n *\n * Calculates label positioning using pixel-based offsets for consistent placement\n * at all zoom levels. Handles Point, LineString, Polygon, and Circle geometries.\n *\n * Priority for positioning:\n * 1. Per-shape properties in styleProperties (highest)\n * 2. Global labelOptions from layer props\n * 3. Default values (fallback)\n *\n * Returns null if no valid coordinates can be determined.\n *\n * @param shape - The shape to position a label for\n * @param options - Optional global label positioning options\n * @returns Label position information or null if coordinates are invalid\n *\n * @example\n * ```typescript\n * import { getLabelPosition2d } from '@accelint/map-toolkit/deckgl/shapes/display-shape-layer/utils/labels';\n * import type { Shape, LabelPositionOptions } from '@accelint/map-toolkit/deckgl/shapes/display-shape-layer/utils/labels';\n *\n * const shape: Shape = {\n * id: 'point-1',\n * name: 'Location',\n * label: 'LOC',\n * feature: {\n * type: 'Feature',\n * geometry: { type: 'Point', coordinates: [-122.4, 37.8] },\n * properties: {},\n * },\n * };\n *\n * // Get default positioning\n * const position = getLabelPosition2d(shape);\n * // Returns: { coordinates: [-122.4, 37.8], textAnchor: 'middle', alignmentBaseline: 'top', pixelOffset: [0, 10] }\n *\n * // Use custom global options\n * const options: LabelPositionOptions = {\n * pointLabelVerticalAnchor: 'bottom',\n * pointLabelOffset: [0, -15],\n * };\n * const customPosition = getLabelPosition2d(shape, options);\n * ```\n */\nexport function getLabelPosition2d(\n shape: Shape,\n options?: LabelPositionOptions,\n): LabelPosition2d | null {\n const { geometry } = shape.feature;\n const styleProps = shape.feature.properties?.styleProperties;\n\n // Check if shape has custom label properties\n const shapeOffset = styleProps?.labelOffset as [number, number] | undefined;\n const shapeVertical = styleProps?.labelVerticalAnchor;\n const shapeHorizontal = styleProps?.labelHorizontalAnchor;\n const shapeCoordinateAnchor = styleProps?.labelCoordinateAnchor;\n\n switch (geometry.type) {\n case 'Point':\n return getPointPosition(\n geometry,\n shapeOffset,\n shapeVertical,\n shapeHorizontal,\n options,\n );\n\n case 'LineString':\n return getLineStringPosition(\n geometry,\n shapeOffset,\n shapeVertical,\n shapeHorizontal,\n shapeCoordinateAnchor,\n options,\n );\n\n case 'Polygon':\n return getPolygonPosition(\n geometry,\n shape,\n shapeOffset,\n shapeVertical,\n shapeHorizontal,\n shapeCoordinateAnchor,\n options,\n );\n\n default:\n // Unknown geometry type - return null\n return null;\n }\n}\n\n/**\n * Get label text for a shape.\n *\n * Returns the display label for the shape on the map in uppercase.\n * - `label`: Optional short display name shown on the map (e.g., \"NYC\")\n * - `name`: Full shape name used internally (e.g., \"New York City Office\")\n *\n * If `label` is not provided, falls back to using `name`.\n * Text is automatically converted to uppercase for display.\n *\n * @param shape - The shape to get label text for\n * @returns The label text in uppercase\n *\n * @example\n * ```typescript\n * import { getLabelText } from '@accelint/map-toolkit/deckgl/shapes/display-shape-layer/utils/labels';\n * import type { Shape } from '@accelint/map-toolkit/deckgl/shapes/shared/types';\n *\n * const shape: Shape = {\n * id: 'location-1',\n * name: 'New York City Office',\n * label: 'NYC',\n * feature: {\n * type: 'Feature',\n * geometry: { type: 'Point', coordinates: [-74.0, 40.7] },\n * properties: {},\n * },\n * };\n *\n * const text = getLabelText(shape);\n * // Returns: \"NYC\"\n *\n * // Without label property, uses name\n * const shapeWithoutLabel: Shape = {\n * id: 'location-2',\n * name: 'Boston Office',\n * feature: {\n * type: 'Feature',\n * geometry: { type: 'Point', coordinates: [-71.0, 42.3] },\n * properties: {},\n * },\n * };\n *\n * const textFromName = getLabelText(shapeWithoutLabel);\n * // Returns: \"BOSTON OFFICE\"\n * ```\n */\nexport function getLabelText(shape: Shape): string {\n return (shape.label || shape.name).toUpperCase();\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"labels.js","names":["DEFAULT_LABEL_OFFSET: [number, number]","DEFAULT_LABEL_VERTICAL: LabelVerticalPosition","DEFAULT_LABEL_HORIZONTAL: LabelHorizontalPosition","DEFAULT_COORDINATE_ANCHOR: CardinalLabelCoordinateAnchor","TEXT_ANCHOR_MAP: Record<\n LabelHorizontalPosition,\n 'start' | 'middle' | 'end'\n>","ALIGNMENT_BASELINE_MAP: Record<\n LabelVerticalPosition,\n 'top' | 'center' | 'bottom'\n>"],"sources":["../../../../../src/deckgl/shapes/display-shape-layer/utils/labels.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 * Label positioning utilities for shapes\n *\n * This module provides utilities for positioning labels on shapes and calculating\n * points along line segments. Use these to customize label placement in your shape data.\n *\n * ## Label Styling\n *\n * Labels use a text-only style for legibility across different map backgrounds:\n * - **Text**: White uppercase with bold Roboto Mono font at 10px\n * - **Outline**: Black 2px outline around text for contrast\n * - **No background or border**: Clean text-only appearance\n *\n * ### Calculating Offsets\n *\n * When positioning labels, the text height is approximately 10px.\n * For example, to position a label exactly 5px above a point:\n * - Label height ≈ text height (10px)\n * - Offset needed: [0, -(10 + 5)] = [0, -15]\n *\n * @example Position label at the middle of a LineString\n * ```typescript\n * import { getLineStringMidpoint } from '@accelint/map-toolkit/deckgl/shapes/display-shape-layer/utils/labels';\n *\n * // Calculate midpoint for custom positioning\n * const coordinates = [[-122.4, 37.8], [-122.3, 37.9], [-122.2, 38.0]];\n * const midpoint = getLineStringMidpoint(coordinates);\n *\n * // Use in shape data with custom label properties\n * const shape = {\n * feature: {\n * properties: {\n * styleProperties: {\n * labelVerticalAnchor: 'top',\n * labelHorizontalAnchor: 'center',\n * labelOffset: [0, -10], // Small offset above the line\n * }\n * }\n * }\n * };\n * ```\n */\n\n'use client';\n\nimport { isCircleShape } from '../../shared/types';\nimport type { LineString, Point, Polygon } from 'geojson';\nimport type { Shape } from '../../shared/types';\n\n/**\n * Label positioning information including coordinates and screen-space offsets.\n *\n * Defines where a label should be positioned on the map, combining geographic coordinates\n * with text alignment and pixel-level offsets for precise placement.\n */\nexport interface LabelPosition2d {\n /** Geographic coordinates [longitude, latitude] */\n coordinates: [number, number];\n /** Horizontal text anchor point */\n textAnchor: 'start' | 'middle' | 'end';\n /** Vertical text alignment */\n alignmentBaseline: 'top' | 'center' | 'bottom';\n /** Pixel offset from coordinates [x, y] */\n pixelOffset: [number, number];\n}\n\n/**\n * Calculate a point along a line segment\n * @param start - Start coordinate [lon, lat]\n * @param end - End coordinate [lon, lat]\n * @param ratio - Position along segment (0 = start, 0.5 = middle, 1 = end)\n * @returns Interpolated coordinate [lon, lat]\n *\n * @example\n * // Get a point 25% along a line segment\n * const start: [number, number] = [-122.4, 37.8];\n * const end: [number, number] = [-122.3, 37.9];\n * const point = interpolatePoint(start, end, 0.25);\n */\nexport function interpolatePoint(\n start: [number, number],\n end: [number, number],\n ratio: number,\n): [number, number] {\n const clampedRatio = Math.max(0, Math.min(1, ratio));\n return [\n start[0] + (end[0] - start[0]) * clampedRatio,\n start[1] + (end[1] - start[1]) * clampedRatio,\n ];\n}\n\n/**\n * Calculate segment lengths for a line\n */\nfunction calculateSegmentLengths(coordinates: [number, number][]): {\n lengths: number[];\n total: number;\n} {\n let total = 0;\n const lengths: number[] = [];\n\n for (let i = 0; i < coordinates.length - 1; i++) {\n const start = coordinates[i];\n const end = coordinates[i + 1];\n if (start && end) {\n const length = Math.sqrt(\n (end[0] - start[0]) ** 2 + (end[1] - start[1]) ** 2,\n );\n lengths.push(length);\n total += length;\n }\n }\n\n return { lengths, total };\n}\n\n/**\n * Find point at specific distance along line\n */\nfunction findPointAtDistance(\n coordinates: [number, number][],\n segmentLengths: number[],\n targetDistance: number,\n): [number, number] {\n let accumulatedLength = 0;\n\n for (let i = 0; i < segmentLengths.length; i++) {\n const segmentLength = segmentLengths[i];\n if (!segmentLength) {\n continue;\n }\n\n if (accumulatedLength + segmentLength >= targetDistance) {\n const start = coordinates[i];\n const end = coordinates[i + 1];\n if (start && end) {\n const ratio = (targetDistance - accumulatedLength) / segmentLength;\n return interpolatePoint(start, end, ratio);\n }\n }\n accumulatedLength += segmentLength;\n }\n\n return coordinates[coordinates.length - 1] ?? [0, 0];\n}\n\n/**\n * Get the midpoint of a LineString.\n *\n * Calculates the geometric center point along a LineString by measuring distances\n * along each segment. This is more accurate than the bounding box center for curved lines.\n *\n * @param coordinates - LineString coordinates array\n * @returns Midpoint coordinate [lon, lat]\n *\n * @example\n * ```typescript\n * import { getLineStringMidpoint } from '@accelint/map-toolkit/deckgl/shapes/display-shape-layer/utils/labels';\n *\n * const lineCoordinates: [number, number][] = [\n * [-122.4, 37.8],\n * [-122.3, 37.85],\n * [-122.2, 37.9],\n * ];\n *\n * const midpoint = getLineStringMidpoint(lineCoordinates);\n * // Returns the coordinate at the middle of the line's total length\n * ```\n */\nexport function getLineStringMidpoint(\n coordinates: [number, number][],\n): [number, number] {\n if (coordinates.length === 0) {\n return [0, 0];\n }\n if (coordinates.length === 1) {\n return coordinates[0] ?? [0, 0];\n }\n\n const { lengths, total } = calculateSegmentLengths(coordinates);\n const halfLength = total / 2;\n\n return findPointAtDistance(coordinates, lengths, halfLength);\n}\n\n/**\n * Get the end point of a LineString.\n *\n * Returns the last coordinate in the LineString, useful for positioning labels\n * at the end of lines (e.g., directional arrows, route endpoints).\n *\n * @param coordinates - LineString coordinates array\n * @returns End coordinate [lon, lat]\n *\n * @example\n * ```typescript\n * import { getLineStringEndpoint } from '@accelint/map-toolkit/deckgl/shapes/display-shape-layer/utils/labels';\n *\n * const lineCoordinates: [number, number][] = [\n * [-122.4, 37.8],\n * [-122.3, 37.85],\n * [-122.2, 37.9],\n * ];\n *\n * const endpoint = getLineStringEndpoint(lineCoordinates);\n * // Returns: [-122.2, 37.9]\n * ```\n */\nexport function getLineStringEndpoint(\n coordinates: [number, number][],\n): [number, number] {\n if (coordinates.length === 0) {\n return [0, 0];\n }\n return coordinates[coordinates.length - 1] ?? [0, 0];\n}\n\n/**\n * Get the midpoint of a Polygon's outer ring.\n *\n * Calculates the geometric center point along the polygon's perimeter by using\n * the midpoint of the outer ring. This is useful for positioning labels on polygons.\n *\n * @param coordinates - Polygon coordinates array (rings)\n * @returns Midpoint of outer ring [lon, lat]\n *\n * @example\n * ```typescript\n * import { getPolygonMidpoint } from '@accelint/map-toolkit/deckgl/shapes/display-shape-layer/utils/labels';\n *\n * const polygonCoordinates: [number, number][][] = [\n * [\n * [-122.4, 37.8],\n * [-122.3, 37.8],\n * [-122.3, 37.9],\n * [-122.4, 37.9],\n * [-122.4, 37.8], // Closing point\n * ]\n * ];\n *\n * const midpoint = getPolygonMidpoint(polygonCoordinates);\n * // Returns the coordinate at the middle of the outer ring's perimeter\n * ```\n */\nexport function getPolygonMidpoint(\n coordinates: [number, number][][],\n): [number, number] {\n const outerRing = coordinates[0];\n if (!outerRing || outerRing.length === 0) {\n return [0, 0];\n }\n // Use the outer ring (first ring)\n return getLineStringMidpoint(outerRing);\n}\n\n/**\n * Vertical label position relative to anchor point.\n *\n * Controls how the label aligns vertically with its anchor coordinate:\n * - `'top'`: Label text appears below the anchor point\n * - `'middle'`: Label text is vertically centered on the anchor point\n * - `'bottom'`: Label text appears above the anchor point\n */\nexport type LabelVerticalPosition = 'top' | 'middle' | 'bottom';\n\n/**\n * Horizontal label position relative to anchor point.\n *\n * Controls how the label aligns horizontally with its anchor coordinate:\n * - `'left'`: Label text appears to the right of the anchor point\n * - `'center'`: Label text is horizontally centered on the anchor point\n * - `'right'`: Label text appears to the left of the anchor point\n */\nexport type LabelHorizontalPosition = 'left' | 'center' | 'right';\n\n/**\n * Cardinal direction anchor for positioning labels on geometry edges.\n *\n * Uses edge positions relative to the geometry's bounding box.\n * Works for LineString, Polygon, and Circle geometries:\n * - `'center'`: centroid/midpoint of the geometry\n * - `'top'`/`'right'`/`'bottom'`/`'left'`: edge positions based on bounding box\n */\nexport type CardinalLabelCoordinateAnchor =\n | 'center'\n | 'top'\n | 'right'\n | 'bottom'\n | 'left';\n\n/**\n * Global label positioning options for DisplayShapeLayer\n *\n * ## Priority System\n * Label positioning follows a three-tier priority system:\n * 1. **Per-shape properties** in `styleProperties` (highest priority)\n * 2. **Global options** via this interface\n * 3. **Default values** (geometry-specific fallbacks)\n *\n * ## Label Appearance\n *\n * Labels use a clean text-only style:\n * - **Text**: White uppercase, bold Roboto Mono, 10px\n * - **Outline**: Black 2px outline for contrast\n * - **No background or border**\n *\n * Text height ≈ 10px\n *\n * ## Positioning Concepts\n *\n * ### Coordinate Anchor\n * Determines *where* on the geometry to place the label:\n * - **Point**: Label is always at the point coordinate\n * - **LineString/Polygon**: 'start', 'middle', or 'end' along the geometry\n * - **Circle**: 'top', 'right', 'bottom', or 'left' on the perimeter\n *\n * ### Vertical/Horizontal Anchor\n * Determines how the label aligns *relative to* the anchor point:\n * - **Vertical**: 'top' (label text below point), 'middle' (centered), 'bottom' (label text above point)\n * - **Horizontal**: 'left' (label text right of point), 'center' (centered), 'right' (label text left of point)\n *\n * ### Pixel Offset\n * Fine-tune label position with [x, y] pixel offsets:\n * - Positive x moves right, negative moves left\n * - Positive y moves down, negative moves up\n *\n * @example Position circle labels at the top with 5px clearance\n * ```tsx\n * const labelOptions: LabelPositionOptions = {\n * circleLabelCoordinateAnchor: 'top',\n * circleLabelVerticalAnchor: 'bottom', // Label above the top point\n * circleLabelOffset: [0, -15], // -(10px text height + 5px clearance)\n * };\n * ```\n *\n * @example Position line labels at middle with offset to the right\n * ```tsx\n * const labelOptions: LabelPositionOptions = {\n * lineStringLabelCoordinateAnchor: 'middle',\n * lineStringLabelHorizontalAnchor: 'left',\n * lineStringLabelOffset: [10, 0], // 10px to the right\n * };\n * ```\n */\nexport interface LabelPositionOptions {\n // Point geometry options\n /** Vertical anchor for Point labels @default 'top' */\n pointLabelVerticalAnchor?: LabelVerticalPosition;\n /** Horizontal anchor for Point labels @default 'center' */\n pointLabelHorizontalAnchor?: LabelHorizontalPosition;\n /** Pixel offset for Point labels [x, y] @default [0, 10] */\n pointLabelOffset?: [number, number];\n\n // LineString geometry options\n /** Position on LineString edge (top/right/bottom/left) @default 'bottom' */\n lineStringLabelCoordinateAnchor?: CardinalLabelCoordinateAnchor;\n /** Vertical anchor for LineString labels @default 'top' */\n lineStringLabelVerticalAnchor?: LabelVerticalPosition;\n /** Horizontal anchor for LineString labels @default 'center' */\n lineStringLabelHorizontalAnchor?: LabelHorizontalPosition;\n /** Pixel offset for LineString labels [x, y] @default [0, 10] */\n lineStringLabelOffset?: [number, number];\n\n // Polygon geometry options\n /** Position on Polygon edge (top/right/bottom/left) @default 'bottom' */\n polygonLabelCoordinateAnchor?: CardinalLabelCoordinateAnchor;\n /** Vertical anchor for Polygon labels @default 'top' */\n polygonLabelVerticalAnchor?: LabelVerticalPosition;\n /** Horizontal anchor for Polygon labels @default 'center' */\n polygonLabelHorizontalAnchor?: LabelHorizontalPosition;\n /** Pixel offset for Polygon labels [x, y] @default [0, 10] */\n polygonLabelOffset?: [number, number];\n\n // Circle geometry options\n /** Position on Circle perimeter (top/right/bottom/left) @default 'bottom' */\n circleLabelCoordinateAnchor?: CardinalLabelCoordinateAnchor;\n /** Vertical anchor for Circle labels @default 'top' */\n circleLabelVerticalAnchor?: LabelVerticalPosition;\n /** Horizontal anchor for Circle labels @default 'center' */\n circleLabelHorizontalAnchor?: LabelHorizontalPosition;\n /** Pixel offset for Circle labels [x, y] @default [0, 10] */\n circleLabelOffset?: [number, number];\n}\n\nconst DEFAULT_LABEL_OFFSET: [number, number] = [0, 10];\nconst DEFAULT_LABEL_VERTICAL: LabelVerticalPosition = 'top';\nconst DEFAULT_LABEL_HORIZONTAL: LabelHorizontalPosition = 'center';\nconst DEFAULT_COORDINATE_ANCHOR: CardinalLabelCoordinateAnchor = 'bottom';\n\nconst TEXT_ANCHOR_MAP: Record<\n LabelHorizontalPosition,\n 'start' | 'middle' | 'end'\n> = {\n left: 'start',\n center: 'middle',\n right: 'end',\n};\n\nconst ALIGNMENT_BASELINE_MAP: Record<\n LabelVerticalPosition,\n 'top' | 'center' | 'bottom'\n> = {\n top: 'top',\n middle: 'center',\n bottom: 'bottom',\n};\n\n/**\n * Convert vertical/horizontal position to deck.gl textAnchor and alignmentBaseline\n */\nfunction convertPositionToAnchors(\n vertical: LabelVerticalPosition,\n horizontal: LabelHorizontalPosition,\n): {\n textAnchor: 'start' | 'middle' | 'end';\n alignmentBaseline: 'top' | 'center' | 'bottom';\n} {\n return {\n textAnchor: TEXT_ANCHOR_MAP[horizontal],\n alignmentBaseline: ALIGNMENT_BASELINE_MAP[vertical],\n };\n}\n\n/**\n * Helper to resolve label properties with priority: shape > options > default\n */\nfunction resolveLabelProperties(\n shapeOffset: [number, number] | undefined,\n shapeVertical: LabelVerticalPosition | undefined,\n shapeHorizontal: LabelHorizontalPosition | undefined,\n defaultOffset: [number, number],\n defaultVertical: LabelVerticalPosition,\n defaultHorizontal: LabelHorizontalPosition,\n optionsOffset?: [number, number],\n optionsVertical?: LabelVerticalPosition,\n optionsHorizontal?: LabelHorizontalPosition,\n): {\n pixelOffset: [number, number];\n textAnchor: 'start' | 'middle' | 'end';\n alignmentBaseline: 'top' | 'center' | 'bottom';\n} {\n const vertical = shapeVertical ?? optionsVertical ?? defaultVertical;\n const horizontal = shapeHorizontal ?? optionsHorizontal ?? defaultHorizontal;\n const pixelOffset = shapeOffset ?? optionsOffset ?? defaultOffset;\n\n const anchors = convertPositionToAnchors(vertical, horizontal);\n\n return {\n pixelOffset,\n ...anchors,\n };\n}\n\n/**\n * Get position for Point geometry\n */\nfunction getPointPosition(\n geometry: Point,\n shapeOffset: [number, number] | undefined,\n shapeVertical: LabelVerticalPosition | undefined,\n shapeHorizontal: LabelHorizontalPosition | undefined,\n options?: LabelPositionOptions,\n): LabelPosition2d {\n const resolved = resolveLabelProperties(\n shapeOffset,\n shapeVertical,\n shapeHorizontal,\n DEFAULT_LABEL_OFFSET,\n DEFAULT_LABEL_VERTICAL,\n DEFAULT_LABEL_HORIZONTAL,\n options?.pointLabelOffset,\n options?.pointLabelVerticalAnchor,\n options?.pointLabelHorizontalAnchor,\n );\n\n return {\n coordinates: [\n geometry.coordinates[0] ?? 0,\n geometry.coordinates[1] ?? 0,\n ] as [number, number],\n ...resolved,\n };\n}\n\n/**\n * Get position for LineString geometry\n * Uses cardinal direction positioning to find the edge point\n */\nfunction getLineStringPosition(\n geometry: LineString,\n shapeOffset: [number, number] | undefined,\n shapeVertical: LabelVerticalPosition | undefined,\n shapeHorizontal: LabelHorizontalPosition | undefined,\n shapeCoordinateAnchor: string | undefined,\n options?: LabelPositionOptions,\n): LabelPosition2d | null {\n const resolved = resolveLabelProperties(\n shapeOffset,\n shapeVertical,\n shapeHorizontal,\n DEFAULT_LABEL_OFFSET,\n DEFAULT_LABEL_VERTICAL,\n DEFAULT_LABEL_HORIZONTAL,\n options?.lineStringLabelOffset,\n options?.lineStringLabelVerticalAnchor,\n options?.lineStringLabelHorizontalAnchor,\n );\n\n // Determine coordinate anchor (priority: shape > options > default)\n const coordinateAnchor = (shapeCoordinateAnchor ??\n options?.lineStringLabelCoordinateAnchor ??\n DEFAULT_COORDINATE_ANCHOR) as CardinalLabelCoordinateAnchor;\n\n // Calculate position based on cardinal direction\n const coordinates = findEdgePoint(geometry.coordinates, coordinateAnchor);\n\n if (!coordinates) {\n return null;\n }\n\n return {\n coordinates,\n ...resolved,\n };\n}\n\n/**\n * Get vertex coordinate from ring\n */\nfunction getVertexCoordinate(\n vertex: number[] | undefined,\n): [number, number] | null {\n if (!vertex || vertex[0] === undefined || vertex[1] === undefined) {\n return null;\n }\n return [vertex[0], vertex[1]];\n}\n\n/**\n * Get the coordinate index based on edge position (0 for x/longitude, 1 for y/latitude)\n */\nfunction getCoordinateIndexForEdgePosition(\n position: CardinalLabelCoordinateAnchor,\n): 0 | 1 {\n return position === 'top' || position === 'bottom' ? 1 : 0;\n}\n\n/**\n * Calculate the centroid (center point) of a set of coordinates\n * For polygons, this calculates the geometric center\n * For lines, this calculates the midpoint of the bounding box\n * Returns null if no valid coordinates exist\n */\nfunction calculateCentroid(coordinates: number[][]): [number, number] | null {\n if (coordinates.length === 0) {\n return null;\n }\n\n let sumX = 0;\n let sumY = 0;\n let count = 0;\n\n for (const coord of coordinates) {\n if (coord && coord[0] !== undefined && coord[1] !== undefined) {\n sumX += coord[0];\n sumY += coord[1];\n count++;\n }\n }\n\n if (count === 0) {\n return null;\n }\n\n return [sumX / count, sumY / count];\n}\n\n/**\n * Find the point on a geometry's perimeter at the specified edge position\n * @param coordinates - Array of coordinates (ring or line)\n * @param position - Edge position (center/top/right/bottom/left) relative to bounding box\n * @returns Coordinate at the specified edge position, or null if no valid coordinates\n */\nfunction findEdgePoint(\n coordinates: number[][] | undefined,\n position: CardinalLabelCoordinateAnchor,\n): [number, number] | null {\n if (!coordinates || coordinates.length === 0) {\n return null;\n }\n\n // Handle center positioning\n if (position === 'center') {\n return calculateCentroid(coordinates);\n }\n\n // Find the vertex with max/min latitude or longitude\n let targetVertex = coordinates[0];\n\n if (!targetVertex) {\n return null;\n }\n\n const coordinateIndex = getCoordinateIndexForEdgePosition(position);\n const findMax = position === 'top' || position === 'right';\n\n for (const vertex of coordinates) {\n if (!vertex) {\n continue;\n }\n\n const vertexValue = vertex[coordinateIndex];\n const targetValue = targetVertex[coordinateIndex];\n\n if (vertexValue === undefined || targetValue === undefined) {\n continue;\n }\n\n const shouldUpdate = findMax\n ? vertexValue > targetValue\n : vertexValue < targetValue;\n\n if (shouldUpdate) {\n targetVertex = vertex;\n }\n }\n\n return getVertexCoordinate(targetVertex);\n}\n\n/**\n * Get position for Circle geometry (special case of Polygon)\n */\nfunction getCirclePosition(\n ring: number[][] | undefined,\n shapeOffset: [number, number] | undefined,\n shapeVertical: LabelVerticalPosition | undefined,\n shapeHorizontal: LabelHorizontalPosition | undefined,\n shapeCoordinateAnchor: string | undefined,\n options?: LabelPositionOptions,\n): LabelPosition2d | null {\n const resolved = resolveLabelProperties(\n shapeOffset,\n shapeVertical,\n shapeHorizontal,\n DEFAULT_LABEL_OFFSET,\n DEFAULT_LABEL_VERTICAL,\n DEFAULT_LABEL_HORIZONTAL,\n options?.circleLabelOffset,\n options?.circleLabelVerticalAnchor,\n options?.circleLabelHorizontalAnchor,\n );\n\n // Determine coordinate anchor (priority: shape > options > default)\n const coordinateAnchor = (shapeCoordinateAnchor ??\n options?.circleLabelCoordinateAnchor ??\n DEFAULT_COORDINATE_ANCHOR) as CardinalLabelCoordinateAnchor;\n\n // Calculate position based on coordinate anchor\n const coordinates = findEdgePoint(ring, coordinateAnchor);\n\n if (!coordinates) {\n return null;\n }\n\n return {\n coordinates,\n ...resolved,\n };\n}\n\n/**\n * Get position for Polygon geometry\n * Uses cardinal direction positioning to find the edge point\n */\nfunction getPolygonPosition(\n geometry: Polygon,\n shape: Shape,\n shapeOffset: [number, number] | undefined,\n shapeVertical: LabelVerticalPosition | undefined,\n shapeHorizontal: LabelHorizontalPosition | undefined,\n shapeCoordinateAnchor: string | undefined,\n options?: LabelPositionOptions,\n): LabelPosition2d | null {\n const ring = geometry.coordinates[0];\n\n // Circle shapes use circle-specific options\n if (isCircleShape(shape)) {\n return getCirclePosition(\n ring,\n shapeOffset,\n shapeVertical,\n shapeHorizontal,\n shapeCoordinateAnchor,\n options,\n );\n }\n\n // Regular polygons use cardinal direction positioning\n const resolved = resolveLabelProperties(\n shapeOffset,\n shapeVertical,\n shapeHorizontal,\n DEFAULT_LABEL_OFFSET,\n DEFAULT_LABEL_VERTICAL,\n DEFAULT_LABEL_HORIZONTAL,\n options?.polygonLabelOffset,\n options?.polygonLabelVerticalAnchor,\n options?.polygonLabelHorizontalAnchor,\n );\n\n // Determine coordinate anchor (priority: shape > options > default)\n const coordinateAnchor = (shapeCoordinateAnchor ??\n options?.polygonLabelCoordinateAnchor ??\n DEFAULT_COORDINATE_ANCHOR) as CardinalLabelCoordinateAnchor;\n\n // Calculate position based on cardinal direction\n const coordinates = findEdgePoint(ring, coordinateAnchor);\n\n if (!coordinates) {\n return null;\n }\n\n return {\n coordinates,\n ...resolved,\n };\n}\n\n/**\n * Get 2D position for label based on geometry type.\n *\n * Calculates label positioning using pixel-based offsets for consistent placement\n * at all zoom levels. Handles Point, LineString, Polygon, and Circle geometries.\n *\n * Priority for positioning:\n * 1. Per-shape properties in styleProperties (highest)\n * 2. Global labelOptions from layer props\n * 3. Default values (fallback)\n *\n * Returns null if no valid coordinates can be determined.\n *\n * @param shape - The shape to position a label for\n * @param options - Optional global label positioning options\n * @returns Label position information or null if coordinates are invalid\n *\n * @example\n * ```typescript\n * import { getLabelPosition2d } from '@accelint/map-toolkit/deckgl/shapes/display-shape-layer/utils/labels';\n * import type { Shape, LabelPositionOptions } from '@accelint/map-toolkit/deckgl/shapes/display-shape-layer/utils/labels';\n *\n * const shape: Shape = {\n * id: 'point-1',\n * name: 'Location',\n * label: 'LOC',\n * feature: {\n * type: 'Feature',\n * geometry: { type: 'Point', coordinates: [-122.4, 37.8] },\n * properties: {},\n * },\n * };\n *\n * // Get default positioning\n * const position = getLabelPosition2d(shape);\n * // Returns: { coordinates: [-122.4, 37.8], textAnchor: 'middle', alignmentBaseline: 'top', pixelOffset: [0, 10] }\n *\n * // Use custom global options\n * const options: LabelPositionOptions = {\n * pointLabelVerticalAnchor: 'bottom',\n * pointLabelOffset: [0, -15],\n * };\n * const customPosition = getLabelPosition2d(shape, options);\n * ```\n */\nexport function getLabelPosition2d(\n shape: Shape,\n options?: LabelPositionOptions,\n): LabelPosition2d | null {\n const { geometry } = shape.feature;\n const styleProps = shape.feature.properties?.styleProperties;\n\n // Check if shape has custom label properties\n const shapeOffset = styleProps?.labelOffset;\n const shapeVertical = styleProps?.labelVerticalAnchor;\n const shapeHorizontal = styleProps?.labelHorizontalAnchor;\n const shapeCoordinateAnchor = styleProps?.labelCoordinateAnchor;\n\n switch (geometry.type) {\n case 'Point':\n return getPointPosition(\n geometry,\n shapeOffset,\n shapeVertical,\n shapeHorizontal,\n options,\n );\n\n case 'LineString':\n return getLineStringPosition(\n geometry,\n shapeOffset,\n shapeVertical,\n shapeHorizontal,\n shapeCoordinateAnchor,\n options,\n );\n\n case 'Polygon':\n return getPolygonPosition(\n geometry,\n shape,\n shapeOffset,\n shapeVertical,\n shapeHorizontal,\n shapeCoordinateAnchor,\n options,\n );\n\n default:\n // Unknown geometry type - return null\n return null;\n }\n}\n\n/**\n * Get label text for a shape.\n *\n * Returns the display label for the shape on the map in uppercase.\n * - `label`: Optional short display name shown on the map (e.g., \"NYC\")\n * - `name`: Full shape name used internally (e.g., \"New York City Office\")\n *\n * If `label` is not provided, falls back to using `name`.\n * Text is automatically converted to uppercase for display.\n *\n * @param shape - The shape to get label text for\n * @returns The label text in uppercase\n *\n * @example\n * ```typescript\n * import { getLabelText } from '@accelint/map-toolkit/deckgl/shapes/display-shape-layer/utils/labels';\n * import type { Shape } from '@accelint/map-toolkit/deckgl/shapes/shared/types';\n *\n * const shape: Shape = {\n * id: 'location-1',\n * name: 'New York City Office',\n * label: 'NYC',\n * feature: {\n * type: 'Feature',\n * geometry: { type: 'Point', coordinates: [-74.0, 40.7] },\n * properties: {},\n * },\n * };\n *\n * const text = getLabelText(shape);\n * // Returns: \"NYC\"\n *\n * // Without label property, uses name\n * const shapeWithoutLabel: Shape = {\n * id: 'location-2',\n * name: 'Boston Office',\n * feature: {\n * type: 'Feature',\n * geometry: { type: 'Point', coordinates: [-71.0, 42.3] },\n * properties: {},\n * },\n * };\n *\n * const textFromName = getLabelText(shapeWithoutLabel);\n * // Returns: \"BOSTON OFFICE\"\n * ```\n */\nexport function getLabelText(shape: Shape): string {\n return (shape.label ?? shape.name).toUpperCase();\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
@@ -11,7 +11,7 @@
11
11
  */
12
12
 
13
13
  import { DrawShapeLayerProps } from "./types.js";
14
- import * as react_jsx_runtime0 from "react/jsx-runtime";
14
+ import * as react_jsx_runtime2 from "react/jsx-runtime";
15
15
 
16
16
  //#region src/deckgl/shapes/draw-shape-layer/index.d.ts
17
17
 
@@ -47,7 +47,7 @@ declare function DrawShapeLayer({
47
47
  id,
48
48
  mapId,
49
49
  unit
50
- }: DrawShapeLayerProps): react_jsx_runtime0.JSX.Element | null;
50
+ }: DrawShapeLayerProps): react_jsx_runtime2.JSX.Element | null;
51
51
  //#endregion
52
52
  export { DrawShapeLayer };
53
53
  //# 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
@@ -13,14 +13,14 @@
13
13
 
14
14
  'use client';
15
15
 
16
- import { MapContext } from "../../base-map/provider.js";
16
+ import { DRAW_SHAPE_LAYER_ID } from "./constants.js";
17
17
  import { DEFAULT_TENTATIVE_COLORS, EMPTY_FEATURE_COLLECTION } from "../shared/constants.js";
18
+ import { cancelDrawingFromLayer, completeDrawingFromLayer, drawStore } from "./store.js";
19
+ import { MapContext } from "../../base-map/provider.js";
18
20
  import { useShiftZoomDisable } from "../shared/hooks/use-shift-zoom-disable.js";
19
21
  import { getDefaultEditableLayerProps } from "../shared/utils/layer-config.js";
20
- import { DRAW_SHAPE_LAYER_ID } from "./constants.js";
21
- import { getModeInstance, triggerDoubleClickFinish } from "./modes/index.js";
22
- import { cancelDrawingFromLayer, completeDrawingFromLayer, drawStore } from "./store.js";
23
- import { useContext, useEffect } from "react";
22
+ import { getModeInstance } from "./modes/index.js";
23
+ import { useContext } from "react";
24
24
  import { jsx } from "react/jsx-runtime";
25
25
 
26
26
  //#region src/deckgl/shapes/draw-shape-layer/index.tsx
@@ -59,22 +59,12 @@ function DrawShapeLayer({ id = DRAW_SHAPE_LAYER_ID, mapId, unit }) {
59
59
  const { state: drawingState } = drawStore.use(actualMapId);
60
60
  const activeShapeType = drawingState?.activeShapeType ?? null;
61
61
  useShiftZoomDisable(actualMapId, activeShapeType === "Rectangle");
62
- useEffect(() => {
63
- if (!activeShapeType) return;
64
- const handleDblClick = () => {
65
- triggerDoubleClickFinish(activeShapeType);
66
- };
67
- document.addEventListener("dblclick", handleDblClick);
68
- return () => {
69
- document.removeEventListener("dblclick", handleDblClick);
70
- };
71
- }, [activeShapeType]);
72
62
  if (!activeShapeType) return null;
73
63
  const styleDefaults = drawingState?.styleDefaults ?? null;
74
64
  const mode = getModeInstance(activeShapeType);
75
65
  const handleEdit = ({ updatedData, editType }) => {
76
66
  if (editType === "addFeature") {
77
- const feature = updatedData.features[updatedData.features.length - 1];
67
+ const feature = updatedData.features.at(-1);
78
68
  if (feature) completeDrawingFromLayer(actualMapId, feature);
79
69
  } else if (editType === "cancelFeature") cancelDrawingFromLayer(actualMapId);
80
70
  };
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../../../src/deckgl/shapes/draw-shape-layer/index.tsx"],"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'use client';\n\nimport { useContext, useEffect } from 'react';\nimport { MapContext } from '../../base-map/provider';\nimport {\n DEFAULT_TENTATIVE_COLORS,\n EMPTY_FEATURE_COLLECTION,\n} from '../shared/constants';\nimport { useShiftZoomDisable } from '../shared/hooks/use-shift-zoom-disable';\nimport { getDefaultEditableLayerProps } from '../shared/utils/layer-config';\nimport { DRAW_SHAPE_LAYER_ID } from './constants';\nimport { getModeInstance, triggerDoubleClickFinish } from './modes';\nimport {\n cancelDrawingFromLayer,\n completeDrawingFromLayer,\n drawStore,\n} from './store';\nimport type {\n EditAction,\n FeatureCollection,\n} from '@deck.gl-community/editable-layers';\nimport type { DrawShapeLayerProps } from './types';\n\n/**\n * DrawShapeLayer - A React component for drawing shapes on the map.\n *\n * This component wraps the EditableGeoJsonLayer from @deck.gl-community/editable-layers\n * and integrates with the map-mode and map-cursor systems for proper coordination.\n *\n * Key features:\n * - Renders only when actively drawing (returns null otherwise)\n * - Uses cached mode instances to prevent deck.gl assertion errors\n * - Integrates with the drawing store for state management\n * - Protected drawing mode (rejects mode change requests while drawing)\n * - Distance/area tooltips during drawing\n *\n * @example\n * ```tsx\n * // Import the fiber registration for JSX support\n * import '@accelint/map-toolkit/deckgl/shapes/draw-shape-layer/fiber';\n *\n * function Map({ mapId }) {\n * return (\n * <BaseMap id={mapId}>\n * <displayShapeLayer data={shapes} mapId={mapId} />\n * <DrawShapeLayer mapId={mapId} />\n * </BaseMap>\n * );\n * }\n * ```\n */\nexport function DrawShapeLayer({\n id = DRAW_SHAPE_LAYER_ID,\n mapId,\n unit,\n}: DrawShapeLayerProps) {\n // Get mapId from context if not provided\n const contextId = useContext(MapContext);\n const actualMapId = mapId ?? contextId;\n\n if (!actualMapId) {\n throw new Error(\n 'DrawShapeLayer requires either a mapId prop or to be used within a MapProvider',\n );\n }\n\n // Subscribe to drawing state using the v2 store API\n const { state: drawingState } = drawStore.use(actualMapId);\n\n const activeShapeType = drawingState?.activeShapeType ?? null;\n\n // Disable zoom while Shift is held during rectangle drawing\n // This prevents boxZoom (Shift+drag) from interfering with Shift-to-square constraint\n useShiftZoomDisable(actualMapId, activeShapeType === 'Rectangle');\n\n // Set up dblclick listener as workaround for deck.gl-community/editable-layers ~9.1\n // which doesn't register 'dblclick' in EVENT_TYPES\n // @see https://github.com/visgl/deck.gl-community/pull/225\n // TODO: Remove this workaround when @deck.gl-community/editable-layers 9.2.0 is released\n useEffect(() => {\n if (!activeShapeType) {\n return;\n }\n\n const handleDblClick = () => {\n triggerDoubleClickFinish(activeShapeType);\n };\n\n // Add listener to document to catch dblclick anywhere on the map\n document.addEventListener('dblclick', handleDblClick);\n\n return () => {\n document.removeEventListener('dblclick', handleDblClick);\n };\n }, [activeShapeType]);\n\n // If not drawing, return null (don't render the editable layer)\n if (!activeShapeType) {\n return null;\n }\n\n const styleDefaults = drawingState?.styleDefaults ?? null;\n\n // Get the cached mode instance\n const mode = getModeInstance(activeShapeType);\n\n // Handle edit events from EditableGeoJsonLayer\n const handleEdit = ({\n updatedData,\n editType,\n }: EditAction<FeatureCollection>) => {\n // Only process addFeature (drawing complete) and cancelFeature (ESC pressed)\n if (editType === 'addFeature') {\n const feature = updatedData.features[updatedData.features.length - 1];\n if (feature) {\n // Type assertion: editable-layers Feature type differs slightly from geojson Feature\n // but they are structurally compatible for our conversion purposes\n completeDrawingFromLayer(\n actualMapId,\n feature as Parameters<typeof completeDrawingFromLayer>[1],\n );\n }\n } else if (editType === 'cancelFeature') {\n cancelDrawingFromLayer(actualMapId);\n }\n // Ignore other edit types during drawing (tentative updates, etc.)\n };\n\n // Get colors from style defaults or use tentative defaults\n const fillColor = styleDefaults?.fillColor ?? DEFAULT_TENTATIVE_COLORS.fill;\n const lineColor = styleDefaults?.lineColor ?? DEFAULT_TENTATIVE_COLORS.line;\n\n return (\n <editableGeoJsonLayer\n id={id}\n data={EMPTY_FEATURE_COLLECTION}\n mode={mode}\n selectedFeatureIndexes={[]}\n onEdit={handleEdit}\n getTentativeFillColor={fillColor}\n getTentativeLineColor={lineColor}\n {...getDefaultEditableLayerProps(unit)}\n />\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+DA,SAAgB,eAAe,EAC7B,KAAK,qBACL,OACA,QACsB;CAEtB,MAAM,YAAY,WAAW,WAAW;CACxC,MAAM,cAAc,SAAS;AAE7B,KAAI,CAAC,YACH,OAAM,IAAI,MACR,iFACD;CAIH,MAAM,EAAE,OAAO,iBAAiB,UAAU,IAAI,YAAY;CAE1D,MAAM,kBAAkB,cAAc,mBAAmB;AAIzD,qBAAoB,aAAa,oBAAoB,YAAY;AAMjE,iBAAgB;AACd,MAAI,CAAC,gBACH;EAGF,MAAM,uBAAuB;AAC3B,4BAAyB,gBAAgB;;AAI3C,WAAS,iBAAiB,YAAY,eAAe;AAErD,eAAa;AACX,YAAS,oBAAoB,YAAY,eAAe;;IAEzD,CAAC,gBAAgB,CAAC;AAGrB,KAAI,CAAC,gBACH,QAAO;CAGT,MAAM,gBAAgB,cAAc,iBAAiB;CAGrD,MAAM,OAAO,gBAAgB,gBAAgB;CAG7C,MAAM,cAAc,EAClB,aACA,eACmC;AAEnC,MAAI,aAAa,cAAc;GAC7B,MAAM,UAAU,YAAY,SAAS,YAAY,SAAS,SAAS;AACnE,OAAI,QAGF,0BACE,aACA,QACD;aAEM,aAAa,gBACtB,wBAAuB,YAAY;;AASvC,QACE,oBAAC;EACK;EACJ,MAAM;EACA;EACN,wBAAwB,EAAE;EAC1B,QAAQ;EACR,uBAVc,eAAe,aAAa,yBAAyB;EAWnE,uBAVc,eAAe,aAAa,yBAAyB;EAWnE,GAAI,6BAA6B,KAAK;GACtC"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../../src/deckgl/shapes/draw-shape-layer/index.tsx"],"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'use client';\n\nimport { useContext } from 'react';\nimport { MapContext } from '../../base-map/provider';\nimport {\n DEFAULT_TENTATIVE_COLORS,\n EMPTY_FEATURE_COLLECTION,\n} from '../shared/constants';\nimport { useShiftZoomDisable } from '../shared/hooks/use-shift-zoom-disable';\nimport { getDefaultEditableLayerProps } from '../shared/utils/layer-config';\nimport { DRAW_SHAPE_LAYER_ID } from './constants';\nimport { getModeInstance } from './modes';\nimport {\n cancelDrawingFromLayer,\n completeDrawingFromLayer,\n drawStore,\n} from './store';\nimport type {\n EditAction,\n FeatureCollection,\n} from '@deck.gl-community/editable-layers';\nimport type { DrawShapeLayerProps } from './types';\n\n/**\n * DrawShapeLayer - A React component for drawing shapes on the map.\n *\n * This component wraps the EditableGeoJsonLayer from @deck.gl-community/editable-layers\n * and integrates with the map-mode and map-cursor systems for proper coordination.\n *\n * Key features:\n * - Renders only when actively drawing (returns null otherwise)\n * - Uses cached mode instances to prevent deck.gl assertion errors\n * - Integrates with the drawing store for state management\n * - Protected drawing mode (rejects mode change requests while drawing)\n * - Distance/area tooltips during drawing\n *\n * @example\n * ```tsx\n * // Import the fiber registration for JSX support\n * import '@accelint/map-toolkit/deckgl/shapes/draw-shape-layer/fiber';\n *\n * function Map({ mapId }) {\n * return (\n * <BaseMap id={mapId}>\n * <displayShapeLayer data={shapes} mapId={mapId} />\n * <DrawShapeLayer mapId={mapId} />\n * </BaseMap>\n * );\n * }\n * ```\n */\nexport function DrawShapeLayer({\n id = DRAW_SHAPE_LAYER_ID,\n mapId,\n unit,\n}: DrawShapeLayerProps) {\n // Get mapId from context if not provided\n const contextId = useContext(MapContext);\n const actualMapId = mapId ?? contextId;\n\n if (!actualMapId) {\n throw new Error(\n 'DrawShapeLayer requires either a mapId prop or to be used within a MapProvider',\n );\n }\n\n // Subscribe to drawing state using the v2 store API\n const { state: drawingState } = drawStore.use(actualMapId);\n\n const activeShapeType = drawingState?.activeShapeType ?? null;\n\n // Disable zoom while Shift is held during rectangle drawing\n // This prevents boxZoom (Shift+drag) from interfering with Shift-to-square constraint\n useShiftZoomDisable(actualMapId, activeShapeType === 'Rectangle');\n\n // If not drawing, return null (don't render the editable layer)\n if (!activeShapeType) {\n return null;\n }\n\n const styleDefaults = drawingState?.styleDefaults ?? null;\n\n // Get the cached mode instance\n const mode = getModeInstance(activeShapeType);\n\n // Handle edit events from EditableGeoJsonLayer\n const handleEdit = ({\n updatedData,\n editType,\n }: EditAction<FeatureCollection>) => {\n // Only process addFeature (drawing complete) and cancelFeature (ESC pressed)\n if (editType === 'addFeature') {\n const feature = updatedData.features.at(-1);\n if (feature) {\n // Type assertion: editable-layers Feature type differs slightly from geojson Feature\n // but they are structurally compatible for our conversion purposes\n completeDrawingFromLayer(\n actualMapId,\n feature as Parameters<typeof completeDrawingFromLayer>[1],\n );\n }\n } else if (editType === 'cancelFeature') {\n cancelDrawingFromLayer(actualMapId);\n }\n // Ignore other edit types during drawing (tentative updates, etc.)\n };\n\n // Get colors from style defaults or use tentative defaults\n const fillColor = styleDefaults?.fillColor ?? DEFAULT_TENTATIVE_COLORS.fill;\n const lineColor = styleDefaults?.lineColor ?? DEFAULT_TENTATIVE_COLORS.line;\n\n return (\n <editableGeoJsonLayer\n id={id}\n data={EMPTY_FEATURE_COLLECTION}\n mode={mode}\n selectedFeatureIndexes={[]}\n onEdit={handleEdit}\n getTentativeFillColor={fillColor}\n getTentativeLineColor={lineColor}\n {...getDefaultEditableLayerProps(unit)}\n />\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+DA,SAAgB,eAAe,EAC7B,KAAK,qBACL,OACA,QACsB;CAEtB,MAAM,YAAY,WAAW,WAAW;CACxC,MAAM,cAAc,SAAS;AAE7B,KAAI,CAAC,YACH,OAAM,IAAI,MACR,iFACD;CAIH,MAAM,EAAE,OAAO,iBAAiB,UAAU,IAAI,YAAY;CAE1D,MAAM,kBAAkB,cAAc,mBAAmB;AAIzD,qBAAoB,aAAa,oBAAoB,YAAY;AAGjE,KAAI,CAAC,gBACH,QAAO;CAGT,MAAM,gBAAgB,cAAc,iBAAiB;CAGrD,MAAM,OAAO,gBAAgB,gBAAgB;CAG7C,MAAM,cAAc,EAClB,aACA,eACmC;AAEnC,MAAI,aAAa,cAAc;GAC7B,MAAM,UAAU,YAAY,SAAS,GAAG,GAAG;AAC3C,OAAI,QAGF,0BACE,aACA,QACD;aAEM,aAAa,gBACtB,wBAAuB,YAAY;;AASvC,QACE,oBAAC;EACK;EACJ,MAAM;EACA;EACN,wBAAwB,EAAE;EAC1B,QAAQ;EACR,uBAVc,eAAe,aAAa,yBAAyB;EAWnE,uBAVc,eAAe,aAAa,yBAAyB;EAWnE,GAAI,6BAA6B,KAAK;GACtC"}
@@ -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,16 +11,16 @@
11
11
  */
12
12
 
13
13
 
14
- import { formatCircleTooltip } from "../../shared/constants.js";
15
14
  import { DEFAULT_DISTANCE_UNITS, getDistanceUnitAbbreviation } from "../../../../shared/units.js";
15
+ import { formatCircleTooltip } from "../../shared/constants.js";
16
16
  import { computeCircleMeasurements } from "../../shared/utils/geometry-measurements.js";
17
17
  import { DrawCircleFromCenterMode } from "@deck.gl-community/editable-layers";
18
18
 
19
19
  //#region src/deckgl/shapes/draw-shape-layer/modes/draw-circle-mode-with-tooltip.ts
20
20
  /**
21
- * Extends DrawCircleFromCenterMode to display diameter and area tooltip.
21
+ * Extends DrawCircleFromCenterMode to display radius and area tooltip.
22
22
  *
23
- * Shows the diameter and area of the circle being drawn based on the radius
23
+ * Shows the radius and area of the circle being drawn based on the radius
24
24
  * from center point to cursor position. The tooltip updates in real-time as
25
25
  * the cursor moves, displaying measurements in the configured distance units.
26
26
  *
@@ -30,7 +30,7 @@ import { DrawCircleFromCenterMode } from "@deck.gl-community/editable-layers";
30
30
  *
31
31
  * ## Drawing Flow
32
32
  * 1. Click to set center point
33
- * 2. Move cursor to set radius (tooltip shows diameter and area)
33
+ * 2. Move cursor to set radius (tooltip shows radius and area)
34
34
  * 3. Click to finish the circle
35
35
  *
36
36
  * @example
@@ -46,7 +46,7 @@ var DrawCircleModeWithTooltip = class extends DrawCircleFromCenterMode {
46
46
  tooltip = null;
47
47
  /**
48
48
  * Handle pointer move events to update the tooltip with circle measurements.
49
- * Calculates diameter and area based on the distance from center to cursor.
49
+ * Calculates radius and area based on the distance from center to cursor.
50
50
  *
51
51
  * @param event - Pointer move event with cursor position
52
52
  * @param props - Mode properties including distance units configuration
@@ -60,11 +60,10 @@ var DrawCircleModeWithTooltip = class extends DrawCircleFromCenterMode {
60
60
  }
61
61
  const { mapCoords } = event;
62
62
  const distanceUnits = props.modeConfig?.distanceUnits ?? DEFAULT_DISTANCE_UNITS;
63
- const centerPoint = clickSequence[clickSequence.length - 1];
64
- const { diameter, area } = computeCircleMeasurements(centerPoint, mapCoords, distanceUnits);
63
+ const { radius, area } = computeCircleMeasurements(clickSequence.at(-1), mapCoords, distanceUnits);
65
64
  this.tooltip = {
66
65
  position: mapCoords,
67
- text: formatCircleTooltip(diameter, area, getDistanceUnitAbbreviation(distanceUnits))
66
+ text: formatCircleTooltip(radius, area, getDistanceUnitAbbreviation(distanceUnits))
68
67
  };
69
68
  }
70
69
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"draw-circle-mode-with-tooltip.js","names":[],"sources":["../../../../../src/deckgl/shapes/draw-shape-layer/modes/draw-circle-mode-with-tooltip.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\nimport {\n DrawCircleFromCenterMode,\n type FeatureCollection,\n type ModeProps,\n type PointerMoveEvent,\n type Tooltip,\n} from '@deck.gl-community/editable-layers';\nimport {\n DEFAULT_DISTANCE_UNITS,\n getDistanceUnitAbbreviation,\n} from '@/shared/units';\nimport { formatCircleTooltip } from '../../shared/constants';\nimport { computeCircleMeasurements } from '../../shared/utils/geometry-measurements';\n\n/**\n * Extends DrawCircleFromCenterMode to display diameter and area tooltip.\n *\n * Shows the diameter and area of the circle being drawn based on the radius\n * from center point to cursor position. The tooltip updates in real-time as\n * the cursor moves, displaying measurements in the configured distance units.\n *\n * ## Usage\n * This mode is automatically used by DrawShapeLayer when drawing circles.\n * The mode is cached at module level to prevent deck.gl assertion failures.\n *\n * ## Drawing Flow\n * 1. Click to set center point\n * 2. Move cursor to set radius (tooltip shows diameter and area)\n * 3. Click to finish the circle\n *\n * @example\n * ```typescript\n * import { DrawCircleModeWithTooltip } from '@accelint/map-toolkit/deckgl/shapes/draw-shape-layer/modes';\n *\n * // Used internally by DrawShapeLayer\n * const mode = new DrawCircleModeWithTooltip();\n * ```\n */\nexport class DrawCircleModeWithTooltip extends DrawCircleFromCenterMode {\n /** Current tooltip state (null when not drawing) */\n private tooltip: Tooltip | null = null;\n\n /**\n * Handle pointer move events to update the tooltip with circle measurements.\n * Calculates diameter and area based on the distance from center to cursor.\n *\n * @param event - Pointer move event with cursor position\n * @param props - Mode properties including distance units configuration\n */\n override handlePointerMove(\n event: PointerMoveEvent,\n props: ModeProps<FeatureCollection>,\n ) {\n super.handlePointerMove(event, props);\n\n const clickSequence = this.getClickSequence();\n if (!clickSequence.length) {\n this.tooltip = null;\n return;\n }\n\n const { mapCoords } = event;\n const distanceUnits =\n props.modeConfig?.distanceUnits ?? DEFAULT_DISTANCE_UNITS;\n\n const centerPoint = clickSequence[clickSequence.length - 1] as [\n number,\n number,\n ];\n const edgePoint = mapCoords as [number, number];\n\n const { diameter, area } = computeCircleMeasurements(\n centerPoint,\n edgePoint,\n distanceUnits,\n );\n const unitAbbrev = getDistanceUnitAbbreviation(distanceUnits);\n\n this.tooltip = {\n position: mapCoords,\n text: formatCircleTooltip(diameter, area, unitAbbrev),\n };\n }\n\n /**\n * Get the current tooltip array for rendering.\n *\n * @returns Array containing the tooltip if one is active, empty array otherwise\n */\n override getTooltips(): Tooltip[] {\n return this.tooltip ? [this.tooltip] : [];\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkDA,IAAa,4BAAb,cAA+C,yBAAyB;;CAEtE,AAAQ,UAA0B;;;;;;;;CASlC,AAAS,kBACP,OACA,OACA;AACA,QAAM,kBAAkB,OAAO,MAAM;EAErC,MAAM,gBAAgB,KAAK,kBAAkB;AAC7C,MAAI,CAAC,cAAc,QAAQ;AACzB,QAAK,UAAU;AACf;;EAGF,MAAM,EAAE,cAAc;EACtB,MAAM,gBACJ,MAAM,YAAY,iBAAiB;EAErC,MAAM,cAAc,cAAc,cAAc,SAAS;EAMzD,MAAM,EAAE,UAAU,SAAS,0BACzB,aAHgB,WAKhB,cACD;AAGD,OAAK,UAAU;GACb,UAAU;GACV,MAAM,oBAAoB,UAAU,MAJnB,4BAA4B,cAAc,CAIN;GACtD;;;;;;;CAQH,AAAS,cAAyB;AAChC,SAAO,KAAK,UAAU,CAAC,KAAK,QAAQ,GAAG,EAAE"}
1
+ {"version":3,"file":"draw-circle-mode-with-tooltip.js","names":[],"sources":["../../../../../src/deckgl/shapes/draw-shape-layer/modes/draw-circle-mode-with-tooltip.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\nimport {\n DrawCircleFromCenterMode,\n type FeatureCollection,\n type ModeProps,\n type PointerMoveEvent,\n type Tooltip,\n} from '@deck.gl-community/editable-layers';\nimport {\n DEFAULT_DISTANCE_UNITS,\n getDistanceUnitAbbreviation,\n} from '@/shared/units';\nimport { formatCircleTooltip } from '../../shared/constants';\nimport { computeCircleMeasurements } from '../../shared/utils/geometry-measurements';\n\n/**\n * Extends DrawCircleFromCenterMode to display radius and area tooltip.\n *\n * Shows the radius and area of the circle being drawn based on the radius\n * from center point to cursor position. The tooltip updates in real-time as\n * the cursor moves, displaying measurements in the configured distance units.\n *\n * ## Usage\n * This mode is automatically used by DrawShapeLayer when drawing circles.\n * The mode is cached at module level to prevent deck.gl assertion failures.\n *\n * ## Drawing Flow\n * 1. Click to set center point\n * 2. Move cursor to set radius (tooltip shows radius and area)\n * 3. Click to finish the circle\n *\n * @example\n * ```typescript\n * import { DrawCircleModeWithTooltip } from '@accelint/map-toolkit/deckgl/shapes/draw-shape-layer/modes';\n *\n * // Used internally by DrawShapeLayer\n * const mode = new DrawCircleModeWithTooltip();\n * ```\n */\nexport class DrawCircleModeWithTooltip extends DrawCircleFromCenterMode {\n /** Current tooltip state (null when not drawing) */\n private tooltip: Tooltip | null = null;\n\n /**\n * Handle pointer move events to update the tooltip with circle measurements.\n * Calculates radius and area based on the distance from center to cursor.\n *\n * @param event - Pointer move event with cursor position\n * @param props - Mode properties including distance units configuration\n */\n override handlePointerMove(\n event: PointerMoveEvent,\n props: ModeProps<FeatureCollection>,\n ) {\n super.handlePointerMove(event, props);\n\n const clickSequence = this.getClickSequence();\n if (!clickSequence.length) {\n this.tooltip = null;\n return;\n }\n\n const { mapCoords } = event;\n const distanceUnits =\n props.modeConfig?.distanceUnits ?? DEFAULT_DISTANCE_UNITS;\n\n const centerPoint = clickSequence.at(-1) as [number, number];\n const edgePoint = mapCoords as [number, number];\n\n const { radius, area } = computeCircleMeasurements(\n centerPoint,\n edgePoint,\n distanceUnits,\n );\n const unitAbbrev = getDistanceUnitAbbreviation(distanceUnits);\n\n this.tooltip = {\n position: mapCoords,\n text: formatCircleTooltip(radius, area, unitAbbrev),\n };\n }\n\n /**\n * Get the current tooltip array for rendering.\n *\n * @returns Array containing the tooltip if one is active, empty array otherwise\n */\n override getTooltips(): Tooltip[] {\n return this.tooltip ? [this.tooltip] : [];\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkDA,IAAa,4BAAb,cAA+C,yBAAyB;;CAEtE,AAAQ,UAA0B;;;;;;;;CASlC,AAAS,kBACP,OACA,OACA;AACA,QAAM,kBAAkB,OAAO,MAAM;EAErC,MAAM,gBAAgB,KAAK,kBAAkB;AAC7C,MAAI,CAAC,cAAc,QAAQ;AACzB,QAAK,UAAU;AACf;;EAGF,MAAM,EAAE,cAAc;EACtB,MAAM,gBACJ,MAAM,YAAY,iBAAiB;EAKrC,MAAM,EAAE,QAAQ,SAAS,0BAHL,cAAc,GAAG,GAAG,EACtB,WAKhB,cACD;AAGD,OAAK,UAAU;GACb,UAAU;GACV,MAAM,oBAAoB,QAAQ,MAJjB,4BAA4B,cAAc,CAIR;GACpD;;;;;;;CAQH,AAAS,cAAyB;AAChC,SAAO,KAAK,UAAU,CAAC,KAAK,QAAQ,GAAG,EAAE"}
@@ -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,10 +11,10 @@
11
11
  */
12
12
 
13
13
 
14
- import { formatDistanceTooltip, formatEllipseTooltip } from "../../shared/constants.js";
15
14
  import { DEFAULT_DISTANCE_UNITS, getDistanceUnitAbbreviation } from "../../../../shared/units.js";
16
- import { DrawEllipseUsingThreePointsMode } from "@deck.gl-community/editable-layers";
15
+ import { formatDistanceTooltip, formatEllipseTooltip } from "../../shared/constants.js";
17
16
  import { distance } from "@turf/turf";
17
+ import { DrawEllipseUsingThreePointsMode } from "@deck.gl-community/editable-layers";
18
18
 
19
19
  //#region src/deckgl/shapes/draw-shape-layer/modes/draw-ellipse-mode-with-tooltip.ts
20
20
  /**