#![allow(clippy::pedantic, reason = "test/inspection tool")]
use std::collections::BTreeMap;
use std::path::Path;
use geo_types::Coord;
use insta::assert_binary_snapshot;
use map_tile_toolkit::{Slicer, TileId};
mod support;
use crate::support::EXTENT;
fn buffers() -> [(u16, &'static str); 2] {
[(0, "points/snapshots"), (5, "points/snapshots-5")]
}
mod files {
use test_each_file::test_each_path;
test_each_path! { for ["geojson"] in "./tests/points/fixtures" => super::snapshot_point_fixture }
}
type Tiles = BTreeMap<TileId, Vec<(usize, u32, Coord<i32>)>>;
fn slice_all(slicer: &Slicer<Coord<i32>>, features: &[Vec<Coord<i32>>]) -> Tiles {
let mut out = Tiles::new();
for (f, points) in features.iter().enumerate() {
for (index, &point) in (0_u32..).zip(points) {
for p in slicer.point(point).expect("point") {
out.entry(p.tile()).or_default().push((f, index, p.point()));
}
}
}
for points in out.values_mut() {
points.sort_by_key(|&(f, index, _)| (f, index));
}
out
}
fn slice_tile(
slicer: &Slicer<Coord<i32>>,
features: &[Vec<Coord<i32>>],
tile: TileId,
) -> Vec<(usize, u32, Coord<i32>)> {
let mut out = Vec::new();
for (f, points) in features.iter().enumerate() {
for (index, &point) in (0_u32..).zip(points) {
if let Some(p) = slicer.tile_point(tile, point).expect("tile") {
out.push((f, index, p));
}
}
}
out
}
fn oracle(features: &[Vec<Coord<i32>>], buffer: u16, lo: TileId, hi: TileId) -> Tiles {
let (e, b) = (EXTENT as i32, i32::from(buffer));
let holds = |t: i32, v: i32| t * e - b <= v && v < (t + 1) * e + b;
let mut out = Tiles::new();
for y in lo.y..=hi.y {
for x in lo.x..=hi.x {
let tile = TileId::new(x, y);
let origin = Coord { x: x * e, y: y * e };
for (f, points) in features.iter().enumerate() {
for (index, &p) in points.iter().enumerate() {
if holds(x, p.x) && holds(y, p.y) {
let index = u32::try_from(index).expect("small fixture");
out.entry(tile).or_default().push((f, index, p - origin));
}
}
}
}
}
out
}
fn snapshot_point_fixture([path]: [&Path; 1]) {
let stem = path.file_stem().and_then(|s| s.to_str()).expect("stem");
let features = support::load_point_features(path);
for (buffer, dir) in buffers() {
snapshot_at_buffer(stem, &features, buffer, dir);
}
}
fn snapshot_at_buffer(stem: &str, features: &[Vec<Coord<i32>>], buffer: u16, dir: &str) {
let slicer = Slicer::<Coord<i32>>::new(EXTENT, buffer).expect("valid config");
let all = slice_all(&slicer, features);
let (lo, hi) = support::padded_tile_span(features);
let mut one = Tiles::new();
for y in lo.y..=hi.y {
for x in lo.x..=hi.x {
let tile = TileId::new(x, y);
let points = slice_tile(&slicer, features, tile);
if !points.is_empty() {
one.insert(tile, points);
}
}
}
assert_eq!(
all, one,
"Slicer::point and Slicer::tile_point differ for {stem} (buffer {buffer})"
);
assert_eq!(
all,
oracle(features, buffer, lo, hi),
"slicing differs from the closed buffered box rule for {stem} (buffer {buffer})"
);
let inputs: Vec<_> = features.iter().flatten().copied().collect();
let starts: Vec<u32> = features
.iter()
.scan(0, |at, f| {
let start = *at;
*at += u32::try_from(f.len()).expect("small fixture");
Some(start)
})
.collect();
let mut out = vec![support::input_points(&inputs)];
for (&tile, points) in &all {
let origin = tile.origin(EXTENT).expect("tile in range");
let global: Vec<_> = points.iter().map(|&(_, _, p)| p + origin).collect();
let indices: Vec<_> = points
.iter()
.map(|&(f, index, _)| starts[f] + index)
.collect();
out.push(support::tile_points(&global, tile, &indices));
}
let bytes = support::feature_collection_bytes(out);
insta::with_settings!({
snapshot_path => dir,
prepend_module_to_snapshot => false,
}, {
let name = if buffer > 0 {
format!("{stem}-{buffer}.geojson")
} else {
format!("{stem}.geojson")
};
assert_binary_snapshot!(&name, bytes);
});
}