voxcity.utils.orientation¶
Grid orientation helpers.
Contract: - Core Phase 3 processing uses uv_m/SOUTH_UP: axis 0 = u/north (row 0 = southern origin edge),
increasing row index moves north. Columns increase eastward: column 0 is west/left and indices increase toward the east/right. All processing functions accept and return 2D grids in this orientation unless explicitly documented otherwise.
Visualization utilities may flip vertically for display purposes only.
3D voxel arrays index (i, j, k) with the same horizontal orientation as the 2D grids (row 0 = southern origin edge, i increases northward; j increases eastward) and k = ground→up. The voxelizer applies no flip between 2D grids and voxel layers.
Utilities here are intentionally minimal to avoid introducing hidden behavior. They can be used at I/O boundaries (e.g., when reading rasters with south_up conventions) to normalize to the internal orientation.
- Coordinate-frame vocabulary (see also voxcity.utils.projector):
uv cell — cell index (i, j) in uv_m/SOUTH_UP (Phase 3); row 0 is south/origin.
- File format (voxcity_results.v3, written by voxcity.io):
Every saved HDF5 file declares this contract: attribute
axes = "north,east,up"(root, voxcity group, and voxcity/voxel_grid dataset), attributerotation_angle(degrees clockwise; the axes tokens apply in the frame rotated by this angle), and root datasetrectangle_vertices(4x2 lon/lat, [SW, NW, NE, SE]).axesandrotation_angleare a documented pair: axis 0 points north rotated clockwise by rotation_angle. Use check_axes() to assert the contract; pre-v3 files are converted with voxcity.io.migrate_h5().- Boundary layouts handled by helpers in this module:
- north_up raster — row 0 = north (GeoTIFF, display, coastline masks).
uv <-> north_up is a pure vertical flip: ensure_orientation().
- rasterio layout — shape (ny, nx): rows along v, cols along u, as produced/
consumed by rasterio.features.rasterize with the uv affine. uv <-> rasterio is a pure transpose: to/from_rasterio_layout().
- rotated raster — legacy layout for rotated-AOI GeoTIFFs:
grid_to_rotated_raster().
MagicaVoxel dense axes — voxels_to_magicavoxel_axes(). OBJ mesher (k, i, j) axes — voxels_to_kji().
Intentional exception: the ENVI-met exporter keeps SOUTH_UP internally and writes north-first rows itself (arr[::-1]) at the file-format boundary. It deliberately does NOT call ensure_orientation(); a guard test (tests/test_exporter_envimet.py::TestEnvimetSouthUpProcessing) enforces this.
Attributes¶
Geographic direction of increasing index for array axes 0, 1, 2, in the |
|
The exact string stored as the |
Functions¶
|
Unit direction |
|
Raise |
|
Return |
|
Convert an internal uv grid (nx, ny) to rasterio layout (ny, nx). |
|
Convert a rasterio-layout array (ny, nx) to an internal uv grid (nx, ny). |
|
Convert a uv grid to the legacy rotated-raster layout for GeoTIFF. |
|
(u/north, v/east, z/height) voxels -> pyvox dense (y=north, z=height, x=east). |
|
(u, v, z) voxels -> (k=z, i=u, j=v) axis order used by the OBJ mesher. |
Module Contents¶
- voxcity.utils.orientation.ORIENTATION_NORTH_UP: Literal['north_up'] = 'north_up'¶
- voxcity.utils.orientation.ORIENTATION_SOUTH_UP: Literal['south_up'] = 'south_up'¶
- voxcity.utils.orientation.AXES = ('north', 'east', 'up')¶
Geographic direction of increasing index for array axes 0, 1, 2, in the un-rotated frame. Axis 0 points north rotated clockwise by the AOI’s
rotation_angledegrees (axis 1 likewise for east; axis 2 is up); for axis-aligned AOIs (rotation_angle == 0) the tokens are literally true.
- voxcity.utils.orientation.AXES_ATTR = ''¶
The exact string stored as the
axesattribute in v3 HDF5 files (root,voxcitygroup, andvoxcity/voxel_griddataset).
- voxcity.utils.orientation.direction_to_axis_vector(azimuth_deg, elevation_deg=0.0, rotation_angle_deg=0.0)[source]¶
Unit direction
(d_axis0, d_axis1, d_axis2)for a compass azimuth.- Parameters:
azimuth_deg (float or np.ndarray) – Compass azimuth in degrees, clockwise from north — a toward direction (where the ray points). Meteorological from-directions (wind direction, “the sun stands at azimuth X”) must be converted by the caller:
az_toward = az_from + 180.elevation_deg (float or np.ndarray, default 0.0) – Elevation above the horizontal plane, degrees.
rotation_angle_deg (float or np.ndarray, default 0.0) – AOI rotation in degrees clockwise, as stored in v3 files. The azimuth is geographic; subtracting this maps it into the rotated grid frame.
- Returns:
Scalar inputs → shape
(3,); array inputs broadcast → shapebroadcast + (3,). Components are array-axis deltas: component 0 is along axis 0 = north (not east!), component 1 along axis 1 = east, component 2 along axis 2 = up. Unit-norm by construction; no re-normalization is applied (bit-parity with the historical inline call sites).- Return type:
np.ndarray
- voxcity.utils.orientation.check_axes(file_or_attrs) None[source]¶
Raise
ValueErrorunless file_or_attrs declares the v3 axis contract.Accepts an
h5pyFile/Group/Dataset (anything with.attrs), a plain attrs mapping, or a path to an HDF5 file. Passes silently when theaxesattribute equalsAXES_ATTR.
- voxcity.utils.orientation.ensure_orientation(grid: numpy.ndarray, orientation_in: Literal['north_up', 'south_up'], orientation_out: Literal['north_up', 'south_up'] = ORIENTATION_NORTH_UP) numpy.ndarray[source]¶
Return
gridconverted fromorientation_intoorientation_out.Both orientations are defined for 2D arrays as: - north_up: row 0 = north/top, last row = south/bottom - south_up: row 0 = south/bottom, last row = north/top
If orientations match, the input array is returned unchanged. When converting between north_up and south_up, a vertical flip is applied using
np.flipud.Notes
This function does not copy when no conversion is needed.
Use at data boundaries (read/write, interop) rather than deep in processing code.
- voxcity.utils.orientation.to_rasterio_layout(grid: numpy.ndarray) numpy.ndarray[source]¶
Convert an internal uv grid (nx, ny) to rasterio layout (ny, nx).
Frames: uv_m/SOUTH_UP
grid[i, j](axis 0 = u, axis 1 = v) -> rasterioarr[row, col]with rows along v and cols along u, as consumed byrasterio.features.rasterizewith the uv affineAffine(du*u_vec[0], dv*v_vec[0], ox, du*u_vec[1], dv*v_vec[1], oy). Pure transpose — no vertical flip. Returns a C-contiguous array (prevents silent Numba performance degradation downstream).
- voxcity.utils.orientation.from_rasterio_layout(arr: numpy.ndarray) numpy.ndarray[source]¶
Convert a rasterio-layout array (ny, nx) to an internal uv grid (nx, ny).
Inverse of
to_rasterio_layout(). Pure transpose — no vertical flip. Returns a C-contiguous array.
- voxcity.utils.orientation.grid_to_rotated_raster(grid: numpy.ndarray) numpy.ndarray[source]¶
Convert a uv grid to the legacy rotated-raster layout for GeoTIFF.
flipud(grid.T): rows advance along -v, columns along +u. Only meaningful together with the rotated affine whose origin is the AOI’s far-v corner (see the rotated-AOI fallback inexporter/geotiff.py). Axis-aligned AOIs use the cell-centre scatter path instead.