glam/scripts/simplify_municipalities_geojson.py
2025-12-03 17:38:46 +01:00

101 lines
3.4 KiB
Python

#!/usr/bin/env python3
"""
Process and simplify Dutch municipality GeoJSON for web use.
Takes the raw PDOK data and creates a simplified version suitable for web display.
Uses Shapely for proper Douglas-Peucker geometry simplification.
Input: frontend/public/data/netherlands_municipalities.geojson (raw from PDOK)
Output: frontend/public/data/netherlands_municipalities_simplified.geojson
"""
import json
from pathlib import Path
from shapely.geometry import shape, mapping
from shapely.validation import make_valid
# Paths
DATA_DIR = Path(__file__).parent.parent / "frontend" / "public" / "data"
INPUT_FILE = DATA_DIR / "netherlands_municipalities.geojson"
OUTPUT_FILE = DATA_DIR / "netherlands_municipalities_simplified.geojson"
# Simplification tolerance in degrees
# 0.001 degrees ≈ 111 meters (good for web display at typical zoom levels)
SIMPLIFY_TOLERANCE = 0.001
def simplify_feature(feature, tolerance=SIMPLIFY_TOLERANCE):
"""Simplify a GeoJSON feature using Shapely."""
try:
geom = shape(feature["geometry"])
# Make geometry valid if needed
if not geom.is_valid:
geom = make_valid(geom)
# Simplify using Douglas-Peucker algorithm
simplified = geom.simplify(tolerance, preserve_topology=True)
# Ensure the result is valid
if not simplified.is_valid:
simplified = make_valid(simplified)
# Skip if geometry becomes too small/empty
if simplified.is_empty:
return None
return {
"type": "Feature",
"properties": feature["properties"],
"geometry": mapping(simplified)
}
except Exception as e:
print(f" Warning: Error simplifying {feature['properties'].get('naam', 'unknown')}: {e}")
return feature # Return original if simplification fails
def main():
print(f"Loading {INPUT_FILE}...")
with open(INPUT_FILE, "r", encoding="utf-8") as f:
data = json.load(f)
print(f"Loaded {len(data['features'])} municipalities")
# Simplify each feature
print(f"Simplifying geometries (tolerance: {SIMPLIFY_TOLERANCE} degrees ≈ {SIMPLIFY_TOLERANCE * 111000:.0f}m)...")
simplified_features = []
for i, feature in enumerate(data["features"]):
if (i + 1) % 50 == 0:
print(f" Processed {i + 1}/{len(data['features'])}...")
simplified = simplify_feature(feature)
if simplified:
simplified_features.append(simplified)
print(f"Simplified to {len(simplified_features)} features")
# Create output GeoJSON
output = {
"type": "FeatureCollection",
"name": "netherlands_municipalities_simplified",
"features": simplified_features
}
# Write with minimal whitespace for smaller file
print(f"Writing to {OUTPUT_FILE}...")
with open(OUTPUT_FILE, "w", encoding="utf-8") as f:
json.dump(output, f, ensure_ascii=False, separators=(",", ":"))
# Compare sizes
input_size = INPUT_FILE.stat().st_size / (1024 * 1024)
output_size = OUTPUT_FILE.stat().st_size / (1024 * 1024)
reduction = (1 - output_size / input_size) * 100
print(f"\nDone!")
print(f" Input: {input_size:.2f} MB")
print(f" Output: {output_size:.2f} MB")
print(f" Reduction: {reduction:.1f}%")
if __name__ == "__main__":
main()