leaflet-city-mapper 0.0.0-stage → 0.0.2

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 (412) hide show
  1. package/LICENSE +674 -0
  2. package/README.md +78 -2
  3. package/cucumber.mjs +9 -0
  4. package/dist/leaflet-city-mapper.js +2 -0
  5. package/dist/leaflet-city-mapper.js.map +7 -0
  6. package/dist/utils/CELL.js +2 -0
  7. package/dist/utils/CELL.js.map +7 -0
  8. package/dist/utils/addBridges.js +2 -0
  9. package/dist/utils/addBridges.js.map +7 -0
  10. package/dist/utils/addRasterTile.js +2 -0
  11. package/dist/utils/addRasterTile.js.map +7 -0
  12. package/dist/utils/along.js +2 -0
  13. package/dist/utils/along.js.map +7 -0
  14. package/dist/utils/atmosphere.js +2 -0
  15. package/dist/utils/atmosphere.js.map +7 -0
  16. package/dist/utils/attach.js +2 -0
  17. package/dist/utils/attach.js.map +7 -0
  18. package/dist/utils/bindSunlight.js +2 -0
  19. package/dist/utils/bindSunlight.js.map +7 -0
  20. package/dist/utils/box.js +2 -0
  21. package/dist/utils/box.js.map +7 -0
  22. package/dist/utils/bridgeElevation.js +2 -0
  23. package/dist/utils/bridgeElevation.js.map +7 -0
  24. package/dist/utils/build.js +2 -0
  25. package/dist/utils/build.js.map +7 -0
  26. package/dist/utils/cameraControls.js +2 -0
  27. package/dist/utils/cameraControls.js.map +7 -0
  28. package/dist/utils/channel.js +2 -0
  29. package/dist/utils/channel.js.map +7 -0
  30. package/dist/utils/collider.js +2 -0
  31. package/dist/utils/collider.js.map +7 -0
  32. package/dist/utils/colliders.js +2 -0
  33. package/dist/utils/colliders.js.map +7 -0
  34. package/dist/utils/computeBounds.js +2 -0
  35. package/dist/utils/computeBounds.js.map +7 -0
  36. package/dist/utils/conformModelToTerrain.js +2 -0
  37. package/dist/utils/conformModelToTerrain.js.map +7 -0
  38. package/dist/utils/createMesh.js +2 -0
  39. package/dist/utils/createMesh.js.map +7 -0
  40. package/dist/utils/createProgram.js +3 -0
  41. package/dist/utils/createProgram.js.map +7 -0
  42. package/dist/utils/decodeDirectory.js +2 -0
  43. package/dist/utils/decodeDirectory.js.map +7 -0
  44. package/dist/utils/decodeHeader.js +2 -0
  45. package/dist/utils/decodeHeader.js.map +7 -0
  46. package/dist/utils/decodeModel.js +2 -0
  47. package/dist/utils/decodeModel.js.map +7 -0
  48. package/dist/utils/decompress.js +2 -0
  49. package/dist/utils/decompress.js.map +7 -0
  50. package/dist/utils/destroyTexture.js +2 -0
  51. package/dist/utils/destroyTexture.js.map +7 -0
  52. package/dist/utils/disposeMesh.js +2 -0
  53. package/dist/utils/disposeMesh.js.map +7 -0
  54. package/dist/utils/drawGeometry.js +2 -0
  55. package/dist/utils/drawGeometry.js.map +7 -0
  56. package/dist/utils/elevationWGSL.js +24 -0
  57. package/dist/utils/elevationWGSL.js.map +7 -0
  58. package/dist/utils/emptyNode.js +2 -0
  59. package/dist/utils/emptyNode.js.map +7 -0
  60. package/dist/utils/endZoom.js +2 -0
  61. package/dist/utils/endZoom.js.map +7 -0
  62. package/dist/utils/findFeature.js +2 -0
  63. package/dist/utils/findFeature.js.map +7 -0
  64. package/dist/utils/geometry.js +1 -0
  65. package/dist/utils/geometry.js.map +7 -0
  66. package/dist/utils/getElevation.js +2 -0
  67. package/dist/utils/getElevation.js.map +7 -0
  68. package/dist/utils/getHeader.js +2 -0
  69. package/dist/utils/getHeader.js.map +7 -0
  70. package/dist/utils/getZxy.js +2 -0
  71. package/dist/utils/getZxy.js.map +7 -0
  72. package/dist/utils/glass.js +2 -0
  73. package/dist/utils/glass.js.map +7 -0
  74. package/dist/utils/headlight.js +2 -0
  75. package/dist/utils/headlight.js.map +7 -0
  76. package/dist/utils/hit.js +2 -0
  77. package/dist/utils/hit.js.map +7 -0
  78. package/dist/utils/horizontalPosition.js +2 -0
  79. package/dist/utils/horizontalPosition.js.map +7 -0
  80. package/dist/utils/horizontalRay.js +2 -0
  81. package/dist/utils/horizontalRay.js.map +7 -0
  82. package/dist/utils/hull.js +2 -0
  83. package/dist/utils/hull.js.map +7 -0
  84. package/dist/utils/initSunlight.js +2 -0
  85. package/dist/utils/initSunlight.js.map +7 -0
  86. package/dist/utils/initTerrain.js +2 -0
  87. package/dist/utils/initTerrain.js.map +7 -0
  88. package/dist/utils/initTraffic.js +40 -0
  89. package/dist/utils/initTraffic.js.map +7 -0
  90. package/dist/utils/initWalk.js +2 -0
  91. package/dist/utils/initWalk.js.map +7 -0
  92. package/dist/utils/intersectMeshes.js +2 -0
  93. package/dist/utils/intersectMeshes.js.map +7 -0
  94. package/dist/utils/isClockWise.js +2 -0
  95. package/dist/utils/isClockWise.js.map +7 -0
  96. package/dist/utils/lampPostGeometry.js +2 -0
  97. package/dist/utils/lampPostGeometry.js.map +7 -0
  98. package/dist/utils/lightGeometry.js +2 -0
  99. package/dist/utils/lightGeometry.js.map +7 -0
  100. package/dist/utils/lightGrid.js +2 -0
  101. package/dist/utils/lightGrid.js.map +7 -0
  102. package/dist/utils/limbs.js +2 -0
  103. package/dist/utils/limbs.js.map +7 -0
  104. package/dist/utils/loadModel.js +2 -0
  105. package/dist/utils/loadModel.js.map +7 -0
  106. package/dist/utils/loadSource.js +2 -0
  107. package/dist/utils/loadSource.js.map +7 -0
  108. package/dist/utils/loadTerrain.js +2 -0
  109. package/dist/utils/loadTerrain.js.map +7 -0
  110. package/dist/utils/mapCamera.js +2 -0
  111. package/dist/utils/mapCamera.js.map +7 -0
  112. package/dist/utils/materialShaders.js +211 -0
  113. package/dist/utils/materialShaders.js.map +7 -0
  114. package/dist/utils/mirror.js +2 -0
  115. package/dist/utils/mirror.js.map +7 -0
  116. package/dist/utils/moonPosition.js +2 -0
  117. package/dist/utils/moonPosition.js.map +7 -0
  118. package/dist/utils/moves.js +2 -0
  119. package/dist/utils/moves.js.map +7 -0
  120. package/dist/utils/nativeEvents.js +2 -0
  121. package/dist/utils/nativeEvents.js.map +7 -0
  122. package/dist/utils/nativeGeometry.js +2 -0
  123. package/dist/utils/nativeGeometry.js.map +7 -0
  124. package/dist/utils/nativeLayer.js +2 -0
  125. package/dist/utils/nativeLayer.js.map +7 -0
  126. package/dist/utils/nodeMatrix.js +2 -0
  127. package/dist/utils/nodeMatrix.js.map +7 -0
  128. package/dist/utils/onAdd3DLayer.js +2 -0
  129. package/dist/utils/onAdd3DLayer.js.map +7 -0
  130. package/dist/utils/onAddSun.js +2 -0
  131. package/dist/utils/onAddSun.js.map +7 -0
  132. package/dist/utils/onClick.js +2 -0
  133. package/dist/utils/onClick.js.map +7 -0
  134. package/dist/utils/onMapChange.js +2 -0
  135. package/dist/utils/onMapChange.js.map +7 -0
  136. package/dist/utils/onPMTileAdd.js +2 -0
  137. package/dist/utils/onPMTileAdd.js.map +7 -0
  138. package/dist/utils/onPMTileRemove.js +2 -0
  139. package/dist/utils/onPMTileRemove.js.map +7 -0
  140. package/dist/utils/onPointerLeave.js +2 -0
  141. package/dist/utils/onPointerLeave.js.map +7 -0
  142. package/dist/utils/onPointerMove.js +2 -0
  143. package/dist/utils/onPointerMove.js.map +7 -0
  144. package/dist/utils/onRemove3DLayer.js +2 -0
  145. package/dist/utils/onRemove3DLayer.js.map +7 -0
  146. package/dist/utils/onRemoveSun.js +2 -0
  147. package/dist/utils/onRemoveSun.js.map +7 -0
  148. package/dist/utils/onTerrainAdd.js +2 -0
  149. package/dist/utils/onTerrainAdd.js.map +7 -0
  150. package/dist/utils/onTerrainChange.js +2 -0
  151. package/dist/utils/onTerrainChange.js.map +7 -0
  152. package/dist/utils/onTerrainRemove.js +2 -0
  153. package/dist/utils/onTerrainRemove.js.map +7 -0
  154. package/dist/utils/onZoomAnim.js +2 -0
  155. package/dist/utils/onZoomAnim.js.map +7 -0
  156. package/dist/utils/paints.js +2 -0
  157. package/dist/utils/paints.js.map +7 -0
  158. package/dist/utils/parseColor.js +2 -0
  159. package/dist/utils/parseColor.js.map +7 -0
  160. package/dist/utils/parts.js +2 -0
  161. package/dist/utils/parts.js.map +7 -0
  162. package/dist/utils/projectCelestialBody.js +2 -0
  163. package/dist/utils/projectCelestialBody.js.map +7 -0
  164. package/dist/utils/pump.js +2 -0
  165. package/dist/utils/pump.js.map +7 -0
  166. package/dist/utils/rasterTiles.js +2 -0
  167. package/dist/utils/rasterTiles.js.map +7 -0
  168. package/dist/utils/raycast.js +2 -0
  169. package/dist/utils/raycast.js.map +7 -0
  170. package/dist/utils/read.js +2 -0
  171. package/dist/utils/read.js.map +7 -0
  172. package/dist/utils/readAccessor.js +2 -0
  173. package/dist/utils/readAccessor.js.map +7 -0
  174. package/dist/utils/releaseArea.js +2 -0
  175. package/dist/utils/releaseArea.js.map +7 -0
  176. package/dist/utils/removeRasterTile.js +2 -0
  177. package/dist/utils/removeRasterTile.js.map +7 -0
  178. package/dist/utils/render.js +15 -0
  179. package/dist/utils/render.js.map +7 -0
  180. package/dist/utils/renderSunlight.js +13 -0
  181. package/dist/utils/renderSunlight.js.map +7 -0
  182. package/dist/utils/renderers.js +2 -0
  183. package/dist/utils/renderers.js.map +7 -0
  184. package/dist/utils/resizeSunlight.js +2 -0
  185. package/dist/utils/resizeSunlight.js.map +7 -0
  186. package/dist/utils/roofCoverage.js +2 -0
  187. package/dist/utils/roofCoverage.js.map +7 -0
  188. package/dist/utils/routeGeometry.js +2 -0
  189. package/dist/utils/routeGeometry.js.map +7 -0
  190. package/dist/utils/scheduledTrips.js +2 -0
  191. package/dist/utils/scheduledTrips.js.map +7 -0
  192. package/dist/utils/setGeoJSONData.js +2 -0
  193. package/dist/utils/setGeoJSONData.js.map +7 -0
  194. package/dist/utils/setHighlight.js +2 -0
  195. package/dist/utils/setHighlight.js.map +7 -0
  196. package/dist/utils/setPMTileOpacity.js +2 -0
  197. package/dist/utils/setPMTileOpacity.js.map +7 -0
  198. package/dist/utils/setPMTileZIndex.js +2 -0
  199. package/dist/utils/setPMTileZIndex.js.map +7 -0
  200. package/dist/utils/setParent.js +2 -0
  201. package/dist/utils/setParent.js.map +7 -0
  202. package/dist/utils/setTerrainHeight.js +2 -0
  203. package/dist/utils/setTerrainHeight.js.map +7 -0
  204. package/dist/utils/setVertexColor.js +2 -0
  205. package/dist/utils/setVertexColor.js.map +7 -0
  206. package/dist/utils/showFeature.js +2 -0
  207. package/dist/utils/showFeature.js.map +7 -0
  208. package/dist/utils/sizes.js +2 -0
  209. package/dist/utils/sizes.js.map +7 -0
  210. package/dist/utils/skin.js +2 -0
  211. package/dist/utils/skin.js.map +7 -0
  212. package/dist/utils/solarPosition.js +2 -0
  213. package/dist/utils/solarPosition.js.map +7 -0
  214. package/dist/utils/sortMeshes.js +2 -0
  215. package/dist/utils/sortMeshes.js.map +7 -0
  216. package/dist/utils/speed.js +2 -0
  217. package/dist/utils/speed.js.map +7 -0
  218. package/dist/utils/startZoom.js +2 -0
  219. package/dist/utils/startZoom.js.map +7 -0
  220. package/dist/utils/taillight.js +2 -0
  221. package/dist/utils/taillight.js.map +7 -0
  222. package/dist/utils/terrainAtlas.js +2 -0
  223. package/dist/utils/terrainAtlas.js.map +7 -0
  224. package/dist/utils/terrainElevation.js +2 -0
  225. package/dist/utils/terrainElevation.js.map +7 -0
  226. package/dist/utils/terrainGrid.js +2 -0
  227. package/dist/utils/terrainGrid.js.map +7 -0
  228. package/dist/utils/textureSampler.js +2 -0
  229. package/dist/utils/textureSampler.js.map +7 -0
  230. package/dist/utils/tileId.js +2 -0
  231. package/dist/utils/tileId.js.map +7 -0
  232. package/dist/utils/toLanes.js +2 -0
  233. package/dist/utils/toLanes.js.map +7 -0
  234. package/dist/utils/toSRGB.js +2 -0
  235. package/dist/utils/toSRGB.js.map +7 -0
  236. package/dist/utils/tooltip.js +3 -0
  237. package/dist/utils/tooltip.js.map +7 -0
  238. package/dist/utils/traffic.js +2 -0
  239. package/dist/utils/traffic.js.map +7 -0
  240. package/dist/utils/traverse.js +2 -0
  241. package/dist/utils/traverse.js.map +7 -0
  242. package/dist/utils/treeGeometry.js +2 -0
  243. package/dist/utils/treeGeometry.js.map +7 -0
  244. package/dist/utils/triangulate.js +2 -0
  245. package/dist/utils/triangulate.js.map +7 -0
  246. package/dist/utils/tripsAt.js +2 -0
  247. package/dist/utils/tripsAt.js.map +7 -0
  248. package/dist/utils/types.js +2 -0
  249. package/dist/utils/types.js.map +7 -0
  250. package/dist/utils/underEntry.js +2 -0
  251. package/dist/utils/underEntry.js.map +7 -0
  252. package/dist/utils/update.js +2 -0
  253. package/dist/utils/update.js.map +7 -0
  254. package/dist/utils/updateArea.js +2 -0
  255. package/dist/utils/updateArea.js.map +7 -0
  256. package/dist/utils/updateCamera.js +2 -0
  257. package/dist/utils/updateCamera.js.map +7 -0
  258. package/dist/utils/updatePointLights.js +2 -0
  259. package/dist/utils/updatePointLights.js.map +7 -0
  260. package/dist/utils/updateWorldMatrix.js +2 -0
  261. package/dist/utils/updateWorldMatrix.js.map +7 -0
  262. package/dist/utils/vehicleOffsets.js +2 -0
  263. package/dist/utils/vehicleOffsets.js.map +7 -0
  264. package/dist/utils/vehicles.js +2 -0
  265. package/dist/utils/vehicles.js.map +7 -0
  266. package/dist/utils/vertexNormals.js +2 -0
  267. package/dist/utils/vertexNormals.js.map +7 -0
  268. package/dist/utils/walk.js +2 -0
  269. package/dist/utils/walk.js.map +7 -0
  270. package/dist/utils/white.js +2 -0
  271. package/dist/utils/white.js.map +7 -0
  272. package/dist/utils/worldLights.js +2 -0
  273. package/dist/utils/worldLights.js.map +7 -0
  274. package/dist/utils/wrap.js +2 -0
  275. package/dist/utils/wrap.js.map +7 -0
  276. package/package.json +43 -4
  277. package/src/index.js +397 -0
  278. package/src/utils/CELL.js +4 -0
  279. package/src/utils/addBridges.js +66 -0
  280. package/src/utils/addRasterTile.js +112 -0
  281. package/src/utils/along.js +9 -0
  282. package/src/utils/atmosphere.js +20 -0
  283. package/src/utils/attach.js +255 -0
  284. package/src/utils/bindSunlight.js +12 -0
  285. package/src/utils/box.js +1 -0
  286. package/src/utils/bridgeElevation.js +18 -0
  287. package/src/utils/build.js +20 -0
  288. package/src/utils/cameraControls.js +287 -0
  289. package/src/utils/channel.js +4 -0
  290. package/src/utils/collider.js +62 -0
  291. package/src/utils/colliders.js +2 -0
  292. package/src/utils/computeBounds.js +17 -0
  293. package/src/utils/conformModelToTerrain.js +69 -0
  294. package/src/utils/createMesh.js +31 -0
  295. package/src/utils/createProgram.js +92 -0
  296. package/src/utils/decodeDirectory.js +40 -0
  297. package/src/utils/decodeHeader.js +15 -0
  298. package/src/utils/decodeModel.js +43 -0
  299. package/src/utils/decompress.js +5 -0
  300. package/src/utils/destroyTexture.js +7 -0
  301. package/src/utils/disposeMesh.js +13 -0
  302. package/src/utils/drawGeometry.js +122 -0
  303. package/src/utils/elevationWGSL.js +27 -0
  304. package/src/utils/emptyNode.js +9 -0
  305. package/src/utils/endZoom.js +15 -0
  306. package/src/utils/findFeature.js +49 -0
  307. package/src/utils/geometry.js +2 -0
  308. package/src/utils/getElevation.js +10 -0
  309. package/src/utils/getHeader.js +9 -0
  310. package/src/utils/getZxy.js +76 -0
  311. package/src/utils/glass.js +1 -0
  312. package/src/utils/headlight.js +1 -0
  313. package/src/utils/hit.js +41 -0
  314. package/src/utils/horizontalPosition.js +16 -0
  315. package/src/utils/horizontalRay.js +12 -0
  316. package/src/utils/hull.js +1 -0
  317. package/src/utils/initSunlight.js +9 -0
  318. package/src/utils/initTerrain.js +19 -0
  319. package/src/utils/initTraffic.js +93 -0
  320. package/src/utils/initWalk.js +72 -0
  321. package/src/utils/intersectMeshes.js +121 -0
  322. package/src/utils/isClockWise.js +12 -0
  323. package/src/utils/lampPostGeometry.js +26 -0
  324. package/src/utils/lightGeometry.js +22 -0
  325. package/src/utils/lightGrid.js +50 -0
  326. package/src/utils/limbs.js +9 -0
  327. package/src/utils/loadModel.js +217 -0
  328. package/src/utils/loadSource.js +17 -0
  329. package/src/utils/loadTerrain.js +65 -0
  330. package/src/utils/mapCamera.js +432 -0
  331. package/src/utils/materialShaders.js +243 -0
  332. package/src/utils/mirror.js +6 -0
  333. package/src/utils/moonPosition.js +17 -0
  334. package/src/utils/moves.js +11 -0
  335. package/src/utils/nativeEvents.js +77 -0
  336. package/src/utils/nativeGeometry.js +131 -0
  337. package/src/utils/nativeLayer.js +317 -0
  338. package/src/utils/nodeMatrix.js +22 -0
  339. package/src/utils/onAdd3DLayer.js +31 -0
  340. package/src/utils/onAddSun.js +147 -0
  341. package/src/utils/onClick.js +13 -0
  342. package/src/utils/onMapChange.js +16 -0
  343. package/src/utils/onPMTileAdd.js +15 -0
  344. package/src/utils/onPMTileRemove.js +8 -0
  345. package/src/utils/onPointerLeave.js +11 -0
  346. package/src/utils/onPointerMove.js +15 -0
  347. package/src/utils/onRemove3DLayer.js +78 -0
  348. package/src/utils/onRemoveSun.js +3 -0
  349. package/src/utils/onTerrainAdd.js +18 -0
  350. package/src/utils/onTerrainChange.js +13 -0
  351. package/src/utils/onTerrainRemove.js +40 -0
  352. package/src/utils/onZoomAnim.js +14 -0
  353. package/src/utils/paints.js +10 -0
  354. package/src/utils/parseColor.js +42 -0
  355. package/src/utils/parts.js +16 -0
  356. package/src/utils/projectCelestialBody.js +13 -0
  357. package/src/utils/pump.js +226 -0
  358. package/src/utils/rasterTiles.js +285 -0
  359. package/src/utils/raycast.js +60 -0
  360. package/src/utils/read.js +29 -0
  361. package/src/utils/readAccessor.js +68 -0
  362. package/src/utils/releaseArea.js +34 -0
  363. package/src/utils/removeRasterTile.js +13 -0
  364. package/src/utils/render.js +406 -0
  365. package/src/utils/renderSunlight.js +170 -0
  366. package/src/utils/renderers.js +2 -0
  367. package/src/utils/resizeSunlight.js +15 -0
  368. package/src/utils/roofCoverage.js +47 -0
  369. package/src/utils/routeGeometry.js +22 -0
  370. package/src/utils/scheduledTrips.js +29 -0
  371. package/src/utils/setGeoJSONData.js +11 -0
  372. package/src/utils/setHighlight.js +13 -0
  373. package/src/utils/setPMTileOpacity.js +7 -0
  374. package/src/utils/setPMTileZIndex.js +7 -0
  375. package/src/utils/setParent.js +7 -0
  376. package/src/utils/setTerrainHeight.js +10 -0
  377. package/src/utils/setVertexColor.js +14 -0
  378. package/src/utils/showFeature.js +27 -0
  379. package/src/utils/sizes.js +1 -0
  380. package/src/utils/skin.js +2 -0
  381. package/src/utils/solarPosition.js +17 -0
  382. package/src/utils/sortMeshes.js +12 -0
  383. package/src/utils/speed.js +3 -0
  384. package/src/utils/startZoom.js +5 -0
  385. package/src/utils/taillight.js +1 -0
  386. package/src/utils/terrainAtlas.js +21 -0
  387. package/src/utils/terrainElevation.js +20 -0
  388. package/src/utils/terrainGrid.js +23 -0
  389. package/src/utils/textureSampler.js +10 -0
  390. package/src/utils/tileId.js +18 -0
  391. package/src/utils/toLanes.js +7 -0
  392. package/src/utils/toSRGB.js +1 -0
  393. package/src/utils/tooltip.js +6 -0
  394. package/src/utils/traffic.js +315 -0
  395. package/src/utils/traverse.js +6 -0
  396. package/src/utils/treeGeometry.js +45 -0
  397. package/src/utils/triangulate.js +100 -0
  398. package/src/utils/tripsAt.js +16 -0
  399. package/src/utils/types.js +8 -0
  400. package/src/utils/underEntry.js +17 -0
  401. package/src/utils/update.js +18 -0
  402. package/src/utils/updateArea.js +40 -0
  403. package/src/utils/updateCamera.js +32 -0
  404. package/src/utils/updatePointLights.js +38 -0
  405. package/src/utils/updateWorldMatrix.js +47 -0
  406. package/src/utils/vehicleOffsets.js +8 -0
  407. package/src/utils/vehicles.js +65 -0
  408. package/src/utils/vertexNormals.js +30 -0
  409. package/src/utils/walk.js +187 -0
  410. package/src/utils/white.js +1 -0
  411. package/src/utils/worldLights.js +25 -0
  412. package/src/utils/wrap.js +1 -0
