A web application for exploring Swiss rooftops and addresses and a processing pipeline for
- converting Swiss addresses to parquet files
- converting some SwissTLM features to parquet files (buildings, bridges, roads). This is also used for Topoprint.
Read all about it in this blog-post.
- Fast web map for approximately 3.2 million roof polygons in Switzerland, using [PMTiles][]
- 3D bridge models generated from Swiss transportation network data (TLM)
- Identification and processing of buildings that might be missing in the original dataset
- Visualization of bridges with customizable parameters (arches, piers, deck width)
- End-to-end data processing pipeline from a FileGeodatabase to PMTiles via [Geoparquet][]
- Swiss addresses stored in a compact parquet file for quick retrieval
- Client-side querying of addresses using [DuckDB-WASM][]
- No server component needed other than a web or object server, making it easy to run locally
- Bookmarking is possible, for example in Bern: https://ping13.net/swiss-rooftop-explorer/?zip=3011 (though it may take a couple of seconds until the app zooms to the area of interest)
- Python 3.11+
- Node.js
- Make
- DuckDB
- Tippecanoe (for PMTiles generation)
uv
- Clone the repository:
git clone https://github.com/ping13/swiss-rooftop-explorer- Install dependencies using
uv sync. In case of macOS, I needed to make sure that GDAL and arrow binaries are built against Homebrew's version (which I use).
make align_with_homebrew
(Details see this issue)
- Install frontend dependencies:
cd web
npm installThe project processes Swiss geographic data through several stages:
make downloadDownloads and extracts:
- Swiss address database (SQLite format)
- SwissBUILDINGS3D 3.0 data (FileGDB format)
- SwissTLM3D data for road and railway bridges
make create_addressesConverts Swiss address data from SQLite to two Parquet formats:
- Full dataset (
addresses_full.parquet) - Minimal dataset (
addresses.parquet)
make create_buildingsProcesses building data through multiple stages:
- Converts FileGDB to Parquet
- Processes 3D geometries
- Creates 2D projections
make create_bridgesProcesses bridge data through multiple stages:
- Extracts bridge geometries from SwissTLM3D data
- Creates parametrized 3D bridge models with arches, piers, and customizable deck width
- Stores bridge parameters in a SQLite database for customization
- Exports bridges to GeoParquet format with compressed 3D models
make display_bridge_from_clipboardVisualizes a bridge in 3D from GeoJSON data in the QGIS project copied to clipboard (useful for testing)
make pmtilesGenerates PMTiles for web visualization:
buildings.pmtiles: Complete dataset including buildings, missing buildings, and bridgesroofs-small.pmtiles: Smaller test dataset
make roads
make railwaysCreates GeoParquet datasets via scripts/create_routes_3d.py containing the full 3D alignment for every TLM road and railway segment. Each record stores a WKB blob, inferred lane width, and flattened 2D length; downstream publishing is handled by make publish_routes.
Start the development server:
make devserverBuild for production:
make build_webTest production build:
make test_webUse the Makefile targets to push generated datasets and the web build to the public host:
make publish_buildingsuploads parquet outputs for building footprints and centers.make publish_bridgessyncs parametrized bridge assets (parquet + GeoJSON).make publish_routespublishes the road and railway GeoParquet produced bycreate_routes_3d.py.make publish_webdeploys the Vite production build viarsync.
├── scripts/ # Data processing scripts
│ ├── addresses_sqlite2pq.py # Convert address data from SQLite to Parquet
│ ├── swissbuildings3D_gdb2pq.py # Convert building data from GDB to Parquet
│ ├── swissbuildings3D_process.py # Process 3D building geometries
│ ├── bridge_creation.py # Core functionality for creating 3D bridge models
│ ├── create_bridges_3d.py # Process bridge data and create 3D models
│ ├── create_bridges_kml.py # Export bridges with parameters to KML/GeoJSON
│ ├── create_addl_buildings_from_roof.py # Identify missing buildings from roof data
│ ├── create_center_points.py # Create center points for buildings
│ ├── create_routes_3d.py # Convert TLM roads/railways into GeoParquet with width metadata
│ ├── create_bridge_db.sh # Seed the SQLite DB for bridge parameter overrides
│ ├── create_topoprint_script_for_bridges.py # Generate helper scripts for TopoPrint exports
│ └── pq2pmtiles.sh # Convert Parquet to PMTiles format
├── web/ # Web application
│ ├── src/
│ ├── public/
│ └── package.json
├── Makefile # Build automation
├── assets/ # Data assets
│ └── bridge_parameters.db # SQLite database for bridge customization
└── pyproject.toml # Python project configuration
- Frontend:
- Vanilla JavaScript
- Vite for build tooling
- DuckDB-WASM for client-side querying
- MapLibre GL for map visualization
- Data Storage:
- Parquet format for efficient data storage
- PMTiles for map visualization
- Data Processing:
- Python with GeoPandas, PyArrow
- DuckDB for SQL operations
- GDAL/OGR for geographic data processing
- PyVista and Trimesh for 3D modeling and visualization
- Shapely for geometric operations
- Build System: Make
