@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
@@ -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,25 +13,23 @@
13
13
 
14
14
  'use client';
15
15
 
16
- import { DASH_ARRAYS, SHAPE_LAYER_IDS } from "../shared/constants.js";
16
+ import { createLoggerDomain } from "../../../shared/logger.js";
17
17
  import { ShapeEvents } from "../shared/events.js";
18
+ import { isLineGeometry, isPointType, isPolygonGeometry } from "../shared/types.js";
19
+ import { SHAPE_LAYER_IDS } from "../shared/constants.js";
18
20
  import { getDashArray, getFillColor, getLineColor } from "../shared/utils/style-utils.js";
19
- import { COFFIN_CORNERS, DEFAULT_DISPLAY_PROPS, MAP_INTERACTION } from "./constants.js";
21
+ import { BRIGHTNESS_FACTOR, COFFIN_CORNERS, DASH_EXTENSION, DEFAULT_DISPLAY_PROPS, HIGHLIGHT_COLOR_TUPLE, MAP_INTERACTION, MATERIAL_SETTINGS } from "./constants.js";
20
22
  import { createShapeLabelLayer } from "./shape-label-layer.js";
21
- import { getHighlightColor, getHighlightLineWidth, getHoverLineWidth } from "./utils/display-style.js";
23
+ import { applyOverlayOpacity, brightenColor, getHighlightLineWidth, getHoverLineWidth, getOverlayFillColor } from "./utils/display-style.js";
24
+ import { buildIndicatorLineData, classifyElevatedFeatures, createCurtainPolygonFeatures, flattenFeatureTo2D, getFeatureElevation, partitionCurtains } from "./utils/elevation.js";
25
+ import { extendMappingWithCoffinCorners, getIconConfig, getIconLayerProps, getIconUpdateTriggers } from "./utils/icon-config.js";
26
+ import { getPointInteractionState } from "./utils/interaction.js";
22
27
  import { Broadcast } from "@accelint/bus";
23
- import { getLogger } from "@accelint/logger";
24
28
  import { CompositeLayer } from "@deck.gl/core";
25
- import { PathStyleExtension } from "@deck.gl/extensions";
26
- import { GeoJsonLayer, IconLayer } from "@deck.gl/layers";
29
+ import { GeoJsonLayer, IconLayer, LineLayer } from "@deck.gl/layers";
27
30
 
28
31
  //#region src/deckgl/shapes/display-shape-layer/index.ts
29
- const logger = getLogger({
30
- enabled: process.env.NODE_ENV !== "production",
31
- level: "warn",
32
- prefix: "[DisplayShapeLayer]",
33
- pretty: true
34
- });
32
+ const logger = createLoggerDomain("[DisplayShapeLayer]");
35
33
  /**
36
34
  * Typed event bus instance for shape events.
37
35
  * Provides type-safe event emission for shape interactions.
@@ -46,24 +44,27 @@ const shapeBus = Broadcast.getInstance();
46
44
  * ## Features
47
45
  * - **Multiple geometry types**: Point, LineString, Polygon, and Circle
48
46
  * - **Icon support**: Custom icons for Point geometries via icon atlases
49
- * - **Interactive selection**: Click handling with dotted border and optional highlight
50
- * - **Hover effects**: Border/outline width increases on hover for better UX
47
+ * - **Interactive selection**: Click handling with brightness overlay on polygon select, optional highlight effect for non-icon-Point shapes (if showHighlight=true)
48
+ * - **Hover effects**: Polygon fills brighten via material lighting; outline width increases by 2px on hover
51
49
  * - **Customizable labels**: Flexible label positioning with per-shape or global options
52
50
  * - **Style properties**: Full control over colors, border/outline patterns, and opacity
53
51
  * - **Event bus integration**: Automatically emits shape events via @accelint/bus
54
52
  * - **Multi-map support**: Events include map instance ID for isolation
55
53
  *
56
- * ## Selection Visual Feedback
57
- * When a shape is selected via `selectedShapeId`:
58
- * - The shape's border/outline pattern changes to dotted
59
- * - An optional highlight renders underneath (controlled by `showHighlight` prop)
54
+ * ## Interaction Philosophy
55
+ * Interactions never modify a shape's innate styling. Hover and selection are always
56
+ * additive overlays rendered apart from the main layer using opacity-scaled fill colors
57
+ * and material-based brightness the base shape is never altered.
60
58
  *
61
59
  * ## Layer Structure
62
- * Renders up to four sublayers (in order, bottom to top):
63
- * 1. **Highlight layer**: Selection highlight effect for non-icon-Point shapes (if showHighlight=true)
64
- * 2. **Coffin corners layer**: Selection/hover feedback for Point shapes with icons
65
- * 3. **Main GeoJsonLayer**: Shape geometries with styling and interaction
66
- * 4. **Label layer**: Text labels (if showLabels enabled)
60
+ * Renders up to seven sublayers (in order, bottom to top):
61
+ * 1. **Select layer**: Selection brightness overlay for polygon shapes
62
+ * 2. **Hover layer**: Hover brightness overlay for polygon shapes
63
+ * 3. **Coffin corners layer**: Selection/hover feedback for Point shapes with icons
64
+ * 4. **Elevation visualization**: Curtains (LineStrings) or wireframes (polygons) elevation only
65
+ * 5. **Elevation indicators**: Vertical strut lines for elevated non-polygon shapes — elevation only
66
+ * 6. **Main GeoJsonLayer**: Shape geometries with styling and interaction
67
+ * 7. **Label layer**: Text labels (if showLabels enabled)
67
68
  *
68
69
  * ## Icon Atlas Constraint
69
70
  * When using icons for Point geometries, all shapes in a single layer must share the
@@ -124,6 +125,10 @@ const shapeBus = Broadcast.getInstance();
124
125
  var DisplayShapeLayer = class extends CompositeLayer {
125
126
  /** Cache for transformed features to avoid recreating objects on every render */
