Skip to content

Commit 7870f2d

Browse files
author
Nathaniel Henry
committed
Normalize blocks_query list params, reproject TIGER blocks to 4326, use real example GEOIDs
1 parent ae8670a commit 7870f2d

5 files changed

Lines changed: 25 additions & 9 deletions

File tree

docs/getting_started.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ Set the client's `spatial` flag to `False` to work with the raw data.
7777

7878
```{code-cell} python
7979
close.spatial = False
80-
summary = close.block_summary("440070036001010", mode = "walk")
80+
summary = close.block_summary("440070008001068", mode = "walk")
8181
summary.results
8282
```
8383

docs/tutorials/home_search.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,11 +63,13 @@ stops.plot(ax = ax, color = "#f36e21", marker = "^")
6363

6464
## Find the blocks that qualify
6565

66-
Pull the per-block walk times for the whole city. The result is a GeoDataFrame with
67-
one row per (block, category); the boundaries come from `pygris`, downloaded once.
66+
Pull the per-block walk times for the blocks around the city centre. The result is a
67+
GeoDataFrame with one row per (block, category); the boundaries come from `pygris`,
68+
downloaded once.
6869

6970
```{code-cell} python
70-
blocks = close.place_blocks(city["geoid"], mode = "walk",
71+
blocks = close.blocks_query(center = {"lon": city["lon"], "lat": city["lat"]},
72+
radius_m = 3000, mode = "walk",
7173
type = [grocery, restaurant, transit])
7274
```
7375

src/closecity/client.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,14 @@ def _clean(params: dict[str, Any] | None) -> dict[str, Any]:
2525
return {k: v for k, v in (params or {}).items() if v is not None}
2626

2727

28+
def _as_list(value: Any) -> Any:
29+
"""Wrap a scalar in a list, for POST-body fields the API requires as arrays.
30+
``None`` and existing lists/tuples pass through unchanged."""
31+
if value is None or isinstance(value, (list, tuple)):
32+
return value
33+
return [value]
34+
35+
2836
def _int(value: str | None) -> int | None:
2937
return int(value) if value is not None else None
3038

@@ -341,8 +349,8 @@ def blocks_query(
341349
`spatial=False`."""
342350
body = _clean({
343351
"polygon": polygon, "center": center, "radius_m": radius_m,
344-
"type": type, "mode": mode, "include_population": include_population,
345-
"limit": limit,
352+
"type": _as_list(type), "mode": _as_list(mode),
353+
"include_population": include_population, "limit": limit,
346354
})
347355
return self._spatial(Paginator(self, "POST", "/v1/blocks/query",
348356
_json = body, _cursor_in = "json"))

src/closecity/spatial.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,9 +103,12 @@ def _blocks_gdf(rows, block_geometry, geoid_col, crs, fetch, gpd):
103103
"blocks with pygris."
104104
)
105105
geo = block_geometry.rename(columns = {geoid_col: "geoid"})
106+
# TIGER blocks arrive in NAD83 (EPSG:4269); reproject so they match the POI and
107+
# isochrone geometry (EPSG:4326) and can be combined without a CRS mismatch.
108+
if crs is not None and geo.crs is not None and str(geo.crs) != str(crs):
109+
geo = geo.to_crs(crs)
106110
merged = df.merge(geo[["geoid", "geometry"]], on = "geoid", how = "left")
107-
out_crs = getattr(block_geometry, "crs", None) or crs
108-
return gpd.GeoDataFrame(merged, geometry = "geometry", crs = out_crs)
111+
return gpd.GeoDataFrame(merged, geometry = "geometry", crs = crs)
109112

110113

111114
def to_geopandas(

tests/test_client.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,13 +147,16 @@ def handler(request):
147147

148148
pg = make_client(handler).blocks_query(
149149
center = {"lon": -123.0, "lat": 44.0}, radius_m = 1000,
150-
include_population = True,
150+
mode = "walk", type = 30, include_population = True,
151151
)
152152
records = list(pg)
153153
assert methods == ["POST", "POST"]
154154
assert len(records) == 2
155155
assert bodies[0]["center"] == {"lon": -123.0, "lat": 44.0}
156156
assert bodies[0]["include_population"] is True
157+
# Scalar mode/type are wrapped in lists (the POST body needs arrays).
158+
assert bodies[0]["mode"] == ["walk"]
159+
assert bodies[0]["type"] == [30]
157160
assert bodies[1]["cursor"] == "C2" # cursor threaded through the body
158161

159162

0 commit comments

Comments
 (0)