def updateGui(self, feature): # The important part: get the feature iterator with an expression request = QgsFeatureRequest().setFilterExpression ( u'"pand_id" = \'' + unicode(feature['gebouwnummer']) + '\'' ) request.setFlags( QgsFeatureRequest.NoGeometry ) features = self.geomLayer.getFeatures( request ) atr = None fields = self.geomLayer.pendingFields() field_names = [field.name() for field in fields] for elem in features: self.editFeature = elem atr = dict(zip(field_names, elem.attributes())) if atr is not None: atr = {i:j for i,j in atr.items() if j } self.newGroupBox.hide() self.editGroupBox.show() self.verwijderWidget.show() self.showValues(feature, atr) self.actionButtonBox.setDisabled(True) else: self.newGroupBox.show() self.editGroupBox.hide() self.verwijderWidget.hide() self.editFeature = None self.statusNieuwLabel.setText(unicode(feature['status'])) self.pandidLabel.setText(unicode(feature['gebouwnummer']))
def _getXYvalues(self, ts_layer, dateField, valueField): # utility function used to get the X and Y values x, y = [], [] # get indexes of date (x) and value (y) fields dateIdx, valueIdx = None, None for idx, fld in enumerate(ts_layer.dataProvider().fields()): if fld.name().lower() == dateField: dateIdx = idx elif fld.name().lower() == valueField: valueIdx = idx if dateIdx is None or valueIdx is None: QgsMessageLog.logMessage("field %s -> index %s, field %s -> index %s. Exiting" % (dateField, dateIdx, valueField, valueIdx), "PSTimeSeriesViewer") return # fetch and loop through all the features request = QgsFeatureRequest() request.setSubsetOfAttributes([dateIdx, valueIdx]) for f in ts_layer.getFeatures( request ): # get x and y values a = f.attributes() x.append( QDate.fromString( a[ dateIdx ], "yyyyMMdd" ).toPyDate() ) y.append( float(a[ valueIdx ]) ) return x, y
def __get_text_flaeche(self, quelle, gstk, layer, fld_name): text = {} #performance! filter by bb of gstk first feat_req = QgsFeatureRequest() feat_req.setFilterRect(gstk.geometry().boundingBox()) for feat in layer.getFeatures(feat_req): if feat.geometry().intersects(gstk.geometry()): #no fld_name defined: means yes/no only if fld_name is None: attr_val = u'Ja' else: attr_val = feat[fld_name] #convert everything to string #JSON only allows for string keys -> settingsfile if isinstance( attr_val, (int, long)): attr_val = unicode(attr_val) elif isinstance(attr_val, float): attr_val = u'{0:.0f}'.format(attr_val) #replace attribute values with mapping text from settings file if not quelle.text is None: if attr_val in quelle.text: attr_val = quelle.text[attr_val] flaeche = feat.geometry().intersection(gstk.geometry()).area() if fld_name in text: text[attr_val] += flaeche else: text[attr_val] = flaeche if len(text) < 1 and fld_name is None: text[u'Nein'] = 0 elif len(text) < 1 and not fld_name is None: text[u'Nein'] = 0 return text
def processAlgorithm(self, progress): network = dataobjects.getObjectFromUri( self.getParameterValue(self.NETWORK_LAYER)) # Ensure that upstream and downstream arc detected idxUpArcId= findField(network, 'UpArcId') if idxUpArcId == -1: raise GeoAlgorithmExecutionException( self.tr('Seems upstream and downstream arcs is not set. ' 'Please run corresponding tool and try again.')) # First add new fields to the network layer networkProvider = network.dataProvider() (idxStrahler, fieldList) = findOrCreateField(network, network.pendingFields(), 'StrahOrder', QVariant.Int, 10, 0) writer = self.getOutputFromName(self.STRAHLER_ORDER).getVectorWriter( fieldList.toList(), networkProvider.geometryType(), networkProvider.crs()) # Generate helper dictionaries myNetwork, arcsPerNodeId = makeHelperDictionaries(network) # Write output file for f in network.getFeatures(): writer.addFeature(f) del writer vl = QgsVectorLayer(self.getOutputValue(self.STRAHLER_ORDER), 'tmp', 'ogr') provider = vl.dataProvider() # calculate Strahler orders # Algorithm at pages 65-66 "Automated AGQ4Vector Watershed.pdf" req = QgsFeatureRequest() progress.setInfo(self.tr('Calculating Strahler orders...')) # Iterate over upsteram node ids starting from the last ones # which represents source arcs for nodeId in sorted(myNetwork.keys(), reverse=True): f = vl.getFeatures(req.setFilterFid(myNetwork[nodeId])).next() fid = f.id() upstreamArcs = f['UpArcId'] if upstreamArcs == NULL: provider.changeAttributeValues({fid:{idxStrahler: 1}}) else: orders = [] for i in upstreamArcs.split(','): f = vl.getFeatures(req.setFilterFid(int(i))).next() if f['StrahOrder']: orders.append(f['StrahOrder']) orders.sort(reverse=True) if len(orders) == 1: order = orders[0] elif len(orders) >= 2: diff = orders[0] - orders[1] if diff == 0: order = orders[0] + 1 else: order = max([orders[0], orders[1]]) provider.changeAttributeValues({fid:{idxStrahler: order}})
def print_attribute_table(layer, limit=-1): """Print the attribute table in the console. :param layer: The layer to print. :type layer: QgsVectorLayer :param limit: The limit in the query. :type limit: integer """ if layer.wkbType() == QGis.WKBNoGeometry: geometry = False else: geometry = True headers = [] if geometry: headers.append('geom') headers.extend( [f.name() + ' : ' + str(f.type()) for f in layer.fields().toList()]) request = QgsFeatureRequest() request.setLimit(limit) data = [] for feature in layer.getFeatures(request): attributes = [] if geometry: attributes.append(feature.geometry().type()) attributes.extend(feature.attributes()) data.append(attributes) print pretty_table(data, headers)
def testRectAndFids(self): """ Test the combination of a filter rect along with filterfids """ # first get feature ids ids = {f['pk']: f.id() for f in self.source.getFeatures()} extent = QgsRectangle(-70, 67, -60, 80) request = QgsFeatureRequest().setFilterFids([ids[3], ids[4]]).setFilterRect(extent) result = set([f['pk'] for f in self.source.getFeatures(request)]) all_valid = (all(f.isValid() for f in self.source.getFeatures(request))) expected = [4] assert set(expected) == result, 'Expected {} and got {} when testing for combination of filterRect and expression'.format(set(expected), result) self.assertTrue(all_valid) # shouldn't matter what order this is done in request = QgsFeatureRequest().setFilterRect(extent).setFilterFids([ids[3], ids[4]]) result = set([f['pk'] for f in self.source.getFeatures(request)]) all_valid = (all(f.isValid() for f in self.source.getFeatures(request))) expected = [4] assert set( expected) == result, 'Expected {} and got {} when testing for combination of filterRect and expression'.format( set(expected), result) self.assertTrue(all_valid) # test that results match QgsFeatureRequest.acceptFeature for f in self.source.getFeatures(): self.assertEqual(request.acceptFeature(f), f['pk'] in expected)
def findFeaturesAt(mapPoint, layerConfig, mapTool): """ To find features from a given position in a given layer :param mapPoint: the map position :param layerConfig: the layer in which we are looking for features :param mapTool: a QgsMapTool instance :return: features found in layer """ if layerConfig is None: return None tolerance = layerConfig.tolerance if layerConfig.unit == QgsTolerance.Pixels: layTolerance = Finder.calcCanvasTolerance(mapTool.toCanvasCoordinates(mapPoint), layerConfig.layer, mapTool, tolerance) elif layerConfig.unit == QgsTolerance.ProjectUnits: layTolerance = Finder.calcMapTolerance(mapPoint, layerConfig.layer, mapTool, tolerance) else: layTolerance = tolerance layPoint = mapTool.toLayerCoordinates(layerConfig.layer, mapPoint) searchRect = QgsRectangle(layPoint.x() - layTolerance, layPoint.y() - layTolerance, layPoint.x() + layTolerance, layPoint.y() + layTolerance) request = QgsFeatureRequest() request.setFilterRect(searchRect) request.setFlags(QgsFeatureRequest.ExactIntersect) features = [] for feature in layerConfig.layer.getFeatures(request): if layerConfig.layer.geometryType() == QGis.Polygon: dist, nearest, vertex = feature.geometry().closestSegmentWithContext(mapPoint) if QgsGeometry.fromPoint(nearest).intersects(searchRect): features.append(QgsFeature(feature)) else: features.append(QgsFeature(feature)) return features
def writePolygonFeature(self, layer, attributes_dict): polygons_svg = [] request = QgsFeatureRequest() request.setFilterRect(self.iface.mapCanvas().extent()) for feature in layer.getFeatures(request): polygons_svg.append(self.writePolygonToSVG(feature, attributes_dict)) return polygons_svg
def clone_layer(layer, keep_selection=True): """Duplicate the layer by taking the same source and copying keywords. :param keep_selection: If we should keep the selection. Default to true. :type keep_selection: bool :param layer: Layer to be duplicated. :type layer: QgsMapLayer :return: The new QgsMapLayer object. :rtype: QgsMapLayer """ if is_vector_layer(layer): new_layer = QgsVectorLayer( layer.source(), layer.name(), layer.providerType()) if keep_selection and layer.selectedFeatureCount() > 0: request = QgsFeatureRequest() request.setFilterFids(layer.selectedFeatureIds()) request.setFlags(QgsFeatureRequest.NoGeometry) iterator = layer.getFeatures(request) new_layer.setSelectedFeatures([k.id() for k in iterator]) else: new_layer = QgsRasterLayer( layer.source(), layer.name(), layer.providerType()) new_layer.keywords = copy_layer_keywords(layer.keywords) return layer
def get_wkt(self, spatial_column, feature_id): """ Gets feature geometry in Well-Known Text format and returns it. :param spatial_column: The spatial column name. :type spatial_column: String :param feature_id: Feature id :type feature_id: Integer :return: Well-Known Text format of a geometry :rtype: WKT """ geom_wkt = None fid = feature_id request = QgsFeatureRequest() request.setFilterFid(fid) features = self.layer.getFeatures(request) geom_col_obj = self.entity.columns[spatial_column] geom_type = geom_col_obj.geometry_type() # get the wkt of the geometry for feature in features: geometry = feature.geometry() if geometry.isGeosValid(): if geom_type in ['MULTIPOLYGON', 'MULTILINESTRING']: geometry.convertToMultiType() geom_wkt = geometry.exportToWkt() return geom_wkt
def _get_tvalue(self, id, epoch, symbol, get_first): req = QgsFeatureRequest() exp = self._time_query_string(epoch, self.timeColumn, symbol) exp += self._id_query_string(id) req.setFilterExpression(exp) warn(exp) s = self.timeLayer.subsetString() self.timeLayer.setSubsetString("") featIt = self.timeLayer.layer.dataProvider().getFeatures(req) self.timeLayer.setSubsetString(s) feats = list(featIt) if get_first: subList = feats[:min(20, len(feats))] else: subList = feats[-min(20, len(feats)):] feats = sorted(subList, key=lambda feat: self.getStartEpochFromFeature(feat, self.timeLayer)) if not feats: return None if get_first: feat = feats[0] else: feat = feats[-1] curr_epoch = self.getStartEpochFromFeature(feat, self.timeLayer) return curr_epoch
def __call__(self): if self.rect: rq = QgsFeatureRequest() rq.setFilterRect(self.rect) features = self.layer.getFeatures(rq) else: features = self.layer.getFeatures() for where in self.wheres: if self.DEBUG: "Has filter" #TODO Index lookup # if self.index: # if self.DEBUG: print "Has index" # min = 6163 # max = 6164 # iters = [iter(self.index[code]) for code in xrange(min, max + 1)] # features = itertools.chain(*iters) features = where(features) # TODO Clean up if self.limit: if self.DEBUG: print "Has Limit" for count in xrange(self.limit): if self.selectstatment: yield self.selectstatment(features.next()) else: yield features.next() else: for f in features: if self.selectstatment: yield self.selectstatment(f) else: yield f
def get_feature_value(self, model=None): self.layer.startEditing() feature = None request = QgsFeatureRequest() if model is None: model = self.host.model() request.setFilterFid(model.id) feature_itr = self.layer.getFeatures(request) for feat in feature_itr: feature = feat break exp = QgsExpression(self.column.expression) if exp.hasParserError(): raise Exception(exp.parserErrorString()) exp.prepare(self.layer.pendingFields()) if feature is not None: value = exp.evaluate(feature) return value else: return None
def writeTmpLayer(layer, restrictToExtent, iface, extent): fields = layer.fields() usedFields = [] for count, field in enumerate(fields): fieldIndex = fields.indexFromName(unicode(field.name())) editorWidget = layer.editorWidgetSetup(fieldIndex).type() addField = False try: if layer.renderer().classAttribute() == field.name(): addField = True except: pass if layer.customProperty("labeling/fieldName") == field.name(): addField = True if (editorWidget != 'Hidden'): addField = True if addField: usedFields.append(count) uri = TYPE_MAP[layer.wkbType()] crs = layer.crs() if crs.isValid(): uri += '?crs=' + crs.authid() for field in usedFields: fieldIndex = layer.fields().indexFromName(unicode( layer.fields().field(field).name())) editorWidget = layer.editorWidgetSetup(fieldIndex).type() fieldType = layer.fields().field(field).type() fieldName = layer.fields().field(field).name() if (editorWidget == 'Hidden'): fieldName = "q2wHide_" + fieldName fieldType = "double" if (fieldType == QVariant.Double or fieldType == QVariant.Int) else ("string") uri += '&field=' + unicode(fieldName) + ":" + fieldType newlayer = QgsVectorLayer(uri, layer.name(), 'memory') writer = newlayer.dataProvider() outFeat = QgsFeature() if restrictToExtent and extent == "Canvas extent": canvas = iface.mapCanvas() extent = canvas.extent() canvasCRS = canvas.mapSettings().destinationCrs() layerCRS = layer.crs() try: transform = QgsCoordinateTransform(canvasCRS, layerCRS, QgsProject.instance()) except: transform = QgsCoordinateTransform(canvasCRS, layerCRS) projectedExtent = transform.transformBoundingBox(extent) request = QgsFeatureRequest(projectedExtent) request.setFlags(QgsFeatureRequest.ExactIntersect) features = layer.getFeatures(request) else: features = layer.getFeatures() for feature in features: if feature.geometry() is not None: outFeat.setGeometry(feature.geometry()) attrs = [feature[f] for f in usedFields] if attrs: outFeat.setAttributes(attrs) writer.addFeatures([outFeat]) return newlayer
def exportImages(layer, field, layerFileName): field_index = layer.fields().indexFromName(field) widget = layer.editorWidgetSetup(field_index).type() if widget != 'Photo': return fr = QgsFeatureRequest() fr.setSubsetOfAttributes([field_index]) for feature in layer.getFeatures(fr): photo_file_name = feature.attribute(field) if type(photo_file_name) is not unicode: continue source_file_name = photo_file_name if not os.path.isabs(source_file_name): prj_fname = QgsProject.instance().fileName() source_file_name = os.path.join(os.path.dirname(prj_fname), source_file_name) photo_file_name = re.sub(r'[\\/:]', '_', photo_file_name).strip() photo_file_name = os.path.join(os.path.dirname(layerFileName), '..', 'images', photo_file_name) try: shutil.copyfile(source_file_name, photo_file_name) except IOError as e: pass
def processAlgorithm(self, parameters, context, feedback): source = self.parameterAsSource(parameters, self.INPUT, context) if source is None: raise QgsProcessingException(self.invalidSourceError(parameters, self.INPUT)) field_names = self.parameterAsFields(parameters, self.FIELDS, context) fields = QgsFields() field_indices = [] for field_name in field_names: field_index = source.fields().lookupField(field_name) if field_index < 0: feedback.reportError(self.tr('Invalid field name {}').format(field_name)) continue field = source.fields()[field_index] fields.append(field) field_indices.append(field_index) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, QgsWkbTypes.NoGeometry, QgsCoordinateReferenceSystem()) results = {} values = set() if len(field_indices) == 1: # one field, can use provider optimised method values = tuple([v] for v in source.uniqueValues(field_indices[0])) else: # have to scan whole table # TODO - add this support to QgsVectorDataProvider so we can run it on # the backend request = QgsFeatureRequest().setFlags(QgsFeatureRequest.NoGeometry) request.setSubsetOfAttributes(field_indices) total = 100.0 / source.featureCount() if source.featureCount() else 0 for current, f in enumerate(source.getFeatures(request, QgsProcessingFeatureSource.FlagSkipGeometryValidityChecks)): if feedback.isCanceled(): break value = tuple(f.attribute(i) for i in field_indices) values.add(value) feedback.setProgress(int(current * total)) if sink: for value in values: if feedback.isCanceled(): break f = QgsFeature() f.setAttributes([attr for attr in value]) sink.addFeature(f, QgsFeatureSink.FastInsert) results[self.OUTPUT] = dest_id output_file = self.parameterAsFileOutput(parameters, self.OUTPUT_HTML_FILE, context) if output_file: self.createHTML(output_file, values) results[self.OUTPUT_HTML_FILE] = output_file results[self.TOTAL_VALUES] = len(values) results[self.UNIQUE_VALUES] = ';'.join([','.join([str(attr) for attr in v]) for v in values]) return results
def processAlgorithm(self, parameters, context, feedback): if parameters[self.SPOKES] == parameters[self.HUBS]: raise QgsProcessingException( self.tr('Same layer given for both hubs and spokes')) hub_source = self.parameterAsSource(parameters, self.HUBS, context) spoke_source = self.parameterAsSource(parameters, self.SPOKES, context) field_hub = self.parameterAsString(parameters, self.HUB_FIELD, context) field_hub_index = hub_source.fields().lookupField(field_hub) field_spoke = self.parameterAsString(parameters, self.SPOKE_FIELD, context) field_spoke_index = hub_source.fields().lookupField(field_spoke) fields = vector.combineFields(hub_source.fields(), spoke_source.fields()) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, QgsWkbTypes.LineString, hub_source.sourceCrs()) hubs = hub_source.getFeatures() total = 100.0 / hub_source.featureCount() if hub_source.featureCount() else 0 matching_field_types = hub_source.fields().at(field_hub_index).type() == spoke_source.fields().at(field_spoke_index).type() for current, hub_point in enumerate(hubs): if feedback.isCanceled(): break if not hub_point.hasGeometry(): continue p = hub_point.geometry().boundingBox().center() hub_x = p.x() hub_y = p.y() hub_id = str(hub_point[field_hub]) hub_attributes = hub_point.attributes() request = QgsFeatureRequest().setDestinationCrs(hub_source.sourceCrs()) if matching_field_types: request.setFilterExpression(QgsExpression.createFieldEqualityExpression(field_spoke, hub_attributes[field_hub_index])) spokes = spoke_source.getFeatures() for spoke_point in spokes: if feedback.isCanceled(): break spoke_id = str(spoke_point[field_spoke]) if hub_id == spoke_id: p = spoke_point.geometry().boundingBox().center() spoke_x = p.x() spoke_y = p.y() f = QgsFeature() f.setAttributes(hub_attributes + spoke_point.attributes()) f.setGeometry(QgsGeometry.fromPolyline( [QgsPointXY(hub_x, hub_y), QgsPointXY(spoke_x, spoke_y)])) sink.addFeature(f, QgsFeatureSink.FastInsert) feedback.setProgress(int(current * total)) return {self.OUTPUT: dest_id}
def featureCount(self): if not self.subsetString(): return len(self._features) else: req = QgsFeatureRequest() req.setFlags(QgsFeatureRequest.NoGeometry) req.setSubsetOfAttributes([]) return len([f for f in self.getFeatures(req)])
def processAlgorithm(self, parameters, context, feedback): source = self.parameterAsSource(parameters, self.INPUT, context) if source is None: raise QgsProcessingException(self.invalidSourceError(parameters, self.INPUT)) radius = self.parameterAsDouble(parameters, self.RADIUS, context) kernel_shape = self.parameterAsEnum(parameters, self.KERNEL, context) pixel_size = self.parameterAsDouble(parameters, self.PIXEL_SIZE, context) decay = self.parameterAsDouble(parameters, self.DECAY, context) output_values = self.parameterAsEnum(parameters, self.OUTPUT_VALUE, context) outputFile = self.parameterAsOutputLayer(parameters, self.OUTPUT, context) output_format = QgsRasterFileWriter.driverForExtension(os.path.splitext(outputFile)[1]) weight_field = self.parameterAsString(parameters, self.WEIGHT_FIELD, context) radius_field = self.parameterAsString(parameters, self.RADIUS_FIELD, context) attrs = [] kde_params = QgsKernelDensityEstimation.Parameters() kde_params.source = source kde_params.radius = radius kde_params.pixelSize = pixel_size # radius field if radius_field: kde_params.radiusField = radius_field attrs.append(source.fields().lookupField(radius_field)) # weight field if weight_field: kde_params.weightField = weight_field attrs.append(source.fields().lookupField(weight_field)) kde_params.shape = kernel_shape kde_params.decayRatio = decay kde_params.outputValues = output_values kde = QgsKernelDensityEstimation(kde_params, outputFile, output_format) if kde.prepare() != QgsKernelDensityEstimation.Success: raise QgsProcessingException( self.tr('Could not create destination layer')) request = QgsFeatureRequest() request.setSubsetOfAttributes(attrs) features = source.getFeatures(request) total = 100.0 / source.featureCount() if source.featureCount() else 0 for current, f in enumerate(features): if feedback.isCanceled(): break if kde.addFeature(f) != QgsKernelDensityEstimation.Success: feedback.reportError(self.tr('Error adding feature with ID {} to heatmap').format(f.id())) feedback.setProgress(int(current * total)) if kde.finalise() != QgsKernelDensityEstimation.Success: raise QgsProcessingException( self.tr('Could not save destination layer')) return {self.OUTPUT: outputFile}
def uniqueValues(self, fieldIndex, limit=1): results = set() if fieldIndex >= 0 and fieldIndex < self.fields().count(): req = QgsFeatureRequest() req.setFlags(QgsFeatureRequest.NoGeometry) req.setSubsetOfAttributes([fieldIndex]) for f in self.getFeatures(req): results.add(f.attributes()[fieldIndex]) return results
def processAlgorithm(self, progress): layer = dataobjects.getObjectFromUri( self.getParameterValue(self.VECTOR)) pointCount = int(self.getParameterValue(self.POINT_NUMBER)) minDistance = float(self.getParameterValue(self.MIN_DISTANCE)) bbox = layer.extent() idxLayer = vector.spatialindex(layer) fields = QgsFields() fields.append(QgsField('id', QVariant.Int, '', 10, 0)) writer = self.getOutputFromName(self.OUTPUT).getVectorWriter( fields, QgsWkbTypes.Point, layer.crs()) nPoints = 0 nIterations = 0 maxIterations = pointCount * 200 total = 100.0 / pointCount index = QgsSpatialIndex() points = dict() request = QgsFeatureRequest() random.seed() while nIterations < maxIterations and nPoints < pointCount: rx = bbox.xMinimum() + bbox.width() * random.random() ry = bbox.yMinimum() + bbox.height() * random.random() pnt = QgsPoint(rx, ry) geom = QgsGeometry.fromPoint(pnt) ids = idxLayer.intersects(geom.buffer(5, 5).boundingBox()) if len(ids) > 0 and \ vector.checkMinDistance(pnt, index, minDistance, points): for i in ids: f = next(layer.getFeatures(request.setFilterFid(i))) tmpGeom = f.geometry() if geom.within(tmpGeom): f = QgsFeature(nPoints) f.initAttributes(1) f.setFields(fields) f.setAttribute('id', nPoints) f.setGeometry(geom) writer.addFeature(f) index.insertFeature(f) points[nPoints] = pnt nPoints += 1 progress.setPercentage(int(nPoints * total)) nIterations += 1 if nPoints < pointCount: ProcessingLog.addToLog(ProcessingLog.LOG_INFO, self.tr('Can not generate requested number of random points. ' 'Maximum number of attempts exceeded.')) del writer
def intersecting_blocks(line, destination, grid): """Function to fetch intersectings polygons from the grid.""" dest_id_field = grid.fieldNameIndex('destination_id') request = QgsFeatureRequest() request.setFilterRect(line.boundingBox()) request.setFilterExpression('"destination_id" is None') for feature in grid.getFeatures(request): if feature.geometry().intersects(line): grid.changeAttributeValue(feature.id(), dest_id_field, destination)
def getLayerFeature(self): layerFeature = QgsFeature() featReq = QgsFeatureRequest().setFilterFid(self.layerFeatureId) if not self.featureLayer.hasGeometryType(): featReq.setFlags(QgsFeatureRequest.NoGeometry) if self.featureLayer.getFeatures(featReq).nextFeature(layerFeature): return layerFeature else: return None
def spatialindex(layer): """Creates a spatial index for the passed vector layer. """ request = QgsFeatureRequest() request.setSubsetOfAttributes([]) if ProcessingConfig.getSetting(ProcessingConfig.USE_SELECTED) and layer.selectedFeatureCount() > 0: idx = QgsSpatialIndex(layer.selectedFeaturesIterator(request)) else: idx = QgsSpatialIndex(layer.getFeatures(request)) return idx
def testCopyMoveFeature(self): """ Test copy and move features""" rqst = QgsFeatureRequest() rqst.setFilterFid(4) self.vl.startEditing() (ok, rqst, msg) = self.vltools.copyMoveFeatures(self.vl, rqst, -0.1, 0.2) self.assertTrue(ok) for f in self.vl.getFeatures(rqst): geom = f.geometry() self.assertAlmostEqual(geom.asPoint().x(), -65.42) self.assertAlmostEqual(geom.asPoint().y(), 78.5)
def selectByAttribute(self, value): layer = getLayerFromId(self.LOCALITIES_LAYER) field_index = self.TAXON_FIELD_INDEX field_name = layer.fields()[int(field_index)].name() selected = [] filter = QgsExpression.createFieldEqualityExpression(field_name, str(value)) request = QgsFeatureRequest().setFilterExpression(filter) request.setSubsetOfAttributes([]) for feature in layer.getFeatures(request): selected.append(feature.id()) layer.selectByIds(selected)
def testTimeout(self): """ Asserts that we will not deadlock if more iterators are opened in parallel than available in the connection pool """ request = QgsFeatureRequest() request.setConnectionTimeout(1) iterators = list() for i in range(100): iterators.append(self.vl.getFeatures(request))
def addHighlight(self, requestOrExpr, lineColor=None, fillColor=None, buff=None, minWidth=None): request = None if isinstance(requestOrExpr, QgsFeatureRequest): request = requestOrExpr self.highlight = request.filterExpression() else: request = QgsFeatureRequest() request.setFilterExpression(requestOrExpr) self.highlight = requestOrExpr for layerKey in self._layers: self._layers[layerKey].addHighlight(request, lineColor, fillColor, buff, minWidth)
def testGetFeaturesFidsTests(self): fids = [f.id() for f in self.source.getFeatures()] self.assertEqual(len(fids), 5) # empty list = no features request = QgsFeatureRequest().setFilterFids([]) result = set([f.id() for f in self.source.getFeatures(request)]) self.assertFalse(result) request = QgsFeatureRequest().setFilterFids([fids[0], fids[2]]) result = set([f.id() for f in self.source.getFeatures(request)]) all_valid = (all(f.isValid() for f in self.source.getFeatures(request))) expected = set([fids[0], fids[2]]) assert result == expected, 'Expected {} and got {} when testing for feature IDs filter'.format(expected, result) self.assertTrue(all_valid) # test that results match QgsFeatureRequest.acceptFeature for f in self.source.getFeatures(): self.assertEqual(request.acceptFeature(f), f.id() in expected) result = set([f.id() for f in self.source.getFeatures(QgsFeatureRequest().setFilterFids([fids[1], fids[3], fids[4]]))]) expected = set([fids[1], fids[3], fids[4]]) assert result == expected, 'Expected {} and got {} when testing for feature IDs filter'.format(expected, result) #sources should ignore non-existent fids result = set([f.id() for f in self.source.getFeatures(QgsFeatureRequest().setFilterFids([-101, fids[1], -102, fids[3], -103, fids[4], -104]))]) expected = set([fids[1], fids[3], fids[4]]) assert result == expected, 'Expected {} and got {} when testing for feature IDs filter'.format(expected, result) result = set([f.id() for f in self.source.getFeatures(QgsFeatureRequest().setFilterFids([]))]) expected = set([]) assert result == expected, 'Expected {} and got {} when testing for feature IDs filter'.format(expected, result) # Rewind mid-way request = QgsFeatureRequest().setFilterFids([fids[1], fids[3], fids[4]]) feature_it = self.source.getFeatures(request) feature = QgsFeature() feature.setValid(True) self.assertTrue(feature_it.nextFeature(feature)) self.assertIn(feature.id(), [fids[1], fids[3], fids[4]]) first_feature = feature self.assertTrue(feature.isValid()) # rewind self.assertTrue(feature_it.rewind()) self.assertTrue(feature_it.nextFeature(feature)) self.assertEqual(feature.id(), first_feature.id()) self.assertTrue(feature.isValid()) # grab all features self.assertTrue(feature_it.nextFeature(feature)) self.assertTrue(feature_it.nextFeature(feature)) # none left self.assertFalse(feature_it.nextFeature(feature)) self.assertFalse(feature.isValid())
def processAlgorithm(self, progress): layer = dataobjects.getObjectFromUri( self.getParameterValue(self.INPUT_LAYER)) radius = self.getParameterValue(self.RADIUS) kernel_shape = self.getParameterValue(self.KERNEL) pixel_size = self.getParameterValue(self.PIXEL_SIZE) decay = self.getParameterValue(self.DECAY) output_values = self.getParameterValue(self.OUTPUT_VALUE) output = self.getOutputValue(self.OUTPUT_LAYER) output_format = raster.formatShortNameFromFileName(output) weight_field = self.getParameterValue(self.WEIGHT_FIELD) radius_field = self.getParameterValue(self.RADIUS_FIELD) attrs = [] kde_params = QgsKernelDensityEstimation.Parameters() kde_params.vectorLayer = layer kde_params.radius = radius kde_params.pixelSize = pixel_size # radius field if radius_field: kde_params.radiusField = radius_field attrs.append(layer.fields().lookupField(radius_field)) # weight field if weight_field: kde_params.weightField = weight_field attrs.append(layer.fields().lookupField(weight_field)) kde_params.shape = kernel_shape kde_params.decayRatio = decay kde_params.outputValues = output_values kde = QgsKernelDensityEstimation(kde_params, output, output_format) if kde.prepare() != QgsKernelDensityEstimation.Success: raise GeoAlgorithmExecutionException( self.tr('Could not create destination layer')) request = QgsFeatureRequest() request.setSubsetOfAttributes(attrs) features = vector.features(layer, request) total = 100.0 / len(features) for current, f in enumerate(features): if kde.addFeature(f) != QgsKernelDensityEstimation.Success: raise GeoAlgorithmExecutionException( self.tr('Error adding feature to heatmap')) progress.setPercentage(int(current * total)) if kde.finalise() != QgsKernelDensityEstimation.Success: raise GeoAlgorithmExecutionException( self.tr('Could not save destination layer'))
def processAlgorithm(self, parameters, context, feedback): source = self.parameterAsSource(parameters, self.INPUT, context) if source is None: raise QgsProcessingException( self.invalidSourceError(parameters, self.INPUT)) join_source = self.parameterAsSource(parameters, self.JOIN, context) if join_source is None: raise QgsProcessingException( self.invalidSourceError(parameters, self.JOIN)) join_fields = self.parameterAsFields(parameters, self.JOIN_FIELDS, context) method = self.parameterAsEnum(parameters, self.METHOD, context) discard_nomatch = self.parameterAsBoolean(parameters, self.DISCARD_NONMATCHING, context) prefix = self.parameterAsString(parameters, self.PREFIX, context) source_fields = source.fields() fields_to_join = QgsFields() join_field_indexes = [] if not join_fields: fields_to_join = join_source.fields() join_field_indexes = [i for i in range(len(fields_to_join))] else: for f in join_fields: idx = join_source.fields().lookupField(f) join_field_indexes.append(idx) if idx >= 0: fields_to_join.append(join_source.fields().at(idx)) if prefix: prefixed_fields = QgsFields() for i in range(len(fields_to_join)): field = fields_to_join[i] field.setName(prefix + field.name()) prefixed_fields.append(field) fields_to_join = prefixed_fields out_fields = QgsProcessingUtils.combineFields(source_fields, fields_to_join) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, out_fields, source.wkbType(), source.sourceCrs(), QgsFeatureSink.RegeneratePrimaryKey) if self.OUTPUT in parameters and parameters[ self.OUTPUT] is not None and sink is None: raise QgsProcessingException( self.invalidSinkError(parameters, self.OUTPUT)) (non_matching_sink, non_matching_dest_id) = self.parameterAsSink( parameters, self.NON_MATCHING, context, source.fields(), source.wkbType(), source.sourceCrs(), QgsFeatureSink.RegeneratePrimaryKey) if self.NON_MATCHING in parameters and parameters[ self.NON_MATCHING] is not None and non_matching_sink is None: raise QgsProcessingException( self.invalidSinkError(parameters, self.NON_MATCHING)) # do the join # build a list of 'reversed' predicates, because in this function # we actually test the reverse of what the user wants (allowing us # to prepare geometries and optimise the algorithm) predicates = [ self.reversed_predicates[self.predicates[i][0]] for i in self.parameterAsEnums(parameters, self.PREDICATE, context) ] remaining = set() if not discard_nomatch or non_matching_sink is not None: remaining = set(source.allFeatureIds()) added_set = set() request = QgsFeatureRequest().setSubsetOfAttributes( join_field_indexes).setDestinationCrs(source.sourceCrs(), context.transformContext()) features = join_source.getFeatures(request) total = 100.0 / join_source.featureCount() if join_source.featureCount( ) else 0 joined_count = 0 unjoined_count = 0 for current, f in enumerate(features): if feedback.isCanceled(): break if not f.hasGeometry(): continue bbox = f.geometry().boundingBox() engine = None request = QgsFeatureRequest().setFilterRect(bbox) for test_feat in source.getFeatures(request): if feedback.isCanceled(): break if method == 1 and test_feat.id() in added_set: # already added this feature, and user has opted to only output first match continue join_attributes = [] for a in join_field_indexes: join_attributes.append(f[a]) if engine is None: engine = QgsGeometry.createGeometryEngine( f.geometry().constGet()) engine.prepareGeometry() for predicate in predicates: if getattr(engine, predicate)(test_feat.geometry().constGet()): added_set.add(test_feat.id()) if sink is not None: # join attributes and add attributes = test_feat.attributes() attributes.extend(join_attributes) output_feature = test_feat output_feature.setAttributes(attributes) sink.addFeature(output_feature, QgsFeatureSink.FastInsert) break feedback.setProgress(int(current * total)) if not discard_nomatch or non_matching_sink is not None: remaining = remaining.difference(added_set) for f in source.getFeatures(QgsFeatureRequest().setFilterFids( list(remaining))): if feedback.isCanceled(): break if sink is not None: sink.addFeature(f, QgsFeatureSink.FastInsert) if non_matching_sink is not None: non_matching_sink.addFeature(f, QgsFeatureSink.FastInsert) result = {} if sink is not None: result[self.OUTPUT] = dest_id if non_matching_sink is not None: result[self.NON_MATCHING] = non_matching_dest_id result[self.JOINED_COUNT] = len(added_set) return result
def processAlgorithm(self, parameters, context, feedback): poly_source = self.parameterAsSource(parameters, self.POLYGONS, context) if poly_source is None: raise QgsProcessingException( self.invalidSourceError(parameters, self.POLYGONS)) point_source = self.parameterAsSource(parameters, self.POINTS, context) if point_source is None: raise QgsProcessingException( self.invalidSourceError(parameters, self.POINTS)) weight_field = self.parameterAsString(parameters, self.WEIGHT, context) weight_field_index = -1 if weight_field: weight_field_index = point_source.fields().lookupField( weight_field) class_field = self.parameterAsString(parameters, self.CLASSFIELD, context) class_field_index = -1 if class_field: class_field_index = point_source.fields().lookupField(class_field) field_name = self.parameterAsString(parameters, self.FIELD, context) fields = poly_source.fields() if fields.lookupField(field_name) < 0: fields.append(QgsField(field_name, QVariant.Int)) field_index = fields.lookupField(field_name) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, poly_source.wkbType(), poly_source.sourceCrs()) if sink is None: raise QgsProcessingException( self.invalidSinkError(parameters, self.OUTPUT)) spatialIndex = QgsSpatialIndex( point_source.getFeatures(QgsFeatureRequest().setSubsetOfAttributes( []).setDestinationCrs(poly_source.sourceCrs(), context.transformContext())), feedback) point_attribute_indices = [] if weight_field_index >= 0: point_attribute_indices.append(weight_field_index) if class_field_index >= 0: point_attribute_indices.append(class_field_index) features = poly_source.getFeatures() total = 100.0 / poly_source.featureCount() if poly_source.featureCount( ) else 0 for current, polygon_feature in enumerate(features): if feedback.isCanceled(): break count = 0 output_feature = QgsFeature() if polygon_feature.hasGeometry(): geom = polygon_feature.geometry() engine = QgsGeometry.createGeometryEngine(geom.constGet()) engine.prepareGeometry() count = 0 classes = set() points = spatialIndex.intersects(geom.boundingBox()) if len(points) > 0: request = QgsFeatureRequest().setFilterFids( points).setDestinationCrs(poly_source.sourceCrs(), context.transformContext()) request.setSubsetOfAttributes(point_attribute_indices) for point_feature in point_source.getFeatures(request): if feedback.isCanceled(): break if engine.contains( point_feature.geometry().constGet()): if weight_field_index >= 0: weight = point_feature.attributes( )[weight_field_index] try: count += float(weight) except: # Ignore fields with non-numeric values pass elif class_field_index >= 0: point_class = point_feature.attributes( )[class_field_index] if point_class not in classes: classes.add(point_class) else: count += 1 output_feature.setGeometry(geom) attrs = polygon_feature.attributes() if class_field_index >= 0: score = len(classes) else: score = count if field_index == len(attrs): attrs.append(score) else: attrs[field_index] = score output_feature.setAttributes(attrs) sink.addFeature(output_feature, QgsFeatureSink.FastInsert) feedback.setProgress(int(current * total)) return {self.OUTPUT: dest_id}
def processAlgorithm(self, feedback): vlayerA = dataobjects.getLayerFromString( self.getParameterValue(self.INPUT)) vlayerB = dataobjects.getLayerFromString( self.getParameterValue(self.INPUT2)) geomType = QgsWkbTypes.multiType(vlayerA.wkbType()) fields = vector.combineVectorFields(vlayerA, vlayerB) writer = self.getOutputFromName(self.OUTPUT).getVectorWriter( fields, geomType, vlayerA.crs()) outFeat = QgsFeature() index = vector.spatialindex(vlayerB) selectionA = vector.features(vlayerA) total = 100.0 / len(selectionA) for current, inFeatA in enumerate(selectionA): feedback.setProgress(int(current * total)) geom = inFeatA.geometry() atMapA = inFeatA.attributes() intersects = index.intersects(geom.boundingBox()) request = QgsFeatureRequest().setFilterFids(intersects) engine = None if len(intersects) > 0: # use prepared geometries for faster intersection tests engine = QgsGeometry.createGeometryEngine(geom.geometry()) engine.prepareGeometry() for inFeatB in vlayerB.getFeatures(request): tmpGeom = inFeatB.geometry() if engine.intersects(tmpGeom.geometry()): atMapB = inFeatB.attributes() int_geom = QgsGeometry(geom.intersection(tmpGeom)) if int_geom.wkbType( ) == QgsWkbTypes.Unknown or QgsWkbTypes.flatType( int_geom.geometry().wkbType( )) == QgsWkbTypes.GeometryCollection: int_com = geom.combine(tmpGeom) int_geom = QgsGeometry() if int_com: int_sym = geom.symDifference(tmpGeom) int_geom = QgsGeometry(int_com.difference(int_sym)) if int_geom.isEmpty() or not int_geom.isGeosValid(): ProcessingLog.addToLog( ProcessingLog.LOG_ERROR, self.tr('GEOS geoprocessing error: One or ' 'more input features have invalid ' 'geometry.')) try: if int_geom.wkbType() in wkbTypeGroups[wkbTypeGroups[ int_geom.wkbType()]]: outFeat.setGeometry(int_geom) attrs = [] attrs.extend(atMapA) attrs.extend(atMapB) outFeat.setAttributes(attrs) writer.addFeature(outFeat) except: ProcessingLog.addToLog( ProcessingLog.LOG_INFO, self. tr('Feature geometry error: One or more output features ignored due to invalid geometry.' )) continue del writer
def testFieldsWithSpecialCharacters(self): ml = QgsVectorLayer("Point?srid=EPSG:4326&field=123:int", "mem_with_nontext_fieldnames", "memory") self.assertEqual(ml.isValid(), True) QgsProject.instance().addMapLayer(ml) ml.startEditing() self.assertTrue(ml.addAttribute(QgsField('abc:123', QVariant.String))) self.assertTrue(ml.addAttribute(QgsField( 'map', QVariant.String))) # matches QGIS expression function name f1 = QgsFeature(ml.fields()) f1.setGeometry(QgsGeometry.fromWkt('POINT(0 0)')) f1.setAttributes([1, 'a', 'b']) f2 = QgsFeature(ml.fields()) f2.setGeometry(QgsGeometry.fromWkt('POINT(1 1)')) f2.setAttributes([2, 'c', 'd']) ml.addFeatures([f1, f2]) ml.commitChanges() vl = QgsVectorLayer("?query=select * from mem_with_nontext_fieldnames", "vl", "virtual") self.assertEqual(vl.isValid(), True) self.assertEqual(vl.fields().at(0).name(), '123') self.assertEqual(vl.fields().at(1).name(), 'abc:123') self.assertEqual(vl.featureCount(), 2) features = [ f for f in vl.getFeatures(QgsFeatureRequest().setFilterExpression( '"abc:123"=\'c\'')) ] self.assertEqual(len(features), 1) self.assertEqual(features[0].attributes(), [2, 'c', 'd']) features = [ f for f in vl.getFeatures(QgsFeatureRequest().setFilterExpression( '"map"=\'b\'')) ] self.assertEqual(len(features), 1) self.assertEqual(features[0].attributes(), [1, 'a', 'b']) vl2 = QgsVectorLayer( "?query=select * from mem_with_nontext_fieldnames where \"abc:123\"='c'", "vl", "virtual") self.assertEqual(vl2.isValid(), True) self.assertEqual(vl2.fields().at(0).name(), '123') self.assertEqual(vl2.fields().at(1).name(), 'abc:123') self.assertEqual(vl2.featureCount(), 1) features = [f for f in vl2.getFeatures()] self.assertEqual(len(features), 1) self.assertEqual(features[0].attributes(), [2, 'c', 'd']) vl3 = QgsVectorLayer( "?query=select * from mem_with_nontext_fieldnames where \"map\"='b'", "vl", "virtual") self.assertEqual(vl3.isValid(), True) self.assertEqual(vl3.fields().at(0).name(), '123') self.assertEqual(vl3.fields().at(1).name(), 'abc:123') self.assertEqual(vl3.featureCount(), 1) features = [f for f in vl3.getFeatures()] self.assertEqual(len(features), 1) self.assertEqual(features[0].attributes(), [1, 'a', 'b']) QgsProject.instance().removeMapLayer(ml)
def doCheck(self, method, parameters, context, feedback): source = self.parameterAsSource(parameters, self.INPUT_LAYER, context) (valid_output_sink, valid_output_dest_id) = self.parameterAsSink(parameters, self.VALID_OUTPUT, context, source.fields(), source.wkbType(), source.sourceCrs()) valid_count = 0 invalid_fields = source.fields() invalid_fields.append( QgsField('_errors', QVariant.String, 'string', 255)) (invalid_output_sink, invalid_output_dest_id) = self.parameterAsSink( parameters, self.INVALID_OUTPUT, context, invalid_fields, source.wkbType(), source.sourceCrs()) invalid_count = 0 error_fields = QgsFields() error_fields.append(QgsField('message', QVariant.String, 'string', 255)) (error_output_sink, error_output_dest_id) = self.parameterAsSink( parameters, self.ERROR_OUTPUT, context, error_fields, QgsWkbTypes.Point, source.sourceCrs()) error_count = 0 features = source.getFeatures( QgsFeatureRequest(), QgsProcessingFeatureSource.FlagSkipGeometryValidityChecks) total = 100.0 / source.featureCount() if source.featureCount() else 0 for current, inFeat in enumerate(features): if feedback.isCanceled(): break geom = inFeat.geometry() attrs = inFeat.attributes() valid = True if not geom.isNull() and not geom.isEmpty(): errors = list(geom.validateGeometry(method)) if errors: # QGIS method return a summary at the end if method == 1: errors.pop() valid = False reasons = [] for error in errors: errFeat = QgsFeature() error_geom = QgsGeometry.fromPointXY(error.where()) errFeat.setGeometry(error_geom) errFeat.setAttributes([error.what()]) if error_output_sink: error_output_sink.addFeature( errFeat, QgsFeatureSink.FastInsert) error_count += 1 reasons.append(error.what()) reason = "\n".join(reasons) if len(reason) > 255: reason = reason[:252] + '…' attrs.append(reason) outFeat = QgsFeature() outFeat.setGeometry(geom) outFeat.setAttributes(attrs) if valid: if valid_output_sink: valid_output_sink.addFeature(outFeat, QgsFeatureSink.FastInsert) valid_count += 1 else: if invalid_output_sink: invalid_output_sink.addFeature(outFeat, QgsFeatureSink.FastInsert) invalid_count += 1 feedback.setProgress(int(current * total)) results = { self.VALID_COUNT: valid_count, self.INVALID_COUNT: invalid_count, self.ERROR_COUNT: error_count } if valid_output_sink: results[self.VALID_OUTPUT] = valid_output_dest_id if invalid_output_sink: results[self.INVALID_OUTPUT] = invalid_output_dest_id if error_output_sink: results[self.ERROR_OUTPUT] = error_output_dest_id return results
def processAlgorithm(self, parameters, context, feedback): source = self.parameterAsSource(parameters, self.INPUT, context) if source is None: raise QgsProcessingException( self.invalidSourceError(parameters, self.INPUT)) method = self.parameterAsEnum(parameters, self.METHOD, context) field = self.parameterAsString(parameters, self.FIELD, context) index = source.fields().lookupField(field) features = source.getFeatures( QgsFeatureRequest(), QgsProcessingFeatureSource.FlagSkipGeometryValidityChecks) featureCount = source.featureCount() unique = source.uniqueValues(index) value = self.parameterAsInt(parameters, self.NUMBER, context) if method == 0: if value > featureCount: raise QgsProcessingException( self.tr('Selected number is greater that feature count. ' 'Choose lesser value and try again.')) else: if value > 100: raise QgsProcessingException( self.tr("Percentage can't be greater than 100. Set " "correct value and try again.")) value = value / 100.0 (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, source.fields(), source.wkbType(), source.sourceCrs()) if sink is None: raise QgsProcessingException( self.invalidSinkError(parameters, self.OUTPUT)) selran = [] total = 100.0 / (featureCount * len(unique)) if featureCount else 1 classes = defaultdict(list) for i, feature in enumerate(features): if feedback.isCanceled(): break attrs = feature.attributes() classes[attrs[index]].append(feature) feedback.setProgress(int(i * total)) for k, subset in classes.items(): selValue = value if method != 1 else int( round(value * len(subset), 0)) if selValue > len(subset): selValue = len(subset) feedback.reportError( self. tr('Subset "{}" is smaller than requested number of features.' .format(k))) selran.extend(random.sample(subset, selValue)) total = 100.0 / featureCount if featureCount else 1 for (i, feat) in enumerate(selran): if feedback.isCanceled(): break sink.addFeature(feat, QgsFeatureSink.FastInsert) feedback.setProgress(int(i * total)) 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 processAlgorithm(self, parameters, context, feedback): line_source = self.parameterAsSource(parameters, self.LINES, context) if line_source is None: raise QgsProcessingException( self.invalidSourceError(parameters, self.LINES)) poly_source = self.parameterAsSource(parameters, self.POLYGONS, context) if poly_source is None: raise QgsProcessingException( self.invalidSourceError(parameters, self.POLYGONS)) length_field_name = self.parameterAsString(parameters, self.LEN_FIELD, context) count_field_name = self.parameterAsString(parameters, self.COUNT_FIELD, context) fields = poly_source.fields() if fields.lookupField(length_field_name) < 0: fields.append(QgsField(length_field_name, QVariant.Double)) length_field_index = fields.lookupField(length_field_name) if fields.lookupField(count_field_name) < 0: fields.append(QgsField(count_field_name, QVariant.Int)) count_field_index = fields.lookupField(count_field_name) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, poly_source.wkbType(), poly_source.sourceCrs()) if sink is None: raise QgsProcessingException( self.invalidSinkError(parameters, self.OUTPUT)) spatialIndex = QgsSpatialIndex( line_source.getFeatures(QgsFeatureRequest().setSubsetOfAttributes( []).setDestinationCrs(poly_source.sourceCrs(), context.transformContext())), feedback) distArea = QgsDistanceArea() distArea.setSourceCrs(poly_source.sourceCrs(), context.transformContext()) distArea.setEllipsoid(context.project().ellipsoid()) features = poly_source.getFeatures() total = 100.0 / poly_source.featureCount() if poly_source.featureCount( ) else 0 for current, poly_feature in enumerate(features): if feedback.isCanceled(): break output_feature = QgsFeature() count = 0 length = 0 if poly_feature.hasGeometry(): poly_geom = poly_feature.geometry() has_intersections = False lines = spatialIndex.intersects(poly_geom.boundingBox()) engine = None if len(lines) > 0: has_intersections = True # use prepared geometries for faster intersection tests engine = QgsGeometry.createGeometryEngine( poly_geom.constGet()) engine.prepareGeometry() if has_intersections: request = QgsFeatureRequest().setFilterFids( lines).setSubsetOfAttributes([]).setDestinationCrs( poly_source.sourceCrs(), context.transformContext()) for line_feature in line_source.getFeatures(request): if feedback.isCanceled(): break if engine.intersects( line_feature.geometry().constGet()): outGeom = poly_geom.intersection( line_feature.geometry()) length += distArea.measureLength(outGeom) count += 1 output_feature.setGeometry(poly_geom) attrs = poly_feature.attributes() if length_field_index == len(attrs): attrs.append(length) else: attrs[length_field_index] = length if count_field_index == len(attrs): attrs.append(count) else: attrs[count_field_index] = count output_feature.setAttributes(attrs) sink.addFeature(output_feature, QgsFeatureSink.FastInsert) feedback.setProgress(int(current * total)) return {self.OUTPUT: dest_id}
def calculate_zonal_stats(raster_layer, polygon_layer): """Calculate zonal statics given two layers. :param raster_layer: A QGIS raster layer. :type raster_layer: QgsRasterLayer, QgsMapLayer :param polygon_layer: A QGIS vector layer containing polygons. :type polygon_layer: QgsVectorLayer, QgsMapLayer :returns: A data structure containing sum, mean, min, max, count of raster values for each polygonal area. :rtype: dict :raises: InvalidParameterError, InvalidGeometryError Note: * InvalidParameterError if incorrect inputs are received. * InvalidGeometryError if none geometry is found during calculations. * Any other exceptions are propagated. Example of output data structure: { 1: {'sum': 10, 'count': 20, 'min': 1, 'max': 4, 'mean': 2}, 2: {'sum': 10, 'count': 20, 'min': 1, 'max': 4, 'mean': 2}, 3 {'sum': 10, 'count': 20, 'min': 1, 'max': 4, 'mean': 2}} The key in the outer dict is the feature id .. note:: This is a python port of the zonal stats implementation in QGIS . See https://github.com/qgis/Quantum-GIS/blob/master/src/analysis/ vector/qgszonalstatistics.cpp .. note:: Currently not projection checks are made to ensure that both layers are in the same CRS - we assume they are. """ if not is_polygon_layer(polygon_layer): raise InvalidParameterError(tr( 'Zonal stats needs a polygon layer in order to compute ' 'statistics.')) if not is_raster_layer(raster_layer): raise InvalidParameterError(tr( 'Zonal stats needs a raster layer in order to compute statistics.' )) LOGGER.debug('Calculating zonal stats for:') LOGGER.debug('Raster: %s' % raster_layer.source()) LOGGER.debug('Vector: %s' % polygon_layer.source()) results = {} raster_source = raster_layer.source() feature_id = gdal.Open(raster_source, gdal.GA_ReadOnly) geo_transform = feature_id.GetGeoTransform() columns = feature_id.RasterXSize rows = feature_id.RasterYSize # Get first band. band = feature_id.GetRasterBand(1) no_data = band.GetNoDataValue() # print 'No data %s' % no_data cell_size_x = geo_transform[1] if cell_size_x < 0: cell_size_x = -cell_size_x cell_size_y = geo_transform[5] if cell_size_y < 0: cell_size_y = -cell_size_y raster_box = QgsRectangle( geo_transform[0], geo_transform[3] - (cell_size_y * rows), geo_transform[0] + (cell_size_x * columns), geo_transform[3]) # noinspection PyCallByClass,PyTypeChecker,PyArgumentList raster_geometry = QgsGeometry.fromRect(raster_box) # Get vector layer provider = polygon_layer.dataProvider() if provider is None: message = tr( 'Could not obtain data provider from layer "%s"') % ( polygon_layer.source()) raise Exception(message) request = QgsFeatureRequest() crs = osr.SpatialReference() crs.ImportFromProj4(str(polygon_layer.crs().toProj4())) count = 0 for myFeature in provider.getFeatures(request): geometry = myFeature.geometry() if geometry is None: message = tr( 'Feature %d has no geometry or geometry is invalid') % ( myFeature.id()) raise InvalidGeometryError(message) count += 1 feature_box = geometry.boundingBox().intersect(raster_box) print 'NEW AGGR: %s' % myFeature.id() # print 'Raster Box: %s' % raster_box.asWktCoordinates() # print 'Feature Box: %s' % feature_box.asWktCoordinates() offset_x, offset_y, cells_x, cells_y = intersection_box( raster_box, feature_box, cell_size_x, cell_size_y) # If the poly does not intersect the raster just continue if None in [offset_x, offset_y, cells_x, cells_y]: continue # avoid access to cells outside of the raster (may occur because of # rounding) if (offset_x + cells_x) > columns: offset_x = columns - offset_x if (offset_y + cells_y) > rows: cells_y = rows - offset_y intersected_geometry = raster_geometry.intersection(geometry) geometry_sum, count = numpy_stats( band, intersected_geometry, geo_transform, no_data, crs) if count <= 1: # The cell resolution is probably larger than the polygon area. # We switch to precise pixel - polygon intersection in this case geometry_sum, count = precise_stats( band, geometry, offset_x, offset_y, cells_x, cells_y, cell_size_x, cell_size_y, raster_box, no_data) # print geometry_sum, count if count == 0: mean = 0 else: mean = geometry_sum / count results[myFeature.id()] = { 'sum': geometry_sum, 'count': count, 'mean': mean} # noinspection PyUnusedLocal feature_id = None # Close return results
def processAlgorithm(self, parameters, context, feedback): network = self.parameterAsSource(parameters, self.INPUT, context) if network is None: raise QgsProcessingException( self.invalidSourceError(parameters, self.INPUT)) startPoints = self.parameterAsSource(parameters, self.START_POINTS, context) if startPoints is None: raise QgsProcessingException( self.invalidSourceError(parameters, self.START_POINTS)) endPoint = self.parameterAsPoint(parameters, self.END_POINT, context, network.sourceCrs()) strategy = self.parameterAsEnum(parameters, self.STRATEGY, context) directionFieldName = self.parameterAsString(parameters, self.DIRECTION_FIELD, context) forwardValue = self.parameterAsString(parameters, self.VALUE_FORWARD, context) backwardValue = self.parameterAsString(parameters, self.VALUE_BACKWARD, context) bothValue = self.parameterAsString(parameters, self.VALUE_BOTH, context) defaultDirection = self.parameterAsEnum(parameters, self.DEFAULT_DIRECTION, context) speedFieldName = self.parameterAsString(parameters, self.SPEED_FIELD, context) defaultSpeed = self.parameterAsDouble(parameters, self.DEFAULT_SPEED, context) tolerance = self.parameterAsDouble(parameters, self.TOLERANCE, context) fields = startPoints.fields() fields.append(QgsField('start', QVariant.String, '', 254, 0)) fields.append(QgsField('end', QVariant.String, '', 254, 0)) fields.append(QgsField('cost', QVariant.Double, '', 20, 7)) feat = QgsFeature() feat.setFields(fields) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, QgsWkbTypes.LineString, network.sourceCrs()) if sink is None: raise QgsProcessingException( self.invalidSinkError(parameters, self.OUTPUT)) directionField = -1 if directionFieldName: directionField = network.fields().lookupField(directionFieldName) speedField = -1 if speedFieldName: speedField = network.fields().lookupField(speedFieldName) director = QgsVectorLayerDirector(network, directionField, forwardValue, backwardValue, bothValue, defaultDirection) distUnit = context.project().crs().mapUnits() multiplier = QgsUnitTypes.fromUnitToUnitFactor( distUnit, QgsUnitTypes.DistanceMeters) if strategy == 0: strategy = QgsNetworkDistanceStrategy() else: strategy = QgsNetworkSpeedStrategy(speedField, defaultSpeed, multiplier * 1000.0 / 3600.0) multiplier = 3600 director.addStrategy(strategy) builder = QgsGraphBuilder(network.sourceCrs(), True, tolerance) feedback.pushInfo( QCoreApplication.translate('ShortestPathLayerToPoint', 'Loading start points…')) request = QgsFeatureRequest() request.setDestinationCrs(network.sourceCrs(), context.transformContext()) features = startPoints.getFeatures(request) total = 100.0 / startPoints.featureCount() if startPoints.featureCount( ) else 0 points = [endPoint] source_attributes = {} i = 1 for current, f in enumerate(features): if feedback.isCanceled(): break if not f.hasGeometry(): continue for p in f.geometry().vertices(): points.append(QgsPointXY(p)) source_attributes[i] = f.attributes() i += 1 feedback.setProgress(int(current * total)) feedback.pushInfo( QCoreApplication.translate('ShortestPathLayerToPoint', 'Building graph…')) snappedPoints = director.makeGraph(builder, points, feedback) feedback.pushInfo( QCoreApplication.translate('ShortestPathLayerToPoint', 'Calculating shortest paths…')) graph = builder.graph() idxEnd = graph.findVertex(snappedPoints[0]) nPoints = len(snappedPoints) total = 100.0 / nPoints if nPoints else 1 for i in range(1, nPoints): if feedback.isCanceled(): break idxStart = graph.findVertex(snappedPoints[i]) tree, costs = QgsGraphAnalyzer.dijkstra(graph, idxStart, 0) if tree[idxEnd] == -1: msg = self.tr( 'There is no route from start point ({}) to end point ({}).' .format(points[i].toString(), endPoint.toString())) feedback.reportError(msg) # add feature with no geometry feat.clearGeometry() attrs = source_attributes[i] attrs.append(points[i].toString()) feat.setAttributes(attrs) sink.addFeature(feat, QgsFeatureSink.FastInsert) continue route = [graph.vertex(idxEnd).point()] cost = costs[idxEnd] current = idxEnd while current != idxStart: current = graph.edge(tree[current]).fromVertex() route.append(graph.vertex(current).point()) route.reverse() geom = QgsGeometry.fromPolylineXY(route) feat.setGeometry(geom) attrs = source_attributes[i] attrs.extend( [points[i].toString(), endPoint.toString(), cost / multiplier]) feat.setAttributes(attrs) sink.addFeature(feat, QgsFeatureSink.FastInsert) feedback.setProgress(int(i * total)) return {self.OUTPUT: dest_id}
def processAlgorithm(self, parameters, context, feedback): source = self.parameterAsSource(parameters, self.INPUT, context) field_names = self.parameterAsFields(parameters, self.FIELDS, context) fields = QgsFields() field_indices = [] for field_name in field_names: field_index = source.fields().lookupField(field_name) if field_index < 0: feedback.reportError( self.tr('Invalid field name {}').format(field_name)) continue field = source.fields()[field_index] fields.append(field) field_indices.append(field_index) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, QgsWkbTypes.NoGeometry, QgsCoordinateReferenceSystem()) results = {} values = set() if len(field_indices) == 1: # one field, can use provider optimised method values = tuple([v] for v in source.uniqueValues(field_indices[0])) else: # have to scan whole table # TODO - add this support to QgsVectorDataProvider so we can run it on # the backend request = QgsFeatureRequest().setFlags( QgsFeatureRequest.NoGeometry) request.setSubsetOfAttributes(field_indices) total = 100.0 / source.featureCount() if source.featureCount( ) else 0 for current, f in enumerate(source.getFeatures(request)): if feedback.isCanceled(): break value = tuple(f.attribute(i) for i in field_indices) values.add(value) feedback.setProgress(int(current * total)) if sink: for value in values: if feedback.isCanceled(): break f = QgsFeature() f.setAttributes([attr for attr in value]) sink.addFeature(f, QgsFeatureSink.FastInsert) results[self.OUTPUT] = dest_id output_file = self.parameterAsFileOutput(parameters, self.OUTPUT_HTML_FILE, context) if output_file: self.createHTML(output_file, values) results[self.OUTPUT_HTML_FILE] = output_file results[self.TOTAL_VALUES] = len(values) results[self.UNIQUE_VALUES] = ';'.join( [','.join([str(attr) for attr in v]) for v in values]) return results
def processAlgorithm(self, parameters, context, feedback): source = self.parameterAsSource(parameters, self.INPUT_VECTOR, context) raster_layer = self.parameterAsRasterLayer(parameters, self.INPUT_RASTER, context) rasterPath = exportRasterLayer(raster_layer) rasterDS = gdal.Open(rasterPath, gdal.GA_ReadOnly) geoTransform = rasterDS.GetGeoTransform() fields = QgsFields() fields.append(QgsField('id', QVariant.Int, '', 10, 0)) fields.append(QgsField('poly_id', QVariant.Int, '', 10, 0)) fields.append(QgsField('point_id', QVariant.Int, '', 10, 0)) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, QgsWkbTypes.Point, raster_layer.crs()) outFeature = QgsFeature() outFeature.setFields(fields) fid = 0 polyId = 0 pointId = 0 features = source.getFeatures(QgsFeatureRequest().setDestinationCrs(raster_layer.crs())) total = 100.0 / source.featureCount() if source.featureCount() else 0 for current, f in enumerate(features): if feedback.isCanceled(): break if not f.hasGeometry(): continue geom = f.geometry() bbox = geom.boundingBox() xMin = bbox.xMinimum() xMax = bbox.xMaximum() yMin = bbox.yMinimum() yMax = bbox.yMaximum() (startRow, startColumn) = raster.mapToPixel(xMin, yMax, geoTransform) (endRow, endColumn) = raster.mapToPixel(xMax, yMin, geoTransform) # use prepared geometries for faster intersection tests engine = QgsGeometry.createGeometryEngine(geom.constGet()) engine.prepareGeometry() for row in range(startRow, endRow + 1): for col in range(startColumn, endColumn + 1): if feedback.isCanceled(): break (x, y) = raster.pixelToMap(row, col, geoTransform) point = QgsPoint(x, y) if engine.contains(point): outFeature.setGeometry(QgsGeometry(point)) outFeature['id'] = fid outFeature['poly_id'] = polyId outFeature['point_id'] = pointId fid += 1 pointId += 1 sink.addFeature(outFeature, QgsFeatureSink.FastInsert) pointId = 0 polyId += 1 feedback.setProgress(int(current * total)) return {self.OUTPUT: dest_id}
def processAlgorithm(self, parameters, context, feedback): sourceA = self.parameterAsSource(parameters, self.INPUT, context) sourceB = self.parameterAsSource(parameters, self.INTERSECT, context) fieldsA = self.parameterAsFields(parameters, self.INPUT_FIELDS, context) fieldsB = self.parameterAsFields(parameters, self.INTERSECT_FIELDS, context) fieldListA = QgsFields() field_indices_a = [] if len(fieldsA) > 0: for f in fieldsA: idxA = sourceA.fields().lookupField(f) if idxA >= 0: field_indices_a.append(idxA) fieldListA.append(sourceA.fields()[idxA]) else: fieldListA = sourceA.fields() field_indices_a = [i for i in range(0, fieldListA.count())] fieldListB = QgsFields() field_indices_b = [] if len(fieldsB) > 0: for f in fieldsB: idxB = sourceB.fields().lookupField(f) if idxB >= 0: field_indices_b.append(idxB) fieldListB.append(sourceB.fields()[idxB]) else: fieldListB = sourceB.fields() field_indices_b = [i for i in range(0, fieldListB.count())] fieldListB = vector.testForUniqueness(fieldListA, fieldListB) for b in fieldListB: fieldListA.append(b) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fieldListA, QgsWkbTypes.Point, sourceA.sourceCrs()) spatialIndex = QgsSpatialIndex(sourceB.getFeatures(QgsFeatureRequest().setSubsetOfAttributes([]).setDestinationCrs(sourceA.sourceCrs())), feedback) outFeat = QgsFeature() features = sourceA.getFeatures(QgsFeatureRequest().setSubsetOfAttributes(field_indices_a)) total = 100.0 / sourceA.featureCount() if sourceA.featureCount() else 0 for current, inFeatA in enumerate(features): if feedback.isCanceled(): break if not inFeatA.hasGeometry(): continue inGeom = inFeatA.geometry() has_intersections = False lines = spatialIndex.intersects(inGeom.boundingBox()) engine = None if len(lines) > 0: has_intersections = True # use prepared geometries for faster intersection tests engine = QgsGeometry.createGeometryEngine(inGeom.geometry()) engine.prepareGeometry() if has_intersections: request = QgsFeatureRequest().setFilterFids(lines) request.setDestinationCrs(sourceA.sourceCrs()) request.setSubsetOfAttributes(field_indices_b) for inFeatB in sourceB.getFeatures(request): if feedback.isCanceled(): break tmpGeom = inFeatB.geometry() points = [] if engine.intersects(tmpGeom.geometry()): tempGeom = inGeom.intersection(tmpGeom) out_attributes = [inFeatA.attributes()[i] for i in field_indices_a] out_attributes.extend([inFeatB.attributes()[i] for i in field_indices_b]) if tempGeom.type() == QgsWkbTypes.PointGeometry: if tempGeom.isMultipart(): points = tempGeom.asMultiPoint() else: points.append(tempGeom.asPoint()) for j in points: outFeat.setGeometry(tempGeom.fromPoint(j)) outFeat.setAttributes(out_attributes) sink.addFeature(outFeat, QgsFeatureSink.FastInsert) feedback.setProgress(int(current * total)) return {self.OUTPUT: dest_id}
def checkLayersEqual(self, layer_expected, layer_result, use_asserts=False, **kwargs): """ :param layer_expected: The first layer to compare :param layer_result: The second layer to compare :param use_asserts: If true, asserts are used to test conditions, if false, asserts are not used and the function will only return False if the test fails :param request: Optional, A feature request. This can be used to specify an order by clause to make sure features are compared in a given sequence if they don't match by default. :keyword compare: A map of comparison options. e.g. { fields: { a: skip, b: { precision: 2 }, geometry: { precision: 5 } } { fields: { __all__: cast( str ) } } :keyword pk: "Primary key" type field - used to match features from the expected table to their corresponding features in the result table. If not specified features are compared by their order in the layer (e.g. first feature compared with first feature, etc) """ try: request = kwargs['request'] except KeyError: request = QgsFeatureRequest() try: compare = kwargs['compare'] except KeyError: compare = {} # Compare CRS if 'ignore_crs_check' not in compare or not compare['ignore_crs_check']: expected_wkt = layer_expected.dataProvider().crs().toWkt( QgsCoordinateReferenceSystem.WKT_PREFERRED) result_wkt = layer_result.dataProvider().crs().toWkt( QgsCoordinateReferenceSystem.WKT_PREFERRED) if use_asserts: _TestCase.assertEqual(self, layer_expected.dataProvider().crs(), layer_result.dataProvider().crs()) elif layer_expected.dataProvider().crs( ) != layer_result.dataProvider().crs(): return False # Compare features if use_asserts: _TestCase.assertEqual(self, layer_expected.featureCount(), layer_result.featureCount()) elif layer_expected.featureCount() != layer_result.featureCount(): return False try: precision = compare['geometry']['precision'] except KeyError: precision = 14 try: topo_equal_check = compare['geometry']['topo_equal_check'] except KeyError: topo_equal_check = False try: ignore_part_order = compare['geometry']['ignore_part_order'] except KeyError: ignore_part_order = False try: unordered = compare['unordered'] except KeyError: unordered = False if unordered: features_expected = [ f for f in layer_expected.getFeatures(request) ] for feat in layer_result.getFeatures(request): feat_expected_equal = None for feat_expected in features_expected: if self.checkGeometriesEqual(feat.geometry(), feat_expected.geometry(), feat.id(), feat_expected.id(), False, precision, topo_equal_check, ignore_part_order) and \ self.checkAttributesEqual(feat, feat_expected, layer_expected.fields(), False, compare): feat_expected_equal = feat_expected break if feat_expected_equal is not None: features_expected.remove(feat_expected_equal) else: if use_asserts: _TestCase.assertTrue( self, False, 'Unexpected result feature: fid {}, geometry: {}, attributes: {}' .format( feat.id(), feat.geometry().constGet().asWkt(precision) if feat.geometry() else 'NULL', feat.attributes())) else: return False if len(features_expected) != 0: if use_asserts: lst_missing = [] for feat in features_expected: lst_missing.append( 'fid {}, geometry: {}, attributes: {}'.format( feat.id(), feat.geometry().constGet().asWkt(precision) if feat.geometry() else 'NULL', feat.attributes())) _TestCase.assertTrue( self, False, 'Some expected features not found in results:\n' + '\n'.join(lst_missing)) else: return False return True def sort_by_pk_or_fid(f): if 'pk' in kwargs and kwargs['pk'] is not None: key = kwargs['pk'] if isinstance(key, list) or isinstance(key, tuple): return [f[k] for k in key] else: return f[kwargs['pk']] else: return f.id() expected_features = sorted(layer_expected.getFeatures(request), key=sort_by_pk_or_fid) result_features = sorted(layer_result.getFeatures(request), key=sort_by_pk_or_fid) for feats in zip(expected_features, result_features): eq = self.checkGeometriesEqual(feats[0].geometry(), feats[1].geometry(), feats[0].id(), feats[1].id(), use_asserts, precision, topo_equal_check, ignore_part_order) if not eq and not use_asserts: return False eq = self.checkAttributesEqual(feats[0], feats[1], layer_expected.fields(), use_asserts, compare) if not eq and not use_asserts: return False return True
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 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()) 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 geom = featA.geometry() diffGeom = QgsGeometry(geom) attrs = featA.attributes() intersects = indexB.intersects(geom.boundingBox()) request = QgsFeatureRequest().setFilterFids(intersects).setSubsetOfAttributes([]) request.setDestinationCrs(sourceA.sourceCrs()) for featB in sourceB.getFeatures(request): if feedback.isCanceled(): break tmpGeom = featB.geometry() if diffGeom.intersects(tmpGeom): diffGeom = QgsGeometry(diffGeom.difference(tmpGeom)) try: outFeat.setGeometry(diffGeom) outFeat.setAttributes(attrs) sink.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.WARNING) continue count += 1 feedback.setProgress(int(count * total)) length = len(sourceA.fields()) for featA in sourceB.getFeatures(QgsFeatureRequest().setDestinationCrs(sourceA.sourceCrs())): if feedback.isCanceled(): break geom = featA.geometry() diffGeom = QgsGeometry(geom) attrs = featA.attributes() attrs = [NULL] * length + attrs intersects = indexA.intersects(geom.boundingBox()) request = QgsFeatureRequest().setFilterFids(intersects).setSubsetOfAttributes([]) for featB in sourceA.getFeatures(request): if feedback.isCanceled(): break tmpGeom = featB.geometry() if diffGeom.intersects(tmpGeom): diffGeom = QgsGeometry(diffGeom.difference(tmpGeom)) try: outFeat.setGeometry(diffGeom) outFeat.setAttributes(attrs) sink.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.WARNING) continue count += 1 feedback.setProgress(int(count * total)) return {self.OUTPUT: dest_id}
def test_sql2(self): l2 = QgsVectorLayer(os.path.join(self.testDataDir, "france_parts.shp"), "france_parts", "ogr", QgsVectorLayer.LayerOptions(False)) self.assertEqual(l2.isValid(), True) QgsProject.instance().addMapLayer(l2) query = toPercent("SELECT * FROM france_parts") l4 = QgsVectorLayer("?query=%s&uid=ObjectId" % query, "tt", "virtual") self.assertEqual(l4.isValid(), True) self.assertEqual(l4.dataProvider().wkbType(), 6) self.assertEqual(l4.dataProvider().crs().postgisSrid(), 4326) n = 0 r = QgsFeatureRequest(QgsRectangle(-1.677, 49.624, -0.816, 49.086)) for f in l4.getFeatures(r): self.assertEqual(f.geometry() is not None, True) self.assertEqual(f.attributes()[0], 2661) n += 1 self.assertEqual(n, 1) # use uid query = toPercent("SELECT * FROM france_parts") l5 = QgsVectorLayer( "?query=%s&geometry=geometry:polygon:4326&uid=ObjectId" % query, "tt", "virtual") self.assertEqual(l5.isValid(), True) idSum = sum(f.id() for f in l5.getFeatures()) self.assertEqual(idSum, 10659) r = QgsFeatureRequest(2661) idSum2 = sum(f.id() for f in l5.getFeatures(r)) self.assertEqual(idSum2, 2661) r = QgsFeatureRequest() r.setFilterFids([2661, 2664]) self.assertEqual(sum(f.id() for f in l5.getFeatures(r)), 2661 + 2664) # test attribute subset r = QgsFeatureRequest() r.setFlags(QgsFeatureRequest.SubsetOfAttributes) r.setSubsetOfAttributes([1]) s = [(f.id(), f.attributes()[1]) for f in l5.getFeatures(r)] self.assertEqual(sum([x[0] for x in s]), 10659) self.assertEqual(sum([x[1] for x in s]), 3064.0) # test NoGeometry # by request flag r = QgsFeatureRequest() r.setFlags(QgsFeatureRequest.NoGeometry) self.assertEqual(all([not f.hasGeometry() for f in l5.getFeatures(r)]), True) # test subset self.assertEqual(l5.dataProvider().featureCount(), 4) l5.setSubsetString("ObjectId = 2661") idSum2 = sum(f.id() for f in l5.getFeatures(r)) self.assertEqual(idSum2, 2661) self.assertEqual(l5.dataProvider().featureCount(), 1)
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): source = self.parameterAsSource(parameters, self.INPUT, context) if source is None: raise QgsProcessingException( self.invalidSourceError(parameters, self.INPUT)) pointCount = self.parameterAsDouble(parameters, self.POINTS_NUMBER, context) minDistance = self.parameterAsDouble(parameters, self.MIN_DISTANCE, context) bbox = source.sourceExtent() sourceIndex = QgsSpatialIndex(source, feedback) fields = QgsFields() fields.append(QgsField('id', QVariant.Int, '', 10, 0)) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, QgsWkbTypes.Point, source.sourceCrs(), QgsFeatureSink.RegeneratePrimaryKey) if sink is None: raise QgsProcessingException( self.invalidSinkError(parameters, self.OUTPUT)) nPoints = 0 nIterations = 0 maxIterations = pointCount * 200 total = 100.0 / pointCount if pointCount else 1 index = QgsSpatialIndex() points = dict() random.seed() while nIterations < maxIterations and nPoints < pointCount: if feedback.isCanceled(): break rx = bbox.xMinimum() + bbox.width() * random.random() ry = bbox.yMinimum() + bbox.height() * random.random() p = QgsPointXY(rx, ry) geom = QgsGeometry.fromPointXY(p) ids = sourceIndex.intersects(geom.buffer(5, 5).boundingBox()) if len(ids) > 0 and \ vector.checkMinDistance(p, index, minDistance, points): request = QgsFeatureRequest().setFilterFids( ids).setSubsetOfAttributes([]) for f in source.getFeatures(request): if feedback.isCanceled(): break tmpGeom = f.geometry() if geom.within(tmpGeom): f = QgsFeature(nPoints) f.initAttributes(1) f.setFields(fields) f.setAttribute('id', nPoints) f.setGeometry(geom) sink.addFeature(f, QgsFeatureSink.FastInsert) index.addFeature(f) points[nPoints] = p nPoints += 1 feedback.setProgress(int(nPoints * total)) nIterations += 1 if nPoints < pointCount: feedback.pushInfo( self.tr( 'Could not generate requested number of random points. ' 'Maximum number of attempts exceeded.')) return {self.OUTPUT: dest_id}
def testUpdateMode(self): """ Test that on-the-fly re-opening in update/read-only mode works """ tmpdir = tempfile.mkdtemp() self.dirs_to_cleanup.append(tmpdir) srcpath = os.path.join(TEST_DATA_DIR, 'provider') for file in glob.glob(os.path.join(srcpath, 'shapefile.*')): shutil.copy(os.path.join(srcpath, file), tmpdir) datasource = os.path.join(tmpdir, 'shapefile.shp') vl = QgsVectorLayer('{}|layerid=0'.format(datasource), 'test', 'ogr') caps = vl.dataProvider().capabilities() self.assertTrue(caps & QgsVectorDataProvider.AddFeatures) self.assertTrue(caps & QgsVectorDataProvider.DeleteFeatures) self.assertTrue(caps & QgsVectorDataProvider.ChangeAttributeValues) self.assertTrue(caps & QgsVectorDataProvider.AddAttributes) self.assertTrue(caps & QgsVectorDataProvider.DeleteAttributes) self.assertTrue(caps & QgsVectorDataProvider.CreateSpatialIndex) self.assertTrue(caps & QgsVectorDataProvider.SelectAtId) self.assertTrue(caps & QgsVectorDataProvider.ChangeGeometries) # self.assertTrue(caps & QgsVectorDataProvider.ChangeFeatures) # We should be really opened in read-only mode even if write capabilities are declared self.assertEqual(vl.dataProvider().property("_debug_open_mode"), "read-only") # Unbalanced call to leaveUpdateMode() self.assertFalse(vl.dataProvider().leaveUpdateMode()) # Test that startEditing() / commitChanges() plays with enterUpdateMode() / leaveUpdateMode() self.assertTrue(vl.startEditing()) self.assertEqual(vl.dataProvider().property("_debug_open_mode"), "read-write") self.assertTrue(vl.dataProvider().isValid()) self.assertTrue(vl.commitChanges()) self.assertEqual(vl.dataProvider().property("_debug_open_mode"), "read-only") self.assertTrue(vl.dataProvider().isValid()) # Manual enterUpdateMode() / leaveUpdateMode() with 2 depths self.assertTrue(vl.dataProvider().enterUpdateMode()) self.assertEqual(vl.dataProvider().property("_debug_open_mode"), "read-write") caps = vl.dataProvider().capabilities() self.assertTrue(caps & QgsVectorDataProvider.AddFeatures) f = QgsFeature() f.setAttributes([200]) f.setGeometry(QgsGeometry.fromWkt('Point (2 49)')) (ret, feature_list) = vl.dataProvider().addFeatures([f]) self.assertTrue(ret) fid = feature_list[0].id() features = [ f_iter for f_iter in vl.getFeatures(QgsFeatureRequest().setFilterFid(fid)) ] values = [f_iter['pk'] for f_iter in features] self.assertEqual(values, [200]) got_geom = [f_iter.geometry() for f_iter in features][0].constGet() self.assertEqual((got_geom.x(), got_geom.y()), (2.0, 49.0)) self.assertTrue(vl.dataProvider().changeGeometryValues( {fid: QgsGeometry.fromWkt('Point (3 50)')})) self.assertTrue(vl.dataProvider().changeAttributeValues( {fid: { 0: 100 }})) features = [ f_iter for f_iter in vl.getFeatures(QgsFeatureRequest().setFilterFid(fid)) ] values = [f_iter['pk'] for f_iter in features] got_geom = [f_iter.geometry() for f_iter in features][0].constGet() self.assertEqual((got_geom.x(), got_geom.y()), (3.0, 50.0)) self.assertTrue(vl.dataProvider().deleteFeatures([fid])) # Check that it has really disappeared osgeo.gdal.PushErrorHandler('CPLQuietErrorHandler') features = [ f_iter for f_iter in vl.getFeatures(QgsFeatureRequest().setFilterFid(fid)) ] osgeo.gdal.PopErrorHandler() self.assertEqual(features, []) self.assertTrue(vl.dataProvider().addAttributes( [QgsField("new_field", QVariant.Int, "integer")])) self.assertTrue(vl.dataProvider().deleteAttributes( [len(vl.dataProvider().fields()) - 1])) self.assertTrue(vl.startEditing()) self.assertEqual(vl.dataProvider().property("_debug_open_mode"), "read-write") self.assertTrue(vl.commitChanges()) self.assertEqual(vl.dataProvider().property("_debug_open_mode"), "read-write") self.assertTrue(vl.dataProvider().enterUpdateMode()) self.assertEqual(vl.dataProvider().property("_debug_open_mode"), "read-write") self.assertTrue(vl.dataProvider().leaveUpdateMode()) self.assertEqual(vl.dataProvider().property("_debug_open_mode"), "read-write") self.assertTrue(vl.dataProvider().leaveUpdateMode()) self.assertEqual(vl.dataProvider().property("_debug_open_mode"), "read-only") # Test that update mode will be implictly enabled if doing an action # that requires update mode (ret, _) = vl.dataProvider().addFeatures([QgsFeature()]) self.assertTrue(ret) self.assertEqual(vl.dataProvider().property("_debug_open_mode"), "read-write")
def getFeatures(self, request=QgsFeatureRequest()): return QgsFeatureIterator( PyFeatureIterator(PyFeatureSource(self), request))
def testSubsetString(self): if not self.source.supportsSubsetString(): print('Provider does not support subset strings') return changed_spy = QSignalSpy(self.source.dataChanged) subset = self.getSubsetString() self.source.setSubsetString(subset) self.assertEqual(self.source.subsetString(), subset) self.assertEqual(len(changed_spy), 1) # No signal should be emitted if the subset string is not modified self.source.setSubsetString(subset) self.assertEqual(len(changed_spy), 1) result = set([f['pk'] for f in self.source.getFeatures()]) all_valid = (all(f.isValid() for f in self.source.getFeatures())) self.source.setSubsetString(None) expected = set([2, 3, 4]) assert set( expected ) == result, 'Expected {} and got {} when testing subset string {}'.format( set(expected), result, subset) self.assertTrue(all_valid) # Subset string AND filter rect self.source.setSubsetString(subset) extent = QgsRectangle(-70, 70, -60, 75) request = QgsFeatureRequest().setFilterRect(extent) result = set([f['pk'] for f in self.source.getFeatures(request)]) all_valid = (all(f.isValid() for f in self.source.getFeatures(request))) self.source.setSubsetString(None) expected = set([2]) assert set( expected ) == result, 'Expected {} and got {} when testing subset string {}'.format( set(expected), result, subset) self.assertTrue(all_valid) # Subset string AND filter rect, version 2 self.source.setSubsetString(subset) extent = QgsRectangle(-71, 65, -60, 80) result = set([ f['pk'] for f in self.source.getFeatures( QgsFeatureRequest().setFilterRect(extent)) ]) self.source.setSubsetString(None) expected = set([2, 4]) assert set( expected ) == result, 'Expected {} and got {} when testing subset string {}'.format( set(expected), result, subset) # Subset string AND expression self.source.setSubsetString(subset) request = QgsFeatureRequest().setFilterExpression('length("name")=5') result = set([f['pk'] for f in self.source.getFeatures(request)]) all_valid = (all(f.isValid() for f in self.source.getFeatures(request))) self.source.setSubsetString(None) expected = set([2, 4]) assert set( expected ) == result, 'Expected {} and got {} when testing subset string {}'.format( set(expected), result, subset) self.assertTrue(all_valid)
def processAlgorithm(self, parameters, context, feedback): try: import numpy except ImportError: raise QgsProcessingException( QCoreApplication.translate( 'HypsometricCurves', 'This algorithm requires the Python “numpy” library. Please install this library and try again.' )) raster_layer = self.parameterAsRasterLayer(parameters, self.INPUT_DEM, context) target_crs = raster_layer.crs() rasterPath = raster_layer.source() source = self.parameterAsSource(parameters, self.BOUNDARY_LAYER, context) if source is None: raise QgsProcessingException( self.invalidSourceError(parameters, self.BOUNDARY_LAYER)) step = self.parameterAsDouble(parameters, self.STEP, context) percentage = self.parameterAsBoolean(parameters, self.USE_PERCENTAGE, context) outputPath = self.parameterAsString(parameters, self.OUTPUT_DIRECTORY, context) if not os.path.exists(outputPath): os.makedirs(outputPath) rasterDS = gdal.Open(rasterPath, gdal.GA_ReadOnly) geoTransform = rasterDS.GetGeoTransform() rasterBand = rasterDS.GetRasterBand(1) noData = rasterBand.GetNoDataValue() cellXSize = abs(geoTransform[1]) cellYSize = abs(geoTransform[5]) rasterXSize = rasterDS.RasterXSize rasterYSize = rasterDS.RasterYSize rasterBBox = QgsRectangle(geoTransform[0], geoTransform[3] - cellYSize * rasterYSize, geoTransform[0] + cellXSize * rasterXSize, geoTransform[3]) rasterGeom = QgsGeometry.fromRect(rasterBBox) crs = osr.SpatialReference() crs.ImportFromProj4(str(target_crs.toProj4())) memVectorDriver = ogr.GetDriverByName('Memory') memRasterDriver = gdal.GetDriverByName('MEM') features = source.getFeatures(QgsFeatureRequest().setDestinationCrs( target_crs, context.transformContext())) total = 100.0 / source.featureCount() if source.featureCount() else 0 for current, f in enumerate(features): if not f.hasGeometry(): continue if feedback.isCanceled(): break geom = f.geometry() intersectedGeom = rasterGeom.intersection(geom) if intersectedGeom.isEmpty(): feedback.pushInfo( self.tr('Feature {0} does not intersect raster or ' 'entirely located in NODATA area').format(f.id())) continue fName = os.path.join( outputPath, 'histogram_{}_{}.csv'.format(source.sourceName(), f.id())) ogrGeom = ogr.CreateGeometryFromWkt(intersectedGeom.asWkt()) bbox = intersectedGeom.boundingBox() xMin = bbox.xMinimum() xMax = bbox.xMaximum() yMin = bbox.yMinimum() yMax = bbox.yMaximum() (startColumn, startRow) = raster.mapToPixel(xMin, yMax, geoTransform) (endColumn, endRow) = raster.mapToPixel(xMax, yMin, geoTransform) width = endColumn - startColumn height = endRow - startRow srcOffset = (startColumn, startRow, width, height) srcArray = rasterBand.ReadAsArray(*srcOffset) if srcOffset[2] == 0 or srcOffset[3] == 0: feedback.pushInfo( self.tr('Feature {0} is smaller than raster ' 'cell size').format(f.id())) continue newGeoTransform = (geoTransform[0] + srcOffset[0] * geoTransform[1], geoTransform[1], 0.0, geoTransform[3] + srcOffset[1] * geoTransform[5], 0.0, geoTransform[5]) memVDS = memVectorDriver.CreateDataSource('out') memLayer = memVDS.CreateLayer('poly', crs, ogr.wkbPolygon) ft = ogr.Feature(memLayer.GetLayerDefn()) ft.SetGeometry(ogrGeom) memLayer.CreateFeature(ft) ft.Destroy() rasterizedDS = memRasterDriver.Create('', srcOffset[2], srcOffset[3], 1, gdal.GDT_Byte) rasterizedDS.SetGeoTransform(newGeoTransform) gdal.RasterizeLayer(rasterizedDS, [1], memLayer, burn_values=[1]) rasterizedArray = rasterizedDS.ReadAsArray() srcArray = numpy.nan_to_num(srcArray) masked = numpy.ma.MaskedArray( srcArray, mask=numpy.logical_or(srcArray == noData, numpy.logical_not(rasterizedArray))) self.calculateHypsometry(f.id(), fName, feedback, masked, cellXSize, cellYSize, percentage, step) memVDS = None rasterizedDS = None feedback.setProgress(int(current * total)) rasterDS = None return {self.OUTPUT_DIRECTORY: outputPath}
def processAlgorithm(self, parameters, context, feedback): source = self.parameterAsSource(parameters, self.INPUT, context) if source is None: raise QgsProcessingException( self.invalidSourceError(parameters, self.INPUT)) value_field_name = self.parameterAsString(parameters, self.VALUES_FIELD_NAME, context) category_field_names = self.parameterAsFields( parameters, self.CATEGORIES_FIELD_NAME, context) value_field_index = source.fields().lookupField(value_field_name) if value_field_index >= 0: value_field = source.fields().at(value_field_index) else: value_field = None category_field_indexes = [ source.fields().lookupField(n) for n in category_field_names ] # generate output fields fields = QgsFields() for c in category_field_indexes: fields.append(source.fields().at(c)) def addField(name): """ Adds a field to the output, keeping the same data type as the value_field """ field = QgsField(value_field) field.setName(name) fields.append(field) if value_field is None: field_type = 'none' fields.append(QgsField('count', QVariant.Int)) elif value_field.isNumeric(): field_type = 'numeric' fields.append(QgsField('count', QVariant.Int)) fields.append(QgsField('unique', QVariant.Int)) fields.append(QgsField('min', QVariant.Double)) fields.append(QgsField('max', QVariant.Double)) fields.append(QgsField('range', QVariant.Double)) fields.append(QgsField('sum', QVariant.Double)) fields.append(QgsField('mean', QVariant.Double)) fields.append(QgsField('median', QVariant.Double)) fields.append(QgsField('stddev', QVariant.Double)) fields.append(QgsField('minority', QVariant.Double)) fields.append(QgsField('majority', QVariant.Double)) fields.append(QgsField('q1', QVariant.Double)) fields.append(QgsField('q3', QVariant.Double)) fields.append(QgsField('iqr', QVariant.Double)) elif value_field.type() in (QVariant.Date, QVariant.Time, QVariant.DateTime): field_type = 'datetime' fields.append(QgsField('count', QVariant.Int)) fields.append(QgsField('unique', QVariant.Int)) fields.append(QgsField('empty', QVariant.Int)) fields.append(QgsField('filled', QVariant.Int)) # keep same data type for these fields addField('min') addField('max') else: field_type = 'string' fields.append(QgsField('count', QVariant.Int)) fields.append(QgsField('unique', QVariant.Int)) fields.append(QgsField('empty', QVariant.Int)) fields.append(QgsField('filled', QVariant.Int)) # keep same data type for these fields addField('min') addField('max') fields.append(QgsField('min_length', QVariant.Int)) fields.append(QgsField('max_length', QVariant.Int)) fields.append(QgsField('mean_length', QVariant.Double)) request = QgsFeatureRequest().setFlags(QgsFeatureRequest.NoGeometry) if value_field is not None: attrs = [value_field_index] else: attrs = [] attrs.extend(category_field_indexes) request.setSubsetOfAttributes(attrs) features = source.getFeatures( request, QgsProcessingFeatureSource.FlagSkipGeometryValidityChecks) total = 50.0 / source.featureCount() if source.featureCount() else 0 if field_type == 'none': values = defaultdict(lambda: 0) else: values = defaultdict(list) for current, feat in enumerate(features): if feedback.isCanceled(): break feedback.setProgress(int(current * total)) attrs = feat.attributes() cat = tuple([attrs[c] for c in category_field_indexes]) if field_type == 'none': values[cat] += 1 continue if field_type == 'numeric': if attrs[value_field_index] == NULL: continue else: value = float(attrs[value_field_index]) elif field_type == 'string': if attrs[value_field_index] == NULL: value = '' else: value = str(attrs[value_field_index]) elif attrs[value_field_index] == NULL: value = NULL else: value = attrs[value_field_index] values[cat].append(value) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, QgsWkbTypes.NoGeometry, QgsCoordinateReferenceSystem()) if sink is None: raise QgsProcessingException( self.invalidSinkError(parameters, self.OUTPUT)) if field_type == 'none': self.saveCounts(values, sink, feedback) elif field_type == 'numeric': self.calcNumericStats(values, sink, feedback) elif field_type == 'datetime': self.calcDateTimeStats(values, sink, feedback) else: self.calcStringStats(values, sink, feedback) return {self.OUTPUT: dest_id}
def regularMatrix(self, parameters, context, source, inField, target_source, targetField, nPoints, feedback): distArea = QgsDistanceArea() distArea.setSourceCrs(source.sourceCrs(), context.transformContext()) distArea.setEllipsoid(context.project().ellipsoid()) inIdx = source.fields().lookupField(inField) targetIdx = target_source.fields().lookupField(targetField) index = QgsSpatialIndex( target_source.getFeatures( QgsFeatureRequest().setSubsetOfAttributes( []).setDestinationCrs(source.sourceCrs(), context.transformContext())), feedback) first = True sink = None dest_id = None 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() if first: featList = index.nearestNeighbor(inGeom.asPoint(), nPoints) first = False fields = QgsFields() input_id_field = source.fields()[inIdx] input_id_field.setName('ID') fields.append(input_id_field) for f in target_source.getFeatures( QgsFeatureRequest().setFilterFids( featList).setSubsetOfAttributes([ targetIdx ]).setDestinationCrs(source.sourceCrs(), context.transformContext())): fields.append( QgsField(str(f[targetField]), QVariant.Double)) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, source.wkbType(), source.sourceCrs()) if sink is None: raise QgsProcessingException( self.invalidSinkError(parameters, self.OUTPUT)) data = [inFeat[inField]] for target in target_source.getFeatures( QgsFeatureRequest().setSubsetOfAttributes( []).setFilterFids(featList).setDestinationCrs( source.sourceCrs(), context.transformContext())): if feedback.isCanceled(): break outGeom = target.geometry() dist = distArea.measureLine(inGeom.asPoint(), outGeom.asPoint()) data.append(dist) out_feature = QgsFeature() out_feature.setGeometry(inGeom) out_feature.setAttributes(data) sink.addFeature(out_feature, QgsFeatureSink.FastInsert) feedback.setProgress(int(current * total)) return {self.OUTPUT: dest_id}
def testMaterialize(self): """ Test materializing layers """ layer = createLayerWithFivePoints() original_features = {f[0]: f for f in layer.getFeatures()} # materialize all features, unchanged request = QgsFeatureRequest() new_layer = layer.materialize(request) self.assertEqual(new_layer.fields(), layer.fields()) self.assertEqual(new_layer.crs(), layer.crs()) self.assertEqual(new_layer.featureCount(), 5) self.assertEqual(new_layer.wkbType(), QgsWkbTypes.Point) new_features = {f[0]: f for f in new_layer.getFeatures()} for id, f in original_features.items(): self.assertEqual(new_features[id].attributes(), f.attributes()) self.assertEqual(new_features[id].geometry().asWkt(), f.geometry().asWkt()) # materialize with no geometry request = QgsFeatureRequest().setFlags(QgsFeatureRequest.NoGeometry) new_layer = layer.materialize(request) self.assertEqual(new_layer.fields(), layer.fields()) self.assertEqual(new_layer.crs(), layer.crs()) self.assertEqual(new_layer.featureCount(), 5) self.assertEqual(new_layer.wkbType(), QgsWkbTypes.NoGeometry) new_features = {f[0]: f for f in new_layer.getFeatures()} for id, f in original_features.items(): self.assertEqual(new_features[id].attributes(), f.attributes()) # materialize with reprojection request = QgsFeatureRequest().setDestinationCrs( QgsCoordinateReferenceSystem('EPSG:3785'), QgsProject.instance().transformContext()) new_layer = layer.materialize(request) self.assertEqual(new_layer.fields(), layer.fields()) self.assertEqual(new_layer.crs().authid(), 'EPSG:3785') self.assertEqual(new_layer.featureCount(), 5) self.assertEqual(new_layer.wkbType(), QgsWkbTypes.Point) new_features = {f[0]: f for f in new_layer.getFeatures()} expected_geometry = { 1: 'Point (111319 222684)', 2: 'Point (222639 222684)', 3: 'Point (333958 222684)', 4: 'Point (445278 334111)', 5: 'Point (0 -0)' } for id, f in original_features.items(): self.assertEqual(new_features[id].attributes(), f.attributes()) self.assertEqual(new_features[id].geometry().asWkt(0), expected_geometry[id]) # materialize with attribute subset request = QgsFeatureRequest().setSubsetOfAttributes([0, 2]) new_layer = layer.materialize(request) self.assertEqual(new_layer.fields().count(), 2) self.assertEqual(new_layer.fields().at(0), layer.fields().at(0)) self.assertEqual(new_layer.fields().at(1), layer.fields().at(2)) self.assertEqual(new_layer.crs(), layer.crs()) self.assertEqual(new_layer.featureCount(), 5) self.assertEqual(new_layer.wkbType(), QgsWkbTypes.Point) new_features = {f.attributes()[0]: f for f in new_layer.getFeatures()} for id, f in original_features.items(): self.assertEqual(new_features[id].attributes()[0], f.attributes()[0]) self.assertEqual(new_features[id].attributes()[1], f.attributes()[2]) request = QgsFeatureRequest().setSubsetOfAttributes([0, 1]) new_layer = layer.materialize(request) self.assertEqual(new_layer.fields().count(), 2) self.assertEqual(new_layer.fields().at(0), layer.fields().at(0)) self.assertEqual(new_layer.fields().at(1), layer.fields().at(1)) new_features = {f.attributes()[0]: f for f in new_layer.getFeatures()} for id, f in original_features.items(): self.assertEqual(new_features[id].attributes()[0], f.attributes()[0]) self.assertEqual(new_features[id].attributes()[1], f.attributes()[1]) request = QgsFeatureRequest().setSubsetOfAttributes([0]) new_layer = layer.materialize(request) self.assertEqual(new_layer.fields().count(), 1) self.assertEqual(new_layer.fields().at(0), layer.fields().at(0)) new_features = {f.attributes()[0]: f for f in new_layer.getFeatures()} for id, f in original_features.items(): self.assertEqual(new_features[id].attributes()[0], f.attributes()[0])
def processAlgorithm(self, parameters, context, feedback): source = self.parameterAsSource(parameters, self.INPUT, context) buf = self.parameterAsDouble(parameters, self.BUFFER, context) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, source.fields(), QgsWkbTypes.Polygon, source.sourceCrs()) outFeat = QgsFeature() extent = source.sourceExtent() extraX = extent.height() * (buf / 100.0) extraY = extent.width() * (buf / 100.0) height = extent.height() width = extent.width() c = voronoi.Context() pts = [] ptDict = {} ptNdx = -1 features = source.getFeatures() total = 100.0 / source.featureCount() if source.featureCount() else 0 for current, inFeat in enumerate(features): if feedback.isCanceled(): break geom = inFeat.geometry() point = geom.asPoint() x = point.x() - extent.xMinimum() y = point.y() - extent.yMinimum() pts.append((x, y)) ptNdx += 1 ptDict[ptNdx] = inFeat.id() feedback.setProgress(int(current * total)) if len(pts) < 3: raise QgsProcessingException( self.tr('Input file should contain at least 3 points. Choose ' 'another file and try again.')) uniqueSet = set(item for item in pts) ids = [pts.index(item) for item in uniqueSet] sl = voronoi.SiteList([voronoi.Site(i[0], i[1], sitenum=j) for (j, i) in enumerate(uniqueSet)]) voronoi.voronoi(sl, c) inFeat = QgsFeature() current = 0 if len(c.polygons) == 0: raise QgsProcessingException( self.tr('There were no polygons created.')) total = 100.0 / len(c.polygons) for (site, edges) in list(c.polygons.items()): if feedback.isCanceled(): break request = QgsFeatureRequest().setFilterFid(ptDict[ids[site]]) inFeat = next(source.getFeatures(request)) lines = self.clip_voronoi(edges, c, width, height, extent, extraX, extraY) geom = QgsGeometry.fromMultiPointXY(lines) geom = QgsGeometry(geom.convexHull()) outFeat.setGeometry(geom) outFeat.setAttributes(inFeat.attributes()) sink.addFeature(outFeat, QgsFeatureSink.FastInsert) current += 1 feedback.setProgress(int(current * total)) return {self.OUTPUT: dest_id}
def processAlgorithm(self, parameters, context, feedback): network = self.parameterAsSource(parameters, self.INPUT, context) if network is None: raise QgsProcessingException(self.invalidSourceError(parameters, self.INPUT)) startPoints = self.parameterAsSource(parameters, self.START_POINTS, context) if startPoints is None: raise QgsProcessingException(self.invalidSourceError(parameters, self.START_POINTS)) strategy = self.parameterAsEnum(parameters, self.STRATEGY, context) travelCost = self.parameterAsDouble(parameters, self.TRAVEL_COST, context) directionFieldName = self.parameterAsString(parameters, self.DIRECTION_FIELD, context) forwardValue = self.parameterAsString(parameters, self.VALUE_FORWARD, context) backwardValue = self.parameterAsString(parameters, self.VALUE_BACKWARD, context) bothValue = self.parameterAsString(parameters, self.VALUE_BOTH, context) defaultDirection = self.parameterAsEnum(parameters, self.DEFAULT_DIRECTION, context) speedFieldName = self.parameterAsString(parameters, self.SPEED_FIELD, context) defaultSpeed = self.parameterAsDouble(parameters, self.DEFAULT_SPEED, context) tolerance = self.parameterAsDouble(parameters, self.TOLERANCE, context) include_bounds = True # default to true to maintain 3.0 API if self.INCLUDE_BOUNDS in parameters: include_bounds = self.parameterAsBool(parameters, self.INCLUDE_BOUNDS, context) fields = startPoints.fields() fields.append(QgsField('type', QVariant.String, '', 254, 0)) fields.append(QgsField('start', QVariant.String, '', 254, 0)) feat = QgsFeature() feat.setFields(fields) directionField = -1 if directionFieldName: directionField = network.fields().lookupField(directionFieldName) speedField = -1 if speedFieldName: speedField = network.fields().lookupField(speedFieldName) director = QgsVectorLayerDirector(network, directionField, forwardValue, backwardValue, bothValue, defaultDirection) distUnit = context.project().crs().mapUnits() multiplier = QgsUnitTypes.fromUnitToUnitFactor(distUnit, QgsUnitTypes.DistanceMeters) if strategy == 0: strategy = QgsNetworkDistanceStrategy() else: strategy = QgsNetworkSpeedStrategy(speedField, defaultSpeed, multiplier * 1000.0 / 3600.0) director.addStrategy(strategy) builder = QgsGraphBuilder(network.sourceCrs(), True, tolerance) feedback.pushInfo(QCoreApplication.translate('ServiceAreaFromLayer', 'Loading start points…')) request = QgsFeatureRequest() request.setDestinationCrs(network.sourceCrs(), context.transformContext()) features = startPoints.getFeatures(request) total = 100.0 / startPoints.featureCount() if startPoints.featureCount() else 0 points = [] source_attributes = {} i = 0 for current, f in enumerate(features): if feedback.isCanceled(): break if not f.hasGeometry(): continue for p in f.geometry().vertices(): points.append(QgsPointXY(p)) source_attributes[i] = f.attributes() i += 1 feedback.setProgress(int(current * total)) feedback.pushInfo(QCoreApplication.translate('ServiceAreaFromLayer', 'Building graph…')) snappedPoints = director.makeGraph(builder, points, feedback) feedback.pushInfo(QCoreApplication.translate('ServiceAreaFromLayer', 'Calculating service areas…')) graph = builder.graph() (point_sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, QgsWkbTypes.MultiPoint, network.sourceCrs()) (line_sink, line_dest_id) = self.parameterAsSink(parameters, self.OUTPUT_LINES, context, fields, QgsWkbTypes.MultiLineString, network.sourceCrs()) total = 100.0 / len(snappedPoints) if snappedPoints else 1 for i, p in enumerate(snappedPoints): if feedback.isCanceled(): break idxStart = graph.findVertex(snappedPoints[i]) origPoint = points[i].toString() tree, cost = QgsGraphAnalyzer.dijkstra(graph, idxStart, 0) vertices = set() area_points = [] lines = [] for vertex, start_vertex_cost in enumerate(cost): inbound_edge_index = tree[vertex] if inbound_edge_index == -1 and vertex != idxStart: # unreachable vertex continue if start_vertex_cost > travelCost: # vertex is too expensive, discard continue vertices.add(vertex) start_point = graph.vertex(vertex).point() # find all edges coming from this vertex for edge_id in graph.vertex(vertex).outgoingEdges(): edge = graph.edge(edge_id) end_vertex_cost = start_vertex_cost + edge.cost(0) end_point = graph.vertex(edge.toVertex()).point() if end_vertex_cost <= travelCost: # end vertex is cheap enough to include vertices.add(edge.toVertex()) lines.append([start_point, end_point]) else: # travelCost sits somewhere on this edge, interpolate position interpolated_end_point = QgsGeometryUtils.interpolatePointOnLineByValue(start_point.x(), start_point.y(), start_vertex_cost, end_point.x(), end_point.y(), end_vertex_cost, travelCost) area_points.append(interpolated_end_point) lines.append([start_point, interpolated_end_point]) for v in vertices: area_points.append(graph.vertex(v).point()) feat = QgsFeature() if point_sink is not None: geomPoints = QgsGeometry.fromMultiPointXY(area_points) feat.setGeometry(geomPoints) attrs = source_attributes[i] attrs.extend(['within', origPoint]) feat.setAttributes(attrs) point_sink.addFeature(feat, QgsFeatureSink.FastInsert) if include_bounds: upperBoundary = [] lowerBoundary = [] vertices = [] for vertex, c in enumerate(cost): if c > travelCost and tree[vertex] != -1: vertexId = graph.edge(tree[vertex]).fromVertex() if cost[vertexId] <= travelCost: vertices.append(vertex) for v in vertices: upperBoundary.append(graph.vertex(graph.edge(tree[v]).toVertex()).point()) lowerBoundary.append(graph.vertex(graph.edge(tree[v]).fromVertex()).point()) geomUpper = QgsGeometry.fromMultiPointXY(upperBoundary) geomLower = QgsGeometry.fromMultiPointXY(lowerBoundary) feat.setGeometry(geomUpper) attrs[-2] = 'upper' feat.setAttributes(attrs) point_sink.addFeature(feat, QgsFeatureSink.FastInsert) feat.setGeometry(geomLower) attrs[-2] = 'lower' feat.setAttributes(attrs) point_sink.addFeature(feat, QgsFeatureSink.FastInsert) if line_sink is not None: geom_lines = QgsGeometry.fromMultiPolylineXY(lines) feat.setGeometry(geom_lines) attrs = source_attributes[i] attrs.extend(['lines', origPoint]) feat.setAttributes(attrs) line_sink.addFeature(feat, QgsFeatureSink.FastInsert) feedback.setProgress(int(i * total)) results = {} if point_sink is not None: results[self.OUTPUT] = dest_id if line_sink is not None: results[self.OUTPUT_LINES] = line_dest_id return results
def processAlgorithm(self, parameters, context, feedback): source = self.parameterAsSource(parameters, self.INPUT_VECTOR, context) if source is None: raise QgsProcessingException( self.invalidSourceError(parameters, self.INPUT_VECTOR)) raster_layer = self.parameterAsRasterLayer(parameters, self.INPUT_RASTER, context) rasterPath = raster_layer.source() rasterDS = gdal.Open(rasterPath, gdal.GA_ReadOnly) geoTransform = rasterDS.GetGeoTransform() rasterDS = None fields = QgsFields() fields.append(QgsField('id', QVariant.Int, '', 10, 0)) fields.append(QgsField('line_id', QVariant.Int, '', 10, 0)) fields.append(QgsField('point_id', QVariant.Int, '', 10, 0)) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, QgsWkbTypes.Point, raster_layer.crs()) if sink is None: raise QgsProcessingException( self.invalidSinkError(parameters, self.OUTPUT)) outFeature = QgsFeature() outFeature.setFields(fields) self.fid = 0 self.lineId = 0 self.pointId = 0 features = source.getFeatures(QgsFeatureRequest().setDestinationCrs( raster_layer.crs(), context.transformContext())) total = 100.0 / source.featureCount() if source.featureCount() else 0 for current, f in enumerate(features): if feedback.isCanceled(): break if not f.hasGeometry(): continue geom = f.geometry() if geom.isMultipart(): lines = geom.asMultiPolyline() for line in lines: for i in range(len(line) - 1): p1 = line[i] p2 = line[i + 1] (x1, y1) = raster.mapToPixel(p1.x(), p1.y(), geoTransform) (x2, y2) = raster.mapToPixel(p2.x(), p2.y(), geoTransform) self.buildLine(x1, y1, x2, y2, geoTransform, sink, outFeature) else: points = geom.asPolyline() for i in range(len(points) - 1): p1 = points[i] p2 = points[i + 1] (x1, y1) = raster.mapToPixel(p1.x(), p1.y(), geoTransform) (x2, y2) = raster.mapToPixel(p2.x(), p2.y(), geoTransform) self.buildLine(x1, y1, x2, y2, geoTransform, sink, outFeature) self.pointId = 0 self.lineId += 1 feedback.setProgress(int(current * total)) return {self.OUTPUT: dest_id}
def processAlgorithm(self, parameters, context, feedback): if parameters[self.INPUT] == parameters[self.HUBS]: raise QgsProcessingException( self.tr('Same layer given for both hubs and spokes')) point_source = self.parameterAsSource(parameters, self.INPUT, context) if point_source is None: raise QgsProcessingException(self.invalidSourceError(parameters, self.INPUT)) hub_source = self.parameterAsSource(parameters, self.HUBS, context) if hub_source is None: raise QgsProcessingException(self.invalidSourceError(parameters, self.HUBS)) fieldName = self.parameterAsString(parameters, self.FIELD, context) units = self.UNITS[self.parameterAsEnum(parameters, self.UNIT, context)] fields = point_source.fields() fields.append(QgsField('HubName', QVariant.String)) fields.append(QgsField('HubDist', QVariant.Double)) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, QgsWkbTypes.Point, point_source.sourceCrs()) if sink is None: raise QgsProcessingException(self.invalidSinkError(parameters, self.OUTPUT)) index = QgsSpatialIndex(hub_source.getFeatures(QgsFeatureRequest().setSubsetOfAttributes([]).setDestinationCrs(point_source.sourceCrs(), context.transformContext()))) distance = QgsDistanceArea() distance.setSourceCrs(point_source.sourceCrs(), context.transformContext()) distance.setEllipsoid(context.ellipsoid()) # Scan source points, find nearest hub, and write to output file features = point_source.getFeatures() total = 100.0 / point_source.featureCount() if point_source.featureCount() else 0 for current, f in enumerate(features): if feedback.isCanceled(): break if not f.hasGeometry(): sink.addFeature(f, QgsFeatureSink.FastInsert) continue src = f.geometry().boundingBox().center() neighbors = index.nearestNeighbor(src, 1) ft = next(hub_source.getFeatures(QgsFeatureRequest().setFilterFid(neighbors[0]).setSubsetOfAttributes([fieldName], hub_source.fields()).setDestinationCrs(point_source.sourceCrs(), context.transformContext()))) closest = ft.geometry().boundingBox().center() hubDist = distance.measureLine(src, closest) if units != self.LAYER_UNITS: hub_dist_in_desired_units = distance.convertLengthMeasurement(hubDist, units) else: hub_dist_in_desired_units = hubDist attributes = f.attributes() attributes.append(ft[fieldName]) attributes.append(hub_dist_in_desired_units) feat = QgsFeature() feat.setAttributes(attributes) feat.setGeometry(QgsGeometry.fromPointXY(src)) sink.addFeature(feat, QgsFeatureSink.FastInsert) feedback.setProgress(int(current * total)) return {self.OUTPUT: dest_id}