126
127
  featuresCache = null;
128
+ /** Cache for elevation classification and curtain features */
129
+ elevationCache = null;
130
+ /** Cache for elevation indicator line segments */
131
+ indicatorCache = null;
127
132
  static layerName = "DisplayShapeLayer";
128
133
  static defaultProps = { ...DEFAULT_DISPLAY_PROPS };
129
134
  /**
@@ -135,19 +140,64 @@ var DisplayShapeLayer = class extends CompositeLayer {
135
140
  lastHoveredId: void 0
136
141
  });
137
142
  this.featuresCache = null;
143
+ this.elevationCache = null;
144
+ this.indicatorCache = null;
145
+ }
146
+ /**
147
+ * Resolved highlight color — uses prop if provided, falls back to default.
148
+ */
149
+ get resolvedHighlight() {
150
+ return this.props.highlightColor ?? HIGHLIGHT_COLOR_TUPLE;
151
+ }
152
+ /**
153
+ * Handle picking events from the main shapes layer
154
+ */
155
+ handleMainLayerPick(info, mode) {
156
+ if (mode === "query") this.handleShapeClick(info);
157
+ if (mode === "hover" || !mode) {
158
+ if (info.index !== void 0 && info.index !== this.state?.hoverIndex) this.setState({ hoverIndex: info.index });
159
+ this.handleShapeHover(info);
160
+ }
161
+ }
162
+ /**
163
+ * Handle picking events from curtain layers and map back to original LineString
164
+ */
165
+ handleCurtainPick(info, mode) {
166
+ if (!info.object) return;
167
+ const curtainShapeId = info.object.properties?.shapeId;
168
+ if (!curtainShapeId) return;
169
+ const features = this.getFeaturesWithId();
170
+ const featureIndex = this.featuresCache?.shapeIdToIndex.get(curtainShapeId);
171
+ if (featureIndex === void 0) return;
172
+ const modifiedInfo = {
173
+ ...info,
174
+ index: featureIndex,
175
+ object: features[featureIndex]
176
+ };
177
+ if (mode === "query") this.handleShapeClick(modifiedInfo);
178
+ if (mode === "hover" || !mode) {
179
+ if (featureIndex !== this.state?.hoverIndex) this.setState({ hoverIndex: featureIndex });
180
+ this.handleShapeHover(modifiedInfo);
181
+ }
182
+ return modifiedInfo;
138
183
  }
139
184
  /**
140
185
  * Override getPickingInfo to handle events from sublayers
141
186
  * This is the correct pattern for CompositeLayer event handling
142
187
  */
143
188
  getPickingInfo({ info, mode, sourceLayer }) {
189
+ if ((mode === "hover" || !mode) && (info.index === void 0 || info.index < 0)) {
190
+ if (this.state?.hoverIndex !== void 0) this.setState({ hoverIndex: void 0 });
191
+ this.handleShapeHover(info);
192
+ return info;
193
+ }
144
194
  if (sourceLayer?.id === `${this.props.id}-${SHAPE_LAYER_IDS.DISPLAY}`) {
145
- if (mode === "query") this.handleShapeClick(info);
146
- if (mode === "hover" || !mode) {
147
- if (info.index !== void 0 && info.index !== this.state?.hoverIndex) this.setState({ hoverIndex: info.index });
148
- else if (info.index === void 0 && this.state?.hoverIndex !== void 0) this.setState({ hoverIndex: void 0 });
149
- this.handleShapeHover(info);
150
- }
195
+ this.handleMainLayerPick(info, mode);
196
+ return info;
197
+ }
198
+ if (sourceLayer?.id?.includes("-elevation-curtain")) {
199
+ const curtainInfo = this.handleCurtainPick(info, mode);
200
+ if (curtainInfo) return curtainInfo;
151
201
  }
152
202
  return info;
153
203
  }
