leaflet-city-mapper
Your data, on a rotated and pitched Leaflet map: a small 3D rendering engine (WebGPU) packaged onto standard L.Layer. Available sources: extruded GeoJSON, glTF models, raster PMTiles and terrain layers.
For a working example see one of the following demos:
How to use
Load Leaflet (the only runtime dependency), then this plugin:
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9/dist/leaflet.css">
<script src="https://unpkg.com/leaflet@1.9/dist/leaflet.js"></script>
<script src="https://unpkg.com/leaflet-city-mapper@0.0.3/dist/leaflet-city-mapper.js"></script>
And then set an approriate source param to layer constructor:
new L.Layer({ source: .. })
For example, here you can use a geojson object, like this:
const buildings = new L.Layer({
source: {
type: 'geojson',
url: GeoJSON,
style: ({ properties }) => ({ top: properties.height, color: properties.color }),
tooltip: ({ properties }) => `${properties.name}: ${properties.height} m`,
},
}).addTo(map);
The build produces a single self-contained dist/leaflet-city-mapper.js bundle; Leaflet remains an external dependency. Vector/raster and feature initializers run on demand, while shared scene, rendering, geometry, glTF and PMTiles source lives in focused folders under src/.
Raster imagery blends coarse-to-fine on the terrain, retaining depth occlusion for 3D objects.
Time and walk controls
const time = new L.Control.Time({ position: 'bottomright' });
new L.Control.Layers({}, { Shadows: time.layer.addTo(map) }).addTo(map);
const walk = new L.Control.Walk({ speed: 6, distance: 20 }).addTo(map);
Time stays under Shadows in the layers control; its checkbox enables/disables
the clock, sky and shadows together. Use time.addTo(map) instead for a standalone
control. time.time and time.live expose the selected UTC time and live mode.
Walk exposes walk.walker (latlng, heading, phase); W/S move, A/D turn,
drag looks around and Esc exits. Both controls support remove() and re-adding.
These replace the former sunlight and walk layer source types.
Update demo data
The Paris example makes extensive use of PMTiles to ensure offline access / long-term availability of the map. To update them:
npm run pmtiles
node build/pmtiles.js --only=trees
node build/pmtiles.js --jobs=6
All archives use the buildings bbox (default: 2.26,48.835,2.38,48.885). Use --bbox=west,south,east,north to override it.
Build Guide
Within your local development environment:
git clone git@github.com:Raruto/leaflet-city-mapper.git
cd ./leaflet-city-mapper
npm i # install dependencies
npm run dev # start dev server at: http://localhost:8080
npm run build # generate "dist" files (once)
npm run test # build and run browser tests
After that you can start developing inside the src folder (eg. open “http://localhost:8080/examples” in your browser to preview changes).
Source layout
src/
index.js Public entry point and source registration
constants/ Exported module-level const bindings
camera/ Camera API, projection and animation
controls/ Compass, dragging, touch and resizing
core/ Leaflet layer integration, events, mutations and overlays
scene/ Source routing, lifecycle, graph nodes and transforms
sources/ GeoJSON and streamed source geometry
picking/ Raycasting, highlighting and feature interaction
rendering/ Frame stages, roof atlases, pipelines, meshes and textures
shadows/ Directional shadow maps, bounds and cache
lighting/ Point lights and spatial light grids
shaders/ Material shader generation
geometry/ Trees, lights, bridges, primitives and Leaflet vector geometry
triangulation/ Polygon triangulation and ring operations
collision/ Mesh intersection and collision queries
models/ glTF loading, decoding and materials
archives/ PMTiles archive reading and decompression
raster/ Raster tile loading, attachment and lifecycle
traffic/ Routes, scheduling, simulation, loading and rendering
vehicles/ Vehicle model geometry
terrain/ Terrain loading and sampling
sunlight/ Sun, moon, sky and time controls
walk/ First-person walking
utils/ Shared colour and numeric helpers
