def test_point_snapping_break_ties(self): """DelineateIt: distance ties are broken using flow accumulation.""" from natcap.invest.delineateit import delineateit srs = osr.SpatialReference() srs.ImportFromEPSG(32731) # WGS84/UTM zone 31s wkt = srs.ExportToWkt() # need stream layer, points stream_matrix = numpy.array( [[0, 1, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0], [0, 1, 1, 1, 1, 1], [0, 1, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0]], dtype=numpy.int8) stream_raster_path = os.path.join(self.workspace_dir, 'streams.tif') flow_accum_array = numpy.array( [[1, 5, 1, 1, 1, 1], [1, 5, 1, 1, 1, 1], [1, 5, 1, 1, 1, 1], [1, 5, 1, 1, 1, 1], [1, 5, 9, 9, 9, 9], [1, 4, 1, 1, 1, 1], [1, 4, 1, 1, 1, 1]], dtype=numpy.int8) flow_accum_path = os.path.join(self.workspace_dir, 'flow_accum.tif') pygeoprocessing.numpy_array_to_raster(stream_matrix, 255, (2, -2), (2, -2), wkt, stream_raster_path) pygeoprocessing.numpy_array_to_raster(flow_accum_array, -1, (2, -2), (2, -2), wkt, flow_accum_path) source_points_path = os.path.join(self.workspace_dir, 'source_features.geojson') source_features = [Point(9, -7)] # equidistant from two streams pygeoprocessing.shapely_geometry_to_vector( source_features, source_points_path, wkt, 'GeoJSON', ogr_geom_type=ogr.wkbUnknown) snapped_points_path = os.path.join(self.workspace_dir, 'snapped_points.gpkg') snap_distance = 10 # large enough to get multiple streams per point. delineateit.snap_points_to_nearest_stream(source_points_path, stream_raster_path, flow_accum_path, snap_distance, snapped_points_path) snapped_points_vector = gdal.OpenEx(snapped_points_path, gdal.OF_VECTOR) snapped_points_layer = snapped_points_vector.GetLayer() # should snap to stream point [4, 3] in the array above # if not considering flow accumulation, it would snap to the # nearest stream point found first in the array, at [2, 1] points = [ shapely.wkb.loads(bytes(feature.GetGeometryRef().ExportToWkb())) for feature in snapped_points_layer ] self.assertEqual(len(points), 1) self.assertEqual((points[0].x, points[0].y), (9, -11))
def test_clip_vector_by_vector_no_intersection(self): """WaveEnergy: testing '_clip_vector_by_vector' w/ no intersection.""" from natcap.invest import wave_energy srs = osr.SpatialReference() srs.ImportFromEPSG(3157) projection_wkt = srs.ExportToWkt() origin = (443723.127327877911739, 4956546.905980412848294) pos_x = origin[0] pos_y = origin[1] fields_pt = {'id': ogr.OFTInteger, 'myattr': ogr.OFTString} attrs_one = [{'id': 1, 'myattr': 'hello'}] fields_poly = {'id': ogr.OFTInteger} attrs_poly = [{'id': 1}] # Create geometry for the points, which will get clipped geom_one = [Point(pos_x + 220, pos_y - 220)] # Create geometry for the polygons, which will be used to clip geom_two = [ Polygon([(pos_x, pos_y), (pos_x + 60, pos_y), (pos_x + 60, pos_y - 60), (pos_x, pos_y - 60), (pos_x, pos_y)]) ] shape_to_clip_path = os.path.join(self.workspace_dir, 'shape_to_clip.shp') # Create the point shapefile pygeoprocessing.shapely_geometry_to_vector(geom_one, shape_to_clip_path, projection_wkt, 'ESRI Shapefile', fields=fields_pt, attribute_list=attrs_one, ogr_geom_type=ogr.wkbPoint) binding_shape_path = os.path.join(self.workspace_dir, 'binding_shape.shp') # Create the polygon shapefile pygeoprocessing.shapely_geometry_to_vector(geom_two, binding_shape_path, projection_wkt, 'ESRI Shapefile', fields=fields_poly, attribute_list=attrs_poly) output_path = os.path.join(self.workspace_dir, 'vector.shp') # Call the function to test self.assertRaises(wave_energy.IntersectionError, wave_energy._clip_vector_by_vector, shape_to_clip_path, binding_shape_path, output_path, projection_wkt, self.workspace_dir)
def test_calculate_distances_land_grid(self): """WindEnergy: testing 'calculate_distances_land_grid' function.""" from natcap.invest import wind_energy srs = osr.SpatialReference() srs.ImportFromEPSG(3157) projection_wkt = srs.ExportToWkt() origin = (443723.127327877911739, 4956546.905980412848294) pos_x = origin[0] pos_y = origin[1] # Setup parameters for creating point shapefile fields = {'id': ogr.OFTReal, 'L2G': ogr.OFTReal} attrs = [{'id': 1, 'L2G': 10}, {'id': 2, 'L2G': 20}] geometries = [ Point(pos_x + 50, pos_y - 50), Point(pos_x + 50, pos_y - 150) ] land_shape_path = os.path.join(self.workspace_dir, 'temp_shape.shp') # Create point shapefile to use for testing input pygeoprocessing.shapely_geometry_to_vector(geometries, land_shape_path, projection_wkt, 'ESRI Shapefile', fields=fields, attribute_list=attrs, ogr_geom_type=ogr.wkbPoint) # Setup parameters for create raster matrix = numpy.array([[1, 1, 1, 1], [1, 1, 1, 1]], dtype=numpy.int32) harvested_masked_path = os.path.join(self.workspace_dir, 'temp_raster.tif') # Create raster to use for testing input pygeoprocessing.numpy_array_to_raster(matrix, -1, (100, -100), origin, projection_wkt, harvested_masked_path) tmp_dist_final_path = os.path.join(self.workspace_dir, 'dist_final.tif') # Call function to test given testing inputs wind_energy._calculate_distances_land_grid(land_shape_path, harvested_masked_path, tmp_dist_final_path, '') # Compare the results res_array = pygeoprocessing.raster_to_numpy_array(tmp_dist_final_path) exp_array = numpy.array([[10, 110, 210, 310], [20, 120, 220, 320]], dtype=numpy.int32) numpy.testing.assert_allclose(res_array, exp_array)
def test_create_distance_raster(self): """WindEnergy: testing '_create_distance_raster' function.""" from natcap.invest import wind_energy srs = osr.SpatialReference() srs.ImportFromEPSG(3157) #UTM Zone 10N projection_wkt = srs.ExportToWkt() origin = (443723.127327877911739, 4956546.905980412848294) pos_x = origin[0] pos_y = origin[1] # Setup and create vector to pass to function fields = {'id': ogr.OFTReal} attrs = [{'id': 1}] # Square polygon that will overlap the 4 pixels of the raster in the # upper left corner poly_geometry = [box(pos_x, pos_y - 17, pos_x + 17, pos_y)] poly_vector_path = os.path.join(self.workspace_dir, 'distance_from_vector.gpkg') # Create polygon shapefile to use as testing input pygeoprocessing.shapely_geometry_to_vector( poly_geometry, poly_vector_path, projection_wkt, 'GPKG', fields=fields, attribute_list=attrs, ogr_geom_type=ogr.wkbPolygon) # Create 2x5 raster matrix = numpy.array([[1, 1, 1, 1, 1], [1, 1, 1, 1, 1]], dtype=numpy.float32) base_raster_path = os.path.join(self.workspace_dir, 'temp_raster.tif') # Create raster to use for testing input pygeoprocessing.numpy_array_to_raster(matrix, -1, (10, -10), origin, projection_wkt, base_raster_path) dist_raster_path = os.path.join(self.workspace_dir, 'dist.tif') # Call function to test given testing inputs wind_energy._create_distance_raster(base_raster_path, poly_vector_path, dist_raster_path, self.workspace_dir) # Compare the results res_array = pygeoprocessing.raster_to_numpy_array(dist_raster_path) exp_array = numpy.array([[0, 0, 10, 20, 30], [0, 0, 10, 20, 30]], dtype=numpy.float32) numpy.testing.assert_allclose(res_array, exp_array)
def test_pollination_bad_farm_type(self): """Pollination: ensure detection of bad farm geometry type.""" from natcap.invest import pollination # make some fake farm points point_geom = [shapely.geometry.Point(20, -20)] farm_shape_path = os.path.join(self.workspace_dir, 'point_farm.shp') # Create the point shapefile srs = osr.SpatialReference() srs.ImportFromEPSG(3157) projection_wkt = srs.ExportToWkt() fields = { 'crop_type': ogr.OFTString, 'half_sat': ogr.OFTReal, 'p_managed': ogr.OFTReal } attrs = [{'crop_type': 'test', 'half_sat': 0.5, 'p_managed': 0.5}] pygeoprocessing.shapely_geometry_to_vector(point_geom, farm_shape_path, projection_wkt, 'ESRI Shapefile', fields=fields, attribute_list=attrs, ogr_geom_type=ogr.wkbPoint) args = { 'results_suffix': '', 'workspace_dir': self.workspace_dir, 'landcover_raster_path': os.path.join(REGRESSION_DATA, 'input', 'clipped_landcover.tif'), 'guild_table_path': os.path.join(REGRESSION_DATA, 'input', 'guild_table.csv'), 'landcover_biophysical_table_path': os.path.join(REGRESSION_DATA, 'input', 'landcover_biophysical_table.csv'), 'farm_vector_path': farm_shape_path, } with self.assertRaises(ValueError): pollination.execute(args)
def test_simplify_geometry_lines(self): """HRA: test _simplify_geometry does not alter geometry given lines.""" from natcap.invest.hra import _simplify_geometry from natcap.invest.utils import _assert_vectors_equal srs = osr.SpatialReference() srs.ImportFromEPSG(EPSG_CODE) projection_wkt = srs.ExportToWkt() base_lines_path = os.path.join(self.workspace_dir, 'base_lines.gpkg') lines = [LineString([(0.0, 0.0), (10.0, 10.0)])] pygeoprocessing.shapely_geometry_to_vector( lines, base_lines_path, projection_wkt, 'GPKG', ogr_geom_type=ogr.wkbLineString) target_simplified_vector_path = os.path.join( self.workspace_dir, 'simplified_vector.gpkg') tolerance = 3000 # in meters _simplify_geometry( base_lines_path, tolerance, target_simplified_vector_path) _assert_vectors_equal( target_simplified_vector_path, base_lines_path)
def test_point_snapping(self): """DelineateIt: test point snapping.""" from natcap.invest.delineateit import delineateit srs = osr.SpatialReference() srs.ImportFromEPSG(32731) # WGS84/UTM zone 31s wkt = srs.ExportToWkt() # need stream layer, points stream_matrix = numpy.array( [[0, 1, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0], [0, 1, 1, 1, 1, 1], [0, 1, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0]], dtype=numpy.int8) stream_raster_path = os.path.join(self.workspace_dir, 'streams.tif') # byte datatype pygeoprocessing.numpy_array_to_raster(stream_matrix, 255, (2, -2), (2, -2), wkt, stream_raster_path) source_points_path = os.path.join(self.workspace_dir, 'source_features.geojson') source_features = [ Point(-1, -1), # off the edge of the stream raster. Point(3, -5), Point(7, -9), Point(13, -5), MultiPoint([(13, -5)]), box(-2, -2, -1, -1), # Off the edge ] fields = {'foo': ogr.OFTInteger, 'bar': ogr.OFTString} attributes = [ { 'foo': 0, 'bar': '0.1' }, { 'foo': 1, 'bar': '1.1' }, { 'foo': 2, 'bar': '2.1' }, { 'foo': 3, 'bar': '3.1' }, { 'foo': 3, 'bar': '3.1' }, # intentional duplicate fields { 'foo': 4, 'bar': '4.1' } ] pygeoprocessing.shapely_geometry_to_vector( source_features, source_points_path, wkt, 'GeoJSON', fields=fields, attribute_list=attributes, ogr_geom_type=ogr.wkbUnknown) snapped_points_path = os.path.join(self.workspace_dir, 'snapped_points.gpkg') snap_distance = -1 with self.assertRaises(ValueError) as cm: delineateit.snap_points_to_nearest_stream(source_points_path, (stream_raster_path, 1), snap_distance, snapped_points_path) self.assertTrue('must be >= 0' in str(cm.exception)) snap_distance = 10 # large enough to get multiple streams per point. delineateit.snap_points_to_nearest_stream(source_points_path, (stream_raster_path, 1), snap_distance, snapped_points_path) snapped_points_vector = gdal.OpenEx(snapped_points_path, gdal.OF_VECTOR) snapped_points_layer = snapped_points_vector.GetLayer() # snapped layer will include 4 valid points and 1 polygon. self.assertEqual(5, snapped_points_layer.GetFeatureCount()) expected_geometries_and_fields = [ (Point(5, -5), { 'foo': 1, 'bar': '1.1' }), (Point(5, -9), { 'foo': 2, 'bar': '2.1' }), (Point(13, -11), { 'foo': 3, 'bar': '3.1' }), (Point(13, -11), { 'foo': 3, 'bar': '3.1' }), # Multipoint now point (box(-2, -2, -1, -1), { 'foo': 4, 'bar': '4.1' }), # unchanged ] for feature, (expected_geom, expected_fields) in zip(snapped_points_layer, expected_geometries_and_fields): shapely_feature = shapely.wkb.loads( bytes(feature.GetGeometryRef().ExportToWkb())) self.assertTrue(shapely_feature.equals(expected_geom)) self.assertEqual(expected_fields, feature.items())
def test_bounding_boxes(self): """Usage logger test that we can extract bounding boxes.""" from natcap.invest import utils from natcap.invest.ui import usage srs = osr.SpatialReference() srs.ImportFromEPSG(32731) # WGS84 / UTM zone 31s srs_wkt = srs.ExportToWkt() raster_path = os.path.join(self.workspace_dir, 'raster.tif') driver = gdal.GetDriverByName('GTiff') raster_array = numpy.ones((20, 20)) raster = driver.Create(raster_path, raster_array.shape[1], raster_array.shape[0], 1, gdal.GDT_Byte, options=('TILED=YES', 'BIGTIFF=YES', 'COMPRESS=LZW', 'BLOCKXSIZE=256', 'BLOCKYSIZE=256')) raster.SetProjection(srs_wkt) raster_band = raster.GetRasterBand(1) raster_band.WriteArray(raster_array) raster_band.SetNoDataValue(255) raster_geotransform = [2, 2, 0, -2, 0, -2] raster.SetGeoTransform(raster_geotransform) raster = None vector_path = os.path.join(self.workspace_dir, 'vector.gpkg') pygeoprocessing.shapely_geometry_to_vector( [shapely.geometry.LineString([(4, -4), (10, -10)])], vector_path, srs_wkt, "GPKG", ogr_geom_type=ogr.wkbLineString) model_args = { 'raster': raster_path, 'vector': vector_path, 'not_a_gis_input': 'foobar' } args_spec = { 'args': { 'raster': { 'type': 'raster' }, 'vector': { 'type': 'vector' }, 'not_a_gis_input': { 'type': 'freestyle_string' } } } output_logfile = os.path.join(self.workspace_dir, 'logfile.txt') with utils.log_to_file(output_logfile): bb_inter, bb_union = usage._calculate_args_bounding_box( model_args, args_spec) numpy.testing.assert_allclose( bb_inter, [-87.234108, -85.526151, -87.233424, -85.526205]) numpy.testing.assert_allclose( bb_union, [-87.237771, -85.526132, -87.23321, -85.526491]) # Verify that no errors were raised in calculating the bounding boxes. self.assertTrue('ERROR' not in open(output_logfile).read(), 'Exception logged when there should not have been.')
def test_clip_vector_by_vector_points(self): """WaveEnergy: testing clipping points from polygons.""" from natcap.invest import wave_energy srs = osr.SpatialReference() srs.ImportFromEPSG(3157) projection_wkt = srs.ExportToWkt() origin = (443723.127327877911739, 4956546.905980412848294) pos_x = origin[0] pos_y = origin[1] fields_pt = {'id': ogr.OFTInteger, 'myattr': ogr.OFTString} attrs_one = [{ 'id': 1, 'myattr': 'hello' }, { 'id': 2, 'myattr': 'bye' }, { 'id': 3, 'myattr': 'highbye' }] fields_poly = {'id': ogr.OFTInteger} attrs_poly = [{'id': 1}] # Create geometry for the points, which will get clipped geom_one = [ Point(pos_x + 20, pos_y - 20), Point(pos_x + 40, pos_y - 20), Point(pos_x + 100, pos_y - 20) ] # Create geometry for the polygons, which will be used to clip geom_two = [ Polygon([(pos_x, pos_y), (pos_x + 60, pos_y), (pos_x + 60, pos_y - 60), (pos_x, pos_y - 60), (pos_x, pos_y)]) ] shape_to_clip_path = os.path.join(self.workspace_dir, 'shape_to_clip.shp') # Create the point shapefile pygeoprocessing.shapely_geometry_to_vector(geom_one, shape_to_clip_path, projection_wkt, 'ESRI Shapefile', fields=fields_pt, attribute_list=attrs_one, ogr_geom_type=ogr.wkbPoint) binding_shape_path = os.path.join(self.workspace_dir, 'binding_shape.shp') # Create the polygon shapefile pygeoprocessing.shapely_geometry_to_vector(geom_two, binding_shape_path, projection_wkt, 'ESRI Shapefile', fields=fields_poly, attribute_list=attrs_poly) output_path = os.path.join(self.workspace_dir, 'vector.shp') # Call the function to test wave_energy._clip_vector_by_vector(shape_to_clip_path, binding_shape_path, output_path, projection_wkt, self.workspace_dir) # Create the expected point shapefile fields_pt = {'id': ogr.OFTInteger, 'myattr': ogr.OFTString} attrs_one = [{'id': 1, 'myattr': 'hello'}, {'id': 2, 'myattr': 'bye'}] geom_three = [ Point(pos_x + 20, pos_y - 20), Point(pos_x + 40, pos_y - 20) ] # Need to save the expected shapefile in a sub folder since it must # have the same layer name / filename as what it will be compared # against. if not os.path.isdir(os.path.join(self.workspace_dir, 'exp_vector')): os.mkdir(os.path.join(self.workspace_dir, 'exp_vector')) expected_path = os.path.join(self.workspace_dir, 'exp_vector', 'vector.shp') pygeoprocessing.shapely_geometry_to_vector(geom_three, expected_path, projection_wkt, 'ESRI Shapefile', fields=fields_pt, attribute_list=attrs_one, ogr_geom_type=ogr.wkbPoint) WaveEnergyRegressionTests._assert_point_vectors_equal( output_path, expected_path)
def test_watersheds_diagnostic_vector(self): """PGP watersheds: test diagnostic vector.""" flow_dir_array = numpy.array( [[6, 6, 6, 6, 6, 6, 6, 6, 6, 6], [6, 6, 6, 6, 6, 6, 6, 6, 6, 6], [6, 6, 6, 6, 6, 6, 6, 6, 6, 6], [6, 6, 6, 6, 6, 6, 6, 6, 6, 255], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [2, 2, 2, 2, 2, 2, 2, 2, 2, 255], [2, 2, 2, 2, 2, 2, 2, 2, 2, 2], [2, 2, 2, 2, 2, 2, 2, 2, 2, 2], [2, 2, 2, 2, 2, 2, 2, 2, 2, 2], [2, 2, 2, 2, 2, 2, 2, 2, 2, 2]], dtype=numpy.int8) srs = osr.SpatialReference() srs.ImportFromEPSG(32731) # WGS84 / UTM zone 31s srs_wkt = srs.ExportToWkt() flow_dir_path = os.path.join(self.workspace_dir, 'flow_dir.tif') pygeoprocessing.numpy_array_to_raster(base_array=flow_dir_array, target_nodata=255, pixel_size=(2, -2), origin=(2, -2), projection_wkt=srs_wkt, target_path=flow_dir_path) # These geometries test: # * Delineation works with varying geometry types # * That we exclude seed pixels that are over nodata # * That we exclude seed pixels off the bounds of the raster horizontal_line = shapely.geometry.LineString([(19, -11), (25, -11)]) vertical_line = shapely.geometry.LineString([(21, -9), (21, -13)]) square = shapely.geometry.box(17, -13, 21, -9) point = shapely.geometry.Point(21, -11) outflow_vector_path = os.path.join(self.workspace_dir, 'outflow.gpkg') pygeoprocessing.shapely_geometry_to_vector( [horizontal_line, vertical_line, square, point], outflow_vector_path, srs_wkt, 'GPKG', { 'polygon_id': ogr.OFTInteger, 'field_string': ogr.OFTString, 'other': ogr.OFTReal }, [{ 'polygon_id': 1, 'field_string': 'hello world', 'other': 1.111 }, { 'polygon_id': 2, 'field_string': 'hello foo', 'other': 2.222 }, { 'polygon_id': 3, 'field_string': 'hello bar', 'other': 3.333 }, { 'polygon_id': 4, 'field_string': 'hello baz', 'other': 4.444 }], ogr_geom_type=ogr.wkbUnknown) target_watersheds_path = os.path.join(self.workspace_dir, 'watersheds.gpkg') pygeoprocessing.routing.delineate_watersheds_d8( (flow_dir_path, 1), outflow_vector_path, target_watersheds_path, write_diagnostic_vector=True, working_dir=self.workspace_dir, remove_temp_files=False) # I'm deliberately only testing that the diagnostic files exist, not # the contents. The diagnostic files should be for debugging only, # so I just want to make sure that they're created. num_diagnostic_files = len( glob.glob(os.path.join(self.workspace_dir, '**/*_seeds.gpkg'))) self.assertEqual(num_diagnostic_files, 3) # 3 features valid
def test_watersheds_trivial(self): """PGP watersheds: test trivial delineation.""" flow_dir_array = numpy.array( [[6, 6, 6, 6, 6, 6, 6, 6, 6, 6], [6, 6, 6, 6, 6, 6, 6, 6, 6, 6], [6, 6, 6, 6, 6, 6, 6, 6, 6, 6], [6, 6, 6, 6, 6, 6, 6, 6, 6, 255], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [2, 2, 2, 2, 2, 2, 2, 2, 2, 255], [2, 2, 2, 2, 2, 2, 2, 2, 2, 2], [2, 2, 2, 2, 2, 2, 2, 2, 2, 2], [2, 2, 2, 2, 2, 2, 2, 2, 2, 2], [2, 2, 2, 2, 2, 2, 2, 2, 2, 2]], dtype=numpy.int8) srs = osr.SpatialReference() srs.ImportFromEPSG(32731) # WGS84 / UTM zone 31s srs_wkt = srs.ExportToWkt() flow_dir_path = os.path.join(self.workspace_dir, 'flow_dir.tif') pygeoprocessing.numpy_array_to_raster(base_array=flow_dir_array, target_nodata=255, pixel_size=(2, -2), origin=(2, -2), projection_wkt=srs_wkt, target_path=flow_dir_path) # These geometries test: # * Delineation works with varying geometry types # * That we exclude seed pixels that are over nodata # * That we exclude seed pixels off the bounds of the raster horizontal_line = shapely.geometry.LineString([(19, -11), (25, -11)]) vertical_line = shapely.geometry.LineString([(21, -9), (21, -13)]) square = shapely.geometry.box(17, -13, 21, -9) point = shapely.geometry.Point(21, -11) outflow_vector_path = os.path.join(self.workspace_dir, 'outflow.gpkg') pygeoprocessing.shapely_geometry_to_vector( [horizontal_line, vertical_line, square, point], outflow_vector_path, srs_wkt, 'GPKG', { 'polygon_id': ogr.OFTInteger, 'field_string': ogr.OFTString, 'other': ogr.OFTReal }, [{ 'polygon_id': 1, 'field_string': 'hello world', 'other': 1.111 }, { 'polygon_id': 2, 'field_string': 'hello foo', 'other': 2.222 }, { 'polygon_id': 3, 'field_string': 'hello bar', 'other': 3.333 }, { 'polygon_id': 4, 'field_string': 'hello baz', 'other': 4.444 }], ogr_geom_type=ogr.wkbUnknown) target_watersheds_path = os.path.join(self.workspace_dir, 'watersheds.gpkg') pygeoprocessing.routing.delineate_watersheds_d8( (flow_dir_path, 1), outflow_vector_path, target_watersheds_path, target_layer_name='watersheds_something') watersheds_vector = gdal.OpenEx(target_watersheds_path, gdal.OF_VECTOR) watersheds_layer = watersheds_vector.GetLayer('watersheds_something') self.assertEqual(watersheds_layer.GetFeatureCount(), 4) # All features should have the same watersheds, both in area and # geometry. flow_dir_bbox = pygeoprocessing.get_raster_info( flow_dir_path)['bounding_box'] expected_watershed_geometry = shapely.geometry.box(*flow_dir_bbox) expected_watershed_geometry = expected_watershed_geometry.difference( shapely.geometry.box(20, -2, 22, -10)) expected_watershed_geometry = expected_watershed_geometry.difference( shapely.geometry.box(20, -12, 22, -22)) pygeoprocessing.shapely_geometry_to_vector( [expected_watershed_geometry], os.path.join(self.workspace_dir, 'foo.gpkg'), srs_wkt, 'GPKG', ogr_geom_type=ogr.wkbGeometryCollection) id_to_fields = {} for feature in watersheds_layer: geometry = feature.GetGeometryRef() shapely_geom = shapely.wkb.loads(geometry.ExportToWkb()) self.assertEqual(shapely_geom.area, expected_watershed_geometry.area) self.assertEqual( shapely_geom.intersection(expected_watershed_geometry).area, expected_watershed_geometry.area) self.assertEqual( shapely_geom.difference(expected_watershed_geometry).area, 0) field_values = feature.items() id_to_fields[field_values['polygon_id']] = field_values outflow_vector = gdal.OpenEx(outflow_vector_path, gdal.OF_VECTOR) outflow_layer = outflow_vector.GetLayer() try: for feature in outflow_layer: self.assertEqual(id_to_fields[feature.GetField('polygon_id')], feature.items()) finally: outflow_layer = None outflow_vector = None
def test_archive_extraction(self): """Datastack: test archive extraction.""" from natcap.invest import datastack from natcap.invest import utils params = { 'blank': '', 'a': 1, 'b': 'hello there', 'c': 'plain bytestring', 'foo': os.path.join(self.workspace, 'foo.txt'), 'bar': os.path.join(self.workspace, 'foo.txt'), 'data_dir': os.path.join(self.workspace, 'data_dir'), 'raster': os.path.join(DATA_DIR, 'dem'), 'vector': os.path.join(DATA_DIR, 'watersheds.shp'), 'simple_table': os.path.join(DATA_DIR, 'carbon_pools_samp.csv'), 'spatial_table': os.path.join(self.workspace, 'spatial_table.csv'), } # synthesize sample data os.makedirs(params['data_dir']) for filename in ('foo.txt', 'bar.txt', 'baz.txt'): data_filepath = os.path.join(params['data_dir'], filename) with open(data_filepath, 'w') as textfile: textfile.write(filename) with open(params['foo'], 'w') as textfile: textfile.write('hello world!') with open(params['spatial_table'], 'w') as spatial_csv: # copy existing DEM # copy existing watersheds # new raster # new vector spatial_csv.write('ID,path\n') spatial_csv.write(f"1,{params['raster']}\n") spatial_csv.write(f"2,{params['vector']}\n") # Create a raster only referenced by the CSV target_csv_raster_path = os.path.join(self.workspace, 'new_raster.tif') pygeoprocessing.new_raster_from_base(params['raster'], target_csv_raster_path, gdal.GDT_UInt16, [0]) spatial_csv.write(f'3,{target_csv_raster_path}\n') # Create a vector only referenced by the CSV target_csv_vector_path = os.path.join(self.workspace, 'new_vector.geojson') pygeoprocessing.shapely_geometry_to_vector( [shapely.geometry.Point(100, 100)], target_csv_vector_path, pygeoprocessing.get_raster_info( params['raster'])['projection_wkt'], 'GeoJSON', ogr_geom_type=ogr.wkbPoint) spatial_csv.write(f'4,{target_csv_vector_path}\n') archive_path = os.path.join(self.workspace, 'archive.invs.tar.gz') datastack.build_datastack_archive( params, 'test_datastack_modules.archive_extraction', archive_path) out_directory = os.path.join(self.workspace, 'extracted_archive') archive_params = datastack.extract_datastack_archive( archive_path, out_directory) model_array = pygeoprocessing.raster_to_numpy_array( archive_params['raster']) reg_array = pygeoprocessing.raster_to_numpy_array(params['raster']) numpy.testing.assert_allclose(model_array, reg_array) utils._assert_vectors_equal(archive_params['vector'], params['vector']) pandas.testing.assert_frame_equal( pandas.read_csv(archive_params['simple_table']), pandas.read_csv(params['simple_table'])) for key in ('blank', 'a', 'b', 'c'): self.assertEqual(archive_params[key], params[key], f'Params differ for key {key}') for key in ('foo', 'bar'): self.assertTrue( filecmp.cmp(archive_params[key], params[key], shallow=False)) spatial_csv_dict = utils.build_lookup_from_csv( archive_params['spatial_table'], 'ID', to_lower=True) spatial_csv_dir = os.path.dirname(archive_params['spatial_table']) numpy.testing.assert_allclose( pygeoprocessing.raster_to_numpy_array( os.path.join(spatial_csv_dir, spatial_csv_dict[3]['path'])), pygeoprocessing.raster_to_numpy_array(target_csv_raster_path)) utils._assert_vectors_equal( os.path.join(spatial_csv_dir, spatial_csv_dict[4]['path']), target_csv_vector_path)
def test_calculate_land_to_grid_distance(self): """WindEnergy: testing 'point_to_polygon_distance' function.""" from natcap.invest import wind_energy # Setup parameters for creating polygon and point shapefiles fields = {'vec_id': ogr.OFTInteger} attr_pt = [{'vec_id': 1}, {'vec_id': 2}, {'vec_id': 3}, {'vec_id': 4}] attr_poly = [{'vec_id': 1}, {'vec_id': 2}] srs = osr.SpatialReference() srs.ImportFromEPSG(3157) projection_wkt = srs.ExportToWkt() origin = (443723.127327877911739, 4956546.905980412848294) pos_x = origin[0] pos_y = origin[1] poly_geoms = { 'poly_1': [(pos_x + 200, pos_y), (pos_x + 250, pos_y), (pos_x + 250, pos_y - 100), (pos_x + 200, pos_y - 100), (pos_x + 200, pos_y)], 'poly_2': [(pos_x, pos_y - 150), (pos_x + 100, pos_y - 150), (pos_x + 100, pos_y - 200), (pos_x, pos_y - 200), (pos_x, pos_y - 150)] } poly_geometries = [ Polygon(poly_geoms['poly_1']), Polygon(poly_geoms['poly_2']) ] poly_vector_path = os.path.join(self.workspace_dir, 'poly_shape.shp') # Create polygon shapefile to use as testing input pygeoprocessing.shapely_geometry_to_vector( poly_geometries, poly_vector_path, projection_wkt, 'ESRI Shapefile', fields=fields, attribute_list=attr_poly, ogr_geom_type=ogr.wkbPolygon) point_geometries = [ Point(pos_x, pos_y), Point(pos_x + 100, pos_y), Point(pos_x, pos_y - 100), Point(pos_x + 100, pos_y - 100) ] point_vector_path = os.path.join(self.workspace_dir, 'point_shape.shp') # Create point shapefile to use as testing input pygeoprocessing.shapely_geometry_to_vector(point_geometries, point_vector_path, projection_wkt, 'ESRI Shapefile', fields=fields, attribute_list=attr_pt, ogr_geom_type=ogr.wkbPoint) target_point_vector_path = os.path.join(self.workspace_dir, 'target_point.shp') # Call function to test field_name = 'L2G' wind_energy._calculate_land_to_grid_distance(point_vector_path, poly_vector_path, field_name, target_point_vector_path) exp_results = [.15, .1, .05, .05] point_vector = gdal.OpenEx(target_point_vector_path) point_layer = point_vector.GetLayer() field_index = point_layer.GetFeature(0).GetFieldIndex(field_name) for i, point_feat in enumerate(point_layer): result_val = point_feat.GetField(field_index) numpy.testing.assert_allclose(result_val, exp_results[i])
def test_ufrm_string_damage_to_infrastructure(self): """UFRM: handle str(int) structure indices. This came up on the forums, where a user had provided a string column type that contained integer data. OGR returned these ints as strings, leading to a ``KeyError``. See https://github.com/natcap/invest/issues/590. """ from natcap.invest import urban_flood_risk_mitigation srs = osr.SpatialReference() srs.ImportFromEPSG(3157) projection_wkt = srs.ExportToWkt() origin = (443723.127327877911739, 4956546.905980412848294) pos_x = origin[0] pos_y = origin[1] aoi_geometry = [ shapely.geometry.box(pos_x, pos_y, pos_x + 200, pos_y + 200), ] def _infra_geom(xoff, yoff): """Create sample infrastructure geometry at a position offset. The geometry will be centered on (x+xoff, y+yoff). Parameters: xoff (number): The x offset, referenced against ``pos_x`` from the outer scope. yoff (number): The y offset, referenced against ``pos_y`` from the outer scope. Returns: A ``shapely.Geometry`` of a point buffered by ``20`` centered on the provided (x+xoff, y+yoff) point. """ return shapely.geometry.Point(pos_x + xoff, pos_y + yoff).buffer(20) infra_geometries = [ _infra_geom(x_offset, 100) for x_offset in range(0, 200, 40) ] infra_fields = {'Type': ogr.OFTString} # THIS IS THE THING TESTED infra_attrs = [{ 'Type': str(index) } for index in range(len(infra_geometries))] infrastructure_path = os.path.join(self.workspace_dir, 'infra_vector.shp') pygeoprocessing.shapely_geometry_to_vector( infra_geometries, infrastructure_path, projection_wkt, 'ESRI Shapefile', fields=infra_fields, attribute_list=infra_attrs, ogr_geom_type=ogr.wkbPolygon) aoi_path = os.path.join(self.workspace_dir, 'aoi.shp') pygeoprocessing.shapely_geometry_to_vector( aoi_geometry, aoi_path, projection_wkt, 'ESRI Shapefile', ogr_geom_type=ogr.wkbPolygon) structures_damage_table_path = os.path.join(self.workspace_dir, 'damage_table_path.csv') with open(structures_damage_table_path, 'w') as csv_file: csv_file.write('"Type","damage"\n') for attr_dict in infra_attrs: type_index = int(attr_dict['Type']) csv_file.write(f'"{type_index}",1\n') aoi_damage_dict = (urban_flood_risk_mitigation. _calculate_damage_to_infrastructure_in_aoi( aoi_path, infrastructure_path, structures_damage_table_path)) # Total damage is the sum of the area of all infrastructure geometries # that intersect the AOI, with each area multiplied by the damage cost. # For this test, damage is always 1, so it's just the intersecting # area. self.assertEqual(len(aoi_damage_dict), 1) numpy.testing.assert_allclose(aoi_damage_dict[0], 5645.787282992962)
def test_clip_vector_by_vector_polygons(self): """WaveEnergy: testing clipping polygons from polygons.""" from natcap.invest import wave_energy from natcap.invest.utils import _assert_vectors_equal projection_wkt = osr.SRS_WKT_WGS84_LAT_LONG origin = (-62.00, 44.00) pos_x = origin[0] pos_y = origin[1] fields_aoi = {'id': ogr.OFTInteger} attrs_aoi = [{'id': 1}] # Create polygon for the aoi aoi_polygon = [ Polygon([(pos_x, pos_y), (pos_x + 2, pos_y), (pos_x + 2, pos_y - 2), (pos_x, pos_y - 2), (pos_x, pos_y)]) ] aoi_path = os.path.join(self.workspace_dir, 'aoi.shp') # Create the polygon shapefile pygeoprocessing.shapely_geometry_to_vector(aoi_polygon, aoi_path, projection_wkt, 'ESRI Shapefile', fields=fields_aoi, attribute_list=attrs_aoi) fields_data = {'id': ogr.OFTInteger, 'myattr': ogr.OFTString} attrs_data = [{'id': 1, 'myattr': 'hello'}] # Create polygon to clip with the aoi data_polygon = [ Polygon([(pos_x - 2, pos_y + 2), (pos_x + 6, pos_y - 2), (pos_x + 6, pos_y - 4), (pos_x - 2, pos_y - 6), (pos_x - 2, pos_y + 2)]) ] data_path = os.path.join(self.workspace_dir, 'data.shp') # Create the polygon shapefile pygeoprocessing.shapely_geometry_to_vector(data_polygon, data_path, projection_wkt, 'ESRI Shapefile', fields=fields_data, attribute_list=attrs_data) result_path = os.path.join(self.workspace_dir, 'aoi_clipped.shp') wave_energy._clip_vector_by_vector(data_path, aoi_path, result_path, projection_wkt, self.workspace_dir) fields_expected = {'id': ogr.OFTInteger, 'myattr': ogr.OFTString} attrs_expected = [{'id': 1, 'myattr': 'hello'}] # Create polygon to clip with the aoi expected_polygon = aoi_polygon expected_path = os.path.join(self.workspace_dir, 'expected.shp') # Create the polygon shapefile pygeoprocessing.shapely_geometry_to_vector( expected_polygon, expected_path, projection_wkt, 'ESRI Shapefile', fields=fields_expected, attribute_list=attrs_expected) _assert_vectors_equal(expected_path, result_path)