Output adapters

The restgdf.adapters subpackage composes streaming primitives into tabular output shapes. Each adapter works against the async iterators exposed by FeatureLayer and avoids importing heavy dependencies (pandas, geopandas) at module load time — they are guarded behind runtime checks and raise OptionalDependencyError with an install hint when missing.

restgdf.adapters.dict

Pure-Python row conversion. Part of the base pip install restgdf install.

Core-install safe row-shaped dict adapters.

Thin, stable aliases for the row-flattening helpers in restgdf.utils.getgdf. No heavy dependencies — this module is safe to import and use on a minimal restgdf install.

restgdf.adapters.dict.as_dict(obj)[source]

Return a plain Python dict view of a restgdf pydantic model.

If obj is a pydantic.BaseModel instance, returns obj.model_dump(mode="python", by_alias=False) — a dict keyed by the model’s Python (snake_case) field names, with nested models also recursively dumped.

If obj is anything else (already-a-dict, None, primitive, list), it is returned unchanged. This lets migration code wrap heterogeneous values uniformly:

for entry in report.services:
    row = as_dict(entry)   # dict whether entry is model or already dict
    save(row["name"], row.get("url"))

This helper is intentionally conservative: it does not recurse into containers of models and does not coerce by_alias=True. Callers that need the ArcGIS-native camelCase round-trip should call model_dump() explicitly.

restgdf.adapters.dict.as_json_dict(obj)[source]

Return a JSON-safe dict view of a restgdf pydantic model.

Like as_dict(), but uses model_dump(mode="json") so every nested value is a JSON-serializable primitive (SecretStr"**********" placeholder, datetime → ISO string, etc.). Handy for structured logging of a model without carrying unserializable objects into the log record.

Non-model values are returned unchanged.

restgdf.adapters.dict.feature_to_row(feature)[source]

Flatten a single ArcGIS feature envelope into a row-shaped dict.

Merges feature["attributes"] with a "geometry" key holding the raw ArcGIS geometry dict verbatim. Safe on a base install — no pandas/geopandas dependency.

Parameters:

feature – A raw ArcGIS feature dict as returned by a /query response (the elements of response["features"]).

Returns:

{**feature["attributes"], "geometry": feature.get("geometry")}.

Return type:

dict[str, Any]

Examples

>>> feature_to_row({"attributes": {"OBJECTID": 1}, "geometry": {"x": 0, "y": 0}})
{'OBJECTID': 1, 'geometry': {'x': 0, 'y': 0}}

See also

restgdf.FeatureLayer.stream_rows()

High-level async iterator that yields row-shaped dicts directly from a live layer.

restgdf.utils.getgdf._feature_to_row_dict()

Underlying flattening primitive.

restgdf.adapters.dict.features_to_rows(features)[source]

Materialize an iterable of ArcGIS features as a list of row-shaped dicts.

Parameters:

features – Any iterable of raw ArcGIS feature dicts (e.g. a page’s "features" list).

Returns:

One feature_to_row() output per input feature.

Return type:

list[dict[str, Any]]

Examples

>>> features_to_rows([{"attributes": {"OBJECTID": 1}, "geometry": None}])
[{'OBJECTID': 1, 'geometry': None}]

See also

restgdf.FeatureLayer.stream_feature_batches()

Async iterator yielding one list[feature_dict] per page; feed each batch through this helper to materialize row tables.

restgdf.adapters.stream

Async iterator helpers that compose pagination into per-row or per-batch shapes. Part of the base install.

Core-install safe streaming adapters over ArcGIS pagination.

Thin async-iterator wrappers over the existing pagination helpers in restgdf.utils.getgdf. Row-level and feature-batch-level iteration are safe on a base install; iter_gdf_chunks requires the optional geo stack and gates at call time via restgdf.utils.getgdf.chunk_generator()’s internal call to restgdf.utils._optional.require_geo_stack().

