@collabdt/core 0.0.42 → 0.0.44
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/Toolbar.d.ts.map +1 -1
- package/dist/core/components/Toolbar.js.map +1 -1
- package/dist/core/components/ToolbarBody.js.map +1 -1
- package/dist/core/components/TopNavigationBar.js.map +1 -1
- package/dist/core/components/authentication/ForgotPassword.js.map +1 -1
- package/dist/core/components/authentication/Signin.js.map +1 -1
- package/dist/core/components/authentication/Signup.js.map +1 -1
- package/dist/core/components/settings/src/OrganizationSkeleton.js.map +1 -1
- package/dist/core/components/settings/src/SettingsSkeleton.js.map +1 -1
- package/dist/core/components/ui/Comments/CollapsibleCommentItem.d.ts.map +1 -1
- package/dist/core/components/ui/Comments/CollapsibleCommentItem.js.map +1 -1
- package/dist/core/components/ui/Comments/CommentsSection.d.ts.map +1 -1
- package/dist/core/components/ui/Comments/CommentsSection.js.map +1 -1
- package/dist/core/components/ui/DataTable.d.ts.map +1 -1
- package/dist/core/components/ui/DataTable.js +7 -0
- package/dist/core/components/ui/DataTable.js.map +1 -1
- package/dist/core/components/ui/FilesManager/src/FileItemComponent.d.ts.map +1 -1
- package/dist/core/components/ui/FilesManager/src/FileItemComponent.js.map +1 -1
- package/dist/core/components/ui/FilesManager/src/FileMarker.d.ts +5 -2
- package/dist/core/components/ui/FilesManager/src/FileMarker.d.ts.map +1 -1
- package/dist/core/components/ui/FilesManager/src/FileMarker.js +84 -21
- package/dist/core/components/ui/FilesManager/src/FileMarker.js.map +1 -1
- package/dist/core/components/ui/FilesManager/src/convertIfcToFragmentsFile.d.ts.map +1 -1
- package/dist/core/components/ui/FilesManager/src/convertIfcToFragmentsFile.js.map +1 -1
- package/dist/core/components/ui/FilesManager/src/useFileUploadHandler.d.ts.map +1 -1
- package/dist/core/components/ui/FilesManager/src/useFileUploadHandler.js.map +1 -1
- package/dist/core/components/ui/Icons/IfcIcon.d.ts.map +1 -1
- package/dist/core/components/ui/Icons/IfcIcon.js.map +1 -1
- package/dist/core/components/ui/InfoSidebar/index.d.ts.map +1 -1
- package/dist/core/components/ui/InfoSidebar/index.js.map +1 -1
- package/dist/core/components/ui/Navbar.d.ts.map +1 -1
- package/dist/core/components/ui/Navbar.js.map +1 -1
- package/dist/core/components/ui/Sensors/CollapsibleSensorItem.d.ts.map +1 -1
- package/dist/core/components/ui/Sensors/CollapsibleSensorItem.js.map +1 -1
- package/dist/core/components/ui/Sensors/SensorTagsSection.d.ts.map +1 -1
- package/dist/core/components/ui/Sensors/SensorTagsSection.js.map +1 -1
- package/dist/core/components/ui/Sensors/SensorsSection.d.ts.map +1 -1
- package/dist/core/components/ui/Sensors/SensorsSection.js.map +1 -1
- package/dist/core/components/ui/ShareFeature/ShareToolSubmenu.d.ts.map +1 -1
- package/dist/core/components/ui/ShareFeature/ShareToolSubmenu.js.map +1 -1
- package/dist/core/components/viewers/Data/buildingDetails/GeocoderInput.js +2 -2
- package/dist/core/components/viewers/Data/buildingDetails/GeocoderInput.js.map +1 -1
- package/dist/core/components/viewers/Data/infrastructureDetails/FieldRenderer.d.ts.map +1 -1
- package/dist/core/components/viewers/Data/infrastructureDetails/FieldRenderer.js.map +1 -1
- package/dist/core/components/viewers/Data/siteDetails/AssociatedBuildings.js +2 -2
- package/dist/core/components/viewers/Data/siteDetails/AssociatedBuildings.js.map +1 -1
- package/dist/core/components/viewers/Viewer.d.ts.map +1 -1
- package/dist/core/components/viewers/Viewer.js +5 -1
- package/dist/core/components/viewers/Viewer.js.map +1 -1
- package/dist/core/components/viewers/bim/BimToolbar.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/BimToolbar.js.map +1 -1
- package/dist/core/components/viewers/bim/BimViewer.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/BimViewer.js +14 -5
- package/dist/core/components/viewers/bim/BimViewer.js.map +1 -1
- package/dist/core/components/viewers/bim/src/BimLoadingState/index.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/BimLoadingState/index.js +6 -0
- package/dist/core/components/viewers/bim/src/BimLoadingState/index.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 +168 -39
- 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.map +1 -1
- package/dist/core/components/viewers/bim/src/BimSidebar/src/LayersTab/src/FloorplanSection.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/BimSidebar/src/LayersTab/src/FloorplanSection.js +1 -1
- package/dist/core/components/viewers/bim/src/BimSidebar/src/LayersTab/src/FloorplanSection.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.map +1 -1
- package/dist/core/components/viewers/bim/src/DXFLoader/index.d.ts +14 -37
- package/dist/core/components/viewers/bim/src/DXFLoader/index.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/DXFLoader/index.js +31 -318
- package/dist/core/components/viewers/bim/src/DXFLoader/index.js.map +1 -1
- package/dist/core/components/viewers/bim/src/FloorplanTool/index.d.ts +66 -7
- package/dist/core/components/viewers/bim/src/FloorplanTool/index.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/FloorplanTool/index.js +253 -21
- package/dist/core/components/viewers/bim/src/FloorplanTool/index.js.map +1 -1
- package/dist/core/components/viewers/bim/src/ModelManager/index.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/ModelManager/index.js +9 -0
- package/dist/core/components/viewers/bim/src/ModelManager/index.js.map +1 -1
- package/dist/core/components/viewers/bim/src/SimpleBimViewer.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/SimpleBimViewer.js.map +1 -1
- package/dist/core/components/viewers/bim/src/lib/TrueNorthPopover.d.ts +3 -2
- package/dist/core/components/viewers/bim/src/lib/TrueNorthPopover.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/lib/TrueNorthPopover.js.map +1 -1
- package/dist/core/components/viewers/bim/src/lib/useFriendlyIfcClassName.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/lib/useFriendlyIfcClassName.js +3 -4
- package/dist/core/components/viewers/bim/src/lib/useFriendlyIfcClassName.js.map +1 -1
- 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 +40 -18
- package/dist/core/components/viewers/bim/src/tools/AddToBim/index.js.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/AddDxf.d.ts +2 -5
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/AddDxf.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/AddDxf.js +45 -59
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/AddDxf.js.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/AddSensor.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/AddSensor.js.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimSensor.js +1 -0
- 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/FileHandler.d.ts +10 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/FileHandler.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/FileHandler.js +25 -73
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/FileHandler.js.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/FileMarkerUtils.d.ts +7 -3
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/FileMarkerUtils.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/FileMarkerUtils.js +42 -78
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/FileMarkerUtils.js.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/Position3DCard.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/Position3DCard.js +52 -75
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/Position3DCard.js.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useFilePlacement.d.ts +15 -3
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useFilePlacement.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useFilePlacement.js +165 -105
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useFilePlacement.js.map +1 -1
- package/dist/core/components/viewers/index.d.ts.map +1 -1
- package/dist/core/components/viewers/index.js.map +1 -1
- package/dist/core/components/viewers/map/MapViewer.d.ts.map +1 -1
- package/dist/core/components/viewers/map/MapViewer.js +32 -8
- package/dist/core/components/viewers/map/MapViewer.js.map +1 -1
- package/dist/core/components/viewers/map/datasets/DatasetManager/index.d.ts.map +1 -1
- package/dist/core/components/viewers/map/datasets/DatasetManager/index.js +4 -3
- package/dist/core/components/viewers/map/datasets/DatasetManager/index.js.map +1 -1
- package/dist/core/components/viewers/map/datasets/RowActions.d.ts.map +1 -1
- package/dist/core/components/viewers/map/datasets/RowActions.js +148 -0
- package/dist/core/components/viewers/map/datasets/RowActions.js.map +1 -1
- package/dist/core/components/viewers/map/datasets/index.d.ts.map +1 -1
- package/dist/core/components/viewers/map/datasets/index.js +32 -15
- package/dist/core/components/viewers/map/datasets/index.js.map +1 -1
- package/dist/core/components/viewers/map/datasets/src/builtinLiveDatasets.d.ts +4 -0
- package/dist/core/components/viewers/map/datasets/src/builtinLiveDatasets.d.ts.map +1 -0
- package/dist/core/components/viewers/map/datasets/src/builtinLiveDatasets.js +43 -0
- package/dist/core/components/viewers/map/datasets/src/builtinLiveDatasets.js.map +1 -0
- package/dist/core/components/viewers/map/datasets/src/localDatasets.d.ts.map +1 -1
- package/dist/core/components/viewers/map/datasets/src/localDatasets.js +39 -3
- package/dist/core/components/viewers/map/datasets/src/localDatasets.js.map +1 -1
- package/dist/core/components/viewers/map/datasets/src/minioDatasets.d.ts +1 -1
- package/dist/core/components/viewers/map/datasets/src/minioDatasets.d.ts.map +1 -1
- package/dist/core/components/viewers/map/datasets/src/minioDatasets.js +2 -1
- package/dist/core/components/viewers/map/datasets/src/minioDatasets.js.map +1 -1
- package/dist/core/components/viewers/map/datasets/src/publishedTiles.d.ts +47 -0
- package/dist/core/components/viewers/map/datasets/src/publishedTiles.d.ts.map +1 -0
- package/dist/core/components/viewers/map/datasets/src/publishedTiles.js +114 -0
- package/dist/core/components/viewers/map/datasets/src/publishedTiles.js.map +1 -0
- package/dist/core/components/viewers/map/datasets/src/wmsTime.d.ts +49 -0
- package/dist/core/components/viewers/map/datasets/src/wmsTime.d.ts.map +1 -0
- package/dist/core/components/viewers/map/datasets/src/wmsTime.js +100 -0
- package/dist/core/components/viewers/map/datasets/src/wmsTime.js.map +1 -0
- package/dist/core/components/viewers/map/src/Geocoder.d.ts.map +1 -1
- package/dist/core/components/viewers/map/src/Geocoder.js +9 -9
- package/dist/core/components/viewers/map/src/Geocoder.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 +25 -43
- package/dist/core/components/viewers/map/src/MapFeaturePopoverMenu/src/NonDatabaseBuildingPopover.js.map +1 -1
- package/dist/core/components/viewers/map/src/MapLayers/index.d.ts.map +1 -1
- package/dist/core/components/viewers/map/src/MapLayers/index.js.map +1 -1
- package/dist/core/components/viewers/map/src/MapLayers/src/BimLayer/index.d.ts.map +1 -1
- package/dist/core/components/viewers/map/src/MapLayers/src/BimLayer/index.js +19 -2
- package/dist/core/components/viewers/map/src/MapLayers/src/BimLayer/index.js.map +1 -1
- package/dist/core/components/viewers/map/src/MapLayers/src/CountryLayer/countryLayerUtils.d.ts +9 -0
- package/dist/core/components/viewers/map/src/MapLayers/src/CountryLayer/countryLayerUtils.d.ts.map +1 -0
- package/dist/core/components/viewers/map/src/MapLayers/src/CountryLayer/countryLayerUtils.js +28 -0
- package/dist/core/components/viewers/map/src/MapLayers/src/CountryLayer/countryLayerUtils.js.map +1 -0
- package/dist/core/components/viewers/map/src/MapLayers/src/CountryLayer/index.d.ts.map +1 -1
- package/dist/core/components/viewers/map/src/MapLayers/src/CountryLayer/index.js +1 -16
- package/dist/core/components/viewers/map/src/MapLayers/src/CountryLayer/index.js.map +1 -1
- package/dist/core/components/viewers/map/src/MapLayers/src/FileLayer/FileModelLayer/FileModelLayer.d.ts.map +1 -1
- package/dist/core/components/viewers/map/src/MapLayers/src/FileLayer/FileModelLayer/FileModelLayer.js.map +1 -1
- package/dist/core/components/viewers/map/src/MapLayers/src/FileLayer/utils/CustomModelLayer.d.ts.map +1 -1
- package/dist/core/components/viewers/map/src/MapLayers/src/FileLayer/utils/CustomModelLayer.js +31 -2
- package/dist/core/components/viewers/map/src/MapLayers/src/FileLayer/utils/CustomModelLayer.js.map +1 -1
- package/dist/core/components/viewers/map/src/MapLayers/src/OpenDataLayer/src/WmsTimeControl.d.ts +21 -0
- package/dist/core/components/viewers/map/src/MapLayers/src/OpenDataLayer/src/WmsTimeControl.d.ts.map +1 -0
- package/dist/core/components/viewers/map/src/MapLayers/src/OpenDataLayer/src/WmsTimeControl.js +111 -0
- package/dist/core/components/viewers/map/src/MapLayers/src/OpenDataLayer/src/WmsTimeControl.js.map +1 -0
- package/dist/core/components/viewers/map/src/MapLayers/src/OpenDataLayer/src/index.d.ts.map +1 -1
- package/dist/core/components/viewers/map/src/MapLayers/src/OpenDataLayer/src/index.js +77 -2
- package/dist/core/components/viewers/map/src/MapLayers/src/OpenDataLayer/src/index.js.map +1 -1
- package/dist/core/components/viewers/map/src/MapSidebar/index.d.ts.map +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.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.map +1 -1
- package/dist/core/components/viewers/map/src/MapSidebar/src/SettingsTab/src/MapProjection.d.ts.map +1 -1
- package/dist/core/components/viewers/map/src/MapSidebar/src/SettingsTab/src/MapProjection.js +19 -9
- package/dist/core/components/viewers/map/src/MapSidebar/src/SettingsTab/src/MapProjection.js.map +1 -1
- package/dist/core/components/viewers/map/src/MapSidebar/src/SettingsTab/src/TerrainLevel.d.ts.map +1 -1
- package/dist/core/components/viewers/map/src/MapSidebar/src/SettingsTab/src/TerrainLevel.js +10 -3
- package/dist/core/components/viewers/map/src/MapSidebar/src/SettingsTab/src/TerrainLevel.js.map +1 -1
- package/dist/core/components/viewers/map/utils/geocoder.d.ts +6 -2
- package/dist/core/components/viewers/map/utils/geocoder.d.ts.map +1 -1
- package/dist/core/components/viewers/map/utils/geocoder.js +10 -62
- package/dist/core/components/viewers/map/utils/geocoder.js.map +1 -1
- package/dist/core/components/viewers/map/utils/geocoding/adapters.d.ts +5 -0
- package/dist/core/components/viewers/map/utils/geocoding/adapters.d.ts.map +1 -0
- package/dist/core/components/viewers/map/utils/geocoding/adapters.js +192 -0
- package/dist/core/components/viewers/map/utils/geocoding/adapters.js.map +1 -0
- package/dist/core/components/viewers/map/utils/geocoding/config.d.ts +6 -0
- package/dist/core/components/viewers/map/utils/geocoding/config.d.ts.map +1 -0
- package/dist/core/components/viewers/map/utils/geocoding/config.js +14 -0
- package/dist/core/components/viewers/map/utils/geocoding/config.js.map +1 -0
- package/dist/core/components/viewers/map/utils/geocoding/index.d.ts +8 -0
- package/dist/core/components/viewers/map/utils/geocoding/index.d.ts.map +1 -0
- package/dist/core/components/viewers/map/utils/geocoding/index.js +44 -0
- package/dist/core/components/viewers/map/utils/geocoding/index.js.map +1 -0
- package/dist/core/components/viewers/map/utils/geocoding/osm.d.ts +6 -0
- package/dist/core/components/viewers/map/utils/geocoding/osm.d.ts.map +1 -0
- package/dist/core/components/viewers/map/utils/geocoding/osm.js +39 -0
- package/dist/core/components/viewers/map/utils/geocoding/osm.js.map +1 -0
- package/dist/core/components/viewers/map/utils/geocoding/pelias.d.ts +8 -0
- package/dist/core/components/viewers/map/utils/geocoding/pelias.d.ts.map +1 -0
- package/dist/core/components/viewers/map/utils/geocoding/pelias.js +35 -0
- package/dist/core/components/viewers/map/utils/geocoding/pelias.js.map +1 -0
- package/dist/core/components/viewers/pointcloud/PointCloudToolbar.d.ts.map +1 -1
- package/dist/core/components/viewers/pointcloud/PointCloudToolbar.js.map +1 -1
- package/dist/core/components/viewers/pointcloud/PointCloudViewer.js +1 -3
- package/dist/core/components/viewers/pointcloud/PointCloudViewer.js.map +1 -1
- package/dist/core/components/viewers/pointcloud/src/PointCloudSidebar/src/FileTab/index.d.ts.map +1 -1
- package/dist/core/components/viewers/pointcloud/src/PointCloudSidebar/src/FileTab/index.js +2 -3
- package/dist/core/components/viewers/pointcloud/src/PointCloudSidebar/src/FileTab/index.js.map +1 -1
- package/dist/core/components/viewers/pointcloud/src/PointCloudSidebar/src/FileTab/src/FilesSection.d.ts.map +1 -1
- package/dist/core/components/viewers/pointcloud/src/PointCloudSidebar/src/FileTab/src/FilesSection.js +8 -2
- package/dist/core/components/viewers/pointcloud/src/PointCloudSidebar/src/FileTab/src/FilesSection.js.map +1 -1
- package/dist/core/components/viewers/pointcloud/src/PointCloudSidebar/src/FileTab/src/PointCloudSection.d.ts.map +1 -1
- package/dist/core/components/viewers/pointcloud/src/PointCloudSidebar/src/FileTab/src/PointCloudSection.js.map +1 -1
- package/dist/core/components/viewers/pointcloud/src/tools/PerformanceSettingsTools/NodeSizeSelectionTool.js +9 -9
- package/dist/core/components/viewers/pointcloud/src/tools/PerformanceSettingsTools/NodeSizeSelectionTool.js.map +1 -1
- package/dist/core/hooks/provider.js.map +1 -1
- package/dist/core/plugins/host/provider.d.ts.map +1 -1
- package/dist/core/plugins/host/provider.js.map +1 -1
- package/dist/core/store/AppConfig/context.d.ts.map +1 -1
- package/dist/core/store/AppConfig/context.js.map +1 -1
- package/dist/core/store/BIM/context.d.ts.map +1 -1
- package/dist/core/store/BIM/context.js.map +1 -1
- package/dist/core/store/BIM/reducer.d.ts +0 -1
- package/dist/core/store/BIM/reducer.d.ts.map +1 -1
- package/dist/core/store/BIM/reducer.js +0 -4
- package/dist/core/store/BIM/reducer.js.map +1 -1
- package/dist/core/store/Buildings/context.d.ts.map +1 -1
- package/dist/core/store/Buildings/context.js.map +1 -1
- package/dist/core/store/Content/context.d.ts.map +1 -1
- package/dist/core/store/Content/context.js.map +1 -1
- package/dist/core/store/Datasets/context.d.ts.map +1 -1
- package/dist/core/store/Datasets/context.js +2 -1
- package/dist/core/store/Datasets/context.js.map +1 -1
- package/dist/core/store/Datasets/reducer.d.ts +2 -0
- package/dist/core/store/Datasets/reducer.d.ts.map +1 -1
- package/dist/core/store/Datasets/reducer.js +9 -4
- package/dist/core/store/Datasets/reducer.js.map +1 -1
- package/dist/core/store/Files/context.d.ts.map +1 -1
- package/dist/core/store/Files/context.js.map +1 -1
- package/dist/core/store/Map/context.d.ts.map +1 -1
- package/dist/core/store/Map/context.js.map +1 -1
- package/dist/core/store/Menus/context.d.ts.map +1 -1
- package/dist/core/store/Menus/context.js.map +1 -1
- package/dist/core/store/Permissions/context.d.ts.map +1 -1
- package/dist/core/store/Permissions/context.js.map +1 -1
- package/dist/core/store/PointCloud/context.d.ts.map +1 -1
- package/dist/core/store/PointCloud/context.js.map +1 -1
- package/dist/core/store/Tools/context.d.ts.map +1 -1
- package/dist/core/store/Tools/context.js.map +1 -1
- package/dist/core/types/datasetTypes.d.ts +8 -1
- package/dist/core/types/datasetTypes.d.ts.map +1 -1
- package/dist/core/types/dbTypes.d.ts +13 -13
- package/dist/core/types/dbTypes.d.ts.map +1 -1
- package/dist/core/types/dbTypes.js +13 -13
- package/dist/core/types/dbTypes.js.map +1 -1
- package/dist/core/types/index.d.ts +1 -1
- package/dist/core/types/index.d.ts.map +1 -1
- package/dist/core/types/index.js.map +1 -1
- package/dist/core/utils/imageUtils.js +1 -1
- package/dist/core/utils/imageUtils.js.map +1 -1
- package/package.json +4 -3
- package/dist/core/components/viewers/pointcloud/src/PointCloudManagement/PointCloudViewer-old.d.ts +0 -3
- package/dist/core/components/viewers/pointcloud/src/PointCloudManagement/PointCloudViewer-old.d.ts.map +0 -1
- package/dist/core/components/viewers/pointcloud/src/PointCloudManagement/PointCloudViewer-old.js +0 -173
- package/dist/core/components/viewers/pointcloud/src/PointCloudManagement/PointCloudViewer-old.js.map +0 -1
package/dist/core/components/viewers/map/src/MapLayers/src/CountryLayer/countryLayerUtils.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare const ARCGIS_BASE = "https://services.arcgis.com/P3ePLMYs2RVChkJx/arcgis/rest/services/World_Administrative_Divisions/FeatureServer/0/query";
|
|
2
|
+
/**
|
|
3
|
+
* Convert a hex colour (`#rgb`, `#rrggbb`, or without `#`) to an `rgba()` string.
|
|
4
|
+
* Expands 3-digit shorthand and falls back to black on malformed input so the
|
|
5
|
+
* MapLibre paint expression never receives a NaN channel.
|
|
6
|
+
*/
|
|
7
|
+
export declare function hexToRgba(hex: string, opacity: number): string;
|
|
8
|
+
export declare const buildSubdivisionUrl: (countryCode: string, base?: string) => string;
|
|
9
|
+
//# sourceMappingURL=countryLayerUtils.d.ts.map
|
package/dist/core/components/viewers/map/src/MapLayers/src/CountryLayer/countryLayerUtils.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"countryLayerUtils.d.ts","sourceRoot":"","sources":["../../../../../../../../../src/core/components/viewers/map/src/MapLayers/src/CountryLayer/countryLayerUtils.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,WAAW,2HAA2H,CAAA;AAEnJ;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAY9D;AAED,eAAO,MAAM,mBAAmB,GAAI,aAAa,MAAM,EAAE,OAAM,MAAoB,KAAG,MAOrF,CAAA"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
const ARCGIS_BASE = "https://services.arcgis.com/P3ePLMYs2RVChkJx/arcgis/rest/services/World_Administrative_Divisions/FeatureServer/0/query";
|
|
2
|
+
function hexToRgba(hex, opacity) {
|
|
3
|
+
let clean = (hex != null ? hex : "").replace("#", "").trim();
|
|
4
|
+
if (clean.length === 3) {
|
|
5
|
+
clean = clean[0] + clean[0] + clean[1] + clean[1] + clean[2] + clean[2];
|
|
6
|
+
}
|
|
7
|
+
const r = parseInt(clean.slice(0, 2), 16);
|
|
8
|
+
const g = parseInt(clean.slice(2, 4), 16);
|
|
9
|
+
const b = parseInt(clean.slice(4, 6), 16);
|
|
10
|
+
if (Number.isNaN(r) || Number.isNaN(g) || Number.isNaN(b)) {
|
|
11
|
+
return `rgba(0, 0, 0, ${opacity})`;
|
|
12
|
+
}
|
|
13
|
+
return `rgba(${r}, ${g}, ${b}, ${opacity})`;
|
|
14
|
+
}
|
|
15
|
+
const buildSubdivisionUrl = (countryCode, base = ARCGIS_BASE) => {
|
|
16
|
+
const params = new URLSearchParams({
|
|
17
|
+
outFields: "NAME,ISO_CC,ISO_CODE,ADMINTYPE",
|
|
18
|
+
where: `ISO_CC='${countryCode}'`,
|
|
19
|
+
f: "geojson"
|
|
20
|
+
});
|
|
21
|
+
return `${base}?${params.toString()}`;
|
|
22
|
+
};
|
|
23
|
+
export {
|
|
24
|
+
ARCGIS_BASE,
|
|
25
|
+
buildSubdivisionUrl,
|
|
26
|
+
hexToRgba
|
|
27
|
+
};
|
|
28
|
+
//# sourceMappingURL=countryLayerUtils.js.map
|
package/dist/core/components/viewers/map/src/MapLayers/src/CountryLayer/countryLayerUtils.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../../../src/core/components/viewers/map/src/MapLayers/src/CountryLayer/countryLayerUtils.ts"],"sourcesContent":["export const ARCGIS_BASE = 'https://services.arcgis.com/P3ePLMYs2RVChkJx/arcgis/rest/services/World_Administrative_Divisions/FeatureServer/0/query'\r\n\r\n/**\r\n * Convert a hex colour (`#rgb`, `#rrggbb`, or without `#`) to an `rgba()` string.\r\n * Expands 3-digit shorthand and falls back to black on malformed input so the\r\n * MapLibre paint expression never receives a NaN channel.\r\n */\r\nexport function hexToRgba(hex: string, opacity: number): string {\r\n let clean = (hex ?? '').replace('#', '').trim()\r\n if (clean.length === 3) {\r\n clean = clean[0] + clean[0] + clean[1] + clean[1] + clean[2] + clean[2]\r\n }\r\n const r = parseInt(clean.slice(0, 2), 16)\r\n const g = parseInt(clean.slice(2, 4), 16)\r\n const b = parseInt(clean.slice(4, 6), 16)\r\n if (Number.isNaN(r) || Number.isNaN(g) || Number.isNaN(b)) {\r\n return `rgba(0, 0, 0, ${opacity})`\r\n }\r\n return `rgba(${r}, ${g}, ${b}, ${opacity})`\r\n}\r\n\r\nexport const buildSubdivisionUrl = (countryCode: string, base: string = ARCGIS_BASE): string => {\r\n const params = new URLSearchParams({\r\n outFields: 'NAME,ISO_CC,ISO_CODE,ADMINTYPE',\r\n where: `ISO_CC='${countryCode}'`,\r\n f: 'geojson',\r\n })\r\n return `${base}?${params.toString()}`\r\n}\r\n"],"mappings":"AAAO,MAAM,cAAc;AAOpB,SAAS,UAAU,KAAa,SAAyB;AAC9D,MAAI,SAAS,oBAAO,IAAI,QAAQ,KAAK,EAAE,EAAE,KAAK;AAC9C,MAAI,MAAM,WAAW,GAAG;AACtB,YAAQ,MAAM,CAAC,IAAI,MAAM,CAAC,IAAI,MAAM,CAAC,IAAI,MAAM,CAAC,IAAI,MAAM,CAAC,IAAI,MAAM,CAAC;AAAA,EACxE;AACA,QAAM,IAAI,SAAS,MAAM,MAAM,GAAG,CAAC,GAAG,EAAE;AACxC,QAAM,IAAI,SAAS,MAAM,MAAM,GAAG,CAAC,GAAG,EAAE;AACxC,QAAM,IAAI,SAAS,MAAM,MAAM,GAAG,CAAC,GAAG,EAAE;AACxC,MAAI,OAAO,MAAM,CAAC,KAAK,OAAO,MAAM,CAAC,KAAK,OAAO,MAAM,CAAC,GAAG;AACzD,WAAO,iBAAiB,OAAO;AAAA,EACjC;AACA,SAAO,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,OAAO;AAC1C;AAEO,MAAM,sBAAsB,CAAC,aAAqB,OAAe,gBAAwB;AAC9F,QAAM,SAAS,IAAI,gBAAgB;AAAA,IACjC,WAAW;AAAA,IACX,OAAO,WAAW,WAAW;AAAA,IAC7B,GAAG;AAAA,EACL,CAAC;AACD,SAAO,GAAG,IAAI,IAAI,OAAO,SAAS,CAAC;AACrC;","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../../../../src/core/components/viewers/map/src/MapLayers/src/CountryLayer/index.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../../../../src/core/components/viewers/map/src/MapLayers/src/CountryLayer/index.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAmJ9B,eAAO,MAAM,YAAY,yBA4IxB,CAAA"}
|
|
@@ -24,7 +24,7 @@ import { Source, Layer } from "react-map-gl/maplibre";
|
|
|
24
24
|
import { MapContext } from "../../../../../../../store/Map/context";
|
|
25
25
|
import { useAppConfigContext } from "../../../../../../../store/AppConfig/context";
|
|
26
26
|
import { useSearchParams, useRouter } from "next/navigation";
|
|
27
|
-
|
|
27
|
+
import { hexToRgba, buildSubdivisionUrl } from "./countryLayerUtils";
|
|
28
28
|
const DEFAULT_BORDER_COLOR = "#73cee2";
|
|
29
29
|
const BOUNDARIES_SOURCE_ID = "openmaptiles-boundaries";
|
|
30
30
|
const MAPTILER_KEY = process.env.NEXT_PUBLIC_MAPTILER_KEY;
|
|
@@ -148,21 +148,6 @@ function addGlobalBorderLayer(map, color) {
|
|
|
148
148
|
} catch (e) {
|
|
149
149
|
}
|
|
150
150
|
}
|
|
151
|
-
function hexToRgba(hex, opacity) {
|
|
152
|
-
const clean = hex.replace("#", "");
|
|
153
|
-
const r = parseInt(clean.slice(0, 2), 16);
|
|
154
|
-
const g = parseInt(clean.slice(2, 4), 16);
|
|
155
|
-
const b = parseInt(clean.slice(4, 6), 16);
|
|
156
|
-
return `rgba(${r}, ${g}, ${b}, ${opacity})`;
|
|
157
|
-
}
|
|
158
|
-
const buildSubdivisionUrl = (countryCode) => {
|
|
159
|
-
const params = new URLSearchParams({
|
|
160
|
-
outFields: "NAME,ISO_CC,ISO_CODE,ADMINTYPE",
|
|
161
|
-
where: `ISO_CC='${countryCode}'`,
|
|
162
|
-
f: "geojson"
|
|
163
|
-
});
|
|
164
|
-
return `${ARCGIS_BASE}?${params.toString()}`;
|
|
165
|
-
};
|
|
166
151
|
const CountryLayer = () => {
|
|
167
152
|
const { state: mapState, dispatch: mapDispatch } = React.useContext(MapContext);
|
|
168
153
|
const { map, currentLocation } = mapState.map;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../../../src/core/components/viewers/map/src/MapLayers/src/CountryLayer/index.tsx"],"sourcesContent":["\"use client\"\r\n\r\nimport * as React from 'react'\r\nimport { Source, Layer } from 'react-map-gl/maplibre'\r\nimport type { MapMouseEvent } from 'maplibre-gl'\r\nimport { MapContext } from '../../../../../../../store/Map/context'\r\nimport { useAppConfigContext } from '../../../../../../../store/AppConfig/context'\r\nimport { useSearchParams, useRouter } from 'next/navigation'\r\n\r\nconst ARCGIS_BASE = 'https://services.arcgis.com/P3ePLMYs2RVChkJx/arcgis/rest/services/World_Administrative_Divisions/FeatureServer/0/query'\r\nconst DEFAULT_BORDER_COLOR = '#73cee2'\r\n// satellite.json's openmaptiles source uses MapTiler's 'tiles/buildings' tileset which lacks the boundary + place layers. Pull both from the full openmaptiles v3 schema (same tileset blank.json uses).\r\nconst BOUNDARIES_SOURCE_ID = 'openmaptiles-boundaries'\r\n// Key is supplied by the consuming app via NEXT_PUBLIC_MAPTILER_KEY (Next inlines\r\n// it at build time). Kept out of source so @collabdt/core carries no secret.\r\nconst MAPTILER_KEY = process.env.NEXT_PUBLIC_MAPTILER_KEY\r\nconst BOUNDARIES_TILESET_URL = `https://api.maptiler.com/tiles/v3/tiles.json?key=${MAPTILER_KEY}`\r\nconst GLOBAL_LAYER_IDS = [\r\n 'global-borders-country',\r\n 'global-borders-region',\r\n 'global-labels-country',\r\n 'global-labels-state',\r\n 'global-labels-city',\r\n] as const\r\n\r\nfunction addGlobalBorderLayer(map: any, color: string) {\r\n try {\r\n if (!map.getSource(BOUNDARIES_SOURCE_ID)) {\r\n map.addSource(BOUNDARIES_SOURCE_ID, {\r\n type: 'vector',\r\n url: BOUNDARIES_TILESET_URL,\r\n })\r\n }\r\n\r\n // Country borders — visible at all zooms, slightly thinner up close where states take over.\r\n if (!map.getLayer('global-borders-country')) {\r\n map.addLayer({\r\n id: 'global-borders-country',\r\n type: 'line',\r\n source: BOUNDARIES_SOURCE_ID,\r\n 'source-layer': 'boundary',\r\n filter: ['all', ['==', 'admin_level', 2], ['==', 'maritime', 0]],\r\n layout: { 'line-cap': 'round', 'line-join': 'round' },\r\n paint: {\r\n 'line-color': color,\r\n 'line-width': { base: 1, stops: [[0, 0.8], [4, 1.4], [10, 1.4], [14, 1] ] },\r\n },\r\n })\r\n }\r\n\r\n // State / province borders — fade in around zoom 3, dominate from 5+.\r\n if (!map.getLayer('global-borders-region')) {\r\n map.addLayer({\r\n id: 'global-borders-region',\r\n type: 'line',\r\n source: BOUNDARIES_SOURCE_ID,\r\n 'source-layer': 'boundary',\r\n filter: ['all', ['in', 'admin_level', 3, 4], ['==', 'maritime', 0]],\r\n minzoom: 2,\r\n layout: { 'line-cap': 'round', 'line-join': 'round' },\r\n paint: {\r\n 'line-color': color,\r\n 'line-width': { base: 1, stops: [[2, 0], [4, 0.5], [8, 1.4], [14, 2.4]] },\r\n 'line-opacity': { base: 1, stops: [[2, 0], [4, 0.7], [6, 1]] },\r\n },\r\n })\r\n }\r\n\r\n // Country labels — small/global up to zoom ~6, gone by 8.\r\n if (!map.getLayer('global-labels-country')) {\r\n map.addLayer({\r\n id: 'global-labels-country',\r\n type: 'symbol',\r\n source: BOUNDARIES_SOURCE_ID,\r\n 'source-layer': 'place',\r\n filter: ['==', 'class', 'country'],\r\n minzoom: 1,\r\n maxzoom: 8,\r\n layout: {\r\n 'text-field': ['coalesce', ['get', 'name:latin'], ['get', 'name']],\r\n 'text-font': ['Noto Sans Regular'],\r\n 'text-size': { base: 1, stops: [[1, 10], [4, 13], [6, 16]] },\r\n 'text-letter-spacing': 0.1,\r\n 'text-max-width': 8,\r\n 'text-transform': 'uppercase',\r\n },\r\n paint: {\r\n 'text-color': '#ffffff',\r\n 'text-halo-color': 'rgba(0,0,0,0.7)',\r\n 'text-halo-width': 1.2,\r\n 'text-opacity': { base: 1, stops: [[5, 1], [7, 0]] },\r\n },\r\n })\r\n }\r\n\r\n // State / province labels — cover zoom 3–8.\r\n if (!map.getLayer('global-labels-state')) {\r\n map.addLayer({\r\n id: 'global-labels-state',\r\n type: 'symbol',\r\n source: BOUNDARIES_SOURCE_ID,\r\n 'source-layer': 'place',\r\n filter: ['in', 'class', 'state', 'province'],\r\n minzoom: 3,\r\n maxzoom: 9,\r\n layout: {\r\n 'text-field': ['coalesce', ['get', 'name:latin'], ['get', 'name']],\r\n 'text-font': ['Noto Sans Regular'],\r\n 'text-size': { base: 1, stops: [[3, 9], [6, 12], [8, 14]] },\r\n 'text-letter-spacing': 0.05,\r\n 'text-max-width': 8,\r\n },\r\n paint: {\r\n 'text-color': '#e8e8e8',\r\n 'text-halo-color': 'rgba(0,0,0,0.6)',\r\n 'text-halo-width': 1,\r\n 'text-opacity': { base: 1, stops: [[3, 0], [4, 1], [8, 1], [9, 0]] },\r\n },\r\n })\r\n }\r\n\r\n // Major city labels (rank ≤ 4 = most prominent cities) — appear when zoomed in past country level.\r\n if (!map.getLayer('global-labels-city')) {\r\n map.addLayer({\r\n id: 'global-labels-city',\r\n type: 'symbol',\r\n source: BOUNDARIES_SOURCE_ID,\r\n 'source-layer': 'place',\r\n filter: ['in', 'class', 'city', 'town'],\r\n minzoom: 5,\r\n maxzoom: 13,\r\n layout: {\r\n 'text-field': ['coalesce', ['get', 'name:latin'], ['get', 'name']],\r\n 'text-font': ['Noto Sans Regular'],\r\n 'text-size': { base: 1, stops: [[5, 10], [9, 13], [12, 15]] },\r\n 'text-max-width': 8,\r\n },\r\n paint: {\r\n 'text-color': '#dcdcdc',\r\n 'text-halo-color': 'rgba(0,0,0,0.7)',\r\n 'text-halo-width': 1.1,\r\n },\r\n })\r\n }\r\n } catch {\r\n // style may be in a transitional state\r\n }\r\n}\r\n\r\nfunction hexToRgba(hex: string, opacity: number): string {\r\n const clean = hex.replace('#', '')\r\n const r = parseInt(clean.slice(0, 2), 16)\r\n const g = parseInt(clean.slice(2, 4), 16)\r\n const b = parseInt(clean.slice(4, 6), 16)\r\n return `rgba(${r}, ${g}, ${b}, ${opacity})`\r\n}\r\n\r\nconst buildSubdivisionUrl = (countryCode: string) => {\r\n const params = new URLSearchParams({\r\n outFields: 'NAME,ISO_CC,ISO_CODE,ADMINTYPE',\r\n where: `ISO_CC='${countryCode}'`,\r\n f: 'geojson',\r\n })\r\n return `${ARCGIS_BASE}?${params.toString()}`\r\n}\r\n\r\nexport const CountryLayer = () => {\r\n const { state: mapState, dispatch: mapDispatch } = React.useContext(MapContext)\r\n const { map, currentLocation } = mapState.map\r\n const { state: appConfigState } = useAppConfigContext()\r\n const organization = appConfigState.appConfig.organization\r\n const searchParams = useSearchParams()\r\n const router = useRouter()\r\n const debounceRef = React.useRef<NodeJS.Timeout | null>(null)\r\n\r\n const countryCode = (organization?.country || 'CA').toUpperCase()\r\n const borderColor = hexToRgba((organization?.mainColor as string) || DEFAULT_BORDER_COLOR, 0.7)\r\n const subdivisionUrl = buildSubdivisionUrl(countryCode)\r\n\r\n // Add the global boundary line layer imperatively (vector-tile lines from the style's openmaptiles source). Re-adds on style swap so it survives basemap changes.\r\n React.useEffect(() => {\r\n if (!map) return\r\n const addNow = () => addGlobalBorderLayer(map, borderColor)\r\n if (map.isStyleLoaded()) addNow()\r\n map.on('styledata', addNow)\r\n return () => {\r\n try {\r\n map.off('styledata', addNow)\r\n for (const id of GLOBAL_LAYER_IDS) {\r\n if (map.getLayer(id)) map.removeLayer(id)\r\n }\r\n } catch {\r\n // map may be destroyed\r\n }\r\n }\r\n }, [map, borderColor])\r\n\r\n // If the org already has a fixed subdivision or municipality, lock them in and skip hover\r\n const orgHasLocation = !!(organization?.countrySubdivision || organization?.municipality)\r\n\r\n // When org has fixed location values, push them into currentLocation once\r\n React.useEffect(() => {\r\n if (!orgHasLocation) return\r\n mapDispatch({\r\n type: 'UPDATE_LOCATION',\r\n payload: {\r\n currentLocation: {\r\n ...(currentLocation ?? {}),\r\n countrySubdivision: organization?.countrySubdivision ?? currentLocation?.countrySubdivision,\r\n municipality: organization?.municipality ?? currentLocation?.municipality,\r\n } as any,\r\n },\r\n })\r\n // eslint-disable-next-line react-hooks/exhaustive-deps\r\n }, [orgHasLocation])\r\n\r\n React.useEffect(() => {\r\n if (orgHasLocation) return\r\n if (!(map && currentLocation)) return\r\n\r\n const currentCountrySubdivision = currentLocation.countrySubdivision || null\r\n const currentMunicipality = currentLocation.municipality || null\r\n\r\n function onMouseMove(e: MapMouseEvent) {\r\n if (!map.isStyleLoaded()) return\r\n\r\n if (debounceRef.current) clearTimeout(debounceRef.current)\r\n\r\n const eventPoint = e.point\r\n\r\n debounceRef.current = setTimeout(() => {\r\n const features = map.queryRenderedFeatures(eventPoint, {\r\n layers: ['admin-subdivisions-fill'],\r\n })\r\n\r\n // if (features.length > 0) {\r\n // console.group('[CountryLayer] hover')\r\n // features.forEach(f => console.log(f.properties))\r\n // console.groupEnd()\r\n // }\r\n\r\n let newCountrySubdivision = null\r\n\r\n if (features.length > 0) {\r\n const f = features[0]\r\n const isoCC = f.properties?.ISO_CC\r\n const isoCode = f.properties?.ISO_CODE\r\n // Build \"CA-ON\" from ISO_CC \"CA\" + ISO_CODE \"CAON\" → strip prefix to get \"ON\"\r\n if (isoCC && isoCode && isoCode.startsWith(isoCC)) {\r\n newCountrySubdivision = `${isoCC}-${isoCode.slice(isoCC.length)}`\r\n } else {\r\n newCountrySubdivision = (isoCode || f.properties?.NAME || null) as string | null\r\n }\r\n }\r\n\r\n const finalCountrySubdivision = newCountrySubdivision || currentCountrySubdivision\r\n\r\n if (finalCountrySubdivision !== currentCountrySubdivision) {\r\n mapDispatch({\r\n type: 'UPDATE_LOCATION',\r\n payload: {\r\n currentLocation: {\r\n ...currentLocation,\r\n countrySubdivision: finalCountrySubdivision,\r\n municipality: currentMunicipality,\r\n },\r\n },\r\n })\r\n\r\n const params = new URLSearchParams(searchParams.toString())\r\n params.set('countrySubdivision', finalCountrySubdivision || '')\r\n router.replace(`?${params.toString()}`, { scroll: false })\r\n }\r\n }, 300)\r\n }\r\n\r\n if (map.isStyleLoaded()) {\r\n map.on('mousemove', onMouseMove)\r\n } else {\r\n map.once('styledata', () => {\r\n map.on('mousemove', onMouseMove)\r\n })\r\n }\r\n\r\n return () => {\r\n map.off('mousemove', onMouseMove)\r\n if (debounceRef.current) clearTimeout(debounceRef.current)\r\n }\r\n }, [map, mapDispatch, currentLocation, searchParams, router])\r\n\r\n if (orgHasLocation) return null\r\n\r\n // Invisible ArcGIS subdivision polygons for hover hit-detection (only mounted when the org hasn't locked a subdivision). The visible borders come from the imperative openmaptiles layer above.\r\n return (\r\n <Source\r\n id=\"admin-subdivisions-source\"\r\n type=\"geojson\"\r\n data={subdivisionUrl}\r\n >\r\n <Layer\r\n id=\"admin-subdivisions-fill\"\r\n type=\"fill\"\r\n paint={{ 'fill-color': 'transparent', 'fill-opacity': 0 }}\r\n />\r\n </Source>\r\n )\r\n}\r\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../../../src/core/components/viewers/map/src/MapLayers/src/CountryLayer/index.tsx"],"sourcesContent":["\"use client\"\r\n\r\nimport * as React from 'react'\r\nimport { Source, Layer } from 'react-map-gl/maplibre'\r\nimport type { MapMouseEvent } from 'maplibre-gl'\r\nimport { MapContext } from '../../../../../../../store/Map/context'\r\nimport { useAppConfigContext } from '../../../../../../../store/AppConfig/context'\r\nimport { useSearchParams, useRouter } from 'next/navigation'\r\nimport { hexToRgba, buildSubdivisionUrl } from './countryLayerUtils'\r\n\r\nconst DEFAULT_BORDER_COLOR = '#73cee2'\r\n// satellite.json's openmaptiles source uses MapTiler's 'tiles/buildings' tileset which lacks the boundary + place layers. Pull both from the full openmaptiles v3 schema (same tileset blank.json uses).\r\nconst BOUNDARIES_SOURCE_ID = 'openmaptiles-boundaries'\r\n// Key is supplied by the consuming app via NEXT_PUBLIC_MAPTILER_KEY (Next inlines\r\n// it at build time). Kept out of source so @collabdt/core carries no secret.\r\nconst MAPTILER_KEY = process.env.NEXT_PUBLIC_MAPTILER_KEY\r\nconst BOUNDARIES_TILESET_URL = `https://api.maptiler.com/tiles/v3/tiles.json?key=${MAPTILER_KEY}`\r\nconst GLOBAL_LAYER_IDS = [\r\n 'global-borders-country',\r\n 'global-borders-region',\r\n 'global-labels-country',\r\n 'global-labels-state',\r\n 'global-labels-city',\r\n] as const\r\n\r\nfunction addGlobalBorderLayer(map: any, color: string) {\r\n try {\r\n if (!map.getSource(BOUNDARIES_SOURCE_ID)) {\r\n map.addSource(BOUNDARIES_SOURCE_ID, {\r\n type: 'vector',\r\n url: BOUNDARIES_TILESET_URL,\r\n })\r\n }\r\n\r\n // Country borders — visible at all zooms, slightly thinner up close where states take over.\r\n if (!map.getLayer('global-borders-country')) {\r\n map.addLayer({\r\n id: 'global-borders-country',\r\n type: 'line',\r\n source: BOUNDARIES_SOURCE_ID,\r\n 'source-layer': 'boundary',\r\n filter: ['all', ['==', 'admin_level', 2], ['==', 'maritime', 0]],\r\n layout: { 'line-cap': 'round', 'line-join': 'round' },\r\n paint: {\r\n 'line-color': color,\r\n 'line-width': { base: 1, stops: [[0, 0.8], [4, 1.4], [10, 1.4], [14, 1]] },\r\n },\r\n })\r\n }\r\n\r\n // State / province borders — fade in around zoom 3, dominate from 5+.\r\n if (!map.getLayer('global-borders-region')) {\r\n map.addLayer({\r\n id: 'global-borders-region',\r\n type: 'line',\r\n source: BOUNDARIES_SOURCE_ID,\r\n 'source-layer': 'boundary',\r\n filter: ['all', ['in', 'admin_level', 3, 4], ['==', 'maritime', 0]],\r\n minzoom: 2,\r\n layout: { 'line-cap': 'round', 'line-join': 'round' },\r\n paint: {\r\n 'line-color': color,\r\n 'line-width': { base: 1, stops: [[2, 0], [4, 0.5], [8, 1.4], [14, 2.4]] },\r\n 'line-opacity': { base: 1, stops: [[2, 0], [4, 0.7], [6, 1]] },\r\n },\r\n })\r\n }\r\n\r\n // Country labels — small/global up to zoom ~6, gone by 8.\r\n if (!map.getLayer('global-labels-country')) {\r\n map.addLayer({\r\n id: 'global-labels-country',\r\n type: 'symbol',\r\n source: BOUNDARIES_SOURCE_ID,\r\n 'source-layer': 'place',\r\n filter: ['==', 'class', 'country'],\r\n minzoom: 1,\r\n maxzoom: 8,\r\n layout: {\r\n 'text-field': ['coalesce', ['get', 'name:latin'], ['get', 'name']],\r\n 'text-font': ['Noto Sans Regular'],\r\n 'text-size': { base: 1, stops: [[1, 10], [4, 13], [6, 16]] },\r\n 'text-letter-spacing': 0.1,\r\n 'text-max-width': 8,\r\n 'text-transform': 'uppercase',\r\n },\r\n paint: {\r\n 'text-color': '#ffffff',\r\n 'text-halo-color': 'rgba(0,0,0,0.7)',\r\n 'text-halo-width': 1.2,\r\n 'text-opacity': { base: 1, stops: [[5, 1], [7, 0]] },\r\n },\r\n })\r\n }\r\n\r\n // State / province labels — cover zoom 3–8.\r\n if (!map.getLayer('global-labels-state')) {\r\n map.addLayer({\r\n id: 'global-labels-state',\r\n type: 'symbol',\r\n source: BOUNDARIES_SOURCE_ID,\r\n 'source-layer': 'place',\r\n filter: ['in', 'class', 'state', 'province'],\r\n minzoom: 3,\r\n maxzoom: 9,\r\n layout: {\r\n 'text-field': ['coalesce', ['get', 'name:latin'], ['get', 'name']],\r\n 'text-font': ['Noto Sans Regular'],\r\n 'text-size': { base: 1, stops: [[3, 9], [6, 12], [8, 14]] },\r\n 'text-letter-spacing': 0.05,\r\n 'text-max-width': 8,\r\n },\r\n paint: {\r\n 'text-color': '#e8e8e8',\r\n 'text-halo-color': 'rgba(0,0,0,0.6)',\r\n 'text-halo-width': 1,\r\n 'text-opacity': { base: 1, stops: [[3, 0], [4, 1], [8, 1], [9, 0]] },\r\n },\r\n })\r\n }\r\n\r\n // Major city labels (rank ≤ 4 = most prominent cities) — appear when zoomed in past country level.\r\n if (!map.getLayer('global-labels-city')) {\r\n map.addLayer({\r\n id: 'global-labels-city',\r\n type: 'symbol',\r\n source: BOUNDARIES_SOURCE_ID,\r\n 'source-layer': 'place',\r\n filter: ['in', 'class', 'city', 'town'],\r\n minzoom: 5,\r\n maxzoom: 13,\r\n layout: {\r\n 'text-field': ['coalesce', ['get', 'name:latin'], ['get', 'name']],\r\n 'text-font': ['Noto Sans Regular'],\r\n 'text-size': { base: 1, stops: [[5, 10], [9, 13], [12, 15]] },\r\n 'text-max-width': 8,\r\n },\r\n paint: {\r\n 'text-color': '#dcdcdc',\r\n 'text-halo-color': 'rgba(0,0,0,0.7)',\r\n 'text-halo-width': 1.1,\r\n },\r\n })\r\n }\r\n } catch {\r\n // style may be in a transitional state\r\n }\r\n}\r\n\r\nexport const CountryLayer = () => {\r\n const { state: mapState, dispatch: mapDispatch } = React.useContext(MapContext)\r\n const { map, currentLocation } = mapState.map\r\n const { state: appConfigState } = useAppConfigContext()\r\n const organization = appConfigState.appConfig.organization\r\n const searchParams = useSearchParams()\r\n const router = useRouter()\r\n const debounceRef = React.useRef<NodeJS.Timeout | null>(null)\r\n\r\n const countryCode = (organization?.country || 'CA').toUpperCase()\r\n const borderColor = hexToRgba((organization?.mainColor as string) || DEFAULT_BORDER_COLOR, 0.7)\r\n const subdivisionUrl = buildSubdivisionUrl(countryCode)\r\n\r\n // Add the global boundary line layer imperatively (vector-tile lines from the style's openmaptiles source). Re-adds on style swap so it survives basemap changes.\r\n React.useEffect(() => {\r\n if (!map) return\r\n const addNow = () => addGlobalBorderLayer(map, borderColor)\r\n if (map.isStyleLoaded()) addNow()\r\n map.on('styledata', addNow)\r\n return () => {\r\n try {\r\n map.off('styledata', addNow)\r\n for (const id of GLOBAL_LAYER_IDS) {\r\n if (map.getLayer(id)) map.removeLayer(id)\r\n }\r\n } catch {\r\n // map may be destroyed\r\n }\r\n }\r\n }, [map, borderColor])\r\n\r\n // If the org already has a fixed subdivision or municipality, lock them in and skip hover\r\n const orgHasLocation = !!(organization?.countrySubdivision || organization?.municipality)\r\n\r\n // When org has fixed location values, push them into currentLocation once\r\n React.useEffect(() => {\r\n if (!orgHasLocation) return\r\n mapDispatch({\r\n type: 'UPDATE_LOCATION',\r\n payload: {\r\n currentLocation: {\r\n ...(currentLocation ?? {}),\r\n countrySubdivision: organization?.countrySubdivision ?? currentLocation?.countrySubdivision,\r\n municipality: organization?.municipality ?? currentLocation?.municipality,\r\n } as any,\r\n },\r\n })\r\n // eslint-disable-next-line react-hooks/exhaustive-deps\r\n }, [orgHasLocation])\r\n\r\n React.useEffect(() => {\r\n if (orgHasLocation) return\r\n if (!(map && currentLocation)) return\r\n\r\n const currentCountrySubdivision = currentLocation.countrySubdivision || null\r\n const currentMunicipality = currentLocation.municipality || null\r\n\r\n function onMouseMove(e: MapMouseEvent) {\r\n if (!map.isStyleLoaded()) return\r\n\r\n if (debounceRef.current) clearTimeout(debounceRef.current)\r\n\r\n const eventPoint = e.point\r\n\r\n debounceRef.current = setTimeout(() => {\r\n const features = map.queryRenderedFeatures(eventPoint, {\r\n layers: ['admin-subdivisions-fill'],\r\n })\r\n\r\n // if (features.length > 0) {\r\n // console.group('[CountryLayer] hover')\r\n // features.forEach(f => console.log(f.properties))\r\n // console.groupEnd()\r\n // }\r\n\r\n let newCountrySubdivision = null\r\n\r\n if (features.length > 0) {\r\n const f = features[0]\r\n const isoCC = f.properties?.ISO_CC\r\n const isoCode = f.properties?.ISO_CODE\r\n // Build \"CA-ON\" from ISO_CC \"CA\" + ISO_CODE \"CAON\" → strip prefix to get \"ON\"\r\n if (isoCC && isoCode && isoCode.startsWith(isoCC)) {\r\n newCountrySubdivision = `${isoCC}-${isoCode.slice(isoCC.length)}`\r\n } else {\r\n newCountrySubdivision = (isoCode || f.properties?.NAME || null) as string | null\r\n }\r\n }\r\n\r\n const finalCountrySubdivision = newCountrySubdivision || currentCountrySubdivision\r\n\r\n if (finalCountrySubdivision !== currentCountrySubdivision) {\r\n mapDispatch({\r\n type: 'UPDATE_LOCATION',\r\n payload: {\r\n currentLocation: {\r\n ...currentLocation,\r\n countrySubdivision: finalCountrySubdivision,\r\n municipality: currentMunicipality,\r\n },\r\n },\r\n })\r\n\r\n const params = new URLSearchParams(searchParams.toString())\r\n params.set('countrySubdivision', finalCountrySubdivision || '')\r\n router.replace(`?${params.toString()}`, { scroll: false })\r\n }\r\n }, 300)\r\n }\r\n\r\n if (map.isStyleLoaded()) {\r\n map.on('mousemove', onMouseMove)\r\n } else {\r\n map.once('styledata', () => {\r\n map.on('mousemove', onMouseMove)\r\n })\r\n }\r\n\r\n return () => {\r\n map.off('mousemove', onMouseMove)\r\n if (debounceRef.current) clearTimeout(debounceRef.current)\r\n }\r\n }, [map, mapDispatch, currentLocation, searchParams, router])\r\n\r\n if (orgHasLocation) return null\r\n\r\n // Invisible ArcGIS subdivision polygons for hover hit-detection (only mounted when the org hasn't locked a subdivision). The visible borders come from the imperative openmaptiles layer above.\r\n return (\r\n <Source\r\n id=\"admin-subdivisions-source\"\r\n type=\"geojson\"\r\n data={subdivisionUrl}\r\n >\r\n <Layer\r\n id=\"admin-subdivisions-fill\"\r\n type=\"fill\"\r\n paint={{ 'fill-color': 'transparent', 'fill-opacity': 0 }}\r\n />\r\n </Source>\r\n )\r\n}\r\n"],"mappings":"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","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FileModelLayer.d.ts","sourceRoot":"","sources":["../../../../../../../../../../src/core/components/viewers/map/src/MapLayers/src/FileLayer/FileModelLayer/FileModelLayer.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAE9B,OAAO,EAAE,MAAM,EAAE,MAAM,uCAAuC,CAAA;AAQ9D,UAAU,mBAAmB;IAC3B,gBAAgB,CAAC,EAAE,KAAK,CAAC,gBAAgB,CAAC,MAAM,CAAC,MAAM,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC,CAAA;IACvF,kBAAkB,CAAC,EAAE,KAAK,CAAC,gBAAgB,CAAC,MAAM,GAAG,IAAI,CAAC,CAAA;IAC1D,gBAAgB,CAAC,EAAE,KAAK,CAAC,gBAAgB,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;IACjE,iBAAiB,CAAC,EAAE,KAAK,CAAC,gBAAgB,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;IAClE,+DAA+D;IAC/D,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;CACzE;AAED,eAAO,MAAM,cAAc,GAAI,+FAM5B,mBAAmB,
|
|
1
|
+
{"version":3,"file":"FileModelLayer.d.ts","sourceRoot":"","sources":["../../../../../../../../../../src/core/components/viewers/map/src/MapLayers/src/FileLayer/FileModelLayer/FileModelLayer.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAE9B,OAAO,EAAE,MAAM,EAAE,MAAM,uCAAuC,CAAA;AAQ9D,UAAU,mBAAmB;IAC3B,gBAAgB,CAAC,EAAE,KAAK,CAAC,gBAAgB,CAAC,MAAM,CAAC,MAAM,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC,CAAA;IACvF,kBAAkB,CAAC,EAAE,KAAK,CAAC,gBAAgB,CAAC,MAAM,GAAG,IAAI,CAAC,CAAA;IAC1D,gBAAgB,CAAC,EAAE,KAAK,CAAC,gBAAgB,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;IACjE,iBAAiB,CAAC,EAAE,KAAK,CAAC,gBAAgB,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;IAClE,+DAA+D;IAC/D,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;CACzE;AAED,eAAO,MAAM,cAAc,GAAI,+FAM5B,mBAAmB,QAoIrB,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../../../../src/core/components/viewers/map/src/MapLayers/src/FileLayer/FileModelLayer/FileModelLayer.tsx"],"sourcesContent":["\"use client\"\r\n\r\nimport * as THREE from 'three'\r\nimport { MapContext, FilesContext } from '../../../../../../../../store'\r\n\r\nimport * as React from 'react'\r\nimport { CustomModelLayer } from '../utils/CustomModelLayer'\r\nimport { DbFile } from '../../../../../../../../types/dbTypes'\r\n\r\ntype LoadedModelFiles = {\r\n file: DbFile\r\n cleanUpFunction: () => void\r\n hitTest: (ndcX: number, ndcY: number) => boolean\r\n}\r\n\r\ninterface FileModelLayerProps {\r\n tempPositionsRef?: React.MutableRefObject<Record<string, { lat: number; lng: number }>>\r\n editingFileNameRef?: React.MutableRefObject<string | null>\r\n tempRotationsRef?: React.MutableRefObject<Record<string, number>>\r\n tempElevationsRef?: React.MutableRefObject<Record<string, number>>\r\n /** Called when the user right-clicks on a rendered 3D mesh. */\r\n onContextMenu?: (file: DbFile, clientX: number, clientY: number) => void\r\n}\r\n\r\nexport const FileModelLayer = ({\r\n tempPositionsRef,\r\n editingFileNameRef,\r\n tempRotationsRef,\r\n tempElevationsRef,\r\n onContextMenu,\r\n}: FileModelLayerProps) => {\r\n const { state: mapState } = React.useContext(MapContext)\r\n const { map } = mapState.map\r\n const { state: fileState } = React.useContext(FilesContext)\r\n const { files: globalFileState, mapFileIds } = fileState.files\r\n\r\n const is3DModelFile = (extension?: string | null): boolean => {\r\n if (!extension) return false\r\n const ext = extension.toLowerCase()\r\n return ['glb', 'gltf', 'fbx', 'obj', 'collada'].includes(ext)\r\n }\r\n\r\n const mapFiles = React.useMemo(\r\n () => globalFileState.filter(file => is3DModelFile(file.extension) && mapFileIds.includes(file.id) && !!file.url),\r\n [globalFileState, mapFileIds]\r\n )\r\n\r\n const [loadedModels, setLoadedModels] = React.useState<LoadedModelFiles[]>([])\r\n const rendererRef = React.useRef<THREE.WebGLRenderer | null>(null)\r\n\r\n // Stable refs so the canvas listener never needs to be re-registered\r\n const loadedModelsRef = React.useRef<LoadedModelFiles[]>([])\r\n const onContextMenuRef = React.useRef(onContextMenu)\r\n loadedModelsRef.current = loadedModels\r\n onContextMenuRef.current = onContextMenu\r\n\r\n // ── Layer management ───────────────────────────────────────────────────────\r\n React.useEffect(() => {\r\n if (!map) return\r\n if (!rendererRef.current) {\r\n rendererRef.current = new THREE.WebGLRenderer({\r\n canvas: map.getCanvas(),\r\n context: map.getCanvas().getContext('webgl') as WebGLRenderingContext,\r\n antialias: true,\r\n })\r\n }\r\n const currentModelFiles = mapFiles.filter(file => {\r\n const ext = file.extension?.toLowerCase()\r\n return ext === 'glb' || ext === 'gltf' || ext === 'fbx' || ext === 'obj' || ext === 'collada'\r\n })\r\n const loadedIds = new Set(loadedModels.map(model => model.file.id))\r\n const currentFileIds = new Set(currentModelFiles.map(f => f.id))\r\n\r\n const modelsToRemove = loadedModels.filter(model => !currentFileIds.has(model.file.id))\r\n\r\n const modelsWithChangedCoords = loadedModels.filter(model => {\r\n if (!currentFileIds.has(model.file.id)) return false\r\n const updated = currentModelFiles.find(f => f.id === model.file.id)\r\n return updated && (\r\n updated.lat !== model.file.lat ||\r\n updated.lng !== model.file.lng ||\r\n updated.rotation !== model.file.rotation ||\r\n updated.elevation !== model.file.elevation\r\n )\r\n })\r\n\r\n for (const model of [...modelsToRemove, ...modelsWithChangedCoords]) {\r\n model.cleanUpFunction()\r\n }\r\n\r\n const staleIds = new Set(modelsWithChangedCoords.map(m => m.file.id))\r\n\r\n const filesNotLoaded = currentModelFiles.filter(\r\n file => !loadedIds.has(file.id) || staleIds.has(file.id)\r\n )\r\n\r\n const newLoadedModels = loadedModels\r\n .filter(model => currentFileIds.has(model.file.id) && !staleIds.has(model.file.id))\r\n\r\n for (const file of filesNotLoaded) {\r\n const { cleanup, hitTest } = CustomModelLayer(\r\n file,\r\n map,\r\n rendererRef.current,\r\n tempPositionsRef,\r\n editingFileNameRef,\r\n tempRotationsRef,\r\n tempElevationsRef,\r\n )\r\n newLoadedModels.push({ file, cleanUpFunction: cleanup, hitTest })\r\n }\r\n\r\n setLoadedModels(newLoadedModels)\r\n }, [mapFiles, map])\r\n\r\n // ── Canvas right-click → raycasting ───────────────────────────────────────\r\n React.useEffect(() => {\r\n if (!map) return\r\n const canvas = map.getCanvas()\r\n\r\n const handleContextMenu = (e: MouseEvent) => {\r\n // Only handle clicks that land directly on the WebGL canvas (not on HTML\r\n // marker overlays — those have their own onContextMenu handlers)\r\n if (e.target !== canvas) return\r\n\r\n const models = loadedModelsRef.current\r\n if (models.length === 0) return\r\n\r\n const rect = canvas.getBoundingClientRect()\r\n const ndcX = ((e.clientX - rect.left) / rect.width) * 2 - 1\r\n const ndcY = -((e.clientY - rect.top) / rect.height) * 2 + 1\r\n\r\n for (const model of models) {\r\n if (model.hitTest(ndcX, ndcY)) {\r\n e.preventDefault()\r\n e.stopPropagation()\r\n onContextMenuRef.current?.(model.file, e.clientX, e.clientY)\r\n break\r\n }\r\n }\r\n }\r\n\r\n // Use capture phase so we see the event before MapLibre's own handlers\r\n canvas.addEventListener('contextmenu', handleContextMenu, true)\r\n return () => canvas.removeEventListener('contextmenu', handleContextMenu, true)\r\n }, [map])\r\n\r\n //
|
|
1
|
+
{"version":3,"sources":["../../../../../../../../../../src/core/components/viewers/map/src/MapLayers/src/FileLayer/FileModelLayer/FileModelLayer.tsx"],"sourcesContent":["\"use client\"\r\n\r\nimport * as THREE from 'three'\r\nimport { MapContext, FilesContext } from '../../../../../../../../store'\r\n\r\nimport * as React from 'react'\r\nimport { CustomModelLayer } from '../utils/CustomModelLayer'\r\nimport { DbFile } from '../../../../../../../../types/dbTypes'\r\n\r\ntype LoadedModelFiles = {\r\n file: DbFile\r\n cleanUpFunction: () => void\r\n hitTest: (ndcX: number, ndcY: number) => boolean\r\n}\r\n\r\ninterface FileModelLayerProps {\r\n tempPositionsRef?: React.MutableRefObject<Record<string, { lat: number; lng: number }>>\r\n editingFileNameRef?: React.MutableRefObject<string | null>\r\n tempRotationsRef?: React.MutableRefObject<Record<string, number>>\r\n tempElevationsRef?: React.MutableRefObject<Record<string, number>>\r\n /** Called when the user right-clicks on a rendered 3D mesh. */\r\n onContextMenu?: (file: DbFile, clientX: number, clientY: number) => void\r\n}\r\n\r\nexport const FileModelLayer = ({\r\n tempPositionsRef,\r\n editingFileNameRef,\r\n tempRotationsRef,\r\n tempElevationsRef,\r\n onContextMenu,\r\n}: FileModelLayerProps) => {\r\n const { state: mapState } = React.useContext(MapContext)\r\n const { map } = mapState.map\r\n const { state: fileState } = React.useContext(FilesContext)\r\n const { files: globalFileState, mapFileIds } = fileState.files\r\n\r\n const is3DModelFile = (extension?: string | null): boolean => {\r\n if (!extension) return false\r\n const ext = extension.toLowerCase()\r\n return ['glb', 'gltf', 'fbx', 'obj', 'collada'].includes(ext)\r\n }\r\n\r\n const mapFiles = React.useMemo(\r\n () => globalFileState.filter(file => is3DModelFile(file.extension) && mapFileIds.includes(file.id) && !!file.url),\r\n [globalFileState, mapFileIds]\r\n )\r\n\r\n const [loadedModels, setLoadedModels] = React.useState<LoadedModelFiles[]>([])\r\n const rendererRef = React.useRef<THREE.WebGLRenderer | null>(null)\r\n\r\n // Stable refs so the canvas listener never needs to be re-registered\r\n const loadedModelsRef = React.useRef<LoadedModelFiles[]>([])\r\n const onContextMenuRef = React.useRef(onContextMenu)\r\n loadedModelsRef.current = loadedModels\r\n onContextMenuRef.current = onContextMenu\r\n\r\n // ── Layer management ───────────────────────────────────────────────────────\r\n React.useEffect(() => {\r\n if (!map) return\r\n if (!rendererRef.current) {\r\n rendererRef.current = new THREE.WebGLRenderer({\r\n canvas: map.getCanvas(),\r\n context: map.getCanvas().getContext('webgl') as WebGLRenderingContext,\r\n antialias: true,\r\n })\r\n }\r\n const currentModelFiles = mapFiles.filter(file => {\r\n const ext = file.extension?.toLowerCase()\r\n return ext === 'glb' || ext === 'gltf' || ext === 'fbx' || ext === 'obj' || ext === 'collada'\r\n })\r\n const loadedIds = new Set(loadedModels.map(model => model.file.id))\r\n const currentFileIds = new Set(currentModelFiles.map(f => f.id))\r\n\r\n const modelsToRemove = loadedModels.filter(model => !currentFileIds.has(model.file.id))\r\n\r\n const modelsWithChangedCoords = loadedModels.filter(model => {\r\n if (!currentFileIds.has(model.file.id)) return false\r\n const updated = currentModelFiles.find(f => f.id === model.file.id)\r\n return updated && (\r\n updated.lat !== model.file.lat ||\r\n updated.lng !== model.file.lng ||\r\n updated.rotation !== model.file.rotation ||\r\n updated.elevation !== model.file.elevation\r\n )\r\n })\r\n\r\n for (const model of [...modelsToRemove, ...modelsWithChangedCoords]) {\r\n model.cleanUpFunction()\r\n }\r\n\r\n const staleIds = new Set(modelsWithChangedCoords.map(m => m.file.id))\r\n\r\n const filesNotLoaded = currentModelFiles.filter(\r\n file => !loadedIds.has(file.id) || staleIds.has(file.id)\r\n )\r\n\r\n const newLoadedModels = loadedModels\r\n .filter(model => currentFileIds.has(model.file.id) && !staleIds.has(model.file.id))\r\n\r\n for (const file of filesNotLoaded) {\r\n const { cleanup, hitTest } = CustomModelLayer(\r\n file,\r\n map,\r\n rendererRef.current,\r\n tempPositionsRef,\r\n editingFileNameRef,\r\n tempRotationsRef,\r\n tempElevationsRef,\r\n )\r\n newLoadedModels.push({ file, cleanUpFunction: cleanup, hitTest })\r\n }\r\n\r\n setLoadedModels(newLoadedModels)\r\n }, [mapFiles, map])\r\n\r\n // ── Canvas right-click → raycasting ───────────────────────────────────────\r\n React.useEffect(() => {\r\n if (!map) return\r\n const canvas = map.getCanvas()\r\n\r\n const handleContextMenu = (e: MouseEvent) => {\r\n // Only handle clicks that land directly on the WebGL canvas (not on HTML\r\n // marker overlays — those have their own onContextMenu handlers)\r\n if (e.target !== canvas) return\r\n\r\n const models = loadedModelsRef.current\r\n if (models.length === 0) return\r\n\r\n const rect = canvas.getBoundingClientRect()\r\n const ndcX = ((e.clientX - rect.left) / rect.width) * 2 - 1\r\n const ndcY = -((e.clientY - rect.top) / rect.height) * 2 + 1\r\n\r\n for (const model of models) {\r\n if (model.hitTest(ndcX, ndcY)) {\r\n e.preventDefault()\r\n e.stopPropagation()\r\n onContextMenuRef.current?.(model.file, e.clientX, e.clientY)\r\n break\r\n }\r\n }\r\n }\r\n\r\n // Use capture phase so we see the event before MapLibre's own handlers\r\n canvas.addEventListener('contextmenu', handleContextMenu, true)\r\n return () => canvas.removeEventListener('contextmenu', handleContextMenu, true)\r\n }, [map])\r\n\r\n // Teardown: remove model layers + dispose the shared renderer on unmount / map\r\n // change, else the WebGLRenderer + its scenes leak across viewer switches.\r\n React.useEffect(() => {\r\n return () => {\r\n for (const model of loadedModelsRef.current) {\r\n try { model.cleanUpFunction() } catch { /* layer or map may already be gone */ }\r\n }\r\n if (rendererRef.current) {\r\n rendererRef.current.dispose()\r\n rendererRef.current = null\r\n }\r\n }\r\n }, [map])\r\n\r\n return null\r\n}\r\n"],"mappings":"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","names":[]}
|
package/dist/core/components/viewers/map/src/MapLayers/src/FileLayer/utils/CustomModelLayer.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CustomModelLayer.d.ts","sourceRoot":"","sources":["../../../../../../../../../../src/core/components/viewers/map/src/MapLayers/src/FileLayer/utils/CustomModelLayer.ts"],"names":[],"mappings":"AAAA,OAAO,EAA8C,KAAK,GAAG,EAAE,MAAM,aAAa,CAAA;AAClF,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAE9B,OAAO,EAAE,MAAM,EAAE,MAAM,uCAAuC,CAAA;AAG9D,KAAK,gBAAgB,GAAG,KAAK,CAAC,gBAAgB,CAAC,MAAM,CAAC,MAAM,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC,CAAA;AAC5F,KAAK,gBAAgB,GAAG,KAAK,CAAC,gBAAgB,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;AACtE,KAAK,iBAAiB,GAAG,KAAK,CAAC,gBAAgB,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;AACvE,KAAK,kBAAkB,GAAG,KAAK,CAAC,gBAAgB,CAAC,MAAM,GAAG,IAAI,CAAC,CAAA;AA8B/D,eAAO,MAAM,gBAAgB,GAC3B,WAAW,MAAM,EACjB,KAAK,GAAG,EACR,UAAU,KAAK,CAAC,aAAa,EAC7B,mBAAmB,gBAAgB,EACnC,qBAAqB,kBAAkB,EACvC,mBAAmB,gBAAgB,EACnC,oBAAoB,iBAAiB,KACpC;IAAE,OAAO,EAAE,MAAM,IAAI,CAAC;IAAC,MAAM,EAAE,MAAM,IAAI,CAAC;IAAC,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"CustomModelLayer.d.ts","sourceRoot":"","sources":["../../../../../../../../../../src/core/components/viewers/map/src/MapLayers/src/FileLayer/utils/CustomModelLayer.ts"],"names":[],"mappings":"AAAA,OAAO,EAA8C,KAAK,GAAG,EAAE,MAAM,aAAa,CAAA;AAClF,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAE9B,OAAO,EAAE,MAAM,EAAE,MAAM,uCAAuC,CAAA;AAG9D,KAAK,gBAAgB,GAAG,KAAK,CAAC,gBAAgB,CAAC,MAAM,CAAC,MAAM,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC,CAAA;AAC5F,KAAK,gBAAgB,GAAG,KAAK,CAAC,gBAAgB,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;AACtE,KAAK,iBAAiB,GAAG,KAAK,CAAC,gBAAgB,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;AACvE,KAAK,kBAAkB,GAAG,KAAK,CAAC,gBAAgB,CAAC,MAAM,GAAG,IAAI,CAAC,CAAA;AA8B/D,eAAO,MAAM,gBAAgB,GAC3B,WAAW,MAAM,EACjB,KAAK,GAAG,EACR,UAAU,KAAK,CAAC,aAAa,EAC7B,mBAAmB,gBAAgB,EACnC,qBAAqB,kBAAkB,EACvC,mBAAmB,gBAAgB,EACnC,oBAAoB,iBAAiB,KACpC;IAAE,OAAO,EAAE,MAAM,IAAI,CAAC;IAAC,MAAM,EAAE,MAAM,IAAI,CAAC;IAAC,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAA;CAgQ7F,CAAA"}
|
package/dist/core/components/viewers/map/src/MapLayers/src/FileLayer/utils/CustomModelLayer.js
CHANGED
|
@@ -44,6 +44,18 @@ const CustomModelLayer = (modelFile, map, renderer, tempPositionsRef, editingFil
|
|
|
44
44
|
let disposed = false;
|
|
45
45
|
const _m = new THREE.Matrix4();
|
|
46
46
|
const _l = new THREE.Matrix4();
|
|
47
|
+
let cachedTerrainElev = 0;
|
|
48
|
+
let lastMoveTime = performance.now();
|
|
49
|
+
const SETTLE_MS = 1e3;
|
|
50
|
+
let onMapMove;
|
|
51
|
+
let onMapMoveEnd;
|
|
52
|
+
const recomputeTerrainElev = () => {
|
|
53
|
+
const { lng, lat } = resolveModelCoordinates(modelFile);
|
|
54
|
+
if (!(lng === 0 && lat === 0)) {
|
|
55
|
+
const e = map.queryTerrainElevation([lng, lat]);
|
|
56
|
+
if (e !== null && e !== void 0) cachedTerrainElev = e;
|
|
57
|
+
}
|
|
58
|
+
};
|
|
47
59
|
return {
|
|
48
60
|
id: `model-${modelFile.id}`,
|
|
49
61
|
type: "custom",
|
|
@@ -86,12 +98,25 @@ const CustomModelLayer = (modelFile, map, renderer, tempPositionsRef, editingFil
|
|
|
86
98
|
}
|
|
87
99
|
}
|
|
88
100
|
scene.add(gltf.scene);
|
|
101
|
+
lastMoveTime = performance.now();
|
|
102
|
+
recomputeTerrainElev();
|
|
103
|
+
map2.triggerRepaint();
|
|
89
104
|
},
|
|
90
105
|
void 0,
|
|
91
106
|
(error) => {
|
|
92
107
|
console.error("Error loading model:", error);
|
|
93
108
|
}
|
|
94
109
|
);
|
|
110
|
+
onMapMove = () => {
|
|
111
|
+
lastMoveTime = performance.now();
|
|
112
|
+
};
|
|
113
|
+
onMapMoveEnd = () => {
|
|
114
|
+
lastMoveTime = performance.now();
|
|
115
|
+
recomputeTerrainElev();
|
|
116
|
+
map2.triggerRepaint();
|
|
117
|
+
};
|
|
118
|
+
map2.on("move", onMapMove);
|
|
119
|
+
map2.on("moveend", onMapMoveEnd);
|
|
95
120
|
},
|
|
96
121
|
render(gl, args) {
|
|
97
122
|
var _a2, _b, _c;
|
|
@@ -119,7 +144,7 @@ const CustomModelLayer = (modelFile, map, renderer, tempPositionsRef, editingFil
|
|
|
119
144
|
}
|
|
120
145
|
const fileElevation = isEditing && (tempElevationsRef == null ? void 0 : tempElevationsRef.current[modelFile.name]) !== void 0 ? tempElevationsRef.current[modelFile.name] : (_b = modelFile.elevation) != null ? _b : 0;
|
|
121
146
|
const modelOrigin = [lng, lat];
|
|
122
|
-
const terrainAltitude = (_c = map.queryTerrainElevation([lng, lat])) != null ? _c :
|
|
147
|
+
const terrainAltitude = isEditing ? (_c = map.queryTerrainElevation([lng, lat])) != null ? _c : cachedTerrainElev : cachedTerrainElev;
|
|
123
148
|
const altitude = terrainAltitude + fileElevation;
|
|
124
149
|
const modelMatrix = map.transform.getMatrixForModel(modelOrigin, altitude);
|
|
125
150
|
_m.fromArray(args.defaultProjectionData.mainMatrix);
|
|
@@ -130,10 +155,14 @@ const CustomModelLayer = (modelFile, map, renderer, tempPositionsRef, editingFil
|
|
|
130
155
|
}
|
|
131
156
|
this.renderer.resetState();
|
|
132
157
|
this.renderer.render(this.scene, this.camera);
|
|
133
|
-
|
|
158
|
+
if (this.mixer || isEditing || performance.now() - lastMoveTime < SETTLE_MS) {
|
|
159
|
+
map.triggerRepaint();
|
|
160
|
+
}
|
|
134
161
|
},
|
|
135
162
|
onRemove() {
|
|
136
163
|
disposed = true;
|
|
164
|
+
if (onMapMove) map.off("move", onMapMove);
|
|
165
|
+
if (onMapMoveEnd) map.off("moveend", onMapMoveEnd);
|
|
137
166
|
if (this.mixer) {
|
|
138
167
|
;
|
|
139
168
|
this.mixer.stopAllAction();
|
package/dist/core/components/viewers/map/src/MapLayers/src/FileLayer/utils/CustomModelLayer.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../../../../src/core/components/viewers/map/src/MapLayers/src/FileLayer/utils/CustomModelLayer.ts"],"sourcesContent":["import { type CustomLayerInterface, type LngLatLike, type Map } from 'maplibre-gl'\r\nimport * as THREE from 'three'\r\nimport { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'\r\nimport { DbFile } from '../../../../../../../../types/dbTypes'\r\nimport { disposeThreeScene } from '../../disposeThreeScene'\r\n\r\ntype TempPositionsRef = React.MutableRefObject<Record<string, { lat: number; lng: number }>>\r\ntype TempRotationsRef = React.MutableRefObject<Record<string, number>>\r\ntype TempElevationsRef = React.MutableRefObject<Record<string, number>>\r\ntype EditingFileNameRef = React.MutableRefObject<string | null>\r\n\r\nfunction resolveModelCoordinates(file: DbFile): { lng: number, lat: number } {\r\n if (typeof file.lng === 'number' && typeof file.lat === 'number') {\r\n return { lng: file.lng, lat: file.lat }\r\n }\r\n\r\n const rawPosition = file.position as unknown\r\n if (rawPosition && typeof rawPosition === 'object') {\r\n const pos = rawPosition as { lng?: number, lat?: number }\r\n if (typeof pos.lng === 'number' && typeof pos.lat === 'number') {\r\n return { lng: pos.lng, lat: pos.lat }\r\n }\r\n }\r\n\r\n if (typeof rawPosition === 'string') {\r\n try {\r\n const parsed = JSON.parse(rawPosition) as { lng?: number, lat?: number }\r\n if (typeof parsed.lng === 'number' && typeof parsed.lat === 'number') {\r\n return { lng: parsed.lng, lat: parsed.lat }\r\n }\r\n }\r\n catch {\r\n // Ignore malformed legacy position payloads.\r\n }\r\n }\r\n\r\n return { lng: 0, lat: 0 }\r\n}\r\n\r\nexport const CustomModelLayer = (\r\n modelFile: DbFile,\r\n map: Map,\r\n renderer: THREE.WebGLRenderer,\r\n tempPositionsRef?: TempPositionsRef,\r\n editingFileNameRef?: EditingFileNameRef,\r\n tempRotationsRef?: TempRotationsRef,\r\n tempElevationsRef?: TempElevationsRef,\r\n): { cleanup: () => void, remove: () => void, hitTest: (ndcX: number, ndcY: number) => boolean } => {\r\n let components = null\r\n let customLayer: CustomLayerInterface | null = null\r\n\r\n if (!map || !modelFile) {\r\n return { cleanup: () => {}, remove: () => {}, hitTest: () => false }\r\n }\r\n\r\n // Refs into the layer's camera and scene so raycasting can read the last\r\n // rendered frame's transform without entering the render loop.\r\n let cameraRef: THREE.Camera | null = null\r\n let sceneRef: THREE.Scene | null = null\r\n\r\n // Reused temps for hitTest raycasting — avoid per-call allocation (audit B3).\r\n const _hitInv = new THREE.Matrix4()\r\n const _hitNear = new THREE.Vector3()\r\n const _hitFar = new THREE.Vector3()\r\n const _hitDir = new THREE.Vector3()\r\n const _raycaster = new THREE.Raycaster()\r\n\r\n const createCustomLayer = (): CustomLayerInterface => {\r\n // Track last applied rotation so we can apply delta increments (same as BimLayer)\r\n let lastAppliedRotation = modelFile.rotation ?? 0\r\n // Layer-removed-mid-load guard (audit B4) + reused per-frame matrices (audit B2)\r\n let disposed = false\r\n const _m = new THREE.Matrix4()\r\n const _l = new THREE.Matrix4()\r\n\r\n return {\r\n id: `model-${modelFile.id}`,\r\n type: 'custom',\r\n renderingMode: '3d',\r\n onAdd(map, gl) {\r\n this.camera = new THREE.Camera()\r\n this.scene = new THREE.Scene()\r\n this.renderer = renderer\r\n\r\n // Expose to closure for raycasting\r\n cameraRef = this.camera as THREE.Camera\r\n sceneRef = this.scene as THREE.Scene\r\n\r\n const scene = this.scene as THREE.Scene\r\n\r\n // Apply initial rotation\r\n if (lastAppliedRotation !== 0) {\r\n scene.rotateY(lastAppliedRotation * (Math.PI / 180))\r\n }\r\n\r\n // Lighting to match OBC library setup\r\n scene.background = null\r\n scene.fog = new THREE.Fog(0x20_29_32, 10, 200)\r\n scene.add(new THREE.AmbientLight(0xFF_FF_FF, 1))\r\n scene.add(new THREE.HemisphereLight(0xFF_FF_BB, 0x08_08_20, 0.5))\r\n\r\n const sun = new THREE.DirectionalLight(0xFF_FF_FF, 1.5)\r\n sun.position.set(0, -70, 100).normalize()\r\n sun.castShadow = true\r\n sun.shadow.mapSize.set(2048, 2048)\r\n scene.add(sun)\r\n\r\n const fill = new THREE.DirectionalLight(0xFF_FF_FF, 1)\r\n fill.position.set(0, 70, 100).normalize()\r\n scene.add(fill)\r\n\r\n this.renderer.toneMapping = THREE.ACESFilmicToneMapping\r\n this.renderer.autoClear = false\r\n this.clock = new THREE.Clock()\r\n\r\n const loader = new GLTFLoader()\r\n loader.load(\r\n modelFile.url!,\r\n (gltf) => {\r\n // Layer was removed before the async load resolved — don't attach to a\r\n // dead scene; let the gltf be GC'd (no GPU upload happened yet). (audit B4)\r\n if (disposed) return\r\n gltf.scene.scale.setScalar(1)\r\n\r\n if (gltf.animations && gltf.animations.length > 0) {\r\n this.mixer = new THREE.AnimationMixer(gltf.scene)\r\n for (const clip of gltf.animations) {\r\n this.mixer!.clipAction(clip).play()\r\n }\r\n }\r\n\r\n scene.add(gltf.scene)\r\n },\r\n undefined,\r\n (error) => { console.error('Error loading model:', error) },\r\n )\r\n },\r\n\r\n render(gl, args) {\r\n if (map.getZoom() < 15.5) return\r\n\r\n const isEditing = editingFileNameRef?.current === modelFile.name\r\n\r\n // ── Position ──────────────────────────────────────────────────────────\r\n let lng: number\r\n let lat: number\r\n if (isEditing && tempPositionsRef?.current[modelFile.name]) {\r\n const tp = tempPositionsRef.current[modelFile.name]\r\n lng = tp.lng\r\n lat = tp.lat\r\n } else {\r\n const coords = resolveModelCoordinates(modelFile)\r\n lng = coords.lng\r\n lat = coords.lat\r\n }\r\n\r\n if (lng === 0 && lat === 0) return\r\n\r\n // ── Rotation (delta-increment, same pattern as BimLayer) ──────────────\r\n const targetRotation = (isEditing && tempRotationsRef?.current[modelFile.name] !== undefined)\r\n ? tempRotationsRef.current[modelFile.name]\r\n : (modelFile.rotation ?? 0)\r\n\r\n if (targetRotation !== lastAppliedRotation) {\r\n ;(this.scene as THREE.Scene).rotateY(\r\n (targetRotation - lastAppliedRotation) * (Math.PI / 180)\r\n )\r\n lastAppliedRotation = targetRotation\r\n }\r\n\r\n // ── Elevation ─────────────────────────────────────────────────────────\r\n const fileElevation = (isEditing && tempElevationsRef?.current[modelFile.name] !== undefined)\r\n ? tempElevationsRef.current[modelFile.name]\r\n : (modelFile.elevation ?? 0)\r\n\r\n const modelOrigin = [lng, lat] as LngLatLike\r\n const terrainAltitude = map.queryTerrainElevation([lng, lat]) ?? 0\r\n const altitude = terrainAltitude + fileElevation\r\n\r\n const modelMatrix = map.transform.getMatrixForModel(modelOrigin, altitude)\r\n // Reuse temps and write into the camera's own matrix — no per-frame\r\n // allocation. The previous `.scale(1,1,1)` was an identity no-op. (audit B2)\r\n _m.fromArray(args.defaultProjectionData.mainMatrix)\r\n _l.fromArray(modelMatrix)\r\n this.camera.projectionMatrix.multiplyMatrices(_m, _l)\r\n\r\n if (this.mixer) {\r\n this.mixer.update((this.clock as THREE.Clock).getDelta())\r\n }\r\n\r\n this.renderer.resetState()\r\n this.renderer.render(this.scene, this.camera)\r\n\r\n map.triggerRepaint()\r\n },\r\n\r\n onRemove() {\r\n disposed = true\r\n // Stop + release the animation mixer so it isn't left running/holding the\r\n // scene after removal (audit B5).\r\n if (this.mixer) {\r\n ;(this.mixer as THREE.AnimationMixer).stopAllAction()\r\n this.mixer = null\r\n }\r\n if (this.scene) disposeThreeScene(this.scene as THREE.Scene)\r\n cameraRef = null\r\n sceneRef = null\r\n this.renderer = null\r\n this.camera = null\r\n this.scene = null\r\n },\r\n }\r\n }\r\n\r\n /**\r\n * Raycast against this model using the last rendered frame's camera matrix.\r\n *\r\n * The camera.projectionMatrix = VP * M (view-projection × model-to-world),\r\n * so its inverse transforms clip-space → model-space, which is exactly the\r\n * coordinate system the Three.js scene lives in. We manually unproject near/far\r\n * clip-space points through that inverse to build the ray.\r\n */\r\n const hitTest = (ndcX: number, ndcY: number): boolean => {\r\n if (!cameraRef || !sceneRef) return false\r\n\r\n // Invert the combined VP*M matrix to go clip-space → model-space.\r\n // Reuses module-scope temps + a singleton raycaster (audit B3).\r\n _hitInv.copy(cameraRef.projectionMatrix).invert()\r\n\r\n // Unproject near and far clip-space points into model space\r\n _hitNear.set(ndcX, ndcY, -1).applyMatrix4(_hitInv)\r\n _hitFar.set(ndcX, ndcY, 1).applyMatrix4(_hitInv)\r\n\r\n _hitDir.copy(_hitFar).sub(_hitNear).normalize()\r\n\r\n _raycaster.set(_hitNear, _hitDir)\r\n\r\n const intersects = _raycaster.intersectObjects(sceneRef.children, true)\r\n return intersects.length > 0\r\n }\r\n\r\n customLayer = createCustomLayer()\r\n\r\n if (!map.getLayer(customLayer.id)) {\r\n map.addLayer(customLayer)\r\n }\r\n\r\n const removeLayer = () => {\r\n if (customLayer && map.getLayer(customLayer.id)) {\r\n map.removeLayer(customLayer.id)\r\n }\r\n }\r\n\r\n const cleanup = () => {\r\n removeLayer()\r\n components = null\r\n customLayer = null\r\n }\r\n\r\n return { cleanup, remove: removeLayer, hitTest }\r\n}\r\n"],"mappings":"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","names":["map","_a"]}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../../../../src/core/components/viewers/map/src/MapLayers/src/FileLayer/utils/CustomModelLayer.ts"],"sourcesContent":["import { type CustomLayerInterface, type LngLatLike, type Map } from 'maplibre-gl'\r\nimport * as THREE from 'three'\r\nimport { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'\r\nimport { DbFile } from '../../../../../../../../types/dbTypes'\r\nimport { disposeThreeScene } from '../../disposeThreeScene'\r\n\r\ntype TempPositionsRef = React.MutableRefObject<Record<string, { lat: number; lng: number }>>\r\ntype TempRotationsRef = React.MutableRefObject<Record<string, number>>\r\ntype TempElevationsRef = React.MutableRefObject<Record<string, number>>\r\ntype EditingFileNameRef = React.MutableRefObject<string | null>\r\n\r\nfunction resolveModelCoordinates(file: DbFile): { lng: number, lat: number } {\r\n if (typeof file.lng === 'number' && typeof file.lat === 'number') {\r\n return { lng: file.lng, lat: file.lat }\r\n }\r\n\r\n const rawPosition = file.position as unknown\r\n if (rawPosition && typeof rawPosition === 'object') {\r\n const pos = rawPosition as { lng?: number, lat?: number }\r\n if (typeof pos.lng === 'number' && typeof pos.lat === 'number') {\r\n return { lng: pos.lng, lat: pos.lat }\r\n }\r\n }\r\n\r\n if (typeof rawPosition === 'string') {\r\n try {\r\n const parsed = JSON.parse(rawPosition) as { lng?: number, lat?: number }\r\n if (typeof parsed.lng === 'number' && typeof parsed.lat === 'number') {\r\n return { lng: parsed.lng, lat: parsed.lat }\r\n }\r\n }\r\n catch {\r\n // Ignore malformed legacy position payloads.\r\n }\r\n }\r\n\r\n return { lng: 0, lat: 0 }\r\n}\r\n\r\nexport const CustomModelLayer = (\r\n modelFile: DbFile,\r\n map: Map,\r\n renderer: THREE.WebGLRenderer,\r\n tempPositionsRef?: TempPositionsRef,\r\n editingFileNameRef?: EditingFileNameRef,\r\n tempRotationsRef?: TempRotationsRef,\r\n tempElevationsRef?: TempElevationsRef,\r\n): { cleanup: () => void, remove: () => void, hitTest: (ndcX: number, ndcY: number) => boolean } => {\r\n let components = null\r\n let customLayer: CustomLayerInterface | null = null\r\n\r\n if (!map || !modelFile) {\r\n return { cleanup: () => {}, remove: () => {}, hitTest: () => false }\r\n }\r\n\r\n // Refs into the layer's camera and scene so raycasting can read the last\r\n // rendered frame's transform without entering the render loop.\r\n let cameraRef: THREE.Camera | null = null\r\n let sceneRef: THREE.Scene | null = null\r\n\r\n // Reused temps for hitTest raycasting — avoid per-call allocation.\r\n const _hitInv = new THREE.Matrix4()\r\n const _hitNear = new THREE.Vector3()\r\n const _hitFar = new THREE.Vector3()\r\n const _hitDir = new THREE.Vector3()\r\n const _raycaster = new THREE.Raycaster()\r\n\r\n const createCustomLayer = (): CustomLayerInterface => {\r\n // Track last applied rotation so we can apply delta increments (same as BimLayer)\r\n let lastAppliedRotation = modelFile.rotation ?? 0\r\n // Layer-removed-mid-load guard + reused per-frame matrices\r\n let disposed = false\r\n const _m = new THREE.Matrix4()\r\n const _l = new THREE.Matrix4()\r\n // Render-on-demand: cache terrain elevation off the per-frame path and only\r\n // keep repainting while the camera recently moved or an animation is playing,\r\n // so an idle map with a placed model stops re-rendering instead of pinning the\r\n // main thread (this was the freeze after a flyTo to high zoom).\r\n let cachedTerrainElev = 0\r\n let lastMoveTime = performance.now()\r\n const SETTLE_MS = 1000\r\n let onMapMove: (() => void) | undefined\r\n let onMapMoveEnd: (() => void) | undefined\r\n const recomputeTerrainElev = () => {\r\n const { lng, lat } = resolveModelCoordinates(modelFile)\r\n if (!(lng === 0 && lat === 0)) {\r\n const e = map.queryTerrainElevation([lng, lat])\r\n if (e !== null && e !== undefined) cachedTerrainElev = e\r\n }\r\n }\r\n\r\n return {\r\n id: `model-${modelFile.id}`,\r\n type: 'custom',\r\n renderingMode: '3d',\r\n onAdd(map, gl) {\r\n this.camera = new THREE.Camera()\r\n this.scene = new THREE.Scene()\r\n this.renderer = renderer\r\n\r\n // Expose to closure for raycasting\r\n cameraRef = this.camera as THREE.Camera\r\n sceneRef = this.scene as THREE.Scene\r\n\r\n const scene = this.scene as THREE.Scene\r\n\r\n // Apply initial rotation\r\n if (lastAppliedRotation !== 0) {\r\n scene.rotateY(lastAppliedRotation * (Math.PI / 180))\r\n }\r\n\r\n // Lighting to match OBC library setup\r\n scene.background = null\r\n scene.fog = new THREE.Fog(0x20_29_32, 10, 200)\r\n scene.add(new THREE.AmbientLight(0xFF_FF_FF, 1))\r\n scene.add(new THREE.HemisphereLight(0xFF_FF_BB, 0x08_08_20, 0.5))\r\n\r\n const sun = new THREE.DirectionalLight(0xFF_FF_FF, 1.5)\r\n sun.position.set(0, -70, 100).normalize()\r\n sun.castShadow = true\r\n sun.shadow.mapSize.set(2048, 2048)\r\n scene.add(sun)\r\n\r\n const fill = new THREE.DirectionalLight(0xFF_FF_FF, 1)\r\n fill.position.set(0, 70, 100).normalize()\r\n scene.add(fill)\r\n\r\n this.renderer.toneMapping = THREE.ACESFilmicToneMapping\r\n this.renderer.autoClear = false\r\n this.clock = new THREE.Clock()\r\n\r\n const loader = new GLTFLoader()\r\n loader.load(\r\n modelFile.url!,\r\n (gltf) => {\r\n // Layer was removed before the async load resolved — don't attach to a\r\n // dead scene; let the gltf be GC'd (no GPU upload happened yet).\r\n if (disposed) return\r\n gltf.scene.scale.setScalar(1)\r\n\r\n if (gltf.animations && gltf.animations.length > 0) {\r\n this.mixer = new THREE.AnimationMixer(gltf.scene)\r\n for (const clip of gltf.animations) {\r\n this.mixer!.clipAction(clip).play()\r\n }\r\n }\r\n\r\n scene.add(gltf.scene)\r\n // Open the settle window so the just-loaded model paints, then idle.\r\n lastMoveTime = performance.now()\r\n recomputeTerrainElev()\r\n map.triggerRepaint()\r\n },\r\n undefined,\r\n (error) => { console.error('Error loading model:', error) },\r\n )\r\n\r\n // Track camera movement for render-on-demand: keep repainting only\r\n // during/just-after movement; refresh cached terrain elevation on settle.\r\n onMapMove = () => { lastMoveTime = performance.now() }\r\n onMapMoveEnd = () => {\r\n lastMoveTime = performance.now()\r\n recomputeTerrainElev()\r\n map.triggerRepaint()\r\n }\r\n map.on('move', onMapMove)\r\n map.on('moveend', onMapMoveEnd)\r\n },\r\n\r\n render(gl, args) {\r\n if (map.getZoom() < 15.5) return\r\n\r\n const isEditing = editingFileNameRef?.current === modelFile.name\r\n\r\n // ── Position ──────────────────────────────────────────────────────────\r\n let lng: number\r\n let lat: number\r\n if (isEditing && tempPositionsRef?.current[modelFile.name]) {\r\n const tp = tempPositionsRef.current[modelFile.name]\r\n lng = tp.lng\r\n lat = tp.lat\r\n } else {\r\n const coords = resolveModelCoordinates(modelFile)\r\n lng = coords.lng\r\n lat = coords.lat\r\n }\r\n\r\n if (lng === 0 && lat === 0) return\r\n\r\n // ── Rotation (delta-increment, same pattern as BimLayer) ──────────────\r\n const targetRotation = (isEditing && tempRotationsRef?.current[modelFile.name] !== undefined)\r\n ? tempRotationsRef.current[modelFile.name]\r\n : (modelFile.rotation ?? 0)\r\n\r\n if (targetRotation !== lastAppliedRotation) {\r\n ;(this.scene as THREE.Scene).rotateY(\r\n (targetRotation - lastAppliedRotation) * (Math.PI / 180)\r\n )\r\n lastAppliedRotation = targetRotation\r\n }\r\n\r\n // ── Elevation ─────────────────────────────────────────────────────────\r\n const fileElevation = (isEditing && tempElevationsRef?.current[modelFile.name] !== undefined)\r\n ? tempElevationsRef.current[modelFile.name]\r\n : (modelFile.elevation ?? 0)\r\n\r\n const modelOrigin = [lng, lat] as LngLatLike\r\n // Cached terrain elevation (refreshed on moveend); query live only while\r\n // editing this model's position. Keeps queryTerrainElevation off the hot\r\n // path — the per-frame query drove the high-zoom freeze.\r\n const terrainAltitude = isEditing\r\n ? (map.queryTerrainElevation([lng, lat]) ?? cachedTerrainElev)\r\n : cachedTerrainElev\r\n const altitude = terrainAltitude + fileElevation\r\n\r\n const modelMatrix = map.transform.getMatrixForModel(modelOrigin, altitude)\r\n // Reuse temps and write into the camera's own matrix — no per-frame\r\n // allocation. The previous `.scale(1,1,1)` was an identity no-op.\r\n _m.fromArray(args.defaultProjectionData.mainMatrix)\r\n _l.fromArray(modelMatrix)\r\n this.camera.projectionMatrix.multiplyMatrices(_m, _l)\r\n\r\n if (this.mixer) {\r\n this.mixer.update((this.clock as THREE.Clock).getDelta())\r\n }\r\n\r\n this.renderer.resetState()\r\n this.renderer.render(this.scene, this.camera)\r\n\r\n // Render-on-demand: keep the frame loop alive only while an animation is\r\n // playing, the camera recently moved (settle window), or this model is\r\n // being edited. Idle static model ⇒ no self-scheduled repaints ⇒ no freeze.\r\n if (this.mixer || isEditing || performance.now() - lastMoveTime < SETTLE_MS) {\r\n map.triggerRepaint()\r\n }\r\n },\r\n\r\n onRemove() {\r\n disposed = true\r\n if (onMapMove) map.off('move', onMapMove)\r\n if (onMapMoveEnd) map.off('moveend', onMapMoveEnd)\r\n // Stop + release the animation mixer so it isn't left running/holding the\r\n // scene after removal.\r\n if (this.mixer) {\r\n ;(this.mixer as THREE.AnimationMixer).stopAllAction()\r\n this.mixer = null\r\n }\r\n if (this.scene) disposeThreeScene(this.scene as THREE.Scene)\r\n cameraRef = null\r\n sceneRef = null\r\n this.renderer = null\r\n this.camera = null\r\n this.scene = null\r\n },\r\n }\r\n }\r\n\r\n /**\r\n * Raycast against this model using the last rendered frame's camera matrix.\r\n *\r\n * The camera.projectionMatrix = VP * M (view-projection × model-to-world),\r\n * so its inverse transforms clip-space → model-space, which is exactly the\r\n * coordinate system the Three.js scene lives in. We manually unproject near/far\r\n * clip-space points through that inverse to build the ray.\r\n */\r\n const hitTest = (ndcX: number, ndcY: number): boolean => {\r\n if (!cameraRef || !sceneRef) return false\r\n\r\n // Invert the combined VP*M matrix to go clip-space → model-space.\r\n // Reuses module-scope temps + a singleton raycaster.\r\n _hitInv.copy(cameraRef.projectionMatrix).invert()\r\n\r\n // Unproject near and far clip-space points into model space\r\n _hitNear.set(ndcX, ndcY, -1).applyMatrix4(_hitInv)\r\n _hitFar.set(ndcX, ndcY, 1).applyMatrix4(_hitInv)\r\n\r\n _hitDir.copy(_hitFar).sub(_hitNear).normalize()\r\n\r\n _raycaster.set(_hitNear, _hitDir)\r\n\r\n const intersects = _raycaster.intersectObjects(sceneRef.children, true)\r\n return intersects.length > 0\r\n }\r\n\r\n customLayer = createCustomLayer()\r\n\r\n if (!map.getLayer(customLayer.id)) {\r\n map.addLayer(customLayer)\r\n }\r\n\r\n const removeLayer = () => {\r\n if (customLayer && map.getLayer(customLayer.id)) {\r\n map.removeLayer(customLayer.id)\r\n }\r\n }\r\n\r\n const cleanup = () => {\r\n removeLayer()\r\n components = null\r\n customLayer = null\r\n }\r\n\r\n return { cleanup, remove: removeLayer, hitTest }\r\n}\r\n"],"mappings":"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","names":["map","_a"]}
|
package/dist/core/components/viewers/map/src/MapLayers/src/OpenDataLayer/src/WmsTimeControl.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
interface WmsTimeControlProps {
|
|
3
|
+
/** Ascending frame timestamps (oldest → newest). */
|
|
4
|
+
frames: string[];
|
|
5
|
+
/** Called with the active frame timestamp whenever it changes. */
|
|
6
|
+
onTimeChange: (time: string) => void;
|
|
7
|
+
/** Card title (e.g. the dataset name). */
|
|
8
|
+
label?: string;
|
|
9
|
+
/** Frame advance interval while playing, ms. */
|
|
10
|
+
stepMs?: number;
|
|
11
|
+
/** WMS GetLegendGraphic image URL; shown under the slider when it loads. */
|
|
12
|
+
legendUrl?: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Floating scrub + play/pause control for a WMS time dimension. Defaults to the
|
|
16
|
+
* latest frame; play advances oldest→newest on a loop. Purely presentational —
|
|
17
|
+
* it owns only the current index + playing state and reports the active time up.
|
|
18
|
+
*/
|
|
19
|
+
export declare const WmsTimeControl: React.FC<WmsTimeControlProps>;
|
|
20
|
+
export {};
|
|
21
|
+
//# sourceMappingURL=WmsTimeControl.d.ts.map
|
package/dist/core/components/viewers/map/src/MapLayers/src/OpenDataLayer/src/WmsTimeControl.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"WmsTimeControl.d.ts","sourceRoot":"","sources":["../../../../../../../../../../src/core/components/viewers/map/src/MapLayers/src/OpenDataLayer/src/WmsTimeControl.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAG/B,UAAU,mBAAmB;IAC3B,oDAAoD;IACpD,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,kEAAkE;IAClE,YAAY,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,0CAA0C;IAC1C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,gDAAgD;IAChD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,4EAA4E;IAC5E,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;GAIG;AACH,eAAO,MAAM,cAAc,EAAE,KAAK,CAAC,EAAE,CAAC,mBAAmB,CA0GxD,CAAC"}
|
package/dist/core/components/viewers/map/src/MapLayers/src/OpenDataLayer/src/WmsTimeControl.js
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { formatFrameTime } from "../../../../../datasets/src/wmsTime";
|
|
5
|
+
const WmsTimeControl = ({
|
|
6
|
+
frames,
|
|
7
|
+
onTimeChange,
|
|
8
|
+
label,
|
|
9
|
+
stepMs = 500,
|
|
10
|
+
legendUrl
|
|
11
|
+
}) => {
|
|
12
|
+
const [index, setIndex] = React.useState(Math.max(0, frames.length - 1));
|
|
13
|
+
const [playing, setPlaying] = React.useState(false);
|
|
14
|
+
const [legendOk, setLegendOk] = React.useState(true);
|
|
15
|
+
React.useEffect(() => {
|
|
16
|
+
setLegendOk(true);
|
|
17
|
+
}, [legendUrl]);
|
|
18
|
+
React.useEffect(() => {
|
|
19
|
+
setIndex(Math.max(0, frames.length - 1));
|
|
20
|
+
}, [frames.length]);
|
|
21
|
+
React.useEffect(() => {
|
|
22
|
+
const time = frames[index];
|
|
23
|
+
if (time) onTimeChange(time);
|
|
24
|
+
}, [index, frames, onTimeChange]);
|
|
25
|
+
React.useEffect(() => {
|
|
26
|
+
if (!playing || frames.length < 2) return;
|
|
27
|
+
const timer = setInterval(() => {
|
|
28
|
+
setIndex((prev) => (prev + 1) % frames.length);
|
|
29
|
+
}, stepMs);
|
|
30
|
+
return () => clearInterval(timer);
|
|
31
|
+
}, [playing, frames.length, stepMs]);
|
|
32
|
+
if (frames.length === 0) return null;
|
|
33
|
+
const activeTime = frames[index];
|
|
34
|
+
return /* @__PURE__ */ jsxs(
|
|
35
|
+
"div",
|
|
36
|
+
{
|
|
37
|
+
style: {
|
|
38
|
+
// Laid out by the bottom-left flex stack it portals into (see MapViewer),
|
|
39
|
+
// so no absolute positioning — it sits above the legend / dataset-manager cards.
|
|
40
|
+
background: "#ffffff",
|
|
41
|
+
borderRadius: 8,
|
|
42
|
+
boxShadow: "0 1px 4px rgba(0,0,0,0.3)",
|
|
43
|
+
padding: "10px 12px",
|
|
44
|
+
width: 260,
|
|
45
|
+
font: "13px/1.3 system-ui, sans-serif",
|
|
46
|
+
color: "#1a1a1a",
|
|
47
|
+
pointerEvents: "auto"
|
|
48
|
+
},
|
|
49
|
+
children: [
|
|
50
|
+
/* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: 8, marginBottom: 6 }, children: [
|
|
51
|
+
/* @__PURE__ */ jsx(
|
|
52
|
+
"button",
|
|
53
|
+
{
|
|
54
|
+
type: "button",
|
|
55
|
+
onClick: () => setPlaying((p) => !p),
|
|
56
|
+
"aria-label": playing ? "Pause" : "Play",
|
|
57
|
+
style: {
|
|
58
|
+
cursor: "pointer",
|
|
59
|
+
border: "none",
|
|
60
|
+
background: "#0d9488",
|
|
61
|
+
color: "#fff",
|
|
62
|
+
borderRadius: 4,
|
|
63
|
+
width: 28,
|
|
64
|
+
height: 28,
|
|
65
|
+
fontSize: 14,
|
|
66
|
+
lineHeight: 1
|
|
67
|
+
},
|
|
68
|
+
children: playing ? "\u275A\u275A" : "\u25B6"
|
|
69
|
+
}
|
|
70
|
+
),
|
|
71
|
+
/* @__PURE__ */ jsx("div", { style: { fontWeight: 600, flex: 1, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, children: label != null ? label : "WMS time" }),
|
|
72
|
+
/* @__PURE__ */ jsx("div", { style: { fontVariantNumeric: "tabular-nums", color: "#475569" }, children: formatFrameTime(activeTime) })
|
|
73
|
+
] }),
|
|
74
|
+
/* @__PURE__ */ jsx(
|
|
75
|
+
"input",
|
|
76
|
+
{
|
|
77
|
+
type: "range",
|
|
78
|
+
min: 0,
|
|
79
|
+
max: frames.length - 1,
|
|
80
|
+
value: index,
|
|
81
|
+
onChange: (e) => {
|
|
82
|
+
setPlaying(false);
|
|
83
|
+
setIndex(Number(e.target.value));
|
|
84
|
+
},
|
|
85
|
+
style: { width: "100%", display: "block", accentColor: "#0d9488" }
|
|
86
|
+
}
|
|
87
|
+
),
|
|
88
|
+
legendUrl && legendOk && /* @__PURE__ */ jsx(
|
|
89
|
+
"img",
|
|
90
|
+
{
|
|
91
|
+
src: legendUrl,
|
|
92
|
+
alt: "Legend",
|
|
93
|
+
onError: () => setLegendOk(false),
|
|
94
|
+
style: {
|
|
95
|
+
display: "block",
|
|
96
|
+
marginTop: 8,
|
|
97
|
+
maxWidth: "100%",
|
|
98
|
+
maxHeight: 220,
|
|
99
|
+
objectFit: "contain"
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
)
|
|
103
|
+
]
|
|
104
|
+
}
|
|
105
|
+
);
|
|
106
|
+
};
|
|
107
|
+
WmsTimeControl.displayName = "WmsTimeControl";
|
|
108
|
+
export {
|
|
109
|
+
WmsTimeControl
|
|
110
|
+
};
|
|
111
|
+
//# sourceMappingURL=WmsTimeControl.js.map
|
package/dist/core/components/viewers/map/src/MapLayers/src/OpenDataLayer/src/WmsTimeControl.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../../../../src/core/components/viewers/map/src/MapLayers/src/OpenDataLayer/src/WmsTimeControl.tsx"],"sourcesContent":["\"use client\";\r\n\r\nimport * as React from \"react\";\r\nimport { formatFrameTime } from \"../../../../../datasets/src/wmsTime\";\r\n\r\ninterface WmsTimeControlProps {\r\n /** Ascending frame timestamps (oldest → newest). */\r\n frames: string[];\r\n /** Called with the active frame timestamp whenever it changes. */\r\n onTimeChange: (time: string) => void;\r\n /** Card title (e.g. the dataset name). */\r\n label?: string;\r\n /** Frame advance interval while playing, ms. */\r\n stepMs?: number;\r\n /** WMS GetLegendGraphic image URL; shown under the slider when it loads. */\r\n legendUrl?: string;\r\n}\r\n\r\n/**\r\n * Floating scrub + play/pause control for a WMS time dimension. Defaults to the\r\n * latest frame; play advances oldest→newest on a loop. Purely presentational —\r\n * it owns only the current index + playing state and reports the active time up.\r\n */\r\nexport const WmsTimeControl: React.FC<WmsTimeControlProps> = ({\r\n frames,\r\n onTimeChange,\r\n label,\r\n stepMs = 500,\r\n legendUrl,\r\n}) => {\r\n const [index, setIndex] = React.useState(Math.max(0, frames.length - 1));\r\n const [playing, setPlaying] = React.useState(false);\r\n const [legendOk, setLegendOk] = React.useState(true);\r\n\r\n // Reset legend visibility if the URL changes (different layer).\r\n React.useEffect(() => { setLegendOk(true); }, [legendUrl]);\r\n\r\n // Clamp + re-seat at the latest frame whenever the frame list changes.\r\n React.useEffect(() => {\r\n setIndex(Math.max(0, frames.length - 1));\r\n }, [frames.length]);\r\n\r\n // Report the active frame up whenever the index (or frame list) changes.\r\n React.useEffect(() => {\r\n const time = frames[index];\r\n if (time) onTimeChange(time);\r\n }, [index, frames, onTimeChange]);\r\n\r\n // Play loop.\r\n React.useEffect(() => {\r\n if (!playing || frames.length < 2) return;\r\n const timer = setInterval(() => {\r\n setIndex(prev => (prev + 1) % frames.length);\r\n }, stepMs);\r\n return () => clearInterval(timer);\r\n }, [playing, frames.length, stepMs]);\r\n\r\n if (frames.length === 0) return null;\r\n\r\n const activeTime = frames[index];\r\n\r\n return (\r\n <div\r\n style={{\r\n // Laid out by the bottom-left flex stack it portals into (see MapViewer),\r\n // so no absolute positioning — it sits above the legend / dataset-manager cards.\r\n background: \"#ffffff\",\r\n borderRadius: 8,\r\n boxShadow: \"0 1px 4px rgba(0,0,0,0.3)\",\r\n padding: \"10px 12px\",\r\n width: 260,\r\n font: \"13px/1.3 system-ui, sans-serif\",\r\n color: \"#1a1a1a\",\r\n pointerEvents: \"auto\",\r\n }}\r\n >\r\n <div style={{ display: \"flex\", alignItems: \"center\", gap: 8, marginBottom: 6 }}>\r\n <button\r\n type=\"button\"\r\n onClick={() => setPlaying(p => !p)}\r\n aria-label={playing ? \"Pause\" : \"Play\"}\r\n style={{\r\n cursor: \"pointer\",\r\n border: \"none\",\r\n background: \"#0d9488\",\r\n color: \"#fff\",\r\n borderRadius: 4,\r\n width: 28,\r\n height: 28,\r\n fontSize: 14,\r\n lineHeight: 1,\r\n }}\r\n >\r\n {playing ? \"❚❚\" : \"▶\"}\r\n </button>\r\n <div style={{ fontWeight: 600, flex: 1, overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\" }}>\r\n {label ?? \"WMS time\"}\r\n </div>\r\n <div style={{ fontVariantNumeric: \"tabular-nums\", color: \"#475569\" }}>\r\n {formatFrameTime(activeTime)}\r\n </div>\r\n </div>\r\n <input\r\n type=\"range\"\r\n min={0}\r\n max={frames.length - 1}\r\n value={index}\r\n onChange={e => {\r\n setPlaying(false);\r\n setIndex(Number(e.target.value));\r\n }}\r\n style={{ width: \"100%\", display: \"block\", accentColor: \"#0d9488\" }}\r\n />\r\n {legendUrl && legendOk && (\r\n <img\r\n src={legendUrl}\r\n alt=\"Legend\"\r\n onError={() => setLegendOk(false)}\r\n style={{\r\n display: \"block\",\r\n marginTop: 8,\r\n maxWidth: \"100%\",\r\n maxHeight: 220,\r\n objectFit: \"contain\",\r\n }}\r\n />\r\n )}\r\n </div>\r\n );\r\n};\r\nWmsTimeControl.displayName = \"WmsTimeControl\";\r\n"],"mappings":"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","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../../../../../src/core/components/viewers/map/src/MapLayers/src/OpenDataLayer/src/index.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../../../../../src/core/components/viewers/map/src/MapLayers/src/OpenDataLayer/src/index.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AA+c/B,eAAO,MAAM,cAAc,yBAqM1B,CAAC"}
|