def test_FeatureRequestSortByVirtualField(self): layer = QgsVectorLayer("Point?field=fldtxt:string&field=fldint:integer", "addfeat", "memory") pr = layer.dataProvider() f1 = QgsFeature() f1.setAttributes(["test", 123]) f2 = QgsFeature() f2.setAttributes(["test", 124]) self.assertTrue(pr.addFeatures([f1, f2])) idx = layer.addExpressionField('if("fldint"=123,3,2)', QgsField('exp1', QVariant.LongLong)) # NOQA QgsProject.instance().addMapLayers([layer]) request = QgsFeatureRequest() request.setOrderBy(QgsFeatureRequest.OrderBy([QgsFeatureRequest.OrderByClause('exp1', True)])) ids = [] for feat in layer.getFeatures(request): ids.append(feat.id()) self.assertEqual(ids, [2, 1]) request.setOrderBy(QgsFeatureRequest.OrderBy([QgsFeatureRequest.OrderByClause('exp1', False)])) ids = [] for feat in layer.getFeatures(request): ids.append(feat.id()) self.assertEqual(ids, [1, 2]) QgsProject.instance().removeMapLayers([layer.id()])
def test_ZFeatureRequestSortByAuxiliaryField(self): s = QgsAuxiliaryStorage() self.assertTrue(s.isValid()) layer = QgsVectorLayer("Point?field=fldtxt:string&field=fldint:integer", "addfeat", "memory") pr = layer.dataProvider() f1 = QgsFeature() f1.setAttributes(["test", 123]) f2 = QgsFeature() f2.setAttributes(["test", 124]) self.assertTrue(pr.addFeatures([f1, f2])) # Create a new auxiliary layer with 'pk' as key pkf = layer.fields().field(layer.fields().indexOf('fldint')) al = s.createAuxiliaryLayer(pkf, layer) self.assertTrue(al.isValid()) layer.setAuxiliaryLayer(al) prop = QgsPropertyDefinition() prop.setComment('test_field') prop.setDataType(QgsPropertyDefinition.DataTypeNumeric) prop.setOrigin('user') prop.setName('custom') self.assertTrue(al.addAuxiliaryField(prop)) layer.startEditing() i = 2 for feat in layer.getFeatures(): feat.setAttribute(2, i) layer.updateFeature(feat) i -= 1 layer.commitChanges() request = QgsFeatureRequest() request.setOrderBy(QgsFeatureRequest.OrderBy([QgsFeatureRequest.OrderByClause(layer.fields()[2].name(), True)])) ids = [] for feat in layer.getFeatures(request): ids.append(feat.id()) self.assertEqual(ids, [2, 1]) request.setOrderBy(QgsFeatureRequest.OrderBy([QgsFeatureRequest.OrderByClause(layer.fields()[2].name(), False)])) ids = [] for feat in layer.getFeatures(request): ids.append(feat.id()) self.assertEqual(ids, [1, 2]) QgsProject.instance().removeMapLayers([layer.id()])
def processAlgorithm(self, parameters, context, feedback): # Get Parameters and assign to variable to work with source_layer = self.parameterAsLayer(parameters, self.SOURCE_LYR, context) orderbyfield = self.parameterAsString(parameters, self.ORDER_FIELD, context) triggerfield = self.parameterAsString(parameters, self.TRIGGER_FIELD, context) groupfieldname = self.parameterAsString(parameters, self.GROUP_IDFIELD, context) newlineindicator = self.parameterAsInt(parameters, self.INDICATOR_VALUE, context) groupid = 0 # initialize groupid counter total = 100.0 / source_layer.featureCount( ) if source_layer.featureCount( ) else 0 # Initialize progress for progressbar fields = source_layer.fields() # get all fields of the sourcelayer fields.append(QgsField(groupfieldname, QVariant.Int, len=20)) # add a new field to this list (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, source_layer.wkbType(), source_layer.sourceCrs()) # order the layer order_by_clause = QgsFeatureRequest.OrderBy( [QgsFeatureRequest.OrderByClause(orderbyfield, ascending=True)]) request = QgsFeatureRequest().setOrderBy(order_by_clause) for current, feat in enumerate( source_layer.getFeatures(request)): # iterate over source if feat[triggerfield] == newlineindicator: # if trigger appears increase groupcounter groupid += 1 new_feat = QgsFeature(fields) # copy source fields + appended idx = 0 # reset attribute fieldindex for attr in feat.attributes( ): # iterate over attributes of source layer for the current feature new_feat[ idx] = attr # copy attribute values over to the new layer idx += 1 # increase fieldindex counter new_feat[groupfieldname] = groupid # assign the groupid new_feat.setGeometry(feat.geometry( )) # copy over the geometry of the source feature sink.addFeature( new_feat, QgsFeatureSink.FastInsert) # add feature to the output if feedback.isCanceled(): # Cancel algorithm if button is pressed break feedback.setProgress(int(current * total)) # Set Progress in Progressbar return {self.OUTPUT: dest_id} # Return result of algorithm
def test_FeatureRequestSortByJoinField(self): """ test sorting requested features using a joined columns """ joinLayer = QgsVectorLayer( "Point?field=x:string&field=y:integer&field=z:integer", "joinlayer", "memory") pr = joinLayer.dataProvider() f1 = QgsFeature() f1.setAttributes(["foo", 123, 321]) f2 = QgsFeature() f2.setAttributes(["bar", 124, 654]) self.assertTrue(pr.addFeatures([f1, f2])) layer = QgsVectorLayer("Point?field=fldtxt:string&field=fldint:integer", "addfeat", "memory") pr = layer.dataProvider() f1 = QgsFeature() f1.setAttributes(["test", 123]) f2 = QgsFeature() f2.setAttributes(["test", 124]) self.assertTrue(pr.addFeatures([f1, f2])) QgsProject.instance().addMapLayers([layer, joinLayer]) join = QgsVectorLayerJoinInfo() join.setTargetFieldName("fldint") join.setJoinLayer(joinLayer) join.setJoinFieldName("y") join.setUsingMemoryCache(True) layer.addJoin(join) request = QgsFeatureRequest() request.setOrderBy(QgsFeatureRequest.OrderBy([QgsFeatureRequest.OrderByClause('joinlayer_z', True)])) ids = [] for feat in layer.getFeatures(request): ids.append(feat.id()) self.assertEqual(ids, [1, 2]) request.setOrderBy(QgsFeatureRequest.OrderBy([QgsFeatureRequest.OrderByClause('joinlayer_z', False)])) ids = [] for feat in layer.getFeatures(request): ids.append(feat.id()) self.assertEqual(ids, [2, 1]) QgsProject.instance().removeMapLayers([layer.id(), joinLayer.id()])
def testOrderBy(self): self.renderer.setOrderBy( QgsFeatureRequest.OrderBy( [QgsFeatureRequest.OrderByClause('Value', False)])) # Setup rendering check renderchecker = QgsMultiRenderChecker() renderchecker.setMapSettings(self.mapsettings) renderchecker.setControlName('expected_singlesymbol_orderby') result = renderchecker.runTest('singlesymbol_orderby') assert result
def testOrderBy(self): self.renderer.setOrderBy(QgsFeatureRequest.OrderBy([QgsFeatureRequest.OrderByClause('Value', False)])) self.renderer.setOrderByEnabled(True) # Setup rendering check renderchecker = QgsMultiRenderChecker() renderchecker.setMapSettings(self.mapsettings) renderchecker.setControlName('expected_singlesymbol_orderby') self.assertTrue(renderchecker.runTest('singlesymbol_orderby')) # disable order by and retest self.renderer.setOrderByEnabled(False) self.assertTrue(renderchecker.runTest('single'))
def apply_filter(self, request, qgis_layer, qgis_feature_request, view): if request.query_params.get('ordering') is not None: ordering_rules = [] for ordering in request.query_params.get('ordering').split(','): ascending = True if ordering.startswith('-'): ordering = ordering[1:] ascending = False if not self._is_valid_field(qgis_layer, ordering, view): continue ordering_rules.append( QgsFeatureRequest.OrderByClause(ordering, ascending)) if ordering_rules: order_by = QgsFeatureRequest.OrderBy(ordering_rules) qgis_feature_request.setOrderBy(order_by)
def testNumeric(self): """ Test calculation of aggregates on numeric fields""" layer = QgsVectorLayer( "Point?field=fldint:integer&field=flddbl:double", "layer", "memory") pr = layer.dataProvider() # must be same length: int_values = [4, 2, 3, 2, 5, None, 8] dbl_values = [5.5, 3.5, 7.5, 5, 9, None, 7] self.assertEqual(len(int_values), len(dbl_values)) features = [] for i in range(len(int_values)): f = QgsFeature() f.setFields(layer.fields()) f.setAttributes([int_values[i], dbl_values[i]]) features.append(f) assert pr.addFeatures(features) tests = [ [QgsAggregateCalculator.Count, 'fldint', 6], [QgsAggregateCalculator.Count, 'flddbl', 6], [QgsAggregateCalculator.Sum, 'fldint', 24], [QgsAggregateCalculator.Sum, 'flddbl', 37.5], [QgsAggregateCalculator.Mean, 'fldint', 4], [QgsAggregateCalculator.Mean, 'flddbl', 6.25], [QgsAggregateCalculator.StDev, 'fldint', 2.0816], [QgsAggregateCalculator.StDev, 'flddbl', 1.7969], [QgsAggregateCalculator.StDevSample, 'fldint', 2.2803], [QgsAggregateCalculator.StDevSample, 'flddbl', 1.9685], [QgsAggregateCalculator.Min, 'fldint', 2], [QgsAggregateCalculator.Min, 'flddbl', 3.5], [QgsAggregateCalculator.Max, 'fldint', 8], [QgsAggregateCalculator.Max, 'flddbl', 9], [QgsAggregateCalculator.Range, 'fldint', 6], [QgsAggregateCalculator.Range, 'flddbl', 5.5], [QgsAggregateCalculator.Median, 'fldint', 3.5], [QgsAggregateCalculator.Median, 'flddbl', 6.25], [QgsAggregateCalculator.CountDistinct, 'fldint', 5], [QgsAggregateCalculator.CountDistinct, 'flddbl', 6], [QgsAggregateCalculator.CountMissing, 'fldint', 1], [QgsAggregateCalculator.CountMissing, 'flddbl', 1], [QgsAggregateCalculator.FirstQuartile, 'fldint', 2], [QgsAggregateCalculator.FirstQuartile, 'flddbl', 5.0], [QgsAggregateCalculator.ThirdQuartile, 'fldint', 5.0], [QgsAggregateCalculator.ThirdQuartile, 'flddbl', 7.5], [QgsAggregateCalculator.InterQuartileRange, 'fldint', 3.0], [QgsAggregateCalculator.InterQuartileRange, 'flddbl', 2.5], [QgsAggregateCalculator.ArrayAggregate, 'fldint', int_values], [QgsAggregateCalculator.ArrayAggregate, 'flddbl', dbl_values], ] agg = QgsAggregateCalculator(layer) for t in tests: val, ok = agg.calculate(t[0], t[1]) self.assertTrue(ok) if isinstance(t[2], (int, list)): self.assertEqual(val, t[2]) else: self.assertAlmostEqual(val, t[2], 3) # bad tests - the following stats should not be calculatable for numeric fields for t in [ QgsAggregateCalculator.StringMinimumLength, QgsAggregateCalculator.StringMaximumLength ]: val, ok = agg.calculate(t, 'fldint') self.assertFalse(ok) val, ok = agg.calculate(t, 'flddbl') self.assertFalse(ok) # with order by agg = QgsAggregateCalculator(layer) val, ok = agg.calculate(QgsAggregateCalculator.ArrayAggregate, 'fldint') self.assertEqual(val, [4, 2, 3, 2, 5, NULL, 8]) params = QgsAggregateCalculator.AggregateParameters() params.orderBy = QgsFeatureRequest.OrderBy( [QgsFeatureRequest.OrderByClause('fldint')]) agg.setParameters(params) val, ok = agg.calculate(QgsAggregateCalculator.ArrayAggregate, 'fldint') self.assertEqual(val, [2, 2, 3, 4, 5, 8, NULL]) params.orderBy = QgsFeatureRequest.OrderBy( [QgsFeatureRequest.OrderByClause('flddbl')]) agg.setParameters(params) val, ok = agg.calculate(QgsAggregateCalculator.ArrayAggregate, 'fldint') self.assertEqual(val, [2, 2, 4, 8, 3, 5, NULL])
def testString(self): """ Test calculation of aggregates on string fields""" layer = QgsVectorLayer("Point?field=fldstring:string", "layer", "memory") pr = layer.dataProvider() values = [ 'cc', 'aaaa', 'bbbbbbbb', 'aaaa', 'eeee', '', 'eeee', '', 'dddd' ] features = [] for v in values: f = QgsFeature() f.setFields(layer.fields()) f.setAttributes([v]) features.append(f) assert pr.addFeatures(features) tests = [ [QgsAggregateCalculator.Count, 'fldstring', 9], [QgsAggregateCalculator.CountDistinct, 'fldstring', 6], [QgsAggregateCalculator.CountMissing, 'fldstring', 2], [QgsAggregateCalculator.Min, 'fldstring', 'aaaa'], [QgsAggregateCalculator.Max, 'fldstring', 'eeee'], [QgsAggregateCalculator.StringMinimumLength, 'fldstring', 0], [QgsAggregateCalculator.StringMaximumLength, 'fldstring', 8], [QgsAggregateCalculator.ArrayAggregate, 'fldstring', values], ] agg = QgsAggregateCalculator(layer) for t in tests: val, ok = agg.calculate(t[0], t[1]) self.assertTrue(ok) self.assertEqual(val, t[2]) # test string concatenation agg.setDelimiter(',') self.assertEqual(agg.delimiter(), ',') val, ok = agg.calculate(QgsAggregateCalculator.StringConcatenate, 'fldstring') self.assertTrue(ok) self.assertEqual(val, 'cc,aaaa,bbbbbbbb,aaaa,eeee,,eeee,,dddd') val, ok = agg.calculate(QgsAggregateCalculator.StringConcatenateUnique, 'fldstring') self.assertTrue(ok) self.assertEqual(val, 'cc,aaaa,bbbbbbbb,eeee,,dddd') # bad tests - the following stats should not be calculatable for string fields for t in [ QgsAggregateCalculator.Sum, QgsAggregateCalculator.Mean, QgsAggregateCalculator.Median, QgsAggregateCalculator.StDev, QgsAggregateCalculator.StDevSample, QgsAggregateCalculator.Range, QgsAggregateCalculator.Minority, QgsAggregateCalculator.Majority, QgsAggregateCalculator.FirstQuartile, QgsAggregateCalculator.ThirdQuartile, QgsAggregateCalculator.InterQuartileRange ]: val, ok = agg.calculate(t, 'fldstring') self.assertFalse(ok) # with order by agg = QgsAggregateCalculator(layer) val, ok = agg.calculate(QgsAggregateCalculator.ArrayAggregate, 'fldstring') self.assertEqual( val, ['cc', 'aaaa', 'bbbbbbbb', 'aaaa', 'eeee', '', 'eeee', '', 'dddd']) params = QgsAggregateCalculator.AggregateParameters() params.orderBy = QgsFeatureRequest.OrderBy( [QgsFeatureRequest.OrderByClause('fldstring')]) agg.setParameters(params) val, ok = agg.calculate(QgsAggregateCalculator.ArrayAggregate, 'fldstring') self.assertEqual( val, ['', '', 'aaaa', 'aaaa', 'bbbbbbbb', 'cc', 'dddd', 'eeee', 'eeee']) val, ok = agg.calculate(QgsAggregateCalculator.StringConcatenate, 'fldstring') self.assertEqual(val, 'aaaaaaaabbbbbbbbccddddeeeeeeee')
def assign_highest_value(exposure, hazard): """Assign the highest hazard value to an indivisible feature. For indivisible polygon exposure layers such as buildings, we need to assigned the greatest hazard that each polygon touches and use that as the effective hazard class. Issue https://github.com/inasafe/inasafe/issues/3192 We follow the concept here that any part of the exposure dataset that touches the hazard is affected, and the greatest hazard is the effective hazard. :param exposure: The building vector layer. :type exposure: QgsVectorLayer :param hazard: The vector layer to use for hazard. :type hazard: QgsVectorLayer :return: The new impact layer. :rtype: QgsVectorLayer .. versionadded:: 4.0 """ output_layer_name = assign_highest_value_steps['output_layer_name'] hazard_inasafe_fields = hazard.keywords['inasafe_fields'] haz_id_field = hazard_inasafe_fields[hazard_id_field['key']] try: aggr_id_field = hazard_inasafe_fields[aggregation_id_field['key']] except AttributeError: aggr_id_field = None if not hazard.keywords.get('classification'): raise InvalidKeywordsForProcessingAlgorithm if not hazard_inasafe_fields.get(hazard_class_field['key']): raise InvalidKeywordsForProcessingAlgorithm indices = [] exposure.startEditing() for field in hazard.fields(): exposure.addAttribute(field) indices.append(exposure.fields().lookupField(field.name())) exposure.commitChanges() provider = exposure.dataProvider() spatial_index = create_spatial_index(exposure) # cache features from exposure layer for faster retrieval exposure_features = {} for f in exposure.getFeatures(): exposure_features[f.id()] = f # Todo callback # total = 100.0 / len(selectionA) hazard_field = hazard_inasafe_fields[hazard_class_field['key']] layer_classification = None for classification in hazard_classification['types']: if classification['key'] == hazard.keywords['classification']: layer_classification = classification break # Get a ordered list of classes like ['high', 'medium', 'low'] levels = [key['key'] for key in layer_classification['classes']] levels.append(not_exposed_class['key']) def _hazard_sort_key(feature): """Custom feature sort function. The function were intended to sort hazard features, in order for maintaining consistencies between subsequent runs. With controlled order, we will have the same output if one hazard spans over multiple aggregation boundaries. """ if aggr_id_field: return (feature[haz_id_field] or feature.id(), feature[aggr_id_field]) return feature[haz_id_field] or feature.id() # Let's loop over the hazard layer, from high to low hazard zone. for hazard_value in levels: expression = '"%s" = \'%s\'' % (hazard_field, hazard_value) order_by_hazard_field = QgsFeatureRequest.OrderByClause(haz_id_field) order_by_aggregation_field = QgsFeatureRequest.OrderByClause( aggr_id_field) order_by = QgsFeatureRequest.OrderBy( [order_by_hazard_field, order_by_aggregation_field]) hazard_request = QgsFeatureRequest()\ .setOrderBy(order_by).setFilterExpression(expression) update_map = {} areas = sorted(hazard.getFeatures(hazard_request), key=_hazard_sort_key) for area in areas: geometry = area.geometry().constGet() intersects = spatial_index.intersects(geometry.boundingBox()) # to force consistencies between subsequent runs, sort the index. # ideally each exposure feature needs to have prioritization # value/score to determine which hazard/aggregation it belongs to, # in case one feature were intersected with one or more high # hazard geometry. sorting the ids works by ignoring this # tendencies but still maintains consistencies for subsequent run. intersects.sort() # use prepared geometry: makes multiple intersection tests faster geometry_prepared = QgsGeometry.createGeometryEngine(geometry) geometry_prepared.prepareGeometry() # We need to loop over each intersections exposure / hazard. for i in intersects: building = exposure_features[i] building_geometry = building.geometry() if geometry_prepared.intersects(building_geometry.constGet()): update_map[building.id()] = {} for index, value in zip(indices, area.attributes()): update_map[building.id()][index] = value # We don't want this building again, let's remove it from # the index. spatial_index.deleteFeature(building) provider.changeAttributeValues(update_map) exposure.updateExtents() exposure.updateFields() exposure.keywords['inasafe_fields'].update( hazard.keywords['inasafe_fields']) exposure.keywords['layer_purpose'] = layer_purpose_exposure_summary['key'] exposure.keywords['exposure_keywords'] = exposure.keywords.copy() exposure.keywords['aggregation_keywords'] = ( hazard.keywords['aggregation_keywords'].copy()) exposure.keywords['hazard_keywords'] = ( hazard.keywords['hazard_keywords'].copy()) exposure.keywords['title'] = output_layer_name check_layer(exposure) return exposure
def __get_qgis_features(qgis_layer, qgis_feature_request=None, bbox_filter=None, attribute_filters=None, search_filter=None, with_geometry=True, page=None, page_size=None, ordering=None, exclude_fields=None, extra_expression=None, extra_subset_string=None): """Private implementation for count and get""" if qgis_feature_request is None: qgis_feature_request = QgsFeatureRequest() if exclude_fields is not None: if exclude_fields == '__all__': qgis_feature_request.setNoAttributes() else: qgis_feature_request.setSubsetOfAttributes([ name for name in qgis_layer.fields().names() if name not in exclude_fields ], qgis_layer.fields()) expression_parts = [] if extra_expression is not None: expression_parts.append(extra_expression) if not with_geometry: qgis_feature_request.setFlags(QgsFeatureRequest.NoGeometry) if bbox_filter is not None: assert isinstance(bbox_filter, QgsRectangle) qgis_feature_request.setFilterRect(bbox_filter) # Ordering if ordering is not None: ascending = True if ordering.startswith('-'): ordering = ordering[1:] ascending = False order_by = QgsFeatureRequest.OrderBy( [QgsFeatureRequest.OrderByClause('"%s"' % ordering, ascending)]) qgis_feature_request.setOrderBy(order_by) # Search if search_filter is not None: exp_template = '"{field_name}" ILIKE \'%' + search_filter.replace( '\'', '\\\'') + '%\'' exp_parts = [] for f in qgis_layer.fields(): exp_parts.append( exp_template.format(field_name=f.name().replace('"', '\\"'))) expression_parts.append(' OR '.join(exp_parts)) # Attribute filters if attribute_filters is not None: exp_parts = [] for field_name, field_value in attribute_filters.items(): exp_parts.append('"{field_name}" ILIKE \'%{field_value}%\''.format( field_name=field_name.replace('"', '\\"'), field_value=str(field_value).replace('\'', '\\\''))) expression_parts.append(' AND '.join(exp_parts)) offset = 0 feature_count = qgis_layer.featureCount() if page is not None and page_size is not None: page_size = int(page_size) page = int(page) offset = page_size * (page - 1) feature_count = page_size * page # Set to max, without taking filters into account qgis_feature_request.setLimit(feature_count) else: page_size = None # make sure it's none # Fetch features if expression_parts: qgis_feature_request.combineFilterExpression( '(' + ') AND ('.join(expression_parts) + ')') logger.debug( 'Fetching features from layer {layer_name} - filter expression: {filter} - BBOX: {bbox}' .format(layer_name=qgis_layer.name(), filter=qgis_feature_request.filterExpression(), bbox=qgis_feature_request.filterRect())) features = [] original_subset_string = qgis_layer.subsetString() if extra_subset_string is not None: subset_string = original_subset_string if subset_string: qgis_layer.setSubsetString( "({original_subset_string}) AND ({extra_subset_string})". format(original_subset_string=original_subset_string, extra_subset_string=extra_subset_string)) else: qgis_layer.setSubsetString(extra_subset_string) iterator = qgis_layer.getFeatures(qgis_feature_request) try: for _ in range(offset): next(iterator) if page_size is not None: for __ in range(page_size): features.append(next(iterator)) else: while True: features.append(next(iterator)) except StopIteration: pass if extra_subset_string is not None: qgis_layer.setSubsetString(original_subset_string) return features
def processAlgorithm(self, parameters, context, model_feedback): # Use a multi-step feedback, so that individual child algorithm progress reports are adjusted for the # overall progress through the model feedback = QgsProcessingMultiStepFeedback(5, model_feedback) results = {} outputs = {} MNT = self.parameterAsRasterLayer(parameters, self.INPUT, context) emprise = self.parameterAsVectorLayer(parameters, self.EMPRISE, context) dZ = self.parameterAsDouble(parameters, self.DZ, context) fichier_html = self.parameterAsFileOutput(parameters, self.OUTPUT, context) fichier_txt = "{}.txt".format(os.path.splitext(fichier_html)[0]) if parameters[self.MAXZ] == None: maxZ = 99999 else: maxZ = self.parameterAsDouble(parameters, self.MAXZ, context) # Découper un raster selon une couche de masquage alg_params = { "ALPHA_BAND": False, "CROP_TO_CUTLINE": True, "DATA_TYPE": 0, "EXTRA": "", "INPUT": MNT.source(), "KEEP_RESOLUTION": True, "MASK": emprise, "MULTITHREADING": False, "NODATA": None, "OPTIONS": "", "SET_RESOLUTION": False, "SOURCE_CRS": None, "TARGET_CRS": "ProjectCrs", "X_RESOLUTION": None, "Y_RESOLUTION": None, "OUTPUT": QgsProcessing.TEMPORARY_OUTPUT, } outputs["Clip"] = processing.run( "gdal:cliprasterbymasklayer", alg_params, context=context, feedback=feedback, is_child_algorithm=True, ) feedback.setCurrentStep(1) if feedback.isCanceled(): return {} # Polygones Courbes de niveau alg_params = { "BAND": 1, "CREATE_3D": False, "EXTRA": "", "FIELD_NAME_MAX": "ELEV_MAX", "FIELD_NAME_MIN": "ELEV_MIN", "IGNORE_NODATA": False, "INPUT": outputs["Clip"]["OUTPUT"], "INTERVAL": dZ, "NODATA": None, "OFFSET": 0, "OUTPUT": QgsProcessing.TEMPORARY_OUTPUT, } outputs["PolygonesCourbesDeNiveau"] = processing.run( "gdal:contour_polygon", alg_params, context=context, feedback=feedback, is_child_algorithm=True, ) feedback.setCurrentStep(2) if feedback.isCanceled(): return {} # Collecter les géométries alg_params = { "FIELD": ["ELEV_MIN"], "INPUT": outputs["PolygonesCourbesDeNiveau"]["OUTPUT"], "OUTPUT": QgsProcessing.TEMPORARY_OUTPUT, } outputs["CollectGeom"] = processing.run("native:collect", alg_params, context=context, feedback=feedback, is_child_algorithm=True) feedback.setCurrentStep(3) if feedback.isCanceled(): return {} # Calcul "Surface" layer = QgsProcessingUtils.generateTempFilename("layer.shp") alg_params = { "FIELD_LENGTH": 12, "FIELD_NAME": "Surface", "FIELD_PRECISION": 2, "FIELD_TYPE": 0, "FORMULA": "$area", "INPUT": outputs["CollectGeom"]["OUTPUT"], "OUTPUT": layer, } outputs["CalculSurface"] = processing.run( "native:fieldcalculator", alg_params, context=context, feedback=feedback, is_child_algorithm=True, ) feedback.setCurrentStep(4) if feedback.isCanceled(): return {} fields = QgsFields() fields.append(QgsField("Z", QVariant.Double)) fields.append(QgsField("Surface", QVariant.Double)) fields.append(QgsField("Volume", QVariant.Double)) (couche, dest_id) = self.parameterAsSink( parameters, self.OUTPUT2, context, fields, QgsWkbTypes.NoGeometry, QgsProject.instance().crs(), ) with open(fichier_html, "w") as f_html, open(fichier_txt, "w") as f_txt: f_html.write(""" <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Courbe HSV</title> <style> html { font-family: sans-serif; } table { border-collapse: collapse; border: 2px solid rgb(200,200,200); letter-spacing: 1px; font-size: 0.8rem; } td, th { border: 1px solid rgb(190,190,190); padding: 10px 20px; } td { text-align: center; } caption { padding: 10px; } </style> </head> <body> <h1>Courbe HSV</h1> <table> <tr> <th>Z</th> <th>Surface (m²)</th> <th>Volume (m³)</th> </tr> """) f_txt.write("Z\tSurface\tVolume\n") z = [] surface = [] volume = [] vLayer = QgsVectorLayer(layer, "temp") request = QgsFeatureRequest() # Ordonner par ELEV_MIN ascendant clause = QgsFeatureRequest.OrderByClause("ELEV_MIN", ascending=True) orderby = QgsFeatureRequest.OrderBy([clause]) request.setOrderBy(orderby) for current, feat in enumerate(vLayer.getFeatures(request)): if feedback.isCanceled(): return {} if feat["ELEV_MAX"] > maxZ: break if current == 0: z.append(round(feat["ELEV_MAX"], 2)) surface.append(round(feat["Surface"], 2)) volume.append(round(feat["Surface"] * dZ / 2, 2)) else: z.append(round(feat["ELEV_MAX"], 2)) surface.append(round(surface[-1] + feat["Surface"], 2)) volume.append( round( feat["Surface"] * dZ / 2 + surface[-2] * dZ + volume[-1], 2)) self.writeHTMLTableLine(f_html, z[-1], surface[-1], volume[-1]) f_txt.write("{}\t{}\t{}\n".format(z[-1], surface[-1], volume[-1])) if couche is not None: fet = QgsFeature() tabAttr = [z[-1], surface[-1], volume[-1]] fet.setAttributes(tabAttr) couche.addFeature(fet) f_html.write(""" </table> </body> </html> """) return {self.OUTPUT: fichier_html, self.OUTPUT2: dest_id}
def route(self): try: import urllib.request import json origin_dest = [] featurelist = [] if self.dlg.inputpoint.isChecked(): vp_layer = self.dlg.point.currentLayer() countfeat = vp_layer.featureCount() else: vp_layer = iface.activeLayer() vp_layer.commitChanges() countfeat = vp_layer.featureCount() result = processing.run( "native:addautoincrementalfield", { 'INPUT': vp_layer, 'FIELD_NAME': 'id', 'START': 1, 'GROUP_FIELDS': [], 'SORT_EXPRESSION': '\"id\"', 'SORT_ASCENDING': True, 'SORT_NULLS_FIRST': False, 'OUTPUT': 'memory:{0}'.format(self.dlg.route_id.text()) }) QgsProject.instance().removeMapLayer(vp_layer) vp_layer = result['OUTPUT'] QgsProject.instance().addMapLayer(vp_layer) features = vp_layer.getFeatures() points = [] pointdist = [] waypoints = [] # if vp_layer.featureCount() == 2: for feature in vp_layer.getFeatures(): point = feature.geometry().asPoint() xpoint = point.x() ypoint = point.y() Qpoint = QgsPointXY(xpoint, ypoint) points.append(Qpoint) distcheck = 0 if self.dlg.direction.currentText() == 'Start->End': # for i in points: # distance = QgsDistanceArea() # Qpoint1 = i # for j in points: # Qpoint2 = j # dist = distance.measureLine(Qpoint1, Qpoint2) # pointdist.append(dist) # if dist > distcheck: # distcheck = dist self.origin = points[0] self.destination = points[countfeat - 1] # print('End->Start', self.origin, self.destination) elif self.dlg.direction.currentText() == 'End->Start': # for i in points: # distance = QgsDistanceArea() # Qpoint1 = i # for j in points: # Qpoint2 = j # dist = distance.measureLine(Qpoint1, Qpoint2) # pointdist.append(dist) # if dist > distcheck: # distcheck = dist self.origin = points[countfeat - 1] self.destination = points[0] # print('Start->End', self.origin, self.destination) # print(vp_layer.featureCount()) if vp_layer.featureCount() > 3: for i in range(countfeat - 1): if i != 0 and i != countfeat - 1: if self.dlg.direction.currentText() == 'Start->End': if len(waypoints) < 1: waypoints.append('optimize:true|via:' + str(points[i].y()) + ',' + str(points[i].x())) else: waypoints.append('via:' + str(points[i].y()) + ',' + str(points[i].x())) elif self.dlg.direction.currentText() == 'End->Start': if len(waypoints) < 1: waypoints.append( 'optimize:true|via:' + str(points[countfeat - i].y()) + ',' + str(points[countfeat - i].x())) else: waypoints.append( 'via:' + str(points[countfeat - i].y()) + ',' + str(points[countfeat - i].x())) print('|'.join(waypoints)) elif vp_layer.featureCount() == 3: for i in points: if i != self.origin and i != self.destination: waypoints.append('optimize:true|via:' + str(i.y()) + ',' + str(i.x())) else: pass # print(waypoints[0], 'waypoints') # print(distcheck, 'dist') # print(pointdist, 'pointdist') # print(origin.x(),origin.y(), destination.x(),destination.y(), 'origin_dest') # vp_layer.select(origin) # vp_layer.select(destination) for feature in vp_layer.getFeatures(): geometry = feature.geometry() origin_dest.append( {geometry.asPoint().y(), geometry.asPoint().x()}) # print(origin_dest) endpoint = 'https://maps.googleapis.com/maps/api/directions/json?' APIkey = self.dlg.api.text() # mode = 'driving' origin_str = self.origin.y(), self.origin.x() destination_str = self.destination.y(), self.destination.x() # departure_time = (self.dlg.timeEdit.time().hour()*3600 + self.dlg.timeEdit.time().minute()*60+ self.dlg.timeEdit.time().second()) # print(departure_time) import time import datetime # departure = self.totimestamp(self.dlg.timeEdit.dateTime()) if self.dlg.nowtime.isChecked(): departure = 'now' else: departure = self.dlg.timeEdit.dateTime().toSecsSinceEpoch() print(departure) if vp_layer.featureCount() > 3: if self.dlg.avoid.currentText() == 'None': nav_request = 'origin={0},{1}&destination={2},{3}&waypoints={4}&departure_time={5}&mode={6}&model={7}&key={8}'.format( self.origin.y(), self.origin.x(), self.destination.y(), self.destination.x(), '|'.join(waypoints), departure, self.dlg.mode.currentText(), self.dlg.model.currentText(), api_key) else: nav_request = 'origin={0},{1}&destination={2},{3}&waypoints={4}&departure_time={5}&avoid={6}&mode={7}&model={8}&key={9}'.format( self.origin.y(), self.origin.x(), self.destination.y(), self.destination.x(), '|'.join(waypoints), departure, self.dlg.avoid.currentText(), self.dlg.mode.currentText(), self.dlg.model.currentText(), api_key) elif vp_layer.featureCount() == 3: if self.dlg.avoid.currentText() == 'None': nav_request = 'origin={0},{1}&destination={2},{3}&waypoints={4}&departure_time={5}&mode={6}&model={7}&key={8}'.format( self.origin.y(), self.origin.x(), self.destination.y(), self.destination.x(), waypoints[0], departure, self.dlg.mode.currentText(), self.dlg.model.currentText(), api_key) else: nav_request = 'origin={0},{1}&destination={2},{3}&waypoints={4}&departure_time={5}&avoid={6}&mode={7}&model={8}&key={9}'.format( self.origin.y(), self.origin.x(), self.destination.y(), self.destination.x(), waypoints[0], departure, self.dlg.avoid.currentText(), self.dlg.mode.currentText(), self.dlg.model.currentText(), api_key) else: if self.dlg.avoid.currentText() == 'None': nav_request = 'origin={0},{1}&destination={2},{3}&departure_time={4}&mode={5}&model={6}&key={7}'.format( self.origin.y(), self.origin.x(), self.destination.y(), self.destination.x(), departure, self.dlg.mode.currentText(), self.dlg.model.currentText(), api_key) # print(nav_request) else: nav_request = 'origin={0},{1}&destination={2},{3}&departure_time{4}&avoid={5}&mode={6}&model={7}&key={8}'.format( self.origin.y(), self.origin.x(), self.destination.y(), self.destination.x(), departure, self.dlg.avoid.currentText(), self.dlg.mode.currentText(), self.dlg.model.currentText(), api_key) request = endpoint + nav_request print(request) response = urllib.request.urlopen(request).read() directions = json.loads(response) keys = directions.keys() # print(keys) # print(directions['error_message'], directions['routes']) routes = directions['routes'] legs = routes[0]['legs'] line = routes[0]['overview_polyline'] # print(routes) points = polyline.decode(line['points']) self.route_layer = QgsVectorLayer( "Point?crs=EPSG:4326&field=route_id:String(100)&field=distance:String(100)&field=time:String(100)&field=ascending/descending:String(100)&field=departure_time:String(100)&field=roads_to_avoid:String(100)&field=traffic_model:String(100)", "route_points", "memory") provider = self.route_layer.dataProvider() # QgsProject.instance().addMapLayer(self.route_layer) if len(legs[0]['duration']['text'].split(' ')) == 2: duration1 = legs[0]['duration']['text'].split(' ') print(duration1, 'dur') duration = duration1[0] else: duration1 = legs[0]['duration']['text'].split(' ') print(duration1, 'dur1') duration = str((int(duration1[0]) * 60) + int(duration1[2])) self.route_layer.startEditing() route_attrib = [ self.dlg.route_id.text(), legs[0]['distance']['text'].split(' ')[0], duration, self.dlg.direction.currentText(), (self.dlg.timeEdit.dateTime()), self.dlg.avoid.currentText(), self.dlg.model.currentText() ] # print((self.dlg.timeEdit.time())) # print(route_attrib) for i in points: outelem = QgsFeature(self.route_layer.fields()) outelem.setGeometry( QgsGeometry.fromPointXY(QgsPointXY(i[1], i[0]))) outelem.setFields(self.route_layer.fields()) outelem.setAttributes(route_attrib) featurelist.append(outelem) # print(outelem) self.route_layer.dataProvider().addFeatures(featurelist) self.route_layer.commitChanges() result = processing.run( "qgis:pointstopath", { 'INPUT': self.route_layer, 'ORDER_FIELD': 'route_id', 'GROUP_FIELD': None, 'DATE_FORMAT': '', 'OUTPUT': 'memory:' }) if not self.dlg.checkBox.isChecked(): route = QgsVectorLayer( "Linestring?crs=EPSG:4326&field=route_id:String(100)&field=distance(km):String(100)&field=time(min):String(100)&field=ascending/descending:String(100)&field=departure_time:String(100)&field=duration_in_traffic(min):String(100)&field=roads_to_avoid:String(100)&field=traffic_model:String(100)&field=no_of_nodes:String(100)", "route", "memory") else: # print(self.dlg.route_id.text(), route_attrib2) route = self.dlg.layer.currentLayer() fields = route.dataProvider().fields() field_name = [field.name() for field in fields] # line_layer = QgsVectorLayer(result['OUTPUT'], 'route') featurelist2 = [] # if self.dlg.mode.currentText() not in ['walking', 'bicycling', 'transit']: if 'duration_in_traffic' in legs[0].keys(): if len(legs[0]['duration']['text'].split(' ')) == 2: duration1 = legs[0]['duration_in_traffic']['text'].split( ' ') print(duration1, 'dur12') duration_in_traffic = duration1[0] else: duration1 = legs[0]['duration_in_traffic']['text'].split( ' ') print(duration1, 'dur13') duration_in_traffic = str((int(duration1[0]) * 60) + int(duration1[2])) if 'fid' in field_name: route_attrib2 = [ route.featureCount(), self.dlg.route_id.text(), legs[0]['distance']['text'].split(' ')[0], duration, self.dlg.direction.currentText(), (self.dlg.timeEdit.dateTime()), duration_in_traffic, self.dlg.avoid.currentText(), self.dlg.model.currentText(), str(self.route_layer.featureCount()) ] else: route_attrib2 = [ self.dlg.route_id.text(), legs[0]['distance']['text'].split(' ')[0], duration, self.dlg.direction.currentText(), (self.dlg.timeEdit.dateTime()), duration_in_traffic, self.dlg.avoid.currentText(), self.dlg.model.currentText(), str(self.route_layer.featureCount()) ] else: if 'fid' in field_name: route_attrib2 = [ route.featureCount(), self.dlg.route_id.text(), legs[0]['distance']['text'].split(' ')[0], duration, self.dlg.direction.currentText(), (self.dlg.timeEdit.dateTime()), 'None', self.dlg.avoid.currentText(), self.dlg.model.currentText(), str(self.route_layer.featureCount()) ] else: route_attrib2 = [ self.dlg.route_id.text(), legs[0]['distance']['text'].split(' ')[0], duration, self.dlg.direction.currentText(), (self.dlg.timeEdit.dateTime()), duration_in_traffic, self.dlg.avoid.currentText(), self.dlg.model.currentText(), str(self.route_layer.featureCount()) ] # print(field_name) # if 'fid' in field_name: # # route.startEditing() # # print(route.dataProvider().fieldNameIndex('fid')) # # route.dataProvider().deleteAttributes([route.dataProvider().fieldNameIndex('fid')]) # # route.updateFields() # # field_name = [field.name() for field in fields] # # print(field_name) # # route_attrib2 = [route.featureCount(), self.dlg.route_id.text(), legs[0]['distance']['text'].split(' ')[0], duration, self.dlg.direction.currentText(),(self.dlg.timeEdit.dateTime()),'None',self.dlg.avoid.currentText(), self.dlg.model.currentText(), str(self.route_layer.featureCount())] # else: # pass provider = route.dataProvider() route.startEditing() request = QgsFeatureRequest() # set order by field clause = QgsFeatureRequest.OrderByClause('route_id', ascending=False) orderby = QgsFeatureRequest.OrderBy([clause]) request.setOrderBy(orderby) fields = route.dataProvider().fields() field_name = [field.name() for field in fields] for feature in result['OUTPUT'].getFeatures(): outelem = QgsFeature(route.fields()) outelem.setGeometry(feature.geometry()) outelem.setFields(route.fields()) if 'fid' not in field_name: outelem.setAttributes(route_attrib2) else: for index, field in enumerate(field_name): if field != 'fid': print(field, route_attrib2[index]) outelem[field] = route_attrib2[index] featurelist2.append(outelem) route.dataProvider().addFeatures(featurelist2) # route.updateFeature(feature) route.commitChanges() # QgsProject.instance().addMapLayer(route) if route.featureCount() == 1: file_path = os.path.abspath( os.path.join(os.path.dirname(__file__), "route_style.qml")) route.loadNamedStyle(file_path) else: # result_del = processing.run("qgis:deletecolumn", { # 'INPUT': route, # 'COLUMN': ['gid'], 'OUTPUT': 'memory:'}) # QgsProject.instance().addMapLayer(result_del['OUTPUT']) # result = processing.run("native:addautoincrementalfield", # {'INPUT': result_del['OUTPUT'], # 'FIELD_NAME': 'gid', 'START': 1, 'GROUP_FIELDS': [], # 'SORT_EXPRESSION': '', # 'SORT_ASCENDING': False, 'SORT_NULLS_FIRST': False, 'OUTPUT': 'memory:route'}) # # QgsProject.instance().removeMapLayer(route) # route = result['OUTPUT'] QgsProject.instance().addMapLayer(route) # provide file name index and field's unique values # fni = route.dataProvider().fieldNameIndex('route_id') # unique_values = route.uniqueValues(fni) # fni2 = route.dataProvider().fieldNameIndex('route_id') # unique_values2 = route.uniqueValues(fni2) # unique_values2 = sorted(unique_values2) unique_values2 = [] unique_values = [] request = QgsFeatureRequest() # set order by field clause = QgsFeatureRequest.OrderByClause('route_id', ascending=False) orderby = QgsFeatureRequest.OrderBy([clause]) request.setOrderBy(orderby) for feature in route.getFeatures(): attrib = feature.attributes() unique_values2.append(attrib[ route.dataProvider().fieldNameIndex('route_id')]) unique_values.append(attrib[ route.dataProvider().fieldNameIndex('route_id')]) from random import randrange # fill categories categories = [] # print(unique_values) # unique_values = sorted(unique_values) print(unique_values, unique_values2) for index, unique_value in enumerate(unique_values): # initialize the default symbol for this geometry type # symbol = QgsSymbol.defaultSymbol(route.geometryType()) # symbol = QgsSymbol.Symbol().setShape(QgsSimpleMarkerSymbolLayerBase.Star # symbol.appendSymbolLayer(symbol_layer) # configure a symbol layer # sym = route.renderer().symbol() # double headed symbol = QgsSymbol.defaultSymbol(route.geometryType()) # double headed sym_layer = QgsArrowSymbolLayer.create({ "arrow_width": "1", "arrow_start_width": "1", "head_length": "1.5", "head_thickness": "1.5", "head_type": "0", "arrow_type": "0", "is_curved": "0", }) fill_sym = QgsFillSymbol.createSimple({ "color": '%d, %d, %d' % (randrange(0, 256), randrange( 0, 256), randrange(0, 256)) }) sym_layer.setSubSymbol(fill_sym) symbol.changeSymbolLayer(0, sym_layer) # layer_style = {} # layer_style['color'] = '%d, %d, %d' % (randrange(0, 256), randrange(0, 256), randrange(0, 256)) # layer_style['outline'] = '#FF0000' # # layer_style['width'] = '7.6' # symbol_layer = QgsSimpleFillSymbolLayer.create(layer_style) # replace default symbol layer with the configured one # if symbol_layer is not None: # symbol.changeSymbolLayer(0, symbol_layer) # symbol.setWidth(0.66) # create renderer object category = QgsRendererCategory(unique_value, symbol, str(unique_values2[index])) # entry for the list of category items categories.append(category) # create renderer object renderer = QgsCategorizedSymbolRenderer('route_id', categories) # assign the created renderer to the layer if renderer is not None: route.setRenderer(renderer) route.triggerRepaint() ltl = QgsProject.instance().layerTreeRoot().findLayer( route.id()) ltm = iface.layerTreeView() # ltm.sortItems(0, Qt.AscendingOrder) # view = iface.layerTreeView() # ltm.model().AllowNodeReorder() index_newfeat = ltm.model().index(0, 0) node = ltm.model().index2node(index_newfeat) nodes = ltm.model().layerLegendNodes(node) legendNodes = ltm.model().layerLegendNodes(ltl) legend_dict = {} legend_dict[node.name()] = legendNodes # print(legend_dict) ltm.setSortingEnabled(True) ltm.sortByColumn(0, Qt.DescendingOrder) for index, ln in enumerate(legendNodes): if index + 1 != route.featureCount(): ln.setData(Qt.Unchecked, Qt.CheckStateRole) # index_newfeat = ltm.model().index(route.featureCount()-1, 0) # print(index_newfeat) # node = ltm.model().index2node(index_newfeat) # print(node) # nodes = ltm.model().layerLegendNodes(node) # # layer_and_nodes[n.name()] = nodes # # print(layer_and_nodes) # # legend_get = ltm.model().index2legendNode(nodes) # print(nodes) # print(index, ln) # print(index, ltm.model().legendRootIndex(ln), ltm.model().legendNode2index(ln), ln, index_newfeat) # if index+1 != int(self.dlg.route_id.text()): # ln.setData(Qt.Checked, Qt.CheckStateRole) if not self.dlg.checkBox.isChecked(): if self.dlg.output.text() != '': path = self.dlg.output.text() QgsVectorFileWriter.writeAsVectorFormat( route, path, 'UTF-8', route.crs(), 'ESRI Shapefile') # layer = QgsProject.instance().layerTreeRoot().findLayer(route.id()) # print(layer.name()) output = self.dlg.output.text().split('/') route_path = QgsVectorLayer( path, output[len(output) - 1].split('.')[0]) QgsProject.instance().addMapLayer(route_path) else: QgsProject.instance().addMapLayer(route) listselect = [] for index, feature in enumerate(route.getFeatures()): if index + 1 == route.featureCount(): listselect.append(feature.id()) route.select(listselect) iface.actionZoomToSelected().trigger() except Exception as e: alert = QMessageBox() alert.setWindowTitle('Alert') if self.dlg.mode.currentText() in [ 'walking', 'bicycling', 'transit' ]: alert.setText( str(e) + '\nRoute not available for selected mode.') else: alert.setText(str(e)) result = alert.exec_() print(e)
def processAlgorithm(self, parameters, context, feedback): #retrieve the layer inputs source1 = self.parameterAsSource( parameters, self.INPUT1, context ) source2 = self.parameterAsSource( parameters, self.INPUT2, context ) source3 = self.parameterAsSource( parameters, self.INPUT3, context ) source4 = self.parameterAsSource( parameters, self.INPUT4, context ) source5 = self.parameterAsSource( parameters, self.INPUT5, context ) source6 = self.parameterAsSource( parameters, self.INPUT6, context ) source7 = self.parameterAsSource( parameters, self.INPUT7, context ) source8 = self.parameterAsSource( parameters, self.INPUT8, context ) #if a layer was not found, throw an exception to indicate that the algorithm encountered a fatal error if source1 is None: raise QgsProcessingException(self.invalidSourceError(parameters, self.INPUT1)) if source2 is None: raise QgsProcessingException(self.invalidSourceError(parameters, self.INPUT2)) if source3 is None: raise QgsProcessingException(self.invalidSourceError(parameters, self.INPUT3)) if source4 is None: raise QgsProcessingException(self.invalidSourceError(parameters, self.INPUT4)) if source5 is None: raise QgsProcessingException(self.invalidSourceError(parameters, self.INPUT5)) if source6 is None: raise QgsProcessingException(self.invalidSourceError(parameters, self.INPUT6)) if source7 is None: raise QgsProcessingException(self.invalidSourceError(parameters, self.INPUT7)) if source8 is None: raise QgsProcessingException(self.invalidSourceError(parameters, self.INPUT8)) feedback.pushInfo('--------------------------------------------------------------------------') feedback.pushInfo("1/5 - Checking CRS...") feedback.pushInfo('--------------------------------------------------------------------------') #get crs of each layer crs1 = source1.sourceCrs().authid() crs2 = source2.sourceCrs().authid() crs3 = source3.sourceCrs().authid() crs4 = source4.sourceCrs().authid() crs5 = source5.sourceCrs().authid() crs6 = source6.sourceCrs().authid() crs7 = source7.sourceCrs().authid() crs8 = source8.sourceCrs().authid() #check crs of each layer and stop script if not all matching if crs1 == crs2 and crs1 == crs3 and crs1 == crs4 and crs1 == crs5 and crs1 == crs6 and crs1 == crs7 and crs1 == crs8: feedback.pushInfo('CRS is ' + (crs1)) feedback.pushInfo('CRS is matching for all layers') else: feedback.pushInfo('Please ensure matching CRS for all layers') feedback.pushInfo('CRS for INPUT1 is ' + (crs1)) feedback.pushInfo('CRS for INPUT2 is ' + (crs2)) feedback.pushInfo('CRS for INPUT3 is ' + (crs3)) feedback.pushInfo('CRS for INPUT4 is ' + (crs4)) feedback.pushInfo('CRS for INPUT5 is ' + (crs5)) feedback.pushInfo('CRS for INPUT6 is ' + (crs6)) feedback.pushInfo('CRS for INPUT7 is ' + (crs7)) feedback.pushInfo('CRS for INPUT8 is ' + (crs8)) return{} #check if script has been cancelled before next stage if feedback.isCanceled(): return{} #output current stage of script feedback.pushInfo('--------------------------------------------------------------------------') feedback.pushInfo("2/5 - Preparing Layer Data...") feedback.pushInfo('--------------------------------------------------------------------------') #get the layer data from the inputs lgaLayer = parameters['INPUT1'] parcelsLayer = parameters['INPUT2'] addressLayer = parameters['INPUT3'] zonesLayer = parameters['INPUT4'] overlaysLayer = parameters['INPUT5'] floodLayer = parameters['INPUT6'] coastLayer = parameters['INPUT7'] watercourseLayer = parameters['INPUT8'] #clip flood area to LGA result = processing.run('native:clip', { 'INPUT': floodLayer, 'OUTPUT': 'memory:', 'OVERLAY': lgaLayer }, context=context, feedback=feedback) floodLayer = result["OUTPUT"] #create a flood check layer (coast and waterways) floodCheckList = [coastLayer,watercourseLayer] result = processing.run('native:mergevectorlayers', {"LAYERS": floodCheckList, "OUTPUT": 'memory:' }, context=context, feedback=feedback) floodCheckLayer = result["OUTPUT"] #clean up flood layer (keep only areas that intersect with flood check layer) result = processing.run('native:extractbylocation', { 'INPUT': floodLayer, 'INTERSECT': floodCheckLayer, 'METHOD': 0, 'OUTPUT': 'memory:', 'PREDICATE': [0] }, context=context, feedback=feedback) floodCleanedLayer = result["OUTPUT"] #get total area of LGA result = processing.run('qgis:exportaddgeometrycolumns', { 'CALC_METHOD': 0, 'INPUT': lgaLayer, 'OUTPUT': 'memory:' }, context=context, feedback=feedback) lgaAreaLayer = result["OUTPUT"] #get zone areas that intersect with flood layer result = processing.run('native:extractbylocation', { 'INPUT': zonesLayer, 'INTERSECT': floodCleanedLayer, 'METHOD': 0, 'OUTPUT': 'memory:', 'PREDICATE': [0] }, context=context, feedback=feedback) zonesLayer = result["OUTPUT"] #add "ZONE_CLASS" field to zones layer zonesLayer.startEditing() zonesLayer.dataProvider().addAttributes([QgsField("ZONE_CLASS", QVariant.String)]) zonesLayer.updateFields() zonesLayer.commitChanges() #add "ZONE_CLASS" attribute for each feature ("ZONE_CODE" without numeric digit) zFeatures = zonesLayer.getFeatures() zonesLayer.startEditing() for feature in zFeatures: ini_string = feature["ZONE_CODE"] res = ''.join([i for i in ini_string if not i.isdigit()]) feature["ZONE_CLASS"] = res zonesLayer.updateFeature(feature) zonesLayer.commitChanges() #check if script has been cancelled before next stage if feedback.isCanceled(): return{} #output current stage of script feedback.pushInfo('--------------------------------------------------------------------------') feedback.pushInfo("3/5 - Calculating Flooded Area...") feedback.pushInfo('--------------------------------------------------------------------------') #clip zones layer to the flooded area #NOTE: must turn off invalid features filtering in QGIS - otherwise this process won't work as some geometry is invalid result = processing.run('native:clip', { 'INPUT': zonesLayer, 'OUTPUT': 'memory:', 'OVERLAY': floodCleanedLayer }, context=context, feedback=feedback) floodedZonesLayer = result["OUTPUT"] #group by "ZONE_CLASS" result = processing.run('native:dissolve', { 'FIELD': ['ZONE_CLASS'], 'INPUT': floodedZonesLayer, 'OUTPUT': 'memory:' }, context=context, feedback=feedback) floodedZonesLayerDissolved = result["OUTPUT"] #add area for each "ZONE_CLASS" result = processing.run('qgis:exportaddgeometrycolumns', { 'CALC_METHOD': 0, 'INPUT': floodedZonesLayerDissolved, 'OUTPUT': 'memory:Flooded Area' }, context=context, feedback=feedback) floodedZonesAreaLayer = result["OUTPUT"] #add the "Flooded Area" layer to the layers panel #first add the layer without showing it QgsProject.instance().addMapLayer(floodedZonesAreaLayer, False) #obtain the layer tree of the top-level group in the project layerTree = iface.layerTreeCanvasBridge().rootGroup() #insert the layer - the position is a number starting from 0, with -1 an alias for the end layerTree.insertChildNode(-1, QgsLayerTreeLayer(floodedZonesAreaLayer)) #customise the symbology for the "Flooded Area" layer layer = QgsProject.instance().mapLayersByName("Flooded Area")[0] single_symbol_renderer = layer.renderer() symbol = single_symbol_renderer.symbol() symbol.setColor(QColor.fromRgb(150, 206, 250)) symbol.symbolLayer(0).setStrokeColor(QColor.fromRgb(70, 130, 180)) symbol.setOpacity(0.3) layer.triggerRepaint() qgis.utils.iface.layerTreeView().refreshLayerSymbology(layer.id()) #check if script has been cancelled before next stage if feedback.isCanceled(): return{} #output current stage of script feedback.pushInfo('--------------------------------------------------------------------------') feedback.pushInfo("4/5 - Calculating Flood-Affected Private Parcels...") feedback.pushInfo('--------------------------------------------------------------------------') #get all flood-affected parcels result = processing.run('native:extractbylocation', { 'INPUT': parcelsLayer, 'INTERSECT': floodCleanedLayer, 'METHOD': 0, 'OUTPUT': 'memory:', 'PREDICATE': [0] }, context=context, feedback=feedback) floodAffectedParcelsOriginal = result["OUTPUT"] #create inside buffer on zones layer (to avoid zones touching other parcels when intersecting) result = processing.run('native:buffer', { 'DISSOLVE': False, 'DISTANCE': -1, 'END_CAP_STYLE': 0, 'INPUT': zonesLayer, 'JOIN_STYLE': 0, 'MITER_LIMIT': 2, 'OUTPUT' : 'memory:', 'SEGMENTS': 200 }, context=context, feedback=feedback) zonesLayerClean = result["OUTPUT"] #exclude irrelevant zones (public zones, PZ and UFZ) request = QgsFeatureRequest().setFilterExpression("\"ZONE_CLASS\" = \'PCRZ\' OR \"ZONE_CLASS\" = \'PPRZ\' OR \"ZONE_CLASS\" = \'PUZ\' OR \"ZONE_CLASS\" = \'RDZ\' OR \"ZONE_CLASS\" = \'CA\' OR \"ZONE_CLASS\" = \'PZ\' OR \"ZONE_CLASS\" = \'UFZ\'") ids = [f.id() for f in zonesLayerClean.getFeatures(request)] zonesLayerClean.startEditing() for fid in ids: zonesLayerClean.deleteFeature(fid) zonesLayerClean.commitChanges() #add zone data to parcels result = processing.run('qgis:joinattributesbylocation', { 'DISCARD_NONMATCHING': True, 'INPUT': floodAffectedParcelsOriginal, 'JOIN': zonesLayerClean, 'METHOD': 1, 'OUTPUT': 'memory:', 'PREDICATE': [0] }, context=context, feedback=feedback) floodAffectedParcelsAll = result["OUTPUT"] #delete parcels with duplicate geometries - a work-around because 'qgis:deleteduplicategeometries' was causing crashes on return{} result = processing.run('qgis:exportaddgeometrycolumns', { 'CALC_METHOD': 0, 'INPUT': floodAffectedParcelsAll, 'OUTPUT': 'memory:' }, context=context, feedback=feedback) floodAffectedParcelsArea = result["OUTPUT"] result = processing.run('native:removeduplicatesbyattribute', { 'FIELDS': ['area','perimeter'], 'INPUT': floodAffectedParcelsArea, 'OUTPUT': 'memory:' }, context=context, feedback=feedback) floodAffectedParcels = result["OUTPUT"] #delete irrelevant parcels by attribute #"PC_LOTNO" LIKE 'CM%' = driveways, carparking and building surrounds #"PC_LOTNO" LIKE 'R%' = park reserves #"PC_STAT" = 'P' = proposed parcels #"PC_CRSTAT" = 'C' = crown parcels #"PC_CRSTAT" = 'G' = road reserves #"PC_SPIC" = '200' = shared driveways request = QgsFeatureRequest().setFilterExpression("\"PC_LOTNO\" LIKE \'CM%\' OR \"PC_LOTNO\" LIKE \'R%\' OR \"PC_STAT\" = \'P\' OR \"PC_CRSTAT\" = \'C\' OR \"PC_CRSTAT\" = \'G\' OR \"PC_SPIC\" = \'200\'") ids = [f.id() for f in floodAffectedParcels.getFeatures(request)] floodAffectedParcels.startEditing() for fid in ids: floodAffectedParcels.deleteFeature(fid) floodAffectedParcels.commitChanges() #remove address points without a specified address number result = processing.run('native:extractbyexpression', {'EXPRESSION': '(\"BUNIT_ID1\" != \'0\' OR \"BUNIT_ID2\" != \'0\' OR \"FLOOR_NO_1\" != \'0\' OR \"FLOOR_NO_2\" != \'0\' OR \"HSE_NUM1\" != \'0\' OR \"HSE_NUM2\" != \'0\' OR \"DISP_NUM1\" != \'0\' OR \"DISP_NUM2\" != \'0\')', 'INPUT': addressLayer, 'OUTPUT': 'memory:' }, context=context, feedback=feedback) addressLayerClean = result["OUTPUT"] #get parcels with a valid address point result = processing.run('native:extractbylocation', { 'INPUT': floodAffectedParcels, 'INTERSECT': addressLayerClean, 'METHOD': 0, 'OUTPUT': 'memory:', 'PREDICATE': [0] }, context=context, feedback=feedback) floodAffectedPrivateParcels = result["OUTPUT"] #remove parcels under 40sqm (indicates it is not a regular private land parcel) result = processing.run('native:extractbyexpression', { 'EXPRESSION': '(\"area\" > \'40\')', 'INPUT': floodAffectedPrivateParcels, 'OUTPUT': 'memory:' }, context=context, feedback=feedback) finalParcelsAreaClean = result["OUTPUT"] #delete parcels with inner rings (indicates it is not a regular private land parcel) #add "POLY_RING" field to parcels layer finalParcelsAreaClean.startEditing() finalParcelsAreaClean.dataProvider().addAttributes([QgsField("POLY_RING", QVariant.Double)]) finalParcelsAreaClean.updateFields() finalParcelsAreaClean.commitChanges() #add "POLY_RING" attribute for each feature (count rings for each polygon within feature geometry) pFeatures = finalParcelsAreaClean.getFeatures() finalParcelsAreaClean.startEditing() for feature in pFeatures: geometry = feature.geometry() polyCount = 0 ringCount = 0 if geometry.isMultipart(): polygons = geometry.asMultiPolygon() else: polygons = geometry.asPolygon() for polygon in polygons: polyCount = polyCount + 1 for ring in polygon: ringCount = ringCount + 1 count = (ringCount/polyCount) feature["POLY_RING"] = count finalParcelsAreaClean.updateFeature(feature) finalParcelsAreaClean.commitChanges() #delete features with more rings than polygons (indicates it has an inner ring) request = QgsFeatureRequest().setFilterExpression("\"POLY_RING\" > \'1\'") ids = [f.id() for f in finalParcelsAreaClean.getFeatures(request)] finalParcelsAreaClean.startEditing() for fid in ids: finalParcelsAreaClean.deleteFeature(fid) finalParcelsAreaClean.commitChanges() #get relevant flood control overlays result = processing.run('native:extractbyexpression', { 'EXPRESSION': '(\"ZONE_CODE\" = \'SBO\' OR \"ZONE_CODE\" = \'LSIO\' OR \"ZONE_CODE\" = \'FO\')', 'INPUT': overlaysLayer, 'OUTPUT': 'memory:' }, context=context, feedback=feedback) relevantOverlays = result["OUTPUT"] #assign parcels with relevant flood control overlays (if intersection) result = processing.run('qgis:joinattributesbylocation', { 'DISCARD_NONMATCHING': False, 'INPUT': finalParcelsAreaClean, 'JOIN': relevantOverlays, 'METHOD': 1, 'OUTPUT': 'memory:Flood-Affected Private Parcels', 'PREDICATE': [0] }, context=context, feedback=feedback) finalParcels = result["OUTPUT"] #add the "Flood-Affected Private Parcels" layer to the layers panel #first add the layer without showing it QgsProject.instance().addMapLayer(finalParcels, False) #obtain the layer tree of the top-level group in the project layerTree = iface.layerTreeCanvasBridge().rootGroup() #insert the layer - the position is a number starting from 0, with -1 an alias for the end layerTree.insertChildNode(-1, QgsLayerTreeLayer(finalParcels)) #customise the symbology for the "Flood-Affected Private Parcels" layer layer = QgsProject.instance().mapLayersByName("Flood-Affected Private Parcels")[0] single_symbol_renderer = layer.renderer() symbol = single_symbol_renderer.symbol() symbol.setColor(QColor.fromRgb(225, 225, 225)) symbol.symbolLayer(0).setStrokeColor(QColor.fromRgb(115, 115, 115)) layer.triggerRepaint() qgis.utils.iface.layerTreeView().refreshLayerSymbology(layer.id()) #check if script has been cancelled before next stage if feedback.isCanceled(): return{} #output current stage of script feedback.pushInfo('--------------------------------------------------------------------------') feedback.pushInfo("5/5 - Calculating statistics...") feedback.pushInfo('--------------------------------------------------------------------------') feedback.pushInfo("FLOODED AREA (sqm):") feedback.pushInfo('--------------------------------------------------------------------------') #get lga area lgaFeatures = lgaAreaLayer.getFeatures() for feature in lgaFeatures: lgaArea = feature["area"] #get flooded lga area lgaFloodArea = 0 zoneFeatures = floodedZonesAreaLayer.getFeatures() for feature in zoneFeatures: zoneArea = feature["area"] lgaFloodArea = lgaFloodArea + zoneArea #get flooded area percentage floodPercent = ((lgaFloodArea/lgaArea)*100) #ouput flooded lga area and percentage feedback.pushInfo('Flooded Area: ' + (str(round(lgaFloodArea, 2)))) feedback.pushInfo((str(round(floodPercent, 2))) + '% of LGA Area') feedback.pushInfo('BY ZONE:') feedback.pushInfo('---------------------------------') #get and output flooded area for each "ZONE_CLASS" request = QgsFeatureRequest() clause = QgsFeatureRequest.OrderByClause('area', ascending=False) orderby = QgsFeatureRequest.OrderBy([clause]) request.setOrderBy(orderby) zoneFeatures = floodedZonesAreaLayer.getFeatures(request) for feature in zoneFeatures: zoneName = feature["ZONE_CLASS"] zoneArea = feature["area"] feedback.pushInfo((zoneName) + ': ' + (str(round(zoneArea, 2)))) lgaFloodArea = lgaFloodArea + zoneArea #output current stage of script feedback.pushInfo('--------------------------------------------------------------------------') feedback.pushInfo("FLOOD-AFFECTED PRIVATE PARCELS:") feedback.pushInfo('--------------------------------------------------------------------------') #get counts for ALL flood-affected private parcels and their flood overlays processing.run('qgis:selectbyexpression', { 'EXPRESSION': '(\"area\" > \'0\')', 'INPUT': finalParcels, 'METHOD': 0 }, context=context) allCount = finalParcels.selectedFeatureCount() processing.run('qgis:selectbyexpression', { 'EXPRESSION': '(\"ZONE_CODE_2\" = \'SBO\')', 'INPUT': finalParcels, 'METHOD': 0 }, context=context) allCountSBO = finalParcels.selectedFeatureCount() processing.run('qgis:selectbyexpression', { 'EXPRESSION': '(\"ZONE_CODE_2\" = \'LSIO\')', 'INPUT': finalParcels, 'METHOD': 0 }, context=context) allCountLSIO = finalParcels.selectedFeatureCount() processing.run('qgis:selectbyexpression', { 'EXPRESSION': '(\"ZONE_CODE_2\" = \'FO\')', 'INPUT': finalParcels, 'METHOD': 0 }, context=context) allCountFO = finalParcels.selectedFeatureCount() #output total parcel counts and by flood overlay feedback.pushInfo('Flood-Affected Private Parcels: ' + (str(allCount))) feedback.pushInfo('With SBO Overlay: ' + (str(allCountSBO))) feedback.pushInfo('With LSIO Overlay: ' + (str(allCountLSIO))) feedback.pushInfo('With FO Overlay: ' + (str(allCountFO))) feedback.pushInfo('BY ZONE:') feedback.pushInfo('---------------------------------') #get counts for GRZ flood-affected private parcels and their flood overlays (if applicable) processing.run('qgis:selectbyexpression', { 'EXPRESSION': '(\"ZONE_CLASS\" = \'GRZ\')', 'INPUT': finalParcels, 'METHOD': 0 }, context=context) grzCount = finalParcels.selectedFeatureCount() if grzCount > 0: processing.run('qgis:selectbyexpression', { 'EXPRESSION': '(\"ZONE_CLASS\" = \'GRZ\' AND \"ZONE_CODE_2\" = \'SBO\')', 'INPUT': finalParcels, 'METHOD': 0 }, context=context) grzCountSBO = finalParcels.selectedFeatureCount() processing.run('qgis:selectbyexpression', { 'EXPRESSION': '(\"ZONE_CLASS\" = \'GRZ\' AND \"ZONE_CODE_2\" = \'LSIO\')', 'INPUT': finalParcels, 'METHOD': 0 }, context=context) grzCountLSIO = finalParcels.selectedFeatureCount() processing.run('qgis:selectbyexpression', { 'EXPRESSION': '(\"ZONE_CLASS\" = \'GRZ\' AND \"ZONE_CODE_2\" = \'FO\')', 'INPUT': finalParcels, 'METHOD': 0 }, context=context) grzCountFO = finalParcels.selectedFeatureCount() #output total parcel counts and by flood overlay feedback.pushInfo('Total General Residential (GRZ): ' + (str(grzCount))) feedback.pushInfo('With SBO Overlay: ' + (str(grzCountSBO))) feedback.pushInfo('With LSIO Overlay: ' + (str(grzCountLSIO))) feedback.pushInfo('With FO Overlay: ' + (str(grzCountFO))) feedback.pushInfo('---------------------------------') #get counts for RZ flood-affected private parcels and thier flood overlays (if applicable) processing.run('qgis:selectbyexpression', { 'EXPRESSION': '(\"ZONE_CLASS\" = \'RZ\')', 'INPUT': finalParcels, 'METHOD': 0 }, context=context) rzCount = finalParcels.selectedFeatureCount() if rzCount > 0: processing.run('qgis:selectbyexpression', { 'EXPRESSION': '(\"ZONE_CLASS\" = \'RZ\' AND \"ZONE_CODE_2\" = \'SBO\')', 'INPUT': finalParcels, 'METHOD': 0 }, context=context) rzCountSBO = finalParcels.selectedFeatureCount() processing.run('qgis:selectbyexpression', { 'EXPRESSION': '(\"ZONE_CLASS\" = \'RZ\' AND \"ZONE_CODE_2\" = \'LSIO\')', 'INPUT': finalParcels, 'METHOD': 0 }, context=context) rzCountLSIO = finalParcels.selectedFeatureCount() processing.run('qgis:selectbyexpression', { 'EXPRESSION': '(\"ZONE_CLASS\" = \'RZ\' AND \"ZONE_CODE_2\" = \'FO\')', 'INPUT': finalParcels, 'METHOD': 0 }, context=context) rzCountFO = finalParcels.selectedFeatureCount() #output total parcel counts and by flood overlay feedback.pushInfo('Total Residential (RZ): ' + (str(rzCount))) feedback.pushInfo('With SBO Overlay: ' + (str(rzCountSBO))) feedback.pushInfo('With LSIO Overlay: ' + (str(rzCountLSIO))) feedback.pushInfo('With FO Overlay: ' + (str(rzCountFO))) feedback.pushInfo('---------------------------------') #get counts for RGZ flood-affected private parcels and their flood overlays (if applicable) processing.run('qgis:selectbyexpression', { 'EXPRESSION': '(\"ZONE_CLASS\" = \'RGZ\')', 'INPUT': finalParcels, 'METHOD': 0 }, context=context) rgzCount = finalParcels.selectedFeatureCount() if rgzCount > 0: processing.run('qgis:selectbyexpression', { 'EXPRESSION': '(\"ZONE_CLASS\" = \'RGZ\' AND \"ZONE_CODE_2\" = \'SBO\')', 'INPUT': finalParcels, 'METHOD': 0 }, context=context) rgzCountSBO = finalParcels.selectedFeatureCount() processing.run('qgis:selectbyexpression', { 'EXPRESSION': '(\"ZONE_CLASS\" = \'RGZ\' AND \"ZONE_CODE_2\" = \'LSIO\')', 'INPUT': finalParcels, 'METHOD': 0 }, context=context) rgzCountLSIO = finalParcels.selectedFeatureCount() processing.run('qgis:selectbyexpression', { 'EXPRESSION': '(\"ZONE_CLASS\" = \'RGZ\' AND \"ZONE_CODE_2\" = \'FO\')', 'INPUT': finalParcels, 'METHOD': 0 }, context=context) rgzCountFO = finalParcels.selectedFeatureCount() #output total parcel counts and by flood overlay feedback.pushInfo('Total Residential Growth (RGZ): ' + (str(rgzCount))) feedback.pushInfo('With SBO Overlay: ' + (str(rgzCountSBO))) feedback.pushInfo('With LSIO Overlay: ' + (str(rgzCountLSIO))) feedback.pushInfo('With FO Overlay: ' + (str(rgzCountFO))) feedback.pushInfo('---------------------------------') #get counts for MUZ flood-affected private parcels and their flood overlays (if applicable) processing.run('qgis:selectbyexpression', { 'EXPRESSION': '(\"ZONE_CLASS\" = \'MUZ\')', 'INPUT': finalParcels, 'METHOD': 0 }, context=context) muzCount = finalParcels.selectedFeatureCount() if muzCount > 0: processing.run('qgis:selectbyexpression', { 'EXPRESSION': '(\"ZONE_CLASS\" = \'MUZ\' AND \"ZONE_CODE_2\" = \'SBO\')', 'INPUT': finalParcels, 'METHOD': 0 }, context=context) muzCountSBO = finalParcels.selectedFeatureCount() processing.run('qgis:selectbyexpression', { 'EXPRESSION': '(\"ZONE_CLASS\" = \'MUZ\' AND \"ZONE_CODE_2\" = \'LSIO\')', 'INPUT': finalParcels, 'METHOD': 0 }, context=context) muzCountLSIO = finalParcels.selectedFeatureCount() processing.run('qgis:selectbyexpression', { 'EXPRESSION': '(\"ZONE_CLASS\" = \'MUZ\' AND \"ZONE_CODE_2\" = \'FO\')', 'INPUT': finalParcels, 'METHOD': 0 }, context=context) muzCountFO = finalParcels.selectedFeatureCount() #output total parcel counts and by flood overlay feedback.pushInfo('Total Mixed Use (MUZ): ' + (str(muzCount))) feedback.pushInfo('With SBO Overlay: ' + (str(muzCountSBO))) feedback.pushInfo('With LSIO Overlay: ' + (str(muzCountLSIO))) feedback.pushInfo('With FO Overlay: ' + (str(muzCountFO))) feedback.pushInfo('---------------------------------') #get counts for CZ flood-affected private parcels and their flood overlays (if applicable) processing.run('qgis:selectbyexpression', { 'EXPRESSION': '(\"ZONE_CLASS\" = \'CZ\')', 'INPUT': finalParcels, 'METHOD': 0 }, context=context) czCount = finalParcels.selectedFeatureCount() if czCount > 0: processing.run('qgis:selectbyexpression', { 'EXPRESSION': '(\"ZONE_CLASS\" = \'CZ\' AND \"ZONE_CODE_2\" = \'SBO\')', 'INPUT': finalParcels, 'METHOD': 0 }, context=context) czCountSBO = finalParcels.selectedFeatureCount() processing.run('qgis:selectbyexpression', { 'EXPRESSION': '(\"ZONE_CLASS\" = \'CZ\' AND \"ZONE_CODE_2\" = \'LSIO\')', 'INPUT': finalParcels, 'METHOD': 0 }, context=context) czCountLSIO = finalParcels.selectedFeatureCount() processing.run('qgis:selectbyexpression', { 'EXPRESSION': '(\"ZONE_CLASS\" = \'CZ\' AND \"ZONE_CODE_2\" = \'FO\')', 'INPUT': finalParcels, 'METHOD': 0 }, context=context) czCountFO = finalParcels.selectedFeatureCount() #output total parcel counts and by flood overlay feedback.pushInfo('Total Commercial (CZ): ' + (str(czCount))) feedback.pushInfo('With SBO Overlay: ' + (str(czCountSBO))) feedback.pushInfo('With LSIO Overlay: ' + (str(czCountLSIO))) feedback.pushInfo('With FO Overlay: ' + (str(czCountFO))) feedback.pushInfo('---------------------------------') #get counts for BZ flood-affected private parcels and their flood overlays (if applicable) processing.run('qgis:selectbyexpression', { 'EXPRESSION': '(\"ZONE_CLASS\" = \'BZ\')', 'INPUT': finalParcels, 'METHOD': 0 }, context=context) bzCount = finalParcels.selectedFeatureCount() if bzCount > 0: processing.run('qgis:selectbyexpression', { 'EXPRESSION': '(\"ZONE_CLASS\" = \'BZ\' AND \"ZONE_CODE_2\" = \'SBO\')', 'INPUT': finalParcels, 'METHOD': 0 }, context=context) bzCountSBO = finalParcels.selectedFeatureCount() processing.run('qgis:selectbyexpression', { 'EXPRESSION': '(\"ZONE_CLASS\" = \'BZ\' AND \"ZONE_CODE_2\" = \'LSIO\')', 'INPUT': finalParcels, 'METHOD': 0 }, context=context) bzCountLSIO = finalParcels.selectedFeatureCount() processing.run('qgis:selectbyexpression', { 'EXPRESSION': '(\"ZONE_CLASS\" = \'BZ\' AND \"ZONE_CODE_2\" = \'FO\')', 'INPUT': finalParcels, 'METHOD': 0 }, context=context) bzCountFO = finalParcels.selectedFeatureCount() #output total parcel counts and by flood overlay feedback.pushInfo('Total Commercial (BZ): ' + (str(bzCount))) feedback.pushInfo('With SBO Overlay: ' + (str(bzCountSBO))) feedback.pushInfo('With LSIO Overlay: ' + (str(bzCountLSIO))) feedback.pushInfo('With FO Overlay: ' + (str(bzCountFO))) feedback.pushInfo('---------------------------------') #get counts for INZ flood-affected private parcels and their flood overlays (if applicable) processing.run('qgis:selectbyexpression', { 'EXPRESSION': '(\"ZONE_CLASS\" = \'INZ\')', 'INPUT': finalParcels, 'METHOD': 0 }, context=context) inzCount = finalParcels.selectedFeatureCount() if inzCount > 0: processing.run('qgis:selectbyexpression', { 'EXPRESSION': '(\"ZONE_CLASS\" = \'INZ\' AND \"ZONE_CODE_2\" = \'SBO\')', 'INPUT': finalParcels, 'METHOD': 0 }, context=context) inzCountSBO = finalParcels.selectedFeatureCount() processing.run('qgis:selectbyexpression', { 'EXPRESSION': '(\"ZONE_CLASS\" = \'INZ\' AND \"ZONE_CODE_2\" = \'LSIO\')', 'INPUT': finalParcels, 'METHOD': 0 }, context=context) inzCountLSIO = finalParcels.selectedFeatureCount() processing.run('qgis:selectbyexpression', { 'EXPRESSION': '(\"ZONE_CLASS\" = \'INZ\' AND \"ZONE_CODE_2\" = \'FO\')', 'INPUT': finalParcels, 'METHOD': 0 }, context=context) inzCountFO = finalParcels.selectedFeatureCount() #output total parcel counts and by flood overlay feedback.pushInfo('Total Industrial (INZ): ' + (str(inzCount))) feedback.pushInfo('With SBO Overlay: ' + (str(inzCountSBO))) feedback.pushInfo('With LSIO Overlay: ' + (str(inzCountLSIO))) feedback.pushInfo('With FO Overlay: ' + (str(inzCountFO))) feedback.pushInfo('---------------------------------') #get counts for SUZ flood-affected private parcels and their flood overlays (if applicable) processing.run('qgis:selectbyexpression', { 'EXPRESSION': '(\"ZONE_CLASS\" = \'SUZ\')', 'INPUT': finalParcels, 'METHOD': 0 }, context=context) suzCount = finalParcels.selectedFeatureCount() if suzCount > 0: processing.run('qgis:selectbyexpression', { 'EXPRESSION': '(\"ZONE_CLASS\" = \'SUZ\' AND \"ZONE_CODE_2\" = \'SBO\')', 'INPUT': finalParcels, 'METHOD': 0 }, context=context) suzCountSBO = finalParcels.selectedFeatureCount() processing.run('qgis:selectbyexpression', { 'EXPRESSION': '(\"ZONE_CLASS\" = \'SUZ\' AND \"ZONE_CODE_2\" = \'LSIO\')', 'INPUT': finalParcels, 'METHOD': 0 }, context=context) suzCountLSIO = finalParcels.selectedFeatureCount() processing.run('qgis:selectbyexpression', { 'EXPRESSION': '(\"ZONE_CLASS\" = \'SUZ\' AND \"ZONE_CODE_2\" = \'FO\')', 'INPUT': finalParcels, 'METHOD': 0 }, context=context) suzCountFO = finalParcels.selectedFeatureCount() #output total parcel counts and by flood overlay feedback.pushInfo('Total Special Use (SUZ): ' + (str(suzCount))) feedback.pushInfo('With SBO Overlay: ' + (str(suzCountSBO))) feedback.pushInfo('With LSIO Overlay: ' + (str(suzCountLSIO))) feedback.pushInfo('With FO Overlay: ' + (str(suzCountFO))) feedback.pushInfo('---------------------------------') #get counts for CDZ flood-affected private parcels and their flood overlays (if applicable) processing.run('qgis:selectbyexpression', { 'EXPRESSION': '(\"ZONE_CLASS\" = \'CDZ\')', 'INPUT': finalParcels, 'METHOD': 0 }, context=context) cdzCount = finalParcels.selectedFeatureCount() if cdzCount > 0: processing.run('qgis:selectbyexpression', { 'EXPRESSION': '(\"ZONE_CLASS\" = \'CDZ\' AND \"ZONE_CODE_2\" = \'SBO\')', 'INPUT': finalParcels, 'METHOD': 0 }, context=context) cdzCountSBO = finalParcels.selectedFeatureCount() processing.run('qgis:selectbyexpression', { 'EXPRESSION': '(\"ZONE_CLASS\" = \'CDZ\' AND \"ZONE_CODE_2\" = \'LSIO\')', 'INPUT': finalParcels, 'METHOD': 0 }, context=context) cdzCountLSIO = finalParcels.selectedFeatureCount() processing.run('qgis:selectbyexpression', { 'EXPRESSION': '(\"ZONE_CLASS\" = \'CDZ\' AND \"ZONE_CODE_2\" = \'FO\')', 'INPUT': finalParcels, 'METHOD': 0 }, context=context) cdzCountFO = finalParcels.selectedFeatureCount() #output total parcel counts and by flood overlay feedback.pushInfo('Total Comprehensive Development (CDZ): ' + (str(cdzCount))) feedback.pushInfo('With SBO Overlay: ' + (str(cdzCountSBO))) feedback.pushInfo('With LSIO Overlay: ' + (str(cdzCountLSIO))) feedback.pushInfo('With FO Overlay: ' + (str(cdzCountFO))) feedback.pushInfo('---------------------------------') #end the script return{}