Mappers#

Mappers are the ground / surface model used by images and batches to intersect rays or sample heights. They work in their own CRS and expose a small, consistent API.

Core interfaces#

  • map_coordinates_from_rays(ray_vectors, ray_starts, crs_s=None, transformer=None): intersects rays with the surface and returns a MappingResult (coordinates + mask + issues).

  • map_heights_from_coordinates(coordinates, crs_s=None, transformer=None): returns 3D coordinates with heights injected/interpolated from the surface model.

  • param_dict / from_dict: serialize and reconstruct mapper configuration.

  • crs / crs_wkt: mapper CRS; inputs are transformed automatically when crs_s differs.

Available mappers#

  • MappingHorizontalPlane: constant-altitude plane; fastest for flat scenes.

  • MappingRaster: samples a raster DEM/DSM (rasterio-based); good for terrain.

  • MappingGeorefArray: in-memory height grid (often produced via MappingRaster.load_window(...)).

  • MappingTrimesh: ray intersections with a 3D mesh via trimesh; supports complex surfaces.

When to choose which#

  • Flat site / sea surface / quick tests → MappingHorizontalPlane with a known altitude.

  • Terrain from DEM/DSM → MappingRaster (disk-backed; bilinear height sampling).

  • In-memory raster subset → MappingGeorefArray (fast repeated operations, no disk IO once loaded).

  • Detailed surface mesh → MappingTrimesh (ray–triangle hits, optional coordinate shift).

Using the core methods#

# Rays (e.g. from ImagePerspective.pixel_to_ray_vector) to ground
result = mapper.map_coordinates_from_rays(ray_vecs, ray_starts, crs_s=image.crs)
if result.ok:
    ground_pts = result.coordinates[result.mask]
else:
    print("Failed:", result.error, "Issues:", result.issues)

# Heights at XY positions
result_h = mapper.map_heights_from_coordinates(xy, crs_s=target_crs)
if result_h.ok:
    pts_with_z = result_h.coordinates[result_h.mask]

Notes & tips#

  • Align mapper CRS with your images when possible; otherwise always pass crs_s (or reuse a transformer).

  • Expect partial success: check result.ok and filter with result.mask; inspect issues for warnings like WRONG_DIRECTION, OUTSIDE_RASTER, RASTER_NO_DATA, MAX_ITTERATION, or NO_INTERSECTION.

  • Heavy mappers (rasters/meshes) typically open external resources at construction—reuse them across many images/batches.

    • For MappingRaster, raster data is read lazily during runtime operations by default; it is only loaded into memory when you enable preload_full_raster / preload_window or call load_window().

Raster backend selection (MappingRaster)#

MappingRaster has two internal backends:

  • disk-backed (default): samples/intersects directly from the raster file via rasterio

  • in-memory (optional): a cached MappingGeorefArray stored in mapper.georef_array

When an in-memory backend is present, MappingRaster.map_coordinates_from_rays and MappingRaster.map_heights_from_coordinates automatically delegate to it.

Load a cached window / full raster#

from pyproj import CRS
from weitsicht import MappingRaster

mapper = MappingRaster(
    raster_path="dem.tif",
    crs=CRS.from_epsg(32633),
    preload_window=(600_000, 5_340_000, 602_000, 5_342_000),
)

# Uses MappingGeorefArray because a window is cached
res = mapper.map_heights_from_coordinates([[601_000, 5_341_000]], crs_s=mapper.crs)

# You can also cache after construction (returns the MappingGeorefArray)
georef = mapper.load_window((600_000, 5_340_000, 602_000, 5_342_000))
assert georef is mapper.georef_array

# Optional helpers
backend = mapper.backend          # MappingGeorefArray if loaded, else MappingRaster
georef2 = mapper.georef_mapper    # raises if not loaded

Important

If you cache only a window, mapping operations are limited to that window extent. Points/rays outside the cached extent will return Issue.OUTSIDE_RASTER.