"""
HydroCouple geospatial abstract base classes.
Python ABC mirrors of the C++ HydroCouple v2.0.0 interfaces defined in
``hydrocouplespatial.h``: OGC Simple Features geometry types, spatial
reference systems, mesh/network structures with bulk structure-of-arrays
views, rasters, regular grids, and the spatial component data items.
Bulk accessors (:class:`IMeshView`, grid coordinate arrays) return NumPy
arrays and are the accessors partitioners, interpolating adapters, IO
writers, and device staging must use; per-entity object accessors are a
convenience for spot queries and editing.
"""
from __future__ import annotations
from abc import ABC, abstractmethod
from enum import IntEnum
from typing import TYPE_CHECKING, Optional
from hydrocouple.core import (
DistanceUnits,
IComponentDataItem,
IDimension,
IIdentity,
)
if TYPE_CHECKING:
import numpy as np
# ---------------------------------------------------------------------------
# Enums
# ---------------------------------------------------------------------------
[docs]
class MeshDataObjectType(IntEnum):
"""Part of a mesh's geometry that data corresponds to.
Mirrors C++ ``Spatial::MeshDataObjectType``.
"""
Cell = 0
Vertex = 1
Edge = 2
Face = 3
[docs]
class NetworkDataObjectType(IntEnum):
"""Part of a network that data corresponds to.
Mirrors C++ ``Spatial::NetworkDataObjectType``.
"""
Node = 0
Edge = 1
[docs]
class SpatialDataType(IntEnum):
"""Structure of per-entity values.
Mirrors C++ ``Spatial::SpatialDataType``.
"""
Scalar = 0
MultiScalar = 1
Vector = 2
Tensor = 3
[docs]
class RegularGridType(IntEnum):
"""Type of a regular grid.
Mirrors C++ ``Spatial::RegularGridType``.
"""
Cartesian = 0
Rectilinear = 1
Curvilinear = 2
[docs]
class GeometryType(IntEnum):
"""OGC geometry type codes mirroring C++ ``IGeometry::GeometryType``."""
Geometry = 0
Point = 1
LineString = 2
Polygon = 3
MultiPoint = 4
MultiLineString = 5
MultiPolygon = 6
GeometryCollection = 7
CircularString = 8
CompoundCurve = 9
CurvePolygon = 10
MultiCurve = 11
MultiSurface = 12
Curve = 13
Surface = 14
PolyhedralSurface = 15
TIN = 16
Triangle = 17
GeometryZ = 1000
PointZ = 1001
LineStringZ = 1002
PolygonZ = 1003
MultiPointZ = 1004
MultiLineStringZ = 1005
MultiPolygonZ = 1006
GeometryCollectionZ = 1007
CircularStringZ = 1008
CompoundCurveZ = 1009
CurvePolygonZ = 1010
MultiCurveZ = 1011
MultiSurfaceZ = 1012
CurveZ = 1013
SurfaceZ = 1014
PolyhedralSurfaceZ = 1015
TINZ = 1016
TriangleZ = 1017
GeometryM = 2000
PointM = 2001
LineStringM = 2002
PolygonM = 2003
MultiPointM = 2004
MultiLineStringM = 2005
MultiPolygonM = 2006
GeometryCollectionM = 2007
CircularStringM = 2008
CompoundCurveM = 2009
CurvePolygonM = 2010
MultiCurveM = 2011
MultiSurfaceM = 2012
CurveM = 2013
SurfaceM = 2014
PolyhedralSurfaceM = 2015
TINM = 2016
TriangleM = 2017
GeometryZM = 3000
PointZM = 3001
LineStringZM = 3002
PolygonZM = 3003
MultiPointZM = 3004
MultiLineStringZM = 3005
MultiPolygonZM = 3006
GeometryCollectionZM = 3007
CircularStringZM = 3008
CompoundCurveZM = 3009
CurvePolygonZM = 3010
MultiCurveZM = 3011
MultiSurfaceZM = 3012
CurveZM = 3013
SurfaceZM = 3014
PolyhedralSurfaceZM = 3015
TINZM = 3016
TriangleZM = 3017
[docs]
class RasterDataType(IntEnum):
"""Raster band element type mirroring C++ ``IRaster::RasterDataType``."""
Unknown = 0
Byte = 1
UInt16 = 2
Int16 = 3
UInt32 = 4
Int32 = 5
Float32 = 6
Float64 = 7
CInt16 = 8
CInt32 = 9
CFloat32 = 10
CFloat64 = 11
ARGB32 = 12
ARGB32_Premultiplied = 13
# ---------------------------------------------------------------------------
# Spatial reference system and envelope
# ---------------------------------------------------------------------------
[docs]
class ISpatialReferenceSystem(ABC):
"""Spatial reference system of a geometric object.
Mirrors C++ ``Spatial::ISpatialReferenceSystem``.
"""
@property
@abstractmethod
def auth_srid(self) -> int:
"""The authority-specific spatial reference id (e.g. EPSG code)."""
raise NotImplementedError
@property
@abstractmethod
def auth_name(self) -> str:
"""The authority name (e.g. ``"EPSG"``)."""
raise NotImplementedError
@property
@abstractmethod
def sr_text(self) -> str:
"""Well-known text representation of the SRS."""
raise NotImplementedError
@property
@abstractmethod
def distance_units(self) -> DistanceUnits:
"""The measurement distance units of the SRS."""
raise NotImplementedError
[docs]
class IEnvelope(ABC):
"""Axis-aligned bounding box.
Mirrors C++ ``Spatial::IEnvelope``.
"""
@property
@abstractmethod
def min_x(self) -> float:
"""Minimum x."""
raise NotImplementedError
@property
@abstractmethod
def max_x(self) -> float:
"""Maximum x."""
raise NotImplementedError
@property
@abstractmethod
def min_y(self) -> float:
"""Minimum y."""
raise NotImplementedError
@property
@abstractmethod
def max_y(self) -> float:
"""Maximum y."""
raise NotImplementedError
@property
@abstractmethod
def min_z(self) -> float:
"""Minimum z."""
raise NotImplementedError
@property
@abstractmethod
def max_z(self) -> float:
"""Maximum z."""
raise NotImplementedError
# ---------------------------------------------------------------------------
# Geometry hierarchy (OGC Simple Features)
# ---------------------------------------------------------------------------
[docs]
class IGeometry(ABC):
"""Base OGC geometry.
Mirrors C++ ``Spatial::IGeometry``.
"""
@property
@abstractmethod
def id(self) -> str:
"""Id of the geometry."""
raise NotImplementedError
@property
@abstractmethod
def index(self) -> int:
"""Index of the geometry within a collection."""
raise NotImplementedError
@property
@abstractmethod
def dimension(self) -> int:
"""Topological dimension: 0 points, 1 lines, 2 surfaces."""
raise NotImplementedError
@property
@abstractmethod
def coordinate_dimension(self) -> int:
"""Dimension of the coordinates (2 or 3; 0 for an empty point)."""
raise NotImplementedError
@property
@abstractmethod
def geometry_type(self) -> GeometryType:
"""The instantiable OGC subtype of this geometry."""
raise NotImplementedError
@property
@abstractmethod
def spatial_reference_system(self) -> ISpatialReferenceSystem:
"""The SRS of this geometry."""
raise NotImplementedError
@property
@abstractmethod
def envelope(self) -> IEnvelope:
"""The bounding envelope of this geometry."""
raise NotImplementedError
[docs]
@abstractmethod
def get_wkt(self) -> str:
"""Well-known text representation."""
raise NotImplementedError
[docs]
@abstractmethod
def get_wkb(self) -> bytes:
"""Well-known binary representation."""
raise NotImplementedError
@property
@abstractmethod
def is_empty(self) -> bool:
"""Whether this geometry is the empty geometry."""
raise NotImplementedError
@property
@abstractmethod
def is_simple(self) -> bool:
"""Whether this geometry has no anomalous points."""
raise NotImplementedError
@property
@abstractmethod
def is_3d(self) -> bool:
"""Whether this geometry has z coordinates."""
raise NotImplementedError
@property
@abstractmethod
def is_measured(self) -> bool:
"""Whether this geometry has m values."""
raise NotImplementedError
@property
@abstractmethod
def boundary(self) -> "IGeometry":
"""The closure of the combinatorial boundary."""
raise NotImplementedError
# -- Spatial predicates -------------------------------------------------
[docs]
@abstractmethod
def equals(self, geom: "IGeometry") -> bool:
"""Spatial equality."""
raise NotImplementedError
[docs]
@abstractmethod
def disjoint(self, geom: "IGeometry") -> bool:
"""Spatially disjoint."""
raise NotImplementedError
[docs]
@abstractmethod
def intersects(self, geom: "IGeometry") -> bool:
"""Spatially intersects."""
raise NotImplementedError
[docs]
@abstractmethod
def touches(self, geom: "IGeometry") -> bool:
"""Spatially touches."""
raise NotImplementedError
[docs]
@abstractmethod
def crosses(self, geom: "IGeometry") -> bool:
"""Spatially crosses."""
raise NotImplementedError
[docs]
@abstractmethod
def within(self, geom: "IGeometry") -> bool:
"""Spatially within."""
raise NotImplementedError
[docs]
@abstractmethod
def contains(self, geom: "IGeometry") -> bool:
"""Spatially contains."""
raise NotImplementedError
[docs]
@abstractmethod
def overlaps(self, geom: "IGeometry") -> bool:
"""Spatially overlaps."""
raise NotImplementedError
[docs]
@abstractmethod
def relate(self, geom: "IGeometry") -> bool:
"""DE-9IM relation test."""
raise NotImplementedError
# -- Measures and constructive operations --------------------------------
[docs]
@abstractmethod
def locate_along(self, value: float) -> "IGeometry":
"""The m-locate-along geometry."""
raise NotImplementedError
[docs]
@abstractmethod
def locate_between(self, m_start: float, m_end: float) -> "IGeometry":
"""The m-locate-between geometry."""
raise NotImplementedError
[docs]
@abstractmethod
def distance(self, geom: "IGeometry") -> float:
"""Shortest distance to another geometry."""
raise NotImplementedError
[docs]
@abstractmethod
def buffer(self, buffer_distance: float) -> "IGeometry":
"""All points within the given distance of this geometry."""
raise NotImplementedError
[docs]
@abstractmethod
def convex_hull(self) -> "IGeometry":
"""The convex hull."""
raise NotImplementedError
[docs]
@abstractmethod
def intersection(self, geom: "IGeometry") -> "IGeometry":
"""Point-set intersection."""
raise NotImplementedError
[docs]
@abstractmethod
def union(self, geom: "IGeometry") -> "IGeometry":
"""Point-set union (C++ ``unionG``)."""
raise NotImplementedError
[docs]
@abstractmethod
def difference(self, geom: "IGeometry") -> "IGeometry":
"""Point-set difference."""
raise NotImplementedError
[docs]
@abstractmethod
def symmetric_difference(self, geom: "IGeometry") -> "IGeometry":
"""Point-set symmetric difference."""
raise NotImplementedError
[docs]
class IGeometryCollection(IGeometry):
"""A collection of geometries. Mirrors C++ ``IGeometryCollection``."""
@property
@abstractmethod
def geometry_count(self) -> int:
"""Number of geometries in the collection."""
raise NotImplementedError
[docs]
@abstractmethod
def geometry(self, index: int) -> IGeometry:
"""The geometry at the given index."""
raise NotImplementedError
[docs]
class IPoint(IGeometry):
"""A 0-dimensional point. Mirrors C++ ``IPoint``."""
@property
@abstractmethod
def x(self) -> float:
"""x coordinate."""
raise NotImplementedError
@property
@abstractmethod
def y(self) -> float:
"""y coordinate."""
raise NotImplementedError
@property
@abstractmethod
def z(self) -> float:
"""z coordinate (when :attr:`IGeometry.is_3d`)."""
raise NotImplementedError
@property
@abstractmethod
def m(self) -> float:
"""m value (when :attr:`IGeometry.is_measured`)."""
raise NotImplementedError
[docs]
class IMultiPoint(IGeometryCollection):
"""A collection of points. Mirrors C++ ``IMultiPoint``."""
[docs]
@abstractmethod
def point(self, index: int) -> IPoint:
"""The point at the given index."""
raise NotImplementedError
[docs]
class IVertex(IPoint):
"""A point participating in topology. Mirrors C++ ``IVertex``."""
@property
@abstractmethod
def vertex_index(self) -> int:
"""Unique vertex index (C++ ``index()``; renamed to avoid clashing
with :attr:`IGeometry.index`)."""
raise NotImplementedError
@property
@abstractmethod
def edge(self) -> "IEdge":
"""An arbitrary outgoing edge of this vertex."""
raise NotImplementedError
[docs]
class ICurve(IGeometry):
"""A 1-dimensional geometry. Mirrors C++ ``ICurve``."""
@property
@abstractmethod
def length(self) -> float:
"""Curve length."""
raise NotImplementedError
@property
@abstractmethod
def start_point(self) -> IPoint:
"""First point of the curve."""
raise NotImplementedError
@property
@abstractmethod
def end_point(self) -> IPoint:
"""Last point of the curve."""
raise NotImplementedError
@property
@abstractmethod
def is_closed(self) -> bool:
"""Whether start and end points coincide."""
raise NotImplementedError
@property
@abstractmethod
def is_ring(self) -> bool:
"""Whether the curve is closed and simple."""
raise NotImplementedError
[docs]
class IMultiCurve(IGeometryCollection):
"""A collection of curves. Mirrors C++ ``IMultiCurve``."""
@property
@abstractmethod
def is_closed(self) -> bool:
"""Whether every member curve is closed."""
raise NotImplementedError
@property
@abstractmethod
def length(self) -> float:
"""Total length of the member curves."""
raise NotImplementedError
[docs]
class ILineString(ICurve):
"""A curve with linear interpolation between points.
Mirrors C++ ``ILineString``.
"""
@property
@abstractmethod
def point_count(self) -> int:
"""Number of points in the line string."""
raise NotImplementedError
[docs]
@abstractmethod
def point(self, index: int) -> IPoint:
"""The point at the given index."""
raise NotImplementedError
[docs]
class IMultiLineString(IMultiCurve):
"""A collection of line strings. Mirrors C++ ``IMultiLineString``."""
[docs]
@abstractmethod
def line_string(self, index: int) -> ILineString:
"""The line string at the given index."""
raise NotImplementedError
[docs]
class ILine(ILineString):
"""A line string with exactly two points. Mirrors C++ ``ILine``."""
[docs]
class ILinearRing(ILineString):
"""A closed, simple line string. Mirrors C++ ``ILinearRing``."""
[docs]
class IEdge(ABC):
"""A quad-edge topology edge.
Mirrors C++ ``IEdge``. Per-entity topology navigation is a convenience
for spot queries and editing; bulk consumers must use
:class:`IMeshView`.
"""
@property
@abstractmethod
def index(self) -> int:
"""Unique edge index."""
raise NotImplementedError
@property
@abstractmethod
def orig(self) -> Optional[IVertex]:
"""Origin vertex, or ``None`` if unknown."""
raise NotImplementedError
@property
@abstractmethod
def dest(self) -> Optional[IVertex]:
"""Destination vertex, or ``None`` if unknown."""
raise NotImplementedError
@property
@abstractmethod
def left(self) -> Optional["IPolygon"]:
"""Left face, or ``None`` if unknown."""
raise NotImplementedError
@property
@abstractmethod
def right(self) -> Optional["IPolygon"]:
"""Right face, or ``None`` if unknown."""
raise NotImplementedError
@property
@abstractmethod
def face(self) -> Optional["IPolygon"]:
"""Target face if dual, else ``None``."""
raise NotImplementedError
@property
@abstractmethod
def rot(self) -> "IEdge":
"""Dual edge, right-to-left."""
raise NotImplementedError
@property
@abstractmethod
def inv_rot(self) -> "IEdge":
"""Dual edge, left-to-right."""
raise NotImplementedError
@property
@abstractmethod
def sym(self) -> "IEdge":
"""The edge from dest to orig."""
raise NotImplementedError
@property
@abstractmethod
def orig_next(self) -> "IEdge":
"""Next ccw edge around the origin."""
raise NotImplementedError
@property
@abstractmethod
def orig_prev(self) -> "IEdge":
"""Next cw edge around the origin."""
raise NotImplementedError
@property
@abstractmethod
def dest_next(self) -> "IEdge":
"""Next ccw edge around the destination."""
raise NotImplementedError
@property
@abstractmethod
def dest_prev(self) -> "IEdge":
"""Next cw edge around the destination."""
raise NotImplementedError
@property
@abstractmethod
def left_next(self) -> "IEdge":
"""Ccw edge around the left face after this edge."""
raise NotImplementedError
@property
@abstractmethod
def left_prev(self) -> "IEdge":
"""Ccw edge around the left face before this edge."""
raise NotImplementedError
@property
@abstractmethod
def right_next(self) -> "IEdge":
"""Ccw edge around the right face after this edge."""
raise NotImplementedError
@property
@abstractmethod
def right_prev(self) -> "IEdge":
"""Ccw edge around the right face before this edge."""
raise NotImplementedError
[docs]
class ISurface(IGeometry):
"""A 2-dimensional geometry. Mirrors C++ ``ISurface``."""
@property
@abstractmethod
def area(self) -> float:
"""Surface area."""
raise NotImplementedError
@property
@abstractmethod
def centroid(self) -> IPoint:
"""Mathematical centroid (not necessarily on the surface)."""
raise NotImplementedError
@property
@abstractmethod
def point_on_surface(self) -> IPoint:
"""A point guaranteed to be on the surface."""
raise NotImplementedError
@property
@abstractmethod
def boundary_multi_curve(self) -> IMultiCurve:
"""The boundary curves of the surface."""
raise NotImplementedError
[docs]
class IMultiSurface(IGeometryCollection):
"""A collection of surfaces. Mirrors C++ ``IMultiSurface``."""
@property
@abstractmethod
def area(self) -> float:
"""Total area of the member surfaces."""
raise NotImplementedError
@property
@abstractmethod
def centroid(self) -> IPoint:
"""Mathematical centroid."""
raise NotImplementedError
@property
@abstractmethod
def point_on_surface(self) -> IPoint:
"""A point guaranteed to be on one of the member surfaces."""
raise NotImplementedError
[docs]
class IPolygon(ISurface):
"""A planar surface with an exterior ring and interior rings.
Mirrors C++ ``IPolygon``.
"""
@property
@abstractmethod
def exterior_ring(self) -> ILineString:
"""The exterior boundary ring."""
raise NotImplementedError
@property
@abstractmethod
def interior_ring_count(self) -> int:
"""Number of interior rings (holes)."""
raise NotImplementedError
[docs]
@abstractmethod
def interior_ring(self, index: int) -> ILineString:
"""The interior ring at the given index."""
raise NotImplementedError
@property
@abstractmethod
def edge(self) -> Optional[IEdge]:
"""An arbitrary boundary edge, when the polygon participates in
topology."""
raise NotImplementedError
@property
@abstractmethod
def polyhedral_surface(self) -> Optional["IPolyhedralSurface"]:
"""The polyhedral surface this polygon is a patch of, if any."""
raise NotImplementedError
[docs]
class IMultiPolygon(IMultiSurface):
"""A collection of polygons. Mirrors C++ ``IMultiPolygon``."""
[docs]
@abstractmethod
def polygon(self, index: int) -> IPolygon:
"""The polygon at the given index."""
raise NotImplementedError
[docs]
class ITriangle(IPolygon):
"""A triangular polygon. Mirrors C++ ``ITriangle``."""
@property
@abstractmethod
def vertex1(self) -> IVertex:
"""First vertex."""
raise NotImplementedError
@property
@abstractmethod
def vertex2(self) -> IVertex:
"""Second vertex."""
raise NotImplementedError
@property
@abstractmethod
def vertex3(self) -> IVertex:
"""Third vertex."""
raise NotImplementedError
[docs]
@abstractmethod
def vertex(self, index: int) -> IVertex:
"""The vertex at the given index (0-2)."""
raise NotImplementedError
# ---------------------------------------------------------------------------
# Bulk mesh view
# ---------------------------------------------------------------------------
[docs]
class IMeshView(ABC):
"""Bulk, structure-of-arrays view of an unstructured mesh or network.
Mirrors C++ ``Spatial::IMeshView``: flat coordinate arrays plus CSR
(compressed sparse row) connectivity, deliberately congruent with the
UGRID conventions so persistence, message packing, and device staging
can consume the view without transformation. Arrays remain valid until
the underlying mesh topology or geometry changes.
"""
@property
@abstractmethod
def node_count(self) -> int:
"""Number of nodes (vertices)."""
raise NotImplementedError
@property
@abstractmethod
def edge_count(self) -> int:
"""Number of edges."""
raise NotImplementedError
@property
@abstractmethod
def face_count(self) -> int:
"""Number of faces (patches/cells); 0 for a pure network."""
raise NotImplementedError
@property
@abstractmethod
def node_x(self) -> "np.ndarray":
"""x coordinates of all nodes (float64, :attr:`node_count`)."""
raise NotImplementedError
@property
@abstractmethod
def node_y(self) -> "np.ndarray":
"""y coordinates of all nodes (float64, :attr:`node_count`)."""
raise NotImplementedError
@property
@abstractmethod
def node_z(self) -> "np.ndarray":
"""z coordinates of all nodes; empty for a 2D mesh."""
raise NotImplementedError
@property
@abstractmethod
def face_node_offsets(self) -> "np.ndarray":
"""CSR row offsets into :attr:`face_nodes` (int64,
:attr:`face_count` + 1 elements; empty for a pure network)."""
raise NotImplementedError
@property
@abstractmethod
def face_nodes(self) -> "np.ndarray":
"""Concatenated node indexes of all faces, ccw per face (int64)."""
raise NotImplementedError
@property
@abstractmethod
def edge_nodes(self) -> "np.ndarray":
"""Node index pairs of all edges (int64, ``2 * edge_count``):
edge ``e`` connects ``edge_nodes[2*e]`` and ``edge_nodes[2*e+1]``."""
raise NotImplementedError
# ---------------------------------------------------------------------------
# Network / polyhedral surface / TIN
# ---------------------------------------------------------------------------
[docs]
class INetwork(IIdentity):
"""A graph of connected vertices and edges.
Mirrors C++ ``Spatial::INetwork``.
"""
@property
@abstractmethod
def edge_count(self) -> int:
"""Number of edges in the network."""
raise NotImplementedError
[docs]
@abstractmethod
def edge(self, index: int) -> IEdge:
"""The edge at the given index (spot queries)."""
raise NotImplementedError
@property
@abstractmethod
def vertex_count(self) -> int:
"""Number of vertices in the network."""
raise NotImplementedError
[docs]
@abstractmethod
def vertex(self, index: int) -> IVertex:
"""The vertex at the given index (spot queries)."""
raise NotImplementedError
@property
@abstractmethod
def mesh_view(self) -> IMeshView:
"""Bulk structure-of-arrays view of this network."""
raise NotImplementedError
[docs]
class IPolyhedralSurface(ISurface):
"""A contiguous collection of polygon patches stitched along shared
boundary edges.
Mirrors C++ ``Spatial::IPolyhedralSurface``.
"""
@property
@abstractmethod
def patch_count(self) -> int:
"""Number of polygon patches."""
raise NotImplementedError
[docs]
@abstractmethod
def patch(self, index: int) -> IPolygon:
"""The patch at the given index (spot queries)."""
raise NotImplementedError
@property
@abstractmethod
def vertex_count(self) -> int:
"""Number of vertices."""
raise NotImplementedError
[docs]
@abstractmethod
def vertex(self, index: int) -> IVertex:
"""The vertex at the given index (spot queries)."""
raise NotImplementedError
[docs]
@abstractmethod
def bounding_polygons(self, polygon: IPolygon) -> IMultiPolygon:
"""The polygons bounding the given polygon."""
raise NotImplementedError
@property
@abstractmethod
def is_closed(self) -> bool:
"""Whether the surface is closed and therefore bounds a solid."""
raise NotImplementedError
@property
@abstractmethod
def mesh_view(self) -> IMeshView:
"""Bulk structure-of-arrays view of this surface."""
raise NotImplementedError
[docs]
class ITIN(IPolyhedralSurface):
"""A triangulated irregular network of :class:`ITriangle` patches.
Mirrors C++ ``Spatial::ITIN``.
"""
[docs]
@abstractmethod
def triangle(self, index: int) -> ITriangle:
"""The triangle at the given index."""
raise NotImplementedError
# ---------------------------------------------------------------------------
# Raster
# ---------------------------------------------------------------------------
[docs]
class IRaster(IIdentity):
"""A raster spatial feature.
Mirrors C++ ``Spatial::IRaster``.
"""
@property
@abstractmethod
def x_size(self) -> int:
"""Number of columns."""
raise NotImplementedError
@property
@abstractmethod
def y_size(self) -> int:
"""Number of rows."""
raise NotImplementedError
@property
@abstractmethod
def raster_band_count(self) -> int:
"""Number of bands."""
raise NotImplementedError
[docs]
@abstractmethod
def add_raster_band(self, data_type: RasterDataType) -> None:
"""Add a band of the given element type."""
raise NotImplementedError
@property
@abstractmethod
def spatial_reference_system(self) -> ISpatialReferenceSystem:
"""The SRS of this raster."""
raise NotImplementedError
[docs]
@abstractmethod
def get_raster_band(self, band_index: int) -> "IRasterBand":
"""The band at the given index."""
raise NotImplementedError
[docs]
class IRasterBand(IIdentity):
"""One band of an :class:`IRaster`.
Mirrors C++ ``Spatial::IRasterBand``. Block read/write is the
storage-level accessor; the canonical exchange path is the raster
component data item's hyperslab API.
"""
@property
@abstractmethod
def x_size(self) -> int:
"""Number of columns."""
raise NotImplementedError
@property
@abstractmethod
def y_size(self) -> int:
"""Number of rows."""
raise NotImplementedError
@property
@abstractmethod
def raster(self) -> IRaster:
"""The owning raster."""
raise NotImplementedError
@property
@abstractmethod
def data_type(self) -> RasterDataType:
"""Element type of this band."""
raise NotImplementedError
[docs]
@abstractmethod
def read(self, x_offset: int, y_offset: int,
x_size: int, y_size: int) -> "np.ndarray":
"""Read a block as a ``[y_size, x_size]`` array."""
raise NotImplementedError
[docs]
@abstractmethod
def write(self, x_offset: int, y_offset: int, image: "np.ndarray") -> None:
"""Write a ``[y_size, x_size]`` block."""
raise NotImplementedError
@property
@abstractmethod
def no_data(self) -> float:
"""The no-data sentinel value."""
raise NotImplementedError
# ---------------------------------------------------------------------------
# Regular grids
# ---------------------------------------------------------------------------
[docs]
class IRegularGrid2D(IIdentity):
"""A two-dimensional structured grid of nodes and cells.
Mirrors C++ ``Spatial::IRegularGrid2D``.
"""
@property
@abstractmethod
def spatial_reference_system(self) -> ISpatialReferenceSystem:
"""The SRS of this grid."""
raise NotImplementedError
@property
@abstractmethod
def grid_type(self) -> RegularGridType:
"""The type of regular grid."""
raise NotImplementedError
@property
@abstractmethod
def num_x_nodes(self) -> int:
"""Number of nodes in the x direction."""
raise NotImplementedError
@property
@abstractmethod
def num_y_nodes(self) -> int:
"""Number of nodes in the y direction."""
raise NotImplementedError
[docs]
@abstractmethod
def x_node_location(self, x_node_index: int, y_node_index: int) -> float:
"""x coordinate of a node (spot queries; bulk consumers use
:attr:`node_xs`)."""
raise NotImplementedError
[docs]
@abstractmethod
def y_node_location(self, x_node_index: int, y_node_index: int) -> float:
"""y coordinate of a node (spot queries; bulk consumers use
:attr:`node_ys`)."""
raise NotImplementedError
@property
@abstractmethod
def node_xs(self) -> "np.ndarray":
"""Bulk x coordinates of all nodes, row-major ``[y][x]`` (float64)."""
raise NotImplementedError
@property
@abstractmethod
def node_ys(self) -> "np.ndarray":
"""Bulk y coordinates of all nodes, row-major ``[y][x]`` (float64)."""
raise NotImplementedError
[docs]
@abstractmethod
def is_active(self, x_cell_index: int, y_cell_index: int) -> bool:
"""Whether a cell is active (spot queries; bulk consumers use
:attr:`active_cells`)."""
raise NotImplementedError
@property
@abstractmethod
def active_cells(self) -> "np.ndarray":
"""Bulk activity mask of all cells, row-major ``[y][x]``; nonzero
means active (uint8)."""
raise NotImplementedError
[docs]
class IRegularGrid3D(IIdentity):
"""A three-dimensional structured grid of nodes and cells.
Mirrors C++ ``Spatial::IRegularGrid3D``.
"""
@property
@abstractmethod
def spatial_reference_system(self) -> ISpatialReferenceSystem:
"""The SRS of this grid."""
raise NotImplementedError
@property
@abstractmethod
def grid_type(self) -> RegularGridType:
"""The type of regular grid."""
raise NotImplementedError
@property
@abstractmethod
def num_x_nodes(self) -> int:
"""Number of nodes in the x direction."""
raise NotImplementedError
@property
@abstractmethod
def num_y_nodes(self) -> int:
"""Number of nodes in the y direction."""
raise NotImplementedError
@property
@abstractmethod
def num_z_nodes(self) -> int:
"""Number of nodes in the z direction."""
raise NotImplementedError
[docs]
@abstractmethod
def x_node_location(self, x_node_index: int, y_node_index: int) -> float:
"""x coordinate of a node (spot queries)."""
raise NotImplementedError
[docs]
@abstractmethod
def y_node_location(self, x_node_index: int, y_node_index: int) -> float:
"""y coordinate of a node (spot queries)."""
raise NotImplementedError
[docs]
@abstractmethod
def z_node_location(self, x_node_index: int, y_node_index: int,
z_node_index: int) -> float:
"""z coordinate of a node (spot queries)."""
raise NotImplementedError
@property
@abstractmethod
def node_xs(self) -> "np.ndarray":
"""Bulk x coordinates of a horizontal layer, row-major ``[y][x]``."""
raise NotImplementedError
@property
@abstractmethod
def node_ys(self) -> "np.ndarray":
"""Bulk y coordinates of a horizontal layer, row-major ``[y][x]``."""
raise NotImplementedError
@property
@abstractmethod
def node_zs(self) -> "np.ndarray":
"""Bulk z coordinates of all nodes, row-major ``[z][y][x]``."""
raise NotImplementedError
[docs]
@abstractmethod
def is_active(self, x_cell_index: int, y_cell_index: int,
z_cell_index: int) -> bool:
"""Whether a cell is active (spot queries)."""
raise NotImplementedError
@property
@abstractmethod
def active_cells(self) -> "np.ndarray":
"""Bulk activity mask of all cells, row-major ``[z][y][x]``
(uint8)."""
raise NotImplementedError
# ---------------------------------------------------------------------------
# Spatial component data items (metadata + canonical dimension orderings;
# data access is the inherited hyperslab API)
# ---------------------------------------------------------------------------
[docs]
class IGeometryComponentDataItem(IComponentDataItem):
"""Data associated with a collection of geometries.
Mirrors C++ ``Spatial::IGeometryComponentDataItem``. Canonical
dimension ordering: the geometry dimension is dimension 0 of
:attr:`shape`; any additional dimensions follow.
"""
@property
@abstractmethod
def geometry_type(self) -> GeometryType:
"""The type of the associated geometries."""
raise NotImplementedError
@property
@abstractmethod
def geometry_count(self) -> int:
"""Number of associated geometries."""
raise NotImplementedError
[docs]
@abstractmethod
def geometry(self, geometry_index: int) -> IGeometry:
"""The geometry at the given index."""
raise NotImplementedError
@property
@abstractmethod
def geometry_dimension(self) -> IDimension:
"""The geometry dimension (dimension 0 of :attr:`shape`)."""
raise NotImplementedError
@property
@abstractmethod
def envelope(self) -> IEnvelope:
"""Envelope bounding all associated geometries."""
raise NotImplementedError
[docs]
class INetworkComponentDataItem(IComponentDataItem):
"""Data associated with the edges and/or vertices of a network.
Mirrors C++ ``Spatial::INetworkComponentDataItem``. Canonical dimension
ordering: the entity dimension selected by :attr:`network_data_type`
(edge or vertex) is dimension 0 of :attr:`shape`.
"""
@property
@abstractmethod
def network(self) -> INetwork:
"""The associated network."""
raise NotImplementedError
@property
@abstractmethod
def network_data_object_type(self) -> NetworkDataObjectType:
"""The kind of network object the values describe."""
raise NotImplementedError
@property
@abstractmethod
def network_data_type(self) -> SpatialDataType:
"""The mesh entity the values are attached to."""
raise NotImplementedError
@property
@abstractmethod
def edge_dimension(self) -> IDimension:
"""The network edge dimension."""
raise NotImplementedError
@property
@abstractmethod
def vertex_dimension(self) -> IDimension:
"""The network vertex dimension."""
raise NotImplementedError
[docs]
class IPolyhedralSurfaceComponentDataItem(IComponentDataItem):
"""Data associated with the patches, edges, or vertices of a
polyhedral surface.
Mirrors C++ ``Spatial::IPolyhedralSurfaceComponentDataItem``. Canonical
dimension ordering: the entity dimension selected by
:attr:`mesh_data_type` is dimension 0 of :attr:`shape`.
"""
@property
@abstractmethod
def mesh_data_object_type(self) -> MeshDataObjectType:
"""The kind of mesh object the values describe."""
raise NotImplementedError
@property
@abstractmethod
def mesh_data_type(self) -> SpatialDataType:
"""The mesh entity the values are attached to."""
raise NotImplementedError
@property
@abstractmethod
def polyhedral_surface(self) -> IPolyhedralSurface:
"""The associated polyhedral surface."""
raise NotImplementedError
@property
@abstractmethod
def patch_dimension(self) -> IDimension:
"""The surface patch dimension."""
raise NotImplementedError
@property
@abstractmethod
def edge_dimension(self) -> IDimension:
"""The surface edge dimension."""
raise NotImplementedError
@property
@abstractmethod
def vertex_dimension(self) -> IDimension:
"""The surface vertex dimension."""
raise NotImplementedError
[docs]
class ITINComponentDataItem(IPolyhedralSurfaceComponentDataItem):
"""A polyhedral-surface data item whose surface is a TIN.
Mirrors C++ ``Spatial::ITINComponentDataItem``.
"""
@property
@abstractmethod
def tin(self) -> ITIN:
"""The associated TIN (C++ ``TIN()``)."""
raise NotImplementedError
[docs]
class IRasterComponentDataItem(IComponentDataItem):
"""Data associated with a raster.
Mirrors C++ ``Spatial::IRasterComponentDataItem``. Canonical dimension
ordering: band is dimension 0, y (row) is dimension 1, x (column) is
dimension 2 of :attr:`shape`.
"""
@property
@abstractmethod
def raster(self) -> IRaster:
"""The associated raster."""
raise NotImplementedError
@property
@abstractmethod
def x_dimension(self) -> IDimension:
"""The column dimension (dimension 2 of :attr:`shape`)."""
raise NotImplementedError
@property
@abstractmethod
def y_dimension(self) -> IDimension:
"""The row dimension (dimension 1 of :attr:`shape`)."""
raise NotImplementedError
@property
@abstractmethod
def band_dimension(self) -> IDimension:
"""The band dimension (dimension 0 of :attr:`shape`)."""
raise NotImplementedError
[docs]
class IRegularGrid2DComponentDataItem(IComponentDataItem):
"""Data associated with the cells of a 2D regular grid.
Mirrors C++ ``Spatial::IRegularGrid2DComponentDataItem``. Canonical
dimension ordering: y-cell is dimension 0, x-cell is dimension 1 of
:attr:`shape`; optional cell edge and cell vertex dimensions follow.
"""
@property
@abstractmethod
def grid(self) -> IRegularGrid2D:
"""The associated grid."""
raise NotImplementedError
@property
@abstractmethod
def mesh_data_object_type(self) -> MeshDataObjectType:
"""The kind of mesh object the values describe."""
raise NotImplementedError
@property
@abstractmethod
def x_cell_dimension(self) -> IDimension:
"""The x-cell dimension (dimension 1 of :attr:`shape`)."""
raise NotImplementedError
@property
@abstractmethod
def y_cell_dimension(self) -> IDimension:
"""The y-cell dimension (dimension 0 of :attr:`shape`)."""
raise NotImplementedError
@property
@abstractmethod
def cell_edge_dimension(self) -> IDimension:
"""The per-cell edge dimension, when values attach to cell edges."""
raise NotImplementedError
@property
@abstractmethod
def cell_vertex_dimension(self) -> IDimension:
"""The per-cell vertex dimension, when values attach to cell
vertices."""
raise NotImplementedError
[docs]
class IRegularGrid3DComponentDataItem(IComponentDataItem):
"""Data associated with the cells of a 3D regular grid.
Mirrors C++ ``Spatial::IRegularGrid3DComponentDataItem``. Canonical
dimension ordering: z-cell is dimension 0, y-cell is dimension 1,
x-cell is dimension 2 of :attr:`shape`; optional cell face and cell
vertex dimensions follow.
"""
@property
@abstractmethod
def grid(self) -> IRegularGrid3D:
"""The associated grid."""
raise NotImplementedError
@property
@abstractmethod
def mesh_data_object_type(self) -> MeshDataObjectType:
"""The kind of mesh object the values describe."""
raise NotImplementedError
@property
@abstractmethod
def x_cell_dimension(self) -> IDimension:
"""The x-cell dimension (dimension 2 of :attr:`shape`)."""
raise NotImplementedError
@property
@abstractmethod
def y_cell_dimension(self) -> IDimension:
"""The y-cell dimension (dimension 1 of :attr:`shape`)."""
raise NotImplementedError
@property
@abstractmethod
def z_cell_dimension(self) -> IDimension:
"""The z-cell dimension (dimension 0 of :attr:`shape`)."""
raise NotImplementedError
@property
@abstractmethod
def cell_face_dimension(self) -> IDimension:
"""The per-cell face dimension, when values attach to cell faces."""
raise NotImplementedError
@property
@abstractmethod
def cell_vertex_dimension(self) -> IDimension:
"""The per-cell vertex dimension, when values attach to cell
vertices."""
raise NotImplementedError