@collabdt/core 0.0.41 → 0.0.43
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/README.md +1 -1
- 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/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/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/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 +1 -0
- 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/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/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 +2 -1
- 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
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../../src/core/components/viewers/map/src/MapFeaturePopoverMenu/src/NonDatabaseBuildingPopover.tsx"],"sourcesContent":["'use client'\r\n// Dependencies\r\nimport * as React from 'react'\r\nimport { useSession } from 'next-auth/react'\r\nimport { useTranslations } from 'next-intl'\r\nimport { usePermissions } from '../../../../../../store'\r\nimport { toast } from 'sonner'\r\n\r\n// Shadcn Components\r\nimport { Button, Label } from '../../../../../ui/'\r\nimport { PopoverContent } from '../../../../../ui/Popover'\r\nimport {\r\n Select,\r\n SelectContent,\r\n SelectItem,\r\n SelectTrigger,\r\n SelectValue,\r\n} from '../../../../../ui/Select'\r\n\r\n// Icons\r\nimport * as LR from 'lucide-react'\r\n\r\n// Types / Utilities\r\nimport type { MapGeoJSONFeature } from 'maplibre-gl'\r\nimport { MapContext } from '../../../../../../store'\r\nimport { useMenusContext } from '../../../../../../store'\r\nimport type { Building } from '../../../../../../types/dbTypes'\r\nimport { MarkerManager } from '../../../utils/MarkerManager'\r\n\r\n// Hooks\r\nimport { useCreateBuilding, useBuildings, useBuilding } from '../../../../../../hooks/buildings/buildings'\r\nimport { LoadingSpinner } from '../../../../../ui/LoadingSpinner'\r\n\r\ninterface NonDatabaseBuildingPopoverProps {\r\n feature: MapGeoJSONFeature\r\n isOpen: boolean\r\n onCloseAction: () => void\r\n}\r\n\r\nexport default function NonDatabaseBuildingPopover({\r\n feature,\r\n onCloseAction,\r\n}: NonDatabaseBuildingPopoverProps) {\r\n // Translation\r\n const t = useTranslations('NonDatabaseBuildingPopover')\r\n // Permissions\r\n const { ability } = usePermissions()\r\n\r\n const { data: session } = useSession()\r\n const user = session?.user\r\n\r\n const { state: mapState } = React.useContext(MapContext)\r\n const { currentLocation, map } = mapState.map\r\n\r\n const { setSelectedItem } = useMenusContext()\r\n\r\n // Use the create building hook\r\n const { createBuilding, isMutating, createError, createdData } = useCreateBuilding()\r\n \r\n // Get all buildings for searching\r\n const { buildings } = useBuildings()\r\n\r\n const featureProps = feature?.properties || {}\r\n const featureid = feature?.id\r\n const [name, setName] = React.useState('')\r\n const [address, setAddress] = React.useState('')\r\n const [type, setType] = React.useState('')\r\n const [matchingBuildings, setMatchingBuildings] = React.useState<Building[]>([])\r\n const [selectedMatchingBuildingId, setSelectedMatchingBuildingId] = React.useState<string>('')\r\n const [showExistingBuildings, setShowExistingBuildings] = React.useState(false)\r\n const [isAttaching, setIsAttaching] = React.useState(false)\r\n\r\n const { updateBuilding, isMutating: isUpdating } = useBuilding(selectedMatchingBuildingId ? Number(selectedMatchingBuildingId) : null)\r\n\r\n const normalizeAddress = React.useCallback((value: string) => {\r\n return value\r\n .toLowerCase()\r\n .replace(/\\./g, '')\r\n .replace(/,/g, ' ')\r\n .replace(/\\bdr\\b/g, 'drive')\r\n .replace(/\\brd\\b/g, 'road')\r\n .replace(/\\bst\\b/g, 'street')\r\n .replace(/\\bave\\b/g, 'avenue')\r\n .replace(/\\s+/g, ' ')\r\n .trim()\r\n }, [])\r\n\r\n const clearExistingBuildingMatches = React.useCallback(() => {\r\n setMatchingBuildings([])\r\n setSelectedMatchingBuildingId('')\r\n setShowExistingBuildings(false)\r\n\r\n if (didSetSelectedItemRef.current && setSelectedItem) {\r\n setSelectedItem(null)\r\n didSetSelectedItemRef.current = false\r\n }\r\n }, [setSelectedItem])\r\n\r\n // Track if we set the selected item in this component\r\n const didSetSelectedItemRef = React.useRef(false)\r\n\r\n // Cleanup on unmount\r\n React.useEffect(() => {\r\n return () => {\r\n // Clear selected item if we set it\r\n if (didSetSelectedItemRef.current && setSelectedItem) {\r\n setSelectedItem(null)\r\n didSetSelectedItemRef.current = false\r\n }\r\n }\r\n }, [setSelectedItem])\r\n\r\n // Get address and search for matching buildings\r\n React.useEffect(() => {\r\n const getAddress = async (latitude: string, longitude: string) => {\r\n const params = new URLSearchParams({\r\n 'api_key': process.env.NEXT_PUBLIC_GEOCODE_EARTH_API_KEY,\r\n 'point.lat': latitude,\r\n 'point.lon': longitude,\r\n 'boundary.country': 'CA',\r\n 'layers': 'address',\r\n 'size': '1', // Limit to 1 result\r\n })\r\n \r\n const reverseGeocoderUrl = `https://api.geocode.earth/v1/reverse?${params}`\r\n \r\n try {\r\n const response = await fetch(reverseGeocoderUrl, {\r\n headers: {\r\n 'User-Agent': 'CDT/1.0 (cdt@email.com)',\r\n },\r\n })\r\n \r\n if (!response.ok) {\r\n throw new Error(`HTTP error! status: ${response.status}`)\r\n }\r\n\r\n const data = await response.json()\r\n \r\n if (data.features && data.features.length > 0) {\r\n const feature = data.features[0]\r\n const properties = feature.properties\r\n \r\n const geocodedAddress = properties.name\r\n setAddress(geocodedAddress)\r\n \r\n // Search for existing buildings with this address\r\n if (geocodedAddress && buildings && buildings.length > 0) {\r\n const normalizedGeocodedAddress = normalizeAddress(geocodedAddress)\r\n\r\n const matches = buildings.filter((building) => {\r\n if (!building.buildingAddress) return false\r\n\r\n const normalizedBuildingAddress = normalizeAddress(building.buildingAddress)\r\n return (\r\n normalizedBuildingAddress === normalizedGeocodedAddress ||\r\n normalizedBuildingAddress.includes(normalizedGeocodedAddress) ||\r\n normalizedGeocodedAddress.includes(normalizedBuildingAddress)\r\n )\r\n })\r\n \r\n if (matches.length > 0) {\r\n setMatchingBuildings(matches)\r\n setSelectedMatchingBuildingId(matches[0].id.toString())\r\n setShowExistingBuildings(true)\r\n \r\n // Set the first match as the selected item for the update hook\r\n if (setSelectedItem) {\r\n setSelectedItem(matches[0])\r\n didSetSelectedItemRef.current = true\r\n }\r\n } else {\r\n clearExistingBuildingMatches()\r\n }\r\n } else {\r\n clearExistingBuildingMatches()\r\n }\r\n } else {\r\n clearExistingBuildingMatches()\r\n }\r\n }\r\n catch (error) {\r\n console.error('Error fetching municipality and countrySubdivision:', error)\r\n }\r\n }\r\n \r\n if (feature.properties?.coordinates) {\r\n const longitude = feature.properties.coordinates[0]\r\n const latitude = feature.properties.coordinates[1]\r\n getAddress(latitude, longitude)\r\n }\r\n }, [feature, buildings, setSelectedItem, normalizeAddress, clearExistingBuildingMatches])\r\n\r\n React.useEffect(() => {\r\n if (!setSelectedItem || !selectedMatchingBuildingId) return\r\n\r\n const selectedBuilding = matchingBuildings.find(\r\n building => building.id.toString() === selectedMatchingBuildingId,\r\n )\r\n\r\n if (!selectedBuilding) return\r\n\r\n setSelectedItem(selectedBuilding)\r\n didSetSelectedItemRef.current = true\r\n }, [matchingBuildings, selectedMatchingBuildingId, setSelectedItem])\r\n\r\n // Fallbacks from feature or map state\r\n const buildingCountrySubdivision = featureProps.de_ao_prov || currentLocation.countrySubdivision || null\r\n const buildingMunicipality = featureProps.de_ao_city || currentLocation.municipality || null\r\n const buildingPostalCode = featureProps.de_ao_post || currentLocation.postalCode || null\r\n const site = currentLocation.site || null\r\n const buildingOsmId = featureid.toString()\r\n\r\n // Create MarkerManager instance\r\n const markerManagerRef = React.useRef<MarkerManager | null>(null)\r\n\r\n // Initialize MarkerManager\r\n React.useEffect(() => {\r\n if (!markerManagerRef.current) {\r\n markerManagerRef.current = new MarkerManager()\r\n }\r\n\r\n // Cleanup on unmount\r\n return () => {\r\n if (markerManagerRef.current) {\r\n markerManagerRef.current.destroy()\r\n markerManagerRef.current = null\r\n }\r\n }\r\n }, [])\r\n\r\n // Get coordinates data from feature\r\n const [coordinates, setCoordinates] = React.useState(featureProps.coordinates || null)\r\n\r\n // Listen for marker coordinate changes\r\n React.useEffect(() => {\r\n if (!markerManagerRef.current) return\r\n\r\n const cleanup = markerManagerRef.current.onMoved((event) => {\r\n const newCoordinates = event.detail.coordinates\r\n if (newCoordinates) {\r\n setCoordinates(newCoordinates)\r\n }\r\n })\r\n\r\n return cleanup\r\n }, [])\r\n\r\n const handleBuildingNameChange = (event: React.ChangeEvent<HTMLInputElement>) => {\r\n setName(event.target.value)\r\n }\r\n\r\n const handleAddressChange = (event: React.ChangeEvent<HTMLInputElement>) => {\r\n setAddress(event.target.value)\r\n }\r\n\r\n const handleBuildingTypeChange = (event: React.ChangeEvent<HTMLSelectElement>) => {\r\n setType(event.target.value)\r\n }\r\n\r\n const selectedMatchingBuilding = matchingBuildings.find(\r\n building => building.id.toString() === selectedMatchingBuildingId,\r\n )\r\n\r\n const handleAttachExistingBuilding = async (building: Building) => {\r\n setIsAttaching(true)\r\n \r\n try {\r\n // Add OSM ID to existing building\r\n const result = await updateBuilding({\r\n buildingOsmId,\r\n // Set default storey number of 2 if building doesn't have one\r\n ...(building.buildingStoreyNum == null && { buildingStoreyNum: 2 })\r\n })\r\n \r\n toast.success(t('toastAttachSuccess'))\r\n \r\n // Clear the selected item after successful update\r\n if (setSelectedItem) {\r\n setSelectedItem(null)\r\n didSetSelectedItemRef.current = false\r\n }\r\n \r\n // Close the popover\r\n onCloseAction()\r\n \r\n } catch (error) {\r\n console.error('Error attaching building:', error)\r\n \r\n if (error?.status !== 401) {\r\n toast.error(t('toastAttachError'))\r\n }\r\n } finally {\r\n setIsAttaching(false)\r\n }\r\n }\r\n\r\n const handleCreateNewBuilding = async (event: React.FormEvent) => {\r\n event.preventDefault()\r\n\r\n // Get final values (prioritize user input, then existing data)\r\n const buildingName = name.trim() || featureProps._name || ''\r\n const finalAddress = address.trim() || address || ''\r\n\r\n if (!user?.organizationId) {\r\n toast.error(t('userNotFound'))\r\n return\r\n }\r\n\r\n // Use updated coordinates from marker if available\r\n const buildingCoordinates = coordinates || [featureProps.longitude, featureProps.latitude]\r\n\r\n const newBuilding: Partial<Building> = {\r\n buildingName,\r\n buildingAddress: finalAddress,\r\n buildingType: [type || 'Other'],\r\n buildingOsmId,\r\n buildingLatitude: buildingCoordinates ? buildingCoordinates[1] : null,\r\n buildingLongitude: buildingCoordinates ? buildingCoordinates[0] : null,\r\n buildingCountrySubdivision,\r\n buildingMunicipality,\r\n buildingPostalCode,\r\n }\r\n\r\n try {\r\n const result = await createBuilding({\r\n buildingData: newBuilding,\r\n organizationId: user.organizationId.toString(),\r\n })\r\n\r\n // Remove marker after creating building\r\n if (markerManagerRef.current) {\r\n markerManagerRef.current.remove()\r\n }\r\n \r\n toast.success(t('toastSuccess'))\r\n \r\n // Close the popover on success\r\n onCloseAction()\r\n }\r\n catch (error) {\r\n console.error('Error creating building:', error)\r\n toast.error(t('toastError'))\r\n }\r\n }\r\n\r\n const handleLocationEdit = () => {\r\n // Create marker at current coordinates with editing enabled\r\n if (markerManagerRef.current && coordinates && map) {\r\n console.log('Creating editable marker at coordinates:', coordinates)\r\n markerManagerRef.current.create(coordinates as [number, number], map, {\r\n editing: true })\r\n }\r\n else {\r\n console.warn('Cannot create marker: missing coordinates or map instance')\r\n }\r\n }\r\n\r\n return (\r\n <PopoverContent className=\"w-64 -m-1\" side=\"top\">\r\n <form onSubmit={handleCreateNewBuilding}>\r\n <div className=\"grid gap-4\">\r\n <Button\r\n type=\"button\"\r\n onClick={onCloseAction}\r\n variant=\"ghost\"\r\n className=\"absolute top-1 right-2 p-1 text-muted-foreground hover:text-foreground\"\r\n aria-label={t('closeAriaLabel')}\r\n >\r\n <LR.X className=\"w-4 h-4\" />\r\n </Button>\r\n \r\n {/* Show existing building if match found */}\r\n {showExistingBuildings && matchingBuildings.length > 0 && (\r\n <div className=\"space-y-1 mt-6\">\r\n <div className=\"text-xs text-muted-foreground\">\r\n {t('existingBuildingFound')}\r\n </div>\r\n <div className=\"flex flex-col justify-between gap-2 p-2 bg-muted/50 border rounded-lg\">\r\n <div className=\"grid gap-1\">\r\n <Label htmlFor=\"existing-building-select\" className=\"text-xs text-muted-foreground\">\r\n {t('selectExistingBuildingLabel')}\r\n </Label>\r\n <Select\r\n value={selectedMatchingBuildingId}\r\n onValueChange={setSelectedMatchingBuildingId}\r\n >\r\n <SelectTrigger id=\"existing-building-select\" className=\"h-9 w-full\">\r\n <SelectValue placeholder={t('selectExistingBuildingPlaceholder')} />\r\n </SelectTrigger>\r\n <SelectContent>\r\n {matchingBuildings.map((building) => (\r\n <SelectItem key={building.id} value={building.id.toString()}>\r\n {building.buildingName || building.buildingAddress || `${t('na')} (${building.id})`}\r\n </SelectItem>\r\n ))}\r\n </SelectContent>\r\n </Select>\r\n </div>\r\n <Button\r\n variant=\"outline\"\r\n size=\"sm\"\r\n className='w-full'\r\n onClick={() => selectedMatchingBuilding && handleAttachExistingBuilding(selectedMatchingBuilding)}\r\n disabled={isAttaching || isUpdating || !selectedMatchingBuilding}\r\n >\r\n {(isAttaching || isUpdating) ? (\r\n <>\r\n <LoadingSpinner />\r\n <span>{t('attaching')}</span>\r\n </>\r\n ) : (\r\n <>\r\n <LR.Link />\r\n {t('attachBuilding')}\r\n </>\r\n )}\r\n </Button>\r\n </div>\r\n </div>\r\n )}\r\n \r\n <div className=\"space-y-4\">\r\n <div className=\"grid gap-2\">\r\n <Label htmlFor=\"building-name\" className=\"text-xs text-muted-foreground\">\r\n {t('nameLabel')}\r\n </Label>\r\n <input\r\n id=\"building-name\"\r\n value={name}\r\n onChange={handleBuildingNameChange}\r\n placeholder={name || t('namePlaceholder')}\r\n className={`font-medium leading-none bg-transparent border-b border-gray-300 focus:outline-none focus:border-primary px-1 py-1 ${name ? 'placeholder:text-foreground' : 'placeholder:text-muted-foreground'}`}\r\n aria-label={t('nameInputAriaLabel')}\r\n required\r\n disabled={!ability.can('create', 'Building')}\r\n />\r\n </div>\r\n\r\n <div className=\"grid gap-2\">\r\n <Label htmlFor=\"building-address\" className=\"text-xs text-muted-foreground\">\r\n {t('addressLabel')}\r\n </Label>\r\n <input\r\n id=\"building-address\"\r\n value={address}\r\n onChange={handleAddressChange}\r\n placeholder={address || t('addressPlaceholder')}\r\n className={`font-medium leading-none bg-transparent border-b border-gray-300 focus:outline-none focus:border-primary px-1 py-1 ${address ? 'placeholder:text-foreground' : 'placeholder:text-muted-foreground'}`}\r\n aria-label={t('addressAriaLabel')}\r\n required\r\n disabled={!ability.can('create', 'Building')}\r\n />\r\n </div>\r\n\r\n <div className=\"grid gap-2\">\r\n <Label htmlFor=\"building-type\" className=\"text-xs text-muted-foreground\">\r\n {t('typeLabel')}\r\n </Label>\r\n <select\r\n id=\"building-type\"\r\n value={type}\r\n onChange={handleBuildingTypeChange}\r\n className=\"font-medium leading-none bg-transparent border-b border-gray-300 focus:outline-none focus:border-primary px-1 py-1\"\r\n aria-label={t('typeAriaLabel')}\r\n required\r\n disabled={!ability.can('create', 'Building')}\r\n >\r\n <option value=\"\">{t('defaultOption')}</option>\r\n <option value=\"Townhouses\">{t('townhouse')}</option>\r\n <option value=\"<4 Storey MURB\">{t('4MURB')}</option>\r\n <option value=\"4+ Storey MURB\">{t('4PlusMURB')}</option>\r\n <option value=\"Stacked Townhouses\">{t('stackedTownhouses')}</option>\r\n <option value=\"Detached\">{t('detached')}</option>\r\n <option value=\"Semi Detached\">{t('semiDetached')}</option>\r\n <option value=\"Other\">{t('other')}</option>\r\n </select>\r\n </div>\r\n </div>\r\n <div className=\"flex items-center gap-2 py-2 px-1\">\r\n <Label className=\"text-xs text-muted-foreground min-w-fit\">{t('osmId')}</Label>\r\n <span className=\"text-sm font-medium text-foreground\">{buildingOsmId ?? t('na')}</span>\r\n </div>\r\n </div>\r\n\r\n <div className=\"flex items-center gap-3 justify-center mt-4\">\r\n <Button\r\n type=\"button\"\r\n variant=\"outline\"\r\n aria-label=\"Edit Location\"\r\n title=\"Edit Location\"\r\n onClick={handleLocationEdit}\r\n disabled={!ability.can('update', 'Building')}\r\n >\r\n <LR.LocationEdit className=\"w-4 h-4\" />\r\n </Button>\r\n\r\n <Button\r\n type=\"submit\"\r\n variant=\"outline\"\r\n aria-label={t('addBuilding')}\r\n title={t('addBuilding')}\r\n disabled={isMutating || !ability.can('create', 'Building')}\r\n >\r\n {isMutating\r\n ? (\r\n <>``\r\n <LoadingSpinner />\r\n <span className=\"text-sm\">{t('creating')}</span>\r\n </>\r\n )\r\n : (\r\n <>\r\n <LR.Building2 className=\"w-4 h-4\" />\r\n <span className=\"text-sm\">{t('addBuilding')}</span>\r\n </>\r\n )}\r\n </Button>\r\n </div>\r\n </form>\r\n </PopoverContent>\r\n )\r\n}"],"mappings":"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","names":["feature"]}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../../src/core/components/viewers/map/src/MapFeaturePopoverMenu/src/NonDatabaseBuildingPopover.tsx"],"sourcesContent":["'use client'\r\n// Dependencies\r\nimport * as React from 'react'\r\nimport { useSession } from 'next-auth/react'\r\nimport { useTranslations } from 'next-intl'\r\nimport { usePermissions } from '../../../../../../store'\r\nimport { toast } from 'sonner'\r\n\r\n// Shadcn Components\r\nimport { Button, Label } from '../../../../../ui/'\r\nimport { PopoverContent } from '../../../../../ui/Popover'\r\nimport {\r\n Select,\r\n SelectContent,\r\n SelectItem,\r\n SelectTrigger,\r\n SelectValue,\r\n} from '../../../../../ui/Select'\r\n\r\n// Icons\r\nimport * as LR from 'lucide-react'\r\n\r\n// Types / Utilities\r\nimport type { MapGeoJSONFeature } from 'maplibre-gl'\r\nimport { MapContext } from '../../../../../../store'\r\nimport { useMenusContext } from '../../../../../../store'\r\nimport type { Building } from '../../../../../../types/dbTypes'\r\nimport { MarkerManager } from '../../../utils/MarkerManager'\r\nimport { getDetailedAddress } from '../../../utils/geocoder'\r\n\r\n// Hooks\r\nimport { useCreateBuilding, useBuildings, useBuilding } from '../../../../../../hooks/buildings/buildings'\r\nimport { LoadingSpinner } from '../../../../../ui/LoadingSpinner'\r\n\r\ninterface NonDatabaseBuildingPopoverProps {\r\n feature: MapGeoJSONFeature\r\n isOpen: boolean\r\n onCloseAction: () => void\r\n}\r\n\r\nexport default function NonDatabaseBuildingPopover({\r\n feature,\r\n onCloseAction,\r\n}: NonDatabaseBuildingPopoverProps) {\r\n // Translation\r\n const t = useTranslations('NonDatabaseBuildingPopover')\r\n // Permissions\r\n const { ability } = usePermissions()\r\n\r\n const { data: session } = useSession()\r\n const user = session?.user\r\n\r\n const { state: mapState } = React.useContext(MapContext)\r\n const { currentLocation, map, organizationCountry } = mapState.map\r\n\r\n const { setSelectedItem } = useMenusContext()\r\n\r\n // Use the create building hook\r\n const { createBuilding, isMutating, createError, createdData } = useCreateBuilding()\r\n\r\n // Get all buildings for searching\r\n const { buildings } = useBuildings()\r\n\r\n const featureProps = feature?.properties || {}\r\n const featureid = feature?.id\r\n const [name, setName] = React.useState('')\r\n const [address, setAddress] = React.useState('')\r\n const [type, setType] = React.useState('')\r\n const [matchingBuildings, setMatchingBuildings] = React.useState<Building[]>([])\r\n const [selectedMatchingBuildingId, setSelectedMatchingBuildingId] = React.useState<string>('')\r\n const [showExistingBuildings, setShowExistingBuildings] = React.useState(false)\r\n const [isAttaching, setIsAttaching] = React.useState(false)\r\n\r\n const { updateBuilding, isMutating: isUpdating } = useBuilding(selectedMatchingBuildingId ? Number(selectedMatchingBuildingId) : null)\r\n\r\n const normalizeAddress = React.useCallback((value: string) => {\r\n return value\r\n .toLowerCase()\r\n .replace(/\\./g, '')\r\n .replace(/,/g, ' ')\r\n .replace(/\\bdr\\b/g, 'drive')\r\n .replace(/\\brd\\b/g, 'road')\r\n .replace(/\\bst\\b/g, 'street')\r\n .replace(/\\bave\\b/g, 'avenue')\r\n .replace(/\\s+/g, ' ')\r\n .trim()\r\n }, [])\r\n\r\n const clearExistingBuildingMatches = React.useCallback(() => {\r\n setMatchingBuildings([])\r\n setSelectedMatchingBuildingId('')\r\n setShowExistingBuildings(false)\r\n\r\n if (didSetSelectedItemRef.current && setSelectedItem) {\r\n setSelectedItem(null)\r\n didSetSelectedItemRef.current = false\r\n }\r\n }, [setSelectedItem])\r\n\r\n // Track if we set the selected item in this component\r\n const didSetSelectedItemRef = React.useRef(false)\r\n\r\n // Cleanup on unmount\r\n React.useEffect(() => {\r\n return () => {\r\n // Clear selected item if we set it\r\n if (didSetSelectedItemRef.current && setSelectedItem) {\r\n setSelectedItem(null)\r\n didSetSelectedItemRef.current = false\r\n }\r\n }\r\n }, [setSelectedItem])\r\n\r\n // Get address and search for matching buildings\r\n React.useEffect(() => {\r\n const getAddress = async (longitude: number, latitude: number) => {\r\n try {\r\n // Reverse geocode through the shared provider (Geocode Earth / self-hosted\r\n // Pelias, falling back to public OSM) so this stays in sync with the geocoder.\r\n const result = await getDetailedAddress([longitude, latitude], organizationCountry)\r\n const geocodedAddress = result?.properties?.name as string | undefined\r\n\r\n if (!geocodedAddress) {\r\n clearExistingBuildingMatches()\r\n return\r\n }\r\n\r\n setAddress(geocodedAddress)\r\n\r\n // Search for existing buildings with this address\r\n if (buildings && buildings.length > 0) {\r\n const normalizedGeocodedAddress = normalizeAddress(geocodedAddress)\r\n\r\n const matches = buildings.filter((building) => {\r\n if (!building.buildingAddress) return false\r\n\r\n const normalizedBuildingAddress = normalizeAddress(building.buildingAddress)\r\n return (\r\n normalizedBuildingAddress === normalizedGeocodedAddress ||\r\n normalizedBuildingAddress.includes(normalizedGeocodedAddress) ||\r\n normalizedGeocodedAddress.includes(normalizedBuildingAddress)\r\n )\r\n })\r\n\r\n if (matches.length > 0) {\r\n setMatchingBuildings(matches)\r\n setSelectedMatchingBuildingId(matches[0].id.toString())\r\n setShowExistingBuildings(true)\r\n\r\n // Set the first match as the selected item for the update hook\r\n if (setSelectedItem) {\r\n setSelectedItem(matches[0])\r\n didSetSelectedItemRef.current = true\r\n }\r\n } else {\r\n clearExistingBuildingMatches()\r\n }\r\n } else {\r\n clearExistingBuildingMatches()\r\n }\r\n }\r\n catch (error) {\r\n console.error('Error fetching municipality and countrySubdivision:', error)\r\n }\r\n }\r\n\r\n if (feature.properties?.coordinates) {\r\n const longitude = feature.properties.coordinates[0]\r\n const latitude = feature.properties.coordinates[1]\r\n getAddress(longitude, latitude)\r\n }\r\n }, [feature, buildings, setSelectedItem, normalizeAddress, clearExistingBuildingMatches])\r\n\r\n React.useEffect(() => {\r\n if (!setSelectedItem || !selectedMatchingBuildingId) return\r\n\r\n const selectedBuilding = matchingBuildings.find(\r\n building => building.id.toString() === selectedMatchingBuildingId,\r\n )\r\n\r\n if (!selectedBuilding) return\r\n\r\n setSelectedItem(selectedBuilding)\r\n didSetSelectedItemRef.current = true\r\n }, [matchingBuildings, selectedMatchingBuildingId, setSelectedItem])\r\n\r\n // Fallbacks from feature or map state\r\n const buildingCountrySubdivision = featureProps.de_ao_prov || currentLocation.countrySubdivision || null\r\n const buildingMunicipality = featureProps.de_ao_city || currentLocation.municipality || null\r\n const buildingPostalCode = featureProps.de_ao_post || currentLocation.postalCode || null\r\n const site = currentLocation.site || null\r\n const buildingOsmId = featureid.toString()\r\n\r\n // Create MarkerManager instance\r\n const markerManagerRef = React.useRef<MarkerManager | null>(null)\r\n\r\n // Initialize MarkerManager\r\n React.useEffect(() => {\r\n if (!markerManagerRef.current) {\r\n markerManagerRef.current = new MarkerManager()\r\n }\r\n\r\n // Cleanup on unmount\r\n return () => {\r\n if (markerManagerRef.current) {\r\n markerManagerRef.current.destroy()\r\n markerManagerRef.current = null\r\n }\r\n }\r\n }, [])\r\n\r\n // Get coordinates data from feature\r\n const [coordinates, setCoordinates] = React.useState(featureProps.coordinates || null)\r\n\r\n // Listen for marker coordinate changes\r\n React.useEffect(() => {\r\n if (!markerManagerRef.current) return\r\n\r\n const cleanup = markerManagerRef.current.onMoved((event) => {\r\n const newCoordinates = event.detail.coordinates\r\n if (newCoordinates) {\r\n setCoordinates(newCoordinates)\r\n }\r\n })\r\n\r\n return cleanup\r\n }, [])\r\n\r\n const handleBuildingNameChange = (event: React.ChangeEvent<HTMLInputElement>) => {\r\n setName(event.target.value)\r\n }\r\n\r\n const handleAddressChange = (event: React.ChangeEvent<HTMLInputElement>) => {\r\n setAddress(event.target.value)\r\n }\r\n\r\n const handleBuildingTypeChange = (event: React.ChangeEvent<HTMLSelectElement>) => {\r\n setType(event.target.value)\r\n }\r\n\r\n const selectedMatchingBuilding = matchingBuildings.find(\r\n building => building.id.toString() === selectedMatchingBuildingId,\r\n )\r\n\r\n const handleAttachExistingBuilding = async (building: Building) => {\r\n setIsAttaching(true)\r\n\r\n try {\r\n // Add OSM ID to existing building\r\n const result = await updateBuilding({\r\n buildingOsmId,\r\n // Set default storey number of 2 if building doesn't have one\r\n ...(building.buildingStoreyNum == null && { buildingStoreyNum: 2 })\r\n })\r\n\r\n toast.success(t('toastAttachSuccess'))\r\n\r\n // Clear the selected item after successful update\r\n if (setSelectedItem) {\r\n setSelectedItem(null)\r\n didSetSelectedItemRef.current = false\r\n }\r\n\r\n // Close the popover\r\n onCloseAction()\r\n\r\n } catch (error) {\r\n console.error('Error attaching building:', error)\r\n\r\n if (error?.status !== 401) {\r\n toast.error(t('toastAttachError'))\r\n }\r\n } finally {\r\n setIsAttaching(false)\r\n }\r\n }\r\n\r\n const handleCreateNewBuilding = async (event: React.FormEvent) => {\r\n event.preventDefault()\r\n\r\n // Get final values (prioritize user input, then existing data)\r\n const buildingName = name.trim() || featureProps._name || ''\r\n const finalAddress = address.trim() || address || ''\r\n\r\n if (!user?.organizationId) {\r\n toast.error(t('userNotFound'))\r\n return\r\n }\r\n\r\n // Use updated coordinates from marker if available\r\n const buildingCoordinates = coordinates || [featureProps.longitude, featureProps.latitude]\r\n\r\n const newBuilding: Partial<Building> = {\r\n buildingName,\r\n buildingAddress: finalAddress,\r\n buildingType: [type || 'Other'],\r\n buildingOsmId,\r\n buildingLatitude: buildingCoordinates ? buildingCoordinates[1] : null,\r\n buildingLongitude: buildingCoordinates ? buildingCoordinates[0] : null,\r\n buildingCountrySubdivision,\r\n buildingMunicipality,\r\n buildingPostalCode,\r\n }\r\n\r\n try {\r\n const result = await createBuilding({\r\n buildingData: newBuilding,\r\n organizationId: user.organizationId.toString(),\r\n })\r\n\r\n // Remove marker after creating building\r\n if (markerManagerRef.current) {\r\n markerManagerRef.current.remove()\r\n }\r\n\r\n toast.success(t('toastSuccess'))\r\n\r\n // Close the popover on success\r\n onCloseAction()\r\n }\r\n catch (error) {\r\n console.error('Error creating building:', error)\r\n toast.error(t('toastError'))\r\n }\r\n }\r\n\r\n const handleLocationEdit = () => {\r\n // Create marker at current coordinates with editing enabled\r\n if (markerManagerRef.current && coordinates && map) {\r\n console.log('Creating editable marker at coordinates:', coordinates)\r\n markerManagerRef.current.create(coordinates as [number, number], map, {\r\n editing: true\r\n })\r\n }\r\n else {\r\n console.warn('Cannot create marker: missing coordinates or map instance')\r\n }\r\n }\r\n\r\n return (\r\n <PopoverContent className=\"w-64 -m-1\" side=\"top\">\r\n <form onSubmit={handleCreateNewBuilding}>\r\n <div className=\"grid gap-4\">\r\n <Button\r\n type=\"button\"\r\n onClick={onCloseAction}\r\n variant=\"ghost\"\r\n className=\"absolute top-1 right-2 p-1 text-muted-foreground hover:text-foreground\"\r\n aria-label={t('closeAriaLabel')}\r\n >\r\n <LR.X className=\"w-4 h-4\" />\r\n </Button>\r\n\r\n {/* Show existing building if match found */}\r\n {showExistingBuildings && matchingBuildings.length > 0 && (\r\n <div className=\"space-y-1 mt-6\">\r\n <div className=\"text-xs text-muted-foreground\">\r\n {t('existingBuildingFound')}\r\n </div>\r\n <div className=\"flex flex-col justify-between gap-2 p-2 bg-muted/50 border rounded-lg\">\r\n <div className=\"grid gap-1\">\r\n <Label htmlFor=\"existing-building-select\" className=\"text-xs text-muted-foreground\">\r\n {t('selectExistingBuildingLabel')}\r\n </Label>\r\n <Select\r\n value={selectedMatchingBuildingId}\r\n onValueChange={setSelectedMatchingBuildingId}\r\n >\r\n <SelectTrigger id=\"existing-building-select\" className=\"h-9 w-full\">\r\n <SelectValue placeholder={t('selectExistingBuildingPlaceholder')} />\r\n </SelectTrigger>\r\n <SelectContent>\r\n {matchingBuildings.map((building) => (\r\n <SelectItem key={building.id} value={building.id.toString()}>\r\n {building.buildingName || building.buildingAddress || `${t('na')} (${building.id})`}\r\n </SelectItem>\r\n ))}\r\n </SelectContent>\r\n </Select>\r\n </div>\r\n <Button\r\n variant=\"outline\"\r\n size=\"sm\"\r\n className='w-full'\r\n onClick={() => selectedMatchingBuilding && handleAttachExistingBuilding(selectedMatchingBuilding)}\r\n disabled={isAttaching || isUpdating || !selectedMatchingBuilding}\r\n >\r\n {(isAttaching || isUpdating) ? (\r\n <>\r\n <LoadingSpinner />\r\n <span>{t('attaching')}</span>\r\n </>\r\n ) : (\r\n <>\r\n <LR.Link />\r\n {t('attachBuilding')}\r\n </>\r\n )}\r\n </Button>\r\n </div>\r\n </div>\r\n )}\r\n\r\n <div className=\"space-y-4\">\r\n <div className=\"grid gap-2\">\r\n <Label htmlFor=\"building-name\" className=\"text-xs text-muted-foreground\">\r\n {t('nameLabel')}\r\n </Label>\r\n <input\r\n id=\"building-name\"\r\n value={name}\r\n onChange={handleBuildingNameChange}\r\n placeholder={name || t('namePlaceholder')}\r\n className={`font-medium leading-none bg-transparent border-b border-gray-300 focus:outline-none focus:border-primary px-1 py-1 ${name ? 'placeholder:text-foreground' : 'placeholder:text-muted-foreground'}`}\r\n aria-label={t('nameInputAriaLabel')}\r\n required\r\n disabled={!ability.can('create', 'Building')}\r\n />\r\n </div>\r\n\r\n <div className=\"grid gap-2\">\r\n <Label htmlFor=\"building-address\" className=\"text-xs text-muted-foreground\">\r\n {t('addressLabel')}\r\n </Label>\r\n <input\r\n id=\"building-address\"\r\n value={address}\r\n onChange={handleAddressChange}\r\n placeholder={address || t('addressPlaceholder')}\r\n className={`font-medium leading-none bg-transparent border-b border-gray-300 focus:outline-none focus:border-primary px-1 py-1 ${address ? 'placeholder:text-foreground' : 'placeholder:text-muted-foreground'}`}\r\n aria-label={t('addressAriaLabel')}\r\n required\r\n disabled={!ability.can('create', 'Building')}\r\n />\r\n </div>\r\n\r\n <div className=\"grid gap-2\">\r\n <Label htmlFor=\"building-type\" className=\"text-xs text-muted-foreground\">\r\n {t('typeLabel')}\r\n </Label>\r\n <select\r\n id=\"building-type\"\r\n value={type}\r\n onChange={handleBuildingTypeChange}\r\n className=\"font-medium leading-none bg-transparent border-b border-gray-300 focus:outline-none focus:border-primary px-1 py-1\"\r\n aria-label={t('typeAriaLabel')}\r\n required\r\n disabled={!ability.can('create', 'Building')}\r\n >\r\n <option value=\"\">{t('defaultOption')}</option>\r\n <option value=\"Townhouses\">{t('townhouse')}</option>\r\n <option value=\"<4 Storey MURB\">{t('4MURB')}</option>\r\n <option value=\"4+ Storey MURB\">{t('4PlusMURB')}</option>\r\n <option value=\"Stacked Townhouses\">{t('stackedTownhouses')}</option>\r\n <option value=\"Detached\">{t('detached')}</option>\r\n <option value=\"Semi Detached\">{t('semiDetached')}</option>\r\n <option value=\"Other\">{t('other')}</option>\r\n </select>\r\n </div>\r\n </div>\r\n <div className=\"flex items-center gap-2 py-2 px-1\">\r\n <Label className=\"text-xs text-muted-foreground min-w-fit\">{t('osmId')}</Label>\r\n <span className=\"text-sm font-medium text-foreground\">{buildingOsmId ?? t('na')}</span>\r\n </div>\r\n </div>\r\n\r\n <div className=\"flex items-center gap-3 justify-center mt-4\">\r\n <Button\r\n type=\"button\"\r\n variant=\"outline\"\r\n aria-label=\"Edit Location\"\r\n title=\"Edit Location\"\r\n onClick={handleLocationEdit}\r\n disabled={!ability.can('update', 'Building')}\r\n >\r\n <LR.LocationEdit className=\"w-4 h-4\" />\r\n </Button>\r\n\r\n <Button\r\n type=\"submit\"\r\n variant=\"outline\"\r\n aria-label={t('addBuilding')}\r\n title={t('addBuilding')}\r\n disabled={isMutating || !ability.can('create', 'Building')}\r\n >\r\n {isMutating\r\n ? (\r\n <>``\r\n <LoadingSpinner />\r\n <span className=\"text-sm\">{t('creating')}</span>\r\n </>\r\n )\r\n : (\r\n <>\r\n <LR.Building2 className=\"w-4 h-4\" />\r\n <span className=\"text-sm\">{t('addBuilding')}</span>\r\n </>\r\n )}\r\n </Button>\r\n </div>\r\n </form>\r\n </PopoverContent>\r\n )\r\n}"],"mappings":"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","names":["_a"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../../src/core/components/viewers/map/src/MapLayers/index.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../../src/core/components/viewers/map/src/MapLayers/index.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAsB9B,eAAO,MAAM,SAAS,yBAYrB,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../src/core/components/viewers/map/src/MapLayers/index.tsx"],"sourcesContent":["'use client'\r\n\r\nimport * as React from 'react'\r\nimport dynamic from 'next/dynamic'\r\n\r\nimport { CountryLayer } from './src/CountryLayer'\r\nimport { CommentLayer } from './src/CommentLayer'\r\nimport { SensorLayers } from './src/SensorsLayer'\r\nimport { FileLayers } from './src/FileLayer'\r\nimport { OpenDataLayers } from './src/OpenDataLayer/src'\r\nimport { BuildingLayer } from './src/BuildingLayers'\r\nimport { useBimContext } from '../../../../../store'\r\n\r\
|
|
1
|
+
{"version":3,"sources":["../../../../../../../src/core/components/viewers/map/src/MapLayers/index.tsx"],"sourcesContent":["\r\n\r\n\r\n\r\n'use client'\r\n\r\nimport * as React from 'react'\r\nimport dynamic from 'next/dynamic'\r\n\r\nimport { CountryLayer } from './src/CountryLayer'\r\nimport { CommentLayer } from './src/CommentLayer'\r\nimport { SensorLayers } from './src/SensorsLayer'\r\nimport { FileLayers } from './src/FileLayer'\r\nimport { OpenDataLayers } from './src/OpenDataLayer/src'\r\nimport { BuildingLayer } from './src/BuildingLayers'\r\nimport { useBimContext } from '../../../../../store'\r\n\r\nconst LazyBimLayer = dynamic(\r\n () => import('./src/BimLayer').then(m => ({ default: m.BimLayer })),\r\n { ssr: false },\r\n)\r\n\r\nfunction BimLayerGate() {\r\n const { state: bimState } = useBimContext()\r\n if (bimState.bim.bimModelsAddedToMap.length === 0) return null\r\n return <LazyBimLayer />\r\n}\r\n\r\nexport const MapLayers = () => {\r\n return (\r\n <>\r\n <CountryLayer />\r\n <OpenDataLayers />\r\n <BuildingLayer />\r\n <CommentLayer />\r\n <SensorLayers />\r\n <FileLayers />\r\n <BimLayerGate />\r\n </>\r\n )\r\n}\r\n"],"mappings":";AAyBS,SAKL,UALK,KAKL,YALK;AAlBT,OAAO,aAAa;AAEpB,SAAS,oBAAoB;AAC7B,SAAS,oBAAoB;AAC7B,SAAS,oBAAoB;AAC7B,SAAS,kBAAkB;AAC3B,SAAS,sBAAsB;AAC/B,SAAS,qBAAqB;AAC9B,SAAS,qBAAqB;AAE9B,MAAM,eAAe;AAAA,EACnB,MAAM,OAAO,gBAAgB,EAAE,KAAK,QAAM,EAAE,SAAS,EAAE,SAAS,EAAE;AAAA,EAClE,EAAE,KAAK,MAAM;AACf;AAEA,SAAS,eAAe;AACtB,QAAM,EAAE,OAAO,SAAS,IAAI,cAAc;AAC1C,MAAI,SAAS,IAAI,oBAAoB,WAAW,EAAG,QAAO;AAC1D,SAAO,oBAAC,gBAAa;AACvB;AAEO,MAAM,YAAY,MAAM;AAC7B,SACE,iCACE;AAAA,wBAAC,gBAAa;AAAA,IACd,oBAAC,kBAAe;AAAA,IAChB,oBAAC,iBAAc;AAAA,IACf,oBAAC,gBAAa;AAAA,IACd,oBAAC,gBAAa;AAAA,IACd,oBAAC,cAAW;AAAA,IACZ,oBAAC,gBAAa;AAAA,KAChB;AAEJ;","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../../../../src/core/components/viewers/map/src/MapLayers/src/BimLayer/index.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAe/B,eAAO,MAAM,QAAQ,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../../../../src/core/components/viewers/map/src/MapLayers/src/BimLayer/index.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAe/B,eAAO,MAAM,QAAQ,yBAwdpB,CAAC"}
|
|
@@ -147,6 +147,16 @@ const BimLayer = () => {
|
|
|
147
147
|
let model = null;
|
|
148
148
|
let lastAppliedRotation = originalRotation;
|
|
149
149
|
let lastLodUpdate = 0;
|
|
150
|
+
let cachedTerrainElev = 0;
|
|
151
|
+
let lastMoveTime = performance.now();
|
|
152
|
+
const SETTLE_MS = 1e3;
|
|
153
|
+
const recomputeTerrainElev = () => {
|
|
154
|
+
const { lng, lat } = extractPositionAndRotation(bimFile, building);
|
|
155
|
+
if (lng !== null && lat !== null) {
|
|
156
|
+
const e = map.queryTerrainElevation([lng, lat]);
|
|
157
|
+
if (e !== null && e !== void 0) cachedTerrainElev = e;
|
|
158
|
+
}
|
|
159
|
+
};
|
|
150
160
|
let onMapMove;
|
|
151
161
|
let onMapMoveEnd;
|
|
152
162
|
const renderCamera = new THREE.PerspectiveCamera();
|
|
@@ -168,6 +178,8 @@ const BimLayer = () => {
|
|
|
168
178
|
this.scene = scene;
|
|
169
179
|
loadModel(buildingModel, fragments, scene, map2, lodCamera).then((loaded) => {
|
|
170
180
|
model = loaded;
|
|
181
|
+
lastMoveTime = performance.now();
|
|
182
|
+
recomputeTerrainElev();
|
|
171
183
|
map2.triggerRepaint();
|
|
172
184
|
});
|
|
173
185
|
if (!rendererRef.current) {
|
|
@@ -180,9 +192,12 @@ const BimLayer = () => {
|
|
|
180
192
|
}
|
|
181
193
|
this.renderer = rendererRef.current;
|
|
182
194
|
onMapMove = () => {
|
|
195
|
+
lastMoveTime = performance.now();
|
|
183
196
|
};
|
|
184
197
|
onMapMoveEnd = () => {
|
|
198
|
+
lastMoveTime = performance.now();
|
|
185
199
|
if (model) fragments.core.update(true);
|
|
200
|
+
recomputeTerrainElev();
|
|
186
201
|
map2.triggerRepaint();
|
|
187
202
|
};
|
|
188
203
|
map2.on("move", onMapMove);
|
|
@@ -223,7 +238,7 @@ const BimLayer = () => {
|
|
|
223
238
|
if (te !== void 0) modelElevation = te;
|
|
224
239
|
}
|
|
225
240
|
const modelOrigin = [modelLongitude, modelLatitude];
|
|
226
|
-
const terrainElev = (_a = map.queryTerrainElevation([modelLongitude, modelLatitude])) != null ? _a :
|
|
241
|
+
const terrainElev = editingBimModelRef.current === bimFile.name ? (_a = map.queryTerrainElevation([modelLongitude, modelLatitude])) != null ? _a : cachedTerrainElev : cachedTerrainElev;
|
|
227
242
|
const modelAltitude = modelElevation + terrainElev;
|
|
228
243
|
const scaling = 1;
|
|
229
244
|
const modelMatrix = map.transform.getMatrixForModel(modelOrigin, modelAltitude);
|
|
@@ -253,7 +268,9 @@ const BimLayer = () => {
|
|
|
253
268
|
lastLodUpdate = now;
|
|
254
269
|
fragments.core.update();
|
|
255
270
|
}
|
|
256
|
-
|
|
271
|
+
if (editingBimModelRef.current === bimFile.name || now - lastMoveTime < SETTLE_MS) {
|
|
272
|
+
map.triggerRepaint();
|
|
273
|
+
}
|
|
257
274
|
}
|
|
258
275
|
this.renderer.resetState();
|
|
259
276
|
this.renderer.render(this.scene, renderCamera);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../../../src/core/components/viewers/map/src/MapLayers/src/BimLayer/index.tsx"],"sourcesContent":["\"use client\"\r\n\r\nimport * as React from \"react\";\r\nimport { type CustomLayerInterface, type LngLatLike } from \"maplibre-gl\";\r\nimport * as OBC from \"@thatopen/components\";\r\nimport * as FRAGS from \"@thatopen/fragments\";\r\nimport * as THREE from \"three\";\r\nimport { BimContext, MapContext } from \"../../../../../../../store\";\r\nimport type { Building, DbFile } from \"../../../../../../../types/dbTypes\";\r\nimport { Map } from \"maplibre-gl\";\r\nimport { Marker } from \"react-map-gl/maplibre\";\r\nimport * as LR from \"lucide-react\";\r\nimport { EditPosition, extractPositionAndRotation } from \"../EditPosition\";\r\nimport { disposeThreeScene } from \"../disposeThreeScene\";\r\nimport type { FileAction } from \"../../../../../../../types/global\";\r\nimport { ViewerContextMenu } from '../../../../../../ui/FilesManager';\r\n\r\nexport const BimLayer = () => {\r\n\r\n const { state: mapState } = React.useContext(MapContext);\r\n const { map } = mapState.map;\r\n\r\n const { state: bimState, dispatch: bimDispatch } = React.useContext(BimContext);\r\n const { bimModelsAddedToMap, editingBimModel } = bimState.bim;\r\n\r\n const rendererRef = React.useRef<THREE.WebGLRenderer | null>(null);\r\n\r\n const addedLayersRef = React.useRef<Set<string>>(new Set());\r\n const prevModelsRef = React.useRef<Set<string>>(new Set());\r\n\r\n const sharedRef = React.useRef<{\r\n components: OBC.Components;\r\n fragments: OBC.FragmentsManager;\r\n } | null>(null);\r\n\r\n const [loadingBuildings, setLoadingBuildings] = React.useState<Set<number>>(new Set());\r\n const [contextMenu, setContextMenu] = React.useState<{\r\n x: number; y: number; bimFile: DbFile\r\n } | null>(null);\r\n\r\n const handleContextMenuAction = React.useCallback((action: FileAction, file: DbFile) => {\r\n if (action === 'view') {\r\n bimDispatch({ type: 'REMOVE_BIM_FROM_MAP', payload: { bimModelName: file.name } });\r\n } else if (action === 'move') {\r\n bimDispatch({ type: 'EDIT_BIM_MODEL_BY_NAME', payload: { editingBimModel: file.name } });\r\n }\r\n }, [bimDispatch]);\r\n\r\n const tempPositionsRef = React.useRef<Record<string, { lat: number; lng: number }>>({});\r\n const tempRotationsRef = React.useRef<Record<string, number>>({});\r\n const tempElevationsRef = React.useRef<Record<string, number>>({});\r\n\r\n const editingBimModelRef = React.useRef<string | null>(editingBimModel);\r\n React.useEffect(() => {\r\n editingBimModelRef.current = editingBimModel;\r\n }, [editingBimModel]);\r\n\r\n const handleExitEditMode = React.useCallback(() => {\r\n bimDispatch({ type: \"EDIT_BIM_MODEL_BY_NAME\", payload: { editingBimModel: null } });\r\n }, [bimDispatch]);\r\n\r\n const handleMapRepaint = React.useCallback(() => {\r\n if (map) map.triggerRepaint();\r\n }, [map]);\r\n const getOrCreateShared = React.useCallback(async () => {\r\n if (sharedRef.current) return sharedRef.current;\r\n\r\n const components = new OBC.Components();\r\n const worlds = components.get(OBC.Worlds);\r\n const world = worlds.create<OBC.SimpleScene, OBC.SimpleCamera, OBC.SimpleRenderer>();\r\n world.scene = new OBC.SimpleScene(components);\r\n world.renderer = new OBC.SimpleRenderer(components, document.createElement(\"div\"));\r\n world.camera = new OBC.SimpleCamera(components);\r\n components.init();\r\n world.scene.setup();\r\n\r\n const fragments = components.get(OBC.FragmentsManager);\r\n const fetchedUrl = await fetch(\"https://thatopen.github.io/engine_fragment/resources/worker.mjs\");\r\n const workerBlob = await fetchedUrl.blob();\r\n const workerUrl = URL.createObjectURL(\r\n new File([workerBlob], \"worker.mjs\", { type: \"text/javascript\" })\r\n );\r\n fragments.init(workerUrl);\r\n\r\n sharedRef.current = { components, fragments };\r\n return sharedRef.current;\r\n }, []);\r\n\r\n React.useEffect(() => {\r\n if (!map) return;\r\n\r\n const currentModelNames = new Set(bimModelsAddedToMap.map(bm => bm.bimFile.name));\r\n\r\n const modelsToRemove = Array.from(prevModelsRef.current).filter(\r\n name => !currentModelNames.has(name)\r\n );\r\n const modelsToAdd = bimModelsAddedToMap.filter(\r\n bm => !prevModelsRef.current.has(bm.bimFile.name)\r\n );\r\n\r\n modelsToRemove.forEach(layerName => {\r\n if (map.getLayer(layerName)) {\r\n map.removeLayer(layerName);\r\n addedLayersRef.current.delete(layerName);\r\n console.log(`Removed layer: ${layerName}`);\r\n }\r\n });\r\n\r\n prevModelsRef.current = currentModelNames;\r\n\r\n if (modelsToAdd.length === 0) return;\r\n\r\n // ─── Load a single BIM model ──────────────────────────────────────────\r\n const loadModel = async (\r\n buildingModel: { bimFile: DbFile; building?: Building | null },\r\n fragments: OBC.FragmentsManager,\r\n scene: THREE.Scene,\r\n map: Map,\r\n lodCamera: THREE.PerspectiveCamera\r\n ): Promise<FRAGS.FragmentsModel | null> => {\r\n\r\n const { bimFile, building } = buildingModel;\r\n if (!bimFile) return null;\r\n\r\n const buildingId = building?.id ?? bimFile.id;\r\n setLoadingBuildings(prev => new Set(prev).add(buildingId));\r\n\r\n try {\r\n const file = await fetch(bimFile.url);\r\n if (!file.ok) throw new Error(`Fetch failed: ${file.status} ${file.statusText}`);\r\n\r\n const buffer = await file.arrayBuffer();\r\n const model = await fragments.core.load(buffer, { modelId: bimFile.name });\r\n\r\n if (!model.box.isEmpty()) {\r\n const center = model.box.getCenter(new THREE.Vector3());\r\n const size = model.box.getSize(new THREE.Vector3());\r\n model.object.position.set(-center.x, 0, -center.z);\r\n\r\n // lodCamera: above the model's new IFC-space centre (XZ is now 0,0)\r\n lodCamera.position.set(0, center.y + Math.max(size.x, size.y, size.z) * 2, 0);\r\n lodCamera.lookAt(0, center.y, 0);\r\n } else {\r\n lodCamera.position.set(0, 100, 0);\r\n lodCamera.lookAt(0, 0, 0);\r\n }\r\n lodCamera.updateMatrixWorld();\r\n model.useCamera(lodCamera);\r\n\r\n model.graphicsQuality = 1;\r\n scene.add(model.object);\r\n fragments.core.update(true);\r\n map.triggerRepaint();\r\n\r\n return model;\r\n\r\n } catch (error) {\r\n console.error(\"Error loading BIM model:\", error);\r\n return null;\r\n } finally {\r\n setLoadingBuildings(prev => {\r\n const next = new Set(prev);\r\n next.delete(buildingId);\r\n return next;\r\n });\r\n }\r\n };\r\n\r\n // ─── Build a MapLibre custom layer for one model ──────────────────────\r\n const createCustomLayer = (\r\n buildingModel: { bimFile: DbFile; building?: Building | null },\r\n fragments: OBC.FragmentsManager,\r\n scene: THREE.Scene\r\n ): CustomLayerInterface | undefined => {\r\n\r\n const { bimFile, building } = buildingModel;\r\n const {\r\n lng: baseLng, lat: baseLat,\r\n rotation: originalRotation,\r\n } = extractPositionAndRotation(bimFile, building);\r\n if (baseLng === null || baseLat === null) return undefined;\r\n\r\n // Per-layer state kept in closure\r\n let model: FRAGS.FragmentsModel | null = null;\r\n let lastAppliedRotation = originalRotation;\r\n let lastLodUpdate = 0;\r\n\r\n let onMapMove: () => void;\r\n let onMapMoveEnd: () => void;\r\n const renderCamera = new THREE.PerspectiveCamera();\r\n const lodCamera = new THREE.PerspectiveCamera();\r\n // Reused per-frame matrices/vectors — render() must not allocate (audit B1).\r\n const _vp = new THREE.Matrix4();\r\n const _m = new THREE.Matrix4();\r\n const _p = new THREE.Matrix4();\r\n const _vm = new THREE.Matrix4();\r\n const _camPos = new THREE.Vector3();\r\n const _center = new THREE.Vector3();\r\n const _lookTarget = new THREE.Vector3();\r\n const _scaleVec = new THREE.Vector3(1, 1, 1);\r\n\r\n return {\r\n id: bimFile.name,\r\n type: \"custom\",\r\n renderingMode: \"3d\",\r\n\r\n onAdd(map, gl) {\r\n scene.rotateY(originalRotation * (Math.PI / 180));\r\n this.scene = scene;\r\n\r\n loadModel(buildingModel, fragments, scene, map, lodCamera).then(loaded => {\r\n model = loaded;\r\n map.triggerRepaint();\r\n });\r\n\r\n if (!rendererRef.current) {\r\n rendererRef.current = new THREE.WebGLRenderer({\r\n canvas: map.getCanvas(),\r\n context: gl,\r\n alpha: true,\r\n });\r\n rendererRef.current.autoClear = false;\r\n }\r\n this.renderer = rendererRef.current;\r\n onMapMove = () => { /* render() loop handles updates during movement */ };\r\n onMapMoveEnd = () => {\r\n if (model) fragments.core.update(true);\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(_, args) {\r\n if (\r\n editingBimModelRef.current === bimFile.name &&\r\n tempRotationsRef.current[bimFile.name] !== undefined\r\n ) {\r\n const cur = tempRotationsRef.current[bimFile.name];\r\n if (cur !== lastAppliedRotation) {\r\n scene.rotateY((cur - lastAppliedRotation) * (Math.PI / 180));\r\n lastAppliedRotation = cur;\r\n map.triggerRepaint();\r\n }\r\n } else {\r\n const { rotation: baseRot } = extractPositionAndRotation(bimFile, building);\r\n if (baseRot !== lastAppliedRotation) {\r\n scene.rotateY((baseRot - lastAppliedRotation) * (Math.PI / 180));\r\n lastAppliedRotation = baseRot;\r\n map.triggerRepaint();\r\n }\r\n }\r\n\r\n const {\r\n lng: baseLngNow, lat: baseLatNow,\r\n elevation: baseElevNow,\r\n } = extractPositionAndRotation(bimFile, building);\r\n\r\n let modelLongitude = baseLngNow;\r\n let modelLatitude = baseLatNow;\r\n let modelElevation = baseElevNow;\r\n\r\n if (editingBimModelRef.current === bimFile.name) {\r\n const tp = tempPositionsRef.current[bimFile.name];\r\n if (tp) { modelLongitude = tp.lng; modelLatitude = tp.lat; }\r\n const te = tempElevationsRef.current[bimFile.name];\r\n if (te !== undefined) modelElevation = te;\r\n }\r\n\r\n const modelOrigin = [modelLongitude, modelLatitude] as LngLatLike;\r\n const terrainElev = map.queryTerrainElevation([modelLongitude, modelLatitude]) ?? 0;\r\n const modelAltitude = modelElevation + terrainElev;\r\n const scaling = 1;\r\n\r\n // ── Build renderCamera (VP × M, for Three.js rendering) ───\r\n // Reused temps — render() allocates nothing (audit B1). Math is\r\n // identical to the prior allocate-every-frame version.\r\n const modelMatrix = map.transform.getMatrixForModel(modelOrigin, modelAltitude);\r\n _scaleVec.set(scaling, scaling, scaling);\r\n _vp.fromArray(args.defaultProjectionData.mainMatrix);\r\n _m.fromArray(modelMatrix).scale(_scaleVec);\r\n renderCamera.projectionMatrix.multiplyMatrices(_vp, _m); // VP × M, into the camera's own matrix\r\n\r\n // camIFCPos = translation of (P⁻¹ · (VP×M))⁻¹\r\n _p.fromArray(args.projectionMatrix).invert(); // _p = P⁻¹\r\n _vm.multiplyMatrices(_p, renderCamera.projectionMatrix).invert();\r\n _camPos.setFromMatrixPosition(_vm);\r\n lodCamera.position.copy(_camPos);\r\n\r\n if (model) {\r\n model.box.getCenter(_center);\r\n _lookTarget.set(0, _center.y, 0);\r\n } else {\r\n _lookTarget.set(0, 0, 0);\r\n }\r\n lodCamera.lookAt(_lookTarget);\r\n lodCamera.fov = args.fov * (180 / Math.PI) + 40;\r\n lodCamera.aspect = map.getCanvas().width / map.getCanvas().height;\r\n lodCamera.updateProjectionMatrix(); // rebuilds projectionMatrix from fov/aspect\r\n lodCamera.updateMatrixWorld(); // rebuilds matrixWorld + matrixWorldInverse\r\n\r\n if (model) {\r\n model.useCamera(lodCamera);\r\n const now = performance.now();\r\n if (now - lastLodUpdate > 50) {\r\n lastLodUpdate = now;\r\n fragments.core.update();\r\n }\r\n map.triggerRepaint();\r\n }\r\n\r\n this.renderer.resetState();\r\n this.renderer.render(this.scene, renderCamera);\r\n },\r\n\r\n onRemove() {\r\n if (onMapMove) map.off(\"move\", onMapMove);\r\n if (onMapMoveEnd) map.off(\"moveend\", onMapMoveEnd);\r\n if (model) {\r\n model.dispose().catch(console.warn);\r\n model = null;\r\n }\r\n // Dispose all Three.js GPU resources (geometries, materials, textures)\r\n disposeThreeScene(scene);\r\n },\r\n };\r\n };\r\n\r\n modelsToAdd.forEach(async buildingModel => {\r\n try {\r\n const { fragments } = await getOrCreateShared();\r\n\r\n const layerName = buildingModel.bimFile.name;\r\n if (map.getLayer(layerName)) return;\r\n const scene = new THREE.Scene();\r\n scene.add(new THREE.AmbientLight(0xffffff, 0.8));\r\n const sun = new THREE.DirectionalLight(0xffffff, 1.0);\r\n sun.position.set(10, 20, 10);\r\n scene.add(sun);\r\n\r\n const customLayer = createCustomLayer(buildingModel, fragments, scene);\r\n if (!customLayer) return;\r\n\r\n map.addLayer(customLayer);\r\n addedLayersRef.current.add(layerName);\r\n console.log(`Added layer: ${layerName}`);\r\n\r\n } catch (err) {\r\n console.error(\"Error adding BIM layer:\", err);\r\n }\r\n });\r\n\r\n }, [map, bimModelsAddedToMap]);\r\n\r\n // Cleanup on unmount — dispose shared components and renderer\r\n React.useEffect(() => {\r\n return () => {\r\n if (map) {\r\n addedLayersRef.current.forEach(layerName => {\r\n if (map.getLayer(layerName)) map.removeLayer(layerName);\r\n });\r\n addedLayersRef.current.clear();\r\n }\r\n if (sharedRef.current) {\r\n sharedRef.current.components.dispose();\r\n sharedRef.current = null;\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 (\r\n <>\r\n {bimModelsAddedToMap.map((buildingModel, index) => {\r\n const { lng, lat } = extractPositionAndRotation(\r\n buildingModel.bimFile, buildingModel.building\r\n );\r\n const isLoading = loadingBuildings.has(\r\n buildingModel.building?.id ?? buildingModel.bimFile.id\r\n );\r\n if (lng === null || lat === null) return null;\r\n return (\r\n <React.Fragment key={buildingModel.bimFile.id + \"-fragment-\" + index}>\r\n {isLoading && (\r\n <Marker\r\n key={buildingModel.bimFile.id + \"-loading-animation\" + index}\r\n latitude={lat}\r\n longitude={lng}\r\n >\r\n <LR.Loader className=\"animate-spin w-6 h-6 text-gray-700\" />\r\n </Marker>\r\n )}\r\n {/* Invisible hit-target for right-click context menu on WebGL-rendered BIM models */}\r\n {buildingModel.bimFile.name !== editingBimModel && (\r\n <Marker\r\n key={buildingModel.bimFile.id + \"-ctx-target-\" + index}\r\n latitude={lat}\r\n longitude={lng}\r\n >\r\n <div\r\n className=\"w-10 h-10 opacity-0 cursor-context-menu\"\r\n onContextMenu={(e) => {\r\n e.preventDefault();\r\n setContextMenu({ x: e.clientX, y: e.clientY, bimFile: buildingModel.bimFile });\r\n }}\r\n />\r\n </Marker>\r\n )}\r\n </React.Fragment>\r\n );\r\n })}\r\n {editingBimModel && (() => {\r\n const currentModel = bimModelsAddedToMap.find(bm => bm.bimFile.name === editingBimModel);\r\n if (!currentModel) return null;\r\n const { lng, lat, rotation, elevation } = extractPositionAndRotation(\r\n currentModel.bimFile, currentModel.building\r\n );\r\n return (\r\n <EditPosition\r\n file={currentModel.bimFile}\r\n displayName={currentModel.building?.buildingName || currentModel.bimFile.name}\r\n mode=\"3d\"\r\n initialLat={lat}\r\n initialLng={lng}\r\n initialRotation={rotation}\r\n initialElevation={elevation}\r\n onExitEditMode={handleExitEditMode}\r\n tempPositionsRef={tempPositionsRef}\r\n tempRotationsRef={tempRotationsRef}\r\n tempElevationsRef={tempElevationsRef}\r\n onMapRepaint={handleMapRepaint}\r\n />\r\n );\r\n })()}\r\n\r\n {contextMenu && (\r\n <ViewerContextMenu\r\n x={contextMenu.x}\r\n y={contextMenu.y}\r\n file={{ ...contextMenu.bimFile, isVisible: true }}\r\n options={['view', 'move']}\r\n onAction={handleContextMenuAction}\r\n onClose={() => setContextMenu(null)}\r\n />\r\n )}\r\n </>\r\n );\r\n\r\n};\r\n"],"mappings":"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","names":["map"]}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../../../src/core/components/viewers/map/src/MapLayers/src/BimLayer/index.tsx"],"sourcesContent":["\"use client\"\r\n\r\nimport * as React from \"react\";\r\nimport { type CustomLayerInterface, type LngLatLike } from \"maplibre-gl\";\r\nimport * as OBC from \"@thatopen/components\";\r\nimport * as FRAGS from \"@thatopen/fragments\";\r\nimport * as THREE from \"three\";\r\nimport { BimContext, MapContext } from \"../../../../../../../store\";\r\nimport type { Building, DbFile } from \"../../../../../../../types/dbTypes\";\r\nimport { Map } from \"maplibre-gl\";\r\nimport { Marker } from \"react-map-gl/maplibre\";\r\nimport * as LR from \"lucide-react\";\r\nimport { EditPosition, extractPositionAndRotation } from \"../EditPosition\";\r\nimport { disposeThreeScene } from \"../disposeThreeScene\";\r\nimport type { FileAction } from \"../../../../../../../types/global\";\r\nimport { ViewerContextMenu } from '../../../../../../ui/FilesManager';\r\n\r\nexport const BimLayer = () => {\r\n\r\n const { state: mapState } = React.useContext(MapContext);\r\n const { map } = mapState.map;\r\n\r\n const { state: bimState, dispatch: bimDispatch } = React.useContext(BimContext);\r\n const { bimModelsAddedToMap, editingBimModel } = bimState.bim;\r\n\r\n const rendererRef = React.useRef<THREE.WebGLRenderer | null>(null);\r\n\r\n const addedLayersRef = React.useRef<Set<string>>(new Set());\r\n const prevModelsRef = React.useRef<Set<string>>(new Set());\r\n\r\n const sharedRef = React.useRef<{\r\n components: OBC.Components;\r\n fragments: OBC.FragmentsManager;\r\n } | null>(null);\r\n\r\n const [loadingBuildings, setLoadingBuildings] = React.useState<Set<number>>(new Set());\r\n const [contextMenu, setContextMenu] = React.useState<{\r\n x: number; y: number; bimFile: DbFile\r\n } | null>(null);\r\n\r\n const handleContextMenuAction = React.useCallback((action: FileAction, file: DbFile) => {\r\n if (action === 'view') {\r\n bimDispatch({ type: 'REMOVE_BIM_FROM_MAP', payload: { bimModelName: file.name } });\r\n } else if (action === 'move') {\r\n bimDispatch({ type: 'EDIT_BIM_MODEL_BY_NAME', payload: { editingBimModel: file.name } });\r\n }\r\n }, [bimDispatch]);\r\n\r\n const tempPositionsRef = React.useRef<Record<string, { lat: number; lng: number }>>({});\r\n const tempRotationsRef = React.useRef<Record<string, number>>({});\r\n const tempElevationsRef = React.useRef<Record<string, number>>({});\r\n\r\n const editingBimModelRef = React.useRef<string | null>(editingBimModel);\r\n React.useEffect(() => {\r\n editingBimModelRef.current = editingBimModel;\r\n }, [editingBimModel]);\r\n\r\n const handleExitEditMode = React.useCallback(() => {\r\n bimDispatch({ type: \"EDIT_BIM_MODEL_BY_NAME\", payload: { editingBimModel: null } });\r\n }, [bimDispatch]);\r\n\r\n const handleMapRepaint = React.useCallback(() => {\r\n if (map) map.triggerRepaint();\r\n }, [map]);\r\n const getOrCreateShared = React.useCallback(async () => {\r\n if (sharedRef.current) return sharedRef.current;\r\n\r\n const components = new OBC.Components();\r\n const worlds = components.get(OBC.Worlds);\r\n const world = worlds.create<OBC.SimpleScene, OBC.SimpleCamera, OBC.SimpleRenderer>();\r\n world.scene = new OBC.SimpleScene(components);\r\n world.renderer = new OBC.SimpleRenderer(components, document.createElement(\"div\"));\r\n world.camera = new OBC.SimpleCamera(components);\r\n components.init();\r\n world.scene.setup();\r\n\r\n const fragments = components.get(OBC.FragmentsManager);\r\n const fetchedUrl = await fetch(\"https://thatopen.github.io/engine_fragment/resources/worker.mjs\");\r\n const workerBlob = await fetchedUrl.blob();\r\n const workerUrl = URL.createObjectURL(\r\n new File([workerBlob], \"worker.mjs\", { type: \"text/javascript\" })\r\n );\r\n fragments.init(workerUrl);\r\n\r\n sharedRef.current = { components, fragments };\r\n return sharedRef.current;\r\n }, []);\r\n\r\n React.useEffect(() => {\r\n if (!map) return;\r\n\r\n const currentModelNames = new Set(bimModelsAddedToMap.map(bm => bm.bimFile.name));\r\n\r\n const modelsToRemove = Array.from(prevModelsRef.current).filter(\r\n name => !currentModelNames.has(name)\r\n );\r\n const modelsToAdd = bimModelsAddedToMap.filter(\r\n bm => !prevModelsRef.current.has(bm.bimFile.name)\r\n );\r\n\r\n modelsToRemove.forEach(layerName => {\r\n if (map.getLayer(layerName)) {\r\n map.removeLayer(layerName);\r\n addedLayersRef.current.delete(layerName);\r\n console.log(`Removed layer: ${layerName}`);\r\n }\r\n });\r\n\r\n prevModelsRef.current = currentModelNames;\r\n\r\n if (modelsToAdd.length === 0) return;\r\n\r\n // ─── Load a single BIM model ──────────────────────────────────────────\r\n const loadModel = async (\r\n buildingModel: { bimFile: DbFile; building?: Building | null },\r\n fragments: OBC.FragmentsManager,\r\n scene: THREE.Scene,\r\n map: Map,\r\n lodCamera: THREE.PerspectiveCamera\r\n ): Promise<FRAGS.FragmentsModel | null> => {\r\n\r\n const { bimFile, building } = buildingModel;\r\n if (!bimFile) return null;\r\n\r\n const buildingId = building?.id ?? bimFile.id;\r\n setLoadingBuildings(prev => new Set(prev).add(buildingId));\r\n\r\n try {\r\n const file = await fetch(bimFile.url);\r\n if (!file.ok) throw new Error(`Fetch failed: ${file.status} ${file.statusText}`);\r\n\r\n const buffer = await file.arrayBuffer();\r\n const model = await fragments.core.load(buffer, { modelId: bimFile.name });\r\n\r\n if (!model.box.isEmpty()) {\r\n const center = model.box.getCenter(new THREE.Vector3());\r\n const size = model.box.getSize(new THREE.Vector3());\r\n model.object.position.set(-center.x, 0, -center.z);\r\n\r\n // lodCamera: above the model's new IFC-space centre (XZ is now 0,0)\r\n lodCamera.position.set(0, center.y + Math.max(size.x, size.y, size.z) * 2, 0);\r\n lodCamera.lookAt(0, center.y, 0);\r\n } else {\r\n lodCamera.position.set(0, 100, 0);\r\n lodCamera.lookAt(0, 0, 0);\r\n }\r\n lodCamera.updateMatrixWorld();\r\n model.useCamera(lodCamera);\r\n\r\n model.graphicsQuality = 1;\r\n scene.add(model.object);\r\n fragments.core.update(true);\r\n map.triggerRepaint();\r\n\r\n return model;\r\n\r\n } catch (error) {\r\n console.error(\"Error loading BIM model:\", error);\r\n return null;\r\n } finally {\r\n setLoadingBuildings(prev => {\r\n const next = new Set(prev);\r\n next.delete(buildingId);\r\n return next;\r\n });\r\n }\r\n };\r\n\r\n // ─── Build a MapLibre custom layer for one model ──────────────────────\r\n const createCustomLayer = (\r\n buildingModel: { bimFile: DbFile; building?: Building | null },\r\n fragments: OBC.FragmentsManager,\r\n scene: THREE.Scene\r\n ): CustomLayerInterface | undefined => {\r\n\r\n const { bimFile, building } = buildingModel;\r\n const {\r\n lng: baseLng, lat: baseLat,\r\n rotation: originalRotation,\r\n } = extractPositionAndRotation(bimFile, building);\r\n if (baseLng === null || baseLat === null) return undefined;\r\n\r\n // Per-layer state kept in closure\r\n let model: FRAGS.FragmentsModel | null = null;\r\n let lastAppliedRotation = originalRotation;\r\n let lastLodUpdate = 0;\r\n // Render-on-demand: cache terrain elevation off the per-frame path and\r\n // only keep repainting while the camera recently moved, so an idle map\r\n // with a placed model stops re-rendering instead of pinning the main\r\n // thread (this was the freeze after a geocoder flyTo to high zoom).\r\n let cachedTerrainElev = 0;\r\n let lastMoveTime = performance.now();\r\n const SETTLE_MS = 1000;\r\n const recomputeTerrainElev = () => {\r\n const { lng, lat } = extractPositionAndRotation(bimFile, building);\r\n if (lng !== null && lat !== null) {\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 let onMapMove: () => void;\r\n let onMapMoveEnd: () => void;\r\n const renderCamera = new THREE.PerspectiveCamera();\r\n const lodCamera = new THREE.PerspectiveCamera();\r\n // Reused per-frame matrices/vectors — render() must not allocate.\r\n const _vp = new THREE.Matrix4();\r\n const _m = new THREE.Matrix4();\r\n const _p = new THREE.Matrix4();\r\n const _vm = new THREE.Matrix4();\r\n const _camPos = new THREE.Vector3();\r\n const _center = new THREE.Vector3();\r\n const _lookTarget = new THREE.Vector3();\r\n const _scaleVec = new THREE.Vector3(1, 1, 1);\r\n\r\n return {\r\n id: bimFile.name,\r\n type: \"custom\",\r\n renderingMode: \"3d\",\r\n\r\n onAdd(map, gl) {\r\n scene.rotateY(originalRotation * (Math.PI / 180));\r\n this.scene = scene;\r\n\r\n loadModel(buildingModel, fragments, scene, map, lodCamera).then(loaded => {\r\n model = loaded;\r\n // Open the settle window so the just-loaded fragments stream\r\n // in and paint over the next frames, then idle.\r\n lastMoveTime = performance.now();\r\n recomputeTerrainElev();\r\n map.triggerRepaint();\r\n });\r\n\r\n if (!rendererRef.current) {\r\n rendererRef.current = new THREE.WebGLRenderer({\r\n canvas: map.getCanvas(),\r\n context: gl,\r\n alpha: true,\r\n });\r\n rendererRef.current.autoClear = false;\r\n }\r\n this.renderer = rendererRef.current;\r\n // Track movement so render() knows when it may stop repainting.\r\n onMapMove = () => { lastMoveTime = performance.now(); };\r\n onMapMoveEnd = () => {\r\n lastMoveTime = performance.now();\r\n if (model) fragments.core.update(true);\r\n // Terrain tiles for the new view are loaded once the camera\r\n // settles — refresh the cached elevation here, not per frame.\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(_, args) {\r\n if (\r\n editingBimModelRef.current === bimFile.name &&\r\n tempRotationsRef.current[bimFile.name] !== undefined\r\n ) {\r\n const cur = tempRotationsRef.current[bimFile.name];\r\n if (cur !== lastAppliedRotation) {\r\n scene.rotateY((cur - lastAppliedRotation) * (Math.PI / 180));\r\n lastAppliedRotation = cur;\r\n map.triggerRepaint();\r\n }\r\n } else {\r\n const { rotation: baseRot } = extractPositionAndRotation(bimFile, building);\r\n if (baseRot !== lastAppliedRotation) {\r\n scene.rotateY((baseRot - lastAppliedRotation) * (Math.PI / 180));\r\n lastAppliedRotation = baseRot;\r\n map.triggerRepaint();\r\n }\r\n }\r\n\r\n const {\r\n lng: baseLngNow, lat: baseLatNow,\r\n elevation: baseElevNow,\r\n } = extractPositionAndRotation(bimFile, building);\r\n\r\n let modelLongitude = baseLngNow;\r\n let modelLatitude = baseLatNow;\r\n let modelElevation = baseElevNow;\r\n\r\n if (editingBimModelRef.current === bimFile.name) {\r\n const tp = tempPositionsRef.current[bimFile.name];\r\n if (tp) { modelLongitude = tp.lng; modelLatitude = tp.lat; }\r\n const te = tempElevationsRef.current[bimFile.name];\r\n if (te !== undefined) modelElevation = te;\r\n }\r\n\r\n const modelOrigin = [modelLongitude, modelLatitude] as LngLatLike;\r\n // Use the cached terrain elevation (refreshed on moveend); query\r\n // live only while actively editing this model's position. Keeps\r\n // the expensive queryTerrainElevation off the hot path — the\r\n // per-frame query was a main driver of the high-zoom freeze.\r\n const terrainElev = editingBimModelRef.current === bimFile.name\r\n ? (map.queryTerrainElevation([modelLongitude, modelLatitude]) ?? cachedTerrainElev)\r\n : cachedTerrainElev;\r\n const modelAltitude = modelElevation + terrainElev;\r\n const scaling = 1;\r\n\r\n // ── Build renderCamera (VP × M, for Three.js rendering) ───\r\n // Reused temps — render() allocates nothing. Math is identical\r\n // to the prior allocate-every-frame version.\r\n const modelMatrix = map.transform.getMatrixForModel(modelOrigin, modelAltitude);\r\n _scaleVec.set(scaling, scaling, scaling);\r\n _vp.fromArray(args.defaultProjectionData.mainMatrix);\r\n _m.fromArray(modelMatrix).scale(_scaleVec);\r\n renderCamera.projectionMatrix.multiplyMatrices(_vp, _m); // VP × M, into the camera's own matrix\r\n\r\n // camIFCPos = translation of (P⁻¹ · (VP×M))⁻¹\r\n _p.fromArray(args.projectionMatrix).invert(); // _p = P⁻¹\r\n _vm.multiplyMatrices(_p, renderCamera.projectionMatrix).invert();\r\n _camPos.setFromMatrixPosition(_vm);\r\n lodCamera.position.copy(_camPos);\r\n\r\n if (model) {\r\n model.box.getCenter(_center);\r\n _lookTarget.set(0, _center.y, 0);\r\n } else {\r\n _lookTarget.set(0, 0, 0);\r\n }\r\n lodCamera.lookAt(_lookTarget);\r\n lodCamera.fov = args.fov * (180 / Math.PI) + 40;\r\n lodCamera.aspect = map.getCanvas().width / map.getCanvas().height;\r\n lodCamera.updateProjectionMatrix(); // rebuilds projectionMatrix from fov/aspect\r\n lodCamera.updateMatrixWorld(); // rebuilds matrixWorld + matrixWorldInverse\r\n\r\n if (model) {\r\n model.useCamera(lodCamera);\r\n const now = performance.now();\r\n if (now - lastLodUpdate > 50) {\r\n lastLodUpdate = now;\r\n fragments.core.update();\r\n }\r\n // Render-on-demand: only keep the frame loop alive while the\r\n // camera recently moved (settle window, for fragment\r\n // streaming) or this model is being edited. Idle map ⇒ no\r\n // self-scheduled repaints ⇒ main thread freed (no freeze).\r\n if (\r\n editingBimModelRef.current === bimFile.name ||\r\n now - lastMoveTime < SETTLE_MS\r\n ) {\r\n map.triggerRepaint();\r\n }\r\n }\r\n\r\n this.renderer.resetState();\r\n this.renderer.render(this.scene, renderCamera);\r\n },\r\n\r\n onRemove() {\r\n if (onMapMove) map.off(\"move\", onMapMove);\r\n if (onMapMoveEnd) map.off(\"moveend\", onMapMoveEnd);\r\n if (model) {\r\n model.dispose().catch(console.warn);\r\n model = null;\r\n }\r\n // Dispose all Three.js GPU resources (geometries, materials, textures)\r\n disposeThreeScene(scene);\r\n },\r\n };\r\n };\r\n\r\n modelsToAdd.forEach(async buildingModel => {\r\n try {\r\n const { fragments } = await getOrCreateShared();\r\n\r\n const layerName = buildingModel.bimFile.name;\r\n if (map.getLayer(layerName)) return;\r\n const scene = new THREE.Scene();\r\n scene.add(new THREE.AmbientLight(0xffffff, 0.8));\r\n const sun = new THREE.DirectionalLight(0xffffff, 1.0);\r\n sun.position.set(10, 20, 10);\r\n scene.add(sun);\r\n\r\n const customLayer = createCustomLayer(buildingModel, fragments, scene);\r\n if (!customLayer) return;\r\n\r\n map.addLayer(customLayer);\r\n addedLayersRef.current.add(layerName);\r\n console.log(`Added layer: ${layerName}`);\r\n\r\n } catch (err) {\r\n console.error(\"Error adding BIM layer:\", err);\r\n }\r\n });\r\n\r\n }, [map, bimModelsAddedToMap]);\r\n\r\n // Cleanup on unmount — dispose shared components and renderer\r\n React.useEffect(() => {\r\n return () => {\r\n if (map) {\r\n addedLayersRef.current.forEach(layerName => {\r\n if (map.getLayer(layerName)) map.removeLayer(layerName);\r\n });\r\n addedLayersRef.current.clear();\r\n }\r\n if (sharedRef.current) {\r\n sharedRef.current.components.dispose();\r\n sharedRef.current = null;\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 (\r\n <>\r\n {bimModelsAddedToMap.map((buildingModel, index) => {\r\n const { lng, lat } = extractPositionAndRotation(\r\n buildingModel.bimFile, buildingModel.building\r\n );\r\n const isLoading = loadingBuildings.has(\r\n buildingModel.building?.id ?? buildingModel.bimFile.id\r\n );\r\n if (lng === null || lat === null) return null;\r\n return (\r\n <React.Fragment key={buildingModel.bimFile.id + \"-fragment-\" + index}>\r\n {isLoading && (\r\n <Marker\r\n key={buildingModel.bimFile.id + \"-loading-animation\" + index}\r\n latitude={lat}\r\n longitude={lng}\r\n >\r\n <LR.Loader className=\"animate-spin w-6 h-6 text-gray-700\" />\r\n </Marker>\r\n )}\r\n {/* Invisible hit-target for right-click context menu on WebGL-rendered BIM models */}\r\n {buildingModel.bimFile.name !== editingBimModel && (\r\n <Marker\r\n key={buildingModel.bimFile.id + \"-ctx-target-\" + index}\r\n latitude={lat}\r\n longitude={lng}\r\n >\r\n <div\r\n className=\"w-10 h-10 opacity-0 cursor-context-menu\"\r\n onContextMenu={(e) => {\r\n e.preventDefault();\r\n setContextMenu({ x: e.clientX, y: e.clientY, bimFile: buildingModel.bimFile });\r\n }}\r\n />\r\n </Marker>\r\n )}\r\n </React.Fragment>\r\n );\r\n })}\r\n {editingBimModel && (() => {\r\n const currentModel = bimModelsAddedToMap.find(bm => bm.bimFile.name === editingBimModel);\r\n if (!currentModel) return null;\r\n const { lng, lat, rotation, elevation } = extractPositionAndRotation(\r\n currentModel.bimFile, currentModel.building\r\n );\r\n return (\r\n <EditPosition\r\n file={currentModel.bimFile}\r\n displayName={currentModel.building?.buildingName || currentModel.bimFile.name}\r\n mode=\"3d\"\r\n initialLat={lat}\r\n initialLng={lng}\r\n initialRotation={rotation}\r\n initialElevation={elevation}\r\n onExitEditMode={handleExitEditMode}\r\n tempPositionsRef={tempPositionsRef}\r\n tempRotationsRef={tempRotationsRef}\r\n tempElevationsRef={tempElevationsRef}\r\n onMapRepaint={handleMapRepaint}\r\n />\r\n );\r\n })()}\r\n\r\n {contextMenu && (\r\n <ViewerContextMenu\r\n x={contextMenu.x}\r\n y={contextMenu.y}\r\n file={{ ...contextMenu.bimFile, isVisible: true }}\r\n options={['view', 'move']}\r\n onAction={handleContextMenuAction}\r\n onClose={() => setContextMenu(null)}\r\n />\r\n )}\r\n </>\r\n );\r\n\r\n};\r\n"],"mappings":"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","names":["map"]}
|
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"}
|