Skip to content

Commit 88c4ca3

Browse files
Merge pull request #334 from geo-engine/raster_reprojection_outside
produce empty tiles when querying outside of area of use of projection
2 parents 13aacd4 + b949edd commit 88c4ca3

File tree

32 files changed

+729
-287
lines changed

32 files changed

+729
-287
lines changed

datatypes/src/raster/geo_transform.rs

Lines changed: 71 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
use std::cmp::max;
22

3-
use crate::primitives::{
4-
AxisAlignedRectangle, Coordinate2D, SpatialPartition2D, SpatialResolution,
3+
use crate::{
4+
primitives::{AxisAlignedRectangle, Coordinate2D, SpatialPartition2D, SpatialResolution},
5+
util::test::TestDefault,
56
};
67
use serde::{de, Deserialize, Deserializer, Serialize};
78

@@ -10,13 +11,16 @@ use super::{GridBoundingBox2D, GridIdx, GridIdx2D};
1011
/// This is a typedef for the `GDAL GeoTransform`. It represents an affine transformation matrix.
1112
pub type GdalGeoTransform = [f64; 6];
1213

13-
/// The `GeoTransform` is a more user friendly representation of the `GDAL GeoTransform` affine transformation matrix.
14+
/// The `GeoTransform` specifies the relation between pixel coordinates and geographic coordinates.
15+
/// In Geo Engine x pixel size is always postive and y pixel size is always negative. For raster tiles
16+
/// the origin is always the upper left corner. In the global grid for the `TilingStrategy` the origin
17+
/// is always located at (0, 0).
1418
#[derive(Copy, Clone, PartialEq, Debug, Serialize, Deserialize)]
1519
#[serde(rename_all = "camelCase")]
1620
pub struct GeoTransform {
1721
pub origin_coordinate: Coordinate2D,
18-
pub x_pixel_size: f64,
19-
pub y_pixel_size: f64,
22+
x_pixel_size: f64,
23+
y_pixel_size: f64,
2024
}
2125

2226
impl GeoTransform {
@@ -32,6 +36,9 @@ impl GeoTransform {
3236
///
3337
#[inline]
3438
pub fn new(origin_coordinate: Coordinate2D, x_pixel_size: f64, y_pixel_size: f64) -> Self {
39+
debug_assert!(x_pixel_size > 0.0);
40+
debug_assert!(y_pixel_size < 0.0);
41+
3542
Self {
3643
origin_coordinate,
3744
x_pixel_size,
@@ -55,13 +62,24 @@ impl GeoTransform {
5562
origin_coordinate_y: f64,
5663
y_pixel_size: f64,
5764
) -> Self {
65+
debug_assert!(x_pixel_size > 0.0);
66+
debug_assert!(y_pixel_size < 0.0);
67+
5868
Self {
5969
origin_coordinate: (origin_coordinate_x, origin_coordinate_y).into(),
6070
x_pixel_size,
6171
y_pixel_size,
6272
}
6373
}
6474

75+
pub fn x_pixel_size(&self) -> f64 {
76+
self.x_pixel_size
77+
}
78+
79+
pub fn y_pixel_size(&self) -> f64 {
80+
self.y_pixel_size
81+
}
82+
6583
/// Transforms a grid coordinate (row, column) ~ (y, x) into a SRS coordinate (x,y)
6684
/// The resulting coordinate is the upper left coordinate of the pixel
6785
/// See GDAL documentation for more details (including the two ignored parameters): <https://gdal.org/user/raster_data_model.html>
@@ -108,8 +126,10 @@ impl GeoTransform {
108126
///
109127
#[inline]
110128
pub fn coordinate_to_grid_idx_2d(&self, coord: Coordinate2D) -> GridIdx2D {
111-
let grid_x_index = ((coord.x - self.origin_coordinate.x) / self.x_pixel_size) as isize;
112-
let grid_y_index = ((coord.y - self.origin_coordinate.y) / self.y_pixel_size) as isize;
129+
let grid_x_index =
130+
((coord.x - self.origin_coordinate.x) / self.x_pixel_size).floor() as isize;
131+
let grid_y_index =
132+
((coord.y - self.origin_coordinate.y) / self.y_pixel_size).floor() as isize;
113133
[grid_y_index, grid_x_index].into()
114134
}
115135

@@ -171,8 +191,8 @@ impl GeoTransform {
171191
}
172192
}
173193

174-
impl Default for GeoTransform {
175-
fn default() -> Self {
194+
impl TestDefault for GeoTransform {
195+
fn test_default() -> Self {
176196
GeoTransform::new_with_coordinate_x_y(0.0, 1.0, 0.0, -1.0)
177197
}
178198
}
@@ -273,6 +293,48 @@ mod tests {
273293
);
274294
}
275295

296+
#[test]
297+
fn geo_transform_coordinate_2d_to_global_grid_2d() {
298+
let geo_transform = GeoTransform::new_with_coordinate_x_y(0.0, 1.0, 0.0, -1.0);
299+
assert_eq!(
300+
geo_transform.coordinate_to_grid_idx_2d((0.0, 0.0).into()),
301+
GridIdx2D::new([0, 0])
302+
);
303+
assert_eq!(
304+
geo_transform.coordinate_to_grid_idx_2d((0.5, 0.0).into()),
305+
GridIdx2D::new([0, 0])
306+
);
307+
assert_eq!(
308+
geo_transform.coordinate_to_grid_idx_2d((0.5, 0.5).into()),
309+
GridIdx2D::new([-1, 0])
310+
);
311+
assert_eq!(
312+
geo_transform.coordinate_to_grid_idx_2d((0.0, 0.5).into()),
313+
GridIdx2D::new([-1, 0])
314+
);
315+
assert_eq!(
316+
geo_transform.coordinate_to_grid_idx_2d((0.5, -0.5).into()),
317+
GridIdx2D::new([0, 0])
318+
);
319+
assert_eq!(
320+
geo_transform.coordinate_to_grid_idx_2d((-0.5, 0.5).into()),
321+
GridIdx2D::new([-1, -1])
322+
);
323+
assert_eq!(
324+
geo_transform.coordinate_to_grid_idx_2d((-0.5, -0.5).into()),
325+
GridIdx2D::new([0, -1])
326+
);
327+
328+
assert_eq!(
329+
geo_transform.coordinate_to_grid_idx_2d((1.5, -0.5).into()),
330+
GridIdx2D::new([0, 1])
331+
);
332+
assert_eq!(
333+
geo_transform.coordinate_to_grid_idx_2d((-1.5, 1.5).into()),
334+
GridIdx2D::new([-2, -2])
335+
);
336+
}
337+
276338
#[test]
277339
fn pixel_center() {
278340
let geo_transform = GeoTransform::new_with_coordinate_x_y(5.0, 1.0, 5.0, -1.0);

datatypes/src/raster/macros_raster_tile.rs

Lines changed: 48 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,7 @@ mod tests {
243243
use crate::{
244244
primitives::TimeInterval,
245245
raster::{GeoTransform, Grid2D, GridIndexAccess, Pixel, RasterTile2D, TypedRasterTile2D},
246+
util::test::TestDefault,
246247
};
247248
use crate::{raster::RasterDataType, util::test::catch_unwind_silent};
248249
use std::marker::PhantomData;
@@ -254,8 +255,11 @@ mod tests {
254255
}
255256

256257
let r = Grid2D::new([3, 2].into(), vec![1, 2, 3, 4, 5, 6], None).unwrap();
257-
let t =
258-
RasterTile2D::new_without_offset(TimeInterval::default(), GeoTransform::default(), r);
258+
let t = RasterTile2D::new_without_offset(
259+
TimeInterval::default(),
260+
GeoTransform::test_default(),
261+
r,
262+
);
259263
let typed_raster = TypedRasterTile2D::U32(t);
260264

261265
assert_eq!(
@@ -271,8 +275,11 @@ mod tests {
271275
}
272276

273277
let r = Grid2D::new([3, 2].into(), vec![1, 2, 3, 4, 5, 6], None).unwrap();
274-
let t =
275-
RasterTile2D::new_without_offset(TimeInterval::default(), GeoTransform::default(), r);
278+
let t = RasterTile2D::new_without_offset(
279+
TimeInterval::default(),
280+
GeoTransform::test_default(),
281+
r,
282+
);
276283
let typed_raster = TypedRasterTile2D::U32(t);
277284

278285
assert_eq!(
@@ -299,14 +306,18 @@ mod tests {
299306
];
300307

301308
let r = Grid2D::new([3, 2].into(), data, None).unwrap();
302-
RasterTile2D::new_without_offset(TimeInterval::default(), GeoTransform::default(), r)
309+
RasterTile2D::new_without_offset(
310+
TimeInterval::default(),
311+
GeoTransform::test_default(),
312+
r,
313+
)
303314
}
304315

305316
assert_eq!(
306317
generate_generic_raster_tile_2d!(RasterDataType::U8, generate()),
307318
TypedRasterTile2D::U8(RasterTile2D::new_without_offset(
308319
TimeInterval::default(),
309-
GeoTransform::default(),
320+
GeoTransform::test_default(),
310321
Grid2D::new([3, 2].into(), vec![1, 2, 3, 4, 5, 6], None,).unwrap()
311322
),)
312323
);
@@ -321,13 +332,19 @@ mod tests {
321332
}
322333

323334
let r = Grid2D::new([3, 2].into(), vec![1, 2, 3, 4, 5, 6], None).unwrap();
324-
let t =
325-
RasterTile2D::new_without_offset(TimeInterval::default(), GeoTransform::default(), r);
335+
let t = RasterTile2D::new_without_offset(
336+
TimeInterval::default(),
337+
GeoTransform::test_default(),
338+
r,
339+
);
326340
let typed_raster_a = TypedRasterTile2D::U32(t);
327341

328342
let r = Grid2D::new([3, 2].into(), vec![1, 2, 3, 4, 5, 6], None).unwrap();
329-
let t =
330-
RasterTile2D::new_without_offset(TimeInterval::default(), GeoTransform::default(), r);
343+
let t = RasterTile2D::new_without_offset(
344+
TimeInterval::default(),
345+
GeoTransform::test_default(),
346+
r,
347+
);
331348
let typed_raster_b = TypedRasterTile2D::U16(t);
332349

333350
assert_eq!(
@@ -348,13 +365,19 @@ mod tests {
348365
}
349366

350367
let r = Grid2D::new([3, 2].into(), vec![1, 2, 3, 4, 5, 6], None).unwrap();
351-
let t =
352-
RasterTile2D::new_without_offset(TimeInterval::default(), GeoTransform::default(), r);
368+
let t = RasterTile2D::new_without_offset(
369+
TimeInterval::default(),
370+
GeoTransform::test_default(),
371+
r,
372+
);
353373
let typed_raster_a = TypedRasterTile2D::U32(t);
354374

355375
let r = Grid2D::new([3, 2].into(), vec![1, 2, 3, 4, 5, 6], None).unwrap();
356-
let t =
357-
RasterTile2D::new_without_offset(TimeInterval::default(), GeoTransform::default(), r);
376+
let t = RasterTile2D::new_without_offset(
377+
TimeInterval::default(),
378+
GeoTransform::test_default(),
379+
r,
380+
);
358381
let typed_raster_b = TypedRasterTile2D::U16(t);
359382

360383
assert_eq!(
@@ -391,13 +414,19 @@ mod tests {
391414
}
392415

393416
let r = Grid2D::new([3, 2].into(), vec![1, 2, 3, 4, 5, 6], None).unwrap();
394-
let t =
395-
RasterTile2D::new_without_offset(TimeInterval::default(), GeoTransform::default(), r);
417+
let t = RasterTile2D::new_without_offset(
418+
TimeInterval::default(),
419+
GeoTransform::test_default(),
420+
r,
421+
);
396422
let typed_raster_a = TypedRasterTile2D::U32(t);
397423

398424
let r = Grid2D::new([3, 2].into(), vec![1, 2, 3, 4, 5, 6], None).unwrap();
399-
let t =
400-
RasterTile2D::new_without_offset(TimeInterval::default(), GeoTransform::default(), r);
425+
let t = RasterTile2D::new_without_offset(
426+
TimeInterval::default(),
427+
GeoTransform::test_default(),
428+
r,
429+
);
401430
let typed_raster_b = TypedRasterTile2D::U16(t);
402431

403432
assert_eq!(
@@ -452,7 +481,7 @@ mod tests {
452481
let typed_raster_a = TypedRasterTile2D::U32(RasterTile2D::new(
453482
TimeInterval::default(),
454483
[0, 0].into(),
455-
[1.0, 1.0, 0.0, 1.0, 0.0, 1.0].into(),
484+
[1.0, 1.0, 0.0, 1.0, 0.0, -1.0].into(),
456485
Grid2D::new([3, 2].into(), vec![1, 2, 3, 4, 5, 6], None)
457486
.unwrap()
458487
.into(),

datatypes/src/raster/operations/blit.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ impl<T: Pixel> Blit<RasterTile2D<T>> for MaterializedRasterTile2D<T> {
1919
// TODO: ensure pixels are aligned
2020

2121
ensure!(
22-
(self.geo_transform().x_pixel_size == source.geo_transform().x_pixel_size)
23-
&& (self.geo_transform().y_pixel_size == source.geo_transform().y_pixel_size),
22+
(self.geo_transform().x_pixel_size() == source.geo_transform().x_pixel_size())
23+
&& (self.geo_transform().y_pixel_size() == source.geo_transform().y_pixel_size()),
2424
error::Blit {
2525
details: "Incompatible pixel size"
2626
}

0 commit comments

Comments
 (0)