@collabdt/core 0.3.1 → 0.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/components/authentication/ForgotPassword.d.ts.map +1 -1
- package/dist/core/components/authentication/ForgotPassword.js +0 -1
- package/dist/core/components/authentication/ForgotPassword.js.map +1 -1
- package/dist/core/components/ui/Comments/CollapsibleCommentItem.d.ts +12 -2
- package/dist/core/components/ui/Comments/CollapsibleCommentItem.d.ts.map +1 -1
- package/dist/core/components/ui/Comments/CollapsibleCommentItem.js +137 -139
- package/dist/core/components/ui/Comments/CollapsibleCommentItem.js.map +1 -1
- package/dist/core/components/ui/Comments/Comment.d.ts +18 -1
- package/dist/core/components/ui/Comments/Comment.d.ts.map +1 -1
- package/dist/core/components/ui/Comments/Comment.js +94 -23
- package/dist/core/components/ui/Comments/Comment.js.map +1 -1
- package/dist/core/components/ui/Comments/CommentActionButtons.d.ts +38 -0
- package/dist/core/components/ui/Comments/CommentActionButtons.d.ts.map +1 -0
- package/dist/core/components/ui/Comments/CommentActionButtons.js +134 -0
- package/dist/core/components/ui/Comments/CommentActionButtons.js.map +1 -0
- package/dist/core/components/ui/Comments/CommentInput.d.ts.map +1 -1
- package/dist/core/components/ui/Comments/CommentInput.js +5 -2
- package/dist/core/components/ui/Comments/CommentInput.js.map +1 -1
- package/dist/core/components/ui/Comments/CommentsSection.d.ts.map +1 -1
- package/dist/core/components/ui/Comments/CommentsSection.js +86 -42
- package/dist/core/components/ui/Comments/CommentsSection.js.map +1 -1
- package/dist/core/components/ui/Comments/commentUtils.d.ts.map +1 -1
- package/dist/core/components/ui/Comments/commentUtils.js +2 -1
- package/dist/core/components/ui/Comments/commentUtils.js.map +1 -1
- package/dist/core/components/ui/FilesManager/src/useFileUploadWithProgress.d.ts.map +1 -1
- package/dist/core/components/ui/FilesManager/src/useFileUploadWithProgress.js +0 -1
- package/dist/core/components/ui/FilesManager/src/useFileUploadWithProgress.js.map +1 -1
- package/dist/core/components/ui/InfoSidebar/Container.d.ts.map +1 -1
- package/dist/core/components/ui/InfoSidebar/Container.js +16 -12
- package/dist/core/components/ui/InfoSidebar/Container.js.map +1 -1
- package/dist/core/components/ui/Sensors/CollapsibleSensorItem.d.ts +1 -2
- package/dist/core/components/ui/Sensors/CollapsibleSensorItem.d.ts.map +1 -1
- package/dist/core/components/ui/Sensors/CollapsibleSensorItem.js +66 -94
- package/dist/core/components/ui/Sensors/CollapsibleSensorItem.js.map +1 -1
- package/dist/core/components/ui/Sensors/Sensor.d.ts +16 -3
- package/dist/core/components/ui/Sensors/Sensor.d.ts.map +1 -1
- package/dist/core/components/ui/Sensors/Sensor.js +76 -37
- package/dist/core/components/ui/Sensors/Sensor.js.map +1 -1
- package/dist/core/components/ui/Sensors/SensorChart.d.ts +12 -2
- package/dist/core/components/ui/Sensors/SensorChart.d.ts.map +1 -1
- package/dist/core/components/ui/Sensors/SensorChart.js +49 -12
- package/dist/core/components/ui/Sensors/SensorChart.js.map +1 -1
- package/dist/core/components/ui/Sensors/SensorDetailDialog.d.ts +9 -0
- package/dist/core/components/ui/Sensors/SensorDetailDialog.d.ts.map +1 -0
- package/dist/core/components/ui/Sensors/SensorDetailDialog.js +128 -0
- package/dist/core/components/ui/Sensors/SensorDetailDialog.js.map +1 -0
- package/dist/core/components/ui/Sensors/SensorInput.d.ts +6 -1
- package/dist/core/components/ui/Sensors/SensorInput.d.ts.map +1 -1
- package/dist/core/components/ui/Sensors/SensorInput.js +94 -9
- package/dist/core/components/ui/Sensors/SensorInput.js.map +1 -1
- package/dist/core/components/ui/Sensors/SensorTagsSection.d.ts +3 -1
- package/dist/core/components/ui/Sensors/SensorTagsSection.d.ts.map +1 -1
- package/dist/core/components/ui/Sensors/SensorTagsSection.js +4 -1
- package/dist/core/components/ui/Sensors/SensorTagsSection.js.map +1 -1
- package/dist/core/components/ui/Sensors/SensorsSection.d.ts +1 -3
- package/dist/core/components/ui/Sensors/SensorsSection.d.ts.map +1 -1
- package/dist/core/components/ui/Sensors/SensorsSection.js +33 -10
- package/dist/core/components/ui/Sensors/SensorsSection.js.map +1 -1
- package/dist/core/components/ui/Sensors/TimeZoneSelect.d.ts +7 -0
- package/dist/core/components/ui/Sensors/TimeZoneSelect.d.ts.map +1 -0
- package/dist/core/components/ui/Sensors/TimeZoneSelect.js +56 -0
- package/dist/core/components/ui/Sensors/TimeZoneSelect.js.map +1 -0
- package/dist/core/components/ui/Sensors/sensorData.d.ts +42 -0
- package/dist/core/components/ui/Sensors/sensorData.d.ts.map +1 -0
- package/dist/core/components/ui/Sensors/sensorData.js +118 -0
- package/dist/core/components/ui/Sensors/sensorData.js.map +1 -0
- package/dist/core/components/ui/Sensors/sensorRange.d.ts +24 -0
- package/dist/core/components/ui/Sensors/sensorRange.d.ts.map +1 -0
- package/dist/core/components/ui/Sensors/sensorRange.js +34 -0
- package/dist/core/components/ui/Sensors/sensorRange.js.map +1 -0
- package/dist/core/components/ui/Sensors/useSensorSeries.d.ts +15 -0
- package/dist/core/components/ui/Sensors/useSensorSeries.d.ts.map +1 -0
- package/dist/core/components/ui/Sensors/useSensorSeries.js +63 -0
- package/dist/core/components/ui/Sensors/useSensorSeries.js.map +1 -0
- package/dist/core/components/ui/Sidebar.d.ts.map +1 -1
- package/dist/core/components/ui/Sidebar.js +27 -4
- package/dist/core/components/ui/Sidebar.js.map +1 -1
- package/dist/core/components/ui/ToolbarButton.d.ts.map +1 -1
- package/dist/core/components/ui/ToolbarButton.js +0 -1
- package/dist/core/components/ui/ToolbarButton.js.map +1 -1
- package/dist/core/components/viewers/Data/advancedFilter/FiltersDialog.d.ts.map +1 -1
- package/dist/core/components/viewers/Data/advancedFilter/FiltersDialog.js +0 -2
- package/dist/core/components/viewers/Data/advancedFilter/FiltersDialog.js.map +1 -1
- package/dist/core/components/viewers/Data/details/EnergySourcesGrid.d.ts.map +1 -1
- package/dist/core/components/viewers/Data/details/EnergySourcesGrid.js +0 -10
- package/dist/core/components/viewers/Data/details/EnergySourcesGrid.js.map +1 -1
- package/dist/core/components/viewers/Data/files/DatatableFileAdder.d.ts.map +1 -1
- package/dist/core/components/viewers/Data/files/DatatableFileAdder.js +0 -1
- package/dist/core/components/viewers/Data/files/DatatableFileAdder.js.map +1 -1
- package/dist/core/components/viewers/Data/files/utils/fuzzySearch.d.ts.map +1 -1
- package/dist/core/components/viewers/Data/files/utils/fuzzySearch.js +0 -1
- package/dist/core/components/viewers/Data/files/utils/fuzzySearch.js.map +1 -1
- package/dist/core/components/viewers/Data/userDetails/UserMoreOptions.d.ts.map +1 -1
- package/dist/core/components/viewers/Data/userDetails/UserMoreOptions.js +0 -1
- package/dist/core/components/viewers/Data/userDetails/UserMoreOptions.js.map +1 -1
- package/dist/core/components/viewers/Data/utils/filterHelper.d.ts.map +1 -1
- package/dist/core/components/viewers/Data/utils/filterHelper.js +0 -2
- package/dist/core/components/viewers/Data/utils/filterHelper.js.map +1 -1
- package/dist/core/components/viewers/Viewer.js +1 -1
- package/dist/core/components/viewers/Viewer.js.map +1 -1
- package/dist/core/components/viewers/bim/src/BimSidebar/src/CommunicationTab/src/TopicsSection.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/BimSidebar/src/CommunicationTab/src/TopicsSection.js +0 -3
- package/dist/core/components/viewers/bim/src/BimSidebar/src/CommunicationTab/src/TopicsSection.js.map +1 -1
- package/dist/core/components/viewers/bim/src/BimSidebar/src/FileTab/src/FilesSection.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/BimSidebar/src/FileTab/src/FilesSection.js +1 -7
- package/dist/core/components/viewers/bim/src/BimSidebar/src/FileTab/src/FilesSection.js.map +1 -1
- package/dist/core/components/viewers/bim/src/BimSidebar/src/FileTab/src/ModelsSection.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/BimSidebar/src/FileTab/src/ModelsSection.js +1 -9
- package/dist/core/components/viewers/bim/src/BimSidebar/src/FileTab/src/ModelsSection.js.map +1 -1
- package/dist/core/components/viewers/bim/src/BimSidebar/src/SensorsTab/index.d.ts +1 -3
- package/dist/core/components/viewers/bim/src/BimSidebar/src/SensorsTab/index.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/BimSidebar/src/SensorsTab/index.js +2 -2
- package/dist/core/components/viewers/bim/src/BimSidebar/src/SensorsTab/index.js.map +1 -1
- package/dist/core/components/viewers/bim/src/BimSidebar/src/TabSelector.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/BimSidebar/src/TabSelector.js +4 -3
- package/dist/core/components/viewers/bim/src/BimSidebar/src/TabSelector.js.map +1 -1
- package/dist/core/components/viewers/bim/src/BimSidebar/src/index.js +1 -1
- package/dist/core/components/viewers/bim/src/BimSidebar/src/index.js.map +1 -1
- package/dist/core/components/viewers/bim/src/ClippingPlane.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/ClippingPlane.js +0 -1
- package/dist/core/components/viewers/bim/src/ClippingPlane.js.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/index.d.ts +1 -2
- package/dist/core/components/viewers/bim/src/tools/AddToBim/index.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/index.js +25 -2
- package/dist/core/components/viewers/bim/src/tools/AddToBim/index.js.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimComment.d.ts +16 -10
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimComment.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimComment.js +26 -6
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimComment.js.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimCommentCluster.d.ts +2 -0
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimCommentCluster.d.ts.map +1 -0
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimCommentCluster.js +5 -0
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimCommentCluster.js.map +1 -0
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimSensor.d.ts +8 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimSensor.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimSensor.js +15 -3
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimSensor.js.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/clusterMarkersByScreenSpace.d.ts +44 -0
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/clusterMarkersByScreenSpace.d.ts.map +1 -0
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/clusterMarkersByScreenSpace.js +77 -0
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/clusterMarkersByScreenSpace.js.map +1 -0
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/commentCameraUtils.d.ts +3 -0
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/commentCameraUtils.d.ts.map +1 -0
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/commentCameraUtils.js +5 -0
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/commentCameraUtils.js.map +1 -0
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/commentMarkerConfig.d.ts +33 -0
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/commentMarkerConfig.d.ts.map +1 -0
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/commentMarkerConfig.js +90 -0
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/commentMarkerConfig.js.map +1 -0
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/renderCSS2DMarkers.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/renderCSS2DMarkers.js +2 -2
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/renderCSS2DMarkers.js.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useCommentMarkers.d.ts +7 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useCommentMarkers.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useCommentMarkers.js +50 -73
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useCommentMarkers.js.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useSensorMarkers.d.ts +7 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useSensorMarkers.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useSensorMarkers.js +39 -11
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useSensorMarkers.js.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/ClippingTool/ClippingTool.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/ClippingTool/ClippingTool.js +1 -5
- package/dist/core/components/viewers/bim/src/tools/ClippingTool/ClippingTool.js.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/ExplodeByLevelTool.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/ExplodeByLevelTool.js +0 -1
- package/dist/core/components/viewers/bim/src/tools/ExplodeByLevelTool.js.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/measureBimTool.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/measureBimTool.js +1 -2
- package/dist/core/components/viewers/bim/src/tools/measureBimTool.js.map +1 -1
- package/dist/core/components/viewers/map/MapViewer.d.ts +1 -2
- package/dist/core/components/viewers/map/MapViewer.d.ts.map +1 -1
- package/dist/core/components/viewers/map/MapViewer.js +2 -2
- package/dist/core/components/viewers/map/MapViewer.js.map +1 -1
- package/dist/core/components/viewers/map/datasets/src/arcGISDatasets.d.ts.map +1 -1
- package/dist/core/components/viewers/map/datasets/src/arcGISDatasets.js +11 -14
- package/dist/core/components/viewers/map/datasets/src/arcGISDatasets.js.map +1 -1
- package/dist/core/components/viewers/map/datasets/src/ckanDatasets.d.ts.map +1 -1
- package/dist/core/components/viewers/map/datasets/src/ckanDatasets.js +2 -5
- package/dist/core/components/viewers/map/datasets/src/ckanDatasets.js.map +1 -1
- package/dist/core/components/viewers/map/datasets/src/localDatasets.d.ts.map +1 -1
- package/dist/core/components/viewers/map/datasets/src/localDatasets.js +0 -3
- package/dist/core/components/viewers/map/datasets/src/localDatasets.js.map +1 -1
- package/dist/core/components/viewers/map/datasets/src/opendatasoftDatasets.d.ts.map +1 -1
- package/dist/core/components/viewers/map/datasets/src/opendatasoftDatasets.js +0 -11
- package/dist/core/components/viewers/map/datasets/src/opendatasoftDatasets.js.map +1 -1
- package/dist/core/components/viewers/map/src/MapFeaturePopoverMenu/src/DatabaseBuildingPopover/src/BuildingsTools.d.ts.map +1 -1
- package/dist/core/components/viewers/map/src/MapFeaturePopoverMenu/src/DatabaseBuildingPopover/src/BuildingsTools.js +0 -1
- package/dist/core/components/viewers/map/src/MapFeaturePopoverMenu/src/DatabaseBuildingPopover/src/BuildingsTools.js.map +1 -1
- package/dist/core/components/viewers/map/src/MapFeaturePopoverMenu/src/NonDatabaseBuildingPopover.d.ts.map +1 -1
- package/dist/core/components/viewers/map/src/MapFeaturePopoverMenu/src/NonDatabaseBuildingPopover.js +0 -1
- package/dist/core/components/viewers/map/src/MapFeaturePopoverMenu/src/NonDatabaseBuildingPopover.js.map +1 -1
- package/dist/core/components/viewers/map/src/MapFeaturePopoverMenu/src/OpenDataFeaturePopover.d.ts.map +1 -1
- package/dist/core/components/viewers/map/src/MapFeaturePopoverMenu/src/OpenDataFeaturePopover.js +0 -1
- package/dist/core/components/viewers/map/src/MapFeaturePopoverMenu/src/OpenDataFeaturePopover.js.map +1 -1
- package/dist/core/components/viewers/map/src/MapLayers/index.d.ts +1 -2
- package/dist/core/components/viewers/map/src/MapLayers/index.d.ts.map +1 -1
- package/dist/core/components/viewers/map/src/MapLayers/index.js +2 -2
- package/dist/core/components/viewers/map/src/MapLayers/index.js.map +1 -1
- package/dist/core/components/viewers/map/src/MapLayers/src/CommentLayer/index.d.ts.map +1 -1
- package/dist/core/components/viewers/map/src/MapLayers/src/CommentLayer/index.js +174 -53
- package/dist/core/components/viewers/map/src/MapLayers/src/CommentLayer/index.js.map +1 -1
- package/dist/core/components/viewers/map/src/MapLayers/src/FileLayer/components/MapFileManager.d.ts.map +1 -1
- package/dist/core/components/viewers/map/src/MapLayers/src/FileLayer/components/MapFileManager.js +2 -1
- package/dist/core/components/viewers/map/src/MapLayers/src/FileLayer/components/MapFileManager.js.map +1 -1
- package/dist/core/components/viewers/map/src/MapLayers/src/SensorsLayer/index.d.ts +1 -3
- package/dist/core/components/viewers/map/src/MapLayers/src/SensorsLayer/index.d.ts.map +1 -1
- package/dist/core/components/viewers/map/src/MapLayers/src/SensorsLayer/index.js +74 -42
- package/dist/core/components/viewers/map/src/MapLayers/src/SensorsLayer/index.js.map +1 -1
- package/dist/core/components/viewers/map/src/MapSidebar/TabSelector.d.ts.map +1 -1
- package/dist/core/components/viewers/map/src/MapSidebar/TabSelector.js +4 -3
- package/dist/core/components/viewers/map/src/MapSidebar/TabSelector.js.map +1 -1
- package/dist/core/components/viewers/map/src/MapSidebar/index.js +1 -1
- package/dist/core/components/viewers/map/src/MapSidebar/index.js.map +1 -1
- package/dist/core/components/viewers/map/src/MapSidebar/src/FileTab/src/FilesSection.d.ts.map +1 -1
- package/dist/core/components/viewers/map/src/MapSidebar/src/FileTab/src/FilesSection.js +0 -1
- package/dist/core/components/viewers/map/src/MapSidebar/src/FileTab/src/FilesSection.js.map +1 -1
- package/dist/core/components/viewers/map/src/MapSidebar/src/FileTab/src/ModelsSection.d.ts.map +1 -1
- package/dist/core/components/viewers/map/src/MapSidebar/src/FileTab/src/ModelsSection.js +1 -5
- package/dist/core/components/viewers/map/src/MapSidebar/src/FileTab/src/ModelsSection.js.map +1 -1
- package/dist/core/components/viewers/map/src/MapSidebar/src/SensorsTab/index.d.ts +1 -3
- package/dist/core/components/viewers/map/src/MapSidebar/src/SensorsTab/index.d.ts.map +1 -1
- package/dist/core/components/viewers/map/src/MapSidebar/src/SensorsTab/index.js +2 -2
- package/dist/core/components/viewers/map/src/MapSidebar/src/SensorsTab/index.js.map +1 -1
- package/dist/core/components/viewers/map/src/tools/AddTools/AddFile/utils/uploadToPresignedURLS.d.ts.map +1 -1
- package/dist/core/components/viewers/map/src/tools/AddTools/AddFile/utils/uploadToPresignedURLS.js +1 -3
- package/dist/core/components/viewers/map/src/tools/AddTools/AddFile/utils/uploadToPresignedURLS.js.map +1 -1
- package/dist/core/components/viewers/map/utils/MapEventManager/MapClickManager.d.ts.map +1 -1
- package/dist/core/components/viewers/map/utils/MapEventManager/MapClickManager.js +0 -1
- package/dist/core/components/viewers/map/utils/MapEventManager/MapClickManager.js.map +1 -1
- package/dist/core/components/viewers/map/utils/flyToEach.d.ts.map +1 -1
- package/dist/core/components/viewers/map/utils/flyToEach.js +0 -1
- package/dist/core/components/viewers/map/utils/flyToEach.js.map +1 -1
- package/dist/core/components/viewers/map/utils/getOsmIdbyCoordinates.d.ts.map +1 -1
- package/dist/core/components/viewers/map/utils/getOsmIdbyCoordinates.js +0 -2
- package/dist/core/components/viewers/map/utils/getOsmIdbyCoordinates.js.map +1 -1
- package/dist/core/components/viewers/shared/markers/BimMarkerCluster.d.ts +19 -0
- package/dist/core/components/viewers/shared/markers/BimMarkerCluster.d.ts.map +1 -0
- package/dist/core/components/viewers/shared/markers/BimMarkerCluster.js +108 -0
- package/dist/core/components/viewers/shared/markers/BimMarkerCluster.js.map +1 -0
- package/dist/core/components/viewers/shared/markers/computeMarkerLookAt.d.ts +24 -0
- package/dist/core/components/viewers/shared/markers/computeMarkerLookAt.d.ts.map +1 -0
- package/dist/core/components/viewers/shared/markers/computeMarkerLookAt.js +27 -0
- package/dist/core/components/viewers/shared/markers/computeMarkerLookAt.js.map +1 -0
- package/dist/core/components/viewers/shared/markers/types.d.ts +66 -0
- package/dist/core/components/viewers/shared/markers/types.d.ts.map +1 -0
- package/dist/core/components/viewers/shared/markers/types.js +1 -0
- package/dist/core/components/viewers/shared/markers/types.js.map +1 -0
- package/dist/core/components/viewers/shared/markers/useMarkerLayerBim.d.ts +22 -0
- package/dist/core/components/viewers/shared/markers/useMarkerLayerBim.d.ts.map +1 -0
- package/dist/core/components/viewers/shared/markers/useMarkerLayerBim.js +233 -0
- package/dist/core/components/viewers/shared/markers/useMarkerLayerBim.js.map +1 -0
- package/dist/core/hooks/comments/comments.d.ts +11 -0
- package/dist/core/hooks/comments/comments.d.ts.map +1 -1
- package/dist/core/hooks/comments/comments.js +3 -1
- package/dist/core/hooks/comments/comments.js.map +1 -1
- package/dist/core/hooks/comments/createCommentHooks.d.ts +11 -0
- package/dist/core/hooks/comments/createCommentHooks.d.ts.map +1 -1
- package/dist/core/hooks/comments/createCommentHooks.js +30 -1
- package/dist/core/hooks/comments/createCommentHooks.js.map +1 -1
- package/dist/core/hooks/files/files.d.ts.map +1 -1
- package/dist/core/hooks/files/files.js +0 -1
- package/dist/core/hooks/files/files.js.map +1 -1
- package/dist/core/hooks/ui/useResizableSidebarWidth.d.ts +30 -0
- package/dist/core/hooks/ui/useResizableSidebarWidth.d.ts.map +1 -0
- package/dist/core/hooks/ui/useResizableSidebarWidth.js +75 -0
- package/dist/core/hooks/ui/useResizableSidebarWidth.js.map +1 -0
- package/dist/core/i18n/messages/en.json +10 -1
- package/dist/core/i18n/messages/es.json +11 -2
- package/dist/core/i18n/messages/fr.json +11 -2
- package/dist/core/store/AppConfig/context.d.ts.map +1 -1
- package/dist/core/store/AppConfig/context.js +4 -1
- package/dist/core/store/AppConfig/context.js.map +1 -1
- package/dist/core/store/AppConfig/reducer.d.ts +10 -0
- package/dist/core/store/AppConfig/reducer.d.ts.map +1 -1
- package/dist/core/store/AppConfig/reducer.js +10 -0
- package/dist/core/store/AppConfig/reducer.js.map +1 -1
- package/dist/core/store/Datasets/reducer.d.ts.map +1 -1
- package/dist/core/store/Datasets/reducer.js +0 -2
- package/dist/core/store/Datasets/reducer.js.map +1 -1
- package/dist/core/store/Files/reducer.js +0 -1
- package/dist/core/store/Files/reducer.js.map +1 -1
- package/dist/core/store/Menus/context.d.ts.map +1 -1
- package/dist/core/store/Menus/context.js +8 -2
- package/dist/core/store/Menus/context.js.map +1 -1
- package/dist/core/store/Menus/reducer.d.ts +65 -1
- package/dist/core/store/Menus/reducer.d.ts.map +1 -1
- package/dist/core/store/Menus/reducer.js +39 -3
- package/dist/core/store/Menus/reducer.js.map +1 -1
- package/dist/core/utils/markerUtils.d.ts +10 -0
- package/dist/core/utils/markerUtils.d.ts.map +1 -1
- package/dist/core/utils/markerUtils.js +9 -1
- package/dist/core/utils/markerUtils.js.map +1 -1
- package/dist/core/utils/memcache.d.ts.map +1 -1
- package/dist/core/utils/memcache.js +0 -7
- package/dist/core/utils/memcache.js.map +1 -1
- package/dist/core/utils/timeUtils.d.ts +24 -1
- package/dist/core/utils/timeUtils.d.ts.map +1 -1
- package/dist/core/utils/timeUtils.js +52 -3
- package/dist/core/utils/timeUtils.js.map +1 -1
- package/package.json +4 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../src/core/components/viewers/map/datasets/src/ckanDatasets.ts"],"sourcesContent":["// SPDX-License-Identifier: AGPL-3.0-or-later\r\n// Copyright (C) 2025 Collab Digital Twins\r\n\r\nimport { layerColorByName } from '../../utils/stringToColour'\r\nimport { formatName, stripHtml } from '../utils'\r\n\r\nimport { buildValidationKey, getCache, setCache, withCache } from './cache'\r\n\r\nimport type { Dataset, FieldType } from '../../../../../types/datasetTypes'\r\nimport type { OpenDataPortal } from '../../../../../types/dbTypes'\r\nimport type { AllGeoJSON } from '@turf/turf'\r\n\r\n\r\n\r\n\r\n// Fetch CKAN GeoJSON datasets and transform to Dataset array\r\nexport async function fetchCkanDatasets(\r\n portal: OpenDataPortal,\r\n): Promise<Dataset[]> {\r\n const { apiUrl, countrySubdivision, municipality, group } = portal\r\n const isServer = typeof window === 'undefined'\r\n\r\n const buildUrl = (target: string) => {\r\n if (isServer) {\r\n return target\r\n }\r\n\r\n const proxied = `/api/ckanProxy?url=${encodeURIComponent(target)}`\r\n return proxied\r\n }\r\n\r\n // https://www.donneesquebec.ca/recherche/dataset/?sort=metadata_created+desc&_organization_limit=0&res_format=GeoJSON\r\n\r\n try {\r\n // Use your local API route to proxy the CKAN request when on the client\r\n const geoJsonListTarget = `${apiUrl}/api/3/action/package_search?fq=res_format:(geojson OR GeoJSON)&rows=100000`\r\n const response = await fetch(buildUrl(geoJsonListTarget))\r\n\r\n if (!response.ok) {\r\n return []\r\n }\r\n\r\n const data = await response.json()\r\n\r\n if (!data.success || !data.result || !Array.isArray(data.result.results)) {\r\n throw new Error('Invalid CKAN response structure')\r\n }\r\n\r\n const transformedDatasets: Dataset[] = (\r\n await Promise.all(\r\n data.result.results.map(async (pkg: any) => {\r\n // Find first GeoJSON resource\r\n const geoJsonResource = pkg.resources?.find(\r\n (resource: any) => resource.format?.toLowerCase() === 'geojson',\r\n )\r\n\r\n // Exclude if no GeoJSON resource\r\n if (!geoJsonResource) return\r\n\r\n // Exclude if not active\r\n const datastore = geoJsonResource.datastore_active?.toString().toLowerCase() === 'true'\r\n const isActive = datastore || geoJsonResource.state === 'active'\r\n if (!(datastore && geoJsonResource.url && isActive)) return\r\n\r\n // Determine the correct URL based on your working method\r\n let resourceUrl: string | null = null\r\n let resourceTarget: string | null = null\r\n let fieldsUrl: string | null = null\r\n\r\n if (datastore) {\r\n resourceTarget = `${apiUrl}/api/3/action/datastore_search?resource_id=${geoJsonResource.id}&limit=1000000`\r\n resourceUrl = buildUrl(resourceTarget)\r\n fieldsUrl = buildUrl(`${apiUrl}/api/3/action/datastore_search?resource_id=${geoJsonResource.id}&limit=0`)\r\n }\r\n else {\r\n resourceTarget = geoJsonResource.url\r\n resourceUrl = resourceTarget ? buildUrl(resourceTarget) : null\r\n }\r\n\r\n try {\r\n const sampleTarget = resourceTarget?.includes('datastore_search')\r\n ? resourceTarget.replace('limit=1000000', 'limit=1')\r\n : resourceTarget\r\n const sampleUrl = sampleTarget\r\n ? (sampleTarget.includes('limit=') ? buildUrl(sampleTarget) : buildUrl(`${sampleTarget}?limit=1`))\r\n : null\r\n\r\n if (sampleUrl) {\r\n const validKey = buildValidationKey('ckan', pkg.id, pkg.metadata_modified || pkg.metadata_created || undefined)\r\n const isValid = await withCache<boolean>(validKey, 24 * 60 * 60 * 1000, async () => {\r\n const sampleRes = await fetch(sampleUrl)\r\n if (sampleRes.ok) {\r\n const sample = await sampleRes.json()\r\n if (sample?.error) {\r\n const code = sample.error.code\r\n const msg = `${sample.error.message || ''} ${sample.error.messageCode || ''}`\r\n if (code === 499 || code === 504 || /token required|unauthorized|forbidden|timed out/i.test(msg)) return false\r\n }\r\n let hasGeom = true\r\n if (sample?.result?.records) {\r\n const rec = sample.result.records[0]\r\n const geom = rec?.geometry || (typeof rec?.geometry === 'string' ? JSON.parse(rec.geometry) : undefined)\r\n hasGeom = !!geom\r\n }\r\n else if (Array.isArray(sample?.features)) {\r\n hasGeom = sample.features.some((f: any) => f && f.geometry)\r\n }\r\n return !!hasGeom\r\n } else if ([401, 403, 504].includes(sampleRes.status)) {\r\n return false\r\n }\r\n return false\r\n })\r\n if (!isValid) return\r\n }\r\n }\r\n catch {\r\n }\r\n\r\n const getFeaturesData = async () => {\r\n if (resourceUrl) {\r\n try {\r\n const response = await fetch(fieldsUrl ?? resourceUrl)\r\n if (!response.ok) {\r\n console.error('Network response was not ok:', response.status)\r\n return\r\n }\r\n return await response.json()\r\n }\r\n catch (error) {\r\n console.error('Error fetching features data:', error)\r\n return\r\n }\r\n }\r\n }\r\n\r\n const getFields = async () => {\r\n if (fieldsUrl) {\r\n try {\r\n const fieldsKey = `fields:ckan:${geoJsonResource.id}:${pkg.metadata_modified || pkg.metadata_created || ''}`\r\n const cached = getCache<any[]>(fieldsKey)\r\n if (cached) return cached\r\n const dsRes = await fetch(fieldsUrl)\r\n if (dsRes.ok) {\r\n const dsData = await dsRes.json()\r\n const fields = (\r\n dsData.result.fields?.map((field: any) => ({\r\n name: field.id,\r\n type: ckanFieldTypes[field.type]?.type || field.type,\r\n ckanType: field.type,\r\n description: field.info || '',\r\n })) || []\r\n )\r\n setCache(fieldsKey, fields, 24 * 60 * 60 * 1000)\r\n return fields\r\n }\r\n }\r\n catch {\r\n return []\r\n }\r\n }\r\n return []\r\n }\r\n\r\n const getFeatures: () => Promise<AllGeoJSON> = async () => {\r\n // Check cache first\r\n const featuresKey = `features:ckan:${geoJsonResource.id}:${pkg.metadata_modified || pkg.metadata_created || ''}`\r\n const cached = getCache<AllGeoJSON>(featuresKey)\r\n if (cached) {\r\n const cachedCount = 'features' in cached ? cached.features?.length ?? 0 : 0\r\n console.log(`✅ CKAN getFeatures: Returning ${cachedCount} cached features for \"${pkg.name}\"`)\r\n return cached\r\n }\r\n\r\n const limit = 5_000\r\n const url2 = resourceUrl?.includes('datastore_search')\r\n ? `${resourceUrl}&limit=${limit}`\r\n : resourceUrl\r\n\r\n try {\r\n const response = await fetch(url2)\r\n if (!response.ok) {\r\n console.error('CKAN: Network response was not ok:', response.status)\r\n return {\r\n type: 'FeatureCollection',\r\n features: [],\r\n } as AllGeoJSON\r\n }\r\n const data = await response.json()\r\n let featuresCollection: AllGeoJSON | undefined\r\n\r\n if (data.result && Array.isArray(data.result.records)) {\r\n // CKAN Datastore: build FeatureCollection from records\r\n const features = data.result.records\r\n .map((properties: any) => {\r\n try {\r\n let geometry\r\n geometry = properties.geometry?.type ? properties.geometry : JSON.parse(properties.geometry)\r\n\r\n return {\r\n type: 'Feature',\r\n geometry,\r\n properties,\r\n }\r\n }\r\n catch {\r\n return null\r\n }\r\n })\r\n .filter((f: any) => f !== null)\r\n\r\n featuresCollection = {\r\n type: 'FeatureCollection',\r\n features,\r\n }\r\n }\r\n else if (\r\n data.type === 'FeatureCollection'\r\n && Array.isArray(data.features)\r\n ) {\r\n // Already a FeatureCollection\r\n featuresCollection = data\r\n }\r\n else {\r\n console.error(\r\n 'CKAN: No features available or unexpected response:',\r\n data,\r\n )\r\n return {\r\n type: 'FeatureCollection',\r\n features: [],\r\n } as AllGeoJSON\r\n }\r\n\r\n // Cache the result for 1 hour\r\n if (featuresCollection) {\r\n setCache(featuresKey, featuresCollection, 60 * 60 * 1000)\r\n }\r\n\r\n return featuresCollection\r\n }\r\n catch (error) {\r\n console.error('CKAN: Error processing dataset:', error)\r\n return {\r\n type: 'FeatureCollection',\r\n features: [],\r\n } as AllGeoJSON\r\n }\r\n }\r\n\r\n const name = formatName(pkg.title || pkg.name)\r\n\r\n return {\r\n id: pkg.id,\r\n name,\r\n url: fieldsUrl,\r\n publisher:\r\n pkg.organization?.title || pkg.organization?.name || 'Unknown',\r\n dataManagementSystem: 'ckan',\r\n license: pkg.license_title || pkg.license_id || 'Unknown',\r\n contact: pkg.author || pkg.maintainer || 'Unknown',\r\n keywords: pkg.tags?.map((tag: any) => tag.name) || [],\r\n description: pkg.notes\r\n ? stripHtml(pkg.notes)\r\n : 'No description available',\r\n dateReleased: pkg.metadata_created || '',\r\n dateUpdated: pkg.metadata_modified || '',\r\n information: pkg.information_url || '',\r\n countrySubdivision,\r\n municipality,\r\n type: pkg.type || 'GeoJSON',\r\n portal,\r\n getFields,\r\n getFeatures,\r\n getFeaturesData,\r\n group,\r\n layerColor: layerColorByName(name),\r\n }\r\n }),\r\n )\r\n ).filter(Boolean) // Remove nulls (excluded datasets)\r\n\r\n return transformedDatasets\r\n }\r\n catch (error) {\r\n console.error('Error fetching CKAN GeoJSON datasets:', error)\r\n return []\r\n }\r\n}\r\n\r\nexport interface CkanFieldTypes {\r\n [key: string]: { description: string, type: FieldType }\r\n}\r\n\r\nfunction ckanFieldTypes(): CkanFieldTypes {\r\n return {\r\n text: { description: 'Text', type: 'string' },\r\n int: { description: 'Integer', type: 'number' },\r\n float: { description: 'Float', type: 'number' },\r\n bool: { description: 'Boolean', type: 'boolean' },\r\n date: { description: 'Date', type: 'datetime' },\r\n timestamp: { description: 'Timestamp', type: 'datetime' },\r\n json: { description: 'JSON', type: 'object' },\r\n // Add more CKAN field types as needed\r\n }\r\n}\r\n"],"mappings":"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","names":["_a","response","_b","data"]}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../src/core/components/viewers/map/datasets/src/ckanDatasets.ts"],"sourcesContent":["// SPDX-License-Identifier: AGPL-3.0-or-later\r\n// Copyright (C) 2025 Collab Digital Twins\r\n\r\nimport { layerColorByName } from '../../utils/stringToColour'\r\nimport { formatName, stripHtml } from '../utils'\r\n\r\nimport { buildValidationKey, getCache, setCache, withCache } from './cache'\r\n\r\nimport type { Dataset, FieldType } from '../../../../../types/datasetTypes'\r\nimport type { OpenDataPortal } from '../../../../../types/dbTypes'\r\nimport type { AllGeoJSON } from '@turf/turf'\r\n\r\n\r\n\r\n\r\n// Fetch CKAN GeoJSON datasets and transform to Dataset array\r\nexport async function fetchCkanDatasets(\r\n portal: OpenDataPortal,\r\n): Promise<Dataset[]> {\r\n const { apiUrl, countrySubdivision, municipality, group } = portal\r\n const isServer = typeof window === 'undefined'\r\n\r\n const buildUrl = (target: string) => {\r\n if (isServer) {\r\n return target\r\n }\r\n\r\n const proxied = `/api/ckanProxy?url=${encodeURIComponent(target)}`\r\n return proxied\r\n }\r\n\r\n // https://www.donneesquebec.ca/recherche/dataset/?sort=metadata_created+desc&_organization_limit=0&res_format=GeoJSON\r\n\r\n try {\r\n // Use your local API route to proxy the CKAN request when on the client\r\n const geoJsonListTarget = `${apiUrl}/api/3/action/package_search?fq=res_format:(geojson OR GeoJSON)&rows=100000`\r\n const response = await fetch(buildUrl(geoJsonListTarget))\r\n\r\n if (!response.ok) {\r\n return []\r\n }\r\n\r\n const data = await response.json()\r\n\r\n if (!data.success || !data.result || !Array.isArray(data.result.results)) {\r\n throw new Error('Invalid CKAN response structure')\r\n }\r\n\r\n const transformedDatasets: Dataset[] = (\r\n await Promise.all(\r\n data.result.results.map(async (pkg: any) => {\r\n // Find first GeoJSON resource\r\n const geoJsonResource = pkg.resources?.find(\r\n (resource: any) => resource.format?.toLowerCase() === 'geojson',\r\n )\r\n\r\n // Exclude if no GeoJSON resource\r\n if (!geoJsonResource) return\r\n\r\n // Exclude if not active\r\n const datastore = geoJsonResource.datastore_active?.toString().toLowerCase() === 'true'\r\n const isActive = datastore || geoJsonResource.state === 'active'\r\n if (!(datastore && geoJsonResource.url && isActive)) return\r\n\r\n // Determine the correct URL based on your working method\r\n let resourceUrl: string | null = null\r\n let resourceTarget: string | null = null\r\n let fieldsUrl: string | null = null\r\n\r\n if (datastore) {\r\n resourceTarget = `${apiUrl}/api/3/action/datastore_search?resource_id=${geoJsonResource.id}&limit=1000000`\r\n resourceUrl = buildUrl(resourceTarget)\r\n fieldsUrl = buildUrl(`${apiUrl}/api/3/action/datastore_search?resource_id=${geoJsonResource.id}&limit=0`)\r\n }\r\n else {\r\n resourceTarget = geoJsonResource.url\r\n resourceUrl = resourceTarget ? buildUrl(resourceTarget) : null\r\n }\r\n\r\n try {\r\n const sampleTarget = resourceTarget?.includes('datastore_search')\r\n ? resourceTarget.replace('limit=1000000', 'limit=1')\r\n : resourceTarget\r\n const sampleUrl = sampleTarget\r\n ? (sampleTarget.includes('limit=') ? buildUrl(sampleTarget) : buildUrl(`${sampleTarget}?limit=1`))\r\n : null\r\n\r\n if (sampleUrl) {\r\n const validKey = buildValidationKey('ckan', pkg.id, pkg.metadata_modified || pkg.metadata_created || undefined)\r\n const isValid = await withCache<boolean>(validKey, 24 * 60 * 60 * 1000, async () => {\r\n const sampleRes = await fetch(sampleUrl)\r\n if (sampleRes.ok) {\r\n const sample = await sampleRes.json()\r\n if (sample?.error) {\r\n const code = sample.error.code\r\n const msg = `${sample.error.message || ''} ${sample.error.messageCode || ''}`\r\n if (code === 499 || code === 504 || /token required|unauthorized|forbidden|timed out/i.test(msg)) return false\r\n }\r\n let hasGeom = true\r\n if (sample?.result?.records) {\r\n const rec = sample.result.records[0]\r\n const geom = rec?.geometry || (typeof rec?.geometry === 'string' ? JSON.parse(rec.geometry) : undefined)\r\n hasGeom = !!geom\r\n }\r\n else if (Array.isArray(sample?.features)) {\r\n hasGeom = sample.features.some((f: any) => f && f.geometry)\r\n }\r\n return !!hasGeom\r\n } else if ([401, 403, 504].includes(sampleRes.status)) {\r\n return false\r\n }\r\n return false\r\n })\r\n if (!isValid) return\r\n }\r\n }\r\n catch {\r\n }\r\n\r\n const getFeaturesData = async () => {\r\n if (resourceUrl) {\r\n try {\r\n const response = await fetch(fieldsUrl ?? resourceUrl)\r\n if (!response.ok) {\r\n console.error('Network response was not ok:', response.status)\r\n return\r\n }\r\n return await response.json()\r\n }\r\n catch (error) {\r\n console.error('Error fetching features data:', error)\r\n return\r\n }\r\n }\r\n }\r\n\r\n const getFields = async () => {\r\n if (fieldsUrl) {\r\n try {\r\n const fieldsKey = `fields:ckan:${geoJsonResource.id}:${pkg.metadata_modified || pkg.metadata_created || ''}`\r\n const cached = getCache<any[]>(fieldsKey)\r\n if (cached) return cached\r\n const dsRes = await fetch(fieldsUrl)\r\n if (dsRes.ok) {\r\n const dsData = await dsRes.json()\r\n const fields = (\r\n dsData.result.fields?.map((field: any) => ({\r\n name: field.id,\r\n type: ckanFieldTypes[field.type]?.type || field.type,\r\n ckanType: field.type,\r\n description: field.info || '',\r\n })) || []\r\n )\r\n setCache(fieldsKey, fields, 24 * 60 * 60 * 1000)\r\n return fields\r\n }\r\n }\r\n catch {\r\n return []\r\n }\r\n }\r\n return []\r\n }\r\n\r\n const getFeatures: () => Promise<AllGeoJSON> = async () => {\r\n // Check cache first\r\n const featuresKey = `features:ckan:${geoJsonResource.id}:${pkg.metadata_modified || pkg.metadata_created || ''}`\r\n const cached = getCache<AllGeoJSON>(featuresKey)\r\n if (cached) {\r\n return cached\r\n }\r\n\r\n const limit = 5_000\r\n const url2 = resourceUrl?.includes('datastore_search')\r\n ? `${resourceUrl}&limit=${limit}`\r\n : resourceUrl\r\n\r\n try {\r\n const response = await fetch(url2)\r\n if (!response.ok) {\r\n console.error('CKAN: Network response was not ok:', response.status)\r\n return {\r\n type: 'FeatureCollection',\r\n features: [],\r\n } as AllGeoJSON\r\n }\r\n const data = await response.json()\r\n let featuresCollection: AllGeoJSON | undefined\r\n\r\n if (data.result && Array.isArray(data.result.records)) {\r\n // CKAN Datastore: build FeatureCollection from records\r\n const features = data.result.records\r\n .map((properties: any) => {\r\n try {\r\n let geometry\r\n geometry = properties.geometry?.type ? properties.geometry : JSON.parse(properties.geometry)\r\n\r\n return {\r\n type: 'Feature',\r\n geometry,\r\n properties,\r\n }\r\n }\r\n catch {\r\n return null\r\n }\r\n })\r\n .filter((f: any) => f !== null)\r\n\r\n featuresCollection = {\r\n type: 'FeatureCollection',\r\n features,\r\n }\r\n }\r\n else if (\r\n data.type === 'FeatureCollection'\r\n && Array.isArray(data.features)\r\n ) {\r\n // Already a FeatureCollection\r\n featuresCollection = data\r\n }\r\n else {\r\n console.error(\r\n 'CKAN: No features available or unexpected response:',\r\n data,\r\n )\r\n return {\r\n type: 'FeatureCollection',\r\n features: [],\r\n } as AllGeoJSON\r\n }\r\n\r\n // Cache the result for 1 hour\r\n if (featuresCollection) {\r\n setCache(featuresKey, featuresCollection, 60 * 60 * 1000)\r\n }\r\n\r\n return featuresCollection\r\n }\r\n catch (error) {\r\n console.error('CKAN: Error processing dataset:', error)\r\n return {\r\n type: 'FeatureCollection',\r\n features: [],\r\n } as AllGeoJSON\r\n }\r\n }\r\n\r\n const name = formatName(pkg.title || pkg.name)\r\n\r\n return {\r\n id: pkg.id,\r\n name,\r\n url: fieldsUrl,\r\n publisher:\r\n pkg.organization?.title || pkg.organization?.name || 'Unknown',\r\n dataManagementSystem: 'ckan',\r\n license: pkg.license_title || pkg.license_id || 'Unknown',\r\n contact: pkg.author || pkg.maintainer || 'Unknown',\r\n keywords: pkg.tags?.map((tag: any) => tag.name) || [],\r\n description: pkg.notes\r\n ? stripHtml(pkg.notes)\r\n : 'No description available',\r\n dateReleased: pkg.metadata_created || '',\r\n dateUpdated: pkg.metadata_modified || '',\r\n information: pkg.information_url || '',\r\n countrySubdivision,\r\n municipality,\r\n type: pkg.type || 'GeoJSON',\r\n portal,\r\n getFields,\r\n getFeatures,\r\n getFeaturesData,\r\n group,\r\n layerColor: layerColorByName(name),\r\n }\r\n }),\r\n )\r\n ).filter(Boolean) // Remove nulls (excluded datasets)\r\n\r\n return transformedDatasets\r\n }\r\n catch (error) {\r\n console.error('Error fetching CKAN GeoJSON datasets:', error)\r\n return []\r\n }\r\n}\r\n\r\nexport interface CkanFieldTypes {\r\n [key: string]: { description: string, type: FieldType }\r\n}\r\n\r\nfunction ckanFieldTypes(): CkanFieldTypes {\r\n return {\r\n text: { description: 'Text', type: 'string' },\r\n int: { description: 'Integer', type: 'number' },\r\n float: { description: 'Float', type: 'number' },\r\n bool: { description: 'Boolean', type: 'boolean' },\r\n date: { description: 'Date', type: 'datetime' },\r\n timestamp: { description: 'Timestamp', type: 'datetime' },\r\n json: { description: 'JSON', type: 'object' },\r\n // Add more CKAN field types as needed\r\n }\r\n}\r\n"],"mappings":"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","names":["_a","response","data"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"localDatasets.d.ts","sourceRoot":"","sources":["../../../../../../../src/core/components/viewers/map/datasets/src/localDatasets.ts"],"names":[],"mappings":"AAMA,OAAO,EAAgB,KAAK,cAAc,EAAE,MAAM,8BAA8B,CAAA;AAOhF,OAAO,KAAK,EAAE,OAAO,EAAgC,MAAM,mCAAmC,CAAA;
|
|
1
|
+
{"version":3,"file":"localDatasets.d.ts","sourceRoot":"","sources":["../../../../../../../src/core/components/viewers/map/datasets/src/localDatasets.ts"],"names":[],"mappings":"AAMA,OAAO,EAAgB,KAAK,cAAc,EAAE,MAAM,8BAA8B,CAAA;AAOhF,OAAO,KAAK,EAAE,OAAO,EAAgC,MAAM,mCAAmC,CAAA;AAuZ9F,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,cAAc,GACrB,OAAO,CAAC,OAAO,EAAE,CAAC,CA+LpB"}
|
|
@@ -185,7 +185,6 @@ async function fetchMartinServerDatasets(portal) {
|
|
|
185
185
|
return [];
|
|
186
186
|
}
|
|
187
187
|
try {
|
|
188
|
-
console.log("Fetching Martin server catalog from:", martinServerUrl);
|
|
189
188
|
const { indexUrl, tileBaseUrl, fallbackCatalogUrl } = resolveMartinUrls(martinServerUrl);
|
|
190
189
|
const publishedLabels = await fetchPublishedTileLabels();
|
|
191
190
|
let response = await fetch(indexUrl);
|
|
@@ -198,7 +197,6 @@ async function fetchMartinServerDatasets(portal) {
|
|
|
198
197
|
}
|
|
199
198
|
const catalog = await response.json();
|
|
200
199
|
const tiles = normalizeMartinCatalog(catalog, tileBaseUrl).filter((t) => !isPublishedOrgTable(t.tileName) && t.tileName !== "cdt_tiles");
|
|
201
|
-
console.log("Martin catalog received:", tiles.length, "tiles");
|
|
202
200
|
const datasets = await Promise.all(tiles.map(async (tileInfo) => {
|
|
203
201
|
const publishedLabel = publishedLabels.get(tileInfo.tileName);
|
|
204
202
|
const cleanName = publishedLabel != null ? publishedLabel : formatName(
|
|
@@ -263,7 +261,6 @@ async function fetchMartinServerDatasets(portal) {
|
|
|
263
261
|
};
|
|
264
262
|
return dataset;
|
|
265
263
|
}));
|
|
266
|
-
console.log(`Transformed ${datasets.length} Martin datasets`);
|
|
267
264
|
let publishedDatasets = [];
|
|
268
265
|
try {
|
|
269
266
|
const fileRes = await fetch("/api/files");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../src/core/components/viewers/map/datasets/src/localDatasets.ts"],"sourcesContent":["// SPDX-License-Identifier: AGPL-3.0-or-later\r\n// Copyright (C) 2025 Collab Digital Twins\r\n\r\nimport { VectorTile } from '@mapbox/vector-tile'\r\nimport Pbf from 'pbf'\r\n\r\nimport { DatasetGroup, type OpenDataPortal } from '../../../../../types/dbTypes'\r\nimport { layerColorByName } from '../../utils/stringToColour'\r\nimport { formatName } from '../utils'\r\n\r\nimport { getCache, setCache } from './cache'\r\nimport { isPublishedOrgTable, buildPublishedTileDatasets, type PublishedFileRow } from './publishedTiles'\r\n\r\nimport type { Dataset, FieldType, LayerGeometryType } from '../../../../../types/datasetTypes'\r\nimport type { AllGeoJSON } from '@turf/turf'\r\n\r\n\r\n// Define the structure of local dataset items\r\ninterface LocalDatasetItem {\r\n name: string\r\n organization: number\r\n featuresUrl: string\r\n tileName: string\r\n sourceLayer: string\r\n layer: {\r\n type: string\r\n properties: Record<string, string>\r\n }\r\n visible: boolean\r\n clickable: boolean\r\n url: string\r\n description: string\r\n publisher: string\r\n countrySubdivision?: string\r\n municipality?: string\r\n}\r\n\r\ninterface MartinCatalogTile {\r\n content_type: string\r\n description: string\r\n}\r\n\r\ninterface MartinCatalog {\r\n tiles: Record<string, MartinCatalogTile>\r\n}\r\n\r\ninterface MartinTileStatsAttribute {\r\n attribute: string\r\n values?: Array<string | number>\r\n min?: number | string\r\n max?: number | string\r\n}\r\n\r\ninterface MartinTileStatsLayer {\r\n layer: string\r\n attributes?: MartinTileStatsAttribute[]\r\n}\r\n\r\ninterface MartinTileStats {\r\n layers?: MartinTileStatsLayer[]\r\n}\r\n\r\ninterface MartinTileJson {\r\n id?: string\r\n name?: string\r\n description?: string\r\n tiles?: string[]\r\n vector_layers?: Array<{\r\n id: string\r\n description?: string\r\n minzoom?: number\r\n maxzoom?: number\r\n fields?: Record<string, string>\r\n }>\r\n tilestats?: MartinTileStats\r\n minzoom?: number\r\n maxzoom?: number\r\n}\r\n\r\ntype MartinIndexResponse =\r\n | MartinCatalog\r\n | MartinTileJson[]\r\n | { tiles: Record<string, MartinTileJson> }\r\n | { tilesets: MartinTileJson[] }\r\n\r\ninterface NormalizedMartinTile {\r\n tileName: string\r\n tileUrl: string\r\n description?: string\r\n vectorLayerId: string\r\n tilestats?: MartinTileStats\r\n minzoom?: number\r\n}\r\n\r\nconst ORGANIZATION_FIELD = 'organization'\r\n\r\nfunction normalizeMartinBaseUrl(apiUrl: string) {\r\n return apiUrl.replace(/\\/+$/, '')\r\n}\r\n\r\nfunction resolveMartinUrls(apiUrl: string) {\r\n const trimmed = normalizeMartinBaseUrl(apiUrl)\r\n\r\n if (trimmed.endsWith('/tiles/index.json')) {\r\n const tileBaseUrl = trimmed.replace(/\\/tiles\\/index\\.json$/, '')\r\n return {\r\n indexUrl: trimmed,\r\n tileBaseUrl,\r\n fallbackCatalogUrl: `${tileBaseUrl}/catalog`,\r\n }\r\n }\r\n\r\n if (trimmed.endsWith('/catalog')) {\r\n const tileBaseUrl = trimmed.replace(/\\/catalog$/, '')\r\n return {\r\n indexUrl: `${tileBaseUrl}/tiles/index.json`,\r\n tileBaseUrl,\r\n fallbackCatalogUrl: trimmed,\r\n }\r\n }\r\n\r\n return {\r\n indexUrl: `${trimmed}/tiles/index.json`,\r\n tileBaseUrl: trimmed,\r\n fallbackCatalogUrl: `${trimmed}/catalog`,\r\n }\r\n}\r\n\r\nfunction ensureAbsoluteTileUrl(tileUrl: string, tileBaseUrl: string) {\r\n if (/^https?:\\/\\//i.test(tileUrl)) {\r\n return tileUrl\r\n }\r\n\r\n const cleanedBase = tileBaseUrl.replace(/\\/+$/, '')\r\n const cleanedPath = tileUrl.replace(/^\\/+/, '')\r\n return `${cleanedBase}/${cleanedPath}`\r\n}\r\n\r\nfunction normalizeTileInfo(tileInfo: MartinTileJson, fallbackName: string | null, tileBaseUrl: string): NormalizedMartinTile | null {\r\n const tileName = tileInfo.id || tileInfo.name || fallbackName\r\n if (!tileName) return null\r\n\r\n const vectorLayerId = tileInfo.vector_layers?.[0]?.id || tileName\r\n const tileUrlFromInfo = Array.isArray(tileInfo.tiles) && tileInfo.tiles.length > 0\r\n ? tileInfo.tiles[0]\r\n : `${tileBaseUrl}/${tileName}/{z}/{x}/{y}`\r\n\r\n return {\r\n tileName,\r\n tileUrl: ensureAbsoluteTileUrl(tileUrlFromInfo, tileBaseUrl),\r\n description: tileInfo.description || tileInfo.vector_layers?.[0]?.description,\r\n vectorLayerId,\r\n tilestats: tileInfo.tilestats,\r\n minzoom: tileInfo.minzoom || tileInfo.vector_layers?.[0]?.minzoom,\r\n }\r\n}\r\n\r\nfunction normalizeMartinCatalog(response: MartinIndexResponse, tileBaseUrl: string): NormalizedMartinTile[] {\r\n if (Array.isArray(response)) {\r\n return response\r\n .map(tile => normalizeTileInfo(tile, null, tileBaseUrl))\r\n .filter((t): t is NormalizedMartinTile => Boolean(t))\r\n }\r\n\r\n if ('tilesets' in response && Array.isArray(response.tilesets)) {\r\n return response.tilesets\r\n .map(tile => normalizeTileInfo(tile, null, tileBaseUrl))\r\n .filter((t): t is NormalizedMartinTile => Boolean(t))\r\n }\r\n\r\n if ('tiles' in response && response.tiles) {\r\n return Object.entries(response.tiles)\r\n .map(([tileName, tileInfo]) => normalizeTileInfo(tileInfo as MartinTileJson, tileName, tileBaseUrl))\r\n .filter((t): t is NormalizedMartinTile => Boolean(t))\r\n }\r\n\r\n return []\r\n}\r\n\r\nfunction parseOrganizationValue(value: unknown): number | undefined {\r\n if (value == null) return undefined\r\n if (typeof value === 'number' && Number.isFinite(value)) return value\r\n\r\n const parsed = Number.parseInt(String(value), 10)\r\n return Number.isFinite(parsed) ? parsed : undefined\r\n}\r\n\r\nfunction extractOrganizationFromTilestats(tilestats?: MartinTileStats): number | undefined {\r\n if (!tilestats?.layers) return undefined\r\n\r\n for (const layer of tilestats.layers) {\r\n const orgAttribute = layer.attributes?.find(attr =>\r\n attr.attribute?.toLowerCase() === ORGANIZATION_FIELD,\r\n )\r\n\r\n if (!orgAttribute) continue\r\n\r\n if (orgAttribute.values && orgAttribute.values.length > 0) {\r\n const value = parseOrganizationValue(orgAttribute.values[0])\r\n if (value !== undefined) return value\r\n }\r\n\r\n if (orgAttribute.min !== undefined && orgAttribute.max !== undefined) {\r\n const min = parseOrganizationValue(orgAttribute.min)\r\n const max = parseOrganizationValue(orgAttribute.max)\r\n if (min !== undefined && max !== undefined && min === max) {\r\n return min\r\n }\r\n }\r\n }\r\n\r\n return undefined\r\n}\r\n\r\nasync function fetchOrganizationFromTile(tileUrlTemplate: string, vectorLayerId: string, preferredZoom?: number) {\r\n try {\r\n const z = typeof preferredZoom === 'number' ? Math.max(0, Math.round(preferredZoom)) : 0\r\n const sampleUrl = tileUrlTemplate\r\n .replace('{z}', String(z))\r\n .replace('{x}', '0')\r\n .replace('{y}', '0')\r\n .replace('{Z}', String(z))\r\n .replace('{X}', '0')\r\n .replace('{Y}', '0')\r\n\r\n const response = await fetch(sampleUrl)\r\n if (!response.ok) return undefined\r\n\r\n const buffer = await response.arrayBuffer()\r\n const vectorTile = new VectorTile(new Pbf(buffer))\r\n const layer = vectorTile.layers[vectorLayerId] || Object.values(vectorTile.layers)[0]\r\n\r\n if (!layer || layer.length === 0) return undefined\r\n\r\n const feature = layer.feature(0)\r\n const { properties } = feature.toGeoJSON(0, 0, z)\r\n\r\n // Determine geometry type\r\n let geometryType: LayerGeometryType | undefined\r\n if (feature.type === 1) { // Point\r\n geometryType = 'circle'\r\n } else if (feature.type === 2) { // LineString\r\n geometryType = 'line'\r\n } else if (feature.type === 3) { // Polygon\r\n geometryType = 'fill'\r\n }\r\n\r\n return {\r\n organization: parseOrganizationValue(properties?.[ORGANIZATION_FIELD]),\r\n geometryType\r\n }\r\n } catch (error) {\r\n console.warn('Failed to read organization from tile', error)\r\n return undefined\r\n }\r\n}\r\n\r\nasync function resolveDatasetOrganization(tile: NormalizedMartinTile) {\r\n const fromTilestats = extractOrganizationFromTilestats(tile.tilestats)\r\n\r\n // Always fetch from tile to get geometry type, even if we have organization from tilestats\r\n const fromTile = await fetchOrganizationFromTile(tile.tileUrl, tile.vectorLayerId, tile.minzoom)\r\n\r\n return {\r\n organization: fromTilestats !== undefined ? fromTilestats : fromTile?.organization,\r\n geometryType: fromTile?.geometryType\r\n }\r\n}\r\n\r\nasync function fetchPublishedTileLabels(): Promise<Map<string, string>> {\r\n try {\r\n const res = await fetch('/api/files')\r\n if (!res.ok) return new Map()\r\n const body = await res.json()\r\n const rows: Array<{ name?: string | null, description?: string | null }> =\r\n Array.isArray(body?.files) ? body.files : []\r\n const map = new Map<string, string>()\r\n for (const row of rows) {\r\n if (!row?.description || !row?.name) continue\r\n try {\r\n const desc = JSON.parse(row.description) as { tiledTable?: unknown }\r\n if (typeof desc?.tiledTable === 'string') {\r\n map.set(desc.tiledTable, row.name.replace(/\\.geojson$/i, ''))\r\n }\r\n }\r\n catch { /* skip malformed */ }\r\n }\r\n return map\r\n }\r\n catch {\r\n return new Map()\r\n }\r\n}\r\n\r\nasync function fetchMartinServerDatasets(\r\n portal: OpenDataPortal,\r\n): Promise<Dataset[]> {\r\n const { countrySubdivision, municipality } = portal\r\n const martinServerUrl = portal.apiUrl\r\n\r\n if (!martinServerUrl) {\r\n console.warn('No Martin server URL configured for portal', portal.name)\r\n return []\r\n }\r\n\r\n try {\r\n console.log('Fetching Martin server catalog from:', martinServerUrl)\r\n\r\n const { indexUrl, tileBaseUrl, fallbackCatalogUrl } = resolveMartinUrls(martinServerUrl)\r\n\r\n const publishedLabels = await fetchPublishedTileLabels()\r\n\r\n let response = await fetch(indexUrl)\r\n if (!response.ok && fallbackCatalogUrl && fallbackCatalogUrl !== indexUrl) {\r\n console.warn(`Failed to fetch Martin index (${indexUrl}), falling back to catalog...`)\r\n response = await fetch(fallbackCatalogUrl)\r\n }\r\n\r\n if (!response.ok) {\r\n throw new Error(`Failed to fetch Martin catalog: ${response.status}`)\r\n }\r\n\r\n const catalog: MartinIndexResponse = await response.json()\r\n const tiles = normalizeMartinCatalog(catalog, tileBaseUrl)\r\n // Drop our published org tables (served via the cdt_tiles function + discovered\r\n // from File metadata) and the cdt_tiles function source itself (no ?source= → empty).\r\n .filter(t => !isPublishedOrgTable(t.tileName) && t.tileName !== 'cdt_tiles')\r\n\r\n console.log('Martin catalog received:', tiles.length, 'tiles')\r\n\r\n const datasets: Dataset[] = await Promise.all(tiles.map(async (tileInfo) => {\r\n const publishedLabel = publishedLabels.get(tileInfo.tileName)\r\n const cleanName = publishedLabel ?? formatName(\r\n tileInfo.tileName\r\n .replace(/_\\d{8}$/, '')\r\n .replace(/__\\d+_\\d+$/, ''),\r\n )\r\n\r\n const { organization, geometryType } = await resolveDatasetOrganization(tileInfo)\r\n\r\n const getFields = async () => {\r\n return []\r\n }\r\n\r\n const getFeatures = async (): Promise<AllGeoJSON> => {\r\n return {\r\n type: 'FeatureCollection',\r\n features: [],\r\n } as AllGeoJSON\r\n }\r\n\r\n const getFeaturesData = async () => {\r\n return {\r\n id: tileInfo.tileName,\r\n name: cleanName,\r\n type: 'MVT',\r\n visible: true,\r\n clickable: true,\r\n fields: [],\r\n source: {\r\n url: tileInfo.tileUrl,\r\n description: tileInfo.description,\r\n publisher: 'Martin Server',\r\n },\r\n serviceInfo: {\r\n tileName: tileInfo.tileName,\r\n sourceLayer: tileInfo.vectorLayerId,\r\n type: 'Martin',\r\n tileUrl: tileInfo.tileUrl,\r\n },\r\n }\r\n }\r\n\r\n const dataset: Dataset = {\r\n id: tileInfo.tileName,\r\n name: cleanName,\r\n organization,\r\n publisher: 'Martin Server',\r\n dataManagementSystem: 'other',\r\n license: 'Unknown',\r\n contact: 'Unknown',\r\n keywords: [],\r\n description: tileInfo.description || `Vector tile layer from Martin server: ${tileInfo.tileName}`,\r\n dateReleased: '',\r\n dateUpdated: '',\r\n information: tileInfo.tileUrl,\r\n url: tileInfo.tileUrl,\r\n countrySubdivision: countrySubdivision,\r\n municipality: municipality,\r\n type: 'MVT',\r\n datasetType: 'MVT' as any,\r\n portal,\r\n getFields,\r\n getFeatures,\r\n getFeaturesData,\r\n group: DatasetGroup.Organizational,\r\n layerColor: layerColorByName(cleanName),\r\n layerType: geometryType,\r\n }\r\n\r\n return dataset\r\n }))\r\n\r\n console.log(`Transformed ${datasets.length} Martin datasets`)\r\n\r\n let publishedDatasets: Dataset[] = []\r\n try {\r\n const fileRes = await fetch('/api/files')\r\n if (fileRes.ok) {\r\n const fileBody = await fileRes.json()\r\n const fileRows: PublishedFileRow[] = Array.isArray(fileBody?.files) ? fileBody.files : []\r\n publishedDatasets = buildPublishedTileDatasets(fileRows, tileBaseUrl, portal)\r\n }\r\n }\r\n catch (err) {\r\n console.warn('Failed to build published-tile datasets from /api/files', err)\r\n }\r\n\r\n return [...datasets, ...publishedDatasets]\r\n } catch (error) {\r\n console.error('Error fetching Martin server datasets:', error)\r\n return []\r\n }\r\n}\r\n\r\n// Fetch Local datasets from localDatasetsJson array and transform to Dataset array\r\nexport async function fetchLocalDatasets(\r\n portal: OpenDataPortal,\r\n): Promise<Dataset[]> {\r\n const { countrySubdivision, municipality } = portal\r\n const apiUrl = portal.apiUrl || ''\r\n const trimmedApiUrl = apiUrl.replace(/\\/+$/, '')\r\n\r\n if (!apiUrl) {\r\n console.warn('No apiUrl provided for portal', portal.name)\r\n return []\r\n }\r\n\r\n // Skip legacy portals with non-absolute URLs (e.g. ./localDatasets/index.json)\r\n if (!apiUrl.startsWith('http://') && !apiUrl.startsWith('https://')) {\r\n return []\r\n }\r\n\r\n if (trimmedApiUrl.includes('/catalog') || trimmedApiUrl.includes('/tiles/index')) {\r\n return fetchMartinServerDatasets(portal)\r\n }\r\n\r\n try {\r\n // Fetch the localDatasets.json file\r\n const response = await fetch(apiUrl)\r\n if (!response.ok) {\r\n const shouldTryMartinFallback =\r\n !trimmedApiUrl.endsWith('.json')\r\n && !trimmedApiUrl.includes('/catalog')\r\n && !trimmedApiUrl.includes('/tiles/index')\r\n\r\n if (response.status === 404 && shouldTryMartinFallback) {\r\n console.warn(\r\n `Local dataset URL returned 404 (${apiUrl}); retrying as a Martin server base URL`,\r\n )\r\n return fetchMartinServerDatasets({\r\n ...portal,\r\n apiUrl: trimmedApiUrl,\r\n })\r\n }\r\n\r\n throw new Error(`Failed to fetch local datasets from ${apiUrl}: ${response.status}`)\r\n }\r\n\r\n const localDatasetsJson: LocalDatasetItem[] = await response.json()\r\n\r\n const transformedDatasets: Dataset[] = localDatasetsJson.map((datasetItem: LocalDatasetItem) => {\r\n // Simple getFields function that returns fields from layer properties\r\n const getFields = async () => {\r\n if (datasetItem.layer?.properties) {\r\n return Object.entries(datasetItem.layer.properties).map(([name, type]) => ({\r\n name,\r\n label: formatName(name),\r\n type: type as string,\r\n description: `${formatName(name)} field`,\r\n }))\r\n }\r\n return []\r\n }\r\n\r\n // Fetch GeoJSON data from the featuresUrl\r\n const getFeatures = async (): Promise<AllGeoJSON> => {\r\n // Create a cache key based on the dataset name and features URL\r\n const featuresKey = `features:local:${datasetItem.name}:${datasetItem.featuresUrl}`\r\n\r\n // Check cache first\r\n const cachedFeatures = getCache<AllGeoJSON>(featuresKey)\r\n if (cachedFeatures) {\r\n return cachedFeatures\r\n }\r\n\r\n try {\r\n // Use the featuresUrl from the dataset item\r\n let geojsonUrl: string\r\n if (datasetItem.featuresUrl.startsWith('http://') || datasetItem.featuresUrl.startsWith('https://')) {\r\n geojsonUrl = datasetItem.featuresUrl\r\n } else {\r\n const baseUrl = apiUrl.endsWith('.json')\r\n ? apiUrl.substring(0, apiUrl.lastIndexOf('/'))\r\n : apiUrl\r\n geojsonUrl = `${baseUrl}/${datasetItem.featuresUrl}`\r\n }\r\n\r\n const response = await fetch(geojsonUrl)\r\n\r\n if (!response.ok) {\r\n throw new Error(`Failed to fetch GeoJSON: ${response.status}`)\r\n }\r\n\r\n const geojsonData = await response.json() as AllGeoJSON\r\n\r\n // Cache for 1 hour\r\n setCache(featuresKey, geojsonData, 60 * 60 * 1000)\r\n\r\n return geojsonData\r\n } catch (error) {\r\n console.error(`Error loading GeoJSON for ${datasetItem.name}:`, error)\r\n return {\r\n type: 'FeatureCollection',\r\n features: [],\r\n } as AllGeoJSON\r\n }\r\n }\r\n\r\n const name = formatName(datasetItem.name)\r\n\r\n const getFeaturesData = async () => {\r\n try {\r\n return {\r\n // Basic layer information\r\n id: datasetItem.tileName || datasetItem.name.toLowerCase().replace(/\\s+/g, '-'),\r\n name: datasetItem.name,\r\n type: datasetItem.layer?.type || 'GeoJSON',\r\n\r\n // Layer configuration\r\n visible: datasetItem.visible,\r\n clickable: datasetItem.clickable,\r\n\r\n // Field information (similar to ArcGIS fields structure)\r\n fields: datasetItem.layer?.properties\r\n ? Object.entries(datasetItem.layer.properties).map(([name, type]) => ({\r\n name,\r\n type: type as FieldType,\r\n alias: formatName(name),\r\n description: `${formatName(name)} field`,\r\n }))\r\n : [],\r\n\r\n // Source information\r\n source: {\r\n url: datasetItem.url,\r\n description: datasetItem.description,\r\n publisher: datasetItem.publisher,\r\n featuresUrl: datasetItem.featuresUrl,\r\n },\r\n\r\n // Additional metadata that DatasetDetails.tsx might use\r\n editingInfo: {\r\n lastEditDate: new Date().toISOString(), // Current date as placeholder\r\n dataLastEditDate: new Date().toISOString(),\r\n },\r\n\r\n // Layer properties summary\r\n properties: datasetItem.layer?.properties || {},\r\n fieldCount: datasetItem.layer?.properties ? Object.keys(datasetItem.layer.properties).length : 0,\r\n\r\n // Geometry information\r\n geometryType: 'Unknown', // You could infer this from the actual GeoJSON if needed\r\n\r\n // Service information (for compatibility)\r\n serviceInfo: {\r\n tileName: datasetItem.tileName,\r\n sourceLayer: datasetItem.sourceLayer,\r\n type: 'Local',\r\n },\r\n }\r\n } catch (error) {\r\n console.error('Error getting features data for dataset:', datasetItem.name, error)\r\n return null\r\n }\r\n }\r\n\r\n return {\r\n id: datasetItem.tileName || datasetItem.name.toLowerCase().replace(/\\s+/g, '-'),\r\n name,\r\n organization: datasetItem.organization || 0,\r\n publisher: datasetItem.publisher || 'Unknown',\r\n dataManagementSystem: 'other',\r\n license: 'Unknown',\r\n contact: 'Unknown',\r\n keywords: [],\r\n description: datasetItem.description || 'No description available',\r\n dateReleased: '',\r\n dateUpdated: '',\r\n information: datasetItem.url || '',\r\n url: datasetItem.url || '#', // Ensure there's always a valid URL for the Link component\r\n countrySubdivision: datasetItem.countrySubdivision || countrySubdivision,\r\n municipality: datasetItem.municipality || municipality,\r\n type: datasetItem.layer?.type || 'GeoJSON',\r\n datasetType: 'GeoJSON' as const,\r\n portal,\r\n getFields,\r\n getFeatures,\r\n getFeaturesData,\r\n group: 'Organizational',\r\n layerColor: layerColorByName(name),\r\n }\r\n })\r\n\r\n return transformedDatasets\r\n } catch (error) {\r\n console.error('Error fetching local datasets:', error)\r\n return []\r\n }\r\n}\r\n"],"mappings":"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","names":["_a","name","response"]}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../src/core/components/viewers/map/datasets/src/localDatasets.ts"],"sourcesContent":["// SPDX-License-Identifier: AGPL-3.0-or-later\r\n// Copyright (C) 2025 Collab Digital Twins\r\n\r\nimport { VectorTile } from '@mapbox/vector-tile'\r\nimport Pbf from 'pbf'\r\n\r\nimport { DatasetGroup, type OpenDataPortal } from '../../../../../types/dbTypes'\r\nimport { layerColorByName } from '../../utils/stringToColour'\r\nimport { formatName } from '../utils'\r\n\r\nimport { getCache, setCache } from './cache'\r\nimport { isPublishedOrgTable, buildPublishedTileDatasets, type PublishedFileRow } from './publishedTiles'\r\n\r\nimport type { Dataset, FieldType, LayerGeometryType } from '../../../../../types/datasetTypes'\r\nimport type { AllGeoJSON } from '@turf/turf'\r\n\r\n\r\n// Define the structure of local dataset items\r\ninterface LocalDatasetItem {\r\n name: string\r\n organization: number\r\n featuresUrl: string\r\n tileName: string\r\n sourceLayer: string\r\n layer: {\r\n type: string\r\n properties: Record<string, string>\r\n }\r\n visible: boolean\r\n clickable: boolean\r\n url: string\r\n description: string\r\n publisher: string\r\n countrySubdivision?: string\r\n municipality?: string\r\n}\r\n\r\ninterface MartinCatalogTile {\r\n content_type: string\r\n description: string\r\n}\r\n\r\ninterface MartinCatalog {\r\n tiles: Record<string, MartinCatalogTile>\r\n}\r\n\r\ninterface MartinTileStatsAttribute {\r\n attribute: string\r\n values?: Array<string | number>\r\n min?: number | string\r\n max?: number | string\r\n}\r\n\r\ninterface MartinTileStatsLayer {\r\n layer: string\r\n attributes?: MartinTileStatsAttribute[]\r\n}\r\n\r\ninterface MartinTileStats {\r\n layers?: MartinTileStatsLayer[]\r\n}\r\n\r\ninterface MartinTileJson {\r\n id?: string\r\n name?: string\r\n description?: string\r\n tiles?: string[]\r\n vector_layers?: Array<{\r\n id: string\r\n description?: string\r\n minzoom?: number\r\n maxzoom?: number\r\n fields?: Record<string, string>\r\n }>\r\n tilestats?: MartinTileStats\r\n minzoom?: number\r\n maxzoom?: number\r\n}\r\n\r\ntype MartinIndexResponse =\r\n | MartinCatalog\r\n | MartinTileJson[]\r\n | { tiles: Record<string, MartinTileJson> }\r\n | { tilesets: MartinTileJson[] }\r\n\r\ninterface NormalizedMartinTile {\r\n tileName: string\r\n tileUrl: string\r\n description?: string\r\n vectorLayerId: string\r\n tilestats?: MartinTileStats\r\n minzoom?: number\r\n}\r\n\r\nconst ORGANIZATION_FIELD = 'organization'\r\n\r\nfunction normalizeMartinBaseUrl(apiUrl: string) {\r\n return apiUrl.replace(/\\/+$/, '')\r\n}\r\n\r\nfunction resolveMartinUrls(apiUrl: string) {\r\n const trimmed = normalizeMartinBaseUrl(apiUrl)\r\n\r\n if (trimmed.endsWith('/tiles/index.json')) {\r\n const tileBaseUrl = trimmed.replace(/\\/tiles\\/index\\.json$/, '')\r\n return {\r\n indexUrl: trimmed,\r\n tileBaseUrl,\r\n fallbackCatalogUrl: `${tileBaseUrl}/catalog`,\r\n }\r\n }\r\n\r\n if (trimmed.endsWith('/catalog')) {\r\n const tileBaseUrl = trimmed.replace(/\\/catalog$/, '')\r\n return {\r\n indexUrl: `${tileBaseUrl}/tiles/index.json`,\r\n tileBaseUrl,\r\n fallbackCatalogUrl: trimmed,\r\n }\r\n }\r\n\r\n return {\r\n indexUrl: `${trimmed}/tiles/index.json`,\r\n tileBaseUrl: trimmed,\r\n fallbackCatalogUrl: `${trimmed}/catalog`,\r\n }\r\n}\r\n\r\nfunction ensureAbsoluteTileUrl(tileUrl: string, tileBaseUrl: string) {\r\n if (/^https?:\\/\\//i.test(tileUrl)) {\r\n return tileUrl\r\n }\r\n\r\n const cleanedBase = tileBaseUrl.replace(/\\/+$/, '')\r\n const cleanedPath = tileUrl.replace(/^\\/+/, '')\r\n return `${cleanedBase}/${cleanedPath}`\r\n}\r\n\r\nfunction normalizeTileInfo(tileInfo: MartinTileJson, fallbackName: string | null, tileBaseUrl: string): NormalizedMartinTile | null {\r\n const tileName = tileInfo.id || tileInfo.name || fallbackName\r\n if (!tileName) return null\r\n\r\n const vectorLayerId = tileInfo.vector_layers?.[0]?.id || tileName\r\n const tileUrlFromInfo = Array.isArray(tileInfo.tiles) && tileInfo.tiles.length > 0\r\n ? tileInfo.tiles[0]\r\n : `${tileBaseUrl}/${tileName}/{z}/{x}/{y}`\r\n\r\n return {\r\n tileName,\r\n tileUrl: ensureAbsoluteTileUrl(tileUrlFromInfo, tileBaseUrl),\r\n description: tileInfo.description || tileInfo.vector_layers?.[0]?.description,\r\n vectorLayerId,\r\n tilestats: tileInfo.tilestats,\r\n minzoom: tileInfo.minzoom || tileInfo.vector_layers?.[0]?.minzoom,\r\n }\r\n}\r\n\r\nfunction normalizeMartinCatalog(response: MartinIndexResponse, tileBaseUrl: string): NormalizedMartinTile[] {\r\n if (Array.isArray(response)) {\r\n return response\r\n .map(tile => normalizeTileInfo(tile, null, tileBaseUrl))\r\n .filter((t): t is NormalizedMartinTile => Boolean(t))\r\n }\r\n\r\n if ('tilesets' in response && Array.isArray(response.tilesets)) {\r\n return response.tilesets\r\n .map(tile => normalizeTileInfo(tile, null, tileBaseUrl))\r\n .filter((t): t is NormalizedMartinTile => Boolean(t))\r\n }\r\n\r\n if ('tiles' in response && response.tiles) {\r\n return Object.entries(response.tiles)\r\n .map(([tileName, tileInfo]) => normalizeTileInfo(tileInfo as MartinTileJson, tileName, tileBaseUrl))\r\n .filter((t): t is NormalizedMartinTile => Boolean(t))\r\n }\r\n\r\n return []\r\n}\r\n\r\nfunction parseOrganizationValue(value: unknown): number | undefined {\r\n if (value == null) return undefined\r\n if (typeof value === 'number' && Number.isFinite(value)) return value\r\n\r\n const parsed = Number.parseInt(String(value), 10)\r\n return Number.isFinite(parsed) ? parsed : undefined\r\n}\r\n\r\nfunction extractOrganizationFromTilestats(tilestats?: MartinTileStats): number | undefined {\r\n if (!tilestats?.layers) return undefined\r\n\r\n for (const layer of tilestats.layers) {\r\n const orgAttribute = layer.attributes?.find(attr =>\r\n attr.attribute?.toLowerCase() === ORGANIZATION_FIELD,\r\n )\r\n\r\n if (!orgAttribute) continue\r\n\r\n if (orgAttribute.values && orgAttribute.values.length > 0) {\r\n const value = parseOrganizationValue(orgAttribute.values[0])\r\n if (value !== undefined) return value\r\n }\r\n\r\n if (orgAttribute.min !== undefined && orgAttribute.max !== undefined) {\r\n const min = parseOrganizationValue(orgAttribute.min)\r\n const max = parseOrganizationValue(orgAttribute.max)\r\n if (min !== undefined && max !== undefined && min === max) {\r\n return min\r\n }\r\n }\r\n }\r\n\r\n return undefined\r\n}\r\n\r\nasync function fetchOrganizationFromTile(tileUrlTemplate: string, vectorLayerId: string, preferredZoom?: number) {\r\n try {\r\n const z = typeof preferredZoom === 'number' ? Math.max(0, Math.round(preferredZoom)) : 0\r\n const sampleUrl = tileUrlTemplate\r\n .replace('{z}', String(z))\r\n .replace('{x}', '0')\r\n .replace('{y}', '0')\r\n .replace('{Z}', String(z))\r\n .replace('{X}', '0')\r\n .replace('{Y}', '0')\r\n\r\n const response = await fetch(sampleUrl)\r\n if (!response.ok) return undefined\r\n\r\n const buffer = await response.arrayBuffer()\r\n const vectorTile = new VectorTile(new Pbf(buffer))\r\n const layer = vectorTile.layers[vectorLayerId] || Object.values(vectorTile.layers)[0]\r\n\r\n if (!layer || layer.length === 0) return undefined\r\n\r\n const feature = layer.feature(0)\r\n const { properties } = feature.toGeoJSON(0, 0, z)\r\n\r\n // Determine geometry type\r\n let geometryType: LayerGeometryType | undefined\r\n if (feature.type === 1) { // Point\r\n geometryType = 'circle'\r\n } else if (feature.type === 2) { // LineString\r\n geometryType = 'line'\r\n } else if (feature.type === 3) { // Polygon\r\n geometryType = 'fill'\r\n }\r\n\r\n return {\r\n organization: parseOrganizationValue(properties?.[ORGANIZATION_FIELD]),\r\n geometryType\r\n }\r\n } catch (error) {\r\n console.warn('Failed to read organization from tile', error)\r\n return undefined\r\n }\r\n}\r\n\r\nasync function resolveDatasetOrganization(tile: NormalizedMartinTile) {\r\n const fromTilestats = extractOrganizationFromTilestats(tile.tilestats)\r\n\r\n // Always fetch from tile to get geometry type, even if we have organization from tilestats\r\n const fromTile = await fetchOrganizationFromTile(tile.tileUrl, tile.vectorLayerId, tile.minzoom)\r\n\r\n return {\r\n organization: fromTilestats !== undefined ? fromTilestats : fromTile?.organization,\r\n geometryType: fromTile?.geometryType\r\n }\r\n}\r\n\r\nasync function fetchPublishedTileLabels(): Promise<Map<string, string>> {\r\n try {\r\n const res = await fetch('/api/files')\r\n if (!res.ok) return new Map()\r\n const body = await res.json()\r\n const rows: Array<{ name?: string | null, description?: string | null }> =\r\n Array.isArray(body?.files) ? body.files : []\r\n const map = new Map<string, string>()\r\n for (const row of rows) {\r\n if (!row?.description || !row?.name) continue\r\n try {\r\n const desc = JSON.parse(row.description) as { tiledTable?: unknown }\r\n if (typeof desc?.tiledTable === 'string') {\r\n map.set(desc.tiledTable, row.name.replace(/\\.geojson$/i, ''))\r\n }\r\n }\r\n catch { /* skip malformed */ }\r\n }\r\n return map\r\n }\r\n catch {\r\n return new Map()\r\n }\r\n}\r\n\r\nasync function fetchMartinServerDatasets(\r\n portal: OpenDataPortal,\r\n): Promise<Dataset[]> {\r\n const { countrySubdivision, municipality } = portal\r\n const martinServerUrl = portal.apiUrl\r\n\r\n if (!martinServerUrl) {\r\n console.warn('No Martin server URL configured for portal', portal.name)\r\n return []\r\n }\r\n\r\n try {\r\n const { indexUrl, tileBaseUrl, fallbackCatalogUrl } = resolveMartinUrls(martinServerUrl)\r\n\r\n const publishedLabels = await fetchPublishedTileLabels()\r\n\r\n let response = await fetch(indexUrl)\r\n if (!response.ok && fallbackCatalogUrl && fallbackCatalogUrl !== indexUrl) {\r\n console.warn(`Failed to fetch Martin index (${indexUrl}), falling back to catalog...`)\r\n response = await fetch(fallbackCatalogUrl)\r\n }\r\n\r\n if (!response.ok) {\r\n throw new Error(`Failed to fetch Martin catalog: ${response.status}`)\r\n }\r\n\r\n const catalog: MartinIndexResponse = await response.json()\r\n const tiles = normalizeMartinCatalog(catalog, tileBaseUrl)\r\n // Drop our published org tables (served via the cdt_tiles function + discovered\r\n // from File metadata) and the cdt_tiles function source itself (no ?source= → empty).\r\n .filter(t => !isPublishedOrgTable(t.tileName) && t.tileName !== 'cdt_tiles')\r\n\r\n const datasets: Dataset[] = await Promise.all(tiles.map(async (tileInfo) => {\r\n const publishedLabel = publishedLabels.get(tileInfo.tileName)\r\n const cleanName = publishedLabel ?? formatName(\r\n tileInfo.tileName\r\n .replace(/_\\d{8}$/, '')\r\n .replace(/__\\d+_\\d+$/, ''),\r\n )\r\n\r\n const { organization, geometryType } = await resolveDatasetOrganization(tileInfo)\r\n\r\n const getFields = async () => {\r\n return []\r\n }\r\n\r\n const getFeatures = async (): Promise<AllGeoJSON> => {\r\n return {\r\n type: 'FeatureCollection',\r\n features: [],\r\n } as AllGeoJSON\r\n }\r\n\r\n const getFeaturesData = async () => {\r\n return {\r\n id: tileInfo.tileName,\r\n name: cleanName,\r\n type: 'MVT',\r\n visible: true,\r\n clickable: true,\r\n fields: [],\r\n source: {\r\n url: tileInfo.tileUrl,\r\n description: tileInfo.description,\r\n publisher: 'Martin Server',\r\n },\r\n serviceInfo: {\r\n tileName: tileInfo.tileName,\r\n sourceLayer: tileInfo.vectorLayerId,\r\n type: 'Martin',\r\n tileUrl: tileInfo.tileUrl,\r\n },\r\n }\r\n }\r\n\r\n const dataset: Dataset = {\r\n id: tileInfo.tileName,\r\n name: cleanName,\r\n organization,\r\n publisher: 'Martin Server',\r\n dataManagementSystem: 'other',\r\n license: 'Unknown',\r\n contact: 'Unknown',\r\n keywords: [],\r\n description: tileInfo.description || `Vector tile layer from Martin server: ${tileInfo.tileName}`,\r\n dateReleased: '',\r\n dateUpdated: '',\r\n information: tileInfo.tileUrl,\r\n url: tileInfo.tileUrl,\r\n countrySubdivision: countrySubdivision,\r\n municipality: municipality,\r\n type: 'MVT',\r\n datasetType: 'MVT' as any,\r\n portal,\r\n getFields,\r\n getFeatures,\r\n getFeaturesData,\r\n group: DatasetGroup.Organizational,\r\n layerColor: layerColorByName(cleanName),\r\n layerType: geometryType,\r\n }\r\n\r\n return dataset\r\n }))\r\n\r\n let publishedDatasets: Dataset[] = []\r\n try {\r\n const fileRes = await fetch('/api/files')\r\n if (fileRes.ok) {\r\n const fileBody = await fileRes.json()\r\n const fileRows: PublishedFileRow[] = Array.isArray(fileBody?.files) ? fileBody.files : []\r\n publishedDatasets = buildPublishedTileDatasets(fileRows, tileBaseUrl, portal)\r\n }\r\n }\r\n catch (err) {\r\n console.warn('Failed to build published-tile datasets from /api/files', err)\r\n }\r\n\r\n return [...datasets, ...publishedDatasets]\r\n } catch (error) {\r\n console.error('Error fetching Martin server datasets:', error)\r\n return []\r\n }\r\n}\r\n\r\n// Fetch Local datasets from localDatasetsJson array and transform to Dataset array\r\nexport async function fetchLocalDatasets(\r\n portal: OpenDataPortal,\r\n): Promise<Dataset[]> {\r\n const { countrySubdivision, municipality } = portal\r\n const apiUrl = portal.apiUrl || ''\r\n const trimmedApiUrl = apiUrl.replace(/\\/+$/, '')\r\n\r\n if (!apiUrl) {\r\n console.warn('No apiUrl provided for portal', portal.name)\r\n return []\r\n }\r\n\r\n // Skip legacy portals with non-absolute URLs (e.g. ./localDatasets/index.json)\r\n if (!apiUrl.startsWith('http://') && !apiUrl.startsWith('https://')) {\r\n return []\r\n }\r\n\r\n if (trimmedApiUrl.includes('/catalog') || trimmedApiUrl.includes('/tiles/index')) {\r\n return fetchMartinServerDatasets(portal)\r\n }\r\n\r\n try {\r\n // Fetch the localDatasets.json file\r\n const response = await fetch(apiUrl)\r\n if (!response.ok) {\r\n const shouldTryMartinFallback =\r\n !trimmedApiUrl.endsWith('.json')\r\n && !trimmedApiUrl.includes('/catalog')\r\n && !trimmedApiUrl.includes('/tiles/index')\r\n\r\n if (response.status === 404 && shouldTryMartinFallback) {\r\n console.warn(\r\n `Local dataset URL returned 404 (${apiUrl}); retrying as a Martin server base URL`,\r\n )\r\n return fetchMartinServerDatasets({\r\n ...portal,\r\n apiUrl: trimmedApiUrl,\r\n })\r\n }\r\n\r\n throw new Error(`Failed to fetch local datasets from ${apiUrl}: ${response.status}`)\r\n }\r\n\r\n const localDatasetsJson: LocalDatasetItem[] = await response.json()\r\n\r\n const transformedDatasets: Dataset[] = localDatasetsJson.map((datasetItem: LocalDatasetItem) => {\r\n // Simple getFields function that returns fields from layer properties\r\n const getFields = async () => {\r\n if (datasetItem.layer?.properties) {\r\n return Object.entries(datasetItem.layer.properties).map(([name, type]) => ({\r\n name,\r\n label: formatName(name),\r\n type: type as string,\r\n description: `${formatName(name)} field`,\r\n }))\r\n }\r\n return []\r\n }\r\n\r\n // Fetch GeoJSON data from the featuresUrl\r\n const getFeatures = async (): Promise<AllGeoJSON> => {\r\n // Create a cache key based on the dataset name and features URL\r\n const featuresKey = `features:local:${datasetItem.name}:${datasetItem.featuresUrl}`\r\n\r\n // Check cache first\r\n const cachedFeatures = getCache<AllGeoJSON>(featuresKey)\r\n if (cachedFeatures) {\r\n return cachedFeatures\r\n }\r\n\r\n try {\r\n // Use the featuresUrl from the dataset item\r\n let geojsonUrl: string\r\n if (datasetItem.featuresUrl.startsWith('http://') || datasetItem.featuresUrl.startsWith('https://')) {\r\n geojsonUrl = datasetItem.featuresUrl\r\n } else {\r\n const baseUrl = apiUrl.endsWith('.json')\r\n ? apiUrl.substring(0, apiUrl.lastIndexOf('/'))\r\n : apiUrl\r\n geojsonUrl = `${baseUrl}/${datasetItem.featuresUrl}`\r\n }\r\n\r\n const response = await fetch(geojsonUrl)\r\n\r\n if (!response.ok) {\r\n throw new Error(`Failed to fetch GeoJSON: ${response.status}`)\r\n }\r\n\r\n const geojsonData = await response.json() as AllGeoJSON\r\n\r\n // Cache for 1 hour\r\n setCache(featuresKey, geojsonData, 60 * 60 * 1000)\r\n\r\n return geojsonData\r\n } catch (error) {\r\n console.error(`Error loading GeoJSON for ${datasetItem.name}:`, error)\r\n return {\r\n type: 'FeatureCollection',\r\n features: [],\r\n } as AllGeoJSON\r\n }\r\n }\r\n\r\n const name = formatName(datasetItem.name)\r\n\r\n const getFeaturesData = async () => {\r\n try {\r\n return {\r\n // Basic layer information\r\n id: datasetItem.tileName || datasetItem.name.toLowerCase().replace(/\\s+/g, '-'),\r\n name: datasetItem.name,\r\n type: datasetItem.layer?.type || 'GeoJSON',\r\n\r\n // Layer configuration\r\n visible: datasetItem.visible,\r\n clickable: datasetItem.clickable,\r\n\r\n // Field information (similar to ArcGIS fields structure)\r\n fields: datasetItem.layer?.properties\r\n ? Object.entries(datasetItem.layer.properties).map(([name, type]) => ({\r\n name,\r\n type: type as FieldType,\r\n alias: formatName(name),\r\n description: `${formatName(name)} field`,\r\n }))\r\n : [],\r\n\r\n // Source information\r\n source: {\r\n url: datasetItem.url,\r\n description: datasetItem.description,\r\n publisher: datasetItem.publisher,\r\n featuresUrl: datasetItem.featuresUrl,\r\n },\r\n\r\n // Additional metadata that DatasetDetails.tsx might use\r\n editingInfo: {\r\n lastEditDate: new Date().toISOString(), // Current date as placeholder\r\n dataLastEditDate: new Date().toISOString(),\r\n },\r\n\r\n // Layer properties summary\r\n properties: datasetItem.layer?.properties || {},\r\n fieldCount: datasetItem.layer?.properties ? Object.keys(datasetItem.layer.properties).length : 0,\r\n\r\n // Geometry information\r\n geometryType: 'Unknown', // You could infer this from the actual GeoJSON if needed\r\n\r\n // Service information (for compatibility)\r\n serviceInfo: {\r\n tileName: datasetItem.tileName,\r\n sourceLayer: datasetItem.sourceLayer,\r\n type: 'Local',\r\n },\r\n }\r\n } catch (error) {\r\n console.error('Error getting features data for dataset:', datasetItem.name, error)\r\n return null\r\n }\r\n }\r\n\r\n return {\r\n id: datasetItem.tileName || datasetItem.name.toLowerCase().replace(/\\s+/g, '-'),\r\n name,\r\n organization: datasetItem.organization || 0,\r\n publisher: datasetItem.publisher || 'Unknown',\r\n dataManagementSystem: 'other',\r\n license: 'Unknown',\r\n contact: 'Unknown',\r\n keywords: [],\r\n description: datasetItem.description || 'No description available',\r\n dateReleased: '',\r\n dateUpdated: '',\r\n information: datasetItem.url || '',\r\n url: datasetItem.url || '#', // Ensure there's always a valid URL for the Link component\r\n countrySubdivision: datasetItem.countrySubdivision || countrySubdivision,\r\n municipality: datasetItem.municipality || municipality,\r\n type: datasetItem.layer?.type || 'GeoJSON',\r\n datasetType: 'GeoJSON' as const,\r\n portal,\r\n getFields,\r\n getFeatures,\r\n getFeaturesData,\r\n group: 'Organizational',\r\n layerColor: layerColorByName(name),\r\n }\r\n })\r\n\r\n return transformedDatasets\r\n } catch (error) {\r\n console.error('Error fetching local datasets:', error)\r\n return []\r\n }\r\n}\r\n"],"mappings":"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","names":["_a","name","response"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"opendatasoftDatasets.d.ts","sourceRoot":"","sources":["../../../../../../../src/core/components/viewers/map/datasets/src/opendatasoftDatasets.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mCAAmC,CAAA;AAChE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAA;AAIlE,wBAAsB,yBAAyB,CAC7C,MAAM,EAAE,cAAc,GACrB,OAAO,CAAC,OAAO,EAAE,CAAC,
|
|
1
|
+
{"version":3,"file":"opendatasoftDatasets.d.ts","sourceRoot":"","sources":["../../../../../../../src/core/components/viewers/map/datasets/src/opendatasoftDatasets.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mCAAmC,CAAA;AAChE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAA;AAIlE,wBAAsB,yBAAyB,CAC7C,MAAM,EAAE,cAAc,GACrB,OAAO,CAAC,OAAO,EAAE,CAAC,CAsRpB"}
|
|
@@ -91,9 +91,6 @@ async function fetchOpenDataSoftDatasets(portal) {
|
|
|
91
91
|
if ([401, 403, 499, 504].includes(code) || /unauthorized|forbidden|token required|timed out/i.test(msg)) return false;
|
|
92
92
|
return true;
|
|
93
93
|
}
|
|
94
|
-
const recs = sample.records || sample.results || [];
|
|
95
|
-
const hasGeom = Array.isArray(recs) ? recs.some((r) => !!(r == null ? void 0 : r.geometry) || !!(r == null ? void 0 : r.geom) || !!(r == null ? void 0 : r.geo_shape) || !!(r == null ? void 0 : r.geo_point_2d)) : false;
|
|
96
|
-
console.log(`\u2139\uFE0F OpenDataSoft validation: Dataset "${datasetId}" has geometry: ${hasGeom}, records: ${recs.length}`);
|
|
97
94
|
return true;
|
|
98
95
|
} else if ([401, 403, 504].includes(sampleRes.status)) {
|
|
99
96
|
console.error(`\u274C OpenDataSoft validation: Dataset "${datasetId}" auth/timeout error: ${sampleRes.status}`);
|
|
@@ -108,7 +105,6 @@ async function fetchOpenDataSoftDatasets(portal) {
|
|
|
108
105
|
}
|
|
109
106
|
});
|
|
110
107
|
if (!isValid) {
|
|
111
|
-
console.log(`\u{1F5D1}\uFE0F OpenDataSoft: Filtering out dataset "${datasetId}" - validation failed`);
|
|
112
108
|
return null;
|
|
113
109
|
}
|
|
114
110
|
const getFields = async () => {
|
|
@@ -130,12 +126,9 @@ async function fetchOpenDataSoftDatasets(portal) {
|
|
|
130
126
|
return fields;
|
|
131
127
|
};
|
|
132
128
|
const getFeatures = async () => {
|
|
133
|
-
var _a2, _b2, _c2;
|
|
134
129
|
const featuresKey = `features:opendatasoft:${datasetId}:${datasetModified != null ? datasetModified : ""}`;
|
|
135
130
|
const cached = getCache(featuresKey);
|
|
136
131
|
if (cached) {
|
|
137
|
-
const cachedCount = "features" in cached ? (_b2 = (_a2 = cached.features) == null ? void 0 : _a2.length) != null ? _b2 : 0 : 0;
|
|
138
|
-
console.log(`\u2705 OpenDataSoft getFeatures: Returning ${cachedCount} cached features for dataset "${datasetId}"`);
|
|
139
132
|
return cached;
|
|
140
133
|
}
|
|
141
134
|
try {
|
|
@@ -143,10 +136,8 @@ async function fetchOpenDataSoftDatasets(portal) {
|
|
|
143
136
|
let offset2 = 0;
|
|
144
137
|
let totalCount2 = 0;
|
|
145
138
|
const maxFeatures = 5e3;
|
|
146
|
-
console.log(`\u{1F50D} OpenDataSoft getFeatures: Starting fetch for dataset "${datasetId}"`);
|
|
147
139
|
do {
|
|
148
140
|
const recordsUrl = `${recordsBaseUrl}?limit=100&offset=${offset2}`;
|
|
149
|
-
console.log(`\u{1F4E1} OpenDataSoft: Fetching ${recordsUrl}`);
|
|
150
141
|
const response = await fetch(buildUrl(recordsUrl));
|
|
151
142
|
if (!response.ok) {
|
|
152
143
|
console.error(`\u274C OpenDataSoft: Failed to fetch records - status ${response.status}`, {
|
|
@@ -162,7 +153,6 @@ async function fetchOpenDataSoftDatasets(portal) {
|
|
|
162
153
|
console.error(`\u274C OpenDataSoft: Failed to parse JSON response:`, parseError);
|
|
163
154
|
break;
|
|
164
155
|
}
|
|
165
|
-
console.log(`\u{1F4E6} OpenDataSoft: Received data, records count: ${((_c2 = data.records) == null ? void 0 : _c2.length) || 0}, data keys:`, Object.keys(data || {}));
|
|
166
156
|
if (offset2 === 0) {
|
|
167
157
|
totalCount2 = Math.min(data.total_count || data.nhits || 0, maxFeatures);
|
|
168
158
|
}
|
|
@@ -198,7 +188,6 @@ async function fetchOpenDataSoftDatasets(portal) {
|
|
|
198
188
|
const fetchedCount = recArray.length;
|
|
199
189
|
if (fetchedCount === 0 || features.length >= maxFeatures) break;
|
|
200
190
|
} while (offset2 < totalCount2);
|
|
201
|
-
console.log(`\u2705 OpenDataSoft getFeatures: Returning ${features.length} features for dataset "${datasetId}"`);
|
|
202
191
|
const result = {
|
|
203
192
|
type: "FeatureCollection",
|
|
204
193
|
features
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../src/core/components/viewers/map/datasets/src/opendatasoftDatasets.ts"],"sourcesContent":["// SPDX-License-Identifier: AGPL-3.0-or-later\r\n// Copyright (C) 2025 Collab Digital Twins\r\n\r\nimport { layerColorByName } from '../../utils/stringToColour'\r\nimport { formatDate, formatName, stripHtml } from '../utils'\r\n\r\nimport { buildValidationKey, getCache, setCache, withCache } from './cache'\r\n\r\nimport type { Dataset } from '../../../../../types/datasetTypes'\r\nimport type { OpenDataPortal } from '../../../../../types/dbTypes'\r\nimport type { AllGeoJSON } from '@turf/turf'\r\n\r\n// Fetch OpenDataSoft datasets and transform to Dataset array\r\nexport async function fetchOpenDataSoftDatasets(\r\n portal: OpenDataPortal,\r\n): Promise<Dataset[]> {\r\n const { apiUrl, countrySubdivision, municipality, group } = portal\r\n const baseUrl = apiUrl.replace(/\\/$/, '')\r\n const catalogBase = `${baseUrl}/catalog`\r\n const isServer = typeof window === 'undefined'\r\n\r\n const buildUrl = (target: string) => {\r\n if (isServer) {\r\n return target\r\n }\r\n return `/api/opendatasoftProxy?url=${encodeURIComponent(target)}`\r\n }\r\n\r\n try {\r\n let allDatasets: any[] = []\r\n let offset = 0\r\n let totalCount = 0\r\n\r\n do {\r\n const catalogUrl = `${catalogBase}/datasets?limit=100&offset=${offset}`\r\n const response = await fetch(buildUrl(catalogUrl))\r\n\r\n if (!response.ok) {\r\n console.error(`OpenDataSoft API error for ${portal.name}:`, response.status, response.statusText)\r\n throw new Error(`HTTP error! status: ${response.status}`)\r\n }\r\n\r\n const data = await response.json()\r\n\r\n // More flexible response structure handling\r\n if (!data) {\r\n throw new Error('Empty response from OpenDataSoft API')\r\n }\r\n\r\n // Handle different possible response structures\r\n let results: any[] = []\r\n let total = 0\r\n\r\n if (data.results && Array.isArray(data.results)) {\r\n // Standard OpenDataSoft v2 API response\r\n results = data.results\r\n total = data.total_count || data.nhits || 0\r\n }\r\n else if (data.datasets && Array.isArray(data.datasets)) {\r\n // Alternative response structure\r\n results = data.datasets\r\n total = data.total || data.count || 0\r\n }\r\n else if (Array.isArray(data)) {\r\n // Direct array response\r\n results = data\r\n total = data.length\r\n }\r\n else {\r\n // Log the actual structure for debugging\r\n console.error('Unexpected response structure:', Object.keys(data))\r\n throw new Error(\r\n `Invalid OpenDataSoft response structure. Expected 'results' array, got: ${Object.keys(\r\n data,\r\n ).join(', ')}`,\r\n )\r\n }\r\n allDatasets = allDatasets.concat(results)\r\n totalCount = total\r\n offset += 100\r\n\r\n // Safety check to prevent infinite loops\r\n if (results.length === 0) {\r\n break\r\n }\r\n } while (offset < totalCount)\r\n\r\n const transformedDatasets: Dataset[] = (await Promise.all(allDatasets.map(async (dataset: any) => {\r\n const metas = dataset.metas?.default || {}\r\n const custom = dataset.metas?.custom || {}\r\n const datasetId = dataset.dataset_id || dataset.id || ''\r\n const datasetModified = metas.modified || dataset.modified || undefined\r\n\r\n const datasetBaseUrl = `${catalogBase}/datasets/${datasetId}`\r\n const recordsBaseUrl = `${datasetBaseUrl}/records`\r\n\r\n const validKey = buildValidationKey('opendatasoft', datasetId, datasetModified)\r\n const isValid = await withCache<boolean>(validKey, 24 * 60 * 60 * 1000, async () => {\r\n try {\r\n const sampleUrl = `${recordsBaseUrl}?limit=1`\r\n const sampleRes = await fetch(buildUrl(sampleUrl))\r\n if (sampleRes.ok) {\r\n const sample = await sampleRes.json()\r\n if (sample?.error) {\r\n const code = sample.error.code\r\n const msg = `${sample.error.message || ''}`\r\n console.warn(`⚠️ OpenDataSoft validation: Dataset \"${datasetId}\" returned error:`, { code, msg })\r\n if ([401, 403, 499, 504].includes(code) || /unauthorized|forbidden|token required|timed out/i.test(msg)) return false\r\n // Even if there's an error, allow the dataset through - getFeatures will handle it\r\n return true\r\n }\r\n // If response is OK, check if it has geometry\r\n const recs = sample.records || sample.results || [];\r\n const hasGeom = Array.isArray(recs)\r\n ? recs.some((r: any) => !!r?.geometry || !!r?.geom || !!r?.geo_shape || !!r?.geo_point_2d)\r\n : false\r\n console.log(`ℹ️ OpenDataSoft validation: Dataset \"${datasetId}\" has geometry: ${hasGeom}, records: ${recs.length}`)\r\n // Allow through even if no geometry - user might want to see the dataset\r\n return true\r\n }\r\n else if ([401, 403, 504].includes(sampleRes.status)) {\r\n console.error(`❌ OpenDataSoft validation: Dataset \"${datasetId}\" auth/timeout error: ${sampleRes.status}`)\r\n return false\r\n }\r\n else {\r\n console.warn(`⚠️ OpenDataSoft validation: Dataset \"${datasetId}\" validation request failed: ${sampleRes.status}`)\r\n // Allow through on other errors\r\n return true\r\n }\r\n }\r\n catch (error) {\r\n console.error(`❌ OpenDataSoft validation: Dataset \"${datasetId}\" validation failed:`, error)\r\n // Allow through on network errors - getFeatures will handle it\r\n return true\r\n }\r\n })\r\n\r\n if (!isValid) {\r\n console.log(`🗑️ OpenDataSoft: Filtering out dataset \"${datasetId}\" - validation failed`)\r\n return null\r\n }\r\n\r\n // Self-contained getFields\r\n const getFields = async () => {\r\n const fieldsKey = `fields:opendatasoft:${datasetId}:${datasetModified ?? ''}`\r\n const cached = getCache<any[]>(fieldsKey)\r\n if (cached) return cached\r\n const fieldsUrl = `${datasetBaseUrl}`\r\n const response = await fetch(buildUrl(fieldsUrl))\r\n if (!response.ok) return []\r\n const data = await response.json()\r\n const fields = (data.metas?.default?.fields || data.fields || []).map((field: any) => ({\r\n name: field.name,\r\n label: field.label || field.name,\r\n type: field.type,\r\n description: field.description || '',\r\n }))\r\n setCache(fieldsKey, fields, 24 * 60 * 60 * 1000)\r\n return fields\r\n }\r\n\r\n // Self-contained getFeatures with caching\r\n const getFeatures = async (): Promise<AllGeoJSON> => {\r\n const featuresKey = `features:opendatasoft:${datasetId}:${datasetModified ?? ''}`\r\n const cached = getCache<AllGeoJSON>(featuresKey)\r\n if (cached) {\r\n const cachedCount = 'features' in cached ? cached.features?.length ?? 0 : 0\r\n console.log(`✅ OpenDataSoft getFeatures: Returning ${cachedCount} cached features for dataset \"${datasetId}\"`)\r\n return cached\r\n }\r\n\r\n try {\r\n const features: any[] = []\r\n let offset = 0\r\n let totalCount = 0\r\n const maxFeatures = 5_000\r\n\r\n console.log(`🔍 OpenDataSoft getFeatures: Starting fetch for dataset \"${datasetId}\"`)\r\n\r\n do {\r\n const recordsUrl = `${recordsBaseUrl}?limit=100&offset=${offset}`\r\n console.log(`📡 OpenDataSoft: Fetching ${recordsUrl}`)\r\n const response = await fetch(buildUrl(recordsUrl))\r\n if (!response.ok) {\r\n console.error(`❌ OpenDataSoft: Failed to fetch records - status ${response.status}`, {\r\n url: recordsUrl,\r\n statusText: response.statusText,\r\n })\r\n break\r\n }\r\n let data;\r\n try {\r\n data = await response.json()\r\n } catch (parseError) {\r\n console.error(`❌ OpenDataSoft: Failed to parse JSON response:`, parseError)\r\n break\r\n }\r\n console.log(`📦 OpenDataSoft: Received data, records count: ${data.records?.length || 0}, data keys:`, Object.keys(data || {}))\r\n\r\n if (offset === 0) {\r\n totalCount = Math.min(data.total_count || data.nhits || 0, maxFeatures)\r\n }\r\n\r\n const recArray: any[] = data.records || data.results || [];\r\n if (Array.isArray(recArray)) {\r\n recArray.forEach((record: any) => {\r\n if (features.length >= maxFeatures) return;\r\n\r\n // Extract geometry from possible fields\r\n let geom: any = null;\r\n if (record.geometry) {\r\n geom = record.geometry;\r\n } else if (record.geom) {\r\n geom = record.geom.type === 'Feature' && record.geom.geometry ? record.geom.geometry : record.geom;\r\n } else if (record.geo_shape) {\r\n geom = record.geo_shape;\r\n } else if (record.geo_point_2d && typeof record.geo_point_2d.lat === 'number' && typeof record.geo_point_2d.lon === 'number') {\r\n geom = { type: 'Point', coordinates: [record.geo_point_2d.lon, record.geo_point_2d.lat] };\r\n }\r\n\r\n if (geom) {\r\n const props: any = { ...record };\r\n delete props.geometry;\r\n delete props.geom;\r\n delete props.geo_shape;\r\n delete props.geo_point_2d;\r\n\r\n features.push({\r\n type: 'Feature',\r\n geometry: geom,\r\n properties: props,\r\n });\r\n }\r\n });\r\n }\r\n\r\n offset += 100\r\n const fetchedCount = recArray.length;\r\n if (fetchedCount === 0 || features.length >= maxFeatures) break\r\n } while (offset < totalCount)\r\n\r\n console.log(`✅ OpenDataSoft getFeatures: Returning ${features.length} features for dataset \"${datasetId}\"`)\r\n\r\n const result = {\r\n type: 'FeatureCollection',\r\n features,\r\n } as AllGeoJSON\r\n\r\n // Cache the result for 1 hour\r\n setCache(featuresKey, result, 60 * 60 * 1000)\r\n\r\n return result\r\n } catch (error) {\r\n console.error(`❌ OpenDataSoft getFeatures: Error fetching features for dataset \"${datasetId}\":`, error)\r\n // Return empty feature collection on error instead of throwing\r\n return {\r\n type: 'FeatureCollection',\r\n features: [],\r\n } as AllGeoJSON\r\n }\r\n }\r\n\r\n const name = formatName(metas.title || dataset.title || datasetId)\r\n\r\n return {\r\n id: datasetId,\r\n name,\r\n publisher:\r\n metas.publisher\r\n || custom['data-owner']?.[0]\r\n || dataset.publisher\r\n || 'Unknown',\r\n dataManagementSystem: 'Opendatasoft',\r\n license: metas.license || dataset.license || 'Unknown',\r\n contact: custom['data-team']?.[0] || dataset.contact || 'Unknown',\r\n keywords: metas.keyword || dataset.keywords || [],\r\n description: stripHtml(\r\n metas.description || dataset.description || 'No description available',\r\n ),\r\n dateReleased: metas.created\r\n ? formatDate(metas.created)\r\n : (dataset.created\r\n ? formatDate(dataset.created)\r\n : ''),\r\n dateUpdated: metas.modified\r\n ? formatDate(metas.modified)\r\n : (dataset.modified\r\n ? formatDate(dataset.modified)\r\n : ''),\r\n information: `${apiUrl}/explore/dataset/${datasetId}/`,\r\n countrySubdivision,\r\n municipality,\r\n type: portal.name,\r\n datasetType: dataset.type || 'GeoJSON',\r\n portal,\r\n getFields,\r\n getFeatures,\r\n group,\r\n layerColor: layerColorByName(name),\r\n }\r\n })))\r\n .filter(Boolean) as Dataset[]\r\n\r\n return transformedDatasets\r\n }\r\n catch (error) {\r\n console.error(`Error fetching OpenDataSoft datasets for ${portal.name}:`, error)\r\n return []\r\n }\r\n}\r\n"],"mappings":";;;;;;;;;;;;;;;;AAGA,SAAS,wBAAwB;AACjC,SAAS,YAAY,YAAY,iBAAiB;AAElD,SAAS,oBAAoB,UAAU,UAAU,iBAAiB;AAOlE,eAAsB,0BACpB,QACoB;AACpB,QAAM,EAAE,QAAQ,oBAAoB,cAAc,MAAM,IAAI;AAC5D,QAAM,UAAU,OAAO,QAAQ,OAAO,EAAE;AACxC,QAAM,cAAc,GAAG,OAAO;AAC9B,QAAM,WAAW,OAAO,WAAW;AAEnC,QAAM,WAAW,CAAC,WAAmB;AACnC,QAAI,UAAU;AACZ,aAAO;AAAA,IACT;AACA,WAAO,8BAA8B,mBAAmB,MAAM,CAAC;AAAA,EACjE;AAEA,MAAI;AACF,QAAI,cAAqB,CAAC;AAC1B,QAAI,SAAS;AACb,QAAI,aAAa;AAEjB,OAAG;AACD,YAAM,aAAa,GAAG,WAAW,8BAA8B,MAAM;AACrE,YAAM,WAAW,MAAM,MAAM,SAAS,UAAU,CAAC;AAEjD,UAAI,CAAC,SAAS,IAAI;AAChB,gBAAQ,MAAM,8BAA8B,OAAO,IAAI,KAAK,SAAS,QAAQ,SAAS,UAAU;AAChG,cAAM,IAAI,MAAM,uBAAuB,SAAS,MAAM,EAAE;AAAA,MAC1D;AAEA,YAAM,OAAO,MAAM,SAAS,KAAK;AAGjC,UAAI,CAAC,MAAM;AACT,cAAM,IAAI,MAAM,sCAAsC;AAAA,MACxD;AAGA,UAAI,UAAiB,CAAC;AACtB,UAAI,QAAQ;AAEZ,UAAI,KAAK,WAAW,MAAM,QAAQ,KAAK,OAAO,GAAG;AAE/C,kBAAU,KAAK;AACf,gBAAQ,KAAK,eAAe,KAAK,SAAS;AAAA,MAC5C,WACS,KAAK,YAAY,MAAM,QAAQ,KAAK,QAAQ,GAAG;AAEtD,kBAAU,KAAK;AACf,gBAAQ,KAAK,SAAS,KAAK,SAAS;AAAA,MACtC,WACS,MAAM,QAAQ,IAAI,GAAG;AAE5B,kBAAU;AACV,gBAAQ,KAAK;AAAA,MACf,OACK;AAEH,gBAAQ,MAAM,kCAAkC,OAAO,KAAK,IAAI,CAAC;AACjE,cAAM,IAAI;AAAA,UACR,2EAA2E,OAAO;AAAA,YAChF;AAAA,UACF,EAAE,KAAK,IAAI,CAAC;AAAA,QACd;AAAA,MACF;AACA,oBAAc,YAAY,OAAO,OAAO;AACxC,mBAAa;AACb,gBAAU;AAGV,UAAI,QAAQ,WAAW,GAAG;AACxB;AAAA,MACF;AAAA,IACF,SAAS,SAAS;AAElB,UAAM,uBAAkC,MAAM,QAAQ,IAAI,YAAY,IAAI,OAAO,YAAiB;AAvFtG;AAwFM,YAAM,UAAQ,aAAQ,UAAR,mBAAe,YAAW,CAAC;AACzC,YAAM,WAAS,aAAQ,UAAR,mBAAe,WAAU,CAAC;AACzC,YAAM,YAAY,QAAQ,cAAc,QAAQ,MAAM;AACtD,YAAM,kBAAkB,MAAM,YAAY,QAAQ,YAAY;AAE9D,YAAM,iBAAiB,GAAG,WAAW,aAAa,SAAS;AAC3D,YAAM,iBAAiB,GAAG,cAAc;AAExC,YAAM,WAAW,mBAAmB,gBAAgB,WAAW,eAAe;AAC9E,YAAM,UAAU,MAAM,UAAmB,UAAU,KAAK,KAAK,KAAK,KAAM,YAAY;AAClF,YAAI;AACF,gBAAM,YAAY,GAAG,cAAc;AACnC,gBAAM,YAAY,MAAM,MAAM,SAAS,SAAS,CAAC;AACjD,cAAI,UAAU,IAAI;AAChB,kBAAM,SAAS,MAAM,UAAU,KAAK;AACpC,gBAAI,iCAAQ,OAAO;AACjB,oBAAM,OAAO,OAAO,MAAM;AAC1B,oBAAM,MAAM,GAAG,OAAO,MAAM,WAAW,EAAE;AACzC,sBAAQ,KAAK,kDAAwC,SAAS,qBAAqB,EAAE,MAAM,IAAI,CAAC;AAChG,kBAAI,CAAC,KAAK,KAAK,KAAK,GAAG,EAAE,SAAS,IAAI,KAAK,mDAAmD,KAAK,GAAG,EAAG,QAAO;AAEhH,qBAAO;AAAA,YACT;AAEA,kBAAM,OAAO,OAAO,WAAW,OAAO,WAAW,CAAC;AAClD,kBAAM,UAAU,MAAM,QAAQ,IAAI,IAC9B,KAAK,KAAK,CAAC,MAAW,CAAC,EAAC,uBAAG,aAAY,CAAC,EAAC,uBAAG,SAAQ,CAAC,EAAC,uBAAG,cAAa,CAAC,EAAC,uBAAG,aAAY,IACvF;AACJ,oBAAQ,IAAI,kDAAwC,SAAS,mBAAmB,OAAO,cAAc,KAAK,MAAM,EAAE;AAElH,mBAAO;AAAA,UACT,WACS,CAAC,KAAK,KAAK,GAAG,EAAE,SAAS,UAAU,MAAM,GAAG;AACnD,oBAAQ,MAAM,4CAAuC,SAAS,yBAAyB,UAAU,MAAM,EAAE;AACzG,mBAAO;AAAA,UACT,OACK;AACH,oBAAQ,KAAK,kDAAwC,SAAS,gCAAgC,UAAU,MAAM,EAAE;AAEhH,mBAAO;AAAA,UACT;AAAA,QACF,SACO,OAAO;AACZ,kBAAQ,MAAM,4CAAuC,SAAS,wBAAwB,KAAK;AAE3F,iBAAO;AAAA,QACT;AAAA,MACF,CAAC;AAED,UAAI,CAAC,SAAS;AACZ,gBAAQ,IAAI,wDAA4C,SAAS,uBAAuB;AACxF,eAAO;AAAA,MACT;AAGA,YAAM,YAAY,YAAY;AA/IpC,YAAAA,KAAAC;AAgJQ,cAAM,YAAY,uBAAuB,SAAS,IAAI,4CAAmB,EAAE;AAC3E,cAAM,SAAS,SAAgB,SAAS;AACxC,YAAI,OAAQ,QAAO;AACnB,cAAM,YAAY,GAAG,cAAc;AACnC,cAAM,WAAW,MAAM,MAAM,SAAS,SAAS,CAAC;AAChD,YAAI,CAAC,SAAS,GAAI,QAAO,CAAC;AAC1B,cAAM,OAAO,MAAM,SAAS,KAAK;AACjC,cAAM,YAAUA,OAAAD,MAAA,KAAK,UAAL,gBAAAA,IAAY,YAAZ,gBAAAC,IAAqB,WAAU,KAAK,UAAU,CAAC,GAAG,IAAI,CAAC,WAAgB;AAAA,UACrF,MAAM,MAAM;AAAA,UACZ,OAAO,MAAM,SAAS,MAAM;AAAA,UAC5B,MAAM,MAAM;AAAA,UACZ,aAAa,MAAM,eAAe;AAAA,QACpC,EAAE;AACF,iBAAS,WAAW,QAAQ,KAAK,KAAK,KAAK,GAAI;AAC/C,eAAO;AAAA,MACT;AAGA,YAAM,cAAc,YAAiC;AAlK3D,YAAAD,KAAAC,KAAAC;AAmKQ,cAAM,cAAc,yBAAyB,SAAS,IAAI,4CAAmB,EAAE;AAC/E,cAAM,SAAS,SAAqB,WAAW;AAC/C,YAAI,QAAQ;AACV,gBAAM,cAAc,cAAc,UAASD,OAAAD,MAAA,OAAO,aAAP,gBAAAA,IAAiB,WAAjB,OAAAC,MAA2B,IAAI;AAC1E,kBAAQ,IAAI,8CAAyC,WAAW,iCAAiC,SAAS,GAAG;AAC7G,iBAAO;AAAA,QACT;AAEA,YAAI;AACF,gBAAM,WAAkB,CAAC;AACzB,cAAIE,UAAS;AACb,cAAIC,cAAa;AACjB,gBAAM,cAAc;AAEpB,kBAAQ,IAAI,mEAA4D,SAAS,GAAG;AAEpF,aAAG;AACD,kBAAM,aAAa,GAAG,cAAc,qBAAqBD,OAAM;AAC/D,oBAAQ,IAAI,oCAA6B,UAAU,EAAE;AACrD,kBAAM,WAAW,MAAM,MAAM,SAAS,UAAU,CAAC;AACjD,gBAAI,CAAC,SAAS,IAAI;AAChB,sBAAQ,MAAM,yDAAoD,SAAS,MAAM,IAAI;AAAA,gBACnF,KAAK;AAAA,gBACL,YAAY,SAAS;AAAA,cACvB,CAAC;AACD;AAAA,YACF;AACA,gBAAI;AACJ,gBAAI;AACF,qBAAO,MAAM,SAAS,KAAK;AAAA,YAC7B,SAAS,YAAY;AACnB,sBAAQ,MAAM,uDAAkD,UAAU;AAC1E;AAAA,YACF;AACA,oBAAQ,IAAI,2DAAkDD,MAAA,KAAK,YAAL,gBAAAA,IAAc,WAAU,CAAC,gBAAgB,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC;AAE9H,gBAAIC,YAAW,GAAG;AAChB,cAAAC,cAAa,KAAK,IAAI,KAAK,eAAe,KAAK,SAAS,GAAG,WAAW;AAAA,YACxE;AAEA,kBAAM,WAAkB,KAAK,WAAW,KAAK,WAAW,CAAC;AACzD,gBAAI,MAAM,QAAQ,QAAQ,GAAG;AAC3B,uBAAS,QAAQ,CAAC,WAAgB;AAChC,oBAAI,SAAS,UAAU,YAAa;AAGpC,oBAAI,OAAY;AAChB,oBAAI,OAAO,UAAU;AACnB,yBAAO,OAAO;AAAA,gBAChB,WAAW,OAAO,MAAM;AACtB,yBAAO,OAAO,KAAK,SAAS,aAAa,OAAO,KAAK,WAAW,OAAO,KAAK,WAAW,OAAO;AAAA,gBAChG,WAAW,OAAO,WAAW;AAC3B,yBAAO,OAAO;AAAA,gBAChB,WAAW,OAAO,gBAAgB,OAAO,OAAO,aAAa,QAAQ,YAAY,OAAO,OAAO,aAAa,QAAQ,UAAU;AAC5H,yBAAO,EAAE,MAAM,SAAS,aAAa,CAAC,OAAO,aAAa,KAAK,OAAO,aAAa,GAAG,EAAE;AAAA,gBAC1F;AAEA,oBAAI,MAAM;AACR,wBAAM,QAAa,mBAAK;AACxB,yBAAO,MAAM;AACb,yBAAO,MAAM;AACb,yBAAO,MAAM;AACb,yBAAO,MAAM;AAEb,2BAAS,KAAK;AAAA,oBACZ,MAAM;AAAA,oBACN,UAAU;AAAA,oBACV,YAAY;AAAA,kBACd,CAAC;AAAA,gBACH;AAAA,cACF,CAAC;AAAA,YACH;AAEA,YAAAD,WAAU;AACV,kBAAM,eAAe,SAAS;AAC9B,gBAAI,iBAAiB,KAAK,SAAS,UAAU,YAAa;AAAA,UAC5D,SAASA,UAASC;AAElB,kBAAQ,IAAI,8CAAyC,SAAS,MAAM,0BAA0B,SAAS,GAAG;AAE1G,gBAAM,SAAS;AAAA,YACb,MAAM;AAAA,YACN;AAAA,UACF;AAGA,mBAAS,aAAa,QAAQ,KAAK,KAAK,GAAI;AAE5C,iBAAO;AAAA,QACT,SAAS,OAAO;AACd,kBAAQ,MAAM,yEAAoE,SAAS,MAAM,KAAK;AAEtG,iBAAO;AAAA,YACL,MAAM;AAAA,YACN,UAAU,CAAC;AAAA,UACb;AAAA,QACF;AAAA,MACF;AAEJ,YAAM,OAAO,WAAW,MAAM,SAAS,QAAQ,SAAS,SAAS;AAE7D,aAAO;AAAA,QACL,IAAI;AAAA,QACJ;AAAA,QACA,WACE,MAAM,eACH,YAAO,YAAY,MAAnB,mBAAuB,OACvB,QAAQ,aACR;AAAA,QACL,sBAAsB;AAAA,QACtB,SAAS,MAAM,WAAW,QAAQ,WAAW;AAAA,QAC7C,WAAS,YAAO,WAAW,MAAlB,mBAAsB,OAAM,QAAQ,WAAW;AAAA,QACxD,UAAU,MAAM,WAAW,QAAQ,YAAY,CAAC;AAAA,QAChD,aAAa;AAAA,UACX,MAAM,eAAe,QAAQ,eAAe;AAAA,QAC9C;AAAA,QACA,cAAc,MAAM,UAChB,WAAW,MAAM,OAAO,IACvB,QAAQ,UACL,WAAW,QAAQ,OAAO,IAC1B;AAAA,QACR,aAAa,MAAM,WACf,WAAW,MAAM,QAAQ,IACxB,QAAQ,WACL,WAAW,QAAQ,QAAQ,IAC3B;AAAA,QACR,aAAa,GAAG,MAAM,oBAAoB,SAAS;AAAA,QACnD;AAAA,QACA;AAAA,QACA,MAAM,OAAO;AAAA,QACb,aAAa,QAAQ,QAAQ;AAAA,QAC7B;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,YAAY,iBAAiB,IAAI;AAAA,MACnC;AAAA,IACF,CAAC,CAAC,GACD,OAAO,OAAO;AAEf,WAAO;AAAA,EACT,SACO,OAAO;AACZ,YAAQ,MAAM,4CAA4C,OAAO,IAAI,KAAK,KAAK;AAC/E,WAAO,CAAC;AAAA,EACV;AACF;","names":["_a","_b","_c","offset","totalCount"]}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../src/core/components/viewers/map/datasets/src/opendatasoftDatasets.ts"],"sourcesContent":["// SPDX-License-Identifier: AGPL-3.0-or-later\r\n// Copyright (C) 2025 Collab Digital Twins\r\n\r\nimport { layerColorByName } from '../../utils/stringToColour'\r\nimport { formatDate, formatName, stripHtml } from '../utils'\r\n\r\nimport { buildValidationKey, getCache, setCache, withCache } from './cache'\r\n\r\nimport type { Dataset } from '../../../../../types/datasetTypes'\r\nimport type { OpenDataPortal } from '../../../../../types/dbTypes'\r\nimport type { AllGeoJSON } from '@turf/turf'\r\n\r\n// Fetch OpenDataSoft datasets and transform to Dataset array\r\nexport async function fetchOpenDataSoftDatasets(\r\n portal: OpenDataPortal,\r\n): Promise<Dataset[]> {\r\n const { apiUrl, countrySubdivision, municipality, group } = portal\r\n const baseUrl = apiUrl.replace(/\\/$/, '')\r\n const catalogBase = `${baseUrl}/catalog`\r\n const isServer = typeof window === 'undefined'\r\n\r\n const buildUrl = (target: string) => {\r\n if (isServer) {\r\n return target\r\n }\r\n return `/api/opendatasoftProxy?url=${encodeURIComponent(target)}`\r\n }\r\n\r\n try {\r\n let allDatasets: any[] = []\r\n let offset = 0\r\n let totalCount = 0\r\n\r\n do {\r\n const catalogUrl = `${catalogBase}/datasets?limit=100&offset=${offset}`\r\n const response = await fetch(buildUrl(catalogUrl))\r\n\r\n if (!response.ok) {\r\n console.error(`OpenDataSoft API error for ${portal.name}:`, response.status, response.statusText)\r\n throw new Error(`HTTP error! status: ${response.status}`)\r\n }\r\n\r\n const data = await response.json()\r\n\r\n // More flexible response structure handling\r\n if (!data) {\r\n throw new Error('Empty response from OpenDataSoft API')\r\n }\r\n\r\n // Handle different possible response structures\r\n let results: any[] = []\r\n let total = 0\r\n\r\n if (data.results && Array.isArray(data.results)) {\r\n // Standard OpenDataSoft v2 API response\r\n results = data.results\r\n total = data.total_count || data.nhits || 0\r\n }\r\n else if (data.datasets && Array.isArray(data.datasets)) {\r\n // Alternative response structure\r\n results = data.datasets\r\n total = data.total || data.count || 0\r\n }\r\n else if (Array.isArray(data)) {\r\n // Direct array response\r\n results = data\r\n total = data.length\r\n }\r\n else {\r\n // Log the actual structure for debugging\r\n console.error('Unexpected response structure:', Object.keys(data))\r\n throw new Error(\r\n `Invalid OpenDataSoft response structure. Expected 'results' array, got: ${Object.keys(\r\n data,\r\n ).join(', ')}`,\r\n )\r\n }\r\n allDatasets = allDatasets.concat(results)\r\n totalCount = total\r\n offset += 100\r\n\r\n // Safety check to prevent infinite loops\r\n if (results.length === 0) {\r\n break\r\n }\r\n } while (offset < totalCount)\r\n\r\n const transformedDatasets: Dataset[] = (await Promise.all(allDatasets.map(async (dataset: any) => {\r\n const metas = dataset.metas?.default || {}\r\n const custom = dataset.metas?.custom || {}\r\n const datasetId = dataset.dataset_id || dataset.id || ''\r\n const datasetModified = metas.modified || dataset.modified || undefined\r\n\r\n const datasetBaseUrl = `${catalogBase}/datasets/${datasetId}`\r\n const recordsBaseUrl = `${datasetBaseUrl}/records`\r\n\r\n const validKey = buildValidationKey('opendatasoft', datasetId, datasetModified)\r\n const isValid = await withCache<boolean>(validKey, 24 * 60 * 60 * 1000, async () => {\r\n try {\r\n const sampleUrl = `${recordsBaseUrl}?limit=1`\r\n const sampleRes = await fetch(buildUrl(sampleUrl))\r\n if (sampleRes.ok) {\r\n const sample = await sampleRes.json()\r\n if (sample?.error) {\r\n const code = sample.error.code\r\n const msg = `${sample.error.message || ''}`\r\n console.warn(`⚠️ OpenDataSoft validation: Dataset \"${datasetId}\" returned error:`, { code, msg })\r\n if ([401, 403, 499, 504].includes(code) || /unauthorized|forbidden|token required|timed out/i.test(msg)) return false\r\n // Even if there's an error, allow the dataset through - getFeatures will handle it\r\n return true\r\n }\r\n // Allow through even if no geometry - user might want to see the dataset\r\n return true\r\n }\r\n else if ([401, 403, 504].includes(sampleRes.status)) {\r\n console.error(`❌ OpenDataSoft validation: Dataset \"${datasetId}\" auth/timeout error: ${sampleRes.status}`)\r\n return false\r\n }\r\n else {\r\n console.warn(`⚠️ OpenDataSoft validation: Dataset \"${datasetId}\" validation request failed: ${sampleRes.status}`)\r\n // Allow through on other errors\r\n return true\r\n }\r\n }\r\n catch (error) {\r\n console.error(`❌ OpenDataSoft validation: Dataset \"${datasetId}\" validation failed:`, error)\r\n // Allow through on network errors - getFeatures will handle it\r\n return true\r\n }\r\n })\r\n\r\n if (!isValid) {\r\n return null\r\n }\r\n\r\n // Self-contained getFields\r\n const getFields = async () => {\r\n const fieldsKey = `fields:opendatasoft:${datasetId}:${datasetModified ?? ''}`\r\n const cached = getCache<any[]>(fieldsKey)\r\n if (cached) return cached\r\n const fieldsUrl = `${datasetBaseUrl}`\r\n const response = await fetch(buildUrl(fieldsUrl))\r\n if (!response.ok) return []\r\n const data = await response.json()\r\n const fields = (data.metas?.default?.fields || data.fields || []).map((field: any) => ({\r\n name: field.name,\r\n label: field.label || field.name,\r\n type: field.type,\r\n description: field.description || '',\r\n }))\r\n setCache(fieldsKey, fields, 24 * 60 * 60 * 1000)\r\n return fields\r\n }\r\n\r\n // Self-contained getFeatures with caching\r\n const getFeatures = async (): Promise<AllGeoJSON> => {\r\n const featuresKey = `features:opendatasoft:${datasetId}:${datasetModified ?? ''}`\r\n const cached = getCache<AllGeoJSON>(featuresKey)\r\n if (cached) {\r\n return cached\r\n }\r\n\r\n try {\r\n const features: any[] = []\r\n let offset = 0\r\n let totalCount = 0\r\n const maxFeatures = 5_000\r\n\r\n do {\r\n const recordsUrl = `${recordsBaseUrl}?limit=100&offset=${offset}`\r\n const response = await fetch(buildUrl(recordsUrl))\r\n if (!response.ok) {\r\n console.error(`❌ OpenDataSoft: Failed to fetch records - status ${response.status}`, {\r\n url: recordsUrl,\r\n statusText: response.statusText,\r\n })\r\n break\r\n }\r\n let data;\r\n try {\r\n data = await response.json()\r\n } catch (parseError) {\r\n console.error(`❌ OpenDataSoft: Failed to parse JSON response:`, parseError)\r\n break\r\n }\r\n if (offset === 0) {\r\n totalCount = Math.min(data.total_count || data.nhits || 0, maxFeatures)\r\n }\r\n\r\n const recArray: any[] = data.records || data.results || [];\r\n if (Array.isArray(recArray)) {\r\n recArray.forEach((record: any) => {\r\n if (features.length >= maxFeatures) return;\r\n\r\n // Extract geometry from possible fields\r\n let geom: any = null;\r\n if (record.geometry) {\r\n geom = record.geometry;\r\n } else if (record.geom) {\r\n geom = record.geom.type === 'Feature' && record.geom.geometry ? record.geom.geometry : record.geom;\r\n } else if (record.geo_shape) {\r\n geom = record.geo_shape;\r\n } else if (record.geo_point_2d && typeof record.geo_point_2d.lat === 'number' && typeof record.geo_point_2d.lon === 'number') {\r\n geom = { type: 'Point', coordinates: [record.geo_point_2d.lon, record.geo_point_2d.lat] };\r\n }\r\n\r\n if (geom) {\r\n const props: any = { ...record };\r\n delete props.geometry;\r\n delete props.geom;\r\n delete props.geo_shape;\r\n delete props.geo_point_2d;\r\n\r\n features.push({\r\n type: 'Feature',\r\n geometry: geom,\r\n properties: props,\r\n });\r\n }\r\n });\r\n }\r\n\r\n offset += 100\r\n const fetchedCount = recArray.length;\r\n if (fetchedCount === 0 || features.length >= maxFeatures) break\r\n } while (offset < totalCount)\r\n\r\n const result = {\r\n type: 'FeatureCollection',\r\n features,\r\n } as AllGeoJSON\r\n\r\n // Cache the result for 1 hour\r\n setCache(featuresKey, result, 60 * 60 * 1000)\r\n\r\n return result\r\n } catch (error) {\r\n console.error(`❌ OpenDataSoft getFeatures: Error fetching features for dataset \"${datasetId}\":`, error)\r\n // Return empty feature collection on error instead of throwing\r\n return {\r\n type: 'FeatureCollection',\r\n features: [],\r\n } as AllGeoJSON\r\n }\r\n }\r\n\r\n const name = formatName(metas.title || dataset.title || datasetId)\r\n\r\n return {\r\n id: datasetId,\r\n name,\r\n publisher:\r\n metas.publisher\r\n || custom['data-owner']?.[0]\r\n || dataset.publisher\r\n || 'Unknown',\r\n dataManagementSystem: 'Opendatasoft',\r\n license: metas.license || dataset.license || 'Unknown',\r\n contact: custom['data-team']?.[0] || dataset.contact || 'Unknown',\r\n keywords: metas.keyword || dataset.keywords || [],\r\n description: stripHtml(\r\n metas.description || dataset.description || 'No description available',\r\n ),\r\n dateReleased: metas.created\r\n ? formatDate(metas.created)\r\n : (dataset.created\r\n ? formatDate(dataset.created)\r\n : ''),\r\n dateUpdated: metas.modified\r\n ? formatDate(metas.modified)\r\n : (dataset.modified\r\n ? formatDate(dataset.modified)\r\n : ''),\r\n information: `${apiUrl}/explore/dataset/${datasetId}/`,\r\n countrySubdivision,\r\n municipality,\r\n type: portal.name,\r\n datasetType: dataset.type || 'GeoJSON',\r\n portal,\r\n getFields,\r\n getFeatures,\r\n group,\r\n layerColor: layerColorByName(name),\r\n }\r\n })))\r\n .filter(Boolean) as Dataset[]\r\n\r\n return transformedDatasets\r\n }\r\n catch (error) {\r\n console.error(`Error fetching OpenDataSoft datasets for ${portal.name}:`, error)\r\n return []\r\n }\r\n}\r\n"],"mappings":"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","names":["_a","_b","offset","totalCount"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BuildingsTools.d.ts","sourceRoot":"","sources":["../../../../../../../../../../src/core/components/viewers/map/src/MapFeaturePopoverMenu/src/DatabaseBuildingPopover/src/BuildingsTools.tsx"],"names":[],"mappings":"AAOA,OAAO,KAAK,MAAM,OAAO,CAAC;AAiB1B,OAAO,KAAK,EAAE,QAAQ,EAAU,MAAM,uCAAuC,CAAC;AAE9E,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAErD,UAAU,kBAAkB;IAC1B,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,QAAQ,GAAG,IAAI,CAAC;IAC1B,OAAO,EAAE,iBAAiB,CAAC;IAC3B,aAAa,EAAE,MAAM,IAAI,CAAC;CAC3B;AAED,MAAM,CAAC,OAAO,UAAU,aAAa,CAAC,EACpC,SAAS,EACT,OAAO,EACP,QAAQ,EACR,OAAO,EACP,aAAa,EACd,EAAE,kBAAkB,
|
|
1
|
+
{"version":3,"file":"BuildingsTools.d.ts","sourceRoot":"","sources":["../../../../../../../../../../src/core/components/viewers/map/src/MapFeaturePopoverMenu/src/DatabaseBuildingPopover/src/BuildingsTools.tsx"],"names":[],"mappings":"AAOA,OAAO,KAAK,MAAM,OAAO,CAAC;AAiB1B,OAAO,KAAK,EAAE,QAAQ,EAAU,MAAM,uCAAuC,CAAC;AAE9E,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAErD,UAAU,kBAAkB;IAC1B,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,QAAQ,GAAG,IAAI,CAAC;IAC1B,OAAO,EAAE,iBAAiB,CAAC;IAC3B,aAAa,EAAE,MAAM,IAAI,CAAC;CAC3B;AAED,MAAM,CAAC,OAAO,UAAU,aAAa,CAAC,EACpC,SAAS,EACT,OAAO,EACP,QAAQ,EACR,OAAO,EACP,aAAa,EACd,EAAE,kBAAkB,qBAwWpB"}
|
|
@@ -281,7 +281,6 @@ function BuildingTools({
|
|
|
281
281
|
] }),
|
|
282
282
|
bimFiles.length === 1 && bimModelsAddedToMap.some((model) => model.building.id === buildingId) && (() => {
|
|
283
283
|
const selectedModel = bimModelsAddedToMap.find((model) => model.building.id === buildingId);
|
|
284
|
-
console.log("selectedModel", selectedModel);
|
|
285
284
|
return true ? /* @__PURE__ */ jsx(
|
|
286
285
|
Button,
|
|
287
286
|
{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../../../../src/core/components/viewers/map/src/MapFeaturePopoverMenu/src/DatabaseBuildingPopover/src/BuildingsTools.tsx"],"sourcesContent":["\"use client\"\r\n\r\n// SPDX-License-Identifier: AGPL-3.0-or-later\r\n// Copyright (C) 2025 Collab Digital Twins\r\n\r\nimport * as LR from \"lucide-react\";\r\nimport { useTranslations } from \"next-intl\";\r\nimport React from \"react\";\r\n\r\nimport { Button, Label, Skeleton, Switch, Checkbox } from \"../../../../../../../../components/ui/\";\r\nimport {\r\n DropdownMenu,\r\n DropdownMenuContent,\r\n DropdownMenuItem,\r\n DropdownMenuTrigger,\r\n} from \"../../../../../../../../components/ui/DropdownMenu\";\r\nimport { useFileUploadHandler } from \"../../../../../../../../components/ui/FilesManager/src/useFileUploadHandler\";\r\nimport { useFilesByBuildingId, useUploadFileToBuilding } from \"../../../../../../../../hooks/files/files\";\r\nimport { usePermissions } from '../../../../../../../../store'\r\nimport { BuildingsContext, BimContext, MapContext, MenusContext, useMenusContext } from \"../../../../../../../../store\";\r\nimport { ViewerNames } from \"../../../../../../../../types/\";\r\nimport { toggleBimToMap as dispatchToggleBimToMap } from '../../../../../utils/toggleBimToMap';\r\nimport Compare from '../../../../compare';\r\n\r\nimport type { Building, DbFile } from \"../../../../../../../../types/dbTypes\";\r\nimport type { Tool } from \"../../../../../../../../types/tools\";\r\nimport type { MapGeoJSONFeature } from \"maplibre-gl\";\r\n\r\ninterface BuildingToolsProps {\r\n isLoading: boolean;\r\n isError: boolean;\r\n building: Building | null;\r\n feature: MapGeoJSONFeature;\r\n onCloseAction: () => void;\r\n}\r\n\r\nexport default function BuildingTools({\r\n isLoading,\r\n isError,\r\n building,\r\n feature,\r\n onCloseAction\r\n}: BuildingToolsProps) {\r\n\r\n const t = useTranslations('BuildingTools')\r\n // Permissions\r\n const { ability } = usePermissions()\r\n\r\n const { dispatch: menusDispatch } = React.useContext(MenusContext);\r\n const { state: mapState, } = React.useContext(MapContext);\r\n const { dispatch: bimDispatch, state: bimState } = React.useContext(BimContext);\r\n const { bimModelsAddedToMap } = bimState.bim;\r\n const { dispatch: buildingsDispatch } = React.useContext(BuildingsContext);\r\n\r\n const fileInputRef = React.useRef<HTMLInputElement>(null);\r\n const { setView } = useMenusContext();\r\n const { setSelectedItem } = useMenusContext();\r\n\r\n const buildingId = React.useMemo(\r\n () => {\r\n\r\n const featureProps = feature?.properties || {};\r\n const id = Number(featureProps.dbId);\r\n return isNaN(id)\r\n ? null\r\n : id;\r\n\r\n },\r\n [feature?.properties],\r\n );\r\n\r\n const { files } = useFilesByBuildingId(buildingId);\r\n\r\n const bimAdded = React.useMemo(\r\n () => (buildingId\r\n ? mapState.map.bimModelsAddedToMap.includes(buildingId)\r\n : false),\r\n [\r\n mapState.map.bimModelsAddedToMap,\r\n buildingId,\r\n ],\r\n );\r\n\r\n const { uploadFile, isMutating: isUploadingFromHook } = useUploadFileToBuilding(building?.id || null);\r\n\r\n // Local state to track the entire upload process from file selection to completion\r\n const [\r\n isLocallyUploading,\r\n setIsLocallyUploading,\r\n ] = React.useState(false);\r\n\r\n // Combined uploading state\r\n const isUploading = isLocallyUploading || isUploadingFromHook;\r\n\r\n // Use the reusable upload handler without passing bimComponents\r\n const { handleFileUpload } = useFileUploadHandler({\r\n buildingId: buildingId || -1,\r\n uploadFile,\r\n onUploadStart: () => setIsLocallyUploading(true),\r\n onUploadEnd: () => setIsLocallyUploading(false),\r\n customMessages: {\r\n success: t('toastFIleUploadSuccess'),\r\n errorPrefix: t('toastFIleUploadError'),\r\n errorSuffix: t('toastUnknownError'),\r\n noIdError: t('buildingIdRequired')\r\n }\r\n });\r\n\r\n const changeViewer = React.useCallback(\r\n (viewer: ViewerNames) => {\r\n\r\n menusDispatch({\r\n type: \"SET_VIEWER\",\r\n payload: { \"currentViewer\": viewer },\r\n });\r\n\r\n buildingsDispatch({\r\n type: \"SET-CURRENT-BUILDING\",\r\n payload: { building },\r\n });\r\n onCloseAction();\r\n\r\n },\r\n [\r\n menusDispatch,\r\n buildingsDispatch,\r\n building,\r\n onCloseAction\r\n ]\r\n );\r\n\r\n const triggerFileInput = React.useCallback(\r\n () => {\r\n\r\n fileInputRef.current?.click();\r\n\r\n },\r\n [],\r\n );\r\n\r\n const toggleBimToMap = React.useCallback(\r\n (bimFile: DbFile) => {\r\n dispatchToggleBimToMap(bimDispatch, bimFile, building);\r\n\r\n },\r\n [\r\n building,\r\n bimDispatch\r\n ]\r\n );\r\n\r\n const onOpenBimViewer = () => {\r\n buildingsDispatch({ type: \"SET-CURRENT-BUILDING\", payload: { building } });\r\n changeViewer(ViewerNames.bim);\r\n setSelectedItem(building);\r\n }\r\n\r\n const buildingFiles = React.useMemo(\r\n () => files || [],\r\n [files],\r\n );\r\n\r\n const bimFiles = React.useMemo(\r\n () => buildingFiles.filter((file: DbFile) => file.extension?.toLowerCase() === \"frag\"),\r\n [buildingFiles],\r\n )\r\n\r\n const pcFiles = React.useMemo(\r\n () => buildingFiles.filter((file: DbFile) => file.type === 'point-cloud-file' || ['laz', 'las'].includes(file.extension || '')),\r\n [buildingFiles],\r\n )\r\n\r\n const buildingButtons: Tool[] = React.useMemo(\r\n () => [\r\n {\r\n id: \"map-compare-buildings\",\r\n icon: LR.Columns3,\r\n title: t('compareTitle'),\r\n component: Compare,\r\n disabled: !ability.can('read', 'Building')\r\n },\r\n {\r\n id: \"open-building-page\",\r\n icon: LR.Building2,\r\n title: t('openBIMTitle'),\r\n onClick: () => {\r\n\r\n changeViewer(ViewerNames.buildings);\r\n setSelectedItem(building);\r\n setView(\"detail\");\r\n\r\n },\r\n disabled: !ability.can('read', 'Building')\r\n },\r\n {\r\n id: \"open-bim-viewer\",\r\n icon: LR.Box,\r\n title: t('OpenBIMViewerTitle'),\r\n onClick: onOpenBimViewer,\r\n disabled: !ability.can('read', 'File')\r\n },\r\n {\r\n id: 'open-pointcloud',\r\n title: t('openPointCloudTitle'),\r\n icon: LR.Grip,\r\n onClick: () => changeViewer(ViewerNames.pointcloud),\r\n disabled: pcFiles.length === 0 || !ability.can('read', 'File')\r\n },\r\n {\r\n id: \"map-add-file\",\r\n icon: isUploading\r\n ? LR.Loader\r\n : LR.FilePlus,\r\n title: t('addFileTitle'),\r\n onClick: triggerFileInput,\r\n disabled: !ability.can('create', 'File')\r\n },\r\n ],\r\n [\r\n changeViewer,\r\n building,\r\n setSelectedItem,\r\n setView,\r\n isUploading,\r\n triggerFileInput\r\n ]\r\n );\r\n\r\n const onLoadBIM = React.useCallback(\r\n (file: DbFile = null) => {\r\n if (bimFiles.length === 0) return;\r\n toggleBimToMap(bimFiles[0]);\r\n },\r\n [\r\n bimFiles,\r\n toggleBimToMap\r\n ]\r\n );\r\n\r\n const handleEditClick = (fileName: string) => {\r\n bimDispatch({\r\n type: \"EDIT_BIM_MODEL_BY_NAME\",\r\n payload: { editingBimModel: fileName }\r\n });\r\n\r\n onCloseAction();\r\n }\r\n\r\n\r\n return (\r\n <div className=\"space-y-4\">\r\n {/* Hidden file input */}\r\n <input\r\n onChange={(e) => handleFileUpload(e.target.files?.[0] || null)}\r\n ref={fileInputRef}\r\n style={{ \"display\": \"none\" }}\r\n type=\"file\"\r\n />\r\n\r\n {/* BIM Controls */}\r\n {!isError &&\r\n <div className=\"flex items-center space-x-2\">\r\n {isLoading\r\n ? <>\r\n <Skeleton className=\"h-4 w-20\" />\r\n\r\n <Skeleton className=\"h-6 w-10\" />\r\n </>\r\n : (\r\n <div className=\"flex items-center justify-between w-full\">\r\n {bimFiles.length > 1 ? (\r\n <div className=\"flex items-center space-x-2\">\r\n <Label className=\"font-semibold\" htmlFor=\"bim-file-dropdown\">\r\n {t('selectBimLabel')}\r\n </Label>\r\n <DropdownMenu>\r\n <DropdownMenuTrigger asChild>\r\n <Button variant=\"outline\" size=\"sm\" id=\"bim-file-dropdown\">\r\n {bimModelsAddedToMap.length > 0\r\n ? `${bimModelsAddedToMap.filter(model => model.bimFile.attachedFilesBuildingId === buildingId).length} ${t('filesSelected')}`\r\n : t('selectFiles')\r\n }\r\n </Button>\r\n </DropdownMenuTrigger>\r\n <DropdownMenuContent>\r\n {bimFiles.map((file: DbFile) => {\r\n const isSelected = bimModelsAddedToMap.some(model => model.bimFile.name === file.name);\r\n return (\r\n <DropdownMenuItem\r\n key={file.name}\r\n onClick={(e) => {\r\n e.preventDefault();\r\n toggleBimToMap(file);\r\n }}\r\n className=\"flex items-center justify-between cursor-pointer\"\r\n >\r\n <div className=\"flex items-center space-x-2\">\r\n <Checkbox\r\n checked={isSelected}\r\n onClick={(e) => e.stopPropagation()}\r\n onCheckedChange={() => {\r\n toggleBimToMap(file);\r\n }}\r\n />\r\n <span>{file.name}</span>\r\n </div>\r\n {isSelected && (\r\n <Button\r\n onClick={(e) => {\r\n e.preventDefault();\r\n e.stopPropagation();\r\n handleEditClick(file.name);\r\n }}\r\n variant=\"ghost\"\r\n size=\"sm\"\r\n className=\"h-6 w-6 p-0\"\r\n title={t('editBimButton')}\r\n >\r\n <LR.LocationEdit className=\"h-3 w-3\" />\r\n </Button>\r\n )}\r\n </DropdownMenuItem>\r\n );\r\n })}\r\n </DropdownMenuContent>\r\n </DropdownMenu>\r\n </div>\r\n ) : (\r\n <div className=\"flex items-center space-x-2\">\r\n <Label\r\n className={`font-semibold ${bimFiles.length === 0 ? 'text-muted-foreground' : ''}`}\r\n htmlFor=\"show-bim\"\r\n >\r\n {bimAdded ? t('hideBIM') : t('showBIM')}\r\n </Label>\r\n <Switch\r\n id=\"show-bim\"\r\n checked={bimFiles.length === 1 && bimModelsAddedToMap.some(model => model.bimFile.name === bimFiles[0]?.name)}\r\n disabled={bimFiles.length === 0 || !ability.can('read', 'File')}\r\n onCheckedChange={() => onLoadBIM()}\r\n />\r\n </div>\r\n )}\r\n {bimFiles.length === 1 && bimModelsAddedToMap.some(model => model.building.id === buildingId) && (() => {\r\n const selectedModel = bimModelsAddedToMap.find(model => model.building.id === buildingId);\r\n console.log('selectedModel', selectedModel);\r\n return true ? (\r\n <Button\r\n onClick={() => handleEditClick(selectedModel.bimFile.name)}\r\n variant=\"ghost\"\r\n size=\"sm\"\r\n title={t('editBimButton')}\r\n disabled={!ability.can('update', 'File')}\r\n >\r\n <LR.LocationEdit className=\"h-4 w-4\" />\r\n </Button>\r\n ) : null;\r\n })()}\r\n </div>\r\n )\r\n }\r\n </div>}\r\n\r\n {/* Tool Buttons */}\r\n <div className=\"flex items-center gap-3\">\r\n {isLoading\r\n ? Array.from({ \"length\": 4 }).map((_, index) => (<Skeleton\r\n key={index}\r\n className=\"w-8 h-8 rounded-sm\"\r\n />),)\r\n : (!isError && building ? (\r\n buildingButtons.map((button) => {\r\n if (button.component) {\r\n const ButtonComponent = button.component\r\n const shouldAnimateSpin = button.id === 'map-add-file' && isUploading\r\n return (\r\n <div\r\n key={button.id}\r\n className={`w-8 h-8 rounded-sm border-2 border-gray-200 inline-flex justify-center items-center ${shouldAnimateSpin ? 'animate-spin' : ''}`}\r\n >\r\n <ButtonComponent tool={button} building={building} />\r\n </div>\r\n )\r\n }\r\n return (\r\n <Button\r\n key={button.id}\r\n variant=\"ghost\"\r\n className=\"w-8 h-8 rounded-sm border-2 border-gray-200 inline-flex justify-center items-center\"\r\n aria-label={button.title}\r\n onClick={button.onClick}\r\n title={button.title}\r\n disabled={button.disabled}\r\n >\r\n <button.icon className={`w-6 h-6 ${button.id === 'map-add-file' && isUploading ? 'animate-spin' : ''}`} />\r\n </Button>\r\n )\r\n })\r\n ) : null)}\r\n </div>\r\n </div>\r\n );\r\n\r\n}\r\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../../../../src/core/components/viewers/map/src/MapFeaturePopoverMenu/src/DatabaseBuildingPopover/src/BuildingsTools.tsx"],"sourcesContent":["\"use client\"\r\n\r\n// SPDX-License-Identifier: AGPL-3.0-or-later\r\n// Copyright (C) 2025 Collab Digital Twins\r\n\r\nimport * as LR from \"lucide-react\";\r\nimport { useTranslations } from \"next-intl\";\r\nimport React from \"react\";\r\n\r\nimport { Button, Label, Skeleton, Switch, Checkbox } from \"../../../../../../../../components/ui/\";\r\nimport {\r\n DropdownMenu,\r\n DropdownMenuContent,\r\n DropdownMenuItem,\r\n DropdownMenuTrigger,\r\n} from \"../../../../../../../../components/ui/DropdownMenu\";\r\nimport { useFileUploadHandler } from \"../../../../../../../../components/ui/FilesManager/src/useFileUploadHandler\";\r\nimport { useFilesByBuildingId, useUploadFileToBuilding } from \"../../../../../../../../hooks/files/files\";\r\nimport { usePermissions } from '../../../../../../../../store'\r\nimport { BuildingsContext, BimContext, MapContext, MenusContext, useMenusContext } from \"../../../../../../../../store\";\r\nimport { ViewerNames } from \"../../../../../../../../types/\";\r\nimport { toggleBimToMap as dispatchToggleBimToMap } from '../../../../../utils/toggleBimToMap';\r\nimport Compare from '../../../../compare';\r\n\r\nimport type { Building, DbFile } from \"../../../../../../../../types/dbTypes\";\r\nimport type { Tool } from \"../../../../../../../../types/tools\";\r\nimport type { MapGeoJSONFeature } from \"maplibre-gl\";\r\n\r\ninterface BuildingToolsProps {\r\n isLoading: boolean;\r\n isError: boolean;\r\n building: Building | null;\r\n feature: MapGeoJSONFeature;\r\n onCloseAction: () => void;\r\n}\r\n\r\nexport default function BuildingTools({\r\n isLoading,\r\n isError,\r\n building,\r\n feature,\r\n onCloseAction\r\n}: BuildingToolsProps) {\r\n\r\n const t = useTranslations('BuildingTools')\r\n // Permissions\r\n const { ability } = usePermissions()\r\n\r\n const { dispatch: menusDispatch } = React.useContext(MenusContext);\r\n const { state: mapState, } = React.useContext(MapContext);\r\n const { dispatch: bimDispatch, state: bimState } = React.useContext(BimContext);\r\n const { bimModelsAddedToMap } = bimState.bim;\r\n const { dispatch: buildingsDispatch } = React.useContext(BuildingsContext);\r\n\r\n const fileInputRef = React.useRef<HTMLInputElement>(null);\r\n const { setView } = useMenusContext();\r\n const { setSelectedItem } = useMenusContext();\r\n\r\n const buildingId = React.useMemo(\r\n () => {\r\n\r\n const featureProps = feature?.properties || {};\r\n const id = Number(featureProps.dbId);\r\n return isNaN(id)\r\n ? null\r\n : id;\r\n\r\n },\r\n [feature?.properties],\r\n );\r\n\r\n const { files } = useFilesByBuildingId(buildingId);\r\n\r\n const bimAdded = React.useMemo(\r\n () => (buildingId\r\n ? mapState.map.bimModelsAddedToMap.includes(buildingId)\r\n : false),\r\n [\r\n mapState.map.bimModelsAddedToMap,\r\n buildingId,\r\n ],\r\n );\r\n\r\n const { uploadFile, isMutating: isUploadingFromHook } = useUploadFileToBuilding(building?.id || null);\r\n\r\n // Local state to track the entire upload process from file selection to completion\r\n const [\r\n isLocallyUploading,\r\n setIsLocallyUploading,\r\n ] = React.useState(false);\r\n\r\n // Combined uploading state\r\n const isUploading = isLocallyUploading || isUploadingFromHook;\r\n\r\n // Use the reusable upload handler without passing bimComponents\r\n const { handleFileUpload } = useFileUploadHandler({\r\n buildingId: buildingId || -1,\r\n uploadFile,\r\n onUploadStart: () => setIsLocallyUploading(true),\r\n onUploadEnd: () => setIsLocallyUploading(false),\r\n customMessages: {\r\n success: t('toastFIleUploadSuccess'),\r\n errorPrefix: t('toastFIleUploadError'),\r\n errorSuffix: t('toastUnknownError'),\r\n noIdError: t('buildingIdRequired')\r\n }\r\n });\r\n\r\n const changeViewer = React.useCallback(\r\n (viewer: ViewerNames) => {\r\n\r\n menusDispatch({\r\n type: \"SET_VIEWER\",\r\n payload: { \"currentViewer\": viewer },\r\n });\r\n\r\n buildingsDispatch({\r\n type: \"SET-CURRENT-BUILDING\",\r\n payload: { building },\r\n });\r\n onCloseAction();\r\n\r\n },\r\n [\r\n menusDispatch,\r\n buildingsDispatch,\r\n building,\r\n onCloseAction\r\n ]\r\n );\r\n\r\n const triggerFileInput = React.useCallback(\r\n () => {\r\n\r\n fileInputRef.current?.click();\r\n\r\n },\r\n [],\r\n );\r\n\r\n const toggleBimToMap = React.useCallback(\r\n (bimFile: DbFile) => {\r\n dispatchToggleBimToMap(bimDispatch, bimFile, building);\r\n\r\n },\r\n [\r\n building,\r\n bimDispatch\r\n ]\r\n );\r\n\r\n const onOpenBimViewer = () => {\r\n buildingsDispatch({ type: \"SET-CURRENT-BUILDING\", payload: { building } });\r\n changeViewer(ViewerNames.bim);\r\n setSelectedItem(building);\r\n }\r\n\r\n const buildingFiles = React.useMemo(\r\n () => files || [],\r\n [files],\r\n );\r\n\r\n const bimFiles = React.useMemo(\r\n () => buildingFiles.filter((file: DbFile) => file.extension?.toLowerCase() === \"frag\"),\r\n [buildingFiles],\r\n )\r\n\r\n const pcFiles = React.useMemo(\r\n () => buildingFiles.filter((file: DbFile) => file.type === 'point-cloud-file' || ['laz', 'las'].includes(file.extension || '')),\r\n [buildingFiles],\r\n )\r\n\r\n const buildingButtons: Tool[] = React.useMemo(\r\n () => [\r\n {\r\n id: \"map-compare-buildings\",\r\n icon: LR.Columns3,\r\n title: t('compareTitle'),\r\n component: Compare,\r\n disabled: !ability.can('read', 'Building')\r\n },\r\n {\r\n id: \"open-building-page\",\r\n icon: LR.Building2,\r\n title: t('openBIMTitle'),\r\n onClick: () => {\r\n\r\n changeViewer(ViewerNames.buildings);\r\n setSelectedItem(building);\r\n setView(\"detail\");\r\n\r\n },\r\n disabled: !ability.can('read', 'Building')\r\n },\r\n {\r\n id: \"open-bim-viewer\",\r\n icon: LR.Box,\r\n title: t('OpenBIMViewerTitle'),\r\n onClick: onOpenBimViewer,\r\n disabled: !ability.can('read', 'File')\r\n },\r\n {\r\n id: 'open-pointcloud',\r\n title: t('openPointCloudTitle'),\r\n icon: LR.Grip,\r\n onClick: () => changeViewer(ViewerNames.pointcloud),\r\n disabled: pcFiles.length === 0 || !ability.can('read', 'File')\r\n },\r\n {\r\n id: \"map-add-file\",\r\n icon: isUploading\r\n ? LR.Loader\r\n : LR.FilePlus,\r\n title: t('addFileTitle'),\r\n onClick: triggerFileInput,\r\n disabled: !ability.can('create', 'File')\r\n },\r\n ],\r\n [\r\n changeViewer,\r\n building,\r\n setSelectedItem,\r\n setView,\r\n isUploading,\r\n triggerFileInput\r\n ]\r\n );\r\n\r\n const onLoadBIM = React.useCallback(\r\n (file: DbFile = null) => {\r\n if (bimFiles.length === 0) return;\r\n toggleBimToMap(bimFiles[0]);\r\n },\r\n [\r\n bimFiles,\r\n toggleBimToMap\r\n ]\r\n );\r\n\r\n const handleEditClick = (fileName: string) => {\r\n bimDispatch({\r\n type: \"EDIT_BIM_MODEL_BY_NAME\",\r\n payload: { editingBimModel: fileName }\r\n });\r\n\r\n onCloseAction();\r\n }\r\n\r\n\r\n return (\r\n <div className=\"space-y-4\">\r\n {/* Hidden file input */}\r\n <input\r\n onChange={(e) => handleFileUpload(e.target.files?.[0] || null)}\r\n ref={fileInputRef}\r\n style={{ \"display\": \"none\" }}\r\n type=\"file\"\r\n />\r\n\r\n {/* BIM Controls */}\r\n {!isError &&\r\n <div className=\"flex items-center space-x-2\">\r\n {isLoading\r\n ? <>\r\n <Skeleton className=\"h-4 w-20\" />\r\n\r\n <Skeleton className=\"h-6 w-10\" />\r\n </>\r\n : (\r\n <div className=\"flex items-center justify-between w-full\">\r\n {bimFiles.length > 1 ? (\r\n <div className=\"flex items-center space-x-2\">\r\n <Label className=\"font-semibold\" htmlFor=\"bim-file-dropdown\">\r\n {t('selectBimLabel')}\r\n </Label>\r\n <DropdownMenu>\r\n <DropdownMenuTrigger asChild>\r\n <Button variant=\"outline\" size=\"sm\" id=\"bim-file-dropdown\">\r\n {bimModelsAddedToMap.length > 0\r\n ? `${bimModelsAddedToMap.filter(model => model.bimFile.attachedFilesBuildingId === buildingId).length} ${t('filesSelected')}`\r\n : t('selectFiles')\r\n }\r\n </Button>\r\n </DropdownMenuTrigger>\r\n <DropdownMenuContent>\r\n {bimFiles.map((file: DbFile) => {\r\n const isSelected = bimModelsAddedToMap.some(model => model.bimFile.name === file.name);\r\n return (\r\n <DropdownMenuItem\r\n key={file.name}\r\n onClick={(e) => {\r\n e.preventDefault();\r\n toggleBimToMap(file);\r\n }}\r\n className=\"flex items-center justify-between cursor-pointer\"\r\n >\r\n <div className=\"flex items-center space-x-2\">\r\n <Checkbox\r\n checked={isSelected}\r\n onClick={(e) => e.stopPropagation()}\r\n onCheckedChange={() => {\r\n toggleBimToMap(file);\r\n }}\r\n />\r\n <span>{file.name}</span>\r\n </div>\r\n {isSelected && (\r\n <Button\r\n onClick={(e) => {\r\n e.preventDefault();\r\n e.stopPropagation();\r\n handleEditClick(file.name);\r\n }}\r\n variant=\"ghost\"\r\n size=\"sm\"\r\n className=\"h-6 w-6 p-0\"\r\n title={t('editBimButton')}\r\n >\r\n <LR.LocationEdit className=\"h-3 w-3\" />\r\n </Button>\r\n )}\r\n </DropdownMenuItem>\r\n );\r\n })}\r\n </DropdownMenuContent>\r\n </DropdownMenu>\r\n </div>\r\n ) : (\r\n <div className=\"flex items-center space-x-2\">\r\n <Label\r\n className={`font-semibold ${bimFiles.length === 0 ? 'text-muted-foreground' : ''}`}\r\n htmlFor=\"show-bim\"\r\n >\r\n {bimAdded ? t('hideBIM') : t('showBIM')}\r\n </Label>\r\n <Switch\r\n id=\"show-bim\"\r\n checked={bimFiles.length === 1 && bimModelsAddedToMap.some(model => model.bimFile.name === bimFiles[0]?.name)}\r\n disabled={bimFiles.length === 0 || !ability.can('read', 'File')}\r\n onCheckedChange={() => onLoadBIM()}\r\n />\r\n </div>\r\n )}\r\n {bimFiles.length === 1 && bimModelsAddedToMap.some(model => model.building.id === buildingId) && (() => {\r\n const selectedModel = bimModelsAddedToMap.find(model => model.building.id === buildingId);\r\n return true ? (\r\n <Button\r\n onClick={() => handleEditClick(selectedModel.bimFile.name)}\r\n variant=\"ghost\"\r\n size=\"sm\"\r\n title={t('editBimButton')}\r\n disabled={!ability.can('update', 'File')}\r\n >\r\n <LR.LocationEdit className=\"h-4 w-4\" />\r\n </Button>\r\n ) : null;\r\n })()}\r\n </div>\r\n )\r\n }\r\n </div>}\r\n\r\n {/* Tool Buttons */}\r\n <div className=\"flex items-center gap-3\">\r\n {isLoading\r\n ? Array.from({ \"length\": 4 }).map((_, index) => (<Skeleton\r\n key={index}\r\n className=\"w-8 h-8 rounded-sm\"\r\n />),)\r\n : (!isError && building ? (\r\n buildingButtons.map((button) => {\r\n if (button.component) {\r\n const ButtonComponent = button.component\r\n const shouldAnimateSpin = button.id === 'map-add-file' && isUploading\r\n return (\r\n <div\r\n key={button.id}\r\n className={`w-8 h-8 rounded-sm border-2 border-gray-200 inline-flex justify-center items-center ${shouldAnimateSpin ? 'animate-spin' : ''}`}\r\n >\r\n <ButtonComponent tool={button} building={building} />\r\n </div>\r\n )\r\n }\r\n return (\r\n <Button\r\n key={button.id}\r\n variant=\"ghost\"\r\n className=\"w-8 h-8 rounded-sm border-2 border-gray-200 inline-flex justify-center items-center\"\r\n aria-label={button.title}\r\n onClick={button.onClick}\r\n title={button.title}\r\n disabled={button.disabled}\r\n >\r\n <button.icon className={`w-6 h-6 ${button.id === 'map-add-file' && isUploading ? 'animate-spin' : ''}`} />\r\n </Button>\r\n )\r\n })\r\n ) : null)}\r\n </div>\r\n </div>\r\n );\r\n\r\n}\r\n"],"mappings":"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","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NonDatabaseBuildingPopover.d.ts","sourceRoot":"","sources":["../../../../../../../../src/core/components/viewers/map/src/MapFeaturePopoverMenu/src/NonDatabaseBuildingPopover.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AA2B9B,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAA;AAMpD,UAAU,+BAA+B;IACvC,OAAO,EAAE,iBAAiB,CAAA;IAC1B,MAAM,EAAE,OAAO,CAAA;IACf,aAAa,EAAE,MAAM,IAAI,CAAA;CAC1B;AAED,MAAM,CAAC,OAAO,UAAU,0BAA0B,CAAC,EACjD,OAAO,EACP,aAAa,GACd,EAAE,+BAA+B,
|
|
1
|
+
{"version":3,"file":"NonDatabaseBuildingPopover.d.ts","sourceRoot":"","sources":["../../../../../../../../src/core/components/viewers/map/src/MapFeaturePopoverMenu/src/NonDatabaseBuildingPopover.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AA2B9B,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAA;AAMpD,UAAU,+BAA+B;IACvC,OAAO,EAAE,iBAAiB,CAAA;IAC1B,MAAM,EAAE,OAAO,CAAA;IACf,aAAa,EAAE,MAAM,IAAI,CAAA;CAC1B;AAED,MAAM,CAAC,OAAO,UAAU,0BAA0B,CAAC,EACjD,OAAO,EACP,aAAa,GACd,EAAE,+BAA+B,qBA8ajC"}
|
package/dist/core/components/viewers/map/src/MapFeaturePopoverMenu/src/NonDatabaseBuildingPopover.js
CHANGED
|
@@ -227,7 +227,6 @@ function NonDatabaseBuildingPopover({
|
|
|
227
227
|
};
|
|
228
228
|
const handleLocationEdit = () => {
|
|
229
229
|
if (markerManagerRef.current && coordinates && map) {
|
|
230
|
-
console.log("Creating editable marker at coordinates:", coordinates);
|
|
231
230
|
markerManagerRef.current.create(coordinates, map, {
|
|
232
231
|
editing: true
|
|
233
232
|
});
|