The public streaming surface (stream_features, stream_feature_batches, ordering guarantees, backpressure options) is planned to expand in phase-4a under MASTER-PLAN BL-24. The names exposed here are the minimum viable set that today’s callers can depend on; their shape is compatible with the planned BL-24 expansion.

async restgdf.adapters.stream.iter_feature_batches(url, session, **kwargs)[source]

Yield ArcGIS feature batches (lists of raw feature dicts) from url.

Thin wrapper around restgdf.utils.getgdf._feature_batch_generator(). Core-install safe — no pandas / geopandas dependency.

Parameters:
Yields:

list[dict[str, Any]] – One list of raw ArcGIS feature dicts per page.

Examples

>>> import asyncio, aiohttp
>>> from restgdf.adapters.stream import iter_feature_batches
>>> async def demo(url):
...     async with aiohttp.ClientSession() as s:
...         async for batch in iter_feature_batches(url, s):
...             print(len(batch))

See also

restgdf.FeatureLayer.stream_feature_batches()

Preferred high-level entrypoint with order, max_concurrent_pages, and on_truncation knobs.

async restgdf.adapters.stream.iter_gdf_chunks(url, session, **kwargs)[source]

Yield GeoDataFrame chunks from url.

Requires the optional geo extra: pip install "restgdf[geo]". Thin wrapper around restgdf.utils.getgdf.chunk_generator(), which itself validates the optional geo stack via restgdf.utils._optional.require_geo_stack().

Parameters:
Yields:

geopandas.GeoDataFrame – One chunk per ArcGIS query page.

Raises:

restgdf.errors.OptionalDependencyError – When pandas, geopandas, or pyogrio is missing.

See also

restgdf.FeatureLayer.stream_gdf_chunks()

Preferred high-level entrypoint. Each yielded chunk carries gdf.attrs["spatial_reference"] populated from layer metadata (R-65).

async restgdf.adapters.stream.iter_rows(url, session, **kwargs)[source]

Yield row-shaped dicts from url.

Thin wrapper around restgdf.utils.getgdf.row_dict_generator(). Core-install safe — no pandas / geopandas dependency.

Parameters:
Yields:

dict[str, Any]{**feature["attributes"], "geometry": feature.get("geometry")} per feature.

Examples

>>> async for row in iter_rows(url, session):
...     print(row["OBJECTID"], row["geometry"])

See also

restgdf.FeatureLayer.stream_rows()

Preferred high-level entrypoint with streaming ordering and truncation-handling knobs.

restgdf.adapters.pandas

DataFrame materialization. Requires pandas (installed via restgdf[geo] or standalone).

Pandas-gated tabular adapters.

Materialize row-shaped dict iterables into a pandas.DataFrame. The module itself is safe to import on a base restgdf install — pandas is loaded lazily via restgdf.utils._optional.require_pandas() inside each adapter function, so importing this module on a pandas-free install does not raise. Calling an adapter function on such an install raises restgdf.errors.OptionalDependencyError with the canonical restgdf[geo] guidance.

async restgdf.adapters.pandas.arows_to_dataframe(rows)[source]

Async counterpart of rows_to_dataframe().

Consumes the async iterable to completion, then delegates to rows_to_dataframe().

Parameters:

rows – Async iterable of row-shaped dicts — typically restgdf.FeatureLayer.stream_rows() or restgdf.adapters.stream.iter_rows().

Return type:

pandas.DataFrame

Raises:

restgdf.errors.OptionalDependencyError – When pandas is not installed. Install via pip install     "restgdf[geo]" (geo extra bundles pandas) or pip install pandas.

Examples

>>> import asyncio
>>> from restgdf.adapters.pandas import arows_to_dataframe
>>> async def demo():
...     async def rows():
...         yield {"OBJECTID": 1}
...     return await arows_to_dataframe(rows())
>>> asyncio.run(demo())
   OBJECTID
0         1

See also

restgdf.FeatureLayer.get_df()

Convenience accessor equivalent to await arows_to_dataframe(layer.stream_rows()).

