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

geo-engine / geoengine / 10178074589

31 Jul 2024 09:34AM UTC coverage: 91.068% (+0.4%) from 90.682%
10178074589

push

github

web-flow
Merge pull request #973 from geo-engine/remove-XGB-update-toolchain

Remove-XGB-update-toolchain

81 of 88 new or added lines in 29 files covered. (92.05%)

456 existing lines in 119 files now uncovered.

131088 of 143945 relevant lines covered (91.07%)

53581.03 hits per line

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

77.97
/datatypes/src/raster/raster_tile.rs
1
use super::masked_grid::MaskedGrid;
2
use super::{
3
    grid_or_empty::GridOrEmpty, GeoTransform, GeoTransformAccess, GridBounds, GridIdx2D,
4
    GridIndexAccess, GridShape, GridShape2D, GridShape3D, GridShapeAccess, GridSize, Raster,
5
    TileInformation,
6
};
7
use super::{GridIndexAccessMut, RasterProperties};
8
use crate::primitives::CacheHint;
9
use crate::primitives::{
10
    SpatialBounded, SpatialPartition2D, SpatialPartitioned, SpatialResolution, TemporalBounded,
11
    TimeInterval,
12
};
13
use crate::raster::Pixel;
14
use crate::util::{ByteSize, Result};
15
use serde::{Deserialize, Serialize};
16

17
/// A `RasterTile` is a `BaseTile` of raster data where the data is represented by `GridOrEmpty`.
18
pub type RasterTile<D, T> = BaseTile<GridOrEmpty<D, T>>;
19
/// A `RasterTile2D` is a `BaseTile` of 2-dimensional raster data where the data is represented by `GridOrEmpty`.
20
pub type RasterTile2D<T> = RasterTile<GridShape2D, T>;
21
/// A `RasterTile3D` is a `BaseTile` of 3-dimensional raster data where the data is represented by `GridOrEmpty`.
22
pub type RasterTile3D<T> = RasterTile<GridShape3D, T>;
23

24
/// A `MaterializedRasterTile` is a `BaseTile` of raster data where the data is represented by `Grid`. It implements mutable access to pixels.
25
pub type MaterializedRasterTile<D, T> = BaseTile<MaskedGrid<D, T>>;
26
/// A `MaterializedRasterTile2D` is a 2-dimensional `BaseTile` of raster data where the data is represented by `Grid`. It implements mutable access to pixels.
27
pub type MaterializedRasterTile2D<T> = MaterializedRasterTile<GridShape2D, T>;
28
/// A `MaterializedRasterTile3D` is a 3-dimensional `BaseTile` of raster data where the data is represented by `Grid`. It implements mutable access to pixels.
29
pub type MaterializedRasterTile3D<T> = MaterializedRasterTile<GridShape3D, T>;
30

31
/// A `BaseTile` is the main type used to iterate over tiles of raster data
32
/// The data of the `RasterTile` is stored as `Grid` or `NoDataGrid`. The enum `GridOrEmpty` allows a combination of both.
33
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
184✔
34
#[serde(rename_all = "camelCase")]
35
pub struct BaseTile<G> {
36
    /// The `TimeInterval` where this tile is valid.
37
    pub time: TimeInterval,
38
    /// The tile position is the position of the tile in the gird of tiles with origin at the origin of the `global_geo_transform`.
39
    /// This is NOT a pixel position inside the tile.
40
    pub tile_position: GridIdx2D,
41
    // the band of the tile, relevant for multi-band raster
42
    pub band: u32,
43
    /// The global geotransform to transform pixels into geographic coordinates
44
    pub global_geo_transform: GeoTransform,
45
    /// The pixels of the tile are stored as `Grid` or, in case they are all no-data as `NoDataGrid`.
46
    /// The enum `GridOrEmpty` allows a combination of both.
47
    pub grid_array: G,
48
    /// Metadata for the `BaseTile`
49
    pub properties: RasterProperties,
50
    /// Indicate how long the tile may be cached, if `None` the tile may be cached indefinitely.
51
    pub cache_hint: CacheHint,
52
}
53

