0
# Transformations
1
2
Affine transformations for converting between pixel and geographic coordinates. Supports creating transforms from bounds, origins, ground control points, and provides coordinate conversion utilities.
3
4
## Capabilities
5
6
### Affine Transformation Matrix
7
8
The core transformation class from the `affine` library, integrated into rasterio for geospatial coordinate transformations.
9
10
```python { .api }
11
class Affine:
12
"""6-parameter affine transformation matrix."""
13
14
def __init__(self, a, b, c, d, e, f):
15
"""
16
Initialize affine transformation.
17
18
Parameters (coefficients for the transformation):
19
- a (float): x-scale factor
20
- b (float): x-shear factor
21
- c (float): x-translation
22
- d (float): y-shear factor
23
- e (float): y-scale factor
24
- f (float): y-translation
25
26
Transformation: x' = a*x + b*y + c, y' = d*x + e*y + f
27
"""
28
29
# Properties
30
a: float # x-scale
31
b: float # x-shear
32
c: float # x-translation
33
d: float # y-shear
34
e: float # y-scale
35
f: float # y-translation
36
37
@classmethod
38
def identity(cls):
39
"""Create identity transformation."""
40
41
@classmethod
42
def translation(cls, x, y):
43
"""Create translation transformation."""
44
45
@classmethod
46
def scale(cls, x, y=None):
47
"""Create scaling transformation."""
48
49
@classmethod
50
def rotation(cls, angle):
51
"""Create rotation transformation."""
52
53
def __mul__(self, other):
54
"""Compose with another transformation."""
55
56
def __invert__(self):
57
"""Get inverse transformation."""
58
59
def determinant(self):
60
"""Calculate transformation determinant."""
61
62
def is_identity(self):
63
"""Test if identity transformation."""
64
65
def is_conformal(self):
66
"""Test if conformal (preserves angles)."""
67
68
def is_orthonormal(self):
69
"""Test if orthonormal (preserves distances and angles)."""
70
71
def is_rectilinear(self):
72
"""Test if rectilinear (axes aligned)."""
73
```
74
75
### Transform Creation Functions
76
77
Utilities for creating affine transformations from various geographic parameters.
78
79
```python { .api }
80
def from_bounds(west, south, east, north, width, height):
81
"""
82
Create transform from geographic bounds and raster dimensions.
83
84
Parameters:
85
- west (float): Left (minimum x) coordinate
86
- south (float): Bottom (minimum y) coordinate
87
- east (float): Right (maximum x) coordinate
88
- north (float): Top (maximum y) coordinate
89
- width (int): Raster width in pixels
90
- height (int): Raster height in pixels
91
92
Returns:
93
Affine: Transformation matrix
94
"""
95
96
def from_origin(west, north, xsize, ysize):
97
"""
98
Create transform from origin point and pixel sizes.
99
100
Parameters:
101
- west (float): Left (minimum x) coordinate
102
- north (float): Top (maximum y) coordinate
103
- xsize (float): Pixel width (x-direction)
104
- ysize (float): Pixel height (y-direction, usually negative)
105
106
Returns:
107
Affine: Transformation matrix
108
"""
109
110
def from_gcps(gcps):
111
"""
112
Create transform from ground control points.
113
114
Parameters:
115
- gcps (sequence): Ground control points as (row, col, x, y) tuples
116
117
Returns:
118
Affine: Transformation matrix
119
"""
120
```
121
122
Usage examples:
123
124
```python
125
from rasterio.transform import from_bounds, from_origin, Affine
126
127
# Create transform from bounds
128
transform = from_bounds(-180, -90, 180, 90, 360, 180)
129
print(transform) # Affine(1.0, 0.0, -180.0, 0.0, -1.0, 90.0)
130
131
# Create transform from origin and pixel size
132
transform = from_origin(-180, 90, 1.0, -1.0) # 1-degree pixels
133
134
# Create transform from ground control points
135
gcps = [
136
(0, 0, -180, 90), # top-left
137
(0, 360, 180, 90), # top-right
138
(180, 0, -180, -90), # bottom-left
139
(180, 360, 180, -90) # bottom-right
140
]
141
transform = from_gcps(gcps)
142
143
# Manual transform creation
144
transform = Affine(1.0, 0.0, -180.0, # x-scale, x-shear, x-offset
145
0.0, -1.0, 90.0) # y-shear, y-scale, y-offset
146
```
147
148
### Coordinate Conversion
149
150
Functions for converting between pixel and geographic coordinate systems.
151
152
```python { .api }
153
def xy(transform, rows, cols, offset='center'):
154
"""
155
Convert pixel coordinates to geographic coordinates.
156
157
Parameters:
158
- transform (Affine): Geospatial transformation
159
- rows (array-like): Row coordinates (y-axis, 0-based)
160
- cols (array-like): Column coordinates (x-axis, 0-based)
161
- offset (str): Pixel offset ('center', 'ul', 'ur', 'll', 'lr')
162
163
Returns:
164
tuple: (x_coords, y_coords) in geographic space
165
"""
166
167
def rowcol(transform, xs, ys, op=math.floor, precision=None):
168
"""
169
Convert geographic coordinates to pixel coordinates.
170
171
Parameters:
172
- transform (Affine): Geospatial transformation
173
- xs (array-like): X coordinates in geographic space
174
- ys (array-like): Y coordinates in geographic space
175
- op (callable): Rounding operation (math.floor, math.ceil, round)
176
- precision (int): Decimal places for rounding
177
178
Returns:
179
tuple: (rows, cols) in pixel space
180
"""
181
```
182
183
Usage examples:
184
185
```python
186
import rasterio
187
from rasterio.transform import xy, rowcol
188
import numpy as np
189
190
# Load dataset with transform
191
with rasterio.open('example.tif') as dataset:
192
transform = dataset.transform
193
194
# Convert pixel coordinates to geographic
195
rows, cols = np.array([100, 200]), np.array([150, 250])
196
x_coords, y_coords = xy(transform, rows, cols)
197
print(f"Geographic coords: {list(zip(x_coords, y_coords))}")
198
199
# Convert geographic coordinates to pixels
200
x_geo, y_geo = [-120.5, -119.8], [45.2, 44.9]
201
pixel_rows, pixel_cols = rowcol(transform, x_geo, y_geo)
202
print(f"Pixel coords: {list(zip(pixel_rows, pixel_cols))}")
203
204
# Different pixel offset options
205
x_center, y_center = xy(transform, [100], [150], offset='center')
206
x_corner, y_corner = xy(transform, [100], [150], offset='ul') # upper-left
207
```
208
209
### Bounds and Array Operations
210
211
Utilities for calculating bounds and working with transformed arrays.
212
213
```python { .api }
214
def array_bounds(height, width, transform):
215
"""
216
Calculate geographic bounds of raster array.
217
218
Parameters:
219
- height (int): Array height in pixels
220
- width (int): Array width in pixels
221
- transform (Affine): Geospatial transformation
222
223
Returns:
224
BoundingBox: Geographic bounds (west, south, east, north)
225
"""
226
227
def guard_transform(transform):
228
"""
229
Validate transformation matrix.
230
231
Parameters:
232
- transform (Affine): Transformation to validate
233
234
Returns:
235
Affine: Validated transformation
236
237
Raises:
238
TransformError: If transform is invalid
239
"""
240
```
241
242
Usage examples:
243
244
```python
245
from rasterio.transform import array_bounds, guard_transform
246
247
# Calculate bounds from array dimensions
248
height, width = 1000, 2000
249
transform = from_bounds(-180, -90, 180, 90, width, height)
250
bounds = array_bounds(height, width, transform)
251
print(f"Bounds: {bounds}") # BoundingBox(left=-180, bottom=-90, right=180, top=90)
252
253
# Validate transformation
254
try:
255
valid_transform = guard_transform(transform)
256
except TransformError as e:
257
print(f"Invalid transform: {e}")
258
```
259
260
### Specialized Transformations
261
262
Advanced transformation operations for specific use cases.
263
264
```python { .api }
265
def tasseledcap(array, coeffs):
266
"""
267
Apply Tasseled Cap transformation to multispectral data.
268
269
Parameters:
270
- array (numpy.ndarray): Input multispectral array
271
- coeffs (numpy.ndarray): Transformation coefficients
272
273
Returns:
274
numpy.ndarray: Transformed array
275
"""
276
```
277
278
Usage example:
279
280
```python
281
# Landsat Tasseled Cap transformation
282
landsat_coeffs = np.array([
283
[0.3037, 0.2793, 0.4743, 0.5585, 0.5082, 0.1863], # Brightness
284
[-0.2848, -0.2435, -0.5436, 0.7243, 0.0840, -0.1800], # Greenness
285
[0.1509, 0.1973, 0.3279, 0.3406, -0.7112, -0.4572] # Wetness
286
])
287
288
# Apply transformation to 6-band Landsat data
289
transformed = tasseledcap(landsat_bands, landsat_coeffs)
290
```
291
292
### Transform Error Handling
293
294
Transformation operations can raise specific exceptions for invalid matrices or operations:
295
296
```python { .api }
297
class TransformError(RasterioError):
298
"""Transformation related errors."""
299
```
300
301
Common error scenarios:
302
303
```python
304
from rasterio.errors import TransformError
305
306
try:
307
# Invalid transformation matrix (zero determinant)
308
invalid_transform = Affine(0, 0, 0, 0, 0, 0)
309
guard_transform(invalid_transform)
310
except TransformError as e:
311
print(f"Invalid transform: {e}")
312
313
# Check transform validity
314
if transform.determinant() == 0:
315
raise TransformError("Singular transformation matrix")
316
```