Metadata-Version: 2.4
Name: restgdf
Version: 2.0.0
Summary: improved esri rest io for geopandas
Author: Joshua Sundance Bailey
License-Expression: MIT
Project-URL: Homepage, https://github.com/joshuasundance-swca/restgdf
Project-URL: Documentation, https://restgdf.readthedocs.io/
Project-URL: Repository, https://github.com/joshuasundance-swca/restgdf
Project-URL: Issues, https://github.com/joshuasundance-swca/restgdf/issues
Project-URL: Changelog, https://github.com/joshuasundance-swca/restgdf/blob/main/CHANGELOG.md
Project-URL: Security Policy, https://github.com/joshuasundance-swca/restgdf/security/policy
Project-URL: Ask DeepWiki, https://deepwiki.com/joshuasundance-swca/restgdf
Project-URL: llms.txt, https://restgdf.readthedocs.io/en/latest/llms.txt
Keywords: geopandas,esri,arcgis,async,aiohttp,pydantic,rest,gis,featureserver,mapserver
Classifier: Development Status :: 5 - Production/Stable
Classifier: Framework :: AsyncIO
Classifier: Framework :: Pydantic
Classifier: Framework :: Pydantic :: 2
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Science/Research
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Scientific/Engineering :: GIS
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: aiohttp
Requires-Dist: geopandas
Requires-Dist: pandas
Requires-Dist: pydantic<3,>=2.13.3
Requires-Dist: eval_type_backport; python_version < "3.10"
Requires-Dist: pyogrio
Requires-Dist: requests
Provides-Extra: dev
Requires-Dist: bumpver; extra == "dev"
Requires-Dist: coverage; extra == "dev"
Requires-Dist: pre-commit; extra == "dev"
Requires-Dist: pytest; extra == "dev"
Requires-Dist: pytest-asyncio; extra == "dev"
Requires-Dist: setuptools>=61.0.0; extra == "dev"
Provides-Extra: doc
Requires-Dist: sphinx<10,>=8.0; extra == "doc"
Requires-Dist: furo>=2024.8.6; extra == "doc"
Requires-Dist: autodoc-pydantic<3,>=2.2; extra == "doc"
Requires-Dist: myst-parser[linkify]>=5.0; extra == "doc"
Requires-Dist: sphinx-copybutton>=0.5.2; extra == "doc"
Requires-Dist: sphinx-design>=0.7; extra == "doc"
Requires-Dist: sphinx-llm>=0.4; extra == "doc"
Dynamic: license-file

# restgdf

improved esri rest io for geopandas


