def test_polygonize(self): """Test if polygonize works""" geometry = polygonize( self.raster, threshold_min=1.0, threshold_max=1.5) # Result is one square self.assertTrue(geometry.isGeosValid()) self.assertFalse(geometry.isMultipart()) # noinspection PyArgumentEqualDefault geometry = polygonize(self.raster, threshold_min=0.0) # Result is several polygons self.assertTrue(geometry.isGeosValid()) self.assertTrue(geometry.isMultipart()) expected = QgsVectorLayer(VECTOR_BASE + '.shp', 'test', 'ogr') for feature in expected.getFeatures(): # the layer has one feature only expected_geom = feature.geometry() self.assertTrue((geometry.isGeosEqual(expected_geom)))
def run(self, layers): """Experimental impact function. Input layers: List of layers expected to contain H: Polygon layer of inundation areas E: Vector layer of roads """ target_field = self.parameters['target_field'] road_type_field = self.parameters['road_type_field'] threshold_min = self.parameters['min threshold [m]'] threshold_max = self.parameters['max threshold [m]'] if threshold_min > threshold_max: message = tr('''The minimal threshold is greater then the maximal specified threshold. Please check the values.''') raise GetDataError(message) # Extract data H = get_hazard_layer(layers) # Flood E = get_exposure_layer(layers) # Roads question = get_question(H.get_name(), E.get_name(), self) H = H.get_layer() E = E.get_layer() # Get necessary width and height of raster height = (self.extent[3] - self.extent[1]) / H.rasterUnitsPerPixelY() height = int(height) width = (self.extent[2] - self.extent[0]) / H.rasterUnitsPerPixelX() width = int(width) # Align raster extent and self.extent raster_extent = H.dataProvider().extent() xmin = raster_extent.xMinimum() xmax = raster_extent.xMaximum() ymin = raster_extent.yMinimum() ymax = raster_extent.yMaximum() x_delta = (xmax - xmin) / H.width() x = xmin for i in range(H.width()): if abs(x - self.extent[0]) < x_delta: # We have found the aligned raster boundary break x += x_delta _ = i y_delta = (ymax - ymin) / H.height() y = ymin for i in range(H.width()): if abs(y - self.extent[1]) < y_delta: # We have found the aligned raster boundary break y += y_delta clip_extent = [x, y, x + width * x_delta, y + height * y_delta] # Clip and polygonize small_raster = clip_raster(H, width, height, QgsRectangle(*clip_extent)) flooded_polygon = polygonize(small_raster, threshold_min, threshold_max) # Filter geometry and data using the extent extent = QgsRectangle(*self.extent) request = QgsFeatureRequest() request.setFilterRect(extent) if flooded_polygon is None: message = tr('''There are no objects in the hazard layer with "value">'%s'. Please check the value or use other extent.''' % (threshold_min, )) raise GetDataError(message) # Clip exposure by the extent extent_as_polygon = QgsGeometry().fromRect(extent) line_layer = clip_by_polygon(E, extent_as_polygon) # Find inundated roads, mark them line_layer = split_by_polygon(line_layer, flooded_polygon, request, mark_value=(target_field, 1)) # Find inundated roads, mark them # line_layer = split_by_polygon( # E, # flooded_polygon, # request, # mark_value=(target_field, 1)) target_field_index = line_layer.dataProvider().\ fieldNameIndex(target_field) # Generate simple impact report epsg = get_utm_epsg(self.extent[0], self.extent[1]) output_crs = QgsCoordinateReferenceSystem(epsg) transform = QgsCoordinateTransform(E.crs(), output_crs) road_len = flooded_len = 0 # Length of roads roads_by_type = dict() # Length of flooded roads by types roads_data = line_layer.getFeatures() road_type_field_index = line_layer.fieldNameIndex(road_type_field) for road in roads_data: attributes = road.attributes() road_type = attributes[road_type_field_index] if road_type.__class__.__name__ == 'QPyNullVariant': road_type = tr('Other') geom = road.geometry() geom.transform(transform) length = geom.length() road_len += length if road_type not in roads_by_type: roads_by_type[road_type] = {'flooded': 0, 'total': 0} roads_by_type[road_type]['total'] += length if attributes[target_field_index] == 1: flooded_len += length roads_by_type[road_type]['flooded'] += length table_body = [ question, TableRow([ tr('Road Type'), tr('Flooded in the threshold (m)'), tr('Total (m)') ], header=True), TableRow([tr('All'), int(flooded_len), int(road_len)]) ] table_body.append(TableRow(tr('Breakdown by road type'), header=True)) for t, v in roads_by_type.iteritems(): table_body.append(TableRow([t, int(v['flooded']), int(v['total'])])) impact_summary = Table(table_body).toNewlineFreeString() map_title = tr('Roads inundated') style_classes = [ dict(label=tr('Not Inundated'), value=0, colour='#1EFC7C', transparency=0, size=0.5), dict(label=tr('Inundated'), value=1, colour='#F31A1C', transparency=0, size=0.5) ] style_info = dict(target_field=target_field, style_classes=style_classes, style_type='categorizedSymbol') # Convert QgsVectorLayer to inasafe layer and return it line_layer = Vector(data=line_layer, name=tr('Flooded roads'), keywords={ 'impact_summary': impact_summary, 'map_title': map_title, 'target_field': target_field }, style_info=style_info) return line_layer
def run(self, layers): """ Experimental impact function Input layers: List of layers expected to contain H: Polygon layer of inundation areas E: Vector layer of roads """ target_field = self.parameters['target_field'] road_type_field = self.parameters['road_type_field'] threshold_min = self.parameters['min threshold [m]'] threshold_max = self.parameters['max threshold [m]'] if threshold_min > threshold_max: message = tr('''The minimal threshold is greater then the maximal specified threshold. Please check the values.''') raise GetDataError(message) # Extract data H = get_hazard_layer(layers) # Flood E = get_exposure_layer(layers) # Roads question = get_question( H.get_name(), E.get_name(), self) H = H.get_layer() E = E.get_layer() # Get necessary width and height of raster height = (self.extent[3] - self.extent[1]) / H.rasterUnitsPerPixelY() height = int(height) width = (self.extent[2] - self.extent[0]) / H.rasterUnitsPerPixelX() width = int(width) # Align raster extent and self.extent raster_extent = H.dataProvider().extent() xmin = raster_extent.xMinimum() xmax = raster_extent.xMaximum() ymin = raster_extent.yMinimum() ymax = raster_extent.yMaximum() x_delta = (xmax - xmin) / H.width() x = xmin for i in range(H.width()): if abs(x - self.extent[0]) < x_delta: # We have found the aligned raster boundary break x += x_delta _ = i y_delta = (ymax - ymin) / H.height() y = ymin for i in range(H.width()): if abs(y - self.extent[1]) < y_delta: # We have found the aligned raster boundary break y += y_delta clip_extent = [x, y, x + width * x_delta, y + height * y_delta] # Clip and polygonize small_raster = clip_raster( H, width, height, QgsRectangle(*clip_extent)) flooded_polygon = polygonize( small_raster, threshold_min, threshold_max) # Filter geometry and data using the extent extent = QgsRectangle(*self.extent) request = QgsFeatureRequest() request.setFilterRect(extent) if flooded_polygon is None: message = tr('''There are no objects in the hazard layer with "value">'%s'. Please check the value or use other extent.''' % (threshold_min, )) raise GetDataError(message) # Clip exposure by the extent extent_as_polygon = QgsGeometry().fromRect(extent) line_layer = clip_by_polygon( E, extent_as_polygon ) # Find inundated roads, mark them line_layer = split_by_polygon( line_layer, flooded_polygon, request, mark_value=(target_field, 1)) # Find inundated roads, mark them # line_layer = split_by_polygon( # E, # flooded_polygon, # request, # mark_value=(target_field, 1)) target_field_index = line_layer.dataProvider().\ fieldNameIndex(target_field) # Generate simple impact report epsg = get_utm_epsg(self.extent[0], self.extent[1]) output_crs = QgsCoordinateReferenceSystem(epsg) transform = QgsCoordinateTransform(E.crs(), output_crs) road_len = flooded_len = 0 # Length of roads roads_by_type = dict() # Length of flooded roads by types roads_data = line_layer.getFeatures() road_type_field_index = line_layer.fieldNameIndex(road_type_field) for road in roads_data: attributes = road.attributes() road_type = attributes[road_type_field_index] if road_type.__class__.__name__ == 'QPyNullVariant': road_type = tr('Other') geom = road.geometry() geom.transform(transform) length = geom.length() road_len += length if not road_type in roads_by_type: roads_by_type[road_type] = {'flooded': 0, 'total': 0} roads_by_type[road_type]['total'] += length if attributes[target_field_index] == 1: flooded_len += length roads_by_type[road_type]['flooded'] += length table_body = [ question, TableRow([ tr('Road Type'), tr('Flooded in the threshold (m)'), tr('Total (m)')], header=True), TableRow([ tr('All'), int(flooded_len), int(road_len)])] table_body.append(TableRow( tr('Breakdown by road type'), header=True)) for t, v in roads_by_type.iteritems(): table_body.append( TableRow([t, int(v['flooded']), int(v['total'])]) ) impact_summary = Table(table_body).toNewlineFreeString() map_title = tr('Roads inundated') style_classes = [dict(label=tr('Not Inundated'), value=0, colour='#1EFC7C', transparency=0, size=0.5), dict(label=tr('Inundated'), value=1, colour='#F31A1C', transparency=0, size=0.5)] style_info = dict(target_field=target_field, style_classes=style_classes, style_type='categorizedSymbol') # Convert QgsVectorLayer to inasafe layer and return it line_layer = Vector(data=line_layer, name=tr('Flooded roads'), keywords={'impact_summary': impact_summary, 'map_title': map_title, 'target_field': target_field}, style_info=style_info) return line_layer