Source code for movement.roi.io

"""I/O functions for saving and loading collections of regions of interest."""

from __future__ import annotations

import json
from collections.abc import Sequence
from typing import TYPE_CHECKING, Any, TypeAlias

import shapely

from movement.roi.base import BaseRegionOfInterest
from movement.roi.line import LineOfInterest
from movement.roi.polygon import PolygonOfInterest
from movement.validators.files import (
    ValidROICollectionGeoJSON,
    validate_file_path,
)

if TYPE_CHECKING:
    from pathlib import Path


# Type alias for collections of RoIs
ROICollection: TypeAlias = Sequence[BaseRegionOfInterest]


[docs] def save_rois( rois: ROICollection, file: str | Path, ) -> None: """Save a collection of regions of interest (RoIs) to a GeoJSON file. The RoIs are saved as a GeoJSON FeatureCollection, with each RoI represented as a Feature containing its geometry and properties (name and RoI type). Parameters ---------- rois The regions of interest to save. file Path to the output file. Should have a ``.geojson`` or a ``.json`` extension. See Also -------- load_rois : Load a collection of RoIs from a GeoJSON file. Examples -------- Create a polygon and a line, then save them to a GeoJSON file: >>> from movement.roi import LineOfInterest, PolygonOfInterest, save_rois >>> square = PolygonOfInterest( ... [(0, 0), (1, 0), (1, 1), (0, 1)], name="square" ... ) >>> diagonal = LineOfInterest([(0, 0), (1, 1)], name="diagonal") >>> save_rois([square, diagonal], "/path/to/rois.geojson") """ valid_path = validate_file_path( file, permission="w", suffixes=ValidROICollectionGeoJSON.suffixes ) features = [ { "type": "Feature", "geometry": json.loads(shapely.to_geojson(roi.region)), "properties": { "name": roi.name, "roi_type": roi.__class__.__name__, }, } for roi in rois ] feature_collection = { "type": "FeatureCollection", "features": features, } with open(valid_path, "w") as f: json.dump(feature_collection, f, indent=2)
[docs] def load_rois(file: str | Path) -> list[LineOfInterest | PolygonOfInterest]: """Load a collection of regions of interest (RoIs) from a GeoJSON file. Parameters ---------- file Path to the GeoJSON file to load. Returns ------- list of LineOfInterest or PolygonOfInterest The loaded regions of interest. See Also -------- save_rois : Save a collection of RoIs to a GeoJSON file. Examples -------- Load a collection of RoIs from a GeoJSON file and inspect their names: >>> from movement.roi import load_rois >>> rois = load_rois("/path/to/rois.geojson") >>> # rois is a list of RoI objects >>> [roi.name for roi in rois] ["square", "diagonal"] """ validated_file = ValidROICollectionGeoJSON(file) return [ _feature_to_roi(feature) for feature in validated_file.data["features"] ]
def _feature_to_roi( feature: dict[str, Any], ) -> LineOfInterest | PolygonOfInterest: """Convert a validated GeoJSON feature to an RoI object.""" geometry = shapely.from_geojson(json.dumps(feature["geometry"])) name = (feature.get("properties") or {}).get("name") if isinstance(geometry, shapely.Polygon): holes = [interior.coords for interior in geometry.interiors] return PolygonOfInterest( geometry.exterior.coords, holes=holes, name=name, ) loop = isinstance(geometry, shapely.LinearRing) return LineOfInterest(geometry.coords, loop=loop, name=name)