facett-map3d/tests/camera_picks_the_town.rs
//! **The 3D pane must extrude where the user is LOOKING, not where the houses are
//! thickest.**
//!
//! The owner panned korp's 2D map to central Stockholm, pressed the 2D⇄3D toggle, and got
//! **Sigtuna** — a town 37 km away that he had not asked for and could not see. The
//! cause was two layers deep:
//!
//! 1. korp built the 3D scene from `self.map_ways` alone. The map camera existed, was
//! correct, and was never passed. Where the user was looking had no bearing on what
//! was extruded.
//! 2. `from_geometry` therefore had to guess, and it guessed by DENSITY —
//! `densest_cluster_subset` votes on a grid of building centroids and takes the
//! heaviest cell. A dormitory grid of small houses out-counts an inner city of large
//! blocks, so the vote lands in a suburb. Reliably. It is not noise.
//!
//! The subsetting itself is right and must stay: at region scale one design unit is
//! ~35 km, an 8 m house is 2e-4 units, and the pane draws a flat white pancake with
//! every count green (MEASURED 2026-08-03, live korp-server, RTX 4090: 340 905 building
//! vertices, median house **0.66 px**). The bug is the *choice of town*.
//!
//! # Why this file asserts on GEOGRAPHY
//!
//! Every observable the 3D viewer published was a COUNT — triangles, building vertices,
//! `buildings_visible` — and a suburb has just as many houses as a city. A test written
//! on counts is green on Sigtuna. That is not a hypothetical: it is what shipped.
//!
//! So every claim here is a **distance in metres** between the camera's centre and the
//! extruded scene's centre, and the fixture is built so the two candidate answers are
//! 37 km apart. A test whose viewport happened to contain the densest cluster could not
//! tell a working camera from a dead one (the identity-value trap); this one can.
use facett_core::{Facet, Theme};
use facett_map::Way;
use facett_map3d::{Map3D, extrude};
/// The ONE writer of the flat-Earth ruler these fixtures are built on: the extruder's
/// own [`facett_map3d::extrude::M_PER_DEG_LAT`]. A fixture built on a private copy of
/// 111_320 measures a DIFFERENT earth from the code under test the moment either side is
/// edited — and the copies agree until then, which is exactly how this survived in nine
/// files (`granska-20`).
use facett_map3d::extrude::M_PER_DEG_LAT;
/// **Central Stockholm** (Gamla stan / Slussen) — where the owner panned to.
const STOCKHOLM: (f64, f64) = (18.0686, 59.3293);
/// **Sigtuna** — 37 km north-west, and what he got instead.
const SIGTUNA: (f64, f64) = (17.7239, 59.6169);
/// korp's MEASURED z12 viewport span at Stockholm on a 1900x1150 window (2026-08-03).
/// Both towns fall inside one such load, which is precisely why the pane can show the
/// wrong one.
const KORP_Z12_SPAN_M: f64 = 55_688.0;
use facett_map3d::extrude::m_per_deg_lon;
fn ring(lon: f64, lat: f64, half_lon: f64, half_lat: f64) -> Vec<(f64, f64)> {
vec![
(lon - half_lon, lat - half_lat),
(lon + half_lon, lat - half_lat),
(lon + half_lon, lat + half_lat),
(lon - half_lon, lat + half_lat),
(lon - half_lon, lat - half_lat),
]
}
fn tags(kv: &[(&str, &str)]) -> Vec<(String, String)> {
kv.iter()
.map(|(k, v)| (k.to_string(), v.to_string()))
.collect()
}
/// `n × n` untagged 12 m houses on a `pitch_m` grid centred on `(clon, clat)`, plus one
/// street through them so the place is a town and not a field of boxes.
fn houses(id0: i64, centre: (f64, f64), n: usize, pitch_m: f64) -> Vec<Way> {
let (clon, clat) = centre;
let mpdl = m_per_deg_lon(clat);
let theme = Theme::default();
let (h_lon, h_lat) = (6.0 / mpdl, 6.0 / M_PER_DEG_LAT);
let mut ways = Vec::new();
let mut id = id0;
for j in 0..n {
for i in 0..n {
let dx = (i as f64 - (n as f64 - 1.0) * 0.5) * pitch_m;
let dy = (j as f64 - (n as f64 - 1.0) * 0.5) * pitch_m;
ways.extend(Way::from_lonlat(
id,
&ring(clon + dx / mpdl, clat + dy / M_PER_DEG_LAT, h_lon, h_lat),
&tags(&[("building", "yes")]),
&theme,
));
id += 1;
}
}
let street_half = (n as f64 * pitch_m * 0.5) / mpdl;
ways.push(Way::line(
id,
"road",
vec![(clon - street_half, clat), (clon + street_half, clat)],
egui::Color32::GRAY,
2.0,
));
ways
}
/// **The fixture: one korp z12 load holding TWO towns, and the denser one is not the one
/// the user is looking at.**
///
/// Stockholm gets 18×18 houses on a 55 m pitch (an inner city: large blocks, wide
/// spacing); Sigtuna gets 26×26 on a 32 m pitch (a dormitory grid: more houses, packed
/// tighter, in a smaller box). That is the real asymmetry — density and importance are
/// different quantities — and it is what makes the two answers differ by 37 km.
///
/// Plus a lake and two trunk roads across the whole extent, because a korp viewport is
/// mostly ground cover and that is what drags the scene scale.
fn two_towns() -> Vec<Way> {
let (clon, clat) = STOCKHOLM;
let mpdl = m_per_deg_lon(clat);
let half_lat = 0.5 * KORP_Z12_SPAN_M / M_PER_DEG_LAT;
let half_lon = 0.5 * KORP_Z12_SPAN_M / mpdl;
let theme = Theme::default();
let mut ways = Vec::new();
// Region ground cover + trunk roads spanning the whole load.
ways.extend(Way::from_lonlat(
1,
&ring(
clon - half_lon * 0.3,
clat + half_lat * 0.2,
half_lon * 0.3,
half_lat * 0.3,
),
&tags(&[("natural", "water")]),
&theme,
));
ways.push(Way::line(
2,
"road",
vec![(clon - half_lon, clat), (clon + half_lon, clat)],
egui::Color32::GRAY,
2.0,
));
ways.push(Way::line(
3,
"road",
vec![(SIGTUNA.0, clat - half_lat), (SIGTUNA.0, clat + half_lat)],
egui::Color32::GRAY,
2.0,
));
ways.extend(houses(1_000, STOCKHOLM, 18, 55.0));
ways.extend(houses(9_000, SIGTUNA, 26, 32.0));
ways
}
/// A [`extrude::ViewWindow`] for a korp viewport centred on `centre` spanning `span_m`.
fn view(centre: (f64, f64), span_m: f64) -> extrude::ViewWindow {
extrude::ViewWindow {
lon: centre.0,
lat: centre.1,
span_m,
}
}
/// **THE FIXTURE IS THE TEST'S OWN RED PROBE.** Before any claim about the camera, prove
/// that the two candidate answers are far apart and that density really does pick the
/// wrong one — otherwise every assertion below is sitting on an identity value and a
/// dead camera would pass it.
#[test]
fn density_and_the_camera_give_answers_37_km_apart() {
let ways = two_towns();
let dens = extrude::town_window(&ways, None).expect("the region has buildings");
assert_eq!(
dens.source,
extrude::WindowSource::Densest,
"no camera ⇒ the vote decides"
);
let d_to_sigtuna = extrude::geo_distance_m((dens.lon, dens.lat), SIGTUNA);
let d_to_stockholm = extrude::geo_distance_m((dens.lon, dens.lat), STOCKHOLM);
println!(
"density votes ({:.4}, {:.4}): {d_to_sigtuna:.0} m from Sigtuna, \
{d_to_stockholm:.0} m from Stockholm ({} buildings)",
dens.lon, dens.lat, dens.buildings,
);
assert!(
d_to_sigtuna < 2_000.0 && d_to_stockholm > 30_000.0,
"the fixture must reproduce the defect: density has to land on the SUBURB \
({d_to_sigtuna:.0} m from Sigtuna, {d_to_stockholm:.0} m from Stockholm). \
If this fails the fixture has stopped being the Sigtuna case and every test \
below has stopped proving anything.",
);
// …and the separation is real, so "within the viewport" is a claim with teeth.
let sep = extrude::geo_distance_m(STOCKHOLM, SIGTUNA);
assert!(sep > 30_000.0, "the two answers are {sep:.0} m apart");
}
/// **THE FIX, on applied output: with the camera over Stockholm, the extruded scene is
/// over Stockholm.**
///
/// Asserted as a distance in metres against the viewport's own extent, not as a count.
#[test]
fn the_extruded_scene_sits_under_the_camera() {
let ways = two_towns();
let v = view(STOCKHOLM, KORP_Z12_SPAN_M);
let map = Map3D::from_geometry_in_view("stockholm", &ways, Some(v));
let centre = map
.geo_centre()
.expect("real lon/lat data has a geographic centre");
let d = extrude::geo_distance_m(centre, STOCKHOLM);
println!(
"camera ({:.4}, {:.4}) → scene ({:.4}, {:.4}): {d:.0} m [window {:?}]",
v.lon,
v.lat,
centre.0,
centre.1,
map.town_window().map(|w| w.source),
);
// 1. The camera chose the window, not the vote.
assert_eq!(
map.town_window().map(|w| w.source),
Some(extrude::WindowSource::Camera),
"a viewport full of buildings is a place the user is looking at",
);
// 2. The scene's centre is INSIDE the viewport — the geographic claim.
let (bb_lon, bb_lat) = (
0.5 * KORP_Z12_SPAN_M / m_per_deg_lon(STOCKHOLM.1),
0.5 * KORP_Z12_SPAN_M / M_PER_DEG_LAT,
);
assert!(
(centre.0 - STOCKHOLM.0).abs() <= bb_lon && (centre.1 - STOCKHOLM.1).abs() <= bb_lat,
"the extruded scene must lie inside the viewport the user is looking at: \
scene ({:.4}, {:.4}) vs viewport ({:.4} ± {bb_lon:.4}, {:.4} ± {bb_lat:.4})",
centre.0,
centre.1,
STOCKHOLM.0,
STOCKHOLM.1,
);
// 3. …and not merely inside it: CENTRED on it, to a small fraction of the viewport.
// A scene that clips the far corner of the viewport is still the wrong town.
assert!(
d < 0.1 * KORP_Z12_SPAN_M,
"camera→scene distance {d:.0} m must be small against the {KORP_Z12_SPAN_M:.0} m \
viewport; the wrong-town answer is 37 000 m away",
);
// 4. NON-VACUITY: it is a real town, not an empty box that trivially centres itself.
assert!(
map.building_vertices() > 0,
"the scene has risen buildings in it"
);
assert!(
map.triangle_count() > 100,
"the scene has a real mesh, got {}",
map.triangle_count()
);
}
/// **The same call with no camera still lands on Sigtuna** — the control that proves the
/// assertion above is measuring the camera and not a fixture that centres itself.
///
/// This is the line that goes RED if the camera is ever unwired again: it is the current
/// shipped behaviour, pinned, so the difference between the two paths is visible in the
/// test output as a pair of numbers.
#[test]
fn without_a_camera_the_scene_is_still_the_wrong_town() {
let ways = two_towns();
let blind = Map3D::from_geometry("no camera", &ways);
let bc = blind.geo_centre().expect("a geographic centre");
let d_blind = extrude::geo_distance_m(bc, STOCKHOLM);
let aimed =
Map3D::from_geometry_in_view("camera", &ways, Some(view(STOCKHOLM, KORP_Z12_SPAN_M)));
let ac = aimed.geo_centre().expect("a geographic centre");
let d_aimed = extrude::geo_distance_m(ac, STOCKHOLM);
println!("camera→scene: blind {d_blind:.0} m aimed {d_aimed:.0} m");
assert!(
d_blind > 30_000.0,
"the camera-blind path is the defect: it must still be the far town ({d_blind:.0} m)",
);
assert!(
d_aimed * 20.0 < d_blind,
"the aimed path must be at least an order of magnitude closer: \
{d_aimed:.0} m vs {d_blind:.0} m",
);
}
/// **A country overview has a centre, and that centre is open water — and the answer is
/// now to REFUSE, not to vote.**
///
/// This test used to assert the opposite (`WindowSource::Densest`), because the vote was
/// believed to be the honest fallback for "nothing under the camera". The 2026-08-05
/// measurement on oden's live sweden pyramid says otherwise — see
/// [`extrude::TownChoice::TooCoarse`] and the z9 test below. A country overview now
/// extrudes NOTHING and the pane says which way to zoom; the vote survives only for
/// callers that have no camera at all ([`extrude::densest_cluster_subset`]).
#[test]
fn a_country_overview_refuses_rather_than_naming_a_town_the_user_cannot_see() {
let ways = two_towns();
// A viewport spanning most of Sweden, centred out at sea (east of Gotland).
let sea = view((19.6, 57.5), 900_000.0);
match extrude::choose_town(&ways, Some(sea)) {
extrude::TownChoice::TooCoarse(r) => {
println!(
"country overview → refused: {} buildings in {:.0} m, {} needed",
r.buildings,
2.0 * r.half_m,
r.needed
);
assert!(
r.buildings < r.needed,
"the refusal carries the numbers behind it"
);
assert_eq!(
r.span_m, 900_000.0,
"…and the camera span the user must change"
);
}
other => panic!("a camera that cannot be answered must refuse, got {other:?}"),
}
assert!(
extrude::town_window(&ways, Some(sea)).is_none(),
"no window is chosen"
);
// The pane extrudes NOTHING, and says why — not the facet-map3d-1 error state.
let map = Map3D::from_geometry_in_view("overview", &ways, Some(sea));
assert_eq!(map.building_vertices(), 0, "a refusal extrudes nothing");
assert!(
map.town_refused().is_some(),
"and it is a refusal, not an empty load"
);
// RED PROBE for the threshold itself: the same camera CENTRE with a town-sized span
// IS honoured. The two differ only in span, which is the quantity under test.
let town_cam = extrude::town_window(&ways, Some(view(STOCKHOLM, 2_000.0))).expect("buildings");
assert_eq!(
town_cam.source,
extrude::WindowSource::Camera,
"a town-sized camera over a town wins"
);
// …and the camera-blind caller still gets the vote — refusal is about a camera that
// was supplied and could not be answered, never about having no camera.
assert_eq!(
extrude::town_window(&ways, None).expect("buildings").source,
extrude::WindowSource::Densest,
"no camera ⇒ the vote still decides",
);
}
/// **THE z9 DEFECT, as a unit test: the region view extruded a city 66 km away.**
///
/// MEASURED on oden's live sweden pyramid 2026-08-05, camera on central Stockholm at z9
/// (span 159 375 m, so the town window clamps to `TOWN_HALF_MAX_M` = 1500 m):
///
/// | what | buildings in that window |
/// |--------------------------------------------------|--------------------------|
/// | what the selector actually SEES (budget-capped) | **0** |
/// | the same z9 tile, uncapped | 155 |
/// | the z15 pyramid over the same box | **4 734** |
///
/// The whole z9 viewport hands the selector 132 building centroids where the same tiles
/// hold 88 441 — **0.149 %** — because korp splits a 40 000-way budget over 15 covering
/// tiles and the rows are stored largest-extent-first. The camera therefore cannot
/// answer, the vote took over, and it named a town **66 113 m** away (ratio 0.415).
///
/// The two tempting repairs are ruled out ON NUMBERS: the window holds ZERO, so no
/// threshold above 0 can be met; and widening the window until the capped sample holds
/// 24 needs `half_m = 20 000` — a 40 km scene, 13.3x the readability cap.
///
/// This fixture reproduces the SHAPE of that measurement (a sparse capped sample under
/// the camera, a denser cluster far away) so the regression is caught without the live
/// pyramid.
#[test]
fn a_region_view_over_a_capped_sample_refuses_instead_of_extruding_a_city_66_km_away() {
// The oden z9 numbers.
const Z9_SPAN_M: f64 = 159_375.13;
const OFFSET_M: f64 = 66_113.0;
// What the cap left under the camera: a handful of buildings, far below the gate.
let mut ways = houses(1_000, STOCKHOLM, 3, 40.0); // 9 houses in the 3 km window
// …and the cluster the vote would name, 66 km away (11x11 = 121 houses, so the
// vote CAN answer — the point is that it must not be asked).
let far = (STOCKHOLM.0, STOCKHOLM.1 + OFFSET_M / M_PER_DEG_LAT);
ways.extend(houses(50_000, far, 11, 30.0));
let cam = view(STOCKHOLM, Z9_SPAN_M);
// RED PROBE FIRST: the vote really would land 66 km away on this fixture. Without
// this, "refused" could be passing because there is nothing to choose at all.
let voted = extrude::town_window(&ways, None).expect("the fixture has buildings");
assert_eq!(voted.source, extrude::WindowSource::Densest);
let voted_off = extrude::geo_distance_m((STOCKHOLM.0, STOCKHOLM.1), (voted.lon, voted.lat));
assert!(
voted_off > 0.3 * Z9_SPAN_M,
"the vote's answer must be far away for this test to mean anything — got {voted_off:.0} m",
);
// THE CLAIM: with the camera supplied, the vote is never consulted.
match extrude::choose_town(&ways, Some(cam)) {
extrude::TownChoice::TooCoarse(r) => {
assert!(
r.buildings < r.needed,
"{} buildings, {} needed",
r.buildings,
r.needed
);
assert_eq!(
r.half_m,
extrude::TOWN_HALF_MAX_M,
"the region camera clamps to the cap"
);
}
other => panic!("z9 must refuse, got {other:?}"),
}
// APPLIED OUTPUT: nothing is extruded, and nothing points 66 km away.
let map = Map3D::from_geometry_in_view("z9", &ways, Some(cam));
assert_eq!(
map.building_vertices(),
0,
"no city is extruded at region zoom"
);
assert!(map.town_window().is_none(), "and no window is claimed");
let st = map.state_json();
assert_eq!(st["town_window"], serde_json::Value::Null);
assert_eq!(st["town_refused"]["reason"], "too-coarse");
assert_eq!(
st["error_code"],
serde_json::Value::Null,
"a refusal is an instruction, not the facet-map3d-1 empty-data fault",
);
// AND z12/z15 MUST NOT MOVE — a fix that repairs z9 and breaks z12 is not a fix.
//
// On the DENSE sample, because that is the other half of the measurement: at z12 the
// viewport is 5.7 km over 1-2 covering tiles, so the budget is not binding and the
// 3 km window really does hold thousands of buildings (the live drive reads
// `src=camera`, offset 0 m, at both z12 and z15). Reusing the starved z9 sample here
// would test the fixture, not the fix.
let dense = two_towns();
assert!(
extrude::town_window(&dense, Some(view(STOCKHOLM, 5_720.6)))
.expect("dense")
.buildings
>= extrude::MIN_BUILDINGS_IN_VIEW,
"the dense leg must actually clear the gate, or it proves nothing",
);
for span in [5_720.6_f64, 1_430.2] {
let ways = &dense;
let w = extrude::town_window(ways, Some(view(STOCKHOLM, span)))
.unwrap_or_else(|| panic!("a {span:.0} m camera over the town must be answered"));
assert_eq!(w.source, extrude::WindowSource::Camera, "span {span}");
assert_eq!(
extrude::geo_distance_m((STOCKHOLM.0, STOCKHOLM.1), (w.lon, w.lat)),
0.0,
"span {span}: the camera-chosen window sits ON the camera",
);
let m = Map3D::from_geometry_in_view("close", ways, Some(view(STOCKHOLM, span)));
assert!(
m.building_vertices() > 0,
"span {span}: the town is still extruded"
);
assert!(
m.town_refused().is_none(),
"span {span}: nothing is refused down here"
);
}
}
/// **The window follows the zoom, between two measured bounds.** A fixed 3 km box makes
/// the 3D pane show the same thing at z17 as at z9, which is not what a camera means.
#[test]
fn the_window_follows_the_zoom_and_is_clamped_at_both_ends() {
let ways = two_towns();
let at = |span_m: f64| {
extrude::town_window(&ways, Some(view(STOCKHOLM, span_m)))
.expect("buildings")
.half_m
};
// The sample spans are DERIVED FROM THE ANCHORS, not written down beside them. They
// used to be the literals 1 200 / 2 000 / 2 600 m with a literal 1 000 m expectation
// — a second, silent statement of where the window travels — and when
// `town_half_for_span` stopped being `0.5·span` clamped (2026-08-05) every one of
// them landed outside and this test failed for a reason that had nothing to do with
// the clamp. One writer (L5): move an anchor and the samples move with it.
let (wide, deep) = (
extrude::TOWN_ZOOM_WIDE_SPAN_M,
extrude::TOWN_ZOOM_DEEP_SPAN_M,
);
let at_span = |span_m: f64| (wide / span_m).ln() / (wide / deep).ln();
let span_at_t = |t: f64| wide * (deep / wide).powf(t);
let _ = at_span;
// Wide viewport → capped at the readability bound (a wider scene is the pancake).
assert_eq!(
at(4.0 * wide),
extrude::TOWN_HALF_MAX_M,
"a region camera is capped"
);
// Deeply zoomed → floored, so a rooftop camera still gets a block to orbit.
assert_eq!(
at(0.25 * deep),
extrude::TOWN_HALF_MIN_M,
"a rooftop camera is floored"
);
// MONOTONIC and STRICTLY moving between the anchors — a relation that always
// returned its bound would satisfy the two points above individually.
let (a, b, c) = (at(span_at_t(0.2)), at(span_at_t(0.5)), at(span_at_t(0.8)));
assert!(
c < b && b < a,
"the window tracks the viewport between the anchors: {a} > {b} > {c}"
);
assert!(
a > c * 1.5,
"and it tracks it by a real amount, not a rounding: {a} → {c}",
);
// …and the step is GEOMETRIC — equal zoom steps give equal ratios — which is what
// makes a wheel feel linear. Asserted as the two ratios agreeing, not as a constant.
let (r1, r2) = (a / b, b / c);
assert!(
(r1 / r2 - 1.0).abs() < 0.02,
"equal zoom steps must give equal ratios: {r1:.3} vs {r2:.3}",
);
}
/// **The scale fix survives the camera fix.** The whole reason the subset exists is that
/// a region-scale scene draws houses sub-pixel; aiming it must not undo that.
#[test]
fn the_camera_chosen_town_is_still_town_scale() {
let ways = two_towns();
let region_span = extrude::world_span_metres(&ways).expect("a region span");
assert!(
region_span > 40_000.0,
"the fixture is region-scale: {region_span:.0} m"
);
let map =
Map3D::from_geometry_in_view("stockholm", &ways, Some(view(STOCKHOLM, KORP_Z12_SPAN_M)));
let mpdu = map.metres_per_design_unit();
let px = map.skyline_screen_height_px(1000.0);
println!("aimed scene: metres_per_design_unit {mpdu:.0} median house {px:.2} px");
// The design unit is a town's, not a region's — the 33 195 m reading that drew the
// pancake would fail this by a factor of ten.
assert!(
mpdu < 5_000.0,
"the aimed subset must still bound the scene scale: {mpdu:.0} m per design unit",
);
// APPLIED OUTPUT: a house you can see. 0.66 px was the pancake.
assert!(
px > 2.0,
"the median house must be legible on a 1000 px pane, got {px:.2} px"
);
}
/// **★ THE OWNER'S CASE: 940 buildings on screen, none under the crosshair, and it refused.**
///
/// MEASURED live 2026-08-08, from `$KORP_STATE.map.map3d.town_refused` verbatim:
///
/// { buildings: 0, needed: 24, half_m: 250.06, span_m: 4487.7, reason: "too-coarse" }
///
/// A 4.5 km camera with 940 buildings in the viewport, a 250 m window in the dead centre,
/// and zero buildings in that particular 500 m. The pane drew `2 trianglar, 0 resta` over a
/// city he could see, and every line of the refusal was working as written — the reasoning
/// behind it is about a COUNTRY overview, where the vote would answer confidently about
/// somewhere the user is not looking.
///
/// At street zoom that reasoning does not hold: the buildings are ON SCREEN. So the window
/// slides to the densest cluster still inside the viewport, and only in a view small enough
/// to be one place.
///
/// RED directions: delete the `densest_within` call and this refuses again; drop the
/// `span_m <= 4 x DEEP` bound and the two country-overview guards above go red instead.
#[test]
fn a_street_view_with_buildings_off_centre_slides_instead_of_refusing() {
use facett_map3d::extrude::{self, TownChoice, ViewWindow, WindowSource};
// A town, deliberately OFF the camera centre — the shape of the live failure.
let centre = (18.0686, 59.3293);
let off = (centre.0 + 0.022, centre.1 + 0.008); // ~1.25 km east, ~0.9 km north — on screen, far outside the 250 m crosshair window
let ways = houses(9000, off, 60, 25.0);
let v = ViewWindow { lon: centre.0, lat: centre.1, span_m: 4487.7 };
match extrude::choose_town(&ways, Some(v)) {
TownChoice::Chosen(w) => {
assert_eq!(
w.source,
WindowSource::CameraSlid,
"the window slid rather than being chosen by the crosshair",
);
assert!(
w.buildings >= extrude::MIN_BUILDINGS_IN_VIEW,
"…onto a real cluster: {} buildings",
w.buildings,
);
// …and it stayed ON SCREEN. That bound is the whole difference between this and
// the unbounded vote that names a town 66 km away.
let half_lat = (v.span_m * 0.5) / extrude::M_PER_DEG_LAT;
let half_lon = (v.span_m * 0.5) / m_per_deg_lon(v.lat);
assert!(
(w.lon - v.lon).abs() <= half_lon && (w.lat - v.lat).abs() <= half_lat,
"the slid window must stay inside the viewport: ({}, {}) from ({}, {})",
w.lon, w.lat, v.lon, v.lat,
);
}
other => panic!("a street view with 60 buildings on screen must not refuse: {other:?}"),
}
}