• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

geo-engine / geoengine / 22324356117

23 Feb 2026 08:51PM UTC coverage: 88.116%. First build
22324356117

Pull #1120

github

web-flow
Merge 771461a67 into 078c12706
Pull Request #1120: fix: Cache and Stacker should keep band order for subsets

968 of 1020 new or added lines in 19 files covered. (94.9%)

112857 of 128078 relevant lines covered (88.12%)

505639.94 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

88.97
/datatypes/src/primitives/query_rectangle.rs
1
use super::{AxisAlignedRectangle, BoundingBox2D, TimeInterval};
2
use crate::raster::{GeoTransform, GridBoundingBox2D};
3
use crate::{
4
    error::{DuplicateBandInQueryBandSelection, QueryBandSelectionMustNotBeEmpty},
5
    util::Result,
6
};
7
use serde::{Deserialize, Serialize};
8
use snafu::ensure;
9

10
/// A spatio-temporal rectangle with a specified resolution and the selected bands
11
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
12
#[serde(rename_all = "camelCase")]
13
pub struct QueryRectangle<SpatialBounds, AttributeSelection> {
14
    spatial_bounds: SpatialBounds,
15
    time_interval: TimeInterval,
16
    attributes: AttributeSelection,
17
}
18

19
impl<SpatialBounds: Copy, A: Clone> QueryRectangle<SpatialBounds, A> {
20
    pub fn time_interval(&self) -> TimeInterval {
19,284✔
21
        self.time_interval
19,284✔
22
    }
19,284✔
23

24
    pub fn spatial_bounds(&self) -> SpatialBounds {
12,178✔
25
        self.spatial_bounds
12,178✔
26
    }
12,178✔
27

28
    pub fn spatial_bounds_mut(&mut self) -> &mut SpatialBounds {
5✔
29
        &mut self.spatial_bounds
5✔
30
    }
5✔
31

32
    pub fn time_interval_mut(&mut self) -> &mut TimeInterval {
5✔
33
        &mut self.time_interval
5✔
34
    }
5✔
35

36
    pub fn attributes(&self) -> &A {
3,320✔
37
        &self.attributes
3,320✔
38
    }
3,320✔
39

40
    pub fn attributes_mut(&mut self) -> &mut A {
×
41
        &mut self.attributes
×
42
    }
×
43

44
    /// Creates a new `QueryRectangle` from a `BoundingBox2D`, and a `TimeInterval`
45
    pub fn new(spatial_bounds: SpatialBounds, time_interval: TimeInterval, attributes: A) -> Self {
990✔
46
        Self {
990✔
47
            spatial_bounds,
990✔
48
            time_interval,
990✔
49
            attributes,
990✔
50
        }
990✔
51
    }
990✔
52

53
    /// Create a clone of `self` with another `TimeInterval`.
54
    #[must_use]
55
    pub fn select_time_interval(&self, time_interval: TimeInterval) -> Self {
6✔
56
        Self {
6✔
57
            spatial_bounds: self.spatial_bounds(),
6✔
58
            time_interval,
6✔
59
            attributes: self.attributes().clone(),
6✔
60
        }
6✔
61
    }
6✔
62

63
    /// Create a clone of `self` with other `SpatialBounds`.
64
    #[must_use]
65
    pub fn select_spatial_bounds(&self, spatial_bounds: SpatialBounds) -> Self {
6✔
66
        Self {
6✔
67
            spatial_bounds,
6✔
68
            time_interval: self.time_interval(),
6✔
69
            attributes: self.attributes().clone(),
6✔
70
        }
6✔
71
    }
6✔
72

73
    /// Create a copy of `self` with other `QueryAttributeSelection`.
74
    /// This method also allow to change the type of `QueryAttributeSelection`.
75
    #[must_use]
76
    pub fn select_attributes<B: QueryAttributeSelection>(
160✔
77
        &self,
160✔
78
        attributes: B,
160✔
79
    ) -> QueryRectangle<SpatialBounds, B> {
160✔
80
        QueryRectangle {
160✔
81
            spatial_bounds: self.spatial_bounds,
160✔
82
            time_interval: self.time_interval,
160✔
83
            attributes,
160✔
84
        }
160✔
85
    }
160✔
86
}
87

