a-b-street/abstreet · error · anyhow::Error

couldn't find where shape leaves map

Error message

couldn't find where shape leaves map

What it means

The mirror case of route entry: create_route computes the route's exit point as the last intersection between the raw GTFS shape and the map boundary ring. If there are no intersections at all (so .last() is None), the shape never leaves the map and this error is thrown, preventing selection of an outgoing border.

Solutions

  1. Fall back to snapping the last shape point to the nearest outgoing border when no intersection exists
  2. Confirm shape and boundary use identical projections/CRS
  3. Skip contained routes with a warning rather than failing the import
  4. If loops are legitimate, special-case them to terminate at the entry border instead

Example fix

// before
let exit_pt = *map.boundary_polygon.get_outer_ring().all_intersections(&route.shape).last().ok_or_else(|| anyhow!("couldn't find where shape leaves map"))?;
// after
let exit_pt = match map.boundary_polygon.get_outer_ring().all_intersections(&route.shape).last() {
    Some(pt) => *pt,
    None => {
        warn!("route {} never leaves the map; reusing entry border as exit", route.route_id);
        entry_pt
    }
};
Defensive patterns

Strategy: fallback

Validate before calling

let hits = map.boundary_polygon.get_outer_ring().all_intersections(&route.shape);
if hits.is_empty() {
    // route neither enters nor leaves; plan border handling up front
}

Try / catch

match create_route(...) { Err(e) if e.to_string().contains("shape leaves map") => { warn!("contained route: {}", e); continue; }, r => r }

Prevention

When it happens

Trigger: Calling finalize_transit on a route whose shape polyline has zero intersections with map.boundary_polygon.get_outer_ring(); the same empty all_intersections result as the entry check, hit at the .last() call.

Common situations: Circular or loop routes entirely contained in the map; feeds where the last trip shape terminates inside the boundary; coordinate projection mismatches; boundary polygon edits that now fully contain the shape.

Understand the failure class

Background: EmptyResultError / "no results found": when an API or scraper succeeds but returns zero rows — this error's family across 9 libraries.

Related errors


AI-assisted analysis of a-b-street/abstreet@0964f29315 (2026-09-13). Data as JSON: /api/errors/30948aa3ff47ee8b. Report an issue: GitHub.

Appendix: source

Thrown at map_model/src/make/transit.rs:196

            Some((l, _)) => l,
            None => bail!(
                "Couldn't find a {:?} border near start {}",
                route.route_type,
                entry_pt
            ),
        }
    };

    let end_border = if map.boundary_polygon.contains_pt(route.shape.last_pt()) {
        None
    } else {
        // Find the last time the route shape hits the map boundary
        let exit_pt = *map
            .boundary_polygon
            .get_outer_ring()
            .all_intersections(&route.shape)
            .last()
            .ok_or_else(|| anyhow!("couldn't find where shape leaves map"))?;
        // Snap that to a border
        let borders = if route.route_type == RawTransitType::Bus {
            &snapper.bus_outgoing_borders
        } else {
            &snapper.train_outgoing_borders
        };
        match borders.closest_pt(exit_pt, border_snap_threshold) {
            Some((lane, _)) => {
                // Edge case: the last stop is on the same road as the border. We can't lane-change
                // suddenly, so match the lane in that case.
                let last_stop_lane = map.get_ts(*stops.last().unwrap()).driving_pos.lane();
                Some(if lane.road == last_stop_lane.road {
                    last_stop_lane
                } else {
                    lane
                })
            }
            None => bail!(

View on GitHub (pinned to 0964f29315)