@ifc-lite/viewer 1.17.3 → 1.17.6
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/.turbo/turbo-build.log +39 -30
- package/.turbo/turbo-typecheck.log +1 -1
- package/CHANGELOG.md +132 -0
- package/DESKTOP_CONTRACT_VERSION +1 -0
- package/dist/assets/arrow-CZ5kQ26f.js +20 -0
- package/dist/assets/basketViewActivator-86rgogji.js +1 -0
- package/dist/assets/{bcf-D5-QWGO9.js → bcf-DOG9_WPX.js} +1 -1
- package/dist/assets/{browser-CKs-FY1P.js → browser-C5TFR7sH.js} +1 -1
- package/dist/assets/cesium-ADbP7waU.css +1 -0
- package/dist/assets/cesium-DUOzBlqv.js +17817 -0
- package/dist/assets/drawing-2d-DoxKMqbO.js +257 -0
- package/dist/assets/{exporters-C_6J153K.js → exporters-CcPS9MK5.js} +2898 -2380
- package/dist/assets/geometry.worker-BFUYA08u.js +1 -0
- package/dist/assets/ids-DQ5jY0E8.js +1 -0
- package/dist/assets/ifc-lite_bg-BINvzoCP.wasm +0 -0
- package/dist/assets/{index-jhBr1wbn.js → index-Bfms9I4A.js} +41036 -34613
- package/dist/assets/index-_bfZsDCC.css +1 -0
- package/dist/assets/{maplibre-gl-BpvwNKKy.js → maplibre-gl-CGLcoNXc.js} +1 -1
- package/dist/assets/native-bridge-DUyLCMZS.js +429 -0
- package/dist/assets/{sandbox-B79eavQ3.js → sandbox-C8575tul.js} +4342 -4324
- package/dist/assets/{server-client-D3bUPJJc.js → server-client-BuZK7OST.js} +4 -4
- package/dist/assets/tauri-core-stub-D8Fa-u43.js +1 -0
- package/dist/assets/tauri-dialog-stub-r7Wksg7o.js +1 -0
- package/dist/assets/tauri-fs-stub-BdeRC7aK.js +1 -0
- package/dist/assets/wasm-bridge-JsqEGDV8.js +1 -0
- package/dist/assets/{zip-B-jFFAGa.js → zip-DBEtpeu6.js} +3 -3
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_0.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_1.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_10.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_11.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_12.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_13.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_14.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_15.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_16.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_17.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_18.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_19.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_2.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_20.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_21.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_22.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_23.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_24.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_25.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_26.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_27.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_3.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_4.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_5.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_6.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_7.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_8.json +1 -0
- package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_9.json +1 -0
- package/dist/cesium/Assets/Images/bing_maps_credit.png +0 -0
- package/dist/cesium/Assets/Images/cesium_credit.png +0 -0
- package/dist/cesium/Assets/Images/google_earth_credit.png +0 -0
- package/dist/cesium/Assets/Images/ion-credit.png +0 -0
- package/dist/cesium/Assets/Textures/LensFlare/DirtMask.jpg +0 -0
- package/dist/cesium/Assets/Textures/LensFlare/StarBurst.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/0/0/0.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/0/1/0.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/1/0/0.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/1/0/1.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/1/1/0.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/1/1/1.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/1/2/0.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/1/2/1.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/1/3/0.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/1/3/1.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/0/0.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/0/1.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/0/2.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/0/3.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/1/0.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/1/1.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/1/2.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/1/3.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/2/0.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/2/1.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/2/2.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/2/3.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/3/0.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/3/1.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/3/2.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/3/3.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/4/0.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/4/1.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/4/2.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/4/3.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/5/0.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/5/1.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/5/2.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/5/3.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/6/0.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/6/1.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/6/2.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/6/3.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/7/0.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/7/1.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/7/2.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/2/7/3.jpg +0 -0
- package/dist/cesium/Assets/Textures/NaturalEarthII/tilemapresource.xml +14 -0
- package/dist/cesium/Assets/Textures/SkyBox/tycho2t3_80_mx.jpg +0 -0
- package/dist/cesium/Assets/Textures/SkyBox/tycho2t3_80_my.jpg +0 -0
- package/dist/cesium/Assets/Textures/SkyBox/tycho2t3_80_mz.jpg +0 -0
- package/dist/cesium/Assets/Textures/SkyBox/tycho2t3_80_px.jpg +0 -0
- package/dist/cesium/Assets/Textures/SkyBox/tycho2t3_80_py.jpg +0 -0
- package/dist/cesium/Assets/Textures/SkyBox/tycho2t3_80_pz.jpg +0 -0
- package/dist/cesium/Assets/Textures/maki/airfield.png +0 -0
- package/dist/cesium/Assets/Textures/maki/airport.png +0 -0
- package/dist/cesium/Assets/Textures/maki/alcohol-shop.png +0 -0
- package/dist/cesium/Assets/Textures/maki/america-football.png +0 -0
- package/dist/cesium/Assets/Textures/maki/art-gallery.png +0 -0
- package/dist/cesium/Assets/Textures/maki/bakery.png +0 -0
- package/dist/cesium/Assets/Textures/maki/bank.png +0 -0
- package/dist/cesium/Assets/Textures/maki/bar.png +0 -0
- package/dist/cesium/Assets/Textures/maki/baseball.png +0 -0
- package/dist/cesium/Assets/Textures/maki/basketball.png +0 -0
- package/dist/cesium/Assets/Textures/maki/beer.png +0 -0
- package/dist/cesium/Assets/Textures/maki/bicycle.png +0 -0
- package/dist/cesium/Assets/Textures/maki/building.png +0 -0
- package/dist/cesium/Assets/Textures/maki/bus.png +0 -0
- package/dist/cesium/Assets/Textures/maki/cafe.png +0 -0
- package/dist/cesium/Assets/Textures/maki/camera.png +0 -0
- package/dist/cesium/Assets/Textures/maki/campsite.png +0 -0
- package/dist/cesium/Assets/Textures/maki/car.png +0 -0
- package/dist/cesium/Assets/Textures/maki/cemetery.png +0 -0
- package/dist/cesium/Assets/Textures/maki/cesium.png +0 -0
- package/dist/cesium/Assets/Textures/maki/chemist.png +0 -0
- package/dist/cesium/Assets/Textures/maki/cinema.png +0 -0
- package/dist/cesium/Assets/Textures/maki/circle-stroked.png +0 -0
- package/dist/cesium/Assets/Textures/maki/circle.png +0 -0
- package/dist/cesium/Assets/Textures/maki/city.png +0 -0
- package/dist/cesium/Assets/Textures/maki/clothing-store.png +0 -0
- package/dist/cesium/Assets/Textures/maki/college.png +0 -0
- package/dist/cesium/Assets/Textures/maki/commercial.png +0 -0
- package/dist/cesium/Assets/Textures/maki/cricket.png +0 -0
- package/dist/cesium/Assets/Textures/maki/cross.png +0 -0
- package/dist/cesium/Assets/Textures/maki/dam.png +0 -0
- package/dist/cesium/Assets/Textures/maki/danger.png +0 -0
- package/dist/cesium/Assets/Textures/maki/disability.png +0 -0
- package/dist/cesium/Assets/Textures/maki/dog-park.png +0 -0
- package/dist/cesium/Assets/Textures/maki/embassy.png +0 -0
- package/dist/cesium/Assets/Textures/maki/emergency-telephone.png +0 -0
- package/dist/cesium/Assets/Textures/maki/entrance.png +0 -0
- package/dist/cesium/Assets/Textures/maki/farm.png +0 -0
- package/dist/cesium/Assets/Textures/maki/fast-food.png +0 -0
- package/dist/cesium/Assets/Textures/maki/ferry.png +0 -0
- package/dist/cesium/Assets/Textures/maki/fire-station.png +0 -0
- package/dist/cesium/Assets/Textures/maki/fuel.png +0 -0
- package/dist/cesium/Assets/Textures/maki/garden.png +0 -0
- package/dist/cesium/Assets/Textures/maki/gift.png +0 -0
- package/dist/cesium/Assets/Textures/maki/golf.png +0 -0
- package/dist/cesium/Assets/Textures/maki/grocery.png +0 -0
- package/dist/cesium/Assets/Textures/maki/hairdresser.png +0 -0
- package/dist/cesium/Assets/Textures/maki/harbor.png +0 -0
- package/dist/cesium/Assets/Textures/maki/heart.png +0 -0
- package/dist/cesium/Assets/Textures/maki/heliport.png +0 -0
- package/dist/cesium/Assets/Textures/maki/hospital.png +0 -0
- package/dist/cesium/Assets/Textures/maki/ice-cream.png +0 -0
- package/dist/cesium/Assets/Textures/maki/industrial.png +0 -0
- package/dist/cesium/Assets/Textures/maki/land-use.png +0 -0
- package/dist/cesium/Assets/Textures/maki/laundry.png +0 -0
- package/dist/cesium/Assets/Textures/maki/library.png +0 -0
- package/dist/cesium/Assets/Textures/maki/lighthouse.png +0 -0
- package/dist/cesium/Assets/Textures/maki/lodging.png +0 -0
- package/dist/cesium/Assets/Textures/maki/logging.png +0 -0
- package/dist/cesium/Assets/Textures/maki/london-underground.png +0 -0
- package/dist/cesium/Assets/Textures/maki/marker-stroked.png +0 -0
- package/dist/cesium/Assets/Textures/maki/marker.png +0 -0
- package/dist/cesium/Assets/Textures/maki/minefield.png +0 -0
- package/dist/cesium/Assets/Textures/maki/mobilephone.png +0 -0
- package/dist/cesium/Assets/Textures/maki/monument.png +0 -0
- package/dist/cesium/Assets/Textures/maki/museum.png +0 -0
- package/dist/cesium/Assets/Textures/maki/music.png +0 -0
- package/dist/cesium/Assets/Textures/maki/oil-well.png +0 -0
- package/dist/cesium/Assets/Textures/maki/park.png +0 -0
- package/dist/cesium/Assets/Textures/maki/park2.png +0 -0
- package/dist/cesium/Assets/Textures/maki/parking-garage.png +0 -0
- package/dist/cesium/Assets/Textures/maki/parking.png +0 -0
- package/dist/cesium/Assets/Textures/maki/pharmacy.png +0 -0
- package/dist/cesium/Assets/Textures/maki/pitch.png +0 -0
- package/dist/cesium/Assets/Textures/maki/place-of-worship.png +0 -0
- package/dist/cesium/Assets/Textures/maki/playground.png +0 -0
- package/dist/cesium/Assets/Textures/maki/police.png +0 -0
- package/dist/cesium/Assets/Textures/maki/polling-place.png +0 -0
- package/dist/cesium/Assets/Textures/maki/post.png +0 -0
- package/dist/cesium/Assets/Textures/maki/prison.png +0 -0
- package/dist/cesium/Assets/Textures/maki/rail-above.png +0 -0
- package/dist/cesium/Assets/Textures/maki/rail-light.png +0 -0
- package/dist/cesium/Assets/Textures/maki/rail-metro.png +0 -0
- package/dist/cesium/Assets/Textures/maki/rail-underground.png +0 -0
- package/dist/cesium/Assets/Textures/maki/rail.png +0 -0
- package/dist/cesium/Assets/Textures/maki/religious-christian.png +0 -0
- package/dist/cesium/Assets/Textures/maki/religious-jewish.png +0 -0
- package/dist/cesium/Assets/Textures/maki/religious-muslim.png +0 -0
- package/dist/cesium/Assets/Textures/maki/restaurant.png +0 -0
- package/dist/cesium/Assets/Textures/maki/roadblock.png +0 -0
- package/dist/cesium/Assets/Textures/maki/rocket.png +0 -0
- package/dist/cesium/Assets/Textures/maki/school.png +0 -0
- package/dist/cesium/Assets/Textures/maki/scooter.png +0 -0
- package/dist/cesium/Assets/Textures/maki/shop.png +0 -0
- package/dist/cesium/Assets/Textures/maki/skiing.png +0 -0
- package/dist/cesium/Assets/Textures/maki/slaughterhouse.png +0 -0
- package/dist/cesium/Assets/Textures/maki/soccer.png +0 -0
- package/dist/cesium/Assets/Textures/maki/square-stroked.png +0 -0
- package/dist/cesium/Assets/Textures/maki/square.png +0 -0
- package/dist/cesium/Assets/Textures/maki/star-stroked.png +0 -0
- package/dist/cesium/Assets/Textures/maki/star.png +0 -0
- package/dist/cesium/Assets/Textures/maki/suitcase.png +0 -0
- package/dist/cesium/Assets/Textures/maki/swimming.png +0 -0
- package/dist/cesium/Assets/Textures/maki/telephone.png +0 -0
- package/dist/cesium/Assets/Textures/maki/tennis.png +0 -0
- package/dist/cesium/Assets/Textures/maki/theatre.png +0 -0
- package/dist/cesium/Assets/Textures/maki/toilets.png +0 -0
- package/dist/cesium/Assets/Textures/maki/town-hall.png +0 -0
- package/dist/cesium/Assets/Textures/maki/town.png +0 -0
- package/dist/cesium/Assets/Textures/maki/triangle-stroked.png +0 -0
- package/dist/cesium/Assets/Textures/maki/triangle.png +0 -0
- package/dist/cesium/Assets/Textures/maki/village.png +0 -0
- package/dist/cesium/Assets/Textures/maki/warehouse.png +0 -0
- package/dist/cesium/Assets/Textures/maki/waste-basket.png +0 -0
- package/dist/cesium/Assets/Textures/maki/water.png +0 -0
- package/dist/cesium/Assets/Textures/maki/wetland.png +0 -0
- package/dist/cesium/Assets/Textures/maki/zoo.png +0 -0
- package/dist/cesium/Assets/Textures/moonSmall.jpg +0 -0
- package/dist/cesium/Assets/Textures/pin.svg +1 -0
- package/dist/cesium/Assets/Textures/waterNormals.jpg +0 -0
- package/dist/cesium/Assets/Textures/waterNormalsSmall.jpg +0 -0
- package/dist/cesium/Assets/approximateTerrainHeights.json +1 -0
- package/dist/cesium/ThirdParty/Workers/package.json +1 -0
- package/dist/cesium/ThirdParty/Workers/zip-web-worker.js +1 -0
- package/dist/cesium/ThirdParty/basis_transcoder.wasm +0 -0
- package/dist/cesium/ThirdParty/draco_decoder.wasm +0 -0
- package/dist/cesium/ThirdParty/google-earth-dbroot-parser.js +1 -0
- package/dist/cesium/ThirdParty/wasm_splats_bg.wasm +0 -0
- package/dist/cesium/ThirdParty/zip-module.wasm +0 -0
- package/dist/cesium/Widgets/Animation/Animation.css +127 -0
- package/dist/cesium/Widgets/Animation/lighter.css +70 -0
- package/dist/cesium/Widgets/BaseLayerPicker/BaseLayerPicker.css +108 -0
- package/dist/cesium/Widgets/BaseLayerPicker/lighter.css +22 -0
- package/dist/cesium/Widgets/Cesium3DTilesInspector/Cesium3DTilesInspector.css +102 -0
- package/dist/cesium/Widgets/CesiumInspector/CesiumInspector.css +113 -0
- package/dist/cesium/Widgets/CesiumWidget/CesiumWidget.css +119 -0
- package/dist/cesium/Widgets/CesiumWidget/lighter.css +14 -0
- package/dist/cesium/Widgets/FullscreenButton/FullscreenButton.css +8 -0
- package/dist/cesium/Widgets/Geocoder/Geocoder.css +70 -0
- package/dist/cesium/Widgets/Geocoder/lighter.css +17 -0
- package/dist/cesium/Widgets/I3SBuildingSceneLayerExplorer/I3SBuildingSceneLayerExplorer.css +27 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/ArcGisMapServiceWorldHillshade.png +0 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/ArcGisMapServiceWorldImagery.png +0 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/ArcGisMapServiceWorldOcean.png +0 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/azureAerial.png +0 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/azureRoads.png +0 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/bingAerial.png +0 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/bingAerialLabels.png +0 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/bingRoads.png +0 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/blueMarble.png +0 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/earthAtNight.png +0 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/googleContour.png +0 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/googleRoadmap.png +0 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/googleSatellite.png +0 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/googleSatelliteLabels.png +0 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/mapQuestOpenStreetMap.png +0 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/mapboxSatellite.png +0 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/mapboxStreets.png +0 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/mapboxTerrain.png +0 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/naturalEarthII.png +0 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/openStreetMap.png +0 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/sentinel-2.png +0 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/stadiaAlidadeSmooth.png +0 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/stadiaAlidadeSmoothDark.png +0 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/stamenToner.png +0 -0
- package/dist/cesium/Widgets/Images/ImageryProviders/stamenWatercolor.png +0 -0
- package/dist/cesium/Widgets/Images/NavigationHelp/Mouse.svg +84 -0
- package/dist/cesium/Widgets/Images/NavigationHelp/MouseLeft.svg +76 -0
- package/dist/cesium/Widgets/Images/NavigationHelp/MouseMiddle.svg +76 -0
- package/dist/cesium/Widgets/Images/NavigationHelp/MouseRight.svg +76 -0
- package/dist/cesium/Widgets/Images/NavigationHelp/Touch.svg +120 -0
- package/dist/cesium/Widgets/Images/NavigationHelp/TouchDrag.svg +129 -0
- package/dist/cesium/Widgets/Images/NavigationHelp/TouchRotate.svg +76 -0
- package/dist/cesium/Widgets/Images/NavigationHelp/TouchTilt.svg +135 -0
- package/dist/cesium/Widgets/Images/NavigationHelp/TouchZoom.svg +74 -0
- package/dist/cesium/Widgets/Images/TerrainProviders/CesiumWorldTerrain.png +0 -0
- package/dist/cesium/Widgets/Images/TerrainProviders/Ellipsoid.png +0 -0
- package/dist/cesium/Widgets/Images/TimelineIcons.png +0 -0
- package/dist/cesium/Widgets/Images/info-loading.gif +0 -0
- package/dist/cesium/Widgets/InfoBox/InfoBox.css +92 -0
- package/dist/cesium/Widgets/InfoBox/InfoBoxDescription.css +178 -0
- package/dist/cesium/Widgets/NavigationHelpButton/NavigationHelpButton.css +93 -0
- package/dist/cesium/Widgets/NavigationHelpButton/lighter.css +38 -0
- package/dist/cesium/Widgets/PerformanceWatchdog/PerformanceWatchdog.css +15 -0
- package/dist/cesium/Widgets/ProjectionPicker/ProjectionPicker.css +38 -0
- package/dist/cesium/Widgets/SceneModePicker/SceneModePicker.css +56 -0
- package/dist/cesium/Widgets/SelectionIndicator/SelectionIndicator.css +20 -0
- package/dist/cesium/Widgets/Timeline/Timeline.css +103 -0
- package/dist/cesium/Widgets/Timeline/lighter.css +23 -0
- package/dist/cesium/Widgets/VRButton/VRButton.css +8 -0
- package/dist/cesium/Widgets/Viewer/Viewer.css +107 -0
- package/dist/cesium/Widgets/VoxelInspector/VoxelInspector.css +16 -0
- package/dist/cesium/Widgets/lighter.css +237 -0
- package/dist/cesium/Widgets/lighterShared.css +46 -0
- package/dist/cesium/Widgets/shared.css +103 -0
- package/dist/cesium/Widgets/widgets.css +1342 -0
- package/dist/cesium/Workers/chunk-23ZQ2IVV.js +29 -0
- package/dist/cesium/Workers/chunk-2EQO3Q56.js +26 -0
- package/dist/cesium/Workers/chunk-2MJIIVP4.js +26 -0
- package/dist/cesium/Workers/chunk-2TE5NTVD.js +26 -0
- package/dist/cesium/Workers/chunk-2ZBHLJST.js +26 -0
- package/dist/cesium/Workers/chunk-5TJMAQVL.js +26 -0
- package/dist/cesium/Workers/chunk-6BD4U3VO.js +26 -0
- package/dist/cesium/Workers/chunk-7TVGLKQF.js +26 -0
- package/dist/cesium/Workers/chunk-BTSYJ5XU.js +26 -0
- package/dist/cesium/Workers/chunk-BXMEEOCS.js +63 -0
- package/dist/cesium/Workers/chunk-BYLCY7GP.js +29 -0
- package/dist/cesium/Workers/chunk-CTHM3W6I.js +26 -0
- package/dist/cesium/Workers/chunk-CUUSNIVQ.js +26 -0
- package/dist/cesium/Workers/chunk-E3JOOS3S.js +26 -0
- package/dist/cesium/Workers/chunk-E7KYDCM5.js +26 -0
- package/dist/cesium/Workers/chunk-EDVBB7SS.js +27 -0
- package/dist/cesium/Workers/chunk-EFBN7QNX.js +26 -0
- package/dist/cesium/Workers/chunk-EQ4YRVWL.js +26 -0
- package/dist/cesium/Workers/chunk-F6PRE7D6.js +26 -0
- package/dist/cesium/Workers/chunk-FC4ZZ65J.js +26 -0
- package/dist/cesium/Workers/chunk-FFBVWF2L.js +26 -0
- package/dist/cesium/Workers/chunk-GBAA6GVX.js +26 -0
- package/dist/cesium/Workers/chunk-ICALLYLG.js +26 -0
- package/dist/cesium/Workers/chunk-ILRYTWTP.js +26 -0
- package/dist/cesium/Workers/chunk-IRNLBSEJ.js +26 -0
- package/dist/cesium/Workers/chunk-IX4VMHEV.js +26 -0
- package/dist/cesium/Workers/chunk-L6QHHACZ.js +26 -0
- package/dist/cesium/Workers/chunk-LI2ZSORM.js +26 -0
- package/dist/cesium/Workers/chunk-LSLE2RL4.js +26 -0
- package/dist/cesium/Workers/chunk-M4HLDBCG.js +26 -0
- package/dist/cesium/Workers/chunk-MJHHSGEH.js +26 -0
- package/dist/cesium/Workers/chunk-NMVKML6W.js +26 -0
- package/dist/cesium/Workers/chunk-OCWJRAXS.js +26 -0
- package/dist/cesium/Workers/chunk-OIRKANTH.js +26 -0
- package/dist/cesium/Workers/chunk-OIT7J4IC.js +26 -0
- package/dist/cesium/Workers/chunk-OLZ3FYUM.js +26 -0
- package/dist/cesium/Workers/chunk-Q5BPHJQF.js +26 -0
- package/dist/cesium/Workers/chunk-QFM5DCMQ.js +26 -0
- package/dist/cesium/Workers/chunk-QKUIYMGC.js +28 -0
- package/dist/cesium/Workers/chunk-S44JILQT.js +26 -0
- package/dist/cesium/Workers/chunk-SLT4J352.js +26 -0
- package/dist/cesium/Workers/chunk-SQMIIXB7.js +26 -0
- package/dist/cesium/Workers/chunk-TJ4XLGBQ.js +26 -0
- package/dist/cesium/Workers/chunk-TNSUQXWK.js +27 -0
- package/dist/cesium/Workers/chunk-UBOGZS7F.js +26 -0
- package/dist/cesium/Workers/chunk-V3OSTMM6.js +26 -0
- package/dist/cesium/Workers/chunk-V7QEYVP3.js +26 -0
- package/dist/cesium/Workers/chunk-VUKYSU4H.js +26 -0
- package/dist/cesium/Workers/chunk-W37FE5GR.js +26 -0
- package/dist/cesium/Workers/chunk-WBOV35NL.js +26 -0
- package/dist/cesium/Workers/chunk-WPMZLB3Y.js +26 -0
- package/dist/cesium/Workers/chunk-WWWZVEEH.js +26 -0
- package/dist/cesium/Workers/chunk-XFIQ5DEQ.js +28 -0
- package/dist/cesium/Workers/chunk-XQHLGIO7.js +26 -0
- package/dist/cesium/Workers/chunk-XUSCFAVF.js +26 -0
- package/dist/cesium/Workers/chunk-YP7I5QBZ.js +26 -0
- package/dist/cesium/Workers/chunk-Z3QF2EHT.js +26 -0
- package/dist/cesium/Workers/combineGeometry.js +26 -0
- package/dist/cesium/Workers/createBoxGeometry.js +26 -0
- package/dist/cesium/Workers/createBoxOutlineGeometry.js +26 -0
- package/dist/cesium/Workers/createCircleGeometry.js +26 -0
- package/dist/cesium/Workers/createCircleOutlineGeometry.js +26 -0
- package/dist/cesium/Workers/createCoplanarPolygonGeometry.js +26 -0
- package/dist/cesium/Workers/createCoplanarPolygonOutlineGeometry.js +26 -0
- package/dist/cesium/Workers/createCorridorGeometry.js +26 -0
- package/dist/cesium/Workers/createCorridorOutlineGeometry.js +26 -0
- package/dist/cesium/Workers/createCylinderGeometry.js +26 -0
- package/dist/cesium/Workers/createCylinderOutlineGeometry.js +26 -0
- package/dist/cesium/Workers/createEllipseGeometry.js +26 -0
- package/dist/cesium/Workers/createEllipseOutlineGeometry.js +26 -0
- package/dist/cesium/Workers/createEllipsoidGeometry.js +26 -0
- package/dist/cesium/Workers/createEllipsoidOutlineGeometry.js +26 -0
- package/dist/cesium/Workers/createFrustumGeometry.js +26 -0
- package/dist/cesium/Workers/createFrustumOutlineGeometry.js +26 -0
- package/dist/cesium/Workers/createGeometry.js +26 -0
- package/dist/cesium/Workers/createGroundPolylineGeometry.js +26 -0
- package/dist/cesium/Workers/createPlaneGeometry.js +26 -0
- package/dist/cesium/Workers/createPlaneOutlineGeometry.js +26 -0
- package/dist/cesium/Workers/createPolygonGeometry.js +26 -0
- package/dist/cesium/Workers/createPolygonOutlineGeometry.js +26 -0
- package/dist/cesium/Workers/createPolylineGeometry.js +26 -0
- package/dist/cesium/Workers/createPolylineVolumeGeometry.js +26 -0
- package/dist/cesium/Workers/createPolylineVolumeOutlineGeometry.js +26 -0
- package/dist/cesium/Workers/createRectangleGeometry.js +26 -0
- package/dist/cesium/Workers/createRectangleOutlineGeometry.js +26 -0
- package/dist/cesium/Workers/createSimplePolylineGeometry.js +26 -0
- package/dist/cesium/Workers/createSphereGeometry.js +26 -0
- package/dist/cesium/Workers/createSphereOutlineGeometry.js +26 -0
- package/dist/cesium/Workers/createTaskProcessorWorker.js +26 -0
- package/dist/cesium/Workers/createVectorTileClampedPolylines.js +26 -0
- package/dist/cesium/Workers/createVectorTileGeometries.js +26 -0
- package/dist/cesium/Workers/createVectorTilePoints.js +26 -0
- package/dist/cesium/Workers/createVectorTilePolygons.js +26 -0
- package/dist/cesium/Workers/createVectorTilePolylines.js +26 -0
- package/dist/cesium/Workers/createVerticesFromCesium3DTilesTerrain.js +26 -0
- package/dist/cesium/Workers/createVerticesFromGoogleEarthEnterpriseBuffer.js +26 -0
- package/dist/cesium/Workers/createVerticesFromHeightmap.js +26 -0
- package/dist/cesium/Workers/createVerticesFromQuantizedTerrainMesh.js +26 -0
- package/dist/cesium/Workers/createWallGeometry.js +26 -0
- package/dist/cesium/Workers/createWallOutlineGeometry.js +26 -0
- package/dist/cesium/Workers/decodeDraco.js +26 -0
- package/dist/cesium/Workers/decodeGoogleEarthEnterprisePacket.js +26 -0
- package/dist/cesium/Workers/decodeI3S.js +26 -0
- package/dist/cesium/Workers/gaussianSplatSorter.js +26 -0
- package/dist/cesium/Workers/gaussianSplatTextureGenerator.js +26 -0
- package/dist/cesium/Workers/incrementallyBuildTerrainPicker.js +26 -0
- package/dist/cesium/Workers/transcodeKTX2.js +56 -0
- package/dist/cesium/Workers/transferTypedArrayTest.js +26 -0
- package/dist/cesium/Workers/upsampleQuantizedTerrainMesh.js +26 -0
- package/dist/cesium/Workers/upsampleVerticesFromCesium3DTilesTerrain.js +26 -0
- package/dist/index.html +13 -10
- package/index.html +1 -0
- package/package.json +18 -15
- package/src/App.tsx +7 -9
- package/src/components/viewer/BCFPanel.tsx +46 -4
- package/src/components/viewer/CesiumOverlay.tsx +715 -0
- package/src/components/viewer/CesiumSettingsDialog.tsx +100 -0
- package/src/components/viewer/ChatPanel.tsx +232 -90
- package/src/components/viewer/CommandPalette.tsx +6 -1
- package/src/components/viewer/DesktopEntitlementBanner.tsx +74 -0
- package/src/components/viewer/ExportChangesButton.tsx +6 -1
- package/src/components/viewer/ExportDialog.tsx +22 -6
- package/src/components/viewer/HierarchyPanel.tsx +196 -0
- package/src/components/viewer/IDSPanel.tsx +52 -3
- package/src/components/viewer/KeyboardShortcutsDialog.tsx +1 -1
- package/src/components/viewer/MainToolbar.tsx +355 -28
- package/src/components/viewer/PropertiesPanel.tsx +234 -81
- package/src/components/viewer/ScriptPanel.tsx +34 -8
- package/src/components/viewer/SettingsPage.tsx +581 -0
- package/src/components/viewer/StatusBar.tsx +17 -1
- package/src/components/viewer/ThemeSwitch.tsx +63 -7
- package/src/components/viewer/ViewerLayout.tsx +48 -6
- package/src/components/viewer/Viewport.tsx +61 -8
- package/src/components/viewer/ViewportContainer.tsx +265 -28
- package/src/components/viewer/ViewportOverlays.tsx +132 -27
- package/src/components/viewer/bcf/BCFTopicDetail.tsx +4 -4
- package/src/components/viewer/chat/ModelSelector.tsx +90 -54
- package/src/components/viewer/properties/GeoreferencingPanel.tsx +229 -55
- package/src/components/viewer/properties/LocationMap.tsx +462 -19
- package/src/components/viewer/properties/ModelMetadataPanel.tsx +1 -1
- package/src/components/viewer/selectionHandlers.ts +4 -3
- package/src/components/viewer/tools/SectionCapControls.tsx +237 -0
- package/src/components/viewer/tools/SectionPanel.tsx +39 -18
- package/src/components/viewer/useAnimationLoop.ts +13 -1
- package/src/components/viewer/useGeometryStreaming.ts +127 -40
- package/src/components/viewer/useMouseControls.ts +4 -1
- package/src/components/viewer/useRenderUpdates.ts +1 -1
- package/src/hooks/ids/idsDataAccessor.ts +60 -24
- package/src/hooks/ingest/viewerModelIngest.ts +280 -0
- package/src/hooks/useIDS.ts +1 -1
- package/src/hooks/useIfc.ts +7 -1
- package/src/hooks/useIfcCache.ts +28 -15
- package/src/hooks/useIfcFederation.ts +378 -291
- package/src/hooks/useIfcLoader.ts +1657 -130
- package/src/hooks/useIfcServer.ts +0 -69
- package/src/hooks/useViewControls.ts +13 -5
- package/src/index.css +484 -10
- package/src/lib/desktop/desktopEntitlementEvents.ts +39 -0
- package/src/lib/desktop-entitlement.ts +43 -0
- package/src/lib/desktop-product.ts +124 -0
- package/src/lib/geo/cesium-bridge.ts +318 -0
- package/src/lib/geo/effective-georef.test.ts +73 -0
- package/src/lib/geo/effective-georef.ts +111 -0
- package/src/lib/geo/reproject.ts +249 -37
- package/src/lib/llm/byok-guard.test.ts +77 -0
- package/src/lib/llm/byok-guard.ts +39 -0
- package/src/lib/llm/free-models.test.ts +0 -6
- package/src/lib/llm/models.ts +104 -42
- package/src/lib/llm/stream-client.ts +74 -110
- package/src/lib/llm/stream-direct.test.ts +130 -0
- package/src/lib/llm/stream-direct.ts +316 -0
- package/src/lib/llm/types.ts +14 -2
- package/src/lib/recent-files.ts +2 -1
- package/src/main.tsx +1 -10
- package/src/services/analysis-extensions.ts +125 -0
- package/src/services/api-keys.ts +73 -0
- package/src/services/app-navigation.ts +13 -0
- package/src/services/bsdd.ts +53 -4
- package/src/services/cacheService.ts +1 -1
- package/src/services/desktop-cache.ts +43 -0
- package/src/services/desktop-export.ts +77 -0
- package/src/services/desktop-harness.ts +196 -0
- package/src/services/desktop-logger.ts +20 -0
- package/src/services/desktop-native-metadata.ts +207 -0
- package/src/services/desktop-panel-actions.ts +43 -0
- package/src/services/desktop-preferences.ts +44 -0
- package/src/services/file-dialog.ts +147 -0
- package/src/services/tauri-core-stub.ts +7 -0
- package/src/services/tauri-dialog-stub.ts +7 -0
- package/src/services/tauri-fs-stub.ts +7 -0
- package/src/store/constants.ts +20 -2
- package/src/store/index.ts +52 -7
- package/src/store/slices/cesiumSlice.ts +127 -0
- package/src/store/slices/chatSlice.test.ts +6 -76
- package/src/store/slices/chatSlice.ts +21 -58
- package/src/store/slices/dataSlice.ts +139 -28
- package/src/store/slices/desktopEntitlementSlice.ts +86 -0
- package/src/store/slices/loadingSlice.ts +14 -2
- package/src/store/slices/modelSlice.ts +58 -3
- package/src/store/slices/sectionSlice.test.ts +87 -7
- package/src/store/slices/sectionSlice.ts +151 -5
- package/src/store/slices/uiSlice.ts +28 -5
- package/src/store/types.ts +122 -2
- package/src/store.ts +1 -1
- package/src/utils/desktopModelSnapshot.ts +358 -0
- package/src/utils/ifcConfig.ts +6 -1
- package/src/utils/nativeSpatialDataStore.ts +253 -0
- package/src/utils/serverDataModel.ts +4 -0
- package/src/utils/spatialHierarchy.ts +10 -11
- package/src/utils/viewportUtils.ts +7 -2
- package/src/vite-env.d.ts +0 -4
- package/vite.config.ts +24 -0
- package/dist/assets/arrow-DJf2ErbF.js +0 -20
- package/dist/assets/basketViewActivator-aojwdomq.js +0 -1
- package/dist/assets/desktop-cache-oPzaWXYE.js +0 -1
- package/dist/assets/drawing-2d-gWfpdfYe.js +0 -257
- package/dist/assets/geometry.worker-Nz9_YIqh.js +0 -1
- package/dist/assets/ids-B4jTqB1O.js +0 -1
- package/dist/assets/ifc-lite_bg-eSkBTizQ.wasm +0 -0
- package/dist/assets/index-pbE7itQS.css +0 -1
- package/dist/assets/native-bridge-DSIyEYXG.js +0 -113
- package/dist/assets/wasm-bridge-B0J07fZZ.js +0 -1
- package/src/components/viewer/UpgradePage.tsx +0 -69
- package/src/lib/llm/ClerkChatSync.tsx +0 -74
- package/src/lib/llm/clerk-auth.ts +0 -62
|
@@ -6,17 +6,24 @@
|
|
|
6
6
|
* LocationMap — a compact MapLibre GL JS minimap that shows the model's
|
|
7
7
|
* real-world position derived from IfcMapConversion + IfcProjectedCRS.
|
|
8
8
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
9
|
+
* Features:
|
|
10
|
+
* - Place/drag pin on map to reposition the model
|
|
11
|
+
* - Search for places via Nominatim geocoding
|
|
12
|
+
* - Query terrain elevation at pin location
|
|
13
|
+
* - Apply pin position back to IfcMapConversion (eastings/northings/height)
|
|
14
|
+
* - Links to Google Maps, OpenStreetMap, and Google Earth (KMZ export)
|
|
11
15
|
*/
|
|
12
16
|
|
|
13
17
|
import { useEffect, useRef, useState, useMemo, useCallback } from 'react';
|
|
14
|
-
import {
|
|
18
|
+
import {
|
|
19
|
+
Map as MapIcon, ExternalLink, Loader2, MapPinOff, Globe2,
|
|
20
|
+
Search, Mountain, MapPin, X, Check,
|
|
21
|
+
} from 'lucide-react';
|
|
15
22
|
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
|
|
16
23
|
import type { MapConversion, ProjectedCRS } from '@ifc-lite/parser';
|
|
17
24
|
import type { CoordinateInfo, GeometryResult } from '@ifc-lite/geometry';
|
|
18
25
|
import { GLTFExporter } from '@ifc-lite/export';
|
|
19
|
-
import { reprojectToLatLon, type LatLon } from '@/lib/geo/reproject';
|
|
26
|
+
import { reprojectToLatLon, reprojectFromLatLon, queryTerrainElevation, computeFootprintGeoJSON, type LatLon } from '@/lib/geo/reproject';
|
|
20
27
|
import { buildKmz, ifcAngleToKmlHeading } from '@/lib/geo/kmz-exporter';
|
|
21
28
|
|
|
22
29
|
// Lazy-load maplibre-gl to avoid bloating the initial bundle
|
|
@@ -28,6 +35,13 @@ function loadMaplibre() {
|
|
|
28
35
|
return maplibrePromise;
|
|
29
36
|
}
|
|
30
37
|
|
|
38
|
+
/** Position picked on the map, ready to be applied to IfcMapConversion */
|
|
39
|
+
export interface PickedPosition {
|
|
40
|
+
easting: number;
|
|
41
|
+
northing: number;
|
|
42
|
+
terrainHeight: number | null;
|
|
43
|
+
}
|
|
44
|
+
|
|
31
45
|
export interface LocationMapProps {
|
|
32
46
|
mapConversion?: MapConversion;
|
|
33
47
|
projectedCRS?: ProjectedCRS;
|
|
@@ -35,19 +49,182 @@ export interface LocationMapProps {
|
|
|
35
49
|
coordinateInfo?: CoordinateInfo;
|
|
36
50
|
/** Geometry result for KMZ export (optional — KMZ button hidden if not provided) */
|
|
37
51
|
geometryResult?: GeometryResult | null;
|
|
52
|
+
/** IFC project length unit → metres (e.g. 0.001 for mm models). Default 1 (metres). */
|
|
53
|
+
lengthUnitScale?: number;
|
|
54
|
+
/** Whether the map is in edit mode (allows repositioning) */
|
|
55
|
+
editable?: boolean;
|
|
56
|
+
/** Called when the user applies a new position from the map */
|
|
57
|
+
onApplyPosition?: (position: PickedPosition) => void;
|
|
38
58
|
}
|
|
39
59
|
|
|
40
60
|
type MapState = 'idle' | 'loading' | 'ready' | 'error';
|
|
41
61
|
|
|
42
|
-
|
|
62
|
+
// Debounce helper
|
|
63
|
+
function useDebouncedValue<T>(value: T, delayMs: number): T {
|
|
64
|
+
const [debounced, setDebounced] = useState(value);
|
|
65
|
+
useEffect(() => {
|
|
66
|
+
const timer = setTimeout(() => setDebounced(value), delayMs);
|
|
67
|
+
return () => clearTimeout(timer);
|
|
68
|
+
}, [value, delayMs]);
|
|
69
|
+
return debounced;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** Geocode a query string via Nominatim */
|
|
73
|
+
async function geocodeSearch(query: string): Promise<Array<{ lat: number; lon: number; display_name: string }>> {
|
|
74
|
+
if (!query.trim()) return [];
|
|
75
|
+
try {
|
|
76
|
+
const q = encodeURIComponent(query.trim());
|
|
77
|
+
const resp = await fetch(
|
|
78
|
+
`https://nominatim.openstreetmap.org/search?format=json&limit=5&q=${q}`,
|
|
79
|
+
{ headers: { 'Accept-Language': 'en' } },
|
|
80
|
+
);
|
|
81
|
+
if (!resp.ok) return [];
|
|
82
|
+
const data = await resp.json();
|
|
83
|
+
return data.map((r: { lat: string; lon: string; display_name: string }) => ({
|
|
84
|
+
lat: parseFloat(r.lat),
|
|
85
|
+
lon: parseFloat(r.lon),
|
|
86
|
+
display_name: r.display_name,
|
|
87
|
+
}));
|
|
88
|
+
} catch {
|
|
89
|
+
return [];
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** Add or update the building footprint GeoJSON polygon on a MapLibre map */
|
|
94
|
+
function addFootprintToMap(map: InstanceType<typeof import('maplibre-gl').Map>, ring: [number, number][]) {
|
|
95
|
+
const geojson: GeoJSON.Feature = {
|
|
96
|
+
type: 'Feature',
|
|
97
|
+
properties: {},
|
|
98
|
+
geometry: {
|
|
99
|
+
type: 'Polygon',
|
|
100
|
+
coordinates: [ring],
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
if (map.getSource('building-footprint')) {
|
|
105
|
+
(map.getSource('building-footprint') as import('maplibre-gl').GeoJSONSource).setData(geojson);
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
map.addSource('building-footprint', { type: 'geojson', data: geojson });
|
|
110
|
+
|
|
111
|
+
map.addLayer({
|
|
112
|
+
id: 'building-footprint-fill',
|
|
113
|
+
type: 'fill',
|
|
114
|
+
source: 'building-footprint',
|
|
115
|
+
paint: {
|
|
116
|
+
'fill-color': '#14b8a6',
|
|
117
|
+
'fill-opacity': ['interpolate', ['linear'], ['zoom'], 15, 0, 16, 0.15, 18, 0.25],
|
|
118
|
+
},
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
map.addLayer({
|
|
122
|
+
id: 'building-footprint-outline',
|
|
123
|
+
type: 'line',
|
|
124
|
+
source: 'building-footprint',
|
|
125
|
+
paint: {
|
|
126
|
+
'line-color': '#0d9488',
|
|
127
|
+
'line-width': ['interpolate', ['linear'], ['zoom'], 15, 0.5, 18, 2.5],
|
|
128
|
+
'line-opacity': ['interpolate', ['linear'], ['zoom'], 15, 0, 16, 0.7, 18, 1],
|
|
129
|
+
},
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/** Remove footprint layers and source from a MapLibre map */
|
|
135
|
+
function removeFootprintFromMap(map: InstanceType<typeof import('maplibre-gl').Map>) {
|
|
136
|
+
if (map.getLayer('building-footprint-outline')) map.removeLayer('building-footprint-outline');
|
|
137
|
+
if (map.getLayer('building-footprint-fill')) map.removeLayer('building-footprint-fill');
|
|
138
|
+
if (map.getSource('building-footprint')) map.removeSource('building-footprint');
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export function LocationMap({
|
|
142
|
+
mapConversion, projectedCRS, coordinateInfo, geometryResult,
|
|
143
|
+
lengthUnitScale = 1, editable, onApplyPosition,
|
|
144
|
+
}: LocationMapProps) {
|
|
43
145
|
const containerRef = useRef<HTMLDivElement>(null);
|
|
44
146
|
const mapRef = useRef<InstanceType<typeof import('maplibre-gl').Map> | null>(null);
|
|
45
147
|
const markerRef = useRef<InstanceType<typeof import('maplibre-gl').Marker> | null>(null);
|
|
148
|
+
const pickedMarkerRef = useRef<InstanceType<typeof import('maplibre-gl').Marker> | null>(null);
|
|
149
|
+
const editableRef = useRef(editable);
|
|
150
|
+
|
|
151
|
+
// Keep editableRef in sync; clean up edit-only state when leaving edit mode
|
|
152
|
+
useEffect(() => {
|
|
153
|
+
editableRef.current = editable;
|
|
154
|
+
if (!editable) {
|
|
155
|
+
setSearchOpen(false);
|
|
156
|
+
setSearchQuery('');
|
|
157
|
+
setSearchResults([]);
|
|
158
|
+
setSearchLoading(false);
|
|
159
|
+
pickedMarkerRef.current?.remove();
|
|
160
|
+
pickedMarkerRef.current = null;
|
|
161
|
+
setPickedLatLon(null);
|
|
162
|
+
setProjectedCoords(null);
|
|
163
|
+
setPickedElevation(null);
|
|
164
|
+
setElevationLoading(false);
|
|
165
|
+
}
|
|
166
|
+
}, [editable]);
|
|
46
167
|
|
|
47
168
|
const [mapState, setMapState] = useState<MapState>('idle');
|
|
48
169
|
const [latLon, setLatLon] = useState<LatLon | null>(null);
|
|
49
170
|
const [error, setError] = useState<string | null>(null);
|
|
50
171
|
|
|
172
|
+
// Picked position state (user-placed pin)
|
|
173
|
+
const [pickedLatLon, setPickedLatLon] = useState<LatLon | null>(null);
|
|
174
|
+
const [pickedElevation, setPickedElevation] = useState<number | null>(null);
|
|
175
|
+
const [elevationLoading, setElevationLoading] = useState(false);
|
|
176
|
+
const [projectedCoords, setProjectedCoords] = useState<{ easting: number; northing: number } | null>(null);
|
|
177
|
+
|
|
178
|
+
// Search state
|
|
179
|
+
const [searchOpen, setSearchOpen] = useState(false);
|
|
180
|
+
const [searchQuery, setSearchQuery] = useState('');
|
|
181
|
+
const [searchResults, setSearchResults] = useState<Array<{ lat: number; lon: number; display_name: string }>>([]);
|
|
182
|
+
const [searchLoading, setSearchLoading] = useState(false);
|
|
183
|
+
const debouncedQuery = useDebouncedValue(searchQuery, 400);
|
|
184
|
+
|
|
185
|
+
// Building footprint state (bounding box polygon in WGS84)
|
|
186
|
+
const [footprint, setFootprint] = useState<[number, number][] | null>(null);
|
|
187
|
+
const [styleVersion, setStyleVersion] = useState(0);
|
|
188
|
+
const footprintRef = useRef<[number, number][] | null>(null);
|
|
189
|
+
|
|
190
|
+
// Compute building footprint from bounding box
|
|
191
|
+
useEffect(() => {
|
|
192
|
+
if (!mapConversion || !projectedCRS || !coordinateInfo) {
|
|
193
|
+
setFootprint(null);
|
|
194
|
+
footprintRef.current = null;
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
let cancelled = false;
|
|
199
|
+
|
|
200
|
+
computeFootprintGeoJSON(mapConversion, projectedCRS, coordinateInfo, lengthUnitScale).then(fp => {
|
|
201
|
+
if (cancelled) return;
|
|
202
|
+
setFootprint(fp);
|
|
203
|
+
footprintRef.current = fp;
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
return () => { cancelled = true; };
|
|
207
|
+
}, [mapConversion, projectedCRS, coordinateInfo, lengthUnitScale]);
|
|
208
|
+
|
|
209
|
+
// Geocode search
|
|
210
|
+
useEffect(() => {
|
|
211
|
+
if (!debouncedQuery.trim()) {
|
|
212
|
+
setSearchResults([]);
|
|
213
|
+
setSearchLoading(false);
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
let cancelled = false;
|
|
217
|
+
setSearchLoading(true);
|
|
218
|
+
geocodeSearch(debouncedQuery).then(results => {
|
|
219
|
+
if (!cancelled) {
|
|
220
|
+
setSearchResults(results);
|
|
221
|
+
setSearchLoading(false);
|
|
222
|
+
}
|
|
223
|
+
});
|
|
224
|
+
return () => { cancelled = true; };
|
|
225
|
+
}, [debouncedQuery]);
|
|
226
|
+
|
|
227
|
+
// Reproject model position to lat/lon
|
|
51
228
|
useEffect(() => {
|
|
52
229
|
if (!mapConversion || !projectedCRS) {
|
|
53
230
|
setLatLon(null);
|
|
@@ -59,7 +236,7 @@ export function LocationMap({ mapConversion, projectedCRS, coordinateInfo, geome
|
|
|
59
236
|
setMapState('loading');
|
|
60
237
|
setError(null);
|
|
61
238
|
|
|
62
|
-
reprojectToLatLon(mapConversion, projectedCRS, coordinateInfo).then(result => {
|
|
239
|
+
reprojectToLatLon(mapConversion, projectedCRS, coordinateInfo, lengthUnitScale).then(result => {
|
|
63
240
|
if (cancelled) return;
|
|
64
241
|
if (result) {
|
|
65
242
|
setLatLon(result);
|
|
@@ -72,7 +249,103 @@ export function LocationMap({ mapConversion, projectedCRS, coordinateInfo, geome
|
|
|
72
249
|
});
|
|
73
250
|
|
|
74
251
|
return () => { cancelled = true; };
|
|
75
|
-
}, [mapConversion, projectedCRS, coordinateInfo]);
|
|
252
|
+
}, [mapConversion, projectedCRS, coordinateInfo, lengthUnitScale]);
|
|
253
|
+
|
|
254
|
+
// When a picked position changes, reverse-project and query elevation
|
|
255
|
+
useEffect(() => {
|
|
256
|
+
if (!pickedLatLon || !projectedCRS) {
|
|
257
|
+
setProjectedCoords(null);
|
|
258
|
+
setPickedElevation(null);
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
let cancelled = false;
|
|
263
|
+
setProjectedCoords(null);
|
|
264
|
+
|
|
265
|
+
// Reverse-project to get IfcMapConversion eastings/northings
|
|
266
|
+
// Accounts for model local geometry offset, rotation, and scale
|
|
267
|
+
reprojectFromLatLon(pickedLatLon, projectedCRS, mapConversion, coordinateInfo, lengthUnitScale).then(coords => {
|
|
268
|
+
if (!cancelled) setProjectedCoords(coords);
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
// Query terrain elevation
|
|
272
|
+
setElevationLoading(true);
|
|
273
|
+
queryTerrainElevation(pickedLatLon).then(elev => {
|
|
274
|
+
if (!cancelled) {
|
|
275
|
+
setPickedElevation(elev);
|
|
276
|
+
setElevationLoading(false);
|
|
277
|
+
}
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
return () => { cancelled = true; };
|
|
281
|
+
}, [pickedLatLon, projectedCRS, mapConversion, coordinateInfo, lengthUnitScale]);
|
|
282
|
+
|
|
283
|
+
// Place or move the picked marker on the map
|
|
284
|
+
const updatePickedMarker = useCallback((pos: LatLon, maplibregl: typeof import('maplibre-gl')) => {
|
|
285
|
+
if (!mapRef.current) return;
|
|
286
|
+
if (pickedMarkerRef.current) {
|
|
287
|
+
pickedMarkerRef.current.setLngLat([pos.lon, pos.lat]);
|
|
288
|
+
} else {
|
|
289
|
+
const el = document.createElement('div');
|
|
290
|
+
el.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="#7c3aed" stroke="white" stroke-width="2"><path d="M20 10c0 6-8 12-8 12s-8-6-8-12a8 8 0 0 1 16 0Z"/><circle cx="12" cy="10" r="3"/></svg>`;
|
|
291
|
+
el.style.cursor = 'grab';
|
|
292
|
+
const marker = new maplibregl.Marker({ element: el, draggable: true })
|
|
293
|
+
.setLngLat([pos.lon, pos.lat])
|
|
294
|
+
.addTo(mapRef.current);
|
|
295
|
+
marker.on('dragend', () => {
|
|
296
|
+
const lngLat = marker.getLngLat();
|
|
297
|
+
setPickedLatLon({ lat: lngLat.lat, lon: lngLat.lng });
|
|
298
|
+
});
|
|
299
|
+
pickedMarkerRef.current = marker;
|
|
300
|
+
}
|
|
301
|
+
}, []);
|
|
302
|
+
|
|
303
|
+
// Handle map click to place pin (reads editable from ref to avoid stale closure)
|
|
304
|
+
const handleMapClick = useCallback((e: { lngLat: { lat: number; lng: number } }) => {
|
|
305
|
+
if (!editableRef.current) return;
|
|
306
|
+
const pos = { lat: e.lngLat.lat, lon: e.lngLat.lng };
|
|
307
|
+
setPickedLatLon(pos);
|
|
308
|
+
loadMaplibre().then(ml => updatePickedMarker(pos, ml));
|
|
309
|
+
}, [updatePickedMarker]);
|
|
310
|
+
|
|
311
|
+
// Handle search result selection
|
|
312
|
+
const handleSearchSelect = useCallback((result: { lat: number; lon: number; display_name: string }) => {
|
|
313
|
+
const pos = { lat: result.lat, lon: result.lon };
|
|
314
|
+
setPickedLatLon(pos);
|
|
315
|
+
setSearchOpen(false);
|
|
316
|
+
setSearchQuery('');
|
|
317
|
+
setSearchResults([]);
|
|
318
|
+
|
|
319
|
+
loadMaplibre().then(ml => {
|
|
320
|
+
updatePickedMarker(pos, ml);
|
|
321
|
+
mapRef.current?.flyTo({ center: [pos.lon, pos.lat], zoom: 16, duration: 1200 });
|
|
322
|
+
});
|
|
323
|
+
}, [updatePickedMarker]);
|
|
324
|
+
|
|
325
|
+
// Handle apply position (waits for elevation to finish loading)
|
|
326
|
+
const handleApply = useCallback(() => {
|
|
327
|
+
if (!projectedCoords || !onApplyPosition || elevationLoading) return;
|
|
328
|
+
onApplyPosition({
|
|
329
|
+
easting: Math.round(projectedCoords.easting * 1000) / 1000,
|
|
330
|
+
northing: Math.round(projectedCoords.northing * 1000) / 1000,
|
|
331
|
+
terrainHeight: pickedElevation,
|
|
332
|
+
});
|
|
333
|
+
// Clear picked state after applying
|
|
334
|
+
pickedMarkerRef.current?.remove();
|
|
335
|
+
pickedMarkerRef.current = null;
|
|
336
|
+
setPickedLatLon(null);
|
|
337
|
+
setProjectedCoords(null);
|
|
338
|
+
setPickedElevation(null);
|
|
339
|
+
}, [projectedCoords, pickedElevation, onApplyPosition, elevationLoading]);
|
|
340
|
+
|
|
341
|
+
// Clear picked pin
|
|
342
|
+
const handleClearPick = useCallback(() => {
|
|
343
|
+
pickedMarkerRef.current?.remove();
|
|
344
|
+
pickedMarkerRef.current = null;
|
|
345
|
+
setPickedLatLon(null);
|
|
346
|
+
setProjectedCoords(null);
|
|
347
|
+
setPickedElevation(null);
|
|
348
|
+
}, []);
|
|
76
349
|
|
|
77
350
|
// Initialize/update the map when we have a valid lat/lon
|
|
78
351
|
useEffect(() => {
|
|
@@ -105,23 +378,60 @@ export function LocationMap({ mapConversion, projectedCRS, coordinateInfo, geome
|
|
|
105
378
|
map.addControl(new maplibregl.NavigationControl({ showCompass: false }), 'top-right');
|
|
106
379
|
map.addControl(new maplibregl.AttributionControl({ compact: false }), 'bottom-right');
|
|
107
380
|
|
|
108
|
-
// Add marker at model location
|
|
381
|
+
// Add marker at model location (teal = current model position)
|
|
109
382
|
const marker = new maplibregl.Marker({ color: '#14b8a6' })
|
|
110
383
|
.setLngLat([latLon.lon, latLon.lat])
|
|
111
384
|
.addTo(map);
|
|
112
385
|
|
|
386
|
+
// Toggle marker vs footprint based on zoom level
|
|
387
|
+
map.on('zoomend', () => {
|
|
388
|
+
const zoom = map.getZoom();
|
|
389
|
+
if (markerRef.current) {
|
|
390
|
+
markerRef.current.getElement().style.opacity = zoom >= 17 ? '0' : '1';
|
|
391
|
+
markerRef.current.getElement().style.pointerEvents = zoom >= 17 ? 'none' : 'auto';
|
|
392
|
+
}
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
// Map click to place pin (only in edit mode)
|
|
396
|
+
map.on('click', handleMapClick);
|
|
397
|
+
|
|
113
398
|
mapRef.current = map;
|
|
114
399
|
markerRef.current = marker;
|
|
400
|
+
|
|
401
|
+
// If footprint was already computed before the map was created, add it now
|
|
402
|
+
if (footprintRef.current) {
|
|
403
|
+
map.once('load', () => {
|
|
404
|
+
addFootprintToMap(map, footprintRef.current!);
|
|
405
|
+
});
|
|
406
|
+
}
|
|
115
407
|
});
|
|
116
408
|
|
|
117
409
|
return () => {
|
|
118
410
|
cancelled = true;
|
|
119
411
|
};
|
|
120
|
-
}, [latLon]);
|
|
412
|
+
}, [latLon, handleMapClick]);
|
|
413
|
+
|
|
414
|
+
// Add/update building footprint GeoJSON layer when footprint or style changes
|
|
415
|
+
useEffect(() => {
|
|
416
|
+
const map = mapRef.current;
|
|
417
|
+
if (!map) return;
|
|
418
|
+
if (!footprint) {
|
|
419
|
+
removeFootprintFromMap(map);
|
|
420
|
+
return;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
if (map.isStyleLoaded()) {
|
|
424
|
+
addFootprintToMap(map, footprint);
|
|
425
|
+
} else {
|
|
426
|
+
map.once('style.load', () => addFootprintToMap(map, footprint));
|
|
427
|
+
}
|
|
428
|
+
}, [footprint, styleVersion]);
|
|
121
429
|
|
|
122
430
|
// Cleanup on unmount
|
|
123
431
|
useEffect(() => {
|
|
124
432
|
return () => {
|
|
433
|
+
pickedMarkerRef.current?.remove();
|
|
434
|
+
pickedMarkerRef.current = null;
|
|
125
435
|
markerRef.current?.remove();
|
|
126
436
|
markerRef.current = null;
|
|
127
437
|
mapRef.current?.remove();
|
|
@@ -152,7 +462,7 @@ export function LocationMap({ mapConversion, projectedCRS, coordinateInfo, geome
|
|
|
152
462
|
glb,
|
|
153
463
|
name: 'IFC Model',
|
|
154
464
|
});
|
|
155
|
-
const blob = new Blob([kmz], { type: 'application/vnd.google-earth.kmz' });
|
|
465
|
+
const blob = new Blob([kmz as BlobPart], { type: 'application/vnd.google-earth.kmz' });
|
|
156
466
|
const url = URL.createObjectURL(blob);
|
|
157
467
|
const a = document.createElement('a');
|
|
158
468
|
a.href = url;
|
|
@@ -174,12 +484,17 @@ export function LocationMap({ mapConversion, projectedCRS, coordinateInfo, geome
|
|
|
174
484
|
? 'https://basemaps.cartocdn.com/gl/dark-matter-gl-style/style.json'
|
|
175
485
|
: 'https://basemaps.cartocdn.com/gl/positron-gl-style/style.json',
|
|
176
486
|
);
|
|
177
|
-
// Re-add
|
|
178
|
-
if (
|
|
487
|
+
// Re-add markers and layers after style fully loads
|
|
488
|
+
if (mapRef.current) {
|
|
179
489
|
mapRef.current.once('style.load', () => {
|
|
180
490
|
if (markerRef.current && mapRef.current) {
|
|
181
491
|
markerRef.current.addTo(mapRef.current);
|
|
182
492
|
}
|
|
493
|
+
if (pickedMarkerRef.current && mapRef.current) {
|
|
494
|
+
pickedMarkerRef.current.addTo(mapRef.current);
|
|
495
|
+
}
|
|
496
|
+
// Trigger footprint layer re-add
|
|
497
|
+
setStyleVersion(v => v + 1);
|
|
183
498
|
});
|
|
184
499
|
}
|
|
185
500
|
}, []);
|
|
@@ -191,19 +506,73 @@ export function LocationMap({ mapConversion, projectedCRS, coordinateInfo, geome
|
|
|
191
506
|
|
|
192
507
|
return (
|
|
193
508
|
<div className="border-t border-zinc-100 dark:border-zinc-900">
|
|
194
|
-
{/* Header */}
|
|
509
|
+
{/* Header with search */}
|
|
195
510
|
<div className="flex items-center gap-2 px-3 py-1.5">
|
|
196
511
|
<MapIcon className="h-3 w-3 text-teal-500 shrink-0" />
|
|
197
512
|
<span className="font-bold text-[11px] text-zinc-700 dark:text-zinc-300 uppercase tracking-wide flex-1">
|
|
198
513
|
Location
|
|
199
514
|
</span>
|
|
200
|
-
{latLon && (
|
|
515
|
+
{latLon && !searchOpen && (
|
|
201
516
|
<span className="text-[10px] font-mono text-teal-600/70 dark:text-teal-500/60">
|
|
202
517
|
{latLon.lat.toFixed(5)}, {latLon.lon.toFixed(5)}
|
|
203
518
|
</span>
|
|
204
519
|
)}
|
|
520
|
+
{editable && (
|
|
521
|
+
<button
|
|
522
|
+
onClick={() => { setSearchOpen(!searchOpen); setSearchQuery(''); setSearchResults([]); }}
|
|
523
|
+
className="p-0.5 text-zinc-400 hover:text-teal-600 dark:hover:text-teal-400 transition-colors"
|
|
524
|
+
title="Search for a place"
|
|
525
|
+
>
|
|
526
|
+
<Search className="h-3 w-3" />
|
|
527
|
+
</button>
|
|
528
|
+
)}
|
|
205
529
|
</div>
|
|
206
530
|
|
|
531
|
+
{/* Search bar */}
|
|
532
|
+
{editable && searchOpen && (
|
|
533
|
+
<div className="px-3 pb-1.5 relative">
|
|
534
|
+
<div className="flex items-center gap-1">
|
|
535
|
+
<div className="flex-1 relative">
|
|
536
|
+
<input
|
|
537
|
+
value={searchQuery}
|
|
538
|
+
onChange={e => setSearchQuery(e.target.value)}
|
|
539
|
+
placeholder="Search for a place..."
|
|
540
|
+
className="w-full text-[11px] px-2 py-1 border border-zinc-200 dark:border-zinc-700 bg-white dark:bg-zinc-900 text-zinc-900 dark:text-zinc-100 outline-none focus:ring-1 focus:ring-teal-400 focus:border-teal-400 placeholder:text-zinc-400/60"
|
|
541
|
+
autoFocus
|
|
542
|
+
onKeyDown={e => { if (e.key === 'Escape') { setSearchOpen(false); setSearchQuery(''); setSearchResults([]); } }}
|
|
543
|
+
/>
|
|
544
|
+
{searchLoading && (
|
|
545
|
+
<Loader2 className="absolute right-2 top-1/2 -translate-y-1/2 h-3 w-3 text-teal-500 animate-spin" />
|
|
546
|
+
)}
|
|
547
|
+
</div>
|
|
548
|
+
<button
|
|
549
|
+
onClick={() => { setSearchOpen(false); setSearchQuery(''); setSearchResults([]); }}
|
|
550
|
+
className="p-0.5 text-zinc-400 hover:text-zinc-600 dark:hover:text-zinc-300"
|
|
551
|
+
>
|
|
552
|
+
<X className="h-3 w-3" />
|
|
553
|
+
</button>
|
|
554
|
+
</div>
|
|
555
|
+
|
|
556
|
+
{/* Search results dropdown */}
|
|
557
|
+
{searchResults.length > 0 && (
|
|
558
|
+
<div className="absolute left-3 right-3 top-full z-50 mt-0.5 bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-700 shadow-lg max-h-[160px] overflow-y-auto">
|
|
559
|
+
{searchResults.map((r, i) => (
|
|
560
|
+
<button
|
|
561
|
+
key={i}
|
|
562
|
+
onClick={() => handleSearchSelect(r)}
|
|
563
|
+
className="w-full text-left px-2 py-1.5 text-[10px] text-zinc-700 dark:text-zinc-300 hover:bg-teal-50 dark:hover:bg-teal-950/50 border-b border-zinc-100 dark:border-zinc-800 last:border-0 transition-colors"
|
|
564
|
+
>
|
|
565
|
+
<div className="flex items-start gap-1.5">
|
|
566
|
+
<MapPin className="h-3 w-3 text-teal-500 shrink-0 mt-0.5" />
|
|
567
|
+
<span className="line-clamp-2">{r.display_name}</span>
|
|
568
|
+
</div>
|
|
569
|
+
</button>
|
|
570
|
+
))}
|
|
571
|
+
</div>
|
|
572
|
+
)}
|
|
573
|
+
</div>
|
|
574
|
+
)}
|
|
575
|
+
|
|
207
576
|
{/* Map container */}
|
|
208
577
|
{mapState === 'loading' && (
|
|
209
578
|
<div className="flex items-center justify-center h-[180px] bg-zinc-50 dark:bg-zinc-900/50">
|
|
@@ -221,11 +590,85 @@ export function LocationMap({ mapConversion, projectedCRS, coordinateInfo, geome
|
|
|
221
590
|
|
|
222
591
|
{(mapState === 'ready' || (mapState === 'loading' && latLon)) && (
|
|
223
592
|
<>
|
|
224
|
-
<div
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
593
|
+
<div className="relative">
|
|
594
|
+
<div
|
|
595
|
+
ref={containerRef}
|
|
596
|
+
className="h-[180px] w-full [&_.maplibregl-ctrl-attrib]:!text-[7px] [&_.maplibregl-ctrl-attrib]:!bg-white/40 [&_.maplibregl-ctrl-attrib]:dark:!bg-black/30 [&_.maplibregl-ctrl-attrib]:!py-0 [&_.maplibregl-ctrl-attrib]:!px-1 [&_.maplibregl-ctrl-attrib]:!shadow-none [&_.maplibregl-ctrl-attrib]:!text-zinc-400/70 [&_.maplibregl-ctrl-attrib_a]:!text-zinc-400/70 [&_.maplibregl-ctrl-attrib]:!leading-normal"
|
|
597
|
+
style={{ minHeight: 180 }}
|
|
598
|
+
/>
|
|
599
|
+
{/* Edit mode hint overlay */}
|
|
600
|
+
{editable && !pickedLatLon && (
|
|
601
|
+
<div className="absolute top-2 left-2 bg-white/90 dark:bg-zinc-900/90 backdrop-blur-sm px-2 py-1 text-[9px] text-zinc-500 dark:text-zinc-400 pointer-events-none shadow-sm border border-zinc-200/50 dark:border-zinc-700/50">
|
|
602
|
+
Click map to place pin
|
|
603
|
+
</div>
|
|
604
|
+
)}
|
|
605
|
+
</div>
|
|
606
|
+
|
|
607
|
+
{/* Picked position info bar */}
|
|
608
|
+
{pickedLatLon && editable && (
|
|
609
|
+
<div className="bg-purple-50/80 dark:bg-purple-950/30 border-t border-purple-200/50 dark:border-purple-800/30 px-3 py-2">
|
|
610
|
+
<div className="flex items-center gap-2 mb-1.5">
|
|
611
|
+
<MapPin className="h-3 w-3 text-purple-600 dark:text-purple-400 shrink-0" />
|
|
612
|
+
<span className="text-[10px] font-semibold text-purple-700 dark:text-purple-300 flex-1">
|
|
613
|
+
New Position
|
|
614
|
+
</span>
|
|
615
|
+
<button
|
|
616
|
+
onClick={handleClearPick}
|
|
617
|
+
className="p-0.5 text-purple-400 hover:text-purple-600 dark:hover:text-purple-300 transition-colors"
|
|
618
|
+
title="Remove pin"
|
|
619
|
+
>
|
|
620
|
+
<X className="h-3 w-3" />
|
|
621
|
+
</button>
|
|
622
|
+
</div>
|
|
623
|
+
|
|
624
|
+
<div className="grid grid-cols-2 gap-x-3 gap-y-0.5 text-[10px] font-mono mb-2">
|
|
625
|
+
<div className="text-zinc-500 dark:text-zinc-400">Lat/Lon</div>
|
|
626
|
+
<div className="text-purple-700 dark:text-purple-300 text-right">
|
|
627
|
+
{pickedLatLon.lat.toFixed(6)}, {pickedLatLon.lon.toFixed(6)}
|
|
628
|
+
</div>
|
|
629
|
+
|
|
630
|
+
{projectedCoords && (
|
|
631
|
+
<>
|
|
632
|
+
<div className="text-zinc-500 dark:text-zinc-400">Easting</div>
|
|
633
|
+
<div className="text-purple-700 dark:text-purple-300 text-right tabular-nums">
|
|
634
|
+
{projectedCoords.easting.toFixed(3)}
|
|
635
|
+
</div>
|
|
636
|
+
<div className="text-zinc-500 dark:text-zinc-400">Northing</div>
|
|
637
|
+
<div className="text-purple-700 dark:text-purple-300 text-right tabular-nums">
|
|
638
|
+
{projectedCoords.northing.toFixed(3)}
|
|
639
|
+
</div>
|
|
640
|
+
</>
|
|
641
|
+
)}
|
|
642
|
+
|
|
643
|
+
<div className="text-zinc-500 dark:text-zinc-400 flex items-center gap-1">
|
|
644
|
+
<Mountain className="h-2.5 w-2.5" />
|
|
645
|
+
Elevation
|
|
646
|
+
</div>
|
|
647
|
+
<div className="text-purple-700 dark:text-purple-300 text-right tabular-nums">
|
|
648
|
+
{elevationLoading ? (
|
|
649
|
+
<Loader2 className="h-2.5 w-2.5 animate-spin inline" />
|
|
650
|
+
) : pickedElevation !== null ? (
|
|
651
|
+
`${pickedElevation.toFixed(1)} m`
|
|
652
|
+
) : (
|
|
653
|
+
<span className="text-zinc-400">—</span>
|
|
654
|
+
)}
|
|
655
|
+
</div>
|
|
656
|
+
</div>
|
|
657
|
+
|
|
658
|
+
{/* Apply button */}
|
|
659
|
+
{onApplyPosition && projectedCoords && (
|
|
660
|
+
<button
|
|
661
|
+
onClick={handleApply}
|
|
662
|
+
disabled={elevationLoading}
|
|
663
|
+
className="w-full flex items-center justify-center gap-1.5 px-2 py-1.5 text-[10px] font-semibold text-white bg-purple-600 hover:bg-purple-700 dark:bg-purple-700 dark:hover:bg-purple-600 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
|
|
664
|
+
>
|
|
665
|
+
<Check className="h-3 w-3" />
|
|
666
|
+
Apply to Eastings / Northings
|
|
667
|
+
{pickedElevation !== null && ' / Height'}
|
|
668
|
+
</button>
|
|
669
|
+
)}
|
|
670
|
+
</div>
|
|
671
|
+
)}
|
|
229
672
|
|
|
230
673
|
{/* Action links */}
|
|
231
674
|
<div className="flex items-center gap-3 px-3 py-1.5 border-t border-zinc-100 dark:border-zinc-900">
|
|
@@ -212,7 +212,7 @@ export function ModelMetadataPanel({ model }: { model: FederatedModel }) {
|
|
|
212
212
|
</div>
|
|
213
213
|
|
|
214
214
|
{/* Georeferencing */}
|
|
215
|
-
<GeoreferencingPanel georef={georef} modelId={model.id} enableEditing schemaVersion={model.schemaVersion} coordinateInfo={model.geometryResult?.coordinateInfo} geometryResult={model.geometryResult} />
|
|
215
|
+
<GeoreferencingPanel georef={georef} modelId={model.id} enableEditing schemaVersion={model.schemaVersion} coordinateInfo={model.geometryResult?.coordinateInfo} geometryResult={model.geometryResult} lengthUnitScale={unitInfo?.scale} />
|
|
216
216
|
|
|
217
217
|
{/* IfcProject Data */}
|
|
218
218
|
{projectData && (
|
|
@@ -39,22 +39,23 @@ export async function handleSelectionClick(ctx: MouseHandlerContext, e: MouseEve
|
|
|
39
39
|
const now = Date.now();
|
|
40
40
|
const timeSinceLastClick = now - ctx.lastClickTimeRef.current;
|
|
41
41
|
const clickPos = { x, y };
|
|
42
|
-
|
|
43
42
|
if (ctx.lastClickPosRef.current &&
|
|
44
43
|
timeSinceLastClick < 300 &&
|
|
45
44
|
Math.abs(clickPos.x - ctx.lastClickPosRef.current.x) < 5 &&
|
|
46
45
|
Math.abs(clickPos.y - ctx.lastClickPosRef.current.y) < 5) {
|
|
46
|
+
const pickOptions = ctx.getPickOptions();
|
|
47
47
|
// Double-click - isolate element
|
|
48
48
|
// Uses visibility filtering so only visible elements can be selected
|
|
49
|
-
const pickResult = await renderer.pick(x, y,
|
|
49
|
+
const pickResult = await renderer.pick(x, y, pickOptions);
|
|
50
50
|
if (pickResult) {
|
|
51
51
|
ctx.handlePickForSelection(pickResult);
|
|
52
52
|
}
|
|
53
53
|
ctx.lastClickTimeRef.current = 0;
|
|
54
54
|
ctx.lastClickPosRef.current = null;
|
|
55
55
|
} else {
|
|
56
|
+
const pickOptions = ctx.getPickOptions();
|
|
56
57
|
// Single click - uses visibility filtering so only visible elements can be selected
|
|
57
|
-
const pickResult = await renderer.pick(x, y,
|
|
58
|
+
const pickResult = await renderer.pick(x, y, pickOptions);
|
|
58
59
|
|
|
59
60
|
// Multi-selection with Ctrl/Cmd
|
|
60
61
|
if (e.ctrlKey || e.metaKey) {
|