@accelint/map-toolkit 1.2.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +11 -0
- package/catalog-info.yaml +3 -3
- package/dist/camera/events.d.ts +45 -0
- package/dist/camera/events.js +45 -0
- package/dist/camera/events.js.map +1 -1
- package/dist/camera/store.d.ts +47 -0
- package/dist/camera/store.js +81 -0
- package/dist/camera/store.js.map +1 -1
- package/dist/camera/types.d.ts +81 -0
- package/dist/cursor-coordinates/constants.d.ts +8 -0
- package/dist/cursor-coordinates/constants.js +22 -0
- package/dist/cursor-coordinates/constants.js.map +1 -0
- package/dist/cursor-coordinates/store.d.ts +1 -0
- package/dist/cursor-coordinates/store.js +1 -0
- package/dist/cursor-coordinates/store.js.map +1 -1
- package/dist/cursor-coordinates/use-cursor-coordinates.d.ts +5 -0
- package/dist/cursor-coordinates/use-cursor-coordinates.js +23 -8
- package/dist/cursor-coordinates/use-cursor-coordinates.js.map +1 -1
- package/dist/deckgl/base-map/constants.d.ts +12 -0
- package/dist/deckgl/base-map/constants.js +12 -0
- package/dist/deckgl/base-map/constants.js.map +1 -1
- package/dist/deckgl/base-map/controls.d.ts +11 -1
- package/dist/deckgl/base-map/controls.js +5 -0
- package/dist/deckgl/base-map/controls.js.map +1 -1
- package/dist/deckgl/base-map/events.d.ts +30 -0
- package/dist/deckgl/base-map/events.js +30 -0
- package/dist/deckgl/base-map/events.js.map +1 -1
- package/dist/deckgl/base-map/index.d.ts +2 -2
- package/dist/deckgl/base-map/index.js +33 -3
- package/dist/deckgl/base-map/index.js.map +1 -1
- package/dist/deckgl/base-map/provider.d.ts +2 -2
- package/dist/deckgl/index.js +1 -1
- package/dist/deckgl/saved-viewports/index.d.ts +75 -0
- package/dist/deckgl/saved-viewports/index.js +58 -0
- package/dist/deckgl/saved-viewports/index.js.map +1 -1
- package/dist/deckgl/saved-viewports/storage.d.ts +51 -0
- package/dist/deckgl/saved-viewports/storage.js +64 -0
- package/dist/deckgl/saved-viewports/storage.js.map +1 -1
- package/dist/deckgl/shapes/display-shape-layer/constants.js +18 -6
- package/dist/deckgl/shapes/display-shape-layer/constants.js.map +1 -1
- package/dist/deckgl/shapes/display-shape-layer/fiber.d.ts +7 -0
- package/dist/deckgl/shapes/display-shape-layer/fiber.js.map +1 -1
- package/dist/deckgl/shapes/display-shape-layer/utils/display-style.js +61 -4
- package/dist/deckgl/shapes/display-shape-layer/utils/display-style.js.map +1 -1
- package/dist/deckgl/shapes/display-shape-layer/utils/labels.d.ts +22 -8
- package/dist/deckgl/shapes/display-shape-layer/utils/labels.js +75 -4
- package/dist/deckgl/shapes/display-shape-layer/utils/labels.js.map +1 -1
- package/dist/deckgl/shapes/draw-shape-layer/constants.js +30 -0
- package/dist/deckgl/shapes/draw-shape-layer/constants.js.map +1 -1
- package/dist/deckgl/shapes/draw-shape-layer/fiber.js +36 -0
- package/dist/deckgl/shapes/draw-shape-layer/fiber.js.map +1 -1
- package/dist/deckgl/shapes/draw-shape-layer/index.d.ts +2 -2
- package/dist/deckgl/shapes/draw-shape-layer/modes/draw-circle-mode-with-tooltip.js +32 -1
- package/dist/deckgl/shapes/draw-shape-layer/modes/draw-circle-mode-with-tooltip.js.map +1 -1
- package/dist/deckgl/shapes/draw-shape-layer/modes/draw-ellipse-mode-with-tooltip.js +37 -8
- package/dist/deckgl/shapes/draw-shape-layer/modes/draw-ellipse-mode-with-tooltip.js.map +1 -1
- package/dist/deckgl/shapes/draw-shape-layer/modes/draw-line-string-mode-with-tooltip.js +43 -1
- package/dist/deckgl/shapes/draw-shape-layer/modes/draw-line-string-mode-with-tooltip.js.map +1 -1
- package/dist/deckgl/shapes/draw-shape-layer/modes/draw-polygon-mode-with-tooltip.js +44 -1
- package/dist/deckgl/shapes/draw-shape-layer/modes/draw-polygon-mode-with-tooltip.js.map +1 -1
- package/dist/deckgl/shapes/draw-shape-layer/modes/draw-rectangle-mode-with-tooltip.js +46 -3
- package/dist/deckgl/shapes/draw-shape-layer/modes/draw-rectangle-mode-with-tooltip.js.map +1 -1
- package/dist/deckgl/shapes/draw-shape-layer/modes/index.js +37 -1
- package/dist/deckgl/shapes/draw-shape-layer/modes/index.js.map +1 -1
- package/dist/deckgl/shapes/draw-shape-layer/store.js +50 -2
- package/dist/deckgl/shapes/draw-shape-layer/store.js.map +1 -1
- package/dist/deckgl/shapes/draw-shape-layer/utils/feature-conversion.js +138 -17
- package/dist/deckgl/shapes/draw-shape-layer/utils/feature-conversion.js.map +1 -1
- package/dist/deckgl/shapes/edit-shape-layer/events.js +1 -1
- package/dist/deckgl/shapes/edit-shape-layer/events.js.map +1 -1
- package/dist/deckgl/shapes/edit-shape-layer/index.d.ts +2 -2
- package/dist/deckgl/shapes/edit-shape-layer/index.js +14 -0
- package/dist/deckgl/shapes/edit-shape-layer/index.js.map +1 -1
- package/dist/deckgl/shapes/edit-shape-layer/modes/base-transform-mode.js +56 -8
- package/dist/deckgl/shapes/edit-shape-layer/modes/base-transform-mode.js.map +1 -1
- package/dist/deckgl/shapes/edit-shape-layer/modes/bounding-transform-mode.js +26 -4
- package/dist/deckgl/shapes/edit-shape-layer/modes/bounding-transform-mode.js.map +1 -1
- package/dist/deckgl/shapes/edit-shape-layer/modes/circle-transform-mode.js +28 -3
- package/dist/deckgl/shapes/edit-shape-layer/modes/circle-transform-mode.js.map +1 -1
- package/dist/deckgl/shapes/edit-shape-layer/modes/index.js +24 -0
- package/dist/deckgl/shapes/edit-shape-layer/modes/index.js.map +1 -1
- package/dist/deckgl/shapes/edit-shape-layer/modes/rotate-mode-with-snap.js +33 -4
- package/dist/deckgl/shapes/edit-shape-layer/modes/rotate-mode-with-snap.js.map +1 -1
- package/dist/deckgl/shapes/edit-shape-layer/modes/scale-mode-with-free-transform.js +21 -2
- package/dist/deckgl/shapes/edit-shape-layer/modes/scale-mode-with-free-transform.js.map +1 -1
- package/dist/deckgl/shapes/edit-shape-layer/modes/vertex-transform-mode.js +35 -11
- package/dist/deckgl/shapes/edit-shape-layer/modes/vertex-transform-mode.js.map +1 -1
- package/dist/deckgl/shapes/edit-shape-layer/store.js +1 -1
- package/dist/deckgl/shapes/edit-shape-layer/store.js.map +1 -1
- package/dist/deckgl/shapes/shared/hooks/use-shift-zoom-disable.js +12 -0
- package/dist/deckgl/shapes/shared/hooks/use-shift-zoom-disable.js.map +1 -1
- package/dist/deckgl/shapes/shared/types.d.ts +3 -3
- package/dist/deckgl/shapes/shared/types.js +2 -2
- package/dist/deckgl/shapes/shared/types.js.map +1 -1
- package/dist/deckgl/shapes/shared/utils/geometry-measurements.js +3 -3
- package/dist/deckgl/shapes/shared/utils/geometry-measurements.js.map +1 -1
- package/dist/deckgl/shapes/shared/utils/pick-filtering.js +1 -1
- package/dist/deckgl/shapes/shared/utils/pick-filtering.js.map +1 -1
- package/dist/deckgl/symbol-layer/fiber.d.ts +18 -0
- package/dist/deckgl/symbol-layer/fiber.js.map +1 -1
- package/dist/deckgl/symbol-layer/index.d.ts +79 -1
- package/dist/deckgl/symbol-layer/index.js +72 -1
- package/dist/deckgl/symbol-layer/index.js.map +1 -1
- package/dist/deckgl/text-layer/character-sets.d.ts +30 -0
- package/dist/deckgl/text-layer/character-sets.js +26 -0
- package/dist/deckgl/text-layer/character-sets.js.map +1 -1
- package/dist/deckgl/text-layer/default-settings.d.ts +29 -0
- package/dist/deckgl/text-layer/default-settings.js +28 -0
- package/dist/deckgl/text-layer/default-settings.js.map +1 -1
- package/dist/deckgl/text-layer/index.d.ts +65 -0
- package/dist/deckgl/text-layer/index.js +56 -0
- package/dist/deckgl/text-layer/index.js.map +1 -1
- package/dist/map-cursor/events.d.ts +19 -0
- package/dist/map-cursor/events.js +19 -0
- package/dist/map-cursor/events.js.map +1 -1
- package/dist/map-cursor/store.d.ts +34 -2
- package/dist/map-cursor/store.js +44 -3
- package/dist/map-cursor/store.js.map +1 -1
- package/dist/map-mode/store.d.ts +43 -4
- package/dist/map-mode/store.js +56 -6
- package/dist/map-mode/store.js.map +1 -1
- package/dist/shared/create-map-store.d.ts +14 -0
- package/dist/shared/create-map-store.js +26 -2
- package/dist/shared/create-map-store.js.map +1 -1
- package/dist/shared/units.d.ts +24 -0
- package/dist/shared/units.js +24 -0
- package/dist/shared/units.js.map +1 -1
- package/dist/viewport/store.d.ts +1 -0
- package/dist/viewport/store.js +4 -0
- package/dist/viewport/store.js.map +1 -1
- package/package.json +3 -3
|
@@ -207,15 +207,49 @@ function getPolygonPosition(geometry, shape, shapeOffset, shapeVertical, shapeHo
|
|
|
207
207
|
};
|
|
208
208
|
}
|
|
209
209
|
/**
|
|
210
|
-
* Get 2D position for label based on geometry type
|
|
211
|
-
*
|
|
210
|
+
* Get 2D position for label based on geometry type.
|
|
211
|
+
*
|
|
212
|
+
* Calculates label positioning using pixel-based offsets for consistent placement
|
|
213
|
+
* at all zoom levels. Handles Point, LineString, Polygon, and Circle geometries.
|
|
212
214
|
*
|
|
213
215
|
* Priority for positioning:
|
|
214
216
|
* 1. Per-shape properties in styleProperties (highest)
|
|
215
217
|
* 2. Global labelOptions from layer props
|
|
216
218
|
* 3. Default values (fallback)
|
|
217
219
|
*
|
|
218
|
-
* Returns null if no valid coordinates can be determined
|
|
220
|
+
* Returns null if no valid coordinates can be determined.
|
|
221
|
+
*
|
|
222
|
+
* @param shape - The shape to position a label for
|
|
223
|
+
* @param options - Optional global label positioning options
|
|
224
|
+
* @returns Label position information or null if coordinates are invalid
|
|
225
|
+
*
|
|
226
|
+
* @example
|
|
227
|
+
* ```typescript
|
|
228
|
+
* import { getLabelPosition2d } from '@accelint/map-toolkit/deckgl/shapes/display-shape-layer/utils/labels';
|
|
229
|
+
* import type { Shape, LabelPositionOptions } from '@accelint/map-toolkit/deckgl/shapes/display-shape-layer/utils/labels';
|
|
230
|
+
*
|
|
231
|
+
* const shape: Shape = {
|
|
232
|
+
* id: 'point-1',
|
|
233
|
+
* name: 'Location',
|
|
234
|
+
* label: 'LOC',
|
|
235
|
+
* feature: {
|
|
236
|
+
* type: 'Feature',
|
|
237
|
+
* geometry: { type: 'Point', coordinates: [-122.4, 37.8] },
|
|
238
|
+
* properties: {},
|
|
239
|
+
* },
|
|
240
|
+
* };
|
|
241
|
+
*
|
|
242
|
+
* // Get default positioning
|
|
243
|
+
* const position = getLabelPosition2d(shape);
|
|
244
|
+
* // Returns: { coordinates: [-122.4, 37.8], textAnchor: 'middle', alignmentBaseline: 'top', pixelOffset: [0, 10] }
|
|
245
|
+
*
|
|
246
|
+
* // Use custom global options
|
|
247
|
+
* const options: LabelPositionOptions = {
|
|
248
|
+
* pointLabelVerticalAnchor: 'bottom',
|
|
249
|
+
* pointLabelOffset: [0, -15],
|
|
250
|
+
* };
|
|
251
|
+
* const customPosition = getLabelPosition2d(shape, options);
|
|
252
|
+
* ```
|
|
219
253
|
*/
|
|
220
254
|
function getLabelPosition2d(shape, options) {
|
|
221
255
|
const { geometry } = shape.feature;
|
|
@@ -232,7 +266,7 @@ function getLabelPosition2d(shape, options) {
|
|
|
232
266
|
}
|
|
233
267
|
}
|
|
234
268
|
/**
|
|
235
|
-
* Get label text for a shape
|
|
269
|
+
* Get label text for a shape.
|
|
236
270
|
*
|
|
237
271
|
* Returns the display label for the shape on the map in uppercase.
|
|
238
272
|
* - `label`: Optional short display name shown on the map (e.g., "NYC")
|
|
@@ -240,6 +274,43 @@ function getLabelPosition2d(shape, options) {
|
|
|
240
274
|
*
|
|
241
275
|
* If `label` is not provided, falls back to using `name`.
|
|
242
276
|
* Text is automatically converted to uppercase for display.
|
|
277
|
+
*
|
|
278
|
+
* @param shape - The shape to get label text for
|
|
279
|
+
* @returns The label text in uppercase
|
|
280
|
+
*
|
|
281
|
+
* @example
|
|
282
|
+
* ```typescript
|
|
283
|
+
* import { getLabelText } from '@accelint/map-toolkit/deckgl/shapes/display-shape-layer/utils/labels';
|
|
284
|
+
* import type { Shape } from '@accelint/map-toolkit/deckgl/shapes/shared/types';
|
|
285
|
+
*
|
|
286
|
+
* const shape: Shape = {
|
|
287
|
+
* id: 'location-1',
|
|
288
|
+
* name: 'New York City Office',
|
|
289
|
+
* label: 'NYC',
|
|
290
|
+
* feature: {
|
|
291
|
+
* type: 'Feature',
|
|
292
|
+
* geometry: { type: 'Point', coordinates: [-74.0, 40.7] },
|
|
293
|
+
* properties: {},
|
|
294
|
+
* },
|
|
295
|
+
* };
|
|
296
|
+
*
|
|
297
|
+
* const text = getLabelText(shape);
|
|
298
|
+
* // Returns: "NYC"
|
|
299
|
+
*
|
|
300
|
+
* // Without label property, uses name
|
|
301
|
+
* const shapeWithoutLabel: Shape = {
|
|
302
|
+
* id: 'location-2',
|
|
303
|
+
* name: 'Boston Office',
|
|
304
|
+
* feature: {
|
|
305
|
+
* type: 'Feature',
|
|
306
|
+
* geometry: { type: 'Point', coordinates: [-71.0, 42.3] },
|
|
307
|
+
* properties: {},
|
|
308
|
+
* },
|
|
309
|
+
* };
|
|
310
|
+
*
|
|
311
|
+
* const textFromName = getLabelText(shapeWithoutLabel);
|
|
312
|
+
* // Returns: "BOSTON OFFICE"
|
|
313
|
+
* ```
|
|
243
314
|
*/
|
|
244
315
|
function getLabelText(shape) {
|
|
245
316
|
return (shape.label || shape.name).toUpperCase();
|
|
@@ -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 */\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 * @param coordinates - LineString coordinates array\n * @returns Midpoint coordinate [lon, lat]\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 * @param coordinates - LineString coordinates array\n * @returns End coordinate [lon, lat]\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 * @param coordinates - Polygon coordinates array (rings)\n * @returns Midpoint of outer ring [lon, lat]\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 */\nexport type LabelVerticalPosition = 'top' | 'middle' | 'bottom';\n\n/**\n * Horizontal label position relative to anchor point\n */\nexport type LabelHorizontalPosition = 'left' | 'center' | 'right';\n\n/**\n * Cardinal direction anchor for positioning labels on geometry edges\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\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 * Uses pixel-based offsets for consistent positioning at all zoom levels\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 */\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 */\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":["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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"}
|
|
@@ -17,6 +17,36 @@ import { ShapeFeatureType } from "../shared/types.js";
|
|
|
17
17
|
|
|
18
18
|
//#region src/deckgl/shapes/draw-shape-layer/constants.ts
|
|
19
19
|
/**
|
|
20
|
+
* Drawing Constants
|
|
21
|
+
*
|
|
22
|
+
* Constants used by the DrawShapeLayer for map-mode and cursor integration.
|
|
23
|
+
*
|
|
24
|
+
* ## Map Mode Integration
|
|
25
|
+
* The drawing system uses a protected mode (`DRAW_SHAPE_MODE`) that prevents
|
|
26
|
+
* other map features from changing the mode or cursor while a shape is being drawn.
|
|
27
|
+
* This ensures drawing operations are not interrupted by other interactions.
|
|
28
|
+
*
|
|
29
|
+
* ## Cursor Management
|
|
30
|
+
* All shape types use the crosshair cursor (`DRAW_CURSOR`) during drawing operations.
|
|
31
|
+
* The cursor is automatically set when drawing starts and restored when complete.
|
|
32
|
+
*
|
|
33
|
+
* ## Layer Identification
|
|
34
|
+
* The `DRAW_SHAPE_LAYER_ID` serves dual purposes:
|
|
35
|
+
* - Default ID for the EditableGeoJsonLayer instance
|
|
36
|
+
* - Owner identifier for map-mode and cursor requests
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* ```typescript
|
|
40
|
+
* import { DRAW_SHAPE_MODE, DRAW_CURSOR, DRAW_SHAPE_LAYER_ID } from '@accelint/map-toolkit/deckgl/shapes/draw-shape-layer';
|
|
41
|
+
*
|
|
42
|
+
* // Request drawing mode and cursor
|
|
43
|
+
* requestModeAndCursor(mapId, DRAW_SHAPE_MODE, DRAW_CURSOR, DRAW_SHAPE_LAYER_ID);
|
|
44
|
+
*
|
|
45
|
+
* // Release when done
|
|
46
|
+
* releaseModeAndCursor(mapId, DRAW_SHAPE_LAYER_ID);
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
/**
|
|
20
50
|
* Mode name for the map-mode integration
|
|
21
51
|
*/
|
|
22
52
|
const DRAW_SHAPE_MODE = "draw-shape";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.js","names":["DRAW_CURSOR: CSSCursorType","DRAW_CURSOR_MAP: Record<ShapeFeatureType, CSSCursorType>"],"sources":["../../../../src/deckgl/shapes/draw-shape-layer/constants.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 { ShapeFeatureType } from '../shared/types';\nimport type { CSSCursorType } from '@/map-cursor/types';\n\n/**\n * Mode name for the map-mode integration\n */\nexport const DRAW_SHAPE_MODE = 'draw-shape';\n\n/**\n * Identifier for the draw shape layer.\n * Used as the owner for map-mode/cursor and as the default layer ID.\n */\nexport const DRAW_SHAPE_LAYER_ID = 'draw-shape-layer';\n\n/**\n * Cursor type to use when drawing shapes\n */\nexport const DRAW_CURSOR: CSSCursorType = 'crosshair';\n\n/**\n * Cursor mapping for each shape type (all use crosshair)\n */\nexport const DRAW_CURSOR_MAP: Record<ShapeFeatureType, CSSCursorType> = {\n [ShapeFeatureType.Point]: DRAW_CURSOR,\n [ShapeFeatureType.LineString]: DRAW_CURSOR,\n [ShapeFeatureType.Polygon]: DRAW_CURSOR,\n [ShapeFeatureType.Rectangle]: DRAW_CURSOR,\n [ShapeFeatureType.Circle]: DRAW_CURSOR,\n [ShapeFeatureType.Ellipse]: DRAW_CURSOR,\n};\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"constants.js","names":["DRAW_CURSOR: CSSCursorType","DRAW_CURSOR_MAP: Record<ShapeFeatureType, CSSCursorType>"],"sources":["../../../../src/deckgl/shapes/draw-shape-layer/constants.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/**\n * Drawing Constants\n *\n * Constants used by the DrawShapeLayer for map-mode and cursor integration.\n *\n * ## Map Mode Integration\n * The drawing system uses a protected mode (`DRAW_SHAPE_MODE`) that prevents\n * other map features from changing the mode or cursor while a shape is being drawn.\n * This ensures drawing operations are not interrupted by other interactions.\n *\n * ## Cursor Management\n * All shape types use the crosshair cursor (`DRAW_CURSOR`) during drawing operations.\n * The cursor is automatically set when drawing starts and restored when complete.\n *\n * ## Layer Identification\n * The `DRAW_SHAPE_LAYER_ID` serves dual purposes:\n * - Default ID for the EditableGeoJsonLayer instance\n * - Owner identifier for map-mode and cursor requests\n *\n * @example\n * ```typescript\n * import { DRAW_SHAPE_MODE, DRAW_CURSOR, DRAW_SHAPE_LAYER_ID } from '@accelint/map-toolkit/deckgl/shapes/draw-shape-layer';\n *\n * // Request drawing mode and cursor\n * requestModeAndCursor(mapId, DRAW_SHAPE_MODE, DRAW_CURSOR, DRAW_SHAPE_LAYER_ID);\n *\n * // Release when done\n * releaseModeAndCursor(mapId, DRAW_SHAPE_LAYER_ID);\n * ```\n */\nimport { ShapeFeatureType } from '../shared/types';\nimport type { CSSCursorType } from '@/map-cursor/types';\n\n/**\n * Mode name for the map-mode integration\n */\nexport const DRAW_SHAPE_MODE = 'draw-shape';\n\n/**\n * Identifier for the draw shape layer.\n * Used as the owner for map-mode/cursor and as the default layer ID.\n */\nexport const DRAW_SHAPE_LAYER_ID = 'draw-shape-layer';\n\n/**\n * Cursor type to use when drawing shapes\n */\nexport const DRAW_CURSOR: CSSCursorType = 'crosshair';\n\n/**\n * Cursor mapping for each shape type (all use crosshair)\n */\nexport const DRAW_CURSOR_MAP: Record<ShapeFeatureType, CSSCursorType> = {\n [ShapeFeatureType.Point]: DRAW_CURSOR,\n [ShapeFeatureType.LineString]: DRAW_CURSOR,\n [ShapeFeatureType.Polygon]: DRAW_CURSOR,\n [ShapeFeatureType.Rectangle]: DRAW_CURSOR,\n [ShapeFeatureType.Circle]: DRAW_CURSOR,\n [ShapeFeatureType.Ellipse]: DRAW_CURSOR,\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiDA,MAAa,kBAAkB;;;;;AAM/B,MAAa,sBAAsB;;;;AAKnC,MAAaA,cAA6B;;;;AAK1C,MAAaC,kBAA2D;EACrE,iBAAiB,QAAQ;EACzB,iBAAiB,aAAa;EAC9B,iBAAiB,UAAU;EAC3B,iBAAiB,YAAY;EAC7B,iBAAiB,SAAS;EAC1B,iBAAiB,UAAU;CAC7B"}
|
|
@@ -15,6 +15,42 @@ import { extend } from "@deckgl-fiber-renderer/dom";
|
|
|
15
15
|
import { EditableGeoJsonLayer } from "@deck.gl-community/editable-layers";
|
|
16
16
|
|
|
17
17
|
//#region src/deckgl/shapes/draw-shape-layer/fiber.ts
|
|
18
|
+
/**
|
|
19
|
+
* DeckGL Fiber Registration for EditableGeoJsonLayer
|
|
20
|
+
*
|
|
21
|
+
* Registers the `EditableGeoJsonLayer` from @deck.gl-community/editable-layers
|
|
22
|
+
* with the DeckGL Fiber renderer, enabling JSX syntax for the layer.
|
|
23
|
+
*
|
|
24
|
+
* ## Why This Is Needed
|
|
25
|
+
* DeckGL Fiber (used by BaseMap) requires explicit registration of deck.gl layers
|
|
26
|
+
* before they can be used as JSX elements. This file extends the fiber renderer
|
|
27
|
+
* to recognize `<editableGeoJsonLayer>` as a valid JSX element.
|
|
28
|
+
*
|
|
29
|
+
* ## Usage
|
|
30
|
+
* Import this file once (typically in DrawShapeLayer) before using the layer in JSX:
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* ```tsx
|
|
34
|
+
* // Import to register the layer
|
|
35
|
+
* import '@accelint/map-toolkit/deckgl/shapes/draw-shape-layer/fiber';
|
|
36
|
+
*
|
|
37
|
+
* // Now you can use it in JSX
|
|
38
|
+
* function DrawShapeLayer() {
|
|
39
|
+
* return (
|
|
40
|
+
* <editableGeoJsonLayer
|
|
41
|
+
* id="draw-layer"
|
|
42
|
+
* mode={drawMode}
|
|
43
|
+
* onEdit={handleEdit}
|
|
44
|
+
* />
|
|
45
|
+
* );
|
|
46
|
+
* }
|
|
47
|
+
* ```
|
|
48
|
+
*
|
|
49
|
+
* ## Note on DrawShapeLayer
|
|
50
|
+
* `DrawShapeLayer` is a React component, not a deck.gl layer class, so it doesn't
|
|
51
|
+
* need fiber registration. It uses `<editableGeoJsonLayer>` internally, which is
|
|
52
|
+
* registered here.
|
|
53
|
+
*/
|
|
18
54
|
extend({ EditableGeoJsonLayer });
|
|
19
55
|
|
|
20
56
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fiber.js","names":[],"sources":["../../../../src/deckgl/shapes/draw-shape-layer/fiber.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 { EditableGeoJsonLayer } from '@deck.gl-community/editable-layers';\nimport { extend } from '@deckgl-fiber-renderer/dom';\n\n// Extend the fiber renderer with EditableGeoJsonLayer\nextend({ EditableGeoJsonLayer });\n\n// Note: DrawShapeLayer is a React component, not a deck.gl layer class,\n// so it doesn't need fiber registration. It uses <editableGeoJsonLayer>\n// internally which is registered above.\n\ndeclare global {\n namespace React {\n // biome-ignore lint/style/useNamingConvention: Built-in React namespace.\n namespace JSX {\n interface IntrinsicElements {\n // biome-ignore lint/suspicious/noExplicitAny: EditableGeoJsonLayer props are complex and vary by mode\n editableGeoJsonLayer: any;\n }\n }\n }\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"fiber.js","names":[],"sources":["../../../../src/deckgl/shapes/draw-shape-layer/fiber.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 * DeckGL Fiber Registration for EditableGeoJsonLayer\n *\n * Registers the `EditableGeoJsonLayer` from @deck.gl-community/editable-layers\n * with the DeckGL Fiber renderer, enabling JSX syntax for the layer.\n *\n * ## Why This Is Needed\n * DeckGL Fiber (used by BaseMap) requires explicit registration of deck.gl layers\n * before they can be used as JSX elements. This file extends the fiber renderer\n * to recognize `<editableGeoJsonLayer>` as a valid JSX element.\n *\n * ## Usage\n * Import this file once (typically in DrawShapeLayer) before using the layer in JSX:\n *\n * @example\n * ```tsx\n * // Import to register the layer\n * import '@accelint/map-toolkit/deckgl/shapes/draw-shape-layer/fiber';\n *\n * // Now you can use it in JSX\n * function DrawShapeLayer() {\n * return (\n * <editableGeoJsonLayer\n * id=\"draw-layer\"\n * mode={drawMode}\n * onEdit={handleEdit}\n * />\n * );\n * }\n * ```\n *\n * ## Note on DrawShapeLayer\n * `DrawShapeLayer` is a React component, not a deck.gl layer class, so it doesn't\n * need fiber registration. It uses `<editableGeoJsonLayer>` internally, which is\n * registered here.\n */\n\nimport { EditableGeoJsonLayer } from '@deck.gl-community/editable-layers';\nimport { extend } from '@deckgl-fiber-renderer/dom';\n\n// Extend the fiber renderer with EditableGeoJsonLayer\nextend({ EditableGeoJsonLayer });\n\n// Note: DrawShapeLayer is a React component, not a deck.gl layer class,\n// so it doesn't need fiber registration. It uses <editableGeoJsonLayer>\n// internally which is registered above.\n\ndeclare global {\n namespace React {\n // biome-ignore lint/style/useNamingConvention: Built-in React namespace.\n namespace JSX {\n interface IntrinsicElements {\n // biome-ignore lint/suspicious/noExplicitAny: EditableGeoJsonLayer props are complex and vary by mode\n editableGeoJsonLayer: any;\n }\n }\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqDA,OAAO,EAAE,sBAAsB,CAAC"}
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
import { DrawShapeLayerProps } from "./types.js";
|
|
14
|
-
import * as
|
|
14
|
+
import * as react_jsx_runtime0 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):
|
|
50
|
+
}: DrawShapeLayerProps): react_jsx_runtime0.JSX.Element | null;
|
|
51
51
|
//#endregion
|
|
52
52
|
export { DrawShapeLayer };
|
|
53
53
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -21,10 +21,36 @@ import { DrawCircleFromCenterMode } from "@deck.gl-community/editable-layers";
|
|
|
21
21
|
* Extends DrawCircleFromCenterMode to display diameter and area tooltip.
|
|
22
22
|
*
|
|
23
23
|
* Shows the diameter and area of the circle being drawn based on the radius
|
|
24
|
-
* from center point to cursor position.
|
|
24
|
+
* from center point to cursor position. The tooltip updates in real-time as
|
|
25
|
+
* the cursor moves, displaying measurements in the configured distance units.
|
|
26
|
+
*
|
|
27
|
+
* ## Usage
|
|
28
|
+
* This mode is automatically used by DrawShapeLayer when drawing circles.
|
|
29
|
+
* The mode is cached at module level to prevent deck.gl assertion failures.
|
|
30
|
+
*
|
|
31
|
+
* ## Drawing Flow
|
|
32
|
+
* 1. Click to set center point
|
|
33
|
+
* 2. Move cursor to set radius (tooltip shows diameter and area)
|
|
34
|
+
* 3. Click to finish the circle
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* ```typescript
|
|
38
|
+
* import { DrawCircleModeWithTooltip } from '@accelint/map-toolkit/deckgl/shapes/draw-shape-layer/modes';
|
|
39
|
+
*
|
|
40
|
+
* // Used internally by DrawShapeLayer
|
|
41
|
+
* const mode = new DrawCircleModeWithTooltip();
|
|
42
|
+
* ```
|
|
25
43
|
*/
|
|
26
44
|
var DrawCircleModeWithTooltip = class extends DrawCircleFromCenterMode {
|
|
45
|
+
/** Current tooltip state (null when not drawing) */
|
|
27
46
|
tooltip = null;
|
|
47
|
+
/**
|
|
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.
|
|
50
|
+
*
|
|
51
|
+
* @param event - Pointer move event with cursor position
|
|
52
|
+
* @param props - Mode properties including distance units configuration
|
|
53
|
+
*/
|
|
28
54
|
handlePointerMove(event, props) {
|
|
29
55
|
super.handlePointerMove(event, props);
|
|
30
56
|
const clickSequence = this.getClickSequence();
|
|
@@ -41,6 +67,11 @@ var DrawCircleModeWithTooltip = class extends DrawCircleFromCenterMode {
|
|
|
41
67
|
text: formatCircleTooltip(diameter, area, getDistanceUnitAbbreviation(distanceUnits))
|
|
42
68
|
};
|
|
43
69
|
}
|
|
70
|
+
/**
|
|
71
|
+
* Get the current tooltip array for rendering.
|
|
72
|
+
*
|
|
73
|
+
* @returns Array containing the tooltip if one is active, empty array otherwise
|
|
74
|
+
*/
|
|
44
75
|
getTooltips() {
|
|
45
76
|
return this.tooltip ? [this.tooltip] : [];
|
|
46
77
|
}
|
|
@@ -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.\n */\nexport class DrawCircleModeWithTooltip extends DrawCircleFromCenterMode {\n private tooltip: Tooltip | null = null;\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 override getTooltips(): Tooltip[] {\n return this.tooltip ? [this.tooltip] : [];\n }\n}\n"],"mappings":"
|
|
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"}
|
|
@@ -20,19 +20,43 @@ import { distance } from "@turf/turf";
|
|
|
20
20
|
/**
|
|
21
21
|
* Extends DrawEllipseUsingThreePointsMode to display contextual tooltips.
|
|
22
22
|
*
|
|
23
|
-
*
|
|
24
|
-
* 1
|
|
25
|
-
* 2
|
|
23
|
+
* Provides different tooltip content based on the drawing stage:
|
|
24
|
+
* - **Stage 1** (after first click): Distance from first point to cursor
|
|
25
|
+
* - **Stage 2** (after second click): Major/minor axes dimensions and area
|
|
26
|
+
*
|
|
27
|
+
* ## Drawing Flow
|
|
28
|
+
* 1. **First click**: Sets the first endpoint of the major axis
|
|
29
|
+
* 2. **Second click**: Sets the second endpoint of the major axis
|
|
26
30
|
* - While moving to second click, shows distance tooltip (like line/polygon)
|
|
27
|
-
* 3. Third click
|
|
28
|
-
* - While moving to third click, shows area tooltip
|
|
31
|
+
* 3. **Third click**: Sets the minor axis radius
|
|
32
|
+
* - While moving to third click, shows dimensions and area tooltip
|
|
33
|
+
*
|
|
34
|
+
* ## Geometry Calculations
|
|
35
|
+
* - **Center**: Midpoint between first and second clicks
|
|
36
|
+
* - **Y semi-axis**: Half the distance between clicks 1 and 2
|
|
37
|
+
* - **X semi-axis**: Distance from center to cursor (becomes distance to click 3)
|
|
38
|
+
* - **Area**: π × X semi-axis × Y semi-axis
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* ```typescript
|
|
42
|
+
* import { DrawEllipseModeWithTooltip } from '@accelint/map-toolkit/deckgl/shapes/draw-shape-layer/modes';
|
|
29
43
|
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
44
|
+
* // Used internally by DrawShapeLayer
|
|
45
|
+
* const mode = new DrawEllipseModeWithTooltip();
|
|
46
|
+
* ```
|
|
33
47
|
*/
|
|
34
48
|
var DrawEllipseModeWithTooltip = class extends DrawEllipseUsingThreePointsMode {
|
|
49
|
+
/** Current tooltip state (null when not drawing) */
|
|
35
50
|
tooltip = null;
|
|
51
|
+
/**
|
|
52
|
+
* Handle pointer move events to update the tooltip based on drawing stage.
|
|
53
|
+
*
|
|
54
|
+
* Stage 1 (1 click): Shows distance from first point to cursor.
|
|
55
|
+
* Stage 2 (2 clicks): Shows major/minor axes dimensions and ellipse area.
|
|
56
|
+
*
|
|
57
|
+
* @param event - Pointer move event with cursor position
|
|
58
|
+
* @param props - Mode properties including distance units configuration
|
|
59
|
+
*/
|
|
36
60
|
handlePointerMove(event, props) {
|
|
37
61
|
super.handlePointerMove(event, props);
|
|
38
62
|
const clickSequence = this.getClickSequence();
|
|
@@ -63,6 +87,11 @@ var DrawEllipseModeWithTooltip = class extends DrawEllipseUsingThreePointsMode {
|
|
|
63
87
|
};
|
|
64
88
|
}
|
|
65
89
|
}
|
|
90
|
+
/**
|
|
91
|
+
* Get the current tooltip array for rendering.
|
|
92
|
+
*
|
|
93
|
+
* @returns Array containing the tooltip if one is active, empty array otherwise
|
|
94
|
+
*/
|
|
66
95
|
getTooltips() {
|
|
67
96
|
return this.tooltip ? [this.tooltip] : [];
|
|
68
97
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"draw-ellipse-mode-with-tooltip.js","names":["center: [number, number]"],"sources":["../../../../../src/deckgl/shapes/draw-shape-layer/modes/draw-ellipse-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 DrawEllipseUsingThreePointsMode,\n type FeatureCollection,\n type ModeProps,\n type PointerMoveEvent,\n type Tooltip,\n} from '@deck.gl-community/editable-layers';\nimport { type Coord, distance } from '@turf/turf';\nimport {\n DEFAULT_DISTANCE_UNITS,\n getDistanceUnitAbbreviation,\n} from '@/shared/units';\nimport {\n formatDistanceTooltip,\n formatEllipseTooltip,\n} from '../../shared/constants';\n\n/**\n * Extends DrawEllipseUsingThreePointsMode to display contextual tooltips.\n *\n *
|
|
1
|
+
{"version":3,"file":"draw-ellipse-mode-with-tooltip.js","names":["center: [number, number]"],"sources":["../../../../../src/deckgl/shapes/draw-shape-layer/modes/draw-ellipse-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 DrawEllipseUsingThreePointsMode,\n type FeatureCollection,\n type ModeProps,\n type PointerMoveEvent,\n type Tooltip,\n} from '@deck.gl-community/editable-layers';\nimport { type Coord, distance } from '@turf/turf';\nimport {\n DEFAULT_DISTANCE_UNITS,\n getDistanceUnitAbbreviation,\n} from '@/shared/units';\nimport {\n formatDistanceTooltip,\n formatEllipseTooltip,\n} from '../../shared/constants';\n\n/**\n * Extends DrawEllipseUsingThreePointsMode to display contextual tooltips.\n *\n * Provides different tooltip content based on the drawing stage:\n * - **Stage 1** (after first click): Distance from first point to cursor\n * - **Stage 2** (after second click): Major/minor axes dimensions and area\n *\n * ## Drawing Flow\n * 1. **First click**: Sets the first endpoint of the major axis\n * 2. **Second click**: Sets the second endpoint of the major axis\n * - While moving to second click, shows distance tooltip (like line/polygon)\n * 3. **Third click**: Sets the minor axis radius\n * - While moving to third click, shows dimensions and area tooltip\n *\n * ## Geometry Calculations\n * - **Center**: Midpoint between first and second clicks\n * - **Y semi-axis**: Half the distance between clicks 1 and 2\n * - **X semi-axis**: Distance from center to cursor (becomes distance to click 3)\n * - **Area**: π × X semi-axis × Y semi-axis\n *\n * @example\n * ```typescript\n * import { DrawEllipseModeWithTooltip } from '@accelint/map-toolkit/deckgl/shapes/draw-shape-layer/modes';\n *\n * // Used internally by DrawShapeLayer\n * const mode = new DrawEllipseModeWithTooltip();\n * ```\n */\nexport class DrawEllipseModeWithTooltip extends DrawEllipseUsingThreePointsMode {\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 based on drawing stage.\n *\n * Stage 1 (1 click): Shows distance from first point to cursor.\n * Stage 2 (2 clicks): Shows major/minor axes dimensions and ellipse area.\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 const unitAbbrev = getDistanceUnitAbbreviation(distanceUnits);\n const tooltipPosition = mapCoords;\n\n if (clickSequence.length === 1) {\n // First segment: show distance from first click to cursor (like line/polygon)\n const firstPoint = clickSequence[0] as Coord;\n const currentPoint = mapCoords as Coord;\n\n const dist = distance(firstPoint, currentPoint, { units: distanceUnits });\n\n this.tooltip = {\n position: tooltipPosition,\n text: formatDistanceTooltip(dist, unitAbbrev),\n };\n } else if (clickSequence.length === 2) {\n // Second segment: show dimensions and area (like rectangle)\n // The ellipse will have:\n // - center at midpoint of click1 and click2\n // - ySemiAxis = distance(click1, click2) / 2\n // - xSemiAxis = distance(center, cursor)\n const click1 = clickSequence[0] as [number, number];\n const click2 = clickSequence[1] as [number, number];\n const cursorPos = mapCoords as [number, number];\n\n // Calculate center (midpoint)\n const center: [number, number] = [\n (click1[0] + click2[0]) / 2,\n (click1[1] + click2[1]) / 2,\n ];\n\n // Calculate semi-axes\n const ySemiAxis = distance(click1, click2, { units: distanceUnits }) / 2;\n const xSemiAxis = distance(center, cursorPos, { units: distanceUnits });\n\n // Full axes (diameter equivalent)\n const majorAxis = Math.max(xSemiAxis, ySemiAxis) * 2;\n const minorAxis = Math.min(xSemiAxis, ySemiAxis) * 2;\n\n // Ellipse area = π × a × b\n const ellipseArea = Math.PI * xSemiAxis * ySemiAxis;\n\n this.tooltip = {\n position: tooltipPosition,\n text: formatEllipseTooltip(\n majorAxis,\n minorAxis,\n ellipseArea,\n unitAbbrev,\n ),\n };\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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyDA,IAAa,6BAAb,cAAgD,gCAAgC;;CAE9E,AAAQ,UAA0B;;;;;;;;;;CAWlC,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;EACrC,MAAM,aAAa,4BAA4B,cAAc;EAC7D,MAAM,kBAAkB;AAExB,MAAI,cAAc,WAAW,GAAG;GAE9B,MAAM,aAAa,cAAc;AAKjC,QAAK,UAAU;IACb,UAAU;IACV,MAAM,sBAJK,SAAS,YAFD,WAE2B,EAAE,OAAO,eAAe,CAAC,EAIrC,WAAW;IAC9C;aACQ,cAAc,WAAW,GAAG;GAMrC,MAAM,SAAS,cAAc;GAC7B,MAAM,SAAS,cAAc;GAC7B,MAAM,YAAY;GAGlB,MAAMA,SAA2B,EAC9B,OAAO,KAAK,OAAO,MAAM,IACzB,OAAO,KAAK,OAAO,MAAM,EAC3B;GAGD,MAAM,YAAY,SAAS,QAAQ,QAAQ,EAAE,OAAO,eAAe,CAAC,GAAG;GACvE,MAAM,YAAY,SAAS,QAAQ,WAAW,EAAE,OAAO,eAAe,CAAC;AASvE,QAAK,UAAU;IACb,UAAU;IACV,MAAM,qBARU,KAAK,IAAI,WAAW,UAAU,GAAG,GACjC,KAAK,IAAI,WAAW,UAAU,GAAG,GAG/B,KAAK,KAAK,YAAY,WAQtC,WACD;IACF;;;;;;;;CASL,AAAS,cAAyB;AAChC,SAAO,KAAK,UAAU,CAAC,KAAK,QAAQ,GAAG,EAAE"}
|
|
@@ -21,17 +21,39 @@ import { distance } from "@turf/turf";
|
|
|
21
21
|
* Extends DrawLineStringMode to display distance tooltip between points.
|
|
22
22
|
*
|
|
23
23
|
* Shows the distance from the last clicked point to the current cursor position
|
|
24
|
-
* while drawing a line string.
|
|
24
|
+
* while drawing a line string. The tooltip updates in real-time as the cursor moves.
|
|
25
25
|
*
|
|
26
|
+
* ## Drawing Flow
|
|
27
|
+
* 1. Click to add first point
|
|
28
|
+
* 2. Move cursor (tooltip shows distance from last point)
|
|
29
|
+
* 3. Click to add more points
|
|
30
|
+
* 4. Double-click to finish the line string
|
|
31
|
+
*
|
|
32
|
+
* ## Double-Click Workaround
|
|
26
33
|
* Includes a workaround for the double-click to finish issue in @deck.gl-community/editable-layers ~9.1.
|
|
27
34
|
* This will be fixed in a future version (PR #225).
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* ```typescript
|
|
38
|
+
* import { DrawLineStringModeWithTooltip } from '@accelint/map-toolkit/deckgl/shapes/draw-shape-layer/modes';
|
|
39
|
+
*
|
|
40
|
+
* // Used internally by DrawShapeLayer
|
|
41
|
+
* const mode = new DrawLineStringModeWithTooltip();
|
|
42
|
+
* ```
|
|
28
43
|
*/
|
|
29
44
|
var DrawLineStringModeWithTooltip = class extends DrawLineStringMode {
|
|
45
|
+
/** Current tooltip state (null when not drawing) */
|
|
30
46
|
tooltip = null;
|
|
47
|
+
/** Cached mode props for double-click workaround */
|
|
31
48
|
lastModeProps = null;
|
|
32
49
|
/**
|
|
33
50
|
* Finish drawing the line string.
|
|
51
|
+
*
|
|
52
|
+
* Creates a LineString geometry from the click sequence and emits an edit action.
|
|
53
|
+
* Requires at least 2 points to create a valid line string.
|
|
34
54
|
* Extracted to share between double-click workaround and parent class logic.
|
|
55
|
+
*
|
|
56
|
+
* @param props - Mode properties with onEdit callback
|
|
35
57
|
*/
|
|
36
58
|
finishDrawing(props) {
|
|
37
59
|
const clickSequence = this.getClickSequence();
|
|
@@ -57,11 +79,26 @@ var DrawLineStringModeWithTooltip = class extends DrawLineStringMode {
|
|
|
57
79
|
}
|
|
58
80
|
/**
|
|
59
81
|
* Override handleClick to store props for double-click workaround.
|
|
82
|
+
*
|
|
83
|
+
* Caches the mode props so that the external double-click handler can
|
|
84
|
+
* access them when finishing the drawing.
|
|
85
|
+
*
|
|
86
|
+
* @param event - Click event with map coordinates
|
|
87
|
+
* @param props - Mode properties including onEdit callback
|
|
60
88
|
*/
|
|
61
89
|
handleClick(event, props) {
|
|
62
90
|
this.lastModeProps = props;
|
|
63
91
|
super.handleClick(event, props);
|
|
64
92
|
}
|
|
93
|
+
/**
|
|
94
|
+
* Handle pointer move events to update the tooltip with distance.
|
|
95
|
+
*
|
|
96
|
+
* Calculates the distance from the last clicked point to the current
|
|
97
|
+
* cursor position and displays it in the configured distance units.
|
|
98
|
+
*
|
|
99
|
+
* @param event - Pointer move event with cursor position
|
|
100
|
+
* @param props - Mode properties including distance units configuration
|
|
101
|
+
*/
|
|
65
102
|
handlePointerMove(event, props) {
|
|
66
103
|
super.handlePointerMove(event, props);
|
|
67
104
|
const clickSequence = this.getClickSequence();
|
|
@@ -77,6 +114,11 @@ var DrawLineStringModeWithTooltip = class extends DrawLineStringMode {
|
|
|
77
114
|
text: formatDistanceTooltip(distance(lastPoint, mapCoords, { units: distanceUnits }), getDistanceUnitAbbreviation(distanceUnits))
|
|
78
115
|
};
|
|
79
116
|
}
|
|
117
|
+
/**
|
|
118
|
+
* Get the current tooltip array for rendering.
|
|
119
|
+
*
|
|
120
|
+
* @returns Array containing the tooltip if one is active, empty array otherwise
|
|
121
|
+
*/
|
|
80
122
|
getTooltips() {
|
|
81
123
|
return this.tooltip ? [this.tooltip] : [];
|
|
82
124
|
}
|