Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion dataretrieval/ogc/shaping.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@

logger = logging.getLogger(__name__)

# Water Data OGC coordinates are published in WGS84, so attach that CRS where the
# GeoDataFrame is built (otherwise the result is CRS-naive and ``to_crs`` /
# spatial joins fail). Mirrors the constants in ``nldi`` (EPSG:4326) and ``nwis``
# (EPSG:4269).
_CRS = "EPSG:4326"

# Whether geopandas is present is a static, environment-level fact, so warn
# once here at import time rather than per query/chunk.
if not GEOPANDAS:
Expand Down Expand Up @@ -141,7 +147,8 @@ def _get_resp_data(
# a plain DataFrame. Features that already carry geometry (the common
# sites case) are passed through without a per-feature dict copy.
df = gpd.GeoDataFrame.from_features(
[f if "geometry" in f else {**f, "geometry": None} for f in features]
[f if "geometry" in f else {**f, "geometry": None} for f in features],
crs=_CRS,
)
# Mirror the non-geopandas branch's defensive ``f.get("id")`` so a feature
# missing a top-level ``id`` yields None rather than a KeyError.
Expand Down
4 changes: 3 additions & 1 deletion dataretrieval/waterdata/stats.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
_run_sync,
)
from dataretrieval.ogc.shaping import (
_CRS,
GEOPANDAS,
_attach_coordinates,
_empty_feature_frame,
Expand Down Expand Up @@ -111,7 +112,8 @@ def _handle_nesting(
# can't ``KeyError`` on a stats feature that omits geometry — mirrors
# the guard in :func:`engine._get_resp_data`.
df = gpd.GeoDataFrame.from_features(
[f if "geometry" in f else {**f, "geometry": None} for f in features]
[f if "geometry" in f else {**f, "geometry": None} for f in features],
crs=_CRS,
).drop(columns=["data"], errors="ignore")

# Unnest json features, properties, data, and values while retaining necessary
Expand Down
52 changes: 52 additions & 0 deletions tests/waterdata_utils_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -596,6 +596,58 @@ class _Sentinel:
assert isinstance(result, _Sentinel)


def test_get_resp_data_attaches_wgs84_crs():
"""A geometry-bearing Water Data page should come back tagged as
EPSG:4326 (the CRS the coordinates are published in), so callers can
run ``to_crs`` / spatial joins without first patching in a CRS by
hand. Regression for the ``.crs is None`` reported in issue #342."""
geopandas = pytest.importorskip("geopandas")

resp = _resp_ok(
[
{
"type": "Feature",
"id": "USGS-01",
"geometry": {"type": "Point", "coordinates": [-76.5, 39.2]},
"properties": {"monitoring_location_id": "USGS-01"},
}
]
)
df = _get_resp_data(resp, geopd=True)
assert isinstance(df, geopandas.GeoDataFrame)
assert df.crs == "EPSG:4326"


def test_handle_nesting_attaches_wgs84_crs():
"""The stats path builds its GeoDataFrame the same way, so it should
carry EPSG:4326 too (issue #342)."""
geopandas = pytest.importorskip("geopandas")

body = {
"next": None,
"features": [
{
"type": "Feature",
"geometry": {"type": "Point", "coordinates": [-76.5, 39.2]},
"properties": {
"monitoring_location_id": "USGS-01",
"data": [
{
"parameter_code": "00060",
"unit_of_measure": "ft^3/s",
"parent_time_series_id": "ts-1",
"values": [{"statistic_id": "mean", "value": 10.0}],
}
],
},
}
],
}
df = _handle_nesting(body, geopd=True)
assert isinstance(df, geopandas.GeoDataFrame)
assert df.crs == "EPSG:4326"


def test_handle_nesting_tolerates_missing_features_key():
"""A 200 response with a body that doesn't carry ``features`` at
all (rare but seen in error envelopes) must also short-circuit
Expand Down