@ifc-lite/viewer 1.17.3 → 1.17.4
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 +15 -0
- package/DESKTOP_CONTRACT_VERSION +1 -0
- package/dist/assets/arrow-CZ5kQ26f.js +20 -0
- package/dist/assets/basketViewActivator-BmnNtVfZ.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/{exporters-C_6J153K.js → exporters-ChAtBmlj.js} +2225 -1754
- package/dist/assets/geometry.worker-BQ0rzNo-.js +1 -0
- package/dist/assets/ifc-lite_bg-BX4E7TX8.wasm +0 -0
- package/dist/assets/{index-jhBr1wbn.js → index-Co8E2-FE.js} +28959 -24612
- package/dist/assets/index-DckuDqlv.css +1 -0
- package/dist/assets/{maplibre-gl-BpvwNKKy.js → maplibre-gl-CGLcoNXc.js} +1 -1
- package/dist/assets/native-bridge-BRvbckFQ.js +429 -0
- package/dist/assets/{sandbox-B79eavQ3.js → sandbox-DZiNLNMk.js} +5 -5
- package/dist/assets/{server-client-D3bUPJJc.js → server-client-BV8zHZ7Y.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-g01g7T9b.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 +10 -8
- package/package.json +15 -12
- package/src/App.tsx +1 -17
- package/src/components/viewer/BCFPanel.tsx +46 -4
- package/src/components/viewer/CesiumOverlay.tsx +672 -0
- package/src/components/viewer/CesiumSettingsDialog.tsx +100 -0
- package/src/components/viewer/ChatPanel.tsx +54 -16
- 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 +353 -27
- package/src/components/viewer/PropertiesPanel.tsx +218 -79
- package/src/components/viewer/ScriptPanel.tsx +34 -8
- package/src/components/viewer/SettingsPage.tsx +430 -0
- package/src/components/viewer/StatusBar.tsx +17 -1
- package/src/components/viewer/UpgradePage.tsx +6 -4
- package/src/components/viewer/ViewerLayout.tsx +47 -6
- package/src/components/viewer/Viewport.tsx +49 -8
- package/src/components/viewer/ViewportContainer.tsx +280 -28
- package/src/components/viewer/ViewportOverlays.tsx +129 -27
- package/src/components/viewer/properties/GeoreferencingPanel.tsx +117 -5
- package/src/components/viewer/properties/LocationMap.tsx +458 -17
- package/src/components/viewer/selectionHandlers.ts +4 -3
- package/src/components/viewer/useAnimationLoop.ts +4 -0
- package/src/components/viewer/useGeometryStreaming.ts +127 -40
- package/src/components/viewer/useMouseControls.ts +4 -1
- package/src/hooks/ingest/viewerModelIngest.ts +275 -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 +57 -225
- package/src/hooks/useIfcLoader.ts +1656 -130
- package/src/hooks/useIfcServer.ts +0 -69
- package/src/lib/desktop/ClerkDesktopEntitlementSync.tsx +175 -0
- package/src/lib/desktop/desktopEntitlementEvents.ts +39 -0
- package/src/lib/desktop-entitlement.ts +45 -0
- package/src/lib/desktop-product.ts +124 -0
- package/src/lib/geo/cesium-bridge.ts +310 -0
- package/src/lib/geo/reproject.ts +151 -25
- package/src/lib/recent-files.ts +2 -1
- package/src/services/analysis-extensions.ts +125 -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/index.ts +40 -2
- package/src/store/slices/cesiumSlice.ts +122 -0
- package/src/store/slices/chatSlice.ts +5 -1
- 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/types.ts +96 -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 +250 -0
- package/src/utils/serverDataModel.ts +4 -0
- package/src/utils/spatialHierarchy.ts +10 -11
- 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/geometry.worker-Nz9_YIqh.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
|
@@ -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,180 @@ 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
|
+
/** Whether the map is in edit mode (allows repositioning) */
|
|
53
|
+
editable?: boolean;
|
|
54
|
+
/** Called when the user applies a new position from the map */
|
|
55
|
+
onApplyPosition?: (position: PickedPosition) => void;
|
|
38
56
|
}
|
|
39
57
|
|
|
40
58
|
type MapState = 'idle' | 'loading' | 'ready' | 'error';
|
|
41
59
|
|
|
42
|
-
|
|
60
|
+
// Debounce helper
|
|
61
|
+
function useDebouncedValue<T>(value: T, delayMs: number): T {
|
|
62
|
+
const [debounced, setDebounced] = useState(value);
|
|
63
|
+
useEffect(() => {
|
|
64
|
+
const timer = setTimeout(() => setDebounced(value), delayMs);
|
|
65
|
+
return () => clearTimeout(timer);
|
|
66
|
+
}, [value, delayMs]);
|
|
67
|
+
return debounced;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** Geocode a query string via Nominatim */
|
|
71
|
+
async function geocodeSearch(query: string): Promise<Array<{ lat: number; lon: number; display_name: string }>> {
|
|
72
|
+
if (!query.trim()) return [];
|
|
73
|
+
try {
|
|
74
|
+
const q = encodeURIComponent(query.trim());
|
|
75
|
+
const resp = await fetch(
|
|
76
|
+
`https://nominatim.openstreetmap.org/search?format=json&limit=5&q=${q}`,
|
|
77
|
+
{ headers: { 'Accept-Language': 'en' } },
|
|
78
|
+
);
|
|
79
|
+
if (!resp.ok) return [];
|
|
80
|
+
const data = await resp.json();
|
|
81
|
+
return data.map((r: { lat: string; lon: string; display_name: string }) => ({
|
|
82
|
+
lat: parseFloat(r.lat),
|
|
83
|
+
lon: parseFloat(r.lon),
|
|
84
|
+
display_name: r.display_name,
|
|
85
|
+
}));
|
|
86
|
+
} catch {
|
|
87
|
+
return [];
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** Add or update the building footprint GeoJSON polygon on a MapLibre map */
|
|
92
|
+
function addFootprintToMap(map: InstanceType<typeof import('maplibre-gl').Map>, ring: [number, number][]) {
|
|
93
|
+
const geojson: GeoJSON.Feature = {
|
|
94
|
+
type: 'Feature',
|
|
95
|
+
properties: {},
|
|
96
|
+
geometry: {
|
|
97
|
+
type: 'Polygon',
|
|
98
|
+
coordinates: [ring],
|
|
99
|
+
},
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
if (map.getSource('building-footprint')) {
|
|
103
|
+
(map.getSource('building-footprint') as import('maplibre-gl').GeoJSONSource).setData(geojson);
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
map.addSource('building-footprint', { type: 'geojson', data: geojson });
|
|
108
|
+
|
|
109
|
+
map.addLayer({
|
|
110
|
+
id: 'building-footprint-fill',
|
|
111
|
+
type: 'fill',
|
|
112
|
+
source: 'building-footprint',
|
|
113
|
+
paint: {
|
|
114
|
+
'fill-color': '#14b8a6',
|
|
115
|
+
'fill-opacity': ['interpolate', ['linear'], ['zoom'], 15, 0, 16, 0.15, 18, 0.25],
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
map.addLayer({
|
|
120
|
+
id: 'building-footprint-outline',
|
|
121
|
+
type: 'line',
|
|
122
|
+
source: 'building-footprint',
|
|
123
|
+
paint: {
|
|
124
|
+
'line-color': '#0d9488',
|
|
125
|
+
'line-width': ['interpolate', ['linear'], ['zoom'], 15, 0.5, 18, 2.5],
|
|
126
|
+
'line-opacity': ['interpolate', ['linear'], ['zoom'], 15, 0, 16, 0.7, 18, 1],
|
|
127
|
+
},
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/** Remove footprint layers and source from a MapLibre map */
|
|
133
|
+
function removeFootprintFromMap(map: InstanceType<typeof import('maplibre-gl').Map>) {
|
|
134
|
+
if (map.getLayer('building-footprint-outline')) map.removeLayer('building-footprint-outline');
|
|
135
|
+
if (map.getLayer('building-footprint-fill')) map.removeLayer('building-footprint-fill');
|
|
136
|
+
if (map.getSource('building-footprint')) map.removeSource('building-footprint');
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export function LocationMap({
|
|
140
|
+
mapConversion, projectedCRS, coordinateInfo, geometryResult,
|
|
141
|
+
editable, onApplyPosition,
|
|
142
|
+
}: LocationMapProps) {
|
|
43
143
|
const containerRef = useRef<HTMLDivElement>(null);
|
|
44
144
|
const mapRef = useRef<InstanceType<typeof import('maplibre-gl').Map> | null>(null);
|
|
45
145
|
const markerRef = useRef<InstanceType<typeof import('maplibre-gl').Marker> | null>(null);
|
|
146
|
+
const pickedMarkerRef = useRef<InstanceType<typeof import('maplibre-gl').Marker> | null>(null);
|
|
147
|
+
const editableRef = useRef(editable);
|
|
148
|
+
|
|
149
|
+
// Keep editableRef in sync; clean up edit-only state when leaving edit mode
|
|
150
|
+
useEffect(() => {
|
|
151
|
+
editableRef.current = editable;
|
|
152
|
+
if (!editable) {
|
|
153
|
+
setSearchOpen(false);
|
|
154
|
+
setSearchQuery('');
|
|
155
|
+
setSearchResults([]);
|
|
156
|
+
setSearchLoading(false);
|
|
157
|
+
pickedMarkerRef.current?.remove();
|
|
158
|
+
pickedMarkerRef.current = null;
|
|
159
|
+
setPickedLatLon(null);
|
|
160
|
+
setProjectedCoords(null);
|
|
161
|
+
setPickedElevation(null);
|
|
162
|
+
setElevationLoading(false);
|
|
163
|
+
}
|
|
164
|
+
}, [editable]);
|
|
46
165
|
|
|
47
166
|
const [mapState, setMapState] = useState<MapState>('idle');
|
|
48
167
|
const [latLon, setLatLon] = useState<LatLon | null>(null);
|
|
49
168
|
const [error, setError] = useState<string | null>(null);
|
|
50
169
|
|
|
170
|
+
// Picked position state (user-placed pin)
|
|
171
|
+
const [pickedLatLon, setPickedLatLon] = useState<LatLon | null>(null);
|
|
172
|
+
const [pickedElevation, setPickedElevation] = useState<number | null>(null);
|
|
173
|
+
const [elevationLoading, setElevationLoading] = useState(false);
|
|
174
|
+
const [projectedCoords, setProjectedCoords] = useState<{ easting: number; northing: number } | null>(null);
|
|
175
|
+
|
|
176
|
+
// Search state
|
|
177
|
+
const [searchOpen, setSearchOpen] = useState(false);
|
|
178
|
+
const [searchQuery, setSearchQuery] = useState('');
|
|
179
|
+
const [searchResults, setSearchResults] = useState<Array<{ lat: number; lon: number; display_name: string }>>([]);
|
|
180
|
+
const [searchLoading, setSearchLoading] = useState(false);
|
|
181
|
+
const debouncedQuery = useDebouncedValue(searchQuery, 400);
|
|
182
|
+
|
|
183
|
+
// Building footprint state (bounding box polygon in WGS84)
|
|
184
|
+
const [footprint, setFootprint] = useState<[number, number][] | null>(null);
|
|
185
|
+
const [styleVersion, setStyleVersion] = useState(0);
|
|
186
|
+
const footprintRef = useRef<[number, number][] | null>(null);
|
|
187
|
+
|
|
188
|
+
// Compute building footprint from bounding box
|
|
189
|
+
useEffect(() => {
|
|
190
|
+
if (!mapConversion || !projectedCRS || !coordinateInfo) {
|
|
191
|
+
setFootprint(null);
|
|
192
|
+
footprintRef.current = null;
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
let cancelled = false;
|
|
197
|
+
|
|
198
|
+
computeFootprintGeoJSON(mapConversion, projectedCRS, coordinateInfo).then(fp => {
|
|
199
|
+
if (cancelled) return;
|
|
200
|
+
setFootprint(fp);
|
|
201
|
+
footprintRef.current = fp;
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
return () => { cancelled = true; };
|
|
205
|
+
}, [mapConversion, projectedCRS, coordinateInfo]);
|
|
206
|
+
|
|
207
|
+
// Geocode search
|
|
208
|
+
useEffect(() => {
|
|
209
|
+
if (!debouncedQuery.trim()) {
|
|
210
|
+
setSearchResults([]);
|
|
211
|
+
setSearchLoading(false);
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
let cancelled = false;
|
|
215
|
+
setSearchLoading(true);
|
|
216
|
+
geocodeSearch(debouncedQuery).then(results => {
|
|
217
|
+
if (!cancelled) {
|
|
218
|
+
setSearchResults(results);
|
|
219
|
+
setSearchLoading(false);
|
|
220
|
+
}
|
|
221
|
+
});
|
|
222
|
+
return () => { cancelled = true; };
|
|
223
|
+
}, [debouncedQuery]);
|
|
224
|
+
|
|
225
|
+
// Reproject model position to lat/lon
|
|
51
226
|
useEffect(() => {
|
|
52
227
|
if (!mapConversion || !projectedCRS) {
|
|
53
228
|
setLatLon(null);
|
|
@@ -74,6 +249,102 @@ export function LocationMap({ mapConversion, projectedCRS, coordinateInfo, geome
|
|
|
74
249
|
return () => { cancelled = true; };
|
|
75
250
|
}, [mapConversion, projectedCRS, coordinateInfo]);
|
|
76
251
|
|
|
252
|
+
// When a picked position changes, reverse-project and query elevation
|
|
253
|
+
useEffect(() => {
|
|
254
|
+
if (!pickedLatLon || !projectedCRS) {
|
|
255
|
+
setProjectedCoords(null);
|
|
256
|
+
setPickedElevation(null);
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
let cancelled = false;
|
|
261
|
+
setProjectedCoords(null);
|
|
262
|
+
|
|
263
|
+
// Reverse-project to get IfcMapConversion eastings/northings
|
|
264
|
+
// Accounts for model local geometry offset, rotation, and scale
|
|
265
|
+
reprojectFromLatLon(pickedLatLon, projectedCRS, mapConversion, coordinateInfo).then(coords => {
|
|
266
|
+
if (!cancelled) setProjectedCoords(coords);
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
// Query terrain elevation
|
|
270
|
+
setElevationLoading(true);
|
|
271
|
+
queryTerrainElevation(pickedLatLon).then(elev => {
|
|
272
|
+
if (!cancelled) {
|
|
273
|
+
setPickedElevation(elev);
|
|
274
|
+
setElevationLoading(false);
|
|
275
|
+
}
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
return () => { cancelled = true; };
|
|
279
|
+
}, [pickedLatLon, projectedCRS, mapConversion, coordinateInfo]);
|
|
280
|
+
|
|
281
|
+
// Place or move the picked marker on the map
|
|
282
|
+
const updatePickedMarker = useCallback((pos: LatLon, maplibregl: typeof import('maplibre-gl')) => {
|
|
283
|
+
if (!mapRef.current) return;
|
|
284
|
+
if (pickedMarkerRef.current) {
|
|
285
|
+
pickedMarkerRef.current.setLngLat([pos.lon, pos.lat]);
|
|
286
|
+
} else {
|
|
287
|
+
const el = document.createElement('div');
|
|
288
|
+
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>`;
|
|
289
|
+
el.style.cursor = 'grab';
|
|
290
|
+
const marker = new maplibregl.Marker({ element: el, draggable: true })
|
|
291
|
+
.setLngLat([pos.lon, pos.lat])
|
|
292
|
+
.addTo(mapRef.current);
|
|
293
|
+
marker.on('dragend', () => {
|
|
294
|
+
const lngLat = marker.getLngLat();
|
|
295
|
+
setPickedLatLon({ lat: lngLat.lat, lon: lngLat.lng });
|
|
296
|
+
});
|
|
297
|
+
pickedMarkerRef.current = marker;
|
|
298
|
+
}
|
|
299
|
+
}, []);
|
|
300
|
+
|
|
301
|
+
// Handle map click to place pin (reads editable from ref to avoid stale closure)
|
|
302
|
+
const handleMapClick = useCallback((e: { lngLat: { lat: number; lng: number } }) => {
|
|
303
|
+
if (!editableRef.current) return;
|
|
304
|
+
const pos = { lat: e.lngLat.lat, lon: e.lngLat.lng };
|
|
305
|
+
setPickedLatLon(pos);
|
|
306
|
+
loadMaplibre().then(ml => updatePickedMarker(pos, ml));
|
|
307
|
+
}, [updatePickedMarker]);
|
|
308
|
+
|
|
309
|
+
// Handle search result selection
|
|
310
|
+
const handleSearchSelect = useCallback((result: { lat: number; lon: number; display_name: string }) => {
|
|
311
|
+
const pos = { lat: result.lat, lon: result.lon };
|
|
312
|
+
setPickedLatLon(pos);
|
|
313
|
+
setSearchOpen(false);
|
|
314
|
+
setSearchQuery('');
|
|
315
|
+
setSearchResults([]);
|
|
316
|
+
|
|
317
|
+
loadMaplibre().then(ml => {
|
|
318
|
+
updatePickedMarker(pos, ml);
|
|
319
|
+
mapRef.current?.flyTo({ center: [pos.lon, pos.lat], zoom: 16, duration: 1200 });
|
|
320
|
+
});
|
|
321
|
+
}, [updatePickedMarker]);
|
|
322
|
+
|
|
323
|
+
// Handle apply position (waits for elevation to finish loading)
|
|
324
|
+
const handleApply = useCallback(() => {
|
|
325
|
+
if (!projectedCoords || !onApplyPosition || elevationLoading) return;
|
|
326
|
+
onApplyPosition({
|
|
327
|
+
easting: Math.round(projectedCoords.easting * 1000) / 1000,
|
|
328
|
+
northing: Math.round(projectedCoords.northing * 1000) / 1000,
|
|
329
|
+
terrainHeight: pickedElevation,
|
|
330
|
+
});
|
|
331
|
+
// Clear picked state after applying
|
|
332
|
+
pickedMarkerRef.current?.remove();
|
|
333
|
+
pickedMarkerRef.current = null;
|
|
334
|
+
setPickedLatLon(null);
|
|
335
|
+
setProjectedCoords(null);
|
|
336
|
+
setPickedElevation(null);
|
|
337
|
+
}, [projectedCoords, pickedElevation, onApplyPosition, elevationLoading]);
|
|
338
|
+
|
|
339
|
+
// Clear picked pin
|
|
340
|
+
const handleClearPick = useCallback(() => {
|
|
341
|
+
pickedMarkerRef.current?.remove();
|
|
342
|
+
pickedMarkerRef.current = null;
|
|
343
|
+
setPickedLatLon(null);
|
|
344
|
+
setProjectedCoords(null);
|
|
345
|
+
setPickedElevation(null);
|
|
346
|
+
}, []);
|
|
347
|
+
|
|
77
348
|
// Initialize/update the map when we have a valid lat/lon
|
|
78
349
|
useEffect(() => {
|
|
79
350
|
if (!latLon || !containerRef.current) return;
|
|
@@ -105,23 +376,60 @@ export function LocationMap({ mapConversion, projectedCRS, coordinateInfo, geome
|
|
|
105
376
|
map.addControl(new maplibregl.NavigationControl({ showCompass: false }), 'top-right');
|
|
106
377
|
map.addControl(new maplibregl.AttributionControl({ compact: false }), 'bottom-right');
|
|
107
378
|
|
|
108
|
-
// Add marker at model location
|
|
379
|
+
// Add marker at model location (teal = current model position)
|
|
109
380
|
const marker = new maplibregl.Marker({ color: '#14b8a6' })
|
|
110
381
|
.setLngLat([latLon.lon, latLon.lat])
|
|
111
382
|
.addTo(map);
|
|
112
383
|
|
|
384
|
+
// Toggle marker vs footprint based on zoom level
|
|
385
|
+
map.on('zoomend', () => {
|
|
386
|
+
const zoom = map.getZoom();
|
|
387
|
+
if (markerRef.current) {
|
|
388
|
+
markerRef.current.getElement().style.opacity = zoom >= 17 ? '0' : '1';
|
|
389
|
+
markerRef.current.getElement().style.pointerEvents = zoom >= 17 ? 'none' : 'auto';
|
|
390
|
+
}
|
|
391
|
+
});
|
|
392
|
+
|
|
393
|
+
// Map click to place pin (only in edit mode)
|
|
394
|
+
map.on('click', handleMapClick);
|
|
395
|
+
|
|
113
396
|
mapRef.current = map;
|
|
114
397
|
markerRef.current = marker;
|
|
398
|
+
|
|
399
|
+
// If footprint was already computed before the map was created, add it now
|
|
400
|
+
if (footprintRef.current) {
|
|
401
|
+
map.once('load', () => {
|
|
402
|
+
addFootprintToMap(map, footprintRef.current!);
|
|
403
|
+
});
|
|
404
|
+
}
|
|
115
405
|
});
|
|
116
406
|
|
|
117
407
|
return () => {
|
|
118
408
|
cancelled = true;
|
|
119
409
|
};
|
|
120
|
-
}, [latLon]);
|
|
410
|
+
}, [latLon, handleMapClick]);
|
|
411
|
+
|
|
412
|
+
// Add/update building footprint GeoJSON layer when footprint or style changes
|
|
413
|
+
useEffect(() => {
|
|
414
|
+
const map = mapRef.current;
|
|
415
|
+
if (!map) return;
|
|
416
|
+
if (!footprint) {
|
|
417
|
+
removeFootprintFromMap(map);
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
if (map.isStyleLoaded()) {
|
|
422
|
+
addFootprintToMap(map, footprint);
|
|
423
|
+
} else {
|
|
424
|
+
map.once('style.load', () => addFootprintToMap(map, footprint));
|
|
425
|
+
}
|
|
426
|
+
}, [footprint, styleVersion]);
|
|
121
427
|
|
|
122
428
|
// Cleanup on unmount
|
|
123
429
|
useEffect(() => {
|
|
124
430
|
return () => {
|
|
431
|
+
pickedMarkerRef.current?.remove();
|
|
432
|
+
pickedMarkerRef.current = null;
|
|
125
433
|
markerRef.current?.remove();
|
|
126
434
|
markerRef.current = null;
|
|
127
435
|
mapRef.current?.remove();
|
|
@@ -152,7 +460,7 @@ export function LocationMap({ mapConversion, projectedCRS, coordinateInfo, geome
|
|
|
152
460
|
glb,
|
|
153
461
|
name: 'IFC Model',
|
|
154
462
|
});
|
|
155
|
-
const blob = new Blob([kmz], { type: 'application/vnd.google-earth.kmz' });
|
|
463
|
+
const blob = new Blob([kmz as BlobPart], { type: 'application/vnd.google-earth.kmz' });
|
|
156
464
|
const url = URL.createObjectURL(blob);
|
|
157
465
|
const a = document.createElement('a');
|
|
158
466
|
a.href = url;
|
|
@@ -174,12 +482,17 @@ export function LocationMap({ mapConversion, projectedCRS, coordinateInfo, geome
|
|
|
174
482
|
? 'https://basemaps.cartocdn.com/gl/dark-matter-gl-style/style.json'
|
|
175
483
|
: 'https://basemaps.cartocdn.com/gl/positron-gl-style/style.json',
|
|
176
484
|
);
|
|
177
|
-
// Re-add
|
|
178
|
-
if (
|
|
485
|
+
// Re-add markers and layers after style fully loads
|
|
486
|
+
if (mapRef.current) {
|
|
179
487
|
mapRef.current.once('style.load', () => {
|
|
180
488
|
if (markerRef.current && mapRef.current) {
|
|
181
489
|
markerRef.current.addTo(mapRef.current);
|
|
182
490
|
}
|
|
491
|
+
if (pickedMarkerRef.current && mapRef.current) {
|
|
492
|
+
pickedMarkerRef.current.addTo(mapRef.current);
|
|
493
|
+
}
|
|
494
|
+
// Trigger footprint layer re-add
|
|
495
|
+
setStyleVersion(v => v + 1);
|
|
183
496
|
});
|
|
184
497
|
}
|
|
185
498
|
}, []);
|
|
@@ -191,19 +504,73 @@ export function LocationMap({ mapConversion, projectedCRS, coordinateInfo, geome
|
|
|
191
504
|
|
|
192
505
|
return (
|
|
193
506
|
<div className="border-t border-zinc-100 dark:border-zinc-900">
|
|
194
|
-
{/* Header */}
|
|
507
|
+
{/* Header with search */}
|
|
195
508
|
<div className="flex items-center gap-2 px-3 py-1.5">
|
|
196
509
|
<MapIcon className="h-3 w-3 text-teal-500 shrink-0" />
|
|
197
510
|
<span className="font-bold text-[11px] text-zinc-700 dark:text-zinc-300 uppercase tracking-wide flex-1">
|
|
198
511
|
Location
|
|
199
512
|
</span>
|
|
200
|
-
{latLon && (
|
|
513
|
+
{latLon && !searchOpen && (
|
|
201
514
|
<span className="text-[10px] font-mono text-teal-600/70 dark:text-teal-500/60">
|
|
202
515
|
{latLon.lat.toFixed(5)}, {latLon.lon.toFixed(5)}
|
|
203
516
|
</span>
|
|
204
517
|
)}
|
|
518
|
+
{editable && (
|
|
519
|
+
<button
|
|
520
|
+
onClick={() => { setSearchOpen(!searchOpen); setSearchQuery(''); setSearchResults([]); }}
|
|
521
|
+
className="p-0.5 text-zinc-400 hover:text-teal-600 dark:hover:text-teal-400 transition-colors"
|
|
522
|
+
title="Search for a place"
|
|
523
|
+
>
|
|
524
|
+
<Search className="h-3 w-3" />
|
|
525
|
+
</button>
|
|
526
|
+
)}
|
|
205
527
|
</div>
|
|
206
528
|
|
|
529
|
+
{/* Search bar */}
|
|
530
|
+
{editable && searchOpen && (
|
|
531
|
+
<div className="px-3 pb-1.5 relative">
|
|
532
|
+
<div className="flex items-center gap-1">
|
|
533
|
+
<div className="flex-1 relative">
|
|
534
|
+
<input
|
|
535
|
+
value={searchQuery}
|
|
536
|
+
onChange={e => setSearchQuery(e.target.value)}
|
|
537
|
+
placeholder="Search for a place..."
|
|
538
|
+
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"
|
|
539
|
+
autoFocus
|
|
540
|
+
onKeyDown={e => { if (e.key === 'Escape') { setSearchOpen(false); setSearchQuery(''); setSearchResults([]); } }}
|
|
541
|
+
/>
|
|
542
|
+
{searchLoading && (
|
|
543
|
+
<Loader2 className="absolute right-2 top-1/2 -translate-y-1/2 h-3 w-3 text-teal-500 animate-spin" />
|
|
544
|
+
)}
|
|
545
|
+
</div>
|
|
546
|
+
<button
|
|
547
|
+
onClick={() => { setSearchOpen(false); setSearchQuery(''); setSearchResults([]); }}
|
|
548
|
+
className="p-0.5 text-zinc-400 hover:text-zinc-600 dark:hover:text-zinc-300"
|
|
549
|
+
>
|
|
550
|
+
<X className="h-3 w-3" />
|
|
551
|
+
</button>
|
|
552
|
+
</div>
|
|
553
|
+
|
|
554
|
+
{/* Search results dropdown */}
|
|
555
|
+
{searchResults.length > 0 && (
|
|
556
|
+
<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">
|
|
557
|
+
{searchResults.map((r, i) => (
|
|
558
|
+
<button
|
|
559
|
+
key={i}
|
|
560
|
+
onClick={() => handleSearchSelect(r)}
|
|
561
|
+
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"
|
|
562
|
+
>
|
|
563
|
+
<div className="flex items-start gap-1.5">
|
|
564
|
+
<MapPin className="h-3 w-3 text-teal-500 shrink-0 mt-0.5" />
|
|
565
|
+
<span className="line-clamp-2">{r.display_name}</span>
|
|
566
|
+
</div>
|
|
567
|
+
</button>
|
|
568
|
+
))}
|
|
569
|
+
</div>
|
|
570
|
+
)}
|
|
571
|
+
</div>
|
|
572
|
+
)}
|
|
573
|
+
|
|
207
574
|
{/* Map container */}
|
|
208
575
|
{mapState === 'loading' && (
|
|
209
576
|
<div className="flex items-center justify-center h-[180px] bg-zinc-50 dark:bg-zinc-900/50">
|
|
@@ -221,11 +588,85 @@ export function LocationMap({ mapConversion, projectedCRS, coordinateInfo, geome
|
|
|
221
588
|
|
|
222
589
|
{(mapState === 'ready' || (mapState === 'loading' && latLon)) && (
|
|
223
590
|
<>
|
|
224
|
-
<div
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
591
|
+
<div className="relative">
|
|
592
|
+
<div
|
|
593
|
+
ref={containerRef}
|
|
594
|
+
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"
|
|
595
|
+
style={{ minHeight: 180 }}
|
|
596
|
+
/>
|
|
597
|
+
{/* Edit mode hint overlay */}
|
|
598
|
+
{editable && !pickedLatLon && (
|
|
599
|
+
<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">
|
|
600
|
+
Click map to place pin
|
|
601
|
+
</div>
|
|
602
|
+
)}
|
|
603
|
+
</div>
|
|
604
|
+
|
|
605
|
+
{/* Picked position info bar */}
|
|
606
|
+
{pickedLatLon && editable && (
|
|
607
|
+
<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">
|
|
608
|
+
<div className="flex items-center gap-2 mb-1.5">
|
|
609
|
+
<MapPin className="h-3 w-3 text-purple-600 dark:text-purple-400 shrink-0" />
|
|
610
|
+
<span className="text-[10px] font-semibold text-purple-700 dark:text-purple-300 flex-1">
|
|
611
|
+
New Position
|
|
612
|
+
</span>
|
|
613
|
+
<button
|
|
614
|
+
onClick={handleClearPick}
|
|
615
|
+
className="p-0.5 text-purple-400 hover:text-purple-600 dark:hover:text-purple-300 transition-colors"
|
|
616
|
+
title="Remove pin"
|
|
617
|
+
>
|
|
618
|
+
<X className="h-3 w-3" />
|
|
619
|
+
</button>
|
|
620
|
+
</div>
|
|
621
|
+
|
|
622
|
+
<div className="grid grid-cols-2 gap-x-3 gap-y-0.5 text-[10px] font-mono mb-2">
|
|
623
|
+
<div className="text-zinc-500 dark:text-zinc-400">Lat/Lon</div>
|
|
624
|
+
<div className="text-purple-700 dark:text-purple-300 text-right">
|
|
625
|
+
{pickedLatLon.lat.toFixed(6)}, {pickedLatLon.lon.toFixed(6)}
|
|
626
|
+
</div>
|
|
627
|
+
|
|
628
|
+
{projectedCoords && (
|
|
629
|
+
<>
|
|
630
|
+
<div className="text-zinc-500 dark:text-zinc-400">Easting</div>
|
|
631
|
+
<div className="text-purple-700 dark:text-purple-300 text-right tabular-nums">
|
|
632
|
+
{projectedCoords.easting.toFixed(3)}
|
|
633
|
+
</div>
|
|
634
|
+
<div className="text-zinc-500 dark:text-zinc-400">Northing</div>
|
|
635
|
+
<div className="text-purple-700 dark:text-purple-300 text-right tabular-nums">
|
|
636
|
+
{projectedCoords.northing.toFixed(3)}
|
|
637
|
+
</div>
|
|
638
|
+
</>
|
|
639
|
+
)}
|
|
640
|
+
|
|
641
|
+
<div className="text-zinc-500 dark:text-zinc-400 flex items-center gap-1">
|
|
642
|
+
<Mountain className="h-2.5 w-2.5" />
|
|
643
|
+
Elevation
|
|
644
|
+
</div>
|
|
645
|
+
<div className="text-purple-700 dark:text-purple-300 text-right tabular-nums">
|
|
646
|
+
{elevationLoading ? (
|
|
647
|
+
<Loader2 className="h-2.5 w-2.5 animate-spin inline" />
|
|
648
|
+
) : pickedElevation !== null ? (
|
|
649
|
+
`${pickedElevation.toFixed(1)} m`
|
|
650
|
+
) : (
|
|
651
|
+
<span className="text-zinc-400">—</span>
|
|
652
|
+
)}
|
|
653
|
+
</div>
|
|
654
|
+
</div>
|
|
655
|
+
|
|
656
|
+
{/* Apply button */}
|
|
657
|
+
{onApplyPosition && projectedCoords && (
|
|
658
|
+
<button
|
|
659
|
+
onClick={handleApply}
|
|
660
|
+
disabled={elevationLoading}
|
|
661
|
+
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"
|
|
662
|
+
>
|
|
663
|
+
<Check className="h-3 w-3" />
|
|
664
|
+
Apply to Eastings / Northings
|
|
665
|
+
{pickedElevation !== null && ' / Height'}
|
|
666
|
+
</button>
|
|
667
|
+
)}
|
|
668
|
+
</div>
|
|
669
|
+
)}
|
|
229
670
|
|
|
230
671
|
{/* Action links */}
|
|
231
672
|
<div className="flex items-center gap-3 px-3 py-1.5 border-t border-zinc-100 dark:border-zinc-900">
|
|
@@ -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) {
|
|
@@ -29,6 +29,8 @@ export interface UseAnimationLoopParams {
|
|
|
29
29
|
lastFrameTimeRef: MutableRefObject<number>;
|
|
30
30
|
mouseIsDraggingRef: MutableRefObject<boolean>;
|
|
31
31
|
activeToolRef: MutableRefObject<string>;
|
|
32
|
+
/** When set, clips model below this Y value (terrain clipping for Cesium overlay). */
|
|
33
|
+
terrainClipYRef: MutableRefObject<number | null>;
|
|
32
34
|
hiddenEntitiesRef: MutableRefObject<Set<number>>;
|
|
33
35
|
isolatedEntitiesRef: MutableRefObject<Set<number> | null>;
|
|
34
36
|
selectedEntityIdRef: MutableRefObject<number | null>;
|
|
@@ -62,6 +64,7 @@ export function useAnimationLoop(params: UseAnimationLoopParams): void {
|
|
|
62
64
|
lastFrameTimeRef,
|
|
63
65
|
mouseIsDraggingRef,
|
|
64
66
|
activeToolRef,
|
|
67
|
+
terrainClipYRef,
|
|
65
68
|
hiddenEntitiesRef,
|
|
66
69
|
isolatedEntitiesRef,
|
|
67
70
|
selectedEntityIdRef,
|
|
@@ -171,6 +174,7 @@ export function useAnimationLoop(params: UseAnimationLoopParams): void {
|
|
|
171
174
|
min: sectionRangeRef.current?.min,
|
|
172
175
|
max: sectionRangeRef.current?.max,
|
|
173
176
|
} : undefined,
|
|
177
|
+
terrainClipY: terrainClipYRef.current ?? undefined,
|
|
174
178
|
});
|
|
175
179
|
lastRenderTime = currentTime;
|
|
176
180
|
}
|