From d4f0f2f9bf47fa5ee9b93d76d0e6b010d7a31e7a Mon Sep 17 00:00:00 2001 From: Brendan Collins Date: Tue, 10 Feb 2026 08:33:26 -0800 Subject: [PATCH] added _utils to examples --- examples/_utils.py | 49 +++++++++++++++++++++++++++++++++++++++++ examples/capetown.py | 44 ++++-------------------------------- examples/guanajuato.py | 46 ++++---------------------------------- examples/los_angeles.py | 46 ++++---------------------------------- examples/playground.py | 36 +++--------------------------- examples/rio.py | 44 ++++-------------------------------- examples/trinidad.py | 46 ++++---------------------------------- 7 files changed, 72 insertions(+), 239 deletions(-) create mode 100644 examples/_utils.py diff --git a/examples/_utils.py b/examples/_utils.py new file mode 100644 index 0000000..191c415 --- /dev/null +++ b/examples/_utils.py @@ -0,0 +1,49 @@ +_MAJOR_WATER = {'river', 'canal'} +_MINOR_WATER = {'stream', 'drain', 'ditch'} + + +def print_controls(): + print("\nControls:") + print(" W/S/A/D or Arrow keys: Move camera") + print(" Q/E or Page Up/Down: Move up/down") + print(" I/J/K/L: Look around") + print(" +/-: Adjust movement speed") + print(" G: Cycle overlay layers") + print(" O: Place observer (for viewshed)") + print(" V: Toggle viewshed (teal glow)") + print(" [/]: Adjust observer height") + print(" T: Toggle shadows") + print(" C: Cycle colormap") + print(" U: Toggle tile overlay") + print(" F: Screenshot") + print(" H: Toggle help overlay") + print(" X: Exit\n") + + +def classify_water_features(water_data): + """Split water GeoJSON features into (major, minor, body) lists.""" + major = [] + minor = [] + body = [] + for f in water_data.get('features', []): + ww = (f.get('properties') or {}).get('waterway', '') + nat = (f.get('properties') or {}).get('natural', '') + if ww in _MAJOR_WATER: + major.append(f) + elif ww in _MINOR_WATER: + minor.append(f) + elif nat == 'water': + body.append(f) + else: + minor.append(f) + return major, minor, body + + +def scale_building_heights(bldg_data, elev_scale=0.025, default_height_m=8.0): + """Scale MS building heights in-place to match terrain elevation scale.""" + for feat in bldg_data.get("features", []): + props = feat.get("properties", {}) + h = props.get("height", -1) + if not isinstance(h, (int, float)) or h <= 0: + h = default_height_m + props["height"] = h * elev_scale diff --git a/examples/capetown.py b/examples/capetown.py index f226eb3..9d01e6e 100644 --- a/examples/capetown.py +++ b/examples/capetown.py @@ -21,10 +21,7 @@ from rtxpy import fetch_dem, fetch_buildings, fetch_roads, fetch_water, fetch_firms import rtxpy - -# Water feature classification -_MAJOR_WATER = {'river', 'canal'} -_MINOR_WATER = {'stream', 'drain', 'ditch'} +from _utils import print_controls, classify_water_features, scale_building_heights # Cape Town bounding box (lon_min, lat_min, lon_max, lat_max) BOUNDS = (18.3, -34.2, 18.7, -33.8) @@ -64,21 +61,7 @@ def load_terrain(): # Load terrain data (downloads if needed) terrain = load_terrain() - print("\nControls:") - print(" W/S/A/D or Arrow keys: Move camera") - print(" Q/E or Page Up/Down: Move up/down") - print(" I/J/K/L: Look around") - print(" +/-: Adjust movement speed") - print(" G: Cycle overlay layers") - print(" O: Place observer (for viewshed)") - print(" V: Toggle viewshed (teal glow)") - print(" [/]: Adjust observer height") - print(" T: Toggle shadows") - print(" C: Cycle colormap") - print(" U: Toggle tile overlay") - print(" F: Screenshot") - print(" H: Toggle help overlay") - print(" X: Exit\n") + print_controls() # Build Dataset with derived layers print("Building Dataset with terrain analysis layers...") @@ -102,15 +85,9 @@ def load_terrain(): cache_path=bldg_cache, ) - # Scale building heights to match the 0.025× terrain elevation. elev_scale = 0.025 default_height_m = 8.0 - for feat in bldg_data.get("features", []): - props = feat.get("properties", {}) - h = props.get("height", -1) - if not isinstance(h, (int, float)) or h <= 0: - h = default_height_m - props["height"] = h * elev_scale + scale_building_heights(bldg_data, elev_scale, default_height_m) mesh_cache_path = Path(__file__).parent / "capetown_buildings_mesh.npz" with warnings.catch_warnings(): @@ -184,20 +161,7 @@ def load_terrain(): cache_path=water_cache, ) - major_features = [] - minor_features = [] - body_features = [] - for f in water_data.get('features', []): - ww = (f.get('properties') or {}).get('waterway', '') - nat = (f.get('properties') or {}).get('natural', '') - if ww in _MAJOR_WATER: - major_features.append(f) - elif ww in _MINOR_WATER: - minor_features.append(f) - elif nat == 'water': - body_features.append(f) - else: - minor_features.append(f) + major_features, minor_features, body_features = classify_water_features(water_data) if major_features: major_fc = {"type": "FeatureCollection", "features": major_features} diff --git a/examples/guanajuato.py b/examples/guanajuato.py index 5ace5c9..32c5241 100644 --- a/examples/guanajuato.py +++ b/examples/guanajuato.py @@ -26,10 +26,7 @@ # Import rtxpy to register the .rtx accessor from rtxpy import fetch_dem, fetch_buildings, fetch_roads, fetch_water, fetch_firms import rtxpy - -# Water feature classification -_MAJOR_WATER = {'river', 'canal'} -_MINOR_WATER = {'stream', 'drain', 'ditch'} +from _utils import print_controls, classify_water_features, scale_building_heights def load_terrain(): @@ -69,21 +66,7 @@ def load_terrain(): # Load terrain data (downloads if needed) terrain = load_terrain() - print("\nControls:") - print(" W/S/A/D or Arrow keys: Move camera") - print(" Q/E or Page Up/Down: Move up/down") - print(" I/J/K/L: Look around") - print(" +/-: Adjust movement speed") - print(" G: Cycle overlay layers") - print(" O: Place observer (for viewshed)") - print(" V: Toggle viewshed (teal glow)") - print(" [/]: Adjust observer height") - print(" T: Toggle shadows") - print(" C: Cycle colormap") - print(" U: Toggle tile overlay") - print(" F: Screenshot") - print(" H: Toggle help overlay") - print(" X: Exit\n") + print_controls() # Build Dataset with derived layers print("Building Dataset with terrain analysis layers...") @@ -107,17 +90,9 @@ def load_terrain(): cache_path=bldg_cache, ) - # Scale building heights to match the 0.025× terrain elevation. - # MS data has height in metres (-1 = unknown); replace unknowns - # with a reasonable default and apply the same scale factor. elev_scale = 0.025 default_height_m = 8.0 - for feat in bldg_data.get("features", []): - props = feat.get("properties", {}) - h = props.get("height", -1) - if not isinstance(h, (int, float)) or h <= 0: - h = default_height_m - props["height"] = h * elev_scale + scale_building_heights(bldg_data, elev_scale, default_height_m) mesh_cache_path = Path(__file__).parent / "guanajuato_buildings_mesh.npz" with warnings.catch_warnings(): @@ -191,20 +166,7 @@ def load_terrain(): cache_path=water_cache, ) - major_features = [] - minor_features = [] - body_features = [] - for f in water_data.get('features', []): - ww = (f.get('properties') or {}).get('waterway', '') - nat = (f.get('properties') or {}).get('natural', '') - if ww in _MAJOR_WATER: - major_features.append(f) - elif ww in _MINOR_WATER: - minor_features.append(f) - elif nat == 'water': - body_features.append(f) - else: - minor_features.append(f) + major_features, minor_features, body_features = classify_water_features(water_data) if major_features: major_fc = {"type": "FeatureCollection", "features": major_features} diff --git a/examples/los_angeles.py b/examples/los_angeles.py index 179ad0e..97410e8 100644 --- a/examples/los_angeles.py +++ b/examples/los_angeles.py @@ -24,10 +24,7 @@ from rtxpy import fetch_dem, fetch_buildings, fetch_roads, fetch_water, fetch_firms import rtxpy - -# Water feature classification -_MAJOR_WATER = {'river', 'canal'} -_MINOR_WATER = {'stream', 'drain', 'ditch'} +from _utils import print_controls, classify_water_features, scale_building_heights # Los Angeles bounding box (WGS84) # Focused area covering DTLA, Echo Park, Silver Lake, Griffith Park, @@ -71,21 +68,7 @@ def load_terrain(): # Load terrain data (downloads if needed) terrain = load_terrain() - print("\nControls:") - print(" W/S/A/D or Arrow keys: Move camera") - print(" Q/E or Page Up/Down: Move up/down") - print(" I/J/K/L: Look around") - print(" +/-: Adjust movement speed") - print(" G: Cycle overlay layers") - print(" O: Place observer (for viewshed)") - print(" V: Toggle viewshed (teal glow)") - print(" [/]: Adjust observer height") - print(" T: Toggle shadows") - print(" C: Cycle colormap") - print(" U: Toggle tile overlay") - print(" F: Screenshot") - print(" H: Toggle help overlay") - print(" X: Exit\n") + print_controls() # Build Dataset with derived layers print("Building Dataset with terrain analysis layers...") @@ -109,17 +92,9 @@ def load_terrain(): cache_path=bldg_cache, ) - # Scale building heights to match the 0.5× terrain elevation. - # MS data has height in metres (-1 = unknown); replace unknowns - # with a reasonable default and apply the same scale factor. elev_scale = 0.5 default_height_m = 8.0 - for feat in bldg_data.get("features", []): - props = feat.get("properties", {}) - h = props.get("height", -1) - if not isinstance(h, (int, float)) or h <= 0: - h = default_height_m - props["height"] = h * elev_scale + scale_building_heights(bldg_data, elev_scale, default_height_m) mesh_cache_path = Path(__file__).parent / "los_angeles_buildings_mesh.npz" with warnings.catch_warnings(): @@ -193,20 +168,7 @@ def load_terrain(): cache_path=water_cache, ) - major_features = [] - minor_features = [] - body_features = [] - for f in water_data.get('features', []): - ww = (f.get('properties') or {}).get('waterway', '') - nat = (f.get('properties') or {}).get('natural', '') - if ww in _MAJOR_WATER: - major_features.append(f) - elif ww in _MINOR_WATER: - minor_features.append(f) - elif nat == 'water': - body_features.append(f) - else: - minor_features.append(f) + major_features, minor_features, body_features = classify_water_features(water_data) if major_features: major_fc = {"type": "FeatureCollection", "features": major_features} diff --git a/examples/playground.py b/examples/playground.py index 8708bc3..7d452e6 100644 --- a/examples/playground.py +++ b/examples/playground.py @@ -26,10 +26,7 @@ # Import rtxpy to register the .rtx accessor from rtxpy import fetch_dem, fetch_roads, fetch_water import rtxpy - -# Water feature classification -_MAJOR_WATER = {'river', 'canal'} -_MINOR_WATER = {'stream', 'drain', 'ditch'} +from _utils import print_controls, classify_water_features def load_terrain(): @@ -69,21 +66,7 @@ def load_terrain(): # Load terrain data (downloads if needed) terrain = load_terrain() - print("\nControls:") - print(" W/S/A/D or Arrow keys: Move camera") - print(" Q/E or Page Up/Down: Move up/down") - print(" I/J/K/L: Look around") - print(" +/-: Adjust movement speed") - print(" G: Cycle overlay layers") - print(" O: Place observer (for viewshed)") - print(" V: Toggle viewshed (teal glow)") - print(" [/]: Adjust observer height") - print(" T: Toggle shadows") - print(" C: Cycle colormap") - print(" U: Toggle tile overlay") - print(" F: Screenshot") - print(" H: Toggle help overlay") - print(" X: Exit\n") + print_controls() # Build Dataset with derived layers print("Building Dataset with terrain analysis layers...") @@ -236,20 +219,7 @@ def load_terrain(): cache_path=water_cache, ) - major_features = [] - minor_features = [] - body_features = [] - for f in water_data.get('features', []): - ww = (f.get('properties') or {}).get('waterway', '') - nat = (f.get('properties') or {}).get('natural', '') - if ww in _MAJOR_WATER: - major_features.append(f) - elif ww in _MINOR_WATER: - minor_features.append(f) - elif nat == 'water': - body_features.append(f) - else: - minor_features.append(f) + major_features, minor_features, body_features = classify_water_features(water_data) if major_features: major_fc = {"type": "FeatureCollection", "features": major_features} diff --git a/examples/rio.py b/examples/rio.py index 630bdd6..de52d0e 100644 --- a/examples/rio.py +++ b/examples/rio.py @@ -21,10 +21,7 @@ from rtxpy import fetch_dem, fetch_buildings, fetch_roads, fetch_water import rtxpy - -# Water feature classification -_MAJOR_WATER = {'river', 'canal'} -_MINOR_WATER = {'stream', 'drain', 'ditch'} +from _utils import print_controls, classify_water_features, scale_building_heights # Rio de Janeiro bounding box (WGS84) # Covers the city from Barra da Tijuca in the west to Ilha do Governador @@ -67,21 +64,7 @@ def load_terrain(): # Load terrain data (downloads if needed) terrain = load_terrain() - print("\nControls:") - print(" W/S/A/D or Arrow keys: Move camera") - print(" Q/E or Page Up/Down: Move up/down") - print(" I/J/K/L: Look around") - print(" +/-: Adjust movement speed") - print(" G: Cycle overlay layers") - print(" O: Place observer (for viewshed)") - print(" V: Toggle viewshed (teal glow)") - print(" [/]: Adjust observer height") - print(" T: Toggle shadows") - print(" C: Cycle colormap") - print(" U: Toggle tile overlay") - print(" F: Screenshot") - print(" H: Toggle help overlay") - print(" X: Exit\n") + print_controls() # Build Dataset with derived layers print("Building Dataset with terrain analysis layers...") @@ -105,15 +88,9 @@ def load_terrain(): cache_path=bldg_cache, ) - # Scale building heights to match the 0.025× terrain elevation. elev_scale = 0.025 default_height_m = 8.0 - for feat in bldg_data.get("features", []): - props = feat.get("properties", {}) - h = props.get("height", -1) - if not isinstance(h, (int, float)) or h <= 0: - h = default_height_m - props["height"] = h * elev_scale + scale_building_heights(bldg_data, elev_scale, default_height_m) mesh_cache_path = Path(__file__).parent / "rio_buildings_mesh.npz" with warnings.catch_warnings(): @@ -187,20 +164,7 @@ def load_terrain(): cache_path=water_cache, ) - major_features = [] - minor_features = [] - body_features = [] - for f in water_data.get('features', []): - ww = (f.get('properties') or {}).get('waterway', '') - nat = (f.get('properties') or {}).get('natural', '') - if ww in _MAJOR_WATER: - major_features.append(f) - elif ww in _MINOR_WATER: - minor_features.append(f) - elif nat == 'water': - body_features.append(f) - else: - minor_features.append(f) + major_features, minor_features, body_features = classify_water_features(water_data) if major_features: major_fc = {"type": "FeatureCollection", "features": major_features} diff --git a/examples/trinidad.py b/examples/trinidad.py index 8209573..839ada1 100644 --- a/examples/trinidad.py +++ b/examples/trinidad.py @@ -22,10 +22,7 @@ from rtxpy import fetch_dem, fetch_buildings, fetch_roads, fetch_water, fetch_firms import rtxpy - -# Water feature classification -_MAJOR_WATER = {'river', 'canal'} -_MINOR_WATER = {'stream', 'drain', 'ditch'} +from _utils import print_controls, classify_water_features, scale_building_heights def load_terrain(): @@ -62,21 +59,7 @@ def load_terrain(): # Load terrain data (downloads if needed) terrain = load_terrain() - print("\nControls:") - print(" W/S/A/D or Arrow keys: Move camera") - print(" Q/E or Page Up/Down: Move up/down") - print(" I/J/K/L: Look around") - print(" +/-: Adjust movement speed") - print(" G: Cycle overlay layers") - print(" O: Place observer (for viewshed)") - print(" V: Toggle viewshed (teal glow)") - print(" [/]: Adjust observer height") - print(" T: Toggle shadows") - print(" C: Cycle colormap") - print(" U: Toggle tile overlay") - print(" F: Screenshot") - print(" H: Toggle help overlay") - print(" X: Exit\n") + print_controls() # Build Dataset with derived layers print("Building Dataset with terrain analysis layers...") @@ -100,17 +83,9 @@ def load_terrain(): cache_path=bldg_cache, ) - # Scale building heights to match the 0.025× terrain elevation. - # MS data has height in metres (-1 = unknown); replace unknowns - # with a reasonable default and apply the same scale factor. elev_scale = 0.025 default_height_m = 8.0 - for feat in bldg_data.get("features", []): - props = feat.get("properties", {}) - h = props.get("height", -1) - if not isinstance(h, (int, float)) or h <= 0: - h = default_height_m - props["height"] = h * elev_scale + scale_building_heights(bldg_data, elev_scale, default_height_m) mesh_cache_path = Path(__file__).parent / "trinidad_buildings_mesh.npz" with warnings.catch_warnings(): @@ -184,20 +159,7 @@ def load_terrain(): cache_path=water_cache, ) - major_features = [] - minor_features = [] - body_features = [] - for f in water_data.get('features', []): - ww = (f.get('properties') or {}).get('waterway', '') - nat = (f.get('properties') or {}).get('natural', '') - if ww in _MAJOR_WATER: - major_features.append(f) - elif ww in _MINOR_WATER: - minor_features.append(f) - elif nat == 'water': - body_features.append(f) - else: - minor_features.append(f) + major_features, minor_features, body_features = classify_water_features(water_data) if major_features: major_fc = {"type": "FeatureCollection", "features": major_features}