use crate::{Res, utils};
use gpx::Waypoint;
use rayon::prelude::*;
pub use std::{
collections::{BTreeSet, HashMap},
path::Path,
};
pub type Coord = (i8, i16);
pub type ElevData = HashMap<Coord, srtm_reader::Tile>;
#[must_use]
pub fn needed_tile_coords(wps: &[Waypoint]) -> BTreeSet<Coord> {
let trunc = |wp: &Waypoint| -> Coord {
let (x, y) = wp.point().x_y();
(y.trunc() as i8, x.trunc() as i16)
};
wps.par_iter()
.filter(|wp| !utils::is_00(wp))
.map(trunc)
.collect()
}
pub fn read_needed_tiles(
needs: &BTreeSet<Coord>,
elev_data_dir: impl AsRef<Path>,
) -> Vec<srtm_reader::Tile> {
log::info!("reading needed tiles into memory");
log::debug!("needed tiles' coordinates are: {needs:?}");
let elev_data_dir = elev_data_dir.as_ref();
needs
.par_iter()
.map(|c| srtm_reader::Coord::from(*c).get_filename())
.map(|t| elev_data_dir.join(t))
.flat_map(|p| {
srtm_reader::Tile::from_file(&p)
.inspect_err(|e| log::error!("error while reading {p:?} into memory: {e:#?}"))
})
.collect()
}
#[must_use]
pub fn index_tiles(tiles: Vec<srtm_reader::Tile>) -> ElevData {
log::info!("indexing all dem tiles");
log::trace!("tiles: {tiles:?}");
tiles
.into_par_iter()
.map(|tile| ((tile.latitude, tile.longitude), tile))
.collect()
}
impl crate::Fit {
pub fn add_elev_read(&mut self, elev_data_dir: impl AsRef<Path>, overwrite: bool) -> Res<()> {
let needed_tile_coords = needed_tile_coords(&self.track_segment.points);
let needed_tiles = read_needed_tiles(&needed_tile_coords, elev_data_dir);
let all_elev_data = index_tiles(needed_tiles);
self.add_elev_loaded(&all_elev_data, overwrite)
}
pub fn add_elev_loaded(&mut self, elev_data: &ElevData, overwrite: bool) -> Res<()> {
let xy_yx = |wp: &Waypoint| -> srtm_reader::Coord {
let (x, y) = wp.point().x_y();
(y, x).into()
};
Ok(self
.track_segment
.points
.par_iter_mut()
.filter(|wp| (wp.elevation.is_none() || overwrite) && !utils::is_00(wp))
.try_for_each(|wp| {
let coord = xy_yx(wp);
if let Some(elev_data) = elev_data.get(&coord.trunc()) {
let elev = elev_data.get(coord);
wp.elevation = elev.map(|x| f64::from(*x));
Ok(())
} else {
Err(format!("`elev_data` didn't contain {coord:?}"))
}
})?)
}
}