def testUpdatedFields(self): """Test when referenced layer update its fields https://issues.qgis.org/issues/20893 """ ml = QgsVectorLayer("Point?srid=EPSG:4326&field=a:int", "mem", "memory") self.assertEqual(ml.isValid(), True) QgsProject.instance().addMapLayer(ml) ml.startEditing() f1 = QgsFeature(ml.fields()) f1.setGeometry(QgsGeometry.fromWkt('POINT(2 3)')) ml.addFeatures([f1]) ml.commitChanges() vl = QgsVectorLayer("?query=select a, geometry from mem", "vl", "virtual") self.assertEqual(vl.isValid(), True) # add one more field ml.dataProvider().addAttributes([QgsField('newfield', QVariant.Int)]) ml.updateFields() self.assertEqual(ml.featureCount(), vl.featureCount()) self.assertEqual(vl.fields().count(), 1) geometry = next(vl.getFeatures()).geometry() self.assertTrue(geometry) point = geometry.asPoint() self.assertEqual(point.x(), 2) self.assertEqual(point.y(), 3) QgsProject.instance().removeMapLayer(ml)
def test_SplitFeature(self): """Test sqlite feature can be split""" tmpfile = os.path.join(self.basetestpath, 'testGeopackageSplitFeatures.sqlite') ds = ogr.GetDriverByName('SQlite').CreateDataSource(tmpfile) lyr = ds.CreateLayer('test', geom_type=ogr.wkbPolygon) lyr.CreateField(ogr.FieldDefn('str_field', ogr.OFTString)) f = ogr.Feature(lyr.GetLayerDefn()) f.SetGeometry(ogr.CreateGeometryFromWkt('POLYGON ((0 0,0 1,1 1,1 0,0 0))')) lyr.CreateFeature(f) f = None ds = None layer = QgsVectorLayer(u'{}'.format(tmpfile) + "|layername=" + "test", 'test', u'ogr') # Check that pk field has unique constraint fields = layer.fields() pkfield = fields.at(0) self.assertTrue(pkfield.constraints().constraints() & QgsFieldConstraints.ConstraintUnique) self.assertTrue(layer.isValid()) self.assertTrue(layer.isSpatial()) self.assertEqual([f for f in layer.getFeatures()][0].geometry().asWkt(), 'Polygon ((0 0, 0 1, 1 1, 1 0, 0 0))') layer.startEditing() self.assertEqual(layer.splitFeatures([QgsPointXY(0.5, 0), QgsPointXY(0.5, 1)], 0), 0) self.assertTrue(layer.commitChanges()) self.assertEqual(layer.featureCount(), 2) layer = QgsVectorLayer(u'{}'.format(tmpfile) + "|layername=" + "test", 'test', u'ogr') self.assertEqual(layer.featureCount(), 2) self.assertEqual([f for f in layer.getFeatures()][0].geometry().asWkt(), 'Polygon ((0.5 0, 0.5 1, 1 1, 1 0, 0.5 0))') self.assertEqual([f for f in layer.getFeatures()][1].geometry().asWkt(), 'Polygon ((0.5 1, 0.5 0, 0 0, 0 1, 0.5 1))')
def testFeatureSourceInput(self): # create a memory layer and add to project and context layer = QgsVectorLayer("Point?crs=epsg:3857&field=fldtxt:string&field=fldint:integer", "testmem", "memory") self.assertTrue(layer.isValid()) pr = layer.dataProvider() f = QgsFeature() f.setAttributes(["test", 123]) f.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(100, 200))) f2 = QgsFeature() f2.setAttributes(["test2", 457]) f2.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(110, 200))) self.assertTrue(pr.addFeatures([f, f2])) self.assertEqual(layer.featureCount(), 2) # select first feature layer.selectByIds([next(layer.getFeatures()).id()]) self.assertEqual(len(layer.selectedFeatureIds()), 1) QgsProject.instance().addMapLayer(layer) context = QgsProcessingContext() context.setProject(QgsProject.instance()) alg = QgsApplication.processingRegistry().createAlgorithmById('grass7:v.buffer') self.assertIsNotNone(alg) temp_file = os.path.join(self.temp_dir, 'grass_output_sel.shp') parameters = {'input': QgsProcessingFeatureSourceDefinition('testmem', True), 'cats': '', 'where': '', 'type': [0, 1, 4], 'distance': 1, 'minordistance': None, 'angle': 0, 'column': None, 'scale': 1, 'tolerance': 0.01, '-s': False, '-c': False, '-t': False, 'output': temp_file, 'GRASS_REGION_PARAMETER': None, 'GRASS_SNAP_TOLERANCE_PARAMETER': -1, 'GRASS_MIN_AREA_PARAMETER': 0.0001, 'GRASS_OUTPUT_TYPE_PARAMETER': 0, 'GRASS_VECTOR_DSCO': '', 'GRASS_VECTOR_LCO': ''} feedback = QgsProcessingFeedback() results, ok = alg.run(parameters, context, feedback) self.assertTrue(ok) self.assertTrue(os.path.exists(temp_file)) # make sure that layer has correct features res = QgsVectorLayer(temp_file, 'res') self.assertTrue(res.isValid()) self.assertEqual(res.featureCount(), 1) QgsProject.instance().removeMapLayer(layer)
def processAlgorithm(self, parameters, context, feedback): layers = self.getParameterValue(self.INPUT_DATASOURCES) query = self.getParameterValue(self.INPUT_QUERY) uid_field = self.getParameterValue(self.INPUT_UID_FIELD) geometry_field = self.getParameterValue(self.INPUT_GEOMETRY_FIELD) geometry_type = self.getParameterValue(self.INPUT_GEOMETRY_TYPE) geometry_crs = self.getParameterValue(self.INPUT_GEOMETRY_CRS) df = QgsVirtualLayerDefinition() layerIdx = 1 if layers: for layerSource in layers.split(';'): layer = QgsProcessingUtils.mapLayerFromString(layerSource, context) if layer: df.addSource('input{}'.format(layerIdx), layer.id()) layerIdx += 1 if query == '': raise GeoAlgorithmExecutionException( self.tr('Empty SQL. Please enter valid SQL expression and try again.')) else: df.setQuery(query) if uid_field: df.setUid(uid_field) if geometry_type == 1: # no geometry df.setGeometryWkbType(QgsWkbTypes.NullGeometry) else: if geometry_field: df.setGeometryField(geometry_field) if geometry_type > 1: df.setGeometryWkbType(geometry_type - 1) if geometry_crs: crs = QgsCoordinateReferenceSystem(geometry_crs) if crs.isValid(): df.setGeometrySrid(crs.postgisSrid()) vLayer = QgsVectorLayer(df.toString(), "temp_vlayer", "virtual") if not vLayer.isValid(): raise GeoAlgorithmExecutionException(vLayer.dataProvider().error().message()) writer = self.getOutputFromName(self.OUTPUT_LAYER).getVectorWriter(vLayer.fields(), vLayer.wkbType() if geometry_type != 1 else 1, vLayer.crs(), context) features = QgsProcessingUtils.getFeatures(vLayer, context) total = 100.0 / vLayer.featureCount() if vLayer.featureCount() else 0 outFeat = QgsFeature() for current, inFeat in enumerate(features): outFeat.setAttributes(inFeat.attributes()) if geometry_type != 1: outFeat.setGeometry(inFeat.geometry()) writer.addFeature(outFeat, QgsFeatureSink.FastInsert) feedback.setProgress(int(current * total)) del writer
def test_SplitTruToCreateCutEdge(self): """Try to creat a cut edge""" layer = QgsVectorLayer("dbname=test.sqlite table=test_pg (geometry)", "test_pg", "spatialite") assert(layer.isValid()) assert(layer.hasGeometryType()) layer.featureCount() == 1 or die("wrong number of features") layer.startEditing() layer.splitFeatures([QgsPoint(1.5, -0.5), QgsPoint(1.5, 1.5)], 0) == 0 or die("error when trying to create an invalid polygon in split") layer.commitChanges() or die("this commit should work") layer.featureCount() == 1 or die("wrong number of features, polygon should be unafected by cut")
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_SplitMultipolygon(self): """Split multipolygon""" layer = QgsVectorLayer("dbname=test.sqlite table=test_mpg (geometry)", "test_mpg", "spatialite") assert(layer.isValid()) assert(layer.hasGeometryType()) layer.featureCount() == 1 or die("wrong number of features") layer.startEditing() layer.splitFeatures([QgsPoint(0.5, -0.5), QgsPoint(0.5, 1.5)], 0) == 0 or die("error in split of one polygon of multipolygon") layer.splitFeatures([QgsPoint(2.5, -0.5), QgsPoint(2.5, 4)], 0) == 0 or die("error in split of two polygons of multipolygon at a time") layer.commitChanges() or die("this commit should work") layer.featureCount() == 7 or die("wrong number of features after 2 split")
def test_SplitFeature(self): """Create spatialite database""" layer = QgsVectorLayer("dbname=%s table=test_pg (geometry)" % self.dbname, "test_pg", "spatialite") assert(layer.isValid()) assert(layer.hasGeometryType()) layer.startEditing() layer.splitFeatures([QgsPoint(0.5, -0.5), QgsPoint(0.5, 1.5)], 0) == 0 or die("error in split") layer.splitFeatures([QgsPoint(-0.5, 0.5), QgsPoint(1.5, 0.5)], 0) == 0 or die("error in split") if not layer.commitChanges(): die("this commit should work") layer.featureCount() == 4 or die("we should have 4 features after 2 split")
def processAlgorithm(self, parameters, context, feedback): layers = self.parameterAsLayerList(parameters, self.INPUT_DATASOURCES, context) query = self.parameterAsString(parameters, self.INPUT_QUERY, context) uid_field = self.parameterAsString(parameters, self.INPUT_UID_FIELD, context) geometry_field = self.parameterAsString(parameters, self.INPUT_GEOMETRY_FIELD, context) geometry_type = self.parameterAsEnum(parameters, self.INPUT_GEOMETRY_TYPE, context) geometry_crs = self.parameterAsCrs(parameters, self.INPUT_GEOMETRY_CRS, context) df = QgsVirtualLayerDefinition() for layerIdx, layer in enumerate(layers): df.addSource('input{}'.format(layerIdx + 1), layer.id()) if query == '': raise QgsProcessingException( self.tr('Empty SQL. Please enter valid SQL expression and try again.')) else: localContext = self.createExpressionContext(parameters, context) expandedQuery = QgsExpression.replaceExpressionText(query, localContext) df.setQuery(expandedQuery) if uid_field: df.setUid(uid_field) if geometry_type == 1: # no geometry df.setGeometryWkbType(QgsWkbTypes.NoGeometry) else: if geometry_field: df.setGeometryField(geometry_field) if geometry_type > 1: df.setGeometryWkbType(geometry_type - 1) if geometry_crs.isValid(): df.setGeometrySrid(geometry_crs.postgisSrid()) vLayer = QgsVectorLayer(df.toString(), "temp_vlayer", "virtual") if not vLayer.isValid(): raise QgsProcessingException(vLayer.dataProvider().error().message()) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, vLayer.fields(), vLayer.wkbType() if geometry_type != 1 else 1, vLayer.crs()) if sink is None: raise QgsProcessingException(self.invalidSinkError(parameters, self.OUTPUT)) features = vLayer.getFeatures() total = 100.0 / vLayer.featureCount() if vLayer.featureCount() else 0 for current, inFeat in enumerate(features): if feedback.isCanceled(): break sink.addFeature(inFeat, QgsFeatureSink.FastInsert) feedback.setProgress(int(current * total)) return {self.OUTPUT: dest_id}
def create_grid(size): """Create a polygonal grid using Processing. :param size: The cell size. :type size: int :return: The grid layer in memory. :rtype: QgsVectorLayer """ output_filename = unique_filename(prefix='grid', suffix='.shp') result = processing.runalg( 'qgis:vectorgrid', '336199.970553,352338.397991,7636164.67975,7648562.41208', size, # X spacing size, # Y spacing 0, # Output as polygons output_filename) layer = QgsVectorLayer(output_filename, 'grid', 'ogr') layer.setCrs(QgsCoordinateReferenceSystem(32740)) remove_fields(layer, ['xmin', 'xmax', 'ymin', 'ymax']) # Make a copy in memory memory = create_memory_layer( 'grid', layer.geometryType(), layer.crs(), layer.fields()) copy_layer(layer, memory) print "NB cells : %s" % layer.featureCount() return memory
def testApproxFeatureCountAndExtent(self): """ Test perf improvement for for https://issues.qgis.org/issues/18402 """ tmpfile = os.path.join(self.basetestpath, 'testApproxFeatureCountAndExtent.gpkg') ds = ogr.GetDriverByName('GPKG').CreateDataSource(tmpfile) lyr = ds.CreateLayer('test', geom_type=ogr.wkbPoint) f = ogr.Feature(lyr.GetLayerDefn()) f.SetGeometry(ogr.CreateGeometryFromWkt('POINT(0 1)')) lyr.CreateFeature(f) f = ogr.Feature(lyr.GetLayerDefn()) f.SetGeometry(ogr.CreateGeometryFromWkt('POINT(2 3)')) lyr.CreateFeature(f) fid = f.GetFID() f = ogr.Feature(lyr.GetLayerDefn()) f.SetGeometry(ogr.CreateGeometryFromWkt('POINT(4 5)')) lyr.CreateFeature(f) lyr.DeleteFeature(fid) ds = None ds = ogr.Open(tmpfile, update=1) ds.ExecuteSQL('DROP TABLE gpkg_ogr_contents') ds = None os.environ['QGIS_GPKG_FC_THRESHOLD'] = '1' vl = QgsVectorLayer(u'{}'.format(tmpfile) + "|layername=" + "test", 'test', u'ogr') self.assertTrue(vl.isValid()) fc = vl.featureCount() del os.environ['QGIS_GPKG_FC_THRESHOLD'] self.assertEqual(fc, 3) # didn't notice the hole reference = QgsGeometry.fromRect(QgsRectangle(0, 1, 4, 5)) provider_extent = QgsGeometry.fromRect(vl.extent()) self.assertTrue(QgsGeometry.compare(provider_extent.asPolygon()[0], reference.asPolygon()[0], 0.00001), provider_extent.asPolygon()[0])
def testDontRepackOnReload(self): ''' Test fix for #18421 ''' 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') feature_count = vl.featureCount() # Start an iterator that will open a new connection iterator = vl.getFeatures() next(iterator) # Delete another feature while in update mode vl.dataProvider().enterUpdateMode() vl.dataProvider().reloadData() vl.dataProvider().deleteFeatures([0]) # Test that repacking has not been done (since in update mode) ds = osgeo.ogr.Open(datasource) self.assertTrue(ds.GetLayer(0).GetFeatureCount() == feature_count) ds = None vl = None
def test_FeatureCount(self): myPath = os.path.join(unitTestDataPath(), 'lines.shp') myLayer = QgsVectorLayer(myPath, 'Lines', 'ogr') myCount = myLayer.featureCount() myExpectedCount = 6 myMessage = '\nExpected: %s\nGot: %s' % (myCount, myExpectedCount) assert myCount == myExpectedCount, myMessage
def testDeleteShapes(self): ''' Test fix for #11007 ''' 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') feature_count = vl.featureCount() # Start an iterator that will open a new connection iterator = vl.getFeatures() f = next(iterator) # Delete a feature self.assertTrue(vl.startEditing()) self.assertTrue(vl.deleteFeature(1)) self.assertTrue(vl.commitChanges()) # Test the content of the shapefile while it is still opened ds = osgeo.ogr.Open(datasource) # Test repacking has been done self.assertTrue(ds.GetLayer(0).GetFeatureCount(), feature_count - 1) ds = None vl = None
def _aliased_sql_helper(self, dbname): queries = ( '(SELECT * FROM (SELECT * from \\"some view\\"))', '(SELECT * FROM \\"some view\\")', '(select sd.* from somedata as sd left join somedata as sd2 on ( sd2.name = sd.name ))', '(select sd.* from \\"somedata\\" as sd left join \\"somedata\\" as sd2 on ( sd2.name = sd.name ))', "(SELECT * FROM somedata as my_alias1\n)", "(SELECT * FROM somedata as my_alias2)", "(SELECT * FROM somedata AS my_alias3)", '(SELECT * FROM \\"somedata\\" as my_alias4\n)', '(SELECT * FROM (SELECT * FROM \\"somedata\\"))', '(SELECT my_alias5.* FROM (SELECT * FROM \\"somedata\\") AS my_alias5)', '(SELECT my_alias6.* FROM (SELECT * FROM \\"somedata\\" as my_alias\n) AS my_alias6)', '(SELECT my_alias7.* FROM (SELECT * FROM \\"somedata\\" as my_alias\n) AS my_alias7\n)', '(SELECT my_alias8.* FROM (SELECT * FROM \\"some data\\") AS my_alias8)', '(SELECT my_alias9.* FROM (SELECT * FROM \\"some data\\" as my_alias\n) AS my_alias9)', '(SELECT my_alias10.* FROM (SELECT * FROM \\"some data\\" as my_alias\n) AS my_alias10\n)', '(select sd.* from \\"some data\\" as sd left join \\"some data\\" as sd2 on ( sd2.name = sd.name ))', '(SELECT * FROM \\"some data\\" as my_alias11\n)', '(SELECT * FROM \\"some data\\" as my_alias12)', '(SELECT * FROM \\"some data\\" AS my_alias13)', '(SELECT * from \\"some data\\" AS my_alias14\n)', '(SELECT * FROM (SELECT * from \\"some data\\"))', ) for sql in queries: vl = QgsVectorLayer('dbname=\'{}\' table="{}" (geom) sql='.format(dbname, sql), 'test', 'spatialite') self.assertTrue(vl.isValid(), 'dbname: {} - sql: {}'.format(dbname, sql)) self.assertTrue(vl.featureCount() > 1) self.assertTrue(vl.isSpatial())
def test_split_by_polygon(self): """Test split_by_polygon work""" line_before = QgsVectorLayer( self.line_before + '.shp', 'test', 'ogr') expected_lines = QgsVectorLayer( self.line_after + '.shp', 'test', 'ogr') polygon_layer = QgsVectorLayer( self.polygon_base + '.shp', 'test', 'ogr') # Only one polygon is stored in the layer for feature in polygon_layer.getFeatures(): polygon = feature.geometry() split_lines = split_by_polygon( line_before, polygon, mark_value=('flooded', 1)) # Test the lines is not multipart for feature in split_lines.getFeatures(): self.assertFalse(feature.geometry().isMultipart()) self.assertEqual(expected_lines.featureCount(), split_lines.featureCount()) # Assert for every line from split_lines # we can find the same line for feature in split_lines.getFeatures(): found = False for expected in expected_lines.getFeatures(): if (feature.attributes() == expected.attributes()) and \ (feature.geometry().isGeosEqual(expected.geometry())): found = True break self.assertTrue(found) # Split by the extent (The result is the copy of the layer) line_before.updateExtents() # Expand extent to cover the lines (add epsilon to bounds) epsilon = 0.0001 # A small number extent = line_before.extent() new_extent = QgsRectangle( extent.xMinimum() - epsilon, extent.yMinimum() - epsilon, extent.xMaximum() + epsilon, extent.yMaximum() + epsilon ) new_extent = QgsGeometry().fromRect(new_extent) split_lines = split_by_polygon( line_before, new_extent) for feature in split_lines.getFeatures(): found = False for expected in line_before.getFeatures(): if (feature.attributes() == expected.attributes()) and \ (feature.geometry().isGeosEqual(expected.geometry())): found = True break self.assertTrue(found)
def testSubsetStringExtent_bug17863(self): """Check that the extent is correct when applied in the ctor and when modified after a subset string is set """ def _lessdigits(s): return re.sub(r'(\d+\.\d{3})\d+', r'\1', s) testPath = "dbname=%s table='test_filter' (geometry) key='id'" % self.dbname subSetString = '"name" = \'int\'' subSet = ' sql=%s' % subSetString # unfiltered vl = QgsVectorLayer(testPath, 'test', 'spatialite') self.assertTrue(vl.isValid()) self.assertEqual(vl.featureCount(), 8) unfiltered_extent = _lessdigits(vl.extent().toString()) self.assertNotEqual('Empty', unfiltered_extent) del(vl) # filter after construction ... subSet_vl2 = QgsVectorLayer(testPath, 'test', 'spatialite') self.assertEqual(_lessdigits(subSet_vl2.extent().toString()), unfiltered_extent) self.assertEqual(subSet_vl2.featureCount(), 8) # ... apply filter now! subSet_vl2.setSubsetString(subSetString) self.assertEqual(subSet_vl2.featureCount(), 4) self.assertEqual(subSet_vl2.subsetString(), subSetString) self.assertNotEqual(_lessdigits(subSet_vl2.extent().toString()), unfiltered_extent) filtered_extent = _lessdigits(subSet_vl2.extent().toString()) del(subSet_vl2) # filtered in constructor subSet_vl = QgsVectorLayer(testPath + subSet, 'subset_test', 'spatialite') self.assertEqual(subSet_vl.subsetString(), subSetString) self.assertTrue(subSet_vl.isValid()) # This was failing in bug 17863 self.assertEqual(subSet_vl.featureCount(), 4) self.assertEqual(_lessdigits(subSet_vl.extent().toString()), filtered_extent) self.assertNotEqual(_lessdigits(subSet_vl.extent().toString()), unfiltered_extent) self.assertTrue(subSet_vl.setSubsetString('')) self.assertEqual(subSet_vl.featureCount(), 8) self.assertEqual(_lessdigits(subSet_vl.extent().toString()), unfiltered_extent)
def testSubsetStringRegexp(self): """Check that the provider supports the REGEXP syntax""" testPath = "dbname=%s table='test_filter' (geometry) key='id'" % self.dbname vl = QgsVectorLayer(testPath, 'test', 'spatialite') self.assertTrue(vl.isValid()) vl.setSubsetString('"name" REGEXP \'[txe]\'') self.assertEqual(vl.featureCount(), 4) del(vl)
def testBboxRestriction(self): """ Test limiting provider to features within a preset bounding box """ endpoint = self.basetestpath + '/fake_qgis_http_endpoint' vl = QgsVectorLayer("url='http://" + endpoint + "' crs='epsg:4326' bbox='-70.000000,67.000000,-60.000000,80.000000'", 'test', 'arcgisfeatureserver') self.assertTrue(vl.isValid()) self.assertEqual(vl.featureCount(), 2) self.assertEqual([f['pk'] for f in vl.getFeatures()], [2, 4])
def createLayerWithOnePoint(): layer = QgsVectorLayer("Point?field=fldtxt:string&field=fldint:integer", "addfeat", "memory") pr = layer.dataProvider() f = QgsFeature() f.setAttributes(["test", 123]) assert pr.addFeatures([f]) assert layer.featureCount() == 1 return layer
def test_SplitFeature(self): """Create SpatiaLite database""" layer = QgsVectorLayer("dbname=%s table=test_pg (geometry)" % self.dbname, "test_pg", "spatialite") self.assertTrue(layer.isValid()) self.assertTrue(layer.isSpatial()) layer.startEditing() self.assertEqual(layer.splitFeatures([QgsPointXY(0.75, -0.5), QgsPointXY(0.75, 1.5)], 0), 0) self.assertEqual(layer.splitFeatures([QgsPointXY(-0.5, 0.25), QgsPointXY(1.5, 0.25)], 0), 0) self.assertTrue(layer.commitChanges()) self.assertEqual(layer.featureCount(), 4)
def testRepackAtFirstSave(self): ''' Test fix for #15407 ''' # This requires a GDAL fix done per https://trac.osgeo.org/gdal/ticket/6672 # but on non-Windows version the test would succeed if int(osgeo.gdal.VersionInfo('VERSION_NUM')) < GDAL_COMPUTE_VERSION(2, 1, 2): return 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') ds = osgeo.ogr.Open(datasource) lyr = ds.GetLayer(0) original_feature_count = lyr.GetFeatureCount() lyr.DeleteFeature(2) ds = None vl = QgsVectorLayer('{}|layerid=0'.format(datasource), 'test', 'ogr') self.assertTrue(vl.featureCount(), original_feature_count) # Edit a feature (attribute change only) self.assertTrue(vl.startEditing()) self.assertTrue(vl.dataProvider().changeAttributeValues({0: {0: 100}})) # Commit changes and check no error is emitted cbk = ErrorReceiver() vl.dataProvider().raiseError.connect(cbk.receiveError) self.assertTrue(vl.commitChanges()) self.assertIsNone(cbk.msg) self.assertTrue(vl.featureCount(), original_feature_count - 1) vl = None # Test repacking has been done ds = osgeo.ogr.Open(datasource) self.assertTrue(ds.GetLayer(0).GetFeatureCount(), original_feature_count - 1) ds = None
def testRepack(self): vl = QgsVectorLayer('{}|layerid=0'.format(self.repackfile), 'test', 'ogr') ids = [f.id() for f in vl.getFeatures(QgsFeatureRequest().setFilterExpression('pk=1'))] vl.selectByIds(ids) self.assertEqual(vl.selectedFeatureIds(), ids) self.assertEqual(vl.featureCount(), 5) self.assertTrue(vl.startEditing()) self.assertTrue(vl.deleteFeature(3)) self.assertTrue(vl.commitChanges()) self.assertTrue(vl.selectedFeatureCount() == 0 or vl.selectedFeatures()[0]['pk'] == 1)
def _modifyAndLoadWfs(): valid = {} urls = os.getenv(TEST_URLS).split(",") for url in urls: try: url = url.strip() + "/wfs" uri = "%s?typename=union&version=1.0.0&request=GetFeature&service=WFS" % url layer = QgsVectorLayer(uri, "testlayer", "WFS") featureCount = layer.featureCount() featureid = list(layer.getFeatures())[0].id() layer.startEditing() layer.deleteFeature(featureid) layer.commitChanges() layer = QgsVectorLayer(uri, "testlayer", "WFS") valid[url] = layer.featureCount() == featureCount - 1 except: valid[url] = False failed = [k for k,v in valid.items() if not v] if failed: raise AssertionError("Test failed for the following URLs: " + str(failed))
def testIntersection(self): myLine = QgsGeometry.fromPolyline([QgsPoint(0, 0),QgsPoint(1, 1),QgsPoint(2, 2)]) myPoint = QgsGeometry.fromPoint(QgsPoint(1, 1)) intersectionGeom = QgsGeometry.intersection(myLine, myPoint) myMessage = ('Expected:\n%s\nGot:\n%s\n' % (QGis.Point, intersectionGeom.type())) assert intersectionGeom.wkbType() == QGis.WKBPoint, myMessage layer = QgsVectorLayer("Point", "intersection", "memory") assert layer.isValid(), "Failed to create valid point memory layer" provider = layer.dataProvider() ft = QgsFeature() ft.setGeometry(intersectionGeom) provider.addFeatures([ft]) myMessage = ('Expected:\n%s\nGot:\n%s\n' % (1, layer.featureCount())) assert layer.featureCount() == 1, myMessage
def test_non_ascii_output(self): # create a memory layer and add to project and context layer = QgsVectorLayer("Point?crs=epsg:3857&field=fldtxt:string&field=fldint:integer", "testmem", "memory") self.assertTrue(layer.isValid()) pr = layer.dataProvider() f = QgsFeature() f.setAttributes(["test", 123]) f.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(100, 200))) f2 = QgsFeature() f2.setAttributes(["test2", 457]) f2.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(110, 200))) self.assertTrue(pr.addFeatures([f, f2])) self.assertEqual(layer.featureCount(), 2) QgsProject.instance().addMapLayer(layer) context = QgsProcessingContext() context.setProject(QgsProject.instance()) alg = QgsApplication.processingRegistry().createAlgorithmById('saga:fixeddistancebuffer') self.assertIsNotNone(alg) temp_file = os.path.join(self.temp_dir, 'non_ascii_ñññ.shp') parameters = {'SHAPES': 'testmem', 'DIST_FIELD_DEFAULT': 5, 'NZONES': 1, 'DARC': 5, 'DISSOLVE': False, 'POLY_INNER': False, 'BUFFER': temp_file} feedback = QgsProcessingFeedback() results, ok = alg.run(parameters, context, feedback) self.assertTrue(ok) self.assertTrue(os.path.exists(temp_file)) # make sure that layer has correct features res = QgsVectorLayer(temp_file, 'res') self.assertTrue(res.isValid()) self.assertEqual(res.featureCount(), 2) QgsProject.instance().removeMapLayer(layer)
def test_filterfid_crossjoin(self): l0 = QgsVectorLayer(os.path.join(self.testDataDir, "france_parts.shp"), "france_parts", "ogr") self.assertTrue(l0.isValid()) QgsProject.instance().addMapLayer(l0) l1 = QgsVectorLayer(os.path.join(self.testDataDir, "points.shp"), "points", "ogr") self.assertTrue(l1.isValid()) QgsProject.instance().addMapLayer(l1) # cross join query = toPercent("SELECT * FROM france_parts,points") vl = QgsVectorLayer("?query=%s" % query, "tt", "virtual") self.assertEqual(vl.featureCount(), l0.featureCount() * l1.featureCount()) # test with FilterFid requests f = next(vl.getFeatures(QgsFeatureRequest().setFilterFid(0))) idx = f.fields().indexOf('Class') self.assertEqual(f.id(), 0) self.assertEqual(f.attributes()[idx], 'Jet') f = next(vl.getFeatures(QgsFeatureRequest().setFilterFid(5))) self.assertEqual(f.id(), 5) self.assertEqual(f.attributes()[idx], 'Biplane') # test with FilterFid requests fit = vl.getFeatures(QgsFeatureRequest().setFilterFids([0, 3, 5])) f = next(fit) self.assertEqual(f.id(), 0) self.assertEqual(f.attributes()[idx], 'Jet') f = next(fit) self.assertEqual(f.id(), 3) self.assertEqual(f.attributes()[idx], 'Jet') f = next(fit) self.assertEqual(f.id(), 5) self.assertEqual(f.attributes()[idx], 'Biplane')
def testGetOgrCompatibleSourceFromFeatureSource(self): # create a memory layer and add to project and context layer = QgsVectorLayer("Point?field=fldtxt:string&field=fldint:integer", "testmem", "memory") self.assertTrue(layer.isValid()) pr = layer.dataProvider() f = QgsFeature() f.setAttributes(["test", 123]) f.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(100, 200))) f2 = QgsFeature() f2.setAttributes(["test2", 457]) f2.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(100, 200))) self.assertTrue(pr.addFeatures([f, f2])) self.assertEqual(layer.featureCount(), 2) # select first feature layer.selectByIds([next(layer.getFeatures()).id()]) self.assertEqual(len(layer.selectedFeatureIds()), 1) QgsProject.instance().addMapLayer(layer) context = QgsProcessingContext() context.setProject(QgsProject.instance()) alg = QgsApplication.processingRegistry().createAlgorithmById('gdal:buffervectors') self.assertIsNotNone(alg) parameters = {'INPUT': QgsProcessingFeatureSourceDefinition('testmem', True)} feedback = QgsProcessingFeedback() # check that memory layer is automatically saved out to shape when required by GDAL algorithms ogr_data_path, ogr_layer_name = alg.getOgrCompatibleSource('INPUT', parameters, context, feedback, executing=True) self.assertTrue(ogr_data_path) self.assertTrue(ogr_data_path.endswith('.shp')) self.assertTrue(os.path.exists(ogr_data_path)) self.assertTrue(ogr_layer_name) # make sure that layer has only selected feature res = QgsVectorLayer(ogr_data_path, 'res') self.assertTrue(res.isValid()) self.assertEqual(res.featureCount(), 1) QgsProject.instance().removeMapLayer(layer)
def polygonize_gdal( raster, threshold_min=0.0, threshold_max=float('inf')): """ Function to polygonize raster. Areas (pixels) with threshold_min < pixel_values < threshold_max will be converted to polygons. :param raster: Raster layer :type raster: QgsRasterLayer :param threshold_min: Value that splits raster to flooded or not flooded. :type threshold_min: float :param threshold_max: Value that splits raster to flooded or not flooded. :type threshold_max: float :returns: Polygonal geometry :rtype: QgsGeometry """ # save qgis raster to disk base_name = unique_filename() file_name = base_name + '.tif' file_writer = QgsRasterFileWriter(file_name) pipe = QgsRasterPipe() provider = raster.dataProvider() if not pipe.set(provider.clone()): msg = "Cannot set pipe provider" raise GetDataError(msg) file_writer.writeRaster( pipe, provider.xSize(), provider.ySize(), provider.extent(), provider.crs()) ( inside_file_name, inside_layer_name, outside_file_name, outside_layer_name ) = polygonize_thresholds(file_name, threshold_min, threshold_max) inside_layer = \ QgsVectorLayer(inside_file_name, inside_layer_name, 'ogr') outside_layer = \ QgsVectorLayer(outside_file_name, outside_layer_name, 'ogr') if inside_layer.featureCount() == 0: return None, None else: return inside_layer, outside_layer
def testPKNotInt(self): """ Check when primary key is not an integer """ # create test db dbname = os.path.join(tempfile.mkdtemp(), "test_pknotint.sqlite") con = spatialite_connect(dbname, isolation_level=None) cur = con.cursor() # try the two different types of index creation for index_creation_method in ['CreateSpatialIndex', 'CreateMbrCache']: table_name = "pk_is_string_{}".format(index_creation_method) cur.execute("BEGIN") sql = "SELECT InitSpatialMetadata()" cur.execute(sql) # create table with spatial index and pk is string sql = "CREATE TABLE {}(id VARCHAR PRIMARY KEY NOT NULL, name TEXT NOT NULL);" cur.execute(sql.format(table_name)) sql = "SELECT AddGeometryColumn('{}', 'geometry', 4326, 'POINT', 'XY')" cur.execute(sql.format(table_name)) sql = "SELECT {}('{}', 'geometry')" cur.execute(sql.format(index_creation_method, table_name)) sql = "insert into {} ('id', 'name', 'geometry') values( 'test_id', 'test_name', st_geomfromtext('POINT(1 2)', 4326))" cur.execute(sql.format(table_name)) cur.execute("COMMIT") testPath = "dbname={} table='{}' (geometry)".format(dbname, table_name) vl = QgsVectorLayer(testPath, 'test', 'spatialite') self.assertTrue(vl.isValid()) self.assertEqual(vl.featureCount(), 1) # make spatial request to force the index use request = QgsFeatureRequest(QgsRectangle(0, 0, 2, 3)) feature = next(vl.getFeatures(request), None) self.assertTrue(feature) self.assertEqual(feature.id(), 1) point = feature.geometry().asPoint() self.assertTrue(point) self.assertEqual(point.x(), 1) self.assertEqual(point.y(), 2) con.close() basepath, filename = os.path.split(dbname) shutil.rmtree(basepath)
def testApproxFeatureCountAndExtent(self): """ Test perf improvement for for https://issues.qgis.org/issues/18402 """ tmpfile = os.path.join(self.basetestpath, 'testApproxFeatureCountAndExtent.gpkg') ds = ogr.GetDriverByName('GPKG').CreateDataSource(tmpfile) lyr = ds.CreateLayer('test', geom_type=ogr.wkbPoint) f = ogr.Feature(lyr.GetLayerDefn()) f.SetGeometry(ogr.CreateGeometryFromWkt('POINT(0 1)')) lyr.CreateFeature(f) f = ogr.Feature(lyr.GetLayerDefn()) f.SetGeometry(ogr.CreateGeometryFromWkt('POINT(2 3)')) lyr.CreateFeature(f) fid = f.GetFID() f = ogr.Feature(lyr.GetLayerDefn()) f.SetGeometry(ogr.CreateGeometryFromWkt('POINT(4 5)')) lyr.CreateFeature(f) lyr.DeleteFeature(fid) ds = None ds = ogr.Open(tmpfile, update=1) ds.ExecuteSQL('DROP TABLE gpkg_ogr_contents') ds = None os.environ['QGIS_GPKG_FC_THRESHOLD'] = '1' vl = QgsVectorLayer(u'{}'.format(tmpfile) + "|layername=" + "test", 'test', u'ogr') self.assertTrue(vl.isValid()) fc = vl.featureCount() del os.environ['QGIS_GPKG_FC_THRESHOLD'] self.assertEqual(fc, 3) # didn't notice the hole reference = QgsGeometry.fromRect(QgsRectangle(0, 1, 4, 5)) provider_extent = QgsGeometry.fromRect(vl.extent()) self.assertTrue( QgsGeometry.compare(provider_extent.asPolygon()[0], reference.asPolygon()[0], 0.00001), provider_extent.asPolygon()[0])
def addIrrepResults(setupObject, irrepDict, irrepFieldName, statusSet): puLayer = QgsVectorLayer(setupObject.puPath, 'Planning units', 'ogr') provider = puLayer.dataProvider() idFieldIndex = puLayer.fields().indexFromName("Unit_ID") statusFieldIndex = puLayer.fields().indexFromName("Status") provider.addAttributes([QgsField(irrepFieldName, QVariant.Double)]) puLayer.updateFields() irrepFieldOrder = provider.fieldNameIndex(irrepFieldName) progressBar = makeProgressBar('Adding summed irreplaceability values to planning unit shapefile') polyCount = 1 polyTotalCount = puLayer.featureCount() puFeatures = puLayer.getFeatures() puLayer.startEditing() for puFeature in puFeatures: progressBar.setValue((polyCount/polyTotalCount) * 100) polyCount += 1 puRow = puFeature.id() puAttributes = puFeature.attributes() puID = puAttributes[idFieldIndex] puStatus = puAttributes[statusFieldIndex] if puStatus in statusSet: summedIrrepValue = 0 try: puIrrepDict = irrepDict[puID] for featID in puIrrepDict: summedIrrepValue += puIrrepDict[featID] except KeyError: pass else: summedIrrepValue = -99 puLayer.changeAttributeValue(puRow, irrepFieldOrder, summedIrrepValue, True) puLayer.commitChanges() clearProgressBar()
def testOverwriteGPKG(self): """Test that overwriting the same origin GPKG file works only if the layername is different""" # Prepare test data ml = QgsVectorLayer('Point?field=firstfield:int&field=secondfield:int', 'test', 'memory') provider = ml.dataProvider() ft = QgsFeature() ft.setAttributes([4, -10]) provider.addFeatures([ft]) filehandle, filename = tempfile.mkstemp('.gpkg') options = QgsVectorFileWriter.SaveVectorOptions() options.driverName = 'GPKG' options.layerName = 'test' write_result, error_message = QgsVectorFileWriter.writeAsVectorFormat( ml, filename, options) self.assertEqual(write_result, QgsVectorFileWriter.NoError, error_message) # Real test vl = QgsVectorLayer("%s|layername=test" % filename, 'src_test', 'ogr') self.assertTrue(vl.isValid()) self.assertEqual(vl.featureCount(), 1) # This must fail write_result, error_message = QgsVectorFileWriter.writeAsVectorFormat( vl, filename, options) self.assertEqual(write_result, QgsVectorFileWriter.ErrCreateDataSource) self.assertEqual(error_message, 'Cannot overwrite a OGR layer in place') options.layerName = 'test2' write_result, error_message = QgsVectorFileWriter.writeAsVectorFormat( vl, filename, options) self.assertEqual(write_result, QgsVectorFileWriter.NoError, error_message)
def testNoSliverPolygons(self): # create a layer with some polygons that will be used as a source for "avoid intersections" l = QgsVectorLayer('MultiPolygon', 'test_layer', 'memory') assert l.isValid() features = [] for i, wkt in enumerate(feat_wkt): f = QgsFeature(i + 1) f.setGeometry(QgsGeometry.fromWkt(wkt)) features.append(f) l.dataProvider().addFeatures(features) assert l.featureCount() == 7 # create a geometry and remove its intersections with other geometries g = QgsGeometry.fromWkt(newg_wkt) assert g.avoidIntersections([l]) == 0 # the resulting multi-polygon must have exactly three parts # (in QGIS 2.0 it has one more tiny part that appears at the border between two of the original polygons) mpg = g.asMultiPolygon() assert len(mpg) == 3
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))) f1 = QgsFeature(ml.fields()) f1.setGeometry(QgsGeometry.fromWkt('POINT(0 0)')) f2 = QgsFeature(ml.fields()) f2.setGeometry(QgsGeometry.fromWkt('POINT(1 1)')) 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) QgsProject.instance().removeMapLayer(ml)
def testLoadStyle(self): """Check that we can store and load a style""" # create test db dbname = os.path.join(tempfile.gettempdir(), "test_loadstyle.sqlite") if os.path.exists(dbname): os.remove(dbname) con = spatialite_connect(dbname, isolation_level=None) cur = con.cursor() cur.execute("BEGIN") sql = "SELECT InitSpatialMetadata()" cur.execute(sql) # simple table with primary key sql = "CREATE TABLE test_pg (id INTEGER NOT NULL PRIMARY KEY, name TEXT NOT NULL)" cur.execute(sql) sql = "SELECT AddGeometryColumn('test_pg', 'geometry', 4326, 'POLYGON', 'XY')" cur.execute(sql) sql = "INSERT INTO test_pg (id, name, geometry) " sql += "VALUES (1, 'toto', GeomFromText('POLYGON((0 0,1 0,1 1,0 1,0 0))', 4326))" cur.execute(sql) cur.execute("COMMIT") con.close() testPath = "dbname=%s table='test_pg' (geometry) key='id'" % dbname vl = QgsVectorLayer(testPath, 'test', 'spatialite') self.assertTrue(vl.isValid()) self.assertEqual(vl.featureCount(), 1) err, ok = vl.loadDefaultStyle() self.assertFalse(ok) vl.saveStyleToDatabase('my_style', 'My description', True, '') err, ok = vl.loadDefaultStyle() self.assertTrue(ok)
def test_AddFeatureNullFid(self): """Test gpkg feature with NULL fid can be added""" tmpfile = os.path.join(self.basetestpath, 'testGeopackageSplitFeatures.gpkg') ds = ogr.GetDriverByName('GPKG').CreateDataSource(tmpfile) lyr = ds.CreateLayer('test', geom_type=ogr.wkbPolygon) lyr.CreateField(ogr.FieldDefn('str_field', ogr.OFTString)) ds = None layer = QgsVectorLayer(u'{}'.format(tmpfile) + "|layername=" + "test", 'test', u'ogr') # Check that pk field has unique constraint fields = layer.fields() pkfield = fields.at(0) self.assertTrue(pkfield.constraints().constraints() & QgsFieldConstraints.ConstraintUnique) # Test add feature with default Fid (NULL) layer.startEditing() f = QgsFeature() feat = QgsFeature(layer.fields()) feat.setGeometry(QgsGeometry.fromWkt('Polygon ((0 0, 0 1, 1 1, 1 0, 0 0))')) feat.setAttribute(1, 'test_value') layer.addFeature(feat) self.assertTrue(layer.commitChanges()) self.assertEqual(layer.featureCount(), 1)
def test_create_geometries(self): layer = QgsVectorLayer('Polygon?crs=epsg:2056&field=value:double(1,0)', 'polygons', "memory") QgsProject.instance().addMapLayers([layer]) pos_x = 2600000 pos_y = 1200000 for value in range(10): points = [] points.append(QgsPointXY(pos_x, pos_y)) points.append(QgsPointXY(pos_x + 200, pos_y)) points.append(QgsPointXY(pos_x + 200, pos_y + 10)) points.append(QgsPointXY(pos_x, pos_y + 10)) points.append(QgsPointXY(pos_x, pos_y)) geometry = QgsGeometry.fromPolygonXY([points]) feature = QgsFeature(layer.fields()) feature.setFields(layer.fields()) feature.setGeometry(geometry) layer.dataProvider().addFeatures([feature]) pos_y += 20 self.assertEqual(10, layer.featureCount())
def testCloneFeatures(self): """ Test that cloning a memory layer also clones features """ vl = QgsVectorLayer( 'Point?crs=epsg:4326&field=f1:integer&field=f2:integer', 'test', 'memory') self.assertTrue(vl.isValid()) f1 = QgsFeature() f1.setAttributes([5, -200]) f2 = QgsFeature() f2.setAttributes([3, 300]) f3 = QgsFeature() f3.setAttributes([1, 100]) res, [f1, f2, f3] = vl.dataProvider().addFeatures([f1, f2, f3]) self.assertEqual(vl.featureCount(), 3) vl2 = vl.clone() self.assertEqual(vl2.featureCount(), 3) features = [f for f in vl2.getFeatures()] self.assertTrue([f for f in features if f['f1'] == 5]) self.assertTrue([f for f in features if f['f1'] == 3]) self.assertTrue([f for f in features if f['f1'] == 1])
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 check_overlaps_in_boundaries(self, db, layer_dict): rule = self.quality_rules_manager.get_quality_rule(EnumQualityRule.Line.OVERLAPS_IN_BOUNDARIES) boundary_layer = list(layer_dict[QUALITY_RULE_LAYERS].values())[0] if layer_dict[QUALITY_RULE_LAYERS] else None if not boundary_layer: return QCoreApplication.translate("LineQualityRules", "'Boundary' layer not found!"), Qgis.Critical # Create error layers structure error_point_layer = QgsVectorLayer("MultiPoint?crs={}".format(boundary_layer.sourceCrs().authid()), "{} (puntos)".format(rule.error_table_name), "memory") data_provider = error_point_layer.dataProvider() data_provider.addAttributes(rule.error_table_fields) error_point_layer.updateFields() error_line_layer = QgsVectorLayer("MultiLineString?crs={}".format(boundary_layer.sourceCrs().authid()), "{} (líneas)".format(rule.error_table_name), "memory") data_provider = error_line_layer.dataProvider() data_provider.addAttributes(rule.error_table_fields) error_line_layer.updateFields() if boundary_layer: overlapping = self.geometry.get_overlapping_lines(boundary_layer) if overlapping is None: return (QCoreApplication.translate("LineQualityRules", "There are no boundaries to check for overlaps!"), Qgis.Warning) else: points_intersected = overlapping['native:saveselectedfeatures_3:Intersected_Points'] lines_intersected = overlapping['native:saveselectedfeatures_2:Intersected_Lines'] if isinstance(points_intersected, QgsVectorLayer): point_features = list() if points_intersected.featureCount() > 0: for feature in points_intersected.getFeatures(): new_feature = QgsVectorLayerUtils().createFeature( error_point_layer, feature.geometry(), {0: feature[db.names.T_ILI_TID_F], 1: feature["{}_2".format(db.names.T_ILI_TID_F)], 2: self.quality_rules_manager.get_error_message(QUALITY_RULE_ERROR_CODE_E200101), 3: QUALITY_RULE_ERROR_CODE_E200101}) point_features.append(new_feature) error_point_layer.dataProvider().addFeatures(point_features) if isinstance(lines_intersected, QgsVectorLayer): line_features = list() if lines_intersected.featureCount() > 0: for feature in lines_intersected.getFeatures(): new_feature = QgsVectorLayerUtils().createFeature( error_line_layer, feature.geometry(), {0: feature[db.names.T_ILI_TID_F], 1: feature["{}_2".format(db.names.T_ILI_TID_F)], 2: self.quality_rules_manager.get_error_message(QUALITY_RULE_ERROR_CODE_E200101), 3: QUALITY_RULE_ERROR_CODE_E200101}) line_features.append(new_feature) error_line_layer.dataProvider().addFeatures(line_features) if error_point_layer.featureCount() == 0 and error_line_layer.featureCount() == 0: return QCoreApplication.translate("LineQualityRules", "There are no overlapping boundaries."), Qgis.Success else: msg = "" if error_point_layer.featureCount() and error_line_layer.featureCount(): self.app.gui.add_error_layer(db, error_point_layer) self.app.gui.add_error_layer(db, error_line_layer) msg = QCoreApplication.translate("LineQualityRules", "Two memory layers with overlapping boundaries ({} point intersections and {} line intersections) have been added to the map.").format(error_point_layer.featureCount(), error_line_layer.featureCount()) elif error_point_layer.featureCount(): self.app.gui.add_error_layer(db, error_point_layer) msg = QCoreApplication.translate("LineQualityRules", "A memory layer with {} overlapping boundaries (point intersections) has been added to the map.").format(error_point_layer.featureCount()) elif error_line_layer.featureCount(): self.app.gui.add_error_layer(db, error_line_layer) msg = QCoreApplication.translate("LineQualityRules", "A memory layer with {} overlapping boundaries (line intersections) has been added to the map.").format(error_line_layer.featureCount()) return msg, Qgis.Critical
class GpxFeatureBuilder: """ Builds gpx layers and features """ def __init__(self, layer_name, attribute_definitions, attribute_select='Last', crs=None): self.error_message = '' layer_definition: str = 'LineString' if crs is not None: layer_definition = layer_definition + "?crs=epsg:" + str(crs.postgisSrid()) self.vector_layer = QgsVectorLayer(layer_definition, layer_name, "memory") self.data_provider = self.vector_layer.dataProvider() # Enter editing mode self.vector_layer.startEditing() attributes = list() for attribute in attribute_definitions: if attribute.selected: # select attribute [boolean] for attribute_select_option in ['First', 'Last']: if attribute_select_option != attribute_select and attribute_select != 'Both': continue key = str(attribute.attribute_key_modified) if attribute_select == 'Both' and attribute.attribute_key_modified != '_distance' \ and attribute.attribute_key_modified != '_duration' \ and attribute.attribute_key_modified != '_speed': if attribute_select_option == 'First': key = 'a_' + key elif attribute_select_option == 'Last': key = 'b_' + key if attribute.datatype == DataTypes.Integer: # data type [Integer|Double|String] attributes.append(QgsField(key, QVariant.Int, 'Integer')) elif attribute.datatype == DataTypes.Double: attributes.append(QgsField(key, QVariant.Double, 'Real')) elif attribute.datatype == DataTypes.Boolean: # QVariant.Bool is not available for QgsField # attributes.append(QgsField(key, QVariant.Bool, 'Boolean')) attributes.append(QgsField(key, QVariant.String, 'String')) # elif attribute.datatype == DataTypes.Date: # attributes.append(QgsField(key, QVariant.DateTime, 'String')) elif attribute.datatype == DataTypes.String: attributes.append(QgsField(key, QVariant.String, 'String')) self.data_provider.addAttributes(attributes) self.vector_layer.updateFields() def add_feature(self, line_coordinates, attributes): feature = QgsFeature() feature.setGeometry(QgsGeometry.fromPolyline(line_coordinates)) feature.setFields(self.vector_layer.fields(), True) for attribute_key in list(attributes.keys()): try: feature.setAttribute(attribute_key, attributes[attribute_key]) except KeyError: pass self.data_provider.addFeatures([feature]) def save_layer(self, output_directory, overwrite): self.vector_layer.commitChanges() self.error_message = '' if self.vector_layer.featureCount() > 0: self.vector_layer.updateExtents() # Write vector layer to file if output_directory is not None: if os.path.isdir(output_directory): vector_layer_writer = VectorFileWriter(output_directory) output_file_path = vector_layer_writer.write(self.vector_layer, overwrite) if output_file_path is not None: return QgsVectorLayer(output_file_path, os.path.basename(output_file_path), 'ogr') else: self.error_message = 'Writing vector layer failed...' return None else: self.error_message = 'Cannot find output directory' return None return self.vector_layer
def run(self): """Run the impact function. :returns: A new line layer with inundated roads marked. :type: safe_layer """ self.validate() self.prepare() self.provenance.append_step( 'Calculating Step', 'Impact function is calculating the impact.') # Thresholds for tsunami hazard zone breakdown. low_max = self.parameters['low_threshold'].value medium_max = self.parameters['medium_threshold'].value high_max = self.parameters['high_threshold'].value target_field = self.target_field # Get parameters from layer's keywords road_class_field = self.exposure.keyword('road_class_field') # reproject self.extent to the hazard projection hazard_crs = self.hazard.layer.crs() hazard_authid = hazard_crs.authid() if hazard_authid == 'EPSG:4326': viewport_extent = self.requested_extent else: geo_crs = QgsCoordinateReferenceSystem() geo_crs.createFromSrid(4326) viewport_extent = extent_to_geo_array( QgsRectangle(*self.requested_extent), geo_crs, hazard_crs) # Align raster extent and viewport # assuming they are both in the same projection raster_extent = self.hazard.layer.dataProvider().extent() clip_xmin = raster_extent.xMinimum() # clip_xmax = raster_extent.xMaximum() clip_ymin = raster_extent.yMinimum() # clip_ymax = raster_extent.yMaximum() if viewport_extent[0] > clip_xmin: clip_xmin = viewport_extent[0] if viewport_extent[1] > clip_ymin: clip_ymin = viewport_extent[1] height = ((viewport_extent[3] - viewport_extent[1]) / self.hazard.layer.rasterUnitsPerPixelY()) height = int(height) width = ((viewport_extent[2] - viewport_extent[0]) / self.hazard.layer.rasterUnitsPerPixelX()) width = int(width) raster_extent = self.hazard.layer.dataProvider().extent() xmin = raster_extent.xMinimum() xmax = raster_extent.xMaximum() ymin = raster_extent.yMinimum() ymax = raster_extent.yMaximum() x_delta = (xmax - xmin) / self.hazard.layer.width() x = xmin for i in range(self.hazard.layer.width()): if abs(x - clip_xmin) < x_delta: # We have found the aligned raster boundary break x += x_delta _ = i y_delta = (ymax - ymin) / self.hazard.layer.height() y = ymin for i in range(self.hazard.layer.width()): if abs(y - clip_ymin) < y_delta: # We have found the aligned raster boundary break y += y_delta clip_extent = [x, y, x + width * x_delta, y + height * y_delta] # Clip hazard raster small_raster = clip_raster(self.hazard.layer, width, height, QgsRectangle(*clip_extent)) # Create vector features from the flood raster # For each raster cell there is one rectangular polygon # Data also get spatially indexed for faster operation ranges = OrderedDict() ranges[0] = [0.0, 0.0] ranges[1] = [0.0, low_max] ranges[2] = [low_max, medium_max] ranges[3] = [medium_max, high_max] ranges[4] = [high_max, None] index, flood_cells_map = _raster_to_vector_cells( small_raster, ranges, self.exposure.layer.crs()) # Filter geometry and data using the extent ct = QgsCoordinateTransform(QgsCoordinateReferenceSystem("EPSG:4326"), self.exposure.layer.crs()) extent = ct.transformBoundingBox(QgsRectangle(*self.requested_extent)) request = QgsFeatureRequest() request.setFilterRect(extent) """ if len(low_max_flood_cells_map) == 0 and \ len(medium_max_flood_cells_map) == 0 and \ len(high_max_flood_cells_map) == 0 and \ len(high_min_flood_cells_map) == 0: message = tr( 'There are no objects in the hazard layer with "value" > 0. ' 'Please check the value or use other extent.' % ( threshold_min, )) raise GetDataError(message) """ # create template for the output layer line_layer_tmp = create_layer(self.exposure.layer) new_field = QgsField(target_field, QVariant.Int) line_layer_tmp.dataProvider().addAttributes([new_field]) line_layer_tmp.updateFields() # create empty output layer and load it filename = unique_filename(suffix='.shp') QgsVectorFileWriter.writeAsVectorFormat(line_layer_tmp, filename, "utf-8", None, "ESRI Shapefile") line_layer = QgsVectorLayer(filename, "flooded roads", "ogr") # Do the heavy work - for each road get flood polygon for that area and # do the intersection/difference to find out which parts are flooded _intersect_lines_with_vector_cells(self.exposure.layer, request, index, flood_cells_map, line_layer, target_field) target_field_index = line_layer.dataProvider().\ fieldNameIndex(target_field) # Generate simple impact report epsg = get_utm_epsg(self.requested_extent[0], self.requested_extent[1]) output_crs = QgsCoordinateReferenceSystem(epsg) transform = QgsCoordinateTransform(self.exposure.layer.crs(), output_crs) # Roads breakdown self.road_lengths = OrderedDict() self.affected_road_categories = self.hazard_classes # Impacted roads breakdown self.affected_road_lengths = OrderedDict([ (self.hazard_classes[0], {}), (self.hazard_classes[1], {}), (self.hazard_classes[2], {}), (self.hazard_classes[3], {}), (self.hazard_classes[4], {}), ]) if line_layer.featureCount() < 1: raise ZeroImpactException() roads_data = line_layer.getFeatures() road_type_field_index = line_layer.fieldNameIndex(road_class_field) for road in roads_data: attributes = road.attributes() affected = attributes[target_field_index] hazard_zone = self.hazard_classes[affected] road_type = attributes[road_type_field_index] if road_type.__class__.__name__ == 'QPyNullVariant': road_type = tr('Other') geom = road.geometry() geom.transform(transform) length = geom.length() if road_type not in self.road_lengths: self.road_lengths[road_type] = 0 if hazard_zone not in self.affected_road_lengths: self.affected_road_lengths[hazard_zone] = {} if road_type not in self.affected_road_lengths[hazard_zone]: self.affected_road_lengths[hazard_zone][road_type] = 0 self.road_lengths[road_type] += length num_classes = len(self.hazard_classes) if attributes[target_field_index] in range(num_classes): self.affected_road_lengths[hazard_zone][road_type] += length impact_summary = self.html_report() # For printing map purpose map_title = tr('Roads inundated') legend_title = tr('Road inundated status') style_classes = [ # FIXME 0 - 0.1 dict(label=self.hazard_classes[0] + ': 0m', value=0, colour='#00FF00', transparency=0, size=1), dict(label=self.hazard_classes[1] + ': <0 - %.1f m' % low_max, value=1, colour='#FFFF00', transparency=0, size=1), dict(label=self.hazard_classes[2] + ': %.1f - %.1f m' % (low_max + 0.1, medium_max), value=2, colour='#FFB700', transparency=0, size=1), dict(label=self.hazard_classes[3] + ': %.1f - %.1f m' % (medium_max + 0.1, high_max), value=3, colour='#FF6F00', transparency=0, size=1), dict(label=self.hazard_classes[4] + ' > %.1f m' % high_max, value=4, colour='#FF0000', transparency=0, size=1), ] style_info = dict(target_field=target_field, style_classes=style_classes, style_type='categorizedSymbol') extra_keywords = { 'impact_summary': impact_summary, 'map_title': map_title, 'legend_title': legend_title, 'target_field': target_field } self.set_if_provenance() impact_layer_keywords = self.generate_impact_keywords(extra_keywords) # Convert QgsVectorLayer to inasafe layer and return it line_layer = Vector(data=line_layer, name=tr('Flooded roads'), keywords=impact_layer_keywords, style_info=style_info) self._impact = line_layer return line_layer
def testStartEditingCommitRollBack(self): ml = QgsVectorLayer( 'Point?crs=epsg:4326&field=int:integer&field=int2:integer', 'test', 'memory') self.assertTrue(ml.isValid()) # Layer A geopackage A d = QTemporaryDir() options = QgsVectorFileWriter.SaveVectorOptions() options.driverName = 'GPKG' options.layerName = 'layer_a' err, msg, newFileName, newLayer = QgsVectorFileWriter.writeAsVectorFormatV3( ml, os.path.join(d.path(), 'test_EditBufferGroup_A.gpkg'), QgsCoordinateTransformContext(), options) self.assertEqual(err, QgsVectorFileWriter.NoError) self.assertTrue(os.path.isfile(newFileName)) layer_a = QgsVectorLayer(newFileName + '|layername=layer_a') self.assertTrue(layer_a.isValid()) # Layer B geopackage B options.layerName = 'layer_b' err, msg, newFileName, newLayer = QgsVectorFileWriter.writeAsVectorFormatV3( ml, os.path.join(d.path(), 'test_EditBufferGroup_B.gpkg'), QgsCoordinateTransformContext(), options) self.assertEqual(err, QgsVectorFileWriter.NoError) self.assertTrue(os.path.isfile(newFileName)) layer_b = QgsVectorLayer(newFileName + '|layername=layer_b') self.assertTrue(layer_b.isValid()) # Layer C memory layer_c = QgsVectorLayer( 'Point?crs=epsg:4326&field=int:integer&field=int2:integer', 'test', 'memory') self.assertTrue(layer_c.isValid()) project = QgsProject() project.addMapLayers([layer_a, layer_b, layer_c]) project.setTransactionMode(Qgis.TransactionMode.BufferedGroups) editBufferGroup = project.editBufferGroup() # Check layers in group self.assertIn(layer_a, editBufferGroup.layers()) self.assertIn(layer_b, editBufferGroup.layers()) self.assertIn(layer_c, editBufferGroup.layers()) self.assertFalse(editBufferGroup.isEditing()) self.assertTrue(editBufferGroup.startEditing()) self.assertTrue(editBufferGroup.isEditing()) self.assertTrue(layer_a.editBuffer()) self.assertTrue(layer_b.editBuffer()) self.assertTrue(layer_c.editBuffer()) self.assertEqual(len(editBufferGroup.modifiedLayers()), 0) commitErrors = [] self.assertTrue(editBufferGroup.commitChanges(commitErrors, False)) self.assertTrue(editBufferGroup.isEditing()) self.assertTrue(editBufferGroup.commitChanges(commitErrors, True)) self.assertFalse(editBufferGroup.isEditing()) self.assertTrue(editBufferGroup.startEditing()) self.assertTrue(editBufferGroup.isEditing()) f = QgsFeature(layer_a.fields()) f.setAttribute('int', 123) f.setGeometry(QgsGeometry.fromWkt('point(7 45)')) self.assertTrue(layer_a.addFeatures([f])) self.assertEqual(len(editBufferGroup.modifiedLayers()), 1) self.assertIn(layer_a, editBufferGroup.modifiedLayers()) # Check feature in layer edit buffer but not in provider till commit self.assertEqual(layer_a.featureCount(), 1) self.assertEqual(layer_a.dataProvider().featureCount(), 0) rollbackErrors = [] self.assertTrue(editBufferGroup.rollBack(rollbackErrors, False)) self.assertTrue(editBufferGroup.isEditing()) self.assertEqual(layer_a.featureCount(), 0) self.assertTrue(layer_a.addFeatures([f])) self.assertEqual(layer_a.featureCount(), 1) self.assertEqual(layer_a.dataProvider().featureCount(), 0) self.assertTrue(editBufferGroup.commitChanges(commitErrors, True)) self.assertFalse(editBufferGroup.isEditing()) self.assertEqual(layer_a.featureCount(), 1) self.assertEqual(layer_a.dataProvider().featureCount(), 1)
def __createCtrlLayers(self, requete): """ Création des couches de contrôles - selon une requête SQL dans la base de données (choix ou des contrôle par l'utilisateur) - selon une zone géographique définie par l'utilisateur :param requete: liste des requêtes """ self.__iface.messageBar().clearWidgets() progressMessageBar = self.__iface.messageBar() # ajout d'une barre de progression pour voir le chargement progressif des couches progress = QProgressBar() progress.setMaximum(100) progressMessageBar.pushWidget(progress) # récupérer la géométrie définie par l'utilisateur pour l'utiliser dans les requêtes SQL # conversion en géométrie binaire et dans le bon système de coordonnée) self.__crs = self.__iface.mapCanvas().mapSettings().destinationCrs( ).postgisSrid() # défintion du système de coordonnées en sortie (par défaut 21781), récupérer des paramètres du projets bbox = "(SELECT ST_GeomFromText('" + self.geom.exportToWkt( ) + "'," + str(self.__crs) + "))" # paramètres de la source des couches à ajouter au projet uri = QgsDataSourceURI() uri.setConnection(self.__db.hostName(), str(self.__db.port()), self.__db.databaseName(), self.__db.userName(), self.__db.password()) uri.setSrid(str(self.__crs)) outputLayers = [ ] # listes des couches de résultats à charger dans le projet styleLayers = [] # listes des styles de couches (fichier qml) i = 0 totalError = 0 # décompte des erreurs détectées (nombre d'objets dans chaque couche) for name in requete: for q in self.__layerCfgControl.getFeatures( QgsFeatureRequest(int(name))): query_fct = q["sql_function"] query_fct = query_fct.replace("bbox", bbox) geom_type = QgsWKBTypes.parseType(q["geom_type"]) # récupérer le type de géométrie QGIS "QgsWKBTypes" depuis un type de géométrie WKT Postgis uri.setWkbType(geom_type) uri.setDataSource('', query_fct, q["geom_name"], "", q["key_attribute"]) layer = QgsVectorLayer(uri.uri(), q["layer_name"], "postgres") totalError = totalError + layer.featureCount() if layer.featureCount() > 0: outputLayers.append(layer) styleLayers.append(str(q["layer_style"])) percent = ( float(i + 1.0) / float(len(requete)) ) * 100 # Faire évoluer la barre de progression du traitement progress.setValue(percent) i += 1 if len(outputLayers) > 0: self.__addCtrlLayers(outputLayers, styleLayers) self.__iface.messageBar().clearWidgets() self.__iface.messageBar().pushMessage( "Info", QCoreApplication.translate( "VDLTools", "All layers have been charged with success in the projet. |" ) + QCoreApplication.translate("VDLTools", "Total errors : ") + str(totalError), level=QgsMessageBar.INFO, duration=10) else: self.__iface.messageBar().clearWidgets() self.__iface.messageBar().pushMessage( "Info", QCoreApplication.translate( "VDLTools", "Good !! No error detected on the defined area"), level=QgsMessageBar.INFO, duration=5)
def testWFSGetOnlyFeaturesInViewExtent(self): """Test 'get only features in view extent' """ endpoint = self.__class__.basetestpath + '/fake_qgis_http_endpoint_only_features_in_view_extent' with open( sanitize( endpoint, '?SERVICE=WFS?REQUEST=GetCapabilities?ACCEPTVERSIONS=2.0.0,1.1.0,1.0.0' ), 'wb') as f: f.write(""" <wfs:WFS_Capabilities version="1.1.0" xmlns="http://www.opengis.net/wfs" xmlns:wfs="http://www.opengis.net/wfs" xmlns:ogc="http://www.opengis.net/ogc" xmlns:ows="http://www.opengis.net/ows" xmlns:gml="http://schemas.opengis.net/gml"> <ows:OperationsMetadata> <ows:Operation name="GetFeature"> <ows:Parameter name="resultType"> <ows:Value>results</ows:Value> <ows:Value>hits</ows:Value> </ows:Parameter> </ows:Operation> <ows:Constraint name="DefaultMaxFeatures"> <ows:Value>2</ows:Value> </ows:Constraint> </ows:OperationsMetadata> <FeatureTypeList> <FeatureType> <Name>my:typename</Name> <Title>Title</Title> <Abstract>Abstract</Abstract> <DefaultCRS>urn:ogc:def:crs:EPSG::4326</DefaultCRS> <ows:WGS84BoundingBox> <ows:LowerCorner>-80 60</ows:LowerCorner> <ows:UpperCorner>-50 80</ows:UpperCorner> </ows:WGS84BoundingBox> </FeatureType> </FeatureTypeList> </wfs:WFS_Capabilities>""") with open( sanitize( endpoint, '?SERVICE=WFS&REQUEST=DescribeFeatureType&VERSION=1.1.0&TYPENAME=my:typename' ), 'wb') as f: f.write(""" <xsd:schema xmlns:my="http://my" xmlns:gml="http://www.opengis.net/gml" xmlns:xsd="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" targetNamespace="http://my"> <xsd:import namespace="http://www.opengis.net/gml"/> <xsd:complexType name="my:typenameType"> <xsd:complexContent> <xsd:extension base="gml:AbstractFeatureType"> <xsd:sequence> <xsd:element maxOccurs="1" minOccurs="0" name="id" nillable="true" type="xsd:int"/> <xsd:element maxOccurs="1" minOccurs="0" name="geometryProperty" nillable="true" type="gml:PointPropertyType"/> </xsd:sequence> </xsd:extension> </xsd:complexContent> </xsd:complexType> <xsd:element name="typename" substitutionGroup="gml:_Feature" type="my:typenameType"/> </xsd:schema> """) # Create test layer vl = QgsVectorLayer( u"url='http://" + endpoint + u"' typename='my:typename' retrictToRequestBBOX=1", u'test', u'WFS') assert vl.isValid() self.assertEquals(vl.wkbType(), QgsWKBTypes.Point) last_url = sanitize( endpoint, '?SERVICE=WFS&REQUEST=GetFeature&VERSION=1.1.0&TYPENAME=my:typename&MAXFEATURES=2&SRSNAME=urn:ogc:def:crs:EPSG::4326&BBOX=60,-70,80,-60' ) with open(last_url, 'wb') as f: f.write(""" <wfs:FeatureCollection xmlns:wfs="http://www.opengis.net/wfs" xmlns:gml="http://www.opengis.net/gml" xmlns:my="http://my" numberOfFeatures="1" timeStamp="2016-03-25T14:51:48.998Z"> <gml:featureMembers> <my:typename gml:id="typename.200"> <my:geometryProperty><gml:Point srsName="urn:ogc:def:crs:EPSG::4326"><gml:pos>70 -65</gml:pos></gml:Point></my:geometryProperty> <my:id>2</my:id> </my:typename> </gml:featureMembers> </wfs:FeatureCollection>""") extent = QgsRectangle(-70, 60, -60, 80) request = QgsFeatureRequest().setFilterRect(extent) values = [f['id'] for f in vl.getFeatures(request)] self.assertEquals(values, [2]) # To show that if we zoom-in, we won't issue a new request with open(last_url, 'wb') as f: f.write(""" <wfs:FeatureCollection xmlns:wfs="http://www.opengis.net/wfs" xmlns:gml="http://www.opengis.net/gml" xmlns:my="http://my" numberOfFeatures="1" timeStamp="2016-03-25T14:51:48.998Z"> <gml:featureMembers> <my:typename gml:id="typename.20000"> <my:geometryProperty><gml:Point srsName="urn:ogc:def:crs:EPSG::4326"><gml:pos>70 -65</gml:pos></gml:Point></my:geometryProperty> <my:id>200</my:id> </my:typename> </gml:featureMembers> </wfs:FeatureCollection>""") extent = QgsRectangle(-66, 62, -62, 78) request = QgsFeatureRequest().setFilterRect(extent) values = [f['id'] for f in vl.getFeatures(request)] self.assertEquals(values, [2]) # Move to a neighbouring area, and reach the download limit last_url = sanitize( endpoint, '?SERVICE=WFS&REQUEST=GetFeature&VERSION=1.1.0&TYPENAME=my:typename&MAXFEATURES=2&SRSNAME=urn:ogc:def:crs:EPSG::4326&BBOX=65,-70,90,-60' ) with open(last_url, 'wb') as f: f.write(""" <wfs:FeatureCollection xmlns:wfs="http://www.opengis.net/wfs" xmlns:gml="http://www.opengis.net/gml" xmlns:my="http://my" numberOfFeatures="1" timeStamp="2016-03-25T14:51:48.998Z"> <gml:featureMembers> <my:typename gml:id="typename.200"> <my:geometryProperty><gml:Point srsName="urn:ogc:def:crs:EPSG::4326"><gml:pos>70 -65</gml:pos></gml:Point></my:geometryProperty> <my:id>2</my:id> </my:typename> <my:typename gml:id="typename.300"> <my:geometryProperty><gml:Point srsName="urn:ogc:def:crs:EPSG::4326"><gml:pos>85 -65</gml:pos></gml:Point></my:geometryProperty> <my:id>3</my:id> </my:typename> </gml:featureMembers> </wfs:FeatureCollection>""") extent = QgsRectangle(-70, 65, -60, 90) request = QgsFeatureRequest().setFilterRect(extent) values = [f['id'] for f in vl.getFeatures(request)] self.assertEquals(values, [2, 3]) # Zoom-in again, and bring more features last_url = sanitize( endpoint, '?SERVICE=WFS&REQUEST=GetFeature&VERSION=1.1.0&TYPENAME=my:typename&MAXFEATURES=2&SRSNAME=urn:ogc:def:crs:EPSG::4326&BBOX=66,-69,89,-61' ) with open(last_url, 'wb') as f: f.write(""" <wfs:FeatureCollection xmlns:wfs="http://www.opengis.net/wfs" xmlns:gml="http://www.opengis.net/gml" xmlns:my="http://my" numberOfFeatures="1" timeStamp="2016-03-25T14:51:48.998Z"> <gml:featureMembers> <my:typename gml:id="typename.200"> <my:geometryProperty><gml:Point srsName="urn:ogc:def:crs:EPSG::4326"><gml:pos>70 -65</gml:pos></gml:Point></my:geometryProperty> <my:id>2</my:id> </my:typename> <my:typename gml:id="typename.400"> <my:geometryProperty><gml:Point srsName="urn:ogc:def:crs:EPSG::4326"><gml:pos>84 -64</gml:pos></gml:Point></my:geometryProperty> <my:id>4</my:id> </my:typename> </gml:featureMembers> </wfs:FeatureCollection>""") extent = QgsRectangle(-69, 66, -61, 89) request = QgsFeatureRequest().setFilterRect(extent) values = [f['id'] for f in vl.getFeatures(request)] self.assertEquals(values, [2, 3, 4]) # Test RESULTTYPE=hits last_url = sanitize( endpoint, '?SERVICE=WFS&REQUEST=GetFeature&VERSION=1.1.0&TYPENAME=my:typename&RESULTTYPE=hits' ) with open(last_url, 'wb') as f: f.write(""" <wfs:FeatureCollection xmlns:wfs="http://www.opengis.net/wfs" numberOfFeatures="10" timeStamp="2016-03-25T14:51:48.998Z"/>""" ) self.assertEquals(vl.featureCount(), 10) # Combine BBOX and FILTER last_url = sanitize( endpoint, """?SERVICE=WFS&REQUEST=GetFeature&VERSION=1.1.0&TYPENAME=my:typename&MAXFEATURES=2&SRSNAME=urn:ogc:def:crs:EPSG::4326&FILTER=<ogc:Filter xmlns:ogc="http://www.opengis.net/ogc" xmlns:gml="http://www.opengis.net/gml"> <ogc:And> <ogc:BBOX> <ogc:PropertyName>geometryProperty</ogc:PropertyName> <gml:Envelope srsName="urn:ogc:def:crs:EPSG::4326"> <gml:lowerCorner>66 -69</gml:lowerCorner> <gml:upperCorner>89 -61</gml:upperCorner> </gml:Envelope> </ogc:BBOX> <ogc:PropertyIsEqualTo xmlns:ogc="http://www.opengis.net/ogc"> <ogc:PropertyName xmlns:ogc="http://www.opengis.net/ogc">id</ogc:PropertyName> <ogc:Literal xmlns:ogc="http://www.opengis.net/ogc">101</ogc:Literal> </ogc:PropertyIsEqualTo> </ogc:And> </ogc:Filter> """) with open(last_url, 'wb') as f: f.write(""" <wfs:FeatureCollection xmlns:wfs="http://www.opengis.net/wfs" xmlns:gml="http://www.opengis.net/gml" xmlns:my="http://my" numberOfFeatures="1" timeStamp="2016-03-25T14:51:48.998Z"> <gml:featureMembers> <my:typename gml:id="typename.101"> <my:geometryProperty><gml:Point srsName="urn:ogc:def:crs:EPSG::4326"><gml:pos>70 -65</gml:pos></gml:Point></my:geometryProperty> <my:id>101</my:id> </my:typename> </gml:featureMembers> </wfs:FeatureCollection>""") vl.dataProvider().setSubsetString('id = 101') extent = QgsRectangle(-69, 66, -61, 89) request = QgsFeatureRequest().setFilterRect(extent) values = [f['id'] for f in vl.getFeatures(request)] self.assertEquals(values, [101])
def accept(self): """Do PetaBencana download and display it in QGIS. .. versionadded: 3.3 """ self.save_state() try: self.require_directory() except CanceledImportDialogError: return QtGui.qApp.setOverrideCursor(QtGui.QCursor(QtCore.Qt.WaitCursor)) source = self.define_url() # save the file as json first name = 'jakarta_flood.json' output_directory = self.output_directory.text() output_prefix = self.filename_prefix.text() overwrite = self.overwrite_flag.isChecked() date_stamp_flag = self.include_date_flag.isChecked() output_base_file_path = self.get_output_base_path( output_directory, output_prefix, date_stamp_flag, name, overwrite) title = self.tr("Can't access API") try: self.download(source, output_base_file_path) # Open downloaded file as QgsMapLayer layer = QgsVectorLayer(output_base_file_path, 'flood', 'ogr', False) except Exception as e: disable_busy_cursor() QMessageBox.critical(self, title, str(e)) return self.time_stamp = time.strftime('%d-%b-%Y %H:%M:%S') # Now save as shp name = 'jakarta_flood.shp' output_base_file_path = self.get_output_base_path( output_directory, output_prefix, date_stamp_flag, name, overwrite) QgsVectorFileWriter.writeAsVectorFormat(layer, output_base_file_path, 'CP1250', None, 'ESRI Shapefile') # Get rid of the GeoJSON layer and rather use local shp del layer self.copy_style(output_base_file_path) self.copy_keywords(output_base_file_path) layer = self.add_flooded_field(output_base_file_path) # check if the layer has feature or not if layer.featureCount() <= 0: city = self.city_combo_box.currentText() message = self.tr('There are no floods data available on {city} ' 'at this time.').format(city=city) display_warning_message_box(self, self.tr('No data'), message) disable_busy_cursor() else: # add the layer to the map registry = QgsMapLayerRegistry.instance() registry.addMapLayer(layer) disable_busy_cursor() self.done(QDialog.Accepted)
def processAlgorithm(self, progress): inLayers = self.getParameterValue(self.LAYERS) paths = inLayers.split(';') layers = [] fields = QgsFields() totalFeatureCount = 0 for x in range(len(paths)): layer = QgsVectorLayer(paths[x], str(x), 'ogr') if (len(layers) > 0): if (layer.wkbType() != layers[0].wkbType()): raise GeoAlgorithmExecutionException( self.tr('All layers must have same geometry type!')) layers.append(layer) totalFeatureCount += layer.featureCount() for sindex, sfield in enumerate(layer.fields()): found = None for dfield in fields: if (dfield.name().upper() == sfield.name().upper()): found = dfield if (dfield.type() != sfield.type()): raise GeoAlgorithmExecutionException( self.tr('{} field in layer {} has different ' 'data type than in other layers.')) if not found: fields.append(sfield) total = 100.0 / totalFeatureCount writer = self.getOutputFromName(self.OUTPUT).getVectorWriter( fields.toList(), layers[0].wkbType(), layers[0].crs()) featureCount = 0 for layer in layers: for feature in layer.getFeatures(): sattributes = feature.attributes() dattributes = [] for dindex, dfield in enumerate(fields): if (dfield.type() == QVariant.Int, QVariant.UInt, QVariant.LongLong, QVariant.ULongLong): dattribute = 0 elif (dfield.type() == QVariant.Double): dattribute = 0.0 else: dattribute = '' for sindex, sfield in enumerate(layer.fields()): if (sfield.name().upper() == dfield.name().upper()): if (sfield.type() != dfield.type()): raise GeoAlgorithmExecutionException( self.tr('Attribute type mismatch')) dattribute = sattributes[sindex] break dattributes.append(dattribute) feature.setAttributes(dattributes) writer.addFeature(feature) featureCount += 1 progress.setPercentage(int(featureCount * total)) del writer
def run(self): """Run the impact function. :returns: A new line layer with inundated roads marked. :type: safe_layer """ self.validate() self.prepare() target_field = self.target_field # Get parameters from layer's keywords road_class_field = self.exposure.keyword('road_class_field') # Get parameters from IF parameter threshold_min = self.parameters['min threshold'].value threshold_max = self.parameters['max threshold'].value if threshold_min > threshold_max: message = tr( 'The minimal threshold is greater than the maximal specified ' 'threshold. Please check the values.') raise GetDataError(message) # reproject self.extent to the hazard projection hazard_crs = self.hazard.layer.crs() hazard_authid = hazard_crs.authid() if hazard_authid == 'EPSG:4326': viewport_extent = self.requested_extent else: geo_crs = QgsCoordinateReferenceSystem() geo_crs.createFromSrid(4326) viewport_extent = extent_to_geo_array( QgsRectangle(*self.requested_extent), geo_crs, hazard_crs) # Align raster extent and viewport # assuming they are both in the same projection raster_extent = self.hazard.layer.dataProvider().extent() clip_xmin = raster_extent.xMinimum() # clip_xmax = raster_extent.xMaximum() clip_ymin = raster_extent.yMinimum() # clip_ymax = raster_extent.yMaximum() if viewport_extent[0] > clip_xmin: clip_xmin = viewport_extent[0] if viewport_extent[1] > clip_ymin: clip_ymin = viewport_extent[1] # TODO: Why have these two clauses when they are not used? # Commenting out for now. # if viewport_extent[2] < clip_xmax: # clip_xmax = viewport_extent[2] # if viewport_extent[3] < clip_ymax: # clip_ymax = viewport_extent[3] height = ((viewport_extent[3] - viewport_extent[1]) / self.hazard.layer.rasterUnitsPerPixelY()) height = int(height) width = ((viewport_extent[2] - viewport_extent[0]) / self.hazard.layer.rasterUnitsPerPixelX()) width = int(width) raster_extent = self.hazard.layer.dataProvider().extent() xmin = raster_extent.xMinimum() xmax = raster_extent.xMaximum() ymin = raster_extent.yMinimum() ymax = raster_extent.yMaximum() x_delta = (xmax - xmin) / self.hazard.layer.width() x = xmin for i in range(self.hazard.layer.width()): if abs(x - clip_xmin) < x_delta: # We have found the aligned raster boundary break x += x_delta _ = i y_delta = (ymax - ymin) / self.hazard.layer.height() y = ymin for i in range(self.hazard.layer.width()): if abs(y - clip_ymin) < y_delta: # We have found the aligned raster boundary break y += y_delta clip_extent = [x, y, x + width * x_delta, y + height * y_delta] # Clip hazard raster small_raster = clip_raster(self.hazard.layer, width, height, QgsRectangle(*clip_extent)) # Create vector features from the flood raster # For each raster cell there is one rectangular polygon # Data also get spatially indexed for faster operation index, flood_cells_map = _raster_to_vector_cells( small_raster, threshold_min, threshold_max, self.exposure.layer.crs()) # Filter geometry and data using the extent ct = QgsCoordinateTransform(QgsCoordinateReferenceSystem("EPSG:4326"), self.exposure.layer.crs()) extent = ct.transformBoundingBox(QgsRectangle(*self.requested_extent)) request = QgsFeatureRequest() request.setFilterRect(extent) if len(flood_cells_map) == 0: message = tr( 'There are no objects in the hazard layer with "value" > %s. ' 'Please check the value or use other extent.' % (threshold_min, )) raise GetDataError(message) # create template for the output layer line_layer_tmp = create_layer(self.exposure.layer) new_field = QgsField(target_field, QVariant.Int) line_layer_tmp.dataProvider().addAttributes([new_field]) line_layer_tmp.updateFields() # create empty output layer and load it filename = unique_filename(suffix='.shp') QgsVectorFileWriter.writeAsVectorFormat(line_layer_tmp, filename, "utf-8", None, "ESRI Shapefile") line_layer = QgsVectorLayer(filename, "flooded roads", "ogr") # Do the heavy work - for each road get flood polygon for that area and # do the intersection/difference to find out which parts are flooded _intersect_lines_with_vector_cells(self.exposure.layer, request, index, flood_cells_map, line_layer, target_field) target_field_index = line_layer.dataProvider().\ fieldNameIndex(target_field) # Generate simple impact report epsg = get_utm_epsg(self.requested_extent[0], self.requested_extent[1]) output_crs = QgsCoordinateReferenceSystem(epsg) transform = QgsCoordinateTransform(self.exposure.layer.crs(), output_crs) flooded_keyword = tr('Flooded in the threshold (m)') self.affected_road_categories = [flooded_keyword] self.affected_road_lengths = OrderedDict([(flooded_keyword, {})]) self.road_lengths = OrderedDict() if line_layer.featureCount() < 1: raise ZeroImpactException() roads_data = line_layer.getFeatures() road_type_field_index = line_layer.fieldNameIndex(road_class_field) for road in roads_data: attributes = road.attributes() road_type = attributes[road_type_field_index] if road_type.__class__.__name__ == 'QPyNullVariant': road_type = tr('Other') geom = road.geometry() geom.transform(transform) length = geom.length() if road_type not in self.road_lengths: self.affected_road_lengths[flooded_keyword][road_type] = 0 self.road_lengths[road_type] = 0 self.road_lengths[road_type] += length if attributes[target_field_index] == 1: self.affected_road_lengths[flooded_keyword][ road_type] += length impact_summary = self.html_report() # For printing map purpose map_title = tr('Roads inundated') legend_title = tr('Road inundated status') style_classes = [ dict(label=tr('Not Inundated'), value=0, colour='#1EFC7C', transparency=0, size=0.5), dict(label=tr('Inundated'), value=1, colour='#F31A1C', transparency=0, size=0.5) ] style_info = dict(target_field=target_field, style_classes=style_classes, style_type='categorizedSymbol') # Convert QgsVectorLayer to inasafe layer and return it line_layer = Vector(data=line_layer, name=tr('Flooded roads'), keywords={ 'impact_summary': impact_summary, 'map_title': map_title, 'legend_title': legend_title, 'target_field': target_field }, style_info=style_info) self._impact = line_layer return line_layer
class TestQgsVectorLayerFeatureCounter(unittest.TestCase): def setUp(self): self.vl = QgsVectorLayer( 'Point?crs=epsg:4326&field=pk:integer&field=cnt:integer&field=name:string(0)&field=name2:string(0)&field=num_char:string&field=dt:datetime&field=date:date&field=time:time&key=pk', 'test', 'memory') assert (self.vl.isValid()) f1 = QgsFeature(5) f1.setAttributes([ 5, -200, NULL, 'NuLl', '5', QDateTime(QDate(2020, 5, 4), QTime(12, 13, 14)), QDate(2020, 5, 2), QTime(12, 13, 1) ]) f1.setGeometry(QgsGeometry.fromWkt('Point (-71.123 78.23)')) f2 = QgsFeature(3) f2.setAttributes([3, 300, 'Pear', 'PEaR', '3', NULL, NULL, NULL]) f3 = QgsFeature(1) f3.setAttributes([ 1, 100, 'Orange', 'oranGe', '1', QDateTime(QDate(2020, 5, 3), QTime(12, 13, 14)), QDate(2020, 5, 3), QTime(12, 13, 14) ]) f3.setGeometry(QgsGeometry.fromWkt('Point (-70.332 66.33)')) f4 = QgsFeature(2) f4.setAttributes([ 2, 200, 'Apple', 'Apple', '2', QDateTime(QDate(2020, 5, 4), QTime(12, 14, 14)), QDate(2020, 5, 4), QTime(12, 14, 14) ]) f4.setGeometry(QgsGeometry.fromWkt('Point (-68.2 70.8)')) f5 = QgsFeature(4) f5.setAttributes([ 4, 400, 'Honey', 'Honey', '4', QDateTime(QDate(2021, 5, 4), QTime(13, 13, 14)), QDate(2021, 5, 4), QTime(13, 13, 14) ]) f5.setGeometry(QgsGeometry.fromWkt('Point (-65.32 78.3)')) assert self.vl.dataProvider().addFeatures([f1, f2, f3, f4, f5]) self.vl2 = QgsVectorLayer( 'Point?crs=epsg:4326&field=pk:integer&field=cnt:integer&field=name:string(0)&field=name2:string(0)&field=num_char:string&field=dt:datetime&field=date:date&field=time:time&key=pk', 'test', 'memory') assert (self.vl2.isValid()) def tearDown(self): del self.vl del self.vl2 def testFeaturesCount(self): self.assertTrue(self.vl.renderer().legendSymbolItems()) signal_spy = QSignalSpy(self.vl.symbolFeatureCountMapChanged) self.vl.countSymbolFeatures() signal_spy.wait() self.assertEqual(len(signal_spy), 1) self.assertEqual( self.vl.featureCount( self.vl.renderer().legendSymbolItems()[0].ruleKey()), 5) def testFeaturesCountOnEmptyLayer(self): self.assertTrue(self.vl2.renderer().legendSymbolItems()) signal_spy = QSignalSpy(self.vl2.symbolFeatureCountMapChanged) self.vl2.countSymbolFeatures() signal_spy.wait() self.assertEqual(len(signal_spy), 1) self.assertEqual( self.vl2.featureCount( self.vl2.renderer().legendSymbolItems()[0].ruleKey()), 0)
def processAlgorithm(self, parameters, context, feedback): layers = self.parameterAsLayerList(parameters, self.INPUT_DATASOURCES, context) query = self.parameterAsString(parameters, self.INPUT_QUERY, context) uid_field = self.parameterAsString(parameters, self.INPUT_UID_FIELD, context) geometry_field = self.parameterAsString(parameters, self.INPUT_GEOMETRY_FIELD, context) geometry_type = self.parameterAsEnum(parameters, self.INPUT_GEOMETRY_TYPE, context) geometry_crs = self.parameterAsCrs(parameters, self.INPUT_GEOMETRY_CRS, context) df = QgsVirtualLayerDefinition() for layerIdx, layer in enumerate(layers): df.addSource('input{}'.format(layerIdx + 1), layer.id()) if query == '': raise QgsProcessingException( self. tr('Empty SQL. Please enter valid SQL expression and try again.' )) else: localContext = self.createExpressionContext(parameters, context) expandedQuery = QgsExpression.replaceExpressionText( query, localContext) df.setQuery(expandedQuery) if uid_field: df.setUid(uid_field) if geometry_type == 1: # no geometry df.setGeometryWkbType(QgsWkbTypes.NoGeometry) else: if geometry_field: df.setGeometryField(geometry_field) if geometry_type > 1: df.setGeometryWkbType(geometry_type - 1) if geometry_crs.isValid(): df.setGeometrySrid(geometry_crs.postgisSrid()) vLayer = QgsVectorLayer(df.toString(), "temp_vlayer", "virtual") if not vLayer.isValid(): raise QgsProcessingException( vLayer.dataProvider().error().message()) (sink, dest_id) = self.parameterAsSink( parameters, self.OUTPUT, context, vLayer.fields(), vLayer.wkbType() if geometry_type != 1 else 1, vLayer.crs()) if sink is None: raise QgsProcessingException( self.invalidSinkError(parameters, self.OUTPUT)) features = vLayer.getFeatures() total = 100.0 / vLayer.featureCount() if vLayer.featureCount() else 0 for current, inFeat in enumerate(features): if feedback.isCanceled(): break sink.addFeature(inFeat, QgsFeatureSink.FastInsert) feedback.setProgress(int(current * total)) return {self.OUTPUT: dest_id}
def testWFS10(self): """Test WFS 1.0 read-only""" endpoint = self.__class__.basetestpath + '/fake_qgis_http_endpoint_WFS1.0' with open( sanitize(endpoint, '?SERVICE=WFS?REQUEST=GetCapabilities?VERSION=1.0.0'), 'wb') as f: f.write(""" <WFS_Capabilities version="1.0.0" xmlns="http://www.opengis.net/wfs" xmlns:ogc="http://www.opengis.net/ogc"> <FeatureTypeList> <FeatureType> <Name>my:typename</Name> <Title>Title</Title> <Abstract>Abstract</Abstract> <SRS>EPSG:4326</SRS> <LatLongBoundingBox minx="-71.123" miny="66.33" maxx="-65.32" maxy="78.3"/> </FeatureType> </FeatureTypeList> </WFS_Capabilities>""") with open( sanitize( endpoint, '?SERVICE=WFS&REQUEST=DescribeFeatureType&VERSION=1.0.0&TYPENAME=my:typename' ), 'wb') as f: f.write(""" <xsd:schema xmlns:my="http://my" xmlns:gml="http://www.opengis.net/gml" xmlns:xsd="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" targetNamespace="http://my"> <xsd:import namespace="http://www.opengis.net/gml"/> <xsd:complexType name="my:typenameType"> <xsd:complexContent> <xsd:extension base="gml:AbstractFeatureType"> <xsd:sequence> <xsd:element maxOccurs="1" minOccurs="0" name="INTFIELD" nillable="true" type="xsd:int"/> <xsd:element maxOccurs="1" minOccurs="0" name="GEOMETRY" nillable="true" type="xsd:int"/> <xsd:element maxOccurs="1" minOccurs="0" name="longfield" nillable="true" type="xsd:long"/> <xsd:element maxOccurs="1" minOccurs="0" name="stringfield" nillable="true" type="xsd:string"/> <xsd:element maxOccurs="1" minOccurs="0" name="geometryProperty" nillable="true" type="gml:PointPropertyType"/> </xsd:sequence> </xsd:extension> </xsd:complexContent> </xsd:complexType> <xsd:element name="typename" substitutionGroup="gml:_Feature" type="my:typenameType"/> </xsd:schema> """) vl = QgsVectorLayer( u"url='http://" + endpoint + u"' typename='my:typename' version='1.0.0'", u'test', u'WFS') assert vl.isValid() self.assertEquals(vl.wkbType(), QgsWKBTypes.Point) self.assertEquals(len(vl.fields()), 4) self.assertEquals(vl.featureCount(), 0) reference = QgsGeometry.fromRect( QgsRectangle(-71.123, 66.33, -65.32, 78.3)) vl_extent = QgsGeometry.fromRect(vl.extent()) assert QgsGeometry.compare(vl_extent.asPolygon(), reference.asPolygon(), 0.00001), 'Expected {}, got {}'.format( reference.exportToWkt(), vl_extent.exportToWkt()) with open( sanitize( endpoint, '?SERVICE=WFS&REQUEST=GetFeature&VERSION=1.0.0&TYPENAME=my:typename&SRSNAME=EPSG:4326' ), 'wb') as f: f.write(""" <wfs:FeatureCollection xmlns:wfs="http://www.opengis.net/wfs" xmlns:gml="http://www.opengis.net/gml" xmlns:my="http://my"> <gml:boundedBy><gml:null>unknown</gml:null></gml:boundedBy> <gml:featureMember> <my:typename fid="typename.0"> <my:geometryProperty> <gml:Point srsName="http://www.opengis.net/gml/srs/epsg.xml#4326"><gml:coordinates decimal="." cs="," ts=" ">2,49</gml:coordinates></gml:Point></my:geometryProperty> <my:INTFIELD>1</my:INTFIELD> <my:GEOMETRY>2</my:GEOMETRY> <my:longfield>1234567890123</my:longfield> <my:stringfield>foo</my:stringfield> </my:typename> </gml:featureMember> </wfs:FeatureCollection>""") values = [f['INTFIELD'] for f in vl.getFeatures()] self.assertEquals(values, [1]) values = [f['GEOMETRY'] for f in vl.getFeatures()] self.assertEquals(values, [2]) values = [f['longfield'] for f in vl.getFeatures()] self.assertEquals(values, [1234567890123]) values = [f['stringfield'] for f in vl.getFeatures()] self.assertEquals(values, ['foo']) got = [f.geometry() for f in vl.getFeatures()][0].geometry() self.assertEquals((got.x(), got.y()), (2.0, 49.0)) self.assertEquals(vl.featureCount(), 1) self.assertEquals(vl.dataProvider().capabilities(), 0) (ret, _) = vl.dataProvider().addFeatures([QgsFeature()]) assert not ret assert not vl.dataProvider().deleteFeatures([0])
def performRequestParcel(self, region, parcel): objectType = self.checkedFeatureType() self.crs = QgsProject.instance().crs().authid().split(":")[1] name = region + ' ' + parcel result = uldk_parcel.getParcelById(name, self.crs) if result is None: self.iface.messageBar().pushMessage( "Nie udało się pobrać obiektu:", 'API nie zwróciło obiektu dla id %s' % name, level=Qgis.Critical, duration=10) return res = result.split("|") if res[0] == '': self.iface.messageBar().pushMessage( "Nie udało się pobrać obiektu:", 'API nie zwróciło geometrii dla id %s' % name, level=Qgis.Critical, duration=10) return wkt = res[0] teryt = res[1] parcel = res[2] region = res[3] commune = res[4] county = res[5] voivodeship = res[6] # print(teryt, parcel, region, commune, county, voivodeship) # layer nazwa = self.nazwy_warstw[objectType] layers = QgsProject.instance().mapLayersByName(nazwa) geom = QgsGeometry().fromWkt(wkt) feat = QgsFeature() feat.setGeometry(geom) canvas = self.iface.mapCanvas() if layers: # jezeli istnieje to dodaj obiekt do warstwy layer = layers[0] else: # jezeli nie istnieje to stworz warstwe epsg = "Polygon?crs=EPSG:" + self.crs layer = QgsVectorLayer(epsg, nazwa, "memory") QgsProject.instance().addMapLayer(layer) box = feat.geometry().boundingBox() canvas.setExtent(box) provider = layer.dataProvider() provider.addFeature(feat) layer.updateExtents() canvas.refresh() counter = layer.featureCount() # add attributes if not layers: identyfikatorField = QgsField('identyfikator', QVariant.String, len=30) provider.addAttributes([identyfikatorField]) voivField = QgsField('województwo', QVariant.String, len=30) provider.addAttributes([voivField]) conField = QgsField('powiat', QVariant.String, len=30) provider.addAttributes([conField]) comField = QgsField('gmina', QVariant.String, len=30) provider.addAttributes([comField]) regField = QgsField('obręb', QVariant.String, len=30) provider.addAttributes([regField]) layer.updateFields() parField = QgsField('numer', QVariant.String, len=30) provider.addAttributes([parField]) layer.updateFields() layer.updateFields() counter = 1 idx = layer.fields().indexFromName('identyfikator') attrMap = {counter: {idx: teryt}} provider.changeAttributeValues(attrMap) voiv = layer.fields().indexFromName('województwo') attrMap = {counter: {voiv: voivodeship}} provider.changeAttributeValues(attrMap) if parcel is not None: par = layer.fields().indexFromName('numer') attrMap = {counter: {par: parcel}} provider.changeAttributeValues(attrMap) if region is not None: reg = layer.fields().indexFromName('obręb') attrMap = {counter: {reg: region}} provider.changeAttributeValues(attrMap) if commune is not None: com = layer.fields().indexFromName('gmina') attrMap = {counter: {com: commune}} provider.changeAttributeValues(attrMap) if county is not None: con = layer.fields().indexFromName('powiat') attrMap = {counter: {con: county}} provider.changeAttributeValues(attrMap) self.iface.messageBar().pushMessage("Sukces:", 'pobrano obrys obiektu %s' % (name), level=Qgis.Success, duration=10)
def testTypeValidation(self): """Test that incompatible types in attributes raise errors""" vl = QgsVectorLayer('Point?crs=epsg:4326&field=int:integer', 'test', 'memory') self.assertTrue(vl.isValid()) invalid = QgsFeature(vl.fields()) invalid.setAttribute('int', 'A string') invalid.setGeometry(QgsGeometry.fromWkt('point(9 45)')) self.assertTrue(vl.startEditing()) # Validation happens on commit self.assertTrue(vl.addFeatures([invalid])) self.assertFalse(vl.commitChanges()) self.assertTrue(vl.rollBack()) self.assertFalse(vl.hasFeatures()) # Add a valid feature valid = QgsFeature(vl.fields()) valid.setAttribute('int', 123) self.assertTrue(vl.startEditing()) self.assertTrue(vl.addFeatures([valid])) self.assertTrue(vl.commitChanges()) self.assertEqual(vl.featureCount(), 1) f = vl.getFeature(1) self.assertEqual(f.attribute('int'), 123) # Add both vl = QgsVectorLayer('Point?crs=epsg:4326&field=int:integer', 'test', 'memory') self.assertEqual(vl.featureCount(), 0) self.assertTrue(vl.startEditing()) self.assertTrue(vl.addFeatures([valid, invalid])) self.assertFalse(vl.commitChanges()) self.assertEqual(vl.featureCount(), 2) self.assertTrue(vl.rollBack()) self.assertEqual(vl.featureCount(), 0) # Add both swapped vl = QgsVectorLayer('Point?crs=epsg:4326&field=int:integer', 'test', 'memory') self.assertTrue(vl.startEditing()) self.assertTrue(vl.addFeatures([invalid, valid])) self.assertFalse(vl.commitChanges()) self.assertEqual(vl.featureCount(), 2) self.assertTrue(vl.rollBack()) self.assertEqual(vl.featureCount(), 0) # Change attribute value vl = QgsVectorLayer('Point?crs=epsg:4326&field=int:integer', 'test', 'memory') self.assertTrue(vl.startEditing()) self.assertTrue(vl.addFeatures([valid])) self.assertTrue(vl.commitChanges()) self.assertTrue(vl.startEditing()) self.assertTrue(vl.changeAttributeValue(1, 0, 'A string')) self.assertFalse(vl.commitChanges()) f = vl.getFeature(1) self.assertEqual(f.attribute('int'), 'A string') self.assertTrue(vl.rollBack()) f = vl.getFeature(1) self.assertEqual(f.attribute('int'), 123) # Change attribute values vl = QgsVectorLayer('Point?crs=epsg:4326&field=int:integer', 'test', 'memory') self.assertTrue(vl.startEditing()) self.assertTrue(vl.addFeatures([valid])) self.assertTrue(vl.commitChanges()) self.assertTrue(vl.startEditing()) self.assertTrue(vl.changeAttributeValues(1, {0: 'A string'})) self.assertFalse(vl.commitChanges()) f = vl.getFeature(1) self.assertEqual(f.attribute('int'), 'A string') self.assertTrue(vl.rollBack()) f = vl.getFeature(1) self.assertEqual(f.attribute('int'), 123) ############################################## # Test direct data provider calls # No rollback (old behavior) vl = QgsVectorLayer('Point?crs=epsg:4326&field=int:integer', 'test', 'memory') dp = vl.dataProvider() self.assertFalse(dp.addFeatures([valid, invalid])[0]) self.assertEqual([f.attributes() for f in dp.getFeatures()], [[123]]) f = vl.getFeature(1) self.assertEqual(f.attribute('int'), 123) # Roll back vl = QgsVectorLayer('Point?crs=epsg:4326&field=int:integer', 'test', 'memory') dp = vl.dataProvider() self.assertFalse( dp.addFeatures([valid, invalid], QgsFeatureSink.RollBackOnErrors)[0]) self.assertFalse(dp.hasFeatures()) # Expected behavior for changeAttributeValues is to always roll back self.assertTrue(dp.addFeatures([valid])[0]) self.assertFalse(dp.changeAttributeValues({1: {0: 'A string'}})) f = vl.getFeature(1) self.assertEqual(f.attribute('int'), 123)
def testWFS20Paging(self): """Test WFS 2.0 paging""" endpoint = self.__class__.basetestpath + '/fake_qgis_http_endpoint_WFS_2.0_paging' with open( sanitize( endpoint, '?SERVICE=WFS?REQUEST=GetCapabilities?ACCEPTVERSIONS=2.0.0,1.1.0,1.0.0' ), 'wb') as f: f.write(""" <wfs:WFS_Capabilities version="2.0.0" xmlns="http://www.opengis.net/wfs/2.0" xmlns:wfs="http://www.opengis.net/wfs/2.0" xmlns:ows="http://www.opengis.net/ows/1.1" xmlns:gml="http://schemas.opengis.net/gml/3.2" xmlns:fes="http://www.opengis.net/fes/2.0" xmlns:xlink="http://www.w3.org/1999/xlink"> <ows:OperationsMetadata> <ows:Operation name="GetFeature"> <ows:Constraint name="CountDefault"> <ows:NoValues/> <ows:DefaultValue>1</ows:DefaultValue> </ows:Constraint> </ows:Operation> <ows:Constraint name="ImplementsResultPaging"> <ows:NoValues/> <ows:DefaultValue>TRUE</ows:DefaultValue> </ows:Constraint> </ows:OperationsMetadata> <FeatureTypeList> <FeatureType> <Name>my:typename</Name> <Title>Title</Title> <Abstract>Abstract</Abstract> <DefaultCRS>urn:ogc:def:crs:EPSG::4326</DefaultCRS> <ows:WGS84BoundingBox> <ows:LowerCorner>-71.123 66.33</ows:LowerCorner> <ows:UpperCorner>-65.32 78.3</ows:UpperCorner> </ows:WGS84BoundingBox> </FeatureType> </FeatureTypeList> </wfs:WFS_Capabilities>""") with open( sanitize( endpoint, '?SERVICE=WFS&REQUEST=DescribeFeatureType&VERSION=2.0.0&TYPENAME=my:typename' ), 'wb') as f: f.write(""" <xsd:schema xmlns:my="http://my" xmlns:gml="http://www.opengis.net/gml/3.2" xmlns:xsd="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" targetNamespace="http://my"> <xsd:import namespace="http://www.opengis.net/gml/3.2"/> <xsd:complexType name="my:typenameType"> <xsd:complexContent> <xsd:extension base="gml:AbstractFeatureType"> <xsd:sequence> <xsd:element maxOccurs="1" minOccurs="0" name="id" nillable="true" type="xsd:int"/> <xsd:element maxOccurs="1" minOccurs="0" name="geometryProperty" nillable="true" type="gml:GeometryPropertyType"/> </xsd:sequence> </xsd:extension> </xsd:complexContent> </xsd:complexType> <xsd:element name="typename" substitutionGroup="gml:_Feature" type="my:typenameType"/> </xsd:schema> """) with open( sanitize( endpoint, '?SERVICE=WFS&REQUEST=GetFeature&VERSION=2.0.0&TYPENAMES=my:typename&STARTINDEX=0&COUNT=1&SRSNAME=urn:ogc:def:crs:EPSG::4326' ), 'wb') as f: f.write(""" <wfs:FeatureCollection xmlns:wfs="http://www.opengis.net/wfs/2.0" xmlns:gml="http://www.opengis.net/gml/3.2" xmlns:my="http://my" numberMatched="2" numberReturned="1" timeStamp="2016-03-25T14:51:48.998Z"> <wfs:member> <my:typename gml:id="typename.100"> <my:geometryProperty><gml:Point srsName="urn:ogc:def:crs:EPSG::4326" gml:id="typename.geom.0"><gml:pos>66.33 -70.332</gml:pos></gml:Point></my:geometryProperty> <my:id>1</my:id> </my:typename> </wfs:member> </wfs:FeatureCollection>""") # Create test layer vl = QgsVectorLayer( u"url='http://" + endpoint + u"' typename='my:typename'", u'test', u'WFS') assert vl.isValid() self.assertEquals(vl.wkbType(), QgsWKBTypes.Point) with open( sanitize( endpoint, '?SERVICE=WFS&REQUEST=GetFeature&VERSION=2.0.0&TYPENAMES=my:typename&STARTINDEX=1&COUNT=1&SRSNAME=urn:ogc:def:crs:EPSG::4326' ), 'wb') as f: f.write(""" <wfs:FeatureCollection xmlns:wfs="http://www.opengis.net/wfs/2.0" xmlns:gml="http://www.opengis.net/gml/3.2" xmlns:my="http://my" numberMatched="2" numberReturned="1" timeStamp="2016-03-25T14:51:48.998Z"> <wfs:member> <my:typename gml:id="typename.200"> <my:geometryProperty><gml:Point srsName="urn:ogc:def:crs:EPSG::4326" gml:id="typename.geom.0"><gml:pos>66.33 -70.332</gml:pos></gml:Point></my:geometryProperty> <my:id>2</my:id> </my:typename> </wfs:member> </wfs:FeatureCollection>""") with open( sanitize( endpoint, '?SERVICE=WFS&REQUEST=GetFeature&VERSION=2.0.0&TYPENAMES=my:typename&STARTINDEX=2&COUNT=1&SRSNAME=urn:ogc:def:crs:EPSG::4326' ), 'wb') as f: f.write(""" <wfs:FeatureCollection xmlns:wfs="http://www.opengis.net/wfs/2.0" xmlns:gml="http://www.opengis.net/gml/3.2" xmlns:my="http://my" numberMatched="2" numberReturned="0" timeStamp="2016-03-25T14:51:48.998Z"> </wfs:FeatureCollection>""") values = [f['id'] for f in vl.getFeatures()] self.assertEquals(values, [1, 2]) # Suppress GetFeature responses to demonstrate that the cache is used os.unlink( sanitize( endpoint, '?SERVICE=WFS&REQUEST=GetFeature&VERSION=2.0.0&TYPENAMES=my:typename&STARTINDEX=0&COUNT=1&SRSNAME=urn:ogc:def:crs:EPSG::4326' )) os.unlink( sanitize( endpoint, '?SERVICE=WFS&REQUEST=GetFeature&VERSION=2.0.0&TYPENAMES=my:typename&STARTINDEX=1&COUNT=1&SRSNAME=urn:ogc:def:crs:EPSG::4326' )) os.unlink( sanitize( endpoint, '?SERVICE=WFS&REQUEST=GetFeature&VERSION=2.0.0&TYPENAMES=my:typename&STARTINDEX=2&COUNT=1&SRSNAME=urn:ogc:def:crs:EPSG::4326' )) values = [f['id'] for f in vl.getFeatures()] self.assertEquals(values, [1, 2]) # No need for hits since the download went to its end self.assertEquals(vl.featureCount(), 2) vl.dataProvider().reloadData() # Hits not working self.assertEquals(vl.featureCount(), 0) vl.dataProvider().reloadData() # Hits working with open( sanitize( endpoint, '?SERVICE=WFS&REQUEST=GetFeature&VERSION=2.0.0&TYPENAMES=my:typename&RESULTTYPE=hits' ), 'wb') as f: f.write(""" <wfs:FeatureCollection xmlns:wfs="http://www.opengis.net/wfs/2.0" xmlns:gml="http://www.opengis.net/gml/3.2" xmlns:my="http://my" numberMatched="2" numberReturned="0" timeStamp="2016-03-25T14:51:48.998Z"> </wfs:FeatureCollection>""") self.assertEquals(vl.featureCount(), 2)
# QGIS_PREFIX_PATH should be set if not run from install dir qgs = QgsApplication([], False) qgs.initQgis() django.setup(set_prefix=False) from djangoprovider import DjangoProvider, register_django_provider register_django_provider() from basic.models import * # after django setup model = Point uri = '%s.%s' % (model._meta.app_label, model._meta.model_name) layer = QgsVectorLayer(uri, 'test', 'django') print('layer valid: %s' % layer.isValid()) print('feature count: %s' % layer.featureCount()) print('crs authid: %s' % layer.crs().authid()) print('extent: %s' % layer.sourceExtent().toString()) QgsProject.instance().addMapLayer(layer) image = QImage(QSize(600, 400), QImage.Format_ARGB32_Premultiplied) image.fill(QColor(255, 255, 128).rgb()) painter = QPainter(image) settings = QgsMapSettings() settings.setOutputDpi(image.logicalDpiX()) settings.setOutputImageFormat(QImage.Format_ARGB32) settings.setDestinationCrs(layer.crs()) settings.setOutputSize(image.size())
class MissionTrack(QObject): mission_changed = pyqtSignal(int) step_removed = pyqtSignal(int) def __init__(self, mission_name="Mission", mission_filename=None, mission_layer=None, mission_renderer=None, canvas=None): super(MissionTrack, self).__init__() self.mission_filename = mission_filename self.mission_name = mission_name self.mission_layer = mission_layer self.mission_renderer = mission_renderer self.mission = Mission() self.canvas = canvas self.start_end_marker = StartEndMarker( canvas, self.find_waypoints_in_mission(), QColor(200, 0, 0)) self.saved = False self.modified = False def is_saved(self): """ :return: return True if mission is saved, otherwise False """ return self.saved def is_modified(self): """ :return: return True if mission is modified, otherwise False """ return self.modified def set_modified(self, modified): """ Set modified state""" self.modified = modified def load_mission(self, filename): """ Load mission with name 'filename'""" try: self.mission.load_mission(filename) self.mission_filename = filename self.update_start_end_markers() self.saved = True self.modifed = False except: logger.error("Invalid mission file") raise Exception("Invalid mission file") def set_mission(self, mission): """ Set Mission.""" self.mission = mission def set_mission_filename(self, filename): """ Set Mission Filename""" self.mission_filename = filename def set_mission_name(self, name): """ Set Mission name.""" self.mission_name = name def set_mission_layer(self, layer): """ Set mission layer.""" self.mission_layer.name = self.mission_name self.mission_layer = layer def set_mission_renderer(self, renderer): """ Set mission renderer.""" self.mission_renderer = renderer self.mission_layer.setRenderer(self.mission_renderer) def get_mission(self): """ Returns the mission""" return self.mission def get_step(self, step): """ Returns the step 'step' of the mission""" return self.mission.get_step(step) def get_mission_length(self): """ Returns mission length""" return self.mission.num_steps def get_mission_layer(self): """ Returns mission layer""" return self.mission_layer def get_mission_name(self): """ Returns mission name""" return self.mission_name def get_mission_filename(self): """ Returns mission filename""" return self.mission_filename def copy_mission(self): """ Returns a copy of the current mission""" return copy.deepcopy(self.mission) def change_position(self, wp_id, point): """ Changes a position of the waypoint 'wp_id' with a new position 'point' :param wp_id: Current step :param point: Point position """ position = self.mission.get_step(wp_id).get_maneuver().get_position() position.set_lat_lon(point.y(), point.x()) if wp_id != self.mission.get_length() - 1: # not last point if self.mission.get_step( wp_id + 1).get_maneuver().get_maneuver_type() == SECTION_MANEUVER: logger.debug("Next step is a section") # if next step is a section change its initial position position_next = self.mission.get_step( wp_id + 1).get_maneuver().get_initial_position() position_next.set_lat_lon(point.y(), point.x()) self.mission_changed.emit(wp_id) self.update_start_end_markers() self.modified = True def remove_step(self, wp_id): """ Remove step 'wp_id' :param wp_id: the step number to delete """ initial_num_steps = self.mission.num_steps # if is first point and next waypoint is a Section, show warning message if (wp_id == 0 and initial_num_steps > 1 and self.mission.get_step(wp_id + 1).get_maneuver().get_maneuver_type() == SECTION_MANEUVER): reply = QMessageBox.warning( None, "Mission Error", "Impossible to remove the point because the next point is a section" ) else: if (initial_num_steps - 1 != wp_id and self.mission.get_step(wp_id + 1).get_maneuver( ).get_maneuver_type() == SECTION_MANEUVER): logger.debug("Next step is a section") # if it's not the last step # if next step is a section change its initial position to the one of the point before position_previous = self.mission.get_step( wp_id - 1).get_maneuver().get_position() position_next = self.mission.get_step( wp_id + 1).get_maneuver().get_initial_position() position_next.set_lat_lon(position_previous.latitude, position_previous.longitude) self.mission.remove_step(wp_id) self.step_removed.emit(wp_id) # When point is deleted, layer may need a geometry type change if initial_num_steps <= 2: self.update_layer_geometry() if wp_id == initial_num_steps - 1: # if last waypoint is removed show previous self.mission_changed.emit(wp_id - 1) else: self.mission_changed.emit(wp_id) self.update_start_end_markers() self.modified = True def update_steps(self, id_list, mission_step_list): """ Update the number of the step 'wp_id' with new number 'step' :param id_list: list of step ids to update :param mission_step_list: list of mission steps to update :return: """ if len(id_list) > 0: for i in range(0, len(id_list)): self.mission.update_step(id_list[i], mission_step_list[i]) self.mission_changed.emit(id_list[0]) self.update_start_end_markers() self.modified = True def add_step(self, wp_id, point): """ Add new step""" initial_num_steps = self.mission.num_steps logger.debug("Add step: Mission has {} steps and wp_id is {}".format( initial_num_steps, wp_id)) step = MissionStep() position = MissionPosition() tolerance = MissionTolerance() # If more than one point, get maneuver from surrounding waypoint and copy data if initial_num_steps > 0: if wp_id != 0: # if point is at the end or in the middle copy from the previous manv_to_copy = self.mission.get_step(wp_id - 1).get_maneuver() if manv_to_copy.get_maneuver_type() == SECTION_MANEUVER: # if section, initial pos will be the final pos of the previous manv = MissionSection() position.copy(manv_to_copy.get_position()) tolerance.copy(manv_to_copy.get_tolerance()) manv.set(manv_to_copy.get_final_position(), position, manv_to_copy.get_speed(), tolerance) manv.get_final_position().set_lat_lon(point.y(), point.x()) if wp_id != initial_num_steps: # if point in the middle check if next is also section, we need to modify it manv_next = self.mission.get_step(wp_id).get_maneuver() if manv_next.get_maneuver_type() == SECTION_MANEUVER: manv_next.get_initial_position().set_lat_lon( point.y(), point.x()) else: # wp_id == 0 # copy from the next wp, next cannot be a section if was first waypoint until now, so no need to check manv_to_copy = self.mission.get_step(wp_id).get_maneuver() if manv_to_copy.get_maneuver_type() == WAYPOINT_MANEUVER: manv = MissionWaypoint() position.copy(manv_to_copy.get_position()) tolerance.copy(manv_to_copy.get_tolerance()) manv.set(position, manv_to_copy.get_speed(), tolerance) manv.get_position().set_lat_lon(point.y(), point.x()) elif manv_to_copy.get_maneuver_type() == PARK_MANEUVER: manv = MissionPark() position.copy(manv_to_copy.get_position()) tolerance.copy(manv_to_copy.get_tolerance()) manv.set(position, manv_to_copy.get_speed(), manv_to_copy.get_time(), tolerance) manv.get_position().set_lat_lon(point.y(), point.x()) else: # is first point of the mission, fill maneuver by default type waypoint with clicked position manv = MissionWaypoint( MissionPosition(point.y(), point.x(), 0.0, False), 0.5, MissionTolerance( 2.0, 2.0, 1.0)) # todo: define default z, mode, speed and tolerance step.add_maneuver(manv) self.mission.insert_step(wp_id, step) logger.debug("Mission has now {} steps".format(self.mission.num_steps)) # When point is added, layer may need a geometry type change if initial_num_steps <= 1: self.update_layer_geometry() self.mission_changed.emit(wp_id) self.update_start_end_markers() self.modified = True def save_mission(self): """ Saves a mission""" if self.mission.num_steps == 0: reply = QMessageBox.question( None, "Save Mission", "You are about to save an empty mission. Do you want to proceed?", QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes) if reply == QMessageBox.No: return False elif float( self.mission.get_step(self.mission.get_length() - 1). get_maneuver().get_position().get_z()) != 0.0: # vehicle last waypoint is not at zero depth. reply = QMessageBox.question( None, "Save Mission", "You are about to save and the last waypoint is not at zero depth. " "Do you want to proceed?", QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes) if reply == QMessageBox.No: return False logger.info("Saving mission to {}".format(self.mission_filename)) self.mission.write_mission(self.mission_filename) self.mission_layer.setCustomProperty("mission_xml", self.mission_filename) self.saved = True self.modified = False return True def saveas_mission(self): """ Save a mission with new name""" if self.mission.num_steps == 0: reply = QMessageBox.question( None, "Save Mission", "You are about to save an empty mission. Do you want to proceed?", QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes) if reply == QMessageBox.No: return False, None elif float( self.mission.get_step(self.mission.get_length() - 1). get_maneuver().get_position().get_z()) != 0.0: # vehicle last waypoint is not at zero depth. reply = QMessageBox.question( None, "Save Mission", "You are about to save and the last waypoint is not at zero depth. " "Do you want to proceed?", QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes) if reply == QMessageBox.No: return False, None mission_filename, selected_filter = QFileDialog.getSaveFileName( None, 'Save mission', self.mission_filename, 'XML (*.xml)') if mission_filename != '': if selected_filter == "XML (*.xml)": file_info = QFileInfo(mission_filename) self.mission.write_mission(mission_filename) logger.debug(file_info.fileName()) self.set_mission_name(file_info.fileName()) self.set_mission_filename(mission_filename) self.mission_layer.setName(file_info.baseName()) self.mission_layer.setCustomProperty("mission_xml", self.mission_filename) self.saved = True self.modified = False return True, mission_filename else: return False, None def render_mission(self): """ Render the mission, it will render the mission layer with Point geometry if it has only 1 point oterwise it will be with LineString geometry. """ waypoints = self.find_waypoints_in_mission() if self.mission_layer is None: if len(waypoints) == 1: self.mission_layer = QgsVectorLayer("Point?crs=epsg:4326", self.mission_name, "memory") self.mission_layer.setCustomProperty("mission_xml", self.mission_filename) feature = QgsFeature() feature.setGeometry(QgsGeometry(waypoints[0])) self.mission_layer.dataProvider().addFeatures([feature]) else: self.mission_layer = QgsVectorLayer("LineString?crs=epsg:4326", self.mission_name, "memory") self.mission_layer.setCustomProperty("mission_xml", self.mission_filename) feature = QgsFeature() feature.setGeometry(QgsGeometry.fromPolyline(waypoints)) self.mission_layer.dataProvider().addFeatures([feature]) else: # mission layer exists, now check if it has already a feature or not feats = self.mission_layer.getFeatures() logger.debug("layer feature count {}".format( self.mission_layer.featureCount())) if self.mission_layer.featureCount() == 0: feature = QgsFeature() if len(waypoints) == 1: feature.setGeometry(QgsGeometry(waypoints[0])) else: feature.setGeometry(QgsGeometry.fromPolyline(waypoints)) self.mission_layer.dataProvider().addFeatures([feature]) else: logger.debug("layer has feature mission already, updating...") for f in feats: self.mission_layer.dataProvider().deleteFeatures([f.id()]) feature = QgsFeature() if len(waypoints) == 1: feature.setGeometry(QgsGeometry(waypoints[0])) else: feature.setGeometry( QgsGeometry.fromPolyline(waypoints)) self.mission_layer.dataProvider().addFeatures([feature]) self.set_mission_renderer(self.get_default_track_renderer()) def find_waypoints_in_mission(self, indexes=None): """ Gets all waypoints from a mission structure """ waypoints = [] if indexes is None: step_indexes = range(0, self.mission.size()) else: step_indexes = indexes for stepindex in step_indexes: step = self.mission.get_step(stepindex) maneuver = step.get_maneuver() if maneuver.get_maneuver_type( ) == SECTION_MANEUVER: # for a Section position = maneuver.get_final_position() else: position = maneuver.get_position() # for Waypoint and Park waypoints.append( QgsPoint(float(position.longitude), float(position.latitude))) return waypoints def get_default_track_renderer(self): # Renderer for track lines registry = QgsSymbolLayerRegistry() line_meta = registry.symbolLayerMetadata("SimpleLine") marker_meta = registry.symbolLayerMetadata("MarkerLine") symbol = QgsSymbol.defaultSymbol(self.mission_layer.geometryType()) # Line layer line_layer = line_meta.createSymbolLayer({ 'width': '0.5', 'color': '255,0,0', 'offset': '0.0', 'penstyle': 'solid', 'use_custom_dash': '0', 'joinstyle': 'bevel', 'capstyle': 'square' }) # Marker layer marker_layer = marker_meta.createSymbolLayer({ 'width': '1.5', 'color': '255,0,0', 'placement': 'vertex', 'offset': '0.0' }) sub_symbol = marker_layer.subSymbol() # Replace the default layer with our own SimpleMarker sub_symbol.deleteSymbolLayer(0) cross = registry.symbolLayerMetadata("SimpleMarker").createSymbolLayer( { 'name': 'circle', 'color': '255,0,0', 'color_border': '0,0,0', 'offset': '0,0', 'size': '2.5', 'angle': '0' }) sub_symbol.appendSymbolLayer(cross) # Replace the default layer with our two custom layers symbol.deleteSymbolLayer(0) symbol.appendSymbolLayer(line_layer) symbol.appendSymbolLayer(marker_layer) # Replace the renderer of the current layer renderer = QgsSingleSymbolRenderer(symbol) return renderer def update_start_end_markers(self): """ Updates start_end_markers""" self.start_end_marker.update_markers(self.find_waypoints_in_mission()) def hide_start_end_markers(self): """ Hide start_end_markers""" self.start_end_marker.hide_markers() def remove_start_end_markers(self): """ Removes start end markers""" self.start_end_marker.close_markers() def update_layer_geometry(self): """ This function needs to be called every time a point is added or deleted from the layer If needed, a new layer is created with the apropiate geometry Point if only one point, LineString otherwise """ options = QgsDataProvider.ProviderOptions() waypoints = self.find_waypoints_in_mission() if (len(waypoints) != 1) and self.mission_layer.geometryType( ) == QgsWkbTypes.PointGeometry: self.mission_layer.setDataSource("LineString?crs=epsg:4326", self.mission_name, "memory", options) self.set_mission_renderer(self.get_default_track_renderer()) elif len(waypoints) == 1 and self.mission_layer.geometryType( ) == QgsWkbTypes.LineGeometry: self.mission_layer.setDataSource("Point?crs=epsg:4326", self.mission_name, "memory", options) self.set_mission_renderer(self.get_default_track_renderer())
def testWFST10(self): """Test WFS-T 1.0 (read-write)""" endpoint = self.__class__.basetestpath + '/fake_qgis_http_endpoint_WFS_T_1.0' with open( sanitize(endpoint, '?SERVICE=WFS?REQUEST=GetCapabilities?VERSION=1.0.0'), 'wb') as f: f.write(""" <WFS_Capabilities version="1.0.0" xmlns="http://www.opengis.net/wfs" xmlns:ogc="http://www.opengis.net/ogc"> <FeatureTypeList> <Operations> <Query/> <Insert/> <Update/> <Delete/> </Operations> <FeatureType> <Name>my:typename</Name> <Title>Title</Title> <Abstract>Abstract</Abstract> <SRS>EPSG:4326</SRS> <LatLongBoundingBox minx="-71.123" miny="66.33" maxx="-65.32" maxy="78.3"/> </FeatureType> </FeatureTypeList> </WFS_Capabilities>""") with open( sanitize( endpoint, '?SERVICE=WFS&REQUEST=DescribeFeatureType&VERSION=1.0.0&TYPENAME=my:typename' ), 'wb') as f: f.write(""" <xsd:schema xmlns:my="http://my" xmlns:gml="http://www.opengis.net/gml" xmlns:xsd="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" targetNamespace="http://my"> <xsd:import namespace="http://www.opengis.net/gml"/> <xsd:complexType name="my:typenameType"> <xsd:complexContent> <xsd:extension base="gml:AbstractFeatureType"> <xsd:sequence> <xsd:element maxOccurs="1" minOccurs="0" name="intfield" nillable="true" type="xsd:int"/> <xsd:element maxOccurs="1" minOccurs="0" name="longfield" nillable="true" type="xsd:long"/> <xsd:element maxOccurs="1" minOccurs="0" name="stringfield" nillable="true" type="xsd:string"/> <xsd:element maxOccurs="1" minOccurs="0" name="geometryProperty" nillable="true" type="gml:PointPropertyType"/> </xsd:sequence> </xsd:extension> </xsd:complexContent> </xsd:complexType> <xsd:element name="typename" substitutionGroup="gml:_Feature" type="my:typenameType"/> </xsd:schema> """) vl = QgsVectorLayer( u"url='http://" + endpoint + u"' typename='my:typename' version='1.0.0'", u'test', u'WFS') assert vl.isValid() self.assertEquals( vl.dataProvider().capabilities(), QgsVectorDataProvider.AddFeatures + QgsVectorDataProvider.ChangeAttributeValues + QgsVectorDataProvider.ChangeGeometries + QgsVectorDataProvider.DeleteFeatures) (ret, _) = vl.dataProvider().addFeatures([QgsFeature()]) assert not ret self.assertEquals(vl.featureCount(), 0) assert not vl.dataProvider().deleteFeatures([0]) self.assertEquals(vl.featureCount(), 0) assert not vl.dataProvider().changeGeometryValues( {0: QgsGeometry.fromWkt('Point (3 50)')}) assert not vl.dataProvider().changeAttributeValues({0: {0: 0}}) # Test addFeatures with open( sanitize( endpoint, '?SERVICE=WFS&POSTDATA=<Transaction xmlns="http://www.opengis.net/wfs" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.0.0" service="WFS" xsi:schemaLocation="http://my http://fake_qgis_http_endpoint?REQUEST=DescribeFeatureType&VERSION=1.0.0&TYPENAME=my:typename" xmlns:my="http://my" xmlns:gml="http://www.opengis.net/gml"><Insert xmlns="http://www.opengis.net/wfs"><typename xmlns="http://my"><intfield xmlns="http://my">1</intfield><longfield xmlns="http://my">1234567890123</longfield><stringfield xmlns="http://my">foo</stringfield><geometryProperty xmlns="http://my"><gml:Point srsName="EPSG:4326"><gml:coordinates cs="," ts=" ">2,49</gml:coordinates></gml:Point></geometryProperty></typename></Insert></Transaction>' ), 'wb') as f: f.write(""" <wfs:WFS_TransactionResponse version="1.0.0" xmlns:wfs="http://www.opengis.net/wfs" xmlns:ogc="http://www.opengis.net/ogc"> <wfs:InsertResult> <ogc:FeatureId fid="typename.1" /> </wfs:InsertResult> <wfs:TransactionResult> <wfs:Status> <wfs:SUCCESS/> </wfs:Status> </wfs:TransactionResult> </wfs:WFS_TransactionResponse> """) f = QgsFeature() f.setAttributes([1, 1234567890123, 'foo']) f.setGeometry(QgsGeometry.fromWkt('Point (2 49)')) (ret, fl) = vl.dataProvider().addFeatures([f]) assert ret self.assertEquals(fl[0].id(), 1) self.assertEquals(vl.featureCount(), 1) values = [f['intfield'] for f in vl.getFeatures()] self.assertEquals(values, [1]) values = [f['longfield'] for f in vl.getFeatures()] self.assertEquals(values, [1234567890123]) values = [f['stringfield'] for f in vl.getFeatures()] self.assertEquals(values, ['foo']) got = [f.geometry() for f in vl.getFeatures()][0].geometry() self.assertEquals((got.x(), got.y()), (2.0, 49.0)) # Test changeGeometryValues with open( sanitize( endpoint, '?SERVICE=WFS&POSTDATA=<Transaction xmlns="http://www.opengis.net/wfs" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.0.0" service="WFS" xsi:schemaLocation="http://my http://fake_qgis_http_endpoint?REQUEST=DescribeFeatureType&VERSION=1.0.0&TYPENAME=my:typename" xmlns:my="http://my" xmlns:gml="http://www.opengis.net/gml"><Update xmlns="http://www.opengis.net/wfs" typeName="my:typename"><Property xmlns="http://www.opengis.net/wfs"><Name xmlns="http://www.opengis.net/wfs">geometryProperty</Name><Value xmlns="http://www.opengis.net/wfs"><gml:Point srsName="EPSG:4326"><gml:coordinates cs="," ts=" ">3,50</gml:coordinates></gml:Point></Value></Property><Filter xmlns="http://www.opengis.net/ogc"><FeatureId xmlns="http://www.opengis.net/ogc" fid="typename.1"/></Filter></Update></Transaction>' ), 'wb') as f: f.write(""" <wfs:WFS_TransactionResponse version="1.0.0" xmlns:wfs="http://www.opengis.net/wfs" xmlns:ogc="http://www.opengis.net/ogc"> <wfs:TransactionResult> <wfs:Status> <wfs:SUCCESS/> </wfs:Status> </wfs:TransactionResult> </wfs:WFS_TransactionResponse> """) ret = vl.dataProvider().changeGeometryValues( {1: QgsGeometry.fromWkt('Point (3 50)')}) assert ret got = [f.geometry() for f in vl.getFeatures()][0].geometry() self.assertEquals((got.x(), got.y()), (3.0, 50.0)) values = [f['intfield'] for f in vl.getFeatures()] self.assertEquals(values, [1]) values = [f['longfield'] for f in vl.getFeatures()] self.assertEquals(values, [1234567890123]) values = [f['stringfield'] for f in vl.getFeatures()] self.assertEquals(values, ['foo']) # Test changeAttributeValues with open( sanitize( endpoint, '?SERVICE=WFS&POSTDATA=<Transaction xmlns="http://www.opengis.net/wfs" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.0.0" service="WFS" xsi:schemaLocation="http://my http://fake_qgis_http_endpoint?REQUEST=DescribeFeatureType&VERSION=1.0.0&TYPENAME=my:typename" xmlns:my="http://my" xmlns:gml="http://www.opengis.net/gml"><Update xmlns="http://www.opengis.net/wfs" typeName="my:typename"><Property xmlns="http://www.opengis.net/wfs"><Name xmlns="http://www.opengis.net/wfs">intfield</Name><Value xmlns="http://www.opengis.net/wfs">2</Value></Property><Property xmlns="http://www.opengis.net/wfs"><Name xmlns="http://www.opengis.net/wfs">longfield</Name><Value xmlns="http://www.opengis.net/wfs">3</Value></Property><Property xmlns="http://www.opengis.net/wfs"><Name xmlns="http://www.opengis.net/wfs">stringfield</Name><Value xmlns="http://www.opengis.net/wfs">bar</Value></Property><Filter xmlns="http://www.opengis.net/ogc"><FeatureId xmlns="http://www.opengis.net/ogc" fid="typename.1"/></Filter></Update></Transaction>' ), 'wb') as f: f.write(""" <wfs:WFS_TransactionResponse version="1.0.0" xmlns:wfs="http://www.opengis.net/wfs" xmlns:ogc="http://www.opengis.net/ogc"> <wfs:TransactionResult> <wfs:Status> <wfs:SUCCESS/> </wfs:Status> </wfs:TransactionResult> </wfs:WFS_TransactionResponse> """) assert vl.dataProvider().changeAttributeValues( {1: { 0: 2, 1: 3, 2: "bar" }}) values = [f['intfield'] for f in vl.getFeatures()] self.assertEquals(values, [2]) values = [f['longfield'] for f in vl.getFeatures()] self.assertEquals(values, [3]) values = [f['stringfield'] for f in vl.getFeatures()] self.assertEquals(values, ['bar']) got = [f.geometry() for f in vl.getFeatures()][0].geometry() self.assertEquals((got.x(), got.y()), (3.0, 50.0)) # Test deleteFeatures with open( sanitize( endpoint, '?SERVICE=WFS&POSTDATA=<Transaction xmlns="http://www.opengis.net/wfs" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.0.0" service="WFS" xsi:schemaLocation="http://my http://fake_qgis_http_endpoint?REQUEST=DescribeFeatureType&VERSION=1.0.0&TYPENAME=my:typename" xmlns:my="http://my" xmlns:gml="http://www.opengis.net/gml"><Delete xmlns="http://www.opengis.net/wfs" typeName="my:typename"><Filter xmlns="http://www.opengis.net/ogc"><FeatureId xmlns="http://www.opengis.net/ogc" fid="typename.1"/></Filter></Delete></Transaction>' ), 'wb') as f: f.write(""" <wfs:WFS_TransactionResponse version="1.0.0" xmlns:wfs="http://www.opengis.net/wfs" xmlns:ogc="http://www.opengis.net/ogc"> <wfs:TransactionResult> <wfs:Status> <wfs:SUCCESS/> </wfs:Status> </wfs:TransactionResult> </wfs:WFS_TransactionResponse> """) assert vl.dataProvider().deleteFeatures([1]) self.assertEquals(vl.featureCount(), 0)
class TestQgsTextEditWidget(unittest.TestCase): @classmethod def setUpClass(cls): QgsGui.editorWidgetRegistry().initEditors() def createLayerWithOnePoint(self): self.layer = QgsVectorLayer("Point?field=fldtxt:string&field=fldint:integer", "addfeat", "memory") pr = self.layer.dataProvider() f = QgsFeature() f.setAttributes(["test", 123]) f.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(100, 200))) self.assertTrue(pr.addFeatures([f])) self.assertEqual(self.layer.featureCount(), 1) return self.layer def doAttributeTest(self, idx, expected): reg = QgsGui.editorWidgetRegistry() configWdg = reg.createConfigWidget('TextEdit', self.layer, idx, None) config = configWdg.config() editwidget = reg.create('TextEdit', self.layer, idx, config, None, None) editwidget.setValue('value') self.assertEqual(editwidget.value(), expected[0]) editwidget.setValue(123) self.assertEqual(editwidget.value(), expected[1]) editwidget.setValue(None) self.assertEqual(editwidget.value(), expected[2]) editwidget.setValue(NULL) self.assertEqual(editwidget.value(), expected[3]) def test_SetValue(self): self.createLayerWithOnePoint() self.doAttributeTest(0, ['value', '123', NULL, NULL]) self.doAttributeTest(1, [NULL, 123, NULL, NULL]) def testStringWithMaxLen(self): """ tests that text edit wrappers correctly handle string fields with a maximum length """ layer = QgsVectorLayer("none?field=fldint:integer", "layer", "memory") self.assertTrue(layer.isValid()) layer.dataProvider().addAttributes([QgsField('max', QVariant.String, 'string', 10), QgsField('nomax', QVariant.String, 'string', 0)]) layer.updateFields() QgsProject.instance().addMapLayer(layer) reg = QgsGui.editorWidgetRegistry() config = {'IsMultiline': 'True'} # first test for field without character limit editor = QTextEdit() editor.setPlainText('this_is_a_long_string') w = reg.create('TextEdit', layer, 2, config, editor, None) self.assertEqual(w.value(), 'this_is_a_long_string') # next test for field with character limit editor = QTextEdit() editor.setPlainText('this_is_a_long_string') w = reg.create('TextEdit', layer, 1, config, editor, None) self.assertEqual(w.value(), 'this_is_a_') QgsProject.instance().removeAllMapLayers()
def test_add_feature_geometry(self): """ Test to add a feature with a geometry """ vl_pipes = QgsVectorLayer( self.dbconn + ' sslmode=disable key=\'pk\' table="qgis_test"."pipes" (geom) sql=', 'pipes', 'postgres') vl_leaks = QgsVectorLayer( self.dbconn + ' sslmode=disable key=\'pk\' table="qgis_test"."leaks" (geom) sql=', 'leaks', 'postgres') vl_leaks.startEditing() QgsProject.instance().addMapLayer(vl_pipes) QgsProject.instance().addMapLayer(vl_leaks) self.assertEqual(vl_pipes.featureCount(), 2) self.assertEqual(vl_leaks.featureCount(), 3) rel = QgsRelation() rel.setReferencingLayer(vl_leaks.id()) rel.setReferencedLayer(vl_pipes.id()) rel.addFieldPair('pipe', 'id') rel.setId('rel_pipe_leak') self.assertTrue(rel.isValid()) self.relMgr.addRelation(rel) # Mock vector layer tool to just set default value on created feature class DummyVlTools(QgsVectorLayerTools): def addFeature(self, layer, defaultValues, defaultGeometry): f = QgsFeature(layer.fields()) for idx, value in defaultValues.items(): f.setAttribute(idx, value) f.setGeometry(defaultGeometry) ok = layer.addFeature(f) return ok, f wrapper = QgsRelationWidgetWrapper(vl_leaks, rel) context = QgsAttributeEditorContext() vltool = DummyVlTools() context.setVectorLayerTools(vltool) context.setMapCanvas(self.mapCanvas) cadDockWidget = QgsAdvancedDigitizingDockWidget(self.mapCanvas) context.setCadDockWidget(cadDockWidget) wrapper.setContext(context) widget = wrapper.widget() widget.show() pipe = next(vl_pipes.getFeatures()) self.assertEqual(pipe.id(), 1) wrapper.setFeature(pipe) table_view = widget.findChild(QTableView) self.assertEqual(table_view.model().rowCount(), 1) btn = widget.findChild(QToolButton, 'mAddFeatureGeometryButton') self.assertTrue(btn.isVisible()) self.assertTrue(btn.isEnabled()) btn.click() self.assertTrue(self.mapCanvas.mapTool()) feature = QgsFeature(vl_leaks.fields()) feature.setGeometry(QgsGeometry.fromWkt('POINT(0 0.8)')) self.mapCanvas.mapTool().digitizingCompleted.emit(feature) self.assertEqual(table_view.model().rowCount(), 2) self.assertEqual(vl_leaks.featureCount(), 4) request = QgsFeatureRequest() request.addOrderBy("id", False) # get new created feature feat = next(vl_leaks.getFeatures('"id" is NULL')) self.assertTrue(feat.isValid()) self.assertTrue(feat.geometry().equals( QgsGeometry.fromWkt('POINT(0 0.8)'))) vl_leaks.rollBack()