def field_keyword_for_the_layer(self): """Return the proper keyword for field for the current layer. Expected values are: 'field', 'structure_class_field', road_class_field :returns: the field keyword :rtype: string """ if self.step_kw_purpose.selected_purpose() == \ layer_purpose_aggregation: # purpose: aggregation return 'aggregation attribute' elif self.step_kw_purpose.selected_purpose() == layer_purpose_hazard: # purpose: hazard if (self.step_kw_layermode.selected_layermode() == layer_mode_classified and is_point_layer(self.layer)): # No field for classified point hazards return '' else: # purpose: exposure layer_mode_key = self.step_kw_layermode.selected_layermode()['key'] layer_geometry_key = self.get_layer_geometry_id() exposure_key = self.step_kw_subcategory.\ selected_subcategory()['key'] exposure_class_fields = self.impact_function_manager.\ exposure_class_fields( layer_mode_key, layer_geometry_key, exposure_key) if exposure_class_fields and len(exposure_class_fields) == 1: return exposure_class_fields[0]['key'] # Fallback to default return 'field'
def polygon_layers_to_combo(self): """Populate the combo with all polygon layers loaded in QGIS.""" # noinspection PyArgumentList registry = QgsMapLayerRegistry.instance() layers = registry.mapLayers().values() found_flag = False for layer in layers: name = layer.name() source = layer.id() # check if layer is a vector polygon layer if is_polygon_layer(layer) or is_point_layer(layer): found_flag = True add_ordered_combo_item(self.cboPolygonLayers, name, source) if found_flag: self.cboPolygonLayers.setCurrentIndex(0)
def polygon_layers_to_combo(self): """Populate the combo with all polygon layers loaded in QGIS.""" # noinspection PyArgumentList registry = QgsMapLayerRegistry.instance() layers = registry.mapLayers().values() found_flag = False for layer in layers: name = layer.name() source = str(layer.id()) # check if layer is a vector polygon layer if is_polygon_layer(layer) or is_point_layer(layer): found_flag = True add_ordered_combo_item(self.cboPolygonLayers, name, source) if found_flag: self.cboPolygonLayers.setCurrentIndex(0)
def geometry_type(layer): """Retrieve the geometry type: point, line, polygon or raster for a layer. :param layer: The layer. :type layer: QgsMapLayer :return: The definition key. :rtype: basestring """ if is_raster_layer(layer): return layer_geometry_raster['key'] elif is_point_layer(layer): return layer_geometry_point['key'] elif is_line_layer(layer): return layer_geometry_line['key'] elif is_polygon_layer(layer): return layer_geometry_polygon['key'] else: return None
def get_layer_geometry_key(self, layer=None): """Obtain layer mode of a given layer. If no layer specified, the current layer is used :param layer : layer to examine :type layer: QgsMapLayer or None :returns: The layer mode. :rtype: str """ if not layer: layer = self.layer if is_raster_layer(layer): return layer_geometry_raster['key'] elif is_point_layer(layer): return layer_geometry_point['key'] elif is_polygon_layer(layer): return layer_geometry_polygon['key'] else: return layer_geometry_line['key']
def get_layer_geometry_id(self, layer=None): """Obtain layer mode of a given layer. If no layer specified, the current layer is used :param layer : layer to examine :type layer: QgsMapLayer or None :returns: The layer mode. :rtype: str """ if not layer: layer = self.layer if is_raster_layer(layer): return 'raster' elif is_point_layer(layer): return 'point' elif is_polygon_layer(layer): return 'polygon' else: return 'line'
def polygon_layers_to_combo(self): """Populate the combo with all polygon layers loaded in QGIS.""" # noinspection PyArgumentList registry = QgsMapLayerRegistry.instance() layers = registry.mapLayers().values() found_flag = False for layer in layers: name = layer.name() source = layer.id() # check if layer is a vector polygon layer if is_polygon_layer(layer) or is_point_layer(layer): found_flag = True add_ordered_combo_item(self.cboPolygonLayers, name, source) # Now disable the run button if no suitable layers were found # see #2206 ok_button = self.button_box.button(QtGui.QDialogButtonBox.Ok) if found_flag: self.cboPolygonLayers.setCurrentIndex(0) ok_button.setEnabled(True) else: ok_button.setEnabled(False)
def get_next_step(self): """Find the proper step when user clicks the Next button. :returns: The step to be switched to :rtype: WizardStep instance or None """ if self.parent.step_kw_layermode.\ selected_layermode() == layer_mode_classified: if is_point_layer(self.parent.layer) \ and self.parent.step_kw_purpose.\ selected_purpose() == layer_purpose_hazard: # Skip FIELD and CLASSIFICATION for point volcanos new_step = self.parent.step_kw_extrakeywords elif self.parent.step_kw_classification.\ classifications_for_layer(): new_step = self.parent.step_kw_classification elif is_raster_layer(self.parent.layer): new_step = self.parent.step_kw_extrakeywords else: new_step = self.parent.step_kw_field else: # CONTINUOUS DATA, ALL GEOMETRIES new_step = self.parent.step_kw_unit return new_step
def layer_description_html(layer, keywords=None): """Form a html description of a given layer based on the layer parameters and keywords if provided :param layer: The layer to get the description :type layer: QgsMapLayer :param keywords: The layer keywords :type keywords: None, dict :returns: The html description in tabular format, ready to use in a label or tool tip. :rtype: str """ if keywords and 'keyword_version' in keywords: keyword_version = str(keywords['keyword_version']) else: keyword_version = None if (keywords and keyword_version and is_keyword_version_supported(keyword_version)): # The layer has valid keywords purpose = keywords.get('layer_purpose') if purpose == layer_purpose_hazard['key']: subcategory = '<tr><td><b>%s</b>: </td><td>%s</td></tr>' % ( tr('Hazard'), keywords.get(purpose)) unit = keywords.get('continuous_hazard_unit') elif purpose == layer_purpose_exposure['key']: subcategory = '<tr><td><b>%s</b>: </td><td>%s</td></tr>' % ( tr('Exposure'), keywords.get(purpose)) unit = keywords.get('exposure_unit') else: subcategory = '' unit = None if keywords.get('layer_mode') == layer_mode_classified['key']: unit = tr('classified data') if unit: unit = '<tr><td><b>%s</b>: </td><td>%s</td></tr>' % (tr('Unit'), unit) description = """ <table border="0" width="100%%"> <tr><td><b>%s</b>: </td><td>%s</td></tr> <tr><td><b>%s</b>: </td><td>%s</td></tr> %s %s <tr><td><b>%s</b>: </td><td>%s</td></tr> </table> """ % (tr('Title'), keywords.get('title'), tr('Purpose'), keywords.get('layer_purpose'), subcategory, unit, tr('Source'), keywords.get('source')) elif keywords: # The layer has keywords, but the version is wrong layer_version = keyword_version or tr('No Version') description = tr( 'Your layer\'s keyword\'s version ({layer_version}) does not ' 'match with your InaSAFE version ({inasafe_version}). If you wish ' 'to use it as an exposure, hazard, or aggregation layer in an ' 'analysis, please update the keywords. Click Next if you want to ' 'assign keywords now.').format(layer_version=layer_version, inasafe_version=get_version()) else: # The layer is keywordless if is_point_layer(layer): geom_type = layer_geometry_point['key'] elif is_polygon_layer(layer): geom_type = layer_geometry_polygon['key'] else: geom_type = layer_geometry_line['key'] # hide password in the layer source source = layer.publicSource() description = """ %s<br/><br/> <b>%s</b>: %s<br/> <b>%s</b>: %s<br/><br/> %s """ % (tr('This layer has no valid keywords assigned'), tr('SOURCE'), source, tr('TYPE'), is_raster_layer(layer) and 'raster' or 'vector (%s)' % geom_type, tr('In the next step you will be able' + ' to assign keywords to this layer.')) return description
def layer_description_html(layer, keywords=None): """Form a html description of a given layer based on the layer parameters and keywords if provided :param layer: The layer to get the description :type layer: QgsMapLayer :param keywords: The layer keywords :type keywords: None, dict :returns: The html description in tabular format, ready to use in a label or tool tip. :rtype: str """ if keywords and 'keyword_version' in keywords: keyword_version = str(keywords['keyword_version']) else: keyword_version = None if (keywords and keyword_version and is_keyword_version_supported(keyword_version)): # The layer has valid keywords purpose = keywords.get('layer_purpose') if purpose == layer_purpose_hazard['key']: subcategory = '<tr><td><b>%s</b>: </td><td>%s</td></tr>' % ( tr('Hazard'), keywords.get(purpose)) unit = keywords.get('continuous_hazard_unit') elif purpose == layer_purpose_exposure['key']: subcategory = '<tr><td><b>%s</b>: </td><td>%s</td></tr>' % ( tr('Exposure'), keywords.get(purpose)) unit = keywords.get('exposure_unit') else: subcategory = '' unit = None if keywords.get('layer_mode') == layer_mode_classified['key']: unit = tr('classified data') if unit: unit = '<tr><td><b>%s</b>: </td><td>%s</td></tr>' % ( tr('Unit'), unit) desc = """ <table border="0" width="100%%"> <tr><td><b>%s</b>: </td><td>%s</td></tr> <tr><td><b>%s</b>: </td><td>%s</td></tr> %s %s <tr><td><b>%s</b>: </td><td>%s</td></tr> </table> """ % (tr('Title'), keywords.get('title'), tr('Purpose'), keywords.get('layer_purpose'), subcategory, unit, tr('Source'), keywords.get('source')) elif keywords: # The layer has keywords, but the version is wrong layer_version = keyword_version or tr('No Version') desc = tr( 'Your layer\'s keyword\'s version ({layer_version}) does not ' 'match with your InaSAFE version ({inasafe_version}). If you wish ' 'to use it as an exposure, hazard, or aggregation layer in an ' 'analysis, please update the keywords. Click Next if you want to ' 'assign keywords now.').format( layer_version=layer_version, inasafe_version=get_version()) else: # The layer is keywordless if is_point_layer(layer): geom_type = 'point' elif is_polygon_layer(layer): geom_type = 'polygon' else: geom_type = 'line' # hide password in the layer source source = layer.publicSource() desc = """ %s<br/><br/> <b>%s</b>: %s<br/> <b>%s</b>: %s<br/><br/> %s """ % (tr('This layer has no valid keywords assigned'), tr('SOURCE'), source, tr('TYPE'), is_raster_layer(layer) and 'raster' or 'vector (%s)' % geom_type, tr('In the next step you will be able' + ' to assign keywords to this layer.')) return desc
def run(self): """Experimental impact function.""" self.validate() self.prepare() self.provenance.append_step( 'Calculating Step', 'Impact function is calculating the impact.') # Get parameters from layer's keywords self.hazard_class_attribute = self.hazard.keyword('field') self.hazard_class_mapping = self.hazard.keyword('value_map') self.exposure_class_attribute = self.exposure.keyword( 'structure_class_field') # Prepare Hazard Layer hazard_provider = self.hazard.layer.dataProvider() # Check affected field exists in the hazard layer affected_field_index = hazard_provider.fieldNameIndex( self.hazard_class_attribute) if affected_field_index == -1: message = tr( 'Field "%s" is not present in the attribute table of the ' 'hazard layer. Please change the Affected Field parameter in ' 'the IF Option.') % self.hazard_class_attribute raise GetDataError(message) srs = self.exposure.layer.crs().toWkt() exposure_provider = self.exposure.layer.dataProvider() exposure_fields = exposure_provider.fields() # Check self.exposure_class_attribute exists in exposure layer building_type_field_index = exposure_provider.fieldNameIndex( self.exposure_class_attribute) if building_type_field_index == -1: message = tr( 'Field "%s" is not present in the attribute table of ' 'the exposure layer. Please change the Building Type ' 'Field parameter in the IF Option.' ) % self.exposure_class_attribute raise GetDataError(message) # If target_field does not exist, add it: if exposure_fields.indexFromName(self.target_field) == -1: exposure_provider.addAttributes( [QgsField(self.target_field, QVariant.Int)]) target_field_index = exposure_provider.fieldNameIndex( self.target_field) exposure_fields = exposure_provider.fields() # Create layer to store the buildings from E and extent buildings_are_points = is_point_layer(self.exposure.layer) if buildings_are_points: building_layer = QgsVectorLayer( 'Point?crs=' + srs, 'impact_buildings', 'memory') else: building_layer = QgsVectorLayer( 'Polygon?crs=' + srs, 'impact_buildings', 'memory') building_provider = building_layer.dataProvider() # Set attributes building_provider.addAttributes(exposure_fields.toList()) building_layer.startEditing() building_layer.commitChanges() # Filter geometry and data using the requested extent requested_extent = QgsRectangle(*self.requested_extent) # This is a hack - we should be setting the extent CRS # in the IF base class via safe/engine/core.py:calculate_impact # for now we assume the extent is in 4326 because it # is set to that from geo_extent # See issue #1857 transform = QgsCoordinateTransform( self.requested_extent_crs, self.hazard.crs()) projected_extent = transform.transformBoundingBox(requested_extent) request = QgsFeatureRequest() request.setFilterRect(projected_extent) # Split building_layer by H and save as result: # 1) Filter from H inundated features # 2) Mark buildings as inundated (1) or not inundated (0) # make spatial index of affected polygons hazard_index = QgsSpatialIndex() hazard_geometries = {} # key = feature id, value = geometry has_hazard_objects = False for feature in self.hazard.layer.getFeatures(request): value = feature[affected_field_index] if value not in self.hazard_class_mapping[self.wet]: continue hazard_index.insertFeature(feature) hazard_geometries[feature.id()] = QgsGeometry(feature.geometry()) has_hazard_objects = True if not has_hazard_objects: message = tr( 'There are no objects in the hazard layer with %s ' 'value in %s. Please check your data or use another ' 'attribute.') % ( self.hazard_class_attribute, ', '.join(self.hazard_class_mapping[self.wet])) raise GetDataError(message) # Filter out just those EXPOSURE features in the analysis extents transform = QgsCoordinateTransform( self.requested_extent_crs, self.exposure.layer.crs()) projected_extent = transform.transformBoundingBox(requested_extent) request = QgsFeatureRequest() request.setFilterRect(projected_extent) # We will use this transform to project each exposure feature into # the CRS of the Hazard. transform = QgsCoordinateTransform( self.exposure.crs(), self.hazard.crs()) features = [] for feature in self.exposure.layer.getFeatures(request): # Make a deep copy as the geometry is passed by reference # If we don't do this, subsequent operations will affect the # original feature geometry as well as the copy TS building_geom = QgsGeometry(feature.geometry()) # Project the building geometry to hazard CRS building_bounds = transform.transform(building_geom.boundingBox()) affected = False # get tentative list of intersecting hazard features # only based on intersection of bounding boxes ids = hazard_index.intersects(building_bounds) for fid in ids: # run (slow) exact intersection test building_geom.transform(transform) if hazard_geometries[fid].intersects(building_geom): affected = True break new_feature = QgsFeature() # We write out the original feature geom, not the projected one new_feature.setGeometry(feature.geometry()) new_feature.setAttributes(feature.attributes()) new_feature[target_field_index] = 1 if affected else 0 features.append(new_feature) # every once in a while commit the created features # to the output layer if len(features) == 1000: (_, __) = building_provider.addFeatures(features) features = [] (_, __) = building_provider.addFeatures(features) building_layer.updateExtents() # Generate simple impact report self.buildings = {} self.affected_buildings = OrderedDict([ (tr('Flooded'), {}) ]) buildings_data = building_layer.getFeatures() building_type_field_index = building_layer.fieldNameIndex( self.exposure_class_attribute) for building in buildings_data: record = building.attributes() building_type = record[building_type_field_index] if building_type in [None, 'NULL', 'null', 'Null']: building_type = 'Unknown type' if building_type not in self.buildings: self.buildings[building_type] = 0 for category in self.affected_buildings.keys(): self.affected_buildings[category][ building_type] = OrderedDict([ (tr('Buildings Affected'), 0)]) self.buildings[building_type] += 1 if record[target_field_index] == 1: self.affected_buildings[tr('Flooded')][building_type][ tr('Buildings Affected')] += 1 # Lump small entries and 'unknown' into 'other' category # Building threshold #2468 postprocessors = self.parameters['postprocessors'] building_postprocessors = postprocessors['BuildingType'][0] self.building_report_threshold = building_postprocessors.value[0].value self._consolidate_to_other() impact_summary = self.html_report() # For printing map purpose map_title = tr('Buildings inundated') legend_title = tr('Structure inundated status') 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=self.target_field, style_classes=style_classes, style_type='categorizedSymbol') # Convert QgsVectorLayer to inasafe layer and return it. if building_layer.featureCount() < 1: raise ZeroImpactException(tr( 'No buildings were impacted by this flood.')) extra_keywords = { 'impact_summary': impact_summary, 'map_title': map_title, 'legend_title': legend_title, 'target_field': self.target_field, 'buildings_total': self.total_buildings, 'buildings_affected': self.total_affected_buildings } self.set_if_provenance() impact_layer_keywords = self.generate_impact_keywords(extra_keywords) building_layer = Vector( data=building_layer, name=tr('Flooded buildings'), keywords=impact_layer_keywords, style_info=style_info) self._impact = building_layer return building_layer