Repository navigation
Releases
meshcity 1.2.0
Coordinates from maps in China. Gaode/AMap, Tencent and Baidu maps show coordinates shifted a few hundred metres from GPS (about 550 m in Beijing). Add --coords gcj02 (Gaode/AMap, Tencent) or --coords bd09 (Baidu) and copy the point straight from the map:
meshcity extract URL --center 116.40374,39.91010 --coords gcj02
meshcity prints the WGS84 point it used. In Python: meshcity.gcj02_to_wgs84(lon, lat), meshcity.bd09_to_wgs84(lon, lat).
uv tool install git+https://github.com/Longado/meshcity@v1.2.0
meshcity 1.1.0
Upgrade from 1.0. In Draco-compressed 3D Tiles, 1.0 gave parts of some buildings to their neighbours (in the Bern example 34 buildings were merged into others and roofs were off by up to 30 m). 1.1 fixes this, records where the cut sits on the globe (--tileset puts it back in CesiumJS, ArcGIS or QGIS), and adds a Python API.
uv tool install git+https://github.com/Longado/meshcity@v1.1.0
Fixed
- Buildings split correctly in Draco-compressed 3D Tiles. Draco can store batch ids as quantized floats
(10 arrives as 9.9963); they were truncated, so part of a building went to its neighbour. In the Bern
example 34 buildings had been merged into others and roofs were off by up to 30 m; now 257 buildings,
every roof top within 5 mm of swisstopo's ownDACH_MAX. - Metadata
noDatavalues are now empty instead of being passed on as numbers (3DBAG used 2147483647
storeys and 3.4e38 m).scale,offset,normalized, enums and booleans are read as the
EXT_structural_metadata spec says; properties of types not read yet are named in a note. - NaN and infinite attribute values no longer produce an invalid GLB.
- Objects without a name no longer share names when tiles in different folders have the same file name.
--radiusis measured on the ellipsoid; before, the box was up to 1 % off in latitude.- Areas across the 180° meridian, reversed or out-of-range boxes, non-positive radius and malformed
numbers are refused with a one-line message instead of being accepted or crashing. - One tile that cannot be downloaded no longer stops the cut: it is left out with a note.
- Concurrent downloads of the same file no longer race on the cache's temporary file.
- Output files are written atomically, and a missing output folder is reported before downloading.
- Requires DracoPy 1.7 or later: older versions do not expose the decoded attributes, so every Draco file failed.
Added
- The GLB records where it sits: origin, height of y = 0, ECEF transform, area and sources (scene extras
meshcity), plusasset.copyrightwhen the source provides one. --tileset out.tileset.jsonwrites a 3D Tiles tileset that puts the GLB back in place in CesiumJS,
ArcGIS or QGIS.--keep-heightskeeps the source's heights.- One group node per source when several layers are cut together, and a
layercolumn in the CSV. - I3S: the object id field is always fetched, so every object can be joined back to its source. Heights
given in feet are converted, and each layer's height system is recorded. - A
tileset.jsonon disk can be read in place of a URL. GLBs whose nodes are one object each (including
meshcity's own output) are read object by object. meshcity --version.- A Python API:
from meshcity import Area, extract. Library code raisesMeshcityError(withAreaErrorand
FetchErroras subclasses) instead of exiting the process. - A live
tiles done / totalline on the terminal while downloading. --refreshdownloads again instead of using the cache; the cache honoursXDG_CACHE_HOME.meshcity discoverreports a page it cannot read instead of printing nothing.- PyPI metadata, a GitHub Actions test matrix (Linux, macOS, Windows; Python 3.10 and 3.13; lowest
dependency versions), ruff config, CONTRIBUTING.md. scripts/check_against_attributes.py, and a "How accurate" section in the README.- The Godot example puts its fallback ground under the road surfaces when the cut has roads.
Changed
- The CSV has a
layercolumn afterobject, and nested values are written as JSON. - Unnamed objects from z/x/y tile layouts are named with their folder too (Bern:
672_1167_10). - The README examples changed with the fixes above: Tokyo 69 objects (the box is 0.3 % smaller north to
south), Bern 257. - Every GLB has one group node per source above the object nodes.
meshcity 1.0.0
First public release.
meshcity cuts a neighbourhood out of a web 3D city map and keeps it as a GLB: real metres, one object per building, named and carrying its attributes when the source has them.
- Reads both formats web 3D cities stream: Esri I3S (1.5–1.10, Draco, geographic, Web Mercator and UTM layers) and OGC 3D Tiles (1.0/1.1, b3dm/glb/cmpt, Draco, meshopt, quantized meshes, WebP textures).
- Downloads only the tiles your area touches; steps down to a coarser level when the finest one would need more than 400 tiles; refuses areas over 1 km² by default.
- Trims photogrammetry meshes at the edge of the area.
- Tested against 13 open layers in 7 countries (see Tested places in the README).
examples/godot: walk the block you cut, with collisions, a spawn on an open street and building names on screen.scripts/blender_export.py: preview render plus OBJ, FBX and STL.- Works as a Claude Code skill (
SKILL.md).
Tested on macOS only. The data you cut keeps its own license.