use std::collections::BTreeSet;
use geo_types::Coord;
use map_tile_toolkit::{FillRun, PolygonSlice, Slicer, TileError, TileId};
fn line(coords: Vec<(i32, i32)>) -> Vec<Coord<i32>> {
coords.into_iter().map(|(x, y)| Coord { x, y }).collect()
}
fn all(extent: u32, buffer: u16) -> Result<Slicer<Coord<i32>>, TileError> {
Slicer::new(extent, buffer)
}
type Slices = Vec<(TileId, Vec<Coord<i32>>)>;
fn slices(s: &Slicer<Coord<i32>>, l: &[Coord<i32>]) -> Result<Slices, TileError> {
Ok(s.line(l)?.map(|s| (s.tile(), s.run().to_vec())).collect())
}
fn overflows_midway() -> Vec<Coord<i32>> {
line(vec![
(i32::MIN + 8200, 0), (i32::MIN + 8250, 0),
(i32::MIN + 8, 0), ])
}
#[test]
fn invalid_extent() {
assert_eq!(all(0, 0).err(), Some(TileError::InvalidExtent));
assert_eq!(all(u32::MAX, 0).err(), Some(TileError::InvalidExtent));
assert!(all(1, 0).is_ok());
assert!(all(i32::MAX as u32, 0).is_ok());
assert_eq!(
all(i32::MAX as u32, u16::MAX).err(),
Some(TileError::InvalidExtent)
);
assert!(all(i32::MAX as u32 - u32::from(u16::MAX), u16::MAX).is_ok());
}
#[test]
fn buffer_too_large() {
assert_eq!(all(10, 5).err(), Some(TileError::BufferTooLarge)); assert_eq!(all(10, 6).err(), Some(TileError::BufferTooLarge));
assert!(all(10, 4).is_ok()); assert!(all(1, 0).is_ok());
assert_eq!(all(2, 1).err(), Some(TileError::BufferTooLarge));
}
#[test]
fn points_at_the_i32_limits_error_instead_of_panicking() {
let s = all(4096, 8).expect("valid config");
let at = |x: i32, y: i32| Coord { x, y };
assert_eq!(s.point(at(i32::MAX, 0)).err(), Some(TileError::Overflow));
assert_eq!(s.point(at(0, 0)).expect("clean point").count(), 4);
assert_eq!(
s.tile_point(TileId::new(i32::MAX, 0), at(0, 0)).err(),
Some(TileError::Overflow)
);
}
#[test]
fn a_far_vertex_overflows_the_local_frame_of_a_tile_it_reaches() {
let s = all(4096, 0).expect("valid config");
let l = line(vec![(1 << 30, 0), (i32::MIN, 0)]);
assert_eq!(
s.tile_line(TileId::new(1 << 18, 0), &l).err(),
Some(TileError::Overflow)
);
assert!(s.tile_line(TileId::new(0, 0), &l).is_ok());
}
#[test]
fn extreme_tile_errors_instead_of_panicking() {
let l = line(vec![(0, 0), (10, 10)]);
let s = all(4096, 0).expect("valid config");
assert_eq!(
s.tile_line(TileId::new(i32::MAX, i32::MAX), &l).err(),
Some(TileError::Overflow)
);
assert_eq!(
s.tile_line(TileId::new(i32::MIN, 0), &l).err(),
Some(TileError::Overflow)
);
let far = s
.tile_line(TileId::new(1000, 1000), &l)
.expect("no error for a far tile");
assert_eq!(far.count(), 0);
}
#[test]
fn spanning_many_tiles_is_lazy() {
let s = all(1, 0).expect("valid config"); let long = line(vec![(0, 0), (40_000_000, 0)]);
let first: Vec<_> = s.line(&long).expect("lazy").take(3).collect();
assert_eq!(first.len(), 3);
let s = all(10, 1).expect("valid config");
let tiles: BTreeSet<TileId> = slices(&s, &line(vec![(0, 0), (1_000_000, 700_010)]))
.expect("a long diagonal")
.into_iter()
.map(|(t, _)| t)
.collect();
assert!(
tiles.len() > 100_000 && tiles.len() < 200_000,
"{}",
tiles.len()
);
}
#[test]
fn unbuffered_gap_rows_are_skipped() {
let s = all(1, 0).expect("valid config");
let got = slices(&s, &line(vec![(0, 0), (1, 1 << 26)])).expect("slice");
let tiles: Vec<TileId> = got.iter().map(|(t, _)| *t).collect();
assert_eq!(tiles, [TileId::new(0, 0), TileId::new(1, 1 << 26)]);
let got = slices(&s, &line(vec![(0, 0), (1, 1000)])).expect("slice");
assert_eq!(got.len(), 2, "only the endpoint tiles are touched");
}
#[test]
fn coordinate_overflow_errors() {
let s = all(4096, 8).expect("valid config");
assert_eq!(
slices(&s, &line(vec![(i32::MAX, 0), (i32::MAX, 10)])).err(),
Some(TileError::Overflow)
);
}
#[test]
fn long_polylines_are_accepted() {
let s = all(1_000_000, 0).expect("valid config");
let coords: Vec<Coord<i32>> = (0..200_000).map(|i| Coord { x: i % 8, y: 0 }).collect();
let runs: Vec<usize> = slices(&s, &coords)
.expect("200 000 vertices")
.iter()
.map(|(_, run)| run.len())
.collect();
assert_eq!(runs, [200_000]);
}
#[test]
fn an_overflow_is_reported_before_any_slice() {
let s = all(4096, 8).expect("valid config");
let bad = overflows_midway();
assert_eq!(s.line(&bad).err(), Some(TileError::Overflow));
let clean = line(bad.iter().take(2).map(|c| (c.x, c.y)).collect());
assert!(s.line(&clean).is_ok());
let good = line(vec![(i32::MIN + 8210, 0), (i32::MIN + 8260, 0)]);
assert_eq!(slices(&s, &good).expect("clean").len(), 2);
}
#[test]
fn empty_and_degenerate_inputs_are_ok() {
let s = all(25, 0).expect("valid config");
assert_eq!(slices(&s, &[]).expect("empty polyline is ok"), []);
let dot = line(vec![(5, 5)]);
assert_eq!(slices(&s, &dot).expect("single-point polyline is ok"), []);
let one = s
.tile_line(TileId::new(0, 0), &dot)
.expect("single-point polyline is ok");
assert_eq!(one.count(), 0);
}
fn poly(coords: Vec<(i32, i32)>) -> Vec<Vec<Coord<i32>>> {
vec![line(coords)]
}
#[derive(Debug, PartialEq, Eq)]
enum Item {
Piece(TileId, Vec<Vec<Coord<i32>>>),
Run(FillRun),
}
fn poly_items(
s: &mut Slicer<Coord<i32>>,
rings: &[Vec<Coord<i32>>],
) -> (Vec<Item>, Option<TileError>) {
let mut slices = s.polygon(rings).expect("starts");
let mut items = Vec::new();
while let Some(slice) = slices.next() {
match slice {
Ok(PolygonSlice::Piece(p)) => items.push(Item::Piece(
p.tile(),
p.rings().map(map_tile_toolkit::Local::to_vec).collect(),
)),
Ok(PolygonSlice::Run(run)) => items.push(Item::Run(run)),
Err(e) => return (items, Some(e)),
}
}
(items, None)
}
#[test]
fn polygons_spanning_many_tiles_are_lazy() {
let mut s = all(1, 0).expect("valid config");
let wide = poly(vec![(0, 0), (40_000_000, 0), (40_000_000, 5)]);
let mut slices = s.polygon(&wide).expect("lazy");
for _ in 0..3 {
assert!(matches!(slices.next(), Some(Ok(_))));
}
let small = poly(vec![(0, 0), (600, 600), (0, 600)]);
let large = poly(vec![(0, 0), (5780, 5780), (0, 5780)]);
let (items, err) = poly_items(&mut s, &small);
assert_eq!(err, None);
let filled: usize = items
.iter()
.map(|i| match i {
Item::Run(run) => run.x.len(),
Item::Piece(..) => 0,
})
.sum();
assert!(filled > 170_000, "{filled}");
let (items, err) = poly_items(&mut s, &large);
assert_eq!(err, None);
let pieces = items
.iter()
.filter(|i| matches!(i, Item::Piece(..)))
.count();
assert!(pieces < 40_000, "{pieces}");
}
#[test]
fn polygon_slivers_between_unbuffered_tiles_yield_little() {
let mut s = all(1, 0).expect("valid config");
let (items, err) = poly_items(&mut s, &poly(vec![(0, 0), (1, 1 << 16), (0, 1)]));
assert_eq!(err, None);
assert!(items.iter().all(|i| matches!(i, Item::Piece(..))));
assert!(items.len() <= 4, "{items:?}");
}
#[test]
fn a_polygon_overflow_is_reported_by_the_call() {
let (x0, x1) = (i32::MIN + 10, i32::MIN + 6000);
let bad = poly(vec![(x0, 0), (x1, 0), (x1, 10_000), (x0, 10_000)]);
let mut s = all(4096, 0).expect("valid config");
let piece = s.tile_polygon(TileId::new(-524_288, 1), &bad);
assert_eq!(
piece.err(),
Some(TileError::Overflow),
"the single-tile clip agrees"
);
assert_eq!(
s.polygon(&bad).err(),
Some(TileError::Overflow),
"from the call, before any slice"
);
let clean = poly_items(&mut s, &clean_square());
assert_eq!(clean.1, None);
assert_eq!(
clean,
poly_items(&mut all(4096, 0).expect("valid config"), &clean_square())
);
}
fn clean_square() -> Vec<Vec<Coord<i32>>> {
poly(vec![(5, 5), (9000, 5), (9000, 9000), (5, 9000)])
}
#[test]
fn polygon_degenerate_inputs() {
let mut s = all(25, 0).expect("valid config");
assert_eq!(poly_items(&mut s, &[]), (vec![], None));
let no_rings: Vec<Vec<Coord<i32>>> = vec![];
let degenerate = vec![
line(vec![(1, 1), (9, 9), (1, 1)]),
line(vec![(2, 2), (8, 2), (8, 8)]),
];
assert_eq!(poly_items(&mut s, °enerate), (vec![], None));
assert!(
s.tile_polygon(TileId::new(0, 0), °enerate)
.expect("ok")
.is_none()
);
assert!(
s.tile_polygon(TileId::new(0, 0), &no_rings)
.expect("ok")
.is_none()
);
let repeated = vec![
line(vec![(2, 2), (60, 2), (60, 60), (2, 60)]),
line(vec![(30, 30), (30, 30), (31, 31), (31, 31)]),
];
let plain_square = vec![line(vec![(2, 2), (60, 2), (60, 60), (2, 60)])];
assert_eq!(
poly_items(&mut s, &repeated),
poly_items(&mut s, &plain_square)
);
let square = line(vec![(2, 2), (60, 2), (60, 60), (2, 60)]);
let with_hole = poly_items(&mut s, &[square.clone(), line(vec![(30, 30), (31, 31)])]);
let plain = poly_items(&mut s, &[square]);
assert_eq!(with_hole, plain);
assert_ne!(plain.0, []);
}