Horizontal Plane Mapper#

Hint

Even if in the mapper CRS all Z-coordinates are the same, transforming results back to the source CRS can change Z. Different ellipsoids and vertical shift grids (geoids) can apply. See pyproj / PROJ grid hints.

CRS and projections#

This mapper can be used with most projected 3D CRSs (UTM, Lambert Conformal Conic, national grids, …). Coordinate transformations are handled via Coordinate Transformations.

When calling mapping methods, pass either crs_s=... (source CRS) or a pre-built transformer=....

Important

The ray/plane intersection is computed with Euclidean math and a fixed plane normal (0, 0, 1). For physically meaningful results, the ray input CRS must be a meter-based 3D CRS where Z represents height.

Do not intersect rays directly in lon/lat degrees; transform to a projected CRS first and convert back if needed.

Get heights from coordinates#

MappingHorizontalPlane provides constant-height results via weitsicht.MappingHorizontalPlane.map_heights_from_coordinates(). Conceptually, it transforms input XY into the mapper CRS, injects a constant Z, then transforms back.

Following steps are performed:

  1. Input coordinates are transformed to the mapper CRS.

  2. The plane altitude (constant height) is assigned to the Z-component in the mapper CRS.

  3. These coordinates in the mapper CRS with the changed Z-component are transformed back to the source CRS.

As long as the coordinate transformation is working this will always return coordinates.

This process assumes that for both systems (source CRS and mapper CRS) the normal vector is vertical in both systems at all coordinates.

Heights from coordinates workflow

Workflow to get heights for coordinates from the horizontal mapper#

Get intersection of ray and horizontal plane#

Ray intersection is implemented in weitsicht.MappingHorizontalPlane.map_coordinates_from_rays(). The ray is defined by a start point and a direction vector, both given in the same source CRS.

Currently this is implemented as following:

  1. For the ray start position (source CRS), a corresponding plane point is derived using weitsicht.MappingHorizontalPlane.map_heights_from_coordinates().

  2. That plane point and a normal vector (0, 0, 1) are used to compute a line/plane intersection.

  3. If a CRS transformation is involved, the plane point is refined iteratively at the current intersection location to reduce small vertical inconsistencies introduced by vertical transformations (see Vertical transformations and “horizontal planes”).

  4. Parallelity is checked.

  5. The intersection direction is checked; intersections “behind” the ray direction are rejected.

There is always an intersection between a line and a plane (unless they are parallel).

Ray intersection on a horizontal plane

Ray intersection on a horizontal plane (conceptual).#

Note

Without a CRS transformation, the intersection is a single ray–plane solve.

If a CRS transformation is involved and contains vertical grids/shifts, then a constant-height plane in the mapper CRS can map to a slightly varying height in the source CRS depending on XY. MappingHorizontalPlane therefore refines the plane point iteratively until the intersection stabilizes (currently 1 mm tolerance or 20 iterations).