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_rasterization_function(): sq1 = GeoFeature( GeoVector.from_bounds(xmin=0, ymin=2, xmax=1, ymax=3, crs=WGS84_CRS), {'value': 1.0} ) sq2 = GeoFeature( GeoVector.from_bounds(xmin=1, ymin=0, xmax=3, ymax=2, crs=WGS84_CRS), {'value': 2.0} ) fc = FeatureCollection([sq1, sq2]) def func(feat): return feat['value'] expected_image = np.ma.masked_array( [[ [1.0, 0.0, 0.0], [0.0, 2.0, 2.0], [0.0, 2.0, 2.0] ]], [ [False, True, True], [True, False, False], [True, False, False], ] ) result = fc.rasterize(1.0, fill_value=func, crs=WGS84_CRS, dtype=np.float32) assert_array_equal(result.image.mask, expected_image.mask) assert_array_equal(result.image.data, expected_image.data)
def test_geovector_from_bounds_has_proper_shape(): shape = Polygon([(0, 0), (0, 1), (1, 1), (1, 0), (0, 0)]) gv1 = GeoVector.from_bounds(xmin=0, ymin=0, xmax=1, ymax=1) gv2 = GeoVector.from_bounds(xmin=0, xmax=1, ymin=0, ymax=1) gv3 = GeoVector.from_bounds(xmax=1, ymax=1, xmin=0, ymin=0) assert gv1 == gv2 == gv3 assert gv1.get_shape(gv1.crs) == shape
def test_convex_hull_and_envelope(): fc = FeatureCollection.from_geovectors([ GeoVector.from_bounds(xmin=0, ymin=0, xmax=1, ymax=1), GeoVector.from_bounds(xmin=1, ymin=0, xmax=2, ymax=1), GeoVector.from_bounds(xmin=1, ymin=1, xmax=2, ymax=2), ]) expected_convex_hull = GeoVector(Polygon([(0, 0), (2, 0), (2, 2), (1, 2), (0, 1), (0, 0)])) expected_envelope = GeoVector.from_bounds(xmin=0, ymin=0, xmax=2, ymax=2) assert fc.convex_hull.equals(expected_convex_hull) assert fc.envelope.equals(expected_envelope)
def test_collection_add(): gv1 = GeoVector.from_bounds(xmin=0, ymin=0, xmax=2, ymax=1) gv2 = GeoVector.from_bounds(xmin=1, ymin=0, xmax=3, ymax=1) gv3 = GeoVector.from_bounds(xmin=2, ymin=0, xmax=4, ymax=1) gv4 = GeoVector.from_bounds(xmin=3, ymin=0, xmax=5, ymax=1) assert (gv1 + gv2) == FeatureCollection.from_geovectors([gv1, gv2]) assert (gv1 + gv2 + gv3) == FeatureCollection.from_geovectors( [gv1, gv2, gv3]) assert (((gv1 + gv2) + (gv3 + gv4)) == (gv1 + gv2 + gv3 + gv4) == FeatureCollection.from_geovectors([gv1, gv2, gv3, gv4]))
def test_get_bounding_box(): src_crs = CRS(init='epsg:4326') dst_crs = CRS(init='epsg:32718') src_bounds = dict(xmin=-73.309037, ymin=-40.665865, xmax=-72.723835, ymax=-40.026434) gv = GeoVector.from_bounds(crs=src_crs, **src_bounds) bounds = transform_bounds(src_crs=src_crs, dst_crs=dst_crs, left=src_bounds['xmin'], bottom=src_bounds['ymin'], right=src_bounds['xmax'], top=src_bounds['ymax']) assert gv.get_bounding_box(dst_crs).almost_equals( GeoVector.from_bounds(*bounds, crs=dst_crs))
def test_groupby_with_dissolve(): fc = FeatureCollection([ GeoFeature(GeoVector.from_bounds(xmin=0, ymin=0, xmax=2, ymax=1, crs=DEFAULT_CRS), {'prop1': 'a', 'b': 1}), GeoFeature(GeoVector.from_bounds(xmin=1, ymin=0, xmax=3, ymax=1, crs=DEFAULT_CRS), {'prop1': 'a', 'b': 2}), GeoFeature(GeoVector.from_bounds(xmin=0, ymin=0, xmax=2, ymax=1, crs=DEFAULT_CRS), {'prop1': 'b', 'b': 3}), ]) expected_result = FeatureCollection([ GeoFeature(GeoVector.from_bounds(xmin=0, ymin=0, xmax=3, ymax=1, crs=DEFAULT_CRS), {'b': 3}), GeoFeature(GeoVector.from_bounds(xmin=0, ymin=0, xmax=2, ymax=1, crs=DEFAULT_CRS), {'b': 3}), ]) assert fc.dissolve('prop1', sum) == fc.groupby('prop1').agg(partial(dissolve, aggfunc=sum)) == expected_result
def test_rasterization_function_raises_error_if_no_dtype_is_given(): sq1 = GeoFeature( GeoVector.from_bounds(xmin=0, ymin=2, xmax=1, ymax=3, crs=WGS84_CRS), {'value': 1.0}) sq2 = GeoFeature( GeoVector.from_bounds(xmin=1, ymin=0, xmax=3, ymax=2, crs=WGS84_CRS), {'value': 2.0}) fc = FeatureCollection([sq1, sq2]) with pytest.raises(ValueError) as excinfo: fc.rasterize(1.0, fill_value=lambda x: 1, crs=WGS84_CRS) assert "dtype must be specified for multivalue rasterization" in excinfo.exconly( )
def test_delegated_binary_predicates_for_impossible_transformations( predicate_name): vector_1 = GeoVector.from_bounds(-180, -90, 180, 90, crs=CRS(init='epsg:4326')) vector_2 = GeoVector.from_bounds(-1000, -1000, 1000, 1000, crs=CRS(init='epsg:3857')) assert getattr(vector_2, predicate_name)(vector_1)
def test_geovector_to_from_geojson(): gv = GeoVector.from_bounds(xmin=0, ymin=0, xmax=1, ymax=1) with tempfile.NamedTemporaryFile('w') as fp: gv.to_geojson(fp.name) assert GeoVector.from_geojson(fp.name) == gv
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_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_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_reproject_respects_units(): # See https://publicgitlab.satellogic.com/telluric/telluric/issues/87 # https://epsg.io/32038 crs = { 'proj': 'lcc', 'lat_0': 31.66666666666667, 'units': 'us-ft', 'x_0': 609601.2192024384, 'lat_2': 33.96666666666667, 'datum': 'NAD27', 'no_defs': True, 'y_0': 0, 'lon_0': -97.5, 'lat_1': 32.13333333333333 } gv = GeoVector.from_bounds( xmin=-1000, xmax=10000, ymin=-10000, ymax=10000, crs=crs ) # Obtained from geojson.io expected_gv = GeoVector( Polygon([ [-103.9206624695924, 31.47120806441344], [-103.92458240704434, 31.52607263749016], [-103.88935335998022, 31.527915387915815], [-103.88545482875507, 31.4730496225029], [-103.9206624695924, 31.47120806441344] ]), WGS84_CRS ) # Equality fails on the last decimal places depending on the platform # assert gv.reproject(WGS84_CRS) == expected_gv assert gv.reproject(WGS84_CRS).equals_exact(expected_gv, 1e-13)
def test_raster_closer_than_resolution_to_roi(): raster_close_to_roi = make_test_raster( 1, [1], height=2255, width=6500, affine=Affine(1.000056241624503, -0.0001677700491717716, 251130.52371896777, -0.00011325628093143738, -1.0000703876618153, 2703061.4308057753), crs=CRS.from_epsg(32613), ) raster_intersecting_roi = make_test_raster( 1, [1], height=3515, width=6497, affine=Affine(1.000063460933417, -2.935588943753421e-05, 250953.40276071787, -3.26265458078499e-05, -1.000053742629815, 2703428.138070052), crs=CRS.from_epsg(32613), ) roi = GeoVector.from_bounds(251726, 2696110, 256422, 2700806, CRS.from_epsg(32613)) merge_all( [raster_close_to_roi, raster_intersecting_roi], roi=roi, dest_resolution=(1, 1), merge_strategy=MergeStrategy.INTERSECTION, )
def test_reproject_respects_units(): # See https://publicgitlab.satellogic.com/telluric/telluric/issues/87 # https://epsg.io/32038 crs = { 'proj': 'lcc', 'lat_0': 31.66666666666667, 'units': 'us-ft', 'x_0': 609601.2192024384, 'lat_2': 33.96666666666667, 'datum': 'NAD27', 'no_defs': True, 'y_0': 0, 'lon_0': -97.5, 'lat_1': 32.13333333333333 } gv = GeoVector.from_bounds(xmin=-1000, xmax=10000, ymin=-10000, ymax=10000, crs=crs) # Obtained from geojson.io expected_gv = GeoVector( Polygon([[-103.9206783868097, 31.47123320311564], [-103.9245979236265, 31.52609724089042], [-103.8893692309087, 31.52793986790464], [-103.8854710802219, 31.47307467582068], [-103.9206783868097, 31.47123320311564]]), WGS84_CRS) # Equality fails on the last decimal places depending on the platform # assert gv.reproject(WGS84_CRS) == expected_gv assert gv.reproject(WGS84_CRS).equals_exact(expected_gv, 1e-13)
def get_tile(self, x, y, z, sort_by=None, desc=False, bands=None): """Generate mercator tile from rasters in FeatureCollection. Parameters ---------- x: int x coordinate of tile y: int y coordinate of tile z: int zoom level sort_by: str attribute in feature to sort by desc: bool True for descending order, False for ascending bands: list list of indices of requested bads, default None which returns all bands Returns ------- GeoRaster2 """ bb = mercantile.xy_bounds(x, y, z) roi = GeoVector.from_bounds(xmin=bb.left, ymin=bb.bottom, xmax=bb.right, ymax=bb.top, crs=WEB_MERCATOR_CRS) filtered_fc = self.filter(roi) def _get_tiled_feature(feature): return feature.get_tiled_feature(x, y, z, bands) with ThreadPoolExecutor(max_workers=MAX_WORKERS) as executer: tiled_features = list( executer.map(_get_tiled_feature, filtered_fc, timeout=CONCURRENCY_TIMEOUT)) # tiled_features can be sort for different merge strategies if sort_by is not None: tiled_features = sorted(tiled_features, reverse=desc, key=lambda f: f[sort_by]) tiles = [f['tile'] for f in tiled_features] if tiles: tile = merge_all(tiles, roi) return tile else: return None
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_rasters_covering_different_overlapping_areas_on_y(): affine_a = Affine.translation(1, 2) * Affine.scale(1, -1) raster_a = make_test_raster(1, [1], height=20, width=20, affine=affine_a) affine_b = Affine.translation(1, -9) * Affine.scale(1, -1) raster_b = make_test_raster(2, [1], height=20, width=20, affine=affine_b) roi = GeoVector.from_bounds(xmin=1, ymin=-29, xmax=21, ymax=2, crs=WEB_MERCATOR_CRS) rasters = [raster_a, raster_b] merged = merge_all(rasters, roi) assert (merged.affine.almost_equals(affine_a)) assert (not merged.image.mask.all()) assert ((merged.image.data[0, 0:20, 0:20] == 1).all()) assert ((merged.image.data[0, 21:30, 0:20] == 2).all())
def test_rasterization_point_single_pixel(): data = np.zeros((5, 5), dtype=np.uint8)[None, :, :] data[0, 2, 2] = 1 mask = ~(data.astype(bool)) expected_image = np.ma.masked_array(data, mask) fc = FeatureCollection.from_geovectors([ GeoVector(Point(2, 2), crs=WEB_MERCATOR_CRS)] ) roi = GeoVector.from_bounds(xmin=0, ymin=0, xmax=5, ymax=5, crs=WEB_MERCATOR_CRS) result = fc.rasterize(1, polygonize_width=1, bounds=roi).image assert_array_equal(result.data, expected_image.data) assert_array_equal(result.mask, expected_image.mask)
def test_rasterization_function_with_empty_collection(): fc = FeatureCollection([]) def func(feat): return feat['value'] expected_image = np.ma.masked_array( [[[0.0, 0.0, 0.0], [0.0, 0.0, 0.0], [0.0, 0.0, 0.0]]], [ [True, True, True], [True, True, True], [True, True, True], ]) bounds = GeoVector.from_bounds(0.0, 0.0, 3.0, 3.0) result = fc.rasterize(1.0, bounds=bounds, fill_value=func, crs=WGS84_CRS, dtype=np.float32) assert_array_equal(result.image.mask, expected_image.mask) assert_array_equal(result.image.data, expected_image.data)
def test_rasters_close_than_resolution_to_roi_2(): one_affine = Affine(1, 0, 598847, 0, -1, 3471062) other_affine = Affine(0.9998121393135052104028, -0.000563382202975665213, 596893.24732190347276628, -0.000249934917683214408, -1.000473374252140335016, 3466367.0648421039804816) one = make_test_raster(1, [1], height=4696, width=4696, affine=one_affine, crs=CRS.from_epsg(32641)) other = make_test_raster(2, [1], height=2616, width=5402, affine=other_affine, crs=CRS.from_epsg(32641)) roi = GeoVector.from_bounds(598847.0000000002, 3466365.999999999, 603542.9999999995, 3471062, crs=one.crs) merged = merge_all([one, other], dest_resolution=(1, 1), roi=roi) assert merged == one
def test_geovector_from_bounds_no_keyword_arguments_raises_typeerror(): with pytest.raises(TypeError): GeoVector.from_bounds(0, 0, 1, 1)
def test_geovector_quick_operations(): gv1 = GeoVector.from_bounds(xmin=0, ymin=0, xmax=2, ymax=1) gv2 = GeoVector.from_bounds(xmin=1, ymin=0, xmax=3, ymax=1) assert (gv1 | gv2) == GeoVector.from_bounds(xmin=0, ymin=0, xmax=3, ymax=1) assert (gv1 & gv2) == GeoVector.from_bounds(xmin=1, ymin=0, xmax=2, ymax=1)
def test_feature_collection_from_vectors_is_not_from_rasters(): gv1 = GeoVector.from_bounds(xmin=0, ymin=0, xmax=2, ymax=1) gv2 = GeoVector.from_bounds(xmin=1, ymin=0, xmax=3, ymax=1) fc = FeatureCollection.from_geovectors([gv1, gv2]) assert not fc.is_rasters_collection()
def test_geovector_copy(): gv1 = GeoVector.from_bounds(xmin=0, ymin=0, xmax=2, ymax=1) gv1_copy = gv1.copy() assert gv1 == gv1_copy assert id(gv1) != id(gv1_copy)