54
impl<G> BaseTile<G>
55
where
56
    G: GridSize,
57
{
58
    pub fn tile_offset(&self) -> GridIdx2D {
×
59
        self.tile_position
×
60
    }
×
61

62
    pub fn tile_information(&self) -> TileInformation {
3,780✔
63
        TileInformation::new(
3,780✔
64
            self.tile_position,
3,780✔
65
            [self.grid_array.axis_size_y(), self.grid_array.axis_size_x()].into(),
3,780✔
66
            self.global_geo_transform,
3,780✔
67
        )
3,780✔
68
    }
3,780✔
69

70
    /// Use this geo transform to transform `Coordinate2D` into local grid indices and vice versa.
71
    #[inline]
72
    pub fn tile_geo_transform(&self) -> GeoTransform {
3,349,013✔
73
        let global_upper_left_idx = self.tile_position
3,349,013✔
74
            * [
3,349,013✔
75
                self.grid_array.axis_size_y() as isize,
3,349,013✔
76
                self.grid_array.axis_size_x() as isize,
3,349,013✔
77
            ];
3,349,013✔
78

3,349,013✔
79
        let tile_upper_left_coord = self
3,349,013✔
80
            .global_geo_transform
3,349,013✔
81
            .grid_idx_to_pixel_upper_left_coordinate_2d(global_upper_left_idx);
3,349,013✔
82

3,349,013✔
83
        GeoTransform::new(
3,349,013✔
84
            tile_upper_left_coord,
3,349,013✔
85
            self.global_geo_transform.x_pixel_size(),
3,349,013✔
86
            self.global_geo_transform.y_pixel_size(),
3,349,013✔
87
        )
3,349,013✔
88
    }
3,349,013✔
89

90
    pub fn spatial_resolution(&self) -> SpatialResolution {
2✔
91
        self.global_geo_transform.spatial_resolution()
2✔
92
    }
2✔
93
}
94

95
impl<G> ByteSize for BaseTile<G>
96
where
97
    G: ByteSize,
98
{
99
    fn heap_byte_size(&self) -> usize {
23✔
100
        self.grid_array.heap_byte_size() + self.properties.heap_byte_size()
23✔
101
    }
23✔
102
}
103

104
/// A way to compare two `BaseTile` ignoring the `CacheHint` and only considering the actual data.
105
pub trait TilesEqualIgnoringCacheHint<G: PartialEq> {
106
    fn tiles_equal_ignoring_cache_hint(&self, other: &dyn IterableBaseTile<G>) -> bool;
107
}
108

109
/// Allow comparing Iterables of `BaseTile` ignoring the `CacheHint` and only considering the actual data.
110
pub trait IterableBaseTile<G> {
111
    fn iter_tiles(&self) -> Box<dyn Iterator<Item = &BaseTile<G>> + '_>;
112
}
113

114
struct SingleBaseTileIter<'a, G> {
115
    tile: Option<&'a BaseTile<G>>,
116
}
117

118
impl<'a, G> Iterator for SingleBaseTileIter<'a, G> {
119
    type Item = &'a BaseTile<G>;
120

121
    fn next(&mut self) -> Option<Self::Item> {
164✔
122
        self.tile.take()
164✔
123
    }
164✔
124
}
125

126
impl<G: PartialEq> IterableBaseTile<G> for BaseTile<G> {
127
    fn iter_tiles(&self) -> Box<dyn Iterator<Item = &BaseTile<G>> + '_> {
82✔
128
        Box::new(SingleBaseTileIter { tile: Some(self) })
82✔
129
    }
82✔
130
}
131

132
impl<G: PartialEq> IterableBaseTile<G> for Vec<BaseTile<G>> {
133
    fn iter_tiles(&self) -> Box<dyn Iterator<Item = &BaseTile<G>> + '_> {
38✔
134
        Box::new(self.iter())
38✔
135
    }
