Maps · Layers: tests hold what clusters promise
Tests hold that a pan keeps a focused cluster, that a cluster away from the view takes no Tab, that a zoom out folding a focused pin hands its focus to the map, and that a press shows members far apart whole. The heat test pins the quartic kernel's value halfway out; a ring of fewer than four positions fails with its own reason, and has a test.
This commit is contained in:
1 parent
e3a96b04e0
commit
3e5c5f37ba
4 files changed
+112
-7
No files matched your search
+10
-2
@@ -141,8 +141,14 @@ fn line(json: &Value) -> Result<Vec<LatLon>> {
|
||||
|
||||
fn ring(json: &Value) -> Result<Vec<LatLon>> {
|
||||
let ring = list(json, position)?;
|
||||
if ring.len() < 4 || ring.first() != ring.last() {
|
||||
bail!("a ring of {} positions that does not close", ring.len());
|
||||
if ring.len() < 4 {
|
||||
bail!(
|
||||
"a ring of {} positions; a ring holds four or more",
|
||||
ring.len()
|
||||
);
|
||||
}
|
||||
if ring.first() != ring.last() {
|
||||
bail!("a ring that does not close");
|
||||
}
|
||||
Ok(ring)
|
||||
}
|
||||
@@ -211,6 +217,8 @@ mod tests {
|
||||
assert!(reason(r#"{"type": "LineString", "coordinates": [[0, 0]]}"#).contains("line of 1"));
|
||||
let open = r#"{"type": "Polygon", "coordinates": [[[0,0],[1,0],[1,1],[0,1]]]}"#;
|
||||
assert!(reason(open).contains("does not close"));
|
||||
let short = r#"{"type": "Polygon", "coordinates": [[[0,0],[1,1],[0,0]]]}"#;
|
||||
assert!(reason(short).contains("four or more"));
|
||||
let named = r#"{"type": "FeatureCollection", "features": [{"type": "Feature", "geometry": {"type": "Point"}}]}"#;
|
||||
let named = reason(named);
|
||||
assert!(
|
||||
|
||||
+5
-4
@@ -13,7 +13,7 @@ impl Heat {
|
||||
}
|
||||
}
|
||||
|
||||
/// Each weighted view point spreads a quartic kernel `reach` pixels wide over cells `cell` pixels square across a view of `size`; the sum is scaled so its densest cell is 1. Fails on a negative or non-finite weight.
|
||||
/// Each weighted view point spreads a quartic kernel reaching `reach` pixels from it over cells `cell` pixels square across a view of `size`; the sum is scaled so its densest cell is 1. Fails on a negative or non-finite weight.
|
||||
pub(crate) fn density(
|
||||
points: &[((f32, f32), f32)],
|
||||
(w, h): (f32, f32),
|
||||
@@ -74,9 +74,10 @@ mod tests {
|
||||
1.0,
|
||||
"the cell under the point is the densest"
|
||||
);
|
||||
assert!(
|
||||
heat.at(3, 2) > 0.0 && heat.at(3, 2) < 1.0,
|
||||
"it falls off beside"
|
||||
assert_eq!(
|
||||
heat.at(3, 2),
|
||||
0.5625,
|
||||
"halfway out a quartic kernel keeps (1 - 1/4) squared"
|
||||
);
|
||||
assert_eq!(heat.at(6, 2), 0.0, "past its reach nothing");
|
||||
}
|
||||
|
||||
@@ -107,3 +107,99 @@ fn every_layer_paints_over_repeated_worlds(cx: &mut TestAppContext) {
|
||||
fn a_route_of_one_place_fails() {
|
||||
RouteLine::new([LatLon::new(0.0, 0.0)]);
|
||||
}
|
||||
|
||||
/// A pan moves the view, not the world's squares: the focused cluster keeps its focus and Enter still presses it.
|
||||
#[gpui::test]
|
||||
fn a_pan_keeps_a_focused_cluster(cx: &mut TestAppContext) {
|
||||
let (host, cx) = stage(crowd, cx);
|
||||
focus_map(cx);
|
||||
press("tab", cx);
|
||||
press("left", cx);
|
||||
press("enter", cx);
|
||||
assert!(
|
||||
last(&host, cx).zoom > europe().zoom,
|
||||
"the cluster kept focus through the pan"
|
||||
);
|
||||
}
|
||||
|
||||
fn far_crowd(_: &Stage, owner: Entity<Stage>) -> AnyElement {
|
||||
MapView::new("map", europe())
|
||||
.markers([
|
||||
MapMarker::new("tokyo", LatLon::new(35.7, 139.7)),
|
||||
MapMarker::new("chiba", LatLon::new(35.9, 140.0)),
|
||||
])
|
||||
.cluster_markers()
|
||||
.on_marker(|_, _, _| {})
|
||||
.on_viewport(move |view, _, cx| hear(&owner, view, cx))
|
||||
.into_any_element()
|
||||
}
|
||||
|
||||
/// A cluster whose place lies outside the view takes no Tab, as a pin there takes none: Tab reaches the zoom button.
|
||||
#[gpui::test]
|
||||
fn a_cluster_away_from_the_view_takes_no_tab(cx: &mut TestAppContext) {
|
||||
let (host, cx) = stage(far_crowd, cx);
|
||||
focus_map(cx);
|
||||
press("tab", cx);
|
||||
press("enter", cx);
|
||||
assert_eq!(last(&host, cx).zoom, 3.0, "Enter on the zoom button");
|
||||
}
|
||||
|
||||
fn folding(_: &Stage, owner: Entity<Stage>) -> AnyElement {
|
||||
let near = CROWD.map(|(key, lat, lon)| MapMarker::new(key, LatLon::new(lat, lon)));
|
||||
MapView::new("map", MapViewport::new(LatLon::new(45.0, 0.0), 3.0))
|
||||
.markers(
|
||||
near.into_iter()
|
||||
.chain([MapMarker::new("rome", LatLon::new(41.9, 12.5))]),
|
||||
)
|
||||
.cluster_markers()
|
||||
.on_marker(|_, _, _| {})
|
||||
.on_viewport(move |view, _, cx| hear(&owner, view, cx))
|
||||
.into_any_element()
|
||||
}
|
||||
|
||||
/// At zoom 3 Rome stands alone; a zoom out folds it into the capitals' cluster, and its focus goes to the map.
|
||||
#[gpui::test]
|
||||
fn a_zoom_out_folding_a_focused_pin_hands_focus_to_the_map(cx: &mut TestAppContext) {
|
||||
let (host, cx) = stage(folding, cx);
|
||||
focus_map(cx);
|
||||
press("tab", cx);
|
||||
press("tab", cx);
|
||||
press("-", cx);
|
||||
assert_eq!(last(&host, cx).zoom, 2.0);
|
||||
let before = moves(&host, cx);
|
||||
press("left", cx);
|
||||
assert_eq!(moves(&host, cx), before + 1, "the map holds focus");
|
||||
}
|
||||
|
||||
const APART: [(f64, f64); 2] = [(20.0, -2.5), (21.0, 16.5)];
|
||||
|
||||
fn apart(_: &Stage, owner: Entity<Stage>) -> AnyElement {
|
||||
MapView::new("map", MapViewport::new(LatLon::new(20.0, 7.0), 2.0))
|
||||
.markers(
|
||||
APART.iter().enumerate().map(|(ix, (lat, lon))| {
|
||||
MapMarker::new(format!("far-{ix}"), LatLon::new(*lat, *lon))
|
||||
}),
|
||||
)
|
||||
.cluster_markers()
|
||||
.on_marker(|_, _, _| {})
|
||||
.on_viewport(move |view, _, cx| hear(&owner, view, cx))
|
||||
.into_any_element()
|
||||
}
|
||||
|
||||
/// Two pins 19 degrees apart in one square: the cluster's press shows both, which no fit of one alone would.
|
||||
#[gpui::test]
|
||||
fn a_clusters_press_shows_far_members_whole(cx: &mut TestAppContext) {
|
||||
let (host, cx) = stage(apart, cx);
|
||||
focus_map(cx);
|
||||
press("tab", cx);
|
||||
press("enter", cx);
|
||||
let fit = last(&host, cx);
|
||||
assert!(fit.zoom > 2.0, "nearer in: {}", fit.zoom);
|
||||
for (lat, lon) in APART {
|
||||
let (x, y) = fit.to_view(LatLon::new(lat, lon), SIZE, TILE);
|
||||
assert!(
|
||||
(0.0..=SIZE.0).contains(&x) && (0.0..=SIZE.1).contains(&y),
|
||||
"{lat}, {lon} at {x}, {y}"
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -12,10 +12,10 @@ use crate::{
|
||||
theme::Theme,
|
||||
};
|
||||
|
||||
/// The view inside the map's 1px border, in pixels.
|
||||
mod layers;
|
||||
mod popup;
|
||||
|
||||
/// The view inside the map's 1px border, in pixels.
|
||||
const SIZE: (f32, f32) = (398.0, 298.0);
|
||||
const TILE: f32 = 256.0;
|
||||
|
||||
|
||||
Reference in new issue
Block a user