def test_geobox_simple(): t = geometry.GeoBox(4000, 4000, Affine(0.00025, 0.0, 151.0, 0.0, -0.00025, -29.0), epsg4326) expect_lon = np.asarray([151.000125, 151.000375, 151.000625, 151.000875, 151.001125, 151.001375, 151.001625, 151.001875, 151.002125, 151.002375]) expect_lat = np.asarray([-29.000125, -29.000375, -29.000625, -29.000875, -29.001125, -29.001375, -29.001625, -29.001875, -29.002125, -29.002375]) expect_resolution = np.asarray([-0.00025, 0.00025]) assert t.coordinates['latitude'].values.shape == (4000,) assert t.coordinates['longitude'].values.shape == (4000,) np.testing.assert_almost_equal(t.resolution, expect_resolution) np.testing.assert_almost_equal(t.coords['latitude'].values[:10], expect_lat) np.testing.assert_almost_equal(t.coords['longitude'].values[:10], expect_lon) assert (t == "some random thing") is False # ensure GeoBox accepts string CRS assert isinstance(geometry.GeoBox(4000, 4000, Affine(0.00025, 0.0, 151.0, 0.0, -0.00025, -29.0), 'epsg:4326').crs, CRS) # Check GeoBox class is hashable t_copy = GeoBox(t.width, t.height, t.transform, t.crs) t_other = GeoBox(t.width+1, t.height, t.transform, t.crs) assert t_copy is not t assert t == t_copy assert len(set([t, t, t_copy])) == 1 assert len(set([t, t_copy, t_other])) == 2
def __call__(self, product, time, group_by) -> Tile: # Do for a specific poly whose boundary is known output_crs = CRS(self.storage['crs']) filtered_item = [ 'geopolygon', 'lon', 'lat', 'longitude', 'latitude', 'x', 'y' ] filtered_dict = { k: v for k, v in filter(lambda t: t[0] in filtered_item, self.input_region.items()) } if 'feature_id' in self.input_region: filtered_dict['geopolygon'] = Geometry( self.input_region['geom_feat'], CRS(self.input_region['crs_txt'])) geopoly = filtered_dict['geopolygon'] else: geopoly = query_geopolygon(**self.input_region) datasets = self.dc.find_datasets(product=product, time=time, group_by=group_by, **filtered_dict) group_by = query_group_by(group_by=group_by) sources = self.dc.group_datasets(datasets, group_by) output_resolution = [ self.storage['resolution'][dim] for dim in output_crs.dimensions ] geopoly = geopoly.to_crs(output_crs) geobox = GeoBox.from_geopolygon(geopoly, resolution=output_resolution) return Tile(sources, geobox)
def fetch(self, grouped: VirtualDatasetBox, **load_settings: Dict[str, Any]) -> xarray.Dataset: """ Convert grouped datasets to `xarray.Dataset`. """ load_keys = self._LOAD_KEYS - {'measurements'} merged = merge_search_terms(select_keys(self, load_keys), select_keys(load_settings, load_keys)) product = grouped.product_definitions[self._product] if 'measurements' in self and 'measurements' in load_settings: for measurement in load_settings['measurements']: self._assert( measurement in self['measurements'], '{} not found in {}'.format(measurement, self._product)) measurement_dicts = self.output_measurements( grouped.product_definitions, load_settings.get('measurements')) if grouped.load_natively: canonical_names = [ product.canonical_measurement(measurement) for measurement in measurement_dicts ] dataset_geobox = geobox_union_conservative([ native_geobox(ds, measurements=canonical_names, basis=merged.get('like')) for ds in grouped.box.sum().item() ]) if grouped.geopolygon is not None: reproject_roi = compute_reproject_roi( dataset_geobox, GeoBox.from_geopolygon( grouped.geopolygon, crs=dataset_geobox.crs, align=dataset_geobox.alignment, resolution=dataset_geobox.resolution)) self._assert(reproject_roi.is_st, "native load is not axis-aligned") self._assert(numpy.isclose(reproject_roi.scale, 1.0), "native load should not require scaling") geobox = dataset_geobox[reproject_roi.roi_src] else: geobox = dataset_geobox else: geobox = grouped.geobox result = Datacube.load_data(grouped.box, geobox, list(measurement_dicts.values()), fuse_func=merged.get('fuse_func'), dask_chunks=merged.get('dask_chunks'), resampling=merged.get( 'resampling', 'nearest')) return result
def assert_same_read_results(source, dst_shape, dst_dtype, dst_transform, dst_nodata, dst_projection, resampling): expected = np.empty(dst_shape, dtype=dst_dtype) with source.open() as src: rasterio.warp.reproject(src.data, expected, src_transform=src.transform, src_crs=str(src.crs), src_nodata=src.nodata, dst_transform=dst_transform, dst_crs=str(dst_projection), dst_nodata=dst_nodata, resampling=resampling) result = np.full(dst_shape, dst_nodata, dtype=dst_dtype) H, W = dst_shape dst_gbox = GeoBox(W, H, dst_transform, dst_projection) with datacube.set_options(reproject_threads=1): with source.open() as rdr: read_time_slice(rdr, result, dst_gbox, dst_nodata=dst_nodata, resampling=resampling) assert np.isclose(result, expected, atol=0, rtol=0.05, equal_nan=True).all() return result
def make_sample_netcdf(tmpdir): """Make a test Geospatial NetCDF file, 4000x4000 int16 random data, in a variable named `sample`. Return the GDAL access string.""" sample_nc = str(tmpdir.mkdir('netcdfs').join('sample.nc')) geobox = GeoBox(4000, 4000, affine=Affine(25.0, 0.0, 1200000, 0.0, -25.0, -4200000), crs=epsg3577) sample_data = np.random.randint(10000, size=(4000, 4000), dtype=np.int16) variables = { 'sample': Variable(sample_data.dtype, nodata=-999, dims=geobox.dimensions, units=1) } nco = create_netcdf_storage_unit(sample_nc, geobox.crs, geobox.coordinates, variables=variables, variable_params={}) nco['sample'][:] = sample_data nco.close() return 'NetCDF:"%s":sample' % sample_nc, geobox, sample_data
def rasterfile_to_xarray(file, geobox=None, name=None, nodata=None): """Blit like""" with rasterio.open(file) as src: assert src.indexes == (1, ) # assume single band if geobox is None: from datacube.utils.geometry import GeoBox, CRS # TODO: fix this heinousness global crs global affine crs = src.crs affine = src.transform geobox = GeoBox( width=src.width, height=src.height, affine=src. affine, # .transform is a list, .affine is an object crs=CRS(src.crs.wkt)) array = src.read(1) else: band = rasterio.band( src, 1) # do not attempt to read entire extent into memory array = np.empty((geobox.height, geobox.width), dtype=band.dtype) rasterio.warp.reproject(source=band, destination=array, dst_crs=geobox.crs.crs_str, dst_transform=geobox.affine, dst_nodata=nodata) return numpy_to_xarray(array, geobox, name)
def internal_make_sample_geotiff(nodata=-999): sample_geotiff = str(tmpdir.mkdir('tiffs').join('sample.tif')) geobox = GeoBox(100, 200, affine=Affine(25.0, 0.0, 0, 0.0, -25.0, 0), crs=epsg3577) if np.isnan(nodata): out_dtype = 'float64' sample_data = 10000 * np.random.random_sample(size=geobox.shape) else: out_dtype = 'int16' sample_data = np.random.randint(10000, size=geobox.shape, dtype=out_dtype) rio_args = { 'height': geobox.height, 'width': geobox.width, 'count': 1, 'dtype': out_dtype, 'crs': 'EPSG:3577', 'transform': geobox.transform, 'nodata': nodata } with rasterio.open(sample_geotiff, 'w', driver='GTiff', **rio_args) as dst: dst.write(sample_data, 1) return sample_geotiff, geobox, sample_data
def __call__(self, index, product, time, group_by) -> Tile: # Do for a specific poly whose boundary is known output_crs = CRS(self.storage['crs']) filtered_items = [ 'geopolygon', 'lon', 'lat', 'longitude', 'latitude', 'x', 'y' ] filtered_dict = { k: v for k, v in self.input_region.items() if k in filtered_items } if self.feature is not None: filtered_dict['geopolygon'] = self.feature.geopolygon geopoly = filtered_dict['geopolygon'] else: geopoly = query_geopolygon(**self.input_region) dc = Datacube(index=index) datasets = dc.find_datasets(product=product, time=time, group_by=group_by, **filtered_dict) group_by = query_group_by(group_by=group_by) sources = dc.group_datasets(datasets, group_by) output_resolution = [ self.storage['resolution'][dim] for dim in output_crs.dimensions ] geopoly = geopoly.to_crs(output_crs) geobox = GeoBox.from_geopolygon(geopoly, resolution=output_resolution) return Tile(sources, geobox)
def mk_sample_xr_dataset(crs="EPSG:3578", shape=(33, 74), resolution=None, xy=(0, 0), time='2020-02-13T11:12:13.1234567Z', name='band', dtype='int16', nodata=-999, units='1'): """ Note that resolution is in Y,X order to match that of GeoBox. shape (height, width) resolution (y: float, x: float) - in YX, to match GeoBox/shape notation xy (x: float, y: float) -- location of the top-left corner of the top-left pixel in CRS units """ if isinstance(crs, str): crs = CRS(crs) if resolution is None: resolution = (-10, 10) if crs is None or crs.projected else (-0.01, 0.01) t_coords = {} if time is not None: t_coords['time'] = mk_time_coord([time]) transform = Affine.translation(*xy)*Affine.scale(*resolution[::-1]) h, w = shape geobox = GeoBox(w, h, transform, crs) return Datacube.create_storage(t_coords, geobox, [Measurement(name=name, dtype=dtype, nodata=nodata, units=units)])
def compute_native_load_geobox(dst_geobox: GeoBox, ds: Dataset, band: str, buffer: Optional[float] = None) -> GeoBox: """ Compute area of interest for a given Dataset given query. Take native projection and resolution from ``ds, band`` pair and compute region in that projection that fully encloses footprint of the ``dst_geobox`` with some padding. Construct GeoBox that encloses that region fully with resolution/pixel alignment copied from supplied band. :param dst_geobox: :param ds: Sample dataset (only resolution and projection is used, not footprint) :param band: Reference band to use (resolution of output GeoBox will match resolution of this band) :param buffer: Buffer in units of CRS of ``ds`` (meters usually), default is 10 pixels worth """ native: GeoBox = native_geobox(ds, basis=band) if buffer is None: buffer = 10 * cast(float, max(map(abs, native.resolution))) # type: ignore assert native.crs is not None return GeoBox.from_geopolygon( dst_geobox.extent.to_crs(native.crs).buffer(buffer), crs=native.crs, resolution=native.resolution, align=native.alignment, )
def test_read_with_reproject(tmpdir): from datacube.testutils import mk_test_image from datacube.testutils.io import write_gtiff from pathlib import Path pp = Path(str(tmpdir)) xx = mk_test_image(128, 64, nodata=None) assert (xx != -999).all() tile = AlbersGS.tile_geobox((17, -40))[:64, :128] mm = write_gtiff(pp / 'tst-read-with-reproject-128x64-int16.tif', xx, crs=str(tile.crs), resolution=tile.resolution[::-1], offset=tile.transform * (0, 0), nodata=-999) assert mm.gbox == tile def _read(gbox, resampling='nearest', fallback_nodata=None, dst_nodata=-999): with RasterFileDataSource(mm.path, 1, nodata=fallback_nodata).open() as rdr: yy = np.full(gbox.shape, dst_nodata, dtype=rdr.dtype) roi = read_time_slice(rdr, yy, gbox, resampling, dst_nodata) return yy, roi gbox = gbx.pad(mm.gbox, 10) gbox = gbx.zoom_out(gbox, 0.873) yy, roi = _read(gbox) assert roi[0].start > 0 and roi[1].start > 0 assert (yy[0] == -999).all() yy_expect, _ = rio_slurp(mm.path, gbox) np.testing.assert_array_equal(yy, yy_expect) gbox = gbx.zoom_out(mm.gbox[3:-3, 10:-10], 2.1) yy, roi = _read(gbox) assert roi_shape(roi) == gbox.shape assert not (yy == -999).any() gbox = GeoBox.from_geopolygon(mm.gbox.extent.to_crs(epsg3857).buffer(50), resolution=mm.gbox.resolution) assert gbox.extent.contains(mm.gbox.extent.to_crs(epsg3857)) assert gbox.crs != mm.gbox.crs yy, roi = _read(gbox) assert roi[0].start > 0 and roi[1].start > 0 assert (yy[0] == -999).all() gbox = gbx.zoom_out(gbox, 4) yy, roi = _read(gbox, resampling='average') nvalid = (yy != -999).sum() nempty = (yy == -999).sum() assert nvalid > nempty
def _read_from_source(source, dest, dst_transform, dst_nodata, dst_projection, resampling): """ Adapt old signature to new function, so that we can keep old tests at least for now """ H, W = dest.shape gbox = GeoBox(W, H, dst_transform, dst_projection) dest[:] = dst_nodata # new code assumes pre-populated image with source.open() as rdr: read_time_slice(rdr, dest, gbox, resampling=resampling, dst_nodata=dst_nodata)
def test_gbox_tiles(): A = Affine.identity() H, W = (300, 200) h, w = (10, 20) gbox = GeoBox(W, H, A, epsg3857) tt = gbx.GeoboxTiles(gbox, (h, w)) assert tt.shape == (300/10, 200/20) assert tt.base is gbox assert tt[0, 0] == gbox[0:h, 0:w] assert tt[0, 1] == gbox[0:h, w:w+w] assert tt[0, 0] is tt[0, 0] # Should cache exact same object assert tt[4, 1].shape == (h, w) H, W = (11, 22) h, w = (10, 9) gbox = GeoBox(W, H, A, epsg3857) tt = gbx.GeoboxTiles(gbox, (h, w)) assert tt.shape == (2, 3) assert tt[1, 2] == gbox[10:11, 18:22] for idx in [tt.shape, (-1, 0), (0, -1), (-33, 1)]: with pytest.raises(IndexError): tt[idx] with pytest.raises(IndexError): tt.chunk_shape(idx) cc = np.zeros(tt.shape, dtype='int32') for idx in tt.tiles(gbox.extent): cc[idx] += 1 np.testing.assert_array_equal(cc, np.ones(tt.shape)) assert list(tt.tiles(gbox[:h, :w].extent)) == [(0, 0)] (H, W) = (11, 22) (h, w) = (10, 20) tt = gbx.GeoboxTiles(GeoBox(W, H, A, epsg3857), (h, w)) assert tt.chunk_shape((0, 0)) == (h, w) assert tt.chunk_shape((0, 1)) == (h, 2) assert tt.chunk_shape((1, 1)) == (1, 2) assert tt.chunk_shape((1, 0)) == (1, w)
def geobox(self): """Object geobox Returns: datacube.utils.geometry.GeoBox """ return GeoBox(width=self.array.shape[2], height=self.array.shape[1], affine=self.affine, crs=CRS(self.crs))
def test_gen_test_image_xy(): gbox = GeoBox(3, 7, Affine.translation(10, 1000), epsg3857) xy, denorm = gen_test_image_xy(gbox, 'float64') assert xy.dtype == 'float64' assert xy.shape == (2, ) + gbox.shape x, y = denorm(xy) x_, y_ = xy_from_gbox(gbox) np.testing.assert_almost_equal(x, x_) np.testing.assert_almost_equal(y, y_) xy, denorm = gen_test_image_xy(gbox, 'uint16') assert xy.dtype == 'uint16' assert xy.shape == (2, ) + gbox.shape x, y = denorm(xy[0], xy[1]) assert x.shape == xy.shape[1:] assert y.shape == xy.shape[1:] assert x.dtype == 'float64' x_, y_ = xy_from_gbox(gbox) np.testing.assert_almost_equal(x, x_, 4) np.testing.assert_almost_equal(y, y_, 4) for dt in ('int8', np.int16, np.dtype(np.uint64)): xy, _ = gen_test_image_xy(gbox, dt) assert xy.dtype == dt # check no-data xy, denorm = gen_test_image_xy(gbox, 'float32') assert xy.dtype == 'float32' assert xy.shape == (2, ) + gbox.shape xy[0, 0, :] = np.nan xy[1, 1, :] = np.nan xy_ = denorm(xy, nodata=np.nan) assert np.isnan(xy_[:, :2]).all() np.testing.assert_almost_equal(xy_[0][2:], x_[2:], 6) np.testing.assert_almost_equal(xy_[1][2:], y_[2:], 6) xy, denorm = gen_test_image_xy(gbox, 'int16') assert xy.dtype == 'int16' assert xy.shape == (2, ) + gbox.shape xy[0, 0, :] = -999 xy[1, 1, :] = -999 xy_ = denorm(xy, nodata=-999) assert np.isnan(xy_[:, :2]).all() np.testing.assert_almost_equal(xy_[0][2:], x_[2:], 4) np.testing.assert_almost_equal(xy_[1][2:], y_[2:], 4) # call without arguments should return linear mapping A = denorm() assert isinstance(A, Affine)
def custom_native_geobox(ds, measurements=None, basis=None): metadata = ds.metadata_doc['image']['bands']['nbart_swir_3']['info'] geotransform = metadata['geotransform'] crs = CRS( ds.metadata_doc['grid_spatial']['projection']['spatial_reference']) affine = Affine(geotransform[1], 0.0, geotransform[0], 0.0, geotransform[5], geotransform[3]) return GeoBox(width=metadata['width'], height=metadata['height'], affine=affine, crs=crs)
def test_compute_reproject_roi_issue647(): """ In some scenarios non-overlapping geoboxes will result in non-empty `roi_dst` even though `roi_src` is empty. Test this case separately. """ from datacube.utils.geometry import CRS src = GeoBox(10980, 10980, Affine(10, 0, 300000, 0, -10, 5900020), CRS('epsg:32756')) dst = GeoBox(976, 976, Affine(10, 0, 1730240, 0, -10, -4170240), CRS('EPSG:3577')) assert src.extent.overlaps(dst.extent.to_crs(src.crs)) is False rr = compute_reproject_roi(src, dst) assert roi_is_empty(rr.roi_src) assert roi_is_empty(rr.roi_dst)
def compute_native_load_geobox(dst_geobox: GeoBox, ds: Dataset, band: str, buffer: Optional[float] = None): native = native_geobox(ds, basis=band) if buffer is None: buffer = 10*max(map(abs, native.resolution)) return GeoBox.from_geopolygon(dst_geobox.extent.to_crs(native.crs).buffer(buffer), crs=native.crs, resolution=native.resolution, align=native.alignment)
def test_geobox_scale_down(): from datacube.utils.geometry import GeoBox, CRS crs = CRS('EPSG:3857') A = mkA(0, (111.2, 111.2), translation=(125671, 251465)) for s in [2, 3, 4, 8, 13, 16]: gbox = GeoBox(233 * s, 755 * s, A, crs) gbox_ = scaled_down_geobox(gbox, s) assert gbox_.width == 233 assert gbox_.height == 755 assert gbox_.crs is crs assert gbox_.extent.contains(gbox.extent) assert gbox.extent.difference(gbox.extent).area == 0.0 gbox = GeoBox(1, 1, A, crs) for s in [2, 3, 5]: gbox_ = scaled_down_geobox(gbox, 3) assert gbox_.shape == (1, 1) assert gbox_.crs is crs assert gbox_.extent.contains(gbox.extent)
def test_compute_reproject_roi_issue1047(): """ `compute_reproject_roi(geobox, geobox[roi])` sometimes returns `src_roi != roi`, when `geobox` has (1) tiny pixels and (2) oddly sized `alignment`. Test this issue is resolved. """ geobox = GeoBox(3000, 3000, Affine(0.00027778, 0.0, 148.72673054908861, 0.0, -0.00027778, -34.98825802556622), "EPSG:4326") src_roi = np.s_[2800:2810, 10:30] rr = compute_reproject_roi(geobox, geobox[src_roi]) assert rr.is_st is True assert rr.roi_src == src_roi assert rr.roi_dst == np.s_[0:10, 0:20]
def gbox_reproject( geobox: GeoBox, crs: SomeCRS, resolution: Optional[Tuple[int, int]] = None, pad: int = 0, pad_wh: Union[int, Tuple[int, int]] = 16, ) -> GeoBox: """ Compute GeoBox in a given projection that fully encloses footprint of the source GeoBox. :param geobox: Source GeoBox :param crs: CRS of the output GeoBox :param resolution: Desired output resolution (defaults to source resolution, if source and destination projections share common units) :param pad: Padding in pixels of output GeoBox :param pad_wh: Expand output GeoBox some more such that (W%pad_wh) == 0 and (H%pad_wh) == 0 """ from datacube.utils.geometry import gbox crs = _norm_crs_or_error(crs) if resolution is None: if geobox.crs.units == crs.units: resolution = geobox.resolution else: raise NotImplementedError( "Source and destination projections have different units: have to supply desired output resolution" ) out = GeoBox.from_geopolygon(geobox.extent, resolution, crs) if pad > 0: out = gbox.pad(out, pad) if pad_wh: if isinstance(pad_wh, int): pad_wh = max(1, pad_wh) pad_wh = (pad_wh, pad_wh) out = gbox.pad_wh(out, *pad_wh) return out
def load_layer(self, geobox, _): import rasterio with rasterio.open(self.path) as src: assert src.indexes == (1,) # only support single-band if geobox is None: from datacube.utils.geometry import GeoBox, CRS geobox = GeoBox(src.width, src.height, src.affine, CRS(src.crs.wkt)) array = src.read(1) else: band = rasterio.band(src, 1) array = np.empty((geobox.height, geobox.width), dtype = band.dtype) rasterio.warp.reproject(source=band, destination=array, dst_crs=geobox.crs.crs_str, dst_transform=geobox.affine, dst_nodata=None) return array
def test_can_paste(): src = AlbersGS.tile_geobox((17, -40)) def check_true(dst, **kwargs): ok, reason = can_paste(compute_reproject_roi(src, dst), **kwargs) if not ok: assert ok is True, reason def check_false(dst, **kwargs): ok, reason = can_paste(compute_reproject_roi(src, dst), **kwargs) if ok: assert ok is False, "Expected can_paste to return False, but got True" check_true(gbx.pad(src, 100)) check_true(src[:10, :10]) check_true(gbx.translate_pix(src, 0.1, 0.3), ttol=0.5) check_true(gbx.translate_pix(src, 3, -4)) check_true(gbx.flipx(src)) check_true(gbx.flipy(src)) check_true(gbx.flipx(gbx.flipy(src))) check_true(gbx.zoom_out(src, 2)) check_true(gbx.zoom_out(src, 4)) check_true(gbx.zoom_out(src[:9, :9], 3)) # Check False code paths dst = GeoBox.from_geopolygon(src.extent.to_crs(epsg3857).buffer(10), resolution=src.resolution) check_false(dst) # non ST check_false(gbx.affine_transform_pix(src, Affine.rotation(1))) # non ST check_false(gbx.zoom_out(src, 1.9)) # non integer scale check_false(gbx.affine_transform_pix(src, Affine.scale(1, 2))) # sx != sy dst = gbx.translate_pix(src, -1, -1) dst = gbx.zoom_out(dst, 2) check_false(dst) # src_roi doesn't align for scale check_false(dst, stol=0.7) # src_roi/scale != dst_roi check_false(gbx.translate_pix(src, 0.3, 0.4)) # sub-pixel translation
def scaled_down_geobox(src_geobox, scaler: int): """Given a source geobox and integer scaler compute geobox of a scaled down image. Output geobox will be padded when shape is not a multiple of scaler. Example: 5x4, scaler=2 -> 3x2 NOTE: here we assume that pixel coordinates are 0,0 at the top-left corner of a top-left pixel. """ from datacube.utils.geometry import GeoBox assert scaler > 1 H, W = [X//scaler + (1 if X % scaler else 0) for X in src_geobox.shape] # Since 0,0 is at the corner of a pixel, not center, there is no # translation between pixel plane coords due to scaling A = src_geobox.transform * Affine.scale(scaler, scaler) return GeoBox(W, H, A, src_geobox.crs)
def test_xy_from_geobox(): gbox = GeoBox(3, 7, Affine.translation(10, 1000), epsg3857) xx, yy = xy_from_gbox(gbox) assert xx.shape == gbox.shape assert yy.shape == gbox.shape assert (xx[:, 0] == 10.5).all() assert (xx[:, 1] == 11.5).all() assert (yy[0, :] == 1000.5).all() assert (yy[6, :] == 1006.5).all() xx_, yy_, A = xy_norm(xx, yy) assert xx_.shape == xx.shape assert yy_.shape == yy.shape np.testing.assert_almost_equal((xx_.min(), xx_.max()), (0, 1)) np.testing.assert_almost_equal((yy_.min(), yy_.max()), (0, 1)) assert (xx_[0] - xx_[1]).sum() != 0 assert (xx_[:, 0] - xx_[:, 1]).sum() != 0 XX, YY = apply_affine(A, xx_, yy_) np.testing.assert_array_almost_equal(xx, XX) np.testing.assert_array_almost_equal(yy, YY)
def make_sample_geotiff(tmpdir): """ Make a sample geotiff, filled with random data, and twice as tall as it is wide. """ sample_geotiff = str(tmpdir.mkdir('tiffs').join('sample.tif')) geobox = GeoBox(100, 200, affine=Affine(25.0, 0.0, 0, 0.0, -25.0, 0), crs=CRS('EPSG:3577')) sample_data = np.random.randint(10000, size=geobox.shape, dtype='int16') rio_args = { 'height': geobox.height, 'width': geobox.width, 'count': 1, 'dtype': 'int16', 'crs': 'EPSG:3577', 'transform': geobox.transform, 'nodata': -999 } with rasterio.open(sample_geotiff, 'w', driver='GTiff', **rio_args) as dst: dst.write(sample_data, 1) return sample_geotiff, geobox, sample_data
def web_geobox(zoom, tx, ty, tile_size=256): """Construct geobox for a given web-tile. Tile indexes should be the same as google maps. http://www.maptiler.org/google-maps-coordinates-tile-bounds-projection/ """ from datacube.utils.geometry import CRS, GeoBox from math import pi R = 6378137 origin = pi * R res0 = 2 * pi * R / tile_size res = res0*(2**(-zoom)) tsz = 2 * pi * R * (2**(-zoom)) # res*tile_size # maps pixel coord to meters in EPSG:3857 # transform = Affine(res, 0, tx*tsz - origin, 0, -res, origin - ty*tsz) return GeoBox(tile_size, tile_size, transform, CRS('epsg:3857'))
def test_geobox_xr_coords(): A = mkA(0, scale=(10, -10), translation=(-48800, -2983006)) w, h = 512, 256 gbox = GeoBox(w, h, A, epsg3577) cc = gbox.xr_coords() assert list(cc) == ['y', 'x'] assert cc['y'].shape == (gbox.shape[0], ) assert cc['x'].shape == (gbox.shape[1], ) assert 'crs' in cc['y'].attrs assert 'crs' in cc['x'].attrs cc = gbox.xr_coords(with_crs=True) assert list(cc) == ['y', 'x', 'spatial_ref'] assert cc['spatial_ref'].shape is () assert cc['spatial_ref'].attrs['spatial_ref'] == gbox.crs.wkt assert isinstance(cc['spatial_ref'].attrs['grid_mapping_name'], str) cc = gbox.xr_coords(with_crs='Albers') assert list(cc) == ['y', 'x', 'Albers'] # geographic CRS A = mkA(0, scale=(0.1, -0.1), translation=(10, 30)) gbox = GeoBox(w, h, A, 'epsg:4326') cc = gbox.xr_coords(with_crs=True) assert list(cc) == ['latitude', 'longitude', 'spatial_ref'] assert cc['spatial_ref'].shape is () assert cc['spatial_ref'].attrs['spatial_ref'] == gbox.crs.wkt assert isinstance(cc['spatial_ref'].attrs['grid_mapping_name'], str) # missing CRS for GeoBox gbox = GeoBox(w, h, A, None) cc = gbox.xr_coords(with_crs=True) assert list(cc) == ['y', 'x'] # check CRS without name crs = MagicMock() crs.projected = True crs.wkt = epsg3577.wkt crs.epsg = epsg3577.epsg crs._crs = MagicMock() crs._crs.to_cf.return_value = {} assert _mk_crs_coord(crs).attrs['grid_mapping_name'] == '??'
def test_gbox_ops(): s = GeoBox(1000, 100, Affine(10, 0, 12340, 0, -10, 316770), epsg3857) assert s.shape == (100, 1000) d = gbx.flipy(s) assert d.shape == s.shape assert d.crs is s.crs assert d.resolution == (-s.resolution[0], s.resolution[1]) assert d.extent.contains(s.extent) with pytest.raises(ValueError): # flipped grid (s | d) with pytest.raises(ValueError): # flipped grid (s & d) d = gbx.flipx(s) assert d.shape == s.shape assert d.crs is s.crs assert d.resolution == (s.resolution[0], -s.resolution[1]) assert d.extent.contains(s.extent) assert gbx.flipy(gbx.flipy(s)).affine == s.affine assert gbx.flipx(gbx.flipx(s)).affine == s.affine d = gbx.zoom_out(s, 2) assert d.shape == (50, 500) assert d.crs is s.crs assert d.extent.contains(s.extent) assert d.resolution == (s.resolution[0]*2, s.resolution[1]*2) d = gbx.zoom_out(s, 2*max(s.shape)) assert d.shape == (1, 1) assert d.crs is s.crs assert d.extent.contains(s.extent) d = gbx.zoom_out(s, 1.33719) assert d.crs is s.crs assert d.extent.contains(s.extent) assert all(ds < ss for ds, ss in zip(d.shape, s.shape)) with pytest.raises(ValueError): # lower resolution grid (s | d) with pytest.raises(ValueError): # lower resolution grid (s & d) d = gbx.zoom_to(s, s.shape) assert d == s d = gbx.zoom_to(s, (1, 3)) assert d.shape == (1, 3) assert d.extent == s.extent d = gbx.zoom_to(s, (10000, 10000)) assert d.shape == (10000, 10000) assert d.extent == s.extent d = gbx.pad(s, 1) assert d.crs is s.crs assert d.resolution == s.resolution assert d.extent.contains(s.extent) assert s.extent.contains(d.extent) is False assert d[1:-1, 1:-1].affine == s.affine assert d[1:-1, 1:-1].shape == s.shape assert d == (s | d) assert s == (s & d) d = gbx.pad_wh(s, 10) assert d == s d = gbx.pad_wh(s, 100, 8) assert d.width == s.width assert d.height % 8 == 0 assert 0 < d.height - s.height < 8 assert d.affine == s.affine assert d.crs is s.crs d = gbx.pad_wh(s, 13, 17) assert d.affine == s.affine assert d.crs is s.crs assert d.height % 17 == 0 assert d.width % 13 == 0 assert 0 < d.height - s.height < 17 assert 0 < d.width - s.width < 13 d = gbx.translate_pix(s, 1, 2) assert d.crs is s.crs assert d.resolution == s.resolution assert d.extent != s.extent assert s[2:3, 1:2].extent == d[:1, :1].extent d = gbx.translate_pix(s, -10, -2) assert d.crs is s.crs assert d.resolution == s.resolution assert d.extent != s.extent assert s[:1, :1].extent == d[2:3, 10:11].extent d = gbx.translate_pix(s, 0.1, 0) assert d.crs is s.crs assert d.shape == s.shape assert d.resolution == s.resolution assert d.extent != s.extent assert d.extent.contains(s[:, 1:].extent) d = gbx.translate_pix(s, 0, -0.5) assert d.crs is s.crs assert d.shape == s.shape assert d.resolution == s.resolution assert d.extent != s.extent assert s.extent.contains(d[1:, :].extent) d = gbx.affine_transform_pix(s, Affine(1, 0, 0, 0, 1, 0)) assert d.crs is s.crs assert d.shape == s.shape assert d.resolution == s.resolution assert d.extent == s.extent d = gbx.rotate(s, 180) assert d.crs is s.crs assert d.shape == s.shape assert d.extent != s.extent np.testing.assert_almost_equal(d.extent.area, s.extent.area, 1e-5) assert s[49:52, 499:502].extent.contains(d[50:51, 500:501].extent), "Check that center pixel hasn't moved"
def mk_gbox(shape=(2, 2), transform=identity, crs=epsg4326): H, W = shape return GeoBox(W, H, transform, crs)