open_vector_tile/open/
grid_layer.rs

1use crate::{delta_decode_array, delta_encode_array, open::Extent};
2use alloc::{string::String, vec::Vec};
3use libm::round;
4use pbf::{ProtoRead, ProtoWrite, Protobuf};
5
6// TODO: This could be faster if we don't read in the grid data on parsing but only if the user needs it
7
8/// Gridded data object to read from
9#[derive(Default, Debug, PartialEq)]
10pub struct GridData {
11    /// The name of the gridded data
12    pub name: String,
13    /// The grid data
14    pub data: Vec<f64>,
15    /// The extent for remapping the data with a value between 0 and extent
16    pub extent: Extent,
17    /// The size of the tile (width and height)
18    pub size: f64,
19    /// The minimum grid value
20    pub min: f64,
21    /// The maximum grid value
22    pub max: f64,
23}
24impl GridData {
25    /// create a new GridData object
26    pub fn new(
27        name: String,
28        extent: Extent,
29        size: f64,
30        min: f64,
31        max: f64,
32        data: Vec<f64>,
33    ) -> Self {
34        GridData { name, data, extent, size, min, max }
35    }
36}
37impl ProtoRead for GridData {
38    fn read(&mut self, tag: u64, pb: &mut Protobuf) {
39        match tag {
40            1 => self.extent = pb.read_varint(),
41            2 => self.size = pb.read_varint(),
42            3 => self.min = pb.read_varint(),
43            4 => self.max = pb.read_varint(),
44            5 => {
45                self.data = delta_decode_array(&pb.read_packed())
46                    .into_iter()
47                    .map(|v| unmap_value(v as f64, self.min, self.max, self.extent.into()))
48                    .collect()
49            }
50            6 => self.name = pb.read_string(),
51            _ => panic!("unknown tag: {}", tag),
52        }
53    }
54}
55impl ProtoWrite for GridData {
56    fn write(&self, pb: &mut Protobuf) {
57        let max = self.data.iter().fold(f64::MIN, |a, b| f64::max(a, *b));
58        let min = self.data.iter().fold(f64::MAX, |a, b| f64::min(a, *b));
59        let re_mapped: Vec<u32> =
60            self.data.iter().map(|v| remap_value(*v, min, max, self.extent.into())).collect();
61        let d_coded = delta_encode_array(&re_mapped);
62
63        pb.write_varint_field(1, self.extent);
64        pb.write_varint_field(2, self.size);
65        pb.write_varint_field(3, min);
66        pb.write_varint_field(4, max);
67        pb.write_packed_varint(5, &d_coded);
68        pb.write_string_field(6, &self.name);
69    }
70}
71
72/// map the value to the range 0->extent
73fn remap_value(value: f64, min: f64, max: f64, extent: f64) -> u32 {
74    round(((value - min) * extent) / (max - min)) as u32
75}
76
77/// map the value back to floats
78fn unmap_value(value: f64, min: f64, max: f64, extent: f64) -> f64 {
79    (value * (max - min)) / extent + min
80}
81
82/// convert rgb to elevation using terrarium formula
83pub fn convert_terrarium_elevation_data(r: u8, g: u8, b: u8) -> f64 {
84    r as f64 * 256. + g as f64 + b as f64 / 256. - 32768.
85}
86
87/// convert rgb to elevation using mapbox formula
88pub fn convert_mapbox_elevation_data(r: u8, g: u8, b: u8) -> f64 {
89    -10000. + (r as f64 * 256. * 256. + g as f64 * 256. + b as f64) * 0.1
90}