@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.
Files changed (495) hide show
  1. package/.turbo/turbo-build.log +39 -30
  2. package/.turbo/turbo-typecheck.log +1 -1
  3. package/CHANGELOG.md +15 -0
  4. package/DESKTOP_CONTRACT_VERSION +1 -0
  5. package/dist/assets/arrow-CZ5kQ26f.js +20 -0
  6. package/dist/assets/basketViewActivator-BmnNtVfZ.js +1 -0
  7. package/dist/assets/{bcf-D5-QWGO9.js → bcf-DOG9_WPX.js} +1 -1
  8. package/dist/assets/{browser-CKs-FY1P.js → browser-C5TFR7sH.js} +1 -1
  9. package/dist/assets/cesium-ADbP7waU.css +1 -0
  10. package/dist/assets/cesium-DUOzBlqv.js +17817 -0
  11. package/dist/assets/{exporters-C_6J153K.js → exporters-ChAtBmlj.js} +2225 -1754
  12. package/dist/assets/geometry.worker-BQ0rzNo-.js +1 -0
  13. package/dist/assets/ifc-lite_bg-BX4E7TX8.wasm +0 -0
  14. package/dist/assets/{index-jhBr1wbn.js → index-Co8E2-FE.js} +28959 -24612
  15. package/dist/assets/index-DckuDqlv.css +1 -0
  16. package/dist/assets/{maplibre-gl-BpvwNKKy.js → maplibre-gl-CGLcoNXc.js} +1 -1
  17. package/dist/assets/native-bridge-BRvbckFQ.js +429 -0
  18. package/dist/assets/{sandbox-B79eavQ3.js → sandbox-DZiNLNMk.js} +5 -5
  19. package/dist/assets/{server-client-D3bUPJJc.js → server-client-BV8zHZ7Y.js} +4 -4
  20. package/dist/assets/tauri-core-stub-D8Fa-u43.js +1 -0
  21. package/dist/assets/tauri-dialog-stub-r7Wksg7o.js +1 -0
  22. package/dist/assets/tauri-fs-stub-BdeRC7aK.js +1 -0
  23. package/dist/assets/wasm-bridge-g01g7T9b.js +1 -0
  24. package/dist/assets/{zip-B-jFFAGa.js → zip-DBEtpeu6.js} +3 -3
  25. package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_0.json +1 -0
  26. package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_1.json +1 -0
  27. package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_10.json +1 -0
  28. package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_11.json +1 -0
  29. package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_12.json +1 -0
  30. package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_13.json +1 -0
  31. package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_14.json +1 -0
  32. package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_15.json +1 -0
  33. package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_16.json +1 -0
  34. package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_17.json +1 -0
  35. package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_18.json +1 -0
  36. package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_19.json +1 -0
  37. package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_2.json +1 -0
  38. package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_20.json +1 -0
  39. package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_21.json +1 -0
  40. package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_22.json +1 -0
  41. package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_23.json +1 -0
  42. package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_24.json +1 -0
  43. package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_25.json +1 -0
  44. package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_26.json +1 -0
  45. package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_27.json +1 -0
  46. package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_3.json +1 -0
  47. package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_4.json +1 -0
  48. package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_5.json +1 -0
  49. package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_6.json +1 -0
  50. package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_7.json +1 -0
  51. package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_8.json +1 -0
  52. package/dist/cesium/Assets/IAU2006_XYS/IAU2006_XYS_9.json +1 -0
  53. package/dist/cesium/Assets/Images/bing_maps_credit.png +0 -0
  54. package/dist/cesium/Assets/Images/cesium_credit.png +0 -0
  55. package/dist/cesium/Assets/Images/google_earth_credit.png +0 -0
  56. package/dist/cesium/Assets/Images/ion-credit.png +0 -0
  57. package/dist/cesium/Assets/Textures/LensFlare/DirtMask.jpg +0 -0
  58. package/dist/cesium/Assets/Textures/LensFlare/StarBurst.jpg +0 -0
  59. package/dist/cesium/Assets/Textures/NaturalEarthII/0/0/0.jpg +0 -0
  60. package/dist/cesium/Assets/Textures/NaturalEarthII/0/1/0.jpg +0 -0
  61. package/dist/cesium/Assets/Textures/NaturalEarthII/1/0/0.jpg +0 -0
  62. package/dist/cesium/Assets/Textures/NaturalEarthII/1/0/1.jpg +0 -0
  63. package/dist/cesium/Assets/Textures/NaturalEarthII/1/1/0.jpg +0 -0
  64. package/dist/cesium/Assets/Textures/NaturalEarthII/1/1/1.jpg +0 -0
  65. package/dist/cesium/Assets/Textures/NaturalEarthII/1/2/0.jpg +0 -0
  66. package/dist/cesium/Assets/Textures/NaturalEarthII/1/2/1.jpg +0 -0
  67. package/dist/cesium/Assets/Textures/NaturalEarthII/1/3/0.jpg +0 -0
  68. package/dist/cesium/Assets/Textures/NaturalEarthII/1/3/1.jpg +0 -0
  69. package/dist/cesium/Assets/Textures/NaturalEarthII/2/0/0.jpg +0 -0
  70. package/dist/cesium/Assets/Textures/NaturalEarthII/2/0/1.jpg +0 -0
  71. package/dist/cesium/Assets/Textures/NaturalEarthII/2/0/2.jpg +0 -0
  72. package/dist/cesium/Assets/Textures/NaturalEarthII/2/0/3.jpg +0 -0
  73. package/dist/cesium/Assets/Textures/NaturalEarthII/2/1/0.jpg +0 -0
  74. package/dist/cesium/Assets/Textures/NaturalEarthII/2/1/1.jpg +0 -0
  75. package/dist/cesium/Assets/Textures/NaturalEarthII/2/1/2.jpg +0 -0
  76. package/dist/cesium/Assets/Textures/NaturalEarthII/2/1/3.jpg +0 -0
  77. package/dist/cesium/Assets/Textures/NaturalEarthII/2/2/0.jpg +0 -0
  78. package/dist/cesium/Assets/Textures/NaturalEarthII/2/2/1.jpg +0 -0
  79. package/dist/cesium/Assets/Textures/NaturalEarthII/2/2/2.jpg +0 -0
  80. package/dist/cesium/Assets/Textures/NaturalEarthII/2/2/3.jpg +0 -0
  81. package/dist/cesium/Assets/Textures/NaturalEarthII/2/3/0.jpg +0 -0
  82. package/dist/cesium/Assets/Textures/NaturalEarthII/2/3/1.jpg +0 -0
  83. package/dist/cesium/Assets/Textures/NaturalEarthII/2/3/2.jpg +0 -0
  84. package/dist/cesium/Assets/Textures/NaturalEarthII/2/3/3.jpg +0 -0
  85. package/dist/cesium/Assets/Textures/NaturalEarthII/2/4/0.jpg +0 -0
  86. package/dist/cesium/Assets/Textures/NaturalEarthII/2/4/1.jpg +0 -0
  87. package/dist/cesium/Assets/Textures/NaturalEarthII/2/4/2.jpg +0 -0
  88. package/dist/cesium/Assets/Textures/NaturalEarthII/2/4/3.jpg +0 -0
  89. package/dist/cesium/Assets/Textures/NaturalEarthII/2/5/0.jpg +0 -0
  90. package/dist/cesium/Assets/Textures/NaturalEarthII/2/5/1.jpg +0 -0
  91. package/dist/cesium/Assets/Textures/NaturalEarthII/2/5/2.jpg +0 -0
  92. package/dist/cesium/Assets/Textures/NaturalEarthII/2/5/3.jpg +0 -0
  93. package/dist/cesium/Assets/Textures/NaturalEarthII/2/6/0.jpg +0 -0
  94. package/dist/cesium/Assets/Textures/NaturalEarthII/2/6/1.jpg +0 -0
  95. package/dist/cesium/Assets/Textures/NaturalEarthII/2/6/2.jpg +0 -0
  96. package/dist/cesium/Assets/Textures/NaturalEarthII/2/6/3.jpg +0 -0
  97. package/dist/cesium/Assets/Textures/NaturalEarthII/2/7/0.jpg +0 -0
  98. package/dist/cesium/Assets/Textures/NaturalEarthII/2/7/1.jpg +0 -0
  99. package/dist/cesium/Assets/Textures/NaturalEarthII/2/7/2.jpg +0 -0
  100. package/dist/cesium/Assets/Textures/NaturalEarthII/2/7/3.jpg +0 -0
  101. package/dist/cesium/Assets/Textures/NaturalEarthII/tilemapresource.xml +14 -0
  102. package/dist/cesium/Assets/Textures/SkyBox/tycho2t3_80_mx.jpg +0 -0
  103. package/dist/cesium/Assets/Textures/SkyBox/tycho2t3_80_my.jpg +0 -0
  104. package/dist/cesium/Assets/Textures/SkyBox/tycho2t3_80_mz.jpg +0 -0
  105. package/dist/cesium/Assets/Textures/SkyBox/tycho2t3_80_px.jpg +0 -0
  106. package/dist/cesium/Assets/Textures/SkyBox/tycho2t3_80_py.jpg +0 -0
  107. package/dist/cesium/Assets/Textures/SkyBox/tycho2t3_80_pz.jpg +0 -0
  108. package/dist/cesium/Assets/Textures/maki/airfield.png +0 -0
  109. package/dist/cesium/Assets/Textures/maki/airport.png +0 -0
  110. package/dist/cesium/Assets/Textures/maki/alcohol-shop.png +0 -0
  111. package/dist/cesium/Assets/Textures/maki/america-football.png +0 -0
  112. package/dist/cesium/Assets/Textures/maki/art-gallery.png +0 -0
  113. package/dist/cesium/Assets/Textures/maki/bakery.png +0 -0
  114. package/dist/cesium/Assets/Textures/maki/bank.png +0 -0
  115. package/dist/cesium/Assets/Textures/maki/bar.png +0 -0
  116. package/dist/cesium/Assets/Textures/maki/baseball.png +0 -0
  117. package/dist/cesium/Assets/Textures/maki/basketball.png +0 -0
  118. package/dist/cesium/Assets/Textures/maki/beer.png +0 -0
  119. package/dist/cesium/Assets/Textures/maki/bicycle.png +0 -0
  120. package/dist/cesium/Assets/Textures/maki/building.png +0 -0
  121. package/dist/cesium/Assets/Textures/maki/bus.png +0 -0
  122. package/dist/cesium/Assets/Textures/maki/cafe.png +0 -0
  123. package/dist/cesium/Assets/Textures/maki/camera.png +0 -0
  124. package/dist/cesium/Assets/Textures/maki/campsite.png +0 -0
  125. package/dist/cesium/Assets/Textures/maki/car.png +0 -0
  126. package/dist/cesium/Assets/Textures/maki/cemetery.png +0 -0
  127. package/dist/cesium/Assets/Textures/maki/cesium.png +0 -0
  128. package/dist/cesium/Assets/Textures/maki/chemist.png +0 -0
  129. package/dist/cesium/Assets/Textures/maki/cinema.png +0 -0
  130. package/dist/cesium/Assets/Textures/maki/circle-stroked.png +0 -0
  131. package/dist/cesium/Assets/Textures/maki/circle.png +0 -0
  132. package/dist/cesium/Assets/Textures/maki/city.png +0 -0
  133. package/dist/cesium/Assets/Textures/maki/clothing-store.png +0 -0
  134. package/dist/cesium/Assets/Textures/maki/college.png +0 -0
  135. package/dist/cesium/Assets/Textures/maki/commercial.png +0 -0
  136. package/dist/cesium/Assets/Textures/maki/cricket.png +0 -0
  137. package/dist/cesium/Assets/Textures/maki/cross.png +0 -0
  138. package/dist/cesium/Assets/Textures/maki/dam.png +0 -0
  139. package/dist/cesium/Assets/Textures/maki/danger.png +0 -0
  140. package/dist/cesium/Assets/Textures/maki/disability.png +0 -0
  141. package/dist/cesium/Assets/Textures/maki/dog-park.png +0 -0
  142. package/dist/cesium/Assets/Textures/maki/embassy.png +0 -0
  143. package/dist/cesium/Assets/Textures/maki/emergency-telephone.png +0 -0
  144. package/dist/cesium/Assets/Textures/maki/entrance.png +0 -0
  145. package/dist/cesium/Assets/Textures/maki/farm.png +0 -0
  146. package/dist/cesium/Assets/Textures/maki/fast-food.png +0 -0
  147. package/dist/cesium/Assets/Textures/maki/ferry.png +0 -0
  148. package/dist/cesium/Assets/Textures/maki/fire-station.png +0 -0
  149. package/dist/cesium/Assets/Textures/maki/fuel.png +0 -0
  150. package/dist/cesium/Assets/Textures/maki/garden.png +0 -0
  151. package/dist/cesium/Assets/Textures/maki/gift.png +0 -0
  152. package/dist/cesium/Assets/Textures/maki/golf.png +0 -0
  153. package/dist/cesium/Assets/Textures/maki/grocery.png +0 -0
  154. package/dist/cesium/Assets/Textures/maki/hairdresser.png +0 -0
  155. package/dist/cesium/Assets/Textures/maki/harbor.png +0 -0
  156. package/dist/cesium/Assets/Textures/maki/heart.png +0 -0
  157. package/dist/cesium/Assets/Textures/maki/heliport.png +0 -0
  158. package/dist/cesium/Assets/Textures/maki/hospital.png +0 -0
  159. package/dist/cesium/Assets/Textures/maki/ice-cream.png +0 -0
  160. package/dist/cesium/Assets/Textures/maki/industrial.png +0 -0
  161. package/dist/cesium/Assets/Textures/maki/land-use.png +0 -0
  162. package/dist/cesium/Assets/Textures/maki/laundry.png +0 -0
  163. package/dist/cesium/Assets/Textures/maki/library.png +0 -0
  164. package/dist/cesium/Assets/Textures/maki/lighthouse.png +0 -0
  165. package/dist/cesium/Assets/Textures/maki/lodging.png +0 -0
  166. package/dist/cesium/Assets/Textures/maki/logging.png +0 -0
  167. package/dist/cesium/Assets/Textures/maki/london-underground.png +0 -0
  168. package/dist/cesium/Assets/Textures/maki/marker-stroked.png +0 -0
  169. package/dist/cesium/Assets/Textures/maki/marker.png +0 -0
  170. package/dist/cesium/Assets/Textures/maki/minefield.png +0 -0
  171. package/dist/cesium/Assets/Textures/maki/mobilephone.png +0 -0
  172. package/dist/cesium/Assets/Textures/maki/monument.png +0 -0
  173. package/dist/cesium/Assets/Textures/maki/museum.png +0 -0
  174. package/dist/cesium/Assets/Textures/maki/music.png +0 -0
  175. package/dist/cesium/Assets/Textures/maki/oil-well.png +0 -0
  176. package/dist/cesium/Assets/Textures/maki/park.png +0 -0
  177. package/dist/cesium/Assets/Textures/maki/park2.png +0 -0
  178. package/dist/cesium/Assets/Textures/maki/parking-garage.png +0 -0
  179. package/dist/cesium/Assets/Textures/maki/parking.png +0 -0
  180. package/dist/cesium/Assets/Textures/maki/pharmacy.png +0 -0
  181. package/dist/cesium/Assets/Textures/maki/pitch.png +0 -0
  182. package/dist/cesium/Assets/Textures/maki/place-of-worship.png +0 -0
  183. package/dist/cesium/Assets/Textures/maki/playground.png +0 -0
  184. package/dist/cesium/Assets/Textures/maki/police.png +0 -0
  185. package/dist/cesium/Assets/Textures/maki/polling-place.png +0 -0
  186. package/dist/cesium/Assets/Textures/maki/post.png +0 -0
  187. package/dist/cesium/Assets/Textures/maki/prison.png +0 -0
  188. package/dist/cesium/Assets/Textures/maki/rail-above.png +0 -0
  189. package/dist/cesium/Assets/Textures/maki/rail-light.png +0 -0
  190. package/dist/cesium/Assets/Textures/maki/rail-metro.png +0 -0
  191. package/dist/cesium/Assets/Textures/maki/rail-underground.png +0 -0
  192. package/dist/cesium/Assets/Textures/maki/rail.png +0 -0
  193. package/dist/cesium/Assets/Textures/maki/religious-christian.png +0 -0
  194. package/dist/cesium/Assets/Textures/maki/religious-jewish.png +0 -0
  195. package/dist/cesium/Assets/Textures/maki/religious-muslim.png +0 -0
  196. package/dist/cesium/Assets/Textures/maki/restaurant.png +0 -0
  197. package/dist/cesium/Assets/Textures/maki/roadblock.png +0 -0
  198. package/dist/cesium/Assets/Textures/maki/rocket.png +0 -0
  199. package/dist/cesium/Assets/Textures/maki/school.png +0 -0
  200. package/dist/cesium/Assets/Textures/maki/scooter.png +0 -0
  201. package/dist/cesium/Assets/Textures/maki/shop.png +0 -0
  202. package/dist/cesium/Assets/Textures/maki/skiing.png +0 -0
  203. package/dist/cesium/Assets/Textures/maki/slaughterhouse.png +0 -0
  204. package/dist/cesium/Assets/Textures/maki/soccer.png +0 -0
  205. package/dist/cesium/Assets/Textures/maki/square-stroked.png +0 -0
  206. package/dist/cesium/Assets/Textures/maki/square.png +0 -0
  207. package/dist/cesium/Assets/Textures/maki/star-stroked.png +0 -0
  208. package/dist/cesium/Assets/Textures/maki/star.png +0 -0
  209. package/dist/cesium/Assets/Textures/maki/suitcase.png +0 -0
  210. package/dist/cesium/Assets/Textures/maki/swimming.png +0 -0
  211. package/dist/cesium/Assets/Textures/maki/telephone.png +0 -0
  212. package/dist/cesium/Assets/Textures/maki/tennis.png +0 -0
  213. package/dist/cesium/Assets/Textures/maki/theatre.png +0 -0
  214. package/dist/cesium/Assets/Textures/maki/toilets.png +0 -0
  215. package/dist/cesium/Assets/Textures/maki/town-hall.png +0 -0
  216. package/dist/cesium/Assets/Textures/maki/town.png +0 -0
  217. package/dist/cesium/Assets/Textures/maki/triangle-stroked.png +0 -0
  218. package/dist/cesium/Assets/Textures/maki/triangle.png +0 -0
  219. package/dist/cesium/Assets/Textures/maki/village.png +0 -0
  220. package/dist/cesium/Assets/Textures/maki/warehouse.png +0 -0
  221. package/dist/cesium/Assets/Textures/maki/waste-basket.png +0 -0
  222. package/dist/cesium/Assets/Textures/maki/water.png +0 -0
  223. package/dist/cesium/Assets/Textures/maki/wetland.png +0 -0
  224. package/dist/cesium/Assets/Textures/maki/zoo.png +0 -0
  225. package/dist/cesium/Assets/Textures/moonSmall.jpg +0 -0
  226. package/dist/cesium/Assets/Textures/pin.svg +1 -0
  227. package/dist/cesium/Assets/Textures/waterNormals.jpg +0 -0
  228. package/dist/cesium/Assets/Textures/waterNormalsSmall.jpg +0 -0
  229. package/dist/cesium/Assets/approximateTerrainHeights.json +1 -0
  230. package/dist/cesium/ThirdParty/Workers/package.json +1 -0
  231. package/dist/cesium/ThirdParty/Workers/zip-web-worker.js +1 -0
  232. package/dist/cesium/ThirdParty/basis_transcoder.wasm +0 -0
  233. package/dist/cesium/ThirdParty/draco_decoder.wasm +0 -0
  234. package/dist/cesium/ThirdParty/google-earth-dbroot-parser.js +1 -0
  235. package/dist/cesium/ThirdParty/wasm_splats_bg.wasm +0 -0
  236. package/dist/cesium/ThirdParty/zip-module.wasm +0 -0
  237. package/dist/cesium/Widgets/Animation/Animation.css +127 -0
  238. package/dist/cesium/Widgets/Animation/lighter.css +70 -0
  239. package/dist/cesium/Widgets/BaseLayerPicker/BaseLayerPicker.css +108 -0
  240. package/dist/cesium/Widgets/BaseLayerPicker/lighter.css +22 -0
  241. package/dist/cesium/Widgets/Cesium3DTilesInspector/Cesium3DTilesInspector.css +102 -0
  242. package/dist/cesium/Widgets/CesiumInspector/CesiumInspector.css +113 -0
  243. package/dist/cesium/Widgets/CesiumWidget/CesiumWidget.css +119 -0
  244. package/dist/cesium/Widgets/CesiumWidget/lighter.css +14 -0
  245. package/dist/cesium/Widgets/FullscreenButton/FullscreenButton.css +8 -0
  246. package/dist/cesium/Widgets/Geocoder/Geocoder.css +70 -0
  247. package/dist/cesium/Widgets/Geocoder/lighter.css +17 -0
  248. package/dist/cesium/Widgets/I3SBuildingSceneLayerExplorer/I3SBuildingSceneLayerExplorer.css +27 -0
  249. package/dist/cesium/Widgets/Images/ImageryProviders/ArcGisMapServiceWorldHillshade.png +0 -0
  250. package/dist/cesium/Widgets/Images/ImageryProviders/ArcGisMapServiceWorldImagery.png +0 -0
  251. package/dist/cesium/Widgets/Images/ImageryProviders/ArcGisMapServiceWorldOcean.png +0 -0
  252. package/dist/cesium/Widgets/Images/ImageryProviders/azureAerial.png +0 -0
  253. package/dist/cesium/Widgets/Images/ImageryProviders/azureRoads.png +0 -0
  254. package/dist/cesium/Widgets/Images/ImageryProviders/bingAerial.png +0 -0
  255. package/dist/cesium/Widgets/Images/ImageryProviders/bingAerialLabels.png +0 -0
  256. package/dist/cesium/Widgets/Images/ImageryProviders/bingRoads.png +0 -0
  257. package/dist/cesium/Widgets/Images/ImageryProviders/blueMarble.png +0 -0
  258. package/dist/cesium/Widgets/Images/ImageryProviders/earthAtNight.png +0 -0
  259. package/dist/cesium/Widgets/Images/ImageryProviders/googleContour.png +0 -0
  260. package/dist/cesium/Widgets/Images/ImageryProviders/googleRoadmap.png +0 -0
  261. package/dist/cesium/Widgets/Images/ImageryProviders/googleSatellite.png +0 -0
  262. package/dist/cesium/Widgets/Images/ImageryProviders/googleSatelliteLabels.png +0 -0
  263. package/dist/cesium/Widgets/Images/ImageryProviders/mapQuestOpenStreetMap.png +0 -0
  264. package/dist/cesium/Widgets/Images/ImageryProviders/mapboxSatellite.png +0 -0
  265. package/dist/cesium/Widgets/Images/ImageryProviders/mapboxStreets.png +0 -0
  266. package/dist/cesium/Widgets/Images/ImageryProviders/mapboxTerrain.png +0 -0
  267. package/dist/cesium/Widgets/Images/ImageryProviders/naturalEarthII.png +0 -0
  268. package/dist/cesium/Widgets/Images/ImageryProviders/openStreetMap.png +0 -0
  269. package/dist/cesium/Widgets/Images/ImageryProviders/sentinel-2.png +0 -0
  270. package/dist/cesium/Widgets/Images/ImageryProviders/stadiaAlidadeSmooth.png +0 -0
  271. package/dist/cesium/Widgets/Images/ImageryProviders/stadiaAlidadeSmoothDark.png +0 -0
  272. package/dist/cesium/Widgets/Images/ImageryProviders/stamenToner.png +0 -0
  273. package/dist/cesium/Widgets/Images/ImageryProviders/stamenWatercolor.png +0 -0
  274. package/dist/cesium/Widgets/Images/NavigationHelp/Mouse.svg +84 -0
  275. package/dist/cesium/Widgets/Images/NavigationHelp/MouseLeft.svg +76 -0
  276. package/dist/cesium/Widgets/Images/NavigationHelp/MouseMiddle.svg +76 -0
  277. package/dist/cesium/Widgets/Images/NavigationHelp/MouseRight.svg +76 -0
  278. package/dist/cesium/Widgets/Images/NavigationHelp/Touch.svg +120 -0
  279. package/dist/cesium/Widgets/Images/NavigationHelp/TouchDrag.svg +129 -0
  280. package/dist/cesium/Widgets/Images/NavigationHelp/TouchRotate.svg +76 -0
  281. package/dist/cesium/Widgets/Images/NavigationHelp/TouchTilt.svg +135 -0
  282. package/dist/cesium/Widgets/Images/NavigationHelp/TouchZoom.svg +74 -0
  283. package/dist/cesium/Widgets/Images/TerrainProviders/CesiumWorldTerrain.png +0 -0
  284. package/dist/cesium/Widgets/Images/TerrainProviders/Ellipsoid.png +0 -0
  285. package/dist/cesium/Widgets/Images/TimelineIcons.png +0 -0
  286. package/dist/cesium/Widgets/Images/info-loading.gif +0 -0
  287. package/dist/cesium/Widgets/InfoBox/InfoBox.css +92 -0
  288. package/dist/cesium/Widgets/InfoBox/InfoBoxDescription.css +178 -0
  289. package/dist/cesium/Widgets/NavigationHelpButton/NavigationHelpButton.css +93 -0
  290. package/dist/cesium/Widgets/NavigationHelpButton/lighter.css +38 -0
  291. package/dist/cesium/Widgets/PerformanceWatchdog/PerformanceWatchdog.css +15 -0
  292. package/dist/cesium/Widgets/ProjectionPicker/ProjectionPicker.css +38 -0
  293. package/dist/cesium/Widgets/SceneModePicker/SceneModePicker.css +56 -0
  294. package/dist/cesium/Widgets/SelectionIndicator/SelectionIndicator.css +20 -0
  295. package/dist/cesium/Widgets/Timeline/Timeline.css +103 -0
  296. package/dist/cesium/Widgets/Timeline/lighter.css +23 -0
  297. package/dist/cesium/Widgets/VRButton/VRButton.css +8 -0
  298. package/dist/cesium/Widgets/Viewer/Viewer.css +107 -0
  299. package/dist/cesium/Widgets/VoxelInspector/VoxelInspector.css +16 -0
  300. package/dist/cesium/Widgets/lighter.css +237 -0
  301. package/dist/cesium/Widgets/lighterShared.css +46 -0
  302. package/dist/cesium/Widgets/shared.css +103 -0
  303. package/dist/cesium/Widgets/widgets.css +1342 -0
  304. package/dist/cesium/Workers/chunk-23ZQ2IVV.js +29 -0
  305. package/dist/cesium/Workers/chunk-2EQO3Q56.js +26 -0
  306. package/dist/cesium/Workers/chunk-2MJIIVP4.js +26 -0
  307. package/dist/cesium/Workers/chunk-2TE5NTVD.js +26 -0
  308. package/dist/cesium/Workers/chunk-2ZBHLJST.js +26 -0
  309. package/dist/cesium/Workers/chunk-5TJMAQVL.js +26 -0
  310. package/dist/cesium/Workers/chunk-6BD4U3VO.js +26 -0
  311. package/dist/cesium/Workers/chunk-7TVGLKQF.js +26 -0
  312. package/dist/cesium/Workers/chunk-BTSYJ5XU.js +26 -0
  313. package/dist/cesium/Workers/chunk-BXMEEOCS.js +63 -0
  314. package/dist/cesium/Workers/chunk-BYLCY7GP.js +29 -0
  315. package/dist/cesium/Workers/chunk-CTHM3W6I.js +26 -0
  316. package/dist/cesium/Workers/chunk-CUUSNIVQ.js +26 -0
  317. package/dist/cesium/Workers/chunk-E3JOOS3S.js +26 -0
  318. package/dist/cesium/Workers/chunk-E7KYDCM5.js +26 -0
  319. package/dist/cesium/Workers/chunk-EDVBB7SS.js +27 -0
  320. package/dist/cesium/Workers/chunk-EFBN7QNX.js +26 -0
  321. package/dist/cesium/Workers/chunk-EQ4YRVWL.js +26 -0
  322. package/dist/cesium/Workers/chunk-F6PRE7D6.js +26 -0
  323. package/dist/cesium/Workers/chunk-FC4ZZ65J.js +26 -0
  324. package/dist/cesium/Workers/chunk-FFBVWF2L.js +26 -0
  325. package/dist/cesium/Workers/chunk-GBAA6GVX.js +26 -0
  326. package/dist/cesium/Workers/chunk-ICALLYLG.js +26 -0
  327. package/dist/cesium/Workers/chunk-ILRYTWTP.js +26 -0
  328. package/dist/cesium/Workers/chunk-IRNLBSEJ.js +26 -0
  329. package/dist/cesium/Workers/chunk-IX4VMHEV.js +26 -0
  330. package/dist/cesium/Workers/chunk-L6QHHACZ.js +26 -0
  331. package/dist/cesium/Workers/chunk-LI2ZSORM.js +26 -0
  332. package/dist/cesium/Workers/chunk-LSLE2RL4.js +26 -0
  333. package/dist/cesium/Workers/chunk-M4HLDBCG.js +26 -0
  334. package/dist/cesium/Workers/chunk-MJHHSGEH.js +26 -0
  335. package/dist/cesium/Workers/chunk-NMVKML6W.js +26 -0
  336. package/dist/cesium/Workers/chunk-OCWJRAXS.js +26 -0
  337. package/dist/cesium/Workers/chunk-OIRKANTH.js +26 -0
  338. package/dist/cesium/Workers/chunk-OIT7J4IC.js +26 -0
  339. package/dist/cesium/Workers/chunk-OLZ3FYUM.js +26 -0
  340. package/dist/cesium/Workers/chunk-Q5BPHJQF.js +26 -0
  341. package/dist/cesium/Workers/chunk-QFM5DCMQ.js +26 -0
  342. package/dist/cesium/Workers/chunk-QKUIYMGC.js +28 -0
  343. package/dist/cesium/Workers/chunk-S44JILQT.js +26 -0
  344. package/dist/cesium/Workers/chunk-SLT4J352.js +26 -0
  345. package/dist/cesium/Workers/chunk-SQMIIXB7.js +26 -0
  346. package/dist/cesium/Workers/chunk-TJ4XLGBQ.js +26 -0
  347. package/dist/cesium/Workers/chunk-TNSUQXWK.js +27 -0
  348. package/dist/cesium/Workers/chunk-UBOGZS7F.js +26 -0
  349. package/dist/cesium/Workers/chunk-V3OSTMM6.js +26 -0
  350. package/dist/cesium/Workers/chunk-V7QEYVP3.js +26 -0
  351. package/dist/cesium/Workers/chunk-VUKYSU4H.js +26 -0
  352. package/dist/cesium/Workers/chunk-W37FE5GR.js +26 -0
  353. package/dist/cesium/Workers/chunk-WBOV35NL.js +26 -0
  354. package/dist/cesium/Workers/chunk-WPMZLB3Y.js +26 -0
  355. package/dist/cesium/Workers/chunk-WWWZVEEH.js +26 -0
  356. package/dist/cesium/Workers/chunk-XFIQ5DEQ.js +28 -0
  357. package/dist/cesium/Workers/chunk-XQHLGIO7.js +26 -0
  358. package/dist/cesium/Workers/chunk-XUSCFAVF.js +26 -0
  359. package/dist/cesium/Workers/chunk-YP7I5QBZ.js +26 -0
  360. package/dist/cesium/Workers/chunk-Z3QF2EHT.js +26 -0
  361. package/dist/cesium/Workers/combineGeometry.js +26 -0
  362. package/dist/cesium/Workers/createBoxGeometry.js +26 -0
  363. package/dist/cesium/Workers/createBoxOutlineGeometry.js +26 -0
  364. package/dist/cesium/Workers/createCircleGeometry.js +26 -0
  365. package/dist/cesium/Workers/createCircleOutlineGeometry.js +26 -0
  366. package/dist/cesium/Workers/createCoplanarPolygonGeometry.js +26 -0
  367. package/dist/cesium/Workers/createCoplanarPolygonOutlineGeometry.js +26 -0
  368. package/dist/cesium/Workers/createCorridorGeometry.js +26 -0
  369. package/dist/cesium/Workers/createCorridorOutlineGeometry.js +26 -0
  370. package/dist/cesium/Workers/createCylinderGeometry.js +26 -0
  371. package/dist/cesium/Workers/createCylinderOutlineGeometry.js +26 -0
  372. package/dist/cesium/Workers/createEllipseGeometry.js +26 -0
  373. package/dist/cesium/Workers/createEllipseOutlineGeometry.js +26 -0
  374. package/dist/cesium/Workers/createEllipsoidGeometry.js +26 -0
  375. package/dist/cesium/Workers/createEllipsoidOutlineGeometry.js +26 -0
  376. package/dist/cesium/Workers/createFrustumGeometry.js +26 -0
  377. package/dist/cesium/Workers/createFrustumOutlineGeometry.js +26 -0
  378. package/dist/cesium/Workers/createGeometry.js +26 -0
  379. package/dist/cesium/Workers/createGroundPolylineGeometry.js +26 -0
  380. package/dist/cesium/Workers/createPlaneGeometry.js +26 -0
  381. package/dist/cesium/Workers/createPlaneOutlineGeometry.js +26 -0
  382. package/dist/cesium/Workers/createPolygonGeometry.js +26 -0
  383. package/dist/cesium/Workers/createPolygonOutlineGeometry.js +26 -0
  384. package/dist/cesium/Workers/createPolylineGeometry.js +26 -0
  385. package/dist/cesium/Workers/createPolylineVolumeGeometry.js +26 -0
  386. package/dist/cesium/Workers/createPolylineVolumeOutlineGeometry.js +26 -0
  387. package/dist/cesium/Workers/createRectangleGeometry.js +26 -0
  388. package/dist/cesium/Workers/createRectangleOutlineGeometry.js +26 -0
  389. package/dist/cesium/Workers/createSimplePolylineGeometry.js +26 -0
  390. package/dist/cesium/Workers/createSphereGeometry.js +26 -0
  391. package/dist/cesium/Workers/createSphereOutlineGeometry.js +26 -0
  392. package/dist/cesium/Workers/createTaskProcessorWorker.js +26 -0
  393. package/dist/cesium/Workers/createVectorTileClampedPolylines.js +26 -0
  394. package/dist/cesium/Workers/createVectorTileGeometries.js +26 -0
  395. package/dist/cesium/Workers/createVectorTilePoints.js +26 -0
  396. package/dist/cesium/Workers/createVectorTilePolygons.js +26 -0
  397. package/dist/cesium/Workers/createVectorTilePolylines.js +26 -0
  398. package/dist/cesium/Workers/createVerticesFromCesium3DTilesTerrain.js +26 -0
  399. package/dist/cesium/Workers/createVerticesFromGoogleEarthEnterpriseBuffer.js +26 -0
  400. package/dist/cesium/Workers/createVerticesFromHeightmap.js +26 -0
  401. package/dist/cesium/Workers/createVerticesFromQuantizedTerrainMesh.js +26 -0
  402. package/dist/cesium/Workers/createWallGeometry.js +26 -0
  403. package/dist/cesium/Workers/createWallOutlineGeometry.js +26 -0
  404. package/dist/cesium/Workers/decodeDraco.js +26 -0
  405. package/dist/cesium/Workers/decodeGoogleEarthEnterprisePacket.js +26 -0
  406. package/dist/cesium/Workers/decodeI3S.js +26 -0
  407. package/dist/cesium/Workers/gaussianSplatSorter.js +26 -0
  408. package/dist/cesium/Workers/gaussianSplatTextureGenerator.js +26 -0
  409. package/dist/cesium/Workers/incrementallyBuildTerrainPicker.js +26 -0
  410. package/dist/cesium/Workers/transcodeKTX2.js +56 -0
  411. package/dist/cesium/Workers/transferTypedArrayTest.js +26 -0
  412. package/dist/cesium/Workers/upsampleQuantizedTerrainMesh.js +26 -0
  413. package/dist/cesium/Workers/upsampleVerticesFromCesium3DTilesTerrain.js +26 -0
  414. package/dist/index.html +10 -8
  415. package/package.json +15 -12
  416. package/src/App.tsx +1 -17
  417. package/src/components/viewer/BCFPanel.tsx +46 -4
  418. package/src/components/viewer/CesiumOverlay.tsx +672 -0
  419. package/src/components/viewer/CesiumSettingsDialog.tsx +100 -0
  420. package/src/components/viewer/ChatPanel.tsx +54 -16
  421. package/src/components/viewer/CommandPalette.tsx +6 -1
  422. package/src/components/viewer/DesktopEntitlementBanner.tsx +74 -0
  423. package/src/components/viewer/ExportChangesButton.tsx +6 -1
  424. package/src/components/viewer/ExportDialog.tsx +22 -6
  425. package/src/components/viewer/HierarchyPanel.tsx +196 -0
  426. package/src/components/viewer/IDSPanel.tsx +52 -3
  427. package/src/components/viewer/KeyboardShortcutsDialog.tsx +1 -1
  428. package/src/components/viewer/MainToolbar.tsx +353 -27
  429. package/src/components/viewer/PropertiesPanel.tsx +218 -79
  430. package/src/components/viewer/ScriptPanel.tsx +34 -8
  431. package/src/components/viewer/SettingsPage.tsx +430 -0
  432. package/src/components/viewer/StatusBar.tsx +17 -1
  433. package/src/components/viewer/UpgradePage.tsx +6 -4
  434. package/src/components/viewer/ViewerLayout.tsx +47 -6
  435. package/src/components/viewer/Viewport.tsx +49 -8
  436. package/src/components/viewer/ViewportContainer.tsx +280 -28
  437. package/src/components/viewer/ViewportOverlays.tsx +129 -27
  438. package/src/components/viewer/properties/GeoreferencingPanel.tsx +117 -5
  439. package/src/components/viewer/properties/LocationMap.tsx +458 -17
  440. package/src/components/viewer/selectionHandlers.ts +4 -3
  441. package/src/components/viewer/useAnimationLoop.ts +4 -0
  442. package/src/components/viewer/useGeometryStreaming.ts +127 -40
  443. package/src/components/viewer/useMouseControls.ts +4 -1
  444. package/src/hooks/ingest/viewerModelIngest.ts +275 -0
  445. package/src/hooks/useIDS.ts +1 -1
  446. package/src/hooks/useIfc.ts +7 -1
  447. package/src/hooks/useIfcCache.ts +28 -15
  448. package/src/hooks/useIfcFederation.ts +57 -225
  449. package/src/hooks/useIfcLoader.ts +1656 -130
  450. package/src/hooks/useIfcServer.ts +0 -69
  451. package/src/lib/desktop/ClerkDesktopEntitlementSync.tsx +175 -0
  452. package/src/lib/desktop/desktopEntitlementEvents.ts +39 -0
  453. package/src/lib/desktop-entitlement.ts +45 -0
  454. package/src/lib/desktop-product.ts +124 -0
  455. package/src/lib/geo/cesium-bridge.ts +310 -0
  456. package/src/lib/geo/reproject.ts +151 -25
  457. package/src/lib/recent-files.ts +2 -1
  458. package/src/services/analysis-extensions.ts +125 -0
  459. package/src/services/app-navigation.ts +13 -0
  460. package/src/services/bsdd.ts +53 -4
  461. package/src/services/cacheService.ts +1 -1
  462. package/src/services/desktop-cache.ts +43 -0
  463. package/src/services/desktop-export.ts +77 -0
  464. package/src/services/desktop-harness.ts +196 -0
  465. package/src/services/desktop-logger.ts +20 -0
  466. package/src/services/desktop-native-metadata.ts +207 -0
  467. package/src/services/desktop-panel-actions.ts +43 -0
  468. package/src/services/desktop-preferences.ts +44 -0
  469. package/src/services/file-dialog.ts +147 -0
  470. package/src/services/tauri-core-stub.ts +7 -0
  471. package/src/services/tauri-dialog-stub.ts +7 -0
  472. package/src/services/tauri-fs-stub.ts +7 -0
  473. package/src/store/index.ts +40 -2
  474. package/src/store/slices/cesiumSlice.ts +122 -0
  475. package/src/store/slices/chatSlice.ts +5 -1
  476. package/src/store/slices/dataSlice.ts +139 -28
  477. package/src/store/slices/desktopEntitlementSlice.ts +86 -0
  478. package/src/store/slices/loadingSlice.ts +14 -2
  479. package/src/store/slices/modelSlice.ts +58 -3
  480. package/src/store/types.ts +96 -2
  481. package/src/store.ts +1 -1
  482. package/src/utils/desktopModelSnapshot.ts +358 -0
  483. package/src/utils/ifcConfig.ts +6 -1
  484. package/src/utils/nativeSpatialDataStore.ts +250 -0
  485. package/src/utils/serverDataModel.ts +4 -0
  486. package/src/utils/spatialHierarchy.ts +10 -11
  487. package/vite.config.ts +24 -0
  488. package/dist/assets/arrow-DJf2ErbF.js +0 -20
  489. package/dist/assets/basketViewActivator-aojwdomq.js +0 -1
  490. package/dist/assets/desktop-cache-oPzaWXYE.js +0 -1
  491. package/dist/assets/geometry.worker-Nz9_YIqh.js +0 -1
  492. package/dist/assets/ifc-lite_bg-eSkBTizQ.wasm +0 -0
  493. package/dist/assets/index-pbE7itQS.css +0 -1
  494. package/dist/assets/native-bridge-DSIyEYXG.js +0 -113
  495. 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
- * Renders as a collapsible panel below the georeferencing fields.
10
- * Includes links to Google Maps, OpenStreetMap, and Google Earth (KMZ export).
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 { Map as MapIcon, ExternalLink, Loader2, MapPinOff, Globe2 } from 'lucide-react';
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
- export function LocationMap({ mapConversion, projectedCRS, coordinateInfo, geometryResult }: LocationMapProps) {
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 marker after style fully loads
178
- if (markerRef.current && mapRef.current) {
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
- ref={containerRef}
226
- 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"
227
- style={{ minHeight: 180 }}
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, ctx.getPickOptions());
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, ctx.getPickOptions());
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
  }