def exportVectorLayer(self, orgFilename): context = dataobjects.createContext() # TODO: improve this. We are now exporting if it is not a shapefile, # but the functionality of v.in.ogr could be used for this. # We also export if there is a selection if not os.path.exists(orgFilename) or not orgFilename.endswith('shp'): layer = QgsProcessingUtils.mapLayerFromString(orgFilename, context, False) if layer: filename = dataobjects.exportVectorLayer(layer) else: layer = QgsProcessingUtils.mapLayerFromString(orgFilename, context, False) if layer: #TODO #useSelection = \ # ProcessingConfig.getSetting(ProcessingConfig.USE_SELECTED) if useSelection and layer.selectedFeatureCount() != 0: filename = dataobjects.exportVectorLayer(layer) else: filename = orgFilename else: filename = orgFilename destFilename = 'a' + os.path.basename(self.getTempFilename()) self.exportedLayers[orgFilename] = destFilename command = 'v.in.ogr' min_area = self.getParameterValue(self.GRASS_MIN_AREA_PARAMETER) command += ' min_area=' + str(min_area) snap = self.getParameterValue(self.GRASS_SNAP_TOLERANCE_PARAMETER) command += ' snap=' + str(snap) command += ' input="' + os.path.dirname(filename) + '"' command += ' layer=' + os.path.basename(filename)[:-4] command += ' output=' + destFilename command += ' --overwrite -o' return command
def test_parameterAs_ScriptMode(self): """ This test will pass an instance of QgsCoordinateReferenceSystem for 'epsg' parameter of otb::Rasterization. There is same test in otb_algorithm_tests.yaml which passes an instance of str for epsg parameter. """ outdir = tempfile.mkdtemp() self.cleanup_paths.append(outdir) context = QgsProcessingContext() context.setProject(QgsProject.instance()) feedback = QgsProcessingFeedback() vectorFile = os.path.join(AlgorithmsTestBase.processingTestDataPath(), 'polys.gml') vectorLayer = QgsProcessingUtils.mapLayerFromString(vectorFile, context) parameters = { 'in': vectorLayer, 'epsg': QgsCoordinateReferenceSystem('EPSG:4326'), 'spx': 1.0, 'spy': 1.0, 'outputpixeltype': 1, 'out': os.path.join(outdir, 'raster.tif') } results = processing.run('otb:Rasterization', parameters, None, feedback) result_lyr = QgsProcessingUtils.mapLayerFromString(results['out'], context) self.assertTrue(result_lyr.isValid())
def processAlgorithm(self, parameters, context, feedback): layer = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.INPUT), context) reference_layer = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.REFERENCE_LAYER), context) tolerance = self.getParameterValue(self.TOLERANCE) mode = self.getParameterValue(self.BEHAVIOR) writer = self.getOutputFromName(self.OUTPUT).getVectorWriter(layer.fields(), layer.wkbType(), layer.crs(), context) features = QgsProcessingUtils.getFeatures(layer, context) self.processed = 0 self.feedback = feedback self.total = 100.0 / QgsProcessingUtils.featureCount(layer, context) if self.getParameterValue(self.INPUT) != self.getParameterValue(self.REFERENCE_LAYER): snapper = QgsGeometrySnapper(reference_layer) snapper.featureSnapped.connect(self.featureSnapped) snapped_features = snapper.snapFeatures(features, tolerance, mode) for f in snapped_features: writer.addFeature(QgsFeature(f)) else: # snapping internally snapper = QgsInternalGeometrySnapper(tolerance, mode) processed = 0 for f in features: out_feature = f out_feature.setGeometry(snapper.snapFeature(f)) writer.addFeature(out_feature) processed += 1 feedback.setProgress(processed * self.total) del writer
def processAlgorithm(self, parameters, context, feedback): layerA = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(Difference.INPUT), context) layerB = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(Difference.OVERLAY), context) geomType = QgsWkbTypes.multiType(layerA.wkbType()) writer = self.getOutputFromName( Difference.OUTPUT).getVectorWriter(layerA.fields(), geomType, layerA.crs(), context) outFeat = QgsFeature() index = QgsProcessingUtils.createSpatialIndex(layerB, context) selectionA = QgsProcessingUtils.getFeatures(layerA, context) total = 100.0 / layerA.featureCount() if layerA.featureCount() else 0 for current, inFeatA in enumerate(selectionA): geom = inFeatA.geometry() diff_geom = QgsGeometry(geom) attrs = inFeatA.attributes() intersections = index.intersects(geom.boundingBox()) request = QgsFeatureRequest().setFilterFids(intersections).setSubsetOfAttributes([]) for inFeatB in layerB.getFeatures(request): tmpGeom = inFeatB.geometry() if diff_geom.intersects(tmpGeom): diff_geom = QgsGeometry(diff_geom.difference(tmpGeom)) try: outFeat.setGeometry(diff_geom) outFeat.setAttributes(attrs) writer.addFeature(outFeat, QgsFeatureSink.FastInsert) except: QgsMessageLog.logMessage(self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.'), self.tr('Processing'), QgsMessageLog.WARNING) continue feedback.setProgress(int(current * total)) del writer
def getDefaultCellsize(self, parameters, context): cellsize = 0 for param in self.parameterDefinitions(): if param.name() in parameters: value = parameters[param.name()] if isinstance(param, ParameterRaster): if isinstance(value, QgsRasterLayer): layer = value else: layer = QgsProcessingUtils.mapLayerFromString(value, context) cellsize = max(cellsize, (layer.extent().xMaximum() - layer.extent().xMinimum()) / layer.width()) elif isinstance(param, ParameterMultipleInput): layers = value.split(';') for layername in layers: layer = QgsProcessingUtils.mapLayerFromString(layername, context) if isinstance(layer, QgsRasterLayer): cellsize = max(cellsize, ( layer.extent().xMaximum() - layer.extent().xMinimum()) / layer.width()) if cellsize == 0: cellsize = 1 return cellsize
def test_check_validity(self): """Test that the output invalid contains the error reason""" polygon_layer = self._make_layer('Polygon') self.assertTrue(polygon_layer.startEditing()) f = QgsFeature(polygon_layer.fields()) f.setAttributes([1]) # Flake! f.setGeometry(QgsGeometry.fromWkt( 'POLYGON ((0 0, 2 2, 0 2, 2 0, 0 0))')) self.assertTrue(f.isValid()) f2 = QgsFeature(polygon_layer.fields()) f2.setAttributes([1]) f2.setGeometry(QgsGeometry.fromWkt( 'POLYGON((1.1 1.1, 1.1 2.1, 2.1 2.1, 2.1 1.1, 1.1 1.1))')) self.assertTrue(f2.isValid()) self.assertTrue(polygon_layer.addFeatures([f, f2])) polygon_layer.commitChanges() polygon_layer.rollBack() self.assertEqual(polygon_layer.featureCount(), 2) QgsProject.instance().addMapLayers([polygon_layer]) alg = self.registry.createAlgorithmById('qgis:checkvalidity') context = QgsProcessingContext() context.setProject(QgsProject.instance()) feedback = ConsoleFeedBack() self.assertIsNotNone(alg) parameters = {} parameters['INPUT_LAYER'] = polygon_layer.id() parameters['VALID_OUTPUT'] = 'memory:' parameters['INVALID_OUTPUT'] = 'memory:' parameters['ERROR_OUTPUT'] = 'memory:' # QGIS method parameters['METHOD'] = 1 ok, results = execute( alg, parameters, context=context, feedback=feedback) self.assertTrue(ok) invalid_layer = QgsProcessingUtils.mapLayerFromString( results['INVALID_OUTPUT'], context) self.assertEqual(invalid_layer.fields().names()[-1], '_errors') self.assertEqual(invalid_layer.featureCount(), 1) f = next(invalid_layer.getFeatures()) self.assertEqual(f.attributes(), [ 1, 'segments 0 and 2 of line 0 intersect at 1, 1']) # GEOS method parameters['METHOD'] = 2 ok, results = execute( alg, parameters, context=context, feedback=feedback) self.assertTrue(ok) invalid_layer = QgsProcessingUtils.mapLayerFromString( results['INVALID_OUTPUT'], context) self.assertEqual(invalid_layer.fields().names()[-1], '_errors') self.assertEqual(invalid_layer.featureCount(), 1) f = next(invalid_layer.getFeatures()) self.assertEqual(f.attributes(), [1, 'Self-intersection'])
def processAlgorithm(self, parameters, context, feedback): inLayer = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.INPUT_LAYER), context) inField = self.getParameterValue(self.INPUT_FIELD) targetLayer = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.TARGET_LAYER), context) targetField = self.getParameterValue(self.TARGET_FIELD) matType = self.getParameterValue(self.MATRIX_TYPE) nPoints = self.getParameterValue(self.NEAREST_POINTS) outputFile = self.getOutputFromName(self.DISTANCE_MATRIX) if nPoints < 1: nPoints = QgsProcessingUtils.featureCount(targetLayer, context) self.writer = outputFile.getTableWriter([]) if matType == 0: # Linear distance matrix self.linearMatrix(context, inLayer, inField, targetLayer, targetField, matType, nPoints, feedback) elif matType == 1: # Standard distance matrix self.regularMatrix(context, inLayer, inField, targetLayer, targetField, nPoints, feedback) elif matType == 2: # Summary distance matrix self.linearMatrix(context, inLayer, inField, targetLayer, targetField, matType, nPoints, feedback)
def processAlgorithm(self, parameters, context, feedback): layerPoints = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.POINTS), context) layerHubs = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.HUBS), context) fieldName = self.getParameterValue(self.FIELD) units = self.UNITS[self.getParameterValue(self.UNIT)] if layerPoints.source() == layerHubs.source(): raise GeoAlgorithmExecutionException( self.tr('Same layer given for both hubs and spokes')) fields = layerPoints.fields() fields.append(QgsField('HubName', QVariant.String)) fields.append(QgsField('HubDist', QVariant.Double)) writer = self.getOutputFromName(self.OUTPUT).getVectorWriter(fields, QgsWkbTypes.LineString, layerPoints.crs(), context) index = QgsProcessingUtils.createSpatialIndex(layerHubs, context) distance = QgsDistanceArea() distance.setSourceCrs(layerPoints.crs()) distance.setEllipsoid(QgsProject.instance().ellipsoid()) # Scan source points, find nearest hub, and write to output file features = QgsProcessingUtils.getFeatures(layerPoints, context) total = 100.0 / layerPoints.featureCount() if layerPoints.featureCount() else 0 for current, f in enumerate(features): src = f.geometry().boundingBox().center() neighbors = index.nearestNeighbor(src, 1) ft = next(layerHubs.getFeatures(QgsFeatureRequest().setFilterFid(neighbors[0]).setSubsetOfAttributes([fieldName], layerHubs.fields()))) closest = ft.geometry().boundingBox().center() hubDist = distance.measureLine(src, closest) attributes = f.attributes() attributes.append(ft[fieldName]) if units == 'Feet': attributes.append(hubDist * 3.2808399) elif units == 'Miles': attributes.append(hubDist * 0.000621371192) elif units == 'Kilometers': attributes.append(hubDist / 1000.0) elif units != 'Meters': attributes.append(sqrt( pow(src.x() - closest.x(), 2.0) + pow(src.y() - closest.y(), 2.0))) else: attributes.append(hubDist) feat = QgsFeature() feat.setAttributes(attributes) feat.setGeometry(QgsGeometry.fromPolyline([src, closest])) writer.addFeature(feat, QgsFeatureSink.FastInsert) feedback.setProgress(int(current * total)) del writer
def processAlgorithm(self, parameters, context, feedback): filename = self.getParameterValue(self.INPUT) inputLayer = QgsProcessingUtils.mapLayerFromString(filename, context) method = self.getParameterValue(self.METHOD) filename2 = self.getParameterValue(self.INTERSECT) selectLayer = QgsProcessingUtils.mapLayerFromString(filename2, context) predicates = self.getParameterValue(self.PREDICATE) precision = self.getParameterValue(self.PRECISION) oldSelection = set(inputLayer.selectedFeatureIds()) inputLayer.removeSelection() index = QgsProcessingUtils.createSpatialIndex(inputLayer, context) if 'disjoint' in predicates: disjoinSet = [] for feat in QgsProcessingUtils.getFeatures(inputLayer, context): disjoinSet.append(feat.id()) geom = QgsGeometry() selectedSet = [] features = QgsProcessingUtils.getFeatures(selectLayer, context) total = 100.0 / QgsProcessingUtils.featureCount(selectLayer, context) for current, f in enumerate(features): geom = vector.snapToPrecision(f.geometry(), precision) bbox = geom.boundingBox() bbox.grow(0.51 * precision) intersects = index.intersects(bbox) request = QgsFeatureRequest().setFilterFids(intersects).setSubsetOfAttributes([]) for feat in inputLayer.getFeatures(request): tmpGeom = vector.snapToPrecision(feat.geometry(), precision) res = False for predicate in predicates: if predicate == 'disjoint': if tmpGeom.intersects(geom): try: disjoinSet.remove(feat.id()) except: pass # already removed else: res = getattr(tmpGeom, predicate)(geom) if res: selectedSet.append(feat.id()) break feedback.setProgress(int(current * total)) if 'disjoint' in predicates: selectedSet = selectedSet + disjoinSet if method == 1: selectedSet = list(oldSelection.union(selectedSet)) elif method == 2: selectedSet = list(oldSelection.difference(selectedSet)) inputLayer.selectByIds(selectedSet) self.setOutputValue(self.OUTPUT, filename)
def processAlgorithm(self, parameters, context, feedback): polyLayer = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.POLYGONS), context) pointLayer = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.POINTS), context) fieldName = self.getParameterValue(self.FIELD) fieldIdx = pointLayer.fields().lookupField(self.getParameterValue(self.WEIGHT)) fields = polyLayer.fields() fields.append(QgsField(fieldName, QVariant.Int)) (idxCount, fieldList) = vector.findOrCreateField(polyLayer, polyLayer.fields(), fieldName) writer = self.getOutputFromName(self.OUTPUT).getVectorWriter(fields, polyLayer.wkbType(), polyLayer.crs(), context) spatialIndex = QgsProcessingUtils.createSpatialIndex(pointLayer, context) ftPoint = QgsFeature() outFeat = QgsFeature() geom = QgsGeometry() features = QgsProcessingUtils.getFeatures(polyLayer, context) total = 100.0 / QgsProcessingUtils.featureCount(polyLayer, context) for current, ftPoly in enumerate(features): geom = ftPoly.geometry() engine = QgsGeometry.createGeometryEngine(geom.geometry()) engine.prepareGeometry() attrs = ftPoly.attributes() count = 0 points = spatialIndex.intersects(geom.boundingBox()) if len(points) > 0: feedback.setProgressText(str(len(points))) request = QgsFeatureRequest().setFilterFids(points).setSubsetOfAttributes([fieldIdx]) fit = pointLayer.getFeatures(request) ftPoint = QgsFeature() while fit.nextFeature(ftPoint): tmpGeom = QgsGeometry(ftPoint.geometry()) if engine.contains(tmpGeom.geometry()): weight = str(ftPoint.attributes()[fieldIdx]) try: count += float(weight) except: # Ignore fields with non-numeric values pass outFeat.setGeometry(geom) if idxCount == len(attrs): attrs.append(count) else: attrs[idxCount] = count outFeat.setAttributes(attrs) writer.addFeature(outFeat) feedback.setProgress(int(current * total)) del writer
def processAlgorithm(self, parameters, context, feedback): polyLayer = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.POLYGONS), context) pointLayer = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.POINTS), context) fieldName = self.getParameterValue(self.FIELD) classFieldName = self.getParameterValue(self.CLASSFIELD) fields = polyLayer.fields() fields.append(QgsField(fieldName, QVariant.Int)) classFieldIndex = pointLayer.fields().lookupField(classFieldName) (idxCount, fieldList) = vector.findOrCreateField(polyLayer, polyLayer.fields(), fieldName) writer = self.getOutputFromName(self.OUTPUT).getVectorWriter(fields, polyLayer.wkbType(), polyLayer.crs(), context) spatialIndex = QgsProcessingUtils.createSpatialIndex(pointLayer, context) ftPoint = QgsFeature() outFeat = QgsFeature() geom = QgsGeometry() features = QgsProcessingUtils.getFeatures(polyLayer, context) total = 100.0 / polyLayer.featureCount() if polyLayer.featureCount() else 0 for current, ftPoly in enumerate(features): geom = ftPoly.geometry() engine = QgsGeometry.createGeometryEngine(geom.geometry()) engine.prepareGeometry() attrs = ftPoly.attributes() classes = set() points = spatialIndex.intersects(geom.boundingBox()) if len(points) > 0: request = QgsFeatureRequest().setFilterFids(points).setSubsetOfAttributes([classFieldIndex]) fit = pointLayer.getFeatures(request) ftPoint = QgsFeature() while fit.nextFeature(ftPoint): tmpGeom = QgsGeometry(ftPoint.geometry()) if engine.contains(tmpGeom.geometry()): clazz = ftPoint.attributes()[classFieldIndex] if clazz not in classes: classes.add(clazz) outFeat.setGeometry(geom) if idxCount == len(attrs): attrs.append(len(classes)) else: attrs[idxCount] = len(classes) outFeat.setAttributes(attrs) writer.addFeature(outFeat, QgsFeatureSink.FastInsert) feedback.setProgress(int(current * total)) del writer
def processAlgorithm(self, parameters, context, feedback): vlayerA = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.INPUT), context) vlayerB = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.INPUT2), context) geomType = QgsWkbTypes.multiType(vlayerA.wkbType()) fields = vector.combineVectorFields(vlayerA, vlayerB) writer = self.getOutputFromName(self.OUTPUT).getVectorWriter(fields, geomType, vlayerA.crs(), context) outFeat = QgsFeature() index = QgsProcessingUtils.createSpatialIndex(vlayerB, context) selectionA = QgsProcessingUtils.getFeatures(vlayerA, context) total = 100.0 / vlayerA.featureCount() if vlayerA.featureCount() else 0 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(): raise GeoAlgorithmExecutionException( 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, QgsFeatureSink.FastInsert) except: raise GeoAlgorithmExecutionException( self.tr('Feature geometry error: One or more ' 'output features ignored due to invalid ' 'geometry.')) del writer
def processAlgorithm(self, parameters, context, feedback): filename = self.getParameterValue(self.INPUT) layer = QgsProcessingUtils.mapLayerFromString(filename, context) filename = self.getParameterValue(self.INTERSECT) selectLayer = QgsProcessingUtils.mapLayerFromString(filename, context) predicates = self.getParameterValue(self.PREDICATE) precision = self.getParameterValue(self.PRECISION) index = QgsProcessingUtils.createSpatialIndex(layer, context) output = self.getOutputFromName(self.OUTPUT) writer = output.getVectorWriter(layer.fields(), layer.wkbType(), layer.crs(), context) if 'disjoint' in predicates: disjoinSet = [] for feat in QgsProcessingUtils.getFeatures(layer, context): disjoinSet.append(feat.id()) selectedSet = [] features = QgsProcessingUtils.getFeatures(selectLayer, context) total = 100.0 / selectLayer.featureCount() if selectLayer.featureCount() else 0 for current, f in enumerate(features): geom = vector.snapToPrecision(f.geometry(), precision) bbox = geom.boundingBox() bbox.grow(0.51 * precision) intersects = index.intersects(bbox) request = QgsFeatureRequest().setFilterFids(intersects).setSubsetOfAttributes([]) for feat in layer.getFeatures(request): tmpGeom = vector.snapToPrecision(feat.geometry(), precision) res = False for predicate in predicates: if predicate == 'disjoint': if tmpGeom.intersects(geom): try: disjoinSet.remove(feat.id()) except: pass # already removed else: res = getattr(tmpGeom, predicate)(geom) if res: selectedSet.append(feat.id()) break feedback.setProgress(int(current * total)) if 'disjoint' in predicates: selectedSet = selectedSet + disjoinSet features = QgsProcessingUtils.getFeatures(layer, context) total = 100.0 / layer.featureCount() if layer.featureCount() else 0 for current, f in enumerate(features): if f.id() in selectedSet: writer.addFeature(f, QgsFeatureSink.FastInsert) feedback.setProgress(int(current * total)) del writer
def convertUnsupportedFormats(self, context, feedback): i = 0 feedback.setProgressText(self.tr('Converting outputs')) for out in self.outputs: if isinstance(out, OutputVector): if out.compatible is not None: layer = QgsProcessingUtils.mapLayerFromString(out.compatible, context) if layer is None: # For the case of memory layer, if the # getCompatible method has been called continue writer = out.getVectorWriter(layer.fields(), layer.wkbType(), layer.crs(), context) features = QgsProcessingUtils.getFeatures(layer, context) for feature in features: writer.addFeature(feature, QgsFeatureSink.FastInsert) elif isinstance(out, OutputRaster): if out.compatible is not None: layer = QgsProcessingUtils.mapLayerFromString(out.compatible, context) format = self.getFormatShortNameFromFilename(out.value) orgFile = out.compatible destFile = out.value crsid = layer.crs().authid() settings = QgsSettings() path = str(settings.value('/GdalTools/gdalPath', '')) envval = str(os.getenv('PATH')) if not path.lower() in envval.lower().split(os.pathsep): envval += '%s%s' % (os.pathsep, path) os.putenv('PATH', envval) command = 'gdal_translate -of %s -a_srs %s %s %s' % (format, crsid, orgFile, destFile) if os.name == 'nt': command = command.split(" ") else: command = [command] proc = subprocess.Popen( command, shell=True, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=False, ) proc.communicate() elif isinstance(out, OutputTable): if out.compatible is not None: layer = QgsProcessingUtils.mapLayerFromString(out.compatible, context) writer = out.getTableWriter(layer.fields()) features = QgsProcessingUtils.getFeatures(layer, context) for feature in features: writer.addRecord(feature) feedback.setProgress(100 * i / float(len(self.outputs)))
def getSafeExportedLayers(self): """ Returns not the value entered by the user, but a string with semicolon-separated filenames which contains the data of the selected layers, but saved in a standard format (currently shapefiles for vector layers and GeoTiff for raster) so that they can be opened by most external applications. If there is a selection and QGIS is configured to use just the selection, it exports the layer even if it is already in a suitable format. Works only if the layer represented by the parameter value is currently loaded in QGIS. Otherwise, it will not perform any export and return the current value string. If the current value represents a layer in a suitable format, it does no export at all and returns that value. Currently, it works just for vector layer. In the case of raster layers, it returns the parameter value. The layers are exported just the first time the method is called. The method can be called several times and it will always return the same string, performing the export only the first time. """ context = dataobjects.createContext() if self.exported: return self.exported self.exported = self.value layers = self.value.split(';') if layers is None or len(layers) == 0: return self.value if self.datatype == dataobjects.TYPE_RASTER: for layerfile in layers: layer = QgsProcessingUtils.mapLayerFromString(layerfile, context, False) if layer: filename = dataobjects.exportRasterLayer(layer) self.exported = self.exported.replace(layerfile, filename) return self.exported elif self.datatype == dataobjects.TYPE_FILE: return self.value else: for layerfile in layers: layer = QgsProcessingUtils.mapLayerFromString(layerfile, context, False) if layer: filename = dataobjects.exportVectorLayer(layer) self.exported = self.exported.replace(layerfile, filename) return self.exported
def checkExtentCRS(self): unmatchingCRS = False hasExtent = False context = dataobjects.createContext() projectCRS = iface.mapCanvas().mapSettings().destinationCrs() layers = QgsProcessingUtils.compatibleLayers(QgsProject.instance()) for param in self.algorithm().parameterDefinitions(): if isinstance(param, (ParameterRaster, ParameterVector, ParameterMultipleInput)): if param.value: if isinstance(param, ParameterMultipleInput): inputlayers = param.value.split(';') else: inputlayers = [param.value] for inputlayer in inputlayers: for layer in layers: if layer.source() == inputlayer: if layer.crs() != projectCRS: unmatchingCRS = True p = QgsProcessingUtils.mapLayerFromString(inputlayer, context) if p is not None: if p.crs() != projectCRS: unmatchingCRS = True if isinstance(param, ParameterExtent): if param.skip_crs_check: continue value = self.mainWidget().wrappers[param.name()].widget.leText.text().strip() if value: hasExtent = True return hasExtent and unmatchingCRS
def processAlgorithm(self, parameters, context, feedback): layer = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.INPUT_LAYER), context) writer = self.getOutputFromName( self.OUTPUT_LAYER).getVectorWriter(layer.fields(), layer.wkbType(), layer.crs(), context) features = QgsProcessingUtils.getFeatures(layer, context) total = 100.0 / layer.featureCount() if layer.featureCount() else 0 for current, inFeat in enumerate(features): outFeat = QgsFeature() attrs = inFeat.attributes() outFeat.setAttributes(attrs) inGeom = inFeat.geometry() if inGeom: outGeom = inGeom.mergeLines() if outGeom is None: raise GeoAlgorithmExecutionException( self.tr('Error merging lines')) outFeat.setGeometry(outGeom) writer.addFeature(outFeat, QgsFeatureSink.FastInsert) feedback.setProgress(int(current * total)) del writer
def getSafeExportedTable(self): """Returns not the value entered by the user, but a string with a filename which contains the data of this table, but saved in a standard format (currently always a DBF file) so that it can be opened by most external applications. Works only if the table represented by the parameter value is currently loaded in QGIS. Otherwise, it will not perform any export and return the current value string. If the current value represents a table in a suitable format, it does not export at all and returns that value. The table is exported just the first time the method is called. The method can be called several times and it will always return the same file, performing the export only the first time. """ context = dataobjects.createContext() if self.exported: return self.exported table = QgsProcessingUtils.mapLayerFromString(self.value, context, False) if table: self.exported = dataobjects.exportTable(table) else: self.exported = self.value return self.exported
def getSafeExportedLayer(self): """Returns not the value entered by the user, but a string with a filename which contains the data of this layer, but saved in a standard format (currently always a shapefile) so that it can be opened by most external applications. If there is a selection and QGIS is configured to use just the selection, if exports the layer even if it is already in a suitable format. Works only if the layer represented by the parameter value is currently loaded in QGIS. Otherwise, it will not perform any export and return the current value string. If the current value represents a layer in a suitable format, it does not export at all and returns that value. The layer is exported just the first time the method is called. The method can be called several times and it will always return the same file, performing the export only the first time. """ context = dataobjects.createContext() if self.exported: return self.exported layer = QgsProcessingUtils.mapLayerFromString(self.value, context, False) if layer: self.exported = dataobjects.exportVectorLayer(layer) else: self.exported = self.value return self.exported
def processAlgorithm(self, parameters, context, feedback): layer = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.INPUT), context) namefieldname = self.getParameterValue(self.NAME_FIELD) valuefieldname = self.getParameterValue(self.VALUE_FIELD) output = self.getOutputValue(self.OUTPUT) values = vector.values(layer, valuefieldname) x_var = [i[namefieldname] for i in layer.getFeatures()] msdIndex = self.getParameterValue(self.MSD) msd = True if msdIndex == 1: msd = 'sd' elif msdIndex == 2: msd = False data = [go.Box( x=x_var, y=values[valuefieldname], boxmean=msd)] plt.offline.plot(data, filename=output, auto_open=False)
def processAlgorithm(self, parameters, context, feedback): layer = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.INPUT), context) fields = layer.fields() fields.append(QgsField('node_index', QVariant.Int)) fields.append(QgsField('distance', QVariant.Double)) fields.append(QgsField('angle', QVariant.Double)) writer = self.getOutputFromName(self.OUTPUT).getVectorWriter(fields, QgsWkbTypes.Point, layer.crs(), context) features = QgsProcessingUtils.getFeatures(layer, context) total = 100.0 / QgsProcessingUtils.featureCount(layer, context) for current, f in enumerate(features): input_geometry = f.geometry() if not input_geometry: writer.addFeature(f) else: points = vector.extractPoints(input_geometry) for i, point in enumerate(points): distance = input_geometry.distanceToVertex(i) angle = math.degrees(input_geometry.angleAtVertex(i)) attrs = f.attributes() attrs.append(i) attrs.append(distance) attrs.append(angle) output_feature = QgsFeature() output_feature.setAttributes(attrs) output_feature.setGeometry(QgsGeometry.fromPoint(point)) writer.addFeature(output_feature) feedback.setProgress(int(current * total)) del writer
def processAlgorithm(self, parameters, context, feedback): layer = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.INPUT_VECTOR), context) rasterPath = str(self.getParameterValue(self.INPUT_RASTER)) 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)) writer = self.getOutputFromName(self.OUTPUT_LAYER).getVectorWriter(fields, QgsWkbTypes.Point, layer.crs(), context) outFeature = QgsFeature() outFeature.setFields(fields) self.fid = 0 self.lineId = 0 self.pointId = 0 features = QgsProcessingUtils.getFeatures(layer, context) total = 100.0 / layer.featureCount() if layer.featureCount() else 0 for current, f in enumerate(features): 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, writer, 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, writer, outFeature) self.pointId = 0 self.lineId += 1 feedback.setProgress(int(current * total)) del writer
def processAlgorithm(self, parameters, context, feedback): layer = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.INPUT_LAYER), context) valuesFieldName = self.getParameterValue(self.VALUES_FIELD_NAME) categoriesFieldName = self.getParameterValue(self.CATEGORIES_FIELD_NAME) output = self.getOutputFromName(self.OUTPUT) valuesField = layer.fields().lookupField(valuesFieldName) categoriesField = layer.fields().lookupField(categoriesFieldName) features = QgsProcessingUtils.getFeatures(layer, context) total = 100.0 / QgsProcessingUtils.featureCount(layer, context) values = {} for current, feat in enumerate(features): feedback.setProgress(int(current * total)) attrs = feat.attributes() try: value = float(attrs[valuesField]) cat = str(attrs[categoriesField]) if cat not in values: values[cat] = [] values[cat].append(value) except: pass fields = ['category', 'min', 'max', 'mean', 'stddev', 'sum', 'count'] writer = output.getTableWriter(fields) stat = QgsStatisticalSummary(QgsStatisticalSummary.Min | QgsStatisticalSummary.Max | QgsStatisticalSummary.Mean | QgsStatisticalSummary.StDevSample | QgsStatisticalSummary.Sum | QgsStatisticalSummary.Count) for (cat, v) in list(values.items()): stat.calculate(v) record = [cat, stat.min(), stat.max(), stat.mean(), stat.sampleStDev(), stat.sum(), stat.count()] writer.addRecord(record)
def setOutputCRS(self): context = dataobjects.createContext() layers = QgsProcessingUtils.compatibleLayers(QgsProject.instance()) for param in self.parameterDefinitions(): if isinstance(param, (ParameterRaster, ParameterVector, ParameterMultipleInput)): if param.value: if isinstance(param, ParameterMultipleInput): inputlayers = param.value.split(';') else: inputlayers = [param.value] for inputlayer in inputlayers: for layer in layers: if layer.source() == inputlayer: self.crs = layer.crs() return p = QgsProcessingUtils.mapLayerFromString(inputlayer, context) if p is not None: self.crs = p.crs() p = None return try: from qgis.utils import iface if iface is not None: self.crs = iface.mapCanvas().mapSettings().destinationCrs() except: pass
def processAlgorithm(self, parameters, context, feedback): fieldname = self.getParameterValue(self.FIELD) output = self.getOutputFromName(self.OUTPUT) vlayer = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.INPUT), context) fieldindex = vlayer.fields().lookupField(fieldname) fields = vlayer.fields() fields.append(QgsField('NUM_FIELD', QVariant.Int)) writer = output.getVectorWriter(fields, vlayer.wkbType(), vlayer.crs(), context) outFeat = QgsFeature() classes = {} features = QgsProcessingUtils.getFeatures(vlayer, context) total = 100.0 / QgsProcessingUtils.featureCount(vlayer, context) for current, feature in enumerate(features): feedback.setProgress(int(current * total)) inGeom = feature.geometry() outFeat.setGeometry(inGeom) atMap = feature.attributes() clazz = atMap[fieldindex] if clazz not in classes: classes[clazz] = len(list(classes.keys())) atMap.append(classes[clazz]) outFeat.setAttributes(atMap) writer.addFeature(outFeat) del writer
def processAlgorithm(self, parameters, context, feedback): fileName = self.getParameterValue(self.INPUT) layer = QgsProcessingUtils.mapLayerFromString(fileName, context) crs = QgsCoordinateReferenceSystem(self.getParameterValue(self.CRS)) provider = layer.dataProvider() ds = provider.dataSourceUri() p = re.compile('\|.*') dsPath = p.sub('', ds) if dsPath.lower().endswith('.shp'): dsPath = dsPath[:-4] wkt = crs.toWkt() with open(dsPath + '.prj', 'w') as f: f.write(wkt) qpjFile = dsPath + '.qpj' if os.path.exists(qpjFile): with open(qpjFile, 'w') as f: f.write(wkt) layer.setCrs(crs) iface.mapCanvas().refresh() self.setOutputValue(self.OUTPUT, fileName)
def getLayerFromValue(self, value): context = createContext() if isinstance(value, QgsProcessingFeatureSourceDefinition): value, ok = value.source.valueAsString(context.expressionContext()) if isinstance(value, str): value = QgsProcessingUtils.mapLayerFromString(value, context) return value
def processAlgorithm(self, parameters, context, feedback): layer = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.INPUT_LAYER), context) byFeature = self.getParameterValue(self.BY_FEATURE) if byFeature and layer.geometryType() == QgsWkbTypes.PointGeometry and layer.featureCount() <= 2: raise GeoAlgorithmExecutionException(self.tr("Can't calculate an OMBB for each point, it's a point. The number of points must be greater than 2")) if byFeature: fields = layer.fields() else: fields = QgsFields() fields.append(QgsField('area', QVariant.Double)) fields.append(QgsField('perimeter', QVariant.Double)) fields.append(QgsField('angle', QVariant.Double)) fields.append(QgsField('width', QVariant.Double)) fields.append(QgsField('height', QVariant.Double)) writer = self.getOutputFromName(self.OUTPUT).getVectorWriter(fields, QgsWkbTypes.Polygon, layer.crs(), context) if byFeature: self.featureOmbb(layer, context, writer, feedback) else: self.layerOmmb(layer, context, writer, feedback) del writer
def processAlgorithm(self, parameters, context, feedback): layer = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.INPUT_LAYER), context) writer = self.getOutputFromName( self.OUTPUT_LAYER).getVectorWriter(layer.fields(), QgsWkbTypes.Polygon, layer.crs(), context) distance = self.getParameterValue(self.DISTANCE) segments = int(self.getParameterValue(self.SEGMENTS)) join_style = self.getParameterValue(self.JOIN_STYLE) + 1 if self.getParameterValue(self.SIDE) == 0: side = QgsGeometry.SideLeft else: side = QgsGeometry.SideRight miter_limit = self.getParameterValue(self.MITRE_LIMIT) features = QgsProcessingUtils.getFeatures(layer, context) total = 100.0 / layer.featureCount() if layer.featureCount() else 0 for current, input_feature in enumerate(features): output_feature = input_feature input_geometry = input_feature.geometry() if input_geometry: output_geometry = input_geometry.singleSidedBuffer(distance, segments, side, join_style, miter_limit) if not output_geometry: raise GeoAlgorithmExecutionException( self.tr('Error calculating single sided buffer')) output_feature.setGeometry(output_geometry) writer.addFeature(output_feature, QgsFeatureSink.FastInsert) feedback.setProgress(int(current * total)) del writer
def checkParameterValues(self, parameters, context): """ We check that there are no multiband layers, which are not supported by SAGA, and that raster layers have the same grid extent """ extent = None for param in self.parameterDefinitions(): files = [] if isinstance(param, ParameterRaster): files = [parameters[param.name()]] elif (isinstance(param, ParameterMultipleInput) and param.datatype == ParameterMultipleInput.TYPE_RASTER): if param.value is not None: files = param.value.split(";") for f in files: layer = QgsProcessingUtils.mapLayerFromString(f, context) if layer is None: continue if layer.bandCount() > 1: return False, self.tr('Input layer {0} has more than one band.\n' 'Multiband layers are not supported by SAGA').format(layer.name()) if not self.allowUnmatchingGridExtents: if extent is None: extent = (layer.extent(), layer.height(), layer.width()) else: extent2 = (layer.extent(), layer.height(), layer.width()) if extent != extent2: return False, self.tr("Input layers do not have the same grid extent.") return super(SagaAlgorithm, self).checkParameterValues(parameters, context)
def processAlgorithm(self, parameters, context, feedback): layer = QgsProcessingUtils.mapLayerFromString( self.getParameterValue(self.INPUT_VECTOR), context) startPoint = self.getParameterValue(self.START_POINT) endPoints = QgsProcessingUtils.mapLayerFromString( self.getParameterValue(self.END_POINTS), context) strategy = self.getParameterValue(self.STRATEGY) directionFieldName = self.getParameterValue(self.DIRECTION_FIELD) forwardValue = self.getParameterValue(self.VALUE_FORWARD) backwardValue = self.getParameterValue(self.VALUE_BACKWARD) bothValue = self.getParameterValue(self.VALUE_BOTH) defaultDirection = self.getParameterValue(self.DEFAULT_DIRECTION) bothValue = self.getParameterValue(self.VALUE_BOTH) defaultDirection = self.getParameterValue(self.DEFAULT_DIRECTION) speedFieldName = self.getParameterValue(self.SPEED_FIELD) defaultSpeed = self.getParameterValue(self.DEFAULT_SPEED) tolerance = self.getParameterValue(self.TOLERANCE) fields = QgsFields() 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) writer = self.getOutputFromName(self.OUTPUT_LAYER).getVectorWriter( fields, QgsWkbTypes.LineString, layer.crs(), context) tmp = startPoint.split(',') startPoint = QgsPointXY(float(tmp[0]), float(tmp[1])) directionField = -1 if directionFieldName is not None: directionField = layer.fields().lookupField(directionFieldName) speedField = -1 if speedFieldName is not None: speedField = layer.fields().lookupField(speedFieldName) director = QgsVectorLayerDirector(layer, directionField, forwardValue, backwardValue, bothValue, defaultDirection) distUnit = iface.mapCanvas().mapSettings().destinationCrs().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( iface.mapCanvas().mapSettings().destinationCrs(), True, tolerance) feedback.pushInfo(self.tr('Loading end points...')) request = QgsFeatureRequest() request.setFlags(request.flags() ^ QgsFeatureRequest.SubsetOfAttributes) features = QgsProcessingUtils.getFeatures(endPoints, context, request) count = QgsProcessingUtils.featureCount(endPoints, context) points = [startPoint] for f in features: points.append(f.geometry().asPoint()) feedback.pushInfo(self.tr('Building graph...')) snappedPoints = director.makeGraph(builder, points) feedback.pushInfo(self.tr('Calculating shortest paths...')) graph = builder.graph() idxStart = graph.findVertex(snappedPoints[0]) tree, cost = QgsGraphAnalyzer.dijkstra(graph, idxStart, 0) route = [] total = 100.0 / count if count else 1 for i in range(1, count + 1): idxEnd = graph.findVertex(snappedPoints[i]) if tree[idxEnd] == -1: msg = self.tr( 'There is no route from start point ({}) to end point ({}).' .format(startPoint.toString(), points[i].toString())) feedback.setProgressText(msg) QgsMessageLog.logMessage(msg, self.tr('Processing'), QgsMessageLog.WARNING) continue cost = 0.0 current = idxEnd while current != idxStart: cost += graph.edge(tree[current]).cost(0) route.append( graph.vertex(graph.edge(tree[current]).inVertex()).point()) current = graph.edge(tree[current]).outVertex() route.append(snappedPoints[0]) route.reverse() geom = QgsGeometry.fromPolyline(route) feat.setGeometry(geom) feat['start'] = startPoint.toString() feat['end'] = points[i].toString() feat['cost'] = cost / multiplier writer.addFeature(feat, QgsFeatureSink.FastInsert) route[:] = [] feedback.setProgress(int(i * total)) del writer
def processAlgorithm(self, parameters, context, feedback): layer = QgsProcessingUtils.mapLayerFromString( self.getParameterValue(self.INPUT_VECTOR), context) startPoint = self.getParameterValue(self.START_POINT) strategy = self.getParameterValue(self.STRATEGY) travelCost = self.getParameterValue(self.TRAVEL_COST) directionFieldName = self.getParameterValue(self.DIRECTION_FIELD) forwardValue = self.getParameterValue(self.VALUE_FORWARD) backwardValue = self.getParameterValue(self.VALUE_BACKWARD) bothValue = self.getParameterValue(self.VALUE_BOTH) defaultDirection = self.getParameterValue(self.DEFAULT_DIRECTION) bothValue = self.getParameterValue(self.VALUE_BOTH) defaultDirection = self.getParameterValue(self.DEFAULT_DIRECTION) speedFieldName = self.getParameterValue(self.SPEED_FIELD) defaultSpeed = self.getParameterValue(self.DEFAULT_SPEED) tolerance = self.getParameterValue(self.TOLERANCE) tmp = startPoint.split(',') startPoint = QgsPointXY(float(tmp[0]), float(tmp[1])) directionField = -1 if directionFieldName is not None: directionField = layer.fields().lookupField(directionFieldName) speedField = -1 if speedFieldName is not None: speedField = layer.fields().lookupField(speedFieldName) director = QgsVectorLayerDirector(layer, directionField, forwardValue, backwardValue, bothValue, defaultDirection) distUnit = iface.mapCanvas().mapSettings().destinationCrs().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( iface.mapCanvas().mapSettings().destinationCrs(), True, tolerance) feedback.pushInfo(self.tr('Building graph...')) snappedPoints = director.makeGraph(builder, [startPoint]) feedback.pushInfo(self.tr('Calculating service area...')) graph = builder.graph() idxStart = graph.findVertex(snappedPoints[0]) tree, cost = QgsGraphAnalyzer.dijkstra(graph, idxStart, 0) vertices = [] for i, v in enumerate(cost): if v > travelCost and tree[i] != -1: vertexId = graph.edge(tree[i]).outVertex() if cost[vertexId] <= travelCost: vertices.append(i) upperBoundary = [] lowerBoundary = [] for i in vertices: upperBoundary.append( graph.vertex(graph.edge(tree[i]).inVertex()).point()) lowerBoundary.append( graph.vertex(graph.edge(tree[i]).outVertex()).point()) feedback.pushInfo(self.tr('Writing results...')) fields = QgsFields() fields.append(QgsField('type', QVariant.String, '', 254, 0)) fields.append(QgsField('start', QVariant.String, '', 254, 0)) feat = QgsFeature() feat.setFields(fields) geomUpper = QgsGeometry.fromMultiPoint(upperBoundary) geomLower = QgsGeometry.fromMultiPoint(lowerBoundary) writer = self.getOutputFromName(self.OUTPUT_POINTS).getVectorWriter( fields, QgsWkbTypes.MultiPoint, layer.crs(), context) feat.setGeometry(geomUpper) feat['type'] = 'upper' feat['start'] = startPoint.toString() writer.addFeature(feat) feat.setGeometry(geomLower) feat['type'] = 'lower' feat['start'] = startPoint.toString() writer.addFeature(feat) del writer upperBoundary.append(startPoint) lowerBoundary.append(startPoint) geomUpper = QgsGeometry.fromMultiPoint(upperBoundary) geomLower = QgsGeometry.fromMultiPoint(lowerBoundary) writer = self.getOutputFromName(self.OUTPUT_POLYGON).getVectorWriter( fields, QgsWkbTypes.Polygon, layer.crs(), context) geom = geomUpper.convexHull() feat.setGeometry(geom) feat['type'] = 'upper' feat['start'] = startPoint.toString() writer.addFeature(feat) geom = geomLower.convexHull() feat.setGeometry(geom) feat['type'] = 'lower' feat['start'] = startPoint.toString() writer.addFeature(feat) del writer
def processAlgorithm(self, context, feedback): useField = not self.getParameterValue(Dissolve.DISSOLVE_ALL) field_names = self.getParameterValue(Dissolve.FIELD) vlayerA = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(Dissolve.INPUT), context) writer = self.getOutputFromName( Dissolve.OUTPUT).getVectorWriter(vlayerA.fields(), vlayerA.wkbType(), vlayerA.crs(), context) outFeat = QgsFeature() features = QgsProcessingUtils.getFeatures(vlayerA, context) total = 100.0 / QgsProcessingUtils.featureCount(vlayerA, context) if not useField: first = True # we dissolve geometries in blocks using unaryUnion geom_queue = [] for current, inFeat in enumerate(features): feedback.setProgress(int(current * total)) if first: outFeat.setAttributes(inFeat.attributes()) first = False tmpInGeom = inFeat.geometry() if tmpInGeom.isNull() or tmpInGeom.isEmpty(): continue errors = tmpInGeom.validateGeometry() if len(errors) != 0: for error in errors: QgsMessageLog.logMessage(self.tr('ValidateGeometry()' 'error: One or more ' 'input features have ' 'invalid geometry: ') + error.what(), self.tr('Processing'), QgsMessageLog.CRITICAL) continue geom_queue.append(tmpInGeom) if len(geom_queue) > 10000: # queue too long, combine it try: temp_output_geometry = QgsGeometry.unaryUnion(geom_queue) geom_queue = [temp_output_geometry] except: raise GeoAlgorithmExecutionException( self.tr('Geometry exception while dissolving')) try: outFeat.setGeometry(QgsGeometry.unaryUnion(geom_queue)) except: raise GeoAlgorithmExecutionException( self.tr('Geometry exception while dissolving')) writer.addFeature(outFeat) else: field_indexes = [vlayerA.fields().lookupField(f) for f in field_names.split(';')] attribute_dict = {} geometry_dict = defaultdict(lambda: []) for inFeat in features: attrs = inFeat.attributes() index_attrs = tuple([attrs[i] for i in field_indexes]) tmpInGeom = QgsGeometry(inFeat.geometry()) if tmpInGeom and tmpInGeom.isEmpty(): continue errors = tmpInGeom.validateGeometry() if len(errors) != 0: for error in errors: QgsMessageLog.logMessage(self.tr('ValidateGeometry() ' 'error: One or more input' 'features have invalid ' 'geometry: ') + error.what(), self.tr('Processing'), QgsMessageLog.CRITICAL) if index_attrs not in attribute_dict: # keep attributes of first feature attribute_dict[index_attrs] = attrs geometry_dict[index_attrs].append(tmpInGeom) nFeat = len(attribute_dict) nElement = 0 for key, value in list(geometry_dict.items()): outFeat = QgsFeature() nElement += 1 feedback.setProgress(int(nElement * 100 / nFeat)) try: tmpOutGeom = QgsGeometry.unaryUnion(value) except: raise GeoAlgorithmExecutionException( self.tr('Geometry exception while dissolving')) outFeat.setGeometry(tmpOutGeom) outFeat.setAttributes(attribute_dict[key]) writer.addFeature(outFeat) del writer
def processAlgorithm(self, parameters, context, feedback): fieldName = self.getParameterValue(self.FIELD_NAME) fieldType = self.getParameterValue(self.FIELD_TYPE) fieldLength = self.getParameterValue(self.FIELD_LENGTH) fieldPrecision = self.getParameterValue(self.FIELD_PRECISION) code = self.getParameterValue(self.FORMULA) globalExpression = self.getParameterValue(self.GLOBAL) output = self.getOutputFromName(self.OUTPUT_LAYER) layer = QgsProcessingUtils.mapLayerFromString( self.getParameterValue(self.INPUT_LAYER), context) fields = layer.fields() fields.append( QgsField(fieldName, self.TYPES[fieldType], '', fieldLength, fieldPrecision)) writer = output.getVectorWriter(fields, layer.wkbType(), layer.crs(), context) outFeat = QgsFeature() new_ns = {} # Run global code if globalExpression.strip() != '': try: bytecode = compile(globalExpression, '<string>', 'exec') exec(bytecode, new_ns) except: raise GeoAlgorithmExecutionException( self. tr("FieldPyculator code execute error.Global code block can't be executed!\n{0}\n{1}" ).format(str(sys.exc_info()[0].__name__), str(sys.exc_info()[1]))) # Replace all fields tags fields = layer.fields() num = 0 for field in fields: field_name = str(field.name()) replval = '__attr[' + str(num) + ']' code = code.replace('<' + field_name + '>', replval) num += 1 # Replace all special vars code = code.replace('$id', '__id') code = code.replace('$geom', '__geom') need_id = code.find('__id') != -1 need_geom = code.find('__geom') != -1 need_attrs = code.find('__attr') != -1 # Compile try: bytecode = compile(code, '<string>', 'exec') except: raise GeoAlgorithmExecutionException( self. tr("FieldPyculator code execute error. Field code block can't be executed!\n{0}\n{1}" ).format(str(sys.exc_info()[0].__name__), str(sys.exc_info()[1]))) # Run features = QgsProcessingUtils.getFeatures(layer, context) total = 100.0 / layer.featureCount() if layer.featureCount() else 0 for current, feat in enumerate(features): feedback.setProgress(int(current * total)) attrs = feat.attributes() feat_id = feat.id() # Add needed vars if need_id: new_ns['__id'] = feat_id if need_geom: geom = feat.geometry() new_ns['__geom'] = geom if need_attrs: pyattrs = [a for a in attrs] new_ns['__attr'] = pyattrs # Clear old result if self.RESULT_VAR_NAME in new_ns: del new_ns[self.RESULT_VAR_NAME] # Exec exec(bytecode, new_ns) # Check result if self.RESULT_VAR_NAME not in new_ns: raise GeoAlgorithmExecutionException( self.tr( "FieldPyculator code execute error\n" "Field code block does not return '{0}' variable! " "Please declare this variable in your code!").format( self.RESULT_VAR_NAME)) # Write feature outFeat.setGeometry(feat.geometry()) attrs.append(new_ns[self.RESULT_VAR_NAME]) outFeat.setAttributes(attrs) writer.addFeature(outFeat, QgsFeatureSink.FastInsert) del writer
def processAlgorithm(self, parameters, context, feedback): layer = QgsProcessingUtils.mapLayerFromString( self.getParameterValue(self.INPUT), context) method = self.getParameterValue(self.METHOD) geometryType = layer.geometryType() fields = layer.fields() export_z = False export_m = False if geometryType == QgsWkbTypes.PolygonGeometry: areaName = vector.createUniqueFieldName('area', fields) fields.append(QgsField(areaName, QVariant.Double)) perimeterName = vector.createUniqueFieldName('perimeter', fields) fields.append(QgsField(perimeterName, QVariant.Double)) elif geometryType == QgsWkbTypes.LineGeometry: lengthName = vector.createUniqueFieldName('length', fields) fields.append(QgsField(lengthName, QVariant.Double)) else: xName = vector.createUniqueFieldName('xcoord', fields) fields.append(QgsField(xName, QVariant.Double)) yName = vector.createUniqueFieldName('ycoord', fields) fields.append(QgsField(yName, QVariant.Double)) if QgsWkbTypes.hasZ(layer.wkbType()): export_z = True zName = vector.createUniqueFieldName('zcoord', fields) fields.append(QgsField(zName, QVariant.Double)) if QgsWkbTypes.hasM(layer.wkbType()): export_m = True zName = vector.createUniqueFieldName('mvalue', fields) fields.append(QgsField(zName, QVariant.Double)) writer = self.getOutputFromName(self.OUTPUT).getVectorWriter( fields, layer.wkbType(), layer.crs(), context) ellips = None crs = None coordTransform = None # Calculate with: # 0 - layer CRS # 1 - project CRS # 2 - ellipsoidal if method == 2: ellips = QgsProject.instance().ellipsoid() crs = layer.crs().srsid() elif method == 1: mapCRS = iface.mapCanvas().mapSettings().destinationCrs() layCRS = layer.crs() coordTransform = QgsCoordinateTransform(layCRS, mapCRS) outFeat = QgsFeature() outFeat.initAttributes(len(fields)) outFeat.setFields(fields) features = QgsProcessingUtils.getFeatures(layer, context) total = 100.0 / QgsProcessingUtils.featureCount(layer, context) for current, f in enumerate(features): inGeom = f.geometry() if method == 1: inGeom.transform(coordTransform) (attr1, attr2) = vector.simpleMeasure(inGeom, method, ellips, crs) outFeat.setGeometry(inGeom) attrs = f.attributes() attrs.append(attr1) if attr2 is not None: attrs.append(attr2) # add point z/m if export_z: attrs.append(inGeom.geometry().z()) if export_m: attrs.append(inGeom.geometry().m()) outFeat.setAttributes(attrs) writer.addFeature(outFeat) feedback.setProgress(int(current * total)) del writer
def processAlgorithm(self, context, feedback): polyLayer = QgsProcessingUtils.mapLayerFromString( self.getParameterValue(self.POLYGONS), context) pointLayer = QgsProcessingUtils.mapLayerFromString( self.getParameterValue(self.POINTS), context) fieldName = self.getParameterValue(self.FIELD) fieldIdx = pointLayer.fields().lookupField( self.getParameterValue(self.WEIGHT)) fields = polyLayer.fields() fields.append(QgsField(fieldName, QVariant.Int)) (idxCount, fieldList) = vector.findOrCreateField(polyLayer, polyLayer.fields(), fieldName) writer = self.getOutputFromName(self.OUTPUT).getVectorWriter( fields, polyLayer.wkbType(), polyLayer.crs(), context) spatialIndex = QgsProcessingUtils.createSpatialIndex( pointLayer, context) ftPoint = QgsFeature() outFeat = QgsFeature() geom = QgsGeometry() features = QgsProcessingUtils.getFeatures(polyLayer, context) total = 100.0 / QgsProcessingUtils.featureCount(polyLayer, context) for current, ftPoly in enumerate(features): geom = ftPoly.geometry() engine = QgsGeometry.createGeometryEngine(geom.geometry()) engine.prepareGeometry() attrs = ftPoly.attributes() count = 0 points = spatialIndex.intersects(geom.boundingBox()) if len(points) > 0: feedback.setProgressText(str(len(points))) request = QgsFeatureRequest().setFilterFids( points).setSubsetOfAttributes([fieldIdx]) fit = pointLayer.getFeatures(request) ftPoint = QgsFeature() while fit.nextFeature(ftPoint): tmpGeom = QgsGeometry(ftPoint.geometry()) if engine.contains(tmpGeom.geometry()): weight = str(ftPoint.attributes()[fieldIdx]) try: count += float(weight) except: # Ignore fields with non-numeric values pass outFeat.setGeometry(geom) if idxCount == len(attrs): attrs.append(count) else: attrs[idxCount] = count outFeat.setAttributes(attrs) writer.addFeature(outFeat) feedback.setProgress(int(current * total)) del writer
def processAlgorithm(self, context, feedback): source_layer = QgsProcessingUtils.mapLayerFromString( self.getParameterValue(Clip.INPUT), context) mask_layer = QgsProcessingUtils.mapLayerFromString( self.getParameterValue(Clip.OVERLAY), context) writer = self.getOutputFromName(self.OUTPUT).getVectorWriter( source_layer.fields(), QgsWkbTypes.multiType(source_layer.wkbType()), source_layer.crs(), context) # first build up a list of clip geometries clip_geoms = [] for maskFeat in QgsProcessingUtils.getFeatures( mask_layer, context, QgsFeatureRequest().setSubsetOfAttributes([])): clip_geoms.append(maskFeat.geometry()) # are we clipping against a single feature? if so, we can show finer progress reports if len(clip_geoms) > 1: combined_clip_geom = QgsGeometry.unaryUnion(clip_geoms) single_clip_feature = False else: combined_clip_geom = clip_geoms[0] single_clip_feature = True # use prepared geometries for faster intersection tests engine = QgsGeometry.createGeometryEngine( combined_clip_geom.geometry()) engine.prepareGeometry() tested_feature_ids = set() for i, clip_geom in enumerate(clip_geoms): input_features = [ f for f in QgsProcessingUtils.getFeatures( source_layer, context, QgsFeatureRequest().setFilterRect(clip_geom.boundingBox())) ] if not input_features: continue if single_clip_feature: total = 100.0 / len(input_features) else: total = 0 for current, in_feat in enumerate(input_features): if not in_feat.geometry(): continue if in_feat.id() in tested_feature_ids: # don't retest a feature we have already checked continue tested_feature_ids.add(in_feat.id()) if not engine.intersects(in_feat.geometry().geometry()): continue if not engine.contains(in_feat.geometry().geometry()): cur_geom = in_feat.geometry() new_geom = combined_clip_geom.intersection(cur_geom) if new_geom.wkbType( ) == QgsWkbTypes.Unknown or QgsWkbTypes.flatType( new_geom.geometry().wkbType( )) == QgsWkbTypes.GeometryCollection: int_com = in_feat.geometry().combine(new_geom) int_sym = in_feat.geometry().symDifference(new_geom) new_geom = int_com.difference(int_sym) else: # clip geometry totally contains feature geometry, so no need to perform intersection new_geom = in_feat.geometry() try: out_feat = QgsFeature() out_feat.setGeometry(new_geom) out_feat.setAttributes(in_feat.attributes()) writer.addFeature(out_feat) except: QgsMessageLog.logMessage( self.tr('Feature geometry error: One or more ' 'output features ignored due to ' 'invalid geometry.'), self.tr('Processing'), QgsMessageLog.CRITICAL) continue if single_clip_feature: feedback.setProgress(int(current * total)) if not single_clip_feature: # coarse progress report for multiple clip geometries feedback.setProgress(100.0 * i / len(clip_geoms)) del writer
def processAlgorithm(self, parameters, context, feedback): cadastre = self.parameterAsVectorLayer(parameters, self.LAYER, context) role = self.parameterAsVectorLayer(parameters, self.ROLE, context) count = 0 feedback = QgsProcessingMultiStepFeedback(1, feedback) results = {} outputs = {} fields_test = { 'clef_2': 'asa_id_parcelle', 'nom_asa': 'nom_asa', 'souscrite': 'asa_droitdeau', 'cod_reseau': 'asa_cod_reseau', 'reseau': 'asa_reseau', 'num_propri': 'asa_num_adherent', 'proprio': 'asa_nom', 'pro_adr1': 'asa_adresse', 'pro_adr2': 'asa_adresse2', 'pro_ville': 'asa_commune', 'pro_cp': 'asa_cp', 'cod_statio': 'asa_cod_station', 'station': 'asa_station', 'cod_tarif': 'asa_cod_tarif', 'tarif': 'asa_tarif', 'cod_cultur': 'asa_cod_culture', 'culture': 'asa_culture' } fields_needed = copy.deepcopy(fields_test) number_item = len(fields_test) for field in role.fields(): f = field.name().lower() if f in fields_test.keys(): count += 1 del fields_needed[f] if count < number_item: msg = 'Erreur dans les champs de la couche de jointure, il manque : ' msg += ', '.join(fields_needed.keys()) results[self.OUTPUT_STRING] = msg return results alg_params = { 'DISCARD_NONMATCHING': True, 'FIELD': 'idu', 'FIELDS_TO_COPY': None, 'FIELD_2': 'CLEF_2', 'INPUT': cadastre, 'INPUT_2': role, 'METHOD': 1, 'PREFIX': 'asa_tmp_', 'OUTPUT': 'memory:' } outputs['JoinAttributesByFieldValue'] = processing.run( 'native:joinattributestable', alg_params, context=context, feedback=feedback, is_child_algorithm=True) layerPath = outputs['JoinAttributesByFieldValue']['OUTPUT'] layer = QgsProcessingUtils.mapLayerFromString(layerPath, context=context) feedback.pushInfo('### RENOMMAGE DES CHAMPS ###') refactor_fields = { 'FIELDS_MAPPING': [], 'INPUT': layer, 'OUTPUT': parameters[self.FEATURE_SINK] } asa_fields_needed = [] cad_field_mapping = [] asa_fields_mapping = [] for field in layer.fields(): new_name = '' start_field = field.name().lower() if start_field.startswith('asa_tmp_'): new_field = start_field.replace('asa_tmp_', '') if new_field in fields_test.keys(): new_name = fields_test[new_field] asa_fields_needed.append({ 'expression': '"' + start_field + '"', 'length': field.length(), 'name': new_name, 'precision': field.precision(), 'type': field.type() }) elif new_field == 'commune': new_name = 'asa_commune_2' asa_fields_mapping.append({ 'expression': '"' + start_field + '"', 'length': field.length(), 'name': new_name, 'precision': field.precision(), 'type': field.type() }) else: new_name = 'asa_' + new_field asa_fields_mapping.append({ 'expression': '"' + start_field + '"', 'length': field.length(), 'name': new_name, 'precision': field.precision(), 'type': field.type() }) else: new_name = 'cad_' + start_field cad_field_mapping.append({ 'expression': '"' + start_field + '"', 'length': field.length(), 'name': new_name, 'precision': field.precision(), 'type': field.type() }) refactor_fields['FIELDS_MAPPING'].extend(cad_field_mapping) refactor_fields['FIELDS_MAPPING'].extend(asa_fields_needed) refactor_fields['FIELDS_MAPPING'].extend(asa_fields_mapping) outputs['refactorFields'] = processing.run('qgis:refactorfields', refactor_fields, context=context, feedback=feedback, is_child_algorithm=True) layerPath = outputs['refactorFields']['OUTPUT'] # return feature sink results[self.FEATURE_SINK] = layerPath results[self.OUTPUT_STRING] = "Success" return results
def processAlgorithm(self, parameters, context, feedback): inLayer = self.parameterAsVectorLayer(parameters, self.INPUT, context) boundary = self.parameterAsEnum(parameters, self.MODE, context) == self.MODE_BOUNDARY smallestArea = self.parameterAsEnum(parameters, self.MODE, context) == self.MODE_SMALLEST_AREA if inLayer.selectedFeatureCount() == 0: feedback.reportError( self.tr('{0}: (No selection in input layer "{1}")').format( self.displayName(), parameters[self.INPUT])) featToEliminate = [] selFeatIds = inLayer.selectedFeatureIds() (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, inLayer.fields(), inLayer.wkbType(), inLayer.sourceCrs()) if sink is None: raise QgsProcessingException( self.invalidSinkError(parameters, self.OUTPUT)) for aFeat in inLayer.getFeatures(): if feedback.isCanceled(): break if aFeat.id() in selFeatIds: # Keep references to the features to eliminate featToEliminate.append(aFeat) else: # write the others to output sink.addFeature(aFeat, QgsFeatureSink.FastInsert) # Delete all features to eliminate in processLayer processLayer = QgsProcessingUtils.mapLayerFromString(dest_id, context) processLayer.startEditing() # ANALYZE if len(featToEliminate) > 0: # Prevent zero division start = 20.00 add = 80.00 / len(featToEliminate) else: start = 100 feedback.setProgress(start) madeProgress = True # We go through the list and see if we find any polygons we can # merge the selected with. If we have no success with some we # merge and then restart the whole story. while madeProgress: # Check if we made any progress madeProgress = False featNotEliminated = [] # Iterate over the polygons to eliminate for i in range(len(featToEliminate)): if feedback.isCanceled(): break feat = featToEliminate.pop() geom2Eliminate = feat.geometry() bbox = geom2Eliminate.boundingBox() fit = processLayer.getFeatures( QgsFeatureRequest().setFilterRect( bbox).setSubsetOfAttributes([])) mergeWithFid = None mergeWithGeom = None max = 0 min = -1 selFeat = QgsFeature() # use prepared geometries for faster intersection tests engine = QgsGeometry.createGeometryEngine( geom2Eliminate.constGet()) engine.prepareGeometry() while fit.nextFeature(selFeat): if feedback.isCanceled(): break selGeom = selFeat.geometry() if engine.intersects(selGeom.constGet()): # We have a candidate iGeom = geom2Eliminate.intersection(selGeom) if not iGeom: continue if boundary: selValue = iGeom.length() else: # area. We need a common boundary in # order to merge if 0 < iGeom.length(): selValue = selGeom.area() else: selValue = -1 if -1 != selValue: useThis = True if smallestArea: if -1 == min: min = selValue else: if selValue < min: min = selValue else: useThis = False else: if selValue > max: max = selValue else: useThis = False if useThis: mergeWithFid = selFeat.id() mergeWithGeom = QgsGeometry(selGeom) # End while fit if mergeWithFid is not None: # A successful candidate newGeom = mergeWithGeom.combine(geom2Eliminate) if processLayer.changeGeometry(mergeWithFid, newGeom): madeProgress = True else: raise QgsProcessingException( self. tr('Could not replace geometry of feature with id {0}' ).format(mergeWithFid)) start = start + add feedback.setProgress(start) else: featNotEliminated.append(feat) # End for featToEliminate featToEliminate = featNotEliminated # End while if not processLayer.commitChanges(): raise QgsProcessingException(self.tr('Could not commit changes')) for feature in featNotEliminated: if feedback.isCanceled(): break sink.addFeature(feature, QgsFeatureSink.FastInsert) return {self.OUTPUT: dest_id}
def ogrConnectionStringAndFormat(uri, context): """Generates OGR connection string and format string from layer source Returned values are a tuple of the connection string and format string """ ogrstr = None format = None layer = QgsProcessingUtils.mapLayerFromString(uri, context, False) if layer is None: path, ext = os.path.splitext(uri) format = QgsVectorFileWriter.driverForExtension(ext) return '"' + uri + '"', '"' + format + '"' provider = layer.dataProvider().name() if provider == 'spatialite': # dbname='/geodata/osm_ch.sqlite' table="places" (Geometry) sql= regex = re.compile("dbname='(.+)'") r = regex.search(str(layer.source())) ogrstr = r.groups()[0] format = 'SQLite' elif provider == 'postgres': # dbname='ktryjh_iuuqef' host=spacialdb.com port=9999 # user='******' password='******' sslmode=disable # key='gid' estimatedmetadata=true srid=4326 type=MULTIPOLYGON # table="t4" (geom) sql= dsUri = QgsDataSourceUri(layer.dataProvider().dataSourceUri()) conninfo = dsUri.connectionInfo() conn = None ok = False while not conn: try: conn = psycopg2.connect(dsUri.connectionInfo()) except psycopg2.OperationalError: (ok, user, passwd) = QgsCredentials.instance().get( conninfo, dsUri.username(), dsUri.password()) if not ok: break dsUri.setUsername(user) dsUri.setPassword(passwd) if not conn: raise RuntimeError( 'Could not connect to PostgreSQL database - check connection info' ) if ok: QgsCredentials.instance().put(conninfo, user, passwd) ogrstr = "PG:%s" % dsUri.connectionInfo() format = 'PostgreSQL' elif provider == "oracle": # OCI:user/password@host:port/service:table dsUri = QgsDataSourceUri(layer.dataProvider().dataSourceUri()) ogrstr = "OCI:" if dsUri.username() != "": ogrstr += dsUri.username() if dsUri.password() != "": ogrstr += "/" + dsUri.password() delim = "@" if dsUri.host() != "": ogrstr += delim + dsUri.host() delim = "" if dsUri.port() != "" and dsUri.port() != '1521': ogrstr += ":" + dsUri.port() ogrstr += "/" if dsUri.database() != "": ogrstr += dsUri.database() elif dsUri.database() != "": ogrstr += delim + dsUri.database() if ogrstr == "OCI:": raise RuntimeError( 'Invalid oracle data source - check connection info') ogrstr += ":" if dsUri.schema() != "": ogrstr += dsUri.schema() + "." ogrstr += dsUri.table() format = 'OCI' else: ogrstr = str(layer.source()).split("|")[0] path, ext = os.path.splitext(ogrstr) format = QgsVectorFileWriter.driverForExtension(ext) return '"' + ogrstr + '"', '"' + format + '"'
def processAlgorithm(self, context, feedback): layer = QgsProcessingUtils.mapLayerFromString( self.getParameterValue(self.INPUT_VECTOR), context) rasterPath = str(self.getParameterValue(self.INPUT_RASTER)) 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('poly_id', QVariant.Int, '', 10, 0)) fields.append(QgsField('point_id', QVariant.Int, '', 10, 0)) writer = self.getOutputFromName(self.OUTPUT_LAYER).getVectorWriter( fields, QgsWkbTypes.Point, layer.crs(), context) outFeature = QgsFeature() outFeature.setFields(fields) fid = 0 polyId = 0 pointId = 0 features = QgsProcessingUtils.getFeatures(layer, context) total = 100.0 / QgsProcessingUtils.featureCount(layer, context) for current, f in enumerate(features): 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.geometry()) engine.prepareGeometry() for row in range(startRow, endRow + 1): for col in range(startColumn, endColumn + 1): (x, y) = raster.pixelToMap(row, col, geoTransform) point = QgsPointV2() point.setX(x) point.setY(y) if engine.contains(point): outFeature.setGeometry(QgsGeometry(point)) outFeature['id'] = fid outFeature['poly_id'] = polyId outFeature['point_id'] = pointId fid += 1 pointId += 1 writer.addFeature(outFeature) pointId = 0 polyId += 1 feedback.setProgress(int(current * total)) del writer
def execute_in_place_run(alg, active_layer, parameters, context=None, feedback=None, raise_exceptions=False): """Executes an algorithm modifying features in-place in the input layer. The input layer must be editable or an exception is raised. :param alg: algorithm to run :type alg: QgsProcessingAlgorithm :param active_layer: the editable layer :type active_layer: QgsVectoLayer :param parameters: parameters of the algorithm :type parameters: dict :param context: context, defaults to None :param context: QgsProcessingContext, optional :param feedback: feedback, defaults to None :param feedback: QgsProcessingFeedback, optional :raises QgsProcessingException: raised when the layer is not editable or the layer cannot be found in the current project :return: a tuple with true if success and results :rtype: tuple """ if feedback is None: feedback = QgsProcessingFeedback() if context is None: context = dataobjects.createContext(feedback) if active_layer is None or not active_layer.isEditable(): raise QgsProcessingException( tr("Layer is not editable or layer is None.")) if not alg.supportInPlaceEdit(active_layer): raise QgsProcessingException( tr("Selected algorithm and parameter configuration are not compatible with in-place modifications." )) parameters['OUTPUT'] = 'memory:' try: new_feature_ids = [] active_layer.beginEditCommand(alg.displayName()) req = QgsFeatureRequest(QgsExpression(r"$id < 0")) req.setFlags(QgsFeatureRequest.NoGeometry) req.setSubsetOfAttributes([]) # Checks whether the algorithm has a processFeature method if hasattr(alg, 'processFeature'): # in-place feature editing # Make a clone or it will crash the second time the dialog # is opened and run alg = alg.create() if not alg.prepare(parameters, context, feedback): raise QgsProcessingException( tr("Could not prepare selected algorithm.")) # Check again for compatibility after prepare if not alg.supportInPlaceEdit(active_layer): raise QgsProcessingException( tr("Selected algorithm and parameter configuration are not compatible with in-place modifications." )) field_idxs = range(len(active_layer.fields())) feature_iterator = active_layer.getFeatures( QgsFeatureRequest(active_layer.selectedFeatureIds()) ) if parameters[ 'INPUT'].selectedFeaturesOnly else active_layer.getFeatures() for f in feature_iterator: # need a deep copy, because python processFeature implementations may return # a shallow copy from processFeature input_feature = QgsFeature(f) new_features = alg.processFeature(input_feature, context, feedback) new_features = QgsVectorLayerUtils.makeFeaturesCompatible( new_features, active_layer) if len(new_features) == 0: active_layer.deleteFeature(f.id()) elif len(new_features) == 1: new_f = new_features[0] if not f.geometry().equals(new_f.geometry()): active_layer.changeGeometry(f.id(), new_f.geometry()) if f.attributes() != new_f.attributes(): active_layer.changeAttributeValues( f.id(), dict(zip(field_idxs, new_f.attributes())), dict(zip(field_idxs, f.attributes()))) new_feature_ids.append(f.id()) else: active_layer.deleteFeature(f.id()) # Get the new ids old_ids = set( [f.id() for f in active_layer.getFeatures(req)]) if not active_layer.addFeatures(new_features): raise QgsProcessingException( tr("Error adding processed features back into the layer." )) new_ids = set( [f.id() for f in active_layer.getFeatures(req)]) new_feature_ids += list(new_ids - old_ids) results, ok = {}, True else: # Traditional 'run' with delete and add features cycle results, ok = alg.run(parameters, context, feedback) if ok: result_layer = QgsProcessingUtils.mapLayerFromString( results['OUTPUT'], context) # TODO: check if features have changed before delete/add cycle active_layer.deleteFeatures(active_layer.selectedFeatureIds()) new_features = [] for f in result_layer.getFeatures(): new_features.extend( QgsVectorLayerUtils.makeFeaturesCompatible( [f], active_layer)) # Get the new ids old_ids = set([f.id() for f in active_layer.getFeatures(req)]) if not active_layer.addFeatures(new_features): raise QgsProcessingException( tr("Error adding processed features back into the layer." )) new_ids = set([f.id() for f in active_layer.getFeatures(req)]) new_feature_ids += list(new_ids - old_ids) active_layer.endEditCommand() if ok and new_feature_ids: active_layer.selectByIds(new_feature_ids) elif not ok: active_layer.rollBack() return ok, results except QgsProcessingException as e: active_layer.endEditCommand() active_layer.rollBack() if raise_exceptions: raise e QgsMessageLog.logMessage(str(sys.exc_info()[0]), 'Processing', Qgis.Critical) if feedback is not None: feedback.reportError(getattr(e, 'msg', str(e))) return False, {}
def doCheck(self, context, feedback): layer = QgsProcessingUtils.mapLayerFromString( self.getParameterValue(self.INPUT_LAYER), context) settings = QgsSettings() method = int(settings.value(settings_method_key, 1)) valid_output = self.getOutputFromName(self.VALID_OUTPUT) valid_fields = layer.fields() valid_writer = valid_output.getVectorWriter(valid_fields, layer.wkbType(), layer.crs(), context) valid_count = 0 invalid_output = self.getOutputFromName(self.INVALID_OUTPUT) invalid_fields = layer.fields() invalid_fields.append(QgsField('_errors', QVariant.String, 255)) invalid_writer = invalid_output.getVectorWriter( invalid_fields, layer.wkbType(), layer.crs(), context) invalid_count = 0 error_output = self.getOutputFromName(self.ERROR_OUTPUT) error_fields = QgsFields() error_fields.append(QgsField('message', QVariant.String, 255)) error_writer = error_output.getVectorWriter(error_fields, QgsWkbTypes.Point, layer.crs(), context) error_count = 0 features = QgsProcessingUtils.getFeatures(layer, context) total = 100.0 / QgsProcessingUtils.featureCount(layer, context) for current, inFeat in enumerate(features): geom = inFeat.geometry() attrs = inFeat.attributes() valid = True if not geom.isNull() and not geom.isEmpty(): errors = list(geom.validateGeometry()) 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.fromPoint(error.where()) errFeat.setGeometry(error_geom) errFeat.setAttributes([error.what()]) error_writer.addFeature(errFeat) 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: valid_writer.addFeature(outFeat) valid_count += 1 else: invalid_writer.addFeature(outFeat) invalid_count += 1 feedback.setProgress(int(current * total)) del valid_writer del invalid_writer del error_writer if valid_count == 0: valid_output.open = False if invalid_count == 0: invalid_output.open = False if error_count == 0: error_output.open = False
def processAlgorithm(self, parameters, context, feedback): layer = QgsProcessingUtils.mapLayerFromString( self.getParameterValue(self.INPUT), context) index = self.getParameterValue(self.TYPE) splitNodes = False if index == 0: newType = QgsWkbTypes.Point elif index == 1: newType = QgsWkbTypes.Point splitNodes = True elif index == 2: newType = QgsWkbTypes.LineString elif index == 3: newType = QgsWkbTypes.MultiLineString elif index == 4: newType = QgsWkbTypes.Polygon else: newType = QgsWkbTypes.Point writer = self.getOutputFromName(self.OUTPUT).getVectorWriter( layer.fields(), newType, layer.crs(), context) features = QgsProcessingUtils.getFeatures(layer, context) total = 100.0 / layer.featureCount() if layer.featureCount() else 0 for current, f in enumerate(features): geom = f.geometry() geomType = geom.wkbType() if geomType in [QgsWkbTypes.Point, QgsWkbTypes.Point25D]: if newType == QgsWkbTypes.Point: writer.addFeature(f, QgsFeatureSink.FastInsert) else: raise GeoAlgorithmExecutionException( self.tr('Cannot convert from {0} to {1}').format( geomType, newType)) elif geomType in [ QgsWkbTypes.MultiPoint, QgsWkbTypes.MultiPoint25D ]: if newType == QgsWkbTypes.Point and splitNodes: points = geom.asMultiPoint() for p in points: feat = QgsFeature() feat.setAttributes(f.attributes()) feat.setGeometry(QgsGeometry.fromPoint(p)) writer.addFeature(feat, QgsFeatureSink.FastInsert) elif newType == QgsWkbTypes.Point: feat = QgsFeature() feat.setAttributes(f.attributes()) feat.setGeometry(geom.centroid()) writer.addFeature(feat, QgsFeatureSink.FastInsert) else: raise GeoAlgorithmExecutionException( self.tr('Cannot convert from {0} to {1}').format( geomType, newType)) elif geomType in [ QgsWkbTypes.LineString, QgsWkbTypes.LineString25D ]: if newType == QgsWkbTypes.Point and splitNodes: points = geom.asPolyline() for p in points: feat = QgsFeature() feat.setAttributes(f.attributes()) feat.setGeometry(QgsGeometry.fromPoint(p)) writer.addFeature(feat, QgsFeatureSink.FastInsert) elif newType == QgsWkbTypes.Point: feat = QgsFeature() feat.setAttributes(f.attributes()) feat.setGeometry(geom.centroid()) writer.addFeature(feat, QgsFeatureSink.FastInsert) elif newType == QgsWkbTypes.LineString: writer.addFeature(f, QgsFeatureSink.FastInsert) else: raise GeoAlgorithmExecutionException( self.tr('Cannot convert from {0} to {1}').format( geomType, newType)) elif geomType in [ QgsWkbTypes.MultiLineString, QgsWkbTypes.MultiLineString25D ]: if newType == QgsWkbTypes.Point and splitNodes: lines = geom.asMultiPolyline() for line in lines: for p in line: feat = QgsFeature() feat.setAttributes(f.attributes()) feat.setGeometry(QgsGeometry.fromPoint(p)) writer.addFeature(feat, QgsFeatureSink.FastInsert) elif newType == QgsWkbTypes.Point: feat = QgsFeature() feat.setAttributes(f.attributes()) feat.setGeometry(geom.centroid()) writer.addFeature(feat, QgsFeatureSink.FastInsert) elif newType == QgsWkbTypes.LineString: lines = geom.asMultiPolyline() for line in lines: feat = QgsFeature() feat.setAttributes(f.attributes()) feat.setGeometry(QgsGeometry.fromPolyline(line)) writer.addFeature(feat, QgsFeatureSink.FastInsert) elif newType == QgsWkbTypes.MultiLineString: writer.addFeature(f, QgsFeatureSink.FastInsert) else: raise GeoAlgorithmExecutionException( self.tr('Cannot convert from {0} to {1}').format( geomType, newType)) elif geomType in [QgsWkbTypes.Polygon, QgsWkbTypes.Polygon25D]: if newType == QgsWkbTypes.Point and splitNodes: rings = geom.asPolygon() for ring in rings: for p in ring: feat = QgsFeature() feat.setAttributes(f.attributes()) feat.setGeometry(QgsGeometry.fromPoint(p)) writer.addFeature(feat, QgsFeatureSink.FastInsert) elif newType == QgsWkbTypes.Point: feat = QgsFeature() feat.setAttributes(f.attributes()) feat.setGeometry(geom.centroid()) writer.addFeature(feat, QgsFeatureSink.FastInsert) elif newType == QgsWkbTypes.MultiLineString: rings = geom.asPolygon() feat = QgsFeature() feat.setAttributes(f.attributes()) feat.setGeometry(QgsGeometry.fromMultiPolyline(rings)) writer.addFeature(feat, QgsFeatureSink.FastInsert) elif newType == QgsWkbTypes.Polygon: writer.addFeature(f, QgsFeatureSink.FastInsert) else: raise GeoAlgorithmExecutionException( self.tr('Cannot convert from {0} to {1}').format( geomType, newType)) elif geomType in [ QgsWkbTypes.MultiPolygon, QgsWkbTypes.MultiPolygon25D ]: if newType == QgsWkbTypes.Point and splitNodes: polygons = geom.asMultiPolygon() for polygon in polygons: for line in polygon: for p in line: feat = QgsFeature() feat.setAttributes(f.attributes()) feat.setGeometry(QgsGeometry.fromPoint(p)) writer.addFeature(feat, QgsFeatureSink.FastInsert) elif newType == QgsWkbTypes.Point: feat = QgsFeature() feat.setAttributes(f.attributes()) feat.setGeometry(geom.centroid()) writer.addFeature(feat, QgsFeatureSink.FastInsert) elif newType == QgsWkbTypes.LineString: polygons = geom.asMultiPolygon() for polygons in polygons: feat = QgsFeature() feat.setAttributes(f.attributes()) feat.setGeometry(QgsGeometry.fromPolyline(polygon)) writer.addFeature(feat, QgsFeatureSink.FastInsert) elif newType == QgsWkbTypes.Polygon: polygons = geom.asMultiPolygon() for polygon in polygons: feat = QgsFeature() feat.setAttributes(f.attributes()) feat.setGeometry(QgsGeometry.fromPolygon(polygon)) writer.addFeature(feat, QgsFeatureSink.FastInsert) elif newType in [ QgsWkbTypes.MultiLineString, QgsWkbTypes.MultiPolygon ]: writer.addFeature(f, QgsFeatureSink.FastInsert) else: raise GeoAlgorithmExecutionException( self.tr('Cannot convert from {0} to {1}').format( geomType, newType)) feedback.setProgress(int(current * total)) del writer
def processAlgorithm(self, context, feedback): layer = QgsProcessingUtils.mapLayerFromString( self.getParameterValue(self.INPUT), context) buf = self.getParameterValue(self.BUFFER) writer = self.getOutputFromName(self.OUTPUT).getVectorWriter( layer.fields(), QgsWkbTypes.Polygon, layer.crs(), context) outFeat = QgsFeature() extent = layer.extent() 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 = QgsProcessingUtils.getFeatures(layer, context) total = 100.0 / QgsProcessingUtils.featureCount(layer, context) for current, inFeat in enumerate(features): 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 GeoAlgorithmExecutionException( 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 GeoAlgorithmExecutionException( self.tr('There were no polygons created.')) total = 100.0 / len(c.polygons) for (site, edges) in list(c.polygons.items()): request = QgsFeatureRequest().setFilterFid(ptDict[ids[site]]) inFeat = next(layer.getFeatures(request)) lines = self.clip_voronoi(edges, c, width, height, extent, extraX, extraY) geom = QgsGeometry.fromMultiPoint(lines) geom = QgsGeometry(geom.convexHull()) outFeat.setGeometry(geom) outFeat.setAttributes(inFeat.attributes()) writer.addFeature(outFeat) current += 1 feedback.setProgress(int(current * total)) del writer
def test_check_validity(self): """Test that the output invalid contains the error reason""" polygon_layer = self._make_layer('Polygon') self.assertTrue(polygon_layer.startEditing()) f = QgsFeature(polygon_layer.fields()) f.setAttributes([1]) # Flake! f.setGeometry( QgsGeometry.fromWkt('POLYGON ((0 0, 2 2, 0 2, 2 0, 0 0))')) self.assertTrue(f.isValid()) f2 = QgsFeature(polygon_layer.fields()) f2.setAttributes([1]) f2.setGeometry( QgsGeometry.fromWkt( 'POLYGON((1.1 1.1, 1.1 2.1, 2.1 2.1, 2.1 1.1, 1.1 1.1))')) self.assertTrue(f2.isValid()) self.assertTrue(polygon_layer.addFeatures([f, f2])) polygon_layer.commitChanges() polygon_layer.rollBack() self.assertEqual(polygon_layer.featureCount(), 2) QgsProject.instance().addMapLayers([polygon_layer]) alg = self.registry.createAlgorithmById('qgis:checkvalidity') context = QgsProcessingContext() context.setProject(QgsProject.instance()) feedback = ConsoleFeedBack() self.assertIsNotNone(alg) parameters = {} parameters['INPUT_LAYER'] = polygon_layer.id() parameters['VALID_OUTPUT'] = 'memory:' parameters['INVALID_OUTPUT'] = 'memory:' parameters['ERROR_OUTPUT'] = 'memory:' # QGIS method parameters['METHOD'] = 1 ok, results = execute(alg, parameters, context=context, feedback=feedback) self.assertTrue(ok) invalid_layer = QgsProcessingUtils.mapLayerFromString( results['INVALID_OUTPUT'], context) self.assertEqual(invalid_layer.fields().names()[-1], '_errors') self.assertEqual(invalid_layer.featureCount(), 1) f = next(invalid_layer.getFeatures()) self.assertEqual(f.attributes(), [ 1, 'segments 0 and 2 of line 0 intersect at 1, 1\nGeometry has 1 errors.' ]) # GEOS method parameters['METHOD'] = 2 ok, results = execute(alg, parameters, context=context, feedback=feedback) self.assertTrue(ok) invalid_layer = QgsProcessingUtils.mapLayerFromString( results['INVALID_OUTPUT'], context) self.assertEqual(invalid_layer.fields().names()[-1], '_errors') self.assertEqual(invalid_layer.featureCount(), 1) f = next(invalid_layer.getFeatures()) self.assertEqual(f.attributes(), [1, 'Self-intersection'])
def processAlgorithm(self, parameters, context, feedback): expression = self.getParameterValue(self.EXPRESSION) layersValue = self.getParameterValue(self.LAYERS) layersDict = {} if layersValue: layers = [ QgsProcessingUtils.mapLayerFromString(f, context) for f in layersValue.split(";") ] layersDict = { os.path.basename(lyr.source().split(".")[0]): lyr for lyr in layers } for lyr in QgsProcessingUtils.compatibleRasterLayers( context.project()): name = lyr.name() if (name + "@") in expression: layersDict[name] = lyr entries = [] for name, lyr in layersDict.items(): for n in range(lyr.bandCount()): entry = QgsRasterCalculatorEntry() entry.ref = '{:s}@{:d}'.format(name, n + 1) entry.raster = lyr entry.bandNumber = n + 1 entries.append(entry) output = self.getOutputValue(self.OUTPUT) extentValue = self.getParameterValue(self.EXTENT) if not extentValue: extentValue = QgsProcessingUtils.combineLayerExtents(layersValue) if extentValue: extent = extentValue.split(',') bbox = QgsRectangle(float(extent[0]), float(extent[2]), float(extent[1]), float(extent[3])) else: if layersDict: bbox = list(layersDict.values())[0].extent() for lyr in layersDict.values(): bbox.combineExtentWith(lyr.extent()) else: raise GeoAlgorithmExecutionException( self.tr("No layers selected")) def _cellsize(layer): return (layer.extent().xMaximum() - layer.extent().xMinimum()) / layer.width() cellsize = self.getParameterValue(self.CELLSIZE) or min( [_cellsize(lyr) for lyr in layersDict.values()]) width = math.floor((bbox.xMaximum() - bbox.xMinimum()) / cellsize) height = math.floor((bbox.yMaximum() - bbox.yMinimum()) / cellsize) driverName = GdalUtils.getFormatShortNameFromFilename(output) calc = QgsRasterCalculator(expression, output, driverName, bbox, width, height, entries) res = calc.processCalculation() if res == QgsRasterCalculator.ParserError: raise GeoAlgorithmExecutionException( self.tr("Error parsing formula"))
def execute_in_place_run(alg, parameters, context=None, feedback=None, raise_exceptions=False): """Executes an algorithm modifying features in-place in the input layer. :param alg: algorithm to run :type alg: QgsProcessingAlgorithm :param parameters: parameters of the algorithm :type parameters: dict :param context: context, defaults to None :type context: QgsProcessingContext, optional :param feedback: feedback, defaults to None :type feedback: QgsProcessingFeedback, optional :param raise_exceptions: useful for testing, if True exceptions are raised, normally exceptions will be forwarded to the feedback :type raise_exceptions: boo, default to False :raises QgsProcessingException: raised when there is no active layer, or it cannot be made editable :return: a tuple with true if success and results :rtype: tuple """ if feedback is None: feedback = QgsProcessingFeedback() if context is None: context = dataobjects.createContext(feedback) # Only feature based algs have sourceFlags try: if alg.sourceFlags( ) & QgsProcessingFeatureSource.FlagSkipGeometryValidityChecks: context.setInvalidGeometryCheck(QgsFeatureRequest.GeometryNoCheck) except AttributeError: pass active_layer = parameters['INPUT'] # Run some checks and prepare the layer for in-place execution by: # - getting the active layer and checking that it is a vector # - making the layer editable if it was not already # - selecting all features if none was selected # - checking in-place support for the active layer/alg/parameters # If one of the check fails and raise_exceptions is True an exception # is raised, else the execution is aborted and the error reported in # the feedback try: if active_layer is None: raise QgsProcessingException(tr("There is not active layer.")) if not isinstance(active_layer, QgsVectorLayer): raise QgsProcessingException( tr("Active layer is not a vector layer.")) if not active_layer.isEditable(): if not active_layer.startEditing(): raise QgsProcessingException( tr("Active layer is not editable (and editing could not be turned on)." )) if not alg.supportInPlaceEdit(active_layer): raise QgsProcessingException( tr("Selected algorithm and parameter configuration are not compatible with in-place modifications." )) except QgsProcessingException as e: if raise_exceptions: raise e QgsMessageLog.logMessage(str(sys.exc_info()[0]), 'Processing', Qgis.Critical) if feedback is not None: feedback.reportError(getattr(e, 'msg', str(e)), fatalError=True) return False, {} if not active_layer.selectedFeatureIds(): active_layer.selectAll() # Make sure we are working on selected features only parameters['INPUT'] = QgsProcessingFeatureSourceDefinition( active_layer.id(), True) parameters['OUTPUT'] = 'memory:' req = QgsFeatureRequest(QgsExpression(r"$id < 0")) req.setFlags(QgsFeatureRequest.NoGeometry) req.setSubsetOfAttributes([]) # Start the execution # If anything goes wrong and raise_exceptions is True an exception # is raised, else the execution is aborted and the error reported in # the feedback try: new_feature_ids = [] active_layer.beginEditCommand(alg.displayName()) # Checks whether the algorithm has a processFeature method if hasattr(alg, 'processFeature'): # in-place feature editing # Make a clone or it will crash the second time the dialog # is opened and run alg = alg.create() if not alg.prepare(parameters, context, feedback): raise QgsProcessingException( tr("Could not prepare selected algorithm.")) # Check again for compatibility after prepare if not alg.supportInPlaceEdit(active_layer): raise QgsProcessingException( tr("Selected algorithm and parameter configuration are not compatible with in-place modifications." )) field_idxs = range(len(active_layer.fields())) iterator_req = QgsFeatureRequest(active_layer.selectedFeatureIds()) iterator_req.setInvalidGeometryCheck( context.invalidGeometryCheck()) feature_iterator = active_layer.getFeatures(iterator_req) step = 100 / len(active_layer.selectedFeatureIds() ) if active_layer.selectedFeatureIds() else 1 for current, f in enumerate(feature_iterator): feedback.setProgress(current * step) if feedback.isCanceled(): break # need a deep copy, because python processFeature implementations may return # a shallow copy from processFeature input_feature = QgsFeature(f) new_features = alg.processFeature(input_feature, context, feedback) new_features = QgsVectorLayerUtils.makeFeaturesCompatible( new_features, active_layer) if len(new_features) == 0: active_layer.deleteFeature(f.id()) elif len(new_features) == 1: new_f = new_features[0] if not f.geometry().equals(new_f.geometry()): active_layer.changeGeometry(f.id(), new_f.geometry()) if f.attributes() != new_f.attributes(): active_layer.changeAttributeValues( f.id(), dict(zip(field_idxs, new_f.attributes())), dict(zip(field_idxs, f.attributes()))) new_feature_ids.append(f.id()) else: active_layer.deleteFeature(f.id()) # Get the new ids old_ids = set( [f.id() for f in active_layer.getFeatures(req)]) if not active_layer.addFeatures(new_features): raise QgsProcessingException( tr("Error adding processed features back into the layer." )) new_ids = set( [f.id() for f in active_layer.getFeatures(req)]) new_feature_ids += list(new_ids - old_ids) results, ok = {}, True else: # Traditional 'run' with delete and add features cycle # There is no way to know if some features have been skipped # due to invalid geometries if context.invalidGeometryCheck( ) == QgsFeatureRequest.GeometrySkipInvalid: selected_ids = active_layer.selectedFeatureIds() else: selected_ids = [] results, ok = alg.run(parameters, context, feedback) if ok: result_layer = QgsProcessingUtils.mapLayerFromString( results['OUTPUT'], context) # TODO: check if features have changed before delete/add cycle new_features = [] # Check if there are any skipped features if context.invalidGeometryCheck( ) == QgsFeatureRequest.GeometrySkipInvalid: missing_ids = list( set(selected_ids) - set(result_layer.allFeatureIds())) if missing_ids: for f in active_layer.getFeatures( QgsFeatureRequest(missing_ids)): if not f.geometry().isGeosValid(): new_features.append(f) active_layer.deleteFeatures(active_layer.selectedFeatureIds()) for f in result_layer.getFeatures(): new_features.extend( QgsVectorLayerUtils.makeFeaturesCompatible( [f], active_layer)) # Get the new ids old_ids = set([f.id() for f in active_layer.getFeatures(req)]) if not active_layer.addFeatures(new_features): raise QgsProcessingException( tr("Error adding processed features back into the layer." )) new_ids = set([f.id() for f in active_layer.getFeatures(req)]) new_feature_ids += list(new_ids - old_ids) active_layer.endEditCommand() if ok and new_feature_ids: active_layer.selectByIds(new_feature_ids) elif not ok: active_layer.rollBack() return ok, results except QgsProcessingException as e: active_layer.endEditCommand() active_layer.rollBack() if raise_exceptions: raise e QgsMessageLog.logMessage(str(sys.exc_info()[0]), 'Processing', Qgis.Critical) if feedback is not None: feedback.reportError(getattr(e, 'msg', str(e)), fatalError=True) return False, {}
def processAlgorithm(self, parameters, context, feedback): layer = QgsProcessingUtils.mapLayerFromString( self.getParameterValue(self.VECTOR), context) groupField = self.getParameterValue(self.GROUP_FIELD) orderField = self.getParameterValue(self.ORDER_FIELD) dateFormat = str(self.getParameterValue(self.DATE_FORMAT)) #gap = int(self.getParameterValue(self.GAP_PERIOD)) dirName = self.getOutputValue(self.OUTPUT_TEXT) fields = QgsFields() fields.append(QgsField('group', QVariant.String, '', 254, 0)) fields.append(QgsField('begin', QVariant.String, '', 254, 0)) fields.append(QgsField('end', QVariant.String, '', 254, 0)) writer = self.getOutputFromName(self.OUTPUT_LINES).getVectorWriter( fields, QgsWkbTypes.LineString, layer.crs(), context) points = dict() features = QgsProcessingUtils.getFeatures(layer, context) total = 100.0 / layer.featureCount() if layer.featureCount() else 0 for current, f in enumerate(features): point = f.geometry().asPoint() group = f[groupField] order = f[orderField] if dateFormat != '': order = datetime.strptime(str(order), dateFormat) if group in points: points[group].append((order, point)) else: points[group] = [(order, point)] feedback.setProgress(int(current * total)) feedback.setProgress(0) da = QgsDistanceArea() da.setSourceCrs(layer.sourceCrs()) da.setEllipsoid(QgsProject.instance().ellipsoid()) current = 0 total = 100.0 / len(points) if points else 1 for group, vertices in list(points.items()): vertices.sort() f = QgsFeature() f.initAttributes(len(fields)) f.setFields(fields) f['group'] = group f['begin'] = vertices[0][0] f['end'] = vertices[-1][0] fileName = os.path.join(dirName, '%s.txt' % group) with open(fileName, 'w') as fl: fl.write('angle=Azimuth\n') fl.write('heading=Coordinate_System\n') fl.write('dist_units=Default\n') line = [] i = 0 for node in vertices: line.append(node[1]) if i == 0: fl.write('startAt=%f;%f;90\n' % (node[1].x(), node[1].y())) fl.write('survey=Polygonal\n') fl.write('[data]\n') else: angle = line[i - 1].azimuth(line[i]) distance = da.measureLine(line[i - 1], line[i]) fl.write('%f;%f;90\n' % (angle, distance)) i += 1 f.setGeometry(QgsGeometry.fromPolyline(line)) writer.addFeature(f, QgsFeatureSink.FastInsert) current += 1 feedback.setProgress(int(current * total)) del writer
def processAlgorithm(self, parameters, context, feedback): commands = list() self.exportedLayers = {} self.preProcessInputs() # 1: Export rasters to sgrd and vectors to shp # Tables must be in dbf format. We check that. for param in self.parameterDefinitions(): if isinstance(param, ParameterRaster): if param.name( ) not in parameters or parameters[param.name()] is None: continue if parameters[param.name()].endswith('sdat'): parameters[ param.name()] = parameters[param.name()][:-4] + "sgrd" elif not parameters[param.name()].endswith('sgrd'): exportCommand = self.exportRasterLayer( parameters[param.name()]) if exportCommand is not None: commands.append(exportCommand) if isinstance(param, ParameterVector): if param.name( ) not in parameters or parameters[param.name()] is None: continue layer = QgsProcessingUtils.mapLayerFromString( parameters[param.name()], context, False) if layer: filename = dataobjects.exportVectorLayer(layer) self.exportedLayers[param.value] = filename elif not parameteres[param.name()].endswith('shp'): raise GeoAlgorithmExecutionException( self.tr('Unsupported file format')) if isinstance(param, ParameterTable): if param.name( ) not in parameters or parameters[param.name()] is None: continue table = QgsProcessingUtils.mapLayerFromString( parameters[param.name()], context, False) if table: filename = dataobjects.exportTable(table) self.exportedLayers[parameters[param.name()]] = filename elif not parameters[param.name()].endswith('shp'): raise GeoAlgorithmExecutionException( self.tr('Unsupported file format')) if isinstance(param, ParameterMultipleInput): if param.name( ) not in parameters or parameters[param.name()] is None: continue layers = param.value.split(';') if layers is None or len(layers) == 0: continue if param.datatype == dataobjects.TYPE_RASTER: for i, layerfile in enumerate(layers): if layerfile.endswith('sdat'): layerfile = param.value[:-4] + "sgrd" layers[i] = layerfile elif not layerfile.endswith('sgrd'): exportCommand = self.exportRasterLayer(layerfile) if exportCommand is not None: commands.append(exportCommand) param.value = ";".join(layers) elif param.datatype in [ dataobjects.TYPE_VECTOR_ANY, dataobjects.TYPE_VECTOR_LINE, dataobjects.TYPE_VECTOR_POLYGON, dataobjects.TYPE_VECTOR_POINT ]: for layerfile in layers: layer = QgsProcessingUtils.mapLayerFromString( layerfile, context, False) if layer: filename = dataobjects.exportVectorLayer(layer) self.exportedLayers[layerfile] = filename elif not layerfile.endswith('shp'): raise GeoAlgorithmExecutionException( self.tr('Unsupported file format')) # TODO - set minimum extent if not extent: extent = QgsProcessingUtils.combineLayerExtents([layer]) # 2: Set parameters and outputs command = self.undecoratedGroup + ' "' + self.cmdname + '"' command += ' ' + ' '.join(self.hardcodedStrings) for param in self.parameterDefinitions(): if not param.name() in parameters or parameters[ param.name()] is None: continue if isinstance(param, (ParameterRaster, ParameterVector, ParameterTable)): value = parameters[param.name()] if value in list(self.exportedLayers.keys()): command += ' -' + param.name() + ' "' \ + self.exportedLayers[value] + '"' else: command += ' -' + param.name() + ' "' + value + '"' elif isinstance(param, ParameterMultipleInput): s = parameters[param.name()] for layer in list(self.exportedLayers.keys()): s = s.replace(layer, self.exportedLayers[layer]) command += ' -' + param.name() + ' "' + s + '"' elif isinstance(param, ParameterBoolean): if parameters[param.name()]: command += ' -' + param.name().strip() + " true" else: command += ' -' + param.name().strip() + " false" elif isinstance(param, ParameterFixedTable): tempTableFile = getTempFilename('txt') with open(tempTableFile, 'w') as f: f.write('\t'.join([col for col in param.cols]) + '\n') values = parameters[param.name()].split(',') for i in range(0, len(values), 3): s = values[i] + '\t' + values[i + 1] + '\t' + values[ i + 2] + '\n' f.write(s) command += ' -' + param.name() + ' "' + tempTableFile + '"' elif isinstance(param, ParameterExtent): # 'We have to substract/add half cell size, since SAGA is # center based, not corner based halfcell = self.getOutputCellsize(parameters) / 2 offset = [halfcell, -halfcell, halfcell, -halfcell] values = parameters[param.name()].split(',') for i in range(4): command += ' -' + self.extentParamNames[i] + ' ' \ + str(float(values[i]) + offset[i]) elif isinstance(param, (ParameterNumber, ParameterSelection)): command += ' -' + param.name() + ' ' + str(param.value) else: command += ' -' + param.name() + ' "' + str(param.value) + '"' for out in self.outputs: command += ' -' + out.name + ' "' + out.getCompatibleFileName( self) + '"' commands.append(command) # special treatment for RGB algorithm # TODO: improve this and put this code somewhere else for out in self.outputs: if isinstance(out, OutputRaster): filename = out.getCompatibleFileName(self) filename2 = filename + '.sgrd' if self.cmdname == 'RGB Composite': commands.append('io_grid_image 0 -IS_RGB -GRID:"' + filename2 + '" -FILE:"' + filename + '"') # 3: Run SAGA commands = self.editCommands(commands) SagaUtils.createSagaBatchJobFileFromSagaCommands(commands) loglines = [] loglines.append(self.tr('SAGA execution commands')) for line in commands: feedback.pushCommandInfo(line) loglines.append(line) if ProcessingConfig.getSetting(SagaUtils.SAGA_LOG_COMMANDS): QgsMessageLog.logMessage('\n'.join(loglines), self.tr('Processing'), QgsMessageLog.INFO) SagaUtils.executeSaga(feedback) if self.crs is not None: for out in self.outputs: if isinstance(out, (OutputVector, OutputRaster)): prjFile = os.path.splitext( out.getCompatibleFileName(self))[0] + ".prj" with open(prjFile, "w") as f: f.write(self.crs.toWkt())
def processAlgorithm(self, context, feedback): layer = QgsProcessingUtils.mapLayerFromString( self.getParameterValue(self.INPUT_LAYER), context) fieldName = self.getParameterValue(self.FIELD_NAME) outputFile = self.getOutputValue(self.OUTPUT_HTML_FILE) request = QgsFeatureRequest().setFlags( QgsFeatureRequest.NoGeometry).setSubsetOfAttributes([fieldName], layer.fields()) stat = QgsStatisticalSummary() features = QgsProcessingUtils.getFeatures(layer, context, request) count = QgsProcessingUtils.featureCount(layer, context) total = 100.0 / float(count) for current, ft in enumerate(features): stat.addVariant(ft[fieldName]) feedback.setProgress(int(current * total)) stat.finalize() count = stat.count() uniqueValue = stat.variety() minValue = stat.min() maxValue = stat.max() rValue = stat.range() sumValue = stat.sum() meanValue = stat.mean() medianValue = stat.median() stdDevValue = stat.stDev() cvValue = stdDevValue / meanValue if meanValue != 0 else 0 minority = stat.minority() majority = stat.majority() firstQuartile = stat.firstQuartile() thirdQuartile = stat.thirdQuartile() iqr = stat.interQuartileRange() nullValues = stat.countMissing() data = [] data.append(self.tr('Analyzed layer: {}').format(layer.name())) data.append(self.tr('Analyzed field: {}').format(fieldName)) data.append(self.tr('Count: {}').format(count)) data.append(self.tr('Unique values: {}').format(uniqueValue)) data.append(self.tr('Minimum value: {}').format(minValue)) data.append(self.tr('Maximum value: {}').format(maxValue)) data.append(self.tr('Range: {}').format(rValue)) data.append(self.tr('Sum: {}').format(sumValue)) data.append(self.tr('Mean value: {}').format(meanValue)) data.append(self.tr('Median value: {}').format(medianValue)) data.append(self.tr('Standard deviation: {}').format(stdDevValue)) data.append(self.tr('Coefficient of Variation: {}').format(cvValue)) data.append( self.tr('Minority (rarest occurring value): {}').format(minority)) data.append( self.tr('Majority (most frequently occurring value): {}').format( majority)) data.append(self.tr('First quartile: {}').format(firstQuartile)) data.append(self.tr('Third quartile: {}').format(thirdQuartile)) data.append(self.tr('NULL (missing) values: {}').format(nullValues)) data.append(self.tr('Interquartile Range (IQR): {}').format(iqr)) self.createHTML(outputFile, data) self.setOutputValue(self.COUNT, count) self.setOutputValue(self.UNIQUE, uniqueValue) self.setOutputValue(self.MIN, minValue) self.setOutputValue(self.MAX, maxValue) self.setOutputValue(self.RANGE, rValue) self.setOutputValue(self.SUM, sumValue) self.setOutputValue(self.MEAN, meanValue) self.setOutputValue(self.MEDIAN, medianValue) self.setOutputValue(self.STD_DEV, stdDevValue) self.setOutputValue(self.MINORITY, minority) self.setOutputValue(self.MAJORITY, majority) self.setOutputValue(self.FIRSTQUARTILE, firstQuartile) self.setOutputValue(self.THIRDQUARTILE, thirdQuartile) self.setOutputValue(self.NULLVALUES, nullValues) self.setOutputValue(self.IQR, iqr)
def processAlgorithm(self, parameters, context, feedback): layer = QgsProcessingUtils.mapLayerFromString( self.getParameterValue(self.INPUT_LAYER), context) fieldName = self.getParameterValue(self.FIELD_NAME) fieldType = self.TYPES[self.getParameterValue(self.FIELD_TYPE)] width = self.getParameterValue(self.FIELD_LENGTH) precision = self.getParameterValue(self.FIELD_PRECISION) newField = self.getParameterValue(self.NEW_FIELD) formula = self.getParameterValue(self.FORMULA) output = self.getOutputFromName(self.OUTPUT_LAYER) fields = layer.fields() if newField: fields.append(QgsField(fieldName, fieldType, '', width, precision)) writer = output.getVectorWriter(fields, layer.wkbType(), layer.crs(), context) exp = QgsExpression(formula) da = QgsDistanceArea() da.setSourceCrs(layer.crs()) da.setEllipsoid(QgsProject.instance().ellipsoid()) exp.setGeomCalculator(da) exp.setDistanceUnits(QgsProject.instance().distanceUnits()) exp.setAreaUnits(QgsProject.instance().areaUnits()) exp_context = QgsExpressionContext( QgsExpressionContextUtils.globalProjectLayerScopes(layer)) if not exp.prepare(exp_context): raise GeoAlgorithmExecutionException( self.tr('Evaluation error: {0}').format(exp.evalErrorString())) outFeature = QgsFeature() outFeature.initAttributes(len(fields)) outFeature.setFields(fields) error = '' calculationSuccess = True features = QgsProcessingUtils.getFeatures(layer, context) total = 100.0 / layer.featureCount() if layer.featureCount() else 0 rownum = 1 for current, f in enumerate(features): rownum = current + 1 exp_context.setFeature(f) exp_context.lastScope().setVariable("row_number", rownum) value = exp.evaluate(exp_context) if exp.hasEvalError(): calculationSuccess = False error = exp.evalErrorString() break else: outFeature.setGeometry(f.geometry()) for fld in f.fields(): outFeature[fld.name()] = f[fld.name()] outFeature[fieldName] = value writer.addFeature(outFeature, QgsFeatureSink.FastInsert) feedback.setProgress(int(current * total)) del writer if not calculationSuccess: raise GeoAlgorithmExecutionException( self.tr('An error occurred while evaluating the calculation ' 'string:\n{0}').format(error))
def processAlgorithm(self, parameters, context, model_feedback): # Use a multi-step feedback, so that individual child algorithm progress reports are adjusted for the # overall progress through the model feedback = QgsProcessingMultiStepFeedback(3, model_feedback) results = {} outputs = {} # Convert enumerator to zero based index value valor_associado_agua = self.parameterAsEnum( parameters, self.VALOR_ELEMENTO_ASSOCIADO_AGUA, context) + 1 # Refactor fields alg_params = { 'FIELDS_MAPPING': [{ 'expression': 'now()', 'length': -1, 'name': 'inicio_objeto', 'precision': -1, 'type': 14 }, { 'expression': str(valor_associado_agua), 'length': 255, 'name': 'valor_elemento_associado_agua', 'precision': -1, 'type': 10 }], 'INPUT': parameters['INPUT'], 'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT } outputs['RefactorFields'] = processing.run('qgis:refactorfields', alg_params, context=context, feedback=feedback, is_child_algorithm=True) feedback.setCurrentStep(1) if feedback.isCanceled(): return {} # Sanitize Z and M values from 3D Layers # Input table only accepts 2D alg_params = { 'DROP_M_VALUES': True, 'DROP_Z_VALUES': True, 'INPUT': outputs['RefactorFields']['OUTPUT'], 'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT } outputs['DropMzValues'] = processing.run('native:dropmzvalues', alg_params, context=context, feedback=feedback, is_child_algorithm=True) feedback.setCurrentStep(2) if feedback.isCanceled(): return {} # Export to PostgreSQL (available connections) idx = self.parameterAsEnum(parameters, self.POSTGRES_CONNECTION, context) postgres_connection = self.postgres_connections_list[idx] # Because the target layer is of the geometry type, one needs to make # sure to use the correct option when importing into PostGIS layer = QgsProcessingUtils.mapLayerFromString( outputs['RefactorFields']['OUTPUT'], context) if layer.geometryType() == 0: gtype = 3 elif layer.geometryType() == 2: gtype = 5 alg_params = { 'ADDFIELDS': True, 'APPEND': True, 'A_SRS': None, 'CLIP': False, 'DATABASE': postgres_connection, 'DIM': 0, 'GEOCOLUMN': 'geometria', 'GT': '', 'GTYPE': gtype, 'INDEX': True, 'INPUT': outputs['DropMzValues']['OUTPUT'], 'LAUNDER': True, 'OPTIONS': '', 'OVERWRITE': False, 'PK': '', 'PRECISION': True, 'PRIMARY_KEY': 'identificador', 'PROMOTETOMULTI': True, 'SCHEMA': 'public', 'SEGMENTIZE': '', 'SHAPE_ENCODING': '', 'SIMPLIFY': '', 'SKIPFAILURES': False, 'SPAT': None, 'S_SRS': None, 'TABLE': 'elem_assoc_agua', 'T_SRS': None, 'WHERE': '' } outputs['ExportToPostgresqlAvailableConnections'] = processing.run( 'gdal:importvectorintopostgisdatabaseavailableconnections', alg_params, context=context, feedback=feedback, is_child_algorithm=True) return results
def processAlgorithm(self, parameters, context, feedback): layer = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.VECTOR), context) pointCount = float(self.getParameterValue(self.POINT_NUMBER)) minDistance = float(self.getParameterValue(self.MIN_DISTANCE)) fields = QgsFields() fields.append(QgsField('id', QVariant.Int, '', 10, 0)) writer = self.getOutputFromName(self.OUTPUT).getVectorWriter(fields, QgsWkbTypes.Point, layer.crs(), context) nPoints = 0 nIterations = 0 maxIterations = pointCount * 200 featureCount = layer.featureCount() total = 100.0 / pointCount index = QgsSpatialIndex() points = dict() da = QgsDistanceArea() request = QgsFeatureRequest() random.seed() while nIterations < maxIterations and nPoints < pointCount: # pick random feature fid = random.randint(0, featureCount - 1) f = next(layer.getFeatures(request.setFilterFid(fid).setSubsetOfAttributes([]))) fGeom = f.geometry() if fGeom.isMultipart(): lines = fGeom.asMultiPolyline() # pick random line lineId = random.randint(0, len(lines) - 1) vertices = lines[lineId] else: vertices = fGeom.asPolyline() # pick random segment if len(vertices) == 2: vid = 0 else: vid = random.randint(0, len(vertices) - 2) startPoint = vertices[vid] endPoint = vertices[vid + 1] length = da.measureLine(startPoint, endPoint) dist = length * random.random() if dist > minDistance: d = dist / (length - dist) rx = (startPoint.x() + d * endPoint.x()) / (1 + d) ry = (startPoint.y() + d * endPoint.y()) / (1 + d) # generate random point pnt = QgsPointXY(rx, ry) geom = QgsGeometry.fromPoint(pnt) if vector.checkMinDistance(pnt, index, minDistance, points): 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 feedback.setProgress(int(nPoints * total)) nIterations += 1 if nPoints < pointCount: QgsMessageLog.logMessage(self.tr('Can not generate requested number of random points. ' 'Maximum number of attempts exceeded.'), self.tr('Processing'), QgsMessageLog.INFO) del writer
def handleAlgorithmResults(alg, context, feedback=None, showResults=True): wrongLayers = [] if feedback is None: feedback = QgsProcessingFeedback() feedback.setProgressText( QCoreApplication.translate('Postprocessing', 'Loading resulting layers')) i = 0 for l, details in context.layersToLoadOnCompletion().items(): if feedback.isCanceled(): return False if len(context.layersToLoadOnCompletion()) > 2: # only show progress feedback if we're loading a bunch of layers feedback.setProgress( 100 * i / float(len(context.layersToLoadOnCompletion()))) try: layer = QgsProcessingUtils.mapLayerFromString(l, context) if layer is not None: if not ProcessingConfig.getSetting( ProcessingConfig.USE_FILENAME_AS_LAYER_NAME): layer.setName(details.name) style = None if details.outputName: style = RenderingStyles.getStyle(alg.id(), details.outputName) if style is None: if layer.type() == QgsMapLayer.RasterLayer: style = ProcessingConfig.getSetting( ProcessingConfig.RASTER_STYLE) else: if layer.geometryType() == QgsWkbTypes.PointGeometry: style = ProcessingConfig.getSetting( ProcessingConfig.VECTOR_POINT_STYLE) elif layer.geometryType() == QgsWkbTypes.LineGeometry: style = ProcessingConfig.getSetting( ProcessingConfig.VECTOR_LINE_STYLE) else: style = ProcessingConfig.getSetting( ProcessingConfig.VECTOR_POLYGON_STYLE) if style: layer.loadNamedStyle(style) details.project.addMapLayer( context.temporaryLayerStore().takeMapLayer(layer)) if details.postProcessor(): details.postProcessor().postProcessLayer( layer, context, feedback) else: wrongLayers.append(str(l)) except Exception: QgsMessageLog.logMessage( QCoreApplication.translate( 'Postprocessing', "Error loading result layer:") + "\n" + traceback.format_exc(), 'Processing', Qgis.Critical) wrongLayers.append(str(l)) i += 1 feedback.setProgress(100) if wrongLayers: msg = QCoreApplication.translate( 'Postprocessing', "The following layers were not correctly generated.") msg += "<ul>" + "".join(["<li>%s</li>" % lay for lay in wrongLayers]) + "</ul>" msg += QCoreApplication.translate( 'Postprocessing', "You can check the 'Log Messages Panel' in QGIS main window to find more information about the execution of the algorithm." ) feedback.reportError(msg) return len(wrongLayers) == 0
def check_results(self, results, context, params, expected): """ Checks if result produced by an algorithm matches with the expected specification. """ for id, expected_result in list(expected.items()): if expected_result['type'] in ('vector', 'table'): if 'compare' in expected_result and not expected_result['compare']: # skipping the comparison, so just make sure output is valid if isinstance(results[id], QgsMapLayer): result_lyr = results[id] else: result_lyr = QgsProcessingUtils.mapLayerFromString(results[id], context) self.assertTrue(result_lyr.isValid()) continue expected_lyrs = self.load_layers(id, expected_result) if 'in_place_result' in expected_result: result_lyr = QgsProcessingUtils.mapLayerFromString(self.in_place_layers[id], context) self.assertTrue(result_lyr.isValid(), self.in_place_layers[id]) else: try: results[id] except KeyError as e: raise KeyError('Expected result {} does not exist in {}'.format(str(e), list(results.keys()))) if isinstance(results[id], QgsMapLayer): result_lyr = results[id] else: result_lyr = QgsProcessingUtils.mapLayerFromString(results[id], context) self.assertTrue(result_lyr, results[id]) compare = expected_result.get('compare', {}) pk = expected_result.get('pk', None) if len(expected_lyrs) == 1: self.assertLayersEqual(expected_lyrs[0], result_lyr, compare=compare, pk=pk) else: res = False for l in expected_lyrs: if self.checkLayersEqual(l, result_lyr, compare=compare, pk=pk): res = True break self.assertTrue(res, 'Could not find matching layer in expected results') elif 'rasterhash' == expected_result['type']: print("id:{} result:{}".format(id, results[id])) self.assertTrue(os.path.exists(results[id]), 'File does not exist: {}, {}'.format(results[id], params)) dataset = gdal.Open(results[id], GA_ReadOnly) dataArray = nan_to_num(dataset.ReadAsArray(0)) strhash = hashlib.sha224(dataArray.data).hexdigest() if not isinstance(expected_result['hash'], str): self.assertIn(strhash, expected_result['hash']) else: self.assertEqual(strhash, expected_result['hash']) elif 'file' == expected_result['type']: expected_filepath = self.filepath_from_param(expected_result) result_filepath = results[id] self.assertFilesEqual(expected_filepath, result_filepath) elif 'regex' == expected_result['type']: with open(results[id], 'r') as file: data = file.read() for rule in expected_result.get('rules', []): self.assertRegex(data, rule)
def processAlgorithm(self, context, feedback): layerPoints = QgsProcessingUtils.mapLayerFromString( self.getParameterValue(self.POINTS), context) layerHubs = QgsProcessingUtils.mapLayerFromString( self.getParameterValue(self.HUBS), context) fieldName = self.getParameterValue(self.FIELD) units = self.UNITS[self.getParameterValue(self.UNIT)] if layerPoints.source() == layerHubs.source(): raise GeoAlgorithmExecutionException( self.tr('Same layer given for both hubs and spokes')) fields = layerPoints.fields() fields.append(QgsField('HubName', QVariant.String)) fields.append(QgsField('HubDist', QVariant.Double)) writer = self.getOutputFromName(self.OUTPUT).getVectorWriter( fields, QgsWkbTypes.Point, layerPoints.crs(), context) index = QgsProcessingUtils.createSpatialIndex(layerHubs, context) distance = QgsDistanceArea() distance.setSourceCrs(layerPoints.crs()) distance.setEllipsoid(QgsProject.instance().ellipsoid()) # Scan source points, find nearest hub, and write to output file features = QgsProcessingUtils.getFeatures(layerPoints, context) total = 100.0 / QgsProcessingUtils.featureCount(layerPoints, context) for current, f in enumerate(features): src = f.geometry().boundingBox().center() neighbors = index.nearestNeighbor(src, 1) ft = next( layerHubs.getFeatures(QgsFeatureRequest().setFilterFid( neighbors[0]).setSubsetOfAttributes([fieldName], layerHubs.fields()))) closest = ft.geometry().boundingBox().center() hubDist = distance.measureLine(src, closest) attributes = f.attributes() attributes.append(ft[fieldName]) if units == 'Feet': attributes.append(hubDist * 3.2808399) elif units == 'Miles': attributes.append(hubDist * 0.000621371192) elif units == 'Kilometers': attributes.append(hubDist / 1000.0) elif units != 'Meters': attributes.append( sqrt( pow(src.x() - closest.x(), 2.0) + pow(src.y() - closest.y(), 2.0))) else: attributes.append(hubDist) feat = QgsFeature() feat.setAttributes(attributes) feat.setGeometry(QgsGeometry.fromPoint(src)) writer.addFeature(feat) feedback.setProgress(int(current * total)) del writer
def handleAlgorithmResults(alg, context, feedback=None, showResults=True, parameters={}): wrongLayers = [] if feedback is None: feedback = QgsProcessingFeedback() feedback.setProgressText( QCoreApplication.translate('Postprocessing', 'Loading resulting layers')) i = 0 for l, details in context.layersToLoadOnCompletion().items(): if feedback.isCanceled(): return False if len(context.layersToLoadOnCompletion()) > 2: # only show progress feedback if we're loading a bunch of layers feedback.setProgress( 100 * i / float(len(context.layersToLoadOnCompletion()))) try: layer = QgsProcessingUtils.mapLayerFromString( l, context, typeHint=details.layerTypeHint) if layer is not None: set_layer_name(layer, details) '''If running a model, the execution will arrive here when an algorithm that is part of that model is executed. We check if its output is a final otuput of the model, and adapt the output name accordingly''' outputName = details.outputName expcontext = QgsExpressionContext() scope = QgsExpressionContextScope() expcontext.appendScope(scope) for out in alg.outputDefinitions(): if out.name() not in parameters: continue outValue = parameters[out.name()] if hasattr(outValue, "sink"): outValue = outValue.sink.valueAsString(expcontext)[0] else: outValue = str(outValue) if outValue == l: outputName = out.name() break style = None if outputName: style = RenderingStyles.getStyle(alg.id(), outputName) if style is None: if layer.type() == QgsMapLayerType.RasterLayer: style = ProcessingConfig.getSetting( ProcessingConfig.RASTER_STYLE) else: if layer.geometryType() == QgsWkbTypes.PointGeometry: style = ProcessingConfig.getSetting( ProcessingConfig.VECTOR_POINT_STYLE) elif layer.geometryType() == QgsWkbTypes.LineGeometry: style = ProcessingConfig.getSetting( ProcessingConfig.VECTOR_LINE_STYLE) else: style = ProcessingConfig.getSetting( ProcessingConfig.VECTOR_POLYGON_STYLE) if style: layer.loadNamedStyle(style) details.project.addMapLayer( context.temporaryLayerStore().takeMapLayer(layer)) if details.postProcessor(): details.postProcessor().postProcessLayer( layer, context, feedback) else: wrongLayers.append(str(l)) except Exception: QgsMessageLog.logMessage( QCoreApplication.translate( 'Postprocessing', "Error loading result layer:") + "\n" + traceback.format_exc(), 'Processing', Qgis.Critical) wrongLayers.append(str(l)) i += 1 feedback.setProgress(100) if wrongLayers: msg = QCoreApplication.translate( 'Postprocessing', "The following layers were not correctly generated.") msg += "<ul>" + "".join(["<li>%s</li>" % lay for lay in wrongLayers]) + "</ul>" msg += QCoreApplication.translate( 'Postprocessing', "You can check the 'Log Messages Panel' in QGIS main window to find more information about the execution of the algorithm." ) feedback.reportError(msg) return len(wrongLayers) == 0
def processAlgorithm(self, context, feedback): layer = QgsProcessingUtils.mapLayerFromString( self.getParameterValue(self.INPUT_VECTOR), context) rasterPath = str(self.getParameterValue(self.INPUT_RASTER)) 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)) writer = self.getOutputFromName(self.OUTPUT_LAYER).getVectorWriter( fields, QgsWkbTypes.Point, layer.crs(), context) outFeature = QgsFeature() outFeature.setFields(fields) self.fid = 0 self.lineId = 0 self.pointId = 0 features = QgsProcessingUtils.getFeatures(layer, context) total = 100.0 / QgsProcessingUtils.featureCount(layer, context) for current, f in enumerate(features): 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, writer, 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, writer, outFeature) self.pointId = 0 self.lineId += 1 feedback.setProgress(int(current * total)) del writer
def processAlgorithm(self, parameters, context, feedback): expr_context = self.createExpressionContext(parameters, context, self.source) self.group_by_expr.prepare(expr_context) # Group features in memory layers source = self.source count = self.source.featureCount() if count: progress_step = 50.0 / count current = 0 groups = {} keys = [] # We need deterministic order for the tests feature = QgsFeature() for feature in self.source.getFeatures(): expr_context.setFeature(feature) group_by_value = self.evaluateExpression(self.group_by_expr, expr_context) # Get an hashable key for the dict key = group_by_value if isinstance(key, list): key = tuple(key) group = groups.get(key, None) if group is None: sink, id = QgsProcessingUtils.createFeatureSink( 'memory:', context, source.fields(), source.wkbType(), source.sourceCrs()) layer = QgsProcessingUtils.mapLayerFromString(id, context) group = {'sink': sink, 'layer': layer, 'feature': feature} groups[key] = group keys.append(key) group['sink'].addFeature(feature, QgsFeatureSink.FastInsert) current += 1 feedback.setProgress(int(current * progress_step)) if feedback.isCanceled(): return (sink, dest_id) = self.parameterAsSink( parameters, self.OUTPUT, context, self.fields, QgsWkbTypes.multiType(source.wkbType()), source.sourceCrs()) if sink is None: raise QgsProcessingException( self.invalidSinkError(parameters, self.OUTPUT)) # Calculate aggregates on memory layers if len(keys): progress_step = 50.0 / len(keys) for current, key in enumerate(keys): group = groups[key] expr_context = self.createExpressionContext(parameters, context) expr_context.appendScope( QgsExpressionContextUtils.layerScope(group['layer'])) expr_context.setFeature(group['feature']) geometry = self.evaluateExpression(self.geometry_expr, expr_context) if geometry is not None and not geometry.isEmpty(): geometry = QgsGeometry.unaryUnion( geometry.asGeometryCollection()) if geometry.isEmpty(): raise QgsProcessingException( 'Impossible to combine geometries for {} = {}'.format( self.group_by, group_by_value)) attrs = [] for fields_expr in self.fields_expr: attrs.append(self.evaluateExpression(fields_expr, expr_context)) # Write output feature outFeat = QgsFeature() if geometry is not None: outFeat.setGeometry(geometry) outFeat.setAttributes(attrs) sink.addFeature(outFeat, QgsFeatureSink.FastInsert) feedback.setProgress(50 + int(current * progress_step)) if feedback.isCanceled(): return return {self.OUTPUT: dest_id}