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_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 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_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_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
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 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 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_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_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_astype_float32_to_uint8_conversion(): with pytest.warns(GeoRaster2Warning): raster_uint8 = some_float32_raster.astype(np.uint8) expected_raster_uint8 = GeoRaster2(image=np.array([ [[0, 51], [102, 153]], [[178, 204], [229, 255]]], dtype=np.uint8), affine=some_float32_raster.affine, crs=some_float32_raster.crs, nodata=None) assert raster_uint8 == expected_raster_uint8
def test_astype_float32_to_uint8_conversion_with_out_range(): with pytest.warns(GeoRaster2Warning): raster_uint8 = some_float32_raster.astype(np.uint8, out_range=(100, 200)) expected_raster_uint8 = GeoRaster2(image=np.array([ [[100, 120], [140, 160]], [[169, 180], [189, 200]]], dtype=np.uint8), affine=some_float32_raster.affine, crs=some_float32_raster.crs, nodata=None) assert raster_uint8 == expected_raster_uint8
def test_astype_float32_to_float16_conversion(): with pytest.warns(GeoRaster2Warning): raster_float16 = some_float32_raster.astype(np.float16, out_range=(0.0, 10.0)) expected_raster_float16 = GeoRaster2(image=np.array([ [[0.0, 2.0], [4.0, 6.0]], [[7.0, 8.0], [9.0, 10.0]]], dtype=np.float16), affine=some_float32_raster.affine, crs=some_float32_raster.crs, nodata=None) assert raster_float16 == expected_raster_float16
def test_astype_float32_to_int8_conversion_with_clip_negative(): with pytest.warns(GeoRaster2Warning): raster_uint8 = some_float32_raster.astype(np.int8, clip_negative=True) expected_raster_uint8 = GeoRaster2(image=np.array([ [[0, 25], [50, 76]], [[88, 101], [114, 127]]], dtype=np.int8), affine=some_float32_raster.affine, crs=some_float32_raster.crs, nodata=None) assert raster_uint8 == expected_raster_uint8
def test_astype_float32_to_int8_conversion(): with pytest.warns(GeoRaster2Warning): raster_uint8 = some_float32_raster.astype(np.int8) expected_raster_uint8 = GeoRaster2(image=np.array([ [[-128, -76], [-25, 25]], [[50, 76], [101, 127]]], dtype=np.int8), affine=some_float32_raster.affine, crs=some_float32_raster.crs, nodata=None) assert raster_uint8 == expected_raster_uint8
def test_astype_uint8_to_float32_conversion(): raster_float32 = some_raster.astype(np.float32, out_range=(0, 1)) expected_raster_float32 = GeoRaster2(image=np.ma.array( np.array([ [0.0, 0.003921568859368563, 0.007843137718737125], [0.0117647061124444, 0.01568627543747425, 1.0] ], dtype=np.float32), mask=some_raster.image.mask), affine=some_float32_raster.affine, crs=some_float32_raster.crs, nodata=None) assert raster_float32 == expected_raster_float32
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_astype_float32_to_float16_conversion_without_stretching(): raster_float16 = some_float32_raster.astype(np.float16, in_range=None, out_range=None) expected_raster_float16 = GeoRaster2(some_float32_array.astype(np.float16), affine=some_float32_raster.affine, crs=some_float32_raster.crs, nodata=None) assert raster_float16 == expected_raster_float16
def test_astype_uint8_to_float32_conversion_with_custom_in_range(): raster_float32 = some_raster.astype(np.float32, in_range=('min', 'max'), out_range=(0, 1)) expected_raster_float32 = GeoRaster2(image=np.ma.array( np.array([ [0.0, 0.25, 0.5], [0.75, 1.0, 1.0] ], dtype=np.float32), mask=some_raster.image.mask), affine=some_float32_raster.affine, crs=some_float32_raster.crs, nodata=None) assert raster_float32 == expected_raster_float32 raster_float32 = some_raster.astype(np.float32, in_range=(2, 4), out_range=(0, 1)) expected_raster_float32 = GeoRaster2(image=np.ma.array( np.array([ [0.0, 0.0, 0.0], [0.5, 1.0, 1.0] ], dtype=np.float32), mask=some_raster.image.mask), affine=some_float32_raster.affine, crs=some_float32_raster.crs, nodata=None) assert raster_float32 == expected_raster_float32
def test_merge_all_non_overlapping_covers_all(): # 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_image = np.ma.masked_array( [[[100, 200], [100, 200]], [[110, 210], [110, 210]]], False) result = merge_all([rs1, rs2, rs3, rs4], rs1.footprint()).limit_to_bands(['red', 'green']) assert_array_equal(result.image.data, expected_image.data) assert_array_equal(result.image.mask, expected_image.mask)
def make_test_raster(value=0, band_names=[], height=3, width=4, dtype=np.uint16, crs=constants.WEB_MERCATOR_CRS, affine=None): if affine is None: affine = Affine.translation(10, 12) * Affine.scale(1, -1) shape = [len(band_names), height, width] array = np.full(shape, value, dtype=dtype) mask = np.full(shape, False, dtype=np.bool) image = np.ma.array(data=array, mask=mask) raster = GeoRaster2(image=image, affine=affine, crs=crs, band_names=band_names) return raster
def test_georaster_save_unity_affine_emits_warning(recwarn): shape = (1, 500, 500) arr = np.ones(shape) aff = Affine.scale(1, -1) raster = GeoRaster2(image=arr, affine=aff, crs=WEB_MERCATOR_CRS) with NamedTemporaryFile(suffix=".tif") as fp: raster.save(fp.name) w = recwarn.pop(NotGeoreferencedWarning) assert "The given matrix is equal to Affine.identity or its flipped counterpart." in str(w.message)
def rasterize(shapes, crs, bounds, dest_resolution, fill_value=None, nodata_value=None, band_names=None, dtype=np.uint8): if fill_value is None: fill_value = FILL_VALUE if nodata_value is None: nodata_value = NODATA_VALUE if band_names is None: band_names = [1] # Affine transformation minx, miny, maxx, maxy = bounds.bounds affine = Affine.translation(minx, maxy) * Affine.scale( dest_resolution, -dest_resolution) # Compute size from scale dx = maxx - minx dy = maxy - miny sx = round(dx / dest_resolution) sy = round(dy / dest_resolution) sz = len(band_names) if sx == 0 or sy == 0: raise ScaleError("Scale is too coarse, decrease it for a bigger image") try: if not shapes: image = np.full((sz, sy, sx), nodata_value, dtype=dtype) else: image = features.rasterize(shapes, out_shape=(sy, sx), fill=nodata_value, transform=affine, default_value=fill_value) except TransformNotInvertibleError: raise ScaleError("Scale is too coarse, decrease it for a bigger image") except (MemoryError, ValueError, CPLE_OutOfMemoryError): raise ScaleError("Scale is too fine, increase it for a smaller image") else: return GeoRaster2(image, affine, crs, nodata=nodata_value, band_names=band_names)
def test_save_temporary(): with NamedTemporaryFile(suffix='.tif', delete=False) as src, NamedTemporaryFile(suffix='.tif') as dst: # create valid raster file some_raster.save(src.name) raster = GeoRaster2(filename=src.name, temporary=True) assert raster._filename == src.name assert raster._temporary raster.save(dst.name) assert not raster._temporary assert raster._filename is None # temporary file is removed assert not os.path.isfile(src.name)
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 make_test_raster(value=0, band_names=[], height=3, width=4, dtype=np.uint16, crs=WEB_MERCATOR_CRS, affine=Affine.identity()): shape = [len(band_names), height, width] array = np.full(shape, value, dtype=dtype) mask = np.full(shape, False, dtype=np.bool) image = np.ma.array(data=array, mask=mask) raster = GeoRaster2(image=image, affine=affine, crs=crs, band_names=band_names) return raster
def test_construction(): # test image - different formats yield identical rasters: raster_masked_2d = GeoRaster2(some_image_2d, affine=some_affine, crs=some_crs) raster_masked_3d = GeoRaster2(some_image_3d, affine=some_affine, crs=some_crs) raster_masked_array = GeoRaster2(some_array, nodata=5, affine=some_affine, crs=some_crs) assert raster_masked_2d == raster_masked_3d assert raster_masked_2d == raster_masked_array assert np.array_equal(raster_masked_2d.image, some_image_3d) assert raster_masked_2d.affine == some_affine assert raster_masked_2d.crs == some_crs assert raster_masked_2d.dtype == some_image_2d.dtype # test bandnames: assert GeoRaster2(some_image_2d, affine=some_affine, crs=some_crs, band_names='gray').band_names == ['gray'] assert GeoRaster2(some_image_2d, affine=some_affine, crs=some_crs, band_names=['gray']).band_names == ['gray'] assert GeoRaster2(some_image_2d, affine=some_affine, crs=some_crs).band_names == [0] with pytest.raises(GeoRaster2Error): GeoRaster2(some_image_2d, affine=some_affine, crs=some_crs, band_names=['gray', 'red'])
def test_destructor(): with NamedTemporaryFile(suffix='.tif', delete=False) as src: raster = GeoRaster2(filename=src.name, temporary=True) raster = some_raster assert not os.path.isfile(src.name)
from telluric.vectors import GeoVector some_array = np.array([[0, 1, 2], [3, 4, 5]], dtype=np.uint8) some_mask = np.array([[False, False, False], [False, False, True]], dtype=np.bool) some_image_2d = np.ma.array(some_array, mask=some_mask) some_image_3d = np.ma.array(some_array[np.newaxis, :, :], mask=some_mask[np.newaxis, :, :]) some_image_3d_multiband = np.ma.array( np.array([some_array, some_array, some_array]), mask=np.array([some_mask, some_mask, some_mask])) raster_origin = Point(2, 3) some_affine = Affine.translation(raster_origin.x, raster_origin.y) some_crs = {'init': 'epsg:32620'} some_raster = GeoRaster2(some_image_2d, affine=some_affine, crs=some_crs, band_names=['r']) some_raster_multiband = GeoRaster2(some_image_3d_multiband, band_names=['r', 'g', 'b'], affine=some_affine, crs=some_crs) default_factors = [2, 4, 8] def make_test_raster(value=0, band_names=[], height=3, width=4, dtype=np.uint16, crs=WEB_MERCATOR_CRS, affine=Affine.identity()):
line_off=0.0, line_scale=0.0, long_off=0.0, long_scale=0.0, samp_den_coeff=[ 4.0, 7.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 1.0, -2.0, -3.0, 7.0, 1.0, 2.0, 7.0, 5.0, 6.0, 7.0 ], samp_num_coeff=[ 8.0, 9.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 8.0, -2.0, -3.0, 6.0, 1.0, 5.0, 4.0, 5.0, 6.0, 7.0 ], samp_off=50.0, samp_scale=50.0) some_raster = GeoRaster2(some_image_2d, affine=some_affine, crs=some_crs, band_names=['r']) some_raster_alt = GeoRaster2(some_image_2d_alt, affine=some_affine, crs=some_crs, band_names=['r']) some_raster_multiband = GeoRaster2(some_image_3d_multiband, band_names=['r', 'g', 'b'], affine=some_affine, crs=some_crs) default_factors = [2, 4, 8, 16] some_float32_array = np.array( [[[0.0, 0.2], [0.4, 0.6]], [[0.7, 0.8], [0.9, 1.0]]], dtype=np.float32) some_float32_raster = GeoRaster2(some_float32_array, band_names=[1, 2],