@@ -160,20 +210,25 @@ var DisplayShapeLayer = class extends CompositeLayer {
160
210
  if (this.featuresCache?.data === data) return this.featuresCache.features;
161
211
  const features = [];
162
212
  const shapeIdToIndex = /* @__PURE__ */ new Map();
213
+ const normalizedLineColors = [];
163
214
  for (const [i, shape] of data.entries()) {
164
- features.push({
215
+ let feature = {
165
216
  ...shape.feature,
166
217
  properties: {
167
218
  ...shape.feature.properties,
168
219
  shapeId: shape.id
169
220
  }
170
- });
221
+ };
222
+ if (isPolygonGeometry(feature.geometry) && getFeatureElevation(feature) > 0) feature = flattenFeatureTo2D(feature);
223
+ features.push(feature);
171
224
  shapeIdToIndex.set(shape.id, i);
225
+ normalizedLineColors.push(getLineColor(shape.feature));
172
226
  }
173
227
  this.featuresCache = {
174
228
  data,
175
229
  features,
176
- shapeIdToIndex
230
+ shapeIdToIndex,
231
+ normalizedLineColors
177
232
  };
178
233
  return features;
179
234
  }
@@ -182,7 +237,8 @@ var DisplayShapeLayer = class extends CompositeLayer {
182
237
  * Used by event handlers to get full shape without storing in feature properties.
183
238
  */
184
239
  getShapeById(shapeId) {
185
- return this.props.data.find((shape) => shape.id === shapeId);
240
+ const index = this.featuresCache?.shapeIdToIndex.get(shapeId);
241
+ return index !== void 0 ? this.props.data[index] : void 0;
186
242
  }
187
243
  /**
188
244
  * Handle shape click
@@ -205,116 +261,166 @@ var DisplayShapeLayer = class extends CompositeLayer {
205
261
  */
206
262
  handleShapeHover = (info) => {
207
263
  const { onShapeHover, mapId } = this.props;
208
- const shapeId = info.object?.properties?.shapeId ?? null;
209
- const shape = shapeId ? this.getShapeById(shapeId) ?? null : null;
264
+ const shapeId = info.object?.properties?.shapeId;
265
+ const shape = shapeId ? this.getShapeById(shapeId) : void 0;
210
266
  if (shapeId !== this.state?.lastHoveredId) {
211
267
  this.setState({ lastHoveredId: shapeId });
212
268
  shapeBus.emit(ShapeEvents.hovered, {
213
- shapeId,
269
+ shapeId: shapeId ?? null,
214
270
  mapId
215
271
  });
216
272
  }
217
273
  if (onShapeHover) onShapeHover(shape);
218
274
  };
219
275
  /**
220
- * Render highlight sublayer (underneath main layer)
221
- * Note: Points with icons use coffin corners instead of highlight layer
276
+ * Get or compute elevation-derived data (feature classification + curtain features).
277
+ * Cached on features identity and applyBaseOpacity to avoid per-frame recomputation.
278
+ */
279
+ getElevationData(features, applyBaseOpacity) {
280
+ if (this.elevationCache !== null && this.elevationCache.features === features && this.elevationCache.applyBaseOpacity === applyBaseOpacity) return this.elevationCache;
281
+ const classification = classifyElevatedFeatures(features, getFeatureElevation);
282
+ const curtainFeatures = createCurtainPolygonFeatures(classification.lines, applyBaseOpacity);
283
+ this.elevationCache = {
284
+ features,
285
+ applyBaseOpacity,
286
+ classification,
287
+ curtainFeatures
288
+ };
289
+ return {
290
+ classification,
291
+ curtainFeatures
292
+ };
293
+ }
294
+ /**
295
+ * Render highlight sublayer (underneath main layer).
296
+ * Note: Points with icons use coffin corners instead of highlight layer.
222
297
  */
223
298
  renderHighlightLayer(features) {
224
- const { selectedShapeId, showHighlight, highlightColor } = this.props;
225
- if (!selectedShapeId || showHighlight === false) return null;
226
- const selectedFeature = features.find((f) => f.properties?.shapeId === selectedShapeId);
227
- if (!selectedFeature) return null;
228
- if (selectedFeature.geometry.type === "Point") {
229
- if (!!selectedFeature.properties?.styleProperties?.icon) return null;
299
+ const { selectedShapeId, showHighlight } = this.props;
300
+ if (!selectedShapeId || showHighlight === false) return [];
301
+ const featureIndex = this.featuresCache?.shapeIdToIndex.get(selectedShapeId);
302
+ const selectedFeature = featureIndex !== void 0 ? features[featureIndex] : void 0;
303
+ if (!selectedFeature) return [];
304
+ if (isPointType(selectedFeature.geometry)) {
305
+ if (!!selectedFeature.properties?.styleProperties?.icon) return [];
230
306
  }
231
- return new GeoJsonLayer({
307
+ const highlightFeature = flattenFeatureTo2D(selectedFeature);
308
+ const lineColor = this.resolvedHighlight;
309
+ return [new GeoJsonLayer({
232
310
  id: `${this.props.id}-${SHAPE_LAYER_IDS.DISPLAY_HIGHLIGHT}`,
233
- data: [selectedFeature],
234
- filled: true,
311
+ data: [highlightFeature],
312
+ filled: false,
235
313
  stroked: true,
236
314
  lineWidthUnits: "pixels",
237
315
  lineWidthMinPixels: MAP_INTERACTION.LINE_WIDTH_MIN_PIXELS,
238
- getFillColor: () => [
239
- 0,
240
- 0,
241
- 0,
242
- 0
243
- ],
244
- getLineColor: () => highlightColor || getHighlightColor(),
316
+ getLineColor: lineColor,
245
317
  getLineWidth: getHighlightLineWidth,
246
318
  pickable: false,
247
319
  updateTriggers: {
248
- getLineColor: [highlightColor],
320
+ getLineColor: [this.props.highlightColor],
249
321
  getLineWidth: [selectedShapeId, features]
250
322
  }
251
- });
323
+ })];
324
+ }
325
+ /**
326
+ * Render selection overlay layer for polygon shapes.
327
+ * Mirrors renderHoverLayer but triggers on selectedShapeId instead of hover.
328
+ * When a shape is both selected and hovered, both layers stack for a brighter combined effect.
329
+ */
330
+ renderSelectLayer(features) {
331
+ const { selectedShapeId, enableElevation } = this.props;
332
+ if (!selectedShapeId) return [];
333
+ const featureIndex = this.featuresCache?.shapeIdToIndex.get(selectedShapeId);
334
+ const selectedFeature = featureIndex !== void 0 ? features[featureIndex] : void 0;
335
+ if (!selectedFeature) return [];
336
+ if (!isPolygonGeometry(selectedFeature.geometry)) return [];
337
+ return [new GeoJsonLayer({
338
+ id: `${this.props.id}-${SHAPE_LAYER_IDS.DISPLAY_SELECTION}`,
339
+ data: [selectedFeature],
340
+ filled: true,
341
+ stroked: false,
342
+ getFillColor: getOverlayFillColor,
343
+ extruded: enableElevation,
344
+ getElevation: getFeatureElevation,
345
+ material: MATERIAL_SETTINGS.HOVER_OR_SELECT,
346
+ pickable: false,
347
+ updateTriggers: {
348
+ data: [features, selectedShapeId],
349
+ getFillColor: [features],
350
+ getElevation: [features]
351
+ }
352
+ })];
353
+ }
354
+ /**
355
+ * Render hover layer for all polygon shapes (2D and 3D).
356
+ * Overlays the shape's base fill with brighter material lighting.
357
+ * Stacks with other interaction layers (e.g. selection highlight underneath).
358
+ */
359
+ renderHoverLayer(features) {
360
+ const { enableElevation, selectedShapeId } = this.props;
361
+ const hoverIndex = this.state?.hoverIndex;
362
+ if (hoverIndex === void 0) return [];
363
+ const hoveredFeature = features[hoverIndex];
364
+ if (!hoveredFeature) return [];
365
+ if (!isPolygonGeometry(hoveredFeature.geometry)) return [];
366
+ const material = hoveredFeature.properties?.shapeId === selectedShapeId ? MATERIAL_SETTINGS.HOVER_AND_SELECT : MATERIAL_SETTINGS.HOVER_OR_SELECT;
367
+ return [new GeoJsonLayer({
368
+ id: `${this.props.id}-${SHAPE_LAYER_IDS.DISPLAY}-hover`,
369
+ data: [hoveredFeature],
370
+ filled: true,
371
+ stroked: false,
372
+ getFillColor: getOverlayFillColor,
373
+ extruded: enableElevation,
374
+ getElevation: getFeatureElevation,
375
+ material,
376
+ pickable: false,
377
+ updateTriggers: {
378
+ data: [features, hoverIndex],
379
+ getFillColor: [features],
380
+ getElevation: [features],
381
+ material: [selectedShapeId, hoverIndex]
382
+ }
383
+ })];
252
384
  }
253
385
  /**
254
386
  * Render coffin corners layer for Point geometries that have icons on hover/select
255
- * Coffin corners provide visual feedback for points instead of highlight layer
387
+ * Coffin corners provide visual feedback for points instead of select layer
256
388
  */
257
389
  renderCoffinCornersLayer(features) {
258
390
  const { selectedShapeId } = this.props;
259
391
  const hoverIndex = this.state?.hoverIndex;
260
392
  const shapeIdToIndex = this.featuresCache?.shapeIdToIndex;
261
- if (!shapeIdToIndex) return null;
262
- const pointFeatures = features.filter((f) => {
263
- if (f.geometry.type !== "Point") return false;
264
- if (!!!f.properties?.styleProperties?.icon) return false;
265
- const shapeId = f.properties?.shapeId;
266
- const isSelected = shapeId === selectedShapeId;
267
- const featureIndex = shapeId ? shapeIdToIndex.get(shapeId) : void 0;
268
- return isSelected || hoverIndex !== void 0 && featureIndex === hoverIndex;
269
- });
270
- if (pointFeatures.length === 0) return null;
393
+ if (!shapeIdToIndex) return [];
394
+ const pointFeatures = [];
395
+ for (const f of features) {
396
+ if (f.geometry.type !== "Point") continue;
397
+ if (!f.properties?.styleProperties?.icon) continue;
398
+ const { isSelected, isHovered } = getPointInteractionState(f, selectedShapeId, hoverIndex, shapeIdToIndex);
399
+ if (isSelected || isHovered) pointFeatures.push(f);
400
+ }
401
+ if (pointFeatures.length === 0) return [];
271
402
  const firstPointIcon = pointFeatures[0]?.properties?.styleProperties?.icon;
272
403
  const iconAtlas = firstPointIcon?.atlas;
273
404
  const iconMapping = firstPointIcon?.mapping;
274
405
  if (!(iconAtlas && iconMapping)) {
275
406
  logger.warn("Point shape has icon style but missing iconAtlas or iconMapping - coffin corners will not render");
276
- return null;
407
+ return [];
277
408
  }
278
- const extendedMapping = {
279
- ...iconMapping,
280
- [COFFIN_CORNERS.HOVER_ICON]: {
281
- x: 0,
282
- y: 0,
283
- width: 76,
284
- height: 76,
285
- mask: false
286
- },
287
- [COFFIN_CORNERS.SELECTED_ICON]: {
288
- x: 76,
289
- y: 0,
290
- width: 76,
291
- height: 76,
292
- mask: false
293
- },
294
- [COFFIN_CORNERS.SELECTED_HOVER_ICON]: {
295
- x: 152,
296
- y: 0,
297
- width: 76,
298
- height: 76,
299
- mask: false
300
- }
301
- };
302
- return new IconLayer({
409
+ const extendedMapping = extendMappingWithCoffinCorners(iconMapping);
410
+ return [new IconLayer({
303
411
  id: `${this.props.id}-${SHAPE_LAYER_IDS.DISPLAY}-coffin-corners`,
304
412
  data: pointFeatures,
305
413
  iconAtlas,
306
414
  iconMapping: extendedMapping,
307
415
  getIcon: (d) => {
308
- const shapeId = d.properties?.shapeId;
309
- const isSelected = shapeId === selectedShapeId;
310
- const featureIndex = shapeId ? shapeIdToIndex.get(shapeId) : void 0;
311
- if (isSelected && hoverIndex !== void 0 && featureIndex === hoverIndex) return COFFIN_CORNERS.SELECTED_HOVER_ICON;
416
+ const { isSelected, isHovered } = getPointInteractionState(d, selectedShapeId, hoverIndex, shapeIdToIndex);
417
+ if (isSelected && isHovered) return COFFIN_CORNERS.SELECTED_HOVER_ICON;
312
418
  if (isSelected) return COFFIN_CORNERS.SELECTED_ICON;
313
419
  return COFFIN_CORNERS.HOVER_ICON;
314
420
  },
315
421
  getSize: COFFIN_CORNERS.SIZE,
316
422
  getPosition: (d) => {
317
- return d.geometry.type === "Point" ? d.geometry.coordinates : [0, 0];
423
+ return isPointType(d.geometry) ? d.geometry.coordinates : [0, 0];
318
424
  },
319
425
  getPixelOffset: (d) => {
320
426
  return [-1, -(d.properties?.styleProperties?.icon?.size ?? MAP_INTERACTION.ICON_SIZE) / 2];
@@ -329,80 +435,63 @@ var DisplayShapeLayer = class extends CompositeLayer {
329
435
  this.state?.hoverIndex
330
436
  ]
331
437
  }
332
- });
333
- }
334
- /**
335
- * Extract icon configuration from features in a single pass.
336
- * Returns the first icon's atlas and mapping (all shapes share the same atlas).
337
- * Uses early return for O(1) best case when first feature has icons.
338
- */
339
- getIconConfig(features) {
340
- for (const f of features) {
341
- const icon = f.properties?.styleProperties?.icon;
342
- if (icon) return {
343
- hasIcons: true,
344
- atlas: icon.atlas,
345
- mapping: icon.mapping
346
- };
347
- }
348
- return { hasIcons: false };
438
+ })];
349
439
  }
350
440
  /**
351
441
  * Render main shapes layer
352
442
  */
353
443
  renderMainLayer(features) {
354
444
  const { pickable, applyBaseOpacity, selectedShapeId } = this.props;
355
- const { hasIcons, atlas: iconAtlas, mapping: iconMapping } = this.getIconConfig(features);
445
+ const { hasIcons, atlas: iconAtlas, mapping: iconMapping } = getIconConfig(features);
356
446
  return new GeoJsonLayer({
357
447
  id: `${this.props.id}-${SHAPE_LAYER_IDS.DISPLAY}`,
358
448
  data: features,
359
449
  filled: true,
360
450
  stroked: true,
361
451
  getFillColor: (d) => getFillColor(d, applyBaseOpacity),
362
- getLineColor,
452
+ getLineColor: (d, info) => {
453
+ const baseColor = this.featuresCache?.normalizedLineColors[info?.index ?? -1] ?? getLineColor(d);
454
+ const isHovered = info?.index === this.state?.hoverIndex;
455
+ const isSelected = d.properties?.shapeId === selectedShapeId;
456
+ if (isHovered && isSelected) return brightenColor(baseColor, BRIGHTNESS_FACTOR.HOVER_AND_SELECT);
457
+ if (isHovered || isSelected) return brightenColor(baseColor, BRIGHTNESS_FACTOR.HOVER_OR_SELECT);
458
+ return baseColor;
459
+ },
363
460
  getLineWidth: (d, info) => {
461
+ if (this.props.enableElevation && isLineGeometry(d.geometry) && getFeatureElevation(d) > 0) return d.properties?.styleProperties?.lineWidth ?? 2;
364
462
  return getHoverLineWidth(d, info?.index === this.state?.hoverIndex);
365
463
  },
366
464
  lineWidthUnits: "pixels",
367
465
  lineWidthMinPixels: MAP_INTERACTION.LINE_WIDTH_MIN_PIXELS,
368
466
  lineWidthMaxPixels: 20,
467
+ extruded: this.props.enableElevation ?? false,
468
+ getElevation: getFeatureElevation,
469
+ ...this.props.enableElevation ? { material: MATERIAL_SETTINGS.NORMAL } : {},
369
470
  pointType: hasIcons ? "icon" : "circle",
370
471
  getPointRadius: (d) => {
371
472
  return d.properties?.styleProperties?.icon?.size ?? 2;
372
473
  },
373
474
  pointRadiusUnits: "pixels",
374
- ...hasIcons && iconAtlas ? { iconAtlas } : {},
375
- ...hasIcons && iconMapping ? { iconMapping } : {},
376
- ...hasIcons ? {
377
- getIcon: (d) => d.properties?.styleProperties?.icon?.name ?? "marker",
378
- getIconSize: (d) => {
379
- return d.properties?.styleProperties?.icon?.size ?? MAP_INTERACTION.ICON_SIZE;
380
- },
381
- getIconColor: getLineColor,
382
- getIconPixelOffset: (d) => {
383
- return [-1, -(d.properties?.styleProperties?.icon?.size ?? MAP_INTERACTION.ICON_SIZE) / 2];
384
- },
385
- iconBillboard: false
386
- } : {},
387
- extensions: [new PathStyleExtension({ dash: true })],
388
- getDashArray: (d) => {
389
- if (d.properties?.shapeId === selectedShapeId) return DASH_ARRAYS.dotted;
390
- return getDashArray(d);
391
- },
475
+ ...getIconLayerProps(hasIcons, iconAtlas, iconMapping),
476
+ extensions: DASH_EXTENSION,
477
+ getDashArray,
392
478
  pickable,
393
479
  autoHighlight: false,
480
+ ...this.props.enableElevation ? { parameters: {
481
+ depthTest: true,
482
+ depthCompare: "less-equal"
483
+ } } : {},
394
484
  updateTriggers: {
395
485
  getFillColor: [features, applyBaseOpacity],
396
- getLineColor: [features],
486
+ getLineColor: [
487
+ features,
488
+ this.state?.hoverIndex,
489
+ selectedShapeId
490
+ ],
397
491
  getLineWidth: [features, this.state?.hoverIndex],
398
- getDashArray: [features, selectedShapeId],
492
+ getDashArray: [features],
399
493
  getPointRadius: [features],
400
- ...hasIcons ? {
401
- getIcon: [features],
402
- getIconSize: [features],
403
- getIconColor: [features],
404
- getIconPixelOffset: [features]
405
- } : {}
494
+ ...getIconUpdateTriggers(hasIcons, features)
406
495
  }
407
496
  });
408
497
  }
@@ -415,32 +504,170 @@ var DisplayShapeLayer = class extends CompositeLayer {
415
504
  */
416
505
  renderLabelsLayer() {
417
506
  const { showLabels, data, labelOptions } = this.props;
418
- if (showLabels === "never") return null;
507
+ if (showLabels === "never") return [];
419
508
  let labelData = data;
420
509
  if (showLabels === "hover") {
421
510
  const hoverIndex = this.state?.hoverIndex;
422
- if (hoverIndex === void 0) return null;
511
+ if (hoverIndex === void 0) return [];
423
512
  const hoveredShape = data[hoverIndex];
424
513
  labelData = hoveredShape ? [hoveredShape] : [];
425
514
  }
426
- if (labelData.length === 0) return null;
427
- return createShapeLabelLayer({
515
+ if (labelData.length === 0) return [];
516
+ return [createShapeLabelLayer({
428
517
  id: `${this.props.id}-${SHAPE_LAYER_IDS.DISPLAY_LABELS}`,
429
518
  data: labelData,
430
519
  labelOptions
520
+ })];
521
+ }
522
+ /**
523
+ * Render vertical elevation indicator lines for non-polygon shapes.
524
+ * Creates vertical "strut" lines from ground level to elevated features.
525
+ * For LineStrings, creates a "curtain" effect with vertical lines at each coordinate.
526
+ * Polygons use wireframe extrusion instead.
527
+ */
528
+ renderElevationIndicatorLayer(features, elevatedNonPolygons) {
529
+ if (elevatedNonPolygons.length === 0) return [];
530
+ const { selectedShapeId } = this.props;
531
+ const hoverIndex = this.state?.hoverIndex;
532
+ const cache = this.indicatorCache;
533
+ let lineData;
534
+ if (cache !== null && cache.features === features && cache.selectedShapeId === selectedShapeId && cache.hoverIndex === hoverIndex) lineData = cache.lineData;
535
+ else {
536
+ lineData = buildIndicatorLineData(elevatedNonPolygons, features, selectedShapeId, hoverIndex);
537
+ this.indicatorCache = {
538
+ features,
539
+ selectedShapeId,
540
+ hoverIndex,
541
+ lineData
542
+ };
543
+ }
544
+ if (lineData.length === 0) return [];
545
+ return [new LineLayer({
546
+ id: `${this.props.id}-${SHAPE_LAYER_IDS.DISPLAY}-elevation-indicators`,
547
+ data: lineData,
548
+ getSourcePosition: (d) => d.source,
549
+ getTargetPosition: (d) => d.target,
550
+ getColor: (d) => d.color,
551
+ getWidth: 2,
552
+ widthUnits: "pixels",
553
+ pickable: false,
554
+ updateTriggers: {
555
+ data: [
556
+ features,
557
+ selectedShapeId,
558
+ hoverIndex
559
+ ],
560
+ getColor: [
561
+ features,
562
+ selectedShapeId,
563
+ hoverIndex
564
+ ]
565
+ }
566
+ })];
567
+ }
568
+ /**
569
+ * Create a single curtain GeoJsonLayer with shared configuration.
570
+ */
571
+ createCurtainGeoJsonLayer(idSuffix, data, getFillColor$1, dataTriggers, fillColorTriggers) {
572
+ return new GeoJsonLayer({
573
+ id: `${this.props.id}-${SHAPE_LAYER_IDS.DISPLAY}-${idSuffix}`,
574
+ data,
575
+ filled: true,
576
+ stroked: false,
577
+ _full3d: true,
578
+ getFillColor: getFillColor$1,
579
+ pickable: this.props.pickable ?? true,
580
+ parameters: {
581
+ depthTest: true,
582
+ depthCompare: "less-equal"
583
+ },
584
+ updateTriggers: {
585
+ data: dataTriggers,
586
+ getFillColor: fillColorTriggers
587
+ }
431
588
  });
432
589
  }
433
590
  /**
591
+ * Render curtain layers for elevated LineStrings.
592
+ * Creates three separate layers for main, hovered, and selected states.
593
+ */
594
+ renderCurtainLayers(features, allCurtainFeatures) {
595
+ const layers = [];
596
+ const { selectedShapeId } = this.props;
597
+ const hoverIndex = this.state?.hoverIndex;
598
+ const hoveredShapeId = hoverIndex !== void 0 && features[hoverIndex] ? features[hoverIndex].properties?.shapeId : void 0;
599
+ const { main, hovered, selected } = partitionCurtains(allCurtainFeatures, hoveredShapeId, selectedShapeId);
600
+ const isSelectedHovered = selectedShapeId === hoveredShapeId;
601
+ const dataTriggers = [
602
+ features,
603
+ hoveredShapeId,
604
+ selectedShapeId
605
+ ];
606
+ if (main.length > 0) layers.push(this.createCurtainGeoJsonLayer("elevation-curtain", main, (d) => d.properties.fillColor, dataTriggers, [features, this.props.applyBaseOpacity]));
607
+ if (hovered.length > 0) {
608
+ const hoveredColor = applyOverlayOpacity(brightenColor(hovered[0].properties.lineColor, BRIGHTNESS_FACTOR.HOVER_OR_SELECT));
609
+ layers.push(this.createCurtainGeoJsonLayer("elevation-curtain-hover", hovered, () => hoveredColor, dataTriggers, [features]));
610
+ }
611
+ if (selected.length > 0) {
612
+ const factor = isSelectedHovered ? BRIGHTNESS_FACTOR.HOVER_AND_SELECT : BRIGHTNESS_FACTOR.HOVER_OR_SELECT;
613
+ const selectedColor = applyOverlayOpacity(brightenColor(selected[0].properties.lineColor, factor));
614
+ layers.push(this.createCurtainGeoJsonLayer("elevation-curtain-selected", selected, () => selectedColor, dataTriggers, [features, isSelectedHovered]));
615
+ }
616
+ return layers;
617
+ }
618
+ /**
619
+ * Render elevation visualization layers (curtains for lines, wireframes for polygons).
620
+ */
621
+ renderElevationVisualizationLayer(features, allCurtainFeatures, elevatedPolygons) {
622
+ const layers = [];
623
+ if (allCurtainFeatures.length > 0) layers.push(...this.renderCurtainLayers(features, allCurtainFeatures));
624
+ if (elevatedPolygons.length > 0) {
625
+ const { applyBaseOpacity } = this.props;
626
+ layers.push(new GeoJsonLayer({
627
+ id: `${this.props.id}-${SHAPE_LAYER_IDS.DISPLAY}-elevation-wireframe`,
628
+ data: elevatedPolygons,
629
+ filled: false,
630
+ stroked: false,
631
+ extruded: true,
632
+ wireframe: true,
633
+ getElevation: getFeatureElevation,
634
+ getFillColor: (d) => getFillColor(d, applyBaseOpacity),
635
+ getLineColor,
636
+ pickable: false,
637
+ parameters: {
638
+ depthTest: true,
639
+ depthCompare: "less-equal"
640
+ },
641
+ updateTriggers: {
642
+ getElevation: [features],
643
+ getFillColor: [features, applyBaseOpacity],
644
+ getLineColor: [features]
645
+ }
646
+ }));
647
+ }
648
+ return layers;
649
+ }
650
+ /**
434
651
  * Render all sublayers
435
652
  */
436
653
  renderLayers() {
437
654
  const features = this.getFeaturesWithId();
438
- return [
439
- this.renderHighlightLayer(features),
440
- this.renderCoffinCornersLayer(features),
441
- this.renderMainLayer(features),
442
- this.renderLabelsLayer()
443
- ].filter(Boolean);
655
+ const enableElevation = this.props.enableElevation ?? false;
656
+ const layers = [
657
+ ...this.renderHighlightLayer(features),
658
+ ...this.renderSelectLayer(features),
659
+ ...this.renderHoverLayer(features),
660
+ ...this.renderCoffinCornersLayer(features)
661
+ ];
662
+ if (enableElevation) {
663
+ const { classification, curtainFeatures } = this.getElevationData(features, this.props.applyBaseOpacity);
664
+ const { polygons, nonPolygons } = classification;
665
+ layers.push(...this.renderElevationVisualizationLayer(features, curtainFeatures, polygons));
666
+ layers.push(...this.renderElevationIndicatorLayer(features, nonPolygons));
667
+ }
668
+ layers.push(this.renderMainLayer(features));
669
+ layers.push(...this.renderLabelsLayer());
670
+ return layers;
444
671
  }
445
672
  };
446
673