def test_merge_does_not_uncover_masked_pixels(): # See https://github.com/satellogic/telluric/issues/65 affine = Affine.translation(0, 2) * Affine.scale(1, -1) rs_a = GeoRaster2( image=np.ma.masked_array( [[[100, 89], [100, 89]], [[110, 99], [110, 99]]], [[[False, True], [False, True]], [[False, True], [False, True]]], dtype=np.uint8), affine=affine, crs=WGS84_CRS, band_names=['red', 'green'], ) rs_b = GeoRaster2( image=np.array([[[0, 210], [0, 210]]], dtype=np.uint8), affine=affine, crs=WGS84_CRS, band_names=['green'], ) expected_image = np.ma.masked_array( [[[100, 89], [100, 89]], [[110, 99], [110, 99]]], [[[False, True], [False, True]], [[False, True], [False, True]]], dtype=np.uint8) result = merge_all([rs_a, rs_b], rs_a.footprint()).limit_to_bands(['red', 'green']) assert_array_equal(np.ma.filled(result.image, 0), np.ma.filled(expected_image, 0)) assert_array_equal(result.image.mask, expected_image.mask)
def test_feature_collection_from_rasters(): rasters_list = [ GeoRaster2.open("./tests/data/raster/overlap1.tif"), GeoRaster2.open("./tests/data/raster/overlap2.tif") ] fc = FeatureCollection.from_georasters(rasters_list) assert fc.is_rasters_collection()
def test_join_use_mask_band(): # https://github.com/OSGeo/gdal/issues/1148 # The test checks the pixel at the coordinate of bottom left corner of the r2 raster is unmasked. r1 = GeoRaster2.open("tests/data/raster/overlap1.tif") r2 = GeoRaster2.open("tests/data/raster/overlap2.tif") joined = join([r1, r2]) assert not joined.get(r2.corners()["bl"]).mask.all()
def test_rpcs_init(): # test rpcs initilization by passing rpcs in different forms rpcs_dict = some_rpcs.to_dict() rpcs_dict = {k.upper(): v for k, v in rpcs_dict.items()} georaster1 = GeoRaster2(some_image_2d, rpcs=some_rpcs) georaster2 = GeoRaster2(some_image_2d, rpcs=rpcs_dict) assert georaster1.rpcs == georaster2.rpcs # rpcs defined as dictionary of str new_rpcs_dict = {} for k, v in rpcs_dict.items(): if isinstance(v, float): new_rpcs_dict[k] = str(v) line_num_coeff = rpcs_dict['LINE_NUM_COEFF'] new_rpcs_dict['LINE_NUM_COEFF'] = ' '.join( str(e) for e in line_num_coeff) line_den_coeff = rpcs_dict['LINE_DEN_COEFF'] new_rpcs_dict['LINE_DEN_COEFF'] = ' '.join( str(e) for e in line_den_coeff) samp_num_coeff = rpcs_dict['SAMP_NUM_COEFF'] new_rpcs_dict['SAMP_NUM_COEFF'] = ' '.join( str(e) for e in samp_num_coeff) samp_den_coeff = rpcs_dict['SAMP_DEN_COEFF'] new_rpcs_dict['SAMP_DEN_COEFF'] = ' '.join( str(e) for e in samp_den_coeff) georaster3 = GeoRaster2(some_image_2d, rpcs=new_rpcs_dict) assert georaster2.rpcs == georaster3.rpcs
def rasters_for_testing_chunks(): rasters = [ GeoRaster2.open("tests/data/raster/overlap2.tif"), GeoRaster2.open("tests/data/raster/overlap2.tif") ] rasters[0].image return rasters
def test_get_tile_merge_tiles(tile): raster1_path = './tests/data/raster/overlap1.tif' raster2_path = './tests/data/raster/overlap2.tif' raster1 = GeoRaster2.open(raster1_path) raster2 = GeoRaster2.open(raster2_path) features = [ GeoFeature(raster1.footprint().reproject(new_crs=WGS84_CRS), {'raster_url': raster1_path, 'created': datetime.now()}), GeoFeature(raster2.footprint().reproject(new_crs=WGS84_CRS), {'raster_url': raster2_path, 'created': datetime.now()}), ] fc = FeatureCollection(features) bounds = mercantile.xy_bounds(*tile) eroi = GeoVector.from_bounds(xmin=bounds.left, xmax=bounds.right, ymin=bounds.bottom, ymax=bounds.top, crs=WEB_MERCATOR_CRS) expected_tile = merge_all([raster1.get_tile(*tile), raster2.get_tile(*tile)], roi=eroi) merged = fc.get_tile(*tile, sort_by='created') if merged is not None: assert merged == expected_tile else: assert expected_tile.image.mask.all() assert (expected_tile.image.data == 0).all()
def test_tags(): with TemporaryDirectory() as folder: path = os.path.join(folder, 'test.tif') some_raster_multiband.save(path, tags={'foo': 'bar'}, factors=default_factors) assert GeoRaster2.tags(path) == {'AREA_OR_POINT': 'Area', 'foo': 'bar', 'telluric_band_names': '["r", "g", "b"]'} # namespace=default assert GeoRaster2.tags(path, 'IMAGE_STRUCTURE') == {'COMPRESSION': 'LZW', 'INTERLEAVE': 'PIXEL'}
def test_crop_boundless_masked(bounds): raster_w_mask = GeoRaster2.open("tests/data/raster/rgb.tif") raster_wo_mask = GeoRaster2.open("tests/data/raster/rgb.jp2") roi = GeoVector(Polygon.from_bounds(*bounds), WEB_MERCATOR_CRS) assert (np.array_equal( raster_w_mask.crop(roi).image.mask, raster_wo_mask.crop(roi).image.mask))
def check_file(filename): try: GeoRaster2.open(filename, lazy_load=False) except (OSError, GeoRaster2IOError): logger.warning(f"Removing file {filename}") os.remove(filename) else: logger.info(f"File {filename} looks correct")
def test_featurecollection_apply_on_rasters_collection(): rasters_list = [ GeoRaster2.open("./tests/data/raster/overlap1.tif"), GeoRaster2.open("./tests/data/raster/overlap2.tif") ] fc = FeatureCollection.from_georasters(rasters_list) new_fc = fc.apply(prop1=3) assert new_fc.is_rasters_collection()
def test_save_preserves_nodata(): expected_nodata = 200 path = '/vsimem/raster_for_test.tif' raster_nodata = GeoRaster2(some_array, nodata=expected_nodata, affine=some_affine, crs=some_crs) raster_nodata.save(path) assert expected_nodata == GeoRaster2.open(path).nodata_value
def test_limit_to_bands_off_memory(): r1 = GeoRaster2.open("tests/data/raster/rgb.tif", band_names=['r', 'g', 'b'], lazy_load=False) r2 = GeoRaster2.open("tests/data/raster/rgb.tif", band_names=['r', 'g', 'b']) assert r1.limit_to_bands(['b', 'r']) == r2.limit_to_bands(['b', 'r']) assert r1.limit_to_bands(['r', 'b']) != r2.limit_to_bands(['b', 'r']) assert r2._image is None
def test_png_thumbnail_has_expected_properties(): raster = GeoRaster2.open("tests/data/raster/rgb.tif") expected_thumbnail = raster.resize(dest_width=512, resampling=Resampling.nearest) result_thumbnail = GeoRaster2.from_bytes( raster.to_png(transparent=True, thumbnail_size=512, resampling=Resampling.nearest, in_range='image'), affine=expected_thumbnail.affine, crs=expected_thumbnail.crs, band_names=expected_thumbnail.band_names ) assert result_thumbnail == expected_thumbnail
def test_png_thumbnail_has_expected_properties(): raster = GeoRaster2.open("tests/data/raster/rgb.tif") expected_thumbnail = raster.resize(dest_width=512, resampling=Resampling.nearest) result_thumbnail = GeoRaster2.from_bytes( raster._repr_png_(), affine=expected_thumbnail.affine, crs=expected_thumbnail.crs, band_names=expected_thumbnail.band_names) assert result_thumbnail == expected_thumbnail
def test_merge_all_non_overlapping_has_correct_metadata(): # See https://github.com/satellogic/telluric/issues/65 affine = Affine.translation(0, 2) * Affine.scale(1, -1) rs1 = GeoRaster2( image=np.array([[[100, 0], [100, 0]]], dtype=np.uint8), affine=affine, crs=WGS84_CRS, band_names=['red'], nodata=0, ) rs2 = GeoRaster2( image=np.array([[[110, 0], [110, 0]]], dtype=np.uint8), affine=affine, crs=WGS84_CRS, band_names=['green'], nodata=0, ) rs3 = GeoRaster2( image=np.array([[[0, 200], [0, 200]]], dtype=np.uint8), affine=affine, crs=WGS84_CRS, band_names=['red'], nodata=0, ) rs4 = GeoRaster2( image=np.array([[[0, 210], [0, 210]]], dtype=np.uint8), affine=affine, crs=WGS84_CRS, band_names=['green'], nodata=0, ) expected_metadata = GeoRaster2(image=np.ma.masked_array([[ [0, 2], [0, 2], ], [ [1, 3], [1, 3], ]], np.ma.nomask), affine=affine, crs=WGS84_CRS, band_names=['red', 'green']) metadata = merge_all([rs1, rs2, rs3, rs4], rs1.footprint(), pixel_strategy=PixelStrategy.INDEX) assert metadata == expected_metadata
def test_empty_from_roi_respects_footprint(): # See https://github.com/satellogic/telluric/issues/39 raster = GeoRaster2.open("tests/data/raster/overlap1.tif") empty = GeoRaster2.empty_from_roi(shape=raster.shape[1:][::-1], ul_corner=(v[0] for v in raster.corner('ul').xy), resolution=raster.res_xy(), crs=raster.crs, band_names=raster.band_names, dtype=raster.dtype) empty_simple = GeoRaster2.empty_from_roi(roi=raster.footprint(), resolution=raster.res_xy(), band_names=raster.band_names, dtype=raster.dtype) assert raster.footprint().almost_equals(empty.footprint()) assert raster.footprint().almost_equals(empty_simple.footprint())
def test_read_write_internal_external_mask(): with TemporaryDirectory() as folder: # internal mask (default) leaves no .msk file: internal_path = os.path.join(folder, 'internal.tif') some_raster_multiband.save(internal_path, factors=default_factors) assert not os.path.exists(internal_path + '.msk') # external mask leaves .msk file: external_path = os.path.join(folder, 'external.tif') some_raster_multiband.save(external_path, GDAL_TIFF_INTERNAL_MASK=False, factors=default_factors) assert os.path.exists(external_path + '.msk') # other than that, both rasters are identical: assert GeoRaster2.open(internal_path) == GeoRaster2.open(external_path)
def test_to_png_from_bytes(): arr = np.array( [np.full((3, 5), 1), np.full((3, 5), 5), np.full((3, 5), 10)], dtype=np.uint8) raster = GeoRaster2(image=arr, affine=Affine.identity(), crs=WEB_MERCATOR_CRS, band_names=['r', 'g', 'b']) png_bytes = raster.to_png() assert raster == GeoRaster2.from_bytes(png_bytes, affine=raster.affine, crs=raster.crs, band_names=raster.band_names)
def test_crop_respects_rounding_precision(): # https://github.com/satellogic/telluric/pull/311 r1 = GeoRaster2.open("tests/data/raster/non_aligned1.tif", lazy_load=False) r2 = GeoRaster2.open("tests/data/raster/non_aligned2.tif", lazy_load=False) roi = GeoVector.from_bounds( 271806.0179640717, 1438839.4164977344, 337216.40041283204, 1519170.2272236191, crs=r1.crs, ) r1_cropped = r1.crop(roi) r2_cropped = r2.crop(roi) assert r1.shape == r2.shape assert r1_cropped.shape == r2_cropped.shape
def test_georaster_save_emits_warning_if_uneven_mask(recwarn): affine = Affine.translation(0, 2) * Affine.scale(1, -1) raster = GeoRaster2( image=np.array([ [ [100, 200], [100, 200] ], [ [110, 0], [110, 0] ] ], dtype=np.uint8), affine=affine, crs=WGS84_CRS, nodata=0 ) orig_mask = raster.image.mask assert not (orig_mask == orig_mask[0]).all() with NamedTemporaryFile(suffix=".tif") as fp: raster.save(fp.name) w = recwarn.pop(GeoRaster2Warning) assert ( "Saving different masks per band is not supported, the union of the masked values will be performed." in str(w.message) )
def test_reproject_rpcs(): # test reprojection when rpcs are defined and input raster has no src_crs # TODO: add smaller test data raster = GeoRaster2.open("tests/data/raster/grayscale.tif") reprojected = raster.reproject(dst_crs=WEB_MERCATOR_CRS, rpcs=raster.rpcs) assert reprojected.shape == (1, 2072, 5241) assert reprojected.mean()[0] == pytest.approx(724.4861459505134, 1e-4)
def test_read_write(): for extension in ['tif', 'png']: with TemporaryDirectory() as folder: path = os.path.join(folder, 'test.%s' % extension) some_raster_multiband.save(path, factors=default_factors) read = GeoRaster2.open(path) assert read == some_raster_multiband
def test_rasterization_of_line_simple(): resolution = 1 pixels_width = 1 line = GeoFeature.from_shape(LineString([(2.5, 0), (2.5, 3)])) roi = GeoVector.from_bounds(xmin=0, ymin=0, xmax=5, ymax=5, crs=DEFAULT_CRS) fc = FeatureCollection([line]) expected_image = np.zeros((5, 5), dtype=np.uint8) expected_image[2:, 2] = 1 expected_affine = Affine(1.0, 0.0, 0.0, 0.0, -1.0, 5.0) expected_crs = DEFAULT_CRS expected_result = GeoRaster2(expected_image, expected_affine, expected_crs, nodata=0) result = fc.rasterize(resolution, polygonize_width=pixels_width, crs=DEFAULT_CRS, bounds=roi) assert result == expected_result
def test_read_non_georeferenced(): crs = CRS(init='epsg:3857') affine = Affine(10.0, 0.0, -6425941.63996855, 0.0, -10.0, -3169315.69478084) raster = GeoRaster2.open('tests/data/raster/no_georef.png', crs=crs, affine=affine, lazy_load=False) assert raster.crs == crs assert raster.affine == affine
def test_blockshapes_for_in_memory_raster(): raster = GeoRaster2(some_image_2d, affine=some_affine, crs=some_crs, band_names=['r']) assert len(raster.blockshapes) == raster.num_bands assert raster.blockshapes == [(raster.height, raster.width)]
def test_rasterization_of_line_has_correct_pixel_width(resolution): xmax, ymax = 11, 5 pixels_width = 1 line = GeoFeature.from_shape( LineString([(xmax / 2, 0), (xmax / 2, ymax * 4 / 5)])) roi = GeoVector.from_bounds(xmin=0, ymin=0, xmax=xmax, ymax=ymax, crs=DEFAULT_CRS) fc = FeatureCollection([line]) expected_image = np.zeros( (int(ymax // resolution), int(xmax // resolution)), dtype=np.uint8) expected_image[int(1 // resolution):, expected_image.shape[1] // 2] = 1 expected_affine = Affine(resolution, 0.0, 0.0, 0.0, -resolution, 5.0) expected_crs = DEFAULT_CRS expected_result = GeoRaster2(expected_image, expected_affine, expected_crs, nodata=0) result = fc.rasterize(resolution, polygonize_width=pixels_width, crs=DEFAULT_CRS, bounds=roi) assert result == expected_result
def black_and_white_raster(band_names=[], height=10, width=10, dtype=np.uint16, crs=WEB_MERCATOR_CRS, affine=None): if affine is None: eps = 1e-100 affine = Affine.translation(10, 12) * Affine.scale(1, -1) bands_num = len(band_names) shape = [bands_num, height, width] array = np.zeros(shape, dtype=dtype) mask = np.full(shape, False, dtype=np.bool) val = 0 for i in range(height): for j in range(width): for z in range(bands_num): array[z, i, j] = val val = 1 - val image = np.ma.array(data=array, mask=mask) raster = GeoRaster2(image=image, affine=affine, crs=crs, band_names=band_names) return raster
def test_rasterization_function_user_dtype(fill_value, dtype): resolution = 1 line = GeoVector.from_bounds(xmin=2, ymin=0, xmax=3, ymax=3, crs=DEFAULT_CRS) roi = GeoVector.from_bounds(xmin=0, ymin=0, xmax=5, ymax=5, crs=DEFAULT_CRS) expected_data = np.zeros((5, 5), dtype=dtype) expected_data[2:, 2] = fill_value expected_mask = np.ones((5, 5), dtype=bool) expected_mask[2:, 2] = False expected_image = np.ma.masked_array( expected_data, expected_mask ) expected_affine = Affine(1.0, 0.0, 0.0, 0.0, -1.0, 5.0) expected_crs = DEFAULT_CRS expected_result = GeoRaster2(expected_image, expected_affine, expected_crs) result = rasterize([line.get_shape(DEFAULT_CRS)], DEFAULT_CRS, roi.get_shape(DEFAULT_CRS), resolution, fill_value=fill_value, dtype=dtype) assert result == expected_result
def test_empty_raster_from_roi_affine_3_bands_high(): affine = Affine.translation(10, 12) * Affine.scale(2, -2) raster = make_test_raster(88, [1, 3, 2], affine=affine, height=1301, width=4) empty = GeoRaster2.empty_from_roi(band_names=raster.band_names, roi=raster.footprint(), resolution=2) assert(affine.almost_equals(empty.affine)) assert(raster.crs == empty.crs) assert(raster.shape == empty.shape)
def test_astype_float32_to_uint8_conversion_with_in_range(): raster_uint8 = some_float32_raster.astype(np.uint8, in_range=(0.5, 1.0)) expected_raster_uint8 = GeoRaster2(image=np.array([ [[0, 0], [0, 51]], [[101, 153], [203, 255]]], dtype=np.uint8), affine=some_float32_raster.affine, crs=some_float32_raster.crs, nodata=None) assert raster_uint8 == expected_raster_uint8