def testPartNum(self): # test geometry_part_num variable s = QgsLineSymbol() s.deleteSymbolLayer(0) sym_layer = QgsGeometryGeneratorSymbolLayer.create({'geometryModifier': 'segments_to_lines($geometry)'}) sym_layer.setSymbolType(QgsSymbol.Line) s.appendSymbolLayer(sym_layer) marker_line = QgsMarkerLineSymbolLayer(False) marker_line.setPlacement(QgsMarkerLineSymbolLayer.FirstVertex) f = QgsFontUtils.getStandardTestFont('Bold', 24) marker = QgsFontMarkerSymbolLayer(f.family(), 'x', 24, QColor(255, 255, 0)) marker.setDataDefinedProperty(QgsSymbolLayer.PropertyCharacter, QgsProperty.fromExpression('@geometry_part_num')) marker_symbol = QgsMarkerSymbol() marker_symbol.changeSymbolLayer(0, marker) marker_line.setSubSymbol(marker_symbol) marker_line.setAverageAngleLength(0) line_symbol = QgsLineSymbol() line_symbol.changeSymbolLayer(0, marker_line) sym_layer.setSubSymbol(line_symbol) # rendering test g = QgsGeometry.fromWkt('LineString(0 0, 10 0, 10 10, 0 10)') rendered_image = self.renderGeometry(s, g, buffer=4) assert self.imageCheck('part_num_variable', 'part_num_variable', rendered_image) marker.setDataDefinedProperty(QgsSymbolLayer.PropertyCharacter, QgsProperty.fromExpression('@geometry_part_count')) # rendering test g = QgsGeometry.fromWkt('LineString(0 0, 10 0, 10 10, 0 10)') rendered_image = self.renderGeometry(s, g, buffer=4) assert self.imageCheck('part_count_variable', 'part_count_variable', rendered_image)
def testCreateLayerMultiPoint(self): layer = QgsVectorLayer("MultiPoint?crs=epsg:3111&field=id:integer&field=fldtxt:string&field=fldint:integer", "addfeat", "memory") pr = layer.dataProvider() f = QgsFeature() f.setAttributes([1, "test", 1]) f.setGeometry(QgsGeometry.fromWkt('MultiPoint(1 2, 3 4)')) f2 = QgsFeature() f2.setAttributes([2, "test2", 3]) f3 = QgsFeature() f3.setAttributes([3, "test2", NULL]) f3.setGeometry(QgsGeometry.fromWkt('MultiPoint(7 8)')) pr.addFeatures([f, f2, f3]) uri = '{} table="qgis_test"."new_table_multipoint" sql='.format(self.dbconn) error, message = QgsVectorLayerExporter.exportLayer(layer, uri, 'mssql', QgsCoordinateReferenceSystem('EPSG:3111')) self.assertEqual(error, QgsVectorLayerExporter.NoError) new_layer = QgsVectorLayer(uri, 'new', 'mssql') self.assertTrue(new_layer.isValid()) self.assertEqual(new_layer.wkbType(), QgsWkbTypes.MultiPoint) self.assertEqual(new_layer.crs().authid(), 'EPSG:3111') self.assertEqual([f.name() for f in new_layer.fields()], ['qgs_fid', 'id', 'fldtxt', 'fldint']) features = [f.attributes() for f in new_layer.getFeatures()] self.assertEqual(features, [[1, 1, 'test', 1], [2, 2, 'test2', 3], [3, 3, 'test2', NULL]]) geom = [f.geometry().asWkt() for f in new_layer.getFeatures()] self.assertEqual(geom, ['MultiPoint ((1 2),(3 4))', '', 'MultiPoint ((7 8))'])
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() QgsProject.instance().addMapLayer(l) QgsProject.instance().writeEntry("Digitizing", "/AvoidIntersectionsList", [l.id()]) 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.pendingFeatureCount() == 7 # create a geometry and remove its intersections with other geometries g = QgsGeometry.fromWkt(newg_wkt) assert g.avoidIntersections() == 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 createLayer(cls): vl = QgsVectorLayer( "Point?crs=epsg:4326&field=pk:integer&field=cnt:integer&field=name:string(0)&field=name2:string(0)&field=num_char:string&key=pk", "test", "memory", ) assert vl.isValid() f1 = QgsFeature() f1.setAttributes([5, -200, NULL, "NuLl", "5"]) f1.setGeometry(QgsGeometry.fromWkt("Point (-71.123 78.23)")) f2 = QgsFeature() f2.setAttributes([3, 300, "Pear", "PEaR", "3"]) f3 = QgsFeature() f3.setAttributes([1, 100, "Orange", "oranGe", "1"]) f3.setGeometry(QgsGeometry.fromWkt("Point (-70.332 66.33)")) f4 = QgsFeature() f4.setAttributes([2, 200, "Apple", "Apple", "2"]) f4.setGeometry(QgsGeometry.fromWkt("Point (-68.2 70.8)")) f5 = QgsFeature() f5.setAttributes([4, 400, "Honey", "Honey", "4"]) f5.setGeometry(QgsGeometry.fromWkt("Point (-65.32 78.3)")) vl.dataProvider().addFeatures([f1, f2, f3, f4, f5]) return vl
def test_value_at_of_two_triangles(self): def test_cross_section(geometry, o, expect, msg): x,y = crayfish.plot.cross_section_plot_data(o, geometry) for xi, yi in zip (x, y): self.assertEqual(str(yi) != "nan", expect, "{} Point {} is {}, expected {}".format(msg, xi, yi, expect)) m = crayfish.Mesh(TEST_DIR + "/2triangle.2dm") m.load_data(TEST_DIR + "/2triangle_ascii_els_depth.dat") self.assertEqual(m.dataset_count(), 2) ds = m.dataset(1) self.assertEqual(ds.type(), crayfish.DS_Scalar) self.assertEqual(ds.output_count(), 2) o = ds.output(1) # geometry on the border of the 2 triagles geometry = QgsGeometry.fromWkt("LINESTRING (1050 2000, 2000 950)") test_cross_section(geometry, o, True, "border") # other diagonal geometry = QgsGeometry.fromWkt("LINESTRING (1000 1000, 1950 2050)") test_cross_section(geometry, o, True, "diag") # outside geometry = QgsGeometry.fromWkt("LINESTRING (975 1000, 950 1925)") test_cross_section(geometry, o, False, "outside")
def currentHistoryChanged(self, current, previous): self.new_geometry.reset() self.old_geometry.reset() self.tblChanges.model().removeRows() if not current.isValid(): return item = current.data(Qt.UserRole) if item is None: data = {} else: data = current.data(Qt.UserRole).getDetails() with SetLocale_CtxDec(): extent = None if data.get('new_geometry'): wkt = CreateGeometryFromJson( json.dumps(data['new_geometry']) ).ExportToWkt() geom = QgsGeometry.fromWkt( wkt ) l = QgsVectorLayer('Point?crs=epsg:4326', 'asd', 'memory') self.new_geometry.setToGeometry( geom, l ) extent = QgsRectangle(geom.boundingBox()) if data.get('old_geometry'): wkt = CreateGeometryFromJson( json.dumps(data['old_geometry']) ).ExportToWkt() geom = QgsGeometry.fromWkt( wkt ) l = QgsVectorLayer('Point?crs=epsg:4326', 'asd', 'memory') self.old_geometry.setToGeometry( geom, l ) if extent is None: extent = QgsRectangle(geom.boundingBox()) else: extent.combineExtentWith( geom.boundingBox() ) if extent is not None: extent.grow(0.01) self.mapCanvas.setExtent( extent ) self.mapCanvas.refresh() if data.get('what_attributes', []): self.tblChanges.model().insertRows( 0, data.get('what_attributes', []) )
def testGeometry(self): """ Test calculation of aggregates on geometry expressions """ layer = QgsVectorLayer("Point?", "layer", "memory") pr = layer.dataProvider() # must be same length: geometry_values = [ QgsGeometry.fromWkt("Point ( 0 0 )"), QgsGeometry.fromWkt("Point ( 1 1 )"), QgsGeometry.fromWkt("Point ( 2 2 )"), ] features = [] for i in range(len(geometry_values)): f = QgsFeature() f.setGeometry(geometry_values[i]) features.append(f) self.assertTrue(pr.addFeatures(features)) agg = QgsAggregateCalculator(layer) val, ok = agg.calculate(QgsAggregateCalculator.GeometryCollect, "$geometry") self.assertTrue(ok) expwkt = "MultiPoint ((0 0), (1 1), (2 2))" wkt = val.exportToWkt() self.assertTrue(compareWkt(expwkt, wkt), "Expected:\n%s\nGot:\n%s\n" % (expwkt, wkt))
def testInsertVertex(self): linestring = QgsGeometry.fromWkt( "LINESTRING(1 0,2 0)" ) if TestQgsGeometry.wkbPtr: # CHANGE old implementation fails to insert vertex assert linestring.insertVertex( 0, 0, 0 ), "Insert vertex 0 0 at 0 failed" expwkt = "LINESTRING(0 0, 1 0, 2 0)" wkt = linestring.exportToWkt() assert compareWkt( expwkt, wkt ), "Expected:\n%s\nGot:\n%s\n" % (expwkt, wkt ) assert linestring.insertVertex( 1.5, 0, 2 if TestQgsGeometry.wkbPtr else 1 ), "Insert vertex 1.5 0 at 2 failed" expwkt = "LINESTRING(0 0, 1 0, 1.5 0, 2 0)" if TestQgsGeometry.wkbPtr else "LINESTRING(1 0, 1.5 0, 2 0)" wkt = linestring.exportToWkt() assert compareWkt( expwkt, wkt ), "Expected:\n%s\nGot:\n%s\n" % (expwkt, wkt ) assert not linestring.insertVertex( 3, 0, 5 ), "Insert vertex 3 0 at 5 should have failed" polygon = QgsGeometry.fromWkt( "MULTIPOLYGON(((0 0,1 0,1 1,2 1,2 2,0 2,0 0)),((4 0,5 0,5 2,3 2,3 1,4 1,4 0)))" ) assert polygon.insertVertex( 0, 0, 8 ), "Insert vertex 0 0 at 8 failed" expwkt = "MULTIPOLYGON(((0 0,1 0,1 1,2 1,2 2,0 2,0 0)),((4 0,0 0,5 0,5 2,3 2,3 1,4 1,4 0)))" wkt = polygon.exportToWkt() assert compareWkt( expwkt, wkt ), "Expected:\n%s\nGot:\n%s\n" % (expwkt, wkt ) polygon = QgsGeometry.fromWkt( "MULTIPOLYGON(((0 0,1 0,1 1,2 1,2 2,0 2,0 0)),((4 0,5 0,5 2,3 2,3 1,4 1,4 0)))" ) assert polygon.insertVertex( 0, 0, 7 ), "Insert vertex 0 0 at 7 failed" expwkt = "MULTIPOLYGON(((0 0,1 0,1 1,2 1,2 2,0 2,0 0)),((0 0,4 0,5 0,5 2,3 2,3 1,4 1,0 0)))" wkt = polygon.exportToWkt() if TestQgsGeometry.wkbPtr: # CHANGE old implementation produces: MULTIPOLYGON(((0 0,1 0,1 1,2 1,2 2,0 2,0 0)),()) assert compareWkt( expwkt, wkt ), "Expected:\n%s\nGot:\n%s\n" % (expwkt, wkt )
def testHashPlacement(self): s = QgsLineSymbol() s.deleteSymbolLayer(0) hash_line = QgsHashedLineSymbolLayer(True) hash_line.setPlacement(QgsTemplatedLineSymbolLayerBase.Vertex) hash_line.setInterval(6) simple_line = QgsSimpleLineSymbolLayer() simple_line.setColor(QColor(0, 255, 0)) simple_line.setWidth(1) line_symbol = QgsLineSymbol() line_symbol.changeSymbolLayer(0, simple_line) hash_line.setSubSymbol(line_symbol) hash_line.setHashLength(7) hash_line.setAverageAngleLength(0) s.appendSymbolLayer(hash_line.clone()) g = QgsGeometry.fromWkt('LineString(0 0, 10 10, 10 0)') rendered_image = self.renderGeometry(s, g) assert self.imageCheck('line_hash_vertex', 'line_hash_vertex', rendered_image) s.symbolLayer(0).setPlacement(QgsTemplatedLineSymbolLayerBase.FirstVertex) g = QgsGeometry.fromWkt('LineString(0 0, 10 10, 10 0)') rendered_image = self.renderGeometry(s, g) assert self.imageCheck('line_hash_first', 'line_hash_first', rendered_image) s.symbolLayer(0).setPlacement(QgsTemplatedLineSymbolLayerBase.LastVertex) g = QgsGeometry.fromWkt('LineString(0 0, 10 10, 10 0)') rendered_image = self.renderGeometry(s, g) assert self.imageCheck('line_hash_last', 'line_hash_last', rendered_image)
def setUpClass(cls): """Run before all tests""" # Create test layer cls.vl = QgsVectorLayer(u'Point?crs=epsg:4326&field=pk:integer&field=cnt:integer&field=name:string(0)&field=name2:string(0)&key=pk', u'test', u'memory') assert (cls.vl.isValid()) cls.provider = cls.vl.dataProvider() f1 = QgsFeature() f1.setAttributes([5, -200, NULL, 'NuLl']) f1.setGeometry(QgsGeometry.fromWkt('Point (-71.123 78.23)')) f2 = QgsFeature() f2.setAttributes([3, 300, 'Pear', 'PEaR']) f3 = QgsFeature() f3.setAttributes([1, 100, 'Orange', 'oranGe']) f3.setGeometry(QgsGeometry.fromWkt('Point (-70.332 66.33)')) f4 = QgsFeature() f4.setAttributes([2, 200, 'Apple', 'Apple']) f4.setGeometry(QgsGeometry.fromWkt('Point (-68.2 70.8)')) f5 = QgsFeature() f5.setAttributes([4, 400, 'Honey', 'Honey']) f5.setGeometry(QgsGeometry.fromWkt('Point (-65.32 78.3)')) cls.provider.addFeatures([f1, f2, f3, f4, f5])
def testLineOffset(self): s = QgsLineSymbol() s.deleteSymbolLayer(0) hash_line = QgsHashedLineSymbolLayer(True) hash_line.setPlacement(QgsTemplatedLineSymbolLayerBase.Interval) hash_line.setInterval(6) simple_line = QgsSimpleLineSymbolLayer() simple_line.setColor(QColor(0, 255, 0)) simple_line.setWidth(1) line_symbol = QgsLineSymbol() line_symbol.changeSymbolLayer(0, simple_line) hash_line.setSubSymbol(line_symbol) hash_line.setHashLength(10) hash_line.setAverageAngleLength(0) s.appendSymbolLayer(hash_line.clone()) s.symbolLayer(0).setOffset(3) g = QgsGeometry.fromWkt('LineString(0 0, 10 10, 10 0)') rendered_image = self.renderGeometry(s, g) assert self.imageCheck('line_offset_positive', 'line_offset_positive', rendered_image) s.symbolLayer(0).setOffset(-3) g = QgsGeometry.fromWkt('LineString(0 0, 10 10, 10 0)') rendered_image = self.renderGeometry(s, g) assert self.imageCheck('line_offset_negative', 'line_offset_negative', rendered_image)
def test_snappointstogrid(self): """Check that this runs correctly""" polygon_layer = self._make_layer('Polygon') f1 = QgsFeature(polygon_layer.fields()) f1.setAttributes([1]) f1.setGeometry(QgsGeometry.fromWkt('POLYGON((1.2 1.2, 1.2 2.2, 2.2 2.2, 2.2 1.2, 1.2 1.2))')) f2 = QgsFeature(polygon_layer.fields()) f2.setAttributes([2]) f2.setGeometry(QgsGeometry.fromWkt('POLYGON((1.1 1.1, 1.1 2.1, 2.1 2.1, 2.1 1.1, 1.1 1.1))')) self.assertTrue(f2.isValid()) self.assertTrue(polygon_layer.startEditing()) self.assertTrue(polygon_layer.addFeatures([f1, f2])) self.assertEqual(polygon_layer.featureCount(), 2) polygon_layer.commitChanges() self.assertEqual(polygon_layer.featureCount(), 2) QgsProject.instance().addMapLayers([polygon_layer]) polygon_layer.selectByIds([next(polygon_layer.getFeatures()).id()]) self.assertEqual(polygon_layer.selectedFeatureCount(), 1) old_features, new_features = self._alg_tester( 'native:snappointstogrid', polygon_layer, { 'HSPACING': 0.5, 'VSPACING': 0.5, } ) g = [f.geometry() for f in new_features][0] self.assertEqual(g.asWkt(), 'Polygon ((1 1, 1 2, 2 2, 2 1, 1 1))') # Check selected self.assertEqual(polygon_layer.selectedFeatureIds(), [1])
def setUpClass(cls): """Run before all tests""" # Create test layer for FeatureSourceTestCase cls.vl = QgsVectorLayer( 'Point?crs=epsg:4326&field=pk:integer&field=cnt:integer&field=name:string(0)&field=name2:string(0)&field=num_char:string&key=pk', 'test', 'memory') assert (cls.vl.isValid()) f1 = QgsFeature(5) f1.setAttributes([5, -200, NULL, 'NuLl', '5']) f1.setGeometry(QgsGeometry.fromWkt('Point (-71.123 78.23)')) f2 = QgsFeature(3) f2.setAttributes([3, 300, 'Pear', 'PEaR', '3']) f3 = QgsFeature(1) f3.setAttributes([1, 100, 'Orange', 'oranGe', '1']) f3.setGeometry(QgsGeometry.fromWkt('Point (-70.332 66.33)')) f4 = QgsFeature(2) f4.setAttributes([2, 200, 'Apple', 'Apple', '2']) f4.setGeometry(QgsGeometry.fromWkt('Point (-68.2 70.8)')) f5 = QgsFeature(4) f5.setAttributes([4, 400, 'Honey', 'Honey', '4']) f5.setGeometry(QgsGeometry.fromWkt('Point (-65.32 78.3)')) assert cls.vl.dataProvider().addFeatures([f1, f2, f3, f4, f5]) cls.source = QgsVectorLayerCache(cls.vl, 100)
def test_check_validity(self): """Test that the output invalid contains the error reason""" polygon_layer = self._make_layer('Polygon') self.assertTrue(polygon_layer.startEditing()) f = QgsFeature(polygon_layer.fields()) f.setAttributes([1]) # Flake! f.setGeometry(QgsGeometry.fromWkt( 'POLYGON ((0 0, 2 2, 0 2, 2 0, 0 0))')) self.assertTrue(f.isValid()) f2 = QgsFeature(polygon_layer.fields()) f2.setAttributes([1]) f2.setGeometry(QgsGeometry.fromWkt( 'POLYGON((1.1 1.1, 1.1 2.1, 2.1 2.1, 2.1 1.1, 1.1 1.1))')) self.assertTrue(f2.isValid()) self.assertTrue(polygon_layer.addFeatures([f, f2])) polygon_layer.commitChanges() polygon_layer.rollBack() self.assertEqual(polygon_layer.featureCount(), 2) QgsProject.instance().addMapLayers([polygon_layer]) alg = self.registry.createAlgorithmById('qgis:checkvalidity') context = QgsProcessingContext() context.setProject(QgsProject.instance()) feedback = ConsoleFeedBack() self.assertIsNotNone(alg) parameters = {} parameters['INPUT_LAYER'] = polygon_layer.id() parameters['VALID_OUTPUT'] = 'memory:' parameters['INVALID_OUTPUT'] = 'memory:' parameters['ERROR_OUTPUT'] = 'memory:' # QGIS method parameters['METHOD'] = 1 ok, results = execute( alg, parameters, context=context, feedback=feedback) self.assertTrue(ok) invalid_layer = QgsProcessingUtils.mapLayerFromString( results['INVALID_OUTPUT'], context) self.assertEqual(invalid_layer.fields().names()[-1], '_errors') self.assertEqual(invalid_layer.featureCount(), 1) f = next(invalid_layer.getFeatures()) self.assertEqual(f.attributes(), [ 1, 'segments 0 and 2 of line 0 intersect at 1, 1']) # GEOS method parameters['METHOD'] = 2 ok, results = execute( alg, parameters, context=context, feedback=feedback) self.assertTrue(ok) invalid_layer = QgsProcessingUtils.mapLayerFromString( results['INVALID_OUTPUT'], context) self.assertEqual(invalid_layer.fields().names()[-1], '_errors') self.assertEqual(invalid_layer.featureCount(), 1) f = next(invalid_layer.getFeatures()) self.assertEqual(f.attributes(), [1, 'Self-intersection'])
def createLayer(cls): vl = QgsVectorLayer( 'Point?crs=epsg:4326&field=pk:integer&field=cnt:integer&field=name:string(0)&field=name2:string(0)&field=num_char:string&key=pk', 'test', 'pythonprovider') assert (vl.isValid()) f1 = QgsFeature() f1.setAttributes([5, -200, NULL, 'NuLl', '5']) f1.setGeometry(QgsGeometry.fromWkt('Point (-71.123 78.23)')) f2 = QgsFeature() f2.setAttributes([3, 300, 'Pear', 'PEaR', '3']) f3 = QgsFeature() f3.setAttributes([1, 100, 'Orange', 'oranGe', '1']) f3.setGeometry(QgsGeometry.fromWkt('Point (-70.332 66.33)')) f4 = QgsFeature() f4.setAttributes([2, 200, 'Apple', 'Apple', '2']) f4.setGeometry(QgsGeometry.fromWkt('Point (-68.2 70.8)')) f5 = QgsFeature() f5.setAttributes([4, 400, 'Honey', 'Honey', '4']) f5.setGeometry(QgsGeometry.fromWkt('Point (-65.32 78.3)')) vl.dataProvider().addFeatures([f1, f2, f3, f4, f5]) return vl
def getSource(self): # create temporary table for edit tests self.execSQLCommand('DROP TABLE IF EXISTS qgis_test.edit_data') self.execSQLCommand( """CREATE TABLE qgis_test.edit_data (pk INTEGER PRIMARY KEY,cnt integer, name nvarchar(max), name2 nvarchar(max), num_char nvarchar(max), geom geometry)""") vl = QgsVectorLayer( self.dbconn + ' sslmode=disable key=\'pk\' srid=4326 type=POINT table="qgis_test"."edit_data" (geom) sql=', 'test', 'mssql') self.assertTrue(vl.isValid(), vl.dataProvider().error().message()) f1 = QgsFeature() f1.setAttributes([5, -200, NULL, 'NuLl', '5']) f1.setGeometry(QgsGeometry.fromWkt('Point (-71.123 78.23)')) f2 = QgsFeature() f2.setAttributes([3, 300, 'Pear', 'PEaR', '3']) f3 = QgsFeature() f3.setAttributes([1, 100, 'Orange', 'oranGe', '1']) f3.setGeometry(QgsGeometry.fromWkt('Point (-70.332 66.33)')) f4 = QgsFeature() f4.setAttributes([2, 200, 'Apple', 'Apple', '2']) f4.setGeometry(QgsGeometry.fromWkt('Point (-68.2 70.8)')) f5 = QgsFeature() f5.setAttributes([4, 400, 'Honey', 'Honey', '4']) f5.setGeometry(QgsGeometry.fromWkt('Point (-65.32 78.3)')) self.assertTrue(vl.dataProvider().addFeatures([f1, f2, f3, f4, f5])) return vl
def setUpClass(cls): """Run before all tests""" # Register the provider r = QgsProviderRegistry.instance() metadata = QgsProviderMetadata(PyProvider.providerKey(), PyProvider.description(), PyProvider.createProvider) assert r.registerProvider(metadata) assert r.providerMetadata(PyProvider.providerKey()) == metadata # Create test layer cls.vl = cls.createLayer() assert (cls.vl.isValid()) cls.source = cls.vl.dataProvider() # poly layer cls.poly_vl = QgsVectorLayer('Polygon?crs=epsg:4326&field=pk:integer&key=pk', 'test', 'pythonprovider') assert (cls.poly_vl.isValid()) cls.poly_provider = cls.poly_vl.dataProvider() f1 = QgsFeature() f1.setAttributes([1]) f1.setGeometry(QgsGeometry.fromWkt('Polygon ((-69.03664108 81.35818902, -69.09237722 80.24346619, -73.718477 80.1319939, -73.718477 76.28620011, -74.88893598 76.34193625, -74.83319983 81.35818902, -69.03664108 81.35818902))')) f2 = QgsFeature() f2.setAttributes([2]) f2.setGeometry(QgsGeometry.fromWkt('Polygon ((-67.58750139 81.1909806, -66.30557012 81.24671674, -66.30557012 76.89929767, -67.58750139 76.89929767, -67.58750139 81.1909806))')) f3 = QgsFeature() f3.setAttributes([3]) f3.setGeometry(QgsGeometry.fromWkt('Polygon ((-68.36780737 75.78457483, -67.53176524 72.60761475, -68.64648808 73.66660144, -70.20710006 72.9420316, -68.36780737 75.78457483))')) f4 = QgsFeature() f4.setAttributes([4]) cls.poly_provider.addFeatures([f1, f2, f3, f4])
def testChangeGeometries(self): if not getattr(self, 'getEditableLayer', None): return l = self.getEditableLayer() self.assertTrue(l.isValid()) # find 2 features to change features = [f for f in l.dataProvider().getFeatures()] to_change = [f for f in features if f.attributes()[0] == 1] to_change.extend([f for f in features if f.attributes()[0] == 3]) # changes by feature id, for changeGeometryValues call changes = {to_change[0].id(): QgsGeometry.fromWkt('Point (10 20)'), to_change[1].id(): QgsGeometry()} # changes by pk, for testing after retrieving changed features new_geom_map = {1: QgsGeometry.fromWkt('Point ( 10 20 )'), 3: QgsGeometry()} if l.dataProvider().capabilities() & QgsVectorDataProvider.ChangeGeometries: # expect success result = l.dataProvider().changeGeometryValues(changes) self.assertTrue(result, 'Provider reported ChangeGeometries capability, but returned False to changeGeometryValues') # check result self.testGetFeatures(l.dataProvider(), changed_geometries=new_geom_map) # change empty list, should return true for consistency self.assertTrue(l.dataProvider().changeGeometryValues({})) else: # expect fail self.assertFalse(l.dataProvider().changeGeometryValues(changes), 'Provider reported no ChangeGeometries capability, but returned true to changeGeometryValues')
def test_should_not_match_perpendicular_segments(self): self.a.setGeometry(QgsGeometry.fromWkt("LINESTRING(0 0, 5 105)")) self.b.setGeometry(QgsGeometry.fromWkt("LINESTRING(0 50, 105 55)")) smf = SegmentMatchFinder(self.features, segmentize=5) matches = smf.findmatching(self.b, maxdistance=10) self.assertEqual(len(matches), 0)
def test_should_match_parallel_segments(self): self.a.setGeometry(QgsGeometry.fromWkt("LINESTRING(0 0, 100 20)")) self.b.setGeometry(QgsGeometry.fromWkt("LINESTRING(5 0, 105 20)")) smf = SegmentMatchFinder(self.features, segmentize=5) matches = len(smf.findmatching(self.a, maxdistance=10)) self.assertEqual(matches, 1)
def test_fix_geometries(self): polygon_layer = self._make_layer('Polygon') self.assertTrue(polygon_layer.startEditing()) f1 = QgsFeature(polygon_layer.fields()) f1.setAttributes([1]) # Flake! f1.setGeometry(QgsGeometry.fromWkt('POLYGON ((0 0, 2 2, 0 2, 2 0, 0 0))')) self.assertTrue(f1.isValid()) f2 = QgsFeature(polygon_layer.fields()) f2.setAttributes([1]) f2.setGeometry(QgsGeometry.fromWkt('POLYGON((1.1 1.1, 1.1 2.1, 2.1 2.1, 2.1 1.1, 1.1 1.1))')) self.assertTrue(f2.isValid()) self.assertTrue(polygon_layer.addFeatures([f1, f2])) polygon_layer.commitChanges() polygon_layer.rollBack() self.assertEqual(polygon_layer.featureCount(), 2) QgsProject.instance().addMapLayers([polygon_layer]) old_features, new_features = self._alg_tester( 'native:fixgeometries', polygon_layer, { }, QgsFeatureRequest.GeometrySkipInvalid ) self.assertEqual(polygon_layer.featureCount(), 3) wkt1, wkt2, wkt3 = [f.geometry().asWkt() for f in new_features] self.assertEqual(wkt1, 'Polygon ((0 0, 1 1, 2 0, 0 0))') self.assertEqual(wkt2, 'Polygon ((1 1, 0 2, 2 2, 1 1))') self.assertEqual(re.sub(r'0000\d+', '', wkt3), 'Polygon ((1.1 1.1, 1.1 2.1, 2.1 2.1, 2.1 1.1, 1.1 1.1))') # Test with Z (interpolated) polygonz_layer = self._make_layer('PolygonZ') self.assertTrue(polygonz_layer.startEditing()) f3 = QgsFeature(polygonz_layer.fields()) f3.setAttributes([1]) f3.setGeometry(QgsGeometry.fromWkt('POLYGON Z((0 0 1, 2 2 1, 0 2 3, 2 0 4, 0 0 1))')) self.assertTrue(f3.isValid()) self.assertTrue(polygonz_layer.addFeatures([f3])) polygonz_layer.commitChanges() polygonz_layer.rollBack() self.assertEqual(polygonz_layer.featureCount(), 1) QgsProject.instance().addMapLayers([polygonz_layer]) old_features, new_features = self._alg_tester( 'native:fixgeometries', polygonz_layer, { } ) self.assertEqual(polygonz_layer.featureCount(), 2) wkt1, wkt2 = [f.geometry().asWkt() for f in new_features] self.assertEqual(wkt1, 'PolygonZ ((0 0 1, 1 1 2.25, 2 0 4, 0 0 1))') self.assertEqual(wkt2, 'PolygonZ ((1 1 2.25, 0 2 3, 2 2 1, 1 1 2.25))')
def showFeatureAttributes(self): conflictItem = self.lastSelectedItem self.oursgeom = None self.theirsgeom = None geoms = (self.oursgeom, self.theirsgeom) self.currentConflictedAttributes = [] attribs = list(conflictItem.origin.keys()) self.attributesTable.setRowCount(len(attribs)) self.conflicted = [] for idx, name in enumerate(attribs): font = QFont() font.setBold(True) font.setWeight(75) item = QTableWidgetItem(name) item.setFont(font) self.attributesTable.setItem(idx, 3, item); self.attributesTable.setItem(idx, 4, ValueItem(None, False)); try: values = (conflictItem.origin[name], conflictItem.local[name], conflictItem.remote[name]) except Exception: #Local has been deleted self._afterSolve(False) self.solveModifyAndDelete(conflictItem.conflict.path, self.REMOTE) return except TypeError: #Remote has been deleted self._afterSolve(False) self.solveModifyAndDelete(conflictItem.conflict.path,self.LOCAL) return try: geom = QgsGeometry.fromWkt(values[0]) except: geom = None if geom is not None: self.theirsgeom = QgsGeometry().fromWkt(values[1]) self.oursgeom = QgsGeometry.fromWkt(values[2]) geoms = (self.oursgeom, self.theirsgeom) ok = values[0] == values[1] or values[1] == values[2] or values[0] == values[2] for i, v in enumerate(values): self.attributesTable.setItem(idx, i, ValueItem(v, not ok, geoms)); if not ok: self.conflicted.append(name) else: if values[0] == values[1]: newvalue = values[2] else: newvalue = values[1] self.attributesTable.setItem(idx, 4, ValueItem(newvalue, False, geoms)); self.attributesTable.resizeRowsToContents() self.attributesTable.horizontalHeader().setMinimumSectionSize(150) self.attributesTable.horizontalHeader().setStretchLastSection(True)
def test_clip_geometry(self): """Test that we can clip a geometry using another geometry.""" geometry = QgsGeometry.fromPolyline([ QgsPoint(10, 10), QgsPoint(20, 20), QgsPoint(30, 30), QgsPoint(40, 40)] ) clip_polygon = QgsGeometry.fromPolygon([ [QgsPoint(20, 20), QgsPoint(20, 30), QgsPoint(30, 30), QgsPoint(30, 20), QgsPoint(20, 20)]] ) result = clip_geometry(clip_polygon, geometry) expected_wkt = 'LINESTRING(20.0 20.0, 30.0 30.0)' # There should only be one feature that intersects this clip # poly so this assertion should work. assert compare_wkt(expected_wkt, str(result.exportToWkt())) # Now poly on poly clip test clip_polygon = QgsGeometry.fromWkt( 'POLYGON((106.8218 -6.1842,106.8232 -6.1842,' '106.82304963 -6.18317148,106.82179736 -6.18314774,' '106.8218 -6.1842))') geometry = QgsGeometry.fromWkt( 'POLYGON((106.8216869 -6.1852067,106.8213233 -6.1843051,' '106.8212891 -6.1835559,106.8222566 -6.1835184,' '106.8227557 -6.1835076,106.8228588 -6.1851204,' '106.8216869 -6.1852067))') result = clip_geometry(clip_polygon, geometry) expected_wkt = ( 'POLYGON((106.82179833 -6.18353616,106.8222566 -6.1835184,' '106.8227557 -6.1835076,106.82279996 -6.1842,' '106.8218 -6.1842,106.82179833 -6.18353616))') # There should only be one feature that intersects this clip # poly so this assertion should work. assert compare_wkt(expected_wkt, str(result.exportToWkt())) # Now point on poly test clip geometry = QgsGeometry.fromWkt('POINT(106.82241 -6.18369)') result = clip_geometry(clip_polygon, geometry) if qgis_version() > 10800: expected_wkt = 'POINT(106.82241 -6.18369)' else: expected_wkt = 'POINT(106.822410 -6.183690)' # There should only be one feature that intersects this clip # poly so this assertion should work. assert compare_wkt(expected_wkt, str(result.exportToWkt()))
def treeItemClicked(self, item): if item.childCount(): return color = {"MODIFIED": QColor(255, 170, 0), "ADDED":Qt.green, "REMOVED":Qt.red , "NO_CHANGE":Qt.white} changeTypeName = ["", "ADDED", "MODIFIED", "REMOVED"] path = item.text(0) if path not in self.changes: return oldfeature = self.changes[path].oldfeature newfeature = self.changes[path].newfeature changetype = self.changes[path].changetype self.attributesTable.clear() self.attributesTable.verticalHeader().show() self.attributesTable.horizontalHeader().show() self.attributesTable.setRowCount(len(newfeature)) self.attributesTable.setVerticalHeaderLabels([a for a in newfeature]) self.attributesTable.setHorizontalHeaderLabels(["Old value", "New value", "Change type"]) for i, attrib in enumerate(newfeature): self.attributesTable.setItem(i, 0, DiffItem(oldfeature.get(attrib, None))) self.attributesTable.setItem(i, 1, DiffItem(newfeature.get(attrib, None))) attribChangeType = changeTypeName[changetype] isChangedGeom = False if changetype == LOCAL_FEATURE_MODIFIED: oldvalue = oldfeature.get(attrib, None) newvalue = newfeature.get(attrib, None) try:# to avoid false change detection due to different precisions oldvalue = QgsGeometry.fromWkt(oldvalue).exportToWkt(7) newvalue = QgsGeometry.fromWkt(newvalue).exportToWkt(7) if oldvalue != newvalue and None not in [oldvalue, newvalue]: widget = QWidget() btn = QPushButton() btn.setText("View detail") g1 = QgsGeometry.fromWkt(oldvalue) g2 = QgsGeometry.fromWkt(newvalue) btn.clicked.connect(lambda: self.viewGeometryChanges(g1, g2)) label = QLabel() label.setText(attribChangeType) layout = QHBoxLayout(widget) layout.addWidget(label); layout.addWidget(btn); layout.setContentsMargins(0, 0, 0, 0) widget.setLayout(layout) self.attributesTable.setItem(i, 2, QTableWidgetItem("")) self.attributesTable.setCellWidget(i, 2, widget) isChangedGeom = True except: pass if oldvalue == newvalue: attribChangeType = "NO_CHANGE" if not isChangedGeom: self.attributesTable.setItem(i, 2, QTableWidgetItem(attribChangeType)) for col in range(3): self.attributesTable.item(i, col).setBackgroundColor(color[attribChangeType]); self.attributesTable.resizeColumnsToContents() self.attributesTable.horizontalHeader().setResizeMode(QHeaderView.Stretch)
def test_should_not_match_perpendicular_segments2(self): self.a.setGeometry(QgsGeometry.fromWkt( "LineStringZ (569592.43999999994412065 6290849.37000000011175871 3, 569591.93000000005122274 6290842.05999999959021807 3, 569587.55000000004656613 6290824.67999999970197678 3, 569577.81999999994877726 6290786.66000000014901161 3.60000000000000009, 569572.72999999998137355 6290765.58000000007450581 3.60000000000000009, 569564.66000000003259629 6290732.62999999988824129 3.79999999999999982, 569557.71999999997206032 6290703.67999999970197678 3.85000000000000009, 569554.53000000002793968 6290691.41999999992549419 3.97999999999999998, 569550.83999999996740371 6290675.74000000022351742 3.85000000000000009, 569541.0400000000372529 6290634.58000000007450581 4.5)" )) self.b.setGeometry(QgsGeometry.fromWkt("LineStringZ (569604.7900000000372529 6290683.5 4.37999999999999989, 569590.43999999994412065 6290686.29999999981373549 4.30999999999999961, 569570.86999999999534339 6290689.01999999955296516 4.28000000000000025, 569554.53000000002793968 6290691.41999999992549419 3.97999999999999998)")) smf = SegmentMatchFinder(self.features, segmentize=5) matches = len(smf.findmatching(self.b, maxdistance=10)) self.assertEqual(matches, 0)
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_difference_on_invalid_geometries(self, geom_option): polygon_layer = self._make_layer('Polygon') self.assertTrue(polygon_layer.startEditing()) f = QgsFeature(polygon_layer.fields()) f.setAttributes([1]) # Flake! f.setGeometry(QgsGeometry.fromWkt('Polygon ((0 0, 2 2, 0 2, 2 0, 0 0))')) self.assertTrue(f.isValid()) self.assertTrue(polygon_layer.addFeatures([f])) polygon_layer.commitChanges() polygon_layer.rollBack() self.assertEqual(polygon_layer.featureCount(), 1) overlay_layer = self._make_layer('Polygon') self.assertTrue(overlay_layer.startEditing()) f = QgsFeature(overlay_layer.fields()) f.setAttributes([1]) f.setGeometry(QgsGeometry.fromWkt('Polygon ((0 0, 2 0, 2 2, 0 2, 0 0))')) self.assertTrue(f.isValid()) self.assertTrue(overlay_layer.addFeatures([f])) overlay_layer.commitChanges() overlay_layer.rollBack() self.assertEqual(overlay_layer.featureCount(), 1) QgsProject.instance().addMapLayers([polygon_layer, overlay_layer]) old_features = [f for f in polygon_layer.getFeatures()] # 'Ignore features with invalid geometries' = 1 ProcessingConfig.setSettingValue(ProcessingConfig.FILTER_INVALID_GEOMETRIES, geom_option) feedback = ConsoleFeedBack() context = dataobjects.createContext(feedback) context.setProject(QgsProject.instance()) alg = self.registry.createAlgorithmById('native:difference') self.assertIsNotNone(alg) parameters = { 'OVERLAY': overlay_layer, 'INPUT': polygon_layer, 'OUTPUT': ':memory', } old_features = [f for f in polygon_layer.getFeatures()] self.assertTrue(polygon_layer.startEditing()) polygon_layer.selectAll() ok, _ = execute_in_place_run( alg, parameters, context=context, feedback=feedback, raise_exceptions=True) new_features = [f for f in polygon_layer.getFeatures()] return old_features, new_features
def zoomToAddress(self): # get x,y from data of record self.removePointer() data = self.dlg.geocoderResultView.selectedIndexes()[0].data(Qt.UserRole) if 'centroide_rd' in data: # free OR lookup service geom = QgsGeometry.fromWkt(data['centroide_rd']) adrestekst = data['adrestekst'] else: # no centroid yet, probably only object id, retrieve it via lookup service id = data['id'] data = self.pdokgeocoder.lookup(id) geom = QgsGeometry.fromWkt(data['centroide_rd']) adrestekst = data['adrestekst'] lookup_data= data['data'] lis = '' for key in lookup_data.keys(): lis = lis + '<li>{}: {}</li>'.format(key, lookup_data[key]) self.dlg.ui.lookupinfo.setHtml( '<h4>{}</h4><lu>{}</lu>'.format(adrestekst, lis)) # just always transform from 28992 to mapcanvas crs crs = self.iface.mapCanvas().mapSettings().destinationCrs() crs28992 = QgsCoordinateReferenceSystem() crs28992.createFromId(28992) crsTransform = QgsCoordinateTransform(crs28992, crs, QgsProject.instance()) z = 1587 if adrestekst.lower().startswith('adres'): z = 794 elif adrestekst.lower().startswith('perceel'): z = 794 elif adrestekst.lower().startswith('hectometer'): z = 1587 elif adrestekst.lower().startswith('straat'): z = 3175 elif adrestekst.lower().startswith('postcode'): z = 6350 elif adrestekst.lower().startswith('woonplaats'): z = 25398 elif adrestekst.lower().startswith('gemeente'): z = 50797 elif adrestekst.lower().startswith('provincie'): z = 812750 geom.transform(crsTransform) center = geom.asPoint() self.setPointer(center) # zoom to with center is actually setting a point rectangle and then zoom rect = QgsRectangle(center, center) self.iface.mapCanvas().setExtent(rect) self.iface.mapCanvas().zoomScale(z) self.iface.mapCanvas().refresh()
def testMultipoint(self): # CHANGE previous implementation didn't support multipoint too much if not TestQgsGeometry.wkbPtr: return # #9423 points = [ QgsPoint(10, 30), QgsPoint(40, 20), QgsPoint(30,10), QgsPoint(20,10) ] wkt = "MULTIPOINT (10 30, 40 20, 30 10, 20 10)" multipoint = QgsGeometry.fromWkt(wkt) assert multipoint.isMultipart(), "Expected MULTIPOINT to be multipart" assert multipoint.wkbType() == QGis.WKBMultiPoint, "Expected wkbType to be WKBMultipoint" i = 0 for p in multipoint.asMultiPoint(): assert p == points[i], "Expected %s at %d, got %s" % (points[i].toString(), i, p.toString()) i+=1 multipoint = QgsGeometry.fromWkt( "MULTIPOINT(5 5)" ) assert multipoint.vertexAt( 0 ) == QgsPoint(5,5), "MULTIPOINT fromWkt failed" assert multipoint.insertVertex(4, 4, 0), "MULTIPOINT insert 4,4 at 0 failed" expwkt = "MULTIPOINT(4 4, 5 5)" wkt = multipoint.exportToWkt() assert compareWkt( expwkt, wkt ), "Expected:\n%s\nGot:\n%s\n" % (expwkt, wkt ) assert multipoint.insertVertex(7, 7, 2), "MULTIPOINT append 7,7 at 2 failed" expwkt = "MULTIPOINT(4 4, 5 5, 7 7)" wkt = multipoint.exportToWkt() assert compareWkt( expwkt, wkt ), "Expected:\n%s\nGot:\n%s\n" % (expwkt, wkt ) assert multipoint.insertVertex(6, 6, 2), "MULTIPOINT append 6,6 at 2 failed" expwkt = "MULTIPOINT(4 4, 5 5, 6 6, 7 7)" wkt = multipoint.exportToWkt() assert compareWkt( expwkt, wkt ), "Expected:\n%s\nGot:\n%s\n" % (expwkt, wkt ) assert not multipoint.deleteVertex(4), "MULTIPOINT delete at 4 unexpectedly succeeded" assert not multipoint.deleteVertex(-1), "MULTIPOINT delete at -1 unexpectedly succeeded" assert multipoint.deleteVertex(1), "MULTIPOINT delete at 1 failed" expwkt = "MULTIPOINT(4 4, 6 6, 7 7)" wkt = multipoint.exportToWkt() assert compareWkt( expwkt, wkt ), "Expected:\n%s\nGot:\n%s\n" % (expwkt, wkt ) assert multipoint.deleteVertex(2), "MULTIPOINT delete at 2 failed" expwkt = "MULTIPOINT(4 4, 6 6)" wkt = multipoint.exportToWkt() assert compareWkt( expwkt, wkt ), "Expected:\n%s\nGot:\n%s\n" % (expwkt, wkt ) assert multipoint.deleteVertex(0), "MULTIPOINT delete at 2 failed" expwkt = "MULTIPOINT(6 6)" wkt = multipoint.exportToWkt() assert compareWkt( expwkt, wkt ), "Expected:\n%s\nGot:\n%s\n" % (expwkt, wkt )
def testWriteShapefileWithAttributeSubsets(self): """Tests writing subsets of attributes to files.""" ml = QgsVectorLayer(( 'Point?crs=epsg:4326&field=id:int&field=field1:int&field=field2:int&field=field3:int' ), 'test', 'memory') self.assertIsNotNone(ml, 'Provider not initialized') self.assertTrue(ml.isValid(), 'Source layer not valid') provider = ml.dataProvider() self.assertIsNotNone(provider) ft = QgsFeature() ft.setGeometry(QgsGeometry.fromWkt('Point (1 2)')) ft.setAttributes([1, 11, 12, 13]) res, features = provider.addFeatures([ft]) self.assertTrue(res) self.assertTrue(features) # first write out with all attributes dest_file_name = os.path.join(str(QDir.tempPath()), 'all_attributes.shp') crs = QgsCoordinateReferenceSystem() crs.createFromId(4326, QgsCoordinateReferenceSystem.EpsgCrsId) write_result, error_message = QgsVectorFileWriter.writeAsVectorFormat( ml, dest_file_name, 'utf-8', crs, 'ESRI Shapefile', attributes=[]) self.assertEqual(write_result, QgsVectorFileWriter.NoError, error_message) # Open result and check created_layer = QgsVectorLayer('{}|layerid=0'.format(dest_file_name), 'test', 'ogr') self.assertEqual(created_layer.fields().count(), 4) f = next(created_layer.getFeatures(QgsFeatureRequest())) self.assertEqual(f['id'], 1) self.assertEqual(f['field1'], 11) self.assertEqual(f['field2'], 12) self.assertEqual(f['field3'], 13) # now test writing out only a subset of attributes dest_file_name = os.path.join(str(QDir.tempPath()), 'subset_attributes.shp') write_result, error_message = QgsVectorFileWriter.writeAsVectorFormat( ml, dest_file_name, 'utf-8', crs, 'ESRI Shapefile', attributes=[1, 3]) self.assertEqual(write_result, QgsVectorFileWriter.NoError, error_message) # Open result and check created_layer = QgsVectorLayer('{}|layerid=0'.format(dest_file_name), 'test', 'ogr') self.assertEqual(created_layer.fields().count(), 2) f = next(created_layer.getFeatures(QgsFeatureRequest())) self.assertEqual(f['field1'], 11) self.assertEqual(f['field3'], 13) # finally test writing no attributes dest_file_name = os.path.join(str(QDir.tempPath()), 'no_attributes.shp') write_result, error_message = QgsVectorFileWriter.writeAsVectorFormat( ml, dest_file_name, 'utf-8', crs, 'ESRI Shapefile', skipAttributeCreation=True) self.assertEqual(write_result, QgsVectorFileWriter.NoError, error_message) # Open result and check created_layer = QgsVectorLayer('{}|layerid=0'.format(dest_file_name), 'test', 'ogr') # expect only a default 'FID' field for shapefiles self.assertEqual(created_layer.fields().count(), 1) self.assertEqual(created_layer.fields()[0].name(), 'FID') # in this case we also check that the geometry exists, to make sure feature has been correctly written # even without attributes f = next(created_layer.getFeatures(QgsFeatureRequest())) g = f.geometry() wkt = g.asWkt() expWkt = 'Point (1 2)' self.assertTrue( compareWkt(expWkt, wkt), "geometry not saved correctly when saving without attributes : mismatch Expected:\n%s\nGot:\n%s\n" % (expWkt, wkt)) self.assertEqual(f['FID'], 0)
def search(self): try: # Current service service = self.tab.tabText(self.tab.currentIndex()) # Params params = {'geometry': self.polygonInput.text()} # Set auth and add params according to service if service == 'BaseMap': # ! Auth self.bmSetAuth() # Set request attributes url = 'https://view.geoapi-airbusds.com/api/v1/images' auth = self.bmAuth headers = None # Update params params.update({ 'size': self.maxResultsInput.value(), 'insertdtstart': '1970-01-01T00:00:00', 'insertdtend': self.now() }) # Dates if self.bmFromCheck.isChecked(): params['insertdtstart'] = self.bmFromInput.dateTime( ).toString(Qt.ISODate) if self.bmToCheck.isChecked(): params['insertdtend'] = self.bmToInput.dateTime().toString( Qt.ISODate) else: # ! Auth self.dtSetAuth() url = 'https://search.oneatlas.geoapi-airbusds.com/api/v1/opensearch' auth = None headers = self.dtHeaders # Constellations (at least one) constellations = [] if self.dtSpotCheck.isChecked(): constellations.append('SPOT') if self.dtPleiadesCheck.isChecked(): constellations.append('PHR') # Dates # MAYBE remove hours from dates dateFrom, dateTo = '1970-01-01T00:00:00', self.now() if self.dtFromCheck.isChecked(): dateFrom = self.dtFromInput.dateTime().toString(Qt.ISODate) if self.dtToCheck.isChecked(): dateTo = self.dtToInput.dateTime().toString(Qt.ISODate) # Angles angleMin, angleMax = 0, 30 if self.dtAngleMinCheck.isChecked(): angleMin = self.dtAngleMinInput.value() if self.dtAngleMaxCheck.isChecked(): angleMax = self.dtAngleMaxInput.value() # Covers (directlly update params) if self.dtCloudCheck.isChecked(): params['cloudCover'] = f'[0,{self.dtCloudInput.value()}]' if self.dtSnowCheck.isChecked(): params['snowCover'] = f'[0,{self.dtSnowInput.value()}]' # Workspaces (at leat one) workspaces = [] if self.dtPublicCheck.isChecked(): workspaces.append('0e33eb50-3404-48ad-b835-b0b4b72a5625') if self.dtPrivateCheck.isChecked(): workspaces.append(self.dtWorkspaceId) # Update all params with right format params.update({ 'itemsPerPage': self.maxResultsInput.value(), 'constellation': ','.join(constellations), 'acquisitionDate': f'[{dateFrom},{dateTo}]', 'incidenceAngle': f'[{angleMin},{angleMax}]', 'workspace': ','.join(workspaces) }) # Finally do the api call t = datetime.datetime.now() print(f'START {service} search') r = self.session.get(url, auth=auth, headers=headers, params=params) rSearch = r.json() # Exception request error if r.status_code != 200: self.error( f'{service} search error {r.status_code}\n{rSearch["message"]}' ) return # Create the search result layer with fields according to current service layer = QgsVectorLayer( f'Polygon?crs=epsg:4326&index=yes&{FIELDS[service]}', providerLib='memory', baseName=f'{service} search results') # Extract features features = [] self.errorFeatures = [] for rFeature in rSearch['features']: # Add a feature of the bbox on the new layer feature = QgsFeature(layer.fields()) # Try to get each attributes feature['service'] = service feature['constellation'] = self.getPropertie( rFeature, 'constellation') feature['incidenceAngle'] = self.getPropertie( rFeature, 'incidenceAngle') feature['cloudCover'] = self.getPropertie( rFeature, 'cloudCover') if service == 'BaseMap': feature['id'] = rFeature['id'] feature['insertionDate'] = self.getPropertie( rFeature, 'insertionDate') feature['wmts'] = self.getPropertie(rFeature, 'wmts') # Bbox fBbox = rFeature['properties']['bbox'] rectangle = QgsRectangle(fBbox[0], fBbox[1], fBbox[2], fBbox[3]) else: feature['id'] = self.getPropertie(rFeature, 'id') feature['acquisitionDate'] = self.getPropertie( rFeature, 'acquisitionDate') feature['snowCover'] = self.getPropertie( rFeature, 'snowCover') try: for json in rFeature['_links']['imagesWmts']: feature[f'wmts_{json["name"]}'] = json['href'] for json in rFeature['_links']['imagesWcs']: if 'buffer' in rFeature['rights']: feature[f'wcs_{json["name"]}'] = json['href'] else: feature[f'wcs_{json["name"]}'] = None except Exception as e: print( f'ERROR * eF = qgis.utils.plugins["OneAtlas"].mySearch.errorFeatures[{len(self.errorFeatures)}]' ) print(str(e)) self.errorFeatures.append(rFeature) continue # Bbox coordinates = rFeature['geometry']['coordinates'][0] rectangle = QgsRectangle(coordinates[0][0], coordinates[0][1], coordinates[2][0], coordinates[2][1]) # Add geometry from rectangle feature.setGeometry( QgsGeometry.fromWkt(rectangle.asWktPolygon())) # Add feature to list features.append(feature) # Total if service == 'BaseMap': # Note : rSearch['totalResults'] is maybe the number of total element in bbox ? # and numberOfElements is the true total result total = rSearch['numberOfElements'] color = QColor.fromRgb(0, 250, 0) else: total = rSearch['totalResults'] color = QColor.fromRgb(0, 250, 250) if len(self.errorFeatures) > 0: total -= len(self.errorFeatures) print(f'* {len(self.errorFeatures)} error feature') # Notification for number of total results msgBox = QMessageBox() msgBox.setWindowTitle(WINDOW_TITLE) msgBox.setText(f'There are {total} results') if total > len(features): msgBox.setIcon(QMessageBox.Warning) msgBox.setInformativeText( f'The maximum is configured to {self.maxResultsInput.value()}\nPlease refine your criteria or your AOI' ) msgBox.setStandardButtons(QMessageBox.Retry | QMessageBox.Ignore) msgBox.setDefaultButton(QMessageBox.Retry) else: msgBox.setIcon(QMessageBox.Information) msgBox.setStandardButtons(QMessageBox.Retry | QMessageBox.Ok) msgBox.setDefaultButton(QMessageBox.Ok) msgBox.button(QMessageBox.Retry).setText('Refine') msgBox.button(QMessageBox.Retry).setIcon( QIcon(os.path.dirname(__file__) + f'/search.png')) reply = msgBox.exec_() if reply == QMessageBox.Retry or len(features) == 0: return # Add result feature to the new layer (res, outFeats) = layer.dataProvider().addFeatures(features) # Change layer syle programmatically # Note : if we styling before save and add layer, we avoid to update legend style # => self.iface.layerTreeView().refreshLayerSymbology(vlayer.id()) symbol = layer.renderer().symbol() symbol.setOpacity(0.2) symbol.setColor(color) QgsProject.instance().addMapLayer(layer) # And refresh view layer.triggerRepaint() self.close() except: return
def testLineGenerationRelativeExtrusion(self): vl = QgsVectorLayer('LineStringZ?crs=EPSG:27700', 'lines', 'memory') self.assertTrue(vl.isValid()) for line in ['LineStringZ(322006 129874 12, 322008 129910 13, 322038 129909 14, 322037 129868 15)', 'LineStringZ(322068 129900 16, 322128 129813 17)', 'LineStringZ(321996 129914 11, 321990 129896 15)', 'LineStringZ(321595 130176 1, 321507 130104 10)', 'LineStringZ(321558 129930 1, 321568 130029 10, 321516 130049 5)', 'LineStringZ(321603 129967 3, 321725 130042 9)']: f = QgsFeature() f.setGeometry(QgsGeometry.fromWkt(line)) self.assertTrue(vl.dataProvider().addFeature(f)) vl.elevationProperties().setClamping(Qgis.AltitudeClamping.Relative) vl.elevationProperties().setZScale(2.5) vl.elevationProperties().setZOffset(10) vl.elevationProperties().setExtrusionEnabled(True) vl.elevationProperties().setExtrusionHeight(7) curve = QgsLineString() curve.fromWkt( 'LineString (-347692.88994020794052631 6632796.97473032586276531, -346576.99897185183363035 6632367.38372825458645821, -346396.02439485350623727 6632344.35087973903864622, -346374.34608158958144486 6632220.09952207934111357)') req = QgsProfileRequest(curve) req.setTransformContext(self.create_transform_context()) rl = QgsRasterLayer(os.path.join(unitTestDataPath(), '3d', 'dtm.tif'), 'DTM') self.assertTrue(rl.isValid()) terrain_provider = QgsRasterDemTerrainProvider() terrain_provider.setLayer(rl) terrain_provider.setScale(0.3) terrain_provider.setOffset(-5) req.setTerrainProvider(terrain_provider) req.setCrs(QgsCoordinateReferenceSystem('EPSG:3857')) generator = vl.createProfileGenerator(req) self.assertTrue(generator.generateProfile()) results = generator.takeResults() if QgsProjUtils.projVersionMajor() >= 8: self.assertEqual(self.round_dict(results.distanceToHeightMap(), 1), {675.2: 84.2, 1195.7: 86.8, 1223.1: 81.4, 1272.0: 90.0, 1339.4: 98.7, 1444.4: 100.0}) else: self.assertEqual(self.round_dict(results.distanceToHeightMap(), 1), {675.2: 80.5, 1195.7: 90.5, 1223.1: 87.4, 1272.0: 94.5, 1339.4: 98.0, 1444.4: 94.0}) if QgsProjUtils.projVersionMajor() >= 8: self.assertCountEqual([g.asWkt(1) for g in results.asGeometries()], ['LineStringZ (-346549.8 6632363.9 81.4, -346549.8 6632363.9 88.4)', 'LineStringZ (-346501.4 6632357.8 90, -346501.4 6632357.8 97)', 'LineStringZ (-346434.5 6632349.2 98.7, -346434.5 6632349.2 105.7)', 'LineStringZ (-346384.6 6632279.1 100, -346384.6 6632279.1 107)', 'LineStringZ (-346577 6632367.4 86.8, -346577 6632367.4 93.8)', 'LineStringZ (-347062.8 6632554.4 84.2, -347062.8 6632554.4 91.2)']) self.assertAlmostEqual(results.zRange().lower(), 81.3588, 2) self.assertAlmostEqual(results.zRange().upper(), 107.009, 2) else: self.assertCountEqual([g.asWkt(1) for g in results.asGeometries()], ['LineStringZ (-346549.8 6632363.9 87.4, -346549.8 6632363.9 94.4)', 'LineStringZ (-346501.4 6632357.8 94.5, -346501.4 6632357.8 101.5)', 'LineStringZ (-346434.5 6632349.2 98, -346434.5 6632349.2 105)', 'LineStringZ (-346384.6 6632279.1 94, -346384.6 6632279.1 101)', 'LineStringZ (-346577 6632367.4 90.5, -346577 6632367.4 97.5)', 'LineStringZ (-347062.8 6632554.4 80.5, -347062.8 6632554.4 87.5)']) self.assertAlmostEqual(results.zRange().lower(), 80.45645, 2) self.assertAlmostEqual(results.zRange().upper(), 104.9811499, 2)
def _test(autoTransaction): """Test buffer methods within and without transactions - create a feature - save - retrieve the feature - change geom and attrs - test changes are seen in the buffer """ def _check_feature(wkt): f = next(layer_a.getFeatures()) self.assertEqual(f.geometry().asWkt().upper(), wkt) f = list(buffer.addedFeatures().values())[0] self.assertEqual(f.geometry().asWkt().upper(), wkt) ml = QgsVectorLayer( 'Point?crs=epsg:4326&field=int:integer&field=int2:integer', 'test', 'memory') self.assertTrue(ml.isValid()) d = QTemporaryDir() options = QgsVectorFileWriter.SaveVectorOptions() options.driverName = 'GPKG' options.layerName = 'layer_a' err, msg, newFileName, newLayer = QgsVectorFileWriter.writeAsVectorFormatV3( ml, os.path.join(d.path(), 'transaction_test.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()) project = QgsProject() project.setAutoTransaction(autoTransaction) project.addMapLayers([layer_a]) ########################################### # Tests with a new feature self.assertTrue(layer_a.startEditing()) buffer = layer_a.editBuffer() f = QgsFeature(layer_a.fields()) f.setAttribute('int', 123) f.setGeometry(QgsGeometry.fromWkt('point(7 45)')) self.assertTrue(layer_a.addFeatures([f])) _check_feature('POINT (7 45)') # Need to fetch the feature because its ID is NULL (-9223372036854775808) f = next(layer_a.getFeatures()) self.assertEqual(len(buffer.addedFeatures()), 1) layer_a.undoStack().undo() self.assertEqual(len(buffer.addedFeatures()), 0) layer_a.undoStack().redo() self.assertEqual(len(buffer.addedFeatures()), 1) f = list(buffer.addedFeatures().values())[0] self.assertEqual(f.attribute('int'), 123) # Now change attribute self.assertEqual(buffer.changedAttributeValues(), {}) spy_attribute_changed = QSignalSpy(layer_a.attributeValueChanged) layer_a.changeAttributeValue(f.id(), 1, 321) self.assertEqual(len(spy_attribute_changed), 1) self.assertEqual(spy_attribute_changed[0], [f.id(), 1, 321]) self.assertEqual(len(buffer.addedFeatures()), 1) # This is surprising: because it was a new feature it has been changed directly self.assertEqual(buffer.changedAttributeValues(), {}) f = list(buffer.addedFeatures().values())[0] self.assertEqual(f.attribute('int'), 321) spy_attribute_changed = QSignalSpy(layer_a.attributeValueChanged) layer_a.undoStack().undo() self.assertEqual(len(spy_attribute_changed), 1) self.assertEqual(spy_attribute_changed[0], [f.id(), 1, 123]) self.assertEqual(buffer.changedAttributeValues(), {}) f = list(buffer.addedFeatures().values())[0] self.assertEqual(f.attribute('int'), 123) f = next(layer_a.getFeatures()) self.assertEqual(f.attribute('int'), 123) # Change multiple attributes spy_attribute_changed = QSignalSpy(layer_a.attributeValueChanged) layer_a.changeAttributeValues(f.id(), {1: 321, 2: 456}) self.assertEqual(len(spy_attribute_changed), 2) self.assertEqual(spy_attribute_changed[0], [f.id(), 1, 321]) self.assertEqual(spy_attribute_changed[1], [f.id(), 2, 456]) buffer = layer_a.editBuffer() # This is surprising: because it was a new feature it has been changed directly self.assertEqual(buffer.changedAttributeValues(), {}) spy_attribute_changed = QSignalSpy(layer_a.attributeValueChanged) layer_a.undoStack().undo() # This is because QgsVectorLayerUndoCommandChangeAttribute plural if not autoTransaction: layer_a.undoStack().undo() f = next(layer_a.getFeatures()) self.assertEqual(f.attribute('int'), 123) self.assertEqual(f.attribute('int2'), None) self.assertEqual(len(spy_attribute_changed), 2) self.assertEqual( spy_attribute_changed[1 if autoTransaction else 0], [f.id(), 2, None]) self.assertEqual( spy_attribute_changed[0 if autoTransaction else 1], [f.id(), 1, 123]) # Change geometry f = next(layer_a.getFeatures()) spy_geometry_changed = QSignalSpy(layer_a.geometryChanged) self.assertTrue( layer_a.changeGeometry(f.id(), QgsGeometry.fromWkt('point(9 43)'))) self.assertTrue(len(spy_geometry_changed), 1) self.assertEqual(spy_geometry_changed[0][0], f.id()) self.assertEqual(spy_geometry_changed[0][1].asWkt(), QgsGeometry.fromWkt('point(9 43)').asWkt()) _check_feature('POINT (9 43)') self.assertEqual(buffer.changedGeometries(), {}) layer_a.undoStack().undo() _check_feature('POINT (7 45)') self.assertEqual(buffer.changedGeometries(), {}) self.assertTrue( layer_a.changeGeometry(f.id(), QgsGeometry.fromWkt('point(9 43)'))) _check_feature('POINT (9 43)') self.assertTrue( layer_a.changeGeometry(f.id(), QgsGeometry.fromWkt('point(10 44)'))) _check_feature('POINT (10 44)') # This is another surprise: geometry edits get collapsed into a single # one because they have the same hardcoded id layer_a.undoStack().undo() _check_feature('POINT (7 45)') self.assertTrue(layer_a.commitChanges()) ########################################### # Tests with the existing feature # Get the feature f = next(layer_a.getFeatures()) self.assertTrue(f.isValid()) self.assertEqual(f.attribute('int'), 123) self.assertEqual(f.geometry().asWkt().upper(), 'POINT (7 45)') # Change single attribute self.assertTrue(layer_a.startEditing()) spy_attribute_changed = QSignalSpy(layer_a.attributeValueChanged) layer_a.changeAttributeValue(f.id(), 1, 321) self.assertEqual(len(spy_attribute_changed), 1) self.assertEqual(spy_attribute_changed[0], [f.id(), 1, 321]) buffer = layer_a.editBuffer() self.assertEqual(buffer.changedAttributeValues(), {1: {1: 321}}) f = next(layer_a.getFeatures()) self.assertEqual(f.attribute(1), 321) spy_attribute_changed = QSignalSpy(layer_a.attributeValueChanged) layer_a.undoStack().undo() f = next(layer_a.getFeatures()) self.assertEqual(f.attribute(1), 123) self.assertEqual(len(spy_attribute_changed), 1) self.assertEqual(spy_attribute_changed[0], [f.id(), 1, 123]) self.assertEqual(buffer.changedAttributeValues(), {}) # Change attributes spy_attribute_changed = QSignalSpy(layer_a.attributeValueChanged) layer_a.changeAttributeValues(f.id(), {1: 111, 2: 654}) self.assertEqual(len(spy_attribute_changed), 2) self.assertEqual(spy_attribute_changed[0], [1, 1, 111]) self.assertEqual(spy_attribute_changed[1], [1, 2, 654]) f = next(layer_a.getFeatures()) self.assertEqual(f.attributes(), [1, 111, 654]) self.assertEqual(buffer.changedAttributeValues(), {1: { 1: 111, 2: 654 }}) spy_attribute_changed = QSignalSpy(layer_a.attributeValueChanged) layer_a.undoStack().undo() # This is because QgsVectorLayerUndoCommandChangeAttribute plural if not autoTransaction: layer_a.undoStack().undo() self.assertEqual(len(spy_attribute_changed), 2) self.assertEqual( spy_attribute_changed[0 if autoTransaction else 1], [1, 1, 123]) self.assertEqual( spy_attribute_changed[1 if autoTransaction else 0], [1, 2, None]) f = next(layer_a.getFeatures()) self.assertEqual(f.attributes(), [1, 123, None]) self.assertEqual(buffer.changedAttributeValues(), {}) # Change geometry spy_geometry_changed = QSignalSpy(layer_a.geometryChanged) self.assertTrue( layer_a.changeGeometry(f.id(), QgsGeometry.fromWkt('point(9 43)'))) self.assertEqual(spy_geometry_changed[0][0], 1) self.assertEqual(spy_geometry_changed[0][1].asWkt(), QgsGeometry.fromWkt('point(9 43)').asWkt()) f = next(layer_a.getFeatures()) self.assertEqual(f.geometry().asWkt().upper(), 'POINT (9 43)') self.assertEqual(buffer.changedGeometries()[1].asWkt().upper(), 'POINT (9 43)') spy_geometry_changed = QSignalSpy(layer_a.geometryChanged) layer_a.undoStack().undo() self.assertEqual(spy_geometry_changed[0][0], 1) self.assertEqual(spy_geometry_changed[0][1].asWkt(), QgsGeometry.fromWkt('point(7 45)').asWkt()) self.assertEqual(buffer.changedGeometries(), {}) f = next(layer_a.getFeatures()) self.assertEqual(f.geometry().asWkt().upper(), 'POINT (7 45)') self.assertEqual(buffer.changedGeometries(), {}) # Delete an existing feature self.assertTrue(layer_a.deleteFeature(f.id())) with self.assertRaises(StopIteration): next(layer_a.getFeatures()) self.assertEqual(buffer.deletedFeatureIds(), [f.id()]) layer_a.undoStack().undo() self.assertTrue(layer_a.getFeature(f.id()).isValid()) self.assertEqual(buffer.deletedFeatureIds(), []) ########################################### # Test delete # Delete a new feature f = QgsFeature(layer_a.fields()) f.setAttribute('int', 555) f.setGeometry(QgsGeometry.fromWkt('point(8 46)')) self.assertTrue(layer_a.addFeatures([f])) f = [ f for f in layer_a.getFeatures() if f.attribute('int') == 555 ][0] self.assertTrue(f.id() in buffer.addedFeatures()) self.assertTrue(layer_a.deleteFeature(f.id())) self.assertFalse(f.id() in buffer.addedFeatures()) self.assertFalse(f.id() in buffer.deletedFeatureIds()) layer_a.undoStack().undo() self.assertTrue(f.id() in buffer.addedFeatures()) ########################################### # Add attribute field = QgsField('attr1', QVariant.String) self.assertTrue(layer_a.addAttribute(field)) self.assertNotEqual(layer_a.fields().lookupField(field.name()), -1) self.assertEqual(buffer.addedAttributes(), [field]) layer_a.undoStack().undo() self.assertEqual(layer_a.fields().lookupField(field.name()), -1) self.assertEqual(buffer.addedAttributes(), []) layer_a.undoStack().redo() self.assertNotEqual(layer_a.fields().lookupField(field.name()), -1) self.assertEqual(buffer.addedAttributes(), [field]) self.assertTrue(layer_a.commitChanges()) ########################################### # Remove attribute self.assertTrue(layer_a.startEditing()) buffer = layer_a.editBuffer() attr_idx = layer_a.fields().lookupField(field.name()) self.assertNotEqual(attr_idx, -1) self.assertTrue(layer_a.deleteAttribute(attr_idx)) self.assertEqual(buffer.deletedAttributeIds(), [attr_idx]) self.assertEqual(layer_a.fields().lookupField(field.name()), -1) layer_a.undoStack().undo() self.assertEqual(buffer.deletedAttributeIds(), []) self.assertEqual(layer_a.fields().lookupField(field.name()), attr_idx) # This is totally broken at least on OGR/GPKG: the rollback # does not restore the original fields if False: layer_a.undoStack().redo() self.assertEqual(buffer.deletedAttributeIds(), [attr_idx]) self.assertEqual(layer_a.fields().lookupField(field.name()), -1) # Rollback! self.assertTrue(layer_a.rollBack()) self.assertIn('attr1', layer_a.dataProvider().fields().names()) self.assertIn('attr1', layer_a.fields().names()) self.assertEqual(layer_a.fields().names(), layer_a.dataProvider().fields().names()) attr_idx = layer_a.fields().lookupField(field.name()) self.assertNotEqual(attr_idx, -1) self.assertTrue(layer_a.startEditing()) attr_idx = layer_a.fields().lookupField(field.name()) self.assertNotEqual(attr_idx, -1) ########################################### # Rename attribute attr_idx = layer_a.fields().lookupField(field.name()) self.assertEqual(layer_a.fields().lookupField('new_name'), -1) self.assertTrue(layer_a.renameAttribute(attr_idx, 'new_name')) self.assertEqual(layer_a.fields().lookupField('new_name'), attr_idx) layer_a.undoStack().undo() self.assertEqual(layer_a.fields().lookupField(field.name()), attr_idx) self.assertEqual(layer_a.fields().lookupField('new_name'), -1) layer_a.undoStack().redo() self.assertEqual(layer_a.fields().lookupField('new_name'), attr_idx) self.assertEqual(layer_a.fields().lookupField(field.name()), -1) ############################################# # Try hard to make this fail for transactions if autoTransaction: self.assertTrue(layer_a.commitChanges()) self.assertTrue(layer_a.startEditing()) f = next(layer_a.getFeatures()) # Do for i in range(10): spy_attribute_changed = QSignalSpy( layer_a.attributeValueChanged) layer_a.changeAttributeValue(f.id(), 2, i) self.assertEqual(len(spy_attribute_changed), 1) self.assertEqual(spy_attribute_changed[0], [f.id(), 2, i]) buffer = layer_a.editBuffer() self.assertEqual(buffer.changedAttributeValues(), {f.id(): { 2: i }}) f = next(layer_a.getFeatures()) self.assertEqual(f.attribute(2), i) # Undo/redo for i in range(9): # Undo spy_attribute_changed = QSignalSpy( layer_a.attributeValueChanged) layer_a.undoStack().undo() f = next(layer_a.getFeatures()) self.assertEqual(f.attribute(2), 8 - i) self.assertEqual(len(spy_attribute_changed), 1) self.assertEqual(spy_attribute_changed[0], [f.id(), 2, 8 - i]) buffer = layer_a.editBuffer() self.assertEqual(buffer.changedAttributeValues(), {f.id(): { 2: 8 - i }}) # Redo spy_attribute_changed = QSignalSpy( layer_a.attributeValueChanged) layer_a.undoStack().redo() f = next(layer_a.getFeatures()) self.assertEqual(f.attribute(2), 9 - i) self.assertEqual(len(spy_attribute_changed), 1) self.assertEqual(spy_attribute_changed[0], [f.id(), 2, 9 - i]) # Undo again spy_attribute_changed = QSignalSpy( layer_a.attributeValueChanged) layer_a.undoStack().undo() f = next(layer_a.getFeatures()) self.assertEqual(f.attribute(2), 8 - i) self.assertEqual(len(spy_attribute_changed), 1) self.assertEqual(spy_attribute_changed[0], [f.id(), 2, 8 - i]) buffer = layer_a.editBuffer() self.assertEqual(buffer.changedAttributeValues(), {f.id(): { 2: 8 - i }}) # Last check f = next(layer_a.getFeatures()) self.assertEqual(f.attribute(2), 8 - i) self.assertEqual(buffer.changedAttributeValues(), {f.id(): { 2: 0 }}) layer_a.undoStack().undo() buffer = layer_a.editBuffer() self.assertEqual(buffer.changedAttributeValues(), {}) f = next(layer_a.getFeatures()) self.assertEqual(f.attribute(2), None)
def test_clipGeometry(self): """Test that we can clip a geometry using another geometry.""" myGeometry = QgsGeometry.fromPolyline([ QgsPoint(10, 10), QgsPoint(20, 20), QgsPoint(30, 30), QgsPoint(40, 40) ]) myClipPolygon = QgsGeometry.fromPolygon([[ QgsPoint(20, 20), QgsPoint(20, 30), QgsPoint(30, 30), QgsPoint(30, 20), QgsPoint(20, 20) ]]) myResult = clip_geometry(myClipPolygon, myGeometry) if qgis_version() > 10800: myExpectedWkt = 'LINESTRING(20.0 20.0, 30.0 30.0)' else: myExpectedWkt = ('LINESTRING(20.000000 20.000000, ' '30.000000 30.000000)') # There should only be one feature that intersects this clip # poly so this assertion should work. self.assertEqual(myExpectedWkt, str(myResult.exportToWkt())) # Now poly on poly clip test myClipPolygon = QgsGeometry.fromWkt( 'POLYGON((106.8218 -6.1842,106.8232 -6.1842,' '106.82304963 -6.18317148,106.82179736 -6.18314774,' '106.8218 -6.1842))') myGeometry = QgsGeometry.fromWkt( 'POLYGON((106.8216869 -6.1852067,106.8213233 -6.1843051,' '106.8212891 -6.1835559,106.8222566 -6.1835184,' '106.8227557 -6.1835076,106.8228588 -6.1851204,' '106.8216869 -6.1852067))') myResult = clip_geometry(myClipPolygon, myGeometry) if qgis_version() > 10800: myExpectedWkt = ( 'POLYGON((106.82179833 -6.18353616,106.8222566 -6.1835184,' '106.8227557 -6.1835076,106.82279996 -6.1842,' '106.8218 -6.1842,106.82179833 -6.18353616))') else: myExpectedWkt = ( 'POLYGON((106.821798 -6.183536,106.822257 -6.183518,' '106.822756 -6.183508,106.822800 -6.184200,' '106.821800 -6.184200,106.821798 -6.183536))') # There should only be one feature that intersects this clip # poly so this assertion should work. self.assertEqual(myExpectedWkt, str(myResult.exportToWkt())) # Now point on poly test clip myGeometry = QgsGeometry.fromWkt('POINT(106.82241 -6.18369)') myResult = clip_geometry(myClipPolygon, myGeometry) if qgis_version() > 10800: myExpectedWkt = 'POINT(106.82241 -6.18369)' else: myExpectedWkt = 'POINT(106.822410 -6.183690)' # There should only be one feature that intersects this clip # poly so this assertion should work. self.assertEqual(myExpectedWkt, str(myResult.exportToWkt()))
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_operations(self, md, conn): """Common tests""" capabilities = conn.capabilities() # Schema operations if (capabilities & QgsAbstractDatabaseProviderConnection.CreateSchema and capabilities & QgsAbstractDatabaseProviderConnection.Schemas and capabilities & QgsAbstractDatabaseProviderConnection.DropSchema): # Start clean if 'myNewSchema' in conn.schemas(): conn.dropSchema('myNewSchema', True) # Create conn.createSchema('myNewSchema') schemas = conn.schemas() self.assertTrue('myNewSchema' in schemas) # Create again with self.assertRaises(QgsProviderConnectionException) as ex: conn.createSchema('myNewSchema') # Test rename if capabilities & QgsAbstractDatabaseProviderConnection.RenameSchema: # Rename conn.renameSchema('myNewSchema', 'myVeryNewSchema') schemas = conn.schemas() self.assertTrue('myVeryNewSchema' in schemas) self.assertFalse('myNewSchema' in schemas) conn.renameSchema('myVeryNewSchema', 'myNewSchema') schemas = conn.schemas() self.assertFalse('myVeryNewSchema' in schemas) self.assertTrue('myNewSchema' in schemas) # Drop conn.dropSchema('myNewSchema') schemas = conn.schemas() self.assertFalse('myNewSchema' in schemas) # UTF8 schema conn.createSchema('myUtf8\U0001f604NewSchema') schemas = conn.schemas() conn.dropSchema('myUtf8\U0001f604NewSchema') schemas = conn.schemas() self.assertFalse('myUtf8\U0001f604NewSchema' in schemas) # Table operations if (capabilities & QgsAbstractDatabaseProviderConnection.CreateVectorTable and capabilities & QgsAbstractDatabaseProviderConnection.Tables and capabilities & QgsAbstractDatabaseProviderConnection.DropVectorTable): if capabilities & QgsAbstractDatabaseProviderConnection.CreateSchema: schema = 'myNewSchema' conn.createSchema('myNewSchema') else: schema = 'public' # Start clean if 'myNewTable' in self._table_names(conn.tables(schema)): conn.dropVectorTable(schema, 'myNewTable') fields = QgsFields() fields.append(QgsField("string_t", QVariant.String)) fields.append(QgsField("long_t", QVariant.LongLong)) fields.append(QgsField("double_t", QVariant.Double)) fields.append(QgsField("integer_t", QVariant.Int)) fields.append(QgsField("date_t", QVariant.Date)) fields.append(QgsField("datetime_t", QVariant.DateTime)) fields.append(QgsField("time_t", QVariant.Time)) options = {} crs = QgsCoordinateReferenceSystem.fromEpsgId(3857) typ = QgsWkbTypes.LineString # Create conn.createVectorTable(schema, 'myNewTable', fields, typ, crs, True, options) table_names = self._table_names(conn.tables(schema)) self.assertTrue('myNewTable' in table_names) # Create UTF8 table conn.createVectorTable(schema, 'myUtf8\U0001f604Table', fields, typ, crs, True, options) table_names = self._table_names(conn.tables(schema)) self.assertTrue('myNewTable' in table_names) self.assertTrue('myUtf8\U0001f604Table' in table_names) conn.dropVectorTable(schema, 'myUtf8\U0001f604Table') table_names = self._table_names(conn.tables(schema)) self.assertFalse('myUtf8\U0001f604Table' in table_names) self.assertTrue('myNewTable' in table_names) # insert something, because otherwise MSSQL cannot guess if self.providerKey == 'mssql': f = QgsFeature(fields) f.setGeometry(QgsGeometry.fromWkt('LineString (-72.345 71.987, -80 80)')) vl = QgsVectorLayer(conn.tableUri('myNewSchema', 'myNewTable'), 'vl', 'mssql') vl.dataProvider().addFeatures([f]) # Check table information table_properties = conn.tables(schema) table_property = self._table_by_name(table_properties, 'myNewTable') self.assertEqual(table_property.maxCoordinateDimensions(), 2) self.assertIsNotNone(table_property) self.assertEqual(table_property.tableName(), 'myNewTable') self.assertEqual(table_property.geometryColumnCount(), 1) self.assertEqual(table_property.geometryColumnTypes()[0].wkbType, QgsWkbTypes.LineString) cols = table_property.geometryColumnTypes() self.assertEqual(cols[0].crs, QgsCoordinateReferenceSystem.fromEpsgId(3857)) self.assertEqual(table_property.defaultName(), 'myNewTable') # Check aspatial tables conn.createVectorTable(schema, 'myNewAspatialTable', fields, QgsWkbTypes.NoGeometry, crs, True, options) table_properties = conn.tables(schema, QgsAbstractDatabaseProviderConnection.Aspatial) table_property = self._table_by_name(table_properties, 'myNewAspatialTable') self.assertIsNotNone(table_property) self.assertEqual(table_property.maxCoordinateDimensions(), 0) self.assertEqual(table_property.tableName(), 'myNewAspatialTable') self.assertEqual(table_property.geometryColumnCount(), 0) self.assertEqual(table_property.geometryColumn(), '') self.assertEqual(table_property.defaultName(), 'myNewAspatialTable') cols = table_property.geometryColumnTypes() # We always return geom col types, even when there is no geometry self.assertEqual(cols[0].wkbType, QgsWkbTypes.NoGeometry) self.assertFalse(cols[0].crs.isValid()) self.assertFalse(table_property.flags() & QgsAbstractDatabaseProviderConnection.Raster) self.assertFalse(table_property.flags() & QgsAbstractDatabaseProviderConnection.Vector) self.assertTrue(table_property.flags() & QgsAbstractDatabaseProviderConnection.Aspatial) # Check executeSql has_schema = capabilities & QgsAbstractDatabaseProviderConnection.Schemas if capabilities & QgsAbstractDatabaseProviderConnection.ExecuteSql: if has_schema: table = "\"%s\".\"myNewAspatialTable\"" % schema else: table = 'myNewAspatialTable' # MSSQL literal syntax for UTF8 requires 'N' prefix sql = "INSERT INTO %s (string_t, long_t, double_t, integer_t, date_t, datetime_t, time_t) VALUES (%s'QGIS Rocks - \U0001f604', 666, 1.234, 1234, '2019-07-08', '2019-07-08T12:00:12', '12:00:13.00')" % ( table, 'N' if self.providerKey == 'mssql' else '') res = conn.executeSql(sql) self.assertEqual(res, []) sql = "SELECT string_t, long_t, double_t, integer_t, date_t, datetime_t FROM %s" % table res = conn.executeSql(sql) # GPKG and spatialite have no type for time self.assertEqual(res, [['QGIS Rocks - \U0001f604', 666, 1.234, 1234, QtCore.QDate(2019, 7, 8), QtCore.QDateTime(2019, 7, 8, 12, 0, 12)]]) sql = "SELECT time_t FROM %s" % table res = conn.executeSql(sql) # This does not work in MSSQL and returns a QByteArray, we have no way to know that it is a time # value and there is no way we can convert it. if self.providerKey != 'mssql': self.assertIn(res, ([[QtCore.QTime(12, 0, 13)]], [['12:00:13.00']])) sql = "DELETE FROM %s WHERE string_t = %s'QGIS Rocks - \U0001f604'" % ( table, 'N' if self.providerKey == 'mssql' else '') res = conn.executeSql(sql) self.assertEqual(res, []) sql = "SELECT string_t, integer_t FROM %s" % table res = conn.executeSql(sql) self.assertEqual(res, []) # Check that we do NOT get the aspatial table when querying for vectors table_names = self._table_names(conn.tables(schema, QgsAbstractDatabaseProviderConnection.Vector)) self.assertTrue('myNewTable' in table_names) self.assertFalse('myNewAspatialTable' in table_names) # Query for rasters (in qgis_test schema or no schema for GPKG, spatialite has no support) if self.providerKey not in ('spatialite', 'mssql'): table_properties = conn.tables('qgis_test', QgsAbstractDatabaseProviderConnection.Raster) # At least one raster should be there (except for spatialite) self.assertTrue(len(table_properties) >= 1) table_property = table_properties[0] self.assertTrue(table_property.flags() & QgsAbstractDatabaseProviderConnection.Raster) self.assertFalse(table_property.flags() & QgsAbstractDatabaseProviderConnection.Vector) self.assertFalse(table_property.flags() & QgsAbstractDatabaseProviderConnection.Aspatial) if capabilities & QgsAbstractDatabaseProviderConnection.RenameVectorTable: # Rename conn.renameVectorTable(schema, 'myNewTable', 'myVeryNewTable') tables = self._table_names(conn.tables(schema)) self.assertFalse('myNewTable' in tables) self.assertTrue('myVeryNewTable' in tables) # Rename it back conn.renameVectorTable(schema, 'myVeryNewTable', 'myNewTable') tables = self._table_names(conn.tables(schema)) self.assertTrue('myNewTable' in tables) self.assertFalse('myVeryNewTable' in tables) # Vacuum if capabilities & QgsAbstractDatabaseProviderConnection.Vacuum: conn.vacuum('myNewSchema', 'myNewTable') # Spatial index spatial_index_exists = False # we don't initially know if a spatial index exists -- some formats may create them by default, others not if capabilities & QgsAbstractDatabaseProviderConnection.SpatialIndexExists: spatial_index_exists = conn.spatialIndexExists('myNewSchema', 'myNewTable', 'geom') if capabilities & QgsAbstractDatabaseProviderConnection.DeleteSpatialIndex: if spatial_index_exists: conn.deleteSpatialIndex('myNewSchema', 'myNewTable', 'geom') if capabilities & QgsAbstractDatabaseProviderConnection.SpatialIndexExists: self.assertFalse(conn.spatialIndexExists('myNewSchema', 'myNewTable', 'geom')) if capabilities & QgsAbstractDatabaseProviderConnection.CreateSpatialIndex: options = QgsAbstractDatabaseProviderConnection.SpatialIndexOptions() options.geometryColumnName = 'geom' conn.createSpatialIndex('myNewSchema', 'myNewTable', options) if capabilities & QgsAbstractDatabaseProviderConnection.SpatialIndexExists: self.assertTrue(conn.spatialIndexExists('myNewSchema', 'myNewTable', 'geom')) # now we know for certain a spatial index exists, let's retry dropping it if capabilities & QgsAbstractDatabaseProviderConnection.DeleteSpatialIndex: conn.deleteSpatialIndex('myNewSchema', 'myNewTable', 'geom') if capabilities & QgsAbstractDatabaseProviderConnection.SpatialIndexExists: self.assertFalse(conn.spatialIndexExists('myNewSchema', 'myNewTable', 'geom')) if capabilities & QgsAbstractDatabaseProviderConnection.DropSchema: # Drop schema (should fail) with self.assertRaises(QgsProviderConnectionException) as ex: conn.dropSchema('myNewSchema') # Check some column types operations table = self._table_by_name(conn.tables(schema), 'myNewTable') self.assertEqual(len(table.geometryColumnTypes()), 1) ct = table.geometryColumnTypes()[0] self.assertEqual(ct.crs, QgsCoordinateReferenceSystem.fromEpsgId(3857)) self.assertEqual(ct.wkbType, QgsWkbTypes.LineString) # Add a new (existing type) table.addGeometryColumnType(QgsWkbTypes.LineString, QgsCoordinateReferenceSystem.fromEpsgId(3857)) self.assertEqual(len(table.geometryColumnTypes()), 1) ct = table.geometryColumnTypes()[0] self.assertEqual(ct.crs, QgsCoordinateReferenceSystem.fromEpsgId(3857)) self.assertEqual(ct.wkbType, QgsWkbTypes.LineString) # Add a new one table.addGeometryColumnType(QgsWkbTypes.LineString, QgsCoordinateReferenceSystem.fromEpsgId(4326)) self.assertEqual(len(table.geometryColumnTypes()), 2) ct = table.geometryColumnTypes()[0] self.assertEqual(ct.crs, QgsCoordinateReferenceSystem.fromEpsgId(3857)) self.assertEqual(ct.wkbType, QgsWkbTypes.LineString) ct = table.geometryColumnTypes()[1] self.assertEqual(ct.crs, QgsCoordinateReferenceSystem.fromEpsgId(4326)) self.assertEqual(ct.wkbType, QgsWkbTypes.LineString) # Check fields fields = conn.fields('myNewSchema', 'myNewTable') for f in ['string_t', 'long_t', 'double_t', 'integer_t', 'date_t', 'datetime_t', 'time_t']: self.assertTrue(f in fields.names()) if capabilities & QgsAbstractDatabaseProviderConnection.AddField: field = QgsField('short_lived_field', QVariant.Int, 'integer') conn.addField(field, 'myNewSchema', 'myNewTable') fields = conn.fields('myNewSchema', 'myNewTable') self.assertTrue('short_lived_field' in fields.names()) if capabilities & QgsAbstractDatabaseProviderConnection.DeleteField: conn.deleteField('short_lived_field', 'myNewSchema', 'myNewTable') # This fails on Travis for spatialite, for no particular reason if self.providerKey == 'spatialite' and not os.environ.get('TRAVIS', False): fields = conn.fields('myNewSchema', 'myNewTable') self.assertFalse('short_lived_field' in fields.names()) # Drop table conn.dropVectorTable(schema, 'myNewTable') conn.dropVectorTable(schema, 'myNewAspatialTable') table_names = self._table_names(conn.tables(schema)) self.assertFalse('myNewTable' in table_names) if capabilities & QgsAbstractDatabaseProviderConnection.DropSchema: # Drop schema conn.dropSchema('myNewSchema') self.assertFalse('myNewSchema' in conn.schemas()) conns = md.connections() self.assertTrue(isinstance(list(conns.values())[0], QgsAbstractDatabaseProviderConnection)) # Remove connection spy_deleted = QSignalSpy(md.connectionDeleted) md.deleteConnection('qgis_test1') self.assertEqual(list(md.connections().values()), []) self.assertEqual(len(spy_deleted), 1)
def test_resetSnappingIndex(self): self.pointsLayer.setDependencies([]) self.linesLayer.setDependencies([]) self.pointsLayer2.setDependencies([]) ms = QgsMapSettings() ms.setOutputSize(QSize(100, 100)) ms.setExtent(QgsRectangle(0, 0, 1, 1)) self.assertTrue(ms.hasValidSettings()) u = QgsSnappingUtils() u.setMapSettings(ms) cfg = u.config() cfg.setEnabled(True) cfg.setMode(QgsSnappingConfig.AdvancedConfiguration) cfg.setIndividualLayerSettings(self.pointsLayer, QgsSnappingConfig.IndividualLayerSettings(True, QgsSnappingConfig.Vertex, 20, QgsTolerance.Pixels)) u.setConfig(cfg) m = u.snapToMap(QPoint(95, 100)) self.assertTrue(m.isValid()) self.assertTrue(m.hasVertex()) self.assertEqual(m.point(), QgsPointXY(1, 0)) f = QgsFeature(self.linesLayer.fields()) f.setId(1) geom = QgsGeometry.fromWkt("LINESTRING(0 0,1 1)") f.setGeometry(geom) self.linesLayer.startEditing() self.linesLayer.addFeatures([f]) self.linesLayer.commitChanges() l1 = len([f for f in self.pointsLayer.getFeatures()]) self.assertEqual(l1, 4) m = u.snapToMap(QPoint(95, 0)) # snapping not updated self.pointsLayer.setDependencies([]) self.assertEqual(m.isValid(), False) # set layer dependencies self.pointsLayer.setDependencies([QgsMapLayerDependency(self.linesLayer.id())]) # add another line f = QgsFeature(self.linesLayer.fields()) f.setId(2) geom = QgsGeometry.fromWkt("LINESTRING(0 0,0.5 0.5)") f.setGeometry(geom) self.linesLayer.startEditing() self.linesLayer.addFeatures([f]) self.linesLayer.commitChanges() # check the snapped point is OK m = u.snapToMap(QPoint(45, 50)) self.assertTrue(m.isValid()) self.assertTrue(m.hasVertex()) self.assertEqual(m.point(), QgsPointXY(0.5, 0.5)) self.pointsLayer.setDependencies([]) # test chained layer dependencies A -> B -> C cfg.setIndividualLayerSettings(self.pointsLayer2, QgsSnappingConfig.IndividualLayerSettings(True, QgsSnappingConfig.Vertex, 20, QgsTolerance.Pixels)) u.setConfig(cfg) self.pointsLayer.setDependencies([QgsMapLayerDependency(self.linesLayer.id())]) self.pointsLayer2.setDependencies([QgsMapLayerDependency(self.pointsLayer.id())]) # add another line f = QgsFeature(self.linesLayer.fields()) f.setId(3) geom = QgsGeometry.fromWkt("LINESTRING(0 0.2,0.5 0.8)") f.setGeometry(geom) self.linesLayer.startEditing() self.linesLayer.addFeatures([f]) self.linesLayer.commitChanges() # check the second snapped point is OK m = u.snapToMap(QPoint(75, 100 - 80)) self.assertTrue(m.isValid()) self.assertTrue(m.hasVertex()) self.assertEqual(m.point(), QgsPointXY(0.7, 0.8)) self.pointsLayer.setDependencies([]) self.pointsLayer2.setDependencies([])
def testReprojectionErrorsWhileRendering(self): # WKT of a polygon which causes reprojection errors while rendering # (apologies for the ridiculously complex wkt, but I can't find a way to reproduce with simplifiction) wkt = 'MultiPolygon (((16.93392988400009358 42.77094147300012139, 16.88493899800005238 42.72939687700012712, ' \ '16.80298912900011032 42.76349518400014915, 16.85816491000014139 42.78400299700011544, ' \ '16.93392988400009358 42.77094147300012139)),((17.38200931100010393 42.79783763200002511, ' \ '17.65894616000011297 42.74298737200008702, 17.74887129000009622 42.69456614800010641, ' \ '17.32374108200008322 42.79083893400003547, 17.38200931100010393 42.79783763200002511)),' \ '((16.768565300000148 42.97223541900014254, 17.03207441500009622 42.98261139500014849, ' \ '17.13184655000009116 42.96954987200014386, 17.20020592500009116 42.92177969000012183, ' \ '16.85141035200010151 42.90070221600008438, 16.65544681100004709 42.92625560099999404, ' \ '16.70679772200014668 42.96954987200014386, 16.63168379000003938 42.98261139500014849, ' \ '16.768565300000148 42.97223541900014254)),((17.05567467500011958 43.02895742400001211, ' \ '17.24024498800011429 43.02277252800014651, 17.74146569100011561 42.83926015800001608, ' \ '17.70736738400009358 42.88703034100014122, 17.65334906206413734 42.8909283361407887, ' \ '17.70158573400010482 42.91950022500007833, 17.81175988700005064 42.909862570000044, ' \ '17.85847538200005147 42.81697418200012351, 18.22413781700009849 42.62807098500009317, ' \ '18.43735477700010961 42.55921213800017711, 18.4371480710000526 42.4934022020000981, ' \ '18.49642988400009358 42.41632721600008438, 18.23894290500010129 42.55906810100005089, ' \ '18.21753991000014139 42.6201032570001388, 18.07601972700010151 42.65131256700003348, ' \ '18.0432235040000819 42.70205312700007028, 17.90162194100014403 42.75189850500014188, ' \ '17.8928328790000819 42.79083893400003547, 17.72095787900005348 42.8262393250000315, ' \ '17.7618921230000808 42.77871328300012976, 17.74870853000004445 42.77204010600017625, ' \ '17.21387780000011958 42.98261139500014849, 17.04615319100011561 42.9950625670000619, ' \ '17.00163821700004974 43.05149974200010377, 17.05567467500011958 43.02895742400001211)),' \ '((16.19467207100007045 43.07440827000000638, 16.254893425000148 43.06854889500006323, ' \ '16.08716881600014403 43.01146067900008063, 16.04883873800011429 43.06517161700004692, ' \ '16.19467207100007045 43.07440827000000638)),((16.56275475400011032 43.22898997600010773, ' \ '16.65951582100009887 43.21596914300012315, 16.72771243600001867 43.16461823100003414, ' \ '17.19336998800014271 43.12726471600016964, 16.67017662900013875 43.12547435099999404, ' \ '16.37159264400014536 43.19550202000006323, 16.49642988400006516 43.21808502800014651, ' \ '16.58326256600014403 43.18866608300005794, 16.52051842500006273 43.22898997600010773, ' \ '16.56275475400011032 43.22898997600010773)),((16.80681399800010922 43.34247467700005529, ' \ '16.89234459700011826 43.31220123900006058, 16.84620201900008851 43.27338288000005662, ' \ '16.62826582100012729 43.26373932500008834, 16.50074303500014139 43.28424713700003679, ' \ '16.42188561300008587 43.31757233300011478, 16.40577233200011165 43.33270905200011214, ' \ '16.45346113400009358 43.35317617400009738, 16.42628014400008851 43.39411041900011412, ' \ '16.44703209700008983 43.39484284100014122, 16.80681399800010922 43.34247467700005529)),' \ '((16.29818769600012729 43.40363190300011809, 16.30274498800008587 43.38727448100009099, ' \ '16.39144941500012465 43.34638092700005529, 16.348643425000148 43.33869049700003018, ' \ '16.20045006600014403 43.40704987200003018, 16.29818769600012729 43.40363190300011809)),' \ '((16.33415774800010922 43.50153229400014254, 16.3752547540000819 43.49017975500008504, ' \ '16.21143639400008851 43.49005768400009231, 16.26441491000014139 43.51288483300011478, ' \ '16.33415774800010922 43.50153229400014254)),((15.67888431100004709 43.64801666900014254, ' \ '15.74040774800010922 43.62750885600009099, 15.67204837300002396 43.63743724200010377, ' \ '15.60377037900013875 43.67470937700007028, 15.67888431100004709 43.64801666900014254)),' \ '((15.36736087300005238 43.79010651200015047, 15.39568118600007551 43.7724063170000619, ' \ '15.22779381600014403 43.87445709800014981, 15.24073326900014536 43.88076406500009341, ' \ '15.36736087300005238 43.79010651200015047)),((15.44271894600009887 43.89907461100013109, ' \ '15.35865319100014403 43.91937897300014981, 15.26124108200011165 44.01105377800003282, ' \ '15.38404381600008719 43.9701602230000077, 15.44271894600009887 43.89907461100013109)),' \ '((15.22575931100010393 44.06622955900014915, 15.25440514400008851 44.01788971600014122, ' \ '15.12183678500014139 44.09223053600005926, 15.06251061300008587 44.16193268400012073, ' \ '15.22575931100010393 44.06622955900014915)),((14.83545983200014007 44.15102773600013109, ' \ '14.85726972700010151 44.15204498900000374, 14.86915123800014271 44.14052969000006499, ' \ '14.83521569100008719 44.14166901200009363, 14.81983483200014007 44.15302155199999845, ' \ '14.82243899800005238 44.16868724200004692, 14.83545983200014007 44.15102773600013109)),' \ '((14.98511803500011297 44.09096914300012315, 15.21680748800008587 43.91278717700008372, ' \ '15.13331139400011693 43.92121002800003282, 15.19450931100004709 43.87262604400017096, ' \ '15.10661868600007551 43.92544179900015422, 14.84961998800014271 44.17560455900014915, ' \ '14.98511803500011297 44.09096914300012315)),((14.765961134000122 44.26504140800015819, ' \ '14.74854576900014536 44.26166413000014188, 14.73959394600012729 44.28017812700015554, ' \ '14.79167728000007287 44.27252838700003679, 14.765961134000122 44.26504140800015819)),' \ '((14.66138756600011561 44.30866120000014519, 14.6407983730000808 44.31183502800003282, ' \ '14.59506269600007045 44.34711334800006455, 14.643565300000148 44.32575104400011412, ' \ '14.66138756600011561 44.30866120000014519)),((14.81120853000004445 44.35004303600000242, ' \ '14.75619550900009358 44.36399974200004692, 14.76343834700008983 44.41535065300017493, ' \ '14.80323326900008851 44.40550364800004957, 14.81120853000004445 44.35004303600000242)),' \ '((14.27116946700002131 44.61253489800004957, 14.23259524800005238 44.62604401200012205, ' \ '14.2657983730000808 44.67951080900003547, 14.28044681100007551 44.67755768400009231, ' \ '14.27116946700002131 44.61253489800004957)),((14.84522545700008322 44.60053131700011875, ' \ '14.93824303500014139 44.59414297100001079, 15.07553144600007045 44.48407623900006058, ' \ '14.91114342500011958 44.54547760600014783, 15.04802493600004709 44.43943919500001982, ' \ '15.09669030000009116 44.41518789300000947, 15.04151451900014536 44.47662995000008834, ' \ '15.25440514400008851 44.34003327000000638, 15.165049675000148 44.36737702000006323, ' \ '15.22022545700008322 44.3127302100001117, 15.13086998800008587 44.33258698100003414, ' \ '15.17237389400014536 44.29913971600016964, 15.12875410200007309 44.31199778900018771, ' \ '15.08920332100009887 44.37421295800000109, 15.11719811300014271 44.38719310099999404, ' \ '15.04900149800010922 44.39468008000015686, 14.89747155000009116 44.49091217699999845, ' \ '14.91863040500010129 44.50454336100013109, 14.87696373800011429 44.55975983300005794, ' \ '14.73365319100008719 44.70319245000014519, 14.84522545700008322 44.60053131700011875)),' \ '((14.41000410200010151 44.60097890800001608, 14.52662194100011561 44.50372955900012073, ' \ '14.53435306100010393 44.48407623900006058, 14.42261803500008455 44.57387929900009738, ' \ '14.36304772200014668 44.57343170800000109, 14.38257897200014668 44.60325755399999537, ' \ '14.33578535200007309 44.71678294500010509, 14.39747155000009116 44.6856143250000315, ' \ '14.41000410200010151 44.60097890800001608)),((14.75326582100007045 44.84585195500012844, ' \ '14.74048912900011032 44.82050202000000638, 14.82243899800005238 44.77142975500005662, ' \ '14.84961998800014271 44.70319245000014519, 14.65788821700004974 44.79877350500014188, ' \ '14.7268172540000819 44.79877350500014188, 14.6858016290000819 44.8471540390000456, ' \ '14.75326582100007045 44.84585195500012844)),((14.47103925900006516 44.95392487200003018, ' \ '14.45191491000008455 44.79877350500014188, 14.47217858200011165 44.7079531920000619, ' \ '14.53435306100010393 44.63426341400010244, 14.51335696700007816 44.618841864000089, ' \ '14.42790774800005238 44.65656159100014122, 14.29420006600008719 44.9086367860001161, ' \ '14.30152428500011297 44.94342682500014519, 14.38738040500004445 44.90900299700003018, ' \ '14.39031009200004974 44.96039459800012139, 14.41138756600008719 44.95636627800014651, ' \ '14.27849368600004709 45.1133487000000315, 14.29957116000014139 45.16233958499999801, ' \ '14.35621178500014139 45.16925690300008966, 14.387705925000148 45.03904857000013351, ' \ '14.47103925900006516 44.95392487200003018)),((14.56332441500012465 45.24974192900008063, ' \ '14.62378991000011297 45.17548248900006058, 14.59742272200011826 45.16644928600005926, ' \ '14.66529381600011561 45.16181061400011743, 14.66529381600011561 45.08734772300006455, ' \ '14.74048912900011032 45.07306549700014386, 14.81495201900008851 44.97748444200009033, ' \ '14.70639082100009887 44.9467227230000077, 14.62891686300014271 44.97817617400004053, ' \ '14.62086022200008983 45.04559967700011214, 14.61695397200008983 45.02464427300007799, ' \ '14.51050866000014139 45.03217194200011875, 14.43873131600014403 45.07050202000006323, ' \ '14.4670516290000819 45.12409088700015047, 14.53012129000009622 45.13483307500014519, ' \ '14.53435306100010393 45.23753489800002114, 14.56332441500012465 45.24974192900008063)),' \ '((16.36947066200013978 46.54057118800012915, 16.63767134600004738 46.47447703100009164, ' \ '16.75508020000012266 46.38187286400001597, 16.83765913900006694 46.38187286400001597, ' \ '16.88923221800007468 46.29216257800014489, 17.05294315600005461 46.15346303300005104, ' \ '17.20859257000006437 46.11656606000003933, 17.27587528500004055 46.01202463800002818, ' \ '17.31680301900004793 45.99765859000002877, 17.29013798000011093 45.98463612900009423, ' \ '17.40620324700006449 45.94365671800015605, 17.59110152100009827 45.93621531200012953, ' \ '17.65652388500006964 45.84541982000014571, 17.80917606600013414 45.81441396100005647, ' \ '17.85806197100004056 45.77172922800004073, 18.21121870900006456 45.78537180600012846, ' \ '18.40438521300006869 45.74180857400001798, 18.57347049900010916 45.81668772400014689, ' \ '18.6556360270001278 45.90758656800015558, 18.7755253500000947 45.88283355700004051, ' \ '18.90130578600007993 45.93120269800006383, 18.87288374800004931 45.89523590100002082, ' \ '18.90699019400011593 45.86795074500018643, 18.85531376100007606 45.85735707600009903, ' \ '18.84497847500006174 45.8157058720000947, 18.96848514800012708 45.66873809800016204, ' \ '18.90357954900008508 45.57308502200005762, 18.94171675700005153 45.53892689999999277, ' \ '19.01809452300011571 45.56740061400002162, 19.10625451700005328 45.51164174500017623, ' \ '19.00961958800010621 45.49867095900005154, 19.00300500400010151 45.45536611000007099, ' \ '19.03742150900006891 45.42229319300010104, 18.97592655400006834 45.39495636000008005, ' \ '19.09199182100007874 45.34999786400005917, 19.12475467900009107 45.29811472600006539, ' \ '19.36308638500014467 45.24824696900010679, 19.40783817500010855 45.20313344400013023, ' \ '19.39068160000005037 45.16933705700016333, 19.22593713300008744 45.16194732700016345, ' \ '19.12186079900010327 45.195795390000157, 19.13767378700009658 45.14603098600004216, ' \ '19.04486291500009543 45.13724599300006446, 19.08227665200013234 45.08494944300004192, ' \ '19.0872375890000967 44.97710072800013847, 19.13167932100006396 44.95317454000003465, ' \ '19.06667036900009293 44.90568389900012392, 18.99142948400006503 44.9149339800001286, ' \ '19.01582076000008215 44.86563466400004074, 18.88962691200009658 44.86119049100013001, ' \ '18.78338016700013213 44.91374542300012251, 18.79175174900009893 45.00154368100008639, ' \ '18.73831831900008638 45.0159097290000858, 18.68405806500004473 45.08479441400000098, ' \ '18.64871138500012648 45.06267689999999959, 18.61667199700013953 45.09766184500010411, ' \ '18.54959598800010667 45.09476796500011631, 18.51703983500007666 45.05585561200003042, ' \ '18.23788374800011525 45.15745147700012296, 18.15365116400005263 45.0975584930001645, ' \ '18.00347945100011771 45.1493382780000303, 17.83573775200005684 45.0644338990000648, ' \ '17.68473921700012852 45.1639627080000281, 17.48185754400009273 45.11440500900012296, ' \ '17.49622359200009214 45.1416901650001563, 17.44775109900012922 45.13430043600014585, ' \ '17.44330692500011537 45.16205068000009248, 17.38243208800008688 45.1396231090000839, ' \ '17.26895064300006766 45.18954254200015441, 17.24548954300007608 45.15538442000017483, ' \ '17.18709517400012032 45.14856313100001728, 17.0363033440001459 45.23047027600007652, ' \ '17.00829471800011561 45.21615590500009318, 17.00829471800011561 45.24416453100009505, ' \ '16.94731652900014751 45.23568959600000028, 16.9243721930001243 45.28452382500016427, ' \ '16.81171757000004163 45.18122263700009, 16.52894413300009546 45.22225372400005483, ' \ '16.38921106000003647 45.11683380099999852, 16.31624393700010955 45.00123362300008978, ' \ '16.12152714000009723 45.09616322900008356, 16.02044803900011516 45.213933818000001, ' \ '15.79234826700013627 45.18980092400012438, 15.76361617000014803 44.97555043600003444, ' \ '15.7308533120001357 44.92723297200008403, 15.77343469200010873 44.84501576800015243, ' \ '15.71607385200013596 44.80320953400008932, 15.72847619600008784 44.76910308800002269, ' \ '15.80568078600006743 44.69665273000013883, 15.88877648900006534 44.72424794500012979, ' \ '15.96897831200004703 44.63924021400013942, 16.02830285600006732 44.62471913700009907, ' \ '16.04473596200011798 44.58937245700018082, 16.00608199000004106 44.54100331600012908, ' \ '16.11646285000011858 44.52146962500013672, 16.15966434700004584 44.41610138000002905, ' \ '16.13827030500004867 44.37760243800015303, 16.20286584400008678 44.35977406800010669, ' \ '16.18756962000011868 44.28241444999999032, 16.21578495300011014 44.20815541600011045, ' \ '16.32688928200008149 44.08237498000012522, 16.50103885900011846 43.99271637000008184, ' \ '16.67859908100004418 43.8406843060001421, 16.71260217300007866 43.77151540100005889, ' \ '17.03051558500007445 43.54847991900005866, 17.27050093600007585 43.46321380700000248, ' \ '17.28993127500007176 43.3034302780000786, 17.44206669100009321 43.15243174300015028, ' \ '17.6284119050001209 43.04657257100008394, 17.66272505700004558 42.96569895500012137, ' \ '17.63450972400008254 42.950402731000068, 17.51563561300008587 42.95888906500012183, ' \ '17.47087649800005238 43.01341380400010905, 17.50196373800014271 43.03099192900005221, ' \ '17.43360436300014271 43.01740143400009231, 17.46021569100011561 43.03099192900005221, ' \ '17.42611738400009358 43.06517161700004692, 17.4045516290000819 43.05149974200010377, ' \ '17.31625410200012993 43.12726471600016964, 17.11394290500004445 43.21320221600008438, ' \ '16.88062584700011826 43.40595123900006058, 16.62582441500009622 43.44904205900009231, ' \ '16.52466881600011561 43.51080963700009363, 16.39144941500012465 43.51080963700009363, ' \ '16.47339928500008455 43.5381533870001789, 16.43384850400013875 43.54975006700000506, ' \ '16.11768639400008851 43.52448151200003679, 16.17237389400014536 43.4896914730000077, ' \ '16.11312910200004467 43.47890859600009605, 15.95948326900011693 43.50397370000008834, ' \ '15.987315300000148 43.54490794500010509, 15.92530358200011165 43.55857982000004824, ' \ '15.91895592500009116 43.62872955900012073, 15.96631920700011165 43.64118073100003414, ' \ '15.90479576900014536 43.64801666900014254, 15.95297285200010151 43.65086497599999404, ' \ '15.95045006600008719 43.68854401200015047, 15.70630944100008719 43.76341380400005221, ' \ '15.6174422540000819 43.82550690300017493, 15.66309655000009116 43.81297435099999404, ' \ '15.67888431100004709 43.81928131700011875, 15.45508873800014271 43.92804596600014122, ' \ '15.14454186300011429 44.19546133000015686, 15.15219160200012993 44.23529694200014717, ' \ '15.11036217500011958 44.26434967700011214, 15.14063561300011429 44.28245677300013483, ' \ '15.17660566500009622 44.24994538000005662, 15.20777428500008455 44.27277252800014651, ' \ '15.19809004000012465 44.30166250200007028, 15.295258009000122 44.25067780199999845, ' \ '15.30274498800008587 44.29913971600016964, 15.26124108200011165 44.33258698100003414, ' \ '15.42448978000001603 44.26797109600006763, 15.52865644600009887 44.27179596600008438, ' \ '15.30795332100009887 44.35439687700007028, 15.00733483200014007 44.56972890800012976, ' \ '14.883799675000148 44.7236188820001388, 14.883799675000148 44.86147695500012844, 14.92164147200008983 ' \ '44.95880768400009231, 14.85279381600011561 45.09365469000000815, 14.65788821700004974 ' \ '45.19660065300017493, 14.57081139400008851 45.29364655200011214, 14.31153405000009116 ' \ '45.34398021000005485, 14.23259524800005238 45.14935944200000506, 14.17937259200007816 ' \ '45.13450755400005221, 14.19312584700008983 45.10561758000012844, 14.14389082100007045 ' \ '45.05939362200003018, 14.151377800000148 44.97748444200009033, 14.06885826900014536 ' \ '44.94953034100014122, 14.08383222700007309 44.9863955750000315, 14.04029381600014403 ' \ '45.03896719000015025, 14.0756942070000548 44.98371002800003282, 14.02051842500011958 ' \ '44.90110911700004692, 13.97266686300011429 44.90110911700004692, 13.99301191500009622 ' \ '44.88129303600014453, 13.97266686300011429 44.82664622599999404, 14.00001061300008587 ' \ '44.81305573100003414, 13.89014733200011165 44.83348216400010244, 13.91797936300014271 ' \ '44.77826569200009033, 13.90316816500009622 44.77240631700014717, 13.89698326900011693 ' \ '44.81305573100003414, 13.78711998800014271 44.87506745000008834, 13.84229576900008851 ' \ '44.88812897300006455, 13.79460696700010658 44.89496491100008768, 13.77409915500007287 ' \ '44.96381256700014717, 13.6232202480000808 45.07306549700014386, 13.61255944100014403 ' \ '45.11786530199999845, 13.72624759200004974 45.13450755400005221, 13.5959578790000819 ' \ '45.14541250200001343, 13.57545006600011561 45.26487864800007799, 13.60271243600001867 ' \ '45.28534577000012007, 13.57545006600011561 45.30646393400006389, 13.60954837300005238 ' \ '45.32013580900017757, 13.54127037900013875 45.34613678600005926, 13.50709069100014403 ' \ '45.51190827000000638, 13.62901778100007277 45.45898346000016943, 13.75929406800014476 ' \ '45.46316925100011019, 13.88900191200011136 45.42363678000005223, 13.98263960800005634 ' \ '45.47531321200001742, 13.97189091000012695 45.5142255660000643, 14.09291711400010172 ' \ '45.47391794800002174, 14.21869755100007637 45.49717234400004884, 14.37279667100006009 ' \ '45.47784535800009564, 14.4689148350000778 45.52559438100014688, 14.49857710800012001 ' \ '45.59618438800005435, 14.58094934100009255 45.66780792200010808, 14.66848921700008646 ' \ '45.53396596300005683, 14.79716353300005949 45.46518463200006011, 14.88160282300009385 ' \ '45.46978383400001178, 14.9226339110000481 45.51494903600017494, 15.13926151500010064 ' \ '45.43004465799999991, 15.32519331800011742 45.45283396399999276, 15.36136682100004691 ' \ '45.48203114900003641, 15.29666792800006192 45.52295888300012905, 15.2685559480001416 ' \ '45.60166208900012919, 15.37376916500011248 45.64021270800010655, 15.25501672300006817 ' \ '45.72346344000011698, 15.42906294700014769 45.77529490200011253, 15.45128381300008868 ' \ '45.81513743100013869, 15.67607629400006886 45.84169911700014666, 15.65943648300003588 ' \ '45.88882802400014782, 15.69798710100010908 46.0362092080000167, 15.58988000500005455 ' \ '46.11351715100001059, 15.62284956800010605 46.19170359400006021, 16.01920780400010358 ' \ '46.29882883700007312, 16.05961877400008575 46.33231516600015709, 16.0579651280001201 ' \ '46.37753204400003426, 16.2756262620000598 46.37316538500006402, 16.23490523300009158 ' \ '46.4933389280001137, 16.36947066200013978 46.54057118800012915))) ' geom = QgsGeometry.fromWkt(wkt) f = QgsFeature() f.setGeometry(geom) image = QImage(200, 200, QImage.Format_RGB32) painter = QPainter() ms = QgsMapSettings() crs = QgsCoordinateReferenceSystem.fromProj4('+proj=ortho +lat_0=36.5 +lon_0=-118.8 +x_0=0 +y_0=0 +a=6371000 +b=6371000 +units=m +no_defs') self.assertTrue(crs.isValid()) ms.setDestinationCrs(crs) ms.setExtent(QgsRectangle(1374999.8, 3912610.7, 4724462.5, 6505499.6)) ms.setOutputSize(image.size()) context = QgsRenderContext.fromMapSettings(ms) context.setPainter(painter) context.setScaleFactor(96 / 25.4) # 96 DPI ct = QgsCoordinateTransform(QgsCoordinateReferenceSystem('epsg:4326'), crs, QgsProject.instance()) self.assertTrue(ct.isValid()) context.setCoordinateTransform(ct) context.setExtent(ct.transformBoundingBox(ms.extent(), QgsCoordinateTransform.ReverseTransform)) fill_symbol = QgsFillSymbol.createSimple({'color': '#ffffff', 'outline_color': '#ffffff', 'outline_width': '10'}) painter.begin(image) try: image.fill(QColor(0, 0, 0)) fill_symbol.startRender(context) fill_symbol.renderFeature(f, context) fill_symbol.stopRender(context) finally: painter.end() assert self.imageCheck('Reprojection errors polygon', 'reprojection_errors_polygon', image) #also test linestring linestring = QgsGeometry(geom.constGet().boundary()) f.setGeometry(linestring) line_symbol = QgsLineSymbol.createSimple({'color': '#ffffff', 'outline_width': '10'}) image = QImage(200, 200, QImage.Format_RGB32) painter.begin(image) try: image.fill(QColor(0, 0, 0)) line_symbol.startRender(context) line_symbol.renderFeature(f, context) line_symbol.stopRender(context) finally: painter.end() assert self.imageCheck('Reprojection errors linestring', 'reprojection_errors_linestring', image)
def clickTool(self): self.rubberBand.reset(QgsWkbTypes.GeometryType(3)) click_point_geom = QgsGeometry.fromPointXY(self.geometry_class.geometry) buffer_geom = click_point_geom.buffer(self.settingsWidget.spinBox_searchArea.value(),20) lines_layers = self.getLayers(1) polygons_layers = self.getLayers(2) len_line_layers = 0 len_polygon_layers = 0 try: len_line_layers += len(lines_layers) except TypeError: pass try: len_polygon_layers += len(polygons_layers) except TypeError: pass if len_polygon_layers == 0 and len_line_layers == 0: QMessageBox.warning(None,'Missing restrictive layers', 'Select one of the settings') return self.target_geom = None # Polygon layers if self.settingsWidget.checkBoxPolygon.isChecked() and len_polygon_layers > 0: for l in polygons_layers: poly_layer = l[0] for f in poly_layer.getFeatures(buffer_geom.boundingBox()): f_geom = f.geometry() if f_geom.intersects(click_point_geom): QMessageBox.warning(None,'No space to fill', 'No space to fill, choose a different location') return if f_geom.intersects(buffer_geom): buffer_geom = buffer_geom.difference(f_geom) if not poly_layer.isEditable(): poly_layer.startEditing() for part in buffer_geom.parts(): part_wkt = part.asWkt() part_geom = QgsGeometry.fromWkt(part_wkt) if part_geom.intersects(click_point_geom): target_geom = part_geom break # Line layers if self.settingsWidget.checkBoxLine.isChecked(): for l in lines_layers: line_layer = l[0] features = [i.geometry() for i in line_layer.getFeatures(buffer_geom.boundingBox())] buffer_geom_linestring = self.polygon2Linestring(buffer_geom) if buffer_geom_linestring != None: features.append(buffer_geom_linestring) features_unary_geom = QgsGeometry.unaryUnion(features) features_unary_geom_parts = [QgsGeometry.fromWkt(i.asWkt()) for i in features_unary_geom.parts()] features_polygonize = QgsGeometry.polygonize(features_unary_geom_parts) for part in features_polygonize.parts(): part_geom = QgsGeometry.fromWkt(part.asWkt()) if part_geom.intersects(click_point_geom): if target_geom == None: target_geom = part_geom else: target_geom = target_geom.intersection(part_geom) break target_geom.convertGeometryCollectionToSubclass(2) for part in target_geom.parts(): if part != None: part_geom = QgsGeometry.fromWkt(part.asWkt()) if part_geom.intersects(click_point_geom): self.target_geom = part_geom if self.target_geom != None: self.rubberBand.setToGeometry(self.target_geom,None) if self.settingsWidget.checkBox_askTargetLayer.isChecked(): self.addMenu() else: t_layer = self.getTargetLayer() if t_layer == None: QMessageBox.warning(None,'Missing target layer', 'Select one of the settings') return else: self.addFeature(t_layer)
def test_signalConnection(self): # remove all layers QgsProject.instance().removeAllMapLayers() # set dependencies and add back layers self.pointsLayer = QgsVectorLayer( "dbname='%s' table=\"node\" (geom) sql=" % self.fn, "points", "spatialite") assert (self.pointsLayer.isValid()) self.linesLayer = QgsVectorLayer( "dbname='%s' table=\"section\" (geom) sql=" % self.fn, "lines", "spatialite") assert (self.linesLayer.isValid()) self.pointsLayer2 = QgsVectorLayer( "dbname='%s' table=\"node2\" (geom) sql=" % self.fn, "_points2", "spatialite") assert (self.pointsLayer2.isValid()) self.pointsLayer.setDependencies( [QgsMapLayerDependency(self.linesLayer.id())]) self.pointsLayer2.setDependencies( [QgsMapLayerDependency(self.pointsLayer.id())]) # this should update connections between layers QgsProject.instance().addMapLayers([self.pointsLayer]) QgsProject.instance().addMapLayers([self.linesLayer]) QgsProject.instance().addMapLayers([self.pointsLayer2]) ms = QgsMapSettings() ms.setOutputSize(QSize(100, 100)) ms.setExtent(QgsRectangle(0, 0, 1, 1)) self.assertTrue(ms.hasValidSettings()) u = QgsSnappingUtils() u.setMapSettings(ms) cfg = u.config() cfg.setEnabled(True) cfg.setMode(QgsSnappingConfig.AdvancedConfiguration) cfg.setIndividualLayerSettings( self.pointsLayer, QgsSnappingConfig.IndividualLayerSettings(True, QgsSnappingConfig.Vertex, 20, QgsTolerance.Pixels)) cfg.setIndividualLayerSettings( self.pointsLayer2, QgsSnappingConfig.IndividualLayerSettings(True, QgsSnappingConfig.Vertex, 20, QgsTolerance.Pixels)) u.setConfig(cfg) # add another line f = QgsFeature(self.linesLayer.fields()) f.setId(4) geom = QgsGeometry.fromWkt("LINESTRING(0.5 0.2,0.6 0)") f.setGeometry(geom) self.linesLayer.startEditing() self.linesLayer.addFeatures([f]) self.linesLayer.commitChanges() # check the second snapped point is OK m = u.snapToMap(QPoint(75, 100 - 0)) self.assertTrue(m.isValid()) self.assertTrue(m.hasVertex()) self.assertEqual(m.point(), QgsPointXY(0.8, 0.0)) self.pointsLayer.setDependencies([]) self.pointsLayer2.setDependencies([])
def setUpClass(cls): """Run before all tests""" # Create test layer cls.vl = QgsVectorLayer( u'Point?crs=epsg:4326&field=pk:integer&field=cnt:integer&field=name:string(0)&field=name2:string(0)&field=num_char:string&key=pk', u'test', u'memory') assert (cls.vl.isValid()) cls.provider = cls.vl.dataProvider() f1 = QgsFeature() f1.setAttributes([5, -200, NULL, 'NuLl', '5']) f1.setGeometry(QgsGeometry.fromWkt('Point (-71.123 78.23)')) f2 = QgsFeature() f2.setAttributes([3, 300, 'Pear', 'PEaR', '3']) f3 = QgsFeature() f3.setAttributes([1, 100, 'Orange', 'oranGe', '1']) f3.setGeometry(QgsGeometry.fromWkt('Point (-70.332 66.33)')) f4 = QgsFeature() f4.setAttributes([2, 200, 'Apple', 'Apple', '2']) f4.setGeometry(QgsGeometry.fromWkt('Point (-68.2 70.8)')) f5 = QgsFeature() f5.setAttributes([4, 400, 'Honey', 'Honey', '4']) f5.setGeometry(QgsGeometry.fromWkt('Point (-65.32 78.3)')) cls.provider.addFeatures([f1, f2, f3, f4, f5]) # poly layer cls.poly_vl = QgsVectorLayer( u'Polygon?crs=epsg:4326&field=pk:integer&key=pk', u'test', u'memory') assert (cls.poly_vl.isValid()) cls.poly_provider = cls.poly_vl.dataProvider() f1 = QgsFeature() f1.setAttributes([1]) f1.setGeometry( QgsGeometry.fromWkt( 'Polygon ((-69.03664108 81.35818902, -69.09237722 80.24346619, -73.718477 80.1319939, -73.718477 76.28620011, -74.88893598 76.34193625, -74.83319983 81.35818902, -69.03664108 81.35818902))' )) f2 = QgsFeature() f2.setAttributes([2]) f2.setGeometry( QgsGeometry.fromWkt( 'Polygon ((-67.58750139 81.1909806, -66.30557012 81.24671674, -66.30557012 76.89929767, -67.58750139 76.89929767, -67.58750139 81.1909806))' )) f3 = QgsFeature() f3.setAttributes([3]) f3.setGeometry( QgsGeometry.fromWkt( 'Polygon ((-68.36780737 75.78457483, -67.53176524 72.60761475, -68.64648808 73.66660144, -70.20710006 72.9420316, -68.36780737 75.78457483))' )) f4 = QgsFeature() f4.setAttributes([4]) cls.poly_provider.addFeatures([f1, f2, f3, f4])
def testChangeFeatures(self): if not getattr(self, 'getEditableLayer', None): return l = self.getEditableLayer() self.assertTrue(l.isValid()) features = [f for f in l.dataProvider().getFeatures()] # find 2 features to change attributes for features = [f for f in l.dataProvider().getFeatures()] # need to keep order here to_change = [f for f in features if f.attributes()[0] == 1] to_change.extend([f for f in features if f.attributes()[0] == 2]) # changes by feature id, for changeAttributeValues call attribute_changes = { to_change[0].id(): { 1: 501, 3: 'new string' }, to_change[1].id(): { 1: 502, 4: 'NEW' } } # changes by pk, for testing after retrieving changed features new_attr_map = {1: {1: 501, 3: 'new string'}, 2: {1: 502, 4: 'NEW'}} # find 2 features to change geometries for to_change = [f for f in features if f.attributes()[0] == 1] to_change.extend([f for f in features if f.attributes()[0] == 3]) # changes by feature id, for changeGeometryValues call geometry_changes = { to_change[0].id(): QgsGeometry.fromWkt('Point (10 20)'), to_change[1].id(): QgsGeometry() } # changes by pk, for testing after retrieving changed features new_geom_map = { 1: QgsGeometry.fromWkt('Point ( 10 20 )'), 3: QgsGeometry() } if l.dataProvider().capabilities( ) & QgsVectorDataProvider.ChangeGeometries and l.dataProvider( ).capabilities() & QgsVectorDataProvider.ChangeAttributeValues: # expect success result = l.dataProvider().changeFeatures(attribute_changes, geometry_changes) self.assertTrue( result, 'Provider reported ChangeGeometries and ChangeAttributeValues capability, but returned False to changeFeatures' ) # check result self.testGetFeatures(l.dataProvider(), changed_attributes=new_attr_map, changed_geometries=new_geom_map) # change empty list, should return true for consistency self.assertTrue(l.dataProvider().changeFeatures({}, {})) elif not l.dataProvider().capabilities( ) & QgsVectorDataProvider.ChangeGeometries: # expect fail self.assertFalse( l.dataProvider().changeFeatures(attribute_changes, geometry_changes), 'Provider reported no ChangeGeometries capability, but returned true to changeFeatures' ) elif not l.dataProvider().capabilities( ) & QgsVectorDataProvider.ChangeAttributeValues: # expect fail self.assertFalse( l.dataProvider().changeFeatures(attribute_changes, geometry_changes), 'Provider reported no ChangeAttributeValues capability, but returned true to changeFeatures' )
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 testSetOutputCrs(self): w = qgis.gui.QgsExtentGroupBox() w.setCheckable(True) # ensure setting output crs doesn't change state of group box w.setChecked(False) w.setOutputCrs(QgsCoordinateReferenceSystem('epsg:4326')) self.assertFalse(w.isChecked()) w.setChecked(True) w.setOutputCrs(QgsCoordinateReferenceSystem('epsg:4326')) self.assertTrue(w.isChecked()) w.setOutputCrs(QgsCoordinateReferenceSystem('epsg:4326')) w.setCurrentExtent(QgsRectangle(1, 2, 3, 4), QgsCoordinateReferenceSystem('epsg:4326')) w.setOutputExtentFromCurrent() self.assertEqual(w.outputExtent(), QgsRectangle(1, 2, 3, 4)) # with reprojection w.setOutputCrs(QgsCoordinateReferenceSystem('epsg:3785')) self.assertEqual( w.outputExtent().toString(4), QgsRectangle(111319.4908, 222684.2085, 333958.4724, 445640.1097).toString(4)) # change CRS back w.setOutputCrs(QgsCoordinateReferenceSystem('epsg:4326')) # extent should be back to current - not a reprojection of the reprojected bounds self.assertEqual(w.outputExtent().toString(20), QgsRectangle(1, 2, 3, 4).toString(20)) # repeat, this time using original extents w = qgis.gui.QgsExtentGroupBox() w.setOutputCrs(QgsCoordinateReferenceSystem('epsg:4326')) w.setOriginalExtent(QgsRectangle(1, 2, 3, 4), QgsCoordinateReferenceSystem('epsg:4326')) w.setOutputExtentFromOriginal() self.assertEqual(w.outputExtent(), QgsRectangle(1, 2, 3, 4)) # with reprojection w.setOutputCrs(QgsCoordinateReferenceSystem('epsg:3785')) self.assertEqual( w.outputExtent().toString(4), QgsRectangle(111319.4908, 222684.2085, 333958.4724, 445640.1097).toString(4)) # change CRS back w.setOutputCrs(QgsCoordinateReferenceSystem('epsg:4326')) # extent should be back to original - not a reprojection of the reprojected bounds self.assertEqual(w.outputExtent().toString(20), QgsRectangle(1, 2, 3, 4).toString(20)) # repeat, this time using layer extent layer = QgsVectorLayer("Polygon?crs=4326", 'memory', 'memory') self.assertTrue(layer.isValid()) f = QgsFeature() f.setGeometry( QgsGeometry.fromWkt('Polygon((1 2, 3 2, 3 4, 1 4, 1 2))')) layer.dataProvider().addFeatures([f]) QgsProject.instance().addMapLayer(layer) w.setOutputCrs(QgsCoordinateReferenceSystem('epsg:4326')) w.setOutputExtentFromLayer(layer) self.assertEqual(w.outputExtent(), QgsRectangle(1, 2, 3, 4)) w.setOutputCrs(QgsCoordinateReferenceSystem('epsg:3785')) self.assertEqual( w.outputExtent().toString(4), QgsRectangle(111319.4908, 222684.2085, 333958.4724, 445640.1097).toString(4)) # change CRS back w.setOutputCrs(QgsCoordinateReferenceSystem('epsg:4326')) # extent should be back to original - not a reprojection of the reprojected bounds self.assertEqual(w.outputExtent().toString(20), QgsRectangle(1, 2, 3, 4).toString(20)) # custom extent w = qgis.gui.QgsExtentGroupBox() w.setOutputCrs(QgsCoordinateReferenceSystem('epsg:4326')) w.setOutputExtentFromUser(QgsRectangle(1, 2, 3, 4), QgsCoordinateReferenceSystem('epsg:4326')) self.assertEqual(w.outputExtent(), QgsRectangle(1, 2, 3, 4)) # with reprojection w.setOutputCrs(QgsCoordinateReferenceSystem('epsg:3785')) self.assertEqual( w.outputExtent().toString(4), QgsRectangle(111319.4908, 222684.2085, 333958.4724, 445640.1097).toString(4)) # change CRS back w.setOutputCrs(QgsCoordinateReferenceSystem('epsg:4326')) # in this case we can't retrieve the original user extent in 4326, so we have a reprojection of the reprojected bounds # just test this by restricting the test to 4 decimals self.assertEqual(w.outputExtent().toString(4), QgsRectangle(1, 2, 3, 4).toString(4))
def testGeopackageTwoLayerEdition(self): ''' test https://issues.qgis.org/issues/17034 ''' tmpfile = os.path.join(self.basetestpath, 'testGeopackageTwoLayerEdition.gpkg') ds = ogr.GetDriverByName('GPKG').CreateDataSource(tmpfile) lyr = ds.CreateLayer('layer1', geom_type=ogr.wkbPoint) lyr.CreateField(ogr.FieldDefn('attr', ogr.OFTInteger)) f = ogr.Feature(lyr.GetLayerDefn()) f.SetGeometry(ogr.CreateGeometryFromWkt('POINT(0 0)')) lyr.CreateFeature(f) f = None lyr = ds.CreateLayer('layer2', geom_type=ogr.wkbPoint) lyr.CreateField(ogr.FieldDefn('attr', ogr.OFTInteger)) f = ogr.Feature(lyr.GetLayerDefn()) f.SetGeometry(ogr.CreateGeometryFromWkt('POINT(1 1)')) lyr.CreateFeature(f) f = None ds = None vl1 = QgsVectorLayer(u'{}'.format(tmpfile) + "|layername=layer1", u'layer1', u'ogr') vl2 = QgsVectorLayer(u'{}'.format(tmpfile) + "|layername=layer2", u'layer2', u'ogr') # Edit vl1, vl2 multiple times self.assertTrue(vl1.startEditing()) self.assertTrue(vl2.startEditing()) self.assertTrue( vl1.changeGeometry(1, QgsGeometry.fromWkt('Point (2 2)'))) self.assertTrue( vl2.changeGeometry(1, QgsGeometry.fromWkt('Point (3 3)'))) self.assertTrue(vl1.commitChanges()) self.assertTrue(vl2.commitChanges()) self.assertTrue(vl1.startEditing()) self.assertTrue(vl2.startEditing()) self.assertTrue(vl1.changeAttributeValue(1, 1, 100)) self.assertTrue(vl2.changeAttributeValue(1, 1, 101)) self.assertTrue(vl1.commitChanges()) self.assertTrue(vl2.commitChanges()) self.assertTrue(vl1.startEditing()) self.assertTrue(vl2.startEditing()) self.assertTrue( vl1.changeGeometry(1, QgsGeometry.fromWkt('Point (4 4)'))) self.assertTrue( vl2.changeGeometry(1, QgsGeometry.fromWkt('Point (5 5)'))) self.assertTrue(vl1.commitChanges()) self.assertTrue(vl2.commitChanges()) vl1 = None vl2 = None # Check everything is as expected after re-opening vl1 = QgsVectorLayer(u'{}'.format(tmpfile) + "|layername=layer1", u'layer1', u'ogr') vl2 = QgsVectorLayer(u'{}'.format(tmpfile) + "|layername=layer2", u'layer2', u'ogr') got = [feat for feat in vl1.getFeatures()][0] got_geom = got.geometry() self.assertEqual(got['attr'], 100) reference = QgsGeometry.fromWkt('Point (4 4)') self.assertEqual( got_geom.asWkb(), reference.asWkb(), 'Expected {}, got {}'.format(reference.asWkt(), got_geom.asWkt())) got = [feat for feat in vl2.getFeatures()][0] got_geom = got.geometry() self.assertEqual(got['attr'], 101) reference = QgsGeometry.fromWkt('Point (5 5)') self.assertEqual( got_geom.asWkb(), reference.asWkb(), 'Expected {}, got {}'.format(reference.asWkt(), got_geom.asWkt()))
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 setUpClass(cls): """Run before all tests""" # Create test layer cls.vl = QgsVectorLayer( 'Point?crs=epsg:4326&index=yes&field=pk:integer&field=cnt:int8&field=name:string(0)&field=name2:string(0)&field=num_char:string&key=pk', 'test', 'memory') assert (cls.vl.isValid()) cls.source = cls.vl.dataProvider() f1 = QgsFeature() f1.setAttributes([5, -200, NULL, 'NuLl', '5']) f1.setGeometry(QgsGeometry.fromWkt('Point (-71.123 78.23)')) f2 = QgsFeature() f2.setAttributes([3, 300, 'Pear', 'PEaR', '3']) f3 = QgsFeature() f3.setAttributes([1, 100, 'Orange', 'oranGe', '1']) f3.setGeometry(QgsGeometry.fromWkt('Point (-70.332 66.33)')) f4 = QgsFeature() f4.setAttributes([2, 200, 'Apple', 'Apple', '2']) f4.setGeometry(QgsGeometry.fromWkt('Point (-68.2 70.8)')) f5 = QgsFeature() f5.setAttributes([4, 400, 'Honey', 'Honey', '4']) f5.setGeometry(QgsGeometry.fromWkt('Point (-65.32 78.3)')) cls.source.addFeatures([f1, f2, f3, f4, f5]) # poly layer cls.poly_vl = QgsVectorLayer( 'Polygon?crs=epsg:4326&index=yes&field=pk:integer&key=pk', 'test', 'memory') assert (cls.poly_vl.isValid()) cls.poly_provider = cls.poly_vl.dataProvider() f1 = QgsFeature() f1.setAttributes([1]) f1.setGeometry( QgsGeometry.fromWkt( 'Polygon ((-69.0 81.4, -69.0 80.2, -73.7 80.2, -73.7 76.3, -74.9 76.3, -74.9 81.4, -69.0 81.4))' )) f2 = QgsFeature() f2.setAttributes([2]) f2.setGeometry( QgsGeometry.fromWkt( 'Polygon ((-67.6 81.2, -66.3 81.2, -66.3 76.9, -67.6 76.9, -67.6 81.2))' )) f3 = QgsFeature() f3.setAttributes([3]) f3.setGeometry( QgsGeometry.fromWkt( 'Polygon ((-68.4 75.8, -67.5 72.6, -68.6 73.7, -70.2 72.9, -68.4 75.8))' )) f4 = QgsFeature() f4.setAttributes([4]) cls.poly_provider.addFeatures([f1, f2, f3, f4])
def test_check_validity(self): """Test that the output invalid contains the error reason""" polygon_layer = self._make_layer('Polygon') self.assertTrue(polygon_layer.startEditing()) f = QgsFeature(polygon_layer.fields()) f.setAttributes([1]) # Flake! f.setGeometry( QgsGeometry.fromWkt('POLYGON ((0 0, 2 2, 0 2, 2 0, 0 0))')) self.assertTrue(f.isValid()) f2 = QgsFeature(polygon_layer.fields()) f2.setAttributes([1]) f2.setGeometry( QgsGeometry.fromWkt( 'POLYGON((1.1 1.1, 1.1 2.1, 2.1 2.1, 2.1 1.1, 1.1 1.1))')) self.assertTrue(f2.isValid()) self.assertTrue(polygon_layer.addFeatures([f, f2])) polygon_layer.commitChanges() polygon_layer.rollBack() self.assertEqual(polygon_layer.featureCount(), 2) QgsProject.instance().addMapLayers([polygon_layer]) alg = self.registry.createAlgorithmById('qgis:checkvalidity') context = QgsProcessingContext() context.setProject(QgsProject.instance()) feedback = ConsoleFeedBack() self.assertIsNotNone(alg) parameters = {} parameters['INPUT_LAYER'] = polygon_layer.id() parameters['VALID_OUTPUT'] = 'memory:' parameters['INVALID_OUTPUT'] = 'memory:' parameters['ERROR_OUTPUT'] = 'memory:' # QGIS method parameters['METHOD'] = 1 ok, results = execute(alg, parameters, context=context, feedback=feedback) self.assertTrue(ok) invalid_layer = QgsProcessingUtils.mapLayerFromString( results['INVALID_OUTPUT'], context) self.assertEqual(invalid_layer.fields().names()[-1], '_errors') self.assertEqual(invalid_layer.featureCount(), 1) f = next(invalid_layer.getFeatures()) self.assertEqual(f.attributes(), [1, 'segments 0 and 2 of line 0 intersect at 1, 1']) # GEOS method parameters['METHOD'] = 2 ok, results = execute(alg, parameters, context=context, feedback=feedback) self.assertTrue(ok) invalid_layer = QgsProcessingUtils.mapLayerFromString( results['INVALID_OUTPUT'], context) self.assertEqual(invalid_layer.fields().names()[-1], '_errors') self.assertEqual(invalid_layer.featureCount(), 1) f = next(invalid_layer.getFeatures()) self.assertEqual(f.attributes(), [1, 'Self-intersection'])
def testGeometryRendering(self): '''Tests rendering a bunch of different geometries, including bad/odd geometries.''' tests = [ { 'name': 'Point', 'wkt': 'Point (1 2)', 'reference_image': 'point' }, { 'name': 'MultiPoint', 'wkt': 'MultiPoint ((10 30),(40 20),(30 10),(20 10))', 'reference_image': 'multipoint' }, { 'name': 'LineString', 'wkt': 'LineString (0 0,3 4,4 3)', 'reference_image': 'linestring' }, { 'name': 'MultiLineString', 'wkt': 'MultiLineString ((0 0, 1 0, 1 1, 2 1, 2 0), (3 1, 5 1, 5 0, 6 0))', 'reference_image': 'multilinestring' }, { 'name': 'Polygon', 'wkt': 'Polygon ((0 0, 10 0, 10 10, 0 10, 0 0),(5 5, 7 5, 7 7 , 5 7, 5 5))', 'reference_image': 'polygon' }, { 'name': 'MultiPolygon', 'wkt': 'MultiPolygon (((0 0, 1 0, 1 1, 2 1, 2 2, 0 2, 0 0)),((4 0, 5 0, 5 2, 3 2, 3 1, 4 1, 4 0)))', 'reference_image': 'multipolygon' }, { 'name': 'CircularString', 'wkt': 'CIRCULARSTRING(268 415,227 505,227 406)', 'reference_image': 'circular_string' }, { 'name': 'CompoundCurve', 'wkt': 'COMPOUNDCURVE((5 3, 5 13), CIRCULARSTRING(5 13, 7 15, 9 13), (9 13, 9 3), CIRCULARSTRING(9 3, 7 1, 5 3))', 'reference_image': 'compound_curve' }, { 'name': 'CurvePolygon', 'wkt': 'CURVEPOLYGON(CIRCULARSTRING(1 3, 3 5, 4 7, 7 3, 1 3))', 'reference_image': 'curve_polygon' }, { 'name': 'MultiCurve', 'wkt': 'MultiCurve((5 5,3 5,3 3,0 3),CIRCULARSTRING(0 0, 2 1,2 2))', 'reference_image': 'multicurve' }, { 'name': 'CurvePolygon_no_arc', # refs #14028 'wkt': 'CURVEPOLYGON(LINESTRING(1 3, 3 5, 4 7, 7 3, 1 3))', 'reference_image': 'curve_polygon_no_arc' } ] for test in tests: geom = QgsGeometry.fromWkt(test['wkt']) assert geom and not geom.isNull( ), 'Could not create geometry {}'.format(test['wkt']) rendered_image = self.renderGeometry(geom) assert self.imageCheck(test['name'], test['reference_image'], rendered_image) #Note - each test is repeated with the same geometry and reference image, but with added #z and m dimensions. This tests that presence of the dimensions does not affect rendering #test with Z geom_z = QgsGeometry.fromWkt(test['wkt']) geom_z.geometry().addZValue(5) rendered_image = self.renderGeometry(geom_z) assert self.imageCheck(test['name'] + 'Z', test['reference_image'], rendered_image) #test with ZM geom_z.geometry().addMValue(15) rendered_image = self.renderGeometry(geom_z) assert self.imageCheck(test['name'] + 'ZM', test['reference_image'], rendered_image) #test with M geom_m = QgsGeometry.fromWkt(test['wkt']) geom_m.geometry().addMValue(15) rendered_image = self.renderGeometry(geom_m) assert self.imageCheck(test['name'] + 'M', test['reference_image'], rendered_image)
def testLines(self): shape = QgsLegendPatchShape( QgsSymbol.Line, QgsGeometry.fromWkt('LineString(5 5, 1 2)'), False) self.assertEqual( self.polys_to_list(shape.toQPolygonF(QgsSymbol.Line, QSizeF(1, 1))), [[[[1.0, 0.0], [0.0, 1.0]]]]) self.assertEqual( self.polys_to_list(shape.toQPolygonF(QgsSymbol.Line, QSizeF(10, 2))), [[[[10.0, 0.0], [0.0, 2.0]]]]) shape = QgsLegendPatchShape( QgsSymbol.Line, QgsGeometry.fromWkt('LineString(1 5, 6 5)'), False) self.assertEqual( self.polys_to_list(shape.toQPolygonF(QgsSymbol.Line, QSizeF(1, 1))), [[[[0.0, 0.5], [1.0, 0.5]]]]) self.assertEqual( self.polys_to_list(shape.toQPolygonF(QgsSymbol.Line, QSizeF(10, 2))), [[[[0.0, 1], [10.0, 1.0]]]]) shape = QgsLegendPatchShape( QgsSymbol.Line, QgsGeometry.fromWkt('LineString(1 5, 1 10)'), False) self.assertEqual( self.polys_to_list(shape.toQPolygonF(QgsSymbol.Line, QSizeF(1, 1))), [[[[0.5, 0.0], [0.5, 1.0]]]]) self.assertEqual( self.polys_to_list(shape.toQPolygonF(QgsSymbol.Line, QSizeF(10, 2))), [[[[5, 0.0], [5, 2.0]]]]) # requesting different symbol type, should return default self.assertEqual( self.polys_to_list(shape.toQPolygonF(QgsSymbol.Fill, QSizeF(1, 1))), [[[[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0], [0.0, 0.0]]]]) # circularstring shape = QgsLegendPatchShape( QgsSymbol.Line, QgsGeometry.fromWkt('CircularString(5 5, 1 2, 3 4)'), False) self.assertEqual( self.polys_to_list(shape.toQPolygonF(QgsSymbol.Line, QSizeF(1, 1)))[0][0][:5], [[0.342, 0.026], [0.35, 0.023], [0.359, 0.02], [0.367, 0.018], [0.375, 0.016]]) self.assertEqual( self.polys_to_list(shape.toQPolygonF(QgsSymbol.Line, QSizeF(10, 2)))[0][0][:5], [[3.419, 0.051], [3.647, 0.042], [3.875, 0.036], [4.104, 0.034], [4.332, 0.036]]) # multilinestring shape = QgsLegendPatchShape( QgsSymbol.Line, QgsGeometry.fromWkt('MultiLineString((5 5, 1 2),(3 6, 4 2))'), False) self.assertEqual( self.polys_to_list(shape.toQPolygonF(QgsSymbol.Line, QSizeF(1, 1))), [[[[1.0, 0.25], [0.0, 1.0]]], [[[0.5, 0.0], [0.75, 1.0]]]]) self.assertEqual( self.polys_to_list(shape.toQPolygonF(QgsSymbol.Line, QSizeF(10, 2))), [[[[10.0, 0.5], [0.0, 2.0]]], [[[5.0, 0.0], [7.5, 2.0]]]])
def testRenderLine(self): shape = QgsLegendPatchShape( QgsSymbol.Line, QgsGeometry.fromWkt('LineString(5 5, 3 4, 1 2)'), False) rendered_image = self.renderPatch(shape) self.assertTrue(self.imageCheck('Line', 'line', rendered_image))
def testPolygonGenerationRelativeExtrusion(self): vl = QgsVectorLayer('PolygonZ?crs=EPSG:27700', 'lines', 'memory') self.assertTrue(vl.isValid()) for line in [ 'PolygonZ ((321829.48893365426920354 129991.38697145861806348 1, 321847.89668515208177269 129996.63588572069420479 1, 321848.97131609614007175 129979.22330882755341008 1, 321830.31725845142500475 129978.07136809575604275 1, 321829.48893365426920354 129991.38697145861806348 1))', 'PolygonZ ((321920.00953056826256216 129924.58260190498549491 2, 321924.65299345907988027 129908.43546159457764588 2, 321904.78543491888558492 129903.99811821122420952 2, 321900.80605239619035274 129931.39860145389684476 2, 321904.84799937985371798 129931.71552911199978553 2, 321908.93646715773502365 129912.90030360443051904 2, 321914.20495146053144708 129913.67693978428724222 2, 321911.30165811872575432 129923.01272751353099011 2, 321920.00953056826256216 129924.58260190498549491 2))', 'PolygonZ ((321923.10517279652412981 129919.61521573827485554 3, 321922.23537852568551898 129928.3598982143739704 3, 321928.60423935484141111 129934.22530528216157109 3, 321929.39881197665818036 129923.29054521876969375 3, 321930.55804549407912418 129916.53248518184409477 3, 321923.10517279652412981 129919.61521573827485554 3))', 'PolygonZ ((321990.47451346553862095 129909.63588680300745182 4, 321995.04325810901354998 129891.84052284323843196 4, 321989.66826330573530868 129890.5092018858413212 4, 321990.78512359503656626 129886.49917887404444627 4, 321987.37291929306229576 129885.64982962771318853 4, 321985.2254804756375961 129893.81317058412241749 4, 321987.63158903241856024 129894.41078495365218259 4, 321984.34022761805681512 129907.57450046355370432 4, 321990.47451346553862095 129909.63588680300745182 4))', 'PolygonZ ((322103.03910495212767273 129795.91051736124791205 5, 322108.25568856322206557 129804.76113295342656784 5, 322113.29666162584908307 129803.9285887333098799 5, 322117.78645010641776025 129794.48194090687320568 5, 322103.03910495212767273 129795.91051736124791205 5))']: f = QgsFeature() f.setGeometry(QgsGeometry.fromWkt(line)) self.assertTrue(vl.dataProvider().addFeature(f)) vl.elevationProperties().setClamping(Qgis.AltitudeClamping.Relative) vl.elevationProperties().setZScale(2.5) vl.elevationProperties().setZOffset(10) vl.elevationProperties().setExtrusionEnabled(True) vl.elevationProperties().setExtrusionHeight(7) curve = QgsLineString() curve.fromWkt( 'LineString (-347701.59207547508412972 6632766.96282589063048363, -346577.00878971704514697 6632369.7371364813297987, -346449.93654899462126195 6632331.81857067719101906, -346383.52035177784273401 6632216.85897350125014782)') req = QgsProfileRequest(curve) req.setTransformContext(self.create_transform_context()) rl = QgsRasterLayer(os.path.join(unitTestDataPath(), '3d', 'dtm.tif'), 'DTM') self.assertTrue(rl.isValid()) terrain_provider = QgsRasterDemTerrainProvider() terrain_provider.setLayer(rl) terrain_provider.setScale(0.3) terrain_provider.setOffset(-5) req.setTerrainProvider(terrain_provider) req.setCrs(QgsCoordinateReferenceSystem('EPSG:3857')) generator = vl.createProfileGenerator(req) self.assertTrue(generator.generateProfile()) results = generator.takeResults() if QgsProjUtils.projVersionMajor() >= 8: self.assertEqual(self.round_dict(results.distanceToHeightMap(), 1), {1041.8: 60.3, 1042.4: 60.2, 1049.5: 60.2, 1070.2: 60.2, 1073.1: 60.2, 1074.8: 60.3, 1078.9: 62.0, 1083.9: 62.0, 1091.1: 62.0, 1186.8: 59.3, 1189.8: 59.2, 1192.7: 59.2, 1199.2: 59.2, 1450.0: 65.5, 1455.6: 65.5, 1458.1: 65.5}) self.assertAlmostEqual(results.zRange().lower(), 59.2499, 2) self.assertAlmostEqual(results.zRange().upper(), 72.50000, 2) else: self.assertEqual(self.round_dict(results.distanceToHeightMap(), 1), {1041.8: 53.5, 1042.4: 53.5, 1049.5: 53.5, 1070.2: 53.5, 1073.1: 53.5, 1074.8: 53.5, 1078.9: 56.0, 1083.9: 56.0, 1091.1: 56.0, 1186.8: 62.3, 1189.8: 62.3, 1192.7: 62.3, 1199.2: 62.2, 1450.0: 67.0, 1455.6: 67.0, 1458.1: 67.0}) self.assertAlmostEqual(results.zRange().lower(), 53.5, 2) self.assertAlmostEqual(results.zRange().upper(), 74.00000, 2) if QgsProjUtils.projVersionMajor() >= 8: self.assertCountEqual([g.asWkt(1) for g in results.asGeometries()], [ 'PolygonZ ((-346718.7 6632419.8 60.3, -346712 6632417.4 60.3, -346712 6632417.4 67.3, -346718.7 6632419.8 67.3, -346718.7 6632419.8 60.3))', 'PolygonZ ((-346719.3 6632420 60.3, -346718.7 6632419.8 60.2, -346718.7 6632419.8 67.3, -346719.3 6632420 67.3, -346719.3 6632420 60.3))', 'PolygonZ ((-346689.7 6632409.5 60.3, -346688.2 6632409 60.3, -346688.2 6632409 67.3, -346689.7 6632409.5 67.3, -346689.7 6632409.5 60.3))', 'PolygonZ ((-346692.5 6632410.5 60.3, -346689.7 6632409.5 60.3, -346689.7 6632409.5 67.3, -346692.5 6632410.5 67.3, -346692.5 6632410.5 60.3))', 'PolygonZ ((-346684.3 6632407.6 62, -346679.6 6632406 62, -346679.6 6632406 69, -346684.3 6632407.6 69, -346684.3 6632407.6 62))', 'PolygonZ ((-346679.6 6632406 62, -346672.8 6632403.6 62, -346672.8 6632403.6 69, -346679.6 6632406 69, -346679.6 6632406 62))', 'PolygonZ ((-346582.6 6632371.7 59.3, -346579.7 6632370.7 59.3, -346579.7 6632370.7 66.3, -346582.6 6632371.7 66.3, -346582.6 6632371.7 59.3))', 'PolygonZ ((-346579.7 6632370.7 59.3, -346577 6632369.7 59.2, -346570.8 6632367.9 59.3, -346570.8 6632367.9 66.3, -346577 6632369.7 66.3, -346579.7 6632370.7 66.3, -346579.7 6632370.7 59.3))', 'PolygonZ ((-346387.6 6632223.9 65.5, -346384.8 6632219 65.5, -346384.8 6632219 72.5, -346387.6 6632223.9 72.5, -346387.6 6632223.9 65.5))', 'PolygonZ ((-346384.8 6632219 65.5, -346383.5 6632216.9 65.5, -346383.5 6632216.9 72.5, -346384.8 6632219 72.5, -346384.8 6632219 65.5))']) else: self.assertCountEqual([g.asWkt(1) for g in results.asGeometries()], [ 'PolygonZ ((-346718.7 6632419.8 53.5, -346712 6632417.4 53.5, -346712 6632417.4 60.5, -346718.7 6632419.8 60.5, -346718.7 6632419.8 53.5))', 'PolygonZ ((-346719.3 6632420 53.5, -346718.7 6632419.8 53.5, -346718.7 6632419.8 60.5, -346719.3 6632420 60.5, -346719.3 6632420 53.5))', 'PolygonZ ((-346689.7 6632409.5 53.5, -346688.2 6632409 53.5, -346688.2 6632409 60.5, -346689.7 6632409.5 60.5, -346689.7 6632409.5 53.5))', 'PolygonZ ((-346692.5 6632410.5 53.5, -346689.7 6632409.5 53.5, -346689.7 6632409.5 60.5, -346692.5 6632410.5 60.5, -346692.5 6632410.5 53.5))', 'PolygonZ ((-346684.3 6632407.6 56, -346679.6 6632406 56, -346679.6 6632406 63, -346684.3 6632407.6 63, -346684.3 6632407.6 56))', 'PolygonZ ((-346679.6 6632406 56, -346672.8 6632403.6 56, -346672.8 6632403.6 63, -346679.6 6632406 63, -346679.6 6632406 56))', 'PolygonZ ((-346582.6 6632371.7 62.3, -346579.7 6632370.7 62.3, -346579.7 6632370.7 69.3, -346582.6 6632371.7 69.3, -346582.6 6632371.7 62.3))', 'PolygonZ ((-346579.7 6632370.7 62.3, -346577 6632369.7 62.3, -346570.8 6632367.9 62.3, -346570.8 6632367.9 69.3, -346577 6632369.7 69.3, -346579.7 6632370.7 69.3, -346579.7 6632370.7 62.3))', 'PolygonZ ((-346387.6 6632223.9 67, -346384.8 6632219 67, -346384.8 6632219 74, -346387.6 6632223.9 74, -346387.6 6632223.9 67))', 'PolygonZ ((-346384.8 6632219 67, -346383.5 6632216.9 67, -346383.5 6632216.9 74, -346384.8 6632219 74, -346384.8 6632219 67))'])
def testFills(self): shape = QgsLegendPatchShape( QgsSymbol.Fill, QgsGeometry.fromWkt('Polygon((5 5, 1 2, 3 4, 5 5))'), False) self.assertEqual( self.polys_to_list(shape.toQPolygonF(QgsSymbol.Fill, QSizeF(1, 1))), [[[[1.0, 0.0], [0.0, 1.0], [0.5, 0.333], [1.0, 0.0]]]]) self.assertEqual( self.polys_to_list(shape.toQPolygonF(QgsSymbol.Fill, QSizeF(10, 2))), [[[[10.0, 0.0], [0.0, 2.0], [5.0, 0.667], [10.0, 0.0]]]]) # requesting different symbol type, should return default self.assertEqual( self.polys_to_list(shape.toQPolygonF(QgsSymbol.Line, QSizeF(1, 1))), [[[[0.0, 0.5], [1.0, 0.5]]]]) # rings shape = QgsLegendPatchShape( QgsSymbol.Fill, QgsGeometry.fromWkt( 'Polygon((5 5, 1 2, 3 4, 5 5), (4.5 4.5, 4.4 4.4, 4.5 4.4, 4.5 4.5))' ), False) self.assertEqual( self.polys_to_list(shape.toQPolygonF(QgsSymbol.Fill, QSizeF(1, 1))), [[[[1.0, 0.0], [0.0, 1.0], [0.5, 0.333], [1.0, 0.0]], [[0.875, 0.167], [0.85, 0.2], [0.875, 0.2], [0.875, 0.167]]]]) self.assertEqual( self.polys_to_list(shape.toQPolygonF(QgsSymbol.Fill, QSizeF(10, 2))), [[[[10.0, 0.0], [0.0, 2.0], [5.0, 0.667], [10.0, 0.0]], [[8.75, 0.333], [8.5, 0.4], [8.75, 0.4], [8.75, 0.333]]]]) # circular shape = QgsLegendPatchShape( QgsSymbol.Fill, QgsGeometry.fromWkt( 'CurvePolygon(CircularString(5 5, 3 4, 1 2, 3 0, 5 5))'), False) self.assertEqual( self.polys_to_list(shape.toQPolygonF(QgsSymbol.Fill, QSizeF(1, 1)))[0][0][:5], [[0.746, -0.0], [0.722, 0.009], [0.698, 0.018], [0.675, 0.028], [0.651, 0.038]]) self.assertEqual( self.polys_to_list(shape.toQPolygonF(QgsSymbol.Fill, QSizeF(10, 2)))[0][0][:5], [[7.459, -0.0], [6.83, 0.04], [6.201, 0.09], [5.574, 0.151], [4.947, 0.223]]) # multipolygon shape = QgsLegendPatchShape( QgsSymbol.Fill, QgsGeometry.fromWkt( 'MultiPolygon(((5 5, 1 2, 3 4, 5 5), (4.5 4.5, 4.4 4.4, 4.5 4.4, 4.5 4.5)),((10 11, 11 11, 11 10, 10 11)))' ), False) self.assertEqual( self.polys_to_list(shape.toQPolygonF(QgsSymbol.Fill, QSizeF(1, 1))), [[[[0.4, 0.667], [0.0, 1.0], [0.2, 0.778], [0.4, 0.667]], [[0.35, 0.722], [0.34, 0.733], [0.35, 0.733], [0.35, 0.722]]], [[[0.9, 0.0], [1.0, 0.0], [1.0, 0.111], [0.9, 0.0]]]]) self.assertEqual( self.polys_to_list(shape.toQPolygonF(QgsSymbol.Fill, QSizeF(10, 2))), [[[[4.0, 1.333], [0.0, 2.0], [2.0, 1.556], [4.0, 1.333]], [[3.5, 1.444], [3.4, 1.467], [3.5, 1.467], [3.5, 1.444]]], [[[9.0, 0.0], [10.0, 0.0], [10.0, 0.222], [9.0, 0.0]]]])
def test_make_features_compatible_attributes(self): """Test corner cases for attributes""" # Test feature with attributes fields = QgsFields() fields.append(QgsField('int_f', QVariant.Int)) fields.append(QgsField('str_f', QVariant.String)) f1 = QgsFeature(fields) f1['int_f'] = 1 f1['str_f'] = 'str' f1.setGeometry(QgsGeometry.fromWkt('Point(9 45)')) f2 = f1 QgsVectorLayerUtils.matchAttributesToFields(f2, fields) self.assertEqual(f1.attributes(), f2.attributes()) self.assertTrue(f1.geometry().asWkt(), f2.geometry().asWkt()) # Test pad with 0 with fields f1.setAttributes([]) QgsVectorLayerUtils.matchAttributesToFields(f1, fields) self.assertEqual(len(f1.attributes()), 2) self.assertEqual(f1.attributes()[0], QVariant()) self.assertEqual(f1.attributes()[1], QVariant()) # Test pad with 0 without fields f1 = QgsFeature() QgsVectorLayerUtils.matchAttributesToFields(f1, fields) self.assertEqual(len(f1.attributes()), 2) self.assertEqual(f1.attributes()[0], QVariant()) self.assertEqual(f1.attributes()[1], QVariant()) # Test drop extra attrs f1 = QgsFeature(fields) f1.setAttributes([1, 'foo', 'extra']) QgsVectorLayerUtils.matchAttributesToFields(f1, fields) self.assertEqual(len(f1.attributes()), 2) self.assertEqual(f1.attributes()[0], 1) self.assertEqual(f1.attributes()[1], 'foo') # Rearranged fields fields2 = QgsFields() fields2.append(QgsField('str_f', QVariant.String)) fields2.append(QgsField('int_f', QVariant.Int)) f1 = QgsFeature(fields2) f1.setAttributes([1, 'foo', 'extra']) QgsVectorLayerUtils.matchAttributesToFields(f1, fields) self.assertEqual(len(f1.attributes()), 2) self.assertEqual(f1.attributes()[0], 'foo') self.assertEqual(f1.attributes()[1], 1) # mixed fields2.append(QgsField('extra', QVariant.String)) fields.append(QgsField('extra2', QVariant.Int)) f1.setFields(fields2) f1.setAttributes([1, 'foo', 'blah']) QgsVectorLayerUtils.matchAttributesToFields(f1, fields) self.assertEqual(len(f1.attributes()), 3) self.assertEqual(f1.attributes()[0], 'foo') self.assertEqual(f1.attributes()[1], 1) self.assertEqual(f1.attributes()[2], QVariant()) fields.append(QgsField('extra', QVariant.Int)) f1.setAttributes([1, 'foo', 'blah']) QgsVectorLayerUtils.matchAttributesToFields(f1, fields) self.assertEqual(len(f1.attributes()), 4) self.assertEqual(f1.attributes()[0], 'foo') self.assertEqual(f1.attributes()[1], 1) self.assertEqual(f1.attributes()[2], QVariant()) self.assertEqual(f1.attributes()[3], 'blah') # case insensitive fields2.append(QgsField('extra3', QVariant.String)) fields.append(QgsField('EXTRA3', QVariant.Int)) f1.setFields(fields2) f1.setAttributes([1, 'foo', 'blah', 'blergh']) QgsVectorLayerUtils.matchAttributesToFields(f1, fields) self.assertEqual(len(f1.attributes()), 5) self.assertEqual(f1.attributes()[0], 'foo') self.assertEqual(f1.attributes()[1], 1) self.assertEqual(f1.attributes()[2], QVariant()) self.assertEqual(f1.attributes()[3], 'blah') self.assertEqual(f1.attributes()[4], 'blergh')
def testWriteShapefileWithZ(self): """Check writing geometries with Z dimension to an ESRI shapefile.""" # start by saving a memory layer and forcing z ml = QgsVectorLayer(('Point?crs=epsg:4326&field=id:int'), 'test', 'memory') self.assertIsNotNone(ml, 'Provider not initialized') self.assertTrue(ml.isValid(), 'Source layer not valid') provider = ml.dataProvider() self.assertIsNotNone(provider) ft = QgsFeature() ft.setGeometry(QgsGeometry.fromWkt('PointZ (1 2 3)')) ft.setAttributes([1]) res, features = provider.addFeatures([ft]) self.assertTrue(res) self.assertTrue(features) # check with both a standard PointZ and 25d style Point25D type for t in [QgsWkbTypes.PointZ, QgsWkbTypes.Point25D]: dest_file_name = os.path.join( str(QDir.tempPath()), 'point_{}.shp'.format(QgsWkbTypes.displayString(t))) crs = QgsCoordinateReferenceSystem() crs.createFromId(4326, QgsCoordinateReferenceSystem.EpsgCrsId) write_result, error_message = QgsVectorFileWriter.writeAsVectorFormat( ml, dest_file_name, 'utf-8', crs, 'ESRI Shapefile', overrideGeometryType=t) self.assertEqual(write_result, QgsVectorFileWriter.NoError, error_message) # Open result and check created_layer = QgsVectorLayer( '{}|layerid=0'.format(dest_file_name), 'test', 'ogr') f = next(created_layer.getFeatures(QgsFeatureRequest())) g = f.geometry() wkt = g.asWkt() expWkt = 'PointZ (1 2 3)' self.assertTrue( compareWkt(expWkt, wkt), "saving geometry with Z failed: mismatch Expected:\n%s\nGot:\n%s\n" % (expWkt, wkt)) # also try saving out the shapefile version again, as an extra test # this tests that saving a layer with z WITHOUT explicitly telling the writer to keep z values, # will stay retain the z values dest_file_name = os.path.join( str(QDir.tempPath()), 'point_{}_copy.shp'.format(QgsWkbTypes.displayString(t))) crs = QgsCoordinateReferenceSystem() crs.createFromId(4326, QgsCoordinateReferenceSystem.EpsgCrsId) write_result, error_message = QgsVectorFileWriter.writeAsVectorFormat( created_layer, dest_file_name, 'utf-8', crs, 'ESRI Shapefile') self.assertEqual(write_result, QgsVectorFileWriter.NoError, error_message) # Open result and check created_layer_from_shp = QgsVectorLayer( '{}|layerid=0'.format(dest_file_name), 'test', 'ogr') f = next(created_layer_from_shp.getFeatures(QgsFeatureRequest())) g = f.geometry() wkt = g.asWkt() self.assertTrue( compareWkt(expWkt, wkt), "saving geometry with Z failed: mismatch Expected:\n%s\nGot:\n%s\n" % (expWkt, wkt))
def processAlgorithm(self, feedback): radius = self.getParameterValue(self.DISTANCE) horizontal = self.getParameterValue(self.HORIZONTAL) output = self.getOutputFromName(self.OUTPUT_LAYER) layer = dataobjects.getObjectFromUri( self.getParameterValue(self.INPUT_LAYER)) writer = output.getVectorWriter(layer.fields(), layer.wkbType(), layer.crs()) features = vector.features(layer) total = 100.0 / len(features) duplicates = dict() for current, f in enumerate(features): wkt = f.geometry().exportToWkt() if wkt not in duplicates: duplicates[wkt] = [f.id()] else: duplicates[wkt].extend([f.id()]) feedback.setProgress(int(current * total)) current = 0 total = 100.0 / len(duplicates) feedback.setProgress(0) fullPerimeter = 2 * math.pi for (geom, fids) in list(duplicates.items()): count = len(fids) if count == 1: f = next( layer.getFeatures(QgsFeatureRequest().setFilterFid( fids[0]))) writer.addFeature(f) else: angleStep = fullPerimeter / count if count == 2 and horizontal: currentAngle = math.pi / 2 else: currentAngle = 0 old_point = QgsGeometry.fromWkt(geom).asPoint() request = QgsFeatureRequest().setFilterFids(fids).setFlags( QgsFeatureRequest.NoGeometry) for f in layer.getFeatures(request): sinusCurrentAngle = math.sin(currentAngle) cosinusCurrentAngle = math.cos(currentAngle) dx = radius * sinusCurrentAngle dy = radius * cosinusCurrentAngle new_point = QgsPoint(old_point.x() + dx, old_point.y() + dy) out_feature = QgsFeature() out_feature.setGeometry(QgsGeometry.fromPoint(new_point)) out_feature.setAttributes(f.attributes()) writer.addFeature(out_feature) currentAngle += angleStep current += 1 feedback.setProgress(int(current * total)) del writer
def predict_points(self, output_layer, transform=""): """ Case connection node entry knows an endpoint -------------------------------------------- Whenever an endpoint entry is present the current connection node is either a network endpoint or the endpoint belongs to the highest ranking calculation type. Either way, it has to be added to the feature collection. Once it is known that the starting point has been added, the objects with their own geometry (culverts, channels, pipes) for which the calculation points have to be predicted, do not need to add their starting point to the collection anymore. Case connection node entry doesn't know of an endpoint ------------------------------------------------------ The starting point will be calculated based on start point of the first line geometry in combination with the calculation type attributes for the connection node of the current iteration. The object, that belongs to the line geometry will be matched against the calculation type information of the connection node. The outcome of the match is important to be able to produce correct ``user_ref_ids`` because they contain a substring based on the count of the calculation points belonging to the same object. """ self._feat_id = 1 data_provider = output_layer.dataProvider() if transform: self._trans = get_coord_transformation_instance(*transform.split(":")) # self._trans = self._set_coord_transformation(transform) for node_id, node_info in self.network_dict.items(): logger.debug("processing node_id {}".format(node_id)) # for the first point we need the network calc_type node_calc_type = node_info["calc_type"] # an entry for end_point means we have to use this his information # over other information for the node end_point = node_info.get("end_point") node_has_been_added = False start_id = 1 if end_point: content_type = end_point["content_type"] content_type_id = end_point["content_type_id"] code = end_point["code"] pnt_geom = QgsGeometry.fromWkt(end_point["the_geom_end"]) last_seq_id = end_point["cnt_segments"] # if the same objects will used elsewhere as starting point # the sequence of calculation points will be longer (by one) last_seq_id += 1 self._add_calc_pnt_feature( calc_type=node_calc_type, pnt_geom=pnt_geom, content_type_id=content_type_id, content_type=content_type, code=code, id=last_seq_id, ) node_has_been_added = True start_id = 2 start_points = node_info.get("start_points") for start_pnt_cnt, start_point in enumerate(start_points): content_type = start_point["content_type"] content_type_id = start_point["content_type_id"] code = start_point["code"] # the calculation type for the interpolated points calc_type = start_point["calc_type"] distances = self.get_distances_on_line( start_point["dist_calc_pnts"], start_point["line_length"] ) logger.debug("processing start point {}".format(start_pnt_cnt)) line_geom = QgsGeometry.fromWkt(start_point["the_geom"]) if not node_has_been_added: # find out if the node info has been derived # from the object we are looking at right now # so we can produce the corect meta data like # calc_type, user-ref-id,... distance = distances.pop(0) start_pnt = line_geom.interpolate(distance) self._add_calc_pnt_feature( calc_type=node_calc_type, pnt_geom=start_pnt, content_type_id=node_info["content_type_id"], content_type=node_info["content_type"], code=node_info["code"], id=1, ) node_has_been_added = True start_id = 2 logger.debug( "node has been added {} {}".format( content_type, content_type_id ) ) else: distances = distances[1:] for i, dist in enumerate(distances, start=start_id): # Get a point along the line at the current distance point_on_line = line_geom.interpolate(dist) # add start and endpoint self._add_calc_pnt_feature( calc_type=calc_type, pnt_geom=point_on_line, content_type_id=content_type_id, content_type=content_type, code=code, id=i, ) succces, features = data_provider.addFeatures(self._calc_pnt_features) cnt_feat = len(features) if succces: logger.info( "[*] Successfully saved {} features to the database".format(cnt_feat) ) if not self.threedi_db.has_valid_spatial_index( constants.TABLE_NAME_CALC_PNT, "the_geom" ): self.threedi_db.recover_spatial_index( constants.TABLE_NAME_CALC_PNT, "the_geom" ) output_layer.updateExtents() else: logger.error("Error while saving {} feaures to database.".format(cnt_feat)) return succces, features
def testQgsMultiPointRepr(self): wkt = "MultiPoint ((10 30),(40 20),(30 10),(20 10))" mp = QgsGeometry.fromWkt(wkt) self.assertEqual( mp.constGet().__repr__(), '<QgsMultiPoint: MultiPoint ((10 30),(40 20),(30 10),(20 10))>')
def run(self): # Remember the first value is an indication of whether dataset is # wrapped across 180th meridian wkts = self.aoi.meridian_split('layer', 'wkt', warn=False)[1] bbs = self.aoi.get_aligned_output_bounds(self.f_loss_vrt) for n in range(len(wkts)): if self.isCanceled(): return False ###################################################################### # Clip layers # Combines SDG 15.3.1 input raster into a VRT and crop to the AOI indic_vrt = GetTempFilename('.vrt') log(u'Saving indicator VRT to: {}'.format(indic_vrt)) # The plus one is because band numbers start at 1, not zero gdal.BuildVRT(indic_vrt, [self.f_loss_vrt, self.tc_vrt], outputBounds=bbs[n], resolution='highest', resampleAlg=gdal.GRA_NearestNeighbour, separate=True) clipped_vrt = GetTempFilename('.tif') log(u'Saving forest loss/carbon clipped file to {}'.format(clipped_vrt)) #clip_task = QgsProcessingAlgRunnerTask( clip_task = processing.run( 'trendsearth:raster_clip', { 'INPUT': indic_vrt, 'GEOJSON': json.dumps(json_geom_to_geojson(QgsGeometry.fromWkt(wkts[n]).asJson())), 'OUTPUT_BOUNDS': str(bbs[n]).strip('[]'), 'OUTPUT': clipped_vrt }) #clip_task.run() # 'masking layers (part {} of {})'.format(n + 1, len(wkts)) # if self.isCanceled(): return False if not clip_task['SUCCESS']: self.exception = Exception('Clipping failed') return False ###################################################################### # Calculate carbon change table log('Calculating summary table...') summary_task = processing.run( 'trendsearth:carbon_summary', { 'INPUT': clipped_vrt, 'YEAR_START': self.year_start, 'YEAR_END': self.year_end }) # 'calculating summary table (part {} of {})'.format(n + 1, # len(wkts)) if self.isCanceled(): return if not clip_task['SUCCESS']: self.exception = Exception('Summarizing carbon change failed') return False os.remove(indic_vrt) os.remove(clipped_vrt) os.remove(self.tc_vrt) os.remove(self.f_loss_vrt) if n == 0: forest_loss = np_array_from_str(summary_task['FOREST_LOSS']) carbon_loss = np_array_from_str(summary_task['CARBON_LOSS']) initial_carbon_total = summary_task['CARBON_INITIAL'] area_forest = summary_task['AREA_FOREST'] area_non_forest = summary_task['AREA_NON_FOREST'] area_water = summary_task['AREA_WATER'] area_missing = summary_task['AREA_MISSING'] area_site = summary_task['AREA_SITE'] else: forest_loss = forest_loss + np_array_from_str(summary_task['FOREST_LOSS']) carbon_loss = carbon_loss + np_array_from_str(summary_task['CARBON_LOSS']) area_forest = area_forest + summary_task['AREA_FOREST'] area_non_forest = area_non_forest + summary_task['AREA_NON_FOREST'] area_water = area_water + summary_task['AREA_WATER'] area_missing = area_missing + summary_task['AREA_MISSING'] area_site = area_site + summary_task['AREA_SITE'] initial_carbon_total = initial_carbon_total + summary_task['CARBON_INITIAL'] log('area_missing: {}'.format(area_missing)) log('area_water: {}'.format(area_water)) log('area_non_forest: {}'.format(area_non_forest)) log('area_site: {}'.format(area_site)) log('area_forest: {}'.format(area_forest)) log('initial_carbon_total: {}'.format(initial_carbon_total)) log('forest loss: {}'.format(forest_loss)) log('carbon loss: {}'.format(carbon_loss)) write_excel_summary(forest_loss, carbon_loss, area_missing, area_water, area_non_forest, area_site, area_forest, initial_carbon_total, self.year_start, self.year_end, self.output_file) return True
def testInsertPolygonInMultiPolygon(self): layer = QgsVectorLayer("MultiPolygon?crs=epsg:4326&field=id:integer", "addfeat", "memory") pr = layer.dataProvider() f = QgsFeature() f.setAttributes([1]) f.setGeometry(QgsGeometry.fromWkt('MultiPolygon(((0 0, 1 0, 1 1, 0 1, 0 0)),((10 0, 11 0, 11 1, 10 1, 10 0)))')) pr.addFeatures([f]) uri = '{} table="qgis_test"."new_table_multipolygon" sql='.format(self.dbconn) error, message = QgsVectorLayerExporter.exportLayer(layer, uri, 'mssql', QgsCoordinateReferenceSystem('EPSG:4326')) self.assertEqual(error, QgsVectorLayerExporter.NoError) new_layer = QgsVectorLayer(uri, 'new', 'mssql') self.assertTrue(new_layer.isValid()) self.assertEqual(new_layer.wkbType(), QgsWkbTypes.MultiPolygon) geom = [f.geometry().asWkt() for f in new_layer.getFeatures()] self.assertEqual(geom, ['MultiPolygon (((0 0, 1 0, 1 1, 0 1, 0 0)),((10 0, 11 0, 11 1, 10 1, 10 0)))']) # add single part f2 = QgsFeature() f2.setAttributes([2]) f2.setGeometry(QgsGeometry.fromWkt('Polygon((30 0, 31 0, 31 1, 30 1, 30 0))')) self.assertTrue(new_layer.dataProvider().addFeatures([f2])) # should become multipart geom = [f.geometry().asWkt() for f in new_layer.getFeatures()] self.assertEqual(geom, ['MultiPolygon (((0 0, 1 0, 1 1, 0 1, 0 0)),((10 0, 11 0, 11 1, 10 1, 10 0)))', 'MultiPolygon (((30 0, 31 0, 31 1, 30 1, 30 0)))'])