Bounding-box and polygon scope ¶
When to use this ¶
You have a spatial filter — a bounding box from a map UI, a polygon from a planning area, or a buffered route — and want only the rows whose geometry falls inside it. Works on any geometry-bearing table; not GMNS-specific.
Quick example ¶
Load the bundled Leavenworth fixture, build a bbox view over the links table, and count the rows that fall inside:
from netstead import Network
from netstead.fixtures import leavenworth
from corral.dataset.view import from_bbox
net = Network.from_source(leavenworth.csv_dir())
view = from_bbox(net.links, minx=-120.67, miny=47.58, maxx=-120.65, maxy=47.60)
print(f"{view.count()} links in bbox")
The view is lazy — no rows materialise until you call .to_pandas() or iterate.
Step-by-step ¶
1. Bounding box (any table) ¶
from_bbox is generic — it works on any Table whose schema declares a WKT geometry column. No shapely required for bbox; the predicate is pure coordinate math:
from corral.dataset.view import from_bbox
view = from_bbox(net.links, minx=-120.67, miny=47.58, maxx=-120.65, maxy=47.60)
df = view.to_pandas()
2. Polygon (shapely or WKT) ¶
For non-rectangular regions, pass a shapely Polygon. Shapely is in the [clean] extra:
from corral.dataset.view import from_polygon
from shapely.geometry import Polygon
poly = Polygon([
(-120.67, 47.58),
(-120.65, 47.58),
(-120.65, 47.60),
(-120.67, 47.60),
(-120.67, 47.58),
])
view = from_polygon(net.links, poly)
If shapely isn’t installed, pass a WKT string instead — the function parses it directly:
3. Buffered geometry ¶
Buffer a geometry (in metres, regardless of CRS — the helper reprojects) and filter to anything inside. Useful for “everything within 100 m of this corridor”:
from corral.dataset.view import from_geometry_buffer
from shapely.geometry import LineString
corridor = LineString([(-120.67, 47.58), (-120.65, 47.60)])
view = from_geometry_buffer(net.links, corridor, distance_m=100)
4. Predicate pushdown ¶
When the underlying source is partitioned Parquet, the spatial predicate compiles to a single DuckDB WHERE clause. Partitions whose statistics fall entirely outside the bbox are pruned before reading. Typical speed-up on a regional network is 10–50× vs full scan:
net = Network.from_source("s3://my-bucket/regional/links.parquet")
# Only the parquet partitions overlapping the bbox are read off S3.
view = from_bbox(net.links, minx=-120.67, miny=47.58, maxx=-120.65, maxy=47.60)
Inspect the compiled predicate via view.explain() — useful when debugging “why is this slow?” against an S3 source. The explain output names every partition that was kept or pruned.
5. Compose with other filters ¶
Spatial views compose with the rest of the View API. The bbox + facility-type predicate fuse into a single SQL pass — no intermediate materialisation:
inside = from_bbox(net.links, -120.67, 47.58, -120.65, 47.60)
arterials = inside.filter(net.links.facility_type == "arterial")
arterials.to_pandas()
Common variations ¶
Default — bbox filter on a single table
Returns a lazy View over the matched rows.
Filter the whole network with FK pushdown
For GMNS networks, use the netstead scope ops instead — they walk the FK graph and produce a fully consistent sub-network.
WGS84 bbox against a projected source
from_bbox compares numbers; it does not reproject. Reproject the bbox to the source CRS first.
Buffered point (everything within 500 m of a location)
from_geometry_buffer accepts any shapely geometry — Point, LineString, Polygon.
Pitfalls ¶
- CRS mismatch is silent.
from_bboxcompares numbers — degrees-vs-metres mismatches produce empty results, not errors. Checknet.spec.crsfirst. distance_monly works if CRS is known. If the source declares no CRS,from_geometry_bufferraises. Setcrs=on the source, or pre-buffer in the geometry’s native units and pass viafrom_polygon.- Shapely needed for polygon input (not bbox).
pip install netstead[clean]brings it in. - Single-table scope is not FK-aware. If you bbox-filter
linkand want only the dependentlanerows too, use the network-aware scope helpers innetstead.scope(from_nodes,from_link,connected_component) instead — those walk the FK graph.
See also ¶
- Build a scope from seed nodes — network-graph scope (vs spatial).
- API reference —
corral.dataset.view.from_bbox,from_polygon,from_geometry_buffer.