Roads Module

Road network visualization using the data layer pipeline.

This module provides efficient road rendering by rasterizing OpenStreetMap road networks as geographic data layers. Roads are automatically aligned with the DEM through the transform pipeline, handling reprojection and downsampling correctly.

Much more efficient than creating individual Blender objects - rasterizes roads in ~5 seconds and automatically handles coordinate transformations.

Quick Start

Simple API for adding roads to terrain:

from src.terrain.roads import add_roads_layer
from examples.detroit_roads import get_roads

# Get roads from OpenStreetMap
roads_geojson = get_roads(bbox)

# Add as a data layer (automatic alignment)
add_roads_layer(
    terrain=terrain,
    roads_geojson=roads_geojson,
    bbox=bbox,  # (south, west, north, east) in WGS84
    road_types=['motorway', 'trunk', 'primary'],
    road_width=3,
    colormap_name="viridis"
)

See Combined Render: Full-Featured Example for full example.

Core Functions

src.terrain.roads.add_roads_layer(terrain, roads_geojson, bbox, resolution=30.0, road_width_pixels=3)[source]

Add roads as a data layer to terrain with automatic coordinate alignment.

This is the high-level API for road integration. Roads are rasterized to a grid with proper geographic metadata and added as a data layer. The library’s data layer pipeline ensures proper alignment even if terrain is downsampled or reprojected.

To color roads, use the multi-overlay color mapping system:

roads_geojson = get_roads(bbox)
terrain.add_roads_layer(terrain, roads_geojson, bbox, road_width_pixels=3)
terrain.set_multi_color_mapping(
    base_colormap=lambda dem: elevation_colormap(dem, 'michigan'),
    base_source_layers=['dem'],
    overlays=[{
        'colormap': road_colormap,
        'source_layers': ['roads'],
        'priority': 10,
    }],
)
terrain.compute_colors()
Parameters:
  • terrain – Terrain object (must have DEM data layer)

  • roads_geojson (Dict[str, Any]) – GeoJSON FeatureCollection with road LineStrings

  • bbox (Tuple[float, float, float, float]) – Bounding box as (south, west, north, east) in WGS84 degrees

  • resolution (float) – Pixel size in meters for rasterization (default: 30.0)

  • road_width_pixels (int) – Width of roads in raster pixels (default: 3). Higher values make roads more visible. At 30m resolution, 3 pixels ≈ 90m visual width.

Raises:

ValueError – If terrain missing DEM data layer

Return type:

None

High-level function to rasterize and add roads as a data layer. Used in Combined Render: Full-Featured Example.

src.terrain.roads.rasterize_roads_to_layer(roads_geojson, bbox, resolution=30.0, road_width_pixels=3)[source]

Rasterize GeoJSON roads to a layer grid with proper geographic transform.

Converts vector road data (GeoJSON LineStrings) to a raster grid where each pixel represents road presence/type. The result includes an Affine transform in WGS84 (EPSG:4326) coordinates for proper geographic alignment.

This is the key function that treats roads as a data layer - the output can be added to terrain via add_data_layer() and will automatically align to the DEM through reprojection and resampling.

Parameters:
  • roads_geojson (Dict[str, Any]) – GeoJSON FeatureCollection with road LineStrings

  • bbox (Tuple[float, float, float, float]) – Bounding box as (south, west, north, east) in WGS84 degrees

  • resolution (float) – Pixel size in meters (default: 30.0). At Detroit latitude, ~30m/pixel gives good detail without excessive memory use.

  • road_width_pixels (int) – Width of roads in raster pixels (default: 3). Draws roads with thickness instead of 1-pixel lines. Use 1 for thin roads, 3-5 for more visible roads. At 30m resolution, 3 pixels ≈ 90m visual width.

Returns:

  • road_grid: 2D array of uint8, values 0=no road, 1-4=road type (motorway > trunk > primary > secondary)

  • road_transform: rasterio.Affine transform mapping pixels to WGS84 coordinates

Return type:

Tuple of

Rasterize road geometries to a grid with proper geographic transform.

Returns road grid and Affine transform in WGS84 (EPSG:4326). Use with add_data_layer() to add to terrain.

Example:

road_grid, road_transform = rasterize_roads_to_layer(
    roads_geojson,
    bbox=(south, west, north, east),
    resolution=30.0,  # 30m pixels
    road_types=['motorway', 'trunk'],
    road_width=3
)

terrain.add_data_layer(
    "roads", road_grid, road_transform,
    "EPSG:4326", target_layer="dem"
)

Vertex Smoothing

src.terrain.roads.smooth_road_vertices(vertices, road_mask, y_valid, x_valid, smoothing_radius=2)[source]

Smooth Z coordinates of mesh vertices that are on roads.

