{"record":{"id":"30948aa3ff47ee8b","repo":"a-b-street/abstreet","slug":"couldn-t-find-where-shape-leaves-map","errorCode":null,"errorMessage":"couldn't find where shape leaves map","messagePattern":"couldn't find where shape leaves map","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"map_model/src/make/transit.rs","lineNumber":196,"sourceCode":"            Some((l, _)) => l,\n            None => bail!(\n                \"Couldn't find a {:?} border near start {}\",\n                route.route_type,\n                entry_pt\n            ),\n        }\n    };\n\n    let end_border = if map.boundary_polygon.contains_pt(route.shape.last_pt()) {\n        None\n    } else {\n        // Find the last time the route shape hits the map boundary\n        let exit_pt = *map\n            .boundary_polygon\n            .get_outer_ring()\n            .all_intersections(&route.shape)\n            .last()\n            .ok_or_else(|| anyhow!(\"couldn't find where shape leaves map\"))?;\n        // Snap that to a border\n        let borders = if route.route_type == RawTransitType::Bus {\n            &snapper.bus_outgoing_borders\n        } else {\n            &snapper.train_outgoing_borders\n        };\n        match borders.closest_pt(exit_pt, border_snap_threshold) {\n            Some((lane, _)) => {\n                // Edge case: the last stop is on the same road as the border. We can't lane-change\n                // suddenly, so match the lane in that case.\n                let last_stop_lane = map.get_ts(*stops.last().unwrap()).driving_pos.lane();\n                Some(if lane.road == last_stop_lane.road {\n                    last_stop_lane\n                } else {\n                    lane\n                })\n            }\n            None => bail!(","sourceCodeStart":178,"sourceCodeEnd":214,"githubUrl":"https://github.com/a-b-street/abstreet/blob/0964f29315820c91b171b585eb51e300164e9197/map_model/src/make/transit.rs#L178-L214","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Fall back to snapping the last shape point to the nearest outgoing border when no intersection exists","Confirm shape and boundary use identical projections/CRS","Skip contained routes with a warning rather than failing the import","If loops are legitimate, special-case them to terminate at the entry border instead"],"exampleFix":"// before\nlet exit_pt = *map.boundary_polygon.get_outer_ring().all_intersections(&route.shape).last().ok_or_else(|| anyhow!(\"couldn't find where shape leaves map\"))?;\n// after\nlet exit_pt = match map.boundary_polygon.get_outer_ring().all_intersections(&route.shape).last() {\n    Some(pt) => *pt,\n    None => {\n        warn!(\"route {} never leaves the map; reusing entry border as exit\", route.route_id);\n        entry_pt\n    }\n};","handlingStrategy":"fallback","validationCode":"let hits = map.boundary_polygon.get_outer_ring().all_intersections(&route.shape);\nif hits.is_empty() {\n    // route neither enters nor leaves; plan border handling up front\n}","typeGuard":null,"tryCatchPattern":"match create_route(...) { Err(e) if e.to_string().contains(\"shape leaves map\") => { warn!(\"contained route: {}\", e); continue; }, r => r }","preventionTips":["Special-case fully-contained loop routes","Clip shapes explicitly to a buffered boundary so they always cross it","Validate feed shapes against the boundary at import time","Keep projections identical between shapes and map geometry"],"tags":["gis","transit","gtfs","geometry"],"backgroundTag":"empty-result-set","analyzedSha":"0964f29315820c91b171b585eb51e300164e9197","analyzedAt":"2026-09-13T18:02:03.421Z","contentChangedAt":"2026-09-13T18:02:03.421Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}