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

geo-engine / geoengine / 22314075928

23 Feb 2026 04:01PM UTC coverage: 88.111%. First build
22314075928

Pull #1120

github

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

900 of 950 new or added lines in 19 files covered. (94.74%)

112789 of 128008 relevant lines covered (88.11%)

516846.56 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 {
21,601✔
21
        self.time_interval
21,601✔
22
    }
21,601✔
23

24
    pub fn spatial_bounds(&self) -> SpatialBounds {
12,398✔
25
        self.spatial_bounds
12,398✔
26
    }
12,398✔
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,769✔
37
        &self.attributes
3,769✔
38
    }
3,769✔
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 {
1,221✔
46
        Self {
1,221✔
47
            spatial_bounds,
1,221✔
48
            time_interval,
1,221✔
49
            attributes,
1,221✔
50
        }
1,221✔
51
    }
1,221✔
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>(
100✔
77
        &self,
100✔
78
        attributes: B,
100✔
79
    ) -> QueryRectangle<SpatialBounds, B> {
100✔
80
        QueryRectangle {
100✔
81
            spatial_bounds: self.spatial_bounds,
100✔
82
            time_interval: self.time_interval,
100✔
83
            attributes,
100✔
84
        }
100✔
85
    }
100✔
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 {
341✔
149
        Self(bands)
341✔
150
    }
341✔
151

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

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

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

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

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

172
    pub fn as_vec(&self) -> Vec<u32> {
1,146✔
173
        self.0.clone()
1,146✔
174
    }
1,146✔
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 {
1,348✔
181
        self.0.contains(&band)
1,348✔
182
    }
1,348✔
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 {
623✔
235
        Self {
623✔
236
            band_selection,
623✔
237
            next_index: 0,
623✔
238
        }
623✔
239
    }
623✔
240

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

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

249
    fn next(&mut self) -> Option<Self::Item> {
430,338✔
250
        if self.next_index >= self.band_selection.0.len() {
430,338✔
251
            return None;
214,619✔
252
        }
215,719✔
253

254
        let item = self.band_selection.0[self.next_index];
215,719✔
255
        self.next_index += 1;
215,719✔
256
        Some(item)
215,719✔
257
    }
430,338✔
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