38✔
136
}
137

138
impl<G: PartialEq, const N: usize> IterableBaseTile<G> for [BaseTile<G>; N] {
139
    fn iter_tiles(&self) -> Box<dyn Iterator<Item = &BaseTile<G>> + '_> {
4✔
140
        Box::new(self.iter())
4✔
141
    }
4✔
142
}
143

144
impl<G: PartialEq, I: IterableBaseTile<G>> TilesEqualIgnoringCacheHint<G> for I {
145
    fn tiles_equal_ignoring_cache_hint(&self, other: &dyn IterableBaseTile<G>) -> bool {
62✔
146
        let mut iter_self = self.iter_tiles();
62✔
147
        let mut iter_other = other.iter_tiles();
62✔
148

149
        loop {
150
            match (iter_self.next(), iter_other.next()) {
303✔
151
                (Some(a), Some(b)) => {
241✔
152
                    if a.time != b.time
241✔
153
                        || a.tile_position != b.tile_position
241✔
154
                        || a.band != b.band
241✔
155
                        || a.global_geo_transform != b.global_geo_transform
241✔
156
                        || a.grid_array != b.grid_array
241✔
157
                        || a.properties != b.properties
241✔
158
                    {
159
                        return false;
×
160
                    }
241✔
161
                }
162
                // both iterators are exhausted
163
                (None, None) => return true,
62✔
164
                // one iterator is exhausted, the other is not, so they are not equal
165
                _ => return false,
×
166
            }
167
        }
168
    }
62✔
169
}
170

171
impl<D, T> BaseTile<GridOrEmpty<D, T>>
172
where
173
    T: Pixel,
174
    D: GridSize + Clone + PartialEq,
