a-b-street/abstreet · error
Unexpected geometry type for
Error message
Unexpected geometry type for {:?} What it means
clip_zones parses GeoJSON census features into geo::Geometry and only accepts Polygon or MultiPolygon (taking the first polygon of a MultiPolygon). Any other geometry type — e.g. LineString, Point, or GeometryCollection — hits the catch-all bail. It protects downstream polygon clipping, which requires areal geometry.
Solutions
- Filter the FeatureCollection to features whose geometry.type is "Polygon" or "MultiPolygon" before calling clip_zones.
- Inspect the failing feature's properties (printed in the message) and remove/fix that record in the source data.
- Buffer points/lines into polygons beforehand if they are legitimate zones.
- Patch clip_zones to handle the geometry type (e.g. keep all polygons of a MultiPolygon, skip others).
Example fix
// before: feed all features let zones = clip_zones(&boundary, gj_str)?; // after: pre-filter let kept: Vec<_> = fc.features.into_iter().filter(|f| matches!(f.geometry_type(), "Polygon" | "MultiPolygon")).collect();
Defensive patterns
Strategy: validation
Validate before calling
let ok = fc.features.iter().all(|f| {
matches!(f.geometry_type(), "Polygon" | "MultiPolygon")
});
if !ok { return Err("census data contains non-polygon features".into()); } Type guard
fn is_areal(f: &GeoJsonFeature) -> bool {
matches!(f.geometry_type(), "Polygon" | "MultiPolygon")
} Try / catch
match clip_zones(&boundary, gj_str) {
Err(e) if e.to_string().starts_with("Unexpected geometry type") => {
// filter out the offending feature (named in the message) and retry
}
other => other?,
} Prevention
- Pre-filter census GeoJSON to Polygon/MultiPolygon features.
- Check geometry.type in the downloaded dataset before processing.
- Buffer or drop point/line features that sneak into boundary datasets.
When it happens
Trigger: Calling clip_zones (popgetter/src/lib.rs:46) with an input GeoJSON FeatureCollection whose features contain non-(multi)polygon geometries; the `try_into` succeeds (any geometry is allowed) but the match falls through to `_ => bail!`.
Common situations: Census boundary downloads containing point/line features (address points, boundary segments), mixed collections, or TopoJSON conversions producing unusual shapes; TODO in code notes MultiPolygon handling is itself questionable.
Related errors
- Unexpected topojson contents
- missing bound rect
- ( ) is a border, but is connected to >1 road
- isn't an endpoint of
- pathfind() returned path that warps
AI-assisted analysis of a-b-street/abstreet@0964f29315 (2026-09-13).
Data as JSON: /api/errors/1de5d2cfad215952.
Report an issue: GitHub.
Appendix: source
Thrown at popgetter/src/lib.rs:46
}
/// Clips existing TopoJSON files to the given boundary. All polygons are in WGS84.
pub fn clip_zones(
topojson_path: &str,
boundary: geo::Polygon<f64>,
) -> Result<Vec<(geo::Polygon<f64>, CensusZone)>> {
let gj = load_all_zones_as_geojson(topojson_path)?;
let start = Instant::now();
let mut output = Vec::new();
for gj_feature in gj {
let geom: geo::Geometry<f64> = gj_feature.clone().try_into()?;
if boundary.intersects(&geom) {
let polygon = match geom {
geo::Geometry::Polygon(p) => p,
// TODO What're these, and what should we do with them?
geo::Geometry::MultiPolygon(mut mp) => mp.0.remove(0),
_ => bail!("Unexpected geometry type for {:?}", gj_feature.properties),
};
let census_zone = CensusZone {
id: gj_feature
.property("ID")
.unwrap()
.as_str()
.unwrap()
.to_string(),
cars_0: gj_feature
.property("cars_0")
.unwrap()
.as_u64()
.unwrap()
.try_into()?,
cars_1: gj_feature
.property("cars_1")
.unwrap()
.as_u64()View on GitHub (pinned to 0964f29315)