#![allow(clippy::pedantic, reason = "profiling helper")]
use std::hint::black_box;
use std::time::Instant;
use geo::Simplify;
use geo_types::{Coord, LineString};
use std::collections::BTreeSet;
use map_tile_toolkit::Slicer;
use wkt::TryFromWkt;
const EARTH_CIRCUMFERENCE: f64 = 40_075_016.686;
const HALF: f64 = EARTH_CIRCUMFERENCE / 2.0;
const EXTENT: u32 = 4096;
const SIMPLIFY_PIXELS: f64 = 3.0;
fn main() {
let mut args = std::env::args().skip(1);
let path = args
.next()
.expect("usage: simplify_slice <path-to.wkt> <zoom>");
let zoom: u32 = args
.next()
.expect("usage: simplify_slice <path-to.wkt> <zoom>")
.parse()
.expect("zoom must be a non-negative integer");
let tile_count = 2f64.powi(i32::try_from(zoom).expect("zoom fits i32"));
let divisor = EARTH_CIRCUMFERENCE / tile_count;
let pixel_m = divisor / f64::from(EXTENT);
let epsilon = SIMPLIFY_PIXELS * pixel_m;
let units_per_m = f64::from(EXTENT) / divisor;
eprintln!(
"zoom {zoom}: divisor {divisor:.3} m/tile, pixel {pixel_m:.4} m, simplify epsilon {epsilon:.4} m"
);
let t = Instant::now();
let text = std::fs::read_to_string(&path).expect("readable WKT file");
let lines: Vec<LineString<f64>> = text
.lines()
.filter(|l| !l.trim().is_empty())
.map(|l| LineString::try_from_wkt_str(l).expect("valid WKT LINESTRING"))
.collect();
eprintln!("loaded {} geometries in {:?}", lines.len(), t.elapsed());
let t = Instant::now();
let geoms: Vec<LineString<i32>> = lines
.iter()
.map(|ls| {
let simplified = ls.simplify(epsilon);
let coords: Vec<Coord<i32>> = simplified
.0
.iter()
.map(|c| Coord {
x: ((c.x + HALF) * units_per_m) as i32,
y: ((HALF - c.y) * units_per_m) as i32,
})
.collect();
LineString(coords)
})
.collect();
let vertices: usize = geoms.iter().map(|ls| ls.0.len()).sum();
eprintln!(
"simplified + projected to {vertices} vertices in {:?}",
t.elapsed()
);
let t = Instant::now();
let mut skipped = 0u64;
let mut pieces = 0u64;
let mut seen = BTreeSet::new();
let slicer = Slicer::new(EXTENT, 0).expect("valid slicer config");
for geom in &geoms {
let Ok(slices) = slicer.line(black_box(geom.0.as_slice())) else {
skipped += 1;
continue;
};
for slice in slices {
seen.insert(black_box(slice.tile()));
for v in slice.run() {
black_box(v);
}
pieces += 1;
}
}
let tiles = seen.len();
black_box(pieces);
eprintln!(
"sliced {} geometries into {tiles} tiles / {pieces} pieces ({skipped} skipped) in {:?}",
geoms.len(),
t.elapsed()
);
}