use std::thread;
use crossbeam_channel::Sender;
use super::{error::Result, gpx::Coordinates};
pub mod heatmap;
pub mod marktile;
pub mod tilehunt;
const CHANNEL_SIZE: usize = 30;
#[derive(Debug, Clone)]
pub struct RenderedTile {
pub x: u64,
pub y: u64,
pub data: Vec<u8>,
}
pub trait Renderer: Send + Sync {
type Prepared: Send;
fn prepare(
&self,
zoom: u32,
tracks: &[Vec<Coordinates>],
tick: Sender<()>,
) -> Result<Self::Prepared>;
fn colorize(&self, prepared: Self::Prepared, saver: Sender<RenderedTile>) -> Result<()>;
fn tile_count(&self, prepared: &Self::Prepared) -> Result<u64>;
}
pub fn prepare<R: Renderer, F: FnMut() -> Result<()>>(
renderer: &R,
zoom: u32,
tracks: &[Vec<Coordinates>],
mut tick: F,
) -> Result<R::Prepared> {
thread::scope(|s| {
let (sender, receiver) = crossbeam_channel::bounded(CHANNEL_SIZE);
let preparer = s.spawn(|| renderer.prepare(zoom, tracks, sender));
for _ in receiver {
tick()?;
}
preparer.join().unwrap()
})
}
pub fn colorize<R: Renderer, F: FnMut(RenderedTile) -> Result<()>>(
renderer: &R,
prepared: R::Prepared,
mut saver: F,
) -> Result<()> {
thread::scope(|s| {
let (sender, receiver) = crossbeam_channel::bounded(CHANNEL_SIZE);
let colorizer = s.spawn(|| renderer.colorize(prepared, sender));
for tile in receiver {
saver(tile)?;
}
colorizer.join().unwrap()
})
}
#[cfg(test)]
mod test {
use super::*;
use rstest::rstest;
fn tracks() -> Vec<Vec<Coordinates>> {
vec![
vec![Coordinates {
latitude: 52.520008,
longitude: 13.404954,
}],
vec![Coordinates {
latitude: 52.520008,
longitude: 13.404954,
}],
vec![Coordinates {
latitude: 52.520008,
longitude: 13.404954,
}],
]
}
#[rstest]
fn test_heatmap_prepare_zoom_0() {
let ts = tracks();
let mut prep = prepare(&heatmap::Renderer, 0, &ts[..1], || Ok(())).unwrap();
assert_eq!(prep.tile_count(), 1);
assert_eq!(prep.tile_mut(0, 0).get_pixel(0, 0).0[0], 0);
assert_eq!(prep.tile_mut(0, 0).get_pixel(255, 255).0[0], 0);
let ones: &[(u32, u32)] = &[(137, 82), (136, 83), (137, 83), (138, 83), (137, 84)];
for (x, y) in ones {
assert_eq!(prep.tile_mut(0, 0).get_pixel(*x, *y).0[0], 1);
}
let mut prep = prepare(&heatmap::Renderer, 0, &ts[..2], || Ok(())).unwrap();
for (x, y) in ones {
assert_eq!(prep.tile_mut(0, 0).get_pixel(*x, *y).0[0], 2);
}
let mut prep = prepare(&heatmap::Renderer, 0, &ts[..3], || Ok(())).unwrap();
for (x, y) in ones {
assert_eq!(prep.tile_mut(0, 0).get_pixel(*x, *y).0[0], 3);
}
}
#[rstest]
fn test_heatmap_prepare_zoom_1() {
let ts = tracks();
let mut prep = prepare(&heatmap::Renderer, 1, &ts[..1], || Ok(())).unwrap();
for (tx, ty) in [(0, 0), (0, 1), (1, 1)] {
assert!(prep.tile_mut(tx, ty).pixels().all(|px| px.0[0] == 0));
}
let ones: &[(u32, u32)] = &[(19, 166), (18, 167), (19, 167), (20, 167), (19, 168)];
for (x, y) in ones {
assert_eq!(prep.tile_mut(1, 0).get_pixel(*x, *y).0[0], 1);
}
}
#[rstest]
fn test_heatmap_prepare_zoom_2() {
let ts = tracks();
let mut prep = prepare(&heatmap::Renderer, 2, &ts[..1], || Ok(())).unwrap();
for (tx, ty) in [(0, 0), (0, 1), (0, 2), (0, 3), (1, 0), (1, 1)] {
assert!(prep.tile_mut(tx, ty).pixels().all(|px| px.0[0] == 0));
}
let ones: &[(u32, u32)] = &[(38, 78), (37, 79), (38, 79), (39, 79), (38, 80)];
for (x, y) in ones {
assert_eq!(prep.tile_mut(2, 1).get_pixel(*x, *y).0[0], 1);
}
}
}