def run(self): """Run the impact function. :returns: A new line layer with inundated roads marked. :type: safe_layer """ # Thresholds for tsunami hazard zone breakdown. low_max = self.parameters['low_threshold'].value medium_max = self.parameters['medium_threshold'].value high_max = self.parameters['high_threshold'].value target_field = self.target_field # Get parameters from layer's keywords road_class_field = self.exposure.keyword('road_class_field') exposure_value_mapping = self.exposure.keyword('value_mapping') # reproject self.extent to the hazard projection hazard_crs = self.hazard.layer.crs() hazard_authid = hazard_crs.authid() if hazard_authid == 'EPSG:4326': viewport_extent = self.requested_extent else: geo_crs = QgsCoordinateReferenceSystem() geo_crs.createFromSrid(4326) viewport_extent = extent_to_geo_array( QgsRectangle(*self.requested_extent), geo_crs, hazard_crs) # Clip hazard raster small_raster = align_clip_raster(self.hazard.layer, viewport_extent) # Create vector features from the flood raster # For each raster cell there is one rectangular polygon # Data also get spatially indexed for faster operation ranges = ranges_according_thresholds(low_max, medium_max, high_max) index, flood_cells_map = _raster_to_vector_cells( small_raster, ranges, self.exposure.layer.crs()) # Filter geometry and data using the extent ct = QgsCoordinateTransform(QgsCoordinateReferenceSystem("EPSG:4326"), self.exposure.layer.crs()) extent = ct.transformBoundingBox(QgsRectangle(*self.requested_extent)) request = QgsFeatureRequest() request.setFilterRect(extent) # create template for the output layer line_layer_tmp = create_layer(self.exposure.layer) new_field = QgsField(target_field, QVariant.Int) line_layer_tmp.dataProvider().addAttributes([new_field]) line_layer_tmp.updateFields() # create empty output layer and load it filename = unique_filename(suffix='.shp') QgsVectorFileWriter.writeAsVectorFormat(line_layer_tmp, filename, "utf-8", None, "ESRI Shapefile") line_layer = QgsVectorLayer(filename, "flooded roads", "ogr") # Do the heavy work - for each road get flood polygon for that area and # do the intersection/difference to find out which parts are flooded _intersect_lines_with_vector_cells(self.exposure.layer, request, index, flood_cells_map, line_layer, target_field) target_field_index = line_layer.dataProvider().\ fieldNameIndex(target_field) # Generate simple impact report epsg = get_utm_epsg(self.requested_extent[0], self.requested_extent[1]) output_crs = QgsCoordinateReferenceSystem(epsg) transform = QgsCoordinateTransform(self.exposure.layer.crs(), output_crs) # Roads breakdown self.init_report_var(self.hazard_classes) if line_layer.featureCount() < 1: raise ZeroImpactException() roads_data = line_layer.getFeatures() road_type_field_index = line_layer.fieldNameIndex(road_class_field) for road in roads_data: attributes = road.attributes() affected = attributes[target_field_index] if isinstance(affected, QPyNullVariant): continue else: hazard_zone = self.hazard_classes[affected] usage = attributes[road_type_field_index] usage = main_type(usage, exposure_value_mapping) geom = road.geometry() geom.transform(transform) length = geom.length() affected = False num_classes = len(self.hazard_classes) if attributes[target_field_index] in range(num_classes): affected = True self.classify_feature(hazard_zone, usage, length, affected) self.reorder_dictionaries() style_classes = [ # FIXME 0 - 0.1 dict(label=self.hazard_classes[0] + ': 0m', value=0, colour='#00FF00', transparency=0, size=1), dict(label=self.hazard_classes[1] + ': >0 - %.1f m' % low_max, value=1, colour='#FFFF00', transparency=0, size=1), dict(label=self.hazard_classes[2] + ': %.1f - %.1f m' % (low_max + 0.1, medium_max), value=2, colour='#FFB700', transparency=0, size=1), dict(label=self.hazard_classes[3] + ': %.1f - %.1f m' % (medium_max + 0.1, high_max), value=3, colour='#FF6F00', transparency=0, size=1), dict(label=self.hazard_classes[4] + ' > %.1f m' % high_max, value=4, colour='#FF0000', transparency=0, size=1), ] style_info = dict(target_field=target_field, style_classes=style_classes, style_type='categorizedSymbol') impact_data = self.generate_data() extra_keywords = { 'map_title': self.map_title(), 'legend_title': self.metadata().key('legend_title'), 'target_field': target_field } impact_layer_keywords = self.generate_impact_keywords(extra_keywords) # Convert QgsVectorLayer to inasafe layer and return it impact_layer = Vector(data=line_layer, name=self.map_title(), keywords=impact_layer_keywords, style_info=style_info) impact_layer.impact_data = impact_data self._impact = impact_layer return impact_layer
def run(self): """Run the impact function. :returns: A new line layer with inundated roads marked. :type: safe_layer """ # Thresholds for tsunami hazard zone breakdown. low_max = self.parameters['low_threshold'].value medium_max = self.parameters['medium_threshold'].value high_max = self.parameters['high_threshold'].value target_field = self.target_field # Get parameters from layer's keywords road_class_field = self.exposure.keyword('road_class_field') exposure_value_mapping = self.exposure.keyword('value_mapping') # reproject self.extent to the hazard projection hazard_crs = self.hazard.layer.crs() hazard_authid = hazard_crs.authid() if hazard_authid == 'EPSG:4326': viewport_extent = self.requested_extent else: geo_crs = QgsCoordinateReferenceSystem() geo_crs.createFromSrid(4326) viewport_extent = extent_to_geo_array( QgsRectangle(*self.requested_extent), geo_crs, hazard_crs) # Clip hazard raster small_raster = align_clip_raster(self.hazard.layer, viewport_extent) # Create vector features from the flood raster # For each raster cell there is one rectangular polygon # Data also get spatially indexed for faster operation ranges = ranges_according_thresholds(low_max, medium_max, high_max) index, flood_cells_map = _raster_to_vector_cells( small_raster, ranges, self.exposure.layer.crs()) # Filter geometry and data using the extent ct = QgsCoordinateTransform( QgsCoordinateReferenceSystem("EPSG:4326"), self.exposure.layer.crs()) extent = ct.transformBoundingBox(QgsRectangle(*self.requested_extent)) request = QgsFeatureRequest() request.setFilterRect(extent) # create template for the output layer line_layer_tmp = create_layer(self.exposure.layer) new_field = QgsField(target_field, QVariant.Int) line_layer_tmp.dataProvider().addAttributes([new_field]) line_layer_tmp.updateFields() # create empty output layer and load it filename = unique_filename(suffix='.shp') QgsVectorFileWriter.writeAsVectorFormat( line_layer_tmp, filename, "utf-8", None, "ESRI Shapefile") line_layer = QgsVectorLayer(filename, "flooded roads", "ogr") # Do the heavy work - for each road get flood polygon for that area and # do the intersection/difference to find out which parts are flooded _intersect_lines_with_vector_cells( self.exposure.layer, request, index, flood_cells_map, line_layer, target_field) target_field_index = line_layer.dataProvider().\ fieldNameIndex(target_field) # Generate simple impact report epsg = get_utm_epsg(self.requested_extent[0], self.requested_extent[1]) output_crs = QgsCoordinateReferenceSystem(epsg) transform = QgsCoordinateTransform( self.exposure.layer.crs(), output_crs) # Roads breakdown self.init_report_var(self.hazard_classes) if line_layer.featureCount() < 1: raise ZeroImpactException() roads_data = line_layer.getFeatures() road_type_field_index = line_layer.fieldNameIndex(road_class_field) for road in roads_data: attributes = road.attributes() affected = attributes[target_field_index] if isinstance(affected, QPyNullVariant): continue else: hazard_zone = self.hazard_classes[affected] usage = attributes[road_type_field_index] usage = main_type(usage, exposure_value_mapping) geom = road.geometry() geom.transform(transform) length = geom.length() affected = False num_classes = len(self.hazard_classes) if attributes[target_field_index] in range(num_classes): affected = True self.classify_feature(hazard_zone, usage, length, affected) self.reorder_dictionaries() style_classes = [ # FIXME 0 - 0.1 dict( label=self.hazard_classes[0] + ': 0m', value=0, colour='#00FF00', transparency=0, size=1 ), dict( label=self.hazard_classes[1] + ': >0 - %.1f m' % low_max, value=1, colour='#FFFF00', transparency=0, size=1 ), dict( label=self.hazard_classes[2] + ': %.1f - %.1f m' % ( low_max + 0.1, medium_max), value=2, colour='#FFB700', transparency=0, size=1 ), dict( label=self.hazard_classes[3] + ': %.1f - %.1f m' % ( medium_max + 0.1, high_max), value=3, colour='#FF6F00', transparency=0, size=1 ), dict( label=self.hazard_classes[4] + ' > %.1f m' % high_max, value=4, colour='#FF0000', transparency=0, size=1 ), ] style_info = dict( target_field=target_field, style_classes=style_classes, style_type='categorizedSymbol') impact_data = self.generate_data() extra_keywords = { 'map_title': self.metadata().key('map_title'), 'legend_title': self.metadata().key('legend_title'), 'target_field': target_field } impact_layer_keywords = self.generate_impact_keywords(extra_keywords) # Convert QgsVectorLayer to inasafe layer and return it impact_layer = Vector( data=line_layer, name=self.metadata().key('layer_name'), keywords=impact_layer_keywords, style_info=style_info) impact_layer.impact_data = impact_data self._impact = impact_layer return impact_layer
def run(self): """Run the impact function. :returns: A vector layer with affected areas marked. :type: safe_layer """ hazard_layer = self.hazard.layer exposure = self.exposure.layer # Thresholds for tsunami hazard zone breakdown. low_max = self.parameters['low_threshold'].value medium_max = self.parameters['medium_threshold'].value high_max = self.parameters['high_threshold'].value ranges = ranges_according_thresholds(low_max, medium_max, high_max) hazard_value_to_class = {} for i, interval in enumerate(ranges): hazard_value_to_class[interval] = self.hazard_classes[i] # Get parameters from layer's keywords class_field = self.exposure.keyword('field') # reproject self.extent to the hazard projection hazard_crs = hazard_layer.crs() hazard_authid = hazard_crs.authid() if hazard_authid == 'EPSG:4326': viewport_extent = self.requested_extent else: geo_crs = QgsCoordinateReferenceSystem() geo_crs.createFromSrid(4326) viewport_extent = extent_to_geo_array( QgsRectangle(*self.requested_extent), geo_crs, hazard_crs) small_raster = align_clip_raster(hazard_layer, viewport_extent) # Create vector features from the flood raster hazard_class_attribute = 'hazard' vector_file_path = reclassify_polygonize( small_raster.source(), ranges, name_field=hazard_class_attribute) hazard = QgsVectorLayer(vector_file_path, 'tsunami', 'ogr') # prepare objects for re-projection of geometries crs_wgs84 = QgsCoordinateReferenceSystem('EPSG:4326') hazard_to_exposure = QgsCoordinateTransform( hazard.crs(), exposure.crs()) wgs84_to_hazard = QgsCoordinateTransform( crs_wgs84, hazard.crs()) wgs84_to_exposure = QgsCoordinateTransform( crs_wgs84, exposure.crs()) extent = QgsRectangle( self.requested_extent[0], self.requested_extent[1], self.requested_extent[2], self.requested_extent[3]) extent_hazard = wgs84_to_hazard.transformBoundingBox(extent) extent_exposure = wgs84_to_exposure.transformBoundingBox(extent) extent_exposure_geom = QgsGeometry.fromRect(extent_exposure) # make spatial index of hazard hazard_index = QgsSpatialIndex() hazard_features = {} for f in hazard.getFeatures(QgsFeatureRequest(extent_hazard)): f.geometry().transform(hazard_to_exposure) hazard_index.insertFeature(f) hazard_features[f.id()] = QgsFeature(f) # create impact layer filename = unique_filename(suffix='.shp') impact_fields = exposure.dataProvider().fields() impact_fields.append(QgsField(self.target_field, QVariant.String)) writer = QgsVectorFileWriter( filename, 'utf-8', impact_fields, QGis.WKBPolygon, exposure.crs()) # iterate over all exposure polygons and calculate the impact _calculate_landcover_impact( exposure, extent_exposure, extent_exposure_geom, hazard_class_attribute, hazard_features, hazard_index, hazard_value_to_class, impact_fields, writer) del writer impact_layer = QgsVectorLayer(filename, 'Impacted Land Cover', 'ogr') if impact_layer.featureCount() == 0: raise ZeroImpactException() zone_field = None if self.aggregator: zone_field = self.aggregator.exposure_aggregation_field impact_data = LandCoverReportMixin( question=self.question, impact_layer=impact_layer, target_field=self.target_field, ordered_columns=self.hazard_classes, affected_columns=self.affected_hazard_columns, land_cover_field=class_field, zone_field=zone_field ).generate_data() # Define style for the impact layer style_classes = [ dict( label=self.hazard_classes[0] + ': 0m', value=self.hazard_classes[0], colour='#00FF00', border_color='#000000', transparency=0), dict( label=self.hazard_classes[1] + ': >0 - %.1f m' % low_max, value=self.hazard_classes[1], colour='#FFFF00', border_color='#000000', transparency=0), dict( label=self.hazard_classes[2] + ': %.1f - %.1f m' % ( low_max + 0.1, medium_max), value=self.hazard_classes[2], colour='#FFB700', border_color='#000000', transparency=0), dict( label=self.hazard_classes[3] + ': %.1f - %.1f m' % ( medium_max + 0.1, high_max), value=self.hazard_classes[3], colour='#FF6F00', border_color='#000000', transparency=0), dict( label=self.hazard_classes[4] + ' > %.1f m' % high_max, value=self.hazard_classes[4], colour='#FF0000', border_color='#000000', transparency=0), ] style_info = dict( target_field=self.target_field, style_classes=style_classes, style_type='categorizedSymbol') extra_keywords = { 'map_title': self.map_title(), 'target_field': self.target_field } impact_layer_keywords = self.generate_impact_keywords(extra_keywords) # Create vector layer and return impact_layer = Vector( data=impact_layer, name=self.map_title(), keywords=impact_layer_keywords, style_info=style_info) impact_layer.impact_data = impact_data self._impact = impact_layer return impact_layer