leaflet-city-mapper 0.0.1 → 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.
- package/README.md +15 -13
- package/cucumber.mjs +4 -0
- package/dist/leaflet-city-mapper.js +1 -1
- package/dist/leaflet-city-mapper.js.map +4 -4
- package/dist/utils/CELL.js +2 -0
- package/dist/utils/CELL.js.map +7 -0
- package/dist/utils/addBridges.js +2 -0
- package/dist/utils/addBridges.js.map +7 -0
- package/dist/utils/addRasterTile.js +2 -0
- package/dist/utils/addRasterTile.js.map +7 -0
- package/dist/utils/along.js +2 -0
- package/dist/utils/along.js.map +7 -0
- package/dist/utils/atmosphere.js +2 -0
- package/dist/utils/atmosphere.js.map +7 -0
- package/dist/utils/attach.js +2 -0
- package/dist/utils/attach.js.map +7 -0
- package/dist/utils/bindSunlight.js +2 -0
- package/dist/utils/bindSunlight.js.map +7 -0
- package/dist/utils/box.js +2 -0
- package/dist/utils/box.js.map +7 -0
- package/dist/utils/bridgeElevation.js +2 -0
- package/dist/utils/bridgeElevation.js.map +7 -0
- package/dist/utils/build.js +2 -0
- package/dist/utils/build.js.map +7 -0
- package/dist/utils/cameraControls.js +2 -0
- package/dist/utils/cameraControls.js.map +7 -0
- package/dist/utils/channel.js +2 -0
- package/dist/utils/channel.js.map +7 -0
- package/dist/utils/collider.js +2 -0
- package/dist/utils/collider.js.map +7 -0
- package/dist/utils/colliders.js +2 -0
- package/dist/utils/colliders.js.map +7 -0
- package/dist/utils/computeBounds.js +2 -0
- package/dist/utils/computeBounds.js.map +7 -0
- package/dist/utils/conformModelToTerrain.js +2 -0
- package/dist/utils/conformModelToTerrain.js.map +7 -0
- package/dist/utils/createMesh.js +2 -0
- package/dist/utils/createMesh.js.map +7 -0
- package/dist/utils/createProgram.js +3 -0
- package/dist/utils/createProgram.js.map +7 -0
- package/dist/utils/decodeDirectory.js +2 -0
- package/dist/utils/decodeDirectory.js.map +7 -0
- package/dist/utils/decodeHeader.js +2 -0
- package/dist/utils/decodeHeader.js.map +7 -0
- package/dist/utils/decodeModel.js +2 -0
- package/dist/utils/decodeModel.js.map +7 -0
- package/dist/utils/decompress.js +2 -0
- package/dist/utils/decompress.js.map +7 -0
- package/dist/utils/destroyTexture.js +2 -0
- package/dist/utils/destroyTexture.js.map +7 -0
- package/dist/utils/disposeMesh.js +2 -0
- package/dist/utils/disposeMesh.js.map +7 -0
- package/dist/utils/drawGeometry.js +2 -0
- package/dist/utils/drawGeometry.js.map +7 -0
- package/dist/utils/elevationWGSL.js +24 -0
- package/dist/utils/elevationWGSL.js.map +7 -0
- package/dist/utils/emptyNode.js +2 -0
- package/dist/utils/emptyNode.js.map +7 -0
- package/dist/utils/endZoom.js +2 -0
- package/dist/utils/endZoom.js.map +7 -0
- package/dist/utils/findFeature.js +2 -0
- package/dist/utils/findFeature.js.map +7 -0
- package/dist/utils/geometry.js +0 -1
- package/dist/utils/geometry.js.map +4 -4
- package/dist/utils/getElevation.js +2 -0
- package/dist/utils/getElevation.js.map +7 -0
- package/dist/utils/getHeader.js +2 -0
- package/dist/utils/getHeader.js.map +7 -0
- package/dist/utils/getZxy.js +2 -0
- package/dist/utils/getZxy.js.map +7 -0
- package/dist/utils/glass.js +2 -0
- package/dist/utils/glass.js.map +7 -0
- package/dist/utils/headlight.js +2 -0
- package/dist/utils/headlight.js.map +7 -0
- package/dist/utils/hit.js +2 -0
- package/dist/utils/hit.js.map +7 -0
- package/dist/utils/horizontalPosition.js +2 -0
- package/dist/utils/horizontalPosition.js.map +7 -0
- package/dist/utils/horizontalRay.js +2 -0
- package/dist/utils/horizontalRay.js.map +7 -0
- package/dist/utils/hull.js +2 -0
- package/dist/utils/hull.js.map +7 -0
- package/dist/utils/initSunlight.js +2 -0
- package/dist/utils/initSunlight.js.map +7 -0
- package/dist/utils/initTerrain.js +2 -0
- package/dist/utils/initTerrain.js.map +7 -0
- package/dist/utils/initTraffic.js +40 -0
- package/dist/utils/initTraffic.js.map +7 -0
- package/dist/utils/initWalk.js +2 -0
- package/dist/utils/initWalk.js.map +7 -0
- package/dist/utils/intersectMeshes.js +2 -0
- package/dist/utils/intersectMeshes.js.map +7 -0
- package/dist/utils/isClockWise.js +2 -0
- package/dist/utils/isClockWise.js.map +7 -0
- package/dist/utils/lampPostGeometry.js +2 -0
- package/dist/utils/lampPostGeometry.js.map +7 -0
- package/dist/utils/lightGeometry.js +2 -0
- package/dist/utils/lightGeometry.js.map +7 -0
- package/dist/utils/lightGrid.js +2 -0
- package/dist/utils/lightGrid.js.map +7 -0
- package/dist/utils/limbs.js +2 -0
- package/dist/utils/limbs.js.map +7 -0
- package/dist/utils/loadModel.js +2 -0
- package/dist/utils/loadModel.js.map +7 -0
- package/dist/utils/loadSource.js +2 -0
- package/dist/utils/loadSource.js.map +7 -0
- package/dist/utils/loadTerrain.js +2 -0
- package/dist/utils/loadTerrain.js.map +7 -0
- package/dist/utils/mapCamera.js +2 -0
- package/dist/utils/mapCamera.js.map +7 -0
- package/dist/utils/{shaders.js → materialShaders.js} +16 -8
- package/dist/utils/materialShaders.js.map +7 -0
- package/dist/utils/mirror.js +2 -0
- package/dist/utils/mirror.js.map +7 -0
- package/dist/utils/moonPosition.js +2 -0
- package/dist/utils/moonPosition.js.map +7 -0
- package/dist/utils/moves.js +2 -0
- package/dist/utils/moves.js.map +7 -0
- package/dist/utils/nativeEvents.js +2 -0
- package/dist/utils/nativeEvents.js.map +7 -0
- package/dist/utils/nativeGeometry.js +2 -0
- package/dist/utils/nativeGeometry.js.map +7 -0
- package/dist/utils/nativeLayer.js +2 -0
- package/dist/utils/nativeLayer.js.map +7 -0
- package/dist/utils/nodeMatrix.js +2 -0
- package/dist/utils/nodeMatrix.js.map +7 -0
- package/dist/utils/onAdd3DLayer.js +2 -0
- package/dist/utils/onAdd3DLayer.js.map +7 -0
- package/dist/utils/onAddSun.js +2 -0
- package/dist/utils/onAddSun.js.map +7 -0
- package/dist/utils/onClick.js +2 -0
- package/dist/utils/onClick.js.map +7 -0
- package/dist/utils/onMapChange.js +2 -0
- package/dist/utils/onMapChange.js.map +7 -0
- package/dist/utils/onPMTileAdd.js +2 -0
- package/dist/utils/onPMTileAdd.js.map +7 -0
- package/dist/utils/onPMTileRemove.js +2 -0
- package/dist/utils/onPMTileRemove.js.map +7 -0
- package/dist/utils/onPointerLeave.js +2 -0
- package/dist/utils/onPointerLeave.js.map +7 -0
- package/dist/utils/onPointerMove.js +2 -0
- package/dist/utils/onPointerMove.js.map +7 -0
- package/dist/utils/onRemove3DLayer.js +2 -0
- package/dist/utils/onRemove3DLayer.js.map +7 -0
- package/dist/utils/onRemoveSun.js +2 -0
- package/dist/utils/onRemoveSun.js.map +7 -0
- package/dist/utils/onTerrainAdd.js +2 -0
- package/dist/utils/onTerrainAdd.js.map +7 -0
- package/dist/utils/onTerrainChange.js +2 -0
- package/dist/utils/onTerrainChange.js.map +7 -0
- package/dist/utils/onTerrainRemove.js +2 -0
- package/dist/utils/onTerrainRemove.js.map +7 -0
- package/dist/utils/onZoomAnim.js +2 -0
- package/dist/utils/onZoomAnim.js.map +7 -0
- package/dist/utils/paints.js +2 -0
- package/dist/utils/paints.js.map +7 -0
- package/dist/utils/parseColor.js +2 -0
- package/dist/utils/parseColor.js.map +7 -0
- package/dist/utils/parts.js +2 -0
- package/dist/utils/parts.js.map +7 -0
- package/dist/utils/projectCelestialBody.js +2 -0
- package/dist/utils/projectCelestialBody.js.map +7 -0
- package/dist/utils/pump.js +2 -0
- package/dist/utils/pump.js.map +7 -0
- package/dist/utils/rasterTiles.js +2 -0
- package/dist/utils/rasterTiles.js.map +7 -0
- package/dist/utils/raycast.js +2 -0
- package/dist/utils/raycast.js.map +7 -0
- package/dist/utils/read.js +2 -0
- package/dist/utils/read.js.map +7 -0
- package/dist/utils/readAccessor.js +2 -0
- package/dist/utils/readAccessor.js.map +7 -0
- package/dist/utils/releaseArea.js +2 -0
- package/dist/utils/releaseArea.js.map +7 -0
- package/dist/utils/removeRasterTile.js +2 -0
- package/dist/utils/removeRasterTile.js.map +7 -0
- package/dist/utils/render.js +15 -0
- package/dist/utils/render.js.map +7 -0
- package/dist/utils/renderSunlight.js +13 -0
- package/dist/utils/renderSunlight.js.map +7 -0
- package/dist/utils/renderers.js +2 -0
- package/dist/utils/renderers.js.map +7 -0
- package/dist/utils/resizeSunlight.js +2 -0
- package/dist/utils/resizeSunlight.js.map +7 -0
- package/dist/utils/roofCoverage.js +2 -0
- package/dist/utils/roofCoverage.js.map +7 -0
- package/dist/utils/routeGeometry.js +2 -0
- package/dist/utils/routeGeometry.js.map +7 -0
- package/dist/utils/scheduledTrips.js +2 -0
- package/dist/utils/scheduledTrips.js.map +7 -0
- package/dist/utils/setGeoJSONData.js +2 -0
- package/dist/utils/setGeoJSONData.js.map +7 -0
- package/dist/utils/setHighlight.js +2 -0
- package/dist/utils/setHighlight.js.map +7 -0
- package/dist/utils/setPMTileOpacity.js +2 -0
- package/dist/utils/setPMTileOpacity.js.map +7 -0
- package/dist/utils/setPMTileZIndex.js +2 -0
- package/dist/utils/setPMTileZIndex.js.map +7 -0
- package/dist/utils/setParent.js +2 -0
- package/dist/utils/setParent.js.map +7 -0
- package/dist/utils/setTerrainHeight.js +2 -0
- package/dist/utils/setTerrainHeight.js.map +7 -0
- package/dist/utils/setVertexColor.js +2 -0
- package/dist/utils/setVertexColor.js.map +7 -0
- package/dist/utils/showFeature.js +2 -0
- package/dist/utils/showFeature.js.map +7 -0
- package/dist/utils/sizes.js +2 -0
- package/dist/utils/sizes.js.map +7 -0
- package/dist/utils/skin.js +2 -0
- package/dist/utils/skin.js.map +7 -0
- package/dist/utils/solarPosition.js +2 -0
- package/dist/utils/solarPosition.js.map +7 -0
- package/dist/utils/sortMeshes.js +2 -0
- package/dist/utils/sortMeshes.js.map +7 -0
- package/dist/utils/speed.js +2 -0
- package/dist/utils/speed.js.map +7 -0
- package/dist/utils/startZoom.js +2 -0
- package/dist/utils/startZoom.js.map +7 -0
- package/dist/utils/taillight.js +2 -0
- package/dist/utils/taillight.js.map +7 -0
- package/dist/utils/terrainAtlas.js +2 -0
- package/dist/utils/terrainAtlas.js.map +7 -0
- package/dist/utils/terrainElevation.js +2 -0
- package/dist/utils/terrainElevation.js.map +7 -0
- package/dist/utils/terrainGrid.js +2 -0
- package/dist/utils/terrainGrid.js.map +7 -0
- package/dist/utils/textureSampler.js +2 -0
- package/dist/utils/textureSampler.js.map +7 -0
- package/dist/utils/tileId.js +2 -0
- package/dist/utils/tileId.js.map +7 -0
- package/dist/utils/toLanes.js +2 -0
- package/dist/utils/toLanes.js.map +7 -0
- package/dist/utils/toSRGB.js +2 -0
- package/dist/utils/toSRGB.js.map +7 -0
- package/dist/utils/tooltip.js +3 -0
- package/dist/utils/tooltip.js.map +7 -0
- package/dist/utils/traffic.js +1 -1
- package/dist/utils/traffic.js.map +3 -3
- package/dist/utils/traverse.js +2 -0
- package/dist/utils/traverse.js.map +7 -0
- package/dist/utils/treeGeometry.js +2 -0
- package/dist/utils/treeGeometry.js.map +7 -0
- package/dist/utils/triangulate.js +2 -0
- package/dist/utils/triangulate.js.map +7 -0
- package/dist/utils/tripsAt.js +2 -0
- package/dist/utils/tripsAt.js.map +7 -0
- package/dist/utils/types.js +2 -0
- package/dist/utils/types.js.map +7 -0
- package/dist/utils/underEntry.js +2 -0
- package/dist/utils/underEntry.js.map +7 -0
- package/dist/utils/update.js +2 -0
- package/dist/utils/update.js.map +7 -0
- package/dist/utils/updateArea.js +2 -0
- package/dist/utils/updateArea.js.map +7 -0
- package/dist/utils/updateCamera.js +2 -0
- package/dist/utils/updateCamera.js.map +7 -0
- package/dist/utils/updatePointLights.js +2 -0
- package/dist/utils/updatePointLights.js.map +7 -0
- package/dist/utils/updateWorldMatrix.js +2 -0
- package/dist/utils/updateWorldMatrix.js.map +7 -0
- package/dist/utils/vehicleOffsets.js +2 -0
- package/dist/utils/vehicleOffsets.js.map +7 -0
- package/dist/utils/vehicles.js +1 -1
- package/dist/utils/vehicles.js.map +3 -3
- package/dist/utils/vertexNormals.js +2 -0
- package/dist/utils/vertexNormals.js.map +7 -0
- package/dist/utils/walk.js +1 -1
- package/dist/utils/walk.js.map +3 -3
- package/dist/utils/white.js +2 -0
- package/dist/utils/white.js.map +7 -0
- package/dist/utils/worldLights.js +2 -0
- package/dist/utils/worldLights.js.map +7 -0
- package/dist/utils/wrap.js +2 -0
- package/dist/utils/wrap.js.map +7 -0
- package/package.json +2 -4
- package/src/index.js +365 -50
- package/src/utils/CELL.js +4 -0
- package/src/utils/addBridges.js +66 -0
- package/src/utils/addRasterTile.js +112 -0
- package/src/utils/along.js +9 -0
- package/src/utils/atmosphere.js +20 -0
- package/src/utils/attach.js +255 -0
- package/src/utils/bindSunlight.js +12 -0
- package/src/utils/box.js +1 -0
- package/src/utils/bridgeElevation.js +18 -0
- package/src/utils/build.js +20 -0
- package/src/utils/cameraControls.js +287 -0
- package/src/utils/channel.js +4 -0
- package/src/utils/collider.js +62 -0
- package/src/utils/colliders.js +2 -0
- package/src/utils/computeBounds.js +17 -0
- package/src/utils/conformModelToTerrain.js +69 -0
- package/src/utils/createMesh.js +31 -0
- package/src/utils/createProgram.js +92 -0
- package/src/utils/decodeDirectory.js +40 -0
- package/src/utils/decodeHeader.js +15 -0
- package/src/utils/decodeModel.js +43 -0
- package/src/utils/decompress.js +5 -0
- package/src/utils/destroyTexture.js +7 -0
- package/src/utils/disposeMesh.js +13 -0
- package/src/utils/drawGeometry.js +122 -0
- package/src/utils/elevationWGSL.js +27 -0
- package/src/utils/emptyNode.js +9 -0
- package/src/utils/endZoom.js +15 -0
- package/src/utils/findFeature.js +49 -0
- package/src/utils/geometry.js +0 -254
- package/src/utils/getElevation.js +10 -0
- package/src/utils/getHeader.js +9 -0
- package/src/utils/getZxy.js +76 -0
- package/src/utils/glass.js +1 -0
- package/src/utils/headlight.js +1 -0
- package/src/utils/hit.js +41 -0
- package/src/utils/horizontalPosition.js +16 -0
- package/src/utils/horizontalRay.js +12 -0
- package/src/utils/hull.js +1 -0
- package/src/utils/initSunlight.js +9 -0
- package/src/utils/initTerrain.js +19 -0
- package/src/utils/initTraffic.js +93 -0
- package/src/utils/initWalk.js +72 -0
- package/src/utils/{picking.js → intersectMeshes.js} +121 -119
- package/src/utils/isClockWise.js +12 -0
- package/src/utils/lampPostGeometry.js +26 -0
- package/src/utils/lightGeometry.js +22 -0
- package/src/utils/{lighting.js → lightGrid.js} +50 -51
- package/src/utils/limbs.js +9 -0
- package/src/{scene/model.js → utils/loadModel.js} +37 -24
- package/src/utils/loadSource.js +17 -0
- package/src/utils/loadTerrain.js +65 -0
- package/src/utils/mapCamera.js +432 -0
- package/src/utils/{shaders.js → materialShaders.js} +243 -237
- package/src/utils/mirror.js +6 -0
- package/src/utils/moonPosition.js +17 -0
- package/src/utils/moves.js +11 -0
- package/src/utils/nativeEvents.js +77 -0
- package/src/utils/nativeGeometry.js +131 -0
- package/src/utils/nativeLayer.js +317 -0
- package/src/utils/nodeMatrix.js +22 -0
- package/src/utils/onAdd3DLayer.js +31 -0
- package/src/utils/onAddSun.js +147 -0
- package/src/utils/onClick.js +13 -0
- package/src/utils/onMapChange.js +16 -0
- package/src/utils/onPMTileAdd.js +15 -0
- package/src/utils/onPMTileRemove.js +8 -0
- package/src/utils/onPointerLeave.js +11 -0
- package/src/utils/onPointerMove.js +15 -0
- package/src/utils/onRemove3DLayer.js +78 -0
- package/src/utils/onRemoveSun.js +3 -0
- package/src/utils/onTerrainAdd.js +18 -0
- package/src/utils/onTerrainChange.js +13 -0
- package/src/utils/onTerrainRemove.js +40 -0
- package/src/utils/onZoomAnim.js +14 -0
- package/src/utils/paints.js +10 -0
- package/src/utils/parseColor.js +42 -0
- package/src/utils/parts.js +16 -0
- package/src/utils/projectCelestialBody.js +13 -0
- package/src/{scene/sources.js → utils/pump.js} +59 -98
- package/src/utils/rasterTiles.js +285 -0
- package/src/utils/raycast.js +60 -0
- package/src/utils/read.js +29 -0
- package/src/utils/readAccessor.js +68 -0
- package/src/utils/releaseArea.js +34 -0
- package/src/utils/removeRasterTile.js +13 -0
- package/src/{scene → utils}/render.js +112 -173
- package/src/utils/renderSunlight.js +170 -0
- package/src/utils/renderers.js +2 -0
- package/src/utils/resizeSunlight.js +15 -0
- package/src/utils/roofCoverage.js +47 -0
- package/src/utils/routeGeometry.js +22 -0
- package/src/utils/scheduledTrips.js +29 -0
- package/src/utils/setGeoJSONData.js +11 -0
- package/src/utils/setHighlight.js +13 -0
- package/src/utils/setPMTileOpacity.js +7 -0
- package/src/utils/setPMTileZIndex.js +7 -0
- package/src/utils/setParent.js +7 -0
- package/src/utils/setTerrainHeight.js +10 -0
- package/src/utils/setVertexColor.js +14 -0
- package/src/utils/showFeature.js +27 -0
- package/src/utils/sizes.js +1 -0
- package/src/utils/skin.js +2 -0
- package/src/utils/solarPosition.js +17 -0
- package/src/utils/sortMeshes.js +12 -0
- package/src/utils/speed.js +3 -0
- package/src/utils/startZoom.js +5 -0
- package/src/utils/taillight.js +1 -0
- package/src/utils/terrainAtlas.js +21 -0
- package/src/utils/terrainElevation.js +20 -0
- package/src/utils/terrainGrid.js +23 -0
- package/src/utils/textureSampler.js +10 -0
- package/src/utils/tileId.js +18 -0
- package/src/utils/toLanes.js +7 -0
- package/src/utils/toSRGB.js +1 -0
- package/src/utils/tooltip.js +6 -0
- package/src/utils/traffic.js +310 -85
- package/src/utils/traverse.js +6 -0
- package/src/utils/treeGeometry.js +45 -0
- package/src/utils/triangulate.js +100 -0
- package/src/utils/tripsAt.js +16 -0
- package/src/utils/types.js +8 -0
- package/src/utils/{control.js → underEntry.js} +1 -2
- package/src/utils/update.js +18 -0
- package/src/utils/updateArea.js +40 -0
- package/src/utils/updateCamera.js +32 -0
- package/src/utils/updatePointLights.js +38 -0
- package/src/utils/updateWorldMatrix.js +47 -0
- package/src/utils/vehicleOffsets.js +8 -0
- package/src/utils/vehicles.js +65 -69
- package/src/utils/vertexNormals.js +30 -0
- package/src/utils/walk.js +181 -66
- package/src/utils/white.js +1 -0
- package/src/utils/worldLights.js +25 -0
- package/src/utils/wrap.js +1 -0
- package/dist/scene/gpu.js +0 -3
- package/dist/scene/gpu.js.map +0 -7
- package/dist/scene/lighting.js +0 -9
- package/dist/scene/lighting.js.map +0 -7
- package/dist/scene/model.js +0 -2
- package/dist/scene/model.js.map +0 -7
- package/dist/scene/picking.js +0 -2
- package/dist/scene/picking.js.map +0 -7
- package/dist/scene/pmtiles.js +0 -2
- package/dist/scene/pmtiles.js.map +0 -7
- package/dist/scene/render.js +0 -15
- package/dist/scene/render.js.map +0 -7
- package/dist/scene/sources.js +0 -2
- package/dist/scene/sources.js.map +0 -7
- package/dist/types/3d.js +0 -2
- package/dist/types/3d.js.map +0 -7
- package/dist/types/geojson.js +0 -2
- package/dist/types/geojson.js.map +0 -7
- package/dist/types/pmtiles.js +0 -2
- package/dist/types/pmtiles.js.map +0 -7
- package/dist/types/sunlight.js +0 -2
- package/dist/types/sunlight.js.map +0 -7
- package/dist/types/terrain.js +0 -24
- package/dist/types/terrain.js.map +0 -7
- package/dist/types/traffic.js +0 -41
- package/dist/types/traffic.js.map +0 -7
- package/dist/types/walk.js +0 -2
- package/dist/types/walk.js.map +0 -7
- package/dist/utils/control.js +0 -2
- package/dist/utils/control.js.map +0 -7
- package/dist/utils/lighting.js +0 -2
- package/dist/utils/lighting.js.map +0 -7
- package/dist/utils/model.js +0 -2
- package/dist/utils/model.js.map +0 -7
- package/dist/utils/picking.js +0 -2
- package/dist/utils/picking.js.map +0 -7
- package/dist/utils/pmtiles.js +0 -2
- package/dist/utils/pmtiles.js.map +0 -7
- package/dist/utils/scene.js +0 -2
- package/dist/utils/scene.js.map +0 -7
- package/dist/utils/shaders.js.map +0 -7
- package/dist/utils/sun.js +0 -2
- package/dist/utils/sun.js.map +0 -7
- package/dist/utils/terrain.js +0 -2
- package/dist/utils/terrain.js.map +0 -7
- package/src/scene/gpu.js +0 -438
- package/src/scene/lighting.js +0 -193
- package/src/scene/picking.js +0 -150
- package/src/scene/pmtiles.js +0 -94
- package/src/types/3d.js +0 -337
- package/src/types/geojson.js +0 -42
- package/src/types/pmtiles.js +0 -143
- package/src/types/sunlight.js +0 -142
- package/src/types/terrain.js +0 -331
- package/src/types/traffic.js +0 -356
- package/src/types/walk.js +0 -219
- package/src/utils/model.js +0 -178
- package/src/utils/pmtiles.js +0 -79
- package/src/utils/scene.js +0 -102
- package/src/utils/sun.js +0 -71
- package/src/utils/terrain.js +0 -78
package/src/types/traffic.js
DELETED
|
@@ -1,356 +0,0 @@
|
|
|
1
|
-
import './3d.js';
|
|
2
|
-
import { computeBounds } from '../utils/geometry.js';
|
|
3
|
-
import { along, speed, toLanes, tripsAt, routeGeometry } from '../utils/traffic.js';
|
|
4
|
-
import { build, vehicles } from '../utils/vehicles.js';
|
|
5
|
-
import { underEntry } from '../utils/control.js';
|
|
6
|
-
|
|
7
|
-
const tooltip = ({ properties: p }) => {
|
|
8
|
-
const content = document.createElement('div');
|
|
9
|
-
content.style.whiteSpace = 'pre-line';
|
|
10
|
-
content.textContent = [p.name, p.departure && `Departure ${p.departure} ${p.timetable.timeZone}`, p.description].filter(Boolean).join('\n');
|
|
11
|
-
return content;
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
function installTraffic(layer, { url, model, lights, side, outline, velocity }) {
|
|
15
|
-
const L = globalThis.L;
|
|
16
|
-
// The 'sunlight' layer on the same map drives the simulation time; without one, vehicles run live.
|
|
17
|
-
const now = () => {
|
|
18
|
-
let control = { live: true };
|
|
19
|
-
layer._map?.eachLayer(other => { if (other.control?.time) control = other.control; });
|
|
20
|
-
return { live: control.live, time: control.live ? new Date() : control.time };
|
|
21
|
-
};
|
|
22
|
-
const status = L.DomUtil.create('small', 'traffic-status');
|
|
23
|
-
status.style.cssText = 'display:block;max-width:200px;margin:0 0 4px 20px;color:#555;';
|
|
24
|
-
const template = build(model(0)), stride = template.position.length, vertices = stride / 3;
|
|
25
|
-
let ready = false, frame, request, lanes = [], tripsKey, mesh, route, litAt = -Infinity;
|
|
26
|
-
// Vehicles move on the GPU: `trips` are rebased at most once per second, a compute pass places them every frame.
|
|
27
|
-
let trips = [], rebasedAt = 0, nextCheck = -Infinity, elapsed = 0, gpu = null, lanesDirty = true, stale = false;
|
|
28
|
-
const params = new ArrayBuffer(16), paramFloats = new Float32Array(params), paramInts = new Uint32Array(params);
|
|
29
|
-
|
|
30
|
-
const createMesh = (data, featureFaces, material, usage = 0) => {
|
|
31
|
-
const result = layer._createMesh(data, material, usage);
|
|
32
|
-
result.extras.featureFaces = featureFaces;
|
|
33
|
-
return result;
|
|
34
|
-
};
|
|
35
|
-
const dispose = object => {
|
|
36
|
-
if (!object) return;
|
|
37
|
-
if (layer.highlightedFeature?.mesh === object) {
|
|
38
|
-
layer._setHighlight(null);
|
|
39
|
-
layer.hoveredFeature = null;
|
|
40
|
-
layer._showFeature(null);
|
|
41
|
-
}
|
|
42
|
-
layer._disposeMesh(object);
|
|
43
|
-
};
|
|
44
|
-
const fail = error => {
|
|
45
|
-
status.textContent = `Error: ${error.message}`;
|
|
46
|
-
status.setAttribute('role', 'alert');
|
|
47
|
-
console.error('Traffic layer', error);
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
const ground = () => {
|
|
51
|
-
const terrain = layer._map._terrain3D?._terrainReady && layer._map._terrain3D, base = layer.root.position[1];
|
|
52
|
-
for (const lane of lanes) lane.points.forEach((point, i) => {
|
|
53
|
-
const elevation = terrain && (lane.elevation ?? terrain._getElevation(lane.latlngs[i]));
|
|
54
|
-
point[2] = Number.isFinite(elevation) && Number.isFinite(base) ? elevation - base : 0;
|
|
55
|
-
});
|
|
56
|
-
lanesDirty = true;
|
|
57
|
-
nextCheck = -Infinity;
|
|
58
|
-
};
|
|
59
|
-
const prepare = () => {
|
|
60
|
-
const crs = layer._map.options.crs, origin = crs.project(layer.location);
|
|
61
|
-
const metres = Math.cos(layer.location.lat * Math.PI / 180);
|
|
62
|
-
for (const lane of lanes) {
|
|
63
|
-
lane.points = lane.latlngs.map(([lat, lng]) => {
|
|
64
|
-
const point = crs.project(new L.LatLng(lat, lng));
|
|
65
|
-
return [(point.x - origin.x) * metres, -(point.y - origin.y) * metres, 0];
|
|
66
|
-
});
|
|
67
|
-
lane.lengths = [0];
|
|
68
|
-
for (let i = 1; i < lane.points.length; i++) {
|
|
69
|
-
lane.lengths.push(lane.lengths[i - 1] + Math.hypot(lane.points[i][0] - lane.points[i - 1][0], lane.points[i][1] - lane.points[i - 1][1]));
|
|
70
|
-
}
|
|
71
|
-
lane.length = lane.lengths.at(-1);
|
|
72
|
-
}
|
|
73
|
-
ground();
|
|
74
|
-
dispose(route); route = null;
|
|
75
|
-
tripsKey = null;
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
// Dashed lane outline, just above the lanes.
|
|
79
|
-
const drawRoute = () => {
|
|
80
|
-
|
|
81
|
-
route = createMesh(routeGeometry(lanes), [], { emissive: [0.03, 0.25, 0.3], alphaMode: 'BLEND' });
|
|
82
|
-
route.renderOrder = 1000;
|
|
83
|
-
};
|
|
84
|
-
// One batched mesh, `vertices` per vehicle; the compute pass writes its position/normal buffers (STORAGE).
|
|
85
|
-
const rebuild = () => {
|
|
86
|
-
dispose(mesh); mesh = null;
|
|
87
|
-
if (!trips.length) return;
|
|
88
|
-
const color = trips.flatMap((trip, i) => build(model(i)).color), count = color.length / 3;
|
|
89
|
-
mesh = createMesh({ position: color.map(() => 0), normal: color.map(() => 0), color },
|
|
90
|
-
trips.map((trip, i) => ({ id: trip.id, properties: trip.properties, faceStart: i * vertices / 3, faceEnd: (i + 1) * vertices / 3,
|
|
91
|
-
vertexStart: i * vertices, vertexEnd: (i + 1) * vertices })), {}, GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC);
|
|
92
|
-
mesh.geometry.drawRange.count = count;
|
|
93
|
-
bounds();
|
|
94
|
-
};
|
|
95
|
-
// Vehicles never leave their lanes: cull and pick against the lanes' extent.
|
|
96
|
-
const bounds = () => {
|
|
97
|
-
const extent = { attributes: { position: { data: lanes.flatMap(lane => lane.points.flatMap(([x, z, y]) => [x - 20, y, z - 20, x + 20, y + 5, z + 20])) } } };
|
|
98
|
-
computeBounds(extent);
|
|
99
|
-
mesh.geometry.bounds = extent.bounds;
|
|
100
|
-
};
|
|
101
|
-
|
|
102
|
-
const destroyGpu = () => {
|
|
103
|
-
for (const buffer of [gpu?.params, gpu?.template, gpu?.points, gpu?.vehicles]) buffer?.destroy();
|
|
104
|
-
gpu = null;
|
|
105
|
-
};
|
|
106
|
-
const storage = (device, data) => {
|
|
107
|
-
const buffer = device.createBuffer({ size: Math.max(16, data.byteLength), usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST });
|
|
108
|
-
device.queue.writeBuffer(buffer, 0, data);
|
|
109
|
-
return buffer;
|
|
110
|
-
};
|
|
111
|
-
// Lane points are [x, z, ground, cumulative length]. Mirrors along() and place().
|
|
112
|
-
const shader = `
|
|
113
|
-
struct Params { seconds: f32, side: f32, vertices: u32, total: u32 }
|
|
114
|
-
struct Vehicle { start: u32, count: u32, base: f32, speed: f32 }
|
|
115
|
-
@group(0) @binding(0) var<uniform> params: Params;
|
|
116
|
-
@group(0) @binding(1) var<storage, read> model: array<f32>;
|
|
117
|
-
@group(0) @binding(2) var<storage, read> points: array<vec4f>;
|
|
118
|
-
@group(0) @binding(3) var<storage, read> vehicles: array<Vehicle>;
|
|
119
|
-
@group(0) @binding(4) var<storage, read_write> position: array<f32>;
|
|
120
|
-
@group(0) @binding(5) var<storage, read_write> normal: array<f32>;
|
|
121
|
-
@compute @workgroup_size(64)
|
|
122
|
-
fn main(@builtin(global_invocation_id) id: vec3u) {
|
|
123
|
-
let v = id.x;
|
|
124
|
-
if (v >= params.total) { return; }
|
|
125
|
-
let vehicle = vehicles[v / params.vertices];
|
|
126
|
-
let last = vehicle.start + vehicle.count - 1u;
|
|
127
|
-
let length = points[last].w;
|
|
128
|
-
var s = vehicle.base + params.seconds * vehicle.speed;
|
|
129
|
-
s -= length * floor(s / length);
|
|
130
|
-
var i = vehicle.start + 1u;
|
|
131
|
-
while (i < last && points[i].w < s) { i++; }
|
|
132
|
-
let a = points[i - 1u];
|
|
133
|
-
let b = points[i];
|
|
134
|
-
let d = select(b.w - a.w, 1.0, b.w == a.w);
|
|
135
|
-
let t = (s - a.w) / d;
|
|
136
|
-
let dx = (b.x - a.x) / d;
|
|
137
|
-
let dz = (b.y - a.y) / d;
|
|
138
|
-
let cx = a.x + (b.x - a.x) * t - dz * params.side;
|
|
139
|
-
let cz = a.y + (b.y - a.y) * t + dx * params.side;
|
|
140
|
-
let y = a.z + (b.z - a.z) * t;
|
|
141
|
-
let k = (v % params.vertices) * 3u;
|
|
142
|
-
let n = params.vertices * 3u + k;
|
|
143
|
-
let o = v * 3u;
|
|
144
|
-
position[o] = cx - model[k] * dz - model[k + 2u] * dx;
|
|
145
|
-
position[o + 1u] = y + model[k + 1u];
|
|
146
|
-
position[o + 2u] = cz + model[k] * dx - model[k + 2u] * dz;
|
|
147
|
-
normal[o] = -model[n] * dz - model[n + 2u] * dx;
|
|
148
|
-
normal[o + 1u] = model[n + 1u];
|
|
149
|
-
normal[o + 2u] = model[n] * dx - model[n + 2u] * dz;
|
|
150
|
-
}`;
|
|
151
|
-
// Upload lanes and the rebased trips; the CPU copy is refreshed only for picking (place()).
|
|
152
|
-
const rebase = () => {
|
|
153
|
-
const { device } = layer.renderer;
|
|
154
|
-
if (gpu?.device !== device) {
|
|
155
|
-
destroyGpu();
|
|
156
|
-
gpu = {
|
|
157
|
-
device,
|
|
158
|
-
pipeline: device.createComputePipeline({ layout: 'auto', compute: { module: device.createShaderModule({ code: shader }), entryPoint: 'main' } }),
|
|
159
|
-
params: device.createBuffer({ size: 16, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST }),
|
|
160
|
-
template: storage(device, new Float32Array([...template.position, ...template.normal])),
|
|
161
|
-
};
|
|
162
|
-
lanesDirty = true;
|
|
163
|
-
}
|
|
164
|
-
if (lanesDirty) {
|
|
165
|
-
const data = new Float32Array(lanes.reduce((count, lane) => count + lane.points.length, 0) * 4);
|
|
166
|
-
let offset = 0;
|
|
167
|
-
for (const lane of lanes) {
|
|
168
|
-
lane.start = offset / 4;
|
|
169
|
-
lane.points.forEach((point, i) => { data.set([point[0], point[1], point[2], lane.lengths[i]], offset); offset += 4; });
|
|
170
|
-
}
|
|
171
|
-
gpu.points?.destroy();
|
|
172
|
-
gpu.points = storage(device, data);
|
|
173
|
-
lanesDirty = false;
|
|
174
|
-
}
|
|
175
|
-
const data = new Float32Array(trips.length * 4), index = new Uint32Array(data.buffer);
|
|
176
|
-
trips.forEach((trip, i) => {
|
|
177
|
-
index[i * 4] = trip.lane.start;
|
|
178
|
-
index[i * 4 + 1] = trip.lane.points.length;
|
|
179
|
-
data[i * 4 + 2] = trip.distance;
|
|
180
|
-
data[i * 4 + 3] = trip.speed;
|
|
181
|
-
mesh.extras.featureFaces[i].properties = trip.properties;
|
|
182
|
-
});
|
|
183
|
-
if (gpu.vehicles?.size >= data.byteLength) device.queue.writeBuffer(gpu.vehicles, 0, data);
|
|
184
|
-
else {
|
|
185
|
-
gpu.vehicles?.destroy();
|
|
186
|
-
gpu.vehicles = storage(device, data);
|
|
187
|
-
}
|
|
188
|
-
const { position, normal } = mesh.geometry.attributes;
|
|
189
|
-
gpu.bindGroup = device.createBindGroup({
|
|
190
|
-
layout: gpu.pipeline.getBindGroupLayout(0),
|
|
191
|
-
entries: [gpu.params, gpu.template, gpu.points, gpu.vehicles, position.buffer, normal.buffer]
|
|
192
|
-
.map((buffer, binding) => ({ binding, resource: { buffer } })),
|
|
193
|
-
});
|
|
194
|
-
};
|
|
195
|
-
const move = seconds => {
|
|
196
|
-
const { device } = gpu;
|
|
197
|
-
paramFloats[0] = elapsed = seconds;
|
|
198
|
-
paramFloats[1] = side;
|
|
199
|
-
paramInts[2] = vertices;
|
|
200
|
-
paramInts[3] = trips.length * vertices;
|
|
201
|
-
device.queue.writeBuffer(gpu.params, 0, params);
|
|
202
|
-
const encoder = device.createCommandEncoder(), pass = encoder.beginComputePass();
|
|
203
|
-
pass.setPipeline(gpu.pipeline);
|
|
204
|
-
pass.setBindGroup(0, gpu.bindGroup);
|
|
205
|
-
pass.dispatchWorkgroups(Math.ceil(paramInts[3] / 64));
|
|
206
|
-
pass.end();
|
|
207
|
-
device.queue.submit([encoder.finish()]);
|
|
208
|
-
stale = true;
|
|
209
|
-
};
|
|
210
|
-
// CPU twin of the compute pass: the centre of `trip` and its heading.
|
|
211
|
-
const locate = trip => {
|
|
212
|
-
const { points, lengths, length } = trip.lane;
|
|
213
|
-
let distance = trip.distance + elapsed * trip.speed;
|
|
214
|
-
distance -= length * Math.floor(distance / length);
|
|
215
|
-
const { x, y, z, dx, dz } = along(points, lengths, distance);
|
|
216
|
-
return { x: x - dz * side, y, z: z + dx * side, dx, dz };
|
|
217
|
-
};
|
|
218
|
-
// Run lazily before picking.
|
|
219
|
-
const place = () => {
|
|
220
|
-
if (!mesh || !stale) return;
|
|
221
|
-
stale = false;
|
|
222
|
-
const { position, normal } = mesh.geometry.attributes;
|
|
223
|
-
trips.forEach((trip, i) => {
|
|
224
|
-
const { x: cx, y, z: cz, dx, dz } = locate(trip);
|
|
225
|
-
for (let k = 0, o = i * stride; k < stride; k += 3, o += 3) {
|
|
226
|
-
const lx = template.position[k], lz = template.position[k + 2], nx = template.normal[k], nz = template.normal[k + 2];
|
|
227
|
-
position.data[o] = cx - lx * dz - lz * dx;
|
|
228
|
-
position.data[o + 1] = y + template.position[k + 1];
|
|
229
|
-
position.data[o + 2] = cz + lx * dx - lz * dz;
|
|
230
|
-
normal.data[o] = -nx * dz - nz * dx;
|
|
231
|
-
normal.data[o + 1] = template.normal[k + 1];
|
|
232
|
-
normal.data[o + 2] = nx * dx - nz * dz;
|
|
233
|
-
}
|
|
234
|
-
});
|
|
235
|
-
};
|
|
236
|
-
const raycast = layer.raycast;
|
|
237
|
-
layer.raycast = function (...args) {
|
|
238
|
-
place();
|
|
239
|
-
return raycast.apply(this, args);
|
|
240
|
-
};
|
|
241
|
-
|
|
242
|
-
const tick = () => {
|
|
243
|
-
frame = undefined;
|
|
244
|
-
if (!ready || !layer._map) return;
|
|
245
|
-
// Re-adding after the last GPU owner was removed can attach asynchronously.
|
|
246
|
-
if (!layer.renderer) { frame = requestAnimationFrame(tick); return; }
|
|
247
|
-
// A renderer change re-creates vertex buffers without STORAGE: rebuild the mesh.
|
|
248
|
-
if (mesh && !(mesh.geometry.attributes.position.buffer.usage & GPUBufferUsage.STORAGE)) { tripsKey = null; nextCheck = -Infinity; }
|
|
249
|
-
const { live, time } = now(), ms = time?.getTime(), valid = Number.isFinite(ms);
|
|
250
|
-
let changed = false;
|
|
251
|
-
// Trips only change at timetable/hour boundaries: re-check once per second while live.
|
|
252
|
-
if (!live || !valid || !(ms >= rebasedAt && ms < nextCheck)) {
|
|
253
|
-
let text;
|
|
254
|
-
try {
|
|
255
|
-
trips = valid ? tripsAt(time, lanes, velocity) : [];
|
|
256
|
-
text = !valid ? 'Select a valid simulation date' : !loaded ? 'Loading…' :
|
|
257
|
-
`${trips.length} vehicle${trips.length === 1 ? '' : 's'}${loaded.note ? ` · ${loaded.note}` : ''}`;
|
|
258
|
-
} catch (error) { return fail(error); }
|
|
259
|
-
rebasedAt = ms;
|
|
260
|
-
nextCheck = ms + 1000;
|
|
261
|
-
const key = trips.map(trip => trip.id).join();
|
|
262
|
-
changed = key !== tripsKey || (outline && !route && lanes.length);
|
|
263
|
-
if (outline && !route && lanes.length) drawRoute();
|
|
264
|
-
if (key !== tripsKey) { rebuild(); tripsKey = key; }
|
|
265
|
-
if (mesh) rebase();
|
|
266
|
-
if (status.textContent !== text) status.textContent = text;
|
|
267
|
-
status.removeAttribute('role');
|
|
268
|
-
}
|
|
269
|
-
if (mesh) move((ms - rebasedAt) / 1000);
|
|
270
|
-
// ponytail: night point lights follow the vehicles at 10 Hz via the shared CPU light grid (~5 ms with thousands of lamps);
|
|
271
|
-
// bin the lights in a compute pass if it shows up in profiles.
|
|
272
|
-
if (mesh && lights && (!live || !(performance.now() - litAt < 100))) {
|
|
273
|
-
litAt = performance.now();
|
|
274
|
-
mesh.extras.lights = trips.flatMap(trip => {
|
|
275
|
-
const { x, y, z, dx, dz } = locate(trip);
|
|
276
|
-
return lights.map(({ position: [lx, ly, lz], ...light }) => ({ ...light, position: [x - lx * dz - lz * dx, y + ly, z + lx * dx - lz * dz] }));
|
|
277
|
-
});
|
|
278
|
-
}
|
|
279
|
-
if (mesh || changed) layer.update();
|
|
280
|
-
if (live && valid) frame = requestAnimationFrame(tick);
|
|
281
|
-
};
|
|
282
|
-
let loaded, file;
|
|
283
|
-
// A JS module serves time-dependent lanes; anything else is a static GeoJSON file, fetched once.
|
|
284
|
-
const fetchLanes = async (time, signal) => {
|
|
285
|
-
const href = new URL(url, document.baseURI).href;
|
|
286
|
-
if (/\.m?js(?:[?#]|$)/i.test(url)) return (await import(href)).default(time, layer, signal);
|
|
287
|
-
if (!file) {
|
|
288
|
-
const response = await fetch(href, { signal });
|
|
289
|
-
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
290
|
-
file = await response.json();
|
|
291
|
-
}
|
|
292
|
-
return file;
|
|
293
|
-
};
|
|
294
|
-
const load = () => {
|
|
295
|
-
const { time } = now();
|
|
296
|
-
if (!Number.isFinite(time?.getTime())) return;
|
|
297
|
-
request?.abort();
|
|
298
|
-
const { signal } = request = new AbortController();
|
|
299
|
-
fetchLanes(time, signal).then(data => {
|
|
300
|
-
if (signal.aborted || !layer._map || data === loaded) return;
|
|
301
|
-
loaded = data;
|
|
302
|
-
lanes = toLanes(data);
|
|
303
|
-
prepare();
|
|
304
|
-
if (!frame) tick();
|
|
305
|
-
}, error => { if (error.name !== 'AbortError') fail(error); });
|
|
306
|
-
};
|
|
307
|
-
// 'layerremove' fires once the clock layer has really left the map.
|
|
308
|
-
const update = event => {
|
|
309
|
-
if (event?.layer === layer) return;
|
|
310
|
-
cancelAnimationFrame(frame);
|
|
311
|
-
load();
|
|
312
|
-
tick();
|
|
313
|
-
};
|
|
314
|
-
const terrain = () => {
|
|
315
|
-
if (!lanes.length) return;
|
|
316
|
-
ground();
|
|
317
|
-
dispose(route); route = null;
|
|
318
|
-
if (mesh) bounds();
|
|
319
|
-
if (!frame) tick();
|
|
320
|
-
};
|
|
321
|
-
const start = () => {
|
|
322
|
-
layer._map.off('simulationtimechange layerremove', update).on('simulationtimechange layerremove', update);
|
|
323
|
-
layer._map.off('terrainchange', terrain).on('terrainchange', terrain);
|
|
324
|
-
underEntry(layer, status);
|
|
325
|
-
update();
|
|
326
|
-
};
|
|
327
|
-
layer.on('ready', () => { ready = true; if (layer._map) start(); });
|
|
328
|
-
layer.on('add', () => { if (ready) start(); });
|
|
329
|
-
layer.on('remove', () => {
|
|
330
|
-
request?.abort();
|
|
331
|
-
cancelAnimationFrame(frame); frame = undefined;
|
|
332
|
-
layer._map.off('simulationtimechange layerremove', update).off('terrainchange', terrain);
|
|
333
|
-
dispose(mesh); dispose(route); mesh = route = null;
|
|
334
|
-
destroyGpu();
|
|
335
|
-
lanes = []; trips = []; tripsKey = loaded = null;
|
|
336
|
-
});
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
/**
|
|
340
|
-
* Vehicles moving along GeoJSON lanes; a compute pass places them on the GPU every frame.
|
|
341
|
-
* Source options:
|
|
342
|
-
* - `url`: GeoJSON (Multi)LineStrings, or a JS module whose default export `(time, layer, signal)` returns them (time-dependent data);
|
|
343
|
-
* - `vehicle`: 'car' (default) or 'boat'; `side`: offset right of the lane centreline in metres (vehicle default);
|
|
344
|
-
* - `speed`: m/s of `flow` lanes; `outline`: draw the lanes.
|
|
345
|
-
* Lane properties: `flow` (vehicles/h) or `timetable` (see scheduledTrips in utils/traffic.js), optional fixed `elevation`
|
|
346
|
-
* in metres, `name`/`description` for the default tooltip. The status line (vehicle count, then the FeatureCollection `note`) sits under the layer's entry in the layers control.
|
|
347
|
-
* The 'sunlight' layer on the same map drives the time; without one, vehicles run live.
|
|
348
|
-
*/
|
|
349
|
-
L.Layer.types.traffic = L.Layer.types['3d'].extend({
|
|
350
|
-
initialize({ url, vehicle = 'car', side, outline = false, speed: velocity = speed, ...options } = {}) {
|
|
351
|
-
if (!url) throw new Error('Traffic needs a source url');
|
|
352
|
-
if (!vehicles[vehicle]) throw new Error(`Unknown vehicle: ${vehicle}`);
|
|
353
|
-
L.Layer.types['3d'].prototype.initialize.call(this, { tooltipContent: tooltip, nightLight: Boolean(vehicles[vehicle].lights), ...options });
|
|
354
|
-
installTraffic(this, { url, ...vehicles[vehicle], side: side ?? vehicles[vehicle].side, outline, velocity });
|
|
355
|
-
},
|
|
356
|
-
});
|
package/src/types/walk.js
DELETED
|
@@ -1,219 +0,0 @@
|
|
|
1
|
-
import { CELL, collider, horizontalRay, parts, limbs } from '../utils/walk.js';
|
|
2
|
-
import './3d.js';
|
|
3
|
-
import { computeBounds } from '../utils/geometry.js';
|
|
4
|
-
import { traverse, updateWorldMatrix } from '../utils/scene.js';
|
|
5
|
-
import { build } from '../utils/vehicles.js';
|
|
6
|
-
|
|
7
|
-
// W/S (↑/↓) run forward/back along the heading, A/D (←/→) turn: [forward, turn].
|
|
8
|
-
const moves = { KeyW: [1, 0], ArrowUp: [1, 0], KeyS: [-1, 0], ArrowDown: [-1, 0], KeyD: [0, 1], ArrowRight: [0, 1], KeyA: [0, -1], ArrowLeft: [0, -1] };
|
|
9
|
-
const wrap = angle => Math.atan2(Math.sin(angle), Math.cos(angle));
|
|
10
|
-
|
|
11
|
-
// Horizontal normal of the nearest wall faced within `reach` metres of the feet at world (x, y, z), walking along
|
|
12
|
-
// the unit (dx, dz); knee and chest rays, so arches and tree crowns above the head let the walker through.
|
|
13
|
-
// ponytail: traffic vehicles and boats move on the GPU and are not obstacles.
|
|
14
|
-
function hit(layer, x, y, z, dx, dz, reach) {
|
|
15
|
-
const types = globalThis.L.Layer.types, skip = other => other === layer ||
|
|
16
|
-
['terrain', 'traffic'].some(name => types[name] && other instanceof types[name]);
|
|
17
|
-
updateWorldMatrix(layer.scene);
|
|
18
|
-
let best = reach, found = null;
|
|
19
|
-
for (const other of layer._shared._layers) {
|
|
20
|
-
if (skip(other)) continue;
|
|
21
|
-
traverse(other.root, mesh => {
|
|
22
|
-
if (!mesh.visible) return true;
|
|
23
|
-
if (!mesh.geometry?.attributes?.position?.data || mesh.program?.depthTest === false) return;
|
|
24
|
-
if (!mesh.geometry.bounds) computeBounds(mesh.geometry);
|
|
25
|
-
const m = mesh.worldMatrix, { center: k, radius } = mesh.geometry.bounds;
|
|
26
|
-
if (Math.hypot(m[0] * k[0] + m[4] * k[1] + m[8] * k[2] + m[12] - x, m[2] * k[0] + m[6] * k[1] + m[10] * k[2] + m[14] - z) >
|
|
27
|
-
radius * Math.hypot(m[0], m[1], m[2]) + reach) return;
|
|
28
|
-
const c = collider(mesh), ox = x - m[12] + c.origin[0], oy = y - m[13] + c.origin[1], oz = z - m[14] + c.origin[2];
|
|
29
|
-
const seen = new Set();
|
|
30
|
-
for (let ix = Math.floor(Math.min(ox, ox + dx * reach) / CELL); ix <= Math.floor(Math.max(ox, ox + dx * reach) / CELL); ix++)
|
|
31
|
-
for (let iz = Math.floor(Math.min(oz, oz + dz * reach) / CELL); iz <= Math.floor(Math.max(oz, oz + dz * reach) / CELL); iz++)
|
|
32
|
-
for (const t of c.cells.get(ix + ',' + iz) || []) {
|
|
33
|
-
// Walls are one-way: walking out of a building (e.g. starting inside one) is allowed.
|
|
34
|
-
if (seen.has(t) || dx * c.nrm[t * 2] + dz * c.nrm[t * 2 + 1] >= 0) continue;
|
|
35
|
-
seen.add(t);
|
|
36
|
-
for (const height of [0.4, 1.5]) {
|
|
37
|
-
const d = horizontalRay(c.tri, t, ox, oy + height, oz, dx, dz);
|
|
38
|
-
if (d >= 0 && d < best) { best = d; found = [c.nrm[t * 2], c.nrm[t * 2 + 1]]; }
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
});
|
|
42
|
-
}
|
|
43
|
-
return found;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function installWalk(layer, { speed, distance, position }) {
|
|
47
|
-
const L = globalThis.L, keys = new Set();
|
|
48
|
-
// Map button (leaflet-bar look) toggling the walk; the key hint shows beside it while walking.
|
|
49
|
-
const control = new L.Control({ position });
|
|
50
|
-
const container = L.DomUtil.create('div', 'leaflet-bar leaflet-control');
|
|
51
|
-
container.style.cssText = 'display:flex;align-items:center;background:#fff;';
|
|
52
|
-
const button = L.DomUtil.create('button', '', container);
|
|
53
|
-
button.type = 'button';
|
|
54
|
-
button.title = 'Walk';
|
|
55
|
-
button.setAttribute('aria-label', 'Walk');
|
|
56
|
-
button.setAttribute('aria-pressed', 'false');
|
|
57
|
-
button.textContent = '🚶';
|
|
58
|
-
button.style.cssText = 'width:30px;height:30px;padding:0;border:0;border-radius:4px;background:#fff;font-size:18px;line-height:30px;cursor:pointer;';
|
|
59
|
-
const hint = L.DomUtil.create('small', '', container);
|
|
60
|
-
hint.style.cssText = 'display:none;padding:0 8px;color:#333;white-space:nowrap;';
|
|
61
|
-
hint.textContent = 'W/S: run · A/D: turn · drag: look around · Esc: exit';
|
|
62
|
-
L.DomEvent.disableClickPropagation(container);
|
|
63
|
-
control.onAdd = () => container;
|
|
64
|
-
let meshes, frame, last, swing = 0, saved, drag;
|
|
65
|
-
// Walker state: position, compass heading (radians, clockwise from north) and stride phase.
|
|
66
|
-
const state = layer.walker = { latlng: null, heading: 0, phase: 0 };
|
|
67
|
-
|
|
68
|
-
// Feet position in this layer's root space.
|
|
69
|
-
const local = () => {
|
|
70
|
-
const map = layer._map, crs = map.options.crs, origin = crs.project(layer.location), metres = Math.cos(layer.location.lat * Math.PI / 180);
|
|
71
|
-
const p = crs.project(state.latlng), terrain = map._terrain3D?._terrainReady && map._terrain3D;
|
|
72
|
-
const ground = (terrain && terrain._getElevation(state.latlng)) || 0;
|
|
73
|
-
return [(p.x - origin.x) * metres, ground - layer.root.position[1], -(p.y - origin.y) * metres];
|
|
74
|
-
};
|
|
75
|
-
const place = () => {
|
|
76
|
-
const [x, y, z] = local();
|
|
77
|
-
// Model forward is -z; a yaw of -heading turns it to the compass heading (x east, -z north).
|
|
78
|
-
const yaw = -state.heading, cos = Math.cos(yaw), sin = Math.sin(yaw);
|
|
79
|
-
limbs.forEach(([, px, py, sign], i) => {
|
|
80
|
-
const mesh = meshes[i];
|
|
81
|
-
mesh.position[0] = x + px * cos; mesh.position[1] = y + py; mesh.position[2] = z - px * sin;
|
|
82
|
-
mesh.rotation[0] = sign * swing * Math.sin(state.phase);
|
|
83
|
-
mesh.rotation[1] = yaw;
|
|
84
|
-
});
|
|
85
|
-
layer.update();
|
|
86
|
-
};
|
|
87
|
-
|
|
88
|
-
const step = now => {
|
|
89
|
-
frame = requestAnimationFrame(step);
|
|
90
|
-
const map = layer._map, dt = Math.min(0.1, (now - (last ?? now)) / 1000);
|
|
91
|
-
last = now;
|
|
92
|
-
if (!layer.renderer) return;
|
|
93
|
-
let changed = false;
|
|
94
|
-
if (!meshes) { meshes = limbs.map(([part]) => layer._createMesh(build(parts[part]))); changed = true; }
|
|
95
|
-
let forward = 0, turn = 0;
|
|
96
|
-
for (const key of keys) { forward += moves[key]?.[0] || 0; turn += moves[key]?.[1] || 0; }
|
|
97
|
-
forward = Math.sign(forward); turn = Math.sign(turn);
|
|
98
|
-
if (turn) { state.heading = wrap(state.heading + turn * 2.5 * dt); changed = true; }
|
|
99
|
-
if (forward) {
|
|
100
|
-
const metres = forward * speed * dt, { lat, lng } = state.latlng, { position, scale } = layer.root;
|
|
101
|
-
let east = Math.sin(state.heading) * metres, north = Math.cos(state.heading) * metres;
|
|
102
|
-
const feet = local().map((value, i) => position[i] + scale[i] * value);
|
|
103
|
-
// World x is east, z south; keep 0.3 m from walls and slide along the one in the way.
|
|
104
|
-
const blocked = () => {
|
|
105
|
-
const length = Math.hypot(east, north);
|
|
106
|
-
return length > 1e-6 && hit(layer, feet[0], feet[1], feet[2], east / length, -north / length, length + 0.3);
|
|
107
|
-
};
|
|
108
|
-
const wall = blocked();
|
|
109
|
-
if (wall) {
|
|
110
|
-
const into = east * wall[0] - north * wall[1];
|
|
111
|
-
east -= into * wall[0]; north += into * wall[1];
|
|
112
|
-
if (blocked()) east = north = 0;
|
|
113
|
-
}
|
|
114
|
-
state.latlng = new L.LatLng(lat + north / 111320, lng + east / (111320 * Math.cos(lat * Math.PI / 180)));
|
|
115
|
-
state.phase += Math.hypot(east, north) / 1.6 * Math.PI;
|
|
116
|
-
changed = true;
|
|
117
|
-
}
|
|
118
|
-
if (forward || turn) {
|
|
119
|
-
// The camera (looking along -bearing) swings back behind the walker.
|
|
120
|
-
const bearing = map.getBearing() * Math.PI / 180, behind = wrap(-state.heading - bearing);
|
|
121
|
-
if (Math.abs(behind) > 1e-3) map._setPitchAndBearing(map.getPitch(), (bearing + behind * Math.min(1, dt * 4)) * 180 / Math.PI);
|
|
122
|
-
map.setView(state.latlng, map.getZoom(), { animate: false });
|
|
123
|
-
}
|
|
124
|
-
const target = forward ? 0.9 : 0;
|
|
125
|
-
if (Math.abs(swing - target) > 1e-3) { swing += (target - swing) * Math.min(1, dt * 10); changed = true; }
|
|
126
|
-
if (changed) place();
|
|
127
|
-
};
|
|
128
|
-
|
|
129
|
-
const onKey = event => {
|
|
130
|
-
// Text fields and sliders keep their keys; buttons and checkboxes (e.g. the Walk button, focused once clicked) don't.
|
|
131
|
-
if (event.target.closest?.('input:not([type=checkbox], [type=radio]), select, textarea, [contenteditable]') ||
|
|
132
|
-
event.ctrlKey || event.altKey || event.metaKey) return;
|
|
133
|
-
if (event.type === 'keydown' && event.code === 'Escape') { stop(); return; }
|
|
134
|
-
if (!moves[event.code]) return;
|
|
135
|
-
event.preventDefault();
|
|
136
|
-
if (event.type === 'keydown') keys.add(event.code); else keys.delete(event.code);
|
|
137
|
-
};
|
|
138
|
-
const onBlur = () => keys.clear();
|
|
139
|
-
// Dragging only turns the camera around the walker: horizontal = bearing, vertical = pitch.
|
|
140
|
-
const onPointer = event => {
|
|
141
|
-
const map = layer._map;
|
|
142
|
-
if (event.type === 'pointerdown') {
|
|
143
|
-
if (event.button !== 0 || event.target.closest('.leaflet-control')) return;
|
|
144
|
-
drag = { x: event.clientX, y: event.clientY };
|
|
145
|
-
map.getContainer().setPointerCapture(event.pointerId);
|
|
146
|
-
} else if (drag && event.type === 'pointermove') {
|
|
147
|
-
map._setPitchAndBearing(Math.max(20, Math.min(85, map.getPitch() - (event.clientY - drag.y) * 0.2)),
|
|
148
|
-
map.getBearing() - (event.clientX - drag.x) * 0.25);
|
|
149
|
-
drag = { x: event.clientX, y: event.clientY };
|
|
150
|
-
} else drag = null;
|
|
151
|
-
};
|
|
152
|
-
const onTerrain = () => { if (meshes) place(); };
|
|
153
|
-
|
|
154
|
-
const start = () => {
|
|
155
|
-
const map = layer._map;
|
|
156
|
-
if (saved || !map) return;
|
|
157
|
-
state.latlng = map.getCenter();
|
|
158
|
-
// Zoom whose camera distance (1.5 viewport heights, as in scene/render.js) is about `distance` metres behind the walker.
|
|
159
|
-
const pixelsPerMetre = map.getSize().y * 1.5 / distance;
|
|
160
|
-
const zoom = Math.min(map.getMaxZoom(), Math.log2(pixelsPerMetre * 40075016.68557849 * Math.cos(state.latlng.lat * Math.PI / 180) / 256));
|
|
161
|
-
saved = { zoom: map.getZoom(), pitch: map.getPitch(), scrollWheelZoom: map.options.scrollWheelZoom,
|
|
162
|
-
handlers: ['dragging', 'keyboard', 'doubleClickZoom', 'boxZoom'].filter(name => map[name]?.enabled()) };
|
|
163
|
-
for (const name of saved.handlers) map[name].disable();
|
|
164
|
-
// Wheel zoom moves the camera closer or farther, keeping the walker centred.
|
|
165
|
-
map.options.scrollWheelZoom = saved.scrollWheelZoom && 'center';
|
|
166
|
-
state.heading = -map.getBearing() * Math.PI / 180;
|
|
167
|
-
map.setView(state.latlng, zoom, { animate: false });
|
|
168
|
-
map.setPitch(75);
|
|
169
|
-
L.DomEvent.on(document, 'keydown keyup', onKey);
|
|
170
|
-
L.DomEvent.on(window, 'blur', onBlur);
|
|
171
|
-
L.DomEvent.on(map.getContainer(), 'pointerdown pointermove pointerup pointercancel', onPointer);
|
|
172
|
-
map.on('terrainchange', onTerrain);
|
|
173
|
-
button.setAttribute('aria-pressed', 'true');
|
|
174
|
-
button.style.background = '#e4e4e4';
|
|
175
|
-
hint.style.display = '';
|
|
176
|
-
last = undefined;
|
|
177
|
-
frame = requestAnimationFrame(step);
|
|
178
|
-
};
|
|
179
|
-
const stop = () => {
|
|
180
|
-
const map = layer._map;
|
|
181
|
-
cancelAnimationFrame(frame);
|
|
182
|
-
keys.clear(); drag = null;
|
|
183
|
-
L.DomEvent.off(document, 'keydown keyup', onKey);
|
|
184
|
-
L.DomEvent.off(window, 'blur', onBlur);
|
|
185
|
-
for (const mesh of meshes || []) layer._disposeMesh(mesh);
|
|
186
|
-
meshes = null;
|
|
187
|
-
button.setAttribute('aria-pressed', 'false');
|
|
188
|
-
button.style.background = '#fff';
|
|
189
|
-
hint.style.display = 'none';
|
|
190
|
-
if (!map || !saved) return;
|
|
191
|
-
L.DomEvent.off(map.getContainer(), 'pointerdown pointermove pointerup pointercancel', onPointer);
|
|
192
|
-
map.off('terrainchange', onTerrain);
|
|
193
|
-
map.options.scrollWheelZoom = saved.scrollWheelZoom;
|
|
194
|
-
for (const name of saved.handlers) map[name].enable();
|
|
195
|
-
map.setView(state.latlng, saved.zoom, { animate: false });
|
|
196
|
-
map.setPitch(saved.pitch);
|
|
197
|
-
saved = null;
|
|
198
|
-
};
|
|
199
|
-
L.DomEvent.on(button, 'click', () => (saved ? stop() : start()));
|
|
200
|
-
let ready = false;
|
|
201
|
-
layer.on('ready', () => { ready = true; if (layer._map) control.addTo(layer._map); });
|
|
202
|
-
layer.on('add', () => { if (ready) control.addTo(layer._map); });
|
|
203
|
-
layer.on('remove', () => { stop(); control.remove(); });
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
/**
|
|
207
|
-
* Third-person walk mode, toggled by a map button (and Esc): a pedestrian at the map centre runs with W/S (or ↑/↓) and
|
|
208
|
-
* turns with A/D (or ←/→); the camera swings back behind it while it moves. Dragging the map only turns the camera
|
|
209
|
-
* (bearing, pitch); the wheel changes the camera distance.
|
|
210
|
-
* The walker follows the terrain, bridges included, and `layer.walker` exposes `{ latlng, heading, phase }`.
|
|
211
|
-
* Source options: `speed` (m/s, default 6), `distance` (camera distance in metres, default 20), `position` (button corner, default 'topleft').
|
|
212
|
-
* Visible meshes of the other 3D layers (buildings, trees, models, streetlights) are solid; terrain and traffic are not.
|
|
213
|
-
*/
|
|
214
|
-
L.Layer.types.walk = L.Layer.types['3d'].extend({
|
|
215
|
-
initialize({ speed = 70, distance = 20, position = 'bottomright', ...options } = {}) {
|
|
216
|
-
L.Layer.types['3d'].prototype.initialize.call(this, options);
|
|
217
|
-
installWalk(this, { speed, distance, position });
|
|
218
|
-
},
|
|
219
|
-
});
|