Source code for restgdf.adapters.geopandas

"""Geopandas-gated geo-tabular adapters.

Materialize row-shaped dict iterables into a :class:`geopandas.GeoDataFrame`.
The module itself is safe to import on a base restgdf install — the geo
stack (``pandas`` + ``geopandas`` + ``pyogrio``) is loaded lazily via
:func:`restgdf.utils._optional.require_geo_stack` **inside** each adapter
function. Calling an adapter on a base install raises
:class:`restgdf.errors.OptionalDependencyError`.

Scope note
----------
Phase-2d keeps geometry handling minimal: the adapters pass the materialized
row table directly to ``geopandas.GeoDataFrame`` with ``geometry=geometry_field``.
Callers are expected to supply shapely-compatible geometry values, already-built
``GeoSeries`` elements, or to leave the field empty for tabular-only flows.
Full ArcGIS geometry-dict normalization (points, polylines, polygons,
mixed-Z/M, spatial-reference promotion) ships with MASTER-PLAN BL-27 / BL-28
in phase-2b and BL-35 in phase-4b.
"""

from __future__ import annotations

from collections.abc import AsyncIterable, Iterable
from typing import TYPE_CHECKING, Any

from restgdf.utils._optional import require_geo_stack, require_geopandas

if TYPE_CHECKING:  # pragma: no cover - import-time only
    from geopandas import GeoDataFrame

__all__ = ["arows_to_geodataframe", "rows_to_geodataframe"]


[docs] def rows_to_geodataframe( rows: Iterable[dict[str, Any]], *, geometry_field: str = "geometry", crs: Any = None, ) -> GeoDataFrame: """Materialize row-shaped dicts as a ``geopandas.GeoDataFrame``. Requires the optional geo extra: ``pip install "restgdf[geo]"`` (brings in ``pandas``, ``geopandas``, and ``pyogrio``). Parameters ---------- rows: Iterable of row-shaped dicts, typically produced by :func:`restgdf.adapters.stream.iter_rows` or :func:`restgdf.adapters.dict.features_to_rows`. Each row's ``geometry_field`` entry must be shapely-compatible (or a ``GeoSeries`` element). geometry_field: Column name holding the geometry values. Defaults to ``"geometry"``. crs: Optional CRS passed through to ``GeoDataFrame(crs=...)``. Returns ------- geopandas.GeoDataFrame Raises ------ restgdf.errors.OptionalDependencyError When any of ``pandas``, ``geopandas``, or ``pyogrio`` is missing. Install via ``pip install "restgdf[geo]"``. Examples -------- >>> from shapely.geometry import Point # doctest: +SKIP >>> rows_to_geodataframe( # doctest: +SKIP ... [{"OBJECTID": 1, "geometry": Point(0, 0)}], ... crs="EPSG:4326", ... ) See Also -------- :meth:`restgdf.FeatureLayer.get_gdf` High-level accessor that returns the full layer as a single ``GeoDataFrame``. :meth:`restgdf.FeatureLayer.stream_gdf_chunks` Async iterator yielding one ``GeoDataFrame`` per page, each with ``gdf.attrs["spatial_reference"]`` populated from layer metadata. """ require_geo_stack("restgdf.adapters.geopandas.rows_to_geodataframe()") GeoDataFrameCls = require_geopandas( "restgdf.adapters.geopandas.rows_to_geodataframe()", ).GeoDataFrame materialized = list(rows) return GeoDataFrameCls(materialized, geometry=geometry_field, crs=crs)
[docs] async def arows_to_geodataframe( rows: AsyncIterable[dict[str, Any]], *, geometry_field: str = "geometry", crs: Any = None, ) -> GeoDataFrame: """Async counterpart of :func:`rows_to_geodataframe`. Consumes the async iterable to completion, then delegates. Requires the optional geo extra: ``pip install "restgdf[geo]"``. Parameters ---------- rows: Async iterable of row-shaped dicts — typically :meth:`restgdf.FeatureLayer.stream_rows` or :func:`restgdf.adapters.stream.iter_rows`. geometry_field: Column name for the geometry column. Defaults to ``"geometry"``. crs: Optional CRS forwarded to ``GeoDataFrame(crs=...)``. Returns ------- geopandas.GeoDataFrame Raises ------ restgdf.errors.OptionalDependencyError When any of ``pandas``, ``geopandas``, or ``pyogrio`` is missing. See Also -------- :meth:`restgdf.FeatureLayer.get_gdf` Equivalent to ``await arows_to_geodataframe(layer.stream_rows())`` with geometry normalization and CRS propagation handled for you. """ materialized: list[dict[str, Any]] = [row async for row in rows] return rows_to_geodataframe( materialized, geometry_field=geometry_field, crs=crs, )