def testUpdateMode(self): """ Test that on-the-fly re-opening in update/read-only mode works """ tmpdir = tempfile.mkdtemp() self.dirs_to_cleanup.append(tmpdir) srcpath = os.path.join(TEST_DATA_DIR, 'provider') for file in glob.glob(os.path.join(srcpath, 'shapefile.*')): shutil.copy(os.path.join(srcpath, file), tmpdir) datasource = os.path.join(tmpdir, 'shapefile.shp') vl = QgsVectorLayer('{}|layerid=0'.format(datasource), 'test', 'ogr') caps = vl.dataProvider().capabilities() self.assertTrue(caps & QgsVectorDataProvider.AddFeatures) self.assertTrue(caps & QgsVectorDataProvider.DeleteFeatures) self.assertTrue(caps & QgsVectorDataProvider.ChangeAttributeValues) self.assertTrue(caps & QgsVectorDataProvider.AddAttributes) self.assertTrue(caps & QgsVectorDataProvider.DeleteAttributes) self.assertTrue(caps & QgsVectorDataProvider.CreateSpatialIndex) self.assertTrue(caps & QgsVectorDataProvider.SelectAtId) self.assertTrue(caps & QgsVectorDataProvider.ChangeGeometries) # self.assertTrue(caps & QgsVectorDataProvider.ChangeFeatures) # We should be really opened in read-only mode even if write capabilities are declared self.assertEqual(vl.dataProvider().property("_debug_open_mode"), "read-only") # Unbalanced call to leaveUpdateMode() self.assertFalse(vl.dataProvider().leaveUpdateMode()) # Test that startEditing() / commitChanges() plays with enterUpdateMode() / leaveUpdateMode() self.assertTrue(vl.startEditing()) self.assertEqual(vl.dataProvider().property("_debug_open_mode"), "read-write") self.assertTrue(vl.dataProvider().isValid()) self.assertTrue(vl.commitChanges()) self.assertEqual(vl.dataProvider().property("_debug_open_mode"), "read-only") self.assertTrue(vl.dataProvider().isValid()) # Manual enterUpdateMode() / leaveUpdateMode() with 2 depths self.assertTrue(vl.dataProvider().enterUpdateMode()) self.assertEqual(vl.dataProvider().property("_debug_open_mode"), "read-write") caps = vl.dataProvider().capabilities() self.assertTrue(caps & QgsVectorDataProvider.AddFeatures) f = QgsFeature() f.setAttributes([200]) f.setGeometry(QgsGeometry.fromWkt('Point (2 49)')) (ret, feature_list) = vl.dataProvider().addFeatures([f]) self.assertTrue(ret) fid = feature_list[0].id() features = [f_iter for f_iter in vl.getFeatures(QgsFeatureRequest().setFilterFid(fid))] values = [f_iter['pk'] for f_iter in features] self.assertEqual(values, [200]) got_geom = [f_iter.geometry() for f_iter in features][0].constGet() self.assertEqual((got_geom.x(), got_geom.y()), (2.0, 49.0)) self.assertTrue(vl.dataProvider().changeGeometryValues({fid: QgsGeometry.fromWkt('Point (3 50)')})) self.assertTrue(vl.dataProvider().changeAttributeValues({fid: {0: 100}})) features = [f_iter for f_iter in vl.getFeatures(QgsFeatureRequest().setFilterFid(fid))] values = [f_iter['pk'] for f_iter in features] got_geom = [f_iter.geometry() for f_iter in features][0].constGet() self.assertEqual((got_geom.x(), got_geom.y()), (3.0, 50.0)) self.assertTrue(vl.dataProvider().deleteFeatures([fid])) # Check that it has really disappeared osgeo.gdal.PushErrorHandler('CPLQuietErrorHandler') features = [f_iter for f_iter in vl.getFeatures(QgsFeatureRequest().setFilterFid(fid))] osgeo.gdal.PopErrorHandler() self.assertEqual(features, []) self.assertTrue(vl.dataProvider().addAttributes([QgsField("new_field", QVariant.Int, "integer")])) self.assertTrue(vl.dataProvider().deleteAttributes([len(vl.dataProvider().fields()) - 1])) self.assertTrue(vl.startEditing()) self.assertEqual(vl.dataProvider().property("_debug_open_mode"), "read-write") self.assertTrue(vl.commitChanges()) self.assertEqual(vl.dataProvider().property("_debug_open_mode"), "read-write") self.assertTrue(vl.dataProvider().enterUpdateMode()) self.assertEqual(vl.dataProvider().property("_debug_open_mode"), "read-write") self.assertTrue(vl.dataProvider().leaveUpdateMode()) self.assertEqual(vl.dataProvider().property("_debug_open_mode"), "read-write") self.assertTrue(vl.dataProvider().leaveUpdateMode()) self.assertEqual(vl.dataProvider().property("_debug_open_mode"), "read-only") # Test that update mode will be implictly enabled if doing an action # that requires update mode (ret, _) = vl.dataProvider().addFeatures([QgsFeature()]) self.assertTrue(ret) self.assertEqual(vl.dataProvider().property("_debug_open_mode"), "read-write")
def processAlgorithm(self, parameters, context, feedback): source = self.parameterAsSource(parameters, self.INPUT, context) if source is None: raise QgsProcessingException(self.invalidSourceError(parameters, self.INPUT)) pointCount = self.parameterAsDouble(parameters, self.POINTS_NUMBER, context) minDistance = self.parameterAsDouble(parameters, self.MIN_DISTANCE, context) fields = QgsFields() fields.append(QgsField('id', QVariant.Int, '', 10, 0)) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, QgsWkbTypes.Point, source.sourceCrs()) if sink is None: raise QgsProcessingException(self.invalidSinkError(parameters, self.OUTPUT)) nPoints = 0 nIterations = 0 maxIterations = pointCount * 200 featureCount = source.featureCount() total = 100.0 / pointCount if pointCount else 1 index = QgsSpatialIndex() points = dict() da = QgsDistanceArea() da.setSourceCrs(source.sourceCrs(), context.transformContext()) da.setEllipsoid(context.project().ellipsoid()) request = QgsFeatureRequest() random.seed() while nIterations < maxIterations and nPoints < pointCount: if feedback.isCanceled(): break # pick random feature fid = random.randint(0, featureCount - 1) f = next(source.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 p = QgsPointXY(rx, ry) geom = QgsGeometry.fromPointXY(p) if vector.checkMinDistance(p, index, minDistance, points): f = QgsFeature(nPoints) f.initAttributes(1) f.setFields(fields) f.setAttribute('id', nPoints) f.setGeometry(geom) sink.addFeature(f, QgsFeatureSink.FastInsert) index.addFeature(f) points[nPoints] = p nPoints += 1 feedback.setProgress(int(nPoints * total)) nIterations += 1 if nPoints < pointCount: feedback.pushInfo(self.tr('Could not generate requested number of random points. ' 'Maximum number of attempts exceeded.')) return {self.OUTPUT: dest_id}
def testStatistics(self): """Test zonal stats""" TEST_DATA_DIR = unitTestDataPath() + "/zonalstatistics/" myTempPath = QDir.tempPath() + "/" testDir = QDir(TEST_DATA_DIR) for f in testDir.entryList(QDir.Files): QFile.remove(myTempPath + f) QFile.copy(TEST_DATA_DIR + f, myTempPath + f) myVector = QgsVectorLayer(myTempPath + "polys.shp", "poly", "ogr") myRaster = QgsRasterLayer(myTempPath + "edge_problem.asc", "raster", "gdal") zs = QgsZonalStatistics(myVector, myRaster, "", 1, QgsZonalStatistics.All) zs.calculateStatistics(None) feat = QgsFeature() # validate statistics for each feature request = QgsFeatureRequest().setFilterFid(0) feat = next(myVector.getFeatures(request)) myMessage = ('Expected: %f\nGot: %f\n' % (12.0, feat[1])) assert feat[1] == 12.0, myMessage myMessage = ('Expected: %f\nGot: %f\n' % (8.0, feat[2])) assert feat[2] == 8.0, myMessage myMessage = ('Expected: %f\nGot: %f\n' % (0.666666666666667, feat[3])) assert abs(feat[3] - 0.666666666666667) < 0.00001, myMessage myMessage = ('Expected: %f\nGot: %f\n' % (1.0, feat[4])) assert feat[4] == 1.0, myMessage myMessage = ('Expected: %f\nGot: %f\n' % (0.47140452079103201, feat[5])) assert abs(feat[5] - 0.47140452079103201) < 0.00001, myMessage myMessage = ('Expected: %f\nGot: %f\n' % (0.0, feat[6])) assert feat[6] == 0.0, myMessage myMessage = ('Expected: %f\nGot: %f\n' % (1.0, feat[7])) assert feat[7] == 1.0, myMessage myMessage = ('Expected: %f\nGot: %f\n' % (1.0, feat[8])) assert feat[8] == 1.0, myMessage myMessage = ('Expected: %f\nGot: %f\n' % (0.0, feat[9])) assert feat[9] == 0.0, myMessage myMessage = ('Expected: %f\nGot: %f\n' % (1.0, feat[10])) assert feat[10] == 1.0, myMessage myMessage = ('Expected: %f\nGot: %f\n' % (2.0, feat[11])) assert feat[11] == 2.0, myMessage request.setFilterFid(1) feat = next(myVector.getFeatures(request)) myMessage = ('Expected: %f\nGot: %f\n' % (9.0, feat[1])) assert feat[1] == 9.0, myMessage myMessage = ('Expected: %f\nGot: %f\n' % (5.0, feat[2])) assert feat[2] == 5.0, myMessage myMessage = ('Expected: %f\nGot: %f\n' % (0.555555555555556, feat[3])) assert abs(feat[3] - 0.555555555555556) < 0.00001, myMessage myMessage = ('Expected: %f\nGot: %f\n' % (1.0, feat[4])) assert feat[4] == 1.0, myMessage myMessage = ('Expected: %f\nGot: %f\n' % (0.49690399499995302, feat[5])) assert abs(feat[5] - 0.49690399499995302) < 0.00001, myMessage myMessage = ('Expected: %f\nGot: %f\n' % (0.0, feat[6])) assert feat[6] == 0.0, myMessage myMessage = ('Expected: %f\nGot: %f\n' % (1.0, feat[7])) assert feat[7] == 1.0, myMessage myMessage = ('Expected: %f\nGot: %f\n' % (1.0, feat[8])) assert feat[8] == 1.0, myMessage myMessage = ('Expected: %f\nGot: %f\n' % (0.0, feat[9])) assert feat[9] == 0.0, myMessage myMessage = ('Expected: %f\nGot: %f\n' % (1.0, feat[10])) assert feat[10] == 1.0, myMessage myMessage = ('Expected: %f\nGot: %f\n' % (2.0, feat[11])) assert feat[11] == 2.0, myMessage request.setFilterFid(2) feat = next(myVector.getFeatures(request)) myMessage = ('Expected: %f\nGot: %f\n' % (6.0, feat[1])) assert feat[1] == 6.0, myMessage myMessage = ('Expected: %f\nGot: %f\n' % (5.0, feat[2])) assert feat[2] == 5.0, myMessage myMessage = ('Expected: %f\nGot: %f\n' % (0.833333333333333, feat[3])) assert abs(feat[3] - 0.833333333333333) < 0.00001, myMessage myMessage = ('Expected: %f\nGot: %f\n' % (1.0, feat[4])) assert feat[4] == 1.0, myMessage myMessage = ('Expected: %f\nGot: %f\n' % (0.372677996249965, feat[5])) assert abs(feat[5] - 0.372677996249965) < 0.00001, myMessage myMessage = ('Expected: %f\nGot: %f\n' % (0.0, feat[6])) assert feat[6] == 0.0, myMessage myMessage = ('Expected: %f\nGot: %f\n' % (1.0, feat[7])) assert feat[7] == 1.0, myMessage myMessage = ('Expected: %f\nGot: %f\n' % (1.0, feat[8])) assert feat[8] == 1.0, myMessage myMessage = ('Expected: %f\nGot: %f\n' % (0.0, feat[9])) assert feat[9] == 0.0, myMessage myMessage = ('Expected: %f\nGot: %f\n' % (1.0, feat[10])) assert feat[10] == 1.0, myMessage myMessage = ('Expected: %f\nGot: %f\n' % (2.0, feat[11])) assert feat[11] == 2.0, myMessage
def processAlgorithm(self, parameters, context, feedback): source = self.parameterAsSource(parameters, self.INPUT, context) if source is None: raise QgsProcessingException( self.invalidSourceError(parameters, self.INPUT)) pointCount = self.parameterAsDouble(parameters, self.POINTS_NUMBER, context) minDistance = self.parameterAsDouble(parameters, self.MIN_DISTANCE, context) bbox = source.sourceExtent() sourceIndex = QgsSpatialIndex(source, feedback) fields = QgsFields() fields.append(QgsField('id', QVariant.Int, '', 10, 0)) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, QgsWkbTypes.Point, source.sourceCrs()) if sink is None: raise QgsProcessingException( self.invalidSinkError(parameters, self.OUTPUT)) nPoints = 0 nIterations = 0 maxIterations = pointCount * 200 total = 100.0 / pointCount if pointCount else 1 index = QgsSpatialIndex() points = dict() random.seed() while nIterations < maxIterations and nPoints < pointCount: if feedback.isCanceled(): break rx = bbox.xMinimum() + bbox.width() * random.random() ry = bbox.yMinimum() + bbox.height() * random.random() p = QgsPointXY(rx, ry) geom = QgsGeometry.fromPointXY(p) ids = sourceIndex.intersects(geom.buffer(5, 5).boundingBox()) if len(ids) > 0 and \ vector.checkMinDistance(p, index, minDistance, points): request = QgsFeatureRequest().setFilterFids( ids).setSubsetOfAttributes([]) for f in source.getFeatures(request): if feedback.isCanceled(): break tmpGeom = f.geometry() if geom.within(tmpGeom): f = QgsFeature(nPoints) f.initAttributes(1) f.setFields(fields) f.setAttribute('id', nPoints) f.setGeometry(geom) sink.addFeature(f, QgsFeatureSink.FastInsert) index.addFeature(f) points[nPoints] = p nPoints += 1 feedback.setProgress(int(nPoints * total)) nIterations += 1 if nPoints < pointCount: feedback.pushInfo( self.tr( 'Could not generate requested number of random points. ' 'Maximum number of attempts exceeded.')) return {self.OUTPUT: dest_id}
def buffering(feedback, context, sink, distance, field, useField, source, dissolve, segments, endCapStyle=1, joinStyle=1, miterLimit=2): if useField: field = source.fields().lookupField(field) outFeat = QgsFeature() current = 0 total = 100.0 / source.featureCount() if source.featureCount() else 0 # With dissolve if dissolve: attributes_to_fetch = [] if useField: attributes_to_fetch.append(field) features = source.getFeatures( QgsFeatureRequest().setSubsetOfAttributes(attributes_to_fetch)) buffered_geometries = [] for inFeat in features: if feedback.isCanceled(): break attrs = inFeat.attributes() if useField: value = attrs[field] else: value = distance inGeom = inFeat.geometry() buffered_geometries.append( inGeom.buffer(float(value), segments, endCapStyle, joinStyle, miterLimit)) current += 1 feedback.setProgress(int(current * total)) final_geometry = QgsGeometry.unaryUnion(buffered_geometries) outFeat.setGeometry(final_geometry) outFeat.setAttributes(attrs) sink.addFeature(outFeat, QgsFeatureSink.FastInsert) else: features = source.getFeatures() # Without dissolve for inFeat in features: if feedback.isCanceled(): break attrs = inFeat.attributes() if useField: value = attrs[field] else: value = distance inGeom = inFeat.geometry() outFeat = QgsFeature() outGeom = inGeom.buffer(float(value), segments, endCapStyle, joinStyle, miterLimit) outFeat.setGeometry(outGeom) outFeat.setAttributes(attrs) sink.addFeature(outFeat, QgsFeatureSink.FastInsert) current += 1 feedback.setProgress(int(current * total))
def processAlgorithm(self, parameters, context, feedback): source = self.parameterAsSource(parameters, self.INPUT, context) buf = self.parameterAsDouble(parameters, self.BUFFER, context) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, source.fields(), QgsWkbTypes.Polygon, source.sourceCrs()) outFeat = QgsFeature() extent = source.sourceExtent() extraX = extent.height() * (buf / 100.0) extraY = extent.width() * (buf / 100.0) height = extent.height() width = extent.width() c = voronoi.Context() pts = [] ptDict = {} ptNdx = -1 features = source.getFeatures() total = 100.0 / source.featureCount() if source.featureCount() else 0 for current, inFeat in enumerate(features): if feedback.isCanceled(): break geom = inFeat.geometry() point = geom.asPoint() x = point.x() - extent.xMinimum() y = point.y() - extent.yMinimum() pts.append((x, y)) ptNdx += 1 ptDict[ptNdx] = inFeat.id() feedback.setProgress(int(current * total)) if len(pts) < 3: raise QgsProcessingException( self.tr('Input file should contain at least 3 points. Choose ' 'another file and try again.')) uniqueSet = set(item for item in pts) ids = [pts.index(item) for item in uniqueSet] sl = voronoi.SiteList([ voronoi.Site(i[0], i[1], sitenum=j) for (j, i) in enumerate(uniqueSet) ]) voronoi.voronoi(sl, c) inFeat = QgsFeature() current = 0 if len(c.polygons) == 0: raise QgsProcessingException( self.tr('There were no polygons created.')) total = 100.0 / len(c.polygons) for (site, edges) in list(c.polygons.items()): if feedback.isCanceled(): break request = QgsFeatureRequest().setFilterFid(ptDict[ids[site]]) inFeat = next(source.getFeatures(request)) lines = self.clip_voronoi(edges, c, width, height, extent, extraX, extraY) geom = QgsGeometry.fromMultiPointXY(lines) geom = QgsGeometry(geom.convexHull()) outFeat.setGeometry(geom) outFeat.setAttributes(inFeat.attributes()) sink.addFeature(outFeat, QgsFeatureSink.FastInsert) current += 1 feedback.setProgress(int(current * total)) return {self.OUTPUT: dest_id}
def testSubsetString(self): if not self.source.supportsSubsetString(): print('Provider does not support subset strings') return subset = self.getSubsetString() self.source.setSubsetString(subset) self.assertEqual(self.source.subsetString(), subset) result = set([f['pk'] for f in self.source.getFeatures()]) all_valid = (all(f.isValid() for f in self.source.getFeatures())) self.source.setSubsetString(None) expected = set([2, 3, 4]) assert set( expected ) == result, 'Expected {} and got {} when testing subset string {}'.format( set(expected), result, subset) self.assertTrue(all_valid) # Subset string AND filter rect self.source.setSubsetString(subset) extent = QgsRectangle(-70, 70, -60, 75) request = QgsFeatureRequest().setFilterRect(extent) result = set([f['pk'] for f in self.source.getFeatures(request)]) all_valid = (all(f.isValid() for f in self.source.getFeatures(request))) self.source.setSubsetString(None) expected = set([2]) assert set( expected ) == result, 'Expected {} and got {} when testing subset string {}'.format( set(expected), result, subset) self.assertTrue(all_valid) # Subset string AND filter rect, version 2 self.source.setSubsetString(subset) extent = QgsRectangle(-71, 65, -60, 80) result = set([ f['pk'] for f in self.source.getFeatures( QgsFeatureRequest().setFilterRect(extent)) ]) self.source.setSubsetString(None) expected = set([2, 4]) assert set( expected ) == result, 'Expected {} and got {} when testing subset string {}'.format( set(expected), result, subset) # Subset string AND expression self.source.setSubsetString(subset) request = QgsFeatureRequest().setFilterExpression('length("name")=5') result = set([f['pk'] for f in self.source.getFeatures(request)]) all_valid = (all(f.isValid() for f in self.source.getFeatures(request))) self.source.setSubsetString(None) expected = set([2, 4]) assert set( expected ) == result, 'Expected {} and got {} when testing subset string {}'.format( set(expected), result, subset) self.assertTrue(all_valid)
def processAlgorithm(self, parameters, context, feedback): sourceA = self.parameterAsSource(parameters, self.INPUT, context) sourceB = self.parameterAsSource(parameters, self.OVERLAY, context) geomType = QgsWkbTypes.multiType(sourceA.wkbType()) fields = QgsProcessingUtils.combineFields(sourceA.fields(), sourceB.fields()) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, geomType, sourceA.sourceCrs()) featA = QgsFeature() featB = QgsFeature() outFeat = QgsFeature() indexA = QgsSpatialIndex(sourceA, feedback) indexB = QgsSpatialIndex( sourceB.getFeatures(QgsFeatureRequest().setSubsetOfAttributes( []).setDestinationCrs(sourceA.sourceCrs(), context.transformContext())), feedback) total = 100.0 / (sourceA.featureCount() * sourceB.featureCount()) if sourceA.featureCount( ) and sourceB.featureCount() else 1 count = 0 for featA in sourceA.getFeatures(): if feedback.isCanceled(): break lstIntersectingB = [] geom = featA.geometry() atMapA = featA.attributes() intersects = indexB.intersects(geom.boundingBox()) if len(intersects) < 1: try: geom.convertToMultiType() outFeat.setGeometry(geom) outFeat.setAttributes(atMapA) sink.addFeature(outFeat, QgsFeatureSink.FastInsert) except: # This really shouldn't happen, as we haven't # edited the input geom at all feedback.pushInfo( self. tr('Feature geometry error: One or more output features ignored due to invalid geometry.' )) else: request = QgsFeatureRequest().setFilterFids( intersects).setSubsetOfAttributes([]) request.setDestinationCrs(sourceA.sourceCrs(), context.transformContext()) engine = QgsGeometry.createGeometryEngine(geom.constGet()) engine.prepareGeometry() for featB in sourceB.getFeatures(request): atMapB = featB.attributes() tmpGeom = featB.geometry() if engine.intersects(tmpGeom.constGet()): int_geom = geom.intersection(tmpGeom) lstIntersectingB.append(tmpGeom) if not int_geom: # There was a problem creating the intersection feedback.pushInfo( self. tr('Feature geometry error: One or more output features ignored due to invalid geometry.' )) int_geom = QgsGeometry() else: int_geom = QgsGeometry(int_geom) if int_geom.wkbType( ) == QgsWkbTypes.Unknown or QgsWkbTypes.flatType( int_geom.wkbType( )) == QgsWkbTypes.GeometryCollection: # Intersection produced different geomety types temp_list = int_geom.asGeometryCollection() for i in temp_list: if i.type() == geom.type(): int_geom = QgsGeometry(i) try: int_geom.convertToMultiType() outFeat.setGeometry(int_geom) outFeat.setAttributes(atMapA + atMapB) sink.addFeature( outFeat, QgsFeatureSink.FastInsert) except: feedback.pushInfo( self. tr('Feature geometry error: One or more output features ignored due to invalid geometry.' )) else: # Geometry list: prevents writing error # in geometries of different types # produced by the intersection # fix #3549 if QgsWkbTypes.geometryType(int_geom.wkbType( )) == QgsWkbTypes.geometryType(geomType): try: int_geom.convertToMultiType() outFeat.setGeometry(int_geom) outFeat.setAttributes(atMapA + atMapB) sink.addFeature(outFeat, QgsFeatureSink.FastInsert) except: feedback.pushInfo( self. tr('Feature geometry error: One or more output features ignored due to invalid geometry.' )) # the remaining bit of featA's geometry # if there is nothing left, this will just silently fail and we're good diff_geom = QgsGeometry(geom) if len(lstIntersectingB) != 0: intB = QgsGeometry.unaryUnion(lstIntersectingB) diff_geom = diff_geom.difference(intB) if diff_geom.wkbType( ) == QgsWkbTypes.Unknown or QgsWkbTypes.flatType( diff_geom.wkbType()) == QgsWkbTypes.GeometryCollection: temp_list = diff_geom.asGeometryCollection() for i in temp_list: if i.type() == geom.type(): diff_geom = QgsGeometry(i) try: diff_geom.convertToMultiType() outFeat.setGeometry(diff_geom) outFeat.setAttributes(atMapA) sink.addFeature(outFeat, QgsFeatureSink.FastInsert) except: feedback.pushInfo( self. tr('Feature geometry error: One or more output features ignored due to invalid geometry.' )) count += 1 feedback.setProgress(int(count * total)) length = len(sourceA.fields()) atMapA = [None] * length for featA in sourceB.getFeatures(QgsFeatureRequest().setDestinationCrs( sourceA.sourceCrs(), context.transformContext())): if feedback.isCanceled(): break add = False geom = featA.geometry() diff_geom = QgsGeometry(geom) atMap = [None] * length atMap.extend(featA.attributes()) intersects = indexA.intersects(geom.boundingBox()) if len(intersects) < 1: try: geom.convertToMultiType() outFeat.setGeometry(geom) outFeat.setAttributes(atMap) sink.addFeature(outFeat, QgsFeatureSink.FastInsert) except: feedback.pushInfo( self. tr('Feature geometry error: One or more output features ignored due to invalid geometry.' )) else: request = QgsFeatureRequest().setFilterFids( intersects).setSubsetOfAttributes([]) request.setDestinationCrs(sourceA.sourceCrs(), context.transformContext()) # use prepared geometries for faster intersection tests engine = QgsGeometry.createGeometryEngine(diff_geom.constGet()) engine.prepareGeometry() for featB in sourceA.getFeatures(request): atMapB = featB.attributes() tmpGeom = featB.geometry() if engine.intersects(tmpGeom.constGet()): add = True diff_geom = QgsGeometry(diff_geom.difference(tmpGeom)) else: try: # Ihis only happens if the bounding box # intersects, but the geometry doesn't diff_geom.convertToMultiType() outFeat.setGeometry(diff_geom) outFeat.setAttributes(atMap) sink.addFeature(outFeat, QgsFeatureSink.FastInsert) except: feedback.pushInfo( self. tr('Feature geometry error: One or more output features ignored due to invalid geometry.' )) if add: try: diff_geom.convertToMultiType() outFeat.setGeometry(diff_geom) outFeat.setAttributes(atMap) sink.addFeature(outFeat, QgsFeatureSink.FastInsert) except: feedback.pushInfo( self. tr('Feature geometry error: One or more output features ignored due to invalid geometry.' )) count += 1 feedback.setProgress(int(count * total)) return {self.OUTPUT: dest_id}
def processAlgorithm(self, parameters, context, feedback): source = self.parameterAsSource(parameters, self.INPUT, context) if source is None: raise QgsProcessingException( self.invalidSourceError(parameters, self.INPUT)) fields = QgsFields() fields.append(QgsField('POINTA', QVariant.Double, '', 24, 15)) fields.append(QgsField('POINTB', QVariant.Double, '', 24, 15)) fields.append(QgsField('POINTC', QVariant.Double, '', 24, 15)) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, QgsWkbTypes.Polygon, source.sourceCrs()) if sink is None: raise QgsProcessingException( self.invalidSinkError(parameters, self.OUTPUT)) pts = [] ptDict = {} ptNdx = -1 c = voronoi.Context() features = source.getFeatures() total = 100.0 / source.featureCount() if source.featureCount() else 0 for current, inFeat in enumerate(features): if feedback.isCanceled(): break geom = QgsGeometry(inFeat.geometry()) if geom.isNull(): continue if geom.isMultipart(): points = geom.asMultiPoint() else: points = [geom.asPoint()] for n, point in enumerate(points): x = point.x() y = point.y() pts.append((x, y)) ptNdx += 1 ptDict[ptNdx] = (inFeat.id(), n) feedback.setProgress(int(current * total)) if len(pts) < 3: raise QgsProcessingException( self.tr('Input file should contain at least 3 points. Choose ' 'another file and try again.')) uniqueSet = set(item for item in pts) ids = [pts.index(item) for item in uniqueSet] sl = voronoi.SiteList([voronoi.Site(*i) for i in uniqueSet]) c.triangulate = True voronoi.voronoi(sl, c) triangles = c.triangles feat = QgsFeature() total = 100.0 / len(triangles) if triangles else 1 for current, triangle in enumerate(triangles): if feedback.isCanceled(): break indices = list(triangle) indices.append(indices[0]) polygon = [] attrs = [] step = 0 for index in indices: fid, n = ptDict[ids[index]] request = QgsFeatureRequest().setFilterFid(fid) inFeat = next(source.getFeatures(request)) geom = QgsGeometry(inFeat.geometry()) if geom.isMultipart(): point = QgsPointXY(geom.asMultiPoint()[n]) else: point = QgsPointXY(geom.asPoint()) polygon.append(point) if step <= 3: attrs.append(ids[index]) step += 1 feat.setAttributes(attrs) geometry = QgsGeometry().fromPolygonXY([polygon]) feat.setGeometry(geometry) sink.addFeature(feat, QgsFeatureSink.FastInsert) feedback.setProgress(int(current * total)) return {self.OUTPUT: dest_id}
def processAlgorithm(self, feedback): layer = dataobjects.getLayerFromString(self.getParameterValue(ConcaveHull.INPUT)) alpha = self.getParameterValue(self.ALPHA) holes = self.getParameterValue(self.HOLES) no_multigeom = self.getParameterValue(self.NO_MULTIGEOMETRY) # Delaunay triangulation from input point layer feedback.setProgressText(self.tr('Creating Delaunay triangles...')) delone_triangles = processing.run("qgis:delaunaytriangulation", layer, None)['OUTPUT'] delaunay_layer = dataobjects.getLayerFromString(delone_triangles) # Get max edge length from Delaunay triangles feedback.setProgressText(self.tr('Computing edges max length...')) features = delaunay_layer.getFeatures() if len(features) == 0: raise GeoAlgorithmExecutionException(self.tr('No Delaunay triangles created.')) counter = 50. / len(features) lengths = [] edges = {} for feat in features: line = feat.geometry().asPolygon()[0] for i in range(len(line) - 1): lengths.append(sqrt(line[i].sqrDist(line[i + 1]))) edges[feat.id()] = max(lengths[-3:]) feedback.setProgress(feat.id() * counter) max_length = max(lengths) # Get features with longest edge longer than alpha*max_length feedback.setProgressText(self.tr('Removing features...')) counter = 50. / len(edges) i = 0 ids = [] for id, max_len in list(edges.items()): if max_len > alpha * max_length: ids.append(id) feedback.setProgress(50 + i * counter) i += 1 # Remove features delaunay_layer.selectByIds(ids) delaunay_layer.startEditing() delaunay_layer.deleteSelectedFeatures() delaunay_layer.commitChanges() # Dissolve all Delaunay triangles feedback.setProgressText(self.tr('Dissolving Delaunay triangles...')) dissolved = processing.run("qgis:dissolve", delaunay_layer, True, None, None)['OUTPUT'] dissolved_layer = dataobjects.getLayerFromString(dissolved) # Save result feedback.setProgressText(self.tr('Saving data...')) feat = QgsFeature() dissolved_layer.getFeatures(QgsFeatureRequest().setFilterFid(0)).nextFeature(feat) writer = self.getOutputFromName(self.OUTPUT).getVectorWriter( layer.fields().toList(), QgsWkbTypes.Polygon, layer.crs()) geom = feat.geometry() if no_multigeom and geom.isMultipart(): # Only singlepart geometries are allowed geom_list = geom.asMultiPolygon() for single_geom_list in geom_list: single_feature = QgsFeature() single_geom = QgsGeometry.fromPolygon(single_geom_list) if not holes: # Delete holes deleted = True while deleted: deleted = single_geom.deleteRing(1) single_feature.setGeometry(single_geom) writer.addFeature(single_feature) else: # Multipart geometries are allowed if not holes: # Delete holes deleted = True while deleted: deleted = geom.deleteRing(1) writer.addFeature(feat) del writer
def processAlgorithm(self, parameters, context, feedback): source = self.parameterAsSource(parameters, self.INPUT, context) if source is None: raise QgsProcessingException( self.invalidSourceError(parameters, self.INPUT)) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, source.fields(), source.wkbType(), source.sourceCrs()) if sink is None: raise QgsProcessingException( self.invalidSinkError(parameters, self.OUTPUT)) features = source.getFeatures( QgsFeatureRequest().setSubsetOfAttributes([])) total = 100.0 / source.featureCount() if source.featureCount() else 0 geoms = dict() null_geom_features = set() index = QgsSpatialIndex() for current, f in enumerate(features): if feedback.isCanceled(): break if not f.hasGeometry(): null_geom_features.add(f.id()) continue geoms[f.id()] = f.geometry() index.addFeature(f) feedback.setProgress(int(0.10 * current * total)) # takes about 10% of time # start by assuming everything is unique, and chop away at this list unique_features = dict(geoms) current = 0 removed = 0 for feature_id, geometry in geoms.items(): if feedback.isCanceled(): break if feature_id not in unique_features: # feature was already marked as a duplicate continue candidates = index.intersects(geometry.boundingBox()) candidates.remove(feature_id) for candidate_id in candidates: if candidate_id not in unique_features: # candidate already marked as a duplicate (not sure if this is possible, # since it would mean the current feature would also have to be a duplicate! # but let's be safe!) continue if geometry.isGeosEqual(geoms[candidate_id]): # candidate is a duplicate of feature del unique_features[candidate_id] removed += 1 current += 1 feedback.setProgress(int(0.80 * current * total) + 10) # takes about 80% of time # now, fetch all the feature attributes for the unique features only # be super-smart and don't re-fetch geometries distinct_geoms = set(unique_features.keys()) output_feature_ids = distinct_geoms.union(null_geom_features) total = 100.0 / len(output_feature_ids) if output_feature_ids else 1 request = QgsFeatureRequest().setFilterFids( list(output_feature_ids)).setFlags(QgsFeatureRequest.NoGeometry) for current, f in enumerate(source.getFeatures(request)): if feedback.isCanceled(): break # use already fetched geometry if f.id() not in null_geom_features: f.setGeometry(unique_features[f.id()]) sink.addFeature(f, QgsFeatureSink.FastInsert) feedback.setProgress(int(0.10 * current * total) + 90) # takes about 10% of time feedback.pushInfo( self.tr('{} duplicate features removed'.format(removed))) return { self.OUTPUT: dest_id, self.DUPLICATE_COUNT: removed, self.RETAINED_COUNT: len(output_feature_ids) }
def processAlgorithm(self, parameters, context, feedback): source = self.parameterAsSource(parameters, self.INPUT, context) if source is None: raise QgsProcessingException( self.invalidSourceError(parameters, self.INPUT)) fields = source.fields() fields.append(QgsField('vertex_pos', QVariant.Int)) fields.append(QgsField('vertex_index', QVariant.Int)) fields.append(QgsField('vertex_part', QVariant.Int)) if QgsWkbTypes.geometryType( source.wkbType()) == QgsWkbTypes.PolygonGeometry: fields.append(QgsField('vertex_part_ring', QVariant.Int)) fields.append(QgsField('vertex_part_index', QVariant.Int)) fields.append(QgsField('distance', QVariant.Double)) fields.append(QgsField('angle', QVariant.Double)) wkb_type = QgsWkbTypes.Point if QgsWkbTypes.hasM(source.wkbType()): wkb_type = QgsWkbTypes.addM(wkb_type) if QgsWkbTypes.hasZ(source.wkbType()): wkb_type = QgsWkbTypes.addZ(wkb_type) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, wkb_type, source.sourceCrs()) if sink is None: raise QgsProcessingException( self.invalidSinkError(parameters, self.OUTPUT)) vertex_indices_string = self.parameterAsString(parameters, self.VERTICES, context) indices = [] for vertex in vertex_indices_string.split(','): try: indices.append(int(vertex)) except: raise QgsProcessingException( self.tr('\'{}\' is not a valid vertex index').format( vertex)) features = source.getFeatures( QgsFeatureRequest(), QgsProcessingFeatureSource.FlagSkipGeometryValidityChecks) total = 100.0 / source.featureCount() if source.featureCount() else 0 for current, f in enumerate(features): if feedback.isCanceled(): break input_geometry = f.geometry() if not input_geometry: sink.addFeature(f, QgsFeatureSink.FastInsert) else: total_vertices = input_geometry.constGet().nCoordinates() for vertex in indices: if vertex < 0: vertex_index = total_vertices + vertex else: vertex_index = vertex if vertex_index < 0 or vertex_index >= total_vertices: continue (success, vertex_id ) = input_geometry.vertexIdFromVertexNr(vertex_index) distance = input_geometry.distanceToVertex(vertex_index) angle = math.degrees( input_geometry.angleAtVertex(vertex_index)) output_feature = QgsFeature() attrs = f.attributes() attrs.append(vertex) attrs.append(vertex_index) attrs.append(vertex_id.part) if QgsWkbTypes.geometryType( source.wkbType()) == QgsWkbTypes.PolygonGeometry: attrs.append(vertex_id.ring) attrs.append(vertex_id.vertex) attrs.append(distance) attrs.append(angle) output_feature.setAttributes(attrs) point = input_geometry.vertexAt(vertex_index) output_feature.setGeometry(QgsGeometry(point)) sink.addFeature(output_feature, QgsFeatureSink.FastInsert) feedback.setProgress(int(current * total)) return {self.OUTPUT: dest_id}
def processAlgorithm(self, progress): inLayer = dataobjects.getObjectFromUri(self.getParameterValue(self.INPUT)) boundary = self.getParameterValue(self.MODE) == self.MODE_BOUNDARY smallestArea = self.getParameterValue(self.MODE) == self.MODE_SMALLEST_AREA keepSelection = self.getParameterValue(self.KEEPSELECTION) processLayer = vector.duplicateInMemory(inLayer) if not keepSelection: # Make a selection with the values provided attribute = self.getParameterValue(self.ATTRIBUTE) comparison = self.comparisons[self.getParameterValue(self.COMPARISON)] comparisonvalue = self.getParameterValue(self.COMPARISONVALUE) selectindex = vector.resolveFieldIndex(processLayer, attribute) selectType = processLayer.fields()[selectindex].type() selectionError = False if selectType == 2: try: y = int(comparisonvalue) except ValueError: selectionError = True msg = self.tr('Cannot convert "%s" to integer' % unicode(comparisonvalue)) elif selectType == 6: try: y = float(comparisonvalue) except ValueError: selectionError = True msg = self.tr('Cannot convert "%s" to float' % unicode(comparisonvalue)) elif selectType == 10: # 10: string, boolean try: y = unicode(comparisonvalue) except ValueError: selectionError = True msg = self.tr('Cannot convert "%s" to unicode' % unicode(comparisonvalue)) elif selectType == 14: # date dateAndFormat = comparisonvalue.split(' ') if len(dateAndFormat) == 1: # QDate object y = QLocale.system().toDate(dateAndFormat[0]) if y.isNull(): msg = self.tr('Cannot convert "%s" to date with system date format %s' % (unicode(dateAndFormat), QLocale.system().dateFormat())) elif len(dateAndFormat) == 2: y = QDate.fromString(dateAndFormat[0], dateAndFormat[1]) if y.isNull(): msg = self.tr('Cannot convert "%s" to date with format string "%s"' % (unicode(dateAndFormat[0]), dateAndFormat[1])) else: y = QDate() msg = '' if y.isNull(): # Conversion was unsuccessfull selectionError = True msg += self.tr('Enter the date and the date format, e.g. "07.26.2011" "MM.dd.yyyy".') if (comparison == 'begins with' or comparison == 'contains') \ and selectType != 10: selectionError = True msg = self.tr('"%s" can only be used with string fields' % comparison) selected = [] if selectionError: raise GeoAlgorithmExecutionException( self.tr('Error in selection input: %s' % msg)) else: for feature in processLayer.getFeatures(): aValue = feature.attributes()[selectindex] if aValue is None: continue if selectType == 2: x = int(aValue) elif selectType == 6: x = float(aValue) elif selectType == 10: # 10: string, boolean x = unicode(aValue) elif selectType == 14: # date x = aValue # should be date match = False if comparison == '==': match = x == y elif comparison == '!=': match = x != y elif comparison == '>': match = x > y elif comparison == '>=': match = x >= y elif comparison == '<': match = x < y elif comparison == '<=': match = x <= y elif comparison == 'begins with': match = x.startswith(y) elif comparison == 'contains': match = x.find(y) >= 0 if match: selected.append(feature.id()) processLayer.setSelectedFeatures(selected) if processLayer.selectedFeatureCount() == 0: ProcessingLog.addToLog(ProcessingLog.LOG_WARNING, self.tr('%s: (No selection in input layer "%s")' % (self.commandLineName(), self.getParameterValue(self.INPUT)))) # Keep references to the features to eliminate featToEliminate = [] for aFeat in processLayer.selectedFeatures(): featToEliminate.append(aFeat) # Delete all features to eliminate in processLayer (we won't save this) processLayer.startEditing() processLayer.deleteSelectedFeatures() # ANALYZE if len(featToEliminate) > 0: # Prevent zero division start = 20.00 add = 80.00 / len(featToEliminate) else: start = 100 progress.setPercentage(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)): feat = featToEliminate.pop() geom2Eliminate = QgsGeometry(feat.geometry()) bbox = geom2Eliminate.boundingBox() fit = processLayer.getFeatures( QgsFeatureRequest().setFilterRect(bbox)) mergeWithFid = None mergeWithGeom = None max = 0 min = -1 selFeat = QgsFeature() while fit.nextFeature(selFeat): selGeom = QgsGeometry(selFeat.geometry()) if geom2Eliminate.intersects(selGeom): # We have a candidate iGeom = geom2Eliminate.intersection(selGeom) if iGeom is None: 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 GeoAlgorithmExecutionException( self.tr('Could not replace geometry of feature with id %s' % mergeWithFid)) start = start + add progress.setPercentage(start) else: featNotEliminated.append(feat) # End for featToEliminate featToEliminate = featNotEliminated # End while # Create output provider = processLayer.dataProvider() output = self.getOutputFromName(self.OUTPUT) writer = output.getVectorWriter(provider.fields(), provider.geometryType(), processLayer.crs()) # Write all features that are left over to output layer iterator = processLayer.getFeatures() for feature in iterator: writer.addFeature(feature) # Leave processLayer untouched processLayer.rollBack() for feature in featNotEliminated: writer.addFeature(feature)
def processAlgorithm(self, parameters, context, feedback): source = self.parameterAsSource(parameters, self.INPUT, context) fields = source.fields() x_field_index = fields.lookupField( self.parameterAsString(parameters, self.XFIELD, context)) y_field_index = fields.lookupField( self.parameterAsString(parameters, self.YFIELD, context)) z_field_index = -1 if self.parameterAsString(parameters, self.ZFIELD, context): z_field_index = fields.lookupField( self.parameterAsString(parameters, self.ZFIELD, context)) m_field_index = -1 if self.parameterAsString(parameters, self.MFIELD, context): m_field_index = fields.lookupField( self.parameterAsString(parameters, self.MFIELD, context)) wkb_type = QgsWkbTypes.Point if z_field_index >= 0: wkb_type = QgsWkbTypes.addZ(wkb_type) if m_field_index >= 0: wkb_type = QgsWkbTypes.addM(wkb_type) target_crs = self.parameterAsCrs(parameters, self.TARGET_CRS, context) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, wkb_type, target_crs) request = QgsFeatureRequest().setFlags(QgsFeatureRequest.NoGeometry) features = source.getFeatures( QgsFeatureRequest(), QgsProcessingFeatureSource.FlagSkipGeometryValidityChecks) total = 100.0 / source.featureCount() if source.featureCount() else 0 for current, feature in enumerate(features): if feedback.isCanceled(): break feedback.setProgress(int(current * total)) attrs = feature.attributes() try: x = float(attrs[x_field_index]) y = float(attrs[y_field_index]) point = QgsPoint(x, y) if z_field_index >= 0: try: point.addZValue(float(attrs[z_field_index])) except: point.addZValue(0.0) if m_field_index >= 0: try: point.addMValue(float(attrs[m_field_index])) except: point.addMValue(0.0) feature.setGeometry(QgsGeometry(point)) except: pass # no geometry sink.addFeature(feature) return {self.OUTPUT: dest_id}
def test_sql2(self): l2 = QgsVectorLayer(os.path.join(self.testDataDir, "france_parts.shp"), "france_parts", "ogr", QgsVectorLayer.LayerOptions(False)) self.assertEqual(l2.isValid(), True) QgsProject.instance().addMapLayer(l2) query = toPercent("SELECT * FROM france_parts") l4 = QgsVectorLayer("?query=%s&uid=ObjectId" % query, "tt", "virtual") self.assertEqual(l4.isValid(), True) self.assertEqual(l4.dataProvider().wkbType(), 6) self.assertEqual(l4.dataProvider().crs().postgisSrid(), 4326) n = 0 r = QgsFeatureRequest(QgsRectangle(-1.677, 49.624, -0.816, 49.086)) for f in l4.getFeatures(r): self.assertEqual(f.geometry() is not None, True) self.assertEqual(f.attributes()[0], 2661) n += 1 self.assertEqual(n, 1) # use uid query = toPercent("SELECT * FROM france_parts") l5 = QgsVectorLayer( "?query=%s&geometry=geometry:polygon:4326&uid=ObjectId" % query, "tt", "virtual") self.assertEqual(l5.isValid(), True) idSum = sum(f.id() for f in l5.getFeatures()) self.assertEqual(idSum, 10659) r = QgsFeatureRequest(2661) idSum2 = sum(f.id() for f in l5.getFeatures(r)) self.assertEqual(idSum2, 2661) r = QgsFeatureRequest() r.setFilterFids([2661, 2664]) self.assertEqual(sum(f.id() for f in l5.getFeatures(r)), 2661 + 2664) # test attribute subset r = QgsFeatureRequest() r.setFlags(QgsFeatureRequest.SubsetOfAttributes) r.setSubsetOfAttributes([1]) s = [(f.id(), f.attributes()[1]) for f in l5.getFeatures(r)] self.assertEqual(sum([x[0] for x in s]), 10659) self.assertEqual(sum([x[1] for x in s]), 3064.0) # test NoGeometry # by request flag r = QgsFeatureRequest() r.setFlags(QgsFeatureRequest.NoGeometry) self.assertEqual(all([not f.hasGeometry() for f in l5.getFeatures(r)]), True) # test subset self.assertEqual(l5.dataProvider().featureCount(), 4) l5.setSubsetString("ObjectId = 2661") idSum2 = sum(f.id() for f in l5.getFeatures(r)) self.assertEqual(idSum2, 2661) self.assertEqual(l5.dataProvider().featureCount(), 1)
def processAlgorithm(self, parameters, context, feedback): source = self.parameterAsSource(parameters, self.INPUT, context) if source is None: raise QgsProcessingException(self.invalidSourceError(parameters, self.INPUT)) join_source = self.parameterAsSource(parameters, self.JOIN, context) if join_source is None: raise QgsProcessingException(self.invalidSourceError(parameters, self.JOIN)) join_fields = self.parameterAsFields(parameters, self.JOIN_FIELDS, context) discard_nomatch = self.parameterAsBool(parameters, self.DISCARD_NONMATCHING, context) summaries = [self.statistics[i][0] for i in sorted(self.parameterAsEnums(parameters, self.SUMMARIES, context))] if not summaries: # none selected, so use all summaries = [s[0] for s in self.statistics] source_fields = source.fields() fields_to_join = QgsFields() join_field_indexes = [] if not join_fields: # no fields selected, use all join_fields = [join_source.fields().at(i).name() for i in range(len(join_source.fields()))] def addFieldKeepType(original, stat): """ Adds a field to the output, keeping the same data type as the original """ field = QgsField(original) field.setName(field.name() + '_' + stat) fields_to_join.append(field) def addField(original, stat, type): """ Adds a field to the output, with a specified type """ field = QgsField(original) field.setName(field.name() + '_' + stat) field.setType(type) if type == QVariant.Double: field.setLength(20) field.setPrecision(6) fields_to_join.append(field) numeric_fields = ( ('count', QVariant.Int, 'count'), ('unique', QVariant.Int, 'variety'), ('min', QVariant.Double, 'min'), ('max', QVariant.Double, 'max'), ('range', QVariant.Double, 'range'), ('sum', QVariant.Double, 'sum'), ('mean', QVariant.Double, 'mean'), ('median', QVariant.Double, 'median'), ('stddev', QVariant.Double, 'stDev'), ('minority', QVariant.Double, 'minority'), ('majority', QVariant.Double, 'majority'), ('q1', QVariant.Double, 'firstQuartile'), ('q3', QVariant.Double, 'thirdQuartile'), ('iqr', QVariant.Double, 'interQuartileRange') ) datetime_fields = ( ('count', QVariant.Int, 'count'), ('unique', QVariant.Int, 'countDistinct'), ('empty', QVariant.Int, 'countMissing'), ('filled', QVariant.Int), ('min', None), ('max', None) ) string_fields = ( ('count', QVariant.Int, 'count'), ('unique', QVariant.Int, 'countDistinct'), ('empty', QVariant.Int, 'countMissing'), ('filled', QVariant.Int), ('min', None, 'min'), ('max', None, 'max'), ('min_length', QVariant.Int, 'minLength'), ('max_length', QVariant.Int, 'maxLength'), ('mean_length', QVariant.Double, 'meanLength') ) field_types = [] for f in join_fields: idx = join_source.fields().lookupField(f) if idx >= 0: join_field_indexes.append(idx) join_field = join_source.fields().at(idx) if join_field.isNumeric(): field_types.append('numeric') field_list = numeric_fields elif join_field.type() in (QVariant.Date, QVariant.Time, QVariant.DateTime): field_types.append('datetime') field_list = datetime_fields else: field_types.append('string') field_list = string_fields for f in field_list: if f[0] in summaries: if f[1] is not None: addField(join_field, f[0], f[1]) else: addFieldKeepType(join_field, f[0]) out_fields = QgsProcessingUtils.combineFields(source_fields, fields_to_join) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, out_fields, source.wkbType(), source.sourceCrs()) if sink is None: raise QgsProcessingException(self.invalidSinkError(parameters, self.OUTPUT)) # do the join predicates = [self.predicates[i][0] for i in self.parameterAsEnums(parameters, self.PREDICATE, context)] features = source.getFeatures() total = 100.0 / source.featureCount() if source.featureCount() else 0 # bounding box transform bbox_transform = QgsCoordinateTransform(source.sourceCrs(), join_source.sourceCrs(), context.project()) for current, f in enumerate(features): if feedback.isCanceled(): break if not f.hasGeometry(): if not discard_nomatch: # ensure consistent count of attributes - otherwise non matching # features will have incorrect attribute length # and provider may reject them attrs = f.attributes() if len(attrs) < len(out_fields): attrs += [NULL] * (len(out_fields) - len(attrs)) f.setAttributes(attrs) sink.addFeature(f, QgsFeatureSink.FastInsert) continue bbox = bbox_transform.transformBoundingBox(f.geometry().boundingBox()) engine = None values = [] request = QgsFeatureRequest().setFilterRect(bbox).setSubsetOfAttributes(join_field_indexes).setDestinationCrs(source.sourceCrs(), context.transformContext()) for test_feat in join_source.getFeatures(request): if feedback.isCanceled(): break join_attributes = [] for a in join_field_indexes: join_attributes.append(test_feat.attributes()[a]) if engine is None: engine = QgsGeometry.createGeometryEngine(f.geometry().constGet()) engine.prepareGeometry() for predicate in predicates: if getattr(engine, predicate)(test_feat.geometry().constGet()): values.append(join_attributes) break feedback.setProgress(int(current * total)) if len(values) == 0: if discard_nomatch: continue else: # ensure consistent count of attributes - otherwise non matching # features will have incorrect attribute length # and provider may reject them attrs = f.attributes() if len(attrs) < len(out_fields): attrs += [NULL] * (len(out_fields) - len(attrs)) f.setAttributes(attrs) sink.addFeature(f, QgsFeatureSink.FastInsert) else: attrs = f.attributes() for i in range(len(join_field_indexes)): attribute_values = [v[i] for v in values] field_type = field_types[i] if field_type == 'numeric': stat = QgsStatisticalSummary() for v in attribute_values: stat.addVariant(v) stat.finalize() for s in numeric_fields: if s[0] in summaries: attrs.append(getattr(stat, s[2])()) elif field_type == 'datetime': stat = QgsDateTimeStatisticalSummary() stat.calculate(attribute_values) for s in datetime_fields: if s[0] in summaries: if s[0] == 'filled': attrs.append(stat.count() - stat.countMissing()) elif s[0] == 'min': attrs.append(stat.statistic(QgsDateTimeStatisticalSummary.Min)) elif s[0] == 'max': attrs.append(stat.statistic(QgsDateTimeStatisticalSummary.Max)) else: attrs.append(getattr(stat, s[2])()) else: stat = QgsStringStatisticalSummary() for v in attribute_values: if v == NULL: stat.addString('') else: stat.addString(str(v)) stat.finalize() for s in string_fields: if s[0] in summaries: if s[0] == 'filled': attrs.append(stat.count() - stat.countMissing()) else: attrs.append(getattr(stat, s[2])()) f.setAttributes(attrs) sink.addFeature(f, QgsFeatureSink.FastInsert) return {self.OUTPUT: dest_id}
def getFeatures(self, request=QgsFeatureRequest()): return QgsFeatureIterator(PyFeatureIterator(PyFeatureSource(self), request))
def processAlgorithm(self, parameters, context, feedback): source = self.parameterAsSource(parameters, self.INPUT, context) join_source = self.parameterAsSource(parameters, self.JOIN, context) join_fields = self.parameterAsFields(parameters, self.JOIN_FIELDS, context) method = self.parameterAsEnum(parameters, self.METHOD, context) discard_nomatch = self.parameterAsBool(parameters, self.DISCARD_NONMATCHING, context) source_fields = source.fields() fields_to_join = QgsFields() join_field_indexes = [] if not join_fields: fields_to_join = join_source.fields() join_field_indexes = [i for i in range(len(fields_to_join))] else: for f in join_fields: idx = join_source.fields().lookupField(f) join_field_indexes.append(idx) if idx >= 0: fields_to_join.append(join_source.fields().at(idx)) out_fields = QgsProcessingUtils.combineFields(source_fields, fields_to_join) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, out_fields, source.wkbType(), source.sourceCrs()) # do the join # build a list of 'reversed' predicates, because in this function # we actually test the reverse of what the user wants (allowing us # to prepare geometries and optimise the algorithm) predicates = [ self.reversed_predicates[self.predicates[i][0]] for i in self.parameterAsEnums(parameters, self.PREDICATE, context) ] remaining = set() if not discard_nomatch: remaining = set(source.allFeatureIds()) added_set = set() request = QgsFeatureRequest().setSubsetOfAttributes( join_field_indexes).setDestinationCrs(source.sourceCrs()) features = join_source.getFeatures(request) total = 100.0 / join_source.featureCount() if join_source.featureCount( ) else 0 for current, f in enumerate(features): if feedback.isCanceled(): break if not f.hasGeometry(): continue bbox = f.geometry().boundingBox() engine = None request = QgsFeatureRequest().setFilterRect(bbox) for test_feat in source.getFeatures(request): if feedback.isCanceled(): break if method == 1 and test_feat.id() in added_set: # already added this feature, and user has opted to only output first match continue join_attributes = [] for a in join_field_indexes: join_attributes.append(f.attributes()[a]) if engine is None: engine = QgsGeometry.createGeometryEngine( f.geometry().constGet()) engine.prepareGeometry() for predicate in predicates: if getattr(engine, predicate)(test_feat.geometry().constGet()): added_set.add(test_feat.id()) # join attributes and add attributes = test_feat.attributes() attributes.extend(join_attributes) output_feature = test_feat output_feature.setAttributes(attributes) sink.addFeature(output_feature, QgsFeatureSink.FastInsert) break feedback.setProgress(int(current * total)) if not discard_nomatch: remaining = remaining.difference(added_set) for f in source.getFeatures(QgsFeatureRequest().setFilterFids( list(remaining))): if feedback.isCanceled(): break sink.addFeature(f, QgsFeatureSink.FastInsert) return {self.OUTPUT: dest_id}
def make_coco_dataset(self): layers = QgsProject.instance().mapLayers().values() vectorlayers = [layer for layer in layers if layer.type() == VectorLayer] rasterLayers = [layer for layer in layers if layer.type() == RasterLayer] vectorlayer = vectorlayers[self.dlg.vectorlayerselector.currentIndex()] rasterlayer = rasterLayers[self.dlg.rasterlayerselector.currentIndex()] img_size = int(self.dlg.imagesizeselector.currentText()) scale_realworld = int(self.dlg.zoomlevel.currentText()) buffer_size = int(self.dlg.buffersize.text()) / 100 target_dir = self.dlg.dirselectline.text() dataset_description = str(self.dlg.datasetdescription.text()) contributor = str(self.dlg.creatorname.text()) url = str(self.dlg.url.text()) version = str(self.dlg.datasetversion.text()) license = str(self.dlg.licenseselector.currentText()) # to implement scale_to_fit = self.dlg.scaletofit.isChecked() use_fieldcategory = bool(self.dlg.usecategoryfields.isChecked()) categoryfield = self.dlg.categoryfields.currentText() QgsMessageLog.logMessage(str(scale_to_fit), "cocotrainer", level=Qgis.Info) QgsMessageLog.logMessage(str(use_fieldcategory), "cocotrainer", level=Qgis.Info) # prepare directories os.makedirs(os.path.join(target_dir, "train"), exist_ok=True) os.makedirs(os.path.join(target_dir, "val"), exist_ok=True) QgsMessageLog.logMessage("====================================", "cocotrainer", level=Qgis.Info) # TODO: # - use field categories features_iterator = vectorlayer.getFeatures(QgsFeatureRequest().setFilterExpression('$area > 1')) features = [feature for feature in features_iterator] QgsMessageLog.logMessage("vector layer: " + vectorlayer.name(), "cocotrainer", level=Qgis.Info) QgsMessageLog.logMessage("Filtered polygons smaller than 1m2 from data", "cocotrainer", level=Qgis.Info) QgsMessageLog.logMessage("features: " + str(len(list(features))), "cocotrainer", level=Qgis.Info) options = QgsMapSettings() options.setLayers([rasterlayer]) options.setOutputSize(QSize(int(img_size), int(img_size))) QgsMessageLog.logMessage(str(scale_realworld), "cocotrainer", level=Qgis.Info) QgsMessageLog.logMessage(str(img_size), "cocotrainer", level=Qgis.Info) date = datetime.now() cat_dict = {} if use_fieldcategory: categories = [] # uniqueprovider = rasterlayer.dataProvider() fields = vectorlayer.fields() id = fields.indexFromName(categoryfield) uniquevalues = vectorlayer.uniqueValues(id) for i, val in enumerate(uniquevalues): categories.append({"supercategory": "object", "id": i, "name": str(val)}) cat_dict[str(val)] = i else: categories = [{"supercategory": "object", "id": 0, "name": "CATEGORYNAME"}] cat_dict["CATEGORYNAME"] = 0 coco_annotation = { "info": { "description": dataset_description, "url": url, "version": version, "year": int(date.strftime("%Y")), "contributor": contributor, "date_created": date.strftime("%d/%m/%y") }, "licenses": [self.license_dict[license]], "images": [], "annotations": [], "categories": categories, # < -- Not in Captionsannotations "segment_info": [] # < -- Only in Panoptic annotations } bboxes, polygons = {}, {} for i, feature in enumerate(features): polygons[i] = feature.geometry() bboxes[i] = feature.geometry().boundingBox() if use_fieldcategory: cats = {} for i, feature in enumerate(features): cats[i] = str(feature[categoryfield]) image_id = 0 annotation_id = 0 for i, feature in enumerate(features): self.dlg.progressBar.setValue((i / len(polygons) * 100)) QgsMessageLog.logMessage("something is happening", "cocotrainer", level=Qgis.Info) geoms = feature.geometry() bbox = geoms.boundingBox() if scale_to_fit: xmax = bbox.xMaximum() ymax = bbox.yMaximum() ymin = bbox.yMinimum() xmin = bbox.xMinimum() diffx = xmax - xmin diffy = ymax - ymin xmax = xmax + (buffer_size * diffx) xmin = xmin - (buffer_size * diffx) ymax = ymax + (buffer_size * diffy) ymin = ymin - (buffer_size * diffy) else: midpoint = bbox.center() QgsMessageLog.logMessage("trying to log centerpoint", "cocotrainer", level=Qgis.Info) QgsMessageLog.logMessage(str(midpoint.x()), "cocotrainer", level=Qgis.Info) QgsMessageLog.logMessage(str(midpoint.y()), "cocotrainer", level=Qgis.Info) xmin = midpoint.x() - scale_realworld / 2 xmax = midpoint.x() + scale_realworld / 2 ymin = midpoint.y() - scale_realworld / 2 ymax = midpoint.y() + scale_realworld / 2 extent = QgsRectangle(xmin, ymin, xmax, ymax) fileid = "{}_{}_{}".format(xmin, ymin, scale_realworld) image = { "license": 0, "file_name": fileid + ".png", "coco_url": None, "height": img_size, "width": img_size, "date_captured": date.strftime("%d/%m/%y"), "flickr_url": None, "id": image_id } coco_annotation["images"].append(image) for j, geo in bboxes.items(): if extent.contains(geo) or extent.intersects(geo): points = polygons[j].asMultiPolygon() xs = np.array([p.x() - xmin for p in points[0][0]]) ys = np.array([(ymax - ymin) - (p.y() - ymin) for p in points[0][0]]) xs *= (img_size / scale_realworld) xs = np.round(xs) xs = xs.astype(np.int32) ys *= (img_size / scale_realworld) ys = np.round(ys) ys = ys.astype(np.int32) ys[ys < 0] = 0 xs[xs < 0] = 0 polygon = [[int(p[0]), int(p[1])] for p in zip(xs, ys)] flat_list = [item for sublist in polygon for item in sublist] QgsMessageLog.logMessage(str(flat_list), "cocotrainer", level=Qgis.Info) pixelposbbox = [int(np.min(xs)), (int(np.min(ys))), geo.width() * (img_size / scale_realworld), geo.height() * (img_size / scale_realworld)] pixelposbbox = [number if number > 0 else 0 for number in pixelposbbox] QgsMessageLog.logMessage(str([geo.xMinimum(), geo.yMaximum(), geo.width(), geo.height()]), "cocotrainer", level=Qgis.Info) QgsMessageLog.logMessage(str(pixelposbbox), "cocotrainer", level=Qgis.Info) annotation = { "segmentation": [flat_list], # format is [x1,y1,x2,y2] "area": polygons[j].area(), "iscrowd": 0, "image_id": image_id, "bbox": pixelposbbox, # format is [top left x position, top left y position, width, height] "category_id": cat_dict[cats[j]], "id": annotation_id } annotation_id += 1 coco_annotation["annotations"].append(annotation) options.setExtent(extent) render = QgsMapRendererParallelJob(options) def finished(): QgsMessageLog.logMessage("saving image to disk", "cocotrainer", level=Qgis.Info) fname = os.path.join(target_dir, 'train', "{}.png".format(fileid)) img = render.renderedImage() if not img.save(fname, "png"): print("Error saving image") render.finished.connect(finished) render.start() render.waitForFinished() image_id += 1 QgsMessageLog.logMessage(str(coco_annotation), "cocotrainer", level=Qgis.Info) with open(os.path.join(target_dir, "annotation.json"), "w") as outfile: json.dump(coco_annotation, outfile) self.dlg.progressBar.setValue(100) self.dlg.finished_label.setText("parsed {} features".format(len(list(features))))
def processAlgorithm(self, parameters, context, feedback): """ Here is where the processing itself takes place. """ sourceL = self.parameterAsSource(parameters, self.INPUT_L, context) sourceF = self.parameterAsMatrix(parameters, self.INPUT_F, context) filtro = self.parameterAsString(parameters, self.GROUP_BY, context) titolo = self.parameterAsString(parameters, self.INPUT_T, context) html = self.parameterAsFileOutput(parameters, self.OUTPUT, context) source_path = self.parameterAsString(parameters, self.INPUT_L, context) f_base = self.parameterAsString(parameters, self.INPUT_D, context) icona = self.parameterAsString(parameters, self.INPUT_I, context) fogliocss = self.parameterAsString(parameters, self.INPUT_S, context) rel_path = self.parameterAsBool(parameters, self.INPUT_ABS, context) #FASE #01 - cerco la path del progetto if QgsProject.instance().homePath(): path_proj = QgsProject.instance().homePath() #windowizzo la path quale che sia path_proj = str(Path(path_proj)) #rimuovo geopakage: se presente path_proj = path_proj.replace('geopackage:', '') #print('path proj ', path_proj) else: feedback.reportError( 'WARNING NO PROJECT PATH: the html file may not work correctly\n' ) #print('***** no project path') path_proj = '' #tolgo %20 e metto spazio path_proj = path_proj.replace('%20', ' ') #FASE #02 - cerco la path del file di input path_file = (self.parameterDefinition('INPUT_L').valueAsPythonString( parameters['INPUT_L'], context)) path_file = path_file[1:path_file.rfind('/') + 1] if 'memory' in path_file: file_mem = True path_file = '' #print('temporary file') else: file_mem = False #windowizzo la path quale che sia path_file = str(Path(path_file)) #print('file path ', path_file) #tolgo %20 e metto spazio path_file = path_file.replace('%20', ' ') #FASE #03 - scelgo la path da usare tra le due: prioritaria quella di progetto if path_proj: path_dir = path_proj if path_proj not in path_file and path_file != '': feedback.reportError('WARNING PATH FILE ' + path_file) feedback.reportError('OUTSIDE PROJECT PATH ' + path_proj) feedback.reportError('MOST LIKELY IT WON' 'T WORK' + '\n') elif path_file == '': feedback.reportError('WARNING TEMPORARY LAYER WITHOUT PATH\n') else: path_dir = path_file if path_dir: feedback.reportError( 'WARNING use the path of the input file ' + path_dir + '\n') else: feedback.reportError('WARNING TEMPORARY LAYER WITHOUT PATH\n') #FASE #04 - controllo se si sta salvando file con percorsi relativi nella cartella di progetto if path_dir not in str(Path(html)) and 'processing' not in str( Path(html)): #print('html ', str(Path(html))) feedback.reportError( 'WARNING HTML WITH RELATIVE PATH SAVED OUTSIDE THE PROJECT PATH DOES NOT WORK PROPERLY\n' ) if 'processing' in str(Path(html)): #print('html ', str(Path(html))) feedback.reportError( 'WARNING TEMPORARY HTML WORK PROPERLY ONLY WITH ABSOLUTE PATH\n' ) #FASE #05 - controllo se icona e css sono entro la cartella progetto if fogliocss and (path_dir not in fogliocss): feedback.reportError( 'WARNING css PATH OUTSIDE PROJECT PATH: the html file may not work correctly\n' ) if icona and path_dir not in icona: feedback.reportError( 'WARNING icon PATH OUTSIDE PROJECT PATH: the html file may not work correctly\n' ) #FASE #06 - aggiungo terminatore di percorso se non è un file temporaneo if path_dir != '': path_dir = path_dir + '\\' #print('Final path ', path_dir) #FASE #07 - modifica se csv in input if source_path.find(".csv"): source_path = 'file:///' + source_path[0:source_path.rfind('/') + 1] #FASE #08 recupero dimensioni foto e icona, titolo e riordino a causa di un bug wi, hi, wf, hf = f_base.split(';') titolo = titolo.replace('\"', '') intestazione = titolo.replace('"', '') intestazione = titolo.replace('\'', '') #riordino campi come da selezione per bug cleanlist = [] [cleanlist.append(x) for x in sourceF if x not in cleanlist] sourceF = cleanlist #FASE #09 - inizializzo variabile per barra % esecuzione script # Compute the number of steps to display within the progress bar and # get features from source total = 100.0 / sourceL.featureCount() if sourceL.featureCount() else 0 #FASE #10 - filtra dati se richiesto if len(filtro) > 0: request = QgsFeatureRequest(QgsExpression(filtro)) features = sourceL.getFeatures(request) else: features = sourceL.getFeatures() #FASE #11 - produco il file in uscita with open(html, 'w') as output_file: # write header line = '<html>\r' output_file.write(line) #FASE #11.01 - se richiesto inserisco foglio css if fogliocss: if not rel_path or 'processing' in html: fogliocss = 'file:///' + fogliocss else: fogliocss = str(Path(fogliocss)) fogliocss = fogliocss.replace(path_dir, '') line = '<head>\r<link rel="stylesheet" href="' + fogliocss + '">\r</head>' output_file.write(line) #FASE #11.02 - se richiesto inserisco icona e titolo if icona or titolo: line = '<div>' if icona: if not rel_path or 'processing' in html: icona = 'file:///' + icona else: icona = str(Path(icona)) icona = icona.replace(path_dir, '') line = '<img src="' + icona + '" style="width:' + wi + ';height:' + hi + ';">' output_file.write(line) line = '' if titolo: if icona: line = line + '<b>' + '  ' + titolo + '</b>' else: line = line + '<b>' + titolo + '</b>' output_file.write(line) line = '</div>' output_file.write(line) line = None #FASE #11.03 - compongo tabella line = '<table class="Table">\r<thead>\r<tr>\r' output_file.write(line) #titoli colonne line = ''.join(('<th style="width:auto">' + str(name) + '</<th>\r') for name in sourceF) + '</tr>\r' output_file.write(line) line = '</thead>\r<tbody>\r' output_file.write(line) #righe tabella for current, f in enumerate(features): line = '<tr>\r' output_file.write(line) for name in sourceF: #controllo se si tratta di una immagine try: img_type = f[name].split(".") img_type = img_type[len(img_type) - 1] #print('img ok ', name, f[name], img_type) except: img_type = '' #print('img no ', name, f[name], img_type) #se è un'immagine e/o ha un percorso if img_type in [ "JPEG", "jpeg", "JPG", "jpg", "PNG", "png" ]: #se non è un file temporaneo o non voglio riferimenti relativi if not rel_path or 'processing' in html: if file_mem: img_name = 'file:///' else: img_name = '' if path_dir not in str(Path(f[name])): img_name = img_name + path_dir img_name = img_name + f[name] else: #se voglio riferimenti relativi img_name = str(Path(f[name])) img_name = img_name.replace(path_dir, '') line = ''.join('<td><center><img src =' + "'" + img_name + "'" + 'alt=' + "'" + img_name + "'" + 'width="' + wf + '" height="' + hf + '"></center></td>\r') else: try: line = ''.join('<td>' + f[name].toString("dd.MM.yyyy") + '</td>\r') except: line = ''.join('<td>' + str(f[name]) + '</td>\r') output_file.write(line) line = '</tr>\r' output_file.write(line) # Update the progress bar feedback.setProgress(int(current * total)) line = '</tbody>\r</table>\r</html>' output_file.write(line) return {self.OUTPUT: html}
def processAlgorithm(self, parameters, context, feedback): source = self.parameterAsSource(parameters, self.INPUT_VECTOR, context) raster_layer = self.parameterAsRasterLayer(parameters, self.INPUT_RASTER, context) rasterPath = raster_layer.source() rasterDS = gdal.Open(rasterPath, gdal.GA_ReadOnly) geoTransform = rasterDS.GetGeoTransform() fields = QgsFields() fields.append(QgsField('id', QVariant.Int, '', 10, 0)) fields.append(QgsField('poly_id', QVariant.Int, '', 10, 0)) fields.append(QgsField('point_id', QVariant.Int, '', 10, 0)) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, QgsWkbTypes.Point, raster_layer.crs()) outFeature = QgsFeature() outFeature.setFields(fields) fid = 0 polyId = 0 pointId = 0 features = source.getFeatures(QgsFeatureRequest().setDestinationCrs(raster_layer.crs())) total = 100.0 / source.featureCount() if source.featureCount() else 0 for current, f in enumerate(features): if feedback.isCanceled(): break if not f.hasGeometry(): continue geom = f.geometry() bbox = geom.boundingBox() xMin = bbox.xMinimum() xMax = bbox.xMaximum() yMin = bbox.yMinimum() yMax = bbox.yMaximum() (startRow, startColumn) = raster.mapToPixel(xMin, yMax, geoTransform) (endRow, endColumn) = raster.mapToPixel(xMax, yMin, geoTransform) # use prepared geometries for faster intersection tests engine = QgsGeometry.createGeometryEngine(geom.constGet()) engine.prepareGeometry() for row in range(startRow, endRow + 1): for col in range(startColumn, endColumn + 1): if feedback.isCanceled(): break (x, y) = raster.pixelToMap(row, col, geoTransform) point = QgsPoint(x, y) if engine.contains(point): outFeature.setGeometry(QgsGeometry(point)) outFeature['id'] = fid outFeature['poly_id'] = polyId outFeature['point_id'] = pointId fid += 1 pointId += 1 sink.addFeature(outFeature, QgsFeatureSink.FastInsert) pointId = 0 polyId += 1 feedback.setProgress(int(current * total)) return {self.OUTPUT: dest_id}
def processAlgorithm(self, parameters, context, feedback): # Get variables from dialog source = self.parameterAsSource(parameters, self.INPUT, context) if source is None: raise QgsProcessingException( self.invalidSourceError(parameters, self.INPUT)) field_name = self.parameterAsString(parameters, self.FIELD, context) kneighbors = self.parameterAsInt(parameters, self.KNEIGHBORS, context) use_field = bool(field_name) field_index = -1 fields = QgsFields() fields.append(QgsField('id', QVariant.Int, '', 20)) current = 0 # Get properties of the field the grouping is based on if use_field: field_index = source.fields().lookupField(field_name) if field_index >= 0: fields.append( source.fields()[field_index] ) # Add a field with the name of the grouping field # Initialize writer (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, QgsWkbTypes.Polygon, source.sourceCrs()) if sink is None: raise QgsProcessingException( self.invalidSinkError(parameters, self.OUTPUT)) success = False fid = 0 # Get unique values of grouping field unique_values = source.uniqueValues(field_index) total = 100.0 / float( source.featureCount() * len(unique_values)) for unique in unique_values: points = [] filter = QgsExpression.createFieldEqualityExpression( field_name, unique) request = QgsFeatureRequest().setFilterExpression(filter) request.setSubsetOfAttributes([]) # Get features with the grouping attribute equal to the current grouping value features = source.getFeatures(request) for in_feature in features: if feedback.isCanceled(): break # Add points or vertices of more complex geometry points.extend(extract_points(in_feature.geometry())) current += 1 feedback.setProgress(int(current * total)) # A minimum of 3 points is necessary to proceed if len(points) >= 3: out_feature = QgsFeature() the_hull = concave_hull(points, kneighbors) if the_hull: vertex = [ QgsPointXY(point[0], point[1]) for point in the_hull ] poly = QgsGeometry().fromPolygonXY([vertex]) out_feature.setGeometry(poly) # Give the polygon the same attribute as the point grouping attribute out_feature.setAttributes([fid, unique]) sink.addFeature(out_feature, QgsFeatureSink.FastInsert) success = True # at least one polygon created fid += 1 if not success: raise QgsProcessingException( 'No hulls could be created. Most likely there were not at least three unique points in any of the groups.' ) else: # Field parameter provided but can't read from it raise QgsProcessingException('Unable to find grouping field') else: # Not grouped by field # Initialize writer (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, QgsWkbTypes.Polygon, source.sourceCrs()) if sink is None: raise QgsProcessingException( self.invalidSinkError(parameters, self.OUTPUT)) points = [] request = QgsFeatureRequest() request.setSubsetOfAttributes([]) features = source.getFeatures(request) # Get all features total = 100.0 / source.featureCount() if source.featureCount( ) else 0 for in_feature in features: if feedback.isCanceled(): break # Add points or vertices of more complex geometry points.extend(extract_points(in_feature.geometry())) current += 1 feedback.setProgress(int(current * total)) # A minimum of 3 points is necessary to proceed if len(points) >= 3: out_feature = QgsFeature() the_hull = concave_hull(points, kneighbors) if the_hull: vertex = [ QgsPointXY(point[0], point[1]) for point in the_hull ] poly = QgsGeometry().fromPolygonXY([vertex]) out_feature.setGeometry(poly) out_feature.setAttributes([0]) sink.addFeature(out_feature, QgsFeatureSink.FastInsert) else: # the_hull returns None only when there are less than three points after cleaning raise QgsProcessingException( 'At least three unique points are required to create a concave hull.' ) else: raise QgsProcessingException( 'At least three points are required to create a concave hull.' ) return {self.OUTPUT: dest_id}
def processAlgorithm(self, progress): layer = dataobjects.getObjectFromUri(self.getParameterValue( self.INPUT)) fields = [ QgsField('POINTA', QVariant.Double, '', 24, 15), QgsField('POINTB', QVariant.Double, '', 24, 15), QgsField('POINTC', QVariant.Double, '', 24, 15) ] writer = self.getOutputFromName(self.OUTPUT).getVectorWriter( fields, QgsWkbTypes.Polygon, layer.crs()) pts = [] ptDict = {} ptNdx = -1 c = voronoi.Context() features = vector.features(layer) total = 100.0 / len(features) for current, inFeat in enumerate(features): geom = QgsGeometry(inFeat.geometry()) if geom.isEmpty(): continue if geom.isMultipart(): points = geom.asMultiPoint() else: points = [geom.asPoint()] for n, point in enumerate(points): x = point.x() y = point.y() pts.append((x, y)) ptNdx += 1 ptDict[ptNdx] = (inFeat.id(), n) progress.setPercentage(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) for i in uniqueSet]) c.triangulate = True voronoi.voronoi(sl, c) triangles = c.triangles feat = QgsFeature() total = 100.0 / len(triangles) for current, triangle in enumerate(triangles): indicies = list(triangle) indicies.append(indicies[0]) polygon = [] attrs = [] step = 0 for index in indicies: fid, n = ptDict[ids[index]] request = QgsFeatureRequest().setFilterFid(fid) inFeat = next(layer.getFeatures(request)) geom = QgsGeometry(inFeat.geometry()) if geom.isMultipart(): point = QgsPoint(geom.asMultiPoint()[n]) else: point = QgsPoint(geom.asPoint()) polygon.append(point) if step <= 3: attrs.append(ids[index]) step += 1 feat.setAttributes(attrs) geometry = QgsGeometry().fromPolygon([polygon]) feat.setGeometry(geometry) writer.addFeature(feat) progress.setPercentage(int(current * total)) del writer
def processAlgorithm(self, parameters, context, feedback): source = self.parameterAsSource(parameters, self.INPUT, context) if source is None: raise QgsProcessingException( self.invalidSourceError(parameters, self.INPUT)) field_names = self.parameterAsFields(parameters, self.FIELDS, context) fields = QgsFields() field_indices = [] for field_name in field_names: field_index = source.fields().lookupField(field_name) if field_index < 0: feedback.reportError( self.tr('Invalid field name {}').format(field_name)) continue field = source.fields()[field_index] fields.append(field) field_indices.append(field_index) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, QgsWkbTypes.NoGeometry, QgsCoordinateReferenceSystem()) results = {} values = set() if len(field_indices) == 1: # one field, can use provider optimised method values = tuple([v] for v in source.uniqueValues(field_indices[0])) else: # have to scan whole table # TODO - add this support to QgsVectorDataProvider so we can run it on # the backend request = QgsFeatureRequest().setFlags( QgsFeatureRequest.NoGeometry) request.setSubsetOfAttributes(field_indices) total = 100.0 / source.featureCount() if source.featureCount( ) else 0 for current, f in enumerate( source.getFeatures( request, QgsProcessingFeatureSource. FlagSkipGeometryValidityChecks)): if feedback.isCanceled(): break value = tuple(f.attribute(i) for i in field_indices) values.add(value) feedback.setProgress(int(current * total)) if sink: for value in values: if feedback.isCanceled(): break f = QgsFeature() f.setAttributes([attr for attr in value]) sink.addFeature(f, QgsFeatureSink.FastInsert) results[self.OUTPUT] = dest_id output_file = self.parameterAsFileOutput(parameters, self.OUTPUT_HTML_FILE, context) if output_file: self.createHTML(output_file, values) results[self.OUTPUT_HTML_FILE] = output_file results[self.TOTAL_VALUES] = len(values) results[self.UNIQUE_VALUES] = ';'.join( [','.join([str(attr) for attr in v]) for v in values]) return results
def processAlgorithm(self, parameters, context, feedback): source = self.parameterAsSource(parameters, self.INPUT, context) if source is None: raise QgsProcessingException( self.invalidSourceError(parameters, self.INPUT)) field_name = self.parameterAsString(parameters, self.FIELD, context) type = self.parameterAsEnum(parameters, self.TYPE, context) use_field = bool(field_name) field_index = -1 fields = QgsFields() fields.append(QgsField('id', QVariant.Int, '', 20)) if use_field: # keep original field type, name and parameters field_index = source.fields().lookupField(field_name) if field_index >= 0: fields.append(source.fields()[field_index]) if type == 0: # envelope fields.append(QgsField('width', QVariant.Double, '', 20, 6)) fields.append(QgsField('height', QVariant.Double, '', 20, 6)) fields.append(QgsField('area', QVariant.Double, '', 20, 6)) fields.append(QgsField('perimeter', QVariant.Double, '', 20, 6)) elif type == 1: # oriented rect fields.append(QgsField('width', QVariant.Double, '', 20, 6)) fields.append(QgsField('height', QVariant.Double, '', 20, 6)) fields.append(QgsField('angle', QVariant.Double, '', 20, 6)) fields.append(QgsField('area', QVariant.Double, '', 20, 6)) fields.append(QgsField('perimeter', QVariant.Double, '', 20, 6)) elif type == 2: # circle fields.append(QgsField('radius', QVariant.Double, '', 20, 6)) fields.append(QgsField('area', QVariant.Double, '', 20, 6)) elif type == 3: # convex hull fields.append(QgsField('area', QVariant.Double, '', 20, 6)) fields.append(QgsField('perimeter', QVariant.Double, '', 20, 6)) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, QgsWkbTypes.Polygon, source.sourceCrs()) if sink is None: raise QgsProcessingException( self.invalidSinkError(parameters, self.OUTPUT)) if field_index >= 0: geometry_dict = {} bounds_dict = {} total = 50.0 / source.featureCount() if source.featureCount( ) else 1 features = source.getFeatures( QgsFeatureRequest().setSubsetOfAttributes([field_index])) for current, f in enumerate(features): if feedback.isCanceled(): break if not f.hasGeometry(): continue if type == 0: # bounding boxes - calculate on the fly for efficiency if not f.attributes()[field_index] in bounds_dict: bounds_dict[f.attributes()[field_index]] = f.geometry( ).boundingBox() else: bounds_dict[f.attributes() [field_index]].combineExtentWith( f.geometry().boundingBox()) else: if not f.attributes()[field_index] in geometry_dict: geometry_dict[f.attributes()[field_index]] = [ f.geometry() ] else: geometry_dict[f.attributes()[field_index]].append( f.geometry()) feedback.setProgress(int(current * total)) if type == 0: # bounding boxes current = 0 total = 50.0 / len(bounds_dict) if bounds_dict else 1 for group, rect in bounds_dict.items(): if feedback.isCanceled(): break # envelope feature = QgsFeature() feature.setGeometry(QgsGeometry.fromRect(rect)) feature.setAttributes([ current, group, rect.width(), rect.height(), rect.area(), rect.perimeter() ]) sink.addFeature(feature, QgsFeatureSink.FastInsert) geometry_dict[group] = None feedback.setProgress(50 + int(current * total)) current += 1 else: current = 0 total = 50.0 / len(geometry_dict) if geometry_dict else 1 for group, geometries in geometry_dict.items(): if feedback.isCanceled(): break feature = self.createFeature(feedback, current, type, geometries, group) sink.addFeature(feature, QgsFeatureSink.FastInsert) geometry_dict[group] = None feedback.setProgress(50 + int(current * total)) current += 1 else: total = 80.0 / source.featureCount() if source.featureCount( ) else 1 features = source.getFeatures( QgsFeatureRequest().setSubsetOfAttributes([])) geometry_queue = [] bounds = QgsRectangle() for current, f in enumerate(features): if feedback.isCanceled(): break if not f.hasGeometry(): continue if type == 0: # bounding boxes, calculate on the fly for efficiency bounds.combineExtentWith(f.geometry().boundingBox()) else: geometry_queue.append(f.geometry()) feedback.setProgress(int(current * total)) if not feedback.isCanceled(): if type == 0: feature = QgsFeature() feature.setGeometry(QgsGeometry.fromRect(bounds)) feature.setAttributes([ 0, bounds.width(), bounds.height(), bounds.area(), bounds.perimeter() ]) else: feature = self.createFeature(feedback, 0, type, geometry_queue) sink.addFeature(feature, QgsFeatureSink.FastInsert) return {self.OUTPUT: dest_id}
def processAlgorithm(self, parameters, context, feedback): sourceA = self.parameterAsSource(parameters, self.INPUT, context) sourceB = self.parameterAsSource(parameters, self.OVERLAY, context) geomType = QgsWkbTypes.multiType(sourceA.wkbType()) fields = QgsProcessingUtils.combineFields(sourceA.fields(), sourceB.fields()) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, geomType, sourceA.sourceCrs()) featB = QgsFeature() outFeat = QgsFeature() indexA = QgsSpatialIndex(sourceA, feedback) indexB = QgsSpatialIndex(sourceB.getFeatures(QgsFeatureRequest().setSubsetOfAttributes([]).setDestinationCrs(sourceA.sourceCrs(), context.transformContext())), feedback) total = 100.0 / (sourceA.featureCount() * sourceB.featureCount()) if sourceA.featureCount() and sourceB.featureCount() else 1 count = 0 for featA in sourceA.getFeatures(): if feedback.isCanceled(): break geom = featA.geometry() diffGeom = QgsGeometry(geom) attrs = featA.attributes() intersects = indexB.intersects(geom.boundingBox()) request = QgsFeatureRequest().setFilterFids(intersects).setSubsetOfAttributes([]) request.setDestinationCrs(sourceA.sourceCrs(), context.transformContext()) for featB in sourceB.getFeatures(request): if feedback.isCanceled(): break tmpGeom = featB.geometry() if diffGeom.intersects(tmpGeom): diffGeom = QgsGeometry(diffGeom.difference(tmpGeom)) try: outFeat.setGeometry(diffGeom) outFeat.setAttributes(attrs) sink.addFeature(outFeat, QgsFeatureSink.FastInsert) except: QgsMessageLog.logMessage(self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.'), self.tr('Processing'), QgsMessageLog.WARNING) continue count += 1 feedback.setProgress(int(count * total)) length = len(sourceA.fields()) for featA in sourceB.getFeatures(QgsFeatureRequest().setDestinationCrs(sourceA.sourceCrs(), context.transformContext())): if feedback.isCanceled(): break geom = featA.geometry() diffGeom = QgsGeometry(geom) attrs = featA.attributes() attrs = [NULL] * length + attrs intersects = indexA.intersects(geom.boundingBox()) request = QgsFeatureRequest().setFilterFids(intersects).setSubsetOfAttributes([]) for featB in sourceA.getFeatures(request): if feedback.isCanceled(): break tmpGeom = featB.geometry() if diffGeom.intersects(tmpGeom): diffGeom = QgsGeometry(diffGeom.difference(tmpGeom)) try: outFeat.setGeometry(diffGeom) outFeat.setAttributes(attrs) sink.addFeature(outFeat, QgsFeatureSink.FastInsert) except: QgsMessageLog.logMessage(self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.'), self.tr('Processing'), QgsMessageLog.WARNING) continue count += 1 feedback.setProgress(int(count * total)) return {self.OUTPUT: dest_id}
def processAlgorithm(self, parameters, context, feedback): poly_source = self.parameterAsSource(parameters, self.POLYGONS, context) point_source = self.parameterAsSource(parameters, self.POINTS, context) weight_field = self.parameterAsString(parameters, self.WEIGHT, context) weight_field_index = -1 if weight_field: weight_field_index = point_source.fields().lookupField(weight_field) class_field = self.parameterAsString(parameters, self.CLASSFIELD, context) class_field_index = -1 if class_field: class_field_index = point_source.fields().lookupField(class_field) field_name = self.parameterAsString(parameters, self.FIELD, context) fields = poly_source.fields() if fields.lookupField(field_name) < 0: fields.append(QgsField(field_name, QVariant.Int)) field_index = fields.lookupField(field_name) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, poly_source.wkbType(), poly_source.sourceCrs()) spatialIndex = QgsSpatialIndex(point_source.getFeatures( QgsFeatureRequest().setSubsetOfAttributes([]).setDestinationCrs(poly_source.sourceCrs())), feedback) point_attribute_indices = [] if weight_field_index >= 0: point_attribute_indices.append(weight_field_index) if class_field_index >= 0: point_attribute_indices.append(class_field_index) features = poly_source.getFeatures() total = 100.0 / poly_source.featureCount() if poly_source.featureCount() else 0 for current, polygon_feature in enumerate(features): if feedback.isCanceled(): break count = 0 output_feature = QgsFeature() if polygon_feature.hasGeometry(): geom = polygon_feature.geometry() engine = QgsGeometry.createGeometryEngine(geom.geometry()) engine.prepareGeometry() count = 0 classes = set() points = spatialIndex.intersects(geom.boundingBox()) if len(points) > 0: request = QgsFeatureRequest().setFilterFids(points).setDestinationCrs(poly_source.sourceCrs()) request.setSubsetOfAttributes(point_attribute_indices) for point_feature in point_source.getFeatures(request): if feedback.isCanceled(): break if engine.contains(point_feature.geometry().geometry()): if weight_field_index >= 0: weight = point_feature.attributes()[weight_field_index] try: count += float(weight) except: # Ignore fields with non-numeric values pass elif class_field_index >= 0: point_class = point_feature.attributes()[class_field_index] if point_class not in classes: classes.add(point_class) else: count += 1 output_feature.setGeometry(geom) attrs = polygon_feature.attributes() if class_field_index >= 0: score = len(classes) else: score = count if field_index == len(attrs): attrs.append(score) else: attrs[field_index] = score output_feature.setAttributes(attrs) sink.addFeature(output_feature, QgsFeatureSink.FastInsert) feedback.setProgress(int(current * total)) return {self.OUTPUT: dest_id}
def testFieldsWithSpecialCharacters(self): ml = QgsVectorLayer("Point?srid=EPSG:4326&field=123:int", "mem_with_nontext_fieldnames", "memory") self.assertEqual(ml.isValid(), True) QgsProject.instance().addMapLayer(ml) ml.startEditing() self.assertTrue(ml.addAttribute(QgsField('abc:123', QVariant.String))) self.assertTrue(ml.addAttribute(QgsField( 'map', QVariant.String))) # matches QGIS expression function name f1 = QgsFeature(ml.fields()) f1.setGeometry(QgsGeometry.fromWkt('POINT(0 0)')) f1.setAttributes([1, 'a', 'b']) f2 = QgsFeature(ml.fields()) f2.setGeometry(QgsGeometry.fromWkt('POINT(1 1)')) f2.setAttributes([2, 'c', 'd']) ml.addFeatures([f1, f2]) ml.commitChanges() vl = QgsVectorLayer("?query=select * from mem_with_nontext_fieldnames", "vl", "virtual") self.assertEqual(vl.isValid(), True) self.assertEqual(vl.fields().at(0).name(), '123') self.assertEqual(vl.fields().at(1).name(), 'abc:123') self.assertEqual(vl.featureCount(), 2) features = [ f for f in vl.getFeatures(QgsFeatureRequest().setFilterExpression( '"abc:123"=\'c\'')) ] self.assertEqual(len(features), 1) self.assertEqual(features[0].attributes(), [2, 'c', 'd']) features = [ f for f in vl.getFeatures(QgsFeatureRequest().setFilterExpression( '"map"=\'b\'')) ] self.assertEqual(len(features), 1) self.assertEqual(features[0].attributes(), [1, 'a', 'b']) vl2 = QgsVectorLayer( "?query=select * from mem_with_nontext_fieldnames where \"abc:123\"='c'", "vl", "virtual") self.assertEqual(vl2.isValid(), True) self.assertEqual(vl2.fields().at(0).name(), '123') self.assertEqual(vl2.fields().at(1).name(), 'abc:123') self.assertEqual(vl2.featureCount(), 1) features = [f for f in vl2.getFeatures()] self.assertEqual(len(features), 1) self.assertEqual(features[0].attributes(), [2, 'c', 'd']) vl3 = QgsVectorLayer( "?query=select * from mem_with_nontext_fieldnames where \"map\"='b'", "vl", "virtual") self.assertEqual(vl3.isValid(), True) self.assertEqual(vl3.fields().at(0).name(), '123') self.assertEqual(vl3.fields().at(1).name(), 'abc:123') self.assertEqual(vl3.featureCount(), 1) features = [f for f in vl3.getFeatures()] self.assertEqual(len(features), 1) self.assertEqual(features[0].attributes(), [1, 'a', 'b']) QgsProject.instance().removeMapLayer(ml)
def test_invalidGeometryFilter(self): layer = QgsVectorLayer("Polygon?field=x:string", "joinlayer", "memory") # add some features, one has invalid geometry pr = layer.dataProvider() f1 = QgsFeature(1) f1.setAttributes(["a"]) f1.setGeometry( QgsGeometry.fromWkt('Polygon((0 0, 1 0, 1 1, 0 1, 0 0))')) # valid f2 = QgsFeature(2) f2.setAttributes(["b"]) f2.setGeometry( QgsGeometry.fromWkt( 'Polygon((0 0, 1 0, 0 1, 1 1, 0 0))')) # invalid f3 = QgsFeature(3) f3.setAttributes(["c"]) f3.setGeometry( QgsGeometry.fromWkt('Polygon((0 0, 1 0, 1 1, 0 1, 0 0))')) # valid self.assertTrue(pr.addFeatures([f1, f2, f3])) res = [ f['x'] for f in layer.getFeatures(QgsFeatureRequest( ).setInvalidGeometryCheck(QgsFeatureRequest.GeometryNoCheck)) ] self.assertEqual(res, ['a', 'b', 'c']) res = [ f['x'] for f in layer.getFeatures(QgsFeatureRequest( ).setInvalidGeometryCheck(QgsFeatureRequest.GeometrySkipInvalid)) ] self.assertEqual(res, ['a', 'c']) res = [ f['x'] for f in layer.getFeatures( QgsFeatureRequest().setInvalidGeometryCheck( QgsFeatureRequest.GeometryAbortOnInvalid)) ] self.assertEqual(res, ['a']) # with callback self.callback_feature_val = None def callback(feature): self.callback_feature_val = feature['x'] res = [ f['x'] for f in layer.getFeatures(QgsFeatureRequest( ).setInvalidGeometryCheck(QgsFeatureRequest.GeometryAbortOnInvalid ).setInvalidGeometryCallback(callback)) ] self.assertEqual(res, ['a']) self.assertEqual(self.callback_feature_val, 'b') # clear callback res = [ f['x'] for f in layer.getFeatures(QgsFeatureRequest( ).setInvalidGeometryCheck(QgsFeatureRequest.GeometryAbortOnInvalid ).setInvalidGeometryCallback(None)) ] self.assertEqual(res, ['a']) # check with filter fids res = [ f['x'] for f in layer.getFeatures(QgsFeatureRequest().setFilterFid(f2.id( )).setInvalidGeometryCheck(QgsFeatureRequest.GeometryNoCheck)) ] self.assertEqual(res, ['b']) res = [ f['x'] for f in layer.getFeatures(QgsFeatureRequest().setFilterFid(f2.id( )).setInvalidGeometryCheck(QgsFeatureRequest.GeometrySkipInvalid)) ] self.assertEqual(res, []) res = [ f['x'] for f in layer.getFeatures(QgsFeatureRequest().setFilterFid( f2.id()).setInvalidGeometryCheck( QgsFeatureRequest.GeometryAbortOnInvalid)) ] self.assertEqual(res, []) f4 = QgsFeature(4) f4.setAttributes(["d"]) f4.setGeometry( QgsGeometry.fromWkt( 'Polygon((0 0, 1 0, 0 1, 1 1, 0 0))')) # invalid # check with added features layer.startEditing() self.assertTrue(layer.addFeatures([f4])) res = [ f['x'] for f in layer.getFeatures(QgsFeatureRequest( ).setInvalidGeometryCheck(QgsFeatureRequest.GeometryNoCheck)) ] self.assertEqual(set(res), {'a', 'b', 'c', 'd'}) res = [ f['x'] for f in layer.getFeatures(QgsFeatureRequest( ).setInvalidGeometryCheck(QgsFeatureRequest.GeometrySkipInvalid)) ] self.assertEqual(set(res), {'a', 'c'}) res = [ f['x'] for f in layer.getFeatures( QgsFeatureRequest().setInvalidGeometryCheck( QgsFeatureRequest.GeometryAbortOnInvalid)) ] self.assertEqual(res, ['a']) # check with features with changed geometry layer.rollBack() layer.startEditing() layer.changeGeometry( 2, QgsGeometry.fromWkt('Polygon((0 0, 1 0, 1 1, 0 1, 0 0))')) # valid layer.changeGeometry( 3, QgsGeometry.fromWkt( 'Polygon((0 0, 1 0, 0 1, 1 1, 0 0))')) # invalid res = [ f['x'] for f in layer.getFeatures(QgsFeatureRequest( ).setInvalidGeometryCheck(QgsFeatureRequest.GeometryNoCheck)) ] self.assertEqual(set(res), {'a', 'b', 'c'}) res = [ f['x'] for f in layer.getFeatures(QgsFeatureRequest( ).setInvalidGeometryCheck(QgsFeatureRequest.GeometrySkipInvalid)) ] self.assertEqual(set(res), {'a', 'b'}) res = [ f['x'] for f in layer.getFeatures( QgsFeatureRequest().setInvalidGeometryCheck( QgsFeatureRequest.GeometryAbortOnInvalid)) ] self.assertEqual(res, ['a', 'b']) layer.rollBack()
def processAlgorithm(self, parameters, context, feedback): source = self.parameterAsSource(parameters, self.INPUT_VECTOR, context) raster_layer = self.parameterAsRasterLayer(parameters, self.INPUT_RASTER, context) rasterPath = exportRasterLayer(raster_layer) rasterDS = gdal.Open(rasterPath, gdal.GA_ReadOnly) geoTransform = rasterDS.GetGeoTransform() rasterDS = None fields = QgsFields() fields.append(QgsField('id', QVariant.Int, '', 10, 0)) fields.append(QgsField('line_id', QVariant.Int, '', 10, 0)) fields.append(QgsField('point_id', QVariant.Int, '', 10, 0)) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, QgsWkbTypes.Point, raster_layer.crs()) outFeature = QgsFeature() outFeature.setFields(fields) self.fid = 0 self.lineId = 0 self.pointId = 0 features = source.getFeatures(QgsFeatureRequest().setDestinationCrs( raster_layer.crs())) total = 100.0 / source.featureCount() if source.featureCount() else 0 for current, f in enumerate(features): if feedback.isCanceled(): break geom = f.geometry() if geom.isMultipart(): lines = geom.asMultiPolyline() for line in lines: for i in range(len(line) - 1): p1 = line[i] p2 = line[i + 1] (x1, y1) = raster.mapToPixel(p1.x(), p1.y(), geoTransform) (x2, y2) = raster.mapToPixel(p2.x(), p2.y(), geoTransform) self.buildLine(x1, y1, x2, y2, geoTransform, sink, outFeature) else: points = geom.asPolyline() for i in range(len(points) - 1): p1 = points[i] p2 = points[i + 1] (x1, y1) = raster.mapToPixel(p1.x(), p1.y(), geoTransform) (x2, y2) = raster.mapToPixel(p2.x(), p2.y(), geoTransform) self.buildLine(x1, y1, x2, y2, geoTransform, sink, outFeature) self.pointId = 0 self.lineId += 1 feedback.setProgress(int(current * total)) return {self.OUTPUT: dest_id}