def test_sql_field_types(self): query = toPercent( "SELECT 42 as t, 'ok'||'ok' as t2, GeomFromText('') as t3, 3.14*2 as t4" ) l4 = QgsVectorLayer("?query=%s" % query, "tt", "virtual", QgsVectorLayer.LayerOptions(False)) self.assertEqual(l4.isValid(), True) self.assertEqual(l4.dataProvider().fields().at(0).name(), "t") self.assertEqual(l4.dataProvider().fields().at(0).type(), QVariant.Int) self.assertEqual(l4.dataProvider().fields().at(1).name(), "t2") self.assertEqual(l4.dataProvider().fields().at(1).type(), QVariant.String) self.assertEqual(l4.dataProvider().fields().at(2).name(), "t3") self.assertEqual(l4.dataProvider().fields().at(2).type(), QVariant.String) self.assertEqual(l4.dataProvider().fields().at(3).name(), "t4") self.assertEqual(l4.dataProvider().fields().at(3).type(), QVariant.Double) # with type annotations query = toPercent( "SELECT '42.0' as t /*:real*/, 3 as t2/*:text */, GeomFromText('') as t3 /*:multiPoInT:4326 */, 3.14*2 as t4/*:int*/" ) l4 = QgsVectorLayer("?query=%s" % query, "tt", "virtual", QgsVectorLayer.LayerOptions(False)) self.assertEqual(l4.isValid(), True) self.assertEqual(l4.dataProvider().fields().at(0).name(), "t") self.assertEqual(l4.dataProvider().fields().at(0).type(), QVariant.Double) self.assertEqual(l4.dataProvider().fields().at(1).name(), "t2") self.assertEqual(l4.dataProvider().fields().at(1).type(), QVariant.String) self.assertEqual(l4.dataProvider().fields().at(2).name(), "t4") self.assertEqual(l4.dataProvider().fields().at(2).type(), QVariant.Int) self.assertEqual(l4.dataProvider().wkbType(), 4) # multipoint # test value types (!= from declared column types) for f in l4.getFeatures(): self.assertEqual(f.attributes()[0], "42.0") self.assertEqual(f.attributes()[1], 3) self.assertEqual(f.attributes()[2], 6.28) # with type annotations and url options query = toPercent( "SELECT 1 as id /*:int*/, geomfromtext('point(0 0)',4326) as geometry/*:point:4326*/" ) l4 = QgsVectorLayer("?query=%s&geometry=geometry" % query, "tt", "virtual", QgsVectorLayer.LayerOptions(False)) self.assertEqual(l4.isValid(), True) self.assertEqual(l4.dataProvider().wkbType(), 1) # point # with type annotations and url options (2) query = toPercent( "SELECT 1 as id /*:int*/, 3.14 as f, geomfromtext('point(0 0)',4326) as geometry/*:point:4326*/" ) l4 = QgsVectorLayer( "?query=%s&geometry=geometry&field=id:text" % query, "tt", "virtual", QgsVectorLayer.LayerOptions(False)) self.assertEqual(l4.isValid(), True) self.assertEqual(l4.dataProvider().fields().at(0).name(), "id") self.assertEqual(l4.dataProvider().fields().at(0).type(), QVariant.String) self.assertEqual(l4.dataProvider().fields().at(1).name(), "f") self.assertEqual(l4.dataProvider().fields().at(1).type(), QVariant.Double) self.assertEqual(l4.dataProvider().wkbType(), 1) # point
def createEmptyLinestringLayer(): layer = QgsVectorLayer("Linestring?field=fldtxt:string&field=fldint:integer", "addfeat", "memory") assert layer.isValid() return layer
def testFidSupport(self): # We do not use @unittest.expectedFailure since the test might actually succeed # on Linux 64bit with GDAL 1.11, where "long" is 64 bit... # GDAL 2.0 is guaranteed to properly support it on all platforms version_num = int(gdal.VersionInfo('VERSION_NUM')) if version_num < GDAL_COMPUTE_VERSION(2, 0, 0): return tmpfile = os.path.join(self.basetestpath, 'testFidSupport.sqlite') ds = ogr.GetDriverByName('SQLite').CreateDataSource(tmpfile) lyr = ds.CreateLayer('test', geom_type=ogr.wkbPoint, options=['FID=fid']) lyr.CreateField(ogr.FieldDefn('strfield', ogr.OFTString)) lyr.CreateField(ogr.FieldDefn('intfield', ogr.OFTInteger)) f = ogr.Feature(lyr.GetLayerDefn()) f.SetFID(12) f.SetField(0, 'foo') f.SetField(1, 123) lyr.CreateFeature(f) f = None ds = None vl = QgsVectorLayer('{}'.format(tmpfile), 'test', 'ogr') self.assertEqual(len(vl.fields()), 3) got = [(f.attribute('fid'), f.attribute('strfield'), f.attribute('intfield')) for f in vl.getFeatures()] self.assertEqual(got, [(12, 'foo', 123)]) got = [(f.attribute('fid'), f.attribute('strfield')) for f in vl.getFeatures(QgsFeatureRequest().setFilterExpression( "strfield = 'foo'"))] self.assertEqual(got, [(12, 'foo')]) got = [(f.attribute('fid'), f.attribute('strfield')) for f in vl.getFeatures(QgsFeatureRequest().setFilterExpression( "fid = 12"))] self.assertEqual(got, [(12, 'foo')]) result = [ f['strfield'] for f in vl.dataProvider().getFeatures(QgsFeatureRequest( ).setSubsetOfAttributes(['strfield'], vl.dataProvider().fields())) ] self.assertEqual(result, ['foo']) result = [ f['fid'] for f in vl.dataProvider().getFeatures(QgsFeatureRequest( ).setSubsetOfAttributes(['fid'], vl.dataProvider().fields())) ] self.assertEqual(result, [12]) # Test that when the 'fid' field is not set, regular insertion is done f = QgsFeature() f.setFields(vl.fields()) f.setAttributes([None, 'automatic_id']) (res, out_f) = vl.dataProvider().addFeatures([f]) self.assertEqual(out_f[0].id(), 13) self.assertEqual(out_f[0].attribute('fid'), 13) self.assertEqual(out_f[0].attribute('strfield'), 'automatic_id') # Test that when the 'fid' field is set, it is really used to set the id f = QgsFeature() f.setFields(vl.fields()) f.setAttributes([9876543210, 'bar']) (res, out_f) = vl.dataProvider().addFeatures([f]) self.assertEqual(out_f[0].id(), 9876543210) self.assertEqual(out_f[0].attribute('fid'), 9876543210) self.assertEqual(out_f[0].attribute('strfield'), 'bar') got = [(f.attribute('fid'), f.attribute('strfield')) for f in vl.getFeatures(QgsFeatureRequest().setFilterExpression( "fid = 9876543210"))] self.assertEqual(got, [(9876543210, 'bar')]) self.assertTrue(vl.dataProvider().changeAttributeValues( {9876543210: { 1: 'baz' }})) got = [(f.attribute('fid'), f.attribute('strfield')) for f in vl.getFeatures(QgsFeatureRequest().setFilterExpression( "fid = 9876543210"))] self.assertEqual(got, [(9876543210, 'baz')]) self.assertTrue(vl.dataProvider().changeAttributeValues( {9876543210: { 0: 9876543210, 1: 'baw' }})) got = [(f.attribute('fid'), f.attribute('strfield')) for f in vl.getFeatures(QgsFeatureRequest().setFilterExpression( "fid = 9876543210"))] self.assertEqual(got, [(9876543210, 'baw')]) # Not allowed: changing the fid regular field self.assertTrue(vl.dataProvider().changeAttributeValues( {9876543210: { 0: 12, 1: 'baw' }})) got = [(f.attribute('fid'), f.attribute('strfield')) for f in vl.getFeatures(QgsFeatureRequest().setFilterExpression( "fid = 9876543210"))] self.assertEqual(got, [(9876543210, 'baw')]) # Cannot delete fid self.assertFalse(vl.dataProvider().deleteAttributes([0])) # Delete first "genuine" attribute self.assertTrue(vl.dataProvider().deleteAttributes([1])) got = [(f.attribute('fid'), f.attribute('intfield')) for f in vl.dataProvider().getFeatures( QgsFeatureRequest().setFilterExpression("fid = 12"))] self.assertEqual(got, [(12, 123)])
def testMapTheme(self): canvas = QgsMapCanvas() canvas.setDestinationCrs(QgsCoordinateReferenceSystem(4326)) canvas.setFrameStyle(0) canvas.resize(600, 400) self.assertEqual(canvas.width(), 600) self.assertEqual(canvas.height(), 400) layer = QgsVectorLayer("Polygon?crs=epsg:4326&field=fldtxt:string", "layer", "memory") # add a polygon to layer f = QgsFeature() f.setGeometry(QgsGeometry.fromRect(QgsRectangle(5, 25, 25, 45))) self.assertTrue(layer.dataProvider().addFeatures([f])) # create a style sym1 = QgsFillSymbol.createSimple({'color': '#ffb200'}) renderer = QgsSingleSymbolRenderer(sym1) layer.setRenderer(renderer) canvas.setLayers([layer]) canvas.setExtent(QgsRectangle(10, 30, 20, 35)) canvas.show() # need to wait until first redraw can occur (note that we first need to wait till drawing starts!) while not canvas.isDrawing(): app.processEvents() canvas.waitWhileRendering() self.assertTrue(self.canvasImageCheck('theme1', 'theme1', canvas)) # add some styles layer.styleManager().addStyleFromLayer('style1') sym2 = QgsFillSymbol.createSimple({'color': '#00b2ff'}) renderer2 = QgsSingleSymbolRenderer(sym2) layer.setRenderer(renderer2) layer.styleManager().addStyleFromLayer('style2') canvas.refresh() canvas.waitWhileRendering() self.assertTrue(self.canvasImageCheck('theme2', 'theme2', canvas)) layer.styleManager().setCurrentStyle('style1') canvas.refresh() canvas.waitWhileRendering() self.assertTrue(self.canvasImageCheck('theme1', 'theme1', canvas)) # OK, so all good with setting/rendering map styles # try setting canvas to a particular theme # make some themes... theme1 = QgsMapThemeCollection.MapThemeRecord() record1 = QgsMapThemeCollection.MapThemeLayerRecord(layer) record1.currentStyle = 'style1' record1.usingCurrentStyle = True theme1.setLayerRecords([record1]) theme2 = QgsMapThemeCollection.MapThemeRecord() record2 = QgsMapThemeCollection.MapThemeLayerRecord(layer) record2.currentStyle = 'style2' record2.usingCurrentStyle = True theme2.setLayerRecords([record2]) QgsProject.instance().mapThemeCollection().insert('theme1', theme1) QgsProject.instance().mapThemeCollection().insert('theme2', theme2) canvas.setTheme('theme2') canvas.refresh() canvas.waitWhileRendering() self.assertTrue(self.canvasImageCheck('theme2', 'theme2', canvas)) canvas.setTheme('theme1') canvas.refresh() canvas.waitWhileRendering() self.assertTrue(self.canvasImageCheck('theme1', 'theme1', canvas)) # add another layer layer2 = QgsVectorLayer("Polygon?crs=epsg:4326&field=fldtxt:string", "layer2", "memory") f = QgsFeature() f.setGeometry(QgsGeometry.fromRect(QgsRectangle(5, 25, 25, 45))) self.assertTrue(layer2.dataProvider().addFeatures([f])) # create a style sym1 = QgsFillSymbol.createSimple({'color': '#b2ff00'}) renderer = QgsSingleSymbolRenderer(sym1) layer2.setRenderer(renderer) # rerender canvas - should NOT show new layer canvas.refresh() canvas.waitWhileRendering() self.assertTrue(self.canvasImageCheck('theme1', 'theme1', canvas)) # test again - this time refresh all layers canvas.refreshAllLayers() canvas.waitWhileRendering() self.assertTrue(self.canvasImageCheck('theme1', 'theme1', canvas)) # add layer 2 to theme1 record3 = QgsMapThemeCollection.MapThemeLayerRecord(layer2) theme1.setLayerRecords([record3]) QgsProject.instance().mapThemeCollection().update('theme1', theme1) canvas.refresh() canvas.waitWhileRendering() self.assertTrue(self.canvasImageCheck('theme3', 'theme3', canvas)) # change the appearance of an active style layer2.styleManager().addStyleFromLayer('original') layer2.styleManager().addStyleFromLayer('style4') record3.currentStyle = 'style4' record3.usingCurrentStyle = True theme1.setLayerRecords([record3]) QgsProject.instance().mapThemeCollection().update('theme1', theme1) canvas.refresh() canvas.waitWhileRendering() self.assertTrue(self.canvasImageCheck('theme3', 'theme3', canvas)) layer2.styleManager().setCurrentStyle('style4') sym3 = QgsFillSymbol.createSimple({'color': '#b200b2'}) layer2.renderer().setSymbol(sym3) canvas.refresh() canvas.waitWhileRendering() self.assertTrue(self.canvasImageCheck('theme4', 'theme4', canvas)) # try setting layers while a theme is in place canvas.setLayers([layer]) canvas.refresh() # should be no change... setLayers should be ignored if canvas is following a theme! canvas.waitWhileRendering() self.assertTrue(self.canvasImageCheck('theme4', 'theme4', canvas)) # setLayerStyleOverrides while theme is in place canvas.setLayerStyleOverrides({layer2.id(): 'original'}) # should be no change... setLayerStyleOverrides should be ignored if canvas is following a theme! canvas.refresh() canvas.waitWhileRendering() self.assertTrue(self.canvasImageCheck('theme4', 'theme4', canvas)) # clear theme canvas.setTheme('') canvas.refresh() canvas.waitWhileRendering() # should be different - we should now render project layers self.assertFalse(self.canvasImageCheck('theme4', 'theme4', canvas))
def testMasterLayerOrder(self): """ test master layer order""" prj = QgsProject.instance() prj.clear() layer = QgsVectorLayer("Point?field=fldtxt:string", "layer1", "memory") layer2 = QgsVectorLayer("Point?field=fldtxt:string", "layer2", "memory") layer3 = QgsVectorLayer("Point?field=fldtxt:string", "layer3", "memory") prj.addMapLayers([layer, layer2, layer3]) prj.layerTreeRoot().setHasCustomLayerOrder(True) prj.layerTreeRoot().setCustomLayerOrder([layer2, layer]) self.assertEqual(prj.mapThemeCollection().masterLayerOrder(), [layer2, layer]) prj.layerTreeRoot().setCustomLayerOrder([layer, layer2, layer3]) # make some themes... theme1 = QgsMapThemeCollection.MapThemeRecord() theme1.setLayerRecords([ QgsMapThemeCollection.MapThemeLayerRecord(layer3), QgsMapThemeCollection.MapThemeLayerRecord(layer) ]) theme2 = QgsMapThemeCollection.MapThemeRecord() theme2.setLayerRecords([ QgsMapThemeCollection.MapThemeLayerRecord(layer3), QgsMapThemeCollection.MapThemeLayerRecord(layer2), QgsMapThemeCollection.MapThemeLayerRecord(layer) ]) theme3 = QgsMapThemeCollection.MapThemeRecord() theme3.setLayerRecords([ QgsMapThemeCollection.MapThemeLayerRecord(layer2), QgsMapThemeCollection.MapThemeLayerRecord(layer) ]) prj.mapThemeCollection().insert('theme1', theme1) prj.mapThemeCollection().insert('theme2', theme2) prj.mapThemeCollection().insert('theme3', theme3) #order of layers in theme should respect master order self.assertEqual( prj.mapThemeCollection().mapThemeVisibleLayers('theme1'), [layer, layer3]) self.assertEqual( prj.mapThemeCollection().mapThemeVisibleLayers('theme2'), [layer, layer2, layer3]) self.assertEqual( prj.mapThemeCollection().mapThemeVisibleLayers('theme3'), [layer, layer2]) # also check ids! self.assertEqual( prj.mapThemeCollection().mapThemeVisibleLayerIds('theme1'), [layer.id(), layer3.id()]) self.assertEqual( prj.mapThemeCollection().mapThemeVisibleLayerIds('theme2'), [layer.id(), layer2.id(), layer3.id()]) self.assertEqual( prj.mapThemeCollection().mapThemeVisibleLayerIds('theme3'), [layer.id(), layer2.id()]) # reset master order prj.layerTreeRoot().setCustomLayerOrder([layer2, layer3, layer]) self.assertEqual( prj.mapThemeCollection().mapThemeVisibleLayers('theme1'), [layer3, layer]) self.assertEqual( prj.mapThemeCollection().mapThemeVisibleLayers('theme2'), [layer2, layer3, layer]) self.assertEqual( prj.mapThemeCollection().mapThemeVisibleLayers('theme3'), [layer2, layer]) self.assertEqual( prj.mapThemeCollection().mapThemeVisibleLayerIds('theme1'), [layer3.id(), layer.id()]) self.assertEqual( prj.mapThemeCollection().mapThemeVisibleLayerIds('theme2'), [layer2.id(), layer3.id(), layer.id()]) self.assertEqual( prj.mapThemeCollection().mapThemeVisibleLayerIds('theme3'), [layer2.id(), layer.id()]) # check that layers include those hidden in the layer tree canvas = QgsMapCanvas() bridge = QgsLayerTreeMapCanvasBridge(prj.layerTreeRoot(), canvas) root = prj.layerTreeRoot() layer_node = root.findLayer(layer2.id()) layer_node.setItemVisibilityChecked(False) app.processEvents() prj.layerTreeRoot().setHasCustomLayerOrder(False) self.assertEqual(prj.mapThemeCollection().masterLayerOrder(), [layer, layer2, layer3]) self.assertEqual( prj.mapThemeCollection().mapThemeVisibleLayers('theme1'), [layer, layer3]) self.assertEqual( prj.mapThemeCollection().mapThemeVisibleLayers('theme2'), [layer, layer2, layer3]) self.assertEqual( prj.mapThemeCollection().mapThemeVisibleLayers('theme3'), [layer, layer2]) self.assertEqual( prj.mapThemeCollection().mapThemeVisibleLayerIds('theme1'), [layer.id(), layer3.id()]) self.assertEqual( prj.mapThemeCollection().mapThemeVisibleLayerIds('theme2'), [layer.id(), layer2.id(), layer3.id()]) self.assertEqual( prj.mapThemeCollection().mapThemeVisibleLayerIds('theme3'), [layer.id(), layer2.id()])
def testCreateExpression(self): """ Test creating an expression using the widget""" layer = QgsVectorLayer("Point?field=fldtxt:string&field=fldint:integer&field=flddate:datetime", "test", "memory") parent = QWidget() w = QgsDefaultSearchWidgetWrapper(layer, 0) w.initWidget(parent) line_edit = w.lineEdit() line_edit.setText('test') case_sensitive = w.caseSensitiveCheckBox() case_sensitive.setChecked(False) self.assertEqual(w.createExpression(QgsSearchWidgetWrapper.IsNull), '"fldtxt" IS NULL') self.assertEqual(w.createExpression(QgsSearchWidgetWrapper.IsNotNull), '"fldtxt" IS NOT NULL') self.assertEqual(w.createExpression(QgsSearchWidgetWrapper.EqualTo), 'lower("fldtxt")=lower(\'test\')') self.assertEqual(w.createExpression(QgsSearchWidgetWrapper.NotEqualTo), 'lower("fldtxt")<>lower(\'test\')') case_sensitive.setChecked(True) self.assertEqual(w.createExpression(QgsSearchWidgetWrapper.EqualTo), '"fldtxt"=\'test\'') self.assertEqual(w.createExpression(QgsSearchWidgetWrapper.NotEqualTo), '"fldtxt"<>\'test\'') case_sensitive.setChecked(False) self.assertEqual(w.createExpression(QgsSearchWidgetWrapper.Contains), '"fldtxt" ILIKE \'%test%\'') self.assertEqual(w.createExpression(QgsSearchWidgetWrapper.DoesNotContain), 'NOT ("fldtxt" ILIKE \'%test%\')') self.assertEqual(w.createExpression(QgsSearchWidgetWrapper.StartsWith), '"fldtxt" ILIKE \'test%\'') self.assertEqual(w.createExpression(QgsSearchWidgetWrapper.EndsWith), '"fldtxt" ILIKE \'%test\'') case_sensitive.setChecked(True) self.assertEqual(w.createExpression(QgsSearchWidgetWrapper.Contains), '"fldtxt" LIKE \'%test%\'') self.assertEqual(w.createExpression(QgsSearchWidgetWrapper.DoesNotContain), 'NOT ("fldtxt" LIKE \'%test%\')') self.assertEqual(w.createExpression(QgsSearchWidgetWrapper.StartsWith), '"fldtxt" LIKE \'test%\'') self.assertEqual(w.createExpression(QgsSearchWidgetWrapper.EndsWith), '"fldtxt" LIKE \'%test\'') case_sensitive.setChecked(False) # numeric field parent = QWidget() w = QgsDefaultSearchWidgetWrapper(layer, 1) w.initWidget(parent) # may need updating if widget layout changes: line_edit = w.lineEdit() line_edit.setText('5.5') self.assertEqual(w.createExpression(QgsSearchWidgetWrapper.EqualTo), '"fldint"=5.5') self.assertEqual(w.createExpression(QgsSearchWidgetWrapper.NotEqualTo), '"fldint"<>5.5') self.assertEqual(w.createExpression(QgsSearchWidgetWrapper.GreaterThan), '"fldint">5.5') self.assertEqual(w.createExpression(QgsSearchWidgetWrapper.LessThan), '"fldint"<5.5') self.assertEqual(w.createExpression(QgsSearchWidgetWrapper.GreaterThanOrEqualTo), '"fldint">=5.5') self.assertEqual(w.createExpression(QgsSearchWidgetWrapper.LessThanOrEqualTo), '"fldint"<=5.5') # date/time/datetime parent = QWidget() w = QgsDefaultSearchWidgetWrapper(layer, 2) w.initWidget(parent) # may need updating if widget layout changes: line_edit = w.lineEdit() line_edit.setText('2015-06-03') self.assertEqual(w.createExpression(QgsSearchWidgetWrapper.EqualTo), '"flddate"=\'2015-06-03\'') self.assertEqual(w.createExpression(QgsSearchWidgetWrapper.NotEqualTo), '"flddate"<>\'2015-06-03\'') self.assertEqual(w.createExpression(QgsSearchWidgetWrapper.GreaterThan), '"flddate">\'2015-06-03\'') self.assertEqual(w.createExpression(QgsSearchWidgetWrapper.LessThan), '"flddate"<\'2015-06-03\'') self.assertEqual(w.createExpression(QgsSearchWidgetWrapper.GreaterThanOrEqualTo), '"flddate">=\'2015-06-03\'') self.assertEqual(w.createExpression(QgsSearchWidgetWrapper.LessThanOrEqualTo), '"flddate"<=\'2015-06-03\'')
def testGdb(self): """ Test opening a GDB database layer""" gdb_path = os.path.join(unitTestDataPath(), 'test_gdb.gdb') for i in range(3): l = QgsVectorLayer(gdb_path + '|layerid=' + str(i), 'test', 'ogr') self.assertTrue(l.isValid())
def run(item, action, mainwindow): db = item.database() uri = db.uri() iface = mainwindow.iface quoteId = db.connector.quoteId quoteStr = db.connector.quoteString # check if the selected item is a topology schema isTopoSchema = False if not hasattr(item, 'schema'): mainwindow.infoBar.pushMessage( "Invalid topology", u'Select a topology schema to continue.', QgsMessageBar.INFO, mainwindow.iface.messageTimeout()) return False if item.schema() is not None: sql = u"SELECT srid FROM topology.topology WHERE name = %s" % quoteStr( item.schema().name) c = db.connector._get_cursor() db.connector._execute(c, sql) res = db.connector._fetchone(c) isTopoSchema = res is not None if not isTopoSchema: mainwindow.infoBar.pushMessage( "Invalid topology", u'Schema "{0}" is not registered in topology.topology.'.format( item.schema().name), QgsMessageBar.WARNING, mainwindow.iface.messageTimeout()) return False if (res[0] < 0): mainwindow.infoBar.pushMessage( "WARNING", u'Topology "{0}" is registered as having a srid of {1} in topology.topology, we will assume 0 (for unknown)' .format(item.schema().name, res[0]), QgsMessageBar.WARNING, mainwindow.iface.messageTimeout()) toposrid = '0' else: toposrid = str(res[0]) # load layers into the current project toponame = item.schema().name template_dir = os.path.join(current_path, 'templates') # do not refresh the canvas until all the layers are added prevRenderFlagState = iface.mapCanvas().renderFlag() iface.mapCanvas().setRenderFlag(False) try: provider = db.dbplugin().providerName() uri = db.uri() # Force use of estimated metadata (topologies can be big) uri.setUseEstimatedMetadata(True) # FACES # face mbr uri.setDataSource(toponame, 'face', 'mbr', '', 'face_id') uri.setSrid(toposrid) uri.setWkbType(QgsWkbTypes.Polygon) layerFaceMbr = QgsVectorLayer(uri.uri(False), u'%s.face_mbr' % toponame, provider) layerFaceMbr.loadNamedStyle(os.path.join(template_dir, 'face_mbr.qml')) face_extent = layerFaceMbr.extent() # face geometry sql = u'SELECT face_id, topology.ST_GetFaceGeometry(%s,' \ 'face_id)::geometry(polygon, %s) as geom ' \ 'FROM %s.face WHERE face_id > 0' % \ (quoteStr(toponame), toposrid, quoteId(toponame)) uri.setDataSource('', u'(%s\n)' % sql, 'geom', '', 'face_id') uri.setSrid(toposrid) uri.setWkbType(QgsWkbTypes.Polygon) layerFaceGeom = QgsVectorLayer(uri.uri(False), u'%s.face' % toponame, provider) layerFaceGeom.setExtent(face_extent) layerFaceGeom.loadNamedStyle(os.path.join(template_dir, 'face.qml')) # face_seed sql = u'SELECT face_id, ST_PointOnSurface(' \ 'topology.ST_GetFaceGeometry(%s,' \ 'face_id))::geometry(point, %s) as geom ' \ 'FROM %s.face WHERE face_id > 0' % \ (quoteStr(toponame), toposrid, quoteId(toponame)) uri.setDataSource('', u'(%s)' % sql, 'geom', '', 'face_id') uri.setSrid(toposrid) uri.setWkbType(QgsWkbTypes.Point) layerFaceSeed = QgsVectorLayer(uri.uri(False), u'%s.face_seed' % toponame, provider) layerFaceSeed.setExtent(face_extent) layerFaceSeed.loadNamedStyle( os.path.join(template_dir, 'face_seed.qml')) # TODO: add polygon0, polygon1 and polygon2 ? # NODES # node uri.setDataSource(toponame, 'node', 'geom', '', 'node_id') uri.setSrid(toposrid) uri.setWkbType(QgsWkbTypes.Point) layerNode = QgsVectorLayer(uri.uri(False), u'%s.node' % toponame, provider) layerNode.loadNamedStyle(os.path.join(template_dir, 'node.qml')) node_extent = layerNode.extent() # node labels uri.setDataSource(toponame, 'node', 'geom', '', 'node_id') uri.setSrid(toposrid) uri.setWkbType(QgsWkbTypes.Point) layerNodeLabel = QgsVectorLayer(uri.uri(False), u'%s.node_id' % toponame, provider) layerNodeLabel.setExtent(node_extent) layerNodeLabel.loadNamedStyle( os.path.join(template_dir, 'node_label.qml')) # EDGES # edge uri.setDataSource(toponame, 'edge_data', 'geom', '', 'edge_id') uri.setSrid(toposrid) uri.setWkbType(QgsWkbTypes.LineString) layerEdge = QgsVectorLayer(uri.uri(False), u'%s.edge' % toponame, provider) edge_extent = layerEdge.extent() # directed edge uri.setDataSource(toponame, 'edge_data', 'geom', '', 'edge_id') uri.setSrid(toposrid) uri.setWkbType(QgsWkbTypes.LineString) layerDirectedEdge = QgsVectorLayer(uri.uri(False), u'%s.directed_edge' % toponame, provider) layerDirectedEdge.setExtent(edge_extent) layerDirectedEdge.loadNamedStyle(os.path.join(template_dir, 'edge.qml')) # edge labels uri.setDataSource(toponame, 'edge_data', 'geom', '', 'edge_id') uri.setSrid(toposrid) uri.setWkbType(QgsWkbTypes.LineString) layerEdgeLabel = QgsVectorLayer(uri.uri(False), u'%s.edge_id' % toponame, provider) layerEdgeLabel.setExtent(edge_extent) layerEdgeLabel.loadNamedStyle( os.path.join(template_dir, 'edge_label.qml')) # face_left uri.setDataSource(toponame, 'edge_data', 'geom', '', 'edge_id') uri.setSrid(toposrid) uri.setWkbType(QgsWkbTypes.LineString) layerFaceLeft = QgsVectorLayer(uri.uri(False), u'%s.face_left' % toponame, provider) layerFaceLeft.setExtent(edge_extent) layerFaceLeft.loadNamedStyle( os.path.join(template_dir, 'face_left.qml')) # face_right uri.setDataSource(toponame, 'edge_data', 'geom', '', 'edge_id') uri.setSrid(toposrid) uri.setWkbType(QgsWkbTypes.LineString) layerFaceRight = QgsVectorLayer(uri.uri(False), u'%s.face_right' % toponame, provider) layerFaceRight.setExtent(edge_extent) layerFaceRight.loadNamedStyle( os.path.join(template_dir, 'face_right.qml')) # next_left uri.setDataSource(toponame, 'edge_data', 'geom', '', 'edge_id') uri.setSrid(toposrid) uri.setWkbType(QgsWkbTypes.LineString) layerNextLeft = QgsVectorLayer(uri.uri(False), u'%s.next_left' % toponame, provider) layerNextLeft.setExtent(edge_extent) layerNextLeft.loadNamedStyle( os.path.join(template_dir, 'next_left.qml')) # next_right uri.setDataSource(toponame, 'edge_data', 'geom', '', 'edge_id') uri.setSrid(toposrid) uri.setWkbType(QgsWkbTypes.LineString) layerNextRight = QgsVectorLayer(uri.uri(False), u'%s.next_right' % toponame, provider) layerNextRight.setExtent(edge_extent) layerNextRight.loadNamedStyle( os.path.join(template_dir, 'next_right.qml')) # Add layers to the layer tree faceLayers = [layerFaceMbr, layerFaceGeom, layerFaceSeed] nodeLayers = [layerNode, layerNodeLabel] edgeLayers = [ layerEdge, layerDirectedEdge, layerEdgeLabel, layerEdgeFaceLeft, layerEdgeFaceRight, layerEdgeNextLeft, layerEdgeNextRight ] QgsMapLayerRegistry.instance().addMapLayers(faceLayers, False) groupFaces = QgsLayerTreeGroup(u'Faces') for layer in faceLayers: nodeLayer = groupFaces.addLayer(layer) nodeLayer.setVisible(Qt.Unchecked) nodeLayer.setExpanded(False) groupNodes = QgsLayerTreeGroup(u'Nodes') for layer in faceLayers: nodeLayer = groupNodes.addLayer(layer) nodeLayer.setVisible(Qt.Unchecked) nodeLayer.setExpanded(False) groupEdges = QgsLayerTreeGroup(u'Edges') for layer in faceLayers: nodeLayer = groupEdges.addLayer(layer) nodeLayer.setVisible(Qt.Unchecked) nodeLayer.setExpanded(False) supergroup = QgsLayerTreeGroup(u'Topology "%s"' % toponame) supergroup.insertChildNodes(-1, [groupFaces, groupNodes, groupEdges]) QgsProject.instance().layerTreeRoot().addChildNode(supergroup) finally: # Set canvas extent to topology extent, if not yet initialized canvas = iface.mapCanvas() if (canvas.fullExtent().isNull()): ext = node_extent ext.combineExtentWith(edge_extent) # Grow by 1/20 of largest side ext = ext.buffer(max(ext.width(), ext.height()) / 20) canvas.setExtent(ext) # restore canvas render flag iface.mapCanvas().setRenderFlag(prevRenderFlagState) return True
def testDateTime(self): """ Test that datetime fields work correctly """ endpoint = self.basetestpath + '/oid_fake_qgis_http_endpoint' with open(sanitize(endpoint, '?f=json'), 'wb') as f: f.write(""" {"currentVersion":10.22,"id":1,"name":"QGIS Test","type":"Feature Layer","description": "QGIS Provider Test Layer.\n","geometryType":"esriGeometryPoint","copyrightText":"","parentLayer":{"id":0,"name":"QGIS Tests"},"subLayers":[], "minScale":72225,"maxScale":0, "defaultVisibility":true, "extent":{"xmin":-71.123,"ymin":66.33,"xmax":-65.32,"ymax":78.3, "spatialReference":{"wkid":4326,"latestWkid":4326}}, "hasAttachments":false,"htmlPopupType":"esriServerHTMLPopupTypeAsHTMLText", "displayField":"LABEL","typeIdField":null, "fields":[{"name":"OBJECTID","type":"esriFieldTypeOID","alias":"OBJECTID","domain":null}, {"name":"pk","type":"esriFieldTypeInteger","alias":"pk","domain":null}, {"name":"dt","type":"esriFieldTypeDate","alias":"dt","length":8,"domain":null}], "relationships":[],"canModifyLayer":false,"canScaleSymbols":false,"hasLabels":false, "capabilities":"Map,Query,Data","maxRecordCount":1000,"supportsStatistics":true, "supportsAdvancedQueries":true,"supportedQueryFormats":"JSON, AMF", "ownershipBasedAccessControlForFeatures":{"allowOthersToQuery":true},"useStandardizedQueries":true}""".encode( 'UTF-8')) with open(sanitize(endpoint, '/query?f=json_where=OBJECTID=OBJECTID_returnIdsOnly=true'), 'wb') as f: f.write(""" { "objectIdFieldName": "OBJECTID", "objectIds": [ 1, 2 ] } """.encode('UTF-8')) # Create test layer vl = QgsVectorLayer("url='http://" + endpoint + "' crs='epsg:4326'", 'test', 'arcgisfeatureserver') self.assertTrue(vl.isValid()) with open(sanitize(endpoint, '/query?f=json&objectIds=1,2&inSR=4326&outSR=4326&returnGeometry=true&outFields=OBJECTID,pk,dt&returnM=false&returnZ=false'), 'wb') as f: f.write(""" { "displayFieldName": "name", "fieldAliases": { "name": "name" }, "geometryType": "esriGeometryPoint", "spatialReference": { "wkid": 4326, "latestWkid": 4326 }, "fields":[{"name":"OBJECTID","type":"esriFieldTypeOID","alias":"OBJECTID","domain":null}, {"name":"pk","type":"esriFieldTypeInteger","alias":"pk","domain":null}, {"name":"dt","type":"esriFieldTypeDate","alias":"dt","domain":null}, {"name":"Shape","type":"esriFieldTypeGeometry","alias":"Shape","domain":null}], "features": [ { "attributes": { "OBJECTID": 1, "pk": 1, "dt":1493769600000 }, "geometry": { "x": -70.332, "y": 66.33 } }, { "attributes": { "OBJECTID": 2, "pk": 2, "dt":null }, "geometry": { "x": -68.2, "y": 70.8 } } ] }""".encode('UTF-8')) features = [f for f in vl.getFeatures()] self.assertEqual(len(features), 2) self.assertEqual([f['dt'] for f in features], [QDate(2017, 5, 3), NULL])
def testCtors(self): testVectors = ["Point", "LineString", "Polygon", "MultiPoint", "MultiLineString", "MultiPolygon", "None"] for v in testVectors: layer = QgsVectorLayer(v, "test", "memory") assert layer.isValid(), "Failed to create valid %s memory layer" % (v)
def testMinMaxCache(self): """ Test that min/max cache is appropriately cleared :return: """ 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]) f4 = QgsFeature() f4.setAttributes([2, 200]) f5 = QgsFeature() f5.setAttributes([4, 400]) res, [f1, f2, f3, f4, f5] = vl.dataProvider().addFeatures([f1, f2, f3, f4, f5]) self.assertTrue(res) self.assertEqual(vl.dataProvider().minimumValue(0), 1) self.assertEqual(vl.dataProvider().minimumValue(1), -200) self.assertEqual(vl.dataProvider().maximumValue(0), 5) self.assertEqual(vl.dataProvider().maximumValue(1), 400) # add feature f6 = QgsFeature() f6.setAttributes([15, 1400]) res, [f6] = vl.dataProvider().addFeatures([f6]) self.assertTrue(res) self.assertEqual(vl.dataProvider().minimumValue(0), 1) self.assertEqual(vl.dataProvider().minimumValue(1), -200) self.assertEqual(vl.dataProvider().maximumValue(0), 15) self.assertEqual(vl.dataProvider().maximumValue(1), 1400) f7 = QgsFeature() f7.setAttributes([-1, -1400]) res, [f7] = vl.dataProvider().addFeatures([f7]) self.assertTrue(res) self.assertEqual(vl.dataProvider().minimumValue(0), -1) self.assertEqual(vl.dataProvider().minimumValue(1), -1400) self.assertEqual(vl.dataProvider().maximumValue(0), 15) self.assertEqual(vl.dataProvider().maximumValue(1), 1400) # change attribute values self.assertTrue(vl.dataProvider().changeAttributeValues({f6.id(): {0: 3, 1: 150}, f7.id(): {0: 4, 1: -100}})) self.assertEqual(vl.dataProvider().minimumValue(0), 1) self.assertEqual(vl.dataProvider().minimumValue(1), -200) self.assertEqual(vl.dataProvider().maximumValue(0), 5) self.assertEqual(vl.dataProvider().maximumValue(1), 400) # delete features self.assertTrue(vl.dataProvider().deleteFeatures([f4.id(), f1.id()])) self.assertEqual(vl.dataProvider().minimumValue(0), 1) self.assertEqual(vl.dataProvider().minimumValue(1), -100) self.assertEqual(vl.dataProvider().maximumValue(0), 4) self.assertEqual(vl.dataProvider().maximumValue(1), 400) # delete attributes self.assertTrue(vl.dataProvider().deleteAttributes([0])) self.assertEqual(vl.dataProvider().minimumValue(0), -100) self.assertEqual(vl.dataProvider().maximumValue(0), 400)
def testInitialSizeSymbolMapUnits(self): """Test initial size of legend with a symbol size in map units""" point_path = os.path.join(TEST_DATA_DIR, 'points.shp') point_layer = QgsVectorLayer(point_path, 'points', 'ogr') QgsProject.instance().addMapLayers([point_layer]) marker_symbol = QgsMarkerSymbol.createSimple({ 'color': '#ff0000', 'outline_style': 'no', 'size': '5', 'size_unit': 'MapUnit' }) point_layer.setRenderer(QgsSingleSymbolRenderer(marker_symbol)) s = QgsMapSettings() s.setLayers([point_layer]) layout = QgsLayout(QgsProject.instance()) layout.initializeDefaults() map = QgsLayoutItemMap(layout) map.attemptSetSceneRect(QRectF(20, 20, 80, 80)) map.setFrameEnabled(True) map.setLayers([point_layer]) layout.addLayoutItem(map) map.setExtent(point_layer.extent()) legend = QgsLayoutItemLegend(layout) legend.setTitle("Legend") legend.attemptSetSceneRect(QRectF(120, 20, 80, 80)) legend.setFrameEnabled(True) legend.setFrameStrokeWidth(QgsLayoutMeasurement(2)) legend.setBackgroundColor(QColor(200, 200, 200)) legend.setTitle('') layout.addLayoutItem(legend) legend.setLinkedMap(map) checker = QgsLayoutChecker('composer_legend_mapunits', layout) checker.setControlPathPrefix("composer_legend") result, message = checker.testLayout() self.assertTrue(result, message) # resize with non-top-left reference point legend.setResizeToContents(False) legend.setReferencePoint(QgsLayoutItem.LowerRight) legend.attemptMove(QgsLayoutPoint(120, 90)) legend.attemptResize(QgsLayoutSize(50, 60)) self.assertEqual(legend.positionWithUnits().x(), 120.0) self.assertEqual(legend.positionWithUnits().y(), 90.0) self.assertAlmostEqual(legend.pos().x(), 70, -1) self.assertAlmostEqual(legend.pos().y(), 30, -1) legend.setResizeToContents(True) legend.updateLegend() self.assertEqual(legend.positionWithUnits().x(), 120.0) self.assertEqual(legend.positionWithUnits().y(), 90.0) self.assertAlmostEqual(legend.pos().x(), 91, -1) self.assertAlmostEqual(legend.pos().y(), 71, -1) QgsProject.instance().removeMapLayers([point_layer.id()])
def testExpressionInText(self): """Test expressions embedded in legend node text""" point_path = os.path.join(TEST_DATA_DIR, 'points.shp') point_layer = QgsVectorLayer(point_path, 'points', 'ogr') layout = QgsPrintLayout(QgsProject.instance()) layout.setName('LAYOUT') layout.initializeDefaults() map = QgsLayoutItemMap(layout) map.attemptSetSceneRect(QRectF(20, 20, 80, 80)) map.setFrameEnabled(True) map.setLayers([point_layer]) layout.addLayoutItem(map) map.setExtent(point_layer.extent()) legend = QgsLayoutItemLegend(layout) legend.setTitle("Legend") legend.attemptSetSceneRect(QRectF(120, 20, 100, 100)) legend.setFrameEnabled(True) legend.setFrameStrokeWidth(QgsLayoutMeasurement(2)) legend.setBackgroundColor(QColor(200, 200, 200)) legend.setTitle('') legend.setLegendFilterByMapEnabled(False) legend.setStyleFont(QgsLegendStyle.Title, QgsFontUtils.getStandardTestFont('Bold', 16)) legend.setStyleFont(QgsLegendStyle.Group, QgsFontUtils.getStandardTestFont('Bold', 16)) legend.setStyleFont(QgsLegendStyle.Subgroup, QgsFontUtils.getStandardTestFont('Bold', 16)) legend.setStyleFont(QgsLegendStyle.Symbol, QgsFontUtils.getStandardTestFont('Bold', 16)) legend.setStyleFont(QgsLegendStyle.SymbolLabel, QgsFontUtils.getStandardTestFont('Bold', 16)) legend.setAutoUpdateModel(False) QgsProject.instance().addMapLayers([point_layer]) s = QgsMapSettings() s.setLayers([point_layer]) group = legend.model().rootGroup().addGroup( "Group [% 1 + 5 %] [% @layout_name %]") layer_tree_layer = group.addLayer(point_layer) layer_tree_layer.setCustomProperty( "legend/title-label", 'bbbb [% 1+2 %] xx [% @layout_name %] [% @layer_name %]') QgsMapLayerLegendUtils.setLegendNodeUserLabel(layer_tree_layer, 0, 'xxxx') legend.model().refreshLayerLegend(layer_tree_layer) legend.model().layerLegendNodes(layer_tree_layer)[0].setUserLabel( 'bbbb [% 1+2 %] xx [% @layout_name %] [% @layer_name %]') layout.addLayoutItem(legend) legend.setLinkedMap(map) map.setExtent(QgsRectangle(-102.51, 41.16, -102.36, 41.30)) checker = QgsLayoutChecker('composer_legend_expressions', layout) checker.setControlPathPrefix("composer_legend") result, message = checker.testLayout() self.assertTrue(result, message) QgsProject.instance().removeMapLayers([point_layer.id()])
def test_joined_layers_conversion(self): v1 = QgsVectorLayer( "Point?field=id:integer&field=b_id:integer&field=c_id:integer&field=name:string", "A", "memory") self.assertEqual(v1.isValid(), True) v2 = QgsVectorLayer( "Point?field=id:integer&field=bname:string&field=bfield:integer", "B", "memory") self.assertEqual(v2.isValid(), True) v3 = QgsVectorLayer("Point?field=id:integer&field=cname:string", "C", "memory") self.assertEqual(v3.isValid(), True) QgsProject.instance().addMapLayers([v1, v2, v3]) joinInfo = QgsVectorLayerJoinInfo() joinInfo.setTargetFieldName("b_id") joinInfo.setJoinLayer(v2) joinInfo.setJoinFieldName("id") #joinInfo.setPrefix("B_") v1.addJoin(joinInfo) self.assertEqual(len(v1.fields()), 6) df = QgsVirtualLayerDefinitionUtils.fromJoinedLayer(v1) self.assertEqual( df.query(), 'SELECT t.rowid AS uid, t.id, t.b_id, t.c_id, t.name, j1.bname AS B_bname, j1.bfield AS B_bfield FROM {} AS t LEFT JOIN {} AS j1 ON t."b_id"=j1."id"' .format(v1.id(), v2.id())) # with a field subset v1.removeJoin(v2.id()) joinInfo.setJoinFieldNamesSubset(["bname"]) v1.addJoin(joinInfo) self.assertEqual(len(v1.fields()), 5) df = QgsVirtualLayerDefinitionUtils.fromJoinedLayer(v1) self.assertEqual( df.query(), 'SELECT t.rowid AS uid, t.id, t.b_id, t.c_id, t.name, j1.bname AS B_bname FROM {} AS t LEFT JOIN {} AS j1 ON t."b_id"=j1."id"' .format(v1.id(), v2.id())) joinInfo.setJoinFieldNamesSubset(None) # add a table prefix to the join v1.removeJoin(v2.id()) joinInfo.setPrefix("BB_") v1.addJoin(joinInfo) self.assertEqual(len(v1.fields()), 6) df = QgsVirtualLayerDefinitionUtils.fromJoinedLayer(v1) self.assertEqual( df.query(), 'SELECT t.rowid AS uid, t.id, t.b_id, t.c_id, t.name, j1.bname AS BB_bname, j1.bfield AS BB_bfield FROM {} AS t LEFT JOIN {} AS j1 ON t."b_id"=j1."id"' .format(v1.id(), v2.id())) joinInfo.setPrefix("") v1.removeJoin(v2.id()) v1.addJoin(joinInfo) # add another join joinInfo2 = QgsVectorLayerJoinInfo() joinInfo2.setTargetFieldName("c_id") joinInfo2.setJoinLayer(v3) joinInfo2.setJoinFieldName("id") v1.addJoin(joinInfo2) self.assertEqual(len(v1.fields()), 7) df = QgsVirtualLayerDefinitionUtils.fromJoinedLayer(v1) self.assertEqual(df.query(), ( 'SELECT t.rowid AS uid, t.id, t.b_id, t.c_id, t.name, j1.bname AS B_bname, j1.bfield AS B_bfield, j2.cname AS C_cname FROM {} AS t ' + 'LEFT JOIN {} AS j1 ON t."b_id"=j1."id" ' + 'LEFT JOIN {} AS j2 ON t."c_id"=j2."id"').format( v1.id(), v2.id(), v3.id())) QgsProject.instance().removeMapLayers([v1.id(), v2.id(), v3.id()])
def testUpdateMode(self): """ Test that on-the-fly re-opening in update/read-only mode works """ tmpdir = tempfile.mkdtemp() self.dirs_to_cleanup.append(tmpdir) srcpath = os.path.join(TEST_DATA_DIR, 'provider') for file in glob.glob(os.path.join(srcpath, 'shapefile.*')): shutil.copy(os.path.join(srcpath, file), tmpdir) datasource = os.path.join(tmpdir, 'shapefile.shp') vl = QgsVectorLayer('{}|layerid=0'.format(datasource), 'test', 'ogr') caps = vl.dataProvider().capabilities() self.assertTrue(caps & QgsVectorDataProvider.AddFeatures) self.assertTrue(caps & QgsVectorDataProvider.DeleteFeatures) self.assertTrue(caps & QgsVectorDataProvider.ChangeAttributeValues) self.assertTrue(caps & QgsVectorDataProvider.AddAttributes) self.assertTrue(caps & QgsVectorDataProvider.DeleteAttributes) self.assertTrue(caps & QgsVectorDataProvider.CreateSpatialIndex) self.assertTrue(caps & QgsVectorDataProvider.SelectAtId) self.assertTrue(caps & QgsVectorDataProvider.ChangeGeometries) # self.assertTrue(caps & QgsVectorDataProvider.ChangeFeatures) # We should be really opened in read-only mode even if write capabilities are declared self.assertEqual(vl.dataProvider().property("_debug_open_mode"), "read-only") # Unbalanced call to leaveUpdateMode() self.assertFalse(vl.dataProvider().leaveUpdateMode()) # Test that startEditing() / commitChanges() plays with enterUpdateMode() / leaveUpdateMode() self.assertTrue(vl.startEditing()) self.assertEqual(vl.dataProvider().property("_debug_open_mode"), "read-write") self.assertTrue(vl.dataProvider().isValid()) self.assertTrue(vl.commitChanges()) self.assertEqual(vl.dataProvider().property("_debug_open_mode"), "read-only") self.assertTrue(vl.dataProvider().isValid()) # Manual enterUpdateMode() / leaveUpdateMode() with 2 depths self.assertTrue(vl.dataProvider().enterUpdateMode()) self.assertEqual(vl.dataProvider().property("_debug_open_mode"), "read-write") caps = vl.dataProvider().capabilities() self.assertTrue(caps & QgsVectorDataProvider.AddFeatures) f = QgsFeature() f.setAttributes([200]) f.setGeometry(QgsGeometry.fromWkt('Point (2 49)')) (ret, feature_list) = vl.dataProvider().addFeatures([f]) self.assertTrue(ret) fid = feature_list[0].id() features = [ f_iter for f_iter in vl.getFeatures(QgsFeatureRequest().setFilterFid(fid)) ] values = [f_iter['pk'] for f_iter in features] self.assertEqual(values, [200]) got_geom = [f_iter.geometry() for f_iter in features][0].constGet() self.assertEqual((got_geom.x(), got_geom.y()), (2.0, 49.0)) self.assertTrue(vl.dataProvider().changeGeometryValues( {fid: QgsGeometry.fromWkt('Point (3 50)')})) self.assertTrue(vl.dataProvider().changeAttributeValues( {fid: { 0: 100 }})) features = [ f_iter for f_iter in vl.getFeatures(QgsFeatureRequest().setFilterFid(fid)) ] values = [f_iter['pk'] for f_iter in features] got_geom = [f_iter.geometry() for f_iter in features][0].constGet() self.assertEqual((got_geom.x(), got_geom.y()), (3.0, 50.0)) self.assertTrue(vl.dataProvider().deleteFeatures([fid])) # Check that it has really disappeared osgeo.gdal.PushErrorHandler('CPLQuietErrorHandler') features = [ f_iter for f_iter in vl.getFeatures(QgsFeatureRequest().setFilterFid(fid)) ] osgeo.gdal.PopErrorHandler() self.assertEqual(features, []) self.assertTrue(vl.dataProvider().addAttributes( [QgsField("new_field", QVariant.Int, "integer")])) self.assertTrue(vl.dataProvider().deleteAttributes( [len(vl.dataProvider().fields()) - 1])) self.assertTrue(vl.startEditing()) self.assertEqual(vl.dataProvider().property("_debug_open_mode"), "read-write") self.assertTrue(vl.commitChanges()) self.assertEqual(vl.dataProvider().property("_debug_open_mode"), "read-write") self.assertTrue(vl.dataProvider().enterUpdateMode()) self.assertEqual(vl.dataProvider().property("_debug_open_mode"), "read-write") self.assertTrue(vl.dataProvider().leaveUpdateMode()) self.assertEqual(vl.dataProvider().property("_debug_open_mode"), "read-write") self.assertTrue(vl.dataProvider().leaveUpdateMode()) self.assertEqual(vl.dataProvider().property("_debug_open_mode"), "read-only") # Test that update mode will be implictly enabled if doing an action # that requires update mode (ret, _) = vl.dataProvider().addFeatures([QgsFeature()]) self.assertTrue(ret) self.assertEqual(vl.dataProvider().property("_debug_open_mode"), "read-write")
def testRenderer(self): """ Test that renderer is correctly acquired from provider """ endpoint = self.basetestpath + '/renderer_fake_qgis_http_endpoint' with open(sanitize(endpoint, '?f=json'), 'wb') as f: f.write(""" {"currentVersion":10.22,"id":1,"name":"QGIS Test","type":"Feature Layer","description": "QGIS Provider Test Layer","geometryType":"esriGeometryPoint","copyrightText":"not copyright","parentLayer":{"id":2,"name":"QGIS Tests"},"subLayers":[], "minScale":72225,"maxScale":0, "defaultVisibility":true, "extent":{"xmin":-71.123,"ymin":66.33,"xmax":-65.32,"ymax":78.3, "spatialReference":{"wkid":4326,"latestWkid":4326}}, "hasAttachments":false,"htmlPopupType":"esriServerHTMLPopupTypeAsHTMLText", "displayField":"LABEL","typeIdField":null, "fields":[{"name":"OBJECTID","type":"esriFieldTypeOID","alias":"OBJECTID","domain":null}], "relationships":[],"canModifyLayer":false,"canScaleSymbols":false,"hasLabels":false, "capabilities":"Map,Query,Data","maxRecordCount":1000,"supportsStatistics":true, "supportsAdvancedQueries":true,"supportedQueryFormats":"JSON, AMF", "drawingInfo":{"renderer": { "type": "uniqueValue", "field1": "COUNTRY", "uniqueValueInfos": [ { "value": "US", "symbol": { "color": [ 253, 127, 111, 255 ], "size": 12.75, "angle": 0, "xoffset": 0, "yoffset": 0, "type": "esriSMS", "style": "esriSMSCircle", "outline": { "color": [ 26, 26, 26, 255 ], "width": 0.75, "type": "esriSLS", "style": "esriSLSSolid" } }, "label": "US" }, { "value": "Canada", "symbol": { "color": [ 126, 176, 213, 255 ], "size": 12.75, "angle": 0, "xoffset": 0, "yoffset": 0, "type": "esriSMS", "style": "esriSMSCircle", "outline": { "color": [ 26, 26, 26, 255 ], "width": 0.75, "type": "esriSLS", "style": "esriSLSSolid" } }, "label": "Canada" }]}}, "ownershipBasedAccessControlForFeatures":{"allowOthersToQuery":true},"useStandardizedQueries":true}""".encode( 'UTF-8')) with open(sanitize(endpoint, '/query?f=json_where=OBJECTID=OBJECTID_returnIdsOnly=true'), 'wb') as f: f.write(""" { "objectIdFieldName": "OBJECTID", "objectIds": [ 1 ] } """.encode('UTF-8')) # Create test layer vl = QgsVectorLayer("url='http://" + endpoint + "' crs='epsg:4326'", 'test', 'arcgisfeatureserver') self.assertTrue(vl.isValid()) self.assertIsNotNone(vl.dataProvider().createRenderer()) self.assertIsInstance(vl.renderer(), QgsCategorizedSymbolRenderer) self.assertEqual(len(vl.renderer().categories()), 2) self.assertEqual(vl.renderer().categories()[0].value(), 'US') self.assertEqual(vl.renderer().categories()[1].value(), 'Canada')
def testCreateExpression(self): """ Test creating an expression using the widget""" layer = QgsVectorLayer("Point?field=fldtxt:string&field=fldint:integer", "test", "memory") # setup value relation parent_layer = QgsVectorLayer("Point?field=stringkey:string&field=intkey:integer&field=display:string", "parent", "memory") f1 = QgsFeature(parent_layer.fields(), 1) f1.setAttributes(['a', 1, 'value a']) f2 = QgsFeature(parent_layer.fields(), 2) f2.setAttributes(['b', 2, 'value b']) f3 = QgsFeature(parent_layer.fields(), 3) f3.setAttributes(['c', 3, 'value c']) parent_layer.dataProvider().addFeatures([f1, f2, f3]) QgsProject.instance().addMapLayers([layer, parent_layer]) config = {"Layer": parent_layer.id(), "Key": 'stringkey', "Value": 'display'} w = QgsValueRelationSearchWidgetWrapper(layer, 0) w.setConfig(config) c = w.widget() # first, set it to the "select value" item c.setCurrentIndex(0) self.assertEqual(w.createExpression(QgsSearchWidgetWrapper.IsNull), '"fldtxt" IS NULL') self.assertEqual(w.createExpression(QgsSearchWidgetWrapper.IsNotNull), '"fldtxt" IS NOT NULL') self.assertEqual(w.createExpression(QgsSearchWidgetWrapper.EqualTo), '') self.assertEqual(w.createExpression(QgsSearchWidgetWrapper.NotEqualTo), '') c.setCurrentIndex(1) self.assertEqual(w.createExpression(QgsSearchWidgetWrapper.IsNull), '"fldtxt" IS NULL') self.assertEqual(w.createExpression(QgsSearchWidgetWrapper.IsNotNull), '"fldtxt" IS NOT NULL') self.assertEqual(w.createExpression(QgsSearchWidgetWrapper.EqualTo), '"fldtxt"=\'a\'') self.assertEqual(w.createExpression(QgsSearchWidgetWrapper.NotEqualTo), '"fldtxt"<>\'a\'') c.setCurrentIndex(2) self.assertEqual(w.createExpression(QgsSearchWidgetWrapper.IsNull), '"fldtxt" IS NULL') self.assertEqual(w.createExpression(QgsSearchWidgetWrapper.IsNotNull), '"fldtxt" IS NOT NULL') self.assertEqual(w.createExpression(QgsSearchWidgetWrapper.EqualTo), '"fldtxt"=\'b\'') self.assertEqual(w.createExpression(QgsSearchWidgetWrapper.NotEqualTo), '"fldtxt"<>\'b\'') # try with numeric field w = QgsValueRelationSearchWidgetWrapper(layer, 1) config['Key'] = 'intkey' w.setConfig(config) c = w.widget() c.setCurrentIndex(c.findText('value c')) self.assertEqual(c.currentIndex(), 3) self.assertEqual(w.createExpression(QgsSearchWidgetWrapper.IsNull), '"fldint" IS NULL') self.assertEqual(w.createExpression(QgsSearchWidgetWrapper.IsNotNull), '"fldint" IS NOT NULL') self.assertEqual(w.createExpression(QgsSearchWidgetWrapper.EqualTo), '"fldint"=3') self.assertEqual(w.createExpression(QgsSearchWidgetWrapper.NotEqualTo), '"fldint"<>3') # try with allow null set w = QgsValueRelationSearchWidgetWrapper(layer, 1) config['AllowNull'] = True w.setConfig(config) c = w.widget() c.setCurrentIndex(c.findText('value c')) self.assertEqual(w.createExpression(QgsSearchWidgetWrapper.IsNull), '"fldint" IS NULL') self.assertEqual(w.createExpression(QgsSearchWidgetWrapper.IsNotNull), '"fldint" IS NOT NULL') self.assertEqual(w.createExpression(QgsSearchWidgetWrapper.EqualTo), '"fldint"=3') self.assertEqual(w.createExpression(QgsSearchWidgetWrapper.NotEqualTo), '"fldint"<>3') # try with line edit w = QgsValueRelationSearchWidgetWrapper(layer, 1) config['UseCompleter'] = True w.setConfig(config) l = w.widget() l.setText('value b') self.assertEqual(w.createExpression(QgsSearchWidgetWrapper.IsNull), '"fldint" IS NULL') self.assertEqual(w.createExpression(QgsSearchWidgetWrapper.IsNotNull), '"fldint" IS NOT NULL') self.assertEqual(w.createExpression(QgsSearchWidgetWrapper.EqualTo), '"fldint"=2') self.assertEqual(w.createExpression(QgsSearchWidgetWrapper.NotEqualTo), '"fldint"<>2')
def setUpClass(cls): """Run before all tests""" QCoreApplication.setOrganizationName("QGIS_Test") QCoreApplication.setOrganizationDomain("TestPyQgsAFSProvider.com") QCoreApplication.setApplicationName("TestPyQgsAFSProvider") QgsSettings().clear() start_app() # On Windows we must make sure that any backslash in the path is # replaced by a forward slash so that QUrl can process it cls.basetestpath = tempfile.mkdtemp().replace('\\', '/') endpoint = cls.basetestpath + '/fake_qgis_http_endpoint' with open(sanitize(endpoint, '?f=json'), 'wb') as f: f.write(""" {"currentVersion":10.22,"id":1,"name":"QGIS Test","type":"Feature Layer","description": "QGIS Provider Test Layer.\n","geometryType":"esriGeometryPoint","copyrightText":"","parentLayer":{"id":0,"name":"QGIS Tests"},"subLayers":[], "minScale":72225,"maxScale":0, "defaultVisibility":true, "extent":{"xmin":-71.123,"ymin":66.33,"xmax":-65.32,"ymax":78.3, "spatialReference":{"wkid":4326,"latestWkid":4326}}, "hasAttachments":false,"htmlPopupType":"esriServerHTMLPopupTypeAsHTMLText", "displayField":"LABEL","typeIdField":null, "fields":[{"name":"OBJECTID","type":"esriFieldTypeOID","alias":"OBJECTID","domain":null}, {"name":"pk","type":"esriFieldTypeInteger","alias":"pk","domain":null}, {"name":"cnt","type":"esriFieldTypeInteger","alias":"cnt","domain":null}, {"name":"name","type":"esriFieldTypeString","alias":"name","length":100,"domain":null}, {"name":"name2","type":"esriFieldTypeString","alias":"name2","length":100,"domain":null}, {"name":"num_char","type":"esriFieldTypeString","alias":"num_char","length":100,"domain":null}, {"name":"Shape","type":"esriFieldTypeGeometry","alias":"Shape","domain":null}], "relationships":[],"canModifyLayer":false,"canScaleSymbols":false,"hasLabels":false, "capabilities":"Map,Query,Data","maxRecordCount":1000,"supportsStatistics":true, "supportsAdvancedQueries":true,"supportedQueryFormats":"JSON, AMF", "ownershipBasedAccessControlForFeatures":{"allowOthersToQuery":true},"useStandardizedQueries":true}""".encode('UTF-8')) with open(sanitize(endpoint, '/query?f=json_where=OBJECTID=OBJECTID_returnIdsOnly=true'), 'wb') as f: f.write(""" { "objectIdFieldName": "OBJECTID", "objectIds": [ 5, 3, 1, 2, 4 ] } """.encode('UTF-8')) # Create test layer cls.vl = QgsVectorLayer("url='http://" + endpoint + "' crs='epsg:4326'", 'test', 'arcgisfeatureserver') assert cls.vl.isValid() cls.source = cls.vl.dataProvider() with open(sanitize(endpoint, '/query?f=json&objectIds=5,3,1,2,4&inSR=4326&outSR=4326&returnGeometry=true&outFields=OBJECTID,pk,cnt,name,name2,num_char&returnM=false&returnZ=false'), 'wb') as f: f.write(""" { "displayFieldName": "name", "fieldAliases": { "name": "name" }, "geometryType": "esriGeometryPoint", "spatialReference": { "wkid": 4326, "latestWkid": 4326 }, "fields":[{"name":"OBJECTID","type":"esriFieldTypeOID","alias":"OBJECTID","domain":null}, {"name":"pk","type":"esriFieldTypeInteger","alias":"pk","domain":null}, {"name":"cnt","type":"esriFieldTypeInteger","alias":"cnt","domain":null}, {"name":"name","type":"esriFieldTypeString","alias":"name","length":100,"domain":null}, {"name":"name2","type":"esriFieldTypeString","alias":"name2","length":100,"domain":null}, {"name":"num_char","type":"esriFieldTypeString","alias":"num_char","length":100,"domain":null}, {"name":"Shape","type":"esriFieldTypeGeometry","alias":"Shape","domain":null}], "features": [ { "attributes": { "OBJECTID": 5, "pk": 5, "cnt": -200, "name": null, "name2":"NuLl", "num_char":"5" }, "geometry": { "x": -71.123, "y": 78.23 } }, { "attributes": { "OBJECTID": 3, "pk": 3, "cnt": 300, "name": "Pear", "name2":"PEaR", "num_char":"3" }, "geometry": null }, { "attributes": { "OBJECTID": 1, "pk": 1, "cnt": 100, "name": "Orange", "name2":"oranGe", "num_char":"1" }, "geometry": { "x": -70.332, "y": 66.33 } }, { "attributes": { "OBJECTID": 2, "pk": 2, "cnt": 200, "name": "Apple", "name2":"Apple", "num_char":"2" }, "geometry": { "x": -68.2, "y": 70.8 } }, { "attributes": { "OBJECTID": 4, "pk": 4, "cnt": 400, "name": "Honey", "name2":"Honey", "num_char":"4" }, "geometry": { "x": -65.32, "y": 78.3 } } ] }""".encode('UTF-8')) with open(sanitize(endpoint, '/query?f=json&objectIds=5,3,1,2,4&inSR=4326&outSR=4326&returnGeometry=true&outFields=OBJECTID,pk,cnt,name,name2,num_char&returnM=false&returnZ=false&geometry=-71.123000,66.330000,-65.320000,78.300000&geometryType=esriGeometryEnvelope&spatialRel=esriSpatialRelEnvelopeIntersects'), 'wb') as f: f.write(""" { "displayFieldName": "name", "fieldAliases": { "name": "name" }, "geometryType": "esriGeometryPoint", "spatialReference": { "wkid": 4326, "latestWkid": 4326 }, "fields":[{"name":"OBJECTID","type":"esriFieldTypeOID","alias":"OBJECTID","domain":null}, {"name":"pk","type":"esriFieldTypeInteger","alias":"pk","domain":null}, {"name":"cnt","type":"esriFieldTypeInteger","alias":"cnt","domain":null}, {"name":"name","type":"esriFieldTypeString","alias":"name","length":100,"domain":null}, {"name":"name2","type":"esriFieldTypeString","alias":"name2","length":100,"domain":null}, {"name":"num_char","type":"esriFieldTypeString","alias":"num_char","length":100,"domain":null}, {"name":"Shape","type":"esriFieldTypeGeometry","alias":"Shape","domain":null}], "features": [ { "attributes": { "OBJECTID": 5, "pk": 5, "cnt": -200, "name": null, "name2":"NuLl", "num_char":"5" }, "geometry": { "x": -71.123, "y": 78.23 } }, { "attributes": { "OBJECTID": 3, "pk": 3, "cnt": 300, "name": "Pear", "name2":"PEaR", "num_char":"3" }, "geometry": null }, { "attributes": { "OBJECTID": 1, "pk": 1, "cnt": 100, "name": "Orange", "name2":"oranGe", "num_char":"1" }, "geometry": { "x": -70.332, "y": 66.33 } }, { "attributes": { "OBJECTID": 2, "pk": 2, "cnt": 200, "name": "Apple", "name2":"Apple", "num_char":"2" }, "geometry": { "x": -68.2, "y": 70.8 } }, { "attributes": { "OBJECTID": 4, "pk": 4, "cnt": 400, "name": "Honey", "name2":"Honey", "num_char":"4" }, "geometry": { "x": -65.32, "y": 78.3 } } ] }""".encode('UTF-8')) with open(sanitize(endpoint, '/query?f=json&objectIds=2,4&inSR=4326&outSR=4326&returnGeometry=true&outFields=OBJECTID,pk,cnt,name,name2,num_char&returnM=false&returnZ=false'), 'wb') as f: f.write(""" { "displayFieldName": "name", "fieldAliases": { "name": "name" }, "geometryType": "esriGeometryPoint", "spatialReference": { "wkid": 4326, "latestWkid": 4326 }, "fields":[{"name":"OBJECTID","type":"esriFieldTypeOID","alias":"OBJECTID","domain":null}, {"name":"pk","type":"esriFieldTypeInteger","alias":"pk","domain":null}, {"name":"cnt","type":"esriFieldTypeInteger","alias":"cnt","domain":null}, {"name":"name","type":"esriFieldTypeString","alias":"name","length":100,"domain":null}, {"name":"name2","type":"esriFieldTypeString","alias":"name2","length":100,"domain":null}, {"name":"num_char","type":"esriFieldTypeString","alias":"num_char","length":100,"domain":null}, {"name":"Shape","type":"esriFieldTypeGeometry","alias":"Shape","domain":null}], "features": [ { "attributes": { "OBJECTID": 2, "pk": 2, "cnt": 200, "name": "Apple", "name2":"Apple", "num_char":"2" }, "geometry": { "x": -68.2, "y": 70.8 } }, { "attributes": { "OBJECTID": 4, "pk": 4, "cnt": 400, "name": "Honey", "name2":"Honey", "num_char":"4" }, "geometry": { "x": -65.32, "y": 78.3 } } ] }""".encode('UTF-8')) with open(sanitize(endpoint, '/query?f=json&where=OBJECTID=OBJECTID&returnIdsOnly=true&geometry=-70.000000,67.000000,-60.000000,80.000000&geometryType=esriGeometryEnvelope&spatialRel=esriSpatialRelEnvelopeIntersects'), 'wb') as f: f.write(""" { "objectIdFieldName": "OBJECTID", "objectIds": [ 2, 4 ] } """.encode('UTF-8')) with open(sanitize(endpoint, '/query?f=json&where=OBJECTID=OBJECTID&returnIdsOnly=true&geometry=-73.000000,70.000000,-63.000000,80.000000&geometryType=esriGeometryEnvelope&spatialRel=esriSpatialRelEnvelopeIntersects'), 'wb') as f: f.write(""" { "objectIdFieldName": "OBJECTID", "objectIds": [ 2, 4 ] } """.encode('UTF-8')) with open(sanitize(endpoint, '/query?f=json&where=OBJECTID=OBJECTID&returnIdsOnly=true&geometry=-68.721119,68.177676,-64.678700,79.123755&geometryType=esriGeometryEnvelope&spatialRel=esriSpatialRelEnvelopeIntersects'), 'wb') as f: f.write(""" { "objectIdFieldName": "OBJECTID", "objectIds": [ 2, 4 ] } """.encode('UTF-8'))
def addVectorLayerFile(self, vectorLayerFile, vectorLayerName): vectorLayer = QgsVectorLayer(vectorLayerFile, vectorLayerName, "ogr") self.addMapLayer(vectorLayer) return vectorLayer
def testDistance(self): self.checkConstructWrapper(QgsProcessingParameterDistance('test'), DistanceWidgetWrapper) alg = QgsApplication.processingRegistry().createAlgorithmById('native:centroids') dlg = AlgorithmDialog(alg) param = QgsProcessingParameterDistance('test') wrapper = DistanceWidgetWrapper(param, dlg) widget = wrapper.createWidget() # test units widget.show() # crs values widget.setUnitParameterValue('EPSG:3111') self.assertEqual(widget.label.text(), 'meters') self.assertFalse(widget.warning_label.isVisible()) self.assertTrue(widget.units_combo.isVisible()) self.assertFalse(widget.label.isVisible()) self.assertEqual(widget.units_combo.currentData(), QgsUnitTypes.DistanceMeters) widget.setUnitParameterValue('EPSG:4326') self.assertEqual(widget.label.text(), 'degrees') self.assertTrue(widget.warning_label.isVisible()) self.assertFalse(widget.units_combo.isVisible()) self.assertTrue(widget.label.isVisible()) widget.setUnitParameterValue(QgsCoordinateReferenceSystem('EPSG:3111')) self.assertEqual(widget.label.text(), 'meters') self.assertFalse(widget.warning_label.isVisible()) self.assertTrue(widget.units_combo.isVisible()) self.assertFalse(widget.label.isVisible()) self.assertEqual(widget.units_combo.currentData(), QgsUnitTypes.DistanceMeters) widget.setUnitParameterValue(QgsCoordinateReferenceSystem('EPSG:4326')) self.assertEqual(widget.label.text(), 'degrees') self.assertTrue(widget.warning_label.isVisible()) self.assertFalse(widget.units_combo.isVisible()) self.assertTrue(widget.label.isVisible()) # layer values vl = QgsVectorLayer("Polygon?crs=epsg:3111&field=pk:int", "vl", "memory") widget.setUnitParameterValue(vl) self.assertEqual(widget.label.text(), 'meters') self.assertFalse(widget.warning_label.isVisible()) self.assertTrue(widget.units_combo.isVisible()) self.assertFalse(widget.label.isVisible()) self.assertEqual(widget.units_combo.currentData(), QgsUnitTypes.DistanceMeters) vl2 = QgsVectorLayer("Polygon?crs=epsg:4326&field=pk:int", "vl", "memory") widget.setUnitParameterValue(vl2) self.assertEqual(widget.label.text(), 'degrees') self.assertTrue(widget.warning_label.isVisible()) self.assertFalse(widget.units_combo.isVisible()) self.assertTrue(widget.label.isVisible()) # unresolvable values widget.setUnitParameterValue(vl.id()) self.assertEqual(widget.label.text(), '<unknown>') self.assertFalse(widget.warning_label.isVisible()) self.assertFalse(widget.units_combo.isVisible()) self.assertTrue(widget.label.isVisible()) # resolvable text value QgsProject.instance().addMapLayer(vl) widget.setUnitParameterValue(vl.id()) self.assertEqual(widget.label.text(), 'meters') self.assertFalse(widget.warning_label.isVisible()) self.assertTrue(widget.units_combo.isVisible()) self.assertFalse(widget.label.isVisible()) self.assertEqual(widget.units_combo.currentData(), QgsUnitTypes.DistanceMeters) widget.setValue(5) self.assertEqual(widget.getValue(), 5) widget.units_combo.setCurrentIndex(widget.units_combo.findData(QgsUnitTypes.DistanceKilometers)) self.assertEqual(widget.getValue(), 5000) widget.setValue(2) self.assertEqual(widget.getValue(), 2000) widget.setUnitParameterValue(vl.id()) self.assertEqual(widget.getValue(), 2) widget.setValue(5) self.assertEqual(widget.getValue(), 5) widget.deleteLater()
def _clip_vector_layer( layer, extent, extra_keywords=None, explode_flag=True, hard_clip_flag=False, explode_attribute=None): """Clip a Hazard or Exposure layer to the extents provided. The layer must be a vector layer or an exception will be thrown. The output layer will always be in WGS84/Geographic. :param layer: A valid QGIS vector or raster layer :type layer: :param extent: Either an array representing the exposure layer extents in the form [xmin, ymin, xmax, ymax]. It is assumed that the coordinates are in EPSG:4326 although currently no checks are made to enforce this. or: A QgsGeometry of type polygon. **Polygon clipping is currently only supported for vector datasets.** :type extent: list(float, float, float, float) :param extra_keywords: Optional keywords dictionary to be added to output layer. :type extra_keywords: dict :param explode_flag: A bool specifying whether multipart features should be 'exploded' into singleparts. **This parameter is ignored for raster layer clipping.** :type explode_flag: bool :param hard_clip_flag: A bool specifying whether line and polygon features that extend beyond the extents should be clipped such that they are reduced in size to the part of the geometry that intersects the extent only. Default is False. **This parameter is ignored for raster layer clipping.** :type hard_clip_flag: bool :param explode_attribute: A str specifying to which attribute #1, #2 and so on will be added in case of explode_flag being true. The attribute is modified only if there are at least 2 parts. :type explode_attribute: str :returns: Clipped layer (placed in the system temp dir). The output layer will be reprojected to EPSG:4326 if needed. :rtype: QgsVectorLayer """ if not layer or not extent: myMessage = tr('Layer or Extent passed to clip is None.') raise InvalidParameterError(myMessage) if layer.type() != QgsMapLayer.VectorLayer: myMessage = tr('Expected a vector layer but received a %s.' % str(layer.type())) raise InvalidParameterError(myMessage) #myHandle, myFilename = tempfile.mkstemp('.sqlite', 'clip_', # temp_dir()) myHandle, myFilename = tempfile.mkstemp('.shp', 'clip_', temp_dir()) # Ensure the file is deleted before we try to write to it # fixes windows specific issue where you get a message like this # ERROR 1: c:\temp\inasafe\clip_jpxjnt.shp is not a directory. # This is because mkstemp creates the file handle and leaves # the file open. os.close(myHandle) os.remove(myFilename) # Get the clip extents in the layer's native CRS myGeoCrs = QgsCoordinateReferenceSystem() myGeoCrs.createFromId(4326, QgsCoordinateReferenceSystem.EpsgCrsId) myXForm = QgsCoordinateTransform(myGeoCrs, layer.crs()) myAllowedClipTypes = [QGis.WKBPolygon, QGis.WKBPolygon25D] if type(extent) is list: myRect = QgsRectangle( extent[0], extent[1], extent[2], extent[3]) # noinspection PyCallByClass myClipPolygon = QgsGeometry.fromRect(myRect) elif (type(extent) is QgsGeometry and extent.wkbType in myAllowedClipTypes): myRect = extent.boundingBox().toRectF() myClipPolygon = extent else: raise InvalidClipGeometryError( tr( 'Clip geometry must be an extent or a single part' 'polygon based geometry.')) myProjectedExtent = myXForm.transformBoundingBox(myRect) # Get vector layer myProvider = layer.dataProvider() if myProvider is None: myMessage = tr('Could not obtain data provider from ' 'layer "%s"' % layer.source()) raise Exception(myMessage) # Get the layer field list, select by our extent then write to disk # .. todo:: FIXME - for different geometry types we should implement # different clipping behaviour e.g. reject polygons that # intersect the edge of the bbox. Tim myAttributes = myProvider.attributeIndexes() myFetchGeometryFlag = True myUseIntersectFlag = True myProvider.select( myAttributes, myProjectedExtent, myFetchGeometryFlag, myUseIntersectFlag) myFieldList = myProvider.fields() myWriter = QgsVectorFileWriter( myFilename, 'UTF-8', myFieldList, layer.wkbType(), myGeoCrs, #'SQLite') # FIXME (Ole): This works but is far too slow 'ESRI Shapefile') if myWriter.hasError() != QgsVectorFileWriter.NoError: myMessage = tr('Error when creating shapefile: <br>Filename:' '%s<br>Error: %s' % (myFilename, myWriter.hasError())) raise Exception(myMessage) # Reverse the coordinate xform now so that we can convert # geometries from layer crs to geocrs. myXForm = QgsCoordinateTransform(layer.crs(), myGeoCrs) # Retrieve every feature with its geometry and attributes myFeature = QgsFeature() myCount = 0 myHasMultipart = False if explode_attribute is not None: theExplodeAttributeIndex = myProvider.fieldNameIndex( explode_attribute) while myProvider.nextFeature(myFeature): myGeometry = myFeature.geometry() if explode_attribute is not None: myAttrs = myFeature.attributeMap() # Loop through the parts adding them to the output file # we write out single part features unless explode_flag is False if explode_flag: myGeometryList = explode_multipart_geometry(myGeometry) else: myGeometryList = [myGeometry] for myPartIndex, myPart in enumerate(myGeometryList): myPart.transform(myXForm) if hard_clip_flag: # Remove any dangling bits so only intersecting area is # kept. myPart = clip_geometry(myClipPolygon, myPart) if myPart is None: continue myFeature.setGeometry(myPart) # There are multiple parts and we want to show it in the # explode_attribute if myPartIndex > 0 and explode_attribute is not None: myHasMultipart = True myPartAttr = QVariant( '%s #%s' % (myAttrs[theExplodeAttributeIndex].toString(), myPartIndex)) myFeature.changeAttribute(theExplodeAttributeIndex, myPartAttr) myWriter.addFeature(myFeature) myCount += 1 del myWriter # Flush to disk if myCount < 1: myMessage = tr( 'No features fall within the clip extents. Try panning / zooming ' 'to an area containing data and then try to run your analysis ' 'again. If hazard and exposure data doesn\'t overlap at all, it ' 'is not possible to do an analysis. Another possibility is that ' 'the layers do overlap but because they may have different ' 'spatial references, they appear to be disjointed. If this is the ' 'case, try to turn on reproject on-the-fly in QGIS.') raise NoFeaturesInExtentError(myMessage) myKeywordIO = KeywordIO() if extra_keywords is None: extra_keywords = {} extra_keywords['HAD_MULTIPART_POLY'] = myHasMultipart myKeywordIO.copy_keywords( layer, myFilename, extra_keywords=extra_keywords) myBaseName = '%s clipped' % layer.name() myLayer = QgsVectorLayer(myFilename, myBaseName, 'ogr') return myLayer
def testDateTime(self): """ Test calculation of aggregates on date/datetime fields""" layer = QgsVectorLayer("Point?field=flddate:date&field=flddatetime:datetime", "layer", "memory") pr = layer.dataProvider() # must be same length: datetime_values = [QDateTime(QDate(2015, 3, 4), QTime(11, 10, 54)), QDateTime(QDate(2011, 1, 5), QTime(15, 3, 1)), QDateTime(QDate(2015, 3, 4), QTime(11, 10, 54)), QDateTime(QDate(2015, 3, 4), QTime(11, 10, 54)), QDateTime(QDate(2019, 12, 28), QTime(23, 10, 1)), QDateTime(), QDateTime(QDate(1998, 1, 2), QTime(1, 10, 54)), QDateTime(), QDateTime(QDate(2011, 1, 5), QTime(11, 10, 54))] date_values = [QDate(2015, 3, 4), QDate(2015, 3, 4), QDate(2019, 12, 28), QDate(), QDate(1998, 1, 2), QDate(), QDate(2011, 1, 5), QDate(2011, 1, 5), QDate(2011, 1, 5)] self.assertEqual(len(datetime_values), len(date_values)) features = [] for i in range(len(datetime_values)): f = QgsFeature() f.setFields(layer.fields()) f.setAttributes([date_values[i], datetime_values[i]]) features.append(f) assert pr.addFeatures(features) tests = [[QgsAggregateCalculator.Count, 'flddatetime', 9], [QgsAggregateCalculator.Count, 'flddate', 9], [QgsAggregateCalculator.CountDistinct, 'flddatetime', 6], [QgsAggregateCalculator.CountDistinct, 'flddate', 5], [QgsAggregateCalculator.CountMissing, 'flddatetime', 2], [QgsAggregateCalculator.CountMissing, 'flddate', 2], [QgsAggregateCalculator.Min, 'flddatetime', QDateTime(QDate(1998, 1, 2), QTime(1, 10, 54))], [QgsAggregateCalculator.Min, 'flddate', QDateTime(QDate(1998, 1, 2), QTime(0, 0, 0))], [QgsAggregateCalculator.Max, 'flddatetime', QDateTime(QDate(2019, 12, 28), QTime(23, 10, 1))], [QgsAggregateCalculator.Max, 'flddate', QDateTime(QDate(2019, 12, 28), QTime(0, 0, 0))], [QgsAggregateCalculator.Range, 'flddatetime', QgsInterval(693871147)], [QgsAggregateCalculator.Range, 'flddate', QgsInterval(693792000)], [QgsAggregateCalculator.ArrayAggregate, 'flddatetime', [None if v.isNull() else v for v in datetime_values]], [QgsAggregateCalculator.ArrayAggregate, 'flddate', [None if v.isNull() else v for v in date_values]], ] agg = QgsAggregateCalculator(layer) for t in tests: val, ok = agg.calculate(t[0], t[1]) self.assertTrue(ok) self.assertEqual(val, t[2]) # bad tests - the following stats should not be calculatable for string fields for t in [QgsAggregateCalculator.Sum, QgsAggregateCalculator.Mean, QgsAggregateCalculator.Median, QgsAggregateCalculator.StDev, QgsAggregateCalculator.StDevSample, QgsAggregateCalculator.Minority, QgsAggregateCalculator.Majority, QgsAggregateCalculator.FirstQuartile, QgsAggregateCalculator.ThirdQuartile, QgsAggregateCalculator.InterQuartileRange, QgsAggregateCalculator.StringMinimumLength, QgsAggregateCalculator.StringMaximumLength, ]: val, ok = agg.calculate(t, 'flddatetime') self.assertFalse(ok)
def testRefreshOnTimer(self): """ test that map canvas refreshes with auto refreshing layers """ canvas = QgsMapCanvas() canvas.setDestinationCrs(QgsCoordinateReferenceSystem(4326)) canvas.setFrameStyle(0) canvas.resize(600, 400) self.assertEqual(canvas.width(), 600) self.assertEqual(canvas.height(), 400) layer = QgsVectorLayer("Polygon?crs=epsg:4326&field=fldtxt:string", "layer", "memory") canvas.setLayers([layer]) canvas.setExtent(QgsRectangle(10, 30, 20, 35)) canvas.show() # need to wait until first redraw can occur (note that we first need to wait till drawing starts!) while not canvas.isDrawing(): app.processEvents() canvas.waitWhileRendering() self.assertTrue(self.canvasImageCheck('empty_canvas', 'empty_canvas', canvas)) # add polygon to layer f = QgsFeature() f.setGeometry(QgsGeometry.fromRect(QgsRectangle(5, 25, 25, 45))) self.assertTrue(layer.dataProvider().addFeatures([f])) # set auto refresh on layer layer.setAutoRefreshInterval(100) layer.setAutoRefreshEnabled(True) timeout = time.time() + 1 # expect canvas to auto refresh... while not canvas.isDrawing(): app.processEvents() self.assertTrue(time.time() < timeout) while canvas.isDrawing(): app.processEvents() self.assertTrue(time.time() < timeout) # add a polygon to layer f = QgsFeature() f.setGeometry(QgsGeometry.fromRect(QgsRectangle(5, 25, 25, 45))) self.assertTrue(layer.dataProvider().addFeatures([f])) # wait for canvas auto refresh while not canvas.isDrawing(): app.processEvents() self.assertTrue(time.time() < timeout) while canvas.isDrawing(): app.processEvents() self.assertTrue(time.time() < timeout) # now canvas should look different... self.assertFalse(self.canvasImageCheck('empty_canvas', 'empty_canvas', canvas)) # switch off auto refresh layer.setAutoRefreshEnabled(False) timeout = time.time() + 0.5 while time.time() < timeout: # messy, but only way to check that canvas redraw doesn't occur self.assertFalse(canvas.isDrawing())
def testExpression(self): """ test aggregate calculation using an expression """ # numeric layer = QgsVectorLayer("Point?field=fldint:integer", "layer", "memory") pr = layer.dataProvider() int_values = [4, 2, 3, 2, 5, None, 8] features = [] for v in int_values: f = QgsFeature() f.setFields(layer.fields()) f.setAttributes([v]) features.append(f) assert pr.addFeatures(features) # int agg = QgsAggregateCalculator(layer) val, ok = agg.calculate(QgsAggregateCalculator.Sum, 'fldint * 2') self.assertTrue(ok) self.assertEqual(val, 48) # double val, ok = agg.calculate(QgsAggregateCalculator.Sum, 'fldint * 1.5') self.assertTrue(ok) self.assertEqual(val, 36) # datetime val, ok = agg.calculate(QgsAggregateCalculator.Max, "to_date('2012-05-04') + to_interval( fldint || ' day' )") self.assertTrue(ok) self.assertEqual(val, QDateTime(QDate(2012, 5, 12), QTime(0, 0, 0))) # date val, ok = agg.calculate(QgsAggregateCalculator.Min, "to_date(to_date('2012-05-04') + to_interval( fldint || ' day' ))") self.assertTrue(ok) self.assertEqual(val, QDateTime(QDate(2012, 5, 6), QTime(0, 0, 0))) # string val, ok = agg.calculate(QgsAggregateCalculator.Max, "fldint || ' oranges'") self.assertTrue(ok) self.assertEqual(val, '8 oranges') # geometry val, ok = agg.calculate(QgsAggregateCalculator.GeometryCollect, "make_point( coalesce(fldint,0), 2 )") self.assertTrue(ok) self.assertTrue(val.asWkt(), 'MultiPoint((4 2, 2 2, 3 2, 2 2,5 2, 0 2,8 2))') # try a bad expression val, ok = agg.calculate(QgsAggregateCalculator.Max, "not_a_field || ' oranges'") self.assertFalse(ok) val, ok = agg.calculate(QgsAggregateCalculator.Max, "5+") self.assertFalse(ok) # test expression context # check default context first # should have layer variables: val, ok = agg.calculate(QgsAggregateCalculator.Min, "@layer_name") self.assertTrue(ok) self.assertEqual(val, 'layer') # but not custom variables: val, ok = agg.calculate(QgsAggregateCalculator.Min, "@my_var") self.assertTrue(ok) self.assertEqual(val, NULL) # test with manual expression context scope = QgsExpressionContextScope() scope.setVariable('my_var', 5) context = QgsExpressionContext() context.appendScope(scope) val, ok = agg.calculate(QgsAggregateCalculator.Min, "@my_var", context) self.assertTrue(ok) self.assertEqual(val, 5)
def testThemeChanged(self): """ Test that the mapTheme(s)Changed signals are correctly emitted in all relevant situations """ project = QgsProject() collection = QgsMapThemeCollection(project) record = QgsMapThemeCollection.MapThemeRecord() theme_changed_spy = QSignalSpy(collection.mapThemeChanged) themes_changed_spy = QSignalSpy(collection.mapThemesChanged) collection.insert('theme1', record) self.assertEqual(len(theme_changed_spy), 1) self.assertEqual(theme_changed_spy[-1][0], 'theme1') self.assertEqual(len(themes_changed_spy), 1) # reinsert collection.insert('theme1', record) self.assertEqual(len(theme_changed_spy), 2) self.assertEqual(theme_changed_spy[-1][0], 'theme1') self.assertEqual(len(themes_changed_spy), 2) # update collection.update('theme1', record) self.assertEqual(len(theme_changed_spy), 3) self.assertEqual(theme_changed_spy[-1][0], 'theme1') self.assertEqual(len(themes_changed_spy), 3) # remove invalid collection.removeMapTheme( 'i wish i was a slave to an age old trade... like riding around on rail cars and working long days' ) self.assertEqual(len(theme_changed_spy), 3) self.assertEqual(len(themes_changed_spy), 3) # remove valid collection.removeMapTheme('theme1') self.assertEqual(len(theme_changed_spy), 3) # not changed - removed! self.assertEqual(len(themes_changed_spy), 4) # reinsert collection.insert('theme1', record) self.assertEqual(len(theme_changed_spy), 4) self.assertEqual(len(themes_changed_spy), 5) # clear collection.clear() self.assertEqual(len(theme_changed_spy), 4) # not changed - removed! self.assertEqual(len(themes_changed_spy), 6) # check that mapThemeChanged is emitted if layer is removed layer = QgsVectorLayer("Point?field=fldtxt:string", "layer1", "memory") layer2 = QgsVectorLayer("Point?field=fldtxt:string", "layer2", "memory") project.addMapLayers([layer, layer2]) # record for layer1 record.addLayerRecord(QgsMapThemeCollection.MapThemeLayerRecord(layer)) collection.insert('theme1', record) self.assertEqual(len(theme_changed_spy), 5) self.assertEqual(len(themes_changed_spy), 7) # now kill layer 2 project.removeMapLayer(layer2) self.assertEqual( len(theme_changed_spy), 5) # signal should not be emitted - layer is not in record # now kill layer 1 project.removeMapLayer(layer) app.processEvents() self.assertEqual(len(theme_changed_spy), 6) # signal should be emitted - layer is in record
def testNumeric(self): """ Test calculation of aggregates on numeric fields""" layer = QgsVectorLayer("Point?field=fldint:integer&field=flddbl:double", "layer", "memory") pr = layer.dataProvider() # must be same length: int_values = [4, 2, 3, 2, 5, None, 8] dbl_values = [5.5, 3.5, 7.5, 5, 9, None, 7] self.assertEqual(len(int_values), len(dbl_values)) features = [] for i in range(len(int_values)): f = QgsFeature() f.setFields(layer.fields()) f.setAttributes([int_values[i], dbl_values[i]]) features.append(f) assert pr.addFeatures(features) tests = [[QgsAggregateCalculator.Count, 'fldint', 6], [QgsAggregateCalculator.Count, 'flddbl', 6], [QgsAggregateCalculator.Sum, 'fldint', 24], [QgsAggregateCalculator.Sum, 'flddbl', 37.5], [QgsAggregateCalculator.Mean, 'fldint', 4], [QgsAggregateCalculator.Mean, 'flddbl', 6.25], [QgsAggregateCalculator.StDev, 'fldint', 2.0816], [QgsAggregateCalculator.StDev, 'flddbl', 1.7969], [QgsAggregateCalculator.StDevSample, 'fldint', 2.2803], [QgsAggregateCalculator.StDevSample, 'flddbl', 1.9685], [QgsAggregateCalculator.Min, 'fldint', 2], [QgsAggregateCalculator.Min, 'flddbl', 3.5], [QgsAggregateCalculator.Max, 'fldint', 8], [QgsAggregateCalculator.Max, 'flddbl', 9], [QgsAggregateCalculator.Range, 'fldint', 6], [QgsAggregateCalculator.Range, 'flddbl', 5.5], [QgsAggregateCalculator.Median, 'fldint', 3.5], [QgsAggregateCalculator.Median, 'flddbl', 6.25], [QgsAggregateCalculator.CountDistinct, 'fldint', 5], [QgsAggregateCalculator.CountDistinct, 'flddbl', 6], [QgsAggregateCalculator.CountMissing, 'fldint', 1], [QgsAggregateCalculator.CountMissing, 'flddbl', 1], [QgsAggregateCalculator.FirstQuartile, 'fldint', 2], [QgsAggregateCalculator.FirstQuartile, 'flddbl', 5.0], [QgsAggregateCalculator.ThirdQuartile, 'fldint', 5.0], [QgsAggregateCalculator.ThirdQuartile, 'flddbl', 7.5], [QgsAggregateCalculator.InterQuartileRange, 'fldint', 3.0], [QgsAggregateCalculator.InterQuartileRange, 'flddbl', 2.5], [QgsAggregateCalculator.ArrayAggregate, 'fldint', int_values], [QgsAggregateCalculator.ArrayAggregate, 'flddbl', dbl_values], ] agg = QgsAggregateCalculator(layer) for t in tests: val, ok = agg.calculate(t[0], t[1]) self.assertTrue(ok) if isinstance(t[2], (int, list)): self.assertEqual(val, t[2]) else: self.assertAlmostEqual(val, t[2], 3) # bad tests - the following stats should not be calculatable for numeric fields for t in [QgsAggregateCalculator.StringMinimumLength, QgsAggregateCalculator.StringMaximumLength]: val, ok = agg.calculate(t, 'fldint') self.assertFalse(ok) val, ok = agg.calculate(t, 'flddbl') self.assertFalse(ok)
def testSubsetStringFids(self): """ - tests that feature ids are stable even if a subset string is set - tests that the subset string is correctly set on the ogr layer event when reloading the data source (issue #17122) """ tmpfile = os.path.join(self.basetestpath, 'subsetStringFids.sqlite') ds = ogr.GetDriverByName('SQLite').CreateDataSource(tmpfile) lyr = ds.CreateLayer('test', geom_type=ogr.wkbPoint, options=['FID=fid']) lyr.CreateField(ogr.FieldDefn('type', ogr.OFTInteger)) lyr.CreateField(ogr.FieldDefn('value', ogr.OFTInteger)) f = ogr.Feature(lyr.GetLayerDefn()) f.SetFID(0) f.SetField(0, 1) f.SetField(1, 11) f.SetGeometry(ogr.CreateGeometryFromWkt('Point (0 0)')) lyr.CreateFeature(f) f = ogr.Feature(lyr.GetLayerDefn()) f.SetFID(1) f.SetField(0, 1) f.SetField(1, 12) f.SetGeometry(ogr.CreateGeometryFromWkt('Point (1 1)')) lyr.CreateFeature(f) f = ogr.Feature(lyr.GetLayerDefn()) f.SetFID(2) f.SetField(0, 1) f.SetField(1, 13) f.SetGeometry(ogr.CreateGeometryFromWkt('Point (2 2)')) lyr.CreateFeature(f) f = ogr.Feature(lyr.GetLayerDefn()) f.SetFID(3) f.SetField(0, 2) f.SetField(1, 14) f.SetGeometry(ogr.CreateGeometryFromWkt('Point (3 3)')) lyr.CreateFeature(f) f = ogr.Feature(lyr.GetLayerDefn()) f.SetFID(4) f.SetField(0, 2) f.SetField(1, 15) f.SetGeometry(ogr.CreateGeometryFromWkt('Point (4 4)')) lyr.CreateFeature(f) f = ogr.Feature(lyr.GetLayerDefn()) f.SetFID(5) f.SetField(0, 2) f.SetField(1, 16) f.SetGeometry(ogr.CreateGeometryFromWkt('Point (5 5)')) lyr.CreateFeature(f) f = None ds = None vl = QgsVectorLayer(tmpfile, 'test', 'ogr') self.assertTrue(vl.isValid()) self.assertEqual([f.name() for f in vl.fields()], ['fid', 'type', 'value']) original_fields = vl.fields() vl = QgsVectorLayer(tmpfile + "|subset=type=2", 'test', 'ogr') self.assertTrue(vl.isValid()) def run_checks(): self.assertEqual([f.name() for f in vl.fields()], ['fid', 'type', 'value']) # expression req = QgsFeatureRequest() req.setFilterExpression("value=16") it = vl.getFeatures(req) f = QgsFeature() self.assertTrue(it.nextFeature(f)) self.assertEqual(f.id(), 5) self.assertEqual(f.attributes(), [5, 2, 16]) self.assertEqual([field.name() for field in f.fields()], ['fid', 'type', 'value']) self.assertEqual(f.geometry().asWkt(), 'Point (5 5)') # filter fid req = QgsFeatureRequest() req.setFilterFid(5) it = vl.getFeatures(req) f = QgsFeature() self.assertTrue(it.nextFeature(f)) self.assertEqual(f.id(), 5) self.assertEqual(f.attributes(), [5, 2, 16]) self.assertEqual([field.name() for field in f.fields()], ['fid', 'type', 'value']) self.assertEqual(f.geometry().asWkt(), 'Point (5 5)') # filter fids req = QgsFeatureRequest() req.setFilterFids([5]) it = vl.getFeatures(req) f = QgsFeature() self.assertTrue(it.nextFeature(f)) self.assertEqual(f.id(), 5) self.assertEqual(f.attributes(), [5, 2, 16]) self.assertEqual([field.name() for field in f.fields()], ['fid', 'type', 'value']) self.assertEqual(f.geometry().asWkt(), 'Point (5 5)') # check with subset of attributes req = QgsFeatureRequest() req.setFilterFids([5]) req.setSubsetOfAttributes([2]) it = vl.getFeatures(req) f = QgsFeature() self.assertTrue(it.nextFeature(f)) self.assertEqual(f.id(), 5) self.assertEqual(f.attributes()[2], 16) self.assertEqual([field.name() for field in f.fields()], ['fid', 'type', 'value']) self.assertEqual(f.geometry().asWkt(), 'Point (5 5)') # filter rect and expression req = QgsFeatureRequest() req.setFilterExpression("value=16 or value=14") req.setFilterRect(QgsRectangle(4.5, 4.5, 5.5, 5.5)) it = vl.getFeatures(req) f = QgsFeature() self.assertTrue(it.nextFeature(f)) self.assertEqual(f.id(), 5) self.assertEqual(f.attributes(), [5, 2, 16]) self.assertEqual([field.name() for field in f.fields()], ['fid', 'type', 'value']) self.assertEqual(f.geometry().asWkt(), 'Point (5 5)') # filter rect and fids req = QgsFeatureRequest() req.setFilterFids([3, 5]) req.setFilterRect(QgsRectangle(4.5, 4.5, 5.5, 5.5)) it = vl.getFeatures(req) f = QgsFeature() self.assertTrue(it.nextFeature(f)) self.assertEqual(f.id(), 5) self.assertEqual(f.attributes(), [5, 2, 16]) self.assertEqual([field.name() for field in f.fields()], ['fid', 'type', 'value']) self.assertEqual(f.geometry().asWkt(), 'Point (5 5)') # Ensure that orig_ogc_fid is still retrieved even if attribute subset is passed req = QgsFeatureRequest() req.setSubsetOfAttributes([]) it = vl.getFeatures(req) ids = [] geoms = {} while it.nextFeature(f): ids.append(f.id()) geoms[f.id()] = f.geometry().asWkt() self.assertCountEqual(ids, [3, 4, 5]) self.assertEqual(geoms, { 3: 'Point (3 3)', 4: 'Point (4 4)', 5: 'Point (5 5)' }) run_checks() # Check that subset string is correctly set on reload vl.reload() run_checks()
def test_invalidGeometryFilter(self): layer = QgsVectorLayer( "Polygon?field=x:string", "joinlayer", "memory") # add some features, one has invalid geometry pr = layer.dataProvider() f1 = QgsFeature(1) f1.setAttributes(["a"]) f1.setGeometry(QgsGeometry.fromWkt('Polygon((0 0, 1 0, 1 1, 0 1, 0 0))')) # valid f2 = QgsFeature(2) f2.setAttributes(["b"]) f2.setGeometry(QgsGeometry.fromWkt('Polygon((0 0, 1 0, 0 1, 1 1, 0 0))')) # invalid f3 = QgsFeature(3) f3.setAttributes(["c"]) f3.setGeometry(QgsGeometry.fromWkt('Polygon((0 0, 1 0, 1 1, 0 1, 0 0))')) # valid self.assertTrue(pr.addFeatures([f1, f2, f3])) res = [f['x'] for f in layer.getFeatures(QgsFeatureRequest().setInvalidGeometryCheck(QgsFeatureRequest.GeometryNoCheck))] self.assertEqual(res, ['a', 'b', 'c']) res = [f['x'] for f in layer.getFeatures(QgsFeatureRequest().setInvalidGeometryCheck(QgsFeatureRequest.GeometrySkipInvalid))] self.assertEqual(res, ['a', 'c']) res = [f['x'] for f in layer.getFeatures(QgsFeatureRequest().setInvalidGeometryCheck(QgsFeatureRequest.GeometryAbortOnInvalid))] self.assertEqual(res, ['a']) # with callback self.callback_feature_val = None def callback(feature): self.callback_feature_val = feature['x'] res = [f['x'] for f in layer.getFeatures(QgsFeatureRequest().setInvalidGeometryCheck( QgsFeatureRequest.GeometryAbortOnInvalid).setInvalidGeometryCallback(callback))] self.assertEqual(res, ['a']) self.assertEqual(self.callback_feature_val, 'b') # clear callback res = [f['x'] for f in layer.getFeatures(QgsFeatureRequest().setInvalidGeometryCheck( QgsFeatureRequest.GeometryAbortOnInvalid).setInvalidGeometryCallback(None))] self.assertEqual(res, ['a']) # check with filter fids res = [f['x'] for f in layer.getFeatures(QgsFeatureRequest().setFilterFid(f2.id()).setInvalidGeometryCheck(QgsFeatureRequest.GeometryNoCheck))] self.assertEqual(res, ['b']) res = [f['x'] for f in layer.getFeatures(QgsFeatureRequest().setFilterFid(f2.id()).setInvalidGeometryCheck(QgsFeatureRequest.GeometrySkipInvalid))] self.assertEqual(res, []) res = [f['x'] for f in layer.getFeatures(QgsFeatureRequest().setFilterFid(f2.id()).setInvalidGeometryCheck(QgsFeatureRequest.GeometryAbortOnInvalid))] self.assertEqual(res, []) f4 = QgsFeature(4) f4.setAttributes(["d"]) f4.setGeometry(QgsGeometry.fromWkt('Polygon((0 0, 1 0, 0 1, 1 1, 0 0))')) # invalid # check with added features layer.startEditing() self.assertTrue(layer.addFeatures([f4])) res = [f['x'] for f in layer.getFeatures(QgsFeatureRequest().setInvalidGeometryCheck(QgsFeatureRequest.GeometryNoCheck))] self.assertEqual(set(res), {'a', 'b', 'c', 'd'}) res = [f['x'] for f in layer.getFeatures(QgsFeatureRequest().setInvalidGeometryCheck(QgsFeatureRequest.GeometrySkipInvalid))] self.assertEqual(set(res), {'a', 'c'}) res = [f['x'] for f in layer.getFeatures(QgsFeatureRequest().setInvalidGeometryCheck(QgsFeatureRequest.GeometryAbortOnInvalid))] self.assertEqual(res, ['a']) # check with features with changed geometry layer.rollBack() layer.startEditing() layer.changeGeometry(2, QgsGeometry.fromWkt('Polygon((0 0, 1 0, 1 1, 0 1, 0 0))')) # valid layer.changeGeometry(3, QgsGeometry.fromWkt('Polygon((0 0, 1 0, 0 1, 1 1, 0 0))'))# invalid res = [f['x'] for f in layer.getFeatures(QgsFeatureRequest().setInvalidGeometryCheck(QgsFeatureRequest.GeometryNoCheck))] self.assertEqual(set(res), {'a', 'b', 'c'}) res = [f['x'] for f in layer.getFeatures(QgsFeatureRequest().setInvalidGeometryCheck(QgsFeatureRequest.GeometrySkipInvalid))] self.assertEqual(set(res), {'a', 'b'}) res = [f['x'] for f in layer.getFeatures(QgsFeatureRequest().setInvalidGeometryCheck(QgsFeatureRequest.GeometryAbortOnInvalid))] self.assertEqual(res, ['a', 'b']) layer.rollBack()
def testSaveLoadProject(self): schema_uri = encode_uri(self.ds_uri, 'qgis_test') project_uri = encode_uri(self.ds_uri, 'qgis_test', 'abc') self.dropProjectsTable() # make sure we have a clean start prj = QgsProject() uri = self.vl.source() vl1 = QgsVectorLayer(uri, 'test', 'postgres') self.assertEqual(vl1.isValid(), True) prj.addMapLayer(vl1) prj_storage = QgsApplication.projectStorageRegistry( ).projectStorageFromType("postgresql") self.assertTrue(prj_storage) lst0 = prj_storage.listProjects(schema_uri) self.assertEqual(lst0, []) # try to save project in the database prj.setFileName(project_uri) res = prj.write() self.assertTrue(res) lst1 = prj_storage.listProjects(schema_uri) self.assertEqual(lst1, ["abc"]) # now try to load the project back prj2 = QgsProject() prj2.setFileName(project_uri) res = prj2.read() self.assertTrue(res) self.assertEqual(len(prj2.mapLayers()), 1) self.assertEqual(prj2.baseName(), "abc") self.assertEqual(prj2.absoluteFilePath(), "") # path not supported for project storages self.assertTrue( abs(prj2.lastModified().secsTo(QDateTime.currentDateTime())) < 10) # try to see project's metadata res, metadata = prj_storage.readProjectStorageMetadata(project_uri) self.assertTrue(res) self.assertEqual(metadata.name, "abc") time_project = metadata.lastModified time_now = QDateTime.currentDateTime() time_diff = time_now.secsTo(time_project) self.assertTrue(abs(time_diff) < 10) # try to remove the project res = prj_storage.removeProject(project_uri) self.assertTrue(res) lst2 = prj_storage.listProjects(schema_uri) self.assertEqual(lst2, []) self.dropProjectsTable( ) # make sure we have a clean finish... "leave no trace"
def test_sql2(self): l2 = QgsVectorLayer(os.path.join(self.testDataDir, "france_parts.shp"), "france_parts", "ogr", QgsVectorLayer.LayerOptions(False)) self.assertEqual(l2.isValid(), True) QgsProject.instance().addMapLayer(l2) query = toPercent("SELECT * FROM france_parts") l4 = QgsVectorLayer("?query=%s&uid=ObjectId" % query, "tt", "virtual") self.assertEqual(l4.isValid(), True) self.assertEqual(l4.dataProvider().wkbType(), 6) self.assertEqual(l4.dataProvider().crs().postgisSrid(), 4326) n = 0 r = QgsFeatureRequest(QgsRectangle(-1.677, 49.624, -0.816, 49.086)) for f in l4.getFeatures(r): self.assertEqual(f.geometry() is not None, True) self.assertEqual(f.attributes()[0], 2661) n += 1 self.assertEqual(n, 1) # use uid query = toPercent("SELECT * FROM france_parts") l5 = QgsVectorLayer( "?query=%s&geometry=geometry:polygon:4326&uid=ObjectId" % query, "tt", "virtual") self.assertEqual(l5.isValid(), True) idSum = sum(f.id() for f in l5.getFeatures()) self.assertEqual(idSum, 10659) r = QgsFeatureRequest(2661) idSum2 = sum(f.id() for f in l5.getFeatures(r)) self.assertEqual(idSum2, 2661) r = QgsFeatureRequest() r.setFilterFids([2661, 2664]) self.assertEqual(sum(f.id() for f in l5.getFeatures(r)), 2661 + 2664) # test attribute subset r = QgsFeatureRequest() r.setFlags(QgsFeatureRequest.SubsetOfAttributes) r.setSubsetOfAttributes([1]) s = [(f.id(), f.attributes()[1]) for f in l5.getFeatures(r)] self.assertEqual(sum([x[0] for x in s]), 10659) self.assertEqual(sum([x[1] for x in s]), 3064.0) # test NoGeometry # by request flag r = QgsFeatureRequest() r.setFlags(QgsFeatureRequest.NoGeometry) self.assertEqual(all([not f.hasGeometry() for f in l5.getFeatures(r)]), True) # test subset self.assertEqual(l5.dataProvider().featureCount(), 4) l5.setSubsetString("ObjectId = 2661") idSum2 = sum(f.id() for f in l5.getFeatures(r)) self.assertEqual(idSum2, 2661) self.assertEqual(l5.dataProvider().featureCount(), 1)