Skip to main content

rusterize/encoding/
arrays.rs

1use crate::{
2    geo::raster::RasterInfo,
3    prelude::{RasterDtype, RasterizeContext},
4    rasterization::pixel_functions::PixelFn,
5};
6use ndarray::Array3;
7use num_traits::Num;
8use rayon::iter::{IndexedParallelIterator, IntoParallelIterator, IntoParallelRefIterator, ParallelIterator};
9
10/// A materialized 3-dimensional array containing the burned geometries and spatial information.
11pub struct DenseArray<N> {
12    array: Array3<N>,
13    band_names: Vec<String>,
14    raster_info: RasterInfo,
15}
16
17impl<N: Num> DenseArray<N> {
18    pub(crate) fn new(array: Array3<N>, band_names: Vec<String>, raster_info: RasterInfo) -> Self {
19        Self {
20            array,
21            band_names,
22            raster_info,
23        }
24    }
25
26    /// Consume self and extract all fields of the DenseArray.
27    pub fn into_parts(self) -> (Array3<N>, Vec<String>, RasterInfo) {
28        (self.array, self.band_names, self.raster_info)
29    }
30
31    /// Inner array containing the burned geometries
32    pub fn array(&self) -> &Array3<N> {
33        &self.array
34    }
35
36    /// Sorted band names for the array. Defaults to "band_1" for a single band.
37    pub fn band_names(&self) -> &[String] {
38        &self.band_names
39    }
40
41    /// Spatial information associated with the array.
42    pub fn raster_info(&self) -> &RasterInfo {
43        &self.raster_info
44    }
45}
46
47/// Triplets of (row, col, value) for all bands as a contiguous block.
48/// Used to store inside a [`SparseArray`].
49struct Triplets<N> {
50    rows: Vec<u64>,
51    cols: Vec<u64>,
52    data: Vec<N>,
53}
54
55impl<N: Num> Triplets<N> {
56    fn new(rows: Vec<u64>, cols: Vec<u64>, data: Vec<N>) -> Self {
57        Self { rows, cols, data }
58    }
59}
60
61/// A sparse array in COOordinate format storing the band/row/col value triplets.
62/// of all burned [`geo::Geometry`].
63pub struct SparseArray<N> {
64    band_names: Vec<String>,
65    triplets: Triplets<N>,
66    offsets: Vec<usize>,
67    raster_info: RasterInfo,
68    pxfn: PixelFn<N>,
69    background: N,
70}
71
72impl<N> SparseArray<N>
73where
74    N: RasterDtype,
75{
76    pub(crate) fn new(
77        band_names: Vec<String>,
78        rows: Vec<u64>,
79        cols: Vec<u64>,
80        data: Vec<N>,
81        offsets: Vec<usize>,
82        ctx: RasterizeContext<N>,
83    ) -> Self {
84        let pxfn = ctx.pixel_fn();
85        let background = ctx.background;
86
87        Self {
88            band_names,
89            triplets: Triplets::new(rows, cols, data),
90            offsets,
91            raster_info: ctx.raster_info,
92            pxfn,
93            background,
94        }
95    }
96
97    /// Get the band names associated with this array.
98    pub fn band_names(&self) -> &[String] {
99        &self.band_names
100    }
101
102    /// Materialize a [`ndarray::Array3`] from this. Drops spatial information.
103    pub fn build_array(&self) -> Array3<N> {
104        let mut raster = self.raster_info.build_raster(self.band_names.len(), self.background);
105
106        let rows = self.triplets.rows.as_slice();
107        let cols = self.triplets.cols.as_slice();
108        let data = self.triplets.data.as_slice();
109
110        // per-band start offset into the contiguous triplet arrays
111        let offsets = self
112            .offsets
113            .iter()
114            .scan(0, |state, &n| {
115                let start = *state;
116                *state += n;
117                Some(start)
118            })
119            .collect::<Vec<usize>>();
120
121        raster
122            .outer_iter_mut()
123            .into_par_iter()
124            .zip(self.offsets.par_iter())
125            .zip(offsets.par_iter())
126            .for_each(|((mut band, n), &off)| {
127                let end = off + *n;
128                let band_rows = &rows[off..end];
129                let band_cols = &cols[off..end];
130                let band_data = &data[off..end];
131
132                for ((band_row, band_col), band_value) in band_rows.iter().zip(band_cols).zip(band_data) {
133                    (self.pxfn)(
134                        &mut band,
135                        *band_row as usize,
136                        *band_col as usize,
137                        *band_value,
138                        self.background,
139                    );
140                }
141            });
142        raster
143    }
144
145    pub fn extent(&self) -> (f64, f64, f64, f64) {
146        (
147            self.raster_info.xmin,
148            self.raster_info.ymin,
149            self.raster_info.xmax,
150            self.raster_info.ymax,
151        )
152    }
153
154    pub fn shape(&self) -> (usize, usize, usize) {
155        (self.band_names.len(), self.raster_info.nrows, self.raster_info.ncols)
156    }
157
158    pub fn resolution(&self) -> (f64, f64) {
159        (self.raster_info.xres, self.raster_info.yres)
160    }
161
162    /// Get spatial information associated with this array.
163    pub fn raster_info(&self) -> &RasterInfo {
164        &self.raster_info
165    }
166
167    pub fn epsg(&self) -> Option<u16> {
168        self.raster_info.epsg
169    }
170}
171
172#[cfg(feature = "polars")]
173mod feature_gated {
174    use super::SparseArray;
175    use crate::prelude::PolarsHandler;
176    use num_traits::Num;
177    use polars::prelude::*;
178
179    impl<N> SparseArray<N>
180    where
181        N: Num + Copy + PolarsHandler,
182    {
183        /// Convert this to a [`polars::prelude::DataFrame`].
184        pub fn to_frame(&self) -> DataFrame {
185            let mut columns: Vec<Column> = Vec::new();
186
187            // add bands for multiband raster
188            if self.offsets.len() > 1 {
189                let bands = self
190                    .offsets
191                    .iter()
192                    .enumerate()
193                    .flat_map(|(i, v)| std::iter::repeat_n(i + 1, *v))
194                    .map(|b| b as u64)
195                    .collect::<Vec<u64>>();
196                let bands_column = Column::new("band".into(), bands);
197                columns.push(bands_column);
198            }
199
200            columns.push(Column::new("row".into(), self.triplets.rows.as_slice()));
201            columns.push(Column::new("col".into(), self.triplets.cols.as_slice()));
202
203            let height = self.triplets.data.len();
204            columns.push(N::from_named_vec("values", &self.triplets.data));
205
206            DataFrame::new(height, columns).unwrap()
207        }
208    }
209}