Source code for omf_io.utils.spatial_encoding

import numpy as np
from typing import Tuple, Union

Point = Tuple[float, float, float]
BlockDimension = Tuple[float, float, float]
ArrayOrFloat = Union[np.ndarray, float]

MAX_XY_VALUE = 1677721.5  # Maximum value for x and y (2^24 - 1) / 10
MAX_Z_VALUE = 6553.5      # Maximum value for z (2^16 - 1) / 10
MAX_DIM_VALUE = 102.3  # Maximum value for dx, dy, dz

[docs] def is_integer(value): return np.floor(value) == value
[docs] def encode_coordinates(x: ArrayOrFloat, y: ArrayOrFloat, z: ArrayOrFloat) -> Union[np.ndarray, int]: """Encode the coordinates into a 64-bit integer or an array of 64-bit integers.""" def check_value(value, max_value): if value > max_value: raise ValueError(f"Value {value} exceeds the maximum supported value of {max_value}") if not is_integer(value * 10): raise ValueError(f"Value {value} has more than 1 decimal place") return value if isinstance(x, np.ndarray) and isinstance(y, np.ndarray) and isinstance(z, np.ndarray): x = np.vectorize(check_value)(x, MAX_XY_VALUE) y = np.vectorize(check_value)(y, MAX_XY_VALUE) z = np.vectorize(check_value)(z, MAX_Z_VALUE) x_int = (x * 10).astype(np.int64) & 0xFFFFFF y_int = (y * 10).astype(np.int64) & 0xFFFFFF z_int = (z * 10).astype(np.int64) & 0xFFFF encoded = (x_int << 40) | (y_int << 16) | z_int return encoded else: x = check_value(x, MAX_XY_VALUE) y = check_value(y, MAX_XY_VALUE) z = check_value(z, MAX_Z_VALUE) x_int = int(x * 10) & 0xFFFFFF y_int = int(y * 10) & 0xFFFFFF z_int = int(z * 10) & 0xFFFF encoded = (x_int << 40) | (y_int << 16) | z_int return encoded
[docs] def decode_coordinates(encoded: Union[np.ndarray, int]) -> Union[Tuple[np.ndarray, np.ndarray, np.ndarray], Point]: """Decode the 64-bit integer or array of 64-bit integers back to the original coordinates.""" x_int = (encoded >> 40) & 0xFFFFFF y_int = (encoded >> 16) & 0xFFFFFF z_int = encoded & 0xFFFF x = x_int / 10.0 y = y_int / 10.0 z = z_int / 10.0 return x, y, z
[docs] def encode_dimensions(dx: ArrayOrFloat, dy: ArrayOrFloat, dz: ArrayOrFloat) -> Union[np.ndarray, int]: """Encode the block dimensions into a 32-bit integer or an array of 32-bit integers.""" def check_value(value, max_value): if value > max_value: raise ValueError(f"Value {value} exceeds the maximum supported value of {max_value}") if not is_integer(value * 10): raise ValueError(f"Value {value} has more than 1 decimal place") return value if isinstance(dx, np.ndarray) and isinstance(dy, np.ndarray) and isinstance(dz, np.ndarray): dx = np.vectorize(check_value)(dx, MAX_DIM_VALUE) dy = np.vectorize(check_value)(dy, MAX_DIM_VALUE) dz = np.vectorize(check_value)(dz, MAX_DIM_VALUE) dx_int = (dx * 10).astype(np.int32) & 0x3FF # 10 bits for dx dy_int = (dy * 10).astype(np.int32) & 0x3FF # 10 bits for dy dz_int = (dz * 10).astype(np.int32) & 0x3FF # 10 bits for dz encoded = (dx_int << 20) | (dy_int << 10) | dz_int return encoded else: dx = check_value(dx, MAX_DIM_VALUE) dy = check_value(dy, MAX_DIM_VALUE) dz = check_value(dz, MAX_DIM_VALUE) dx_int = int(dx * 10) & 0x3FF dy_int = int(dy * 10) & 0x3FF dz_int = int(dz * 10) & 0x3FF encoded = (dx_int << 20) | (dy_int << 10) | dz_int return encoded
[docs] def decode_dimensions(encoded: Union[np.ndarray, int]) -> Union[Tuple[np.ndarray, np.ndarray, np.ndarray], BlockDimension]: """Decode the 32-bit integer or array of 32-bit integers back to the original block dimensions.""" dx_int = (encoded >> 20) & 0x3FF dy_int = (encoded >> 10) & 0x3FF dz_int = encoded & 0x3FF dx = dx_int / 10.0 dy = dy_int / 10.0 dz = dz_int / 10.0 return dx, dy, dz