175
{
176
    /// create a new `RasterTile`
177
    pub fn new_with_tile_info(
483✔
178
        time: TimeInterval,
483✔
179
        tile_info: TileInformation,
483✔
180
        band: u32,
483✔
181
        data: GridOrEmpty<D, T>,
483✔
182
        cache_hint: CacheHint,
483✔
183
    ) -> Self
483✔
184
    where
483✔
185
        D: GridSize,
483✔
186
    {
483✔
187
        debug_assert_eq!(
483✔
188
            tile_info.tile_size_in_pixels.axis_size_x(),
483✔
189
            data.shape_ref().axis_size_x()
483✔
190
        );
191

192
        debug_assert_eq!(
483✔
193
            tile_info.tile_size_in_pixels.axis_size_y(),
483✔
194
            data.shape_ref().axis_size_y()
483✔
195
        );
196

197
        debug_assert_eq!(
483✔
198
            tile_info.tile_size_in_pixels.number_of_elements(),
483✔
199
            data.shape_ref().number_of_elements()
483✔
200
        );
201

202
        Self {
483✔
203
            time,
483✔
204
            tile_position: tile_info.global_tile_position,
483✔
205
            band,
483✔
206
            global_geo_transform: tile_info.global_geo_transform,
483✔
207
            grid_array: data,
483✔
208
            properties: Default::default(),
483✔
209
            cache_hint,
483✔
210
        }
483✔
211
    }
483✔
212

213
    /// create a new `RasterTile`
214
    pub fn new_with_tile_info_and_properties(
946✔
215
        time: TimeInterval,
946✔
216
        tile_info: TileInformation,
946✔
217
        band: u32,
946✔
218
        data: GridOrEmpty<D, T>,
946✔
219
        properties: RasterProperties,
946✔
220
        cache_hint: CacheHint,
946✔
221
    ) -> Self {
946✔
222
        debug_assert_eq!(
946✔
223
            tile_info.tile_size_in_pixels.axis_size_x(),
946✔
224
            data.shape_ref().axis_size_x()
946✔
225
        );
226

227
        debug_assert_eq!(
946✔
228
            tile_info.tile_size_in_pixels.axis_size_y(),
946✔
229
            data.shape_ref().axis_size_y()
946✔
230
        );
231

232
        debug_assert_eq!(
946✔
233
            tile_info.tile_size_in_pixels.number_of_elements(),
946✔
234
            data.shape_ref().number_of_elements()
946✔
235
        );
236

237
        Self {
946✔
238
            time,
946✔
239
            tile_position: tile_info.global_tile_position,
946✔
240
            band,
946✔
241
            global_geo_transform: tile_info.global_geo_transform,
946✔
242
            grid_array: data,
946✔
243
            properties,
946✔
244
            cache_hint,
946✔
245
        }
946✔
246
    }
946✔
247

248
    /// create a new `RasterTile`
249
    pub fn new(
213,323✔
250
        time: TimeInterval,
213,323✔
251
        tile_position: GridIdx2D,
213,323✔
252
        band: u32,
213,323✔
253
        global_geo_transform: GeoTransform,
213,323✔
254
        data: GridOrEmpty<D, T>,
213,323✔
255
        cache_hint: CacheHint,
213,323✔
256
    ) -> Self {
213,323✔
257
        Self {
213,323✔
258
            time,
213,323✔
259
            tile_position,
213,323✔
260
            band,
213,323✔
261
            global_geo_transform,
213,323✔
262
            grid_array: data,
213,323✔
263
            properties: RasterProperties::default(),
213,323✔
264
            cache_hint,
213,323✔
265
        }
213,323✔
266
    }
213,323✔
267

268
    /// create a new `RasterTile`
UNCOV
269
    pub fn new_with_properties(
×
UNCOV
270
        time: TimeInterval,
×
UNCOV
271
        tile_position: GridIdx2D,
×
UNCOV
272
        band: u32,
×
UNCOV
273
        global_geo_transform: GeoTransform,
×
UNCOV
274
        data: GridOrEmpty<D, T>,
×
UNCOV
275
        properties: RasterProperties,
×
UNCOV
276
        cache_hint: CacheHint,
×
UNCOV
277
    ) -> Self {
×
UNCOV
278
        Self {
×
UNCOV
279
            time,
×
UNCOV
280
            tile_position,
×
UNCOV
281
            band,
×
UNCOV
282
            global_geo_transform,
×
UNCOV
283
            grid_array: data,
×
UNCOV
284
            properties,
×
UNCOV
285
            cache_hint,
×
UNCOV
286
        }
×
UNCOV
287
    }
×
288

289
    /// create a new `RasterTile`
290
    pub fn new_without_offset<G>(
31✔
291
        time: TimeInterval,
31✔
292
        global_geo_transform: GeoTransform,
31✔
293
        data: G,
31✔
294
        cache_hint: CacheHint,
31✔
295
    ) -> Self
31✔
296
    where
31✔
297
        G: Into<GridOrEmpty<D, T>>,
31✔
298
    {
31✔
299
        Self {
31✔
300
            time,
31✔
301
            tile_position: [0, 0].into(),
31✔
302
            band: 0,
31✔
303
            global_geo_transform,
31✔
304
            grid_array: data.into(),
31✔
305
            properties: RasterProperties::default(),
31✔
306
            cache_hint,
31✔
307
        }
31✔
308
    }
31✔
309

310
    /// Returns true if the grid is a `NoDataGrid`
311
    pub fn is_empty(&self) -> bool {
340✔
312
        self.grid_array.is_empty()
340✔
313
    }
340✔
314

315
    /// Convert the tile into a materialized tile.
316
    pub fn into_materialized_tile(self) -> MaterializedRasterTile<D, T> {
324✔
317
        MaterializedRasterTile {
324✔
318
            grid_array: self.grid_array.into_materialized_masked_grid(),
324✔
319
            time: self.time,
324✔
320
            tile_position: self.tile_position,
324✔
321
            band: 0,
324✔
322
            global_geo_transform: self.global_geo_transform,
324✔
323
            properties: self.properties,
324✔
324
            cache_hint: self.cache_hint.clone_with_current_datetime(),
324✔
325
        }
324✔
326
    }
324✔
327

328
    pub fn materialize(&mut self) {
×
329
        match self.grid_array {
×
330
            GridOrEmpty::Grid(_) => {}
×
331
            GridOrEmpty::Empty(_) => {
×
332
                self.grid_array = self
×
333
                    .grid_array
×
334
                    .clone()
×
335
                    .into_materialized_masked_grid()
×
336
                    .into();
×
337
            }
×
338
        }
339
    }
×
340
}
341