88
pub type VectorQueryRectangle = QueryRectangle<BoundingBox2D, ColumnSelection>;
89
pub type RasterQueryRectangle = QueryRectangle<GridBoundingBox2D, BandSelection>;
90
pub type PlotQueryRectangle = QueryRectangle<BoundingBox2D, PlotSeriesSelection>;
91

92
// Implementation for VectorQueryRectangle and PlotQueryRectangle
93
impl<A> QueryRectangle<BoundingBox2D, A>
94
where
95
    A: QueryAttributeSelection,
96
{
97
    /// Creates a new `QueryRectangle` with bounds and time from a `RasterQueryRectangle` and supplied attributes.
98
    ///
99
    /// # Panics
100
    /// If the `geo_transform` can't transform the raster bounds into a valid `SpatialPartition`
101
    pub fn from_raster_query_and_geo_transform_replace_attributes(
×
102
        raster_query: &RasterQueryRectangle,
×
103
        geo_transform: GeoTransform,
×
104
        attributes: A,
×
105
    ) -> QueryRectangle<BoundingBox2D, A> {
×
106
        let bounds = geo_transform.grid_to_spatial_bounds(&raster_query.spatial_bounds());
×
107
        let bounding_box = BoundingBox2D::from_min_max(bounds.lower_left(), bounds.upper_right())
×
108
            .expect("Bounds are already valid");
×
109

110
        QueryRectangle::new(bounding_box, raster_query.time_interval, attributes)
×
111
    }
×
112
}
113

114
impl RasterQueryRectangle {
115
    /// Creates a new `QueryRectangle` that describes the requested grid.
116
    /// The spatial query is derived from a vector query rectangle and a `GeoTransform`.
117
    /// The temporal query is defined by a `TimeInterval`.
118
    /// NOTE: If the distance between the upper left of the spatial partition and the origin coordinate is not at a multiple of the spatial resolution, the grid bounds will be shifted.
119
    pub fn from_bounds_and_geo_transform<A: QueryAttributeSelection>(
29✔
120
        query: &QueryRectangle<BoundingBox2D, A>,
29✔
121
        bands: BandSelection,
29✔
122
        geo_transform: GeoTransform,
29✔
123
    ) -> Self {
29✔
124
        let grid_bounds =
29✔
125
            geo_transform.bounding_box_2d_to_intersecting_grid_bounds(&query.spatial_bounds());
29✔
126
        Self::new(grid_bounds, query.time_interval, bands)
29✔
127
    }
29✔
128
}
129

130
pub trait QueryAttributeSelection: Clone + Send + Sync {}
131

132
#[derive(Clone, Debug, PartialEq, Serialize)]
133
pub struct BandSelection(Vec<u32>);
134

135
impl BandSelection {
136
    pub fn new(bands: Vec<u32>) -> Result<Self> {
32✔
137
        fn has_no_duplicates<T: std::hash::Hash + std::cmp::Eq>(vec: &[T]) -> bool {
32✔
138
            let set: std::collections::HashSet<_> = vec.iter().collect();
32✔
139
            set.len() == vec.len()
32✔
140
        }
32✔
141

142
        ensure!(has_no_duplicates(&bands), DuplicateBandInQueryBandSelection);
32✔
143
        ensure!(!bands.is_empty(), QueryBandSelectionMustNotBeEmpty);
32✔
144

145
        Ok(Self(bands))
32✔
146
    }
32✔
147

148
    pub fn new_unchecked(bands: Vec<u32>) -> Self {
260✔
149
        Self(bands)
260✔
150
    }
260✔
151

152
    pub fn first() -> Self {
207✔
153
        Self(vec![0])
207✔
154
    }
207✔
155

156
    pub fn first_n(n: u32) -> Self {
362✔
157
        Self((0..n).collect())
362✔
158
    }
362✔
159

160
    pub fn new_single(band: u32) -> Self {
137✔
161
        Self(vec![band])
137✔
162
    }
137✔
163

164
    pub fn count(&self) -> u32 {
296✔
165
        self.0.len() as u32
296✔
166
    }
296✔
167

168
    pub fn as_slice(&self) -> &[u32] {
9,698✔
169
        &self.0
9,698✔
170
    }
9,698✔
171

172
    pub fn as_vec(&self) -> Vec<u32> {
3,725✔
173
        self.0.clone()
3,725✔
174
    }
3,725✔
175

176
    pub fn is_single(&self) -> bool {
19✔
177
        self.count() == 1
19✔
178
    }
19✔
179

180
    pub fn contains(&self, band: u32) -> bool {
270✔
181
        self.0.contains(&band)
270✔
182
    }
270✔
183

184
    pub fn contains_all(&self, bands: &[u32]) -> bool {
7✔
185
        bands.iter().all(|band| self.contains(*band))
7✔
186
    }
7✔
187
}
188