restgdf.adapters.pandas.resolve_domains(df, fields)[source]

Replace coded-value domain codes with their human-readable names.

Post-processes an already-materialized pandas.DataFrame using ArcGIS layer field metadata:

  • Coded-value domains — values present in the domain’s codedValues table are substituted for their name. Codes absent from the table pass through unchanged.

  • Range domains — values are left as-is. Out-of-range values are not flagged or coerced (callers who need strict validation should check [min, max] themselves using the layer’s field metadata).

The input DataFrame is not mutated; a shallow copy is returned when any substitution is performed, and the original object is returned unchanged when fields is empty / None or carries no applicable domains.

Parameters:
Returns:

A DataFrame with applicable coded-value columns resolved.

Return type:

pandas.DataFrame

Examples

>>> import pandas as pd
>>> from restgdf.adapters.pandas import resolve_domains
>>> df = pd.DataFrame({"STATUS": [1, 2, 99]})
>>> fields = [{
...     "name": "STATUS",
...     "domain": {
...         "type": "codedValue",
...         "codedValues": [
...             {"name": "Active", "code": 1},
...             {"name": "Inactive", "code": 2},
...         ],
...     },
... }]
>>> resolve_domains(df, fields)["STATUS"].tolist()
['Active', 'Inactive', 99]
restgdf.adapters.pandas.rows_to_dataframe(rows)[source]

Materialize an iterable of row-shaped dicts as a pandas.DataFrame.

Parameters:

rows – Any iterable of row-shaped dicts — typically produced by restgdf.adapters.dict.features_to_rows() or collected from restgdf.FeatureLayer.stream_rows().

Return type:

pandas.DataFrame

Raises:

restgdf.errors.OptionalDependencyError – When pandas is not installed. Install the optional extra via pip install "restgdf[geo]" (which ships pandas alongside the geo stack) or install pandas directly.

Examples

>>> from restgdf.adapters.pandas import rows_to_dataframe
>>> rows_to_dataframe([{"OBJECTID": 1, "NAME": "A"}])
   OBJECTID NAME
0         1    A

See also

restgdf.FeatureLayer.get_df()

Async pandas-first tabular accessor that wraps this adapter over a live layer.

restgdf.adapters.geopandas

GeoDataFrame materialization. Requires restgdf[geo] (geopandas + pyogrio).

Geopandas-gated geo-tabular adapters.

Materialize row-shaped dict iterables into a geopandas.GeoDataFrame. The module itself is safe to import on a base restgdf install — the geo stack (pandas + geopandas + pyogrio) is loaded lazily via restgdf.utils._optional.require_geo_stack() inside each adapter function. Calling an adapter on a base install raises 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.

async restgdf.adapters.geopandas.arows_to_geodataframe(rows, *, geometry_field='geometry', crs=None)[source]

Async counterpart of rows_to_geodataframe().

Consumes the async iterable to completion, then delegates. Requires the optional geo extra: pip install "restgdf[geo]".

Parameters:
Return type:

geopandas.GeoDataFrame

Raises:

restgdf.errors.OptionalDependencyError – When any of pandas, geopandas, or pyogrio is missing.

See also

restgdf.FeatureLayer.get_gdf()

Equivalent to await arows_to_geodataframe(layer.stream_rows()) with geometry normalization and CRS propagation handled for you.

restgdf.adapters.geopandas.rows_to_geodataframe(rows, *, geometry_field='geometry', crs=None)[source]

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 restgdf.adapters.stream.iter_rows() or 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=...).

Return type:

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
>>> rows_to_geodataframe(
...     [{"OBJECTID": 1, "geometry": Point(0, 0)}],
...     crs="EPSG:4326",
... )

See also

restgdf.FeatureLayer.get_gdf()

High-level accessor that returns the full layer as a single GeoDataFrame.

restgdf.FeatureLayer.stream_gdf_chunks()

Async iterator yielding one GeoDataFrame per page, each with gdf.attrs["spatial_reference"] populated from layer metadata.