@@ -0,0 +1,226 @@
1
+ import { emptyNode } from "./emptyNode.js";
2
+ import { isClockWise } from "./isClockWise.js";
3
+ import { lampPostGeometry } from "./lampPostGeometry.js";
4
+ import { lightGeometry } from "./lightGeometry.js";
5
+ import { parseColor } from "./parseColor.js";
6
+ import { treeGeometry } from "./treeGeometry.js";
7
+ import { triangulate } from "./triangulate.js";
8
+ import { createProgram } from './createProgram.js';
9
+ import { updateStatus } from "./releaseArea.js";
10
+ import { loadSource } from './loadSource.js';
11
+ import { setParent } from './setParent.js';
12
+ import { update } from './update.js';
13
+ import { getElevation } from "./getElevation.js";
14
+
15
+ /** Share four load/build slots across source layers, nearest ground blocks first. */
16
+
17
+ export function pump(backend, map = backend._map) {
18
+ // Meshes need the shared renderer; _attach() re-runs _updateArea() once it exists.
19
+ const shared = map && L.Layer._renderers.get(map);
20
+ if (!shared) return;
21
+ const layers = [...shared._layers].filter(layer =>
22
+ Number.isFinite(layer._inflight) && layer._areas && (layer.url || layer._geojson));
23
+ const pending = layers.flatMap(layer => [...layer._areas.values()]
24
+ .filter(area => !area.started && !area.request.signal.aborted)
25
+ .map(area => {
26
+ const center = area.coords && map.project(map.getCenter(), area.coords.z).divideBy(256);
27
+ return { layer, area, distance: center
28
+ ? Math.hypot(area.coords.x + 0.5 - center.x, area.coords.y + 0.5 - center.y) / 2 ** area.coords.z : 0 };
29
+ })).sort((a, b) => a.distance - b.distance);
30
+ let inflight = layers.reduce((sum, layer) => sum + layer._inflight, 0);
31
+ for (const { layer: backend, area } of pending) {
32
+ if (inflight >= 4) break;
33
+ if (backend._inflight >= 2) continue;
34
+ area.started = true;
35
+ backend._inflight++;
36
+ inflight++;
37
+ (async () => {
38
+ try {
39
+ // Let ground requests start first, and avoid rebuilding flat geometry when the DEM arrives.
40
+ await new Promise(requestAnimationFrame);
41
+ await map._terrain3D?._terrainPromise;
42
+ if (area.request.signal.aborted) return;
43
+ const coords = area.coords;
44
+ const data = await loadSource(backend, area);
45
+ if (area.request.signal.aborted) return;
46
+ if (data?.type !== 'FeatureCollection' || !Array.isArray(data.features)) {
47
+ throw new Error('Invalid GeoJSON response');
48
+ }
49
+ if (backend._geojson && typeof backend.options.style !== 'function') {
50
+ throw new Error('style must be a function');
51
+ }
52
+ const features = data.features;
53
+ const positions = [], normals = [], colors = [], uvs = [], featureFaces = [], lights = [];
54
+ const supports = new Set();
55
+ let skipped = 0;
56
+ const crs = map.options.crs;
57
+ const origin = crs.project(backend.location);
58
+ const metres = Math.cos(backend.location.lat * Math.PI / 180);
59
+ const groundOffset = point => point.ground || 0;
60
+ const addTriangle = (a, ah, b, bh, c, ch, nx, ny, nz, color) => {
61
+ const ux = b.x - a.x, uy = bh - ah, uz = a.y - b.y;
62
+ const vx = c.x - a.x, vy = ch - ah, vz = a.y - c.y;
63
+ // Orient the triangle to its intended outward normal after flipping north to -z.
64
+ if ((uy * vz - uz * vy) * nx + (uz * vx - ux * vz) * ny + (ux * vy - uy * vx) * nz < 0) {
65
+ const point = b, height = bh;
66
+ b = c; bh = ch; c = point; ch = height;
67
+ }
68
+ positions.push(a.x, ah + groundOffset(a), -a.y,
69
+ b.x, bh + groundOffset(b), -b.y,
70
+ c.x, ch + groundOffset(c), -c.y);
71
+ normals.push(nx, ny, nz, nx, ny, nz, nx, ny, nz);
72
+ colors.push(color.r, color.g, color.b, color.r, color.g, color.b, color.r, color.g, color.b);
73
+ if (backend.options.nightLight) uvs.push(...a.uv, ...b.uv, ...c.uv);
74
+ };
75
+ let deadline = performance.now() + 4;
76
+ for (const feature of features) {
77
+ if (area.request.signal.aborted) return;
78
+ if (performance.now() >= deadline) {
79
+ await new Promise(requestAnimationFrame);
80
+ if (area.request.signal.aborted) return;
81
+ deadline = performance.now() + 4;
82
+ }
83
+ const geometry = feature?.geometry;
84
+ const isTree = geometry?.type === 'Point';
85
+ if (backend.options.nightLight && !isTree) throw new Error('Night lights require Point geometry');
86
+ const polygons = geometry?.type === 'Polygon' ? [geometry.coordinates] : geometry?.type === 'MultiPolygon' ? geometry.coordinates : null;
87
+ if (!polygons && !isTree) throw new Error('Unsupported GeoJSON geometry');
88
+ const [lon, lat] = (isTree ? geometry.coordinates : polygons[0]?.[0]?.[0]) ?? [];
89
+ if (!Number.isFinite(lon) || !Number.isFinite(lat)) throw new Error('Invalid GeoJSON coordinates');
90
+ if (coords) {
91
+ const anchor = map.project(new L.LatLng(lat, lon), coords.z);
92
+ // Overlapping archive blocks may contain the same polygon; its first vertex owns it.
93
+ if (Math.floor(anchor.x / 256) !== coords.x || Math.floor(anchor.y / 256) !== coords.y) continue;
94
+ }
95
+ const style = backend.options.style(feature);
96
+ if (style === null) {
97
+ skipped++;
98
+ continue;
99
+ }
100
+ if (!style || typeof style !== 'object') throw new Error('style must return an extrusion style or null');
101
+ const { top, bottom = 0, color = 0xd9b38c } = style;
102
+ if (!Number.isFinite(top) || !Number.isFinite(bottom) || bottom < 0 || top <= bottom) {
103
+ throw new Error('Invalid extrusion heights: expected top > bottom >= 0');
104
+ }
105
+ const baseColor = parseColor(color);
106
+ const vertexStart = positions.length / 3;
107
+ if (isTree) {
108
+ const point = crs.project(new L.LatLng(lat, lon));
109
+ const elevation = map._terrain3D ? getElevation(map._terrain3D, [lat, lon]) : undefined;
110
+ const projected = {
111
+ x: (point.x - origin.x) * metres, y: (point.y - origin.y) * metres,
112
+ ground: Number.isFinite(elevation) && Number.isFinite(backend.root.position[1])
113
+ ? elevation - backend.root.position[1] : 0,
114
+ };
115
+ if (backend.options.nightLight) {
116
+ if (!Number.isFinite(style.lightFlux) || style.lightFlux <= 0) throw new Error('Invalid light flux');
117
+ const support = feature.properties?.cod_ouvrag || `${lon},${lat}`;
118
+ if (style.poleRadius > 0 && !supports.has(support)) {
119
+ lampPostGeometry(projected, style, addTriangle);
120
+ supports.add(support);
121
+ }
122
+ lightGeometry(projected, style, addTriangle);
123
+ lights.push({
124
+ position: [projected.x, projected.ground + top, -projected.y],
125
+ radius: Math.hypot(top, style.lightRadius), color: [baseColor.r, baseColor.g, baseColor.b],
126
+ flux: style.lightFlux,
127
+ });
128
+ } else treeGeometry(projected, style, addTriangle);
129
+ }
130
+ for (const rings of polygons || []) {
131
+ if (!rings.length) throw new Error('Empty GeoJSON polygon');
132
+ const paths = rings.map(ring => {
133
+ if (ring.length < 4) throw new Error('Invalid GeoJSON polygon ring');
134
+ return ring.slice(0, -1).map(([lng, lat]) => {
135
+ if (!Number.isFinite(lng) || !Number.isFinite(lat)) throw new Error('Invalid GeoJSON coordinates');
136
+ const point = crs.project(new L.LatLng(lat, lng));
137
+ const elevation = backend._map._terrain3D ? getElevation(backend._map._terrain3D, [lat, lng]) : undefined;
138
+ return {
139
+ x: (point.x - origin.x) * metres,
140
+ y: (point.y - origin.y) * metres,
141
+ ground: Number.isFinite(elevation) && Number.isFinite(backend.root.position[1])
142
+ ? elevation - backend.root.position[1] : 0,
143
+ };
144
+ });
145
+ });
146
+ const points = paths.flat();
147
+ for (const [a, b, c] of triangulate(paths[0], paths.slice(1))) {
148
+ addTriangle(points[a], top, points[b], top, points[c], top, 0, 1, 0, baseColor);
149
+ if (bottom > 0) addTriangle(points[a], bottom, points[b], bottom, points[c], bottom, 0, -1, 0, baseColor);
150
+ }
151
+ paths.forEach((path, index) => {
152
+ // Interior walls face into the courtyard; exterior walls face away from the building.
153
+ const sign = isClockWise(path) === (index === 0) ? -1 : 1;
154
+ for (let i = 0; i < path.length; i++) {
155
+ const a = path[i], b = path[(i + 1) % path.length];
156
+ const length = Math.hypot(b.x - a.x, b.y - a.y);
157
+ if (!length) continue;
158
+ const nx = sign * (b.y - a.y) / length, nz = sign * (b.x - a.x) / length;
159
+ addTriangle(a, bottom, b, bottom, b, top, nx, 0, nz, baseColor);
160
+ addTriangle(a, bottom, b, top, a, top, nx, 0, nz, baseColor);
161
+ }
162
+ });
163
+ }
164
+ const vertexEnd = positions.length / 3;
165
+ featureFaces.push({
166
+ id: feature.id ?? feature.properties?.objectid ?? JSON.stringify(geometry),
167
+ properties: feature.properties, baseColor,
168
+ faceStart: vertexStart / 3, faceEnd: vertexEnd / 3, vertexStart, vertexEnd,
169
+ });
170
+ }
171
+ if (!positions.length) {
172
+ Object.assign(area, { mesh: null, buildings: 0, skipped, done: true });
173
+ return;
174
+ }
175
+ const device = backend.renderer.device;
176
+ const attributes = {
177
+ position: { size: 3, data: new Float32Array(positions) },
178
+ normal: { size: 3, data: new Float32Array(normals) },
179
+ color: { size: 3, data: new Float32Array(colors) },
180
+ };
181
+ if (backend.options.nightLight) attributes.uv = { size: 2, data: new Float32Array(uvs) };
182
+ for (const [name, attribute] of Object.entries(attributes)) {
183
+ attribute.count = attribute.data.length / attribute.size;
184
+ attribute.buffer = device.createBuffer({
185
+ size: Math.max(4, attribute.data.byteLength),
186
+ usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
187
+ });
188
+ device.queue.writeBuffer(attribute.buffer, 0, attribute.data);
189
+ }
190
+ const meshGeometry = {
191
+ device,
192
+ attributes,
193
+ drawRange: { start: 0, count: attributes.position.count },
194
+ };
195
+ Object.assign(area, {
196
+ mesh: {
197
+ ...emptyNode(),
198
+ device,
199
+ geometry: meshGeometry,
200
+ program: createProgram(backend, {
201
+ vertexColors: true, roofTexture: Boolean(backend.options.roofTexture),
202
+ ...(backend.options.nightLight ? { nightLight: true, alphaMode: 'BLEND', doubleSided: true } : {})
203
+ }),
204
+ id: L.Layer._nextId++,
205
+ renderOrder: 0,
206
+ fadeDuration: 250,
207
+ extras: { featureFaces, lights },
208
+ },
209
+ buildings: featureFaces.length,
210
+ skipped,
211
+ done: true
212
+ });
213
+ setParent(area.mesh, backend.root);
214
+ update(backend);
215
+ } catch (error) {
216
+ if (area.request.signal.aborted) return;
217
+ area.error = error.message;
218
+ backend.fire('error', { error });
219
+ }
220
+ })().finally(() => {
221
+ backend._inflight--;
222
+ pump(backend, map);
223
+ });
224
+ }
225
+ for (const layer of layers) updateStatus(layer);
226
+ }
@@ -0,0 +1,285 @@
1
+ import { addRasterTile } from './addRasterTile.js';
2
+ import { createProgram } from './createProgram.js';
3
+ import { destroyTexture } from './destroyTexture.js';
4
+ import { removeRasterTile } from './removeRasterTile.js';
5
+ import { setParent } from './setParent.js';
6
+ import { update } from './update.js';
7
+
8
+ const TILE_SIZE = 256;
9
+ // ponytail: cap horizon views at 169 tiles; raise the radius if extreme pitches need more.
10
+ const TILE_RADIUS = 6;
11
+
12
+ export function updateRasterTiles(backend) {
13
+ const map = backend._map;
14
+ if (!map || !backend._shared || !backend.header) return;
15
+
16
+ const zoom = Math.max(backend.header.minZoom ?? 0, Math.min(
17
+ Math.round(map.getZoom()),
18
+ backend.options.maxZoom,
19
+ backend.options.maxNativeZoom
20
+ ));
21
+ backend._tileZoom = zoom;
22
+ const bounds = map.getBounds();
23
+ const northWest = map.project(bounds.getNorthWest(), zoom).divideBy(TILE_SIZE).floor();
24
+ const southEast = map.project(bounds.getSouthEast(), zoom).divideBy(TILE_SIZE).floor();
25
+ const archiveNorthWest = backend.header.maxLat === undefined || backend.header.minLon === undefined
26
+ ? new L.Point(0, 0)
27
+ : map.project([backend.header.maxLat, backend.header.minLon], zoom).divideBy(TILE_SIZE).floor();
28
+ const archiveSouthEast = backend.header.minLat === undefined || backend.header.maxLon === undefined
29
+ ? new L.Point(2 ** zoom - 1, 2 ** zoom - 1)
30
+ : map.project([backend.header.minLat, backend.header.maxLon], zoom).divideBy(TILE_SIZE).floor();
31
+ const center = map.project(map.getCenter(), zoom).divideBy(TILE_SIZE).floor();
32
+ const wanted = new Map();
33
+ for (let x = Math.max(0, northWest.x, archiveNorthWest.x, center.x - TILE_RADIUS);
34
+ x <= Math.min(2 ** zoom - 1, southEast.x, archiveSouthEast.x, center.x + TILE_RADIUS); x++) {
35
+ for (let y = Math.max(0, northWest.y, archiveNorthWest.y, center.y - TILE_RADIUS);
36
+ y <= Math.min(2 ** zoom - 1, southEast.y, archiveSouthEast.y, center.y + TILE_RADIUS); y++) {
37
+ wanted.set(`${zoom}/${x}/${y}`, { z: zoom, x, y });
38
+ }
39
+ }
40
+ backend._wantedRasterTiles = new Set(wanted.keys());
41
+ const coverage = new Map();
42
+ const parentZoom = Math.max(backend.header.minZoom ?? 0, zoom - 2), factor = 2 ** (zoom - parentZoom);
43
+ if (parentZoom < zoom) {
44
+ for (const [key, coords] of wanted) {
45
+ if (backend._rasterTiles.get(key)?.done) continue;
46
+ const parent = { z: parentZoom, x: Math.floor(coords.x / factor), y: Math.floor(coords.y / factor) };
47
+ coverage.set(`${parent.z}/${parent.x}/${parent.y}`, parent);
48
+ }
49
+ }
50
+ const required = new Map([...coverage, ...wanted]);
51
+ for (const [key, tile] of backend._rasterTiles) {
52
+ if (!required.has(key) && !tile.done) releaseRasterTile(backend, tile);
53
+ }
54
+ for (const [key, coords] of required) {
55
+ if (backend._rasterTiles.has(key)) continue;
56
+ const tile = { coords, request: new AbortController(), done: false };
57
+ backend._rasterTiles.set(key, tile);
58
+ }
59
+ pumpRasterTiles(backend);
60
+ pruneRasterTiles(backend);
61
+ }
62
+
63
+ function pumpRasterTiles(backend) {
64
+ const map = backend._map;
65
+ if (!map) return;
66
+ const center = map.project(map.getCenter(), 0).divideBy(TILE_SIZE);
67
+ const pending = [...backend._rasterTiles.values()].filter(tile => !tile.started && !tile.request.signal.aborted);
68
+ const distance = ({ coords }) => Math.hypot((coords.x + 0.5) / 2 ** coords.z - center.x,
69
+ (coords.y + 0.5) / 2 ** coords.z - center.y);
70
+ pending.sort((a, b) => a.coords.z - b.coords.z || distance(a) - distance(b));
71
+ for (const tile of pending) {
72
+ if (backend._loadingTiles >= 4) break;
73
+ loadRasterTile(backend, tile);
74
+ }
75
+ }
76
+
77
+ export function releaseRasterTiles(backend) {
78
+ for (const tile of backend._rasterTiles.values()) releaseRasterTile(backend, tile);
79
+ backend._rasterProgram?.pipelineCache.clear();
80
+ backend._rasterProgram = null;
81
+ if (backend._rasterGeometry) {
82
+ for (const attribute of Object.values(backend._rasterGeometry.attributes)) {
83
+ destroyTexture(backend, attribute.buffer);
84
+ }
85
+ backend._rasterGeometry = null;
86
+ }
87
+ }
88
+
89
+ export function setRasterTerrain(backend, terrain) {
90
+ if (backend._terrain3D === terrain) return;
91
+ for (const tile of backend._rasterTiles.values()) {
92
+ if(backend._terrain3D) removeRasterTile(backend._terrain3D, tile);
93
+ if (tile.mesh) {
94
+ tile.mesh.visible = true;
95
+ setParent(tile.mesh, backend.root);
96
+ }
97
+ }
98
+ backend._terrain3D = terrain || null;
99
+ if (terrain) {
100
+ for (const tile of backend._rasterTiles.values()) {
101
+ if (tile.done) registerTerrainTile(backend, tile.coords, tile);
102
+ }
103
+ }
104
+ }
105
+
106
+ export function registerTerrainTile(backend, coords, tile) {
107
+ if (backend._terrain3D && tile.done && !tile.request.signal.aborted &&
108
+ backend._rasterTiles.get(`${coords.z}/${coords.x}/${coords.y}`) === tile) {
109
+ addRasterTile(backend._terrain3D, backend, coords, tile)
110
+ .catch(error => backend.fire('tileerror', { tile, coords, error }));
111
+ }
112
+ }
113
+
114
+ export function setRasterOpacity(backend, opacity) {
115
+ for (const tile of backend._rasterTiles.values()) {
116
+ if (tile.mesh) tile.mesh.opacity = opacity;
117
+ const terrainMesh = backend._terrain3D?._rasterTiles.get(tile);
118
+ if (terrainMesh) terrainMesh.opacity = opacity;
119
+ }
120
+ update(backend);
121
+ }
122
+
123
+ export function setRasterZIndex(backend, zIndex) {
124
+ for (const tile of backend._rasterTiles.values()) {
125
+ if (tile.mesh) tile.mesh.renderOrder = zIndex;
126
+ const terrainMesh = backend._terrain3D?._rasterTiles.get(tile);
127
+ if (terrainMesh) terrainMesh.renderOrder = zIndex;
128
+ }
129
+ update(backend);
130
+ }
131
+
132
+ export function getRasterTileSize() {
133
+ return new L.Point(TILE_SIZE, TILE_SIZE);
134
+ }
135
+
136
+ function loadRasterTile(backend, tile) {
137
+ tile.started = true;
138
+ const first = backend._loadingTiles === 0;
139
+ backend._loadingTiles++;
140
+ backend.fire('tileloadstart', { tile, coords: tile.coords });
141
+ if (first) backend.fire('loading');
142
+ return Promise.resolve(backend.ready).then(() => backend.options.archive.getZxy(
143
+ tile.coords.z, tile.coords.x, tile.coords.y, tile.request.signal
144
+ )).then(async result => {
145
+ if (tile.request.signal.aborted) return;
146
+ if (!result) throw new Error(`Tile ${tile.coords.z}/${tile.coords.x}/${tile.coords.y} is not in the offline archive`);
147
+ try {
148
+ tile.image = await createImageBitmap(new Blob([result.data]));
149
+ } catch {
150
+ throw new Error(`Unable to decode tile ${tile.coords.z}/${tile.coords.x}/${tile.coords.y}`);
151
+ }
152
+ if (tile.request.signal.aborted) {
153
+ tile.image.close();
154
+ tile.image = null;
155
+ return;
156
+ }
157
+ createRasterMesh(backend, tile);
158
+ tile.loadedAt = performance.now();
159
+ tile.done = true;
160
+ backend._rasterVersion = (backend._rasterVersion || 0) + 1;
161
+ registerTerrainTile(backend, tile.coords, tile);
162
+ backend.fire('tileload', { tile, coords: tile.coords });
163
+ update(backend);
164
+ }).catch(error => {
165
+ if (tile.request.signal.aborted) return;
166
+ tile.error = error.message;
167
+ backend._rasterVersion = (backend._rasterVersion || 0) + 1;
168
+ backend.fire('tileerror', { tile, coords: tile.coords, error });
169
+ backend.fire('error', { error, coords: tile.coords });
170
+ }).finally(() => {
171
+ backend._loadingTiles--;
172
+ if (backend._map) {
173
+ pumpRasterTiles(backend);
174
+ pruneRasterTiles(backend);
175
+ if (!backend._loadingTiles) backend.fire('load');
176
+ update(backend);
177
+ }
178
+ });
179
+ }
180
+
181
+ function createRasterMesh(backend, tile) {
182
+ const device = backend.renderer.device;
183
+ const texture = {
184
+ device,
185
+ texture: device.createTexture({
186
+ size: [tile.image.width, tile.image.height],
187
+ format: 'rgba8unorm',
188
+ usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST | GPUTextureUsage.RENDER_ATTACHMENT,
189
+ }),
190
+ sampler: device.createSampler({ addressModeU: 'clamp-to-edge', addressModeV: 'clamp-to-edge',
191
+ magFilter: 'linear', minFilter: 'linear' }),
192
+ image: tile.image, format: 'rgba8unorm', srgb: false, uploaded: null,
193
+ };
194
+ if (!backend._rasterProgram) {
195
+ backend._rasterProgram = createProgram(backend, {
196
+ baseTexture: texture, unlit: true, alphaMode: 'BLEND',
197
+ });
198
+ }
199
+ if (!backend._rasterGeometry) backend._rasterGeometry = createGeometry(device);
200
+ const { coords } = tile;
201
+ const crs = backend._map.options.crs;
202
+ const origin = crs.project(backend.location);
203
+ const metres = Math.cos(backend.location.lat * Math.PI / 180);
204
+ const scale = crs.scale(coords.z);
205
+ const corner = crs.transformation.untransform(new L.Point(coords.x * TILE_SIZE, coords.y * TILE_SIZE), scale);
206
+ const opposite = crs.transformation.untransform(new L.Point((coords.x + 1) * TILE_SIZE, (coords.y + 1) * TILE_SIZE), scale);
207
+ const sizeX = (opposite.x - corner.x) * metres;
208
+ const sizeZ = -(opposite.y - corner.y) * metres;
209
+ tile.texture = texture;
210
+ tile.mesh = {
211
+ position: [(corner.x - origin.x) * metres, 0, -(corner.y - origin.y) * metres],
212
+ rotation: [0, 0, 0],
213
+ scale: [sizeX, 1, sizeZ],
214
+ worldMatrix: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1],
215
+ children: [], visible: true, device, geometry: backend._rasterGeometry,
216
+ program: backend._rasterProgram, texture,
217
+ opacity: backend.options.opacity, id: L.Layer._nextId++,
218
+ renderOrder: backend.options.zIndex || 0, tile, fadeDuration: 250, ground: true,
219
+ };
220
+ setParent(tile.mesh, backend.root);
221
+ }
222
+
223
+ function createGeometry(device) {
224
+ const attributes = {
225
+ position: { size: 3, data: new Float32Array([0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1]) },
226
+ normal: { size: 3, data: new Float32Array([0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0]) },
227
+ uv: { size: 2, data: new Float32Array([0, 0, 1, 0, 0, 1, 1, 1]) },
228
+ index: { data: new Uint16Array([0, 2, 1, 1, 2, 3]) },
229
+ };
230
+ for (const [name, attribute] of Object.entries(attributes)) {
231
+ attribute.count = attribute.data.length / (attribute.size || 1);
232
+ attribute.indexFormat = 'uint16';
233
+ attribute.buffer = device.createBuffer({
234
+ size: attribute.data.byteLength,
235
+ usage: name === 'index' ? GPUBufferUsage.INDEX | GPUBufferUsage.COPY_DST
236
+ : GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
237
+ });
238
+ device.queue.writeBuffer(attribute.buffer, 0, attribute.data);
239
+ }
240
+ return { device, attributes, drawRange: { start: 0, count: attributes.index.count } };
241
+ }
242
+
243
+ function releaseRasterTile(backend, tile) {
244
+ const key = `${tile.coords.z}/${tile.coords.x}/${tile.coords.y}`;
245
+ if (backend._rasterTiles.get(key) !== tile) return;
246
+ backend._rasterTiles.delete(key);
247
+ backend._rasterVersion = (backend._rasterVersion || 0) + 1;
248
+ tile.request.abort();
249
+ if(backend._terrain3D) removeRasterTile(backend._terrain3D, tile);
250
+ if (tile.mesh) setParent(tile.mesh, null);
251
+ if (tile.texture) destroyTexture(backend, tile.texture.texture);
252
+ tile.image?.close();
253
+ backend.fire('tileunload', { tile, coords: tile.coords });
254
+ update(backend);
255
+ }
256
+
257
+ export function pruneRasterTiles(backend) {
258
+ const wanted = backend._wantedRasterTiles;
259
+ const now = performance.now();
260
+ if (backend._pruneVersion === backend._rasterVersion && backend._pruneWanted === wanted &&
261
+ now < backend._pruneAfter) return Number.isFinite(backend._pruneAfter);
262
+ backend._pruneVersion = backend._rasterVersion;
263
+ backend._pruneWanted = wanted;
264
+ backend._pruneAfter = Infinity;
265
+ const pending = [...wanted].filter(key => {
266
+ const tile = backend._rasterTiles.get(key);
267
+ if (tile?.done && now - tile.loadedAt < 250) {
268
+ backend._pruneAfter = Math.min(backend._pruneAfter, tile.loadedAt + 250);
269
+ }
270
+ return !tile?.done || now - tile.loadedAt < 250;
271
+ }).map(key => {
272
+ const [z, x, y] = key.split('/').map(Number);
273
+ return { z, x, y };
274
+ });
275
+ for (const [key, tile] of backend._rasterTiles) {
276
+ if (wanted.has(key)) continue;
277
+ const overlaps = pending.some(coords => {
278
+ const zoom = Math.min(coords.z, tile.coords.z);
279
+ return Math.floor(coords.x / 2 ** (coords.z - zoom)) === Math.floor(tile.coords.x / 2 ** (tile.coords.z - zoom)) &&
280
+ Math.floor(coords.y / 2 ** (coords.z - zoom)) === Math.floor(tile.coords.y / 2 ** (tile.coords.z - zoom));
281
+ });
282
+ if (!overlaps) releaseRasterTile(backend, tile);
283
+ }
284
+ return Number.isFinite(backend._pruneAfter);
285
+ }
@@ -0,0 +1,60 @@
1
+ import { intersectMeshes } from './intersectMeshes.js';
2
+ import { traverse } from './traverse.js';
3
+ import { updateCamera } from './updateCamera.js';
4
+ import { updateWorldMatrix } from './updateWorldMatrix.js';
5
+
6
+ /** Raycasting, hover/click highlight and tooltips. */
7
+ /**
8
+ * Finds the objects under a mouse or pointer event, nearest first.
9
+ *
10
+ * @param {MouseEvent} event
11
+ * @param {Object[]} [objects] Defaults to this layer's root children, not all map layers.
12
+ * @param {boolean} [recursive] Also test the descendants of `objects`.
13
+ * @returns {Object[]} Nearest hit per mesh: object, shared-world point/distance and triangle faceIndex.
14
+ */
15
+
16
+ export function raycast(backend, event, objects = backend.root.children, recursive = true) {
17
+ if (!backend.renderer) {
18
+ return [];
19
+ }
20
+ backend._beforeRaycast?.();
21
+ const rect = backend._map.getContainer().getBoundingClientRect();
22
+ updateWorldMatrix(backend.scene);
23
+ updateCamera(backend.camera);
24
+ // Unproject the mouse (-1..1) into a world-space ray from the camera.
25
+ const inverseCamera = new DOMMatrix(backend.camera.projectionViewMatrix).inverse();
26
+ const inverseCameraValues = inverseCamera.toFloat64Array();
27
+ if (![...inverseCameraValues].every(Number.isFinite)) {
28
+ throw new Error('Singular 3D transform');
29
+ }
30
+ backend.raycaster.origin[0] = backend.camera.position[0];
31
+ backend.raycaster.origin[1] = backend.camera.position[1];
32
+ backend.raycaster.origin[2] = backend.camera.position[2];
33
+ const unprojected = new DOMPoint(
34
+ ((event.clientX - rect.left) / rect.width) * 2 - 1,
35
+ -((event.clientY - rect.top) / rect.height) * 2 + 1,
36
+ 0,
37
+ 1
38
+ ).matrixTransform(inverseCamera);
39
+ const end = [
40
+ unprojected.x / unprojected.w,
41
+ unprojected.y / unprojected.w,
42
+ unprojected.z / unprojected.w
43
+ ];
44
+ backend.raycaster.direction[0] = end[0] - backend.raycaster.origin[0];
45
+ backend.raycaster.direction[1] = end[1] - backend.raycaster.origin[1];
46
+ backend.raycaster.direction[2] = end[2] - backend.raycaster.origin[2];
47
+ const rayLength = Math.hypot(...backend.raycaster.direction) || 1;
48
+ backend.raycaster.direction[0] /= rayLength;
49
+ backend.raycaster.direction[1] /= rayLength;
50
+ backend.raycaster.direction[2] /= rayLength;
51
+ const meshes = [];
52
+ for (const object of objects) {
53
+ if (recursive) traverse(object, mesh => {
54
+ if (!mesh.visible) return true;
55
+ if (mesh.geometry && mesh._appearance !== 0) meshes.push(mesh);
56
+ });
57
+ else if (object.geometry && object.visible && object._appearance !== 0) meshes.push(object);
58
+ }
59
+ return intersectMeshes(backend.raycaster, meshes, backend.renderer.uniformData?.[55]);
60
+ }
@@ -0,0 +1,29 @@
1
+ export async function read(backend, offset, length, signal) {
2
+ signal?.throwIfAborted();
3
+ if (!Number.isSafeInteger(offset) || !Number.isSafeInteger(length) ||
4
+ offset < 0 || length <= 0 || !Number.isSafeInteger(offset + length)) {
5
+ throw new Error('Invalid PMTiles byte range');
6
+ }
7
+ const response = await fetch(backend.url, {
8
+ headers: { Range: `bytes=${offset}-${offset + length - 1}` }, signal,
9
+ });
10
+ if (!response.ok) {
11
+ throw new Error(`Unable to read PMTiles: HTTP ${response.status}`);
12
+ }
13
+ const etag = response.headers.get('ETag');
14
+ if (backend._etag && etag !== backend._etag) {
15
+ throw new Error('PMTiles archive changed; recreate the layer');
16
+ }
17
+ if (etag) backend._etag = etag;
18
+ let data = await response.arrayBuffer();
19
+ if (response.status === 200) {
20
+ data = data.slice(offset, offset + length);
21
+ } else if (response.status !== 206 ||
22
+ !response.headers.get('Content-Range')?.startsWith(`bytes ${offset}-${offset + length - 1}/`)) {
23
+ throw new Error('Invalid PMTiles range response');
24
+ }
25
+ if (data.byteLength !== length) {
26
+ throw new Error('Truncated PMTiles range');
27
+ }
28
+ return data;
29
+ }
@@ -0,0 +1,68 @@
1
+ import { types } from "./types.js";
2
+ import { sizes } from "./sizes.js";
3
+
4
+ /** Decode packed, interleaved, normalized or sparse glTF accessor data. */
5
+
6
+ export function readAccessor(description, index) {
7
+ const spec = description.accessors[index], Type = types[spec.componentType], size = sizes[spec.type];
8
+ if (!Type || !size || !Number.isInteger(spec.count) || spec.count < 0) {
9
+ throw new Error('Invalid glTF accessor');
10
+ }
11
+ const data = new Type(spec.count * size);
12
+ // The destination callback is sequential for base data and sparse-indexed for overrides.
13
+ const read = (viewIndex, offset, count, stride, output) => {
14
+ const view = description.bufferViews[viewIndex], bytes = Type.BYTES_PER_ELEMENT;
15
+ const buffer = description.buffers[view.buffer].binary;
16
+ stride ||= size * bytes;
17
+ if (!buffer || stride < size * bytes || offset < 0 ||
18
+ (view.byteOffset || 0) + view.byteLength > buffer.byteLength ||
19
+ (count - 1) * stride + size * bytes + offset > view.byteLength) throw new Error('glTF accessor outside buffer view');
20
+ for (let i = 0; i < count; i++) {
21
+ const values = new Type(buffer.slice((view.byteOffset || 0) + offset + i * stride,
22
+ (view.byteOffset || 0) + offset + i * stride + size * bytes));
23
+ data.set(values, output(i) * size);
24
+ }
25
+ };
26
+ if (spec.bufferView !== undefined) {
27
+ read(
28
+ spec.bufferView,
29
+ spec.byteOffset || 0,
30
+ spec.count,
31
+ description.bufferViews[spec.bufferView].byteStride,
32
+ i => i
33
+ );
34
+ }
35
+ if (spec.sparse) {
36
+ const sparse = spec.sparse, Index = types[sparse.indices.componentType];
37
+ if (![Uint8Array, Uint16Array, Uint32Array].includes(Index)) {
38
+ throw new Error('Invalid sparse glTF indices');
39
+ }
40
+ const view = description.bufferViews[sparse.indices.bufferView];
41
+ const offset = (view.byteOffset || 0) + (sparse.indices.byteOffset || 0);
42
+ if (sparse.count > spec.count || (sparse.indices.byteOffset || 0) + sparse.count * Index.BYTES_PER_ELEMENT > view.byteLength) {
43
+ throw new Error('Invalid sparse glTF indices');
44
+ }
45
+ const indices = new Index(description.buffers[view.buffer].binary.slice(offset, offset + sparse.count * Index.BYTES_PER_ELEMENT));
46
+ read(
47
+ sparse.values.bufferView,
48
+ sparse.values.byteOffset || 0,
49
+ sparse.count,
50
+ 0,
51
+ i => {
52
+ if (indices[i] >= spec.count) throw new Error('Invalid sparse glTF index');
53
+ return indices[i];
54
+ }
55
+ );
56
+ }
57
+ if (spec.componentType === 5126) {
58
+ return { data, size };
59
+ }
60
+ const normalized = new Float32Array(data.length);
61
+ // Normalization is a glTF accessor rule, not sRGB conversion; indices stay integers.
62
+ for (let i = 0; i < data.length; i++) {
63
+ normalized[i] = spec.normalized
64
+ ? Math.max(-1, data[i] / (Type === Int8Array ? 127 : Type === Int16Array ? 32767 : Type === Uint8Array ? 255 : Type === Uint32Array ? 4294967295 : 65535))
65
+ : data[i];
66
+ }
67
+ return { data: normalized, size, indices: data };
68
+ }