|
18 | 18 | from rasterio.transform import RPCTransformer |
19 | 19 |
|
20 | 20 | from .angles import Angle2D, compute_raa, compute_sga |
| 21 | +from .dem import DEM |
21 | 22 | from .denoise import Denoise |
22 | 23 | from .hsi2rgb import Hsi2rgb |
23 | 24 | from .orthorectification import Ortho |
@@ -260,28 +261,50 @@ def _scale_units(self, units): |
260 | 261 |
|
261 | 262 | return scale |
262 | 263 |
|
263 | | - def _rpc_transform(self, scn, rpc): |
| 264 | + def _rpc_transform(self, scn, rpc, bounds): |
| 265 | + """ |
| 266 | + Compute a more accurate longitude/latitude transform for a scene |
| 267 | + using Rational Polynomial Coefficients (RPC). |
| 268 | +
|
| 269 | + Parameters |
| 270 | + ---------- |
| 271 | + scn : Scene |
| 272 | + Source scene object to be geolocated. |
| 273 | +
|
| 274 | + rpc : RPC |
| 275 | + Rational Polynomial Coefficients describing the sensor model. |
| 276 | +
|
| 277 | + bounds : list[float] |
| 278 | + Bounding box in EPSG:4326 coordinates formatted as |
| 279 | + [xmin, ymin, xmax, ymax], where x = longitude and y = latitude. |
| 280 | +
|
| 281 | + Returns |
| 282 | + ------- |
| 283 | + area : pyresample.geometry.AreaDefinition |
| 284 | + Pyresample area definition describing the transformed scene geometry. |
| 285 | + """ |
| 286 | + # Download DEM data |
| 287 | + dem = DEM(self.filename[0], bounds) |
| 288 | + dem.download() |
| 289 | + |
264 | 290 | # Create RPCTransformer |
265 | | - transformer = RPCTransformer(rpc) |
| 291 | + transformer = RPCTransformer(rpc, rpc_dem=dem.file_dem) |
266 | 292 |
|
267 | | - # Create 2D coordinate grids for image of shape (1024, 1000) |
| 293 | + # Get image dimensions |
268 | 294 | height, width = scn['radiance'].sizes['y'], scn['radiance'].sizes['x'] |
269 | 295 |
|
270 | | - # Create meshgrid of pixel coordinates |
271 | | - rows, cols = np.mgrid[0:height, 0:width] |
| 296 | + # Create pixel coordinate grids |
| 297 | + x_coords = np.arange(width) |
| 298 | + y_coords = np.arange(height) |
| 299 | + xx, yy = np.meshgrid(x_coords, y_coords) |
272 | 300 |
|
273 | | - # Flatten to 1D arrays |
274 | | - cols_flat = cols.ravel() # sample/column coordinates (x) |
275 | | - rows_flat = rows.ravel() # line/row coordinates (y) |
276 | | - heights_flat = np.zeros_like(cols_flat, dtype=float) # elevation (ground level = 0) |
| 301 | + # Transform pixel coordinates to lon/lat using xy() method |
| 302 | + # xy() takes (row, col) and returns (x, y) which is (lon, lat) |
| 303 | + lons, lats = transformer.xy(yy, xx) |
277 | 304 |
|
278 | | - # Transform from image coordinates to lon, lat |
279 | | - # Try xy() method instead - it transforms pixel to geographic |
280 | | - lons_flat, lats_flat = transformer.xy(rows_flat, cols_flat, heights_flat) |
281 | | - |
282 | | - # Reshape back to 2D arrays |
283 | | - lons = np.array(lons_flat).reshape(height, width) |
284 | | - lats = np.array(lats_flat).reshape(height, width) |
| 305 | + # Reshape back to 2D |
| 306 | + lons = lons.reshape(height, width) |
| 307 | + lats = lats.reshape(height, width) |
285 | 308 |
|
286 | 309 | area = SwathDefinition(lons, lats) |
287 | 310 | area.name = '_'.join([scn['radiance'].attrs['platform_name'], str(scn['radiance'].attrs['start_time'])]) |
@@ -329,13 +352,26 @@ def load(self, drop_waterbands=True): |
329 | 352 |
|
330 | 353 | # recalculate the lon and lat for EnMAP |
331 | 354 | # because the default yc and xc coords of TIFF files are not accurate |
332 | | - if self.reader == 'hsi_l1b': |
333 | | - rpc_swir = scn['rpc_coef_swir'].isel(bands_swir=0).item() |
334 | | - new_area = self._rpc_transform(scn, rpc_swir) |
| 355 | + if self.reader == "hsi_l1b": |
| 356 | + # Get default lon/lat from radiance area |
| 357 | + lons_default, lats_default = scn["radiance"].attrs["area"].get_lonlats() |
| 358 | + |
| 359 | + bounds = [ |
| 360 | + lons_default.min(), |
| 361 | + lats_default.min(), |
| 362 | + lons_default.max(), |
| 363 | + lats_default.max(), |
| 364 | + ] |
| 365 | + |
| 366 | + LOG.info("Calculating lon/lat using RPC and DEM data") |
| 367 | + |
| 368 | + rpc_swir = scn["rpc_coef_swir"].isel(bands_swir=0).item() |
| 369 | + new_area = self._rpc_transform(scn, rpc_swir, bounds) |
| 370 | + |
| 371 | + # Replace area definition for all datasets that contain an area attribute |
335 | 372 | for key in scn.keys(): |
336 | | - if 'area' in scn[key].attrs: |
337 | | - # Replace with the new area definition |
338 | | - scn[key].attrs['area'] = new_area |
| 373 | + if "area" in scn[key].attrs: |
| 374 | + scn[key].attrs["area"] = new_area |
339 | 375 |
|
340 | 376 | # get attrs |
341 | 377 | self.start_time = scn['radiance'].attrs['start_time'] |
|
0 commit comments