def create_valid_aggregation(layer): """Create a local copy of the aggregation layer and try to make it valid. We need to make the layer valid if we can. We got some issues with DKI Jakarta dataset : Districts and Subdistricts layers. See issue : https://github.com/inasafe/inasafe/issues/3713 :param layer: The aggregation layer. :type layer: QgsVectorLayer :return: The new aggregation layer in memory. :rtype: QgsVectorLayer """ cleaned = create_memory_layer('aggregation', layer.geometryType(), layer.crs(), layer.fields()) # We transfer keywords to the output. cleaned.keywords = layer.keywords try: use_selected_only = layer.use_selected_features_only except AttributeError: use_selected_only = False cleaned.use_selected_features_only = use_selected_only copy_layer(layer, cleaned) return cleaned
def create_valid_aggregation(layer): """Create a local copy of the aggregation layer and try to make it valid. We need to make the layer valid if we can. We got some issues with DKI Jakarta dataset : Districts and Subdistricts layers. See issue : https://github.com/inasafe/inasafe/issues/3713 :param layer: The aggregation layer. :type layer: QgsVectorLayer :return: The new aggregation layer in memory. :rtype: QgsVectorLayer """ cleaned = create_memory_layer( 'aggregation', layer.geometryType(), layer.crs(), layer.fields()) # We transfer keywords to the output. cleaned.keywords = layer.keywords try: use_selected_only = layer.use_selected_features_only except AttributeError: use_selected_only = False cleaned.use_selected_features_only = use_selected_only copy_layer(layer, cleaned) return cleaned
def prepare_new_layer(self, input_layer): """Prepare new layer for the output layer. :param input_layer: Vector layer. :type input_layer: QgsVectorLayer :return: New memory layer duplicated from input_layer. :rtype: QgsVectorLayer """ # create memory layer output_layer_name = os.path.splitext(input_layer.name())[0] output_layer_name = unique_filename( prefix=('%s_minimum_needs_' % output_layer_name), dir='minimum_needs_calculator') output_layer = create_memory_layer( output_layer_name, input_layer.geometryType(), input_layer.crs(), input_layer.fields()) # monkey patching input layer to make it work with # prepare vector layer function temp_layer = input_layer temp_layer.keywords = { 'layer_purpose': layer_purpose_aggregation['key']} # copy features to output layer copy_layer(temp_layer, output_layer) # Monkey patching output layer to make it work with # minimum needs calculator output_layer.keywords['layer_purpose'] = ( layer_purpose_aggregation['key']) output_layer.keywords['inasafe_fields'] = { displaced_field['key']: self.displaced.currentField() } if self.aggregation_name.currentField(): output_layer.keywords['inasafe_fields'][ aggregation_name_field['key']] = ( self.aggregation_name.currentField()) # remove unnecessary fields & rename inasafe fields clean_inasafe_fields(output_layer) return output_layer
def load_path_vector_layer(path, **kwargs): """Return the test vector layer. :param path: Path to the vector layer. :type path: str :param kwargs: It can be : clone=True if you want to copy the layer first to a temporary file. clone_to_memory=True if you want to create a memory layer. with_keywords=False if you do not want keywords. "clone_to_memory" is required. :type kwargs: dict :return: The vector layer. :rtype: QgsVectorLayer .. versionadded:: 4.0 """ if not exists(path): raise Exception('%s do not exist.' % path) path = os.path.normcase(os.path.abspath(path)) name = splitext(basename(path))[0] extension = splitext(path)[1] extensions = [ '.shp', '.shx', '.dbf', '.prj', '.gpkg', '.geojson', '.xml', '.qml' ] if kwargs.get('with_keywords'): if not kwargs.get('clone_to_memory'): raise Exception('with_keywords needs a clone_to_memory') if kwargs.get('clone', False): target_directory = mkdtemp() current_path = splitext(path)[0] path = join(target_directory, name + extension) for ext in extensions: src_path = current_path + ext if exists(src_path): target_path = join(target_directory, name + ext) shutil.copy2(src_path, target_path) if path.endswith('.csv'): # Explicitly use URI with delimiter or tests fail in Windows. TS. uri = 'file:///%s?delimiter=%s' % (path, ',') layer = QgsVectorLayer(uri, name, 'delimitedtext') else: layer = QgsVectorLayer(path, name, 'ogr') if not layer.isValid(): raise Exception('%s is not a valid layer.' % name) monkey_patch_keywords(layer) if kwargs.get('clone_to_memory', False): keywords = layer.keywords.copy() memory_layer = create_memory_layer(name, layer.geometryType(), layer.crs(), layer.fields()) copy_layer(layer, memory_layer) if kwargs.get('with_keywords', True): memory_layer.keywords = keywords return memory_layer else: return layer
def zonal_stats(raster, vector): """Reclassify a continuous raster layer. Issue https://github.com/inasafe/inasafe/issues/3190 The algorithm will take care about projections. We don't want to reproject the raster layer. So if CRS are different, we reproject the vector layer and then we do a lookup from the reprojected layer to the original vector layer. :param raster: The raster layer. :type raster: QgsRasterLayer :param vector: The vector layer. :type vector: QgsVectorLayer :return: The output of the zonal stats. :rtype: QgsVectorLayer .. versionadded:: 4.0 """ output_layer_name = zonal_stats_steps['output_layer_name'] exposure = raster.keywords['exposure'] if raster.crs().authid() != vector.crs().authid(): layer = reproject(vector, raster.crs()) # We prepare the copy output_layer = create_memory_layer( output_layer_name, vector.geometryType(), vector.crs(), vector.fields() ) copy_layer(vector, output_layer) else: layer = create_memory_layer( output_layer_name, vector.geometryType(), vector.crs(), vector.fields() ) copy_layer(vector, layer) input_band = layer.keywords.get('active_band', 1) analysis = QgsZonalStatistics( layer, raster, 'exposure_', input_band, QgsZonalStatistics.Sum) result = analysis.calculateStatistics(None) LOGGER.debug(tr('Zonal stats on %s : %s' % (raster.source(), result))) output_field = exposure_count_field['field_name'] % exposure if raster.crs().authid() != vector.crs().authid(): output_layer.startEditing() field = create_field_from_definition( exposure_count_field, exposure) output_layer.addAttribute(field) new_index = output_layer.fields().lookupField(field.name()) old_index = layer.fields().lookupField('exposure_sum') for feature_input, feature_output in zip( layer.getFeatures(), output_layer.getFeatures()): output_layer.changeAttributeValue( feature_input.id(), new_index, feature_input[old_index]) output_layer.commitChanges() layer = output_layer else: fields_to_rename = { 'exposure_sum': output_field } if qgis_version() >= 21600: rename_fields(layer, fields_to_rename) else: copy_fields(layer, fields_to_rename) remove_fields(layer, list(fields_to_rename.keys())) layer.commitChanges() # The zonal stats is producing some None values. We need to fill these # with 0. See issue : #3778 # We should start a new editing session as previous fields need to be # committed first. layer.startEditing() request = QgsFeatureRequest() expression = '\"%s\" is None' % output_field request.setFilterExpression(expression) request.setFlags(QgsFeatureRequest.NoGeometry) index = layer.fields().lookupField(output_field) for feature in layer.getFeatures(): if feature[output_field] is None: layer.changeAttributeValue(feature.id(), index, 0) layer.commitChanges() layer.keywords = raster.keywords.copy() layer.keywords['inasafe_fields'] = vector.keywords['inasafe_fields'].copy() layer.keywords['inasafe_default_values'] = ( raster.keywords['inasafe_default_values'].copy()) key = exposure_count_field['key'] % raster.keywords['exposure'] # Special case here, one field is the exposure count and the total. layer.keywords['inasafe_fields'][key] = output_field layer.keywords['inasafe_fields'][total_field['key']] = output_field layer.keywords['exposure_keywords'] = raster.keywords.copy() layer.keywords['hazard_keywords'] = vector.keywords[ 'hazard_keywords'].copy() layer.keywords['aggregation_keywords'] = ( vector.keywords['aggregation_keywords']) layer.keywords['layer_purpose'] = ( layer_purpose_aggregate_hazard_impacted['key']) layer.keywords['title'] = output_layer_name check_layer(layer) return layer
def prepare_vector_layer(layer, callback=None): """This function will prepare the layer to be used in InaSAFE : * Make a local copy of the layer. * Make sure that we have an InaSAFE ID column. * Rename fields according to our definitions. * Remove fields which are not used. :param layer: The layer to prepare. :type layer: QgsVectorLayer :param callback: A function to all to indicate progress. The function should accept params 'current' (int), 'maximum' (int) and 'step' (str). Defaults to None. :type callback: function :return: Cleaned memory layer. :rtype: QgsVectorLayer .. versionadded:: 4.0 """ output_layer_name = prepare_vector_steps['output_layer_name'] output_layer_name = output_layer_name % layer.keywords['layer_purpose'] processing_step = prepare_vector_steps['step_name'] if not layer.keywords.get('inasafe_fields'): msg = 'inasafe_fields is missing in keywords from %s' % layer.name() raise InvalidKeywordsForProcessingAlgorithm(msg) feature_count = layer.featureCount() cleaned = create_memory_layer( output_layer_name, layer.geometryType(), layer.crs(), layer.fields()) # We transfer keywords to the output. cleaned.keywords = layer.keywords copy_layer(layer, cleaned) _remove_features(cleaned) # After removing rows, let's check if there is still a feature. request = QgsFeatureRequest().setFlags(QgsFeatureRequest.NoGeometry) iterator = cleaned.getFeatures(request) try: next(iterator) except StopIteration: LOGGER.warning( tr('No feature has been found in the {purpose}' .format(purpose=layer.keywords['layer_purpose']))) raise NoFeaturesInExtentError _add_id_column(cleaned) rename_remove_inasafe_fields(cleaned) if _size_is_needed(cleaned): LOGGER.info( 'We noticed some counts in your exposure layer. Before to update ' 'geometries, we compute the original size for each feature.') run_single_post_processor(cleaned, post_processor_size) if cleaned.keywords['layer_purpose'] == 'exposure': fields = cleaned.keywords['inasafe_fields'] if exposure_type_field['key'] not in fields: _add_default_exposure_class(cleaned) # Check value mapping _check_value_mapping(cleaned) cleaned.keywords['title'] = output_layer_name check_layer(cleaned) return cleaned
def zonal_stats(raster, vector, callback=None): """Reclassify a continuous raster layer. Issue https://github.com/inasafe/inasafe/issues/3190 :param raster: The raster layer. :type raster: QgsRasterLayer :param vector: The vector layer. :type vector: QgsVectorLayer :param callback: A function to all to indicate progress. The function should accept params 'current' (int), 'maximum' (int) and 'step' (str). Defaults to None. :type callback: function :return: The output of the zonal stats. :rtype: QgsVectorLayer .. versionadded:: 4.0 """ output_layer_name = zonal_stats_steps['output_layer_name'] processing_step = zonal_stats_steps['step_name'] layer = create_memory_layer( output_layer_name, vector.geometryType(), vector.crs(), vector.fields() ) copy_layer(vector, layer) input_band = layer.keywords.get('active_band', 1) analysis = QgsZonalStatistics( layer, raster.source(), 'exposure_', input_band, QgsZonalStatistics.Sum) result = analysis.calculateStatistics(None) LOGGER.debug(tr('Zonal stats on %s : %s' % (raster.source(), result))) layer.startEditing() exposure = raster.keywords['exposure'] output_field = exposure_count_field['field_name'] % exposure fields_to_rename = { 'exposure_sum': output_field } copy_fields(layer, fields_to_rename) remove_fields(layer, fields_to_rename.keys()) layer.commitChanges() # The zonal stats is producing some None values. We need to fill these # with 0. See issue : #3778 # We should start a new editing session as previous fields need to be # commited first. layer.startEditing() request = QgsFeatureRequest() expression = '\"%s\" is None' % output_field request.setFilterExpression(expression) request.setFlags(QgsFeatureRequest.NoGeometry) index = layer.fieldNameIndex(output_field) for feature in layer.getFeatures(): if feature[output_field] is None: layer.changeAttributeValue(feature.id(), index, 0) layer.commitChanges() layer.keywords = raster.keywords.copy() layer.keywords['inasafe_fields'] = vector.keywords['inasafe_fields'].copy() layer.keywords['inasafe_default_values'] = ( raster.keywords['inasafe_default_values'].copy()) key = exposure_count_field['key'] % raster.keywords['exposure'] # Special case here, one field is the exposure count and the total. layer.keywords['inasafe_fields'][key] = output_field layer.keywords['inasafe_fields'][total_field['key']] = output_field layer.keywords['exposure_keywords'] = raster.keywords.copy() layer.keywords['hazard_keywords'] = vector.keywords[ 'hazard_keywords'].copy() layer.keywords['aggregation_keywords'] = ( vector.keywords['aggregation_keywords']) layer.keywords['layer_purpose'] = ( layer_purpose_aggregate_hazard_impacted['key']) layer.keywords['title'] = output_layer_name check_layer(layer) return layer
def prepare_vector_layer(layer, callback=None): """This function will prepare the layer to be used in InaSAFE : * Make a local copy of the layer. * Make sure that we have an InaSAFE ID column. * Rename fields according to our definitions. * Remove fields which are not used. :param layer: The layer to prepare. :type layer: QgsVectorLayer :param callback: A function to all to indicate progress. The function should accept params 'current' (int), 'maximum' (int) and 'step' (str). Defaults to None. :type callback: function :return: Cleaned memory layer. :rtype: QgsVectorLayer .. versionadded:: 4.0 """ output_layer_name = prepare_vector_steps['output_layer_name'] output_layer_name = output_layer_name % layer.keywords['layer_purpose'] processing_step = prepare_vector_steps['step_name'] if not layer.keywords.get('inasafe_fields'): msg = 'inasafe_fields is missing in keywords from %s' % layer.name() raise InvalidKeywordsForProcessingAlgorithm(msg) feature_count = layer.featureCount() cleaned = create_memory_layer( output_layer_name, layer.geometryType(), layer.crs(), layer.fields()) # We transfer keywords to the output. cleaned.keywords = layer.keywords copy_layer(layer, cleaned) _remove_features(cleaned) # After removing rows, let's check if there is still a feature. request = QgsFeatureRequest().setFlags(QgsFeatureRequest.NoGeometry) iterator = cleaned.getFeatures(request) try: next(iterator) except StopIteration: LOGGER.warning( tr('No feature has been found in the {purpose}' .format(purpose=layer.keywords['layer_purpose']))) raise NoFeaturesInExtentError _add_id_column(cleaned) clean_inasafe_fields(cleaned) if _size_is_needed(cleaned): LOGGER.info( 'We noticed some counts in your exposure layer. Before to update ' 'geometries, we compute the original size for each feature.') run_single_post_processor(cleaned, post_processor_size) if cleaned.keywords['layer_purpose'] == 'exposure': fields = cleaned.keywords['inasafe_fields'] if exposure_type_field['key'] not in fields: _add_default_exposure_class(cleaned) # Check value mapping _check_value_mapping(cleaned) cleaned.keywords['title'] = output_layer_name check_layer(cleaned) return cleaned
def zonal_stats(raster, vector, callback=None): """Reclassify a continuous raster layer. Issue https://github.com/inasafe/inasafe/issues/3190 :param raster: The raster layer. :type raster: QgsRasterLayer :param vector: The vector layer. :type vector: QgsVectorLayer :param callback: A function to all to indicate progress. The function should accept params 'current' (int), 'maximum' (int) and 'step' (str). Defaults to None. :type callback: function :return: The output of the zonal stats. :rtype: QgsVectorLayer .. versionadded:: 4.0 """ output_layer_name = zonal_stats_steps['output_layer_name'] processing_step = zonal_stats_steps['step_name'] layer = create_memory_layer( output_layer_name, vector.geometryType(), vector.crs(), vector.fields() ) copy_layer(vector, layer) analysis = QgsZonalStatistics( layer, raster.source(), 'exposure_', 1, QgsZonalStatistics.Sum) result = analysis.calculateStatistics(None) LOGGER.debug(tr('Zonal stats on %s : %s' % (raster.source(), result))) layer.startEditing() exposure = raster.keywords['exposure'] output_field = exposure_count_field['field_name'] % exposure fields_to_rename = { 'exposure_sum': output_field } copy_fields(layer, fields_to_rename) remove_fields(layer, fields_to_rename.keys()) layer.commitChanges() # The zonal stats is producing some None values. We need to fill these # with 0. See issue : #3778 # We should start a new editing session as previous fields need to be # commited first. layer.startEditing() request = QgsFeatureRequest() expression = '\"%s\" is None' % output_field request.setFilterExpression(expression) request.setFlags(QgsFeatureRequest.NoGeometry) index = layer.fieldNameIndex(output_field) for feature in layer.getFeatures(): if feature[output_field] is None: layer.changeAttributeValue(feature.id(), index, 0) layer.commitChanges() layer.keywords = raster.keywords.copy() layer.keywords['inasafe_fields'] = vector.keywords['inasafe_fields'].copy() layer.keywords['inasafe_default_values'] = ( raster.keywords['inasafe_default_values'].copy()) key = exposure_count_field['key'] % raster.keywords['exposure'] # Special case here, one field is the exposure count and the total. layer.keywords['inasafe_fields'][key] = output_field layer.keywords['inasafe_fields'][total_field['key']] = output_field layer.keywords['exposure_keywords'] = raster.keywords.copy() layer.keywords['hazard_keywords'] = vector.keywords[ 'hazard_keywords'].copy() layer.keywords['aggregation_keywords'] = ( vector.keywords['aggregation_keywords']) layer.keywords['layer_purpose'] = ( layer_purpose_aggregate_hazard_impacted['key']) layer.keywords['title'] = output_layer_name check_layer(layer) return layer
def load_path_vector_layer(path, **kwargs): """Return the test vector layer. :param path: Path to the vector layer. :type path: str :param kwargs: It can be : clone=True if you want to copy the layer first to a temporary file. clone_to_memory=True if you want to create a memory layer. with_keywords=False if you do not want keywords. "clone_to_memory" is required. :type kwargs: dict :return: The vector layer. :rtype: QgsVectorLayer .. versionadded:: 4.0 """ if not exists(path): raise Exception('%s do not exist.' % path) path = os.path.normcase(os.path.abspath(path)) name = splitext(basename(path))[0] extension = splitext(path)[1] extensions = [ '.shp', '.shx', '.dbf', '.prj', '.gpkg', '.geojson', '.xml', '.qml'] if kwargs.get('with_keywords'): if not kwargs.get('clone_to_memory'): raise Exception('with_keywords needs a clone_to_memory') if kwargs.get('clone', False): target_directory = mkdtemp() current_path = splitext(path)[0] path = join(target_directory, name + extension) for ext in extensions: src_path = current_path + ext if exists(src_path): target_path = join(target_directory, name + ext) shutil.copy2(src_path, target_path) if path.endswith('.csv'): # Explicitly use URI with delimiter or tests fail in Windows. TS. uri = 'file:///%s?delimiter=%s' % (path, ',') layer = QgsVectorLayer(uri, name, 'delimitedtext') else: layer = QgsVectorLayer(path, name, 'ogr') if not layer.isValid(): raise Exception('%s is not a valid layer.' % name) monkey_patch_keywords(layer) if kwargs.get('clone_to_memory', False): keywords = layer.keywords.copy() memory_layer = create_memory_layer( name, layer.geometryType(), layer.crs(), layer.fields()) copy_layer(layer, memory_layer) if kwargs.get('with_keywords', True): memory_layer.keywords = keywords return memory_layer else: return layer