<!-- Package -->
[![PyPI version](https://img.shields.io/pypi/v/restgdf.svg)](https://pypi.org/project/restgdf/)
[![Python versions](https://img.shields.io/pypi/pyversions/restgdf.svg)](https://pypi.org/project/restgdf/)
[![Downloads](https://static.pepy.tech/badge/restgdf/month)](https://pepy.tech/project/restgdf)
[![License](https://img.shields.io/github/license/joshuasundance-swca/restgdf.svg)](https://github.com/joshuasundance-swca/restgdf/blob/main/LICENSE)

<!-- Build & coverage -->
[![CI](https://img.shields.io/github/actions/workflow/status/joshuasundance-swca/restgdf/pytest.yml?event=pull_request&label=CI&logo=github)](https://github.com/joshuasundance-swca/restgdf/actions/workflows/pytest.yml)
[![Publish to PyPI](https://github.com/joshuasundance-swca/restgdf/actions/workflows/publish_on_pypi.yml/badge.svg)](https://github.com/joshuasundance-swca/restgdf/actions/workflows/publish_on_pypi.yml)
[![coverage](https://raw.githubusercontent.com/joshuasundance-swca/restgdf/main/coverage.svg)](https://github.com/joshuasundance-swca/restgdf/blob/main/COVERAGE.md)

<!-- Docs & discovery -->
[![Read the Docs](https://img.shields.io/readthedocs/restgdf)](https://restgdf.readthedocs.io/en/latest/)
[![llms.txt](https://img.shields.io/badge/llms.txt-green)](https://restgdf.readthedocs.io/en/latest/llms.txt)
[![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/joshuasundance-swca/restgdf)

<!-- Built with & code quality -->
[![Pydantic v2](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/pydantic/pydantic/main/docs/badge/v2.json)](https://docs.pydantic.dev/latest/contributing/#badges)
[![pre-commit](https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white)](https://github.com/pre-commit/pre-commit)
[![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v1.json)](https://github.com/astral-sh/ruff)
[![Checked with mypy](https://www.mypy-lang.org/static/mypy_badge.svg)](https://mypy-lang.org/)
[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
[![security: bandit](https://img.shields.io/badge/security-bandit-yellow.svg)](https://github.com/PyCQA/bandit)

## What's new in 2.0

restgdf 2.0 is a **major release** built on [pydantic 2.13](https://docs.pydantic.dev/).
See [`MIGRATION.md`](https://github.com/joshuasundance-swca/restgdf/blob/main/MIGRATION.md) for the full breaking-changes table and
code-rewrite recipes.

- **Typed responses.** `FeatureLayer.metadata`, `Directory.metadata` /
  `.services` / `.report`, and helpers like `get_metadata`, `safe_crawl`
  now return pydantic models instead of raw dicts.
- **Validated envelopes.** `get_feature_count`, `get_object_ids`, and
  token refresh surface malformed ArcGIS payloads as a typed
  `RestgdfResponseError` (with `model_name`, `context`, `raw`).
- **Schema-drift observability.** Vendor variance in permissive payloads
  (metadata, crawl) is logged through the opt-in `restgdf.schema_drift`
  logger instead of silently `KeyError`-ing.
- **Redacted credentials.** `AGOLUserPass.password` is a
  `pydantic.SecretStr` so passwords are never in `repr()` or logs.
- **Centralized settings.** `Settings` / `get_settings()` reads
  `RESTGDF_*` environment variables (chunk size, timeout, user agent,
  token URL, refresh threshold, etc.).
- **Migration helpers.** `restgdf.compat.as_dict` and `as_json_dict`
  convert any returned model back to a plain dict during a transitional
  upgrade window.
- **Deprecated shim.** `restgdf._types.*` still imports the legacy
  `TypedDict` names, but they now re-export the pydantic classes and
  emit `DeprecationWarning`. The shim will be removed in 3.x.
- **Dependency bump.** `pydantic>=2.13.3,<3` is a new required
  dependency.

`gpd.read_file(url, driver="ESRIJSON")` does not account for max record count limitations

so if you read a service with 100000 features but there's a limit of 1000 records per query, then your gdf will only have 1000 features

these functions use asyncio to read all features from a service, not limited by max record count

keyword arguments to `FeatureLayer.getgdf` are passed on to `aiohttp.ClientSession.post`; include query parameters like `where` and `token` in the `data` dict when needed

this enables enhanced control over queries and supports either direct `data={"token": "..."}` usage or a reusable `ArcGISTokenSession`

# Usage

```bash
pip install restgdf
```

```python
import asyncio

from aiohttp import ClientSession

from restgdf import FeatureLayer


beaches_url = r"https://maps1.vcgov.org/arcgis/rest/services/Beaches/MapServer/6"

zipcodes_url = "https://services.arcgis.com/P3ePLMYs2RVChkJx/ArcGIS/rest/services/USA_ZIP_Codes_2016/FeatureServer/0"

async def main():
    async with ClientSession() as session:
        beaches = await FeatureLayer.from_url(beaches_url, session=session)
        beaches_gdf = await beaches.getgdf()

        daytona = await beaches.where("LOWER(City) LIKE 'daytona%'")
        daytona_gdf = await daytona.getgdf()

        oh_zipcodes = await FeatureLayer.from_url(zipcodes_url, where="STATE = 'OH'", session=session)
        oh_zipcodes_gdf = await oh_zipcodes.getgdf()

    return beaches_gdf, daytona_gdf, oh_zipcodes_gdf


beaches_gdf, daytona_gdf, oh_zipcodes_gdf = asyncio.run(main())

print(beaches_gdf.shape)
# (243, 10)

print(daytona_gdf.shape)
# (83, 10)

print(oh_zipcodes_gdf.shape)
# (1026, 8)
```

## Token authentication

```python
import asyncio

from aiohttp import ClientSession

from restgdf import AGOLUserPass, ArcGISTokenSession, FeatureLayer


secured_url = "https://example.com/arcgis/rest/services/Secured/FeatureServer/0"


async def main():
    async with ClientSession() as base_session:
        token_session = ArcGISTokenSession(
            session=base_session,
            credentials=AGOLUserPass(
                username="my-username",
                password="my-password",
            ),
        )
        layer = await FeatureLayer.from_url(secured_url, session=token_session)
        return await layer.getgdf()


secured_gdf = asyncio.run(main())
```

If you already have a token, you can pass it with `token="..."` or `data={"token": "..."}`.

## Typed responses

Every response is a pydantic model. Attribute access replaces dict
indexing, and `model_dump(by_alias=True)` round-trips back to ArcGIS
camelCase:

```python
import asyncio

from aiohttp import ClientSession

from restgdf import FeatureLayer


async def main():
    async with ClientSession() as session:
        fl = await FeatureLayer.from_url(beaches_url, session=session)
        md = fl.metadata                      # restgdf.LayerMetadata
        return md.name, md.max_record_count, md.model_dump(by_alias=True)


name, max_record_count, arcgis_dict = asyncio.run(main())
```

Need a plain dict during a transitional migration? Use
`restgdf.compat.as_dict(md)`. See [`MIGRATION.md`](https://github.com/joshuasundance-swca/restgdf/blob/main/MIGRATION.md) for
the full 1.x → 2.0 rewrite table.

# Documentation

Full docs live at **<https://restgdf.readthedocs.io/>** (hosted by Read the Docs).

## Docs for humans *and* LLMs

Every page is published in three formats so you can feed it to a teammate
*or* to a language model without any preprocessing:

| Format                  | URL                                                                                                      |
| ----------------------- | -------------------------------------------------------------------------------------------------------- |
| Rendered HTML           | <https://restgdf.readthedocs.io/en/latest/>                                                              |
| Plain Markdown (per page) | append `.md` to any page — e.g. <https://restgdf.readthedocs.io/en/latest/quickstart.html.md>            |
| [llms.txt][llmstxt] index | <https://restgdf.readthedocs.io/en/latest/llms.txt>                                                      |
| llms-full.txt (all pages) | <https://restgdf.readthedocs.io/en/latest/llms-full.txt>                                                 |
| Ask DeepWiki           | <https://deepwiki.com/joshuasundance-swca/restgdf>                                                       |

[llmstxt]: https://llmstxt.org/

Point your coding agent or RAG pipeline at `llms-full.txt` for the entire
reference in a single file, or at `llms.txt` for a concise table of
contents.

# Uses

- [restgdf_api](https://github.com/joshuasundance-swca/restgdf_api)
- [govgis_nov2023](https://huggingface.co/datasets/joshuasundance/govgis_nov2023)