342
impl<G> TemporalBounded for BaseTile<G> {
343
    fn temporal_bounds(&self) -> TimeInterval {
×
344
        self.time
×
345
    }
×
346
}
347

348
impl<G> SpatialPartitioned for BaseTile<G>
349
where
350
    G: GridSize,
351
{
352
    fn spatial_partition(&self) -> SpatialPartition2D {
220✔
353
        self.tile_information().spatial_partition()
220✔
354
    }
220✔
355
}
356

357
impl<D, T, G> Raster<D, T> for BaseTile<G>
358
where
359
    D: GridSize + GridBounds + Clone,
360
    T: Pixel,
361
    G: GridIndexAccess<D::IndexArray, T>,
362
    Self: SpatialBounded + GridShapeAccess<ShapeArray = D::ShapeArray>,
363
{
364
    type DataContainer = G;
365

366
    fn data_container(&self) -> &G {
×
367
        &self.grid_array
×
368
    }
×
369
}
370

371
impl<T, G, I> GridIndexAccess<Option<T>, I> for BaseTile<G>
372
where
373
    G: GridIndexAccess<Option<T>, I>,
374
    T: Pixel,
375
{
376
    fn get_at_grid_index(&self, grid_index: I) -> Result<Option<T>> {
150✔
377
        self.grid_array.get_at_grid_index(grid_index)
150✔
378
    }
150✔
379

380
    fn get_at_grid_index_unchecked(&self, grid_index: I) -> Option<T> {
25,370,538✔
381
        self.grid_array.get_at_grid_index_unchecked(grid_index)
25,370,538✔
382
    }
25,370,538✔
383
}
384

385
impl<T, G, I> GridIndexAccessMut<Option<T>, I> for BaseTile<G>
386
where
387
    G: GridIndexAccessMut<Option<T>, I>,
388
    T: Pixel,
389
{
390
    fn set_at_grid_index(&mut self, grid_index: I, value: Option<T>) -> Result<()> {
×
391
        self.grid_array.set_at_grid_index(grid_index, value)
×
392
    }
×
393

394
    fn set_at_grid_index_unchecked(&mut self, grid_index: I, value: Option<T>) {
×
395
        self.grid_array
×
396
            .set_at_grid_index_unchecked(grid_index, value);
×
397
    }
×
398
}
399

400
impl<G, A> GridShapeAccess for BaseTile<G>
401
where
402
    G: GridShapeAccess<ShapeArray = A>,
403
    A: AsRef<[usize]> + Into<GridShape<A>>,
404
{
405
    type ShapeArray = A;
406

407
    fn grid_shape_array(&self) -> Self::ShapeArray {
2,233✔
408
        self.grid_array.grid_shape_array()
2,233✔
409
    }
2,233✔
410
}
411

412
impl<G> GeoTransformAccess for BaseTile<G> {
413
    fn geo_transform(&self) -> GeoTransform {
1,386✔
414
        self.global_geo_transform
1,386✔
415
    }
1,386✔
416
}
417

418
impl<D, T> From<MaterializedRasterTile<D, T>> for RasterTile<D, T>
419
where
420
    T: Clone,
421
{
422
    fn from(mat_tile: MaterializedRasterTile<D, T>) -> Self {
188✔
423
        RasterTile {
188✔
424
            grid_array: mat_tile.grid_array.into(),
188✔
425
            global_geo_transform: mat_tile.global_geo_transform,
188✔
426
            tile_position: mat_tile.tile_position,
188✔
427
            band: mat_tile.band,
188✔
428
            time: mat_tile.time,
188✔
429
            properties: mat_tile.properties,
188✔
430
            cache_hint: mat_tile.cache_hint,
188✔
431
        }
188✔
432
    }
188✔
433
}
434

