registry module¶
Sensor registry for common HyperCoast workflows.
SensorHandler
dataclass
¶
Describe a HyperCoast sensor workflow.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name |
str |
Canonical sensor name. |
required |
read |
Optional[Callable[..., xr.Dataset]] |
Function used to read local sensor data. |
None |
to_image |
Optional[Callable[..., Any]] |
Function used to convert sensor data to an image. |
None |
extract |
Optional[Callable[..., xr.DataArray]] |
Function used to extract a point spectrum. |
None |
search |
Optional[Callable[..., Any]] |
Function used to search remote sensor data. |
None |
download |
Optional[Callable[..., Any]] |
Function used to download remote sensor data. |
None |
aliases |
tuple[str, ...] |
Additional case-insensitive names accepted for the sensor. |
() |
extensions |
tuple[str, ...] |
Supported file extensions. |
() |
default_rgb |
tuple[float, float, float] |
Default RGB wavelengths in nanometers. |
() |
default_variable |
str |
Default raster variable name. |
'' |
crs |
str |
Expected CRS behavior or CRS default. |
'' |
qgis |
bool |
Whether the sensor should be exposed in the QGIS plugin. |
True |
qgis_name |
str |
Optional QGIS display key. |
'' |
sample_data |
Dict[str, str] |
Optional sample dataset metadata. |
<factory> |
description |
str |
Short human-readable description. |
'' |
Source code in hypercoast/registry.py
@dataclass(frozen=True)
class SensorHandler:
"""Describe a HyperCoast sensor workflow.
Args:
name: Canonical sensor name.
read: Function used to read local sensor data.
to_image: Function used to convert sensor data to an image.
extract: Function used to extract a point spectrum.
search: Function used to search remote sensor data.
download: Function used to download remote sensor data.
aliases: Additional case-insensitive names accepted for the sensor.
extensions: Supported file extensions.
default_rgb: Default RGB wavelengths in nanometers.
default_variable: Default raster variable name.
crs: Expected CRS behavior or CRS default.
qgis: Whether the sensor should be exposed in the QGIS plugin.
qgis_name: Optional QGIS display key.
sample_data: Optional sample dataset metadata.
description: Short human-readable description.
"""
name: str
read: Optional[Callable[..., xr.Dataset]] = None
to_image: Optional[Callable[..., Any]] = None
extract: Optional[Callable[..., xr.DataArray]] = None
search: Optional[Callable[..., Any]] = None
download: Optional[Callable[..., Any]] = None
aliases: tuple[str, ...] = ()
extensions: tuple[str, ...] = ()
default_rgb: tuple[float, float, float] = ()
default_variable: str = ""
crs: str = ""
qgis: bool = True
qgis_name: str = ""
sample_data: Dict[str, str] = field(default_factory=dict)
description: str = ""
def as_dict(self) -> Dict[str, Any]:
"""Return serializable sensor metadata.
Returns:
dict: Sensor metadata excluding function objects.
"""
data = asdict(self)
for key in ("read", "to_image", "extract", "search", "download"):
data.pop(key, None)
data["has_reader"] = self.read is not None
data["has_image_converter"] = self.to_image is not None
data["has_extractor"] = self.extract is not None
data["has_search"] = self.search is not None
data["has_download"] = self.download is not None
data["qgis_name"] = self.qgis_name or self.name
return data
as_dict(self)
¶
Return serializable sensor metadata.
Returns:
| Type | Description |
|---|---|
dict |
Sensor metadata excluding function objects. |
Source code in hypercoast/registry.py
def as_dict(self) -> Dict[str, Any]:
"""Return serializable sensor metadata.
Returns:
dict: Sensor metadata excluding function objects.
"""
data = asdict(self)
for key in ("read", "to_image", "extract", "search", "download"):
data.pop(key, None)
data["has_reader"] = self.read is not None
data["has_image_converter"] = self.to_image is not None
data["has_extractor"] = self.extract is not None
data["has_search"] = self.search is not None
data["has_download"] = self.download is not None
data["qgis_name"] = self.qgis_name or self.name
return data
download_sensor(sensor, items, **kwargs)
¶
Download remote data using the registered download function.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
sensor |
str |
Sensor name or alias. |
required |
items |
Iterable[Any] |
Granules or STAC items accepted by the download function. |
required |
**kwargs |
Any |
Download keyword arguments. |
{} |
Returns:
| Type | Description |
|---|---|
Any |
Download function result. |
Exceptions:
| Type | Description |
|---|---|
NotImplementedError |
If the sensor has no registered download function. |
Source code in hypercoast/registry.py
def download_sensor(sensor: str, items: Iterable[Any], **kwargs: Any) -> Any:
"""Download remote data using the registered download function.
Args:
sensor: Sensor name or alias.
items: Granules or STAC items accepted by the download function.
**kwargs: Download keyword arguments.
Returns:
Any: Download function result.
Raises:
NotImplementedError: If the sensor has no registered download function.
"""
handler = get_sensor(sensor)
if handler.download is None:
raise NotImplementedError(
f"{handler.name} has no registered download function."
)
return handler.download(items, **kwargs)
extract_sensor(sensor, data, lat, lon, **kwargs)
¶
Extract a point spectrum using the registered extractor.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
sensor |
str |
Sensor name or alias. |
required |
data |
Any |
Dataset or local path accepted by the sensor reader. |
required |
lat |
float |
Latitude of the point to extract. |
required |
lon |
float |
Longitude of the point to extract. |
required |
**kwargs |
Any |
Additional extractor keyword arguments. |
{} |
Returns:
| Type | Description |
|---|---|
xr.DataArray |
Extracted spectrum. |
Exceptions:
| Type | Description |
|---|---|
NotImplementedError |
If the sensor has no registered extractor. |
Source code in hypercoast/registry.py
def extract_sensor(
sensor: str,
data: Any,
lat: float,
lon: float,
**kwargs: Any,
) -> xr.DataArray:
"""Extract a point spectrum using the registered extractor.
Args:
sensor: Sensor name or alias.
data: Dataset or local path accepted by the sensor reader.
lat: Latitude of the point to extract.
lon: Longitude of the point to extract.
**kwargs: Additional extractor keyword arguments.
Returns:
xr.DataArray: Extracted spectrum.
Raises:
NotImplementedError: If the sensor has no registered extractor.
"""
handler = get_sensor(sensor)
if handler.extract is None:
raise NotImplementedError(f"{handler.name} has no registered extractor.")
if isinstance(data, (str, bytes, os.PathLike)):
data = read_sensor(sensor, data)
return handler.extract(data, lat, lon, **kwargs)
get_sensor(name)
¶
Return the handler for a sensor name or alias.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name |
str |
Sensor name or alias. |
required |
Returns:
| Type | Description |
|---|---|
SensorHandler |
Matching sensor handler. |
Exceptions:
| Type | Description |
|---|---|
KeyError |
If the sensor is not registered. |
Source code in hypercoast/registry.py
def get_sensor(name: str) -> SensorHandler:
"""Return the handler for a sensor name or alias.
Args:
name: Sensor name or alias.
Returns:
SensorHandler: Matching sensor handler.
Raises:
KeyError: If the sensor is not registered.
"""
key = _normalize_name(name)
if key not in _ALIASES:
available = ", ".join(list_sensors())
raise KeyError(f"Unsupported sensor '{name}'. Available sensors: {available}")
return SENSOR_REGISTRY[_ALIASES[key]]
list_sensors()
¶
Return registered sensor names.
Returns:
| Type | Description |
|---|---|
list[str] |
Sorted canonical sensor names. |
Source code in hypercoast/registry.py
def list_sensors() -> List[str]:
"""Return registered sensor names.
Returns:
list[str]: Sorted canonical sensor names.
"""
return sorted(SENSOR_REGISTRY)
qgis_data_types(include_generic=True)
¶
Return QGIS data-type metadata derived from the registry.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
include_generic |
bool |
Whether to include the generic fallback type. |
True |
Returns:
| Type | Description |
|---|---|
dict |
Mapping compatible with the QGIS plugin |
Source code in hypercoast/registry.py
def qgis_data_types(include_generic: bool = True) -> Dict[str, Dict[str, Any]]:
"""Return QGIS data-type metadata derived from the registry.
Args:
include_generic: Whether to include the generic fallback type.
Returns:
dict: Mapping compatible with the QGIS plugin ``DATA_TYPES`` table.
"""
data_types = {}
for handler in SENSOR_REGISTRY.values():
if not handler.qgis:
continue
key = handler.qgis_name or handler.name
data_types[key] = {
"extensions": list(handler.extensions),
"description": handler.description,
"variable": handler.default_variable or "data",
"default_rgb": list(handler.default_rgb),
}
if include_generic:
data_types["Generic"] = {
"extensions": [".tif", ".tiff", ".nc", ".nc4"],
"description": "Generic Hyperspectral (GeoTIFF/NetCDF)",
"variable": "data",
"default_rgb": [650, 550, 450],
}
return data_types
read_sensor(sensor, source, **kwargs)
¶
Read sensor data using the registered reader.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
sensor |
str |
Sensor name or alias. |
required |
source |
Any |
Local source path or object accepted by the sensor reader. |
required |
**kwargs |
Any |
Additional reader keyword arguments. |
{} |
Returns:
| Type | Description |
|---|---|
xr.Dataset |
Loaded sensor dataset. |
Exceptions:
| Type | Description |
|---|---|
NotImplementedError |
If the sensor has no registered reader. |
Source code in hypercoast/registry.py
def read_sensor(sensor: str, source: Any, **kwargs: Any) -> xr.Dataset:
"""Read sensor data using the registered reader.
Args:
sensor: Sensor name or alias.
source: Local source path or object accepted by the sensor reader.
**kwargs: Additional reader keyword arguments.
Returns:
xr.Dataset: Loaded sensor dataset.
Raises:
NotImplementedError: If the sensor has no registered reader.
"""
handler = get_sensor(sensor)
if handler.read is None:
raise NotImplementedError(f"{handler.name} has no registered reader.")
return handler.read(source, **kwargs)
register_sensor(handler)
¶
Register a sensor handler.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
handler |
SensorHandler |
The sensor handler to register. |
required |
Returns:
| Type | Description |
|---|---|
SensorHandler |
The registered handler. |
Source code in hypercoast/registry.py
def register_sensor(handler: SensorHandler) -> SensorHandler:
"""Register a sensor handler.
Args:
handler: The sensor handler to register.
Returns:
SensorHandler: The registered handler.
"""
key = _normalize_name(handler.name)
SENSOR_REGISTRY[key] = handler
_ALIASES[key] = key
for alias in handler.aliases:
_ALIASES[_normalize_name(alias)] = key
return handler
registry_as_dict()
¶
Return serializable metadata for all registered sensors.
Returns:
| Type | Description |
|---|---|
dict |
Mapping of sensor names to metadata dictionaries. |
Source code in hypercoast/registry.py
def registry_as_dict() -> Dict[str, Dict[str, Any]]:
"""Return serializable metadata for all registered sensors.
Returns:
dict: Mapping of sensor names to metadata dictionaries.
"""
return {name: SENSOR_REGISTRY[name].as_dict() for name in list_sensors()}
search_sensor(sensor, **kwargs)
¶
Search remote data using the registered search function.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
sensor |
str |
Sensor name or alias. |
required |
**kwargs |
Any |
Search keyword arguments. |
{} |
Returns:
| Type | Description |
|---|---|
Any |
Search function result. |
Exceptions:
| Type | Description |
|---|---|
NotImplementedError |
If the sensor has no registered search function. |
Source code in hypercoast/registry.py
def search_sensor(sensor: str, **kwargs: Any) -> Any:
"""Search remote data using the registered search function.
Args:
sensor: Sensor name or alias.
**kwargs: Search keyword arguments.
Returns:
Any: Search function result.
Raises:
NotImplementedError: If the sensor has no registered search function.
"""
handler = get_sensor(sensor)
if handler.search is None:
raise NotImplementedError(f"{handler.name} has no registered search function.")
return handler.search(**kwargs)
sensor_to_image(sensor, data, output=None, **kwargs)
¶
Convert sensor data to an image using the registered converter.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
sensor |
str |
Sensor name or alias. |
required |
data |
Any |
Dataset or local path accepted by the image converter. |
required |
output |
Optional[str] |
Optional output image path. |
None |
**kwargs |
Any |
Additional image converter keyword arguments. |
{} |
Returns:
| Type | Description |
|---|---|
Any |
The sensor converter return value. |
Exceptions:
| Type | Description |
|---|---|
NotImplementedError |
If the sensor has no registered image converter. |
Source code in hypercoast/registry.py
def sensor_to_image(
sensor: str,
data: Any,
output: Optional[str] = None,
**kwargs: Any,
):
"""Convert sensor data to an image using the registered converter.
Args:
sensor: Sensor name or alias.
data: Dataset or local path accepted by the image converter.
output: Optional output image path.
**kwargs: Additional image converter keyword arguments.
Returns:
Any: The sensor converter return value.
Raises:
NotImplementedError: If the sensor has no registered image converter.
"""
handler = get_sensor(sensor)
if handler.to_image is None:
raise NotImplementedError(f"{handler.name} has no registered image converter.")
return handler.to_image(data, output=output, **kwargs)