sarkit-convert 0.1.0__py3-none-any.whl → 0.3.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,201 @@
1
+ import enum
2
+ import logging
3
+
4
+ import numpy as np
5
+ import numpy.typing as npt
6
+ import pyproj
7
+
8
+ import sarkit_convert._utils
9
+
10
+ logger = logging.getLogger(__name__)
11
+
12
+
13
+ class _ModelTypeCodes(enum.IntEnum):
14
+ """GeoTIFF GTModelTypeGeoKey values"""
15
+
16
+ ModelTypeProjected = 1
17
+ ModelTypeGeographic = 2
18
+ ModelTypeGeocentric = 3
19
+
20
+
21
+ def _get_transformation_matrix(
22
+ model_transformation_tag: npt.ArrayLike | None,
23
+ model_tiepoint_tag: npt.ArrayLike | None,
24
+ model_pixel_scale_tag: npt.ArrayLike | None,
25
+ ):
26
+ """Matrix to convert from image coordinates to model coordinates
27
+
28
+ See: http://geotiff.maptools.org/spec/geotiff2.6.html
29
+ """
30
+
31
+ if model_transformation_tag is not None:
32
+ matrix = np.asarray(model_transformation_tag).reshape(4, 4)
33
+ if np.any(matrix[-1] != (0.0, 0.0, 0.0, 1.0)):
34
+ logger.warning(
35
+ "Last row of ModelTransformation matrix must be (0, 0, 0, 1)"
36
+ )
37
+ return matrix
38
+
39
+ if model_pixel_scale_tag is not None and model_tiepoint_tag is not None:
40
+ sx, sy, sz = np.asarray(model_pixel_scale_tag)
41
+ mtp = np.atleast_2d(model_tiepoint_tag)
42
+
43
+ if mtp.shape[0] != 1:
44
+ # Probably want to interpolate tiepoints if there is more than one
45
+ # Should only be one tiepoint when ModelPixelScale is present
46
+ return None
47
+
48
+ i, j, k, x, y, z = mtp[0]
49
+ tx = x - i * sx
50
+ ty = y + j * sy
51
+ tz = z - k * sz
52
+ matrix = np.asarray(
53
+ [
54
+ [sx, 0.0, 0.0, tx],
55
+ [0.0, -sy, 0.0, ty],
56
+ [0.0, 0.0, sz, tz],
57
+ [0.0, 0.0, 0.0, 1.0],
58
+ ]
59
+ )
60
+ return matrix
61
+
62
+ return None
63
+
64
+
65
+ def sidd_projection_from_geotiff(
66
+ shape: tuple[int, int],
67
+ gt_model_type_geo_key: int,
68
+ projected_cs_type_geo_key: int | None,
69
+ geographic_type_geo_key: int | None,
70
+ model_transformation_tag: npt.ArrayLike | None,
71
+ model_tiepoint_tag: npt.ArrayLike | None,
72
+ model_pixel_scale_tag: npt.ArrayLike | None,
73
+ grid_size: int = 11,
74
+ max_order: int = 5,
75
+ ) -> tuple[npt.NDArray, npt.NDArray, npt.NDArray, npt.NDArray]:
76
+ """
77
+ Generate SIDD PolynomialProjection polynomials for a GeoTIFF
78
+
79
+ Parameters
80
+ ----------
81
+ shape : tuple of int
82
+ Shape of the image
83
+ gt_model_type_geo_key : int
84
+ Value of the GTModelTypeGeoKey
85
+ projected_cs_type_geo_key : int, optional
86
+ Value of the ProjectedCSTypeGeoKey. Required if gt_model_type_geo_key == 1
87
+ geographic_type_geo_key : int, optional
88
+ Value of the GeographicTypeGeoKey. Required if gt_model_type_geo_key == 2
89
+ model_transformation_tag : array-like, optional
90
+ Value of the ModelTransformationTag
91
+ model_tiepoint_tag : array-like, optional
92
+ Value of the ModelTiepointTag
93
+ model_pixel_scale_tag : array-like, optional
94
+ Value of the ModelPixelScaleTag
95
+ grid_size : int, optional
96
+ Number of fit points in each dimension
97
+ max_order : int, optional
98
+ Maximum order of generated polynomials
99
+
100
+ Returns
101
+ -------
102
+ rowcol_to_lat : ndarray
103
+ 2D polynomial coefficients. (row, col) -> latitude degrees
104
+ rowcol_to_lon : ndarray
105
+ 2D polynomial coefficients. (row, col) -> longitude degrees
106
+ latlon_to_row : ndarray
107
+ 2D polynomial coefficients. (latitude degrees, longitude degrees) -> row
108
+ latlon_to_col : ndarray
109
+ 2D polynomial coefficients. (latitude degrees, longitude degrees) -> col
110
+
111
+ Notes
112
+ -----
113
+ See:
114
+ * http://geotiff.maptools.org/spec/geotiff2.5.html
115
+ * http://geotiff.maptools.org/spec/geotiff2.6.html
116
+
117
+ """
118
+ # Image Coordinates are [I J K 1] -> (column, row, vertical, 1)
119
+ image_coords = np.stack(
120
+ [
121
+ *np.meshgrid(
122
+ np.linspace(0, shape[1], grid_size),
123
+ np.linspace(0, shape[0], grid_size),
124
+ ),
125
+ np.zeros((grid_size, grid_size)), # no vertical component
126
+ np.ones((grid_size, grid_size)),
127
+ ],
128
+ axis=-1,
129
+ )
130
+
131
+ matrix = _get_transformation_matrix(
132
+ model_transformation_tag, model_tiepoint_tag, model_pixel_scale_tag
133
+ )
134
+
135
+ if matrix is None:
136
+ raise RuntimeError("Failed to get transformation matrix")
137
+
138
+ swap_axis = False
139
+ if gt_model_type_geo_key == _ModelTypeCodes.ModelTypeProjected:
140
+ if projected_cs_type_geo_key is None:
141
+ raise ValueError(
142
+ "projected_cs_type_geo_key must be provided for Projected model type"
143
+ )
144
+
145
+ crs = pyproj.CRS.from_epsg(int(projected_cs_type_geo_key))
146
+ elif gt_model_type_geo_key == _ModelTypeCodes.ModelTypeGeographic:
147
+ if geographic_type_geo_key is None:
148
+ raise ValueError(
149
+ "geographic_type_geo_key must be provided for Geographic model type"
150
+ )
151
+
152
+ crs = pyproj.CRS.from_epsg(int(geographic_type_geo_key))
153
+ # GeoTIFF axis order convention is reversed from CRS for Geographic coordinate systems
154
+ swap_axis = True
155
+ else:
156
+ raise RuntimeError(
157
+ f"GTModelTypeGeoKey == {gt_model_type_geo_key} not supported"
158
+ )
159
+
160
+ # model image
161
+ # coords = matrix * coords
162
+ # |- -| |- -| |- -|
163
+ # | X | | a b c d | | I |
164
+ # | | | | | |
165
+ # | Y | | e f g h | | J |
166
+ # | | = | | | |
167
+ # | Z | | i j k l | | K |
168
+ # | | | | | |
169
+ # | 1 | | m n o p | | 1 |
170
+ # |- -| |- -| |- -|
171
+ model_coords = np.inner(matrix, image_coords)
172
+ if swap_axis:
173
+ model_x = model_coords[1]
174
+ model_y = model_coords[0]
175
+ else:
176
+ model_x = model_coords[0]
177
+ model_y = model_coords[1]
178
+
179
+ transformer = pyproj.Transformer.from_crs(crs, 4326) # 4326 = WGS84 Lat/Lon
180
+ lats, lons = transformer.transform(model_x, model_y)
181
+ rows = image_coords[..., 1]
182
+ cols = image_coords[..., 0]
183
+
184
+ rc_span = max(np.ptp(rows), np.ptp(cols))
185
+ tol_px = 0.1
186
+ tol_lat = np.ptp(lats) / rc_span * tol_px
187
+ tol_lon = np.ptp(lons) / rc_span * tol_px
188
+
189
+ rowcol_to_lat = sarkit_convert._utils.polyfit2d_tol(
190
+ rows.flatten(), cols.flatten(), lats.flatten(), max_order, max_order, tol_lat
191
+ )
192
+ rowcol_to_lon = sarkit_convert._utils.polyfit2d_tol(
193
+ rows.flatten(), cols.flatten(), lons.flatten(), max_order, max_order, tol_lon
194
+ )
195
+ latlon_to_row = sarkit_convert._utils.polyfit2d_tol(
196
+ lats.flatten(), lons.flatten(), rows.flatten(), max_order, max_order, tol=tol_px
197
+ )
198
+ latlon_to_col = sarkit_convert._utils.polyfit2d_tol(
199
+ lats.flatten(), lons.flatten(), cols.flatten(), max_order, max_order, tol=tol_px
200
+ )
201
+ return rowcol_to_lat, rowcol_to_lon, latlon_to_row, latlon_to_col