435
/// Pretty printer for raster tiles with 2D ASCII grids
436
pub fn display_raster_tile_2d<P: Pixel + std::fmt::Debug>(
×
437
    raster_tile_2d: &RasterTile2D<P>,
×
438
) -> impl std::fmt::Debug + '_ {
×
439
    struct DebugTile<'a, P>(&'a RasterTile2D<P>);
×
440

×
441
    impl<P: Pixel> std::fmt::Debug for DebugTile<'_, P> {
×
442
        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
×
443
            let tile = self.0;
×
444
            let mut fmt = f.debug_struct(stringify!(RasterTile2D));
×
445
            fmt.field("time", &tile.time);
×
446
            fmt.field("tile_position", &tile.tile_position);
×
447
            fmt.field("global_geo_transform", &tile.global_geo_transform);
×
448
            fmt.field("properties", &tile.properties);
×
449

×
450
            let grid = if let Some(grid) = tile.grid_array.as_masked_grid() {
×
451
                let values: Vec<String> = grid
×
452
                    .masked_element_ref_iterator()
×
453
                    .map(|v| v.map_or('_'.to_string(), |v| format!("{v:?}")))
×
454
                    .collect();
×
455
                let max_digits = values.iter().map(String::len).max().unwrap_or(0);
×
456

×
457
                let mut s = vec![String::new()];
×
458

×
459
                let last_value_index = values.len() - 1;
×
460
                for (i, value) in values.into_iter().enumerate() {
×
461
                    let str_ref = s
×
462
                        .last_mut()
×
463
                        .expect("it shouldn't be empty since it was populated before the loop");
×
464

×
465
                    str_ref.push_str(&format!("{value:max_digits$}"));
×
466

×
467
                    let is_new_line = (i + 1) % grid.grid_shape().axis_size_x() == 0;
×
468
                    if is_new_line && i < last_value_index {
×
469
                        s.push(String::new());
×
470
                    } else {
×
471
                        str_ref.push(' ');
×
472
                    }
×
473
                }
×
474

×
475
                s
×
476
            } else {
×
477
                vec!["empty".to_string()]
×
478
            };
×
479

×
480
            fmt.field("grid", &grid);
×
481

×
482
            fmt.finish()
×
483
        }
×
484
    }
×
485

×
486
    DebugTile(raster_tile_2d)
×
487
}
×
488