Reconstructs a 2D Z grid from vertex positions, applies 2D Gaussian smoothing in grid space, then writes smoothed values back to road vertices only. Non-road vertices are unchanged.

This ensures smoothing follows the spatial layout of the road rather than the arbitrary vertex index order.

Parameters:
  • vertices (ndarray) – Mesh vertex positions (N, 3) array with [x, y, z] coords

  • road_mask (ndarray) – 2D array (H, W) where >0.5 indicates road pixels

  • y_valid (ndarray) – Array (N,) of y indices mapping vertices to road_mask rows

  • x_valid (ndarray) – Array (N,) of x indices mapping vertices to road_mask columns

  • smoothing_radius (int) – Gaussian smoothing sigma (default: 2, use 0 to disable)

Returns:

Modified vertices array with smoothed Z values on roads. X and Y coordinates are never modified.

Return type:

ndarray

Smooth road elevations to eliminate sudden height changes. Used in Combined Render: Full-Featured Example.

Example:

smooth_road_vertices(
    terrain, road_mask,
    kernel_size=5,
    iterations=2
)
src.terrain.roads.offset_road_vertices(vertices, road_mask, y_valid, x_valid, offset=0.0)[source]

Offset Z coordinates of mesh vertices that are on roads by a fixed amount.

A simpler alternative to smooth_road_vertices. Raises or lowers all road vertices by a constant offset, making roads visually distinct from terrain.

Parameters:
  • vertices (ndarray) – Mesh vertex positions (N, 3) array with [x, y, z] coords

  • road_mask (ndarray) – 2D array (H, W) where >0.5 indicates road pixels

  • y_valid (ndarray) – Array (N,) of y indices mapping vertices to road_mask rows

  • x_valid (ndarray) – Array (N,) of x indices mapping vertices to road_mask columns

  • offset (float) – Z offset to apply to road vertices. Positive = raise, negative = lower. Default: 0.0 (no change)

Returns:

Modified vertices array with offset Z values on roads. X and Y coordinates are never modified.

Return type:

ndarray

Lower road vertices to create embankment effect.

Example:

offset_road_vertices(
    terrain, road_mask,
    offset_meters=-2.0  # Lower by 2 meters
)

DEM Smoothing

src.terrain.roads.smooth_dem_along_roads(dem, road_mask, smoothing_radius=2)[source]

Smooth the DEM along roads to reduce elevation detail (GPU-accelerated).

Applies Gaussian smoothing only to road pixels. Non-road pixels are unchanged. The smoothing kernel should be about half the road width.

Uses PyTorch GPU acceleration when available (6x speedup on CUDA).

Parameters:
  • dem (ndarray) – 2D array of elevation values

  • road_mask (ndarray) – 2D boolean or float array where >0.5 = road

  • smoothing_radius (int) – Radius for Gaussian smoothing (default: 2 pixels)

Returns:

Smoothed DEM array (same shape as input)

Return type:

ndarray

Smooth DEM elevation data along roads before mesh creation.

Mask Operations

src.terrain.roads.smooth_road_mask(road_mask, sigma=1.0)[source]

Apply Gaussian blur to road mask for anti-aliased edges (GPU-accelerated).

The Bresenham line algorithm creates stair-step (aliased) edges. Applying Gaussian smoothing creates soft anti-aliased boundaries that render more smoothly, especially after the mask goes through resampling.

Uses PyTorch GPU acceleration when available (6x speedup on CUDA).

Parameters:
  • road_mask (ndarray) – 2D array of road values (0=no road, >0=road)

  • sigma (float) – Gaussian blur sigma in pixels (default: 1.0). Higher values = softer edges. Typical range: 0.5-2.0. - 0.5: Minimal softening - 1.0: Standard anti-aliasing (recommended) - 2.0: Very soft/blurry edges

Returns:

Smoothed road mask as float32 array. Values are now continuous (not binary) and may need thresholding if binary mask is needed.

Return type:

ndarray

Smooth road mask to avoid harsh transitions.

Example:

smoothed_mask = smooth_road_mask(
    road_mask,
    kernel_size=5,
    threshold=0.3
)

Colormaps

src.terrain.roads.road_colormap(road_grid, score=None)[source]

Map roads to a distinctive red color for special material treatment.

The red color (180, 30, 30) is used as a marker so the Blender material shader can identify road pixels and apply a glassy/emissive effect.

Parameters:
  • road_grid – 2D array of road values (0=no road, >0=road)

  • score – Unused, kept for API compatibility.

Returns:

Array of RGB colors with shape (height, width, 3) as uint8

Map roads to distinctive marker color for shader detection.

src.terrain.roads.get_viridis_colormap()[source]

Get viridis colormap function.

Returns:

Function that maps normalized values (0-1) to (R, G, B)

Get viridis colormap for road visualization.