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

No building within 100m of

Error message

No building within 100m of {}

What it means

synthpop's import converts an external trip endpoint given as a GPS position into a TripEndpoint. If the GPS point lies inside the map boundary but no building is within 100 meters (per the prebuilt ClosestEdges structure), the conversion fails with this error. It guards against positions too far from any mapped building to snap to.

Solutions

  1. Snap such GPS points to the nearest border instead (the code already does this for outside-boundary points).
  2. Increase the 100m search radius in closest_pt when maps have sparse building coverage.
  3. Pre-filter external trip data to drop endpoints far from any building.
  4. Rebuild the map ensuring building extraction covers the area containing the trips.

Example fix

// before
match closest.closest_pt(pt, Distance::meters(100.0)) {
    Some((x, _)) => Ok(x),
    None => Err(anyhow!("No building within 100m of {}", gps)),
}
// after
match closest.closest_pt(pt, Distance::meters(100.0)).or_else(|| closest.closest_pt(pt, Distance::meters(500.0))) {
    Some((x, _)) => Ok(x),
    None => Ok(TripEndpoint::Border(nearest_border)), // fallback
}
Defensive patterns

Strategy: fallback

Validate before calling

// Pre-filter external trips before import
fn snap_ok(gps: &GPS, map: &Map) -> bool {
    let pt = gps.to_pt(map.get_gps_bounds());
    !map.get_boundary_polygon().contains_pt(pt) || pt.is_within(<> /* 100m of some building */)
}

Try / catch

// Catch and snap to the nearest border instead
match import(...) {
    Err(e) if e.to_string().starts_with("No building within 100m") => {
        import_as_border_trip(...)  // reuse outside-boundary logic
    }
    other => other,
}

Prevention

When it happens

Trigger: Importing external trip data (e.g., from a data source like元气city/seattle data) where an origin/destination GPS coordinate is inside the map polygon but more than 100m from the nearest building in closest_pt's index.

Common situations: GPS coordinates in parks, water bodies, industrial areas, or newly developed zones with no buildings in the map; maps built with building filtering that removed nearby buildings; slightly-out-of-bounds coordinates that pass the polygon check.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


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

Appendix: source

Thrown at synthpop/src/external.rs:57

    pub fn import(
        map: &Map,
        input: Vec<ExternalPerson>,
        skip_problems: bool,
    ) -> Result<Vec<PersonSpec>> {
        let mut closest: FindClosest<TripEndpoint> = FindClosest::new();
        for b in map.all_buildings() {
            closest.add_polygon(TripEndpoint::Building(b.id), &b.polygon);
        }
        let borders = MapBorders::new(map);

        let lookup_pt = |endpt, is_origin, mode| match endpt {
            ExternalTripEndpoint::TripEndpoint(endpt) => Ok(endpt),
            ExternalTripEndpoint::Position(gps) => {
                let pt = gps.to_pt(map.get_gps_bounds());
                if map.get_boundary_polygon().contains_pt(pt) {
                    match closest.closest_pt(pt, Distance::meters(100.0)) {
                        Some((x, _)) => Ok(x),
                        None => Err(anyhow!("No building within 100m of {}", gps)),
                    }
                } else {
                    let (incoming, outgoing) = borders.for_mode(mode);
                    let candidates = if is_origin { incoming } else { outgoing };
                    Ok(TripEndpoint::Border(
                        candidates
                            .iter()
                            .min_by_key(|border| border.gps_pos.fast_dist(gps))
                            .ok_or_else(|| anyhow!("No border for {}", mode.ongoing_verb()))?
                            .i,
                    ))
                }
            }
        };

        let mut results = Vec::new();
        for person in input {
            let mut spec = PersonSpec {

View on GitHub (pinned to 0964f29315)