489
#[cfg(test)]
490
mod tests {
491
    use crate::{primitives::Coordinate2D, util::test::TestDefault};
492

493
    use super::*;
494
    use crate::raster::GridIdx;
495

496
    #[test]
497
    fn tile_information_new() {
1✔
498
        let ti = TileInformation::new(
1✔
499
            GridIdx([0, 0]),
1✔
500
            GridShape2D::from([100, 100]),
1✔
501
            GeoTransform::test_default(),
1✔
502
        );
1✔
503
        assert_eq!(ti.global_geo_transform, GeoTransform::test_default());
1✔
504
        assert_eq!(ti.global_tile_position, GridIdx([0, 0]));
1✔
505
        assert_eq!(ti.tile_size_in_pixels, GridShape2D::from([100, 100]));
1✔
506
    }
1✔
507

508
    #[test]
509
    fn tile_information_global_tile_position() {
1✔
510
        let ti = TileInformation::new(
1✔
511
            GridIdx([0, 0]),
1✔
512
            GridShape2D::from([100, 100]),
1✔
513
            GeoTransform::test_default(),
1✔
514
        );
1✔
515
        assert_eq!(ti.global_tile_position(), GridIdx([0, 0]));
1✔
516
    }
1✔
517

518
    #[test]
519
    fn tile_information_local_upper_left() {
1✔
520
        let ti = TileInformation::new(
1✔
521
            GridIdx([0, 0]),
1✔
522
            GridShape2D::from([100, 100]),
1✔
523
            GeoTransform::test_default(),
1✔
524
        );
1✔
525
        assert_eq!(ti.local_upper_left_pixel_idx(), GridIdx([0, 0]));
1✔
526
    }
1✔
527

528
    #[test]
529
    fn tile_information_local_lower_left() {
1✔
530
        let ti = TileInformation::new(
1✔
531
            GridIdx([0, 0]),
1✔
532
            GridShape2D::from([100, 100]),
1✔
533
            GeoTransform::test_default(),
1✔
534
        );
1✔
535
        assert_eq!(ti.local_lower_left_pixel_idx(), GridIdx([99, 0]));
1✔
536
    }
1✔
537

538
    #[test]
539
    fn tile_information_local_upper_right() {
1✔
540
        let ti = TileInformation::new(
1✔
541
            GridIdx([0, 0]),
1✔
542
            GridShape2D::from([100, 100]),
1✔
543
            GeoTransform::test_default(),
1✔
544
        );
1✔
545
        assert_eq!(ti.local_upper_right_pixel_idx(), GridIdx([0, 99]));
1✔
546
    }
1✔
547

548
    #[test]
549
    fn tile_information_local_lower_right() {
1✔
550
        let ti = TileInformation::new(
1✔
551
            GridIdx([0, 0]),
1✔
552
            GridShape2D::from([100, 100]),
1✔
553
            GeoTransform::test_default(),
1✔
554
        );
1✔
555
        assert_eq!(ti.local_lower_right_pixel_idx(), GridIdx([99, 99]));
1✔
556
    }
1✔
557

558
    #[test]
559
    fn tile_information_global_upper_left_idx() {
1✔
560
        let ti = TileInformation::new(
1✔
561
            GridIdx([0, 0]),
1✔
562
            GridShape2D::from([100, 100]),
1✔
563
            GeoTransform::test_default(),
1✔
564
        );
1✔
565
        assert_eq!(ti.global_upper_left_pixel_idx(), GridIdx([0, 0]));
1✔
566
    }
1✔
567

568
    #[test]
569
    fn tile_information_global_upper_left_idx_2_3() {
1✔
570
        let ti = TileInformation::new(
1✔
571
            GridIdx([-2, 3]),
1✔
572
            GridShape2D::from([100, 1000]),
1✔
573
            GeoTransform::test_default(),
1✔
574
        );
1✔
575
        assert_eq!(ti.global_upper_left_pixel_idx(), GridIdx([-200, 3000]));
1✔
576
    }
1✔
577

578
    #[test]
579
    fn tile_information_global_upper_right_idx() {
1✔
580
        let ti = TileInformation::new(
1✔
581
            GridIdx([0, 0]),
1✔
582
            GridShape2D::from([100, 100]),
1✔
583
            GeoTransform::test_default(),
1✔
584
        );
1✔
585
        assert_eq!(ti.global_upper_right_pixel_idx(), GridIdx([0, 99]));
1✔
586
    }
1✔
587

588
    #[test]
589
    fn tile_information_global_upper_right_idx_2_3() {
1✔
590
        let ti = TileInformation::new(
1✔
591
            GridIdx([-2, 3]),
1✔
592
            GridShape2D::from([100, 1000]),
1✔
593
            GeoTransform::test_default(),
1✔
594
        );
1✔
595
        assert_eq!(ti.global_upper_right_pixel_idx(), GridIdx([-200, 3999]));
1✔
596
    }
1✔
597

598
    #[test]
599
    fn tile_information_global_lower_right_idx() {
1✔
600
        let ti = TileInformation::new(
1✔
601
            GridIdx([0, 0]),
1✔
602
            GridShape2D::from([100, 100]),
1✔
603
            GeoTransform::test_default(),
1✔
604
        );
1✔
605
        assert_eq!(ti.global_lower_right_pixel_idx(), GridIdx([99, 99]));
1✔
606
    }
1✔
607

608
    #[test]
609
    fn tile_information_global_lower_right_idx_2_3() {
1✔
610
        let ti = TileInformation::new(
1✔
611
            GridIdx([-2, 3]),
1✔
612
            GridShape2D::from([100, 1000]),
1✔
613
            GeoTransform::test_default(),
1✔
614
        );
1✔
615
        assert_eq!(ti.global_lower_right_pixel_idx(), GridIdx([-101, 3999]));
1✔
616
    }
1✔
617

618
    #[test]
619
    fn tile_information_global_lower_left_idx() {
1✔
620
        let ti = TileInformation::new(
1✔
621
            GridIdx([0, 0]),
1✔
622
            GridShape2D::from([100, 100]),
1✔
623
            GeoTransform::test_default(),
1✔
624
        );
1✔
625
        assert_eq!(ti.global_lower_left_pixel_idx(), GridIdx([99, 0]));
1✔
626
    }
1✔
627

628
    #[test]
629
    fn tile_information_global_lower_left_idx_2_3() {
1✔
630
        let ti = TileInformation::new(
1✔
631
            GridIdx([-2, 3]),
1✔
632
            GridShape2D::from([100, 1000]),
1✔
633
            GeoTransform::test_default(),
1✔
634
        );
1✔
635
        assert_eq!(ti.global_lower_left_pixel_idx(), GridIdx([-101, 3000]));
1✔
636
    }
1✔
637

638
    #[test]
639
    fn tile_information_local_to_global_idx_0_0() {
1✔
640
        let ti = TileInformation::new(
1✔
641
            GridIdx([0, 0]),
1✔
642
            GridShape2D::from([100, 100]),
1✔
643
            GeoTransform::test_default(),
1✔
644
        );
1✔
645
        assert_eq!(
1✔
646
            ti.local_to_global_pixel_idx(GridIdx([25, 75])),
1✔
647
            GridIdx([25, 75])
1✔
648
        );
1✔
649
    }
1✔
650

651
    #[test]
652
    fn tile_information_local_to_global_idx_2_3() {
1✔
653
        let ti = TileInformation::new(
1✔
654
            GridIdx([-2, 3]),
1✔
655
            GridShape2D::from([100, 1000]),
1✔
656
            GeoTransform::test_default(),
1✔
657
        );
1✔
658
        assert_eq!(
1✔
659
            ti.local_to_global_pixel_idx(GridIdx([25, 75])),
1✔
660
            GridIdx([-175, 3075])
1✔
661
        );
1✔
662
    }
1✔
663

664
    #[test]
665
    fn tile_information_spatial_partition() {
1✔
666
        let ti = TileInformation::new(
1✔
667
            GridIdx([-2, 3]),
1✔
668
            GridShape2D::from([100, 1000]),
1✔
669
            GeoTransform::test_default(),
1✔
670
        );
1✔
671
        assert_eq!(
1✔
672
            ti.spatial_partition(),
1✔
673
            SpatialPartition2D::new_unchecked(
1✔
674
                Coordinate2D::new(3000., 200.),
1✔
675
                Coordinate2D::new(4000., 100.)
1✔
676
            )
1✔
677
        );
1✔
678
    }
1✔
679

680
    #[test]
681
    fn tile_information_spatial_bounds_geotransform() {
1✔
682
        let ti = TileInformation::new(
1✔
683
            GridIdx([2, 3]),
1✔
684
            GridShape2D::from([10, 10]),
1✔
685
            GeoTransform::new_with_coordinate_x_y(-180., 0.1, 90., -0.1),
1✔
686
        );
1✔
687
        assert_eq!(
1✔
688
            ti.spatial_partition(),
1✔
689
            SpatialPartition2D::new_unchecked(
1✔
690
                Coordinate2D::new(-177., 88.),
1✔
691
                Coordinate2D::new(-176., 87.)
1✔
692
            )
1✔
693
        );
1✔
694
    }
1✔
695
}
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