def processAlgorithm(self, parameters, context, feedback): source = self.parameterAsSource(parameters, self.INPUT, context) if source is None: raise QgsProcessingException(self.invalidSourceError(parameters, self.INPUT)) if self.parameterAsBool(parameters, self.KEEP_FIELDS, context): fields = source.fields() else: fields = QgsFields() (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, QgsWkbTypes.Polygon, source.sourceCrs()) if sink is None: raise QgsProcessingException(self.invalidSinkError(parameters, self.OUTPUT)) allLinesList = [] features = source.getFeatures(QgsFeatureRequest().setSubsetOfAttributes([])) feedback.pushInfo(QCoreApplication.translate('Polygonize', 'Processing lines…')) total = (40.0 / source.featureCount()) if source.featureCount() else 1 for current, inFeat in enumerate(features): if feedback.isCanceled(): break if inFeat.geometry(): allLinesList.append(inFeat.geometry()) feedback.setProgress(int(current * total)) feedback.setProgress(40) feedback.pushInfo(QCoreApplication.translate('Polygonize', 'Noding lines…')) allLines = QgsGeometry.unaryUnion(allLinesList) if feedback.isCanceled(): return {} feedback.setProgress(45) feedback.pushInfo(QCoreApplication.translate('Polygonize', 'Polygonizing…')) polygons = QgsGeometry.polygonize([allLines]) if polygons.isEmpty(): feedback.reportError(self.tr('No polygons were created!')) feedback.setProgress(50) if not polygons.isEmpty(): feedback.pushInfo(QCoreApplication.translate('Polygonize', 'Saving polygons…')) total = 50.0 / polygons.constGet().numGeometries() for i in range(polygons.constGet().numGeometries()): if feedback.isCanceled(): break outFeat = QgsFeature() geom = QgsGeometry(polygons.constGet().geometryN(i).clone()) outFeat.setGeometry(geom) sink.addFeature(outFeat, QgsFeatureSink.FastInsert) feedback.setProgress(50 + int(current * total)) return {self.OUTPUT: dest_id}
def buffering(feedback, context, writer, distance, field, useField, layer, dissolve, segments, endCapStyle=1, joinStyle=1, mitreLimit=2): if useField: field = layer.fields().lookupField(field) outFeat = QgsFeature() current = 0 features = QgsProcessingUtils.getFeatures(layer, context) total = 100.0 / QgsProcessingUtils.featureCount(layer, context) # With dissolve if dissolve: buffered_geometries = [] for inFeat in features: attrs = inFeat.attributes() if useField: value = attrs[field] else: value = distance inGeom = inFeat.geometry() buffered_geometries.append(inGeom.buffer(float(value), segments, endCapStyle, joinStyle, mitreLimit)) current += 1 feedback.setProgress(int(current * total)) final_geometry = QgsGeometry.unaryUnion(buffered_geometries) outFeat.setGeometry(final_geometry) outFeat.setAttributes(attrs) writer.addFeature(outFeat) else: # Without dissolve for inFeat in features: attrs = inFeat.attributes() if useField: value = attrs[field] else: value = distance inGeom = inFeat.geometry() outFeat = QgsFeature() outGeom = inGeom.buffer(float(value), segments, endCapStyle, joinStyle, mitreLimit) outFeat.setGeometry(outGeom) outFeat.setAttributes(attrs) writer.addFeature(outFeat) current += 1 feedback.setProgress(int(current * total)) del writer
def processAlgorithm(self, parameters, context, feedback): vlayer = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.INPUT), context) output = self.getOutputFromName(self.OUTPUT) if self.getParameterValue(self.FIELDS): fields = vlayer.fields() else: fields = QgsFields() if self.getParameterValue(self.GEOMETRY): fieldsCount = fields.count() fields.append(QgsField('area', QVariant.Double, 'double', 16, 2)) fields.append(QgsField('perimeter', QVariant.Double, 'double', 16, 2)) allLinesList = [] features = QgsProcessingUtils.getFeatures(vlayer, context, QgsFeatureRequest().setSubsetOfAttributes([])) feedback.pushInfo(self.tr('Processing lines...')) total = 40.0 / QgsProcessingUtils.featureCount(vlayer, context) for current, inFeat in enumerate(features): if inFeat.geometry(): allLinesList.append(inFeat.geometry()) feedback.setProgress(int(current * total)) feedback.setProgress(40) feedback.pushInfo(self.tr('Noding lines...')) allLines = QgsGeometry.unaryUnion(allLinesList) feedback.setProgress(45) feedback.pushInfo(self.tr('Polygonizing...')) polygons = QgsGeometry.polygonize([allLines]) if polygons.isEmpty(): raise GeoAlgorithmExecutionException(self.tr('No polygons were created!')) feedback.setProgress(50) feedback.pushInfo('Saving polygons...') writer = output.getVectorWriter(fields, QgsWkbTypes.Polygon, vlayer.crs(), context) total = 50.0 / polygons.geometry().numGeometries() for i in range(polygons.geometry().numGeometries()): outFeat = QgsFeature() geom = QgsGeometry(polygons.geometry().geometryN(i).clone()) outFeat.setGeometry(geom) if self.getParameterValue(self.GEOMETRY): outFeat.setAttributes([None] * fieldsCount + [geom.geometry().area(), geom.geometry().perimeter()]) writer.addFeature(outFeat, QgsFeatureSink.FastInsert) feedback.setProgress(50 + int(current * total)) del writer
def _union_geometries(geometries): """ Return a geometry which is union of the passed list of geometries. :param geometries: Geometries for the union operation. :type geometries: list :returns: union of geometries :rtype: QgsGeometry """ if QGis.QGIS_VERSION_INT >= 20400: # woohoo we can use fast union (needs GEOS >= 3.3) return QgsGeometry.unaryUnion(geometries) else: # uhh we need to use slow iterative union if len(geometries) == 0: return QgsGeometry() result_geometry = QgsGeometry(geometries[0]) for g in geometries[1:]: result_geometry = result_geometry.combine(g) return result_geometry
def buffering(progress, writer, distance, field, useField, layer, dissolve, segments, endCapStyle=1, joinStyle=1, mitreLimit=2): if useField: field = layer.fields().lookupField(field) outFeat = QgsFeature() current = 0 features = vector.features(layer) total = 100.0 / float(len(features)) # With dissolve if dissolve: first = True buffered_geometries = [] for inFeat in features: attrs = inFeat.attributes() if useField: value = attrs[field] else: value = distance inGeom = inFeat.geometry() if not inGeom: ProcessingLog.addToLog(ProcessingLog.LOG_WARNING, 'Feature {} has empty geometry. Skipping...'.format(inFeat.id())) continue if not inGeom.isGeosValid(): ProcessingLog.addToLog(ProcessingLog.LOG_WARNING, 'Feature {} has invalid geometry. Skipping...'.format(inFeat.id())) continue buffered_geometries.append(inGeom.buffer(float(value), segments, endCapStyle, joinStyle, mitreLimit)) current += 1 progress.setPercentage(int(current * total)) final_geometry = QgsGeometry.unaryUnion(buffered_geometries) outFeat.setGeometry(final_geometry) outFeat.setAttributes(attrs) writer.addFeature(outFeat) else: # Without dissolve for inFeat in features: attrs = inFeat.attributes() if useField: value = attrs[field] else: value = distance inGeom = inFeat.geometry() outFeat = QgsFeature() if inGeom.isEmpty() or inGeom.isGeosEmpty(): pass elif not inGeom.isGeosValid(): ProcessingLog.addToLog(ProcessingLog.LOG_WARNING, 'Feature {} has invalid geometry. Skipping...'.format(inFeat.id())) continue else: outGeom = inGeom.buffer(float(value), segments, endCapStyle, joinStyle, mitreLimit) outFeat.setGeometry(outGeom) outFeat.setAttributes(attrs) writer.addFeature(outFeat) current += 1 progress.setPercentage(int(current * total)) del writer
def processAlgorithm(self, progress): useField = not self.getParameterValue(Dissolve.DISSOLVE_ALL) field_names = self.getParameterValue(Dissolve.FIELD) vlayerA = dataobjects.getObjectFromUri( self.getParameterValue(Dissolve.INPUT)) writer = self.getOutputFromName( Dissolve.OUTPUT).getVectorWriter( vlayerA.fields().toList(), vlayerA.wkbType(), vlayerA.crs()) outFeat = QgsFeature() features = vector.features(vlayerA) total = 100.0 / len(features) if not useField: first = True # we dissolve geometries in blocks using unaryUnion geom_queue = [] for current, inFeat in enumerate(features): progress.setPercentage(int(current * total)) if first: outFeat.setAttributes(inFeat.attributes()) first = False tmpInGeom = inFeat.geometry() if tmpInGeom.isEmpty() or tmpInGeom.isGeosEmpty(): continue errors = tmpInGeom.validateGeometry() if len(errors) != 0: for error in errors: ProcessingLog.addToLog(ProcessingLog.LOG_ERROR, self.tr('ValidateGeometry()' 'error: One or more ' 'input features have ' 'invalid geometry: ') + error.what()) continue geom_queue.append(tmpInGeom) if len(geom_queue) > 10000: # queue too long, combine it try: temp_output_geometry = QgsGeometry.unaryUnion(geom_queue) geom_queue = [temp_output_geometry] except: raise GeoAlgorithmExecutionException( self.tr('Geometry exception while dissolving')) try: outFeat.setGeometry(QgsGeometry.unaryUnion(geom_queue)) except: raise GeoAlgorithmExecutionException( self.tr('Geometry exception while dissolving')) writer.addFeature(outFeat) else: field_indexes = [vlayerA.fields().lookupField(f) for f in field_names.split(';')] attribute_dict = {} geometry_dict = defaultdict(lambda: []) for inFeat in features: attrs = inFeat.attributes() index_attrs = tuple([attrs[i] for i in field_indexes]) tmpInGeom = QgsGeometry(inFeat.geometry()) if tmpInGeom.isGeosEmpty(): continue errors = tmpInGeom.validateGeometry() if len(errors) != 0: for error in errors: ProcessingLog.addToLog(ProcessingLog.LOG_ERROR, self.tr('ValidateGeometry() ' 'error: One or more input' 'features have invalid ' 'geometry: ') + error.what()) if not index_attrs in attribute_dict: # keep attributes of first feature attribute_dict[index_attrs] = attrs geometry_dict[index_attrs].append(tmpInGeom) nFeat = len(attribute_dict) nElement = 0 for key, value in list(geometry_dict.items()): outFeat = QgsFeature() nElement += 1 progress.setPercentage(int(nElement * 100 / nFeat)) try: tmpOutGeom = QgsGeometry.unaryUnion(value) except: raise GeoAlgorithmExecutionException( self.tr('Geometry exception while dissolving')) outFeat.setGeometry(tmpOutGeom) outFeat.setAttributes(attribute_dict[key]) writer.addFeature(outFeat) del writer
def buffering(feedback, context, sink, distance, field, useField, source, dissolve, segments, endCapStyle=1, joinStyle=1, miterLimit=2): if useField: field = source.fields().lookupField(field) outFeat = QgsFeature() current = 0 total = 100.0 / source.featureCount() if source.featureCount() else 0 # With dissolve if dissolve: attributes_to_fetch = [] if useField: attributes_to_fetch.append(field) features = source.getFeatures(QgsFeatureRequest().setSubsetOfAttributes(attributes_to_fetch)) buffered_geometries = [] for inFeat in features: if feedback.isCanceled(): break attrs = inFeat.attributes() if useField: value = attrs[field] else: value = distance inGeom = inFeat.geometry() buffered_geometries.append(inGeom.buffer(float(value), segments, endCapStyle, joinStyle, miterLimit)) current += 1 feedback.setProgress(int(current * total)) final_geometry = QgsGeometry.unaryUnion(buffered_geometries) outFeat.setGeometry(final_geometry) outFeat.setAttributes(attrs) sink.addFeature(outFeat, QgsFeatureSink.FastInsert) else: features = source.getFeatures() # Without dissolve for inFeat in features: if feedback.isCanceled(): break attrs = inFeat.attributes() if useField: value = attrs[field] else: value = distance inGeom = inFeat.geometry() outFeat = QgsFeature() outGeom = inGeom.buffer(float(value), segments, endCapStyle, joinStyle, miterLimit) outFeat.setGeometry(outGeom) outFeat.setAttributes(attrs) sink.addFeature(outFeat, QgsFeatureSink.FastInsert) current += 1 feedback.setProgress(int(current * total))
def processAlgorithm(self, progress): useField = not self.getParameterValue(Dissolve.DISSOLVE_ALL) fieldname = self.getParameterValue(Dissolve.FIELD) vlayerA = dataobjects.getObjectFromUri( self.getParameterValue(Dissolve.INPUT)) vproviderA = vlayerA.dataProvider() fields = vlayerA.fields() writer = self.getOutputFromName( Dissolve.OUTPUT).getVectorWriter(fields, vproviderA.geometryType(), vproviderA.crs()) outFeat = QgsFeature() features = vector.features(vlayerA) total = 100.0 / len(features) if not useField: first = True for current, inFeat in enumerate(features): progress.setPercentage(int(current * total)) if first: attrs = inFeat.attributes() tmpInGeom = inFeat.geometry() if tmpInGeom.isEmpty() or tmpInGeom.isGeosEmpty(): continue errors = tmpInGeom.validateGeometry() if len(errors) != 0: for error in errors: ProcessingLog.addToLog(ProcessingLog.LOG_ERROR, self.tr('ValidateGeometry()' 'error: One or more ' 'input features have ' 'invalid geometry: ') + error.what()) continue outFeat.setGeometry(tmpInGeom) first = False else: tmpInGeom = inFeat.geometry() if tmpInGeom.isEmpty() or tmpInGeom.isGeosEmpty(): continue tmpOutGeom = outFeat.geometry() errors = tmpInGeom.validateGeometry() if len(errors) != 0: for error in errors: ProcessingLog.addToLog(ProcessingLog.LOG_ERROR, self.tr('ValidateGeometry()' 'error:One or more input' 'features have invalid ' 'geometry: ') + error.what()) continue try: tmpOutGeom = QgsGeometry(tmpOutGeom.combine(tmpInGeom)) outFeat.setGeometry(tmpOutGeom) except: raise GeoAlgorithmExecutionException( self.tr('Geometry exception while dissolving')) outFeat.setAttributes(attrs) writer.addFeature(outFeat) else: fieldIdx = vlayerA.fieldNameIndex(fieldname) unique = vector.getUniqueValues(vlayerA, int(fieldIdx)) nFeat = len(unique) myDict = {} attrDict = {} for item in unique: myDict[unicode(item).strip()] = [] attrDict[unicode(item).strip()] = None unique = None for inFeat in features: attrs = inFeat.attributes() tempItem = attrs[fieldIdx] tmpInGeom = QgsGeometry(inFeat.geometry()) if tmpInGeom.isGeosEmpty(): continue errors = tmpInGeom.validateGeometry() if len(errors) != 0: for error in errors: ProcessingLog.addToLog(ProcessingLog.LOG_ERROR, self.tr('ValidateGeometry() ' 'error: One or more input' 'features have invalid ' 'geometry: ') + error.what()) if attrDict[unicode(tempItem).strip()] is None: # keep attributes of first feature attrDict[unicode(tempItem).strip()] = attrs myDict[unicode(tempItem).strip()].append(tmpInGeom) features = None nElement = 0 for key, value in myDict.items(): nElement += 1 progress.setPercentage(int(nElement * 100 / nFeat)) try: tmpOutGeom = QgsGeometry.unaryUnion(value) except: raise GeoAlgorithmExecutionException( self.tr('Geometry exception while dissolving')) outFeat.setGeometry(tmpOutGeom) outFeat.setAttributes(attrDict[key]) writer.addFeature(outFeat) del writer
def buffering(feedback, writer, distance, field, useField, layer, dissolve, segments, endCapStyle=1, joinStyle=1, mitreLimit=2): if useField: field = layer.fields().lookupField(field) outFeat = QgsFeature() current = 0 features = vector.features(layer) total = 100.0 / float(len(features)) # With dissolve if dissolve: buffered_geometries = [] for inFeat in features: attrs = inFeat.attributes() if useField: value = attrs[field] else: value = distance inGeom = inFeat.geometry() buffered_geometries.append( inGeom.buffer(float(value), segments, endCapStyle, joinStyle, mitreLimit)) current += 1 feedback.setProgress(int(current * total)) final_geometry = QgsGeometry.unaryUnion(buffered_geometries) outFeat.setGeometry(final_geometry) outFeat.setAttributes(attrs) writer.addFeature(outFeat) else: # Without dissolve for inFeat in features: attrs = inFeat.attributes() if useField: value = attrs[field] else: value = distance inGeom = inFeat.geometry() outFeat = QgsFeature() outGeom = inGeom.buffer(float(value), segments, endCapStyle, joinStyle, mitreLimit) outFeat.setGeometry(outGeom) outFeat.setAttributes(attrs) writer.addFeature(outFeat) current += 1 feedback.setProgress(int(current * total)) del writer
def processAlgorithm(self, context, feedback): source_layer = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(Clip.INPUT), context) mask_layer = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(Clip.OVERLAY), context) writer = self.getOutputFromName(self.OUTPUT).getVectorWriter(source_layer.fields(), QgsWkbTypes.multiType(source_layer.wkbType()), source_layer.crs(), context) # first build up a list of clip geometries clip_geoms = [] for maskFeat in QgsProcessingUtils.getFeatures(mask_layer, context, QgsFeatureRequest().setSubsetOfAttributes([])): clip_geoms.append(maskFeat.geometry()) # are we clipping against a single feature? if so, we can show finer progress reports if len(clip_geoms) > 1: combined_clip_geom = QgsGeometry.unaryUnion(clip_geoms) single_clip_feature = False else: combined_clip_geom = clip_geoms[0] single_clip_feature = True # use prepared geometries for faster intersection tests engine = QgsGeometry.createGeometryEngine(combined_clip_geom.geometry()) engine.prepareGeometry() tested_feature_ids = set() for i, clip_geom in enumerate(clip_geoms): input_features = [f for f in QgsProcessingUtils.getFeatures(source_layer, context, QgsFeatureRequest().setFilterRect(clip_geom.boundingBox()))] if not input_features: continue if single_clip_feature: total = 100.0 / len(input_features) else: total = 0 for current, in_feat in enumerate(input_features): if not in_feat.geometry(): continue if in_feat.id() in tested_feature_ids: # don't retest a feature we have already checked continue tested_feature_ids.add(in_feat.id()) if not engine.intersects(in_feat.geometry().geometry()): continue if not engine.contains(in_feat.geometry().geometry()): cur_geom = in_feat.geometry() new_geom = combined_clip_geom.intersection(cur_geom) if new_geom.wkbType() == QgsWkbTypes.Unknown or QgsWkbTypes.flatType(new_geom.geometry().wkbType()) == QgsWkbTypes.GeometryCollection: int_com = in_feat.geometry().combine(new_geom) int_sym = in_feat.geometry().symDifference(new_geom) new_geom = int_com.difference(int_sym) else: # clip geometry totally contains feature geometry, so no need to perform intersection new_geom = in_feat.geometry() try: out_feat = QgsFeature() out_feat.setGeometry(new_geom) out_feat.setAttributes(in_feat.attributes()) writer.addFeature(out_feat) except: QgsMessageLog.logMessage(self.tr('Feature geometry error: One or more ' 'output features ignored due to ' 'invalid geometry.'), self.tr('Processing'), QgsMessageLog.CRITICAL) continue if single_clip_feature: feedback.setProgress(int(current * total)) if not single_clip_feature: # coarse progress report for multiple clip geometries feedback.setProgress(100.0 * i / len(clip_geoms)) del writer
def union(union_a, union_b, callback=None): """Union of two vector layers. Issue https://github.com/inasafe/inasafe/issues/3186 Note : This algorithm is copied from : https://github.com/qgis/QGIS/blob/master/python/plugins/processing/algs/ qgis/Union.py :param union_a: The vector layer for the union. :type union_a: QgsVectorLayer :param union_b: The vector layer for the union. :type union_b: 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 clip vector layer. :rtype: QgsVectorLayer .. versionadded:: 4.0 """ output_layer_name = union_steps['output_layer_name'] processing_step = union_steps['step_name'] output_layer_name = output_layer_name % ( union_a.keywords['layer_purpose'], union_b.keywords['layer_purpose'] ) fields = union_a.fields() fields.extend(union_b.fields()) writer = create_memory_layer( output_layer_name, union_a.geometryType(), union_a.crs(), fields ) keywords_union_1 = union_a.keywords keywords_union_2 = union_b.keywords inasafe_fields_union_1 = keywords_union_1['inasafe_fields'] inasafe_fields_union_2 = keywords_union_2['inasafe_fields'] inasafe_fields = inasafe_fields_union_1 inasafe_fields.update(inasafe_fields_union_2) # use to avoid modifying original source writer.keywords = dict(union_a.keywords) writer.keywords['inasafe_fields'] = inasafe_fields writer.keywords['title'] = output_layer_name writer.keywords['layer_purpose'] = 'aggregate_hazard' writer.keywords['hazard_keywords'] = keywords_union_1.copy() writer.keywords['aggregation_keywords'] = keywords_union_2.copy() skip_field = inasafe_fields_union_2[aggregation_id_field['key']] not_null_field_index = writer.fieldNameIndex(skip_field) writer.startEditing() # Begin copy/paste from Processing plugin. # Please follow their code as their code is optimized. # The code below is not following our coding standards because we want to # be able to track any diffs from QGIS easily. index_a = create_spatial_index(union_b) index_b = create_spatial_index(union_a) count = 0 n_element = 0 # Todo fix callback # nFeat = len(union_a.getFeatures()) for in_feat_a in union_a.getFeatures(): # progress.setPercentage(nElement / float(nFeat) * 50) n_element += 1 list_intersecting_b = [] geom = geometry_checker(in_feat_a.geometry()) at_map_a = in_feat_a.attributes() intersects = index_a.intersects(geom.boundingBox()) if len(intersects) < 1: try: _write_feature(at_map_a, geom, writer, not_null_field_index) except: # This really shouldn't happen, as we haven't # edited the input geom at all LOGGER.debug( tr('Feature geometry error: One or more output features ' 'ignored due to invalid geometry.')) else: request = QgsFeatureRequest().setFilterFids(intersects) engine = QgsGeometry.createGeometryEngine(geom.geometry()) engine.prepareGeometry() for in_feat_b in union_b.getFeatures(request): count += 1 at_map_b = in_feat_b.attributes() tmp_geom = geometry_checker(in_feat_b.geometry()) if engine.intersects(tmp_geom.geometry()): int_geom = geometry_checker(geom.intersection(tmp_geom)) list_intersecting_b.append(QgsGeometry(tmp_geom)) if not int_geom: # There was a problem creating the intersection # LOGGER.debug( # tr('GEOS geoprocessing error: One or more input ' # 'features have invalid geometry.')) pass int_geom = QgsGeometry() else: int_geom = QgsGeometry(int_geom) if int_geom.wkbType() == QgsWKBTypes.Unknown\ or QgsWKBTypes.flatType( int_geom.geometry().wkbType()) == \ QgsWKBTypes.GeometryCollection: # Intersection produced different geometry types temp_list = int_geom.asGeometryCollection() for i in temp_list: if i.type() == geom.type(): int_geom = QgsGeometry(geometry_checker(i)) try: _write_feature( at_map_a + at_map_b, int_geom, writer, not_null_field_index, ) except: LOGGER.debug( tr('Feature geometry error: One or ' 'more output features ignored due ' 'to invalid geometry.')) else: # Geometry list: prevents writing error # in geometries of different types # produced by the intersection # fix #3549 if int_geom.wkbType() in wkb_type_groups[ wkb_type_groups[int_geom.wkbType()]]: try: _write_feature( at_map_a + at_map_b, int_geom, writer, not_null_field_index) except: LOGGER.debug( tr('Feature geometry error: One or more ' 'output features ignored due to ' 'invalid geometry.')) # the remaining bit of inFeatA's geometry # if there is nothing left, this will just silently fail and we # are good diff_geom = QgsGeometry(geom) if len(list_intersecting_b) != 0: int_b = QgsGeometry.unaryUnion(list_intersecting_b) diff_geom = geometry_checker(diff_geom.difference(int_b)) if diff_geom is None or \ diff_geom.isGeosEmpty() or not diff_geom.isGeosValid(): # LOGGER.debug( # tr('GEOS geoprocessing error: One or more input ' # 'features have invalid geometry.')) pass if diff_geom is not None and ( diff_geom.wkbType() == 0 or QgsWKBTypes.flatType( diff_geom.geometry().wkbType()) == QgsWKBTypes.GeometryCollection): temp_list = diff_geom.asGeometryCollection() for i in temp_list: if i.type() == geom.type(): diff_geom = QgsGeometry(geometry_checker(i)) try: _write_feature( at_map_a, diff_geom, writer, not_null_field_index) except: LOGGER.debug( tr('Feature geometry error: One or more output features ' 'ignored due to invalid geometry.')) length = len(union_a.fields()) at_map_a = [None] * length # nFeat = len(union_b.getFeatures()) for in_feat_a in union_b.getFeatures(): # progress.setPercentage(nElement / float(nFeat) * 100) add = False geom = geometry_checker(in_feat_a.geometry()) atMap = [None] * length atMap.extend(in_feat_a.attributes()) intersects = index_b.intersects(geom.boundingBox()) lstIntersectingA = [] for id in intersects: request = QgsFeatureRequest().setFilterFid(id) inFeatB = union_a.getFeatures(request).next() atMapB = inFeatB.attributes() tmpGeom = QgsGeometry(geometry_checker(inFeatB.geometry())) if geom.intersects(tmpGeom): lstIntersectingA.append(tmpGeom) if len(lstIntersectingA) == 0: res_geom = geom else: intA = QgsGeometry.unaryUnion(lstIntersectingA) res_geom = geom.difference(intA) if res_geom is None: # LOGGER.debug( # tr('GEOS geoprocessing error: One or more input features ' # 'have null geometry.')) pass continue # maybe it is better to fail like @gustry # does below .... if res_geom.isGeosEmpty() or not res_geom.isGeosValid(): # LOGGER.debug( # tr('GEOS geoprocessing error: One or more input features ' # 'have invalid geometry.')) pass try: _write_feature(atMap, res_geom, writer, not_null_field_index) except: # LOGGER.debug( # tr('Feature geometry error: One or more output features ' # 'ignored due to invalid geometry.')) pass n_element += 1 # End of copy/paste from processing writer.commitChanges() fill_hazard_class(writer) check_layer(writer) return writer
def processAlgorithm(self, parameters, context, feedback): expr_context = self.createExpressionContext(parameters, context, self.source) self.group_by_expr.prepare(expr_context) # Group features in memory layers source = self.source count = self.source.featureCount() if count: progress_step = 50.0 / count current = 0 groups = {} keys = [] # We need deterministic order for the tests feature = QgsFeature() for feature in self.source.getFeatures(): expr_context.setFeature(feature) group_by_value = self.evaluateExpression(self.group_by_expr, expr_context) # Get an hashable key for the dict key = group_by_value if isinstance(key, list): key = tuple(key) group = groups.get(key, None) if group is None: sink, id = QgsProcessingUtils.createFeatureSink( 'memory:', context, source.fields(), source.wkbType(), source.sourceCrs()) layer = QgsProcessingUtils.mapLayerFromString(id, context) group = { 'sink': sink, 'layer': layer, 'feature': feature } groups[key] = group keys.append(key) group['sink'].addFeature(feature, QgsFeatureSink.FastInsert) current += 1 feedback.setProgress(int(current * progress_step)) if feedback.isCanceled(): return (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, self.fields, QgsWkbTypes.multiType(source.wkbType()), source.sourceCrs()) if sink is None: raise QgsProcessingException(self.invalidSinkError(parameters, self.OUTPUT)) # Calculate aggregates on memory layers if len(keys): progress_step = 50.0 / len(keys) for current, key in enumerate(keys): group = groups[key] expr_context = self.createExpressionContext(parameters, context) expr_context.appendScope(QgsExpressionContextUtils.layerScope(group['layer'])) expr_context.setFeature(group['feature']) geometry = self.evaluateExpression(self.geometry_expr, expr_context) if geometry is not None and not geometry.isEmpty(): geometry = QgsGeometry.unaryUnion(geometry.asGeometryCollection()) if geometry.isEmpty(): raise QgsProcessingException( 'Impossible to combine geometries for {} = {}' .format(self.group_by, group_by_value)) attrs = [] for fields_expr in self.fields_expr: attrs.append(self.evaluateExpression(fields_expr, expr_context)) # Write output feature outFeat = QgsFeature() if geometry is not None: outFeat.setGeometry(geometry) outFeat.setAttributes(attrs) sink.addFeature(outFeat, QgsFeatureSink.FastInsert) feedback.setProgress(50 + int(current * progress_step)) if feedback.isCanceled(): return return {self.OUTPUT: dest_id}
def buffering(feedback, context, sink, distance, field, useField, source, dissolve, segments, endCapStyle=1, joinStyle=1, miterLimit=2): if useField: field = source.fields().lookupField(field) outFeat = QgsFeature() current = 0 total = 100.0 / source.featureCount() if source.featureCount() else 0 # With dissolve if dissolve: attributes_to_fetch = [] if useField: attributes_to_fetch.append(field) features = source.getFeatures( QgsFeatureRequest().setSubsetOfAttributes(attributes_to_fetch)) buffered_geometries = [] for inFeat in features: if feedback.isCanceled(): break attrs = inFeat.attributes() if useField: value = attrs[field] else: value = distance inGeom = inFeat.geometry() buffered_geometries.append( inGeom.buffer(float(value), segments, endCapStyle, joinStyle, miterLimit)) current += 1 feedback.setProgress(int(current * total)) final_geometry = QgsGeometry.unaryUnion(buffered_geometries) outFeat.setGeometry(final_geometry) outFeat.setAttributes(attrs) sink.addFeature(outFeat, QgsFeatureSink.FastInsert) else: features = source.getFeatures() # Without dissolve for inFeat in features: if feedback.isCanceled(): break attrs = inFeat.attributes() if useField: value = attrs[field] else: value = distance inGeom = inFeat.geometry() outFeat = QgsFeature() outGeom = inGeom.buffer(float(value), segments, endCapStyle, joinStyle, miterLimit) outFeat.setGeometry(outGeom) outFeat.setAttributes(attrs) sink.addFeature(outFeat, QgsFeatureSink.FastInsert) current += 1 feedback.setProgress(int(current * total))
def linearMatrix(self, parameters, context, source, inField, target_source, targetField, same_source_and_target, matType, nPoints, feedback): if same_source_and_target: # need to fetch an extra point from the index, since the closest match will always be the same # as the input feature nPoints += 1 inIdx = source.fields().lookupField(inField) outIdx = target_source.fields().lookupField(targetField) fields = QgsFields() input_id_field = source.fields()[inIdx] input_id_field.setName('InputID') fields.append(input_id_field) if matType == 0: target_id_field = target_source.fields()[outIdx] target_id_field.setName('TargetID') fields.append(target_id_field) fields.append(QgsField('Distance', QVariant.Double)) else: fields.append(QgsField('MEAN', QVariant.Double)) fields.append(QgsField('STDDEV', QVariant.Double)) fields.append(QgsField('MIN', QVariant.Double)) fields.append(QgsField('MAX', QVariant.Double)) out_wkb = QgsWkbTypes.multiType( source.wkbType()) if matType == 0 else source.wkbType() (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, out_wkb, source.sourceCrs()) if sink is None: raise QgsProcessingException( self.invalidSinkError(parameters, self.OUTPUT)) index = QgsSpatialIndex( target_source.getFeatures( QgsFeatureRequest().setSubsetOfAttributes( []).setDestinationCrs(source.sourceCrs(), context.transformContext())), feedback) distArea = QgsDistanceArea() distArea.setSourceCrs(source.sourceCrs(), context.transformContext()) distArea.setEllipsoid(context.project().ellipsoid()) features = source.getFeatures( QgsFeatureRequest().setSubsetOfAttributes([inIdx])) total = 100.0 / source.featureCount() if source.featureCount() else 0 for current, inFeat in enumerate(features): if feedback.isCanceled(): break inGeom = inFeat.geometry() inID = str(inFeat[inIdx]) featList = index.nearestNeighbor(inGeom.asPoint(), nPoints) distList = [] vari = 0.0 request = QgsFeatureRequest().setFilterFids( featList).setSubsetOfAttributes([outIdx]).setDestinationCrs( source.sourceCrs(), context.transformContext()) for outFeat in target_source.getFeatures(request): if feedback.isCanceled(): break if same_source_and_target and inFeat.id() == outFeat.id(): continue outID = outFeat[outIdx] outGeom = outFeat.geometry() dist = distArea.measureLine(inGeom.asPoint(), outGeom.asPoint()) if matType == 0: out_feature = QgsFeature() out_geom = QgsGeometry.unaryUnion( [inFeat.geometry(), outFeat.geometry()]) out_feature.setGeometry(out_geom) out_feature.setAttributes([inID, outID, dist]) sink.addFeature(out_feature, QgsFeatureSink.FastInsert) else: distList.append(float(dist)) if matType != 0: mean = sum(distList) / len(distList) for i in distList: vari += (i - mean) * (i - mean) vari = math.sqrt(vari / len(distList)) out_feature = QgsFeature() out_feature.setGeometry(inFeat.geometry()) out_feature.setAttributes( [inID, mean, vari, min(distList), max(distList)]) sink.addFeature(out_feature, QgsFeatureSink.FastInsert) feedback.setProgress(int(current * total)) return {self.OUTPUT: dest_id}
def processAlgorithm(self, progress): useField = not self.getParameterValue(Dissolve.DISSOLVE_ALL) field_names = self.getParameterValue(Dissolve.FIELD) vlayerA = dataobjects.getObjectFromUri( self.getParameterValue(Dissolve.INPUT)) writer = self.getOutputFromName(Dissolve.OUTPUT).getVectorWriter( vlayerA.fields().toList(), vlayerA.wkbType(), vlayerA.crs()) outFeat = QgsFeature() features = vector.features(vlayerA) total = 100.0 / len(features) if not useField: first = True # we dissolve geometries in blocks using unaryUnion geom_queue = [] for current, inFeat in enumerate(features): progress.setPercentage(int(current * total)) if first: outFeat.setAttributes(inFeat.attributes()) first = False tmpInGeom = inFeat.geometry() if tmpInGeom.isEmpty() or tmpInGeom.isGeosEmpty(): continue errors = tmpInGeom.validateGeometry() if len(errors) != 0: for error in errors: ProcessingLog.addToLog( ProcessingLog.LOG_ERROR, self.tr('ValidateGeometry()' 'error: One or more ' 'input features have ' 'invalid geometry: ') + error.what()) continue geom_queue.append(tmpInGeom) if len(geom_queue) > 10000: # queue too long, combine it try: temp_output_geometry = QgsGeometry.unaryUnion( geom_queue) geom_queue = [temp_output_geometry] except: raise GeoAlgorithmExecutionException( self.tr('Geometry exception while dissolving')) try: outFeat.setGeometry(QgsGeometry.unaryUnion(geom_queue)) except: raise GeoAlgorithmExecutionException( self.tr('Geometry exception while dissolving')) writer.addFeature(outFeat) else: field_indexes = [ vlayerA.fields().lookupField(f) for f in field_names.split(';') ] attribute_dict = {} geometry_dict = defaultdict(lambda: []) for inFeat in features: attrs = inFeat.attributes() index_attrs = tuple([attrs[i] for i in field_indexes]) tmpInGeom = QgsGeometry(inFeat.geometry()) if tmpInGeom.isGeosEmpty(): continue errors = tmpInGeom.validateGeometry() if len(errors) != 0: for error in errors: ProcessingLog.addToLog( ProcessingLog.LOG_ERROR, self.tr('ValidateGeometry() ' 'error: One or more input' 'features have invalid ' 'geometry: ') + error.what()) if not index_attrs in attribute_dict: # keep attributes of first feature attribute_dict[index_attrs] = attrs geometry_dict[index_attrs].append(tmpInGeom) nFeat = len(attribute_dict) nElement = 0 for key, value in list(geometry_dict.items()): outFeat = QgsFeature() nElement += 1 progress.setPercentage(int(nElement * 100 / nFeat)) try: tmpOutGeom = QgsGeometry.unaryUnion(value) except: raise GeoAlgorithmExecutionException( self.tr('Geometry exception while dissolving')) outFeat.setGeometry(tmpOutGeom) outFeat.setAttributes(attribute_dict[key]) writer.addFeature(outFeat) del writer
def processAlgorithm(self, progress): vlayerA = dataobjects.getObjectFromUri(self.getParameterValue(Union.INPUT)) vlayerB = dataobjects.getObjectFromUri(self.getParameterValue(Union.INPUT2)) geomType = vlayerA.wkbType() fields = vector.combineVectorFields(vlayerA, vlayerB) writer = self.getOutputFromName(Union.OUTPUT).getVectorWriter(fields, geomType, vlayerA.crs()) inFeatA = QgsFeature() inFeatB = QgsFeature() outFeat = QgsFeature() indexA = vector.spatialindex(vlayerB) indexB = vector.spatialindex(vlayerA) count = 0 nElement = 0 featuresA = vector.features(vlayerA) nFeat = len(featuresA) for inFeatA in featuresA: progress.setPercentage(nElement / float(nFeat) * 50) nElement += 1 lstIntersectingB = [] geom = inFeatA.geometry() atMapA = inFeatA.attributes() intersects = indexA.intersects(geom.boundingBox()) if len(intersects) < 1: try: outFeat.setGeometry(geom) outFeat.setAttributes(atMapA) writer.addFeature(outFeat) except: # This really shouldn't happen, as we haven't # edited the input geom at all ProcessingLog.addToLog(ProcessingLog.LOG_INFO, self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.')) else: request = QgsFeatureRequest().setFilterFids(intersects) engine = QgsGeometry.createGeometryEngine(geom.geometry()) engine.prepareGeometry() for inFeatB in vlayerB.getFeatures(request): count += 1 atMapB = inFeatB.attributes() tmpGeom = inFeatB.geometry() if engine.intersects(tmpGeom.geometry()): int_geom = geom.intersection(tmpGeom) lstIntersectingB.append(tmpGeom) if not int_geom: # There was a problem creating the intersection ProcessingLog.addToLog(ProcessingLog.LOG_INFO, self.tr('GEOS geoprocessing error: One or more input features have invalid geometry.')) int_geom = QgsGeometry() else: int_geom = QgsGeometry(int_geom) if int_geom.wkbType() == QgsWkbTypes.Unknown or QgsWkbTypes.flatType(int_geom.geometry().wkbType()) == QgsWkbTypes.GeometryCollection: # Intersection produced different geomety types temp_list = int_geom.asGeometryCollection() for i in temp_list: if i.type() == geom.type(): int_geom = QgsGeometry(i) try: outFeat.setGeometry(int_geom) outFeat.setAttributes(atMapA + atMapB) writer.addFeature(outFeat) except: ProcessingLog.addToLog(ProcessingLog.LOG_INFO, self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.')) else: # Geometry list: prevents writing error # in geometries of different types # produced by the intersection # fix #3549 if int_geom.wkbType() in wkbTypeGroups[wkbTypeGroups[int_geom.wkbType()]]: try: outFeat.setGeometry(int_geom) outFeat.setAttributes(atMapA + atMapB) writer.addFeature(outFeat) except: ProcessingLog.addToLog(ProcessingLog.LOG_INFO, self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.')) # the remaining bit of inFeatA's geometry # if there is nothing left, this will just silently fail and we're good diff_geom = QgsGeometry(geom) if len(lstIntersectingB) != 0: intB = QgsGeometry.unaryUnion(lstIntersectingB) diff_geom = diff_geom.difference(intB) if diff_geom.isGeosEmpty() or not diff_geom.isGeosValid(): ProcessingLog.addToLog(ProcessingLog.LOG_ERROR, self.tr('GEOS geoprocessing error: One or more input features have invalid geometry.')) if diff_geom.wkbType() == 0 or QgsWkbTypes.flatType(diff_geom.geometry().wkbType()) == QgsWkbTypes.GeometryCollection: temp_list = diff_geom.asGeometryCollection() for i in temp_list: if i.type() == geom.type(): diff_geom = QgsGeometry(i) try: outFeat.setGeometry(diff_geom) outFeat.setAttributes(atMapA) writer.addFeature(outFeat) except: ProcessingLog.addToLog(ProcessingLog.LOG_INFO, self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.')) length = len(vlayerA.fields()) atMapA = [None] * length featuresA = vector.features(vlayerB) nFeat = len(featuresA) for inFeatA in featuresA: progress.setPercentage(nElement / float(nFeat) * 100) add = False geom = inFeatA.geometry() diff_geom = QgsGeometry(geom) atMap = [None] * length atMap.extend(inFeatA.attributes()) intersects = indexB.intersects(geom.boundingBox()) if len(intersects) < 1: try: outFeat.setGeometry(geom) outFeat.setAttributes(atMap) writer.addFeature(outFeat) except: ProcessingLog.addToLog(ProcessingLog.LOG_INFO, self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.')) else: request = QgsFeatureRequest().setFilterFids(intersects) # use prepared geometries for faster intersection tests engine = QgsGeometry.createGeometryEngine(diff_geom.geometry()) engine.prepareGeometry() for inFeatB in vlayerA.getFeatures(request): atMapB = inFeatB.attributes() tmpGeom = inFeatB.geometry() if engine.intersects(tmpGeom.geometry()): add = True diff_geom = QgsGeometry(diff_geom.difference(tmpGeom)) if diff_geom.isGeosEmpty() or not diff_geom.isGeosValid(): ProcessingLog.addToLog(ProcessingLog.LOG_ERROR, self.tr('GEOS geoprocessing error: One or more input features have invalid geometry.')) else: try: # Ihis only happends if the bounding box # intersects, but the geometry doesn't outFeat.setGeometry(diff_geom) outFeat.setAttributes(atMap) writer.addFeature(outFeat) except: ProcessingLog.addToLog(ProcessingLog.LOG_INFO, self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.')) if add: try: outFeat.setGeometry(diff_geom) outFeat.setAttributes(atMap) writer.addFeature(outFeat) except: ProcessingLog.addToLog(ProcessingLog.LOG_INFO, self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.')) nElement += 1 del writer
def get_access_isochrones(self): """ Making the accessibility isochrones in few steps: - make a grid of points aroung the origin point, - snap each point (using OSRM locate function) on the road network, - get the time-distance between the origin point and each of these pts (using OSRM table function), - make an interpolation grid to extract polygons corresponding to the desired time intervals (using matplotlib library), - render the polygon. """ try: self.host = check_host(self.lineEdit_host.text()) self.profile = check_profile_name(self.lineEdit_profileName.text()) except (ValueError, AssertionError) as err: self.iface.messageBar().pushMessage( "Error", "Please provide a valid non-empty URL", duration=10) return if 'clicking' in self.comboBox_method.currentText(): pts = self.get_points_from_canvas() elif 'selecting' in self.comboBox_method.currentText(): layer = self.comboBox_pointlayer.currentLayer() pts, _ = get_coords_ids( layer, '', on_selected=self.checkBox_selectedFt.isChecked()) pts = tuple(pts) if not pts: return max_time = self.spinBox_max.value() interval_time = self.spinBox_intervall.value() nb_inter = int(round(max_time / interval_time)) + 1 levels = tuple([nb for nb in xrange(0, int( max_time + 1) + interval_time, interval_time)][:nb_inter]) self.make_prog_bar() self.max_points = 750 if len(pts) == 1 else 250 self.polygons = [] pts = [{"point": pt, "max": max_time, "levels": levels, "host": self.host, "profile": self.profile, "max_points": self.max_points} for pt in pts] pool = ThreadPool(processes=4 if len(pts) >= 4 else len(pts)) try: self.polygons = [i for i in pool.map(prep_access, pts)] except Exception as err: self.display_error(err, 1) return if len(self.polygons) == 1: self.polygons = self.polygons[0] else: self.polygons = np.array(self.polygons).transpose().tolist() self.polygons = [QgsGeometry.unaryUnion(polys) for polys in self.polygons] isochrone_layer = QgsVectorLayer( "MultiPolygon?crs=epsg:4326&field=id:integer" "&field=min:integer(10)" "&field=max:integer(10)", "isochrone_osrm_{}".format(self.nb_isocr), "memory") data_provider = isochrone_layer.dataProvider() # Add the features to the layer to display : features = [] levels = levels[1:] self.progress.setValue(8.5) for i, poly in enumerate(self.polygons): if not poly: continue ft = QgsFeature() ft.setGeometry(poly) ft.setAttributes( [i, levels[i] - interval_time, levels[i]]) features.append(ft) data_provider.addFeatures(features[::-1]) self.nb_isocr += 1 self.progress.setValue(9.5) # Render the value : renderer = self.prepare_renderer( levels, interval_time, len(self.polygons)) isochrone_layer.setRendererV2(renderer) isochrone_layer.setLayerTransparency(25) self.iface.messageBar().clearWidgets() QgsMapLayerRegistry.instance().addMapLayer(isochrone_layer) self.add_final_pts(pts) self.iface.setActiveLayer(isochrone_layer)
def processAlgorithm(self, parameters, context, feedback): sourceA = self.parameterAsSource(parameters, self.INPUT, context) sourceB = self.parameterAsSource(parameters, self.OVERLAY, context) geomType = QgsWkbTypes.multiType(sourceA.wkbType()) fields = QgsProcessingUtils.combineFields(sourceA.fields(), sourceB.fields()) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, geomType, sourceA.sourceCrs()) featA = QgsFeature() featB = QgsFeature() outFeat = QgsFeature() indexA = QgsSpatialIndex(sourceA, feedback) indexB = QgsSpatialIndex( sourceB.getFeatures(QgsFeatureRequest().setSubsetOfAttributes( []).setDestinationCrs(sourceA.sourceCrs(), context.transformContext())), feedback) total = 100.0 / (sourceA.featureCount() * sourceB.featureCount()) if sourceA.featureCount( ) and sourceB.featureCount() else 1 count = 0 for featA in sourceA.getFeatures(): if feedback.isCanceled(): break lstIntersectingB = [] geom = featA.geometry() atMapA = featA.attributes() intersects = indexB.intersects(geom.boundingBox()) if len(intersects) < 1: try: geom.convertToMultiType() outFeat.setGeometry(geom) outFeat.setAttributes(atMapA) sink.addFeature(outFeat, QgsFeatureSink.FastInsert) except: # This really shouldn't happen, as we haven't # edited the input geom at all feedback.pushInfo( self. tr('Feature geometry error: One or more output features ignored due to invalid geometry.' )) else: request = QgsFeatureRequest().setFilterFids( intersects).setSubsetOfAttributes([]) request.setDestinationCrs(sourceA.sourceCrs(), context.transformContext()) engine = QgsGeometry.createGeometryEngine(geom.constGet()) engine.prepareGeometry() for featB in sourceB.getFeatures(request): atMapB = featB.attributes() tmpGeom = featB.geometry() if engine.intersects(tmpGeom.constGet()): int_geom = geom.intersection(tmpGeom) lstIntersectingB.append(tmpGeom) if not int_geom: # There was a problem creating the intersection feedback.pushInfo( self. tr('Feature geometry error: One or more output features ignored due to invalid geometry.' )) int_geom = QgsGeometry() else: int_geom = QgsGeometry(int_geom) if int_geom.wkbType( ) == QgsWkbTypes.Unknown or QgsWkbTypes.flatType( int_geom.wkbType( )) == QgsWkbTypes.GeometryCollection: # Intersection produced different geomety types temp_list = int_geom.asGeometryCollection() for i in temp_list: if i.type() == geom.type(): int_geom = QgsGeometry(i) try: int_geom.convertToMultiType() outFeat.setGeometry(int_geom) outFeat.setAttributes(atMapA + atMapB) sink.addFeature( outFeat, QgsFeatureSink.FastInsert) except: feedback.pushInfo( self. tr('Feature geometry error: One or more output features ignored due to invalid geometry.' )) else: # Geometry list: prevents writing error # in geometries of different types # produced by the intersection # fix #3549 if QgsWkbTypes.geometryType(int_geom.wkbType( )) == QgsWkbTypes.geometryType(geomType): try: int_geom.convertToMultiType() outFeat.setGeometry(int_geom) outFeat.setAttributes(atMapA + atMapB) sink.addFeature(outFeat, QgsFeatureSink.FastInsert) except: feedback.pushInfo( self. tr('Feature geometry error: One or more output features ignored due to invalid geometry.' )) # the remaining bit of featA's geometry # if there is nothing left, this will just silently fail and we're good diff_geom = QgsGeometry(geom) if len(lstIntersectingB) != 0: intB = QgsGeometry.unaryUnion(lstIntersectingB) diff_geom = diff_geom.difference(intB) if diff_geom.wkbType( ) == QgsWkbTypes.Unknown or QgsWkbTypes.flatType( diff_geom.wkbType()) == QgsWkbTypes.GeometryCollection: temp_list = diff_geom.asGeometryCollection() for i in temp_list: if i.type() == geom.type(): diff_geom = QgsGeometry(i) try: diff_geom.convertToMultiType() outFeat.setGeometry(diff_geom) outFeat.setAttributes(atMapA) sink.addFeature(outFeat, QgsFeatureSink.FastInsert) except: feedback.pushInfo( self. tr('Feature geometry error: One or more output features ignored due to invalid geometry.' )) count += 1 feedback.setProgress(int(count * total)) length = len(sourceA.fields()) atMapA = [None] * length for featA in sourceB.getFeatures(QgsFeatureRequest().setDestinationCrs( sourceA.sourceCrs(), context.transformContext())): if feedback.isCanceled(): break add = False geom = featA.geometry() diff_geom = QgsGeometry(geom) atMap = [None] * length atMap.extend(featA.attributes()) intersects = indexA.intersects(geom.boundingBox()) if len(intersects) < 1: try: geom.convertToMultiType() outFeat.setGeometry(geom) outFeat.setAttributes(atMap) sink.addFeature(outFeat, QgsFeatureSink.FastInsert) except: feedback.pushInfo( self. tr('Feature geometry error: One or more output features ignored due to invalid geometry.' )) else: request = QgsFeatureRequest().setFilterFids( intersects).setSubsetOfAttributes([]) request.setDestinationCrs(sourceA.sourceCrs(), context.transformContext()) # use prepared geometries for faster intersection tests engine = QgsGeometry.createGeometryEngine(diff_geom.constGet()) engine.prepareGeometry() for featB in sourceA.getFeatures(request): atMapB = featB.attributes() tmpGeom = featB.geometry() if engine.intersects(tmpGeom.constGet()): add = True diff_geom = QgsGeometry(diff_geom.difference(tmpGeom)) else: try: # Ihis only happens if the bounding box # intersects, but the geometry doesn't diff_geom.convertToMultiType() outFeat.setGeometry(diff_geom) outFeat.setAttributes(atMap) sink.addFeature(outFeat, QgsFeatureSink.FastInsert) except: feedback.pushInfo( self. tr('Feature geometry error: One or more output features ignored due to invalid geometry.' )) if add: try: diff_geom.convertToMultiType() outFeat.setGeometry(diff_geom) outFeat.setAttributes(atMap) sink.addFeature(outFeat, QgsFeatureSink.FastInsert) except: feedback.pushInfo( self. tr('Feature geometry error: One or more output features ignored due to invalid geometry.' )) count += 1 feedback.setProgress(int(count * total)) return {self.OUTPUT: dest_id}
def processAlgorithm(self, progress): vlayerA = dataobjects.getObjectFromUri(self.getParameterValue(Union.INPUT)) vlayerB = dataobjects.getObjectFromUri(self.getParameterValue(Union.INPUT2)) GEOS_EXCEPT = True FEATURE_EXCEPT = True vproviderA = vlayerA.dataProvider() fields = vector.combineVectorFields(vlayerA, vlayerB) names = [field.name() for field in fields] ProcessingLog.addToLog(ProcessingLog.LOG_INFO, unicode(names)) writer = self.getOutputFromName(Union.OUTPUT).getVectorWriter(fields, vproviderA.geometryType(), vproviderA.crs()) inFeatA = QgsFeature() inFeatB = QgsFeature() outFeat = QgsFeature() indexA = vector.spatialindex(vlayerB) indexB = vector.spatialindex(vlayerA) count = 0 nElement = 0 featuresA = vector.features(vlayerA) nFeat = len(featuresA) for inFeatA in featuresA: progress.setPercentage(nElement / float(nFeat) * 50) nElement += 1 lstIntersectingB = [] geom = QgsGeometry(inFeatA.geometry()) atMapA = inFeatA.attributes() intersects = indexA.intersects(geom.boundingBox()) if len(intersects) < 1: try: outFeat.setGeometry(geom) outFeat.setAttributes(atMapA) writer.addFeature(outFeat) except: # This really shouldn't happen, as we haven't # edited the input geom at all raise GeoAlgorithmExecutionException( self.tr('Feature exception while computing union')) else: for id in intersects: count += 1 request = QgsFeatureRequest().setFilterFid(id) inFeatB = vlayerB.getFeatures(request).next() atMapB = inFeatB.attributes() tmpGeom = QgsGeometry(inFeatB.geometry()) if geom.intersects(tmpGeom): int_geom = geom.intersection(tmpGeom) lstIntersectingB.append(tmpGeom) if int_geom is None: # There was a problem creating the intersection raise GeoAlgorithmExecutionException( self.tr('Geometry exception while computing ' 'intersection')) else: int_geom = QgsGeometry(int_geom) if int_geom.wkbType() == 0: # Intersection produced different geomety types temp_list = int_geom.asGeometryCollection() for i in temp_list: if i.type() == geom.type(): int_geom = QgsGeometry(i) try: outFeat.setGeometry(int_geom) attrs = [] attrs.extend(atMapA) attrs.extend(atMapB) outFeat.setAttributes(attrs) writer.addFeature(outFeat) except Exception as err: raise GeoAlgorithmExecutionException( self.tr('Feature exception while computing union')) try: # the remaining bit of inFeatA's geometry # if there is nothing left, this will just silently fail and we're good diff_geom = QgsGeometry(geom) if len(lstIntersectingB) != 0: intB = QgsGeometry.unaryUnion(lstIntersectingB) diff_geom = diff_geom.difference(intB) if diff_geom.wkbType() == 0: temp_list = diff_geom.asGeometryCollection() for i in temp_list: if i.type() == geom.type(): diff_geom = QgsGeometry(i) outFeat.setGeometry(diff_geom) outFeat.setAttributes(atMapA) writer.addFeature(outFeat) except Exception as err: raise GeoAlgorithmExecutionException( self.tr('Feature exception while computing union')) length = len(vproviderA.fields()) featuresA = vector.features(vlayerB) nFeat = len(featuresA) for inFeatA in featuresA: progress.setPercentage(nElement / float(nFeat) * 100) add = False geom = QgsGeometry(inFeatA.geometry()) diff_geom = QgsGeometry(geom) atMap = [None] * length atMap.extend(inFeatA.attributes()) intersects = indexB.intersects(geom.boundingBox()) if len(intersects) < 1: try: outFeat.setGeometry(geom) outFeat.setAttributes(atMap) writer.addFeature(outFeat) except Exception as err: raise GeoAlgorithmExecutionException( self.tr('Feature exception while computing union')) else: for id in intersects: request = QgsFeatureRequest().setFilterFid(id) inFeatB = vlayerA.getFeatures(request).next() atMapB = inFeatB.attributes() tmpGeom = QgsGeometry(inFeatB.geometry()) try: if diff_geom.intersects(tmpGeom): add = True diff_geom = QgsGeometry( diff_geom.difference(tmpGeom)) else: # Ihis only happends if the bounding box # intersects, but the geometry doesn't outFeat.setGeometry(diff_geom) outFeat.setAttributes(atMap) writer.addFeature(outFeat) except Exception as err: raise GeoAlgorithmExecutionException( self.tr('Geometry exception while computing intersection')) if add: try: outFeat.setGeometry(diff_geom) outFeat.setAttributes(atMap) writer.addFeature(outFeat) except Exception as err: raise err FEATURE_EXCEPT = False nElement += 1 del writer if not GEOS_EXCEPT: ProcessingLog.addToLog(ProcessingLog.LOG_WARNING, self.tr('Geometry exception while computing intersection')) if not FEATURE_EXCEPT: ProcessingLog.addToLog(ProcessingLog.LOG_WARNING, self.tr('Feature exception while computing intersection'))
def processAlgorithm(self, progress): vlayerA = dataobjects.getObjectFromUri(self.getParameterValue(Union.INPUT)) vlayerB = dataobjects.getObjectFromUri(self.getParameterValue(Union.INPUT2)) geomType = vlayerA.wkbType() fields = vector.combineVectorFields(vlayerA, vlayerB) writer = self.getOutputFromName(Union.OUTPUT).getVectorWriter(fields, geomType, vlayerA.crs()) inFeatA = QgsFeature() inFeatB = QgsFeature() outFeat = QgsFeature() indexA = vector.spatialindex(vlayerB) indexB = vector.spatialindex(vlayerA) count = 0 nElement = 0 featuresA = vector.features(vlayerA) nFeat = len(featuresA) for inFeatA in featuresA: progress.setPercentage(nElement / float(nFeat) * 50) nElement += 1 lstIntersectingB = [] geom = inFeatA.geometry() atMapA = inFeatA.attributes() intersects = indexA.intersects(geom.boundingBox()) if len(intersects) < 1: try: outFeat.setGeometry(geom) outFeat.setAttributes(atMapA) writer.addFeature(outFeat) except: # This really shouldn't happen, as we haven't # edited the input geom at all ProcessingLog.addToLog( ProcessingLog.LOG_INFO, self.tr("Feature geometry error: One or more output features ignored due to invalid geometry."), ) else: request = QgsFeatureRequest().setFilterFids(intersects) engine = QgsGeometry.createGeometryEngine(geom.geometry()) engine.prepareGeometry() for inFeatB in vlayerB.getFeatures(request): count += 1 atMapB = inFeatB.attributes() tmpGeom = inFeatB.geometry() if engine.intersects(tmpGeom.geometry()): int_geom = geom.intersection(tmpGeom) lstIntersectingB.append(tmpGeom) if not int_geom: # There was a problem creating the intersection ProcessingLog.addToLog( ProcessingLog.LOG_INFO, self.tr("GEOS geoprocessing error: One or more input features have invalid geometry."), ) int_geom = QgsGeometry() else: int_geom = QgsGeometry(int_geom) if ( int_geom.wkbType() == QgsWkbTypes.Unknown or QgsWkbTypes.flatType(int_geom.geometry().wkbType()) == QgsWkbTypes.GeometryCollection ): # Intersection produced different geomety types temp_list = int_geom.asGeometryCollection() for i in temp_list: if i.type() == geom.type(): int_geom = QgsGeometry(i) try: outFeat.setGeometry(int_geom) outFeat.setAttributes(atMapA + atMapB) writer.addFeature(outFeat) except: ProcessingLog.addToLog( ProcessingLog.LOG_INFO, self.tr( "Feature geometry error: One or more output features ignored due to invalid geometry." ), ) else: # Geometry list: prevents writing error # in geometries of different types # produced by the intersection # fix #3549 if int_geom.wkbType() in wkbTypeGroups[wkbTypeGroups[int_geom.wkbType()]]: try: outFeat.setGeometry(int_geom) outFeat.setAttributes(atMapA + atMapB) writer.addFeature(outFeat) except: ProcessingLog.addToLog( ProcessingLog.LOG_INFO, self.tr( "Feature geometry error: One or more output features ignored due to invalid geometry." ), ) # the remaining bit of inFeatA's geometry # if there is nothing left, this will just silently fail and we're good diff_geom = QgsGeometry(geom) if len(lstIntersectingB) != 0: intB = QgsGeometry.unaryUnion(lstIntersectingB) diff_geom = diff_geom.difference(intB) if diff_geom.isGeosEmpty() or not diff_geom.isGeosValid(): ProcessingLog.addToLog( ProcessingLog.LOG_ERROR, self.tr("GEOS geoprocessing error: One or more input features have invalid geometry."), ) if ( diff_geom.wkbType() == 0 or QgsWkbTypes.flatType(diff_geom.geometry().wkbType()) == QgsWkbTypes.GeometryCollection ): temp_list = diff_geom.asGeometryCollection() for i in temp_list: if i.type() == geom.type(): diff_geom = QgsGeometry(i) try: outFeat.setGeometry(diff_geom) outFeat.setAttributes(atMapA) writer.addFeature(outFeat) except: ProcessingLog.addToLog( ProcessingLog.LOG_INFO, self.tr("Feature geometry error: One or more output features ignored due to invalid geometry."), ) length = len(vlayerA.fields()) atMapA = [None] * length featuresA = vector.features(vlayerB) nFeat = len(featuresA) for inFeatA in featuresA: progress.setPercentage(nElement / float(nFeat) * 100) add = False geom = inFeatA.geometry() diff_geom = QgsGeometry(geom) atMap = [None] * length atMap.extend(inFeatA.attributes()) intersects = indexB.intersects(geom.boundingBox()) if len(intersects) < 1: try: outFeat.setGeometry(geom) outFeat.setAttributes(atMap) writer.addFeature(outFeat) except: ProcessingLog.addToLog( ProcessingLog.LOG_INFO, self.tr("Feature geometry error: One or more output features ignored due to invalid geometry."), ) else: request = QgsFeatureRequest().setFilterFids(intersects) # use prepared geometries for faster intersection tests engine = QgsGeometry.createGeometryEngine(diff_geom.geometry()) engine.prepareGeometry() for inFeatB in vlayerA.getFeatures(request): atMapB = inFeatB.attributes() tmpGeom = inFeatB.geometry() if engine.intersects(tmpGeom.geometry()): add = True diff_geom = QgsGeometry(diff_geom.difference(tmpGeom)) if diff_geom.isGeosEmpty() or not diff_geom.isGeosValid(): ProcessingLog.addToLog( ProcessingLog.LOG_ERROR, self.tr("GEOS geoprocessing error: One or more input features have invalid geometry."), ) else: try: # Ihis only happends if the bounding box # intersects, but the geometry doesn't outFeat.setGeometry(diff_geom) outFeat.setAttributes(atMap) writer.addFeature(outFeat) except: ProcessingLog.addToLog( ProcessingLog.LOG_INFO, self.tr( "Feature geometry error: One or more output features ignored due to invalid geometry." ), ) if add: try: outFeat.setGeometry(diff_geom) outFeat.setAttributes(atMap) writer.addFeature(outFeat) except: ProcessingLog.addToLog( ProcessingLog.LOG_INFO, self.tr("Feature geometry error: One or more output features ignored due to invalid geometry."), ) nElement += 1 del writer
def clip(layer_to_clip, mask_layer, callback=None): """Clip a vector layer with another. Issue https://github.com/inasafe/inasafe/issues/3186 Note : This algorithm is copied from : https://github.com/qgis/QGIS/blob/master/python/plugins/processing/algs/ qgis/Clip.py :param layer_to_clip: The vector layer to clip. :type layer_to_clip: QgsVectorLayer :param mask_layer: The vector layer to use for clipping. :type mask_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: The clip vector layer. :rtype: QgsVectorLayer .. versionadded:: 4.0 """ output_layer_name = clip_steps['output_layer_name'] output_layer_name = output_layer_name % ( layer_to_clip.keywords['layer_purpose']) processing_step = clip_steps['step_name'] writer = create_memory_layer( output_layer_name, layer_to_clip.geometryType(), layer_to_clip.crs(), layer_to_clip.fields() ) writer.startEditing() # Begin copy/paste from Processing plugin. # Please follow their code as their code is optimized. # The code below is not following our coding standards because we want to # be able to track any diffs from QGIS easily. # first build up a list of clip geometries clip_geometries = [] request = QgsFeatureRequest().setSubsetOfAttributes([]) for mask_feature in mask_layer.getFeatures(request): clip_geometries.append(QgsGeometry(mask_feature.geometry())) # are we clipping against a single feature? if so, # we can show finer progress reports if len(clip_geometries) > 1: # noinspection PyTypeChecker,PyCallByClass,PyArgumentList combined_clip_geom = QgsGeometry.unaryUnion(clip_geometries) single_clip_feature = False else: combined_clip_geom = clip_geometries[0] single_clip_feature = True # use prepared geometries for faster intersection tests # noinspection PyArgumentList engine = QgsGeometry.createGeometryEngine(combined_clip_geom.geometry()) engine.prepareGeometry() tested_feature_ids = set() for i, clip_geom in enumerate(clip_geometries): request = QgsFeatureRequest().setFilterRect(clip_geom.boundingBox()) input_features = [f for f in layer_to_clip.getFeatures(request)] if not input_features: continue if single_clip_feature: total = 100.0 / len(input_features) else: total = 0 for current, in_feat in enumerate(input_features): if not in_feat.geometry(): continue if in_feat.id() in tested_feature_ids: # don't retest a feature we have already checked continue tested_feature_ids.add(in_feat.id()) if not engine.intersects(in_feat.geometry().geometry()): continue if not engine.contains(in_feat.geometry().geometry()): cur_geom = in_feat.geometry() new_geom = combined_clip_geom.intersection(cur_geom) if new_geom.wkbType() == QgsWKBTypes.Unknown \ or QgsWKBTypes.flatType( new_geom.geometry().wkbType()) == \ QgsWKBTypes.GeometryCollection: int_com = in_feat.geometry().combine(new_geom) int_sym = in_feat.geometry().symDifference(new_geom) if not int_com or not int_sym: # LOGGER.debug( # tr('GEOS geoprocessing error: One or more input ' # 'features have invalid geometry.')) pass else: new_geom = int_com.difference(int_sym) if new_geom.isGeosEmpty()\ or not new_geom.isGeosValid(): # LOGGER.debug( # tr('GEOS geoprocessing error: One or more ' # 'input features have invalid geometry.')) pass else: # clip geometry totally contains feature geometry, # so no need to perform intersection new_geom = in_feat.geometry() try: out_feat = QgsFeature() out_feat.setGeometry(new_geom) out_feat.setAttributes(in_feat.attributes()) if new_geom.type() == layer_to_clip.geometryType(): writer.addFeature(out_feat) except: LOGGER.debug( tr('Feature geometry error: One or more output features ' 'ignored due to invalid geometry.')) continue # TODO implement callback if single_clip_feature: # progress.setPercentage(int(current * total)) pass if not single_clip_feature: # coarse progress report for multiple clip geometries # progress.setPercentage(100.0 * i / len(clip_geoms)) pass # End copy/paste from Processing plugin. writer.commitChanges() writer.keywords = layer_to_clip.keywords.copy() writer.keywords['title'] = output_layer_name check_layer(writer) return writer
def processAlgorithm(self, parameters, context, feedback): source = self.parameterAsSource(parameters, self.INPUT, context) if source is None: raise QgsProcessingException( self.invalidSourceError(parameters, self.INPUT)) if self.parameterAsBool(parameters, self.KEEP_FIELDS, context): fields = source.fields() else: fields = QgsFields() (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, QgsWkbTypes.Polygon, source.sourceCrs()) if sink is None: raise QgsProcessingException( self.invalidSinkError(parameters, self.OUTPUT)) allLinesList = [] features = source.getFeatures( QgsFeatureRequest().setSubsetOfAttributes([])) feedback.pushInfo( QCoreApplication.translate('Polygonize', 'Processing lines…')) total = (40.0 / source.featureCount()) if source.featureCount() else 1 for current, inFeat in enumerate(features): if feedback.isCanceled(): break if inFeat.geometry(): allLinesList.append(inFeat.geometry()) feedback.setProgress(int(current * total)) feedback.setProgress(40) feedback.pushInfo( QCoreApplication.translate('Polygonize', 'Noding lines…')) allLines = QgsGeometry.unaryUnion(allLinesList) if feedback.isCanceled(): return {} feedback.setProgress(45) feedback.pushInfo( QCoreApplication.translate('Polygonize', 'Polygonizing…')) polygons = QgsGeometry.polygonize([allLines]) if polygons.isEmpty(): feedback.reportError(self.tr('No polygons were created!')) feedback.setProgress(50) if not polygons.isEmpty(): feedback.pushInfo( QCoreApplication.translate('Polygonize', 'Saving polygons…')) total = 50.0 / polygons.constGet().numGeometries() for i in range(polygons.constGet().numGeometries()): if feedback.isCanceled(): break outFeat = QgsFeature() geom = QgsGeometry(polygons.constGet().geometryN(i).clone()) outFeat.setGeometry(geom) sink.addFeature(outFeat, QgsFeatureSink.FastInsert) feedback.setProgress(50 + int(current * total)) return {self.OUTPUT: dest_id}
def get_gaps_in_polygon_layer(self, layer, include_roads): """ Find gaps in a continuous layer in space. Ported/adapted to Python from: https://github.com/qgis/QGIS/blob/2c536307476e205b83d86863b903d7ea9d628f0d/src/plugins/topology/topolTest.cpp#L579-L726 """ request = QgsFeatureRequest().setSubsetOfAttributes([]) features = layer.getFeatures(request) featureCollection = list() for feature in features: if feature.geometry().isEmpty(): continue if not feature.geometry().isGeosValid(): continue if feature.geometry().isMultipart() and feature.geometry().type( ) == QgsWkbTypes.PolygonGeometry: for polygon in feature.geometry().asMultiPolygon(): featureCollection.append( QgsGeometry.fromPolygonXY(polygon)) continue featureCollection.append(feature.geometry()) union_geom = QgsGeometry.unaryUnion(featureCollection) aux_convex_hull = union_geom.convexHull() buffer_extent = QgsGeometry.fromRect(union_geom.boundingBox()).buffer( 2, 3) buffer_diff = buffer_extent.difference( QgsGeometry.fromRect(union_geom.boundingBox())) diff_geoms = buffer_extent.difference(union_geom).difference( buffer_diff) if not diff_geoms: return None feature_error = list() if not diff_geoms.isMultipart(): if include_roads and diff_geoms.touches( union_geom) and diff_geoms.intersects(buffer_diff): print("Unique value and no error") return None for geometry in diff_geoms.asMultiPolygon(): conflict_geom = QgsGeometry.fromPolygonXY(geometry) if not include_roads and conflict_geom.touches( union_geom) and conflict_geom.intersects(buffer_diff): continue if not union_geom.isMultipart() and conflict_geom.touches( union_geom) and conflict_geom.intersects(buffer_diff): continue feature_error.append(conflict_geom) unified_error = QgsGeometry.collectGeometry(feature_error) feature_error.clear() clean_errors = unified_error.intersection(aux_convex_hull) return self.extract_geoms_by_type(clean_errors, [QgsWkbTypes.PolygonGeometry])
def buffering(progress, writer, distance, field, useField, layer, dissolve, segments, endCapStyle=1, joinStyle=1, mitreLimit=2): if useField: field = layer.fieldNameIndex(field) outFeat = QgsFeature() current = 0 features = vector.features(layer) total = 100.0 / float(len(features)) # With dissolve if dissolve: first = True buffered_geometries = [] for inFeat in features: attrs = inFeat.attributes() if useField: value = attrs[field] else: value = distance inGeom = inFeat.geometry() if not inGeom: ProcessingLog.addToLog(ProcessingLog.LOG_WARNING, 'Feature {} has empty geometry. Skipping...'.format(inFeat.id())) continue if not inGeom.isGeosValid(): ProcessingLog.addToLog(ProcessingLog.LOG_WARNING, 'Feature {} has invalid geometry. Skipping...'.format(inFeat.id())) continue buffered_geometries.append(inGeom.buffer(float(value), segments, endCapStyle, joinStyle, mitreLimit)) current += 1 progress.setPercentage(int(current * total)) final_geometry = QgsGeometry.unaryUnion(buffered_geometries) outFeat.setGeometry(final_geometry) outFeat.setAttributes(attrs) writer.addFeature(outFeat) else: # Without dissolve for inFeat in features: attrs = inFeat.attributes() if useField: value = attrs[field] else: value = distance inGeom = inFeat.geometry() outFeat = QgsFeature() if inGeom.isEmpty() or inGeom.isGeosEmpty(): pass elif not inGeom.isGeosValid(): ProcessingLog.addToLog(ProcessingLog.LOG_WARNING, 'Feature {} has invalid geometry. Skipping...'.format(inFeat.id())) continue else: outGeom = inGeom.buffer(float(value), segments, endCapStyle, joinStyle, mitreLimit) outFeat.setGeometry(outGeom) outFeat.setAttributes(attrs) writer.addFeature(outFeat) current += 1 progress.setPercentage(int(current * total)) del writer
def processAlgorithm(self, progress): vlayerA = dataobjects.getObjectFromUri(self.getParameterValue(Union.INPUT)) vlayerB = dataobjects.getObjectFromUri(self.getParameterValue(Union.INPUT2)) GEOS_EXCEPT = True FEATURE_EXCEPT = True vproviderA = vlayerA.dataProvider() fields = vector.combineVectorFields(vlayerA, vlayerB) names = [field.name() for field in fields] ProcessingLog.addToLog(ProcessingLog.LOG_INFO, unicode(names)) writer = self.getOutputFromName(Union.OUTPUT).getVectorWriter(fields, vproviderA.geometryType(), vproviderA.crs()) inFeatA = QgsFeature() inFeatB = QgsFeature() outFeat = QgsFeature() indexA = vector.spatialindex(vlayerB) indexB = vector.spatialindex(vlayerA) count = 0 nElement = 0 featuresA = vector.features(vlayerA) nFeat = len(featuresA) for inFeatA in featuresA: progress.setPercentage(nElement / float(nFeat) * 50) nElement += 1 lstIntersectingB = [] geom = QgsGeometry(inFeatA.geometry()) atMapA = inFeatA.attributes() intersects = indexA.intersects(geom.boundingBox()) if len(intersects) < 1: try: outFeat.setGeometry(geom) outFeat.setAttributes(atMapA) writer.addFeature(outFeat) except: # This really shouldn't happen, as we haven't # edited the input geom at all raise GeoAlgorithmExecutionException( self.tr('Feature exception while computing union')) else: for id in intersects: count += 1 request = QgsFeatureRequest().setFilterFid(id) inFeatB = vlayerB.getFeatures(request).next() atMapB = inFeatB.attributes() tmpGeom = QgsGeometry(inFeatB.geometry()) if geom.intersects(tmpGeom): int_geom = geom.intersection(tmpGeom) lstIntersectingB.append(tmpGeom) if int_geom is None: # There was a problem creating the intersection raise GeoAlgorithmExecutionException( self.tr('Geometry exception while computing ' 'intersection')) else: int_geom = QgsGeometry(int_geom) if int_geom.wkbType() == QGis.WKBUnknown or QgsWKBTypes.flatType(int_geom.geometry().wkbType()) == QgsWKBTypes.GeometryCollection: # Intersection produced different geomety types temp_list = int_geom.asGeometryCollection() for i in temp_list: if i.type() == geom.type(): int_geom = QgsGeometry(i) try: outFeat.setGeometry(int_geom) attrs = [] attrs.extend(atMapA) attrs.extend(atMapB) outFeat.setAttributes(attrs) writer.addFeature(outFeat) except Exception as err: raise GeoAlgorithmExecutionException( self.tr('Feature exception while computing union')) try: # the remaining bit of inFeatA's geometry # if there is nothing left, this will just silently fail and we're good diff_geom = QgsGeometry(geom) if len(lstIntersectingB) != 0: intB = QgsGeometry.unaryUnion(lstIntersectingB) diff_geom = diff_geom.difference(intB) if diff_geom.wkbType() == 0 or QgsWKBTypes.flatType(int_geom.geometry().wkbType()) == QgsWKBTypes.GeometryCollection: temp_list = diff_geom.asGeometryCollection() for i in temp_list: if i.type() == geom.type(): diff_geom = QgsGeometry(i) outFeat.setGeometry(diff_geom) outFeat.setAttributes(atMapA) writer.addFeature(outFeat) except Exception as err: raise GeoAlgorithmExecutionException( self.tr('Feature exception while computing union')) length = len(vproviderA.fields()) featuresA = vector.features(vlayerB) nFeat = len(featuresA) for inFeatA in featuresA: progress.setPercentage(nElement / float(nFeat) * 100) add = False geom = QgsGeometry(inFeatA.geometry()) diff_geom = QgsGeometry(geom) atMap = [None] * length atMap.extend(inFeatA.attributes()) intersects = indexB.intersects(geom.boundingBox()) if len(intersects) < 1: try: outFeat.setGeometry(geom) outFeat.setAttributes(atMap) writer.addFeature(outFeat) except Exception as err: raise GeoAlgorithmExecutionException( self.tr('Feature exception while computing union')) else: for id in intersects: request = QgsFeatureRequest().setFilterFid(id) inFeatB = vlayerA.getFeatures(request).next() atMapB = inFeatB.attributes() tmpGeom = QgsGeometry(inFeatB.geometry()) try: if diff_geom.intersects(tmpGeom): add = True diff_geom = QgsGeometry( diff_geom.difference(tmpGeom)) else: # Ihis only happends if the bounding box # intersects, but the geometry doesn't outFeat.setGeometry(diff_geom) outFeat.setAttributes(atMap) writer.addFeature(outFeat) except Exception as err: raise GeoAlgorithmExecutionException( self.tr('Geometry exception while computing intersection')) if add: try: outFeat.setGeometry(diff_geom) outFeat.setAttributes(atMap) writer.addFeature(outFeat) except Exception as err: raise err FEATURE_EXCEPT = False nElement += 1 del writer if not GEOS_EXCEPT: ProcessingLog.addToLog(ProcessingLog.LOG_WARNING, self.tr('Geometry exception while computing intersection')) if not FEATURE_EXCEPT: ProcessingLog.addToLog(ProcessingLog.LOG_WARNING, self.tr('Feature exception while computing intersection'))
def processAlgorithm(self, progress): source_layer = dataobjects.getObjectFromUri( self.getParameterValue(Clip.INPUT)) mask_layer = dataobjects.getObjectFromUri( self.getParameterValue(Clip.OVERLAY)) writer = self.getOutputFromName(self.OUTPUT).getVectorWriter( source_layer.fields(), QgsWkbTypes.multiType(source_layer.wkbType()), source_layer.crs()) # first build up a list of clip geometries clip_geoms = [] for maskFeat in vector.features(mask_layer, QgsFeatureRequest().setSubsetOfAttributes([])): clip_geoms.append(maskFeat.geometry()) # are we clipping against a single feature? if so, we can show finer progress reports if len(clip_geoms) > 1: combined_clip_geom = QgsGeometry.unaryUnion(clip_geoms) single_clip_feature = False else: combined_clip_geom = clip_geoms[0] single_clip_feature = True # use prepared geometries for faster intersection tests engine = QgsGeometry.createGeometryEngine(combined_clip_geom.geometry()) engine.prepareGeometry() tested_feature_ids = set() for i, clip_geom in enumerate(clip_geoms): input_features = [f for f in vector.features(source_layer, QgsFeatureRequest().setFilterRect(clip_geom.boundingBox()))] if not input_features: continue if single_clip_feature: total = 100.0 / len(input_features) else: total = 0 for current, in_feat in enumerate(input_features): if not in_feat.geometry(): continue if in_feat.id() in tested_feature_ids: # don't retest a feature we have already checked continue tested_feature_ids.add(in_feat.id()) if not engine.intersects(in_feat.geometry().geometry()): continue if not engine.contains(in_feat.geometry().geometry()): cur_geom = in_feat.geometry() new_geom = combined_clip_geom.intersection(cur_geom) if new_geom.wkbType() == QgsWkbTypes.Unknown or QgsWkbTypes.flatType(new_geom.geometry().wkbType()) == QgsWkbTypes.GeometryCollection: int_com = in_feat.geometry().combine(new_geom) int_sym = in_feat.geometry().symDifference(new_geom) if not int_com or not int_sym: ProcessingLog.addToLog(ProcessingLog.LOG_ERROR, self.tr('GEOS geoprocessing error: One or more ' 'input features have invalid geometry.')) else: new_geom = int_com.difference(int_sym) if new_geom.isGeosEmpty() or not new_geom.isGeosValid(): ProcessingLog.addToLog(ProcessingLog.LOG_ERROR, self.tr('GEOS geoprocessing error: One or more ' 'input features have invalid geometry.')) else: # clip geometry totally contains feature geometry, so no need to perform intersection new_geom = in_feat.geometry() try: out_feat = QgsFeature() out_feat.setGeometry(new_geom) out_feat.setAttributes(in_feat.attributes()) writer.addFeature(out_feat) except: ProcessingLog.addToLog(ProcessingLog.LOG_ERROR, self.tr('Feature geometry error: One or more ' 'output features ignored due to ' 'invalid geometry.')) continue if single_clip_feature: progress.setPercentage(int(current * total)) if not single_clip_feature: # coarse progress report for multiple clip geometries progress.setPercentage(100.0 * i / len(clip_geoms)) del writer
def processAlgorithm(self, progress): vlayerA = dataobjects.getObjectFromUri(self.getParameterValue(Union.INPUT)) vlayerB = dataobjects.getObjectFromUri(self.getParameterValue(Union.INPUT2)) geomType = vlayerA.wkbType() fields = vector.combineVectorFields(vlayerA, vlayerB) writer = self.getOutputFromName(Union.OUTPUT).getVectorWriter(fields, geomType, vlayerA.crs()) inFeatA = QgsFeature() inFeatB = QgsFeature() outFeat = QgsFeature() indexA = vector.spatialindex(vlayerB) indexB = vector.spatialindex(vlayerA) count = 0 nElement = 0 featuresA = vector.features(vlayerA) nFeat = len(featuresA) if len(featuresA) > 0 else 1 for inFeatA in featuresA: progress.setPercentage(nElement / float(nFeat) * 50) nElement += 1 lstIntersectingB = [] geom = QgsGeometry(inFeatA.geometry()) atMapA = inFeatA.attributes() intersects = indexA.intersects(geom.boundingBox()) if len(intersects) < 1: try: outFeat.setGeometry(geom) outFeat.setAttributes(atMapA) writer.addFeature(outFeat) except: # This really shouldn't happen, as we haven't # edited the input geom at all ProcessingLog.addToLog(ProcessingLog.LOG_INFO, self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.')) else: for id in intersects: count += 1 request = QgsFeatureRequest().setFilterFid(id) inFeatB = vlayerB.getFeatures(request).next() atMapB = inFeatB.attributes() tmpGeom = QgsGeometry(inFeatB.geometry()) if geom.intersects(tmpGeom): int_geom = geom.intersection(tmpGeom) lstIntersectingB.append(tmpGeom) if int_geom is None: # There was a problem creating the intersection ProcessingLog.addToLog(ProcessingLog.LOG_INFO, self.tr('GEOS geoprocessing error: One or more input features have invalid geometry.')) int_geom = QgsGeometry() else: int_geom = QgsGeometry(int_geom) # TODO: the result may have a different dimension (e.g. intersection of two polygons may result in a single point) # or the result may be a collection of geometries (e.g. intersection of two polygons results in three polygons and one linestring). # We need to filter out all acceptable geometries into a single (possibly multi-part) geometry - and we need # to do it consistently also in the code further below if int_geom.wkbType() == QGis.WKBUnknown or QgsWKBTypes.flatType(int_geom.geometry().wkbType()) == QgsWKBTypes.GeometryCollection: # Intersection produced different geomety types temp_list = int_geom.asGeometryCollection() for i in temp_list: if i.type() == geom.type(): int_geom = QgsGeometry(i) try: outFeat.setGeometry(int_geom) outFeat.setAttributes(atMapA + atMapB) writer.addFeature(outFeat) except: ProcessingLog.addToLog(ProcessingLog.LOG_INFO, self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.')) else: # Geometry list: prevents writing error # in geometries of different types # produced by the intersection # fix #3549 if int_geom.wkbType() in wkbTypeGroups[wkbTypeGroups[int_geom.wkbType()]]: try: outFeat.setGeometry(int_geom) outFeat.setAttributes(atMapA + atMapB) writer.addFeature(outFeat) except: ProcessingLog.addToLog(ProcessingLog.LOG_INFO, self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.')) # the remaining bit of inFeatA's geometry # if there is nothing left, this will just silently fail and we're good diff_geom = QgsGeometry(geom) if len(lstIntersectingB) != 0: intB = QgsGeometry.unaryUnion(lstIntersectingB) diff_geom = diff_geom.difference(intB) if diff_geom is None: ProcessingLog.addToLog(ProcessingLog.LOG_INFO, self.tr('GEOS geoprocessing error: One or more input features have invalid geometry.')) diff_geom = QgsGeometry() if diff_geom.isGeosEmpty() or not diff_geom.isGeosValid(): ProcessingLog.addToLog(ProcessingLog.LOG_ERROR, self.tr('GEOS geoprocessing error: One or more input features have invalid geometry.')) # TODO: correctly handly different output geometry types (see todo above) if diff_geom.wkbType() == 0 or QgsWKBTypes.flatType(diff_geom.geometry().wkbType()) == QgsWKBTypes.GeometryCollection: temp_list = diff_geom.asGeometryCollection() for i in temp_list: if i.type() == geom.type(): diff_geom = QgsGeometry(i) try: outFeat.setGeometry(diff_geom) outFeat.setAttributes(atMapA) writer.addFeature(outFeat) except: ProcessingLog.addToLog(ProcessingLog.LOG_INFO, self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.')) length = len(vlayerA.fields()) atMapA = [None] * length featuresA = vector.features(vlayerB) nFeat = len(featuresA) if len(featuresA) else 1 for inFeatA in featuresA: progress.setPercentage(nElement / float(nFeat) * 100) add = False geom = QgsGeometry(inFeatA.geometry()) atMap = [None] * length atMap.extend(inFeatA.attributes()) intersects = indexB.intersects(geom.boundingBox()) lstIntersectingA = [] for id in intersects: request = QgsFeatureRequest().setFilterFid(id) inFeatB = vlayerA.getFeatures(request).next() atMapB = inFeatB.attributes() tmpGeom = QgsGeometry(inFeatB.geometry()) if geom.intersects(tmpGeom): lstIntersectingA.append(tmpGeom) if len(lstIntersectingA) == 0: res_geom = geom else: intA = QgsGeometry.unaryUnion(lstIntersectingA) res_geom = geom.difference(intA) if res_geom is None: ProcessingLog.addToLog(ProcessingLog.LOG_INFO, self.tr('GEOS geoprocessing error: One or more input features have invalid geometry.')) res_geom = QgsGeometry() if res_geom.isGeosEmpty() or not res_geom.isGeosValid(): ProcessingLog.addToLog(ProcessingLog.LOG_ERROR, self.tr('GEOS geoprocessing error: One or more input features have invalid geometry.')) # TODO: correctly handly different output geometry types (see todo above) try: outFeat.setGeometry(res_geom) outFeat.setAttributes(atMap) writer.addFeature(outFeat) except: ProcessingLog.addToLog(ProcessingLog.LOG_INFO, self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.')) nElement += 1 del writer
def linearMatrix(self, parameters, context, source, inField, target_source, targetField, same_source_and_target, matType, nPoints, feedback): if same_source_and_target: # need to fetch an extra point from the index, since the closest match will always be the same # as the input feature nPoints += 1 inIdx = source.fields().lookupField(inField) outIdx = target_source.fields().lookupField(targetField) fields = QgsFields() input_id_field = source.fields()[inIdx] input_id_field.setName('InputID') fields.append(input_id_field) if matType == 0: target_id_field = target_source.fields()[outIdx] target_id_field.setName('TargetID') fields.append(target_id_field) fields.append(QgsField('Distance', QVariant.Double)) else: fields.append(QgsField('MEAN', QVariant.Double)) fields.append(QgsField('STDDEV', QVariant.Double)) fields.append(QgsField('MIN', QVariant.Double)) fields.append(QgsField('MAX', QVariant.Double)) out_wkb = QgsWkbTypes.multiType(source.wkbType()) if matType == 0 else source.wkbType() (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, out_wkb, source.sourceCrs()) if sink is None: raise QgsProcessingException(self.invalidSinkError(parameters, self.OUTPUT)) index = QgsSpatialIndex(target_source.getFeatures(QgsFeatureRequest().setSubsetOfAttributes([]).setDestinationCrs(source.sourceCrs(), context.transformContext())), feedback) distArea = QgsDistanceArea() distArea.setSourceCrs(source.sourceCrs(), context.transformContext()) distArea.setEllipsoid(context.project().ellipsoid()) features = source.getFeatures(QgsFeatureRequest().setSubsetOfAttributes([inIdx])) total = 100.0 / source.featureCount() if source.featureCount() else 0 for current, inFeat in enumerate(features): if feedback.isCanceled(): break inGeom = inFeat.geometry() inID = str(inFeat.attributes()[inIdx]) featList = index.nearestNeighbor(inGeom.asPoint(), nPoints) distList = [] vari = 0.0 request = QgsFeatureRequest().setFilterFids(featList).setSubsetOfAttributes([outIdx]).setDestinationCrs(source.sourceCrs(), context.transformContext()) for outFeat in target_source.getFeatures(request): if feedback.isCanceled(): break if same_source_and_target and inFeat.id() == outFeat.id(): continue outID = outFeat.attributes()[outIdx] outGeom = outFeat.geometry() dist = distArea.measureLine(inGeom.asPoint(), outGeom.asPoint()) if matType == 0: out_feature = QgsFeature() out_geom = QgsGeometry.unaryUnion([inFeat.geometry(), outFeat.geometry()]) out_feature.setGeometry(out_geom) out_feature.setAttributes([inID, outID, dist]) sink.addFeature(out_feature, QgsFeatureSink.FastInsert) else: distList.append(float(dist)) if matType != 0: mean = sum(distList) / len(distList) for i in distList: vari += (i - mean) * (i - mean) vari = math.sqrt(vari / len(distList)) out_feature = QgsFeature() out_feature.setGeometry(inFeat.geometry()) out_feature.setAttributes([inID, mean, vari, min(distList), max(distList)]) sink.addFeature(out_feature, QgsFeatureSink.FastInsert) feedback.setProgress(int(current * total)) return {self.OUTPUT: dest_id}
def processAlgorithm(self, parameters, context, feedback): vlayerA = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(Union.INPUT), context) vlayerB = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(Union.INPUT2), context) geomType = vlayerA.wkbType() fields = vector.combineFields(vlayerA.fields(), vlayerB.fields()) writer = self.getOutputFromName(Union.OUTPUT).getVectorWriter(fields, geomType, vlayerA.crs(), context) inFeatA = QgsFeature() inFeatB = QgsFeature() outFeat = QgsFeature() indexA = QgsProcessingUtils.createSpatialIndex(vlayerB, context) indexB = QgsProcessingUtils.createSpatialIndex(vlayerA, context) count = 0 nElement = 0 featuresA = QgsProcessingUtils.getFeatures(vlayerA, context) nFeat = QgsProcessingUtils.featureCount(vlayerA, context) for inFeatA in featuresA: feedback.setProgress(nElement / float(nFeat) * 50) nElement += 1 lstIntersectingB = [] geom = inFeatA.geometry() atMapA = inFeatA.attributes() intersects = indexA.intersects(geom.boundingBox()) if len(intersects) < 1: try: outFeat.setGeometry(geom) outFeat.setAttributes(atMapA) writer.addFeature(outFeat, QgsFeatureSink.FastInsert) except: # This really shouldn't happen, as we haven't # edited the input geom at all QgsMessageLog.logMessage(self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.'), self.tr('Processing'), QgsMessageLog.INFO) else: request = QgsFeatureRequest().setFilterFids(intersects) engine = QgsGeometry.createGeometryEngine(geom.geometry()) engine.prepareGeometry() for inFeatB in vlayerB.getFeatures(request): count += 1 atMapB = inFeatB.attributes() tmpGeom = inFeatB.geometry() if engine.intersects(tmpGeom.geometry()): int_geom = geom.intersection(tmpGeom) lstIntersectingB.append(tmpGeom) if not int_geom: # There was a problem creating the intersection QgsMessageLog.logMessage(self.tr('GEOS geoprocessing error: One or more input features have invalid geometry.'), self.tr('Processing'), QgsMessageLog.INFO) int_geom = QgsGeometry() else: int_geom = QgsGeometry(int_geom) if int_geom.wkbType() == QgsWkbTypes.Unknown or QgsWkbTypes.flatType(int_geom.geometry().wkbType()) == QgsWkbTypes.GeometryCollection: # Intersection produced different geomety types temp_list = int_geom.asGeometryCollection() for i in temp_list: if i.type() == geom.type(): int_geom = QgsGeometry(i) try: outFeat.setGeometry(int_geom) outFeat.setAttributes(atMapA + atMapB) writer.addFeature(outFeat, QgsFeatureSink.FastInsert) except: QgsMessageLog.logMessage(self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.'), self.tr('Processing'), QgsMessageLog.INFO) else: # Geometry list: prevents writing error # in geometries of different types # produced by the intersection # fix #3549 if int_geom.wkbType() in wkbTypeGroups[wkbTypeGroups[int_geom.wkbType()]]: try: outFeat.setGeometry(int_geom) outFeat.setAttributes(atMapA + atMapB) writer.addFeature(outFeat, QgsFeatureSink.FastInsert) except: QgsMessageLog.logMessage(self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.'), self.tr('Processing'), QgsMessageLog.INFO) # the remaining bit of inFeatA's geometry # if there is nothing left, this will just silently fail and we're good diff_geom = QgsGeometry(geom) if len(lstIntersectingB) != 0: intB = QgsGeometry.unaryUnion(lstIntersectingB) diff_geom = diff_geom.difference(intB) if diff_geom.wkbType() == 0 or QgsWkbTypes.flatType(diff_geom.geometry().wkbType()) == QgsWkbTypes.GeometryCollection: temp_list = diff_geom.asGeometryCollection() for i in temp_list: if i.type() == geom.type(): diff_geom = QgsGeometry(i) try: outFeat.setGeometry(diff_geom) outFeat.setAttributes(atMapA) writer.addFeature(outFeat, QgsFeatureSink.FastInsert) except: QgsMessageLog.logMessage(self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.'), self.tr('Processing'), QgsMessageLog.INFO) length = len(vlayerA.fields()) atMapA = [None] * length featuresA = QgsProcessingUtils.getFeatures(vlayerB, context) nFeat = QgsProcessingUtils.featureCount(vlayerB, context) for inFeatA in featuresA: feedback.setProgress(nElement / float(nFeat) * 100) add = False geom = inFeatA.geometry() diff_geom = QgsGeometry(geom) atMap = [None] * length atMap.extend(inFeatA.attributes()) intersects = indexB.intersects(geom.boundingBox()) if len(intersects) < 1: try: outFeat.setGeometry(geom) outFeat.setAttributes(atMap) writer.addFeature(outFeat, QgsFeatureSink.FastInsert) except: QgsMessageLog.logMessage(self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.'), self.tr('Processing'), QgsMessageLog.INFO) else: request = QgsFeatureRequest().setFilterFids(intersects) # use prepared geometries for faster intersection tests engine = QgsGeometry.createGeometryEngine(diff_geom.geometry()) engine.prepareGeometry() for inFeatB in vlayerA.getFeatures(request): atMapB = inFeatB.attributes() tmpGeom = inFeatB.geometry() if engine.intersects(tmpGeom.geometry()): add = True diff_geom = QgsGeometry(diff_geom.difference(tmpGeom)) else: try: # Ihis only happens if the bounding box # intersects, but the geometry doesn't outFeat.setGeometry(diff_geom) outFeat.setAttributes(atMap) writer.addFeature(outFeat, QgsFeatureSink.FastInsert) except: QgsMessageLog.logMessage(self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.'), self.tr('Processing'), QgsMessageLog.INFO) if add: try: outFeat.setGeometry(diff_geom) outFeat.setAttributes(atMap) writer.addFeature(outFeat, QgsFeatureSink.FastInsert) except: QgsMessageLog.logMessage(self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.'), self.tr('Processing'), QgsMessageLog.INFO) nElement += 1 del writer
def processAlgorithm(self, parameters, context, feedback): sourceA = self.parameterAsSource(parameters, self.INPUT, context) sourceB = self.parameterAsSource(parameters, self.OVERLAY, context) geomType = QgsWkbTypes.multiType(sourceA.wkbType()) fields = vector.combineFields(sourceA.fields(), sourceB.fields()) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, geomType, sourceA.sourceCrs()) featA = QgsFeature() featB = QgsFeature() outFeat = QgsFeature() indexA = QgsSpatialIndex(sourceA, feedback) indexB = QgsSpatialIndex(sourceB.getFeatures(QgsFeatureRequest().setSubsetOfAttributes([]).setDestinationCrs(sourceA.sourceCrs())), feedback) total = 100.0 / (sourceA.featureCount() * sourceB.featureCount()) if sourceA.featureCount() and sourceB.featureCount() else 1 count = 0 for featA in sourceA.getFeatures(): if feedback.isCanceled(): break lstIntersectingB = [] geom = featA.geometry() atMapA = featA.attributes() intersects = indexB.intersects(geom.boundingBox()) if len(intersects) < 1: try: outFeat.setGeometry(geom) outFeat.setAttributes(atMapA) sink.addFeature(outFeat, QgsFeatureSink.FastInsert) except: # This really shouldn't happen, as we haven't # edited the input geom at all feedback.pushInfo(self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.')) else: request = QgsFeatureRequest().setFilterFids(intersects).setSubsetOfAttributes([]) request.setDestinationCrs(sourceA.sourceCrs()) engine = QgsGeometry.createGeometryEngine(geom.geometry()) engine.prepareGeometry() for featB in sourceB.getFeatures(request): atMapB = featB.attributes() tmpGeom = featB.geometry() if engine.intersects(tmpGeom.geometry()): int_geom = geom.intersection(tmpGeom) lstIntersectingB.append(tmpGeom) if not int_geom: # There was a problem creating the intersection feedback.pushInfo(self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.')) int_geom = QgsGeometry() else: int_geom = QgsGeometry(int_geom) if int_geom.wkbType() == QgsWkbTypes.Unknown or QgsWkbTypes.flatType(int_geom.geometry().wkbType()) == QgsWkbTypes.GeometryCollection: # Intersection produced different geomety types temp_list = int_geom.asGeometryCollection() for i in temp_list: if i.type() == geom.type(): int_geom = QgsGeometry(i) try: outFeat.setGeometry(int_geom) outFeat.setAttributes(atMapA + atMapB) sink.addFeature(outFeat, QgsFeatureSink.FastInsert) except: feedback.pushInfo(self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.')) else: # Geometry list: prevents writing error # in geometries of different types # produced by the intersection # fix #3549 if int_geom.wkbType() in wkbTypeGroups[wkbTypeGroups[int_geom.wkbType()]]: try: outFeat.setGeometry(int_geom) outFeat.setAttributes(atMapA + atMapB) sink.addFeature(outFeat, QgsFeatureSink.FastInsert) except: feedback.pushInfo(self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.')) # the remaining bit of featA's geometry # if there is nothing left, this will just silently fail and we're good diff_geom = QgsGeometry(geom) if len(lstIntersectingB) != 0: intB = QgsGeometry.unaryUnion(lstIntersectingB) diff_geom = diff_geom.difference(intB) if diff_geom.wkbType() == QgsWkbTypes.Unknown or QgsWkbTypes.flatType(diff_geom.geometry().wkbType()) == QgsWkbTypes.GeometryCollection: temp_list = diff_geom.asGeometryCollection() for i in temp_list: if i.type() == geom.type(): diff_geom = QgsGeometry(i) try: outFeat.setGeometry(diff_geom) outFeat.setAttributes(atMapA) sink.addFeature(outFeat, QgsFeatureSink.FastInsert) except: feedback.pushInfo(self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.')) count += 1 feedback.setProgress(int(count * total)) length = len(sourceA.fields()) atMapA = [None] * length for featA in sourceB.getFeatures(QgsFeatureRequest().setDestinationCrs(sourceA.sourceCrs())): if feedback.isCanceled(): break add = False geom = featA.geometry() diff_geom = QgsGeometry(geom) atMap = [None] * length atMap.extend(featA.attributes()) intersects = indexA.intersects(geom.boundingBox()) if len(intersects) < 1: try: outFeat.setGeometry(geom) outFeat.setAttributes(atMap) sink.addFeature(outFeat, QgsFeatureSink.FastInsert) except: feedback.pushInfo(self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.')) else: request = QgsFeatureRequest().setFilterFids(intersects).setSubsetOfAttributes([]) request.setDestinationCrs(sourceA.sourceCrs()) # use prepared geometries for faster intersection tests engine = QgsGeometry.createGeometryEngine(diff_geom.geometry()) engine.prepareGeometry() for featB in sourceA.getFeatures(request): atMapB = featB.attributes() tmpGeom = featB.geometry() if engine.intersects(tmpGeom.geometry()): add = True diff_geom = QgsGeometry(diff_geom.difference(tmpGeom)) else: try: # Ihis only happens if the bounding box # intersects, but the geometry doesn't outFeat.setGeometry(diff_geom) outFeat.setAttributes(atMap) sink.addFeature(outFeat, QgsFeatureSink.FastInsert) except: feedback.pushInfo(self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.')) if add: try: outFeat.setGeometry(diff_geom) outFeat.setAttributes(atMap) sink.addFeature(outFeat, QgsFeatureSink.FastInsert) except: feedback.pushInfo(self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.')) count += 1 feedback.setProgress(int(count * total)) return {self.OUTPUT: dest_id}
def clip(layer_to_clip, mask_layer, callback=None): """Clip a vector layer with another. Issue https://github.com/inasafe/inasafe/issues/3186 Note : This algorithm is copied from : https://github.com/qgis/QGIS/blob/master/python/plugins/processing/algs/ qgis/Clip.py :param layer_to_clip: The vector layer to clip. :type layer_to_clip: QgsVectorLayer :param mask_layer: The vector layer to use for clipping. :type mask_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: The clip vector layer. :rtype: QgsVectorLayer .. versionadded:: 4.0 """ output_layer_name = clip_steps['output_layer_name'] output_layer_name = output_layer_name % ( layer_to_clip.keywords['layer_purpose']) processing_step = clip_steps['step_name'] writer = create_memory_layer(output_layer_name, layer_to_clip.geometryType(), layer_to_clip.crs(), layer_to_clip.fields()) writer.startEditing() # Begin copy/paste from Processing plugin. # Please follow their code as their code is optimized. # The code below is not following our coding standards because we want to # be able to track any diffs from QGIS easily. # first build up a list of clip geometries clip_geometries = [] request = QgsFeatureRequest().setSubsetOfAttributes([]) for mask_feature in mask_layer.getFeatures(request): clip_geometries.append(QgsGeometry(mask_feature.geometry())) # are we clipping against a single feature? if so, # we can show finer progress reports if len(clip_geometries) > 1: # noinspection PyTypeChecker,PyCallByClass,PyArgumentList combined_clip_geom = QgsGeometry.unaryUnion(clip_geometries) single_clip_feature = False else: combined_clip_geom = clip_geometries[0] single_clip_feature = True # use prepared geometries for faster intersection tests # noinspection PyArgumentList engine = QgsGeometry.createGeometryEngine(combined_clip_geom.geometry()) engine.prepareGeometry() tested_feature_ids = set() for i, clip_geom in enumerate(clip_geometries): request = QgsFeatureRequest().setFilterRect(clip_geom.boundingBox()) input_features = [f for f in layer_to_clip.getFeatures(request)] if not input_features: continue if single_clip_feature: total = 100.0 / len(input_features) else: total = 0 for current, in_feat in enumerate(input_features): if not in_feat.geometry(): continue if in_feat.id() in tested_feature_ids: # don't retest a feature we have already checked continue tested_feature_ids.add(in_feat.id()) if not engine.intersects(in_feat.geometry().geometry()): continue if not engine.contains(in_feat.geometry().geometry()): cur_geom = in_feat.geometry() new_geom = combined_clip_geom.intersection(cur_geom) if new_geom.wkbType() == QgsWKBTypes.Unknown \ or QgsWKBTypes.flatType( new_geom.geometry().wkbType()) == \ QgsWKBTypes.GeometryCollection: int_com = in_feat.geometry().combine(new_geom) int_sym = in_feat.geometry().symDifference(new_geom) if not int_com or not int_sym: # LOGGER.debug( # tr('GEOS geoprocessing error: One or more input ' # 'features have invalid geometry.')) pass else: new_geom = int_com.difference(int_sym) if new_geom.isGeosEmpty()\ or not new_geom.isGeosValid(): # LOGGER.debug( # tr('GEOS geoprocessing error: One or more ' # 'input features have invalid geometry.')) pass else: # clip geometry totally contains feature geometry, # so no need to perform intersection new_geom = in_feat.geometry() try: out_feat = QgsFeature() out_feat.setGeometry(new_geom) out_feat.setAttributes(in_feat.attributes()) if new_geom.type() == layer_to_clip.geometryType(): writer.addFeature(out_feat) except: LOGGER.debug( tr('Feature geometry error: One or more output features ' 'ignored due to invalid geometry.')) continue # TODO implement callback if single_clip_feature: # progress.setPercentage(int(current * total)) pass if not single_clip_feature: # coarse progress report for multiple clip geometries # progress.setPercentage(100.0 * i / len(clip_geoms)) pass # End copy/paste from Processing plugin. writer.commitChanges() writer.keywords = layer_to_clip.keywords.copy() writer.keywords['title'] = output_layer_name check_layer(writer) return writer