From 1b7ac75d8163fe5ce9ebdd2c3c8c4a0ce26ef4c2 Mon Sep 17 00:00:00 2001 From: ellamartirosyan Date: Tue, 16 Jun 2026 11:52:30 +0300 Subject: [PATCH 01/46] fix: new checkboxes layout and active layers transparent service layers --- .../active-layers/active-layers-panel.tsx | 61 ++++++++++++++++--- .../cesium-map/debug/debugger-widget.css | 8 +++ .../cesium-map/debug/debugger-widget.tsx | 4 +- .../src/components/cesium-map/map.stories.tsx | 1 + 4 files changed, 63 insertions(+), 11 deletions(-) diff --git a/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.tsx b/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.tsx index a23632be..25993d3c 100644 --- a/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.tsx +++ b/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.tsx @@ -1,5 +1,5 @@ import { Rectangle } from 'cesium'; -import { get } from 'lodash'; +import { get, isEmpty } from 'lodash'; import React, { useEffect, useState } from 'react'; import { Tooltip, Typography } from '@map-colonies/react-core'; import bbox from '@turf/bbox'; @@ -10,13 +10,16 @@ import { useCesiumMap } from '../map'; import './active-layers-panel.css'; const IMAGERY = 'Imagery'; +const SERVICE = 'Service'; const DATA = 'Data'; +const TRANSPARENT_LAYER = '### TRANSPARENT_LAYER_FOR_OPTIMIZATION ###'; +const SERVICE_LAYER = '### LAYER_WITH_NO_ID ###'; interface IActiveLayer { id: string; name: string; rect: Rectangle; - isBaseMap: boolean; + isDisabled: boolean; } interface ISection { @@ -30,7 +33,7 @@ interface IActiveLayersPanelProps { export const ActiveLayersPanel: React.FC = ({ locale }) => { const mapViewer = useCesiumMap(); - const [sections, setSections] = useState([ { id: IMAGERY, values: [] }, { id: DATA, values: [] } ]); + const [sections, setSections] = useState([ { id: IMAGERY, values: [] }, { id: SERVICE, values: [] }, { id: DATA, values: [] } ]); const [collapsedSections, setCollapsedSections] = useState>({}); const getLabel = (key: string) => { @@ -42,13 +45,42 @@ export const ActiveLayersPanel: React.FC = ({ locale }) ? Array.from({ length: mapViewer.imageryLayers.length }, (_, i) => { const layer = mapViewer.imageryLayers.get(i); const meta = (layer as any).meta; + const isImageryLayer = !isEmpty(meta?.id) && meta.id !== TRANSPARENT_LAYER_ID; + if (!isImageryLayer) { + return undefined; + } return { - id: meta?.id as string, + id: meta.id as string, name: (get(meta, 'layerRecord.productName') ?? meta?.id) as string, rect: layer.rectangle, - isBaseMap: mapViewer.layersManager?.isBaseMapLayer(meta) as boolean + isDisabled: mapViewer.layersManager?.isBaseMapLayer(meta) as boolean }; - }).filter((layer) => layer.id !== TRANSPARENT_LAYER_ID) + }).filter((layer): layer is IActiveLayer => layer !== undefined) + : []; + }; + + const getServiceLayers = (): IActiveLayer[] => { + return mapViewer.imageryLayers + ? Array.from({ length: mapViewer.imageryLayers.length }, (_, i) => { + const layer = mapViewer.imageryLayers.get(i); + const meta = (layer as any).meta; + const isServiceLayer = isEmpty(meta?.id) || meta.id === TRANSPARENT_LAYER_ID; + if (!isServiceLayer) { + return undefined; + } + const isTransparentLayer = meta?.id === TRANSPARENT_LAYER_ID; + const providerName = (layer as any).imageryProvider?.constructor?.name as string | undefined; + const name = isTransparentLayer + ? TRANSPARENT_LAYER + : `${SERVICE_LAYER} ${String(i + 1)}`; + + return { + id: (meta?.id as string | undefined) ?? `SERVICE_LAYER_${String(i)}`, + name: isTransparentLayer ? name : providerName ?? name, + rect: layer.rectangle, + isDisabled: true + }; + }).filter((layer): layer is IActiveLayer => layer !== undefined) : []; }; @@ -58,7 +90,7 @@ export const ActiveLayersPanel: React.FC = ({ locale }) id: dataLayer.meta?.id as string, name: (get(dataLayer.meta, 'featureStructure.aliasLayerName') ?? dataLayer.meta.productName) as string, rect: Rectangle.fromDegrees(...bbox(dataLayer.meta?.footprint)), - isBaseMap: false + isDisabled: false }; }) || []; }; @@ -69,6 +101,10 @@ export const ActiveLayersPanel: React.FC = ({ locale }) id: IMAGERY, values: getImageryLayers() }, + { + id: SERVICE, + values: getServiceLayers() + }, { id: DATA, values: getDataLayers() @@ -90,15 +126,22 @@ export const ActiveLayersPanel: React.FC = ({ locale }) ...item, values: getImageryLayers() } + : item.id === SERVICE + ? { + ...item, + values: getServiceLayers() + } : item ) ); }; mapViewer.layersManager.addLayerUpdatedListener(handleLayerEvent); + mapViewer.imageryLayers.layerAdded.addEventListener(handleLayerEvent); mapViewer.imageryLayers.layerRemoved.addEventListener(handleLayerEvent); return () => { if (get(mapViewer, '_cesiumWidget') !== undefined) { mapViewer.layersManager?.removeLayerUpdatedListener(handleLayerEvent); + mapViewer.imageryLayers.layerAdded.removeEventListener(handleLayerEvent); mapViewer.imageryLayers.layerRemoved.removeEventListener(handleLayerEvent); } }; @@ -149,7 +192,7 @@ export const ActiveLayersPanel: React.FC = ({ locale }) section.values.map((activeLayer: IActiveLayer) => ( - {activeLayer.name} + {activeLayer.name} @@ -161,7 +204,7 @@ export const ActiveLayersPanel: React.FC = ({ locale }) {/* - { event.stopPropagation(); }}> + { event.stopPropagation(); }}> diff --git a/packages/react-components/src/components/cesium-map/debug/debugger-widget.css b/packages/react-components/src/components/cesium-map/debug/debugger-widget.css index 92a9e8d7..2a5f2d49 100644 --- a/packages/react-components/src/components/cesium-map/debug/debugger-widget.css +++ b/packages/react-components/src/components/cesium-map/debug/debugger-widget.css @@ -58,11 +58,19 @@ body[dir='rtl'] .cesium-viewer .debuggerWidgetSectionHeaderToggle { .cesium-viewer .debuggerWidgetSectionContent .optimizationCheckbox, .cesium-viewer .debuggerWidgetSectionContent .cesiumInspectorCheckbox { + display: flex; + align-items: center; + width: 100%; margin-bottom: 0; } .cesium-viewer .debuggerWidgetSectionContent .optimizationCheckbox label, .cesium-viewer .debuggerWidgetSectionContent .cesiumInspectorCheckbox label { + flex: 1 1 auto; + min-width: 0; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; cursor: pointer; } diff --git a/packages/react-components/src/components/cesium-map/debug/debugger-widget.tsx b/packages/react-components/src/components/cesium-map/debug/debugger-widget.tsx index cff95382..04ce5870 100644 --- a/packages/react-components/src/components/cesium-map/debug/debugger-widget.tsx +++ b/packages/react-components/src/components/cesium-map/debug/debugger-widget.tsx @@ -1,5 +1,5 @@ import React, { useEffect, useMemo, useState } from 'react'; -import { get } from 'lodash'; +import { get, isEmpty } from 'lodash'; import { Checkbox, Tooltip } from '@map-colonies/react-core'; import { Box } from '../../box'; import { EXAMINED_TILES_META_PROP } from '../helpers/customImageryProviders'; @@ -68,7 +68,7 @@ const DebuggerComponent: React.FC = ({ locale, isOpen, set if (!mapViewer.layersManager?.layerList) return; setLayersMeta( mapViewer.layersManager.layerList - .filter((layer): boolean => layer.meta?.id !== TRANSPARENT_LAYER_ID) + .filter((layer): boolean => !isEmpty(layer.meta?.id) && layer.meta?.id !== TRANSPARENT_LAYER_ID) .map( (layer): LayerMetaItem => ({ layerId: layer.meta?.id as string | undefined, diff --git a/packages/react-components/src/components/cesium-map/map.stories.tsx b/packages/react-components/src/components/cesium-map/map.stories.tsx index ec8b69ab..c367fdeb 100644 --- a/packages/react-components/src/components/cesium-map/map.stories.tsx +++ b/packages/react-components/src/components/cesium-map/map.stories.tsx @@ -422,6 +422,7 @@ LocalizedMap.argTypes = { NO_DATA_LAYERS: 'לא נמצאו שכבות מידע בתצוגה', ACTIVE_LAYERS_TITLE: 'שכבות פעילות', IMAGERY: 'ראסטר', + SERVICE: 'שירות', DATA: 'מידע', FLY_TO: 'הצג מיקום', REMOVE: 'הסר', From 9af24243c6df60969c67d8287168a97e70897d96 Mon Sep 17 00:00:00 2001 From: ellamartirosyan Date: Tue, 16 Jun 2026 13:47:33 +0300 Subject: [PATCH 02/46] fix: active layers fly to icon --- .../cesium-map/active-layers/active-layers-panel.css | 5 +++++ .../cesium-map/active-layers/active-layers-panel.tsx | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.css b/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.css index 4dffcfe4..ecb6f701 100644 --- a/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.css +++ b/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.css @@ -38,6 +38,11 @@ body[dir='rtl'] .cesium-viewer .activeLayersPanel .cesium-cesiumInspector-sectio cursor: pointer; } +body[dir='rtl'] .activeLayersPanel .icon { + margin-left: unset; + margin-right: 8px; +} + .cesium-viewer .activeLayersPanel .icon.disabled { opacity: 0; pointer-events: none; diff --git a/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.tsx b/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.tsx index 25993d3c..b15d3329 100644 --- a/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.tsx +++ b/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.tsx @@ -12,8 +12,8 @@ import './active-layers-panel.css'; const IMAGERY = 'Imagery'; const SERVICE = 'Service'; const DATA = 'Data'; -const TRANSPARENT_LAYER = '### TRANSPARENT_LAYER_FOR_OPTIMIZATION ###'; -const SERVICE_LAYER = '### LAYER_WITH_NO_ID ###'; +const TRANSPARENT_LAYER = '# TRANSPARENT_LAYER_FOR_OPTIMIZATION #'; +const SERVICE_LAYER = '# LAYER_WITH_NO_ID #'; interface IActiveLayer { id: string; From 6c491add68975f4dadd40395b26ddbf53dc797fb Mon Sep 17 00:00:00 2001 From: ellamartirosyan Date: Wed, 17 Jun 2026 11:38:24 +0300 Subject: [PATCH 03/46] fix: cesium inspector tile coordinates if checked should be always on top --- .../cesium-map/tools/inspector.tool.tsx | 30 ++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/packages/react-components/src/components/cesium-map/tools/inspector.tool.tsx b/packages/react-components/src/components/cesium-map/tools/inspector.tool.tsx index 2df9c79c..78a832f6 100644 --- a/packages/react-components/src/components/cesium-map/tools/inspector.tool.tsx +++ b/packages/react-components/src/components/cesium-map/tools/inspector.tool.tsx @@ -1,5 +1,5 @@ import React, { useEffect } from 'react'; -import { viewerCesiumInspectorMixin } from 'cesium'; +import { viewerCesiumInspectorMixin, TileCoordinatesImageryProvider } from 'cesium'; import { Box } from '../../box'; import { CesiumViewer, useCesiumMap } from '../map'; @@ -19,6 +19,21 @@ const applyInspectorContainerStyles = (container: HTMLElement): void => { container.style.position = 'relative'; }; +const keepTileCoordinatesLayerOnTop = (viewer: CesiumViewer): void => { + const layers = viewer.imageryLayers; + const tileCoordinatesLayer = Array.from({ length: layers.length }, (_, index) => layers.get(index)).find((layer) => { + const provider = (layer as any).imageryProvider; + return provider instanceof TileCoordinatesImageryProvider || provider?.constructor?.name === 'TileCoordinatesImageryProvider'; + }); + if (tileCoordinatesLayer === undefined) { + return; + } + const topLayer = layers.get(layers.length - 1); + if (topLayer !== tileCoordinatesLayer) { + layers.raiseToTop(tileCoordinatesLayer); + } +}; + export const InspectorTool: React.FC = () => { const mapViewer: CesiumViewer = useCesiumMap(); @@ -40,7 +55,20 @@ export const InspectorTool: React.FC = () => { applyInspectorContainerStyles(inspectorContainer); } + const refreshTileCoordinatesOrder = (): void => { + keepTileCoordinatesLayerOnTop(mapViewer); + }; + + const removeLayerAdded = mapViewer.imageryLayers.layerAdded.addEventListener(refreshTileCoordinatesOrder); + const removeLayerMoved = mapViewer.imageryLayers.layerMoved.addEventListener(refreshTileCoordinatesOrder); + const removeLayerRemoved = mapViewer.imageryLayers.layerRemoved.addEventListener(refreshTileCoordinatesOrder); + + setTimeout(refreshTileCoordinatesOrder, 0); + return () => { + removeLayerAdded(); + removeLayerMoved(); + removeLayerRemoved(); if (inspectorContainer) { inspectorContainer.style.display = 'none'; } From 16c9c63f995a90eaddee37e58fc36aaea555f558 Mon Sep 17 00:00:00 2001 From: ellamartirosyan Date: Wed, 17 Jun 2026 11:39:09 +0300 Subject: [PATCH 04/46] fix: bdi with ellipsis --- .../active-layers/active-layers-panel.css | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.css b/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.css index ecb6f701..7f83b255 100644 --- a/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.css +++ b/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.css @@ -11,6 +11,12 @@ body[dir='rtl'] .cesium-viewer .activeLayersPanel .cesium-cesiumInspector-sectio } .cesium-viewer .activeLayersPanel .name { + min-width: 0; + flex: 1 1 auto; +} + +.cesium-viewer .activeLayersPanel .name bdi { + display: block; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; @@ -34,15 +40,10 @@ body[dir='rtl'] .cesium-viewer .activeLayersPanel .cesium-cesiumInspector-sectio .cesium-viewer .activeLayersPanel .icon { width: 17px; height: 17px; - margin-left: 8px; + margin: 0 8px; cursor: pointer; } -body[dir='rtl'] .activeLayersPanel .icon { - margin-left: unset; - margin-right: 8px; -} - .cesium-viewer .activeLayersPanel .icon.disabled { opacity: 0; pointer-events: none; From aecbbddb8af4d54456602b749cc3d1cfcc2da2b5 Mon Sep 17 00:00:00 2001 From: ellamartirosyan Date: Wed, 17 Jun 2026 12:01:21 +0300 Subject: [PATCH 05/46] feat: add 3d models to active layers panel --- .../active-layers/active-layers-panel.tsx | 96 ++++++++++++++++--- .../src/components/cesium-map/map.stories.tsx | 1 + 2 files changed, 86 insertions(+), 11 deletions(-) diff --git a/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.tsx b/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.tsx index b15d3329..a6fc650a 100644 --- a/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.tsx +++ b/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.tsx @@ -1,4 +1,4 @@ -import { Rectangle } from 'cesium'; +import { Cesium3DTileset, Rectangle } from 'cesium'; import { get, isEmpty } from 'lodash'; import React, { useEffect, useState } from 'react'; import { Tooltip, Typography } from '@map-colonies/react-core'; @@ -12,13 +12,15 @@ import './active-layers-panel.css'; const IMAGERY = 'Imagery'; const SERVICE = 'Service'; const DATA = 'Data'; -const TRANSPARENT_LAYER = '# TRANSPARENT_LAYER_FOR_OPTIMIZATION #'; -const SERVICE_LAYER = '# LAYER_WITH_NO_ID #'; +const THREE_D = '3D'; +const TRANSPARENT_LAYER = 'TRANSPARENT_LAYER_FOR_OPTIMIZATION'; +const SERVICE_LAYER = 'LAYER_WITH_NO_ID #'; interface IActiveLayer { id: string; name: string; - rect: Rectangle; + rect?: Rectangle; + zoomToTarget?: Cesium3DTileset; isDisabled: boolean; } @@ -33,7 +35,12 @@ interface IActiveLayersPanelProps { export const ActiveLayersPanel: React.FC = ({ locale }) => { const mapViewer = useCesiumMap(); - const [sections, setSections] = useState([ { id: IMAGERY, values: [] }, { id: SERVICE, values: [] }, { id: DATA, values: [] } ]); + const [sections, setSections] = useState([ + { id: IMAGERY, values: [] }, + { id: SERVICE, values: [] }, + { id: DATA, values: [] }, + { id: THREE_D, values: [] } + ]); const [collapsedSections, setCollapsedSections] = useState>({}); const getLabel = (key: string) => { @@ -42,7 +49,7 @@ export const ActiveLayersPanel: React.FC = ({ locale }) const getImageryLayers = (): IActiveLayer[] => { return mapViewer.imageryLayers - ? Array.from({ length: mapViewer.imageryLayers.length }, (_, i) => { + ? Array.from({ length: mapViewer.imageryLayers.length }, (_, i): IActiveLayer | undefined => { const layer = mapViewer.imageryLayers.get(i); const meta = (layer as any).meta; const isImageryLayer = !isEmpty(meta?.id) && meta.id !== TRANSPARENT_LAYER_ID; @@ -61,7 +68,7 @@ export const ActiveLayersPanel: React.FC = ({ locale }) const getServiceLayers = (): IActiveLayer[] => { return mapViewer.imageryLayers - ? Array.from({ length: mapViewer.imageryLayers.length }, (_, i) => { + ? Array.from({ length: mapViewer.imageryLayers.length }, (_, i): IActiveLayer | undefined => { const layer = mapViewer.imageryLayers.get(i); const meta = (layer as any).meta; const isServiceLayer = isEmpty(meta?.id) || meta.id === TRANSPARENT_LAYER_ID; @@ -75,7 +82,7 @@ export const ActiveLayersPanel: React.FC = ({ locale }) : `${SERVICE_LAYER} ${String(i + 1)}`; return { - id: (meta?.id as string | undefined) ?? `SERVICE_LAYER_${String(i)}`, + id: `SERVICE_LAYER_${String(i)}`, name: isTransparentLayer ? name : providerName ?? name, rect: layer.rectangle, isDisabled: true @@ -94,6 +101,33 @@ export const ActiveLayersPanel: React.FC = ({ locale }) }; }) || []; }; + const get3DModels = (): IActiveLayer[] => { + const primitives = mapViewer.scene?.primitives as any; + if (primitives === undefined || typeof primitives.length !== 'number') { + return []; + } + return Array.from({ length: primitives.length }, (_, i) => primitives.get(i)) + .map((primitive: any, index: number): IActiveLayer | undefined => { + const isTileset = primitive instanceof Cesium3DTileset || primitive?.constructor?.name === 'Cesium3DTileset'; + if (!isTileset) { + return undefined; + } + const modelUrl = + (primitive as any).url as string | undefined ?? + (primitive as any)._url as string | undefined ?? + (primitive as any)._resource?._url as string | undefined; + const modelName = modelUrl ?? `${get(locale, THREE_D) ?? '3D Model'} ${String(index + 1)}`; + return { + id: `MODEL_3D_${String(index)}`, + name: modelName, + rect: undefined, + zoomToTarget: primitive as Cesium3DTileset, + isDisabled: false, + }; + }) + .filter((layer): layer is IActiveLayer => layer !== undefined); + }; + useEffect(() => { const updateSections = () => { const newSections = [ @@ -109,6 +143,10 @@ export const ActiveLayersPanel: React.FC = ({ locale }) id: DATA, values: getDataLayers() }, + { + id: THREE_D, + values: get3DModels() + }, ]; setSections(newSections); setCollapsedSections(newSections.reduce((acc, section) => ({ ...acc, [section.id]: true }), {})); @@ -131,6 +169,11 @@ export const ActiveLayersPanel: React.FC = ({ locale }) ...item, values: getServiceLayers() } + : item.id === THREE_D + ? { + ...item, + values: get3DModels() + } : item ) ); @@ -167,12 +210,43 @@ export const ActiveLayersPanel: React.FC = ({ locale }) }; }, [mapViewer.layersManager?.dataLayerList]); + useEffect(() => { + const primitives = mapViewer.scene?.primitives as any; + if (primitives?.primitiveAdded === undefined || primitives?.primitiveRemoved === undefined) { + return; + } + const handlePrimitiveEvent = (): void => { + setSections((prev) => + prev.map((item) => + item.id === THREE_D + ? { + ...item, + values: get3DModels() + } + : item + ) + ); + }; + primitives.primitiveAdded.addEventListener(handlePrimitiveEvent); + primitives.primitiveRemoved.addEventListener(handlePrimitiveEvent); + return () => { + primitives.primitiveAdded.removeEventListener(handlePrimitiveEvent); + primitives.primitiveRemoved.removeEventListener(handlePrimitiveEvent); + }; + }, [mapViewer.scene]); + const toggleSection = (id: string) => { setCollapsedSections((prev) => ({ ...prev, [id]: !prev[id] })); }; - const handleFlyTo = (rect: Rectangle) => { - mapViewer.camera.flyTo({ destination: rect }); + const handleFlyTo = (activeLayer: IActiveLayer) => { + if (activeLayer.zoomToTarget !== undefined) { + void mapViewer.zoomTo(activeLayer.zoomToTarget); + return; + } + if (activeLayer.rect !== undefined) { + mapViewer.camera.flyTo({ destination: activeLayer.rect }); + } }; return ( @@ -196,7 +270,7 @@ export const ActiveLayersPanel: React.FC = ({ locale }) - { event.stopPropagation(); handleFlyTo(activeLayer.rect); }}> + { event.stopPropagation(); handleFlyTo(activeLayer); }}> diff --git a/packages/react-components/src/components/cesium-map/map.stories.tsx b/packages/react-components/src/components/cesium-map/map.stories.tsx index c367fdeb..f20c04b7 100644 --- a/packages/react-components/src/components/cesium-map/map.stories.tsx +++ b/packages/react-components/src/components/cesium-map/map.stories.tsx @@ -424,6 +424,7 @@ LocalizedMap.argTypes = { IMAGERY: 'ראסטר', SERVICE: 'שירות', DATA: 'מידע', + '3D': 'תלת-מימד', FLY_TO: 'הצג מיקום', REMOVE: 'הסר', BASE_MAP_TITLE: 'מפות בסיס', From 89e33221444b304e082ebff37b816318df0655ac Mon Sep 17 00:00:00 2001 From: ellamartirosyan Date: Wed, 17 Jun 2026 15:30:11 +0300 Subject: [PATCH 06/46] fix: for 3d models when scene doesn't support event listeners --- .../active-layers/active-layers-panel.tsx | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.tsx b/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.tsx index a6fc650a..04c39da0 100644 --- a/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.tsx +++ b/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.tsx @@ -212,9 +212,6 @@ export const ActiveLayersPanel: React.FC = ({ locale }) useEffect(() => { const primitives = mapViewer.scene?.primitives as any; - if (primitives?.primitiveAdded === undefined || primitives?.primitiveRemoved === undefined) { - return; - } const handlePrimitiveEvent = (): void => { setSections((prev) => prev.map((item) => @@ -227,11 +224,17 @@ export const ActiveLayersPanel: React.FC = ({ locale }) ) ); }; - primitives.primitiveAdded.addEventListener(handlePrimitiveEvent); - primitives.primitiveRemoved.addEventListener(handlePrimitiveEvent); + if (primitives?.primitiveAdded !== undefined && primitives?.primitiveRemoved !== undefined) { + primitives.primitiveAdded.addEventListener(handlePrimitiveEvent); + primitives.primitiveRemoved.addEventListener(handlePrimitiveEvent); + return () => { + primitives.primitiveAdded.removeEventListener(handlePrimitiveEvent); + primitives.primitiveRemoved.removeEventListener(handlePrimitiveEvent); + }; + } + const intervalId = globalThis.setInterval(handlePrimitiveEvent, 1000); return () => { - primitives.primitiveAdded.removeEventListener(handlePrimitiveEvent); - primitives.primitiveRemoved.removeEventListener(handlePrimitiveEvent); + globalThis.clearInterval(intervalId); }; }, [mapViewer.scene]); From 836241e0a32e3eec90e519178d0b62defcaed372 Mon Sep 17 00:00:00 2001 From: ellamartirosyan Date: Wed, 17 Jun 2026 16:22:30 +0300 Subject: [PATCH 07/46] fix: 3d model name --- .../active-layers/active-layers-panel.tsx | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.tsx b/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.tsx index 04c39da0..639d3bd5 100644 --- a/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.tsx +++ b/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.tsx @@ -33,6 +33,21 @@ interface IActiveLayersPanelProps { locale?: { [key: string]: string }; } +const GENERIC_PATH_SEGMENTS = new Set(['data', 'act', 'assets', 'cesium', 'tiles', 'tileset', '3d', 'model', 'models']); + +const extractModelName = (rawUrl: string): string => { + try { + const { hostname, pathname } = new URL(rawUrl); + const segments = pathname.split('/').filter((s) => s.length > 0 && !s.includes('.')); + const named = [...segments] + .reverse() + .find((s) => !GENERIC_PATH_SEGMENTS.has(s.toLowerCase()) && /[a-zA-Z]/.test(s)); + return named ?? hostname; + } catch { + return rawUrl; + } +}; + export const ActiveLayersPanel: React.FC = ({ locale }) => { const mapViewer = useCesiumMap(); const [sections, setSections] = useState([ @@ -115,12 +130,13 @@ export const ActiveLayersPanel: React.FC = ({ locale }) const modelUrl = (primitive as any).url as string | undefined ?? (primitive as any)._url as string | undefined ?? + (primitive as any).resource?.url as string | undefined ?? + (primitive as any)._resource?.url as string | undefined ?? (primitive as any)._resource?._url as string | undefined; - const modelName = modelUrl ?? `${get(locale, THREE_D) ?? '3D Model'} ${String(index + 1)}`; + const modelName = extractModelName(modelUrl ?? `Model #${String(index + 1)}`); return { - id: `MODEL_3D_${String(index)}`, + id: `3D_MODEL_${String(index)}`, name: modelName, - rect: undefined, zoomToTarget: primitive as Cesium3DTileset, isDisabled: false, }; From cb8d2ddf162bf4068b3eb3f3a6f381fc08450f73 Mon Sep 17 00:00:00 2001 From: ellamartirosyan Date: Wed, 17 Jun 2026 16:34:15 +0300 Subject: [PATCH 08/46] fix: 3d model url --- .../active-layers/active-layers-panel.tsx | 32 +++++++++++++++---- 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.tsx b/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.tsx index 639d3bd5..00df78de 100644 --- a/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.tsx +++ b/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.tsx @@ -48,6 +48,31 @@ const extractModelName = (rawUrl: string): string => { } }; +const getTilesetUrl = (tileset: Cesium3DTileset): string | undefined => { + const directUrl = get(tileset, 'url'); + if (typeof directUrl === 'string') { + return directUrl; + } + const nestedDirectUrl = get(tileset, 'url.url'); + if (typeof nestedDirectUrl === 'string') { + return nestedDirectUrl; + } + const resourceUrl = get(tileset, 'resource.url'); + if (typeof resourceUrl === 'string') { + return resourceUrl; + } + const getUrlComponent = get(tileset, 'resource.getUrlComponent') as + | ((query?: boolean, proxy?: boolean) => string) + | undefined; + if (typeof getUrlComponent === 'function') { + const url = getUrlComponent(true, true); + if (typeof url === 'string' && url.length > 0) { + return url; + } + } + return undefined; +}; + export const ActiveLayersPanel: React.FC = ({ locale }) => { const mapViewer = useCesiumMap(); const [sections, setSections] = useState([ @@ -127,12 +152,7 @@ export const ActiveLayersPanel: React.FC = ({ locale }) if (!isTileset) { return undefined; } - const modelUrl = - (primitive as any).url as string | undefined ?? - (primitive as any)._url as string | undefined ?? - (primitive as any).resource?.url as string | undefined ?? - (primitive as any)._resource?.url as string | undefined ?? - (primitive as any)._resource?._url as string | undefined; + const modelUrl = getTilesetUrl(primitive as Cesium3DTileset); const modelName = extractModelName(modelUrl ?? `Model #${String(index + 1)}`); return { id: `3D_MODEL_${String(index)}`, From e5232030883a7e96748a354b624d8610acdb558e Mon Sep 17 00:00:00 2001 From: ellamartirosyan Date: Wed, 17 Jun 2026 16:41:34 +0300 Subject: [PATCH 09/46] fix: 3d model url --- .../active-layers/active-layers-panel.tsx | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.tsx b/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.tsx index 00df78de..19659057 100644 --- a/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.tsx +++ b/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.tsx @@ -49,27 +49,10 @@ const extractModelName = (rawUrl: string): string => { }; const getTilesetUrl = (tileset: Cesium3DTileset): string | undefined => { - const directUrl = get(tileset, 'url'); - if (typeof directUrl === 'string') { - return directUrl; - } - const nestedDirectUrl = get(tileset, 'url.url'); - if (typeof nestedDirectUrl === 'string') { - return nestedDirectUrl; - } const resourceUrl = get(tileset, 'resource.url'); if (typeof resourceUrl === 'string') { return resourceUrl; } - const getUrlComponent = get(tileset, 'resource.getUrlComponent') as - | ((query?: boolean, proxy?: boolean) => string) - | undefined; - if (typeof getUrlComponent === 'function') { - const url = getUrlComponent(true, true); - if (typeof url === 'string' && url.length > 0) { - return url; - } - } return undefined; }; From fd889c4d1a1cae0251feec8a1cc9ae06f2f383ab Mon Sep 17 00:00:00 2001 From: ellamartirosyan Date: Wed, 17 Jun 2026 16:43:41 +0300 Subject: [PATCH 10/46] fix: 3d model url --- .../cesium-map/active-layers/active-layers-panel.tsx | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.tsx b/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.tsx index 19659057..58e14f9b 100644 --- a/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.tsx +++ b/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.tsx @@ -48,14 +48,6 @@ const extractModelName = (rawUrl: string): string => { } }; -const getTilesetUrl = (tileset: Cesium3DTileset): string | undefined => { - const resourceUrl = get(tileset, 'resource.url'); - if (typeof resourceUrl === 'string') { - return resourceUrl; - } - return undefined; -}; - export const ActiveLayersPanel: React.FC = ({ locale }) => { const mapViewer = useCesiumMap(); const [sections, setSections] = useState([ @@ -135,7 +127,7 @@ export const ActiveLayersPanel: React.FC = ({ locale }) if (!isTileset) { return undefined; } - const modelUrl = getTilesetUrl(primitive as Cesium3DTileset); + const modelUrl = get(primitive, 'resource.url'); const modelName = extractModelName(modelUrl ?? `Model #${String(index + 1)}`); return { id: `3D_MODEL_${String(index)}`, From 2f107caf21fb7a7ee5884a29cc6b5181ebcdfce4 Mon Sep 17 00:00:00 2001 From: ellamartirosyan Date: Wed, 17 Jun 2026 17:28:52 +0300 Subject: [PATCH 11/46] fix: 3d model use product name --- .../components/cesium-map/active-layers/active-layers-panel.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.tsx b/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.tsx index 58e14f9b..56ecb37d 100644 --- a/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.tsx +++ b/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.tsx @@ -128,7 +128,7 @@ export const ActiveLayersPanel: React.FC = ({ locale }) return undefined; } const modelUrl = get(primitive, 'resource.url'); - const modelName = extractModelName(modelUrl ?? `Model #${String(index + 1)}`); + const modelName = get(primitive, 'productName') ?? extractModelName(modelUrl ?? `Model #${String(index + 1)}`); return { id: `3D_MODEL_${String(index)}`, name: modelName, From b672133d259911af6e48c4131f3b3be4a684b9c4 Mon Sep 17 00:00:00 2001 From: ellamartirosyan Date: Thu, 18 Jun 2026 09:12:25 +0300 Subject: [PATCH 12/46] fix: 3d model properties name --- .../cesium-map/active-layers/active-layers-panel.tsx | 2 +- .../src/components/cesium-map/layers/3d.tileset.tsx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.tsx b/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.tsx index 56ecb37d..8333851b 100644 --- a/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.tsx +++ b/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.tsx @@ -128,7 +128,7 @@ export const ActiveLayersPanel: React.FC = ({ locale }) return undefined; } const modelUrl = get(primitive, 'resource.url'); - const modelName = get(primitive, 'productName') ?? extractModelName(modelUrl ?? `Model #${String(index + 1)}`); + const modelName = get(primitive, 'properties.name') ?? extractModelName(modelUrl ?? `Model #${String(index + 1)}`); return { id: `3D_MODEL_${String(index)}`, name: modelName, diff --git a/packages/react-components/src/components/cesium-map/layers/3d.tileset.tsx b/packages/react-components/src/components/cesium-map/layers/3d.tileset.tsx index f876dc5a..c30d963c 100644 --- a/packages/react-components/src/components/cesium-map/layers/3d.tileset.tsx +++ b/packages/react-components/src/components/cesium-map/layers/3d.tileset.tsx @@ -16,8 +16,6 @@ export const Cesium3DTileset: React.FC = (props) => { { - // props.onReady?.(tileset); - if (props.isZoomTo === true) { void mapViewer.zoomTo(tileset); } @@ -31,6 +29,8 @@ export const Cesium3DTileset: React.FC = (props) => { const translation = Cartesian3.subtract(offset, surface, new Cartesian3()); tileset.modelMatrix = Matrix4.fromTranslation(translation); } + + props.onReady?.(tileset); }} /> ); From 1fc120754bb54b1b2770e037da8326ea2db8eb46 Mon Sep 17 00:00:00 2001 From: ellamartirosyan Date: Sun, 21 Jun 2026 16:39:43 +0300 Subject: [PATCH 13/46] fix: use meta --- .../cesium-map/active-layers/active-layers-panel.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.tsx b/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.tsx index 8333851b..71df47dc 100644 --- a/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.tsx +++ b/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.tsx @@ -19,9 +19,9 @@ const SERVICE_LAYER = 'LAYER_WITH_NO_ID #'; interface IActiveLayer { id: string; name: string; + isDisabled: boolean; rect?: Rectangle; zoomToTarget?: Cesium3DTileset; - isDisabled: boolean; } interface ISection { @@ -128,7 +128,7 @@ export const ActiveLayersPanel: React.FC = ({ locale }) return undefined; } const modelUrl = get(primitive, 'resource.url'); - const modelName = get(primitive, 'properties.name') ?? extractModelName(modelUrl ?? `Model #${String(index + 1)}`); + const modelName = get(primitive, 'meta.layerRecord.productName') ?? extractModelName(modelUrl ?? `Model #${String(index + 1)}`); return { id: `3D_MODEL_${String(index)}`, name: modelName, From 67e53c5cf963e2392bbe494178f297f5e0f0727e Mon Sep 17 00:00:00 2001 From: ellamartirosyan Date: Sun, 21 Jun 2026 20:38:05 +0300 Subject: [PATCH 14/46] fix: use layersManager instead of cesium mapViewer.imageryLayers directly --- .../active-layers/active-layers-panel.tsx | 49 +++++++++++-------- .../components/cesium-map/layers-manager.ts | 15 ++++-- 2 files changed, 41 insertions(+), 23 deletions(-) diff --git a/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.tsx b/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.tsx index 71df47dc..03259119 100644 --- a/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.tsx +++ b/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.tsx @@ -1,10 +1,16 @@ import { Cesium3DTileset, Rectangle } from 'cesium'; -import { get, isEmpty } from 'lodash'; +import { get } from 'lodash'; import React, { useEffect, useState } from 'react'; import { Tooltip, Typography } from '@map-colonies/react-core'; import bbox from '@turf/bbox'; import { Box } from '../../box'; -import { TRANSPARENT_LAYER_ID } from '../layers-manager'; +import { + ICesiumImageryLayer, + TRANSPARENT_LAYER_ID, + getLayerId, + isServiceLayer, + isManagedImageryLayer +} from '../layers-manager'; import { useCesiumMap } from '../map'; import './active-layers-panel.css'; @@ -62,36 +68,39 @@ export const ActiveLayersPanel: React.FC = ({ locale }) return get(locale, key.toUpperCase()) ?? key; }; + const getLayerList = (): ICesiumImageryLayer[] => { + return mapViewer.layersManager?.layerList ?? []; + }; + const getImageryLayers = (): IActiveLayer[] => { - return mapViewer.imageryLayers - ? Array.from({ length: mapViewer.imageryLayers.length }, (_, i): IActiveLayer | undefined => { - const layer = mapViewer.imageryLayers.get(i); - const meta = (layer as any).meta; - const isImageryLayer = !isEmpty(meta?.id) && meta.id !== TRANSPARENT_LAYER_ID; - if (!isImageryLayer) { + const layerList = getLayerList(); + return layerList.length > 0 + ? layerList.map((layer): IActiveLayer | undefined => { + const meta = get(layer, 'meta'); + const layerId = getLayerId(layer); + if (!isManagedImageryLayer(layerId)) { return undefined; } return { - id: meta.id as string, - name: (get(meta, 'layerRecord.productName') ?? meta?.id) as string, + id: layerId as string, + name: (get(meta, 'layerRecord.productName') ?? layerId) as string, rect: layer.rectangle, isDisabled: mapViewer.layersManager?.isBaseMapLayer(meta) as boolean }; - }).filter((layer): layer is IActiveLayer => layer !== undefined) + }).filter((item): item is IActiveLayer => item !== undefined) : []; }; const getServiceLayers = (): IActiveLayer[] => { - return mapViewer.imageryLayers - ? Array.from({ length: mapViewer.imageryLayers.length }, (_, i): IActiveLayer | undefined => { - const layer = mapViewer.imageryLayers.get(i); - const meta = (layer as any).meta; - const isServiceLayer = isEmpty(meta?.id) || meta.id === TRANSPARENT_LAYER_ID; - if (!isServiceLayer) { + const layerList = getLayerList(); + return layerList.length > 0 + ? layerList.map((layer, i): IActiveLayer | undefined => { + const layerId = getLayerId(layer); + if (!isServiceLayer(layerId)) { return undefined; } - const isTransparentLayer = meta?.id === TRANSPARENT_LAYER_ID; - const providerName = (layer as any).imageryProvider?.constructor?.name as string | undefined; + const isTransparentLayer = layerId === TRANSPARENT_LAYER_ID; + const providerName = get(layer, 'imageryProvider.constructor.name') as string | undefined; const name = isTransparentLayer ? TRANSPARENT_LAYER : `${SERVICE_LAYER} ${String(i + 1)}`; @@ -102,7 +111,7 @@ export const ActiveLayersPanel: React.FC = ({ locale }) rect: layer.rectangle, isDisabled: true }; - }).filter((layer): layer is IActiveLayer => layer !== undefined) + }).filter((item): item is IActiveLayer => item !== undefined) : []; }; diff --git a/packages/react-components/src/components/cesium-map/layers-manager.ts b/packages/react-components/src/components/cesium-map/layers-manager.ts index c52ee5f7..aef7498d 100644 --- a/packages/react-components/src/components/cesium-map/layers-manager.ts +++ b/packages/react-components/src/components/cesium-map/layers-manager.ts @@ -55,6 +55,18 @@ export type LegendExtractor = (layers: (any & { meta: any })[]) => IMapLegend[]; export const TRANSPARENT_LAYER_ID = 'TRANSPARENT_BASE_LAYER'; +export const getLayerId = (layer: ICesiumImageryLayer): string | undefined => { + return get(layer, 'meta.id') as string | undefined; +}; + +export const isServiceLayer = (layerId: string | undefined): boolean => { + return isEmpty(layerId) || layerId === TRANSPARENT_LAYER_ID; +}; + +export const isManagedImageryLayer = (layerId: string | undefined): boolean => { + return !isServiceLayer(layerId); +}; + class LayerManager { public mapViewer: CesiumViewer; @@ -478,7 +490,6 @@ class LayerManager { const move = from > to ? INC : DEC; const min = from < to ? from : to; const max = from < to ? to : from; - this.layers.forEach((layer) => { const parentId = get(layer.meta, 'parentBasetMapId') as string; if (!parentId) { @@ -494,12 +505,10 @@ class LayerManager { if (layer.meta?.id === TRANSPARENT_LAYER_ID) { continue; } - const relevantToExtent = layer.meta?.relevantToExtent; if (typeof relevantToExtent !== 'boolean') { continue; } - if (relevantToExtent !== layer.show && layer.imageryProvider.ready) { layer.show = relevantToExtent; } From 3678561f1e328284fdea5efdc3da197c00c72196 Mon Sep 17 00:00:00 2001 From: ellamartirosyan Date: Sun, 21 Jun 2026 21:10:00 +0300 Subject: [PATCH 15/46] chore: fix --- .../components/cesium-map/active-layers/active-layers-panel.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.tsx b/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.tsx index 03259119..df960b24 100644 --- a/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.tsx +++ b/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.tsx @@ -145,7 +145,7 @@ export const ActiveLayersPanel: React.FC = ({ locale }) isDisabled: false, }; }) - .filter((layer): layer is IActiveLayer => layer !== undefined); + .filter((item): item is IActiveLayer => item !== undefined); }; useEffect(() => { From 0d759c8396734ba7336dcde7433725e1bba6532c Mon Sep 17 00:00:00 2001 From: ellamartirosyan Date: Sun, 21 Jun 2026 21:55:13 +0300 Subject: [PATCH 16/46] feat: add 3D models to layersManager --- .../active-layers/active-layers-panel.tsx | 59 ++++++------------- .../components/cesium-map/layers-manager.ts | 42 +++++++++++++ .../cesium-map/layers/3d.tileset.tsx | 21 ++++++- .../layers/3d.tileset.with.update.tsx | 13 +++- 4 files changed, 90 insertions(+), 45 deletions(-) diff --git a/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.tsx b/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.tsx index df960b24..10b5833d 100644 --- a/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.tsx +++ b/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.tsx @@ -126,26 +126,16 @@ export const ActiveLayersPanel: React.FC = ({ locale }) }; const get3DModels = (): IActiveLayer[] => { - const primitives = mapViewer.scene?.primitives as any; - if (primitives === undefined || typeof primitives.length !== 'number') { - return []; - } - return Array.from({ length: primitives.length }, (_, i) => primitives.get(i)) - .map((primitive: any, index: number): IActiveLayer | undefined => { - const isTileset = primitive instanceof Cesium3DTileset || primitive?.constructor?.name === 'Cesium3DTileset'; - if (!isTileset) { - return undefined; - } - const modelUrl = get(primitive, 'resource.url'); - const modelName = get(primitive, 'meta.layerRecord.productName') ?? extractModelName(modelUrl ?? `Model #${String(index + 1)}`); - return { - id: `3D_MODEL_${String(index)}`, - name: modelName, - zoomToTarget: primitive as Cesium3DTileset, - isDisabled: false, - }; - }) - .filter((item): item is IActiveLayer => item !== undefined); + return (mapViewer.layersManager?.modelList ?? []).map((model, index): IActiveLayer => { + const modelUrl = get(model.tileset, 'resource.url') as string | undefined; + const modelName = (get(model.meta, 'layerRecord.productName') ?? extractModelName(modelUrl ?? `Model #${String(index + 1)}`)) as string; + return { + id: (model.meta.id as string) ?? `3D_MODEL_${String(index)}`, + name: modelName, + zoomToTarget: model.tileset, + isDisabled: false, + }; + }); }; useEffect(() => { @@ -175,7 +165,7 @@ export const ActiveLayersPanel: React.FC = ({ locale }) }, []); useEffect(() => { - if (!mapViewer.layersManager) return; + if (!mapViewer.layersManager) { return; } const handleLayerEvent = (): void => { setSections((prev) => prev.map((item) => @@ -189,12 +179,7 @@ export const ActiveLayersPanel: React.FC = ({ locale }) ...item, values: getServiceLayers() } - : item.id === THREE_D - ? { - ...item, - values: get3DModels() - } - : item + : item ) ); }; @@ -211,7 +196,7 @@ export const ActiveLayersPanel: React.FC = ({ locale }) }, [mapViewer.layersManager?.layerList]); useEffect(() => { - if (!mapViewer.layersManager) return; + if (!mapViewer.layersManager) { return; } const handleDataLayerEvent = (): void => { setSections((prev) => prev.map((item) => @@ -231,8 +216,8 @@ export const ActiveLayersPanel: React.FC = ({ locale }) }, [mapViewer.layersManager?.dataLayerList]); useEffect(() => { - const primitives = mapViewer.scene?.primitives as any; - const handlePrimitiveEvent = (): void => { + if (!mapViewer.layersManager) { return; } + const handle3DModelEvent = (): void => { setSections((prev) => prev.map((item) => item.id === THREE_D @@ -244,19 +229,11 @@ export const ActiveLayersPanel: React.FC = ({ locale }) ) ); }; - if (primitives?.primitiveAdded !== undefined && primitives?.primitiveRemoved !== undefined) { - primitives.primitiveAdded.addEventListener(handlePrimitiveEvent); - primitives.primitiveRemoved.addEventListener(handlePrimitiveEvent); - return () => { - primitives.primitiveAdded.removeEventListener(handlePrimitiveEvent); - primitives.primitiveRemoved.removeEventListener(handlePrimitiveEvent); - }; - } - const intervalId = globalThis.setInterval(handlePrimitiveEvent, 1000); + mapViewer.layersManager.addModelUpdatedListener(handle3DModelEvent); return () => { - globalThis.clearInterval(intervalId); + mapViewer.layersManager?.removeModelUpdatedListener(handle3DModelEvent); }; - }, [mapViewer.scene]); + }, [mapViewer.layersManager?.modelList]); const toggleSection = (id: string) => { setCollapsedSections((prev) => ({ ...prev, [id]: !prev[id] })); diff --git a/packages/react-components/src/components/cesium-map/layers-manager.ts b/packages/react-components/src/components/cesium-map/layers-manager.ts index aef7498d..d7ca53a2 100644 --- a/packages/react-components/src/components/cesium-map/layers-manager.ts +++ b/packages/react-components/src/components/cesium-map/layers-manager.ts @@ -1,5 +1,6 @@ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ import { + Cesium3DTileset as CesiumTileset, ImageryLayer, UrlTemplateImageryProvider, WebMapServiceImageryProvider, @@ -44,6 +45,11 @@ export interface IRasterLayer { details?: Record; } +export interface ICesium3DModel { + tileset: CesiumTileset; + meta: Record; +} + export interface IVectorLayer { id: string; opacity: number; @@ -73,8 +79,10 @@ class LayerManager { public legendsList: IMapLegend[]; public layerUpdated: Event; public dataLayerUpdated: Event; + public modelUpdated: Event; private readonly layers: ICesiumImageryLayer[]; private readonly dataLayers: ICesiumWFSLayer[]; + private readonly models: ICesium3DModel[]; private readonly legendsExtractor?: LegendExtractor; private readonly layerManagerFootprintMetaFieldPath: string | undefined; private shouldOptimizedTileRequests?: boolean; @@ -92,10 +100,12 @@ class LayerManager { // eslint-disable-next-line this.layers = (this.mapViewer.imageryLayers as any)._layers; this.dataLayers = []; + this.models = []; this.legendsList = []; this.legendsExtractor = legendsExtractor; this.layerUpdated = new Event(); this.dataLayerUpdated = new Event(); + this.modelUpdated = new Event(); this.layerManagerFootprintMetaFieldPath = layerManagerFootprintMetaFieldPath; this.shouldOptimizedTileRequests = shouldOptimizedTileRequests ?? false; this.relevancyListenersCleanup = []; @@ -118,6 +128,10 @@ class LayerManager { return this.dataLayers; } + public get modelList(): ICesium3DModel[] { + return this.models; + } + public isBaseMapLayer(meta: any): boolean { return !!get(meta, 'parentBasetMapId'); } @@ -437,6 +451,14 @@ class LayerManager { this.dataLayerUpdated.removeEventListener(callback, this); } + public addModelUpdatedListener(callback: (models: ICesium3DModel[]) => void): void { + this.modelUpdated.addEventListener(callback, this); + } + + public removeModelUpdatedListener(callback: (models: ICesium3DModel[]) => void): void { + this.modelUpdated.removeEventListener(callback, this); + } + public setShouldOptimizedTileRequests(shouldOptimize: boolean): void { if (this.shouldOptimizedTileRequests === shouldOptimize) { return; @@ -465,6 +487,26 @@ class LayerManager { }); } + public addModel(model: ICesium3DModel): void { + this.models.push({ ...model }); + this.modelUpdated.raiseEvent(this.models); + } + + public removeModel(modelId: string): void { + const model = this.findModelById(modelId); + if (model) { + const index = this.models.indexOf(model); + if (index > -1) { + this.models.splice(index, 1); + } + this.modelUpdated.raiseEvent(this.models); + } + } + + public findModelById(modelId: string): ICesium3DModel | undefined { + return this.models.find((model) => model.meta.id === modelId); + } + private setLegends(): void { if (typeof this.legendsExtractor !== 'undefined') { this.legendsList = this.legendsExtractor(this.layers); diff --git a/packages/react-components/src/components/cesium-map/layers/3d.tileset.tsx b/packages/react-components/src/components/cesium-map/layers/3d.tileset.tsx index c30d963c..d76a88d7 100644 --- a/packages/react-components/src/components/cesium-map/layers/3d.tileset.tsx +++ b/packages/react-components/src/components/cesium-map/layers/3d.tileset.tsx @@ -1,5 +1,5 @@ -import React, { ComponentProps } from 'react'; -import { Cartesian3, Cartographic, Matrix4 } from 'cesium'; +import React, { ComponentProps, useEffect, useRef } from 'react'; +import { Cartesian3, Cartographic, Matrix4, Cesium3DTileset as CesiumTileset } from 'cesium'; import { Cesium3DTileset as Resium3DTileset } from 'resium'; import { CesiumViewer, useCesiumMap } from '../map'; @@ -8,14 +8,29 @@ const GROUND_LEVEL = 0.0; export interface RCesium3DTilesetProps extends ComponentProps { isZoomTo?: boolean; heightFromGround?: number; + meta?: Record; } -export const Cesium3DTileset: React.FC = (props) => { +export const Cesium3DTileset: React.FC = ({ meta, ...props }) => { const mapViewer: CesiumViewer = useCesiumMap(); + const tilesetRef = useRef(null); + + useEffect(() => { + return () => { + if (tilesetRef.current !== null && meta?.id !== undefined) { + mapViewer.layersManager?.removeModel(meta.id as string); + } + }; + }, []); + return ( { + tilesetRef.current = tileset; + if (meta !== undefined) { + mapViewer.layersManager?.addModel({ tileset, meta }); + } if (props.isZoomTo === true) { void mapViewer.zoomTo(tileset); } diff --git a/packages/react-components/src/components/cesium-map/layers/3d.tileset.with.update.tsx b/packages/react-components/src/components/cesium-map/layers/3d.tileset.with.update.tsx index a240b3b4..20f7d803 100644 --- a/packages/react-components/src/components/cesium-map/layers/3d.tileset.with.update.tsx +++ b/packages/react-components/src/components/cesium-map/layers/3d.tileset.with.update.tsx @@ -13,9 +13,10 @@ import { CesiumViewer, useCesiumMap } from '../map'; export interface Cesium3DTilesetWithUpdateProps { url: string; withUpdate?: boolean; + meta?: Record; } -export const Cesium3DTilesetWithUpdate: React.FC = ({ url, withUpdate }) => { +export const Cesium3DTilesetWithUpdate: React.FC = ({ url, withUpdate, meta }) => { const mapViewer: CesiumViewer = useCesiumMap(); const scene = mapViewer.scene; const [cesium3DTileset] = useState( @@ -34,6 +35,16 @@ export const Cesium3DTilesetWithUpdate: React.FC // eslint-disable-next-line react-hooks/exhaustive-deps }, []); + useEffect(() => { + if (meta === undefined) return; + mapViewer.layersManager?.addModel({ tileset, meta }); + return () => { + if (meta.id !== undefined) { + mapViewer.layersManager?.removeModel(meta.id as string); + } + }; + }, [mapViewer.layersManager]); + const updateContent = (model: Cesium3DTileContent, boundingVolume: any): void => { const height = boundingVolume.minimumHeight ? boundingVolume.minimumHeight : boundingVolume.center.z - boundingVolume.radius; // @ts-ignore From 24ae45667ef261fbbe1572b0d475ae2ca9fb65bf Mon Sep 17 00:00:00 2001 From: ellamartirosyan Date: Mon, 22 Jun 2026 08:38:31 +0300 Subject: [PATCH 17/46] fix: stories with 3D model meta --- .../cesium-map/layers/3d.tileset.stories.tsx | 12 +++++++++--- .../cesium-map/layers/wfs.layer.stories.tsx | 18 +++++++++++++++--- .../terrain-provider-heights-tool.stories.tsx | 6 +++++- .../terrain-provider.stories.tsx | 6 +++++- 4 files changed, 34 insertions(+), 8 deletions(-) diff --git a/packages/react-components/src/components/cesium-map/layers/3d.tileset.stories.tsx b/packages/react-components/src/components/cesium-map/layers/3d.tileset.stories.tsx index 9e27d627..86c904d7 100644 --- a/packages/react-components/src/components/cesium-map/layers/3d.tileset.stories.tsx +++ b/packages/react-components/src/components/cesium-map/layers/3d.tileset.stories.tsx @@ -28,8 +28,9 @@ export const Cesium3DTilesetLayer: Story = (args: Record) => (
+
); diff --git a/packages/react-components/src/components/cesium-map/layers/wfs.layer.stories.tsx b/packages/react-components/src/components/cesium-map/layers/wfs.layer.stories.tsx index b64726d7..135ef74a 100644 --- a/packages/react-components/src/components/cesium-map/layers/wfs.layer.stories.tsx +++ b/packages/react-components/src/components/cesium-map/layers/wfs.layer.stories.tsx @@ -101,7 +101,11 @@ export const MapWithWFSLayer: Story = (args: Record) => { return (
- + ) return (
- +
@@ -210,7 +218,11 @@ export const MapWithWFSLayerWithVisualizer: Story = (args: Record - + { imageryProvider={false} baseMaps={BASE_MAPS} > - + diff --git a/packages/react-components/src/components/cesium-map/terrain-providers/terrain-provider.stories.tsx b/packages/react-components/src/components/cesium-map/terrain-providers/terrain-provider.stories.tsx index 021bef3e..2213eac9 100644 --- a/packages/react-components/src/components/cesium-map/terrain-providers/terrain-provider.stories.tsx +++ b/packages/react-components/src/components/cesium-map/terrain-providers/terrain-provider.stories.tsx @@ -136,7 +136,11 @@ export const QuantizedMeshProviders: Story = () => { baseMaps={BASE_MAPS} mapProjection={new WebMercatorProjection()} > - + From 637e602cf19e6b648b21ec970af65bb22796e203 Mon Sep 17 00:00:00 2001 From: ellamartirosyan Date: Mon, 22 Jun 2026 08:41:34 +0300 Subject: [PATCH 18/46] chore: fix --- .../src/components/cesium-map/layers/3d.tileset.with.update.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-components/src/components/cesium-map/layers/3d.tileset.with.update.tsx b/packages/react-components/src/components/cesium-map/layers/3d.tileset.with.update.tsx index 20f7d803..6bf61143 100644 --- a/packages/react-components/src/components/cesium-map/layers/3d.tileset.with.update.tsx +++ b/packages/react-components/src/components/cesium-map/layers/3d.tileset.with.update.tsx @@ -36,7 +36,7 @@ export const Cesium3DTilesetWithUpdate: React.FC }, []); useEffect(() => { - if (meta === undefined) return; + if (meta === undefined) { return; } mapViewer.layersManager?.addModel({ tileset, meta }); return () => { if (meta.id !== undefined) { From 465bdea26f158691968e0bc0fcf39d57ba31d328 Mon Sep 17 00:00:00 2001 From: ellamartirosyan Date: Mon, 22 Jun 2026 11:30:06 +0300 Subject: [PATCH 19/46] fix: naming convention --- .../cesium-map/debug/debugger-widget.tsx | 4 ++-- .../components/cesium-map/layers-manager.ts | 20 +++++++++---------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/packages/react-components/src/components/cesium-map/debug/debugger-widget.tsx b/packages/react-components/src/components/cesium-map/debug/debugger-widget.tsx index 04ce5870..ace5c8c9 100644 --- a/packages/react-components/src/components/cesium-map/debug/debugger-widget.tsx +++ b/packages/react-components/src/components/cesium-map/debug/debugger-widget.tsx @@ -216,7 +216,7 @@ const DebuggerComponent: React.FC = ({ locale, isOpen, set const idText = layer.layerId ?? `LAYER-${layersMeta.length - index}`; const nameText = (get(layer.meta, 'layerRecord.productName') as string | undefined) ?? idText; const statusText = - layer.meta?.relevantToExtent === true ? ' → show' : layer.meta?.relevantToExtent === false ? ' → hide' : ''; + layer.meta?.isRelevantToExtent === true ? ' → show' : layer.meta?.isRelevantToExtent === false ? ' → hide' : ''; const transparencyText = layer.meta?.hasTransparency === true ? withTransparencyTiles : layer.meta?.hasTransparency === false ? withoutTransparencyTiles : ''; const tileCoordinatesFromMeta = get(layer.meta, EXAMINED_TILES_META_PROP) as @@ -235,7 +235,7 @@ const DebuggerComponent: React.FC = ({ locale, isOpen, set transparencyText === '' ? undefined : {transparencyText}: {formattedTileCoordinates.join(', ')}; - const isRelevant = layer.meta?.relevantToExtent !== false; + const isRelevant = layer.meta?.isRelevantToExtent !== false; if (tooltipContent === undefined) { return ( diff --git a/packages/react-components/src/components/cesium-map/layers-manager.ts b/packages/react-components/src/components/cesium-map/layers-manager.ts index d7ca53a2..57d268eb 100644 --- a/packages/react-components/src/components/cesium-map/layers-manager.ts +++ b/packages/react-components/src/components/cesium-map/layers-manager.ts @@ -547,12 +547,12 @@ class LayerManager { if (layer.meta?.id === TRANSPARENT_LAYER_ID) { continue; } - const relevantToExtent = layer.meta?.relevantToExtent; - if (typeof relevantToExtent !== 'boolean') { + const isRelevantToExtent = layer.meta?.isRelevantToExtent; + if (typeof isRelevantToExtent !== 'boolean') { continue; } - if (relevantToExtent !== layer.show && layer.imageryProvider.ready) { - layer.show = relevantToExtent; + if (isRelevantToExtent !== layer.show && layer.imageryProvider.ready) { + layer.show = isRelevantToExtent; } } } @@ -573,9 +573,9 @@ class LayerManager { if (layer.meta?.id === TRANSPARENT_LAYER_ID) { continue; } - if (layer.meta && 'relevantToExtent' in layer.meta) { - const { relevantToExtent, ...restMeta } = layer.meta; - void relevantToExtent; + if (layer.meta && 'isRelevantToExtent' in layer.meta) { + const { isRelevantToExtent, ...restMeta } = layer.meta; + void isRelevantToExtent; layer.meta = restMeta; } } @@ -647,11 +647,11 @@ class LayerManager { const layer = this.layers[i]; const intersectsExtent = !isEmpty(layer.rectangle) && Rectangle.intersection(extent, layer.rectangle) instanceof Rectangle; if (layer.meta?.skipRelevancyCheck === true) { - layer.meta = { ...layer.meta, relevantToExtent: true }; + layer.meta = { ...layer.meta, isRelevantToExtent: true }; continue; } if (!intersectsExtent) { - layer.meta = { ...(layer.meta ?? {}), relevantToExtent: false }; + layer.meta = { ...(layer.meta ?? {}), isRelevantToExtent: false }; continue; } let isOccludedByOpaqueLayerAbove = false; @@ -675,7 +675,7 @@ class LayerManager { // Layer is relevant if it intersects extent and has no opaque layer above it layer.meta = { ...(layer.meta ?? {}), - relevantToExtent: !isOccludedByOpaqueLayerAbove, + isRelevantToExtent: !isOccludedByOpaqueLayerAbove, }; } } catch (e) { From 9d60f09a45e777f857660e37fd4e035d8c255a8d Mon Sep 17 00:00:00 2001 From: ellamartirosyan Date: Mon, 22 Jun 2026 13:09:25 +0300 Subject: [PATCH 20/46] fix: base map internal --- .../active-layers/active-layers-panel.tsx | 5 +-- .../components/cesium-map/layers-manager.ts | 36 +++++++++---------- 2 files changed, 20 insertions(+), 21 deletions(-) diff --git a/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.tsx b/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.tsx index 10b5833d..191b5552 100644 --- a/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.tsx +++ b/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.tsx @@ -9,7 +9,8 @@ import { TRANSPARENT_LAYER_ID, getLayerId, isServiceLayer, - isManagedImageryLayer + isManagedImageryLayer, + isBaseMapLayer } from '../layers-manager'; import { useCesiumMap } from '../map'; @@ -85,7 +86,7 @@ export const ActiveLayersPanel: React.FC = ({ locale }) id: layerId as string, name: (get(meta, 'layerRecord.productName') ?? layerId) as string, rect: layer.rectangle, - isDisabled: mapViewer.layersManager?.isBaseMapLayer(meta) as boolean + isDisabled: isBaseMapLayer(meta as Record) }; }).filter((item): item is IActiveLayer => item !== undefined) : []; diff --git a/packages/react-components/src/components/cesium-map/layers-manager.ts b/packages/react-components/src/components/cesium-map/layers-manager.ts index 57d268eb..91555b06 100644 --- a/packages/react-components/src/components/cesium-map/layers-manager.ts +++ b/packages/react-components/src/components/cesium-map/layers-manager.ts @@ -73,6 +73,14 @@ export const isManagedImageryLayer = (layerId: string | undefined): boolean => { return !isServiceLayer(layerId); }; +export const getParentBaseMapId = (meta: Record | undefined): string | undefined => { + return get(meta, 'parentBaseMapId') as string | undefined; +}; + +export const isBaseMapLayer = (meta: Record | undefined): boolean => { + return !!getParentBaseMapId(meta); +}; + class LayerManager { public mapViewer: CesiumViewer; @@ -132,10 +140,6 @@ class LayerManager { return this.models; } - public isBaseMapLayer(meta: any): boolean { - return !!get(meta, 'parentBasetMapId'); - } - public addDataLayer(dataLayer: ICesiumWFSLayer): void { this.dataLayers.push({ ...dataLayer }); this.dataLayerUpdated.raiseEvent(this.dataLayers); @@ -229,7 +233,7 @@ class LayerManager { if (cesiumLayer) { cesiumLayer.alpha = layer.opacity; cesiumLayer.meta = { - parentBasetMapId: parentId, + parentBaseMapId: parentId, ...layer, }; if (layer.show !== undefined) { @@ -258,7 +262,7 @@ class LayerManager { public removeBaseMapLayers(): void { const layerToDelete = this.layers.filter((layer) => { - return this.isBaseMapLayer(layer.meta); + return isBaseMapLayer(layer.meta); }); layerToDelete.forEach((layer) => { this.mapViewer.imageryLayers.remove(layer, true); @@ -268,8 +272,7 @@ class LayerManager { public removeNotBaseMapLayers(): void { const layerToDelete = this.layers.filter((layer) => { - const parentId = get(layer.meta, 'parentBasetMapId') as string; - return parentId ? false : true; + return !isBaseMapLayer(layer.meta); }); layerToDelete.forEach((layer) => { this.mapViewer.imageryLayers.remove(layer, true); @@ -347,8 +350,7 @@ class LayerManager { public showAllNotBase(isShow: boolean): void { const nonBaseLayers = this.layers.filter((layer) => { - const parentId = get(layer.meta, 'parentBasetMapId') as string; - return parentId ? false : true; + return !isBaseMapLayer(layer.meta); }); nonBaseLayers.forEach((layer: ICesiumImageryLayer) => { this.show(layer.meta?.id as string, isShow); @@ -369,8 +371,7 @@ class LayerManager { if (pickRay) { nonBaseLayers = this.mapViewer.imageryLayers.pickImageryLayers(pickRay, this.mapViewer.scene)?.filter((layer: ICesiumImageryLayer) => { - const parentId = get(layer.meta, 'parentBasetMapId') as string; - return parentId ? false : true; + return !isBaseMapLayer(layer.meta); }); } @@ -382,8 +383,7 @@ class LayerManager { const position = pointToGeoJSON(this.mapViewer, x, y) as Feature; const nonBaseLayers = this.layers.filter((layer) => { - const parentId = get(layer.meta, 'parentBasetMapId') as string; - return parentId ? false : true; + return !isBaseMapLayer(layer.meta); }); const selectedVisibleLayers = nonBaseLayers.filter((layer) => { @@ -431,7 +431,7 @@ class LayerManager { (transparentLayer as ICesiumImageryLayer).meta = { id: TRANSPARENT_LAYER_ID, skipRelevancyCheck: true, - parentBasetMapId: 'TRANSPARENT_LAYER', + parentBaseMapId: 'TRANSPARENT_LAYER', }; } @@ -515,8 +515,7 @@ class LayerManager { private getBaseLayersCount(): number { const baseLayers = this.layers.filter((layer) => { - const parentId = get(layer.meta, 'parentBasetMapId') as string; - return parentId ? true : false; + return isBaseMapLayer(layer.meta); }); return baseLayers.length; } @@ -533,8 +532,7 @@ class LayerManager { const min = from < to ? from : to; const max = from < to ? to : from; this.layers.forEach((layer) => { - const parentId = get(layer.meta, 'parentBasetMapId') as string; - if (!parentId) { + if (!isBaseMapLayer(layer.meta)) { const layerOrder = layer.meta?.zIndex as number; (layer.meta as Record).zIndex = layerOrder >= min && layerOrder <= max && layerOrder !== from ? layerOrder + move : layerOrder === from ? to : layerOrder; From 067d3de3d01e5de523ce901cb710b48401c2dd87 Mon Sep 17 00:00:00 2001 From: ellamartirosyan Date: Mon, 22 Jun 2026 18:37:36 +0300 Subject: [PATCH 21/46] fix: encapsulate cesium internals --- .../active-layers/active-layers-panel.tsx | 12 +++++---- .../components/cesium-map/layers-manager.ts | 27 +++++++++++++++---- .../optimized-tile-requests.stories.tsx | 8 +++--- .../components/cesium-map/proxied.types.ts | 11 +++++--- .../cesium-map/tools/inspector.tool.tsx | 13 ++++----- 5 files changed, 46 insertions(+), 25 deletions(-) diff --git a/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.tsx b/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.tsx index 191b5552..1557ed61 100644 --- a/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.tsx +++ b/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.tsx @@ -5,12 +5,14 @@ import { Tooltip, Typography } from '@map-colonies/react-core'; import bbox from '@turf/bbox'; import { Box } from '../../box'; import { - ICesiumImageryLayer, - TRANSPARENT_LAYER_ID, + getImageryProvider, + getImageryProviderName, getLayerId, - isServiceLayer, + ICesiumImageryLayer, + isBaseMapLayer, isManagedImageryLayer, - isBaseMapLayer + isServiceLayer, + TRANSPARENT_LAYER_ID, } from '../layers-manager'; import { useCesiumMap } from '../map'; @@ -101,7 +103,7 @@ export const ActiveLayersPanel: React.FC = ({ locale }) return undefined; } const isTransparentLayer = layerId === TRANSPARENT_LAYER_ID; - const providerName = get(layer, 'imageryProvider.constructor.name') as string | undefined; + const providerName = getImageryProviderName(getImageryProvider(layer)); const name = isTransparentLayer ? TRANSPARENT_LAYER : `${SERVICE_LAYER} ${String(i + 1)}`; diff --git a/packages/react-components/src/components/cesium-map/layers-manager.ts b/packages/react-components/src/components/cesium-map/layers-manager.ts index 91555b06..812a3b9b 100644 --- a/packages/react-components/src/components/cesium-map/layers-manager.ts +++ b/packages/react-components/src/components/cesium-map/layers-manager.ts @@ -12,19 +12,24 @@ import { import { get, isEmpty } from 'lodash'; import { Feature, Point, Polygon } from 'geojson'; import booleanPointInPolygon from '@turf/boolean-point-in-polygon'; -import { RCesiumOSMLayerOptions, RCesiumWMSLayerOptions, RCesiumWMTSLayerOptions, RCesiumXYZLayerOptions } from './layers'; -import { CesiumViewer, IBaseMap } from './map'; -import { pointToGeoJSON } from './helpers/geojson/point.geojson'; -import { IMapLegend } from './legend'; import { CustomUrlTemplateImageryProvider, CustomWebMapServiceImageryProvider, CustomWebMapTileServiceImageryProvider, HAS_TRANSPARENCY_META_PROP, } from './helpers/customImageryProviders'; +import { pointToGeoJSON } from './helpers/geojson/point.geojson'; import { cesiumRectangleContained } from './helpers/utils'; +import { + RCesiumOSMLayerOptions, + RCesiumWMSLayerOptions, + RCesiumWMTSLayerOptions, + RCesiumXYZLayerOptions +} from './layers'; import { ICesiumWFSLayer } from './layers/wfs.layer'; -import { CesiumCartesian2 } from './proxied.types'; +import { IMapLegend } from './legend'; +import { CesiumViewer, IBaseMap } from './map'; +import { CesiumCartesian2, CesiumImageryProvider } from './proxied.types'; const INC = 1; const DEC = -1; @@ -81,6 +86,18 @@ export const isBaseMapLayer = (meta: Record | undefined): boole return !!getParentBaseMapId(meta); }; +export const getImageryProvider = (layer: ICesiumImageryLayer): CesiumImageryProvider => { + return get(layer, 'imageryProvider'); +}; + +export const getImageryProviderUrl = (layer: ICesiumImageryLayer): string | undefined => { + return get(layer, 'imageryProvider.url'); +}; + +export const getImageryProviderName = (provider: CesiumImageryProvider): string => { + return provider.constructor.name; +}; + class LayerManager { public mapViewer: CesiumViewer; diff --git a/packages/react-components/src/components/cesium-map/layers/optimized-tile-requests.stories.tsx b/packages/react-components/src/components/cesium-map/layers/optimized-tile-requests.stories.tsx index a8a094fe..4b45d773 100644 --- a/packages/react-components/src/components/cesium-map/layers/optimized-tile-requests.stories.tsx +++ b/packages/react-components/src/components/cesium-map/layers/optimized-tile-requests.stories.tsx @@ -1,9 +1,9 @@ import React, { ReactNode, useState } from 'react'; import { ImageryLayer, Rectangle } from 'cesium'; -import { get } from 'lodash'; import { Story, Meta } from '@storybook/react'; import bbox from '@turf/bbox'; import { BASE_MAPS } from '../helpers/constants'; +import { getImageryProviderUrl } from '../layers-manager'; import { CesiumMap, CesiumMapProps, IBaseMaps } from '../map'; import { CesiumXYZLayer } from './xyz.layer'; @@ -82,8 +82,7 @@ const LayersContainer: React.FC = () => { id: 'Transparent Layer', options: { ...optionsXYZTransparency }, searchLayerPredicate: (layer: ImageryLayer): boolean => - get(layer, 'imageryProvider.url') === optionsXYZTransparency.url || - get(layer, 'imageryProvider._url') === optionsXYZTransparency.url, + getImageryProviderUrl(layer) === optionsXYZTransparency.url }} rectangle={Rectangle.fromDegrees(...bbox(optionsXYZTransparency.footprint))} options={optionsXYZTransparency} @@ -102,8 +101,7 @@ const LayersContainer: React.FC = () => { id: 'Opaque Layer', options: { ...optionsXYZOpaque }, searchLayerPredicate: (layer: ImageryLayer): boolean => - get(layer, 'imageryProvider.url') === optionsXYZOpaque.url || - get(layer, 'imageryProvider._url') === optionsXYZOpaque.url, + getImageryProviderUrl(layer) === optionsXYZOpaque.url }} rectangle={Rectangle.fromDegrees(...bbox(optionsXYZOpaque.footprint))} options={optionsXYZOpaque} diff --git a/packages/react-components/src/components/cesium-map/proxied.types.ts b/packages/react-components/src/components/cesium-map/proxied.types.ts index 1a2857c5..601a3bea 100644 --- a/packages/react-components/src/components/cesium-map/proxied.types.ts +++ b/packages/react-components/src/components/cesium-map/proxied.types.ts @@ -5,6 +5,7 @@ import { Cartesian3, Cartographic, CesiumTerrainProvider, + Color, ConstantPositionProperty, ConstantProperty, Ellipsoid, @@ -12,18 +13,18 @@ import { GeographicTilingScheme, HeightReference, HorizontalOrigin, + ImageryProvider, JulianDate, LabelStyle, + PolygonHierarchy, PolylineDashMaterialProperty, PolylineGraphics, PositionProperty, Rectangle, Resource, - VerticalOrigin, - SceneMode, Scene, - Color, - PolygonHierarchy, + SceneMode, + VerticalOrigin, } from 'cesium'; // PROXIED CLASSES @@ -67,6 +68,8 @@ export class CesiumPolygonHierarchy extends PolygonHierarchy {} export class CesiumScene extends Scene {} +export class CesiumImageryProvider extends ImageryProvider {} + // PROXIED ENUMS // eslint-disable-next-line @typescript-eslint/naming-convention export const CesiumVerticalOrigin = VerticalOrigin; diff --git a/packages/react-components/src/components/cesium-map/tools/inspector.tool.tsx b/packages/react-components/src/components/cesium-map/tools/inspector.tool.tsx index 78a832f6..c1986f00 100644 --- a/packages/react-components/src/components/cesium-map/tools/inspector.tool.tsx +++ b/packages/react-components/src/components/cesium-map/tools/inspector.tool.tsx @@ -2,6 +2,7 @@ import React, { useEffect } from 'react'; import { viewerCesiumInspectorMixin, TileCoordinatesImageryProvider } from 'cesium'; import { Box } from '../../box'; import { CesiumViewer, useCesiumMap } from '../map'; +import { getImageryProvider, getImageryProviderName } from '../layers-manager'; interface ICesiumInspectorInstance { container?: HTMLElement; @@ -20,17 +21,17 @@ const applyInspectorContainerStyles = (container: HTMLElement): void => { }; const keepTileCoordinatesLayerOnTop = (viewer: CesiumViewer): void => { - const layers = viewer.imageryLayers; - const tileCoordinatesLayer = Array.from({ length: layers.length }, (_, index) => layers.get(index)).find((layer) => { - const provider = (layer as any).imageryProvider; - return provider instanceof TileCoordinatesImageryProvider || provider?.constructor?.name === 'TileCoordinatesImageryProvider'; + const layerList = viewer.layersManager?.layerList; + const tileCoordinatesLayer = layerList?.find((layer) => { + const provider = getImageryProvider(layer); + return provider instanceof TileCoordinatesImageryProvider || getImageryProviderName(provider) === 'TileCoordinatesImageryProvider'; }); if (tileCoordinatesLayer === undefined) { return; } - const topLayer = layers.get(layers.length - 1); + const topLayer = layerList?.[layerList.length - 1]; if (topLayer !== tileCoordinatesLayer) { - layers.raiseToTop(tileCoordinatesLayer); + viewer.imageryLayers.raiseToTop(tileCoordinatesLayer); } }; From d23d34a06dc403935064cf6f9a24a1ea67482edd Mon Sep 17 00:00:00 2001 From: ellamartirosyan Date: Tue, 23 Jun 2026 09:24:01 +0300 Subject: [PATCH 22/46] fix: when predicate fails meta.id is never attached so managed raster treated as service layer --- .../src/components/cesium-map/layers-manager.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-components/src/components/cesium-map/layers-manager.ts b/packages/react-components/src/components/cesium-map/layers-manager.ts index 812a3b9b..480690f0 100644 --- a/packages/react-components/src/components/cesium-map/layers-manager.ts +++ b/packages/react-components/src/components/cesium-map/layers-manager.ts @@ -91,7 +91,7 @@ export const getImageryProvider = (layer: ICesiumImageryLayer): CesiumImageryPro }; export const getImageryProviderUrl = (layer: ICesiumImageryLayer): string | undefined => { - return get(layer, 'imageryProvider.url'); + return get(layer, '_imageryProvider._resource._url'); }; export const getImageryProviderName = (provider: CesiumImageryProvider): string => { From f3d6493497216b0b9bf462df01d4479ec539da3f Mon Sep 17 00:00:00 2001 From: ellamartirosyan Date: Tue, 23 Jun 2026 12:23:52 +0300 Subject: [PATCH 23/46] fix: debugger --- .../cesium-map/debug/debugger-widget.tsx | 91 ++++++++++--------- 1 file changed, 48 insertions(+), 43 deletions(-) diff --git a/packages/react-components/src/components/cesium-map/debug/debugger-widget.tsx b/packages/react-components/src/components/cesium-map/debug/debugger-widget.tsx index ace5c8c9..73408383 100644 --- a/packages/react-components/src/components/cesium-map/debug/debugger-widget.tsx +++ b/packages/react-components/src/components/cesium-map/debug/debugger-widget.tsx @@ -1,10 +1,10 @@ -import React, { useEffect, useMemo, useState } from 'react'; -import { get, isEmpty } from 'lodash'; +import React, { useCallback, useEffect, useMemo, useState } from 'react'; +import { get } from 'lodash'; import { Checkbox, Tooltip } from '@map-colonies/react-core'; import { Box } from '../../box'; -import { EXAMINED_TILES_META_PROP } from '../helpers/customImageryProviders'; +import { EXAMINED_TILES_META_PROP, HAS_TRANSPARENCY_META_PROP } from '../helpers/customImageryProviders'; import { ICesiumWFSLayer } from '../layers/wfs.layer'; -import { TRANSPARENT_LAYER_ID } from '../layers-manager'; +import { getLayerId, isManagedImageryLayer } from '../layers-manager'; import { useCesiumMap, useCesiumMapViewstate } from '../map'; import { CesiumIcon } from '../widget/cesium-icon'; import { CesiumTool } from '../widget/cesium-tool'; @@ -30,16 +30,24 @@ export interface IDebuggerWidgetProps extends IWidgetProps { locale?: { [key: string]: string }; } -interface LayerMetaItem { - layerId?: string; - meta?: Record; +interface LayerDebugMeta extends Record { + id?: string; + layerRecord?: { + productName?: string; + }; + isRelevantToExtent?: boolean; +} + +interface LayerDebugItem { + layerId: string; + meta: LayerDebugMeta; } type DebuggerSectionId = 'data' | 'layers' | 'tools'; const DebuggerComponent: React.FC = ({ locale, isOpen, setIsOpen }) => { const [featureTypes, setFeatureTypes] = useState([]); - const [layersMeta, setLayersMeta] = useState([]); + const [layersMeta, setLayersMeta] = useState([]); const [collapsedSections, setCollapsedSections] = useState>({ data: false, layers: false, @@ -64,19 +72,22 @@ const DebuggerComponent: React.FC = ({ locale, isOpen, set })); }; - const updateLayerMeta = (): void => { - if (!mapViewer.layersManager?.layerList) return; - setLayersMeta( - mapViewer.layersManager.layerList - .filter((layer): boolean => !isEmpty(layer.meta?.id) && layer.meta?.id !== TRANSPARENT_LAYER_ID) - .map( - (layer): LayerMetaItem => ({ - layerId: layer.meta?.id as string | undefined, - meta: layer.meta as Record | undefined, - }) - ) - ); - }; + const updateLayersMeta = useCallback((): void => { + if (!mapViewer.layersManager?.layerList) { return; } + const nextLayersMeta = mapViewer.layersManager.layerList + .map((layer): LayerDebugItem | undefined => { + const layerId = getLayerId(layer); + if (layerId === undefined || !isManagedImageryLayer(layerId)) { + return undefined; + } + return { + layerId, + meta: (layer.meta ?? {}) as LayerDebugMeta, + }; + }) + .filter((item): item is LayerDebugItem => item !== undefined); + setLayersMeta(nextLayersMeta); + }, [mapViewer.layersManager]); useEffect(() => { let moveEndRefreshTimeoutId: ReturnType | undefined; @@ -85,13 +96,13 @@ const DebuggerComponent: React.FC = ({ locale, isOpen, set clearTimeout(moveEndRefreshTimeoutId); } moveEndRefreshTimeoutId = setTimeout(() => { - updateLayerMeta(); + updateLayersMeta(); }, 0); }; const removeTileLoad = mapViewer.scene.globe.tileLoadProgressEvent.addEventListener((tilesLoadingCount) => { if (tilesLoadingCount === 0) { - updateLayerMeta(); + updateLayersMeta(); removeTileLoad(); } }); @@ -101,7 +112,7 @@ const DebuggerComponent: React.FC = ({ locale, isOpen, set const removeLayerRemoved = mapViewer.imageryLayers.layerRemoved.addEventListener(() => { scheduleLayerMetaRefresh(); }); - mapViewer.layersManager?.addLayerUpdatedListener(updateLayerMeta); + mapViewer.layersManager?.addLayerUpdatedListener(updateLayersMeta); return (): void => { if (moveEndRefreshTimeoutId !== undefined) { clearTimeout(moveEndRefreshTimeoutId); @@ -109,27 +120,24 @@ const DebuggerComponent: React.FC = ({ locale, isOpen, set removeTileLoad(); removeMoveEnd(); removeLayerRemoved(); - mapViewer.layersManager?.removeLayerUpdatedListener(updateLayerMeta); + mapViewer.layersManager?.removeLayerUpdatedListener(updateLayersMeta); }; - }, []); + }, [mapViewer, updateLayersMeta]); useEffect(() => { - updateLayerMeta(); - }, [viewState?.shouldOptimizedTileRequests]); + updateLayersMeta(); + }, [updateLayersMeta, viewState?.shouldOptimizedTileRequests]); useEffect(() => { - if (!mapViewer.layersManager) return; - - const handleDataLayerUpdated = (dataLayers: ICesiumWFSLayer[], LayerId?: string | undefined): void => { + if (!mapViewer.layersManager) { return; } + const handleDataLayerUpdated = (dataLayers: ICesiumWFSLayer[], layerId?: string | undefined): void => { dataLayers.forEach((layer: ICesiumWFSLayer): void => { - if (LayerId !== undefined && LayerId !== layer.meta.id) { + if (layerId !== undefined && layerId !== layer.meta.id) { return; } - const { options, meta } = layer; const { zoomLevel } = options; const { id, items, total, cache, currentZoomLevel, featureStructure } = meta as unknown as IFeatureTypeMetadata; - setFeatureTypes((prevFeatureTypes) => { const existingIndex = prevFeatureTypes.findIndex((type) => type.id === id); if (existingIndex >= 0) { @@ -147,14 +155,10 @@ const DebuggerComponent: React.FC = ({ locale, isOpen, set return prevFeatureTypes; }); }); - const activeDataLayerIds = new Set(mapViewer.layersManager?.dataLayerList.map((layer) => layer.meta.id)); - setFeatureTypes((prevFeatureTypes) => prevFeatureTypes.filter((type) => activeDataLayerIds.has(type.id))); }; - mapViewer.layersManager.addDataLayerUpdatedListener(handleDataLayerUpdated); - return () => { mapViewer.layersManager?.removeDataLayerUpdatedListener(handleDataLayerUpdated); }; @@ -212,14 +216,15 @@ const DebuggerComponent: React.FC = ({ locale, isOpen, set /> {viewState?.shouldOptimizedTileRequests === true && ( - {[...layersMeta].reverse().map((layer, index) => { - const idText = layer.layerId ?? `LAYER-${layersMeta.length - index}`; - const nameText = (get(layer.meta, 'layerRecord.productName') as string | undefined) ?? idText; + {[...layersMeta].reverse().map((layer) => { + const idText = layer.layerId; + const nameText = layer.meta.layerRecord?.productName ?? idText; const statusText = layer.meta?.isRelevantToExtent === true ? ' → show' : layer.meta?.isRelevantToExtent === false ? ' → hide' : ''; + const hasTransparency = layer.meta[HAS_TRANSPARENCY_META_PROP] as boolean | undefined; const transparencyText = - layer.meta?.hasTransparency === true ? withTransparencyTiles : layer.meta?.hasTransparency === false ? withoutTransparencyTiles : ''; - const tileCoordinatesFromMeta = get(layer.meta, EXAMINED_TILES_META_PROP) as + hasTransparency === true ? withTransparencyTiles : hasTransparency === false ? withoutTransparencyTiles : ''; + const tileCoordinatesFromMeta = layer.meta[EXAMINED_TILES_META_PROP] as | Array<{ x?: number; y?: number; level?: number }> | { x?: number; y?: number; level?: number } | undefined; From 78abd0f56fbb8b41dd81d75389af796204d1a8e9 Mon Sep 17 00:00:00 2001 From: ellamartirosyan Date: Tue, 23 Jun 2026 12:52:28 +0300 Subject: [PATCH 24/46] fix: reuse --- .../active-layers/active-layers-panel.tsx | 4 +- .../cesium-map/context-menu.stories.tsx | 38 +++++++++---------- .../cesium-map/debug/debugger-widget.tsx | 8 ++-- .../components/cesium-map/layers-manager.ts | 20 +++++----- 4 files changed, 35 insertions(+), 35 deletions(-) diff --git a/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.tsx b/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.tsx index 1557ed61..cdd9465e 100644 --- a/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.tsx +++ b/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.tsx @@ -121,7 +121,7 @@ export const ActiveLayersPanel: React.FC = ({ locale }) const getDataLayers = (): IActiveLayer[] => { return mapViewer.layersManager?.dataLayerList.map((dataLayer) => { return { - id: dataLayer.meta?.id as string, + id: getLayerId(dataLayer) as string, name: (get(dataLayer.meta, 'featureStructure.aliasLayerName') ?? dataLayer.meta.productName) as string, rect: Rectangle.fromDegrees(...bbox(dataLayer.meta?.footprint)), isDisabled: false @@ -133,7 +133,7 @@ export const ActiveLayersPanel: React.FC = ({ locale }) const modelUrl = get(model.tileset, 'resource.url') as string | undefined; const modelName = (get(model.meta, 'layerRecord.productName') ?? extractModelName(modelUrl ?? `Model #${String(index + 1)}`)) as string; return { - id: (model.meta.id as string) ?? `3D_MODEL_${String(index)}`, + id: (getLayerId(model) as string) ?? `3D_MODEL_${String(index)}`, name: modelName, zoomToTarget: model.tileset, isDisabled: false, diff --git a/packages/react-components/src/components/cesium-map/context-menu.stories.tsx b/packages/react-components/src/components/cesium-map/context-menu.stories.tsx index a549f9d5..50a50bfc 100644 --- a/packages/react-components/src/components/cesium-map/context-menu.stories.tsx +++ b/packages/react-components/src/components/cesium-map/context-menu.stories.tsx @@ -1,9 +1,10 @@ import React, { useEffect, useState } from 'react'; +import { get } from 'lodash'; import { Menu, MenuItem, MenuSurfaceAnchor } from '@map-colonies/react-core'; import { Story, Meta } from '@storybook/react'; import { Box } from '../box'; import { BASE_MAPS } from './helpers/constants'; -import { ICesiumImageryLayer, IRasterLayer } from './layers-manager'; +import { getLayerId, ICesiumImageryLayer, IRasterLayer } from './layers-manager'; import { CesiumMap, IBaseMaps, IContextMenuData, useCesiumMap } from './map'; import { CesiumCartesian2, CesiumSceneMode } from './proxied.types'; @@ -19,21 +20,26 @@ interface ILayersMozaikProps { layers: IRasterLayer[]; } +const getDebugLayerText = (layer: unknown): string => { + const imageryLayer = layer as ICesiumImageryLayer; + const layerId = getLayerId(imageryLayer) ?? 'UNKNOWN_LAYER_ID'; + const zIndex = get(layer, 'meta.zIndex') ?? 'NA'; + return `${layerId} <--> ${String(zIndex)}`; +}; + const mapDivStyle = { height: '90%', width: '100%', position: 'absolute' as const, }; -const layers = [ +const layers: IRasterLayer[] = [ { id: 'near_amphy', type: 'XYZ_LAYER', + zIndex: 0, opacity: 1, show: true, - meta: { - zIndex: 0, - }, options: { url: 'https://tiles.openaerialmap.org/5a9f90c42553e6000ce5ad6c/0/eee1a570-128e-4947-9ffa-1e69c1efab7c/{z}/{x}/{y}.png', }, @@ -55,11 +61,9 @@ const layers = [ { id: 'coin_zoom_17', type: 'XYZ_LAYER', + zIndex: 1, opacity: 1, show: true, - meta: { - zIndex: 1, - }, options: { url: 'https://tiles.openaerialmap.org/5a8316e22553e6000ce5ac7f/0/c3fcbe99-d339-41b6-8ec0-33d90ccca020/{z}/{x}/{y}.png', }, @@ -81,11 +85,9 @@ const layers = [ { id: 'biggest', type: 'XYZ_LAYER', + zIndex: 2, opacity: 1, show: true, - meta: { - zIndex: 2, - }, options: { url: 'https://tiles.openaerialmap.org/5a831b4a2553e6000ce5ac80/0/d02ddc76-9c2e-4994-97d4-a623eb371456/{z}/{x}/{y}.png', }, @@ -106,7 +108,7 @@ const layers = [ }, ]; -const ContextMenu: React.FC = ({ data, position, style, size, handleClose }) => { +const ContextMenu: React.FC = ({ data, position, style, handleClose }) => { const mapViewer = useCesiumMap(); const [pickedLayers, setPickedLayers] = useState(); @@ -142,9 +144,7 @@ const ContextMenu: React.FC = ({ data, position, style, size,
{data?.map((layer) => { return ( -

{`${(layer as unknown as Record).meta?.id} <--> ${ - (layer as unknown as Record).meta?.meta?.zIndex - }`}

+

{getDebugLayerText(layer)}

); })}
@@ -156,14 +156,12 @@ const ContextMenu: React.FC = ({ data, position, style, size,
{pickedLayers?.map((layer) => { return ( -

{`${(layer as unknown as Record).meta?.id} <--> ${ - (layer as unknown as Record).meta?.meta?.zIndex - }`}

+

{getDebugLayerText(layer)}

); })}
- handleClose()} style={{ visibility: 'hidden', width: '100%' }}> + handleClose()} style={{ visibility: 'hidden', width: '100%' }}> @@ -183,7 +181,7 @@ const ContextMenu: React.FC = ({ data, position, style, size, >

No data found

- handleClose()} style={{ visibility: 'hidden', width: '100%' }}> + handleClose()} style={{ visibility: 'hidden', width: '100%' }}> diff --git a/packages/react-components/src/components/cesium-map/debug/debugger-widget.tsx b/packages/react-components/src/components/cesium-map/debug/debugger-widget.tsx index 73408383..a52d827b 100644 --- a/packages/react-components/src/components/cesium-map/debug/debugger-widget.tsx +++ b/packages/react-components/src/components/cesium-map/debug/debugger-widget.tsx @@ -77,11 +77,11 @@ const DebuggerComponent: React.FC = ({ locale, isOpen, set const nextLayersMeta = mapViewer.layersManager.layerList .map((layer): LayerDebugItem | undefined => { const layerId = getLayerId(layer); - if (layerId === undefined || !isManagedImageryLayer(layerId)) { + if (!isManagedImageryLayer(layerId)) { return undefined; } return { - layerId, + layerId: layerId as string, meta: (layer.meta ?? {}) as LayerDebugMeta, }; }) @@ -132,7 +132,7 @@ const DebuggerComponent: React.FC = ({ locale, isOpen, set if (!mapViewer.layersManager) { return; } const handleDataLayerUpdated = (dataLayers: ICesiumWFSLayer[], layerId?: string | undefined): void => { dataLayers.forEach((layer: ICesiumWFSLayer): void => { - if (layerId !== undefined && layerId !== layer.meta.id) { + if (layerId !== undefined && layerId !== getLayerId(layer)) { return; } const { options, meta } = layer; @@ -155,7 +155,7 @@ const DebuggerComponent: React.FC = ({ locale, isOpen, set return prevFeatureTypes; }); }); - const activeDataLayerIds = new Set(mapViewer.layersManager?.dataLayerList.map((layer) => layer.meta.id)); + const activeDataLayerIds = new Set(mapViewer.layersManager?.dataLayerList.map((layer) => getLayerId(layer))); setFeatureTypes((prevFeatureTypes) => prevFeatureTypes.filter((type) => activeDataLayerIds.has(type.id))); }; mapViewer.layersManager.addDataLayerUpdatedListener(handleDataLayerUpdated); diff --git a/packages/react-components/src/components/cesium-map/layers-manager.ts b/packages/react-components/src/components/cesium-map/layers-manager.ts index 480690f0..a66f17c7 100644 --- a/packages/react-components/src/components/cesium-map/layers-manager.ts +++ b/packages/react-components/src/components/cesium-map/layers-manager.ts @@ -66,7 +66,7 @@ export type LegendExtractor = (layers: (any & { meta: any })[]) => IMapLegend[]; export const TRANSPARENT_LAYER_ID = 'TRANSPARENT_BASE_LAYER'; -export const getLayerId = (layer: ICesiumImageryLayer): string | undefined => { +export const getLayerId = (layer: ICesiumImageryLayer | ICesiumWFSLayer | ICesium3DModel): string | undefined => { return get(layer, 'meta.id') as string | undefined; }; @@ -370,7 +370,10 @@ class LayerManager { return !isBaseMapLayer(layer.meta); }); nonBaseLayers.forEach((layer: ICesiumImageryLayer) => { - this.show(layer.meta?.id as string, isShow); + const layerId = getLayerId(layer); + if (layerId !== undefined) { + this.show(layerId, isShow); + } }); } @@ -500,7 +503,7 @@ class LayerManager { public findDataLayerById(dataLayerId: string): ICesiumWFSLayer | undefined { return this.dataLayers.find((dataLayer) => { - return dataLayer.meta.id === dataLayerId; + return getLayerId(dataLayer) === dataLayerId; }); } @@ -521,7 +524,7 @@ class LayerManager { } public findModelById(modelId: string): ICesium3DModel | undefined { - return this.models.find((model) => model.meta.id === modelId); + return this.models.find((model) => getLayerId(model) === modelId); } private setLegends(): void { @@ -539,8 +542,7 @@ class LayerManager { private findLayerById(layerId: string): ICesiumImageryLayer | undefined { return this.layers.find((layer) => { - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - return layer.meta !== undefined ? layer.meta.id === layerId : false; + return getLayerId(layer) === layerId; }); } @@ -559,7 +561,7 @@ class LayerManager { private hideNonRelevantLayers(): void { for (const layer of this.layers) { - if (layer.meta?.id === TRANSPARENT_LAYER_ID) { + if (getLayerId(layer) === TRANSPARENT_LAYER_ID) { continue; } const isRelevantToExtent = layer.meta?.isRelevantToExtent; @@ -574,7 +576,7 @@ class LayerManager { private restoreAllLayersVisibility(): void { for (const layer of this.layers) { - if (layer.meta?.id === TRANSPARENT_LAYER_ID) { + if (getLayerId(layer) === TRANSPARENT_LAYER_ID) { continue; } if (layer.imageryProvider.ready) { @@ -585,7 +587,7 @@ class LayerManager { private clearLayersRelevancy(): void { for (const layer of this.layers) { - if (layer.meta?.id === TRANSPARENT_LAYER_ID) { + if (getLayerId(layer) === TRANSPARENT_LAYER_ID) { continue; } if (layer.meta && 'isRelevantToExtent' in layer.meta) { From cddbe4d8ac66924b0138bb780894c680f73b45e7 Mon Sep 17 00:00:00 2001 From: ellamartirosyan Date: Tue, 23 Jun 2026 13:11:47 +0300 Subject: [PATCH 25/46] fix: reuse --- .../cesium-map/helpers/customImageryProviders.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/react-components/src/components/cesium-map/helpers/customImageryProviders.ts b/packages/react-components/src/components/cesium-map/helpers/customImageryProviders.ts index 8642fc70..af190c8d 100644 --- a/packages/react-components/src/components/cesium-map/helpers/customImageryProviders.ts +++ b/packages/react-components/src/components/cesium-map/helpers/customImageryProviders.ts @@ -8,7 +8,7 @@ import { ImageryTypes, } from 'cesium'; import { get } from 'lodash'; -import { ICesiumImageryLayer } from '../layers-manager'; +import { getImageryProviderUrl, ICesiumImageryLayer } from '../layers-manager'; import { CesiumViewer } from '../map'; import { imageHasTransparency } from './utils'; @@ -43,7 +43,7 @@ function customCommonRequestImage( const requestedLayerMeta = this.layerListInstance.find( /* eslint-disable */ (layer: ImageryLayer): boolean => { - return (layer as any)._imageryProvider._resource?._url === (this as any)._resource?._url; + return getImageryProviderUrl(layer) === (this as any)._resource?._url; } /* eslint-enable */ )?.meta; @@ -58,7 +58,7 @@ function customCommonRequestImage( { [EXAMINED_TILES_META_PROP]: this.examinedTilesForTransparencyCheck }, /* eslint-disable */ (layer: ImageryLayer): boolean => { - return (layer as any)._imageryProvider._resource._url === (this as any)._resource._url; + return getImageryProviderUrl(layer) === (this as any)._resource._url; } /* eslint-enable */ ); @@ -67,7 +67,7 @@ function customCommonRequestImage( { [HAS_TRANSPARENCY_META_PROP]: hasTransparency }, /* eslint-disable */ (layer: ImageryLayer): boolean => { - return (layer as any)._imageryProvider._resource._url === (this as any)._resource._url; + return getImageryProviderUrl(layer) === (this as any)._resource._url; } /* eslint-enable */ ); From c5e3419f1706a68702920e02f32357c022c2735b Mon Sep 17 00:00:00 2001 From: ellamartirosyan Date: Tue, 23 Jun 2026 15:20:59 +0300 Subject: [PATCH 26/46] fix: data layer layerRecord --- .../active-layers/active-layers-panel.tsx | 2 +- .../cesium-map/debug/debugger-widget.tsx | 10 +- .../cesium-map/layers/wfs.layer.stories.tsx | 322 +++++++++--------- .../cesium-map/layers/wfs.layer.tsx | 2 +- 4 files changed, 171 insertions(+), 165 deletions(-) diff --git a/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.tsx b/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.tsx index cdd9465e..22bec3f9 100644 --- a/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.tsx +++ b/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.tsx @@ -122,7 +122,7 @@ export const ActiveLayersPanel: React.FC = ({ locale }) return mapViewer.layersManager?.dataLayerList.map((dataLayer) => { return { id: getLayerId(dataLayer) as string, - name: (get(dataLayer.meta, 'featureStructure.aliasLayerName') ?? dataLayer.meta.productName) as string, + name: (get(dataLayer.meta, 'layerRecord.featureStructure.aliasLayerName') ?? get(dataLayer.meta, 'layerRecord.productName')) as string, rect: Rectangle.fromDegrees(...bbox(dataLayer.meta?.footprint)), isDisabled: false }; }) || []; diff --git a/packages/react-components/src/components/cesium-map/debug/debugger-widget.tsx b/packages/react-components/src/components/cesium-map/debug/debugger-widget.tsx index a52d827b..ea8286e7 100644 --- a/packages/react-components/src/components/cesium-map/debug/debugger-widget.tsx +++ b/packages/react-components/src/components/cesium-map/debug/debugger-widget.tsx @@ -19,7 +19,7 @@ interface IFeatureTypeMetadata { total: number; cache: number; currentZoomLevel: number; - featureStructure: Record; + layerRecord: Record; } export type IActiveFeatureTypes = IFeatureTypeMetadata & { @@ -137,20 +137,20 @@ const DebuggerComponent: React.FC = ({ locale, isOpen, set } const { options, meta } = layer; const { zoomLevel } = options; - const { id, items, total, cache, currentZoomLevel, featureStructure } = meta as unknown as IFeatureTypeMetadata; + const { id, items, total, cache, currentZoomLevel, layerRecord } = meta as unknown as IFeatureTypeMetadata; setFeatureTypes((prevFeatureTypes) => { const existingIndex = prevFeatureTypes.findIndex((type) => type.id === id); if (existingIndex >= 0) { if ( JSON.stringify(prevFeatureTypes[existingIndex]) !== - JSON.stringify({ id, items, total, cache, currentZoomLevel, featureStructure, zoomLevel }) + JSON.stringify({ id, items, total, cache, currentZoomLevel, layerRecord, zoomLevel }) ) { const updatedFeatureTypes = [...prevFeatureTypes]; - updatedFeatureTypes[existingIndex] = { id, items, total, cache, currentZoomLevel, featureStructure, zoomLevel }; + updatedFeatureTypes[existingIndex] = { id, items, total, cache, currentZoomLevel, layerRecord, zoomLevel }; return updatedFeatureTypes; } } else { - return [...prevFeatureTypes, { id, items, total, cache, currentZoomLevel, featureStructure, zoomLevel }]; + return [...prevFeatureTypes, { id, items, total, cache, currentZoomLevel, layerRecord, zoomLevel }]; } return prevFeatureTypes; }); diff --git a/packages/react-components/src/components/cesium-map/layers/wfs.layer.stories.tsx b/packages/react-components/src/components/cesium-map/layers/wfs.layer.stories.tsx index 135ef74a..8e239702 100644 --- a/packages/react-components/src/components/cesium-map/layers/wfs.layer.stories.tsx +++ b/packages/react-components/src/components/cesium-map/layers/wfs.layer.stories.tsx @@ -1,4 +1,4 @@ -import { useRef, useState } from 'react'; +import { useState } from 'react'; import { BBox } from 'geojson'; import area from '@turf/area'; import intersect from '@turf/intersect'; @@ -491,112 +491,115 @@ const optionsPolygonParts = { const metaPolygonParts = { id: '00000000', - keywords: 'PolygonParts', - links: 'cyprus,,WFS,https://raster-pp-serving', // RASTER PP - type: 'RECORD_VECTOR', - classification: '5', - productName: 'בדיקות עדכון', - description: 'PolygonParts layer', - srsId: '4326', - srsName: '4326', - producerName: 'IDFMU', - footprint: {"type":"Polygon","coordinates":[[[-180,-90],[180,-90],[180,90],[-180,90],[-180,-90]]]}, - productType: 'VECTOR_BEST', - featureStructure: { - layerName: 'polygonParts:layer', - aliasLayerName: 'בדיקות עדכון', - fields: [ - { - fieldName: 'id', - aliasFieldName: 'id', - type: 'String', - }, - { - fieldName: 'catalogId', - aliasFieldName: 'catalogId', - type: 'String', - }, - { - fieldName: 'productId', - aliasFieldName: 'productId', - type: 'String', - }, - { - fieldName: 'productType', - aliasFieldName: 'productType', - type: 'String', - }, - { - fieldName: 'sourceId', - aliasFieldName: 'sourceId', - type: 'String', - }, - { - fieldName: 'sourceName', - aliasFieldName: 'sourceName', - type: 'String', - }, - { - fieldName: 'productVersion', - aliasFieldName: 'productVersion', - type: 'String', - }, - { - fieldName: 'ingestionDateUTC', - aliasFieldName: 'ingestionDateUTC', - type: 'Date', - }, - { - fieldName: 'imagingTimeBeginUTC', - aliasFieldName: 'imagingTimeBeginUTC', - type: 'Date', - }, - { - fieldName: 'imagingTimeEndUTC', - aliasFieldName: 'imagingTimeEndUTC', - type: 'Date', - }, - { - fieldName: 'resolutionDegree', - aliasFieldName: 'resolutionDegree', - type: 'Number', - }, - { - fieldName: 'resolutionMeter', - aliasFieldName: 'resolutionMeter', - type: 'Number', - }, - { - fieldName: 'sourceResolutionMeter', - aliasFieldName: 'sourceResolutionMeter', - type: 'Number', - }, - { - fieldName: 'horizontalAccuracyCe90', - aliasFieldName: 'horizontalAccuracyCe90', - type: 'Number', - }, - { - fieldName: 'sensors', - aliasFieldName: 'sensors', - type: 'String', - }, - { - fieldName: 'countries', - aliasFieldName: 'countries', - type: 'String', - }, - { - fieldName: 'cities', - aliasFieldName: 'cities', - type: 'String', - }, - { - fieldName: '_description', - aliasFieldName: 'description', - type: 'String', - }, - ], + layerRecord: { + id: '00000000', + keywords: 'PolygonParts', + links: 'cyprus,,WFS,https://raster-pp-serving', // RASTER PP + type: 'RECORD_VECTOR', + classification: '5', + productName: 'בדיקות עדכון', + description: 'PolygonParts layer', + srsId: '4326', + srsName: '4326', + producerName: 'IDFMU', + footprint: {"type":"Polygon","coordinates":[[[-180,-90],[180,-90],[180,90],[-180,90],[-180,-90]]]}, + productType: 'VECTOR_BEST', + featureStructure: { + layerName: 'polygonParts:layer', + aliasLayerName: 'בדיקות עדכון', + fields: [ + { + fieldName: 'id', + aliasFieldName: 'id', + type: 'String', + }, + { + fieldName: 'catalogId', + aliasFieldName: 'catalogId', + type: 'String', + }, + { + fieldName: 'productId', + aliasFieldName: 'productId', + type: 'String', + }, + { + fieldName: 'productType', + aliasFieldName: 'productType', + type: 'String', + }, + { + fieldName: 'sourceId', + aliasFieldName: 'sourceId', + type: 'String', + }, + { + fieldName: 'sourceName', + aliasFieldName: 'sourceName', + type: 'String', + }, + { + fieldName: 'productVersion', + aliasFieldName: 'productVersion', + type: 'String', + }, + { + fieldName: 'ingestionDateUTC', + aliasFieldName: 'ingestionDateUTC', + type: 'Date', + }, + { + fieldName: 'imagingTimeBeginUTC', + aliasFieldName: 'imagingTimeBeginUTC', + type: 'Date', + }, + { + fieldName: 'imagingTimeEndUTC', + aliasFieldName: 'imagingTimeEndUTC', + type: 'Date', + }, + { + fieldName: 'resolutionDegree', + aliasFieldName: 'resolutionDegree', + type: 'Number', + }, + { + fieldName: 'resolutionMeter', + aliasFieldName: 'resolutionMeter', + type: 'Number', + }, + { + fieldName: 'sourceResolutionMeter', + aliasFieldName: 'sourceResolutionMeter', + type: 'Number', + }, + { + fieldName: 'horizontalAccuracyCe90', + aliasFieldName: 'horizontalAccuracyCe90', + type: 'Number', + }, + { + fieldName: 'sensors', + aliasFieldName: 'sensors', + type: 'String', + }, + { + fieldName: 'countries', + aliasFieldName: 'countries', + type: 'String', + }, + { + fieldName: 'cities', + aliasFieldName: 'cities', + type: 'String', + }, + { + fieldName: '_description', + aliasFieldName: 'description', + type: 'String', + }, + ], + }, }, }; @@ -873,57 +876,60 @@ const optionsBuildings = { const metaBuildings = { id: '1111111', - keywords: 'buildings, osm', - links: 'buildings,,WFS,http://geoserver-vector', - type: 'RECORD_VECTOR', - classification: '5', - productName: 'מבנים', - description: 'Buildings layer', - srsId: '4326', - srsName: '4326', - producerName: 'Moria', - footprint: {"type":"Polygon","coordinates":[[[-180,-90],[180,-90],[180,90],[-180,90],[-180,-90]]]}, - productType: 'VECTOR_BEST', - featureStructure: { - layerName: 'buildings', - aliasLayerName: 'מבנים', - fields: [ - { - fieldName: 'osm_id', - aliasFieldName: 'מזהה OSM', - type: 'String', - }, - { - fieldName: 'id', - aliasFieldName: 'מזהה', - type: 'String', - }, - { - fieldName: 'building_type', - aliasFieldName: 'סוג', - type: 'String', - }, - { - fieldName: 'sensitivity', - aliasFieldName: 'רגישות', - type: 'String', - }, - { - fieldName: 'entity_id', - aliasFieldName: 'מזהה יישות', - type: 'String', - }, - { - fieldName: 'is_sensitive', - aliasFieldName: 'רגיש', - type: 'Boolean', - }, - { - fieldName: 'date', - aliasFieldName: 'תאריך', - type: 'Date', - }, - ], + layerRecord: { + id: '1111111', + keywords: 'buildings, osm', + links: 'buildings,,WFS,http://geoserver-vector', + type: 'RECORD_VECTOR', + classification: '5', + productName: 'מבנים', + description: 'Buildings layer', + srsId: '4326', + srsName: '4326', + producerName: 'Moria', + footprint: {"type":"Polygon","coordinates":[[[-180,-90],[180,-90],[180,90],[-180,90],[-180,-90]]]}, + productType: 'VECTOR_BEST', + featureStructure: { + layerName: 'buildings', + aliasLayerName: 'מבנים', + fields: [ + { + fieldName: 'osm_id', + aliasFieldName: 'מזהה OSM', + type: 'String', + }, + { + fieldName: 'id', + aliasFieldName: 'מזהה', + type: 'String', + }, + { + fieldName: 'building_type', + aliasFieldName: 'סוג', + type: 'String', + }, + { + fieldName: 'sensitivity', + aliasFieldName: 'רגישות', + type: 'String', + }, + { + fieldName: 'entity_id', + aliasFieldName: 'מזהה יישות', + type: 'String', + }, + { + fieldName: 'is_sensitive', + aliasFieldName: 'רגיש', + type: 'Boolean', + }, + { + fieldName: 'date', + aliasFieldName: 'תאריך', + type: 'Date', + }, + ], + }, }, }; diff --git a/packages/react-components/src/components/cesium-map/layers/wfs.layer.tsx b/packages/react-components/src/components/cesium-map/layers/wfs.layer.tsx index 01210caf..1d36f790 100644 --- a/packages/react-components/src/components/cesium-map/layers/wfs.layer.tsx +++ b/packages/react-components/src/components/cesium-map/layers/wfs.layer.tsx @@ -130,7 +130,7 @@ export const CesiumWFSLayer: React.FC = (props) => { const describe = (properties: Record): string => { const rows: string[] = []; - const featureStructure = meta.featureStructure as { fields: { fieldName: string; aliasFieldName: string; type: string }[] }; + const featureStructure = (meta.layerRecord as any)?.featureStructure as { fields: { fieldName: string; aliasFieldName: string; type: string }[] }; if (featureStructure && featureStructure.fields) { for (const field of featureStructure.fields) { const { fieldName, aliasFieldName } = field; From 52768380f127ebb41d7b3c79fab032f851133634 Mon Sep 17 00:00:00 2001 From: MARTIROSYAN ELLA Date: Tue, 23 Jun 2026 19:33:15 +0300 Subject: [PATCH 27/46] fix: featureStructure --- .../cesium-map/active-layers/active-layers-panel.tsx | 2 +- .../src/components/cesium-map/debug/wfs.tsx | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.tsx b/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.tsx index 22bec3f9..cc44f5fb 100644 --- a/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.tsx +++ b/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.tsx @@ -123,7 +123,7 @@ export const ActiveLayersPanel: React.FC = ({ locale }) return { id: getLayerId(dataLayer) as string, name: (get(dataLayer.meta, 'layerRecord.featureStructure.aliasLayerName') ?? get(dataLayer.meta, 'layerRecord.productName')) as string, - rect: Rectangle.fromDegrees(...bbox(dataLayer.meta?.footprint)), + rect: Rectangle.fromDegrees(...bbox((dataLayer.meta?.layerRecord as Record)?.footprint)), isDisabled: false }; }) || []; }; diff --git a/packages/react-components/src/components/cesium-map/debug/wfs.tsx b/packages/react-components/src/components/cesium-map/debug/wfs.tsx index d9fd4946..88bb8f31 100644 --- a/packages/react-components/src/components/cesium-map/debug/wfs.tsx +++ b/packages/react-components/src/components/cesium-map/debug/wfs.tsx @@ -27,9 +27,11 @@ export const WFS: React.FC = ({ featureTypes, locale }) => { {featureTypes.length > 0 ? ( featureTypes.map((type, index) => ( - + - {type.featureStructure.aliasLayerName as string} ({String(type.zoomLevel)}): + {(get(type, 'layerRecord.featureStructure.aliasLayerName') ?? '') as string} ({String(type.zoomLevel)}): From 2c6c4d3433f116639ad636ec1c9e7f0fc1e661ee Mon Sep 17 00:00:00 2001 From: MARTIROSYAN ELLA Date: Tue, 23 Jun 2026 20:00:11 +0300 Subject: [PATCH 28/46] fix: wfs --- .../src/components/cesium-map/debug/wfs.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/react-components/src/components/cesium-map/debug/wfs.tsx b/packages/react-components/src/components/cesium-map/debug/wfs.tsx index 88bb8f31..0d1ca629 100644 --- a/packages/react-components/src/components/cesium-map/debug/wfs.tsx +++ b/packages/react-components/src/components/cesium-map/debug/wfs.tsx @@ -28,10 +28,10 @@ export const WFS: React.FC = ({ featureTypes, locale }) => { featureTypes.map((type, index) => ( - - {(get(type, 'layerRecord.featureStructure.aliasLayerName') ?? '') as string} ({String(type.zoomLevel)}): + + {(get(type, 'layerRecord.featureStructure.aliasLayerName') ?? '') as string} ({String(get(type, 'layerRecord.zoomLevel'))}): From f8164960ad77940e92a557b7c9a2ba66d8c27312 Mon Sep 17 00:00:00 2001 From: MARTIROSYAN ELLA Date: Wed, 24 Jun 2026 07:00:08 +0300 Subject: [PATCH 29/46] fix: zoomLevel --- .../src/components/cesium-map/debug/wfs.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/react-components/src/components/cesium-map/debug/wfs.tsx b/packages/react-components/src/components/cesium-map/debug/wfs.tsx index 0d1ca629..88bb8f31 100644 --- a/packages/react-components/src/components/cesium-map/debug/wfs.tsx +++ b/packages/react-components/src/components/cesium-map/debug/wfs.tsx @@ -28,10 +28,10 @@ export const WFS: React.FC = ({ featureTypes, locale }) => { featureTypes.map((type, index) => ( - - {(get(type, 'layerRecord.featureStructure.aliasLayerName') ?? '') as string} ({String(get(type, 'layerRecord.zoomLevel'))}): + + {(get(type, 'layerRecord.featureStructure.aliasLayerName') ?? '') as string} ({String(type.zoomLevel)}): From eccf6e9bd3c3fdcccca7cb31384b21a989d37a64 Mon Sep 17 00:00:00 2001 From: MARTIROSYAN ELLA Date: Wed, 24 Jun 2026 07:38:40 +0300 Subject: [PATCH 30/46] fix: guard on http error --- .../cesium-map/layers/wfs.layer.tsx | 30 +++++++++++++++---- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/packages/react-components/src/components/cesium-map/layers/wfs.layer.tsx b/packages/react-components/src/components/cesium-map/layers/wfs.layer.tsx index 1d36f790..411282af 100644 --- a/packages/react-components/src/components/cesium-map/layers/wfs.layer.tsx +++ b/packages/react-components/src/components/cesium-map/layers/wfs.layer.tsx @@ -300,11 +300,20 @@ export const CesiumWFSLayer: React.FC = (props) => { }; const fetchWfsData = async (wfsDataUrl: string, options: RequestInit = { method: 'GET' }): Promise => { - const response = await fetch(wfsDataUrl, options); - if (response.status === 200) { - return await response.json(); + if (!wfsDataUrl) { + throw new Error('WFS request URL is missing'); } - return undefined; + let response: Response; + try { + response = await fetch(wfsDataUrl, options); + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + throw new Error(`WFS network request failed for ${wfsDataUrl}: ${message}`); + } + if (!response.ok) { + throw new Error(`WFS request failed (${response.status} ${response.statusText}) for ${wfsDataUrl}`); + } + return await response.json(); }; // Create a temporary canvas to measure max width @@ -602,6 +611,11 @@ export const CesiumWFSLayer: React.FC = (props) => { const waitForTilesLoaded = () => { return new Promise((resolve) => { const interval = setInterval(() => { + if (get(mapViewer, '_cesiumWidget') === undefined) { + clearInterval(interval); + resolve(); + return; + } if (mapViewer.scene.globe.tilesLoaded) { clearInterval(interval); resolve(); @@ -617,11 +631,14 @@ export const CesiumWFSLayer: React.FC = (props) => { }; useEffect((): void => { + if (!mapViewer?.scene || !mapViewer?.dataSources) { + return; + } const dataSource = mapViewer.dataSources.getByName(dataSourceName)[0] as GeoJsonDataSource; if (dataSource) { applyVisualization(mapViewer, dataSource, [], undefined); } - }, [mapViewer.scene.mode]); + }, [mapViewer?.scene?.mode]); useEffect(() => { // Happens each time the metadata from STATE changes @@ -640,6 +657,9 @@ export const CesiumWFSLayer: React.FC = (props) => { }, [mapViewer.layersManager]); useEffect(() => { + if (!mapViewer?.scene || !mapViewer?.dataSources) { + return; + } // DataSource mapViewer.dataSources.add(wfsDataSource); From 63698edc1542df8cdfc8599a37a7b3f9e30a3170 Mon Sep 17 00:00:00 2001 From: ellamartirosyan Date: Wed, 24 Jun 2026 10:08:52 +0300 Subject: [PATCH 31/46] chore: fix --- .../src/components/cesium-map/layers/wfs.layer.stories.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/react-components/src/components/cesium-map/layers/wfs.layer.stories.tsx b/packages/react-components/src/components/cesium-map/layers/wfs.layer.stories.tsx index 8e239702..734dcdbd 100644 --- a/packages/react-components/src/components/cesium-map/layers/wfs.layer.stories.tsx +++ b/packages/react-components/src/components/cesium-map/layers/wfs.layer.stories.tsx @@ -32,6 +32,7 @@ import { CesiumEllipsoid, CesiumCesiumTerrainProvider, } from '../proxied.types'; +import { ZoomLevelTrackerTool } from '../tools/zoom-level-tracker.tool'; import { CesiumWFSLayer, ICesiumWFSLayerLabelTextField } from './wfs.layer'; import { Cesium3DTileset } from './3d.tileset'; @@ -61,7 +62,8 @@ export const MapWithPPWFSLayer: Story = (args: Record) => { return (
Go to ME
- + + { const screenPosition = CesiumSceneTransforms.wgs84ToWindowCoordinates(scene, position); - if (!screenPosition) return null; + if (!screenPosition) { return null; } const xRight = screenPosition.x + pixelWidth; const yBottom = screenPosition.y + pixelHeight; From 0dc567c39c729272d39c635c0f67e70529f1fef0 Mon Sep 17 00:00:00 2001 From: ellamartirosyan Date: Wed, 24 Jun 2026 10:09:17 +0300 Subject: [PATCH 32/46] chore: fix --- .../src/components/cesium-map/layers/wfs.layer.stories.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/react-components/src/components/cesium-map/layers/wfs.layer.stories.tsx b/packages/react-components/src/components/cesium-map/layers/wfs.layer.stories.tsx index 734dcdbd..ad714835 100644 --- a/packages/react-components/src/components/cesium-map/layers/wfs.layer.stories.tsx +++ b/packages/react-components/src/components/cesium-map/layers/wfs.layer.stories.tsx @@ -32,7 +32,6 @@ import { CesiumEllipsoid, CesiumCesiumTerrainProvider, } from '../proxied.types'; -import { ZoomLevelTrackerTool } from '../tools/zoom-level-tracker.tool'; import { CesiumWFSLayer, ICesiumWFSLayerLabelTextField } from './wfs.layer'; import { Cesium3DTileset } from './3d.tileset'; @@ -62,8 +61,7 @@ export const MapWithPPWFSLayer: Story = (args: Record) => { return (
Go to ME
- - + Date: Wed, 24 Jun 2026 14:45:57 +0300 Subject: [PATCH 33/46] fix: pp story --- .../components/cesium-map/layers/wfs.layer.stories.tsx | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/packages/react-components/src/components/cesium-map/layers/wfs.layer.stories.tsx b/packages/react-components/src/components/cesium-map/layers/wfs.layer.stories.tsx index ad714835..1bbabb89 100644 --- a/packages/react-components/src/components/cesium-map/layers/wfs.layer.stories.tsx +++ b/packages/react-components/src/components/cesium-map/layers/wfs.layer.stories.tsx @@ -60,7 +60,6 @@ const BRIGHT_PURPLE = '#B734EB'; export const MapWithPPWFSLayer: Story = (args: Record) => { return (
-
Go to ME
Date: Wed, 24 Jun 2026 21:52:22 +0300 Subject: [PATCH 34/46] fix: encapsulate catalog data --- .../active-layers/active-layers-panel.tsx | 8 +-- .../cesium-map/debug/debugger-widget.tsx | 12 ++--- .../src/components/cesium-map/debug/wfs.tsx | 11 +++- .../helpers/customImageryProviders.ts | 8 ++- .../components/cesium-map/layers-manager.ts | 54 +++++++++++++++---- .../cesium-map/layers/3d.tileset.tsx | 10 ++-- .../layers/3d.tileset.with.update.tsx | 23 ++++---- .../cesium-map/layers/layers.rect.stories.tsx | 2 +- .../cesium-map/layers/wfs.layer.tsx | 51 ++++++++++-------- 9 files changed, 119 insertions(+), 60 deletions(-) diff --git a/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.tsx b/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.tsx index cc44f5fb..ad3ff1f0 100644 --- a/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.tsx +++ b/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.tsx @@ -7,7 +7,9 @@ import { Box } from '../../box'; import { getImageryProvider, getImageryProviderName, + getDataLayerName, getLayerId, + getLayerName, ICesiumImageryLayer, isBaseMapLayer, isManagedImageryLayer, @@ -86,7 +88,7 @@ export const ActiveLayersPanel: React.FC = ({ locale }) } return { id: layerId as string, - name: (get(meta, 'layerRecord.productName') ?? layerId) as string, + name: (getLayerName(layer) ?? layerId) as string, rect: layer.rectangle, isDisabled: isBaseMapLayer(meta as Record) }; @@ -122,7 +124,7 @@ export const ActiveLayersPanel: React.FC = ({ locale }) return mapViewer.layersManager?.dataLayerList.map((dataLayer) => { return { id: getLayerId(dataLayer) as string, - name: (get(dataLayer.meta, 'layerRecord.featureStructure.aliasLayerName') ?? get(dataLayer.meta, 'layerRecord.productName')) as string, + name: (getDataLayerName(dataLayer.meta) ?? getLayerName(dataLayer)) as string, rect: Rectangle.fromDegrees(...bbox((dataLayer.meta?.layerRecord as Record)?.footprint)), isDisabled: false }; }) || []; @@ -131,7 +133,7 @@ export const ActiveLayersPanel: React.FC = ({ locale }) const get3DModels = (): IActiveLayer[] => { return (mapViewer.layersManager?.modelList ?? []).map((model, index): IActiveLayer => { const modelUrl = get(model.tileset, 'resource.url') as string | undefined; - const modelName = (get(model.meta, 'layerRecord.productName') ?? extractModelName(modelUrl ?? `Model #${String(index + 1)}`)) as string; + const modelName = (getLayerName(model) ?? extractModelName(modelUrl ?? `Model #${String(index + 1)}`)) as string; return { id: (getLayerId(model) as string) ?? `3D_MODEL_${String(index)}`, name: modelName, diff --git a/packages/react-components/src/components/cesium-map/debug/debugger-widget.tsx b/packages/react-components/src/components/cesium-map/debug/debugger-widget.tsx index ea8286e7..f6fd7c7d 100644 --- a/packages/react-components/src/components/cesium-map/debug/debugger-widget.tsx +++ b/packages/react-components/src/components/cesium-map/debug/debugger-widget.tsx @@ -4,7 +4,7 @@ import { Checkbox, Tooltip } from '@map-colonies/react-core'; import { Box } from '../../box'; import { EXAMINED_TILES_META_PROP, HAS_TRANSPARENCY_META_PROP } from '../helpers/customImageryProviders'; import { ICesiumWFSLayer } from '../layers/wfs.layer'; -import { getLayerId, isManagedImageryLayer } from '../layers-manager'; +import { getLayerId, getLayerName, isManagedImageryLayer } from '../layers-manager'; import { useCesiumMap, useCesiumMapViewstate } from '../map'; import { CesiumIcon } from '../widget/cesium-icon'; import { CesiumTool } from '../widget/cesium-tool'; @@ -30,16 +30,15 @@ export interface IDebuggerWidgetProps extends IWidgetProps { locale?: { [key: string]: string }; } -interface LayerDebugMeta extends Record { +interface LayerDebugMeta { id?: string; - layerRecord?: { - productName?: string; - }; isRelevantToExtent?: boolean; + [key: string]: unknown; } interface LayerDebugItem { layerId: string; + layerName?: string; meta: LayerDebugMeta; } @@ -82,6 +81,7 @@ const DebuggerComponent: React.FC = ({ locale, isOpen, set } return { layerId: layerId as string, + layerName: getLayerName(layer), meta: (layer.meta ?? {}) as LayerDebugMeta, }; }) @@ -218,7 +218,7 @@ const DebuggerComponent: React.FC = ({ locale, isOpen, set {[...layersMeta].reverse().map((layer) => { const idText = layer.layerId; - const nameText = layer.meta.layerRecord?.productName ?? idText; + const nameText = layer.layerName ?? idText; const statusText = layer.meta?.isRelevantToExtent === true ? ' → show' : layer.meta?.isRelevantToExtent === false ? ' → hide' : ''; const hasTransparency = layer.meta[HAS_TRANSPARENCY_META_PROP] as boolean | undefined; diff --git a/packages/react-components/src/components/cesium-map/debug/wfs.tsx b/packages/react-components/src/components/cesium-map/debug/wfs.tsx index 88bb8f31..31e87914 100644 --- a/packages/react-components/src/components/cesium-map/debug/wfs.tsx +++ b/packages/react-components/src/components/cesium-map/debug/wfs.tsx @@ -2,6 +2,8 @@ import { get } from 'lodash'; import React, { useMemo } from 'react'; import { Tooltip } from '@map-colonies/react-core'; import { Box } from '../../box'; +import { ICesiumWFSLayerMeta } from '../layers/wfs.layer'; +import { getDataLayerName } from '../layers-manager'; import { IActiveFeatureTypes } from './debugger-widget'; import './wfs.css'; @@ -27,13 +29,18 @@ export const WFS: React.FC = ({ featureTypes, locale }) => { {featureTypes.length > 0 ? ( featureTypes.map((type, index) => ( + {(() => { + const dataLayerName = getDataLayerName(type as unknown as ICesiumWFSLayerMeta) ?? ''; + return ( - {(get(type, 'layerRecord.featureStructure.aliasLayerName') ?? '') as string} ({String(type.zoomLevel)}): + {dataLayerName} ({String(type.zoomLevel)}): + ); + })()} {cacheLabel}: {type.cache ?? 0} diff --git a/packages/react-components/src/components/cesium-map/helpers/customImageryProviders.ts b/packages/react-components/src/components/cesium-map/helpers/customImageryProviders.ts index af190c8d..374c0491 100644 --- a/packages/react-components/src/components/cesium-map/helpers/customImageryProviders.ts +++ b/packages/react-components/src/components/cesium-map/helpers/customImageryProviders.ts @@ -8,8 +8,8 @@ import { ImageryTypes, } from 'cesium'; import { get } from 'lodash'; -import { getImageryProviderUrl, ICesiumImageryLayer } from '../layers-manager'; -import { CesiumViewer } from '../map'; +import type { ICesiumImageryLayer } from '../layers-manager'; +import type { CesiumViewer } from '../map'; import { imageHasTransparency } from './utils'; export interface CustomImageryProvider extends ImageryProvider { @@ -30,6 +30,10 @@ const NUMBER_OF_TILES_TO_CHECK = 3; export const HAS_TRANSPARENCY_META_PROP = 'hasTransparency'; export const EXAMINED_TILES_META_PROP = 'examinedTiles'; +const getImageryProviderUrl = (layer: ImageryLayer): string | undefined => { + return get(layer, '_imageryProvider._resource._url') as string | undefined; +}; + function customCommonRequestImage( this: CustomImageryProvider, requestImageFn: ImageryProvider['requestImage'], diff --git a/packages/react-components/src/components/cesium-map/layers-manager.ts b/packages/react-components/src/components/cesium-map/layers-manager.ts index a66f17c7..7395547e 100644 --- a/packages/react-components/src/components/cesium-map/layers-manager.ts +++ b/packages/react-components/src/components/cesium-map/layers-manager.ts @@ -26,16 +26,32 @@ import { RCesiumWMTSLayerOptions, RCesiumXYZLayerOptions } from './layers'; -import { ICesiumWFSLayer } from './layers/wfs.layer'; +import type { ICesiumWFSLayer, ICesiumWFSLayerMeta } from './layers/wfs.layer'; import { IMapLegend } from './legend'; -import { CesiumViewer, IBaseMap } from './map'; +import type { CesiumViewer, IBaseMap } from './map'; import { CesiumCartesian2, CesiumImageryProvider } from './proxied.types'; const INC = 1; const DEC = -1; +export interface ICesiumImageryLayerMeta { + id?: string; + parentBaseMapId?: string; + zIndex?: number; + type?: LayerType; + opacity?: number; + show?: boolean; + options?: RCesiumOSMLayerOptions | RCesiumWMSLayerOptions | RCesiumWMTSLayerOptions | RCesiumXYZLayerOptions; + details?: Record; + skipRelevancyCheck?: boolean; + isRelevantToExtent?: boolean; + hasTransparency?: boolean; + examinedTiles?: Array<{ x?: number; y?: number; level?: number }>; + [key: string]: unknown; +} + export interface ICesiumImageryLayer extends InstanceType { - meta?: Record; + meta?: ICesiumImageryLayerMeta; } export type LayerType = 'OSM_LAYER' | 'WMTS_LAYER' | 'WMS_LAYER' | 'XYZ_LAYER'; @@ -50,16 +66,20 @@ export interface IRasterLayer { details?: Record; } +export interface ICesium3DModelMeta { + id?: string; + [key: string]: unknown; +} + export interface ICesium3DModel { tileset: CesiumTileset; - meta: Record; + meta: ICesium3DModelMeta; } -export interface IVectorLayer { - id: string; - opacity: number; - zIndex: number; - url: string; +export interface ICesiumDataLayerField { + fieldName: string; + aliasFieldName: string; + [key: string]: unknown; } export type LegendExtractor = (layers: (any & { meta: any })[]) => IMapLegend[]; @@ -67,7 +87,19 @@ export type LegendExtractor = (layers: (any & { meta: any })[]) => IMapLegend[]; export const TRANSPARENT_LAYER_ID = 'TRANSPARENT_BASE_LAYER'; export const getLayerId = (layer: ICesiumImageryLayer | ICesiumWFSLayer | ICesium3DModel): string | undefined => { - return get(layer, 'meta.id') as string | undefined; + return get(layer, 'meta.id'); +}; + +export const getLayerName = (layer: ICesiumImageryLayer | ICesiumWFSLayer | ICesium3DModel): string | undefined => { + return get(layer, 'meta.layerRecord.productName'); +}; + +export const getDataLayerName = (meta: ICesiumWFSLayerMeta): string | undefined => { + return get(meta, 'layerRecord.featureStructure.aliasLayerName') as string | undefined; +}; + +export const getDataLayerFields = (meta: ICesiumWFSLayerMeta | undefined): ICesiumDataLayerField[] => { + return (get(meta, 'layerRecord.featureStructure.fields') as ICesiumDataLayerField[] | undefined) ?? []; }; export const isServiceLayer = (layerId: string | undefined): boolean => { @@ -162,7 +194,7 @@ class LayerManager { this.dataLayerUpdated.raiseEvent(this.dataLayers); } - // A general place to extend layer's data. Should be done when all providers(different types) are initialized + // A general place to extend layer's data. Should be done when all providers (different types) are initialized public addMetaToLayer(meta: any, layerPredicate: (layer: ImageryLayer, idx: number) => boolean): void { const layersReadyPromises = this.layers.map((item) => item.imageryProvider.readyPromise); diff --git a/packages/react-components/src/components/cesium-map/layers/3d.tileset.tsx b/packages/react-components/src/components/cesium-map/layers/3d.tileset.tsx index d76a88d7..dee2e4ea 100644 --- a/packages/react-components/src/components/cesium-map/layers/3d.tileset.tsx +++ b/packages/react-components/src/components/cesium-map/layers/3d.tileset.tsx @@ -1,24 +1,25 @@ import React, { ComponentProps, useEffect, useRef } from 'react'; import { Cartesian3, Cartographic, Matrix4, Cesium3DTileset as CesiumTileset } from 'cesium'; import { Cesium3DTileset as Resium3DTileset } from 'resium'; +import { ICesium3DModelMeta } from '../layers-manager'; import { CesiumViewer, useCesiumMap } from '../map'; const GROUND_LEVEL = 0.0; -export interface RCesium3DTilesetProps extends ComponentProps { +export interface ICesium3DTileset extends ComponentProps { isZoomTo?: boolean; heightFromGround?: number; - meta?: Record; + meta?: ICesium3DModelMeta; } -export const Cesium3DTileset: React.FC = ({ meta, ...props }) => { +export const Cesium3DTileset: React.FC = ({ meta, ...props }) => { const mapViewer: CesiumViewer = useCesiumMap(); const tilesetRef = useRef(null); useEffect(() => { return () => { if (tilesetRef.current !== null && meta?.id !== undefined) { - mapViewer.layersManager?.removeModel(meta.id as string); + mapViewer.layersManager?.removeModel(meta.id); } }; }, []); @@ -44,7 +45,6 @@ export const Cesium3DTileset: React.FC = ({ meta, ...prop const translation = Cartesian3.subtract(offset, surface, new Cartesian3()); tileset.modelMatrix = Matrix4.fromTranslation(translation); } - props.onReady?.(tileset); }} /> diff --git a/packages/react-components/src/components/cesium-map/layers/3d.tileset.with.update.tsx b/packages/react-components/src/components/cesium-map/layers/3d.tileset.with.update.tsx index 6bf61143..da013be4 100644 --- a/packages/react-components/src/components/cesium-map/layers/3d.tileset.with.update.tsx +++ b/packages/react-components/src/components/cesium-map/layers/3d.tileset.with.update.tsx @@ -7,23 +7,28 @@ */ import React, { useEffect, useState } from 'react'; -import { Cesium3DTileset, Cesium3DTile, Cartographic, Cartesian3, defined, sampleTerrainMostDetailed, Cesium3DTileContent } from 'cesium'; +import { + Cesium3DTileset, + Cesium3DTile, + Cartographic, + Cartesian3, + defined, + sampleTerrainMostDetailed, + Cesium3DTileContent +} from 'cesium'; +import { ICesium3DModelMeta } from '../layers-manager'; import { CesiumViewer, useCesiumMap } from '../map'; -export interface Cesium3DTilesetWithUpdateProps { +export interface ICesium3DTilesetWithUpdate { url: string; withUpdate?: boolean; - meta?: Record; + meta?: ICesium3DModelMeta; } -export const Cesium3DTilesetWithUpdate: React.FC = ({ url, withUpdate, meta }) => { +export const Cesium3DTilesetWithUpdate: React.FC = ({ url, withUpdate, meta }) => { const mapViewer: CesiumViewer = useCesiumMap(); const scene = mapViewer.scene; - const [cesium3DTileset] = useState( - new Cesium3DTileset({ - url: url, - }) - ); + const [cesium3DTileset] = useState(new Cesium3DTileset({ url })); const [tileset] = useState(scene.primitives.add(cesium3DTileset)); useEffect(() => { diff --git a/packages/react-components/src/components/cesium-map/layers/layers.rect.stories.tsx b/packages/react-components/src/components/cesium-map/layers/layers.rect.stories.tsx index 2bfc5dfa..e4e4b9b5 100644 --- a/packages/react-components/src/components/cesium-map/layers/layers.rect.stories.tsx +++ b/packages/react-components/src/components/cesium-map/layers/layers.rect.stories.tsx @@ -73,7 +73,7 @@ export const MapWithSettings: Story = () => { return (
- +
); diff --git a/packages/react-components/src/components/cesium-map/layers/wfs.layer.tsx b/packages/react-components/src/components/cesium-map/layers/wfs.layer.tsx index 411282af..4bb7270a 100644 --- a/packages/react-components/src/components/cesium-map/layers/wfs.layer.tsx +++ b/packages/react-components/src/components/cesium-map/layers/wfs.layer.tsx @@ -19,6 +19,7 @@ import pMap from 'p-map'; import { v4 as uuidv4 } from 'uuid'; import booleanValid from '@turf/boolean-valid'; import { distance, center, rectangle2bbox, computeLimitedViewRectangle, defaultVisualizationHandler, rectangle2Feature } from '../helpers/utils'; +import { getDataLayerFields } from '../layers-manager'; import { CesiumViewer, useCesiumMap, useCesiumMapViewstate } from '../map'; export interface ICesiumWFSLayerLabelTextField { @@ -85,9 +86,19 @@ export interface ICesiumWFSLayerOptions { labeling?: ICesiumWFSLayerLabelingOptions; } +export interface ICesiumWFSLayerMeta { + id: string; + items?: number; + total?: number; + cache?: number; + currentZoomLevel?: number; + zoomLevel?: number; + [key: string]: unknown; +} + export interface ICesiumWFSLayer extends React.Attributes { options: ICesiumWFSLayerOptions; - meta: Record; + meta: ICesiumWFSLayerMeta; visualizationHandler?: (mapViewer: CesiumViewer, wfsDataSource: GeoJsonDataSource, processEntityIds: string[], extent?: BBox) => void; withGeometryValidation?: boolean; } @@ -130,27 +141,25 @@ export const CesiumWFSLayer: React.FC = (props) => { const describe = (properties: Record): string => { const rows: string[] = []; - const featureStructure = (meta.layerRecord as any)?.featureStructure as { fields: { fieldName: string; aliasFieldName: string; type: string }[] }; - if (featureStructure && featureStructure.fields) { - for (const field of featureStructure.fields) { - const { fieldName, aliasFieldName } = field; - const key = aliasFieldName; - const value = properties[fieldName] ?? 'N/A'; - const keyMaxWidth = Math.max(100, Math.min(180, key.length * 10)); - const valueMaxWidth = '260px'; - rows.push(` - - - ${key}: - - - ${value} - - - `); - } + const dataLayerFields = getDataLayerFields(meta); + for (const field of dataLayerFields) { + const { fieldName, aliasFieldName } = field; + const key = aliasFieldName; + const value = properties[fieldName] ?? 'N/A'; + const keyMaxWidth = Math.max(100, Math.min(180, key.length * 10)); + const valueMaxWidth = '260px'; + rows.push(` + + + ${key}: + + + ${value} + + + `); } - const isRightToLeft = featureStructure.fields.some((field) => field.aliasFieldName !== field.fieldName); + const isRightToLeft = dataLayerFields.some((field) => field.aliasFieldName !== field.fieldName); return ` From 98f2cfd3391185091cdee313fc8d4d093c6715b9 Mon Sep 17 00:00:00 2001 From: ellamartirosyan Date: Thu, 25 Jun 2026 07:57:04 +0300 Subject: [PATCH 35/46] fix: details renamed to layerRecord --- .../src/components/cesium-map/context-menu.stories.tsx | 8 ++++---- .../src/components/cesium-map/layers-manager.ts | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/react-components/src/components/cesium-map/context-menu.stories.tsx b/packages/react-components/src/components/cesium-map/context-menu.stories.tsx index 50a50bfc..3cf350bb 100644 --- a/packages/react-components/src/components/cesium-map/context-menu.stories.tsx +++ b/packages/react-components/src/components/cesium-map/context-menu.stories.tsx @@ -43,7 +43,7 @@ const layers: IRasterLayer[] = [ options: { url: 'https://tiles.openaerialmap.org/5a9f90c42553e6000ce5ad6c/0/eee1a570-128e-4947-9ffa-1e69c1efab7c/{z}/{x}/{y}.png', }, - details: { + layerRecord: { footprint: { type: 'Polygon', coordinates: [ @@ -67,7 +67,7 @@ const layers: IRasterLayer[] = [ options: { url: 'https://tiles.openaerialmap.org/5a8316e22553e6000ce5ac7f/0/c3fcbe99-d339-41b6-8ec0-33d90ccca020/{z}/{x}/{y}.png', }, - details: { + layerRecord: { footprint: { type: 'Polygon', coordinates: [ @@ -91,7 +91,7 @@ const layers: IRasterLayer[] = [ options: { url: 'https://tiles.openaerialmap.org/5a831b4a2553e6000ce5ac80/0/d02ddc76-9c2e-4994-97d4-a623eb371456/{z}/{x}/{y}.png', }, - details: { + layerRecord: { footprint: { type: 'Polygon', coordinates: [ @@ -307,7 +307,7 @@ export const MapWithLayersManagerAndContextMenu: Story = () => { // @ts-ignore imageryContextMenu={} imageryContextMenuSize={{ height: 340, width: 200 }} - layerManagerFootprintMetaFieldPath={'details.footprint'} + layerManagerFootprintMetaFieldPath={'layerRecord.footprint'} > diff --git a/packages/react-components/src/components/cesium-map/layers-manager.ts b/packages/react-components/src/components/cesium-map/layers-manager.ts index 7395547e..c61bd7b8 100644 --- a/packages/react-components/src/components/cesium-map/layers-manager.ts +++ b/packages/react-components/src/components/cesium-map/layers-manager.ts @@ -42,7 +42,7 @@ export interface ICesiumImageryLayerMeta { opacity?: number; show?: boolean; options?: RCesiumOSMLayerOptions | RCesiumWMSLayerOptions | RCesiumWMTSLayerOptions | RCesiumXYZLayerOptions; - details?: Record; + layerRecord?: Record; skipRelevancyCheck?: boolean; isRelevantToExtent?: boolean; hasTransparency?: boolean; @@ -61,9 +61,9 @@ export interface IRasterLayer { type: LayerType; opacity: number; zIndex: number; - show?: boolean; options: RCesiumOSMLayerOptions | RCesiumWMSLayerOptions | RCesiumWMTSLayerOptions | RCesiumXYZLayerOptions; - details?: Record; + show?: boolean; + layerRecord?: Record; } export interface ICesium3DModelMeta { From eed776da145e443c06134af2e3821e5b14127c96 Mon Sep 17 00:00:00 2001 From: ellamartirosyan Date: Thu, 25 Jun 2026 09:37:08 +0300 Subject: [PATCH 36/46] fix: new cesium map props --- .../components/cesium-map/layers-manager.ts | 18 +++++++++++++++--- .../src/components/cesium-map/map.tsx | 19 ++++++++++++++++++- 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/packages/react-components/src/components/cesium-map/layers-manager.ts b/packages/react-components/src/components/cesium-map/layers-manager.ts index c61bd7b8..3a31ebef 100644 --- a/packages/react-components/src/components/cesium-map/layers-manager.ts +++ b/packages/react-components/src/components/cesium-map/layers-manager.ts @@ -87,11 +87,11 @@ export type LegendExtractor = (layers: (any & { meta: any })[]) => IMapLegend[]; export const TRANSPARENT_LAYER_ID = 'TRANSPARENT_BASE_LAYER'; export const getLayerId = (layer: ICesiumImageryLayer | ICesiumWFSLayer | ICesium3DModel): string | undefined => { - return get(layer, 'meta.id'); + return get(layer.meta, 'id'); }; export const getLayerName = (layer: ICesiumImageryLayer | ICesiumWFSLayer | ICesium3DModel): string | undefined => { - return get(layer, 'meta.layerRecord.productName'); + return get(layer.meta, 'layerRecord.productName') as string | undefined; }; export const getDataLayerName = (meta: ICesiumWFSLayerMeta): string | undefined => { @@ -141,7 +141,11 @@ class LayerManager { private readonly dataLayers: ICesiumWFSLayer[]; private readonly models: ICesium3DModel[]; private readonly legendsExtractor?: LegendExtractor; - private readonly layerManagerFootprintMetaFieldPath: string | undefined; + private readonly layerManagerLayerIdMetaFieldPath?: string; + private readonly layerManagerLayerNameMetaFieldPath?: string; + private readonly layerManagerDataLayerNameMetaFieldPath?: string; + private readonly layerManagerDataLayerFieldsMetaFieldPath?: string; + private readonly layerManagerFootprintMetaFieldPath?: string; private shouldOptimizedTileRequests?: boolean; private relevancyListenersCleanup: Array<() => void>; private relevancyLayerUpdatedHandler?: (meta: Record) => void; @@ -150,6 +154,10 @@ class LayerManager { mapViewer: CesiumViewer, legendsExtractor?: LegendExtractor, onLayersUpdate?: () => void, + layerManagerLayerIdMetaFieldPath?: string, + layerManagerLayerNameMetaFieldPath?: string, + layerManagerDataLayerNameMetaFieldPath?: string, + layerManagerDataLayerFieldsMetaFieldPath?: string, layerManagerFootprintMetaFieldPath?: string, shouldOptimizedTileRequests?: boolean ) { @@ -163,6 +171,10 @@ class LayerManager { this.layerUpdated = new Event(); this.dataLayerUpdated = new Event(); this.modelUpdated = new Event(); + this.layerManagerLayerIdMetaFieldPath = layerManagerLayerIdMetaFieldPath; + this.layerManagerLayerNameMetaFieldPath = layerManagerLayerNameMetaFieldPath; + this.layerManagerDataLayerNameMetaFieldPath = layerManagerDataLayerNameMetaFieldPath; + this.layerManagerDataLayerFieldsMetaFieldPath = layerManagerDataLayerFieldsMetaFieldPath; this.layerManagerFootprintMetaFieldPath = layerManagerFootprintMetaFieldPath; this.shouldOptimizedTileRequests = shouldOptimizedTileRequests ?? false; this.relevancyListenersCleanup = []; diff --git a/packages/react-components/src/components/cesium-map/map.tsx b/packages/react-components/src/components/cesium-map/map.tsx index 06aa97ae..8ec28dba 100644 --- a/packages/react-components/src/components/cesium-map/map.tsx +++ b/packages/react-components/src/components/cesium-map/map.tsx @@ -166,6 +166,10 @@ export interface CesiumMapProps extends ViewerProps { dynamicHeightIncrement?: number; }; legends?: ILegends; + layerManagerLayerIdMetaFieldPath?: string; + layerManagerLayerNameMetaFieldPath?: string; + layerManagerDataLayerNameMetaFieldPath?: string; + layerManagerDataLayerFieldsMetaFieldPath?: string; layerManagerFootprintMetaFieldPath?: string; geocoderPanel?: GeocoderOptions[]; } @@ -304,6 +308,10 @@ export const CesiumMap: React.FC = (props) => { () => { setLegendsList(mapViewRef.layersManager?.legendsList as IMapLegend[]); }, + props.layerManagerLayerIdMetaFieldPath, + props.layerManagerLayerNameMetaFieldPath, + props.layerManagerDataLayerNameMetaFieldPath, + props.layerManagerDataLayerFieldsMetaFieldPath, props.layerManagerFootprintMetaFieldPath, viewState?.shouldOptimizedTileRequests ), @@ -314,7 +322,16 @@ export const CesiumMap: React.FC = (props) => { setViewState, }; } - }, [props.legends, props.layerManagerFootprintMetaFieldPath, mapViewRef, viewState]); + }, [ + props.legends, + props.layerManagerLayerIdMetaFieldPath, + props.layerManagerLayerNameMetaFieldPath, + props.layerManagerDataLayerNameMetaFieldPath, + props.layerManagerDataLayerFieldsMetaFieldPath, + props.layerManagerFootprintMetaFieldPath, + mapViewRef, + viewState + ]); useEffect(() => { setBaseMaps(props.baseMaps); From 7231baa6561a7626e45a7f990bb85763dfcb4cdc Mon Sep 17 00:00:00 2001 From: ellamartirosyan Date: Thu, 25 Jun 2026 12:12:08 +0300 Subject: [PATCH 37/46] fix: get layerRecord props from app --- .../active-layers/active-layers-panel.tsx | 3 +- .../cesium-map/debug/debugger-widget.tsx | 25 ++--- .../src/components/cesium-map/debug/wfs.tsx | 22 ++--- .../components/cesium-map/layers-manager.ts | 96 +++++++++++++++---- .../cesium-map/layers/3d.tileset.tsx | 7 +- .../layers/3d.tileset.with.update.tsx | 9 +- .../cesium-map/layers/wfs.layer.stories.tsx | 13 +-- .../cesium-map/layers/wfs.layer.tsx | 10 +- 8 files changed, 119 insertions(+), 66 deletions(-) diff --git a/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.tsx b/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.tsx index ad3ff1f0..3f0a9a92 100644 --- a/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.tsx +++ b/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.tsx @@ -8,6 +8,7 @@ import { getImageryProvider, getImageryProviderName, getDataLayerName, + getLayerFootprint, getLayerId, getLayerName, ICesiumImageryLayer, @@ -125,7 +126,7 @@ export const ActiveLayersPanel: React.FC = ({ locale }) return { id: getLayerId(dataLayer) as string, name: (getDataLayerName(dataLayer.meta) ?? getLayerName(dataLayer)) as string, - rect: Rectangle.fromDegrees(...bbox((dataLayer.meta?.layerRecord as Record)?.footprint)), + rect: Rectangle.fromDegrees(...bbox(getLayerFootprint(dataLayer.meta))), isDisabled: false }; }) || []; }; diff --git a/packages/react-components/src/components/cesium-map/debug/debugger-widget.tsx b/packages/react-components/src/components/cesium-map/debug/debugger-widget.tsx index f6fd7c7d..b5f94d39 100644 --- a/packages/react-components/src/components/cesium-map/debug/debugger-widget.tsx +++ b/packages/react-components/src/components/cesium-map/debug/debugger-widget.tsx @@ -3,8 +3,8 @@ import { get } from 'lodash'; import { Checkbox, Tooltip } from '@map-colonies/react-core'; import { Box } from '../../box'; import { EXAMINED_TILES_META_PROP, HAS_TRANSPARENCY_META_PROP } from '../helpers/customImageryProviders'; -import { ICesiumWFSLayer } from '../layers/wfs.layer'; -import { getLayerId, getLayerName, isManagedImageryLayer } from '../layers-manager'; +import { ICesiumWFSLayer, ICesiumWFSLayerMeta } from '../layers/wfs.layer'; +import { getLayerId, getLayerIdFromMeta, getLayerName, isManagedImageryLayer } from '../layers-manager'; import { useCesiumMap, useCesiumMapViewstate } from '../map'; import { CesiumIcon } from '../widget/cesium-icon'; import { CesiumTool } from '../widget/cesium-tool'; @@ -13,19 +13,6 @@ import { WFS } from './wfs'; import './debugger-widget.css'; -interface IFeatureTypeMetadata { - id: string; - items: number; - total: number; - cache: number; - currentZoomLevel: number; - layerRecord: Record; -} - -export type IActiveFeatureTypes = IFeatureTypeMetadata & { - zoomLevel: number; -}; - export interface IDebuggerWidgetProps extends IWidgetProps { locale?: { [key: string]: string }; } @@ -45,7 +32,7 @@ interface LayerDebugItem { type DebuggerSectionId = 'data' | 'layers' | 'tools'; const DebuggerComponent: React.FC = ({ locale, isOpen, setIsOpen }) => { - const [featureTypes, setFeatureTypes] = useState([]); + const [featureTypes, setFeatureTypes] = useState([]); const [layersMeta, setLayersMeta] = useState([]); const [collapsedSections, setCollapsedSections] = useState>({ data: false, @@ -137,9 +124,9 @@ const DebuggerComponent: React.FC = ({ locale, isOpen, set } const { options, meta } = layer; const { zoomLevel } = options; - const { id, items, total, cache, currentZoomLevel, layerRecord } = meta as unknown as IFeatureTypeMetadata; + const { id, items, total, cache, currentZoomLevel, layerRecord } = meta; setFeatureTypes((prevFeatureTypes) => { - const existingIndex = prevFeatureTypes.findIndex((type) => type.id === id); + const existingIndex = prevFeatureTypes.findIndex((featureType) => getLayerIdFromMeta(featureType) === id); if (existingIndex >= 0) { if ( JSON.stringify(prevFeatureTypes[existingIndex]) !== @@ -156,7 +143,7 @@ const DebuggerComponent: React.FC = ({ locale, isOpen, set }); }); const activeDataLayerIds = new Set(mapViewer.layersManager?.dataLayerList.map((layer) => getLayerId(layer))); - setFeatureTypes((prevFeatureTypes) => prevFeatureTypes.filter((type) => activeDataLayerIds.has(type.id))); + setFeatureTypes((prevFeatureTypes) => prevFeatureTypes.filter((featureType) => activeDataLayerIds.has(getLayerIdFromMeta(featureType)))); }; mapViewer.layersManager.addDataLayerUpdatedListener(handleDataLayerUpdated); return () => { diff --git a/packages/react-components/src/components/cesium-map/debug/wfs.tsx b/packages/react-components/src/components/cesium-map/debug/wfs.tsx index 31e87914..76b633a1 100644 --- a/packages/react-components/src/components/cesium-map/debug/wfs.tsx +++ b/packages/react-components/src/components/cesium-map/debug/wfs.tsx @@ -3,13 +3,12 @@ import React, { useMemo } from 'react'; import { Tooltip } from '@map-colonies/react-core'; import { Box } from '../../box'; import { ICesiumWFSLayerMeta } from '../layers/wfs.layer'; -import { getDataLayerName } from '../layers-manager'; -import { IActiveFeatureTypes } from './debugger-widget'; +import { getDataLayerName, getLayerIdFromMeta } from '../layers-manager'; import './wfs.css'; interface IWFSProps { - featureTypes: IActiveFeatureTypes[]; + featureTypes: ICesiumWFSLayerMeta[]; locale?: { [key: string]: string }; } @@ -27,27 +26,28 @@ export const WFS: React.FC = ({ featureTypes, locale }) => { return ( <> {featureTypes.length > 0 ? ( - featureTypes.map((type, index) => ( + featureTypes.map((featureType, index) => ( {(() => { - const dataLayerName = getDataLayerName(type as unknown as ICesiumWFSLayerMeta) ?? ''; + const dataLayerName = getDataLayerName(featureType) ?? ''; + const zoomLevel = featureType.zoomLevel ?? 0; return ( - - {dataLayerName} ({String(type.zoomLevel)}): + + {dataLayerName} ({zoomLevel}): ); })()} - {cacheLabel}: {type.cache ?? 0} + {cacheLabel}: {featureType.cache ?? 0} - {type.total > 0 && ( + {(featureType.total ?? 0) > 0 && ( - {extentLabel}: {type.items} / {type.total} + {extentLabel}: {featureType.items} / {featureType.total} )} diff --git a/packages/react-components/src/components/cesium-map/layers-manager.ts b/packages/react-components/src/components/cesium-map/layers-manager.ts index 3a31ebef..3236d45d 100644 --- a/packages/react-components/src/components/cesium-map/layers-manager.ts +++ b/packages/react-components/src/components/cesium-map/layers-manager.ts @@ -9,7 +9,7 @@ import { Rectangle, SingleTileImageryProvider, } from 'cesium'; -import { get, isEmpty } from 'lodash'; +import { get, isEmpty, set } from 'lodash'; import { Feature, Point, Polygon } from 'geojson'; import booleanPointInPolygon from '@turf/boolean-point-in-polygon'; import { @@ -34,6 +34,52 @@ import { CesiumCartesian2, CesiumImageryProvider } from './proxied.types'; const INC = 1; const DEC = -1; +const DEFAULT_LAYER_ID_META_FIELD_PATH = 'id'; +const DEFAULT_LAYER_NAME_META_FIELD_PATH = 'layerRecord.productName'; +const DEFAULT_DATA_LAYER_NAME_META_FIELD_PATH = 'layerRecord.featureStructure.aliasLayerName'; +const DEFAULT_DATA_LAYER_FIELDS_META_FIELD_PATH = 'layerRecord.featureStructure.fields'; +const DEFAULT_FOOTPRINT_META_FIELD_PATH = 'layerRecord.footprint'; + +export interface ILayerManagerMetaFieldPaths { + layerIdMetaFieldPath: string; + layerNameMetaFieldPath: string; + dataLayerNameMetaFieldPath: string; + dataLayerFieldsMetaFieldPath: string; + footprintMetaFieldPath: string; +} + +const layerManagerMetaFieldPaths: ILayerManagerMetaFieldPaths = { + layerIdMetaFieldPath: DEFAULT_LAYER_ID_META_FIELD_PATH, + layerNameMetaFieldPath: DEFAULT_LAYER_NAME_META_FIELD_PATH, + dataLayerNameMetaFieldPath: DEFAULT_DATA_LAYER_NAME_META_FIELD_PATH, + dataLayerFieldsMetaFieldPath: DEFAULT_DATA_LAYER_FIELDS_META_FIELD_PATH, + footprintMetaFieldPath: DEFAULT_FOOTPRINT_META_FIELD_PATH, +}; + +const configureLayerManagerMetaFieldPaths = ( + paths: Partial +): void => { + if (paths.layerIdMetaFieldPath) { + layerManagerMetaFieldPaths.layerIdMetaFieldPath = paths.layerIdMetaFieldPath; + } + if (paths.layerNameMetaFieldPath) { + layerManagerMetaFieldPaths.layerNameMetaFieldPath = paths.layerNameMetaFieldPath; + } + if (paths.dataLayerNameMetaFieldPath) { + layerManagerMetaFieldPaths.dataLayerNameMetaFieldPath = paths.dataLayerNameMetaFieldPath; + } + if (paths.dataLayerFieldsMetaFieldPath) { + layerManagerMetaFieldPaths.dataLayerFieldsMetaFieldPath = paths.dataLayerFieldsMetaFieldPath; + } + if (paths.footprintMetaFieldPath) { + layerManagerMetaFieldPaths.footprintMetaFieldPath = paths.footprintMetaFieldPath; + } +}; + +export const getLayerManagerMetaFieldPaths = (): ILayerManagerMetaFieldPaths => { + return { ...layerManagerMetaFieldPaths }; +}; + export interface ICesiumImageryLayerMeta { id?: string; parentBaseMapId?: string; @@ -42,7 +88,6 @@ export interface ICesiumImageryLayerMeta { opacity?: number; show?: boolean; options?: RCesiumOSMLayerOptions | RCesiumWMSLayerOptions | RCesiumWMTSLayerOptions | RCesiumXYZLayerOptions; - layerRecord?: Record; skipRelevancyCheck?: boolean; isRelevantToExtent?: boolean; hasTransparency?: boolean; @@ -63,7 +108,7 @@ export interface IRasterLayer { zIndex: number; options: RCesiumOSMLayerOptions | RCesiumWMSLayerOptions | RCesiumWMTSLayerOptions | RCesiumXYZLayerOptions; show?: boolean; - layerRecord?: Record; + [key: string]: unknown; } export interface ICesium3DModelMeta { @@ -87,19 +132,27 @@ export type LegendExtractor = (layers: (any & { meta: any })[]) => IMapLegend[]; export const TRANSPARENT_LAYER_ID = 'TRANSPARENT_BASE_LAYER'; export const getLayerId = (layer: ICesiumImageryLayer | ICesiumWFSLayer | ICesium3DModel): string | undefined => { - return get(layer.meta, 'id'); + return get(layer.meta, layerManagerMetaFieldPaths.layerIdMetaFieldPath) as string | undefined; +}; + +export const getLayerIdFromMeta = (meta: ICesiumImageryLayerMeta | ICesiumWFSLayerMeta | ICesium3DModelMeta | undefined): string | undefined => { + return get(meta, layerManagerMetaFieldPaths.layerIdMetaFieldPath) as string | undefined; }; export const getLayerName = (layer: ICesiumImageryLayer | ICesiumWFSLayer | ICesium3DModel): string | undefined => { - return get(layer.meta, 'layerRecord.productName') as string | undefined; + return get(layer.meta, layerManagerMetaFieldPaths.layerNameMetaFieldPath) as string | undefined; +}; + +export const getLayerFootprint = (meta: ICesiumWFSLayerMeta | undefined): unknown => { + return get(meta, layerManagerMetaFieldPaths.footprintMetaFieldPath); }; export const getDataLayerName = (meta: ICesiumWFSLayerMeta): string | undefined => { - return get(meta, 'layerRecord.featureStructure.aliasLayerName') as string | undefined; + return get(meta, layerManagerMetaFieldPaths.dataLayerNameMetaFieldPath) as string | undefined; }; export const getDataLayerFields = (meta: ICesiumWFSLayerMeta | undefined): ICesiumDataLayerField[] => { - return (get(meta, 'layerRecord.featureStructure.fields') as ICesiumDataLayerField[] | undefined) ?? []; + return (get(meta, layerManagerMetaFieldPaths.dataLayerFieldsMetaFieldPath) as ICesiumDataLayerField[] | undefined) ?? []; }; export const isServiceLayer = (layerId: string | undefined): boolean => { @@ -141,10 +194,6 @@ class LayerManager { private readonly dataLayers: ICesiumWFSLayer[]; private readonly models: ICesium3DModel[]; private readonly legendsExtractor?: LegendExtractor; - private readonly layerManagerLayerIdMetaFieldPath?: string; - private readonly layerManagerLayerNameMetaFieldPath?: string; - private readonly layerManagerDataLayerNameMetaFieldPath?: string; - private readonly layerManagerDataLayerFieldsMetaFieldPath?: string; private readonly layerManagerFootprintMetaFieldPath?: string; private shouldOptimizedTileRequests?: boolean; private relevancyListenersCleanup: Array<() => void>; @@ -171,14 +220,18 @@ class LayerManager { this.layerUpdated = new Event(); this.dataLayerUpdated = new Event(); this.modelUpdated = new Event(); - this.layerManagerLayerIdMetaFieldPath = layerManagerLayerIdMetaFieldPath; - this.layerManagerLayerNameMetaFieldPath = layerManagerLayerNameMetaFieldPath; - this.layerManagerDataLayerNameMetaFieldPath = layerManagerDataLayerNameMetaFieldPath; - this.layerManagerDataLayerFieldsMetaFieldPath = layerManagerDataLayerFieldsMetaFieldPath; this.layerManagerFootprintMetaFieldPath = layerManagerFootprintMetaFieldPath; this.shouldOptimizedTileRequests = shouldOptimizedTileRequests ?? false; this.relevancyListenersCleanup = []; + configureLayerManagerMetaFieldPaths({ + layerIdMetaFieldPath: layerManagerLayerIdMetaFieldPath, + layerNameMetaFieldPath: layerManagerLayerNameMetaFieldPath, + dataLayerNameMetaFieldPath: layerManagerDataLayerNameMetaFieldPath, + dataLayerFieldsMetaFieldPath: layerManagerDataLayerFieldsMetaFieldPath, + footprintMetaFieldPath: layerManagerFootprintMetaFieldPath, + }); + if (onLayersUpdate) { this.addLayerUpdatedListener(onLayersUpdate); } @@ -221,10 +274,14 @@ class LayerManager { } public addMetaToDataLayer(meta: any): void { - const dataLayer = this.findDataLayerById(meta.id); + const dataLayerId = getLayerIdFromMeta(meta); + if (dataLayerId === undefined) { + return; + } + const dataLayer = this.findDataLayerById(dataLayerId); if (dataLayer) { dataLayer.meta = { ...(dataLayer.meta ?? {}), ...meta }; - this.dataLayerUpdated.raiseEvent(this.dataLayers, meta.id); + this.dataLayerUpdated.raiseEvent(this.dataLayers, dataLayerId as any); } } @@ -492,11 +549,12 @@ class LayerManager { 0 ); - (transparentLayer as ICesiumImageryLayer).meta = { - id: TRANSPARENT_LAYER_ID, + const transparentLayerMeta: Record = { skipRelevancyCheck: true, parentBaseMapId: 'TRANSPARENT_LAYER', }; + set(transparentLayerMeta, layerManagerMetaFieldPaths.layerIdMetaFieldPath, TRANSPARENT_LAYER_ID); + (transparentLayer as ICesiumImageryLayer).meta = transparentLayerMeta; } public addLayerUpdatedListener(callback: (meta: any) => void): void { diff --git a/packages/react-components/src/components/cesium-map/layers/3d.tileset.tsx b/packages/react-components/src/components/cesium-map/layers/3d.tileset.tsx index dee2e4ea..b2c9b34c 100644 --- a/packages/react-components/src/components/cesium-map/layers/3d.tileset.tsx +++ b/packages/react-components/src/components/cesium-map/layers/3d.tileset.tsx @@ -1,7 +1,7 @@ import React, { ComponentProps, useEffect, useRef } from 'react'; import { Cartesian3, Cartographic, Matrix4, Cesium3DTileset as CesiumTileset } from 'cesium'; import { Cesium3DTileset as Resium3DTileset } from 'resium'; -import { ICesium3DModelMeta } from '../layers-manager'; +import { getLayerIdFromMeta, ICesium3DModelMeta } from '../layers-manager'; import { CesiumViewer, useCesiumMap } from '../map'; const GROUND_LEVEL = 0.0; @@ -18,8 +18,9 @@ export const Cesium3DTileset: React.FC = ({ meta, ...props }) useEffect(() => { return () => { - if (tilesetRef.current !== null && meta?.id !== undefined) { - mapViewer.layersManager?.removeModel(meta.id); + const modelId = getLayerIdFromMeta(meta); + if (tilesetRef.current !== null && modelId !== undefined) { + mapViewer.layersManager?.removeModel(modelId); } }; }, []); diff --git a/packages/react-components/src/components/cesium-map/layers/3d.tileset.with.update.tsx b/packages/react-components/src/components/cesium-map/layers/3d.tileset.with.update.tsx index da013be4..adc9333c 100644 --- a/packages/react-components/src/components/cesium-map/layers/3d.tileset.with.update.tsx +++ b/packages/react-components/src/components/cesium-map/layers/3d.tileset.with.update.tsx @@ -16,7 +16,7 @@ import { sampleTerrainMostDetailed, Cesium3DTileContent } from 'cesium'; -import { ICesium3DModelMeta } from '../layers-manager'; +import { getLayerIdFromMeta, ICesium3DModelMeta } from '../layers-manager'; import { CesiumViewer, useCesiumMap } from '../map'; export interface ICesium3DTilesetWithUpdate { @@ -44,8 +44,9 @@ export const Cesium3DTilesetWithUpdate: React.FC = ( if (meta === undefined) { return; } mapViewer.layersManager?.addModel({ tileset, meta }); return () => { - if (meta.id !== undefined) { - mapViewer.layersManager?.removeModel(meta.id as string); + const modelId = getLayerIdFromMeta(meta); + if (modelId !== undefined) { + mapViewer.layersManager?.removeModel(modelId); } }; }, [mapViewer.layersManager]); @@ -57,7 +58,7 @@ export const Cesium3DTilesetWithUpdate: React.FC = ( const normal = scene.globe.ellipsoid.geodeticSurfaceNormal(center, new Cartesian3()); const offset = Cartesian3.multiplyByScalar(normal, height, new Cartesian3()); const carto = Cartographic.fromCartesian(center); - void new Promise((resolve, reject) => { + void new Promise((resolve) => { // @ts-ignore if (scene.terrainProvider._ready !== true) { const result = { ...carto }; diff --git a/packages/react-components/src/components/cesium-map/layers/wfs.layer.stories.tsx b/packages/react-components/src/components/cesium-map/layers/wfs.layer.stories.tsx index 1bbabb89..5aa3ea04 100644 --- a/packages/react-components/src/components/cesium-map/layers/wfs.layer.stories.tsx +++ b/packages/react-components/src/components/cesium-map/layers/wfs.layer.stories.tsx @@ -9,6 +9,7 @@ import bboxPolygon from '@turf/bbox-polygon'; import { Story, Meta } from '@storybook/react/types-6-0'; import { getValue } from '../../utils/config'; import { BASE_MAPS, DEFAULT_TERRAIN_PROVIDER_URL } from '../helpers/constants'; +import { getLayerIdFromMeta } from '../layers-manager'; import { CesiumMap, CesiumViewer } from '../map'; import { CesiumMath, @@ -62,7 +63,7 @@ export const MapWithPPWFSLayer: Story = (args: Record) => {
) => { isZoomTo={true} /> {/* */} @@ -164,7 +165,7 @@ export const MapWithWFSLayerAPPScenario: Story = (args: Record) { show && = (props) => { const wfsCache = useRef(new Set()); const page = useRef(0); const [metadata, setMetadata] = useState(meta); + const dataLayerId = getLayerIdFromMeta(meta); const geojsonHoveredColor = useMemo(() => CesiumColor.fromCssColorString((hover as string) ?? '#24AEE9').withAlpha(0.5), [hover]); const dataSourceName = useMemo(() => `wfs_${featureType}_${uuidv4()}`, [featureType]); const hasRunFetchRef = useRef(false); @@ -654,7 +655,8 @@ export const CesiumWFSLayer: React.FC = (props) => { if ( mapViewer.layersManager && mapViewer.layersManager.dataLayerList.length > 0 && - mapViewer.layersManager.findDataLayerById(meta.id as string) !== undefined + dataLayerId !== undefined && + mapViewer.layersManager.findDataLayerById(dataLayerId) !== undefined ) { mapViewer.layersManager.addMetaToDataLayer(metadata); } @@ -695,7 +697,9 @@ export const CesiumWFSLayer: React.FC = (props) => { fetchMetadata.current.clear(); mapViewer.dataSources.remove(mapViewer.dataSources.getByName(`${labeling?.dataSourcePrefix}${wfsDataSource.name}`)[0]); mapViewer.dataSources.remove(wfsDataSource, true); - mapViewer.layersManager?.removeDataLayer(meta.id as string); + if (dataLayerId !== undefined) { + mapViewer.layersManager?.removeDataLayer(dataLayerId); + } mapViewer.scene.camera.moveEnd.removeEventListener(fetchHandler); handler.removeInputAction(ScreenSpaceEventType.MOUSE_MOVE); } From 663a3a540f4b5be67df802cfd4171afdd01f8e31 Mon Sep 17 00:00:00 2001 From: ellamartirosyan Date: Thu, 25 Jun 2026 13:01:03 +0300 Subject: [PATCH 38/46] fix: sync stories with new props --- .../cesium-map/context-menu.stories.tsx | 2 + .../drawings.data-source.stories.tsx | 9 +++- .../entities/entity.graphics.stories.tsx | 6 ++- .../cesium-map/entities/entity.stories.tsx | 18 +++++-- .../components/cesium-map/layers-manager.ts | 20 ++++---- .../cesium-map/layers/3d.tileset.stories.tsx | 21 ++++++-- .../layers/geojson.layer.stories.tsx | 7 ++- .../layers/imagery.layer.stories.tsx | 6 ++- .../cesium-map/layers/layers.rect.stories.tsx | 14 +++++- .../optimized-tile-requests.stories.tsx | 8 +++- .../cesium-map/layers/osm.layer.stories.tsx | 9 +++- .../cesium-map/layers/wfs.layer.stories.tsx | 41 ++++++++++++++-- .../cesium-map/layers/wms.layer.stories.tsx | 6 ++- .../cesium-map/layers/wmts.layer.stories.tsx | 6 ++- .../cesium-map/layers/xyz.layer.stories.tsx | 6 ++- .../legend/legends-sidebar.stories.tsx | 3 ++ .../src/components/cesium-map/map.stories.tsx | 48 ++++++++++++++++--- .../terrain-provider-heights-tool.stories.tsx | 3 ++ .../terrain-provider.stories.tsx | 3 ++ 19 files changed, 199 insertions(+), 37 deletions(-) diff --git a/packages/react-components/src/components/cesium-map/context-menu.stories.tsx b/packages/react-components/src/components/cesium-map/context-menu.stories.tsx index 3cf350bb..496e2e31 100644 --- a/packages/react-components/src/components/cesium-map/context-menu.stories.tsx +++ b/packages/react-components/src/components/cesium-map/context-menu.stories.tsx @@ -307,6 +307,8 @@ export const MapWithLayersManagerAndContextMenu: Story = () => { // @ts-ignore imageryContextMenu={} imageryContextMenuSize={{ height: 340, width: 200 }} + layerManagerLayerIdMetaFieldPath={'id'} + layerManagerLayerNameMetaFieldPath={'layerRecord.productName'} layerManagerFootprintMetaFieldPath={'layerRecord.footprint'} > diff --git a/packages/react-components/src/components/cesium-map/data-sources/drawings.data-source.stories.tsx b/packages/react-components/src/components/cesium-map/data-sources/drawings.data-source.stories.tsx index 57cf482c..2a432386 100644 --- a/packages/react-components/src/components/cesium-map/data-sources/drawings.data-source.stories.tsx +++ b/packages/react-components/src/components/cesium-map/data-sources/drawings.data-source.stories.tsx @@ -136,7 +136,14 @@ export const Drawings: Story = (args) => { Draw rectangle by coordinates
- + (
- +

Hello!

diff --git a/packages/react-components/src/components/cesium-map/entities/entity.stories.tsx b/packages/react-components/src/components/cesium-map/entities/entity.stories.tsx index d0f3c370..466524ea 100644 --- a/packages/react-components/src/components/cesium-map/entities/entity.stories.tsx +++ b/packages/react-components/src/components/cesium-map/entities/entity.stories.tsx @@ -64,7 +64,11 @@ const CanvasEntity: React.FC = (props) => { export const Basic: Story = (args) => (
- + = (args) => { const [count, setCount] = useState(0); return ( - + = (args) => { }; export const AnimatedCanvas: Story = () => ( - + ); diff --git a/packages/react-components/src/components/cesium-map/layers-manager.ts b/packages/react-components/src/components/cesium-map/layers-manager.ts index 3236d45d..c0995f85 100644 --- a/packages/react-components/src/components/cesium-map/layers-manager.ts +++ b/packages/react-components/src/components/cesium-map/layers-manager.ts @@ -34,11 +34,11 @@ import { CesiumCartesian2, CesiumImageryProvider } from './proxied.types'; const INC = 1; const DEC = -1; -const DEFAULT_LAYER_ID_META_FIELD_PATH = 'id'; -const DEFAULT_LAYER_NAME_META_FIELD_PATH = 'layerRecord.productName'; -const DEFAULT_DATA_LAYER_NAME_META_FIELD_PATH = 'layerRecord.featureStructure.aliasLayerName'; -const DEFAULT_DATA_LAYER_FIELDS_META_FIELD_PATH = 'layerRecord.featureStructure.fields'; -const DEFAULT_FOOTPRINT_META_FIELD_PATH = 'layerRecord.footprint'; +// const DEFAULT_LAYER_ID_META_FIELD_PATH = 'id'; +// const DEFAULT_LAYER_NAME_META_FIELD_PATH = 'layerRecord.productName'; +// const DEFAULT_DATA_LAYER_NAME_META_FIELD_PATH = 'layerRecord.featureStructure.aliasLayerName'; +// const DEFAULT_DATA_LAYER_FIELDS_META_FIELD_PATH = 'layerRecord.featureStructure.fields'; +// const DEFAULT_FOOTPRINT_META_FIELD_PATH = 'layerRecord.footprint'; export interface ILayerManagerMetaFieldPaths { layerIdMetaFieldPath: string; @@ -49,11 +49,11 @@ export interface ILayerManagerMetaFieldPaths { } const layerManagerMetaFieldPaths: ILayerManagerMetaFieldPaths = { - layerIdMetaFieldPath: DEFAULT_LAYER_ID_META_FIELD_PATH, - layerNameMetaFieldPath: DEFAULT_LAYER_NAME_META_FIELD_PATH, - dataLayerNameMetaFieldPath: DEFAULT_DATA_LAYER_NAME_META_FIELD_PATH, - dataLayerFieldsMetaFieldPath: DEFAULT_DATA_LAYER_FIELDS_META_FIELD_PATH, - footprintMetaFieldPath: DEFAULT_FOOTPRINT_META_FIELD_PATH, + layerIdMetaFieldPath: '', + layerNameMetaFieldPath: '', + dataLayerNameMetaFieldPath: '', + dataLayerFieldsMetaFieldPath: '', + footprintMetaFieldPath: '', }; const configureLayerManagerMetaFieldPaths = ( diff --git a/packages/react-components/src/components/cesium-map/layers/3d.tileset.stories.tsx b/packages/react-components/src/components/cesium-map/layers/3d.tileset.stories.tsx index 86c904d7..4b30afc1 100644 --- a/packages/react-components/src/components/cesium-map/layers/3d.tileset.stories.tsx +++ b/packages/react-components/src/components/cesium-map/layers/3d.tileset.stories.tsx @@ -26,7 +26,12 @@ const ArcGisProvider = new ArcGISTiledElevationTerrainProvider({ export const Cesium3DTilesetLayer: Story = (args: Record) => (
- + ) => (
- + ) => (
- + (
- + (
- +
@@ -72,7 +77,12 @@ export const MapWithSettings: Story = () => { return (
- +
diff --git a/packages/react-components/src/components/cesium-map/layers/optimized-tile-requests.stories.tsx b/packages/react-components/src/components/cesium-map/layers/optimized-tile-requests.stories.tsx index 4b45d773..704b6a21 100644 --- a/packages/react-components/src/components/cesium-map/layers/optimized-tile-requests.stories.tsx +++ b/packages/react-components/src/components/cesium-map/layers/optimized-tile-requests.stories.tsx @@ -120,7 +120,13 @@ const LayersContainer: React.FC = () => { export const OptimizedTileRequestingMap: Story = () => { return (
- +
diff --git a/packages/react-components/src/components/cesium-map/layers/osm.layer.stories.tsx b/packages/react-components/src/components/cesium-map/layers/osm.layer.stories.tsx index 3342bf9f..8e065b8c 100644 --- a/packages/react-components/src/components/cesium-map/layers/osm.layer.stories.tsx +++ b/packages/react-components/src/components/cesium-map/layers/osm.layer.stories.tsx @@ -30,7 +30,14 @@ export const MapWithOSMLayers: Story = (args) => { const [center] = useState<[number, number]>([34.82, 32.04]); return (
- + diff --git a/packages/react-components/src/components/cesium-map/layers/wfs.layer.stories.tsx b/packages/react-components/src/components/cesium-map/layers/wfs.layer.stories.tsx index 5aa3ea04..c1f5289f 100644 --- a/packages/react-components/src/components/cesium-map/layers/wfs.layer.stories.tsx +++ b/packages/react-components/src/components/cesium-map/layers/wfs.layer.stories.tsx @@ -61,7 +61,15 @@ const BRIGHT_PURPLE = '#B734EB'; export const MapWithPPWFSLayer: Story = (args: Record) => { return (
- + ) => { return (
- + ) return (
- + ) => { return (
- + (
- + diff --git a/packages/react-components/src/components/cesium-map/layers/wmts.layer.stories.tsx b/packages/react-components/src/components/cesium-map/layers/wmts.layer.stories.tsx index be69d27b..7e90afd4 100644 --- a/packages/react-components/src/components/cesium-map/layers/wmts.layer.stories.tsx +++ b/packages/react-components/src/components/cesium-map/layers/wmts.layer.stories.tsx @@ -42,7 +42,11 @@ const optionsWMTS2 = { export const MapWithWMTSLayers: Story = () => (
- + diff --git a/packages/react-components/src/components/cesium-map/layers/xyz.layer.stories.tsx b/packages/react-components/src/components/cesium-map/layers/xyz.layer.stories.tsx index 564c758a..076a1125 100644 --- a/packages/react-components/src/components/cesium-map/layers/xyz.layer.stories.tsx +++ b/packages/react-components/src/components/cesium-map/layers/xyz.layer.stories.tsx @@ -27,7 +27,11 @@ const optionsXYZ2 = { export const MapWithXYZLayers: Story = () => (
- + diff --git a/packages/react-components/src/components/cesium-map/legend/legends-sidebar.stories.tsx b/packages/react-components/src/components/cesium-map/legend/legends-sidebar.stories.tsx index f256b88c..811041c3 100644 --- a/packages/react-components/src/components/cesium-map/legend/legends-sidebar.stories.tsx +++ b/packages/react-components/src/components/cesium-map/legend/legends-sidebar.stories.tsx @@ -49,6 +49,9 @@ export const MapWithLegends: Story = () => { title: 'Map Legends', emptyText: 'No legends for this basemap', }} + layerManagerLayerIdMetaFieldPath={'id'} + layerManagerLayerNameMetaFieldPath={'layerRecord.productName'} + layerManagerFootprintMetaFieldPath={'layerRecord.footprint'} > diff --git a/packages/react-components/src/components/cesium-map/map.stories.tsx b/packages/react-components/src/components/cesium-map/map.stories.tsx index f20c04b7..342174f5 100644 --- a/packages/react-components/src/components/cesium-map/map.stories.tsx +++ b/packages/react-components/src/components/cesium-map/map.stories.tsx @@ -222,7 +222,13 @@ const LOCALIZED_GEOCODER_OPTIONS = [ export const BaseMap: Story = (args: CesiumMapProps) => (
- + +
); @@ -258,7 +264,13 @@ BaseMap.argTypes = { export const ZoomedMap: Story = (args: CesiumMapProps) => (
- + +
); @@ -289,7 +301,13 @@ const cesiumTheme = { export const GeocoderPanel: Story = (args: CesiumMapProps) => (
- + +
); @@ -324,7 +342,13 @@ GeocoderPanel.storyName = 'Geocoder'; export const MapWithProjection: Story = (args: CesiumMapProps) => (
- + +
); @@ -354,7 +378,13 @@ MapWithProjection.argTypes = { export const Map2DWithProjection: Story = (args: CesiumMapProps) => (
- + +
); @@ -388,7 +418,13 @@ Map2DWithProjection.storyName = '2D Map With Projection'; export const LocalizedMap: Story = (args: CesiumMapProps) => (
- + +
); diff --git a/packages/react-components/src/components/cesium-map/terrain-providers/terrain-provider-heights-tool.stories.tsx b/packages/react-components/src/components/cesium-map/terrain-providers/terrain-provider-heights-tool.stories.tsx index a0bfafad..ecb5e3ba 100644 --- a/packages/react-components/src/components/cesium-map/terrain-providers/terrain-provider-heights-tool.stories.tsx +++ b/packages/react-components/src/components/cesium-map/terrain-providers/terrain-provider-heights-tool.stories.tsx @@ -119,6 +119,9 @@ export const QuantizedMeshHeightsTool: Story = () => { zoom={5} imageryProvider={false} baseMaps={BASE_MAPS} + layerManagerLayerIdMetaFieldPath={'id'} + layerManagerLayerNameMetaFieldPath={'layerRecord.productName'} + layerManagerFootprintMetaFieldPath={'layerRecord.footprint'} > { imageryProvider={false} baseMaps={BASE_MAPS} mapProjection={new WebMercatorProjection()} + layerManagerLayerIdMetaFieldPath={'id'} + layerManagerLayerNameMetaFieldPath={'layerRecord.productName'} + layerManagerFootprintMetaFieldPath={'layerRecord.footprint'} > Date: Fri, 26 Jun 2026 08:15:08 +0300 Subject: [PATCH 39/46] fix: active layers update when reordering layers on the map --- .../active-layers/active-layers-panel.tsx | 71 +++++++++---------- 1 file changed, 32 insertions(+), 39 deletions(-) diff --git a/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.tsx b/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.tsx index 3f0a9a92..4dd282e9 100644 --- a/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.tsx +++ b/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.tsx @@ -144,62 +144,55 @@ export const ActiveLayersPanel: React.FC = ({ locale }) }); }; + const refreshSections = (): void => { + setSections([ + { + id: IMAGERY, + values: getImageryLayers(), + }, + { + id: SERVICE, + values: getServiceLayers(), + }, + { + id: DATA, + values: getDataLayers(), + }, + { + id: THREE_D, + values: get3DModels(), + }, + ]); + }; + useEffect(() => { - const updateSections = () => { - const newSections = [ - { - id: IMAGERY, - values: getImageryLayers() - }, - { - id: SERVICE, - values: getServiceLayers() - }, - { - id: DATA, - values: getDataLayers() - }, - { - id: THREE_D, - values: get3DModels() - }, - ]; - setSections(newSections); - setCollapsedSections(newSections.reduce((acc, section) => ({ ...acc, [section.id]: true }), {})); - }; - updateSections(); + refreshSections(); + setCollapsedSections({ + [IMAGERY]: true, + [SERVICE]: true, + [DATA]: true, + [THREE_D]: true, + }); }, []); useEffect(() => { if (!mapViewer.layersManager) { return; } const handleLayerEvent = (): void => { - setSections((prev) => - prev.map((item) => - item.id === IMAGERY - ? { - ...item, - values: getImageryLayers() - } - : item.id === SERVICE - ? { - ...item, - values: getServiceLayers() - } - : item - ) - ); + refreshSections(); }; mapViewer.layersManager.addLayerUpdatedListener(handleLayerEvent); mapViewer.imageryLayers.layerAdded.addEventListener(handleLayerEvent); mapViewer.imageryLayers.layerRemoved.addEventListener(handleLayerEvent); + mapViewer.imageryLayers.layerMoved.addEventListener(handleLayerEvent); return () => { if (get(mapViewer, '_cesiumWidget') !== undefined) { mapViewer.layersManager?.removeLayerUpdatedListener(handleLayerEvent); mapViewer.imageryLayers.layerAdded.removeEventListener(handleLayerEvent); mapViewer.imageryLayers.layerRemoved.removeEventListener(handleLayerEvent); + mapViewer.imageryLayers.layerMoved.removeEventListener(handleLayerEvent); } }; - }, [mapViewer.layersManager?.layerList]); + }, [mapViewer.layersManager]); useEffect(() => { if (!mapViewer.layersManager) { return; } From 07a359c09a887bb1b56a792ae7589c54a7e28f6b Mon Sep 17 00:00:00 2001 From: MARTIROSYAN ELLA Date: Fri, 26 Jun 2026 08:37:36 +0300 Subject: [PATCH 40/46] fix: acdebugger optimization section layers order --- .../src/components/cesium-map/context-menu.stories.tsx | 1 + .../src/components/cesium-map/debug/debugger-widget.tsx | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/packages/react-components/src/components/cesium-map/context-menu.stories.tsx b/packages/react-components/src/components/cesium-map/context-menu.stories.tsx index 496e2e31..21deac33 100644 --- a/packages/react-components/src/components/cesium-map/context-menu.stories.tsx +++ b/packages/react-components/src/components/cesium-map/context-menu.stories.tsx @@ -307,6 +307,7 @@ export const MapWithLayersManagerAndContextMenu: Story = () => { // @ts-ignore imageryContextMenu={} imageryContextMenuSize={{ height: 340, width: 200 }} + showDebuggerTool={true} layerManagerLayerIdMetaFieldPath={'id'} layerManagerLayerNameMetaFieldPath={'layerRecord.productName'} layerManagerFootprintMetaFieldPath={'layerRecord.footprint'} diff --git a/packages/react-components/src/components/cesium-map/debug/debugger-widget.tsx b/packages/react-components/src/components/cesium-map/debug/debugger-widget.tsx index b5f94d39..c04da588 100644 --- a/packages/react-components/src/components/cesium-map/debug/debugger-widget.tsx +++ b/packages/react-components/src/components/cesium-map/debug/debugger-widget.tsx @@ -96,6 +96,12 @@ const DebuggerComponent: React.FC = ({ locale, isOpen, set const removeMoveEnd = mapViewer.camera.moveEnd.addEventListener(() => { scheduleLayerMetaRefresh(); }); + const removeLayerMoved = mapViewer.imageryLayers.layerMoved.addEventListener(() => { + scheduleLayerMetaRefresh(); + }); + const removeLayerAdded = mapViewer.imageryLayers.layerAdded.addEventListener(() => { + scheduleLayerMetaRefresh(); + }); const removeLayerRemoved = mapViewer.imageryLayers.layerRemoved.addEventListener(() => { scheduleLayerMetaRefresh(); }); @@ -106,6 +112,8 @@ const DebuggerComponent: React.FC = ({ locale, isOpen, set } removeTileLoad(); removeMoveEnd(); + removeLayerMoved(); + removeLayerAdded(); removeLayerRemoved(); mapViewer.layersManager?.removeLayerUpdatedListener(updateLayersMeta); }; From fbafacfe77a0f34bdfb138b48bc4cfe71edbbec6 Mon Sep 17 00:00:00 2001 From: MARTIROSYAN ELLA Date: Fri, 26 Jun 2026 09:08:50 +0300 Subject: [PATCH 41/46] fix: inspector tool moved to debugger panel --- .../terrain-provider-heights-tool.stories.tsx | 3 +-- .../terrain-providers/terrain-provider.stories.tsx | 14 +++++++------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/packages/react-components/src/components/cesium-map/terrain-providers/terrain-provider-heights-tool.stories.tsx b/packages/react-components/src/components/cesium-map/terrain-providers/terrain-provider-heights-tool.stories.tsx index ecb5e3ba..0d6eb6cf 100644 --- a/packages/react-components/src/components/cesium-map/terrain-providers/terrain-provider-heights-tool.stories.tsx +++ b/packages/react-components/src/components/cesium-map/terrain-providers/terrain-provider-heights-tool.stories.tsx @@ -11,7 +11,6 @@ import { getValue } from '../../utils/config'; import { BASE_MAPS } from '../helpers/constants'; import { Cesium3DTileset } from '../layers'; import { CesiumMap, CesiumViewer, useCesiumMap } from '../map'; -import { InspectorTool } from '../tools/inspector.tool'; import { TerrainianHeightTool } from '../tools/terranian-height.tool'; export default { @@ -119,6 +118,7 @@ export const QuantizedMeshHeightsTool: Story = () => { zoom={5} imageryProvider={false} baseMaps={BASE_MAPS} + showDebuggerTool={true} layerManagerLayerIdMetaFieldPath={'id'} layerManagerLayerNameMetaFieldPath={'layerRecord.productName'} layerManagerFootprintMetaFieldPath={'layerRecord.footprint'} @@ -130,7 +130,6 @@ export const QuantizedMeshHeightsTool: Story = () => { /> -
); diff --git a/packages/react-components/src/components/cesium-map/terrain-providers/terrain-provider.stories.tsx b/packages/react-components/src/components/cesium-map/terrain-providers/terrain-provider.stories.tsx index de12cead..821f9e7c 100644 --- a/packages/react-components/src/components/cesium-map/terrain-providers/terrain-provider.stories.tsx +++ b/packages/react-components/src/components/cesium-map/terrain-providers/terrain-provider.stories.tsx @@ -14,7 +14,6 @@ import { getValue } from '../../utils/config'; import { BASE_MAPS } from '../helpers/constants'; import { Cesium3DTileset } from '../layers'; import { CesiumMap, useCesiumMap } from '../map'; -import { InspectorTool } from '../tools/inspector.tool'; import QuantizedMeshTerrainProvider from './custom/quantized-mesh-terrain-provider'; export default { @@ -105,7 +104,11 @@ const TerrainProviderSelector: React.FC = ({ terr }; return ( - <> +
+
+ + +
-
- - - +
); }; @@ -135,6 +135,7 @@ export const QuantizedMeshProviders: Story = () => { imageryProvider={false} baseMaps={BASE_MAPS} mapProjection={new WebMercatorProjection()} + showDebuggerTool={true} layerManagerLayerIdMetaFieldPath={'id'} layerManagerLayerNameMetaFieldPath={'layerRecord.productName'} layerManagerFootprintMetaFieldPath={'layerRecord.footprint'} @@ -145,7 +146,6 @@ export const QuantizedMeshProviders: Story = () => { isZoomTo={true} /> -
); From 21d1a5d900c9f27e9f8e81953afb30af9c3e5bca Mon Sep 17 00:00:00 2001 From: ellamartirosyan Date: Sun, 28 Jun 2026 09:31:33 +0300 Subject: [PATCH 42/46] fix: rerun optimization when layer order changes --- .../src/components/cesium-map/layers-manager.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/packages/react-components/src/components/cesium-map/layers-manager.ts b/packages/react-components/src/components/cesium-map/layers-manager.ts index c0995f85..47b7907d 100644 --- a/packages/react-components/src/components/cesium-map/layers-manager.ts +++ b/packages/react-components/src/components/cesium-map/layers-manager.ts @@ -409,6 +409,7 @@ class LayerManager { } this.updateLayersOrder(layerId, order, order + positions); + this.reinvokeOptimizationAfterOrderChange(); } public lower(layerId: string, positions = 1): void { @@ -428,6 +429,7 @@ class LayerManager { } this.updateLayersOrder(layerId, order, order - positions); + this.reinvokeOptimizationAfterOrderChange(); } public raiseToTop(layerId: string): void { @@ -439,6 +441,7 @@ class LayerManager { } this.updateLayersOrder(layerId, order, this.mapViewer.imageryLayers.length - this.getBaseLayersCount() - 1); + this.reinvokeOptimizationAfterOrderChange(); } public lowerToBottom(layerId: string): void { @@ -755,6 +758,14 @@ class LayerManager { this.relevancyLayerUpdatedHandler = undefined; } + private reinvokeOptimizationAfterOrderChange(): void { + if (!this.shouldOptimizedTileRequests) { + return; + } + this.markRelevantLayersForExtent(); + this.hideNonRelevantLayers(); + } + private markRelevantLayersForExtent(): void { try { const extent = this.mapViewer.camera.computeViewRectangle() as Rectangle; From e397e89bccc65e200410ff8b2396b17641b1982d Mon Sep 17 00:00:00 2001 From: ellamartirosyan Date: Sun, 28 Jun 2026 16:48:02 +0300 Subject: [PATCH 43/46] fix: code review --- .../active-layers/active-layers-panel.tsx | 17 ++-- .../cesium-map/context-menu.stories.tsx | 90 +++++++++++++++---- .../cesium-map/debug/debugger-widget.css | 56 ++++++++++-- .../cesium-map/debug/debugger-widget.tsx | 82 +++++++++-------- 4 files changed, 178 insertions(+), 67 deletions(-) diff --git a/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.tsx b/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.tsx index 4dd282e9..6f1510ea 100644 --- a/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.tsx +++ b/packages/react-components/src/components/cesium-map/active-layers/active-layers-panel.tsx @@ -45,16 +45,17 @@ interface IActiveLayersPanelProps { locale?: { [key: string]: string }; } -const GENERIC_PATH_SEGMENTS = new Set(['data', 'act', 'assets', 'cesium', 'tiles', 'tileset', '3d', 'model', 'models']); - const extractModelName = (rawUrl: string): string => { try { const { hostname, pathname } = new URL(rawUrl); - const segments = pathname.split('/').filter((s) => s.length > 0 && !s.includes('.')); - const named = [...segments] - .reverse() - .find((s) => !GENERIC_PATH_SEGMENTS.has(s.toLowerCase()) && /[a-zA-Z]/.test(s)); - return named ?? hostname; + const segments = pathname.split('/').filter((s) => s.length > 0); + if (segments.length >= 2) { + return `${segments[segments.length - 2]}/${segments[segments.length - 1]}`; + } + if (segments.length === 1) { + return segments[0]; + } + return hostname; } catch { return rawUrl; } @@ -134,7 +135,7 @@ export const ActiveLayersPanel: React.FC = ({ locale }) const get3DModels = (): IActiveLayer[] => { return (mapViewer.layersManager?.modelList ?? []).map((model, index): IActiveLayer => { const modelUrl = get(model.tileset, 'resource.url') as string | undefined; - const modelName = (getLayerName(model) ?? extractModelName(modelUrl ?? `Model #${String(index + 1)}`)) as string; + const modelName = getLayerName(model) ?? extractModelName(modelUrl ?? `Model #${String(index + 1)}`); return { id: (getLayerId(model) as string) ?? `3D_MODEL_${String(index)}`, name: modelName, diff --git a/packages/react-components/src/components/cesium-map/context-menu.stories.tsx b/packages/react-components/src/components/cesium-map/context-menu.stories.tsx index 21deac33..f3e13c07 100644 --- a/packages/react-components/src/components/cesium-map/context-menu.stories.tsx +++ b/packages/react-components/src/components/cesium-map/context-menu.stories.tsx @@ -1,6 +1,6 @@ import React, { useEffect, useState } from 'react'; import { get } from 'lodash'; -import { Menu, MenuItem, MenuSurfaceAnchor } from '@map-colonies/react-core'; +import { Button, Menu, MenuItem, MenuSurfaceAnchor } from '@map-colonies/react-core'; import { Story, Meta } from '@storybook/react'; import { Box } from '../box'; import { BASE_MAPS } from './helpers/constants'; @@ -232,10 +232,57 @@ const LayersMozaik: React.FC = (props) => { setAllShow(!allShow); }; + const controlsContainerStyle = { + display: 'flex', + flexWrap: 'wrap' as const, + alignItems: 'center', + gap: '8px', + padding: '10px 12px', + borderRadius: '10px', + background: 'rgba(0, 0, 0, 0.75)', + border: '1px solid rgba(255, 255, 255, 0.2)', + color: 'white', + position: 'absolute' as const, + top: '10px', + left: '50%', + transform: 'translateX(-50%)', + zIndex: 2, + maxWidth: 'calc(100% - 20px)', + }; + + const messageStyle = { + margin: 0, + color: '#8dff9f', + fontSize: '18px', + fontWeight: 700, + flexBasis: '100%', + }; + + const fieldStyle = { + height: '30px', + borderRadius: '6px', + border: '1px solid rgba(255, 255, 255, 0.3)', + background: 'rgba(255, 255, 255, 0.08)', + color: 'white', + padding: '0 8px', + }; + + const buttonStyle = { + height: '30px', + borderRadius: '6px', + border: '1px solid rgba(255, 255, 255, 0.25)', + background: 'rgba(96, 165, 250, 0.25)', + color: 'white', + padding: '0 10px', + cursor: 'pointer', + fontWeight: 600, + }; + return ( -
-

Change BASE MAP to see effective layers

+
+

Change BASE MAP to see effective layers

{ setTimes(parseInt(evt.target.value)); }} > - - - - - +
); }; @@ -317,3 +375,5 @@ export const MapWithLayersManagerAndContextMenu: Story = () => {
); }; + +MapWithLayersManagerAndContextMenu.storyName = 'Layers Manager and Context Menu'; diff --git a/packages/react-components/src/components/cesium-map/debug/debugger-widget.css b/packages/react-components/src/components/cesium-map/debug/debugger-widget.css index 2a5f2d49..ce02ab8a 100644 --- a/packages/react-components/src/components/cesium-map/debug/debugger-widget.css +++ b/packages/react-components/src/components/cesium-map/debug/debugger-widget.css @@ -74,8 +74,45 @@ body[dir='rtl'] .cesium-viewer .debuggerWidgetSectionHeaderToggle { cursor: pointer; } -.cesium-viewer .debuggerLayerList { +.cesium-viewer .debuggerLayers { margin-top: 8px; + display: flex; + align-items: stretch; + gap: 8px; +} + +.cesium-viewer .debuggerLayersArrow { + width: 32px; + display: flex; + flex-direction: column; + align-items: center; + color: var(--mdc-theme-cesium-color); + opacity: 0.85; + padding-top: 13px; +} + +.cesium-viewer .debuggerLayersArrowLine { + position: relative; + flex: 1; + width: 1px; + margin: 0; + background: linear-gradient(to bottom, rgba(170, 238, 255, 0.95), rgba(174, 238, 255, 0.1)); +} + +.cesium-viewer .debuggerLayersArrowLine::before { + content: ''; + position: absolute; + top: -6px; + left: 50%; + transform: translateX(-50%); + width: 0; + height: 0; + border-left: 4px solid transparent; + border-right: 4px solid transparent; + border-bottom: 6px solid rgba(170, 238, 255, 0.95); +} + +.cesium-viewer .debuggerLayersList { display: flex; flex-direction: column; gap: 4px; @@ -86,6 +123,8 @@ body[dir='rtl'] .cesium-viewer .debuggerWidgetSectionHeaderToggle { } .cesium-viewer .debuggerLayerItem { + display: flex; + align-items: center; font-size: 11px; line-height: 1.6; font-weight: bold; @@ -93,11 +132,18 @@ body[dir='rtl'] .cesium-viewer .debuggerWidgetSectionHeaderToggle { cursor: text; } +.cesium-viewer .debuggerLayerItem[data-has-tooltip='true'] { + cursor: default; +} + +.cesium-viewer .debuggerLayerText { + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + .cesium-viewer .debuggerLayerItem.relevant { color: #AEF; opacity: 1; } - -.cesium-viewer .debuggerLayerItem[data-has-tooltip='true'] { - cursor: default; -} diff --git a/packages/react-components/src/components/cesium-map/debug/debugger-widget.tsx b/packages/react-components/src/components/cesium-map/debug/debugger-widget.tsx index c04da588..a831416a 100644 --- a/packages/react-components/src/components/cesium-map/debug/debugger-widget.tsx +++ b/packages/react-components/src/components/cesium-map/debug/debugger-widget.tsx @@ -210,47 +210,51 @@ const DebuggerComponent: React.FC = ({ locale, isOpen, set }} /> {viewState?.shouldOptimizedTileRequests === true && ( - - {[...layersMeta].reverse().map((layer) => { - const idText = layer.layerId; - const nameText = layer.layerName ?? idText; - const statusText = - layer.meta?.isRelevantToExtent === true ? ' → show' : layer.meta?.isRelevantToExtent === false ? ' → hide' : ''; - const hasTransparency = layer.meta[HAS_TRANSPARENCY_META_PROP] as boolean | undefined; - const transparencyText = - hasTransparency === true ? withTransparencyTiles : hasTransparency === false ? withoutTransparencyTiles : ''; - const tileCoordinatesFromMeta = layer.meta[EXAMINED_TILES_META_PROP] as - | Array<{ x?: number; y?: number; level?: number }> - | { x?: number; y?: number; level?: number } - | undefined; - const tileCoordinatesList = Array.isArray(tileCoordinatesFromMeta) - ? tileCoordinatesFromMeta - : tileCoordinatesFromMeta !== undefined - ? [tileCoordinatesFromMeta] - : []; - const formattedTileCoordinates = tileCoordinatesList - .filter((tile) => tile.x !== undefined && tile.y !== undefined && tile.level !== undefined) - .map((tile) => `( L: ${String(tile.level)}, X: ${String(tile.x)}, Y: ${String(tile.y)} )`); - const tooltipContent = - transparencyText === '' - ? undefined - : {transparencyText}: {formattedTileCoordinates.join(', ')}; - const isRelevant = layer.meta?.isRelevantToExtent !== false; - if (tooltipContent === undefined) { - return ( - - {nameText + statusText} + + + + + + {[...layersMeta].reverse().map((layer) => { + const idText = layer.layerId; + const nameText = layer.layerName ?? idText; + const statusText = + layer.meta?.isRelevantToExtent === true ? ' → show' : layer.meta?.isRelevantToExtent === false ? ' → hide' : ''; + const hasTransparency = layer.meta[HAS_TRANSPARENCY_META_PROP] as boolean | undefined; + const transparencyText = + hasTransparency === true ? withTransparencyTiles : hasTransparency === false ? withoutTransparencyTiles : ''; + const tileCoordinatesFromMeta = layer.meta[EXAMINED_TILES_META_PROP] as + | Array<{ x?: number; y?: number; level?: number }> + | { x?: number; y?: number; level?: number } + | undefined; + const tileCoordinatesList = Array.isArray(tileCoordinatesFromMeta) + ? tileCoordinatesFromMeta + : tileCoordinatesFromMeta !== undefined + ? [tileCoordinatesFromMeta] + : []; + const formattedTileCoordinates = tileCoordinatesList + .filter((tile) => tile.x !== undefined && tile.y !== undefined && tile.level !== undefined) + .map((tile) => `( L: ${String(tile.level)}, X: ${String(tile.x)}, Y: ${String(tile.y)} )`); + const tooltipContent = + transparencyText === '' + ? undefined + : {transparencyText}: {formattedTileCoordinates.join(', ')}; + const isRelevant = layer.meta?.isRelevantToExtent !== false; + const itemContent = ( + + {nameText + statusText} ); - } - return ( - - - {nameText + statusText} - - - ); - })} + if (tooltipContent === undefined) { + return {itemContent}; + } + return ( + + {itemContent} + + ); + })} + )} From c9d26920883f90340189241b3545183507207024 Mon Sep 17 00:00:00 2001 From: ellamartirosyan Date: Sun, 28 Jun 2026 18:24:10 +0300 Subject: [PATCH 44/46] fix: ol map stories --- .../components/ol-map/source/stories/legend.stories.tsx | 2 +- .../src/components/ol-map/source/stories/mvt.stories.tsx | 2 +- .../ol-map/source/stories/vector-source.stories.tsx | 2 +- .../src/components/ol-map/source/stories/wms.stories.tsx | 2 +- .../src/components/ol-map/source/stories/wmts.stories.tsx | 2 +- .../src/components/ol-map/source/stories/xyz.stories.tsx | 2 +- .../src/components/ol-map/stories/map.stories.tsx | 7 +++---- 7 files changed, 9 insertions(+), 10 deletions(-) diff --git a/packages/react-components/src/components/ol-map/source/stories/legend.stories.tsx b/packages/react-components/src/components/ol-map/source/stories/legend.stories.tsx index d04d6adf..c320ecac 100644 --- a/packages/react-components/src/components/ol-map/source/stories/legend.stories.tsx +++ b/packages/react-components/src/components/ol-map/source/stories/legend.stories.tsx @@ -11,7 +11,7 @@ import { TileOsm } from '..'; import { VectorSource } from '../vector-source'; export default { - title: 'Map/Map Tiles/Legend', + title: 'OL Map/Map/Tiles/Legend', component: Legend, subcomponents: GeoJSONFeature, parameters: { diff --git a/packages/react-components/src/components/ol-map/source/stories/mvt.stories.tsx b/packages/react-components/src/components/ol-map/source/stories/mvt.stories.tsx index fbf630ec..39034dc7 100644 --- a/packages/react-components/src/components/ol-map/source/stories/mvt.stories.tsx +++ b/packages/react-components/src/components/ol-map/source/stories/mvt.stories.tsx @@ -8,7 +8,7 @@ import { MVTSource, getMVTOptions } from '../mvt'; import { TileOsm } from '..'; export default { - title: 'Map/Map Tiles/MVT', + title: 'OL Map/Map/Tiles/MVT', component: VectorTileLayer, subcomponents: MVTSource, parameters: { diff --git a/packages/react-components/src/components/ol-map/source/stories/vector-source.stories.tsx b/packages/react-components/src/components/ol-map/source/stories/vector-source.stories.tsx index 1412db9d..6a3dfd19 100644 --- a/packages/react-components/src/components/ol-map/source/stories/vector-source.stories.tsx +++ b/packages/react-components/src/components/ol-map/source/stories/vector-source.stories.tsx @@ -9,7 +9,7 @@ import { TileOsm } from '..'; import { VectorSource } from '../vector-source'; export default { - title: 'Map/Map Tiles/Geojson', + title: 'OL Map/Map/Tiles/Geojson', component: VectorLayer, subcomponents: GeoJSONFeature, parameters: { diff --git a/packages/react-components/src/components/ol-map/source/stories/wms.stories.tsx b/packages/react-components/src/components/ol-map/source/stories/wms.stories.tsx index f67fc935..985d2b82 100644 --- a/packages/react-components/src/components/ol-map/source/stories/wms.stories.tsx +++ b/packages/react-components/src/components/ol-map/source/stories/wms.stories.tsx @@ -19,7 +19,7 @@ const mapDivStyle = { }; const story = { - title: 'Map/Map Tiles/WMS', + title: 'OL Map/Map/Tiles/WMS', component: TileWMS, }; export default story; diff --git a/packages/react-components/src/components/ol-map/source/stories/wmts.stories.tsx b/packages/react-components/src/components/ol-map/source/stories/wmts.stories.tsx index 332b98eb..0bab802e 100644 --- a/packages/react-components/src/components/ol-map/source/stories/wmts.stories.tsx +++ b/packages/react-components/src/components/ol-map/source/stories/wmts.stories.tsx @@ -32,7 +32,7 @@ const mapDivStyle = { }; const story = { - title: 'Map/Map Tiles/WMTS', + title: 'OL Map/Map/Tiles/WMTS', component: TileWMTS, }; diff --git a/packages/react-components/src/components/ol-map/source/stories/xyz.stories.tsx b/packages/react-components/src/components/ol-map/source/stories/xyz.stories.tsx index 3e70c727..907a6092 100644 --- a/packages/react-components/src/components/ol-map/source/stories/xyz.stories.tsx +++ b/packages/react-components/src/components/ol-map/source/stories/xyz.stories.tsx @@ -15,7 +15,7 @@ const mapDivStyle = { }; const story = { - title: 'Map/Map Tiles/XYZ', + title: 'OL Map/Map/Tiles/XYZ', component: TileXYZ, }; diff --git a/packages/react-components/src/components/ol-map/stories/map.stories.tsx b/packages/react-components/src/components/ol-map/stories/map.stories.tsx index ec7e6f49..cab89346 100644 --- a/packages/react-components/src/components/ol-map/stories/map.stories.tsx +++ b/packages/react-components/src/components/ol-map/stories/map.stories.tsx @@ -6,7 +6,7 @@ import { TileLayer } from '../layers'; import { Proj } from '../../utils/projections'; export default { - title: 'Map', + title: 'OL Map/Map', component: Map, parameters: { layout: 'fullscreen', @@ -49,12 +49,11 @@ BaseMap.argTypes = { export const ConfiguredMap: Story = () => (
- +
); - -ConfiguredMap.storyName = 'with zoom and center'; +ConfiguredMap.storyName = 'Map with Zoom and Center'; From bb3ad187c4b648273941ed210ba93640dce00af8 Mon Sep 17 00:00:00 2001 From: ellamartirosyan Date: Sun, 28 Jun 2026 19:13:26 +0300 Subject: [PATCH 45/46] fix: ol map stories --- .../ol-map/source/stories/mvt.stories.tsx | 5 +- .../source/stories/vector-source.stories.tsx | 90 ------------------- .../ol-map/source/stories/wms.stories.tsx | 6 +- .../ol-map/source/stories/wmts.stories.tsx | 6 +- .../ol-map/source/stories/xyz.stories.tsx | 6 +- .../{source => }/stories/legend.stories.tsx | 21 ++--- .../components/ol-map/stories/map.stories.tsx | 1 - 7 files changed, 22 insertions(+), 113 deletions(-) delete mode 100644 packages/react-components/src/components/ol-map/source/stories/vector-source.stories.tsx rename packages/react-components/src/components/ol-map/{source => }/stories/legend.stories.tsx (83%) diff --git a/packages/react-components/src/components/ol-map/source/stories/mvt.stories.tsx b/packages/react-components/src/components/ol-map/source/stories/mvt.stories.tsx index 39034dc7..afa5d511 100644 --- a/packages/react-components/src/components/ol-map/source/stories/mvt.stories.tsx +++ b/packages/react-components/src/components/ol-map/source/stories/mvt.stories.tsx @@ -1,4 +1,3 @@ -import React from 'react'; import { Style, Fill, Circle, Stroke } from 'ol/style'; import { Proj } from '../../../utils/projections'; import { VectorTileLayer } from '../../layers/vector-tile-layer'; @@ -8,7 +7,7 @@ import { MVTSource, getMVTOptions } from '../mvt'; import { TileOsm } from '..'; export default { - title: 'OL Map/Map/Tiles/MVT', + title: 'OL Map/Tiles/MVT', component: VectorTileLayer, subcomponents: MVTSource, parameters: { @@ -22,7 +21,7 @@ const mapDivStyle = { position: 'absolute' as const, }; -export const Basic = (): JSX.Element => ( +export const VectorTiles = (): JSX.Element => (
diff --git a/packages/react-components/src/components/ol-map/source/stories/vector-source.stories.tsx b/packages/react-components/src/components/ol-map/source/stories/vector-source.stories.tsx deleted file mode 100644 index 6a3dfd19..00000000 --- a/packages/react-components/src/components/ol-map/source/stories/vector-source.stories.tsx +++ /dev/null @@ -1,90 +0,0 @@ -import React from 'react'; -import { Geometries } from '@turf/helpers'; -import { Fill, Stroke, Style } from 'ol/style'; -import { Proj } from '../../../utils/projections'; -import { Map } from '../../map'; -import { TileLayer, VectorLayer } from '../../layers'; -import { GeoJSONFeature } from '../../feature'; -import { TileOsm } from '..'; -import { VectorSource } from '../vector-source'; - -export default { - title: 'OL Map/Map/Tiles/Geojson', - component: VectorLayer, - subcomponents: GeoJSONFeature, - parameters: { - layout: 'fullscreen', - }, -}; - -const geometries: Geometries[] = [ - { - type: 'Polygon', - coordinates: [ - [ - [3864197.52, 3750764.97], - [3884682.65, 3750764.98], - [3884682.65, 3766052.38], - [3864197.53, 3766052.38], - [3864197.52, 3750764.97], - ], - ], - }, - { - type: 'Polygon', - coordinates: [ - [ - [3904403.4, 3765899.51], - [3896912.58, 3758255.81], - [3905779.27, 3743579.9], - [3918162.07, 3755962.7], - [3904403.4, 3765899.51], - ], - ], - }, - { - type: 'LineString', - coordinates: [ - [3931767.86, 3763147.78], - [3931003.49, 3724776.39], - ], - }, - { - type: 'Point', - coordinates: [3890186.12, 3734254.58], - }, -]; - -const mapDivStyle = { - height: '100%', - width: '100%', - position: 'absolute' as const, -}; - -export const Basic = (): JSX.Element => ( -
- - - - - - - {geometries.map((geometry, index) => { - const selected_polygon_style = new Style({ - stroke: new Stroke({ - width: 5, - color: '#ff0000', - }), - fill: new Fill({ - color: '#aa2727', - }), - }); - let featStyle = index === 0 ? selected_polygon_style : undefined; - - return ; - })} - - - -
-); diff --git a/packages/react-components/src/components/ol-map/source/stories/wms.stories.tsx b/packages/react-components/src/components/ol-map/source/stories/wms.stories.tsx index 985d2b82..00344963 100644 --- a/packages/react-components/src/components/ol-map/source/stories/wms.stories.tsx +++ b/packages/react-components/src/components/ol-map/source/stories/wms.stories.tsx @@ -19,12 +19,12 @@ const mapDivStyle = { }; const story = { - title: 'OL Map/Map/Tiles/WMS', + title: 'OL Map/Tiles/WMS', component: TileWMS, }; export default story; -export const Basic: CSFStory = () => ( +export const WmsTiles: CSFStory = () => (
@@ -34,7 +34,7 @@ export const Basic: CSFStory = () => (
); -Basic.argTypes = { +WmsTiles.argTypes = { options: { description: `{ Options } from 'ol/source/TileWMS'`, table: { diff --git a/packages/react-components/src/components/ol-map/source/stories/wmts.stories.tsx b/packages/react-components/src/components/ol-map/source/stories/wmts.stories.tsx index 0bab802e..06248d20 100644 --- a/packages/react-components/src/components/ol-map/source/stories/wmts.stories.tsx +++ b/packages/react-components/src/components/ol-map/source/stories/wmts.stories.tsx @@ -32,13 +32,13 @@ const mapDivStyle = { }; const story = { - title: 'OL Map/Map/Tiles/WMTS', + title: 'OL Map/Tiles/WMTS', component: TileWMTS, }; export default story; -export const Basic: CSFStory = () => ( +export const WmtsTiles: CSFStory = () => (
@@ -54,7 +54,7 @@ export const Basic: CSFStory = () => (
); -Basic.argTypes = { +WmtsTiles.argTypes = { options: { description: `{ Options } from 'ol/source/WMTS'`, table: { diff --git a/packages/react-components/src/components/ol-map/source/stories/xyz.stories.tsx b/packages/react-components/src/components/ol-map/source/stories/xyz.stories.tsx index 907a6092..8c167fb1 100644 --- a/packages/react-components/src/components/ol-map/source/stories/xyz.stories.tsx +++ b/packages/react-components/src/components/ol-map/source/stories/xyz.stories.tsx @@ -15,13 +15,13 @@ const mapDivStyle = { }; const story = { - title: 'OL Map/Map/Tiles/XYZ', + title: 'OL Map/Tiles/XYZ', component: TileXYZ, }; export default story; -export const Basic: CSFStory = () => ( +export const XyzTiles: CSFStory = () => (
@@ -31,7 +31,7 @@ export const Basic: CSFStory = () => (
); -Basic.argTypes = { +XyzTiles.argTypes = { options: { description: `{ Options } from 'ol/source/XYZ'`, table: { diff --git a/packages/react-components/src/components/ol-map/source/stories/legend.stories.tsx b/packages/react-components/src/components/ol-map/stories/legend.stories.tsx similarity index 83% rename from packages/react-components/src/components/ol-map/source/stories/legend.stories.tsx rename to packages/react-components/src/components/ol-map/stories/legend.stories.tsx index c320ecac..efabe137 100644 --- a/packages/react-components/src/components/ol-map/source/stories/legend.stories.tsx +++ b/packages/react-components/src/components/ol-map/stories/legend.stories.tsx @@ -1,17 +1,16 @@ -import React from 'react'; import { Geometries } from '@turf/helpers'; import { Fill, Stroke, Style } from 'ol/style'; import { Vector } from 'ol/layer'; -import { Proj } from '../../../utils/projections'; -import { Map } from '../../map'; -import { TileLayer, VectorLayer } from '../../layers'; -import { Legend, LegendItem } from '../../legend'; -import { GeoJSONFeature } from '../../feature'; -import { TileOsm } from '..'; -import { VectorSource } from '../vector-source'; +import { Proj } from '../../utils/projections'; +import { Map } from '../map'; +import { TileLayer, VectorLayer } from '../layers'; +import { Legend, LegendItem } from '../legend'; +import { GeoJSONFeature } from '../feature'; +import { TileOsm } from '../source'; +import { VectorSource } from '../source/vector-source'; export default { - title: 'OL Map/Map/Tiles/Legend', + title: 'OL Map/Map', component: Legend, subcomponents: GeoJSONFeature, parameters: { @@ -110,7 +109,7 @@ const LegendsArray: LegendItem[] = [ }, ]; -export const Basic = (): JSX.Element => ( +export const GeojsonFeaturesWithLegend = (): JSX.Element => (
@@ -128,3 +127,5 @@ export const Basic = (): JSX.Element => (
); + +GeojsonFeaturesWithLegend.storyName = 'GeoJSON Features with Legend'; diff --git a/packages/react-components/src/components/ol-map/stories/map.stories.tsx b/packages/react-components/src/components/ol-map/stories/map.stories.tsx index cab89346..ed238137 100644 --- a/packages/react-components/src/components/ol-map/stories/map.stories.tsx +++ b/packages/react-components/src/components/ol-map/stories/map.stories.tsx @@ -1,4 +1,3 @@ -import React from 'react'; import { Story, Meta } from '@storybook/react/types-6-0'; import { Map } from '../map'; import { TileOsm } from '../source'; From ec715028b2c560e3ff6f646c6770c3b8d9832e9c Mon Sep 17 00:00:00 2001 From: ellamartirosyan Date: Sun, 28 Jun 2026 20:25:07 +0300 Subject: [PATCH 46/46] fix: layer manager meta mapping --- .../components/cesium-map/layers-manager.ts | 80 +++++++------------ .../src/components/cesium-map/map.tsx | 20 +---- 2 files changed, 31 insertions(+), 69 deletions(-) diff --git a/packages/react-components/src/components/cesium-map/layers-manager.ts b/packages/react-components/src/components/cesium-map/layers-manager.ts index 47b7907d..08921e13 100644 --- a/packages/react-components/src/components/cesium-map/layers-manager.ts +++ b/packages/react-components/src/components/cesium-map/layers-manager.ts @@ -34,12 +34,6 @@ import { CesiumCartesian2, CesiumImageryProvider } from './proxied.types'; const INC = 1; const DEC = -1; -// const DEFAULT_LAYER_ID_META_FIELD_PATH = 'id'; -// const DEFAULT_LAYER_NAME_META_FIELD_PATH = 'layerRecord.productName'; -// const DEFAULT_DATA_LAYER_NAME_META_FIELD_PATH = 'layerRecord.featureStructure.aliasLayerName'; -// const DEFAULT_DATA_LAYER_FIELDS_META_FIELD_PATH = 'layerRecord.featureStructure.fields'; -// const DEFAULT_FOOTPRINT_META_FIELD_PATH = 'layerRecord.footprint'; - export interface ILayerManagerMetaFieldPaths { layerIdMetaFieldPath: string; layerNameMetaFieldPath: string; @@ -48,36 +42,26 @@ export interface ILayerManagerMetaFieldPaths { footprintMetaFieldPath: string; } -const layerManagerMetaFieldPaths: ILayerManagerMetaFieldPaths = { - layerIdMetaFieldPath: '', - layerNameMetaFieldPath: '', - dataLayerNameMetaFieldPath: '', - dataLayerFieldsMetaFieldPath: '', - footprintMetaFieldPath: '', -}; +export interface ILayerManagerMetaMapping { + layer?: { + id?: string; + name?: string; + footprint?: string; + }; + dataLayer?: { + name?: string; + fields?: string; + }; +} -const configureLayerManagerMetaFieldPaths = ( - paths: Partial -): void => { - if (paths.layerIdMetaFieldPath) { - layerManagerMetaFieldPaths.layerIdMetaFieldPath = paths.layerIdMetaFieldPath; - } - if (paths.layerNameMetaFieldPath) { - layerManagerMetaFieldPaths.layerNameMetaFieldPath = paths.layerNameMetaFieldPath; - } - if (paths.dataLayerNameMetaFieldPath) { - layerManagerMetaFieldPaths.dataLayerNameMetaFieldPath = paths.dataLayerNameMetaFieldPath; - } - if (paths.dataLayerFieldsMetaFieldPath) { - layerManagerMetaFieldPaths.dataLayerFieldsMetaFieldPath = paths.dataLayerFieldsMetaFieldPath; - } - if (paths.footprintMetaFieldPath) { - layerManagerMetaFieldPaths.footprintMetaFieldPath = paths.footprintMetaFieldPath; - } +let mapping: ILayerManagerMetaMapping = {}; + +const configureLayerManagerMetaMapping = (metaMapping: ILayerManagerMetaMapping): void => { + mapping = { ...metaMapping }; }; -export const getLayerManagerMetaFieldPaths = (): ILayerManagerMetaFieldPaths => { - return { ...layerManagerMetaFieldPaths }; +export const getLayerManagerMetaMapping = (): ILayerManagerMetaMapping => { + return { ...mapping }; }; export interface ICesiumImageryLayerMeta { @@ -132,27 +116,27 @@ export type LegendExtractor = (layers: (any & { meta: any })[]) => IMapLegend[]; export const TRANSPARENT_LAYER_ID = 'TRANSPARENT_BASE_LAYER'; export const getLayerId = (layer: ICesiumImageryLayer | ICesiumWFSLayer | ICesium3DModel): string | undefined => { - return get(layer.meta, layerManagerMetaFieldPaths.layerIdMetaFieldPath) as string | undefined; + return get(layer.meta, mapping.layer?.id ?? '') as string | undefined; }; export const getLayerIdFromMeta = (meta: ICesiumImageryLayerMeta | ICesiumWFSLayerMeta | ICesium3DModelMeta | undefined): string | undefined => { - return get(meta, layerManagerMetaFieldPaths.layerIdMetaFieldPath) as string | undefined; + return get(meta, mapping.layer?.id ?? '') as string | undefined; }; export const getLayerName = (layer: ICesiumImageryLayer | ICesiumWFSLayer | ICesium3DModel): string | undefined => { - return get(layer.meta, layerManagerMetaFieldPaths.layerNameMetaFieldPath) as string | undefined; + return get(layer.meta, mapping.layer?.name ?? '') as string | undefined; }; export const getLayerFootprint = (meta: ICesiumWFSLayerMeta | undefined): unknown => { - return get(meta, layerManagerMetaFieldPaths.footprintMetaFieldPath); + return get(meta, mapping.layer?.footprint ?? ''); }; export const getDataLayerName = (meta: ICesiumWFSLayerMeta): string | undefined => { - return get(meta, layerManagerMetaFieldPaths.dataLayerNameMetaFieldPath) as string | undefined; + return get(meta, mapping.dataLayer?.name ?? '') as string | undefined; }; export const getDataLayerFields = (meta: ICesiumWFSLayerMeta | undefined): ICesiumDataLayerField[] => { - return (get(meta, layerManagerMetaFieldPaths.dataLayerFieldsMetaFieldPath) as ICesiumDataLayerField[] | undefined) ?? []; + return (get(meta, mapping.dataLayer?.fields ?? '') as ICesiumDataLayerField[] | undefined) ?? []; }; export const isServiceLayer = (layerId: string | undefined): boolean => { @@ -203,11 +187,7 @@ class LayerManager { mapViewer: CesiumViewer, legendsExtractor?: LegendExtractor, onLayersUpdate?: () => void, - layerManagerLayerIdMetaFieldPath?: string, - layerManagerLayerNameMetaFieldPath?: string, - layerManagerDataLayerNameMetaFieldPath?: string, - layerManagerDataLayerFieldsMetaFieldPath?: string, - layerManagerFootprintMetaFieldPath?: string, + layerManagerMetaMapping?: ILayerManagerMetaMapping, shouldOptimizedTileRequests?: boolean ) { this.mapViewer = mapViewer; @@ -220,17 +200,11 @@ class LayerManager { this.layerUpdated = new Event(); this.dataLayerUpdated = new Event(); this.modelUpdated = new Event(); - this.layerManagerFootprintMetaFieldPath = layerManagerFootprintMetaFieldPath; + this.layerManagerFootprintMetaFieldPath = layerManagerMetaMapping?.layer?.footprint; this.shouldOptimizedTileRequests = shouldOptimizedTileRequests ?? false; this.relevancyListenersCleanup = []; - configureLayerManagerMetaFieldPaths({ - layerIdMetaFieldPath: layerManagerLayerIdMetaFieldPath, - layerNameMetaFieldPath: layerManagerLayerNameMetaFieldPath, - dataLayerNameMetaFieldPath: layerManagerDataLayerNameMetaFieldPath, - dataLayerFieldsMetaFieldPath: layerManagerDataLayerFieldsMetaFieldPath, - footprintMetaFieldPath: layerManagerFootprintMetaFieldPath, - }); + configureLayerManagerMetaMapping(layerManagerMetaMapping ?? {}); if (onLayersUpdate) { this.addLayerUpdatedListener(onLayersUpdate); @@ -556,7 +530,7 @@ class LayerManager { skipRelevancyCheck: true, parentBaseMapId: 'TRANSPARENT_LAYER', }; - set(transparentLayerMeta, layerManagerMetaFieldPaths.layerIdMetaFieldPath, TRANSPARENT_LAYER_ID); + set(transparentLayerMeta, mapping.layer?.id ?? '', TRANSPARENT_LAYER_ID); (transparentLayer as ICesiumImageryLayer).meta = transparentLayerMeta; } diff --git a/packages/react-components/src/components/cesium-map/map.tsx b/packages/react-components/src/components/cesium-map/map.tsx index 8ec28dba..05185f4c 100644 --- a/packages/react-components/src/components/cesium-map/map.tsx +++ b/packages/react-components/src/components/cesium-map/map.tsx @@ -36,7 +36,7 @@ import { GeocoderOptions } from './geocoder/geocoder-panel'; import { GeocoderWidget } from './geocoder/geocoder-widget'; import { DEFAULT_TERRAIN_PROVIDER_URL } from './helpers/constants'; import { pointToLonLat } from './helpers/geojson/point.geojson'; -import LayerManager, { IRasterLayer, LegendExtractor } from './layers-manager'; +import LayerManager, { IRasterLayer, LegendExtractor, type ILayerManagerMetaMapping } from './layers-manager'; import { LegendWidget, IMapLegend, LegendSidebar } from './legend'; import { CesiumCompassTool } from './tools/cesium-compass.tool'; import { CoordinatesTrackerTool } from './tools/coordinates-tracker.tool'; @@ -166,11 +166,7 @@ export interface CesiumMapProps extends ViewerProps { dynamicHeightIncrement?: number; }; legends?: ILegends; - layerManagerLayerIdMetaFieldPath?: string; - layerManagerLayerNameMetaFieldPath?: string; - layerManagerDataLayerNameMetaFieldPath?: string; - layerManagerDataLayerFieldsMetaFieldPath?: string; - layerManagerFootprintMetaFieldPath?: string; + layerManagerMetaMapping?: ILayerManagerMetaMapping; geocoderPanel?: GeocoderOptions[]; } @@ -308,11 +304,7 @@ export const CesiumMap: React.FC = (props) => { () => { setLegendsList(mapViewRef.layersManager?.legendsList as IMapLegend[]); }, - props.layerManagerLayerIdMetaFieldPath, - props.layerManagerLayerNameMetaFieldPath, - props.layerManagerDataLayerNameMetaFieldPath, - props.layerManagerDataLayerFieldsMetaFieldPath, - props.layerManagerFootprintMetaFieldPath, + props.layerManagerMetaMapping, viewState?.shouldOptimizedTileRequests ), }); @@ -324,11 +316,7 @@ export const CesiumMap: React.FC = (props) => { } }, [ props.legends, - props.layerManagerLayerIdMetaFieldPath, - props.layerManagerLayerNameMetaFieldPath, - props.layerManagerDataLayerNameMetaFieldPath, - props.layerManagerDataLayerFieldsMetaFieldPath, - props.layerManagerFootprintMetaFieldPath, + props.layerManagerMetaMapping, mapViewRef, viewState ]);