4
4
from beartype .typing import List , Literal , Sequence , Tuple
5
5
from rasterio import warp
6
6
from rasterio .enums import Resampling
7
+ from rasterio .profiles import Profile
7
8
8
9
from eis_toolkit .exceptions import InvalidParameterValueException
9
10
from eis_toolkit .raster_processing .resampling import RESAMPLE_METHOD_MAP
10
11
11
12
13
+ def _calculate_snapped_grid (
14
+ raster : rasterio .io .DatasetReader , dst_crs : rasterio .crs .CRS , dst_resolution : Tuple [float , float ]
15
+ ) -> Tuple [warp .Affine , int , int ]:
16
+ dst_transform , dst_width , dst_height = warp .calculate_default_transform (
17
+ raster .crs , dst_crs , raster .width , raster .height , * raster .bounds , resolution = dst_resolution
18
+ )
19
+ # The created transform might not be aligned with the base raster grid, so
20
+ # we still need to snap/align the transformation to closest grid corner
21
+ x_distance_to_grid = dst_transform .c % dst_resolution [0 ]
22
+ y_distance_to_grid = dst_transform .f % dst_resolution [1 ]
23
+
24
+ if x_distance_to_grid > dst_resolution [0 ] / 2 : # Snap towards right
25
+ c = dst_transform .c - x_distance_to_grid + dst_resolution [0 ]
26
+ else : # Snap towards left
27
+ c = dst_transform .c - x_distance_to_grid
28
+
29
+ if y_distance_to_grid > dst_resolution [1 ] / 2 : # Snap towards up
30
+ f = dst_transform .f - y_distance_to_grid + dst_resolution [1 ]
31
+ else : # Snap towards bottom
32
+ f = dst_transform .f - y_distance_to_grid
33
+
34
+ # Create new transform with updated corner coordinates
35
+ dst_transform = warp .Affine (
36
+ dst_transform .a , # Pixel size x
37
+ dst_transform .b , # Shear parameter
38
+ c , # Up-left corner x-coordinate
39
+ dst_transform .d , # Shear parameter
40
+ dst_transform .e , # Pixel size y
41
+ f , # Up-left corner y-coordinate
42
+ )
43
+ return dst_transform , dst_width , dst_height
44
+
45
+
12
46
def _unify_raster_grids (
13
47
base_raster : rasterio .io .DatasetReader ,
14
48
rasters_to_unify : Sequence [rasterio .io .DatasetReader ],
15
49
resampling_method : Resampling ,
16
50
same_extent : bool ,
17
- ) -> List [Tuple [np .ndarray , dict ]]:
51
+ ) -> List [Tuple [np .ndarray , Profile ]]:
18
52
19
53
dst_crs = base_raster .crs
20
54
dst_width = base_raster .width
21
55
dst_height = base_raster .height
22
56
dst_transform = base_raster .transform
23
57
dst_resolution = (base_raster .transform .a , abs (base_raster .transform .e ))
24
58
25
- out_rasters = [(base_raster .read (), base_raster .meta .copy ())]
26
- out_meta = base_raster .meta .copy ()
59
+ out_rasters = [(base_raster .read (), base_raster .profile .copy ())]
27
60
28
61
for raster in rasters_to_unify :
62
+ out_profile = raster .profile .copy ()
29
63
30
64
# If we unify without clipping, things are more complicated and we need to
31
65
# calculate corner coordinates, width and height, and snap the grid to nearest corner
32
66
if not same_extent :
33
- dst_transform , dst_width , dst_height = warp .calculate_default_transform (
34
- raster .crs , dst_crs , raster .width , raster .height , * raster .bounds , resolution = dst_resolution
35
- )
36
- # The created transform might not be aligned with the base raster grid, so
37
- # we still need to snap/align the transformation to closest grid corner
38
- x_distance_to_grid = dst_transform .c % dst_resolution [0 ]
39
- y_distance_to_grid = dst_transform .f % dst_resolution [1 ]
40
-
41
- if x_distance_to_grid > dst_resolution [0 ] / 2 : # Snap towards right
42
- c = dst_transform .c - x_distance_to_grid + dst_resolution [0 ]
43
- else : # Snap towards left
44
- c = dst_transform .c - x_distance_to_grid
45
-
46
- if y_distance_to_grid > dst_resolution [1 ] / 2 : # Snap towards up
47
- f = dst_transform .f - y_distance_to_grid + dst_resolution [1 ]
48
- else : # Snap towards bottom
49
- f = dst_transform .f - y_distance_to_grid
50
-
51
- # Create new transform with updated corner coordinates
52
- dst_transform = warp .Affine (
53
- dst_transform .a , # Pixel size x
54
- dst_transform .b , # Shear parameter
55
- c , # Up-left corner x-coordinate
56
- dst_transform .d , # Shear parameter
57
- dst_transform .e , # Pixel size y
58
- f , # Up-left corner y-coordinate
59
- )
60
-
61
- out_meta ["transform" ] = dst_transform
62
- out_meta ["width" ] = dst_width
63
- out_meta ["height" ] = dst_height
67
+ dst_transform , dst_width , dst_height = _calculate_snapped_grid (raster , dst_crs , dst_resolution )
64
68
65
69
# Initialize output raster arrary
66
70
dst_array = np .empty ((base_raster .count , dst_height , dst_width ))
67
- dst_array .fill (base_raster .meta ["nodata" ])
71
+ nodata = out_profile ["nodata" ]
72
+ dst_array .fill (nodata )
68
73
69
74
src_array = raster .read ()
70
75
71
76
out_image = warp .reproject (
72
77
source = src_array ,
73
78
src_crs = raster .crs ,
74
79
src_transform = raster .transform ,
75
- src_nodata = raster . meta [ " nodata" ] ,
80
+ src_nodata = nodata ,
76
81
destination = dst_array ,
77
82
dst_crs = dst_crs ,
78
83
dst_transform = dst_transform ,
79
- dst_nodata = base_raster . meta [ " nodata" ] ,
84
+ dst_nodata = nodata ,
80
85
resampling = resampling_method ,
81
86
)[0 ]
82
87
83
- out_rasters .append ((out_image , out_meta ))
88
+ out_profile .update ({"transform" : dst_transform , "width" : dst_width , "height" : dst_height , "crs" : dst_crs })
89
+
90
+ out_rasters .append ((out_image , out_profile ))
84
91
85
92
return out_rasters
86
93
@@ -91,7 +98,7 @@ def unify_raster_grids(
91
98
rasters_to_unify : Sequence [rasterio .io .DatasetReader ],
92
99
resampling_method : Literal ["nearest" , "bilinear" , "cubic" , "average" , "gauss" , "max" , "min" ] = "nearest" ,
93
100
same_extent : bool = False ,
94
- ) -> List [Tuple [np .ndarray , dict ]]:
101
+ ) -> List [Tuple [np .ndarray , Profile ]]:
95
102
"""Unifies (reprojects, resamples, aligns and optionally clips) given rasters relative to base raster.
96
103
97
104
Args:
@@ -104,7 +111,7 @@ def unify_raster_grids(
104
111
as the base raster. Expands smaller rasters with nodata cells. Defaults to False.
105
112
106
113
Returns:
107
- List of unified rasters' data and metadata . First element is the base raster.
114
+ List of unified rasters' data and profiles . First element is the base raster.
108
115
109
116
Raises:
110
117
InvalidParameterValueException: Rasters to unify is empty.
0 commit comments