189
impl From<u32> for BandSelection {
190
    fn from(value: u32) -> Self {
301✔
191
        Self(vec![value])
301✔
192
    }
301✔
193
}
194

195
impl AsRef<[u32]> for BandSelection {
196
    fn as_ref(&self) -> &[u32] {
7✔
197
        self.as_slice()
7✔
198
    }
7✔
199
}
200

201
impl TryFrom<Vec<u32>> for BandSelection {
202
    type Error = crate::error::Error;
203

204
    fn try_from(value: Vec<u32>) -> Result<Self, Self::Error> {
3✔
205
        Self::new(value)
3✔
206
    }
3✔
207
}
208

209
impl<const N: usize> TryFrom<[u32; N]> for BandSelection {
210
    type Error = crate::error::Error;
211

212
    fn try_from(value: [u32; N]) -> Result<Self, Self::Error> {
15✔
213
        Self::new(value.to_vec())
15✔
214
    }
15✔
215
}
216

217
impl TryFrom<&[u32]> for BandSelection {
218
    type Error = crate::error::Error;
219

NEW
220
    fn try_from(value: &[u32]) -> Result<Self, Self::Error> {
×
NEW
221
        Self::new(value.to_vec())
×
NEW
222
    }
×
223
}
224

225
impl QueryAttributeSelection for BandSelection {}
226

227
#[derive(Clone, Debug, PartialEq, Serialize)]
228
pub struct BandSelectionIter {
229
    pub band_selection: BandSelection,
230
    next_index: usize,
231
}
232

233
impl BandSelectionIter {
234
    pub fn new(band_selection: BandSelection) -> Self {
493✔
235
        Self {
493✔
236
            band_selection,
493✔
237
            next_index: 0,
493✔
238
        }
493✔
239
    }
493✔
240

241
    pub fn reset(&mut self) {
214,041✔
242
        self.next_index = 0;
214,041✔
243
    }
214,041✔
244
}
245

246
impl Iterator for BandSelectionIter {
247
    type Item = u32;
248

249
    fn next(&mut self) -> Option<Self::Item> {
428,314✔
250
        if self.next_index >= self.band_selection.0.len() {
428,314✔
251
            return None;
213,965✔
252
        }
214,349✔
253

254
        let item = self.band_selection.0[self.next_index];
214,349✔
255
        self.next_index += 1;
214,349✔
256
        Some(item)
214,349✔
257
    }
428,314✔
258
}
259

260
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
261
pub struct ColumnSelection {}
262

263
impl ColumnSelection {
264
    pub fn all() -> Self {
177✔
265
        Self {}
177✔
266
    }
177✔
267
}
268

269
impl QueryAttributeSelection for ColumnSelection {}
270

271
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
272
pub struct PlotSeriesSelection {}
273

274
impl PlotSeriesSelection {
275
    pub fn all() -> Self {
56✔
276
        Self {}
56✔
277
    }
56✔
278
}
279

280
impl QueryAttributeSelection for PlotSeriesSelection {}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc