def test_calculation_of_tie_point_grid_float_coords(self): # NOTE: This does not test against unequaly sized output of get_image_windows_to_match(). # overwrite gt and prj ref = GeoArray(self.ref_path) ref.to_mem() ref.filePath = None tgt = GeoArray(self.tgt_path) tgt.to_mem() tgt.filePath = None ref.gt = [ 330000.19999996503, 10.00000001, 0.0, 5862000.7999997628, 0.0, -10.00000001 ] # ref.gt = [330000.1, 10.1, 0.0, 5862000.1, 0.0, -10.1] tgt.gt = [ 335440.19999996503, 10.00000001, 0.0, 5866490.7999997628, 0.0, -10.00000001 ] # tgt.gt = [330000.1, 10.1, 0.0, 5862000.1, 0.0, -10.1] # get instance of COREG_LOCAL object CRL = COREG_LOCAL(ref, tgt, **dict(CPUs=32, **self.coreg_kwargs)) CRL.calculate_spatial_shifts()
def coerce_to_geoarray(dataset): """ Attempt to coerce inputs of various types to a GeoArray object. """ if isinstance(dataset, GeoArray): geo_array = dataset elif isinstance(dataset, gdal.Dataset): gt = dataset.GetGeoTransform() crs = dataset.GetProjection() crs_wkt = crs.ExportToWkt() geo_array = GeoArray(dataset, geotransform=gt, projection=crs_wkt) elif isinstance(dataset, np.memmap): ### Need to pass gt & crs_wkt as arguments... raise NotImplementedError # elif isinstance(dataset, hypro.spatial.RasterMap): # raise NotImplementedError elif isinstance(dataset, Path): geo_array = GeoArray(str(dataset)) elif type(dataset) is str: geo_array = GeoArray(dataset) else: raise TypeError(f'Cannot coerce type "{type(dataset)}" to GeoArray.') return geo_array
def setUpClass(cls) -> None: config = EnPTConfig(**config_for_testing_dlr) # get lons / lats with TemporaryDirectory() as td, ZipFile(config.path_l1b_enmap_image, "r") as zf: zf.extractall(td) cls.L1_obj = L1B_Reader(config=config).read_inputdata( root_dir_main=td, compute_snr=False) cls.data2transform_vnir_sensorgeo_3D = cls.L1_obj.vnir.data cls.data2transform_vnir_sensorgeo_2D = cls.L1_obj.vnir.data[:, :, 0] # a single VNIR band in sensor geometry cls.gA2transform_vnir_mapgeo = GeoArray( config.path_dem) # a DEM in map geometry given by the user cls.data2transform_swir_sensorgeo_3D = cls.L1_obj.swir.data cls.data2transform_swir_sensorgeo_2D = cls.L1_obj.swir.data[:, :, -1] # a single SWIR band in sensor geometry cls.gA2transform_swir_mapgeo = GeoArray( config.path_dem) # a DEM in map geometry given by the user cls.VS_SGT = VNIR_SWIR_SensorGeometryTransformer( lons_vnir=cls.L1_obj.meta.vnir.lons, lats_vnir=cls.L1_obj.meta.vnir.lats, lons_swir=cls.L1_obj.meta.swir.lons, lats_swir=cls.L1_obj.meta.swir.lats, prj_vnir=32632, prj_swir=32632, res_vnir=(30, 30), res_swir=(30, 30), resamp_alg='nearest', # radius_of_influence=45 )
def test_shift_calculation_inmem_gAs_path_out_auto(self): """Test input parameter path_out='auto' in case input reference/ target image are in-memory GeoArrays.""" ref = GeoArray(np.random.randint(1, 100, (1000, 1000))) tgt = GeoArray(np.random.randint(1, 100, (1000, 1000))) with self.assertRaises(ValueError): self.run_shift_detection_correction(ref, tgt, **dict(self.coreg_kwargs, path_out='auto', fmt_out='ENVI', v=True))
def data(self, *geoArr_initArgs): if geoArr_initArgs[0] is not None: # TODO this must be able to handle subset inputs in tiled processing if isinstance(geoArr_initArgs[0], np.ndarray): self._data = GeoArray(geoArr_initArgs[0], geotransform=self._data.gt, projection=self._data.prj) else: self._data = GeoArray(*geoArr_initArgs) else: del self.data
def test_coreg_init_from_inMem_GeoArray(self): # get GeoArray instances self.ref_gA = GeoArray(self.ref_path) self.tgt_gA = GeoArray(self.tgt_path) # assure the raster data are in-memory self.ref_gA.to_mem() self.tgt_gA.to_mem() # get instance of COREG_LOCAL object self.CRL = COREG_LOCAL(self.ref_gA, self.tgt_gA, **self.coreg_kwargs)
def setUp(self): self.vnir_gA = GeoArray(np.random.randint(0, 255, (10, 10, 10)), geotransform=(331185.0, 30.0, -0.0, 5840115.0, -0.0, -30.0), projection=CRS(32633).to_wkt()) self.swir_gA = GeoArray(np.random.randint(0, 255, (10, 10, 20)), geotransform=(331185.0, 30.0, -0.0, 5840115.0, -0.0, -30.0), projection=CRS(32633).to_wkt()) self.vnir_wvls = np.arange(900, 1000, 10) self.swir_wvls = np.arange(935, 1135, 10) self.VSSt = VNIR_SWIR_Stacker(vnir=self.vnir_gA, swir=self.swir_gA, vnir_wvls=self.vnir_wvls, swir_wvls=self.swir_wvls)
def test_shift_calculation_different_geographic_datum(self): """Test if shift computation properly raises a RunTimeError if the matching window is centered at a cloudy image position. """ ref = GeoArray(self.ref_path).to_mem() tgt = GeoArray(self.tgt_path).to_mem() # force to overwrite projection ref.filePath = None ref.prj = EPSG2WKT(3035) # ETRS89_LAEA_Europe with self.assertRaises(RuntimeError): self.run_shift_detection_correction(ref, tgt, **dict(self.coreg_kwargs))
def _Kriging_sp(self, attrName, skip_nodata=1, skip_nodata_col='ABS_SHIFT', outGridRes=None, fName_out=None, tilepos=None): GDF = self.CoRegPoints_table GDF2pass = GDF if not skip_nodata else GDF[GDF[skip_nodata_col] != self.outFillVal] X_coords, Y_coords, ABS_SHIFT = GDF2pass['X_UTM'], GDF2pass['Y_UTM'], GDF2pass[attrName] xmin, ymin, xmax, ymax = GDF2pass.total_bounds grid_res = outGridRes if outGridRes else int(min(xmax - xmin, ymax - ymin) / 250) grid_x, grid_y = np.arange(xmin, xmax + grid_res, grid_res), np.arange(ymax, ymin - grid_res, -grid_res) # Reference: P.K. Kitanidis, Introduction to Geostatistcs: Applications in Hydrogeology, # (Cambridge University Press, 1997) 272 p. from pykrige.ok import OrdinaryKriging OK = OrdinaryKriging(X_coords, Y_coords, ABS_SHIFT, variogram_model='spherical', verbose=False) zvalues, sigmasq = OK.execute('grid', grid_x, grid_y, backend='C', n_closest_points=12) if self.CPUs is None or self.CPUs > 1: fName_out = fName_out if fName_out else \ "Kriging__%s__grid%s_ws%s_%s.tif" % (attrName, self.grid_res, self.COREG_obj.win_size_XY, tilepos) else: fName_out = fName_out if fName_out else \ "Kriging__%s__grid%s_ws%s.tif" % (attrName, self.grid_res, self.COREG_obj.win_size_XY) path_out = get_generic_outpath(dir_out=self.dir_out, fName_out=fName_out) # add a half pixel grid points are centered on the output pixels xmin, ymin, xmax, ymax = xmin - grid_res / 2, ymin - grid_res / 2, xmax + grid_res / 2, ymax + grid_res / 2 GeoArray(zvalues, geotransform=(xmin, grid_res, 0, ymax, 0, -grid_res), projection=self.COREG_obj.shift.prj).save(path_out) return zvalues
def compute_stack(self, algorithm: str) -> GeoArray: """Stack VNIR and SWIR bands with respect to their spectral overlap. :param algorithm: 'order_by_wvl': keep spectral bands unchanged, order bands by wavelength 'average': average the spectral information within the overlap 'vnir_only': only use the VNIR bands (cut overlapping SWIR bands) 'swir_only': only use the SWIR bands (cut overlapping VNIR bands) :return: the stacked data cube as GeoArray instance """ # TODO: This should also set an output nodata value. if algorithm == 'order_by_wvl': data_stacked, wvls = self._get_stack_order_by_wvl() elif algorithm == 'average': data_stacked, wvls = self._get_stack_average() elif algorithm == 'vnir_only': data_stacked, wvls = self._get_stack_vnir_only() elif algorithm == 'swir_only': data_stacked, wvls = self._get_stack_swir_only() else: raise ValueError(algorithm) gA_stacked = GeoArray(data_stacked, geotransform=self.vnir.gt, projection=self.vnir.prj, nodata=self.vnir.nodata) gA_stacked.meta.band_meta['wavelength'] = list(wvls) return gA_stacked
def get_flat_dem_from_average_elevation(cls, corner_coords_lonlat, average_elevation, xres=30, yres=30): """Return a 'flat DEM' instance of DEM_Processor. (a GeoArray fully covering the given coorner coordinates with the average elevation as pixel values) :param corner_coords_lonlat: corner coordinates to be covered by the output DEM :param average_elevation: average elevation in meters :param xres: x-resolution in meters :param yres: y-resolution in meters :return: """ # compute the dimensions of the flat output DEM tgt_utm_epsg = get_UTMEPSG_from_LonLat( *get_center_coord(corner_coords_lonlat)) corner_coords_utm = [ transform_any_prj(prj_src=4326, prj_tgt=tgt_utm_epsg, x=x, y=y) for x, y in corner_coords_lonlat ] x_all, y_all = list(zip(*corner_coords_utm)) cols = int(np.ceil((max(x_all) - min(x_all)) / xres)) + 2 rows = int(np.ceil((max(y_all) - min(y_all)) / yres)) + 2 # get a GeoArray instance dem_gA = GeoArray(np.full((rows, cols), fill_value=average_elevation), geotransform=(np.floor(min(x_all)) - xres, xres, 0, np.ceil(max(y_all)) + yres, 0, -yres), projection=CRS(tgt_utm_epsg).to_wkt()) return cls(dem_gA, corner_coords_lonlat)
def _get_enmap_band_for_matching(self)\ -> GeoArray: """Return the EnMAP band to be used in co-registration in the projection of the reference image.""" self._EnMAP_Im.logger.warning( 'Statically using band 40 for co-registration.') bandidx = 39 # FIXME hardcoded enmap_band_sensorgeo = self._EnMAP_Im.vnir.data[:, :, bandidx] # transform from sensor to map geometry to make it usable for tie point detection self._EnMAP_Im.logger.info( 'Temporarily transforming EnMAP band %d to map geometry for co-registration.' % (bandidx + 1)) GT = Geometry_Transformer(lons=self._EnMAP_Im.meta.vnir.lons[:, :, bandidx], lats=self._EnMAP_Im.meta.vnir.lats[:, :, bandidx], fill_value=0, resamp_alg='gauss', radius_of_influence=30, nprocs=self.cfg.CPUs) self._EnMAP_band = \ GeoArray(*GT.to_map_geometry(enmap_band_sensorgeo, tgt_prj=self._ref_Im.prj, # TODO correct? tgt_coordgrid=self._ref_Im.xygrid_specs), nodata=0) return self._EnMAP_band
def to_map_geometry(self, path_or_geoarray_sensorgeo: Union[str, GeoArray, np.ndarray], tgt_prj: Union[str, int] = None, tgt_extent: Tuple[float, float, float, float] = None, tgt_res: Tuple[float, float] = None, tgt_coordgrid: Tuple[Tuple, Tuple] = None, area_definition: AreaDefinition = None): data_sensorgeo = GeoArray(path_or_geoarray_sensorgeo) if data_sensorgeo.is_map_geo: raise RuntimeError( 'The dataset to be transformed into map geometry already represents map geometry.' ) # run transformation (output extent/area definition etc. is internally computed from the geolayers if not given) out_data, out_gt, out_prj = \ super(Geometry_Transformer, self).to_map_geometry(data_sensorgeo[:], tgt_prj=tgt_prj, tgt_extent=tgt_extent, tgt_res=tgt_res, tgt_coordgrid=tgt_coordgrid, area_definition=self.area_definition) return out_data, out_gt, out_prj
def __init__(self, filepath='', satellite='', sensor='', LayerBandsAssignment=None): # type: (str, str, str, list) -> None """Get instance of RefCube. :param filepath: file path for importing an existing reference cube from disk :param satellite: the satellite for which the reference cube holds its spectral data :param sensor: the sensor for which the reference cube holds its spectral data :param LayerBandsAssignment: the LayerBandsAssignment for which the reference cube holds its spectral data """ # privates self._col_imName_dict = dict() self._wavelenths = [] # defaults self.data = GeoArray(np.empty( (0, 0, len(LayerBandsAssignment) if LayerBandsAssignment else 0)), nodata=-9999) self.srcImNames = [] # args/ kwargs self.filepath = filepath self.satellite = satellite self.sensor = sensor self.LayerBandsAssignment = LayerBandsAssignment or [] if filepath: self.read_data_from_disk(filepath) if self.satellite and self.sensor and self.LayerBandsAssignment: self._add_bandnames_wavelenghts_to_meta()
def correct_shifts(self, max_GCP_count: int = None, cliptoextent: bool = False, min_points_local_corr: int = 5) -> OrderedDict: """Perform a local shift correction using all points from the previously calculated tie point grid. NOTE: Only valid matches are used as GCP points. :param max_GCP_count: maximum number of GCPs to use :param cliptoextent: whether to clip the output image to its real extent :param min_points_local_corr: number of valid tie points, below which a global shift correction is performed instead of a local correction (global X/Y shift is then computed as the mean shift of the remaining points)(default: 5 tie points) :return: """ if not self._tiepoint_grid: self.calculate_spatial_shifts() if self.tiepoint_grid.GCPList: if max_GCP_count: self.coreg_info['GCPList'] = self.coreg_info[ 'GCPList'][:max_GCP_count] # make sure the correction is applied to the original target image im2shift = GeoArray(self.params['im_tgt'], nodata=self.nodata[1], progress=self.progress, q=self.q) if has_metaRotation(im2shift): # resample the target image because (so far) the computed shifts cannot be applied to a dataset with # a metadata rotation (GDAL GeoTransform not 0 at positons 2 and 4) im2shift = remove_metaRotation(im2shift) # apply the correction DS = DESHIFTER( im2shift, self.coreg_info, path_out=self.path_out, fmt_out=self.fmt_out, out_crea_options=self.out_creaOpt, align_grids=self.align_grids, match_gsd=self.match_gsd, out_gsd=self.out_gsd, target_xyGrid=self.target_xyGrid, min_points_local_corr=min_points_local_corr, resamp_alg=self.rspAlg_DS, cliptoextent=cliptoextent, # clipextent=self.im2shift.box.boxMapYX, progress=self.progress, v=self.v, q=self.q) self.deshift_results = DS.correct_shifts() return self.deshift_results else: if not self.q: warnings.warn( 'Correction of geometric shifts failed because the input GCP list is empty!' )
def test_shift_calculation_noWGS84(self): """Test if shift computation properly raises a RunTimeError if the matching window is centered at a cloudy image position. """ ref = GeoArray(self.ref_path).to_mem() tgt = GeoArray(self.tgt_path).to_mem() # force to overwrite projection ref.filePath = None tgt.filePath = None ref.prj = EPSG2WKT(3035) # ETRS89_LAEA_Europe tgt.prj = EPSG2WKT(3035) # ETRS89_LAEA_Europe CR = self.run_shift_detection_correction(ref, tgt, **dict(self.coreg_kwargs)) self.assertTrue(CR.success)
def __init__(self, rpc_coeffs: dict, elevation: Union[str, GeoArray, int, float], enmapIm_cornerCoords: Tuple[Tuple[float, float]], enmapIm_dims_sensorgeo: Tuple[int, int]): """Get an instance of RPC_Geolayer_Generator. :param rpc_coeffs: RPC coefficients for a single EnMAP band :param elevation: digital elevation model in map geometry (file path or instance of GeoArray) OR average elevation in meters as integer or float :param enmapIm_cornerCoords: corner coordinates as tuple of lon/lat pairs :param enmapIm_dims_sensorgeo: dimensions of the EnMAP image in sensor geometry (rows, colunms) """ self.row_off = rpc_coeffs['row_off'] self.col_off = rpc_coeffs['col_off'] self.lat_off = rpc_coeffs['lat_off'] self.lon_off = rpc_coeffs['long_off'] self.height_off = rpc_coeffs['height_off'] self.row_scale = rpc_coeffs['row_scale'] self.col_scale = rpc_coeffs['col_scale'] self.lat_scale = rpc_coeffs['lat_scale'] self.lon_scale = rpc_coeffs['long_scale'] self.height_scale = rpc_coeffs['height_scale'] self.row_num_coeffs = rpc_coeffs['row_num_coeffs'] self.row_den_coeffs = rpc_coeffs['row_den_coeffs'] self.col_num_coeffs = rpc_coeffs['col_num_coeffs'] self.col_den_coeffs = rpc_coeffs['col_den_coeffs'] self.elevation = elevation if isinstance( elevation, (int, float)) else GeoArray(elevation) self.enmapIm_cornerCoords = enmapIm_cornerCoords self.enmapIm_dims_sensorgeo = enmapIm_dims_sensorgeo
def to_map_geometry(self, path_or_geoarray_sensorgeo: Union[str, GeoArray, np.ndarray], tgt_prj: Union[str, int] = None, tgt_extent: Tuple[float, float, float, float] = None, tgt_res: Tuple[float, float] = None, tgt_coordgrid: Tuple[Tuple, Tuple] = None, area_definition: AreaDefinition = None): data_sensorgeo = GeoArray(path_or_geoarray_sensorgeo) if data_sensorgeo.is_map_geo: raise RuntimeError( 'The dataset to be transformed into map geometry already represents map geometry.' ) # run transformation (output extent/area definition etc. is internally computed from the geolayers if not given) with warnings.catch_warnings(): # FIXME remove that after removing pyresample pinning warnings.filterwarnings( 'ignore', category=DeprecationWarning, message='.*is a deprecated alias for the builtin.*') out_data, out_gt, out_prj = \ super(Geometry_Transformer, self).to_map_geometry(data_sensorgeo[:], tgt_prj=tgt_prj, tgt_extent=tgt_extent, tgt_res=tgt_res, tgt_coordgrid=tgt_coordgrid, area_definition=self.area_definition) return out_data, out_gt, out_prj
def test_cluster_image_and_get_uniform_samples(self): src_im = self.SHC.ims_ref[0] baseN = os.path.splitext(os.path.basename(src_im))[0] unif_random_spectra = self.SHC.cluster_image_and_get_uniform_spectra( src_im, basename_clf_dump=baseN) self.assertIsInstance(unif_random_spectra, np.ndarray) self.assertEqual(unif_random_spectra.shape, (self.tgt_n_samples, GeoArray(src_im).bands))
def setUpClass(cls): cls.geoArr = GeoArray(testdata) cls.geoArr.to_mem() # get RSR instance for Landsat-8 cls.rsr_l8 = RSR(satellite='Landsat-8', sensor='OLI_TIRS', no_thermal=True)
def interpolate_1x1(lon, lat, values, lon0=0.): """ Make a regional mean and get a time series """ newlon, newlat, newvalues = grid.interpolate_1x1(lon, lat, values, lon0=lon0) return GeoArray(newvalues, lat=newlat, lon=newlon)
def setUpClass(cls): cls.geoArr = GeoArray(testdata, nodata=-9999) cls.geoArr.to_mem() cls.geoArr[:10, :10, :10] = -9999 # simulate some pixels that have nodata in some bands (unusable for KMeans) cls.kmeans = KMeansRSImage(cls.geoArr, n_clusters=5, sam_classassignment=False) os.environ['MPLBACKEND'] = 'Template' # disables matplotlib figure popups # NOTE: import geoarray sets 'Agg'
def test_init_demTooSmall(self): # covers only 100x100 px in the upper left (<5%) dem = GeoArray(np.random.randint(0, 500, (100, 100)), geotransform=(626938.928052, 30.0, 0, 5267203.56579, 0, -30.0), projection=CRS(32632).to_wkt()) with self.assertRaises(ValueError): DEM_Processor(dem, enmapIm_cornerCoords=self.ll_cornerCoords)
def test_init_noWGS84(self): # NAD83 projection ({'proj': 'longlat', 'ellps': 'GRS80', 'towgs84': '0,0,0,0,0,0,0'}) with self.assertRaises(ValueError): dem = GeoArray( np.array([1, 2]), geotransform=(10.6, 0.00036, -0.0, 47.5, -0.0, -0.00036), # can be anything projection=CRS(4269).to_wkt()) # NAD83 DEM_Processor(dem, enmapIm_cornerCoords=self.ll_cornerCoords)
def __init__(self, config: EnPTConfig): """Create an instance of Spatial_Optimizer.""" self.cfg = config self._ref_Im: Optional[GeoArray, None] = GeoArray(self.cfg.path_reference_image) self._EnMAP_Im: Optional[EnMAPL1Product_SensorGeo, None] = None self._EnMAP_band: Optional[GeoArray, None] = None self._EnMAP_mask: Optional[GeoArray, None] = None
def __init__(self, dem_path_geoarray: Union[str, GeoArray], enmapIm_cornerCoords: Tuple[Tuple[float, float]], CPUs: int = None): self.dem = GeoArray(dem_path_geoarray) self.enmapIm_cornerCoords = enmapIm_cornerCoords self.CPUs = CPUs or cpu_count() self._set_nodata_if_not_provided() self._validate_input()
def test_shift_calculation_nonquadratic_pixels(self): """Test with default parameters - should compute X/Y shifts properly and write the de-shifted target image.""" # overwrite gt and prj ref = GeoArray(self.ref_path) ref.to_mem() ref.filePath = None ref.gt = [330000.00000001, 5.8932, 0.0, 5862000.0000001, 0.0, -10.1] tgt = GeoArray(self.tgt_path) tgt.to_mem() tgt.filePath = None tgt.gt = [335440.0000001, 5.8933, 0.0, 5866490.0000001, 0.0, -10.1] CR = self.run_shift_detection_correction(ref, tgt, **dict(self.coreg_kwargs, wp=(341500.0, 5861440.0), footprint_poly_ref=None, footprint_poly_tgt=None)) self.assertTrue(CR.success)
def get_preprocessed_dem(self) -> GeoArray: """Get a digital elevation model in EnMAP sensor geometry of the current detector.""" if self.cfg.path_dem: self.logger.info('Pre-processing DEM for %s...' % self.detector_name) DP = DEM_Processor(self.cfg.path_dem, enmapIm_cornerCoords=tuple( zip(self.detector_meta.lon_UL_UR_LL_LR, self.detector_meta.lat_UL_UR_LL_LR)), CPUs=self.cfg.CPUs) DP.fill_gaps() # FIXME this will also be needed at other places R, C = self.data.shape[:2] if DP.dem.is_map_geo: lons = self.detector_meta.lons lats = self.detector_meta.lats if not (lons.ndim == 2 and lats.ndim == 2) and not (lons.ndim == 3 and lats.ndim == 3): raise ValueError( (lons.ndim, lats.ndim), 'Geolayer must be either 2- or 3-dimensional.') msg_bandinfo = '' if lons.ndim == 3: # 3D geolayer (the usual case for EnMAP data provided by DLR) lons = lons[:, :, 0] lats = lats[:, :, 0] msg_bandinfo = ' (using first band of %s geolayer)' % self.detector_name else: # 2D geolayer (GFZ-internal test case) # FIXME replace linear interpolation by native geolayers if lons.shape != self.data.shape: lons = self.detector_meta.interpolate_corners( *self.detector_meta.lon_UL_UR_LL_LR, nx=C, ny=R) if lats.shape != self.data.shape: lats = self.detector_meta.interpolate_corners( *self.detector_meta.lat_UL_UR_LL_LR, nx=C, ny=R) self.logger.info( ('Transforming DEM to %s sensor geometry%s...' % (self.detector_name, msg_bandinfo))) self.dem = DP.to_sensor_geometry(lons=lons, lats=lats) else: self.dem = DP.dem else: self.logger.info( 'No DEM for the %s detector provided. Falling back to an average elevation of %d meters.' % (self.detector_name, self.cfg.average_elevation)) self.dem = GeoArray( np.full(self.data.shape[:2], self.cfg.average_elevation).astype(np.int16)) return self.dem
def arosics_test(): """ 测试几何校正工具 See https://gitext.gfz-potsdam.de/danschef/arosics https://www.mdpi.com/2072-4292/9/7/676 """ # Global coregistration test from geoarray import GeoArray from arosics import COREG im_reference = r'I:\sentinel\unzip\T49QEE_20180930T030541_B08.jp2' im_target = r'I:\sentinel\unzip\T49QEE_20181005T030549_B08.jp2' # im_target = r'D:\T50QKM_geo.tif' # get a sample numpy array with corresponding geoinformation as reference geoArr = GeoArray(im_reference) ref_ndarray = geoArr[:] # numpy.ndarray with shape (10980, 10980) ref_gt = geoArr.geotransform # GDAL geotransform ref_prj = geoArr.projection # projection as WKT string # get a sample numpy array with corresponding geoinformation as target geoArr = GeoArray(im_target) tgt_ndarray = geoArr[:] # numpy.ndarray with shape (10980, 10980) tgt_gt = geoArr.geotransform # GDAL geotransform tgt_prj = geoArr.projection # projection as WKT string # pass an instance of GeoArray to COREG and calculate spatial shifts geoArr_reference = GeoArray(ref_ndarray, ref_gt, ref_prj) geoArr_target = GeoArray(tgt_ndarray, tgt_gt, tgt_prj) CR = COREG(geoArr_reference, geoArr_target, path_out='D:/T49QEE_20181005_correct.tif', wp=(595951, 2480095), ws=(256, 256), v=True, max_iter=20, max_shift=100) CR.calculate_spatial_shifts() CR.correct_shifts()
def remove_metaRotation(gA_rot: GeoArray, rspAlg='cubic') -> GeoArray: """Remove any metadata rotation (a rotation that only exists in the map info).""" gA = GeoArray(*warp_ndarray(gA_rot[:], gA_rot.gt, gA_rot.prj, rspAlg=rspAlg, # out_gsd=(gA_rot.xgsd, gA_rot.ygsd) ), nodata=gA_rot.nodata) gA.basename = os.path.basename(gA.basename) gA.meta = gA.meta return gA