From 1b7ac75d8163fe5ce9ebdd2c3c8c4a0ce26ef4c2 Mon Sep 17 00:00:00 2001 From: ellamartirosyan Date: Tue, 16 Jun 2026 11:52:30 +0300 Subject: [PATCH 01/38] 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/38] 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/38] 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/38] 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/38] 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/38] 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/38] 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/38] 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/38] 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/38] 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/38] 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/38] 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/38] 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/38] 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/38] 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/38] 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/38] 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/38] 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/38] 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/38] 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/38] 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/38] 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/38] 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/38] 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/38] 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/38] 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/38] 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/38] 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/38] 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/38] 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/38] 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/38] 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/38] 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/38] 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/38] 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/38] 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/38] 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/38] 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'} >