def testGeopackageLargeFID(self): tmpfile = os.path.join(self.basetestpath, 'testGeopackageLargeFID.gpkg') ds = ogr.GetDriverByName('GPKG').CreateDataSource(tmpfile) lyr = ds.CreateLayer('test', geom_type=ogr.wkbPoint) lyr.CreateField(ogr.FieldDefn('str_field', ogr.OFTString)) ds = None vl = QgsVectorLayer(u'{}'.format(tmpfile) + "|layername=" + "test", 'test', u'ogr') f = QgsFeature() f.setAttributes([1234567890123, None]) self.assertTrue(vl.startEditing()) self.assertTrue(vl.dataProvider().addFeatures([f])) self.assertTrue(vl.commitChanges()) got = [feat for feat in vl.getFeatures()][0] self.assertEqual(got['fid'], 1234567890123) self.assertTrue(vl.startEditing()) self.assertTrue(vl.changeGeometry(1234567890123, QgsGeometry.fromWkt('Point (3 50)'))) self.assertTrue(vl.changeAttributeValue(1234567890123, 1, 'foo')) self.assertTrue(vl.commitChanges()) got = [feat for feat in vl.getFeatures()][0] self.assertEqual(got['str_field'], 'foo') got_geom = got.geometry() self.assertIsNotNone(got_geom) self.assertTrue(vl.startEditing()) self.assertTrue(vl.deleteFeature(1234567890123)) self.assertTrue(vl.commitChanges())
def testDeleteShapes(self): ''' Test fix for #11007 ''' tmpdir = tempfile.mkdtemp() self.dirs_to_cleanup.append(tmpdir) srcpath = os.path.join(TEST_DATA_DIR, 'provider') for file in glob.glob(os.path.join(srcpath, 'shapefile.*')): shutil.copy(os.path.join(srcpath, file), tmpdir) datasource = os.path.join(tmpdir, 'shapefile.shp') vl = QgsVectorLayer('{}|layerid=0'.format(datasource), 'test', 'ogr') feature_count = vl.featureCount() # Start an iterator that will open a new connection iterator = vl.getFeatures() f = next(iterator) # Delete a feature self.assertTrue(vl.startEditing()) self.assertTrue(vl.deleteFeature(1)) self.assertTrue(vl.commitChanges()) # Test the content of the shapefile while it is still opened ds = osgeo.ogr.Open(datasource) # Test repacking has been done self.assertTrue(ds.GetLayer(0).GetFeatureCount(), feature_count - 1) ds = None vl = None
def testEditSubsetString(self): tmpfile = os.path.join(self.basetestpath, 'testEditSubsetString.gpkg') ds = ogr.GetDriverByName('GPKG').CreateDataSource(tmpfile) lyr = ds.CreateLayer('test', geom_type=ogr.wkbMultiPolygon) lyr.CreateField(ogr.FieldDefn('foo', ogr.OFTString)) f = ogr.Feature(lyr.GetLayerDefn()) f['foo'] = 'bar' lyr.CreateFeature(f) f = None f = ogr.Feature(lyr.GetLayerDefn()) f['foo'] = 'baz' lyr.CreateFeature(f) f = None ds = None vl = QgsVectorLayer('{}|layerid=0'.format(tmpfile), 'test', 'ogr') self.assertEqual(vl.dataProvider().featureCount(), 2) # Test adding features vl.setSubsetString("foo = 'baz'") self.assertTrue(vl.startEditing()) feature = QgsFeature(vl.fields()) feature['foo'] = 'abc' vl.addFeature(feature) vl.commitChanges() vl.setSubsetString(None) self.assertEqual(vl.dataProvider().featureCount(), 3) # Test deleting a feature vl.setSubsetString("foo = 'baz'") self.assertTrue(vl.startEditing()) vl.deleteFeature(1) vl.commitChanges() vl.setSubsetString(None) self.assertEqual(vl.dataProvider().featureCount(), 2) # Test editing a feature vl.setSubsetString("foo = 'baz'") self.assertTrue(vl.startEditing()) vl.changeAttributeValue(2, 1, 'xx') vl.commitChanges() vl.setSubsetString(None) self.assertEqual(set((feat['foo'] for feat in vl.getFeatures())), set(['xx', 'abc']))
def testRepack(self): vl = QgsVectorLayer("{}|layerid=0".format(self.repackfile), "test", "ogr") ids = [f.id() for f in vl.getFeatures(QgsFeatureRequest().setFilterExpression("pk=1"))] vl.selectByIds(ids) self.assertEqual(vl.selectedFeaturesIds(), ids) self.assertEqual(vl.pendingFeatureCount(), 5) self.assertTrue(vl.startEditing()) self.assertTrue(vl.deleteFeature(3)) self.assertTrue(vl.commitChanges()) self.assertTrue(vl.selectedFeatureCount() == 0 or vl.selectedFeatures()[0]["pk"] == 1)
def testRepack(self): vl = QgsVectorLayer(u'{}|layerid=0'.format(self.repackfile), u'test', u'ogr') ids = [f.id() for f in vl.getFeatures(QgsFeatureRequest().setFilterExpression('pk=1'))] vl.setSelectedFeatures(ids) assert vl.selectedFeaturesIds() == ids, vl.selectedFeaturesIds() assert vl.pendingFeatureCount() == 5, vl.pendingFeatureCount() assert vl.startEditing() assert vl.deleteFeature(3) assert vl.commitChanges() assert vl.selectedFeatureCount() == 0 or vl.selectedFeatures()[0]['pk'] == 1
def testRepack(self): vl = QgsVectorLayer('{}|layerid=0'.format(self.repackfile), 'test', 'ogr') ids = [f.id() for f in vl.getFeatures(QgsFeatureRequest().setFilterExpression('pk=1'))] vl.selectByIds(ids) self.assertEqual(vl.selectedFeatureIds(), ids) self.assertEqual(vl.featureCount(), 5) self.assertTrue(vl.startEditing()) self.assertTrue(vl.deleteFeature(3)) self.assertTrue(vl.commitChanges()) self.assertTrue(vl.selectedFeatureCount() == 0 or vl.selectedFeatures()[0]['pk'] == 1)
def _modifyAndLoadWfs(): valid = {} urls = os.getenv(TEST_URLS).split(",") for url in urls: try: url = url.strip() + "/wfs" uri = "%s?typename=union&version=1.0.0&request=GetFeature&service=WFS" % url layer = QgsVectorLayer(uri, "testlayer", "WFS") featureCount = layer.featureCount() featureid = list(layer.getFeatures())[0].id() layer.startEditing() layer.deleteFeature(featureid) layer.commitChanges() layer = QgsVectorLayer(uri, "testlayer", "WFS") valid[url] = layer.featureCount() == featureCount - 1 except: valid[url] = False failed = [k for k,v in valid.items() if not v] if failed: raise AssertionError("Test failed for the following URLs: " + str(failed))
def testRepack(self): vl = QgsVectorLayer(u'{}|layerid=0'.format(self.repackfile), u'test', u'ogr') ids = [ f.id() for f in vl.getFeatures( QgsFeatureRequest().setFilterExpression('pk=1')) ] vl.setSelectedFeatures(ids) assert vl.selectedFeaturesIds() == ids, vl.selectedFeaturesIds() assert vl.pendingFeatureCount() == 5, vl.pendingFeatureCount() assert vl.startEditing() assert vl.deleteFeature(3) assert vl.commitChanges() assert vl.selectedFeatureCount() == 0 or vl.selectedFeatures( )[0]['pk'] == 1
def testRepack(self): vl = QgsVectorLayer('{}|layerid=0'.format(self.repackfile), 'test', 'ogr') ids = [ f.id() for f in vl.getFeatures( QgsFeatureRequest().setFilterExpression('pk=1')) ] vl.selectByIds(ids) self.assertEqual(vl.selectedFeatureIds(), ids) self.assertEqual(vl.pendingFeatureCount(), 5) self.assertTrue(vl.startEditing()) self.assertTrue(vl.deleteFeature(3)) self.assertTrue(vl.commitChanges()) self.assertTrue(vl.selectedFeatureCount() == 0 or vl.selectedFeatures()[0]['pk'] == 1)
def testGeopackageExtentUpdate(self): """ test http://hub.qgis.org/issues/15273 """ tmpfile = os.path.join(self.basetestpath, "testGeopackageExtentUpdate.gpkg") ds = ogr.GetDriverByName("GPKG").CreateDataSource(tmpfile) lyr = ds.CreateLayer("test", geom_type=ogr.wkbPoint) f = ogr.Feature(lyr.GetLayerDefn()) f.SetGeometry(ogr.CreateGeometryFromWkt("POINT(0 0)")) lyr.CreateFeature(f) f = ogr.Feature(lyr.GetLayerDefn()) f.SetGeometry(ogr.CreateGeometryFromWkt("POINT(1 1)")) lyr.CreateFeature(f) f = None f = ogr.Feature(lyr.GetLayerDefn()) f.SetGeometry(ogr.CreateGeometryFromWkt("POINT(1 0.5)")) lyr.CreateFeature(f) f = None gdal.ErrorReset() ds.ExecuteSQL("RECOMPUTE EXTENT ON test") has_error = gdal.GetLastErrorMsg() != "" ds = None if has_error: print("Too old GDAL trunk version. Please update") return vl = QgsVectorLayer(u"{}".format(tmpfile), u"test", u"ogr") # Test moving a geometry that touches the bbox self.assertTrue(vl.startEditing()) self.assertTrue(vl.changeGeometry(1, QgsGeometry.fromWkt("Point (0.5 0)"))) self.assertTrue(vl.commitChanges()) reference = QgsGeometry.fromRect(QgsRectangle(0.5, 0.0, 1.0, 1.0)) provider_extent = QgsGeometry.fromRect(vl.extent()) self.assertTrue( QgsGeometry.compare(provider_extent.asPolygon()[0], reference.asPolygon()[0], 0.00001), provider_extent.asPolygon()[0], ) # Test deleting a geometry that touches the bbox self.assertTrue(vl.startEditing()) self.assertTrue(vl.deleteFeature(2)) self.assertTrue(vl.commitChanges()) reference = QgsGeometry.fromRect(QgsRectangle(0.5, 0.0, 1.0, 0.5)) provider_extent = QgsGeometry.fromRect(vl.extent()) self.assertTrue( QgsGeometry.compare(provider_extent.asPolygon()[0], reference.asPolygon()[0], 0.00001), provider_extent.asPolygon()[0], )
def testDeleteShapes(self): ''' Test fix for #11007 ''' tmpdir = tempfile.mkdtemp() self.dirs_to_cleanup.append(tmpdir) srcpath = os.path.join(TEST_DATA_DIR, 'provider') for file in glob.glob(os.path.join(srcpath, 'shapefile.*')): shutil.copy(os.path.join(srcpath, file), tmpdir) datasource = os.path.join(tmpdir, 'shapefile.shp') vl = QgsVectorLayer('{}|layerid=0'.format(datasource), 'test', 'ogr') feature_count = vl.featureCount() # Start an iterator that will open a new connection iterator = vl.getFeatures() next(iterator) # Delete a feature self.assertTrue(vl.startEditing()) self.assertTrue(vl.deleteFeature(1)) self.assertTrue(vl.commitChanges()) # Test the content of the shapefile while it is still opened ds = osgeo.ogr.Open(datasource) # Test repacking has been done self.assertTrue(ds.GetLayer(0).GetFeatureCount() == feature_count - 1) ds = None # Delete another feature while in update mode self.assertTrue(2 == 2) vl.dataProvider().enterUpdateMode() vl.dataProvider().deleteFeatures([0]) # Test that repacking has not been done (since in update mode) ds = osgeo.ogr.Open(datasource) self.assertTrue(ds.GetLayer(0).GetFeatureCount() == feature_count - 1) ds = None # Test that repacking was performed when leaving updateMode vl.dataProvider().leaveUpdateMode() ds = osgeo.ogr.Open(datasource) self.assertTrue(ds.GetLayer(0).GetFeatureCount() == feature_count - 2) ds = None vl = None
def testRepackUnderFileLocks(self): ''' Test fix for #15570 and #15393 ''' # This requires a GDAL fix done per https://trac.osgeo.org/gdal/ticket/6672 # but on non-Windows version the test would succeed if int(osgeo.gdal.VersionInfo('VERSION_NUM')) < GDAL_COMPUTE_VERSION( 2, 1, 2): return tmpdir = tempfile.mkdtemp() self.dirs_to_cleanup.append(tmpdir) srcpath = os.path.join(TEST_DATA_DIR, 'provider') for file in glob.glob(os.path.join(srcpath, 'shapefile.*')): shutil.copy(os.path.join(srcpath, file), tmpdir) datasource = os.path.join(tmpdir, 'shapefile.shp') vl = QgsVectorLayer('{}|layerid=0'.format(datasource), 'test', 'ogr') feature_count = vl.featureCount() # Keep a file descriptor opened on the .dbf, .shp and .shx f_shp = open(os.path.join(tmpdir, 'shapefile.shp'), 'rb') f_shx = open(os.path.join(tmpdir, 'shapefile.shx'), 'rb') f_dbf = open(os.path.join(tmpdir, 'shapefile.dbf'), 'rb') # Delete a feature self.assertTrue(vl.startEditing()) self.assertTrue(vl.deleteFeature(1)) # Commit changes and check no error is emitted cbk = ErrorReceiver() vl.dataProvider().raiseError.connect(cbk.receiveError) self.assertTrue(vl.commitChanges()) self.assertIsNone(cbk.msg) vl = None del f_shp del f_shx del f_dbf # Test repacking has been done ds = osgeo.ogr.Open(datasource) self.assertTrue(ds.GetLayer(0).GetFeatureCount(), feature_count - 1) ds = None
def testRepackUnderFileLocks(self): ''' Test fix for #15570 and #15393 ''' # This requires a GDAL fix done per https://trac.osgeo.org/gdal/ticket/6672 # but on non-Windows version the test would succeed if int(osgeo.gdal.VersionInfo('VERSION_NUM')) < GDAL_COMPUTE_VERSION(2, 1, 2): return tmpdir = tempfile.mkdtemp() self.dirs_to_cleanup.append(tmpdir) srcpath = os.path.join(TEST_DATA_DIR, 'provider') for file in glob.glob(os.path.join(srcpath, 'shapefile.*')): shutil.copy(os.path.join(srcpath, file), tmpdir) datasource = os.path.join(tmpdir, 'shapefile.shp') vl = QgsVectorLayer('{}|layerid=0'.format(datasource), 'test', 'ogr') feature_count = vl.featureCount() # Keep a file descriptor opened on the .dbf, .shp and .shx f_shp = open(os.path.join(tmpdir, 'shapefile.shp'), 'rb') f_shx = open(os.path.join(tmpdir, 'shapefile.shx'), 'rb') f_dbf = open(os.path.join(tmpdir, 'shapefile.dbf'), 'rb') # Delete a feature self.assertTrue(vl.startEditing()) self.assertTrue(vl.deleteFeature(1)) # Commit changes and check no error is emitted cbk = ErrorReceiver() vl.dataProvider().raiseError.connect(cbk.receiveError) self.assertTrue(vl.commitChanges()) self.assertIsNone(cbk.msg) vl = None del f_shp del f_shx del f_dbf # Test repacking has been done ds = osgeo.ogr.Open(datasource) self.assertTrue(ds.GetLayer(0).GetFeatureCount(), feature_count - 1) ds = None
def testGeopackageExtentUpdate(self): ''' test http://hub.qgis.org/issues/15273 ''' tmpfile = os.path.join(self.basetestpath, 'testGeopackageExtentUpdate.gpkg') ds = ogr.GetDriverByName('GPKG').CreateDataSource(tmpfile) lyr = ds.CreateLayer('test', geom_type=ogr.wkbPoint) f = ogr.Feature(lyr.GetLayerDefn()) f.SetGeometry(ogr.CreateGeometryFromWkt('POINT(0 0)')) lyr.CreateFeature(f) f = ogr.Feature(lyr.GetLayerDefn()) f.SetGeometry(ogr.CreateGeometryFromWkt('POINT(1 1)')) lyr.CreateFeature(f) f = None f = ogr.Feature(lyr.GetLayerDefn()) f.SetGeometry(ogr.CreateGeometryFromWkt('POINT(1 0.5)')) lyr.CreateFeature(f) f = None gdal.ErrorReset() ds.ExecuteSQL('RECOMPUTE EXTENT ON test') has_error = gdal.GetLastErrorMsg() != '' ds = None if has_error: print('Too old GDAL trunk version. Please update') return vl = QgsVectorLayer(u'{}'.format(tmpfile), u'test', u'ogr') # Test moving a geometry that touches the bbox self.assertTrue(vl.startEditing()) self.assertTrue(vl.changeGeometry(1, QgsGeometry.fromWkt('Point (0.5 0)'))) self.assertTrue(vl.commitChanges()) reference = QgsGeometry.fromRect(QgsRectangle(0.5, 0.0, 1.0, 1.0)) provider_extent = QgsGeometry.fromRect(vl.extent()) self.assertTrue(QgsGeometry.compare(provider_extent.asPolygon()[0], reference.asPolygon()[0], 0.00001), provider_extent.asPolygon()[0]) # Test deleting a geometry that touches the bbox self.assertTrue(vl.startEditing()) self.assertTrue(vl.deleteFeature(2)) self.assertTrue(vl.commitChanges()) reference = QgsGeometry.fromRect(QgsRectangle(0.5, 0.0, 1.0, 0.5)) provider_extent = QgsGeometry.fromRect(vl.extent()) self.assertTrue(QgsGeometry.compare(provider_extent.asPolygon()[0], reference.asPolygon()[0], 0.00001), provider_extent.asPolygon()[0])
def _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)
class EliminadorMMC: """ MMC Deletion class """ def __init__(self, municipi_id, coast=False): # Common self.arr_nom_municipis = np.genfromtxt(DIC_NOM_MUNICIPIS, dtype=None, encoding=None, delimiter=';', names=True) self.arr_lines_data = np.genfromtxt(DIC_LINES, dtype=None, encoding=None, delimiter=';', names=True) # ADT PostGIS connection self.pg_adt = PgADTConnection(HOST, DBNAME, USER, PWD, SCHEMA) self.pg_adt.connect() # ### # Input dependant that don't need data from the layers self.municipi_id = int(municipi_id) self.coast = coast self.municipi_codi_ine = self.get_municipi_codi_ine(self.municipi_id) self.municipi_lines = self.get_municipi_lines( ) # Get a list with all the lines ID if self.coast: self.municipi_coast_line = self.get_municipi_coast_line() # Input layers self.input_points_layer, self.input_lines_layer, self.input_polygons_layer, self.input_coast_lines_layer, self.input_full_bt5_table, self.input_points_table, self.input_line_table, self.input_coast_line_table = ( None, ) * 8 def get_municipi_codi_ine(self, municipi_id): """ Get the municipi INE ID :param municipi_id -> ID of the municipi which to obtain its INE ID :return: codi_ine -> INE ID of the municipi """ muni_data = self.arr_nom_municipis[np.where( self.arr_nom_municipis['id_area'] == f'"{municipi_id}"')] if muni_data: codi_ine = muni_data['codi_ine_muni'][0].strip('"') return codi_ine def get_municipi_lines(self): """ Get all the municipal boundary lines that make the input municipi :return lines_muni_list -> List with all the boundary lines that make the municipi """ lines_muni_1 = self.arr_lines_data[np.where( self.arr_lines_data['CODIMUNI1'] == self.municipi_id)] lines_muni_2 = self.arr_lines_data[np.where( self.arr_lines_data['CODIMUNI2'] == self.municipi_id)] lines_muni_1_list = lines_muni_1['IDLINIA'].tolist() lines_muni_2_list = lines_muni_2['IDLINIA'].tolist() lines_muni_list = lines_muni_1_list + lines_muni_2_list return lines_muni_list def check_mm_exists(self, municipi_codi_ine, layer='postgis'): """ Check if the input municipi exists as a Municipal Map into the database or into the input polygon layer. :param municipi_codi_ine -> Municipi INE ID of the municipi to check if exists its MM :param layer -> Layer into check if the MM exists :return Boolean True/False -> Boolean that means if the MM exists into the given layer or not """ mapa_muni_table, expression = None, None if layer == 'postgis': mapa_muni_table = self.pg_adt.get_table('mapa_muni_icc') expression = f'"codi_muni"=\'{municipi_codi_ine}\' and "vig_mm" is True' elif layer == 'input': mapa_muni_table = self.input_polygons_layer expression = f'"CodiMuni"=\'{municipi_codi_ine}\'' mapa_muni_table.selectByExpression(expression, QgsVectorLayer.SetSelection) count = mapa_muni_table.selectedFeatureCount() if count == 0: return False else: return True def get_municipi_coast_line(self): """ Get the municipi coast line, if exists :return coast_line_id -> ID of the coast boundary line """ coast_line_id = '' for line_id in self.municipi_lines: line_data = self.arr_lines_data[np.where( self.arr_lines_data['IDLINIA'] == line_id)] if line_data['LIMCOSTA'] == 'S': coast_line_id = line_id return coast_line_id def remove_municipi_data(self): """ Main entry point. This function removes all the data of the municipi that the user wants to remove from the database. """ self.set_layers() self.remove_polygons() self.remove_lines_layer() self.remove_lines_table() self.remove_points_layer() self.remove_points_table() if self.coast: self.remove_coast_line_layer() self.remove_coast_lines_table() self.remove_full_bt5m() def set_layers(self): """ Set the paths of the working vector layers """ directory_list = os.listdir(ELIMINADOR_WORK_DIR) directory_path = os.path.join(ELIMINADOR_WORK_DIR, directory_list[0]) shapefiles_list = os.listdir(directory_path) for shapefile in shapefiles_list: if '-fita-' in shapefile and shapefile.endswith('.shp'): self.input_points_layer = QgsVectorLayer( os.path.join(directory_path, shapefile)) elif '-liniaterme-' in shapefile and shapefile.endswith('.shp'): self.input_lines_layer = QgsVectorLayer( os.path.join(directory_path, shapefile)) elif '-poligon-' in shapefile and shapefile.endswith('.shp'): self.input_polygons_layer = QgsVectorLayer( os.path.join(directory_path, shapefile)) elif '-liniacosta-' in shapefile and shapefile.endswith('.shp'): self.input_coast_lines_layer = QgsVectorLayer( os.path.join(directory_path, shapefile)) elif '-liniacostataula-' in shapefile and shapefile.endswith( '.dbf'): self.input_coast_line_table = QgsVectorLayer( os.path.join(directory_path, shapefile)) elif '-tallfullbt5m-' in shapefile and shapefile.endswith('.dbf'): self.input_full_bt5_table = QgsVectorLayer( os.path.join(directory_path, shapefile)) elif '-liniatermetaula-' in shapefile and shapefile.endswith( '.dbf'): self.input_line_table = QgsVectorLayer( os.path.join(directory_path, shapefile)) elif '-fitataula-' in shapefile and shapefile.endswith('.dbf'): self.input_points_table = QgsVectorLayer( os.path.join(directory_path, shapefile)) def remove_polygons(self): """ Remove the municipi's polygons from the database """ self.input_polygons_layer.selectByExpression( f'"CodiMuni"=\'{self.municipi_codi_ine}\'', QgsVectorLayer.SetSelection) with edit(self.input_polygons_layer): for polygon in self.input_polygons_layer.getSelectedFeatures(): self.input_polygons_layer.deleteFeature(polygon.id()) def remove_coast_line_layer(self): """ Remove the municipi's coast lines from the database's layer """ # 5065 self.input_coast_lines_layer.selectByExpression( f'"IdLinia"={self.municipi_coast_line}', QgsVectorLayer.SetSelection) with edit(self.input_coast_lines_layer): for line in self.input_coast_lines_layer.getSelectedFeatures(): self.input_coast_lines_layer.deleteFeature(line.id()) def remove_full_bt5m(self): """ Remove the municipi's BT5M full from the database's table """ self.input_full_bt5_table.selectByExpression( f'"IdLinia"={self.municipi_coast_line}', QgsVectorLayer.SetSelection) with edit(self.input_full_bt5_table): for line in self.input_full_bt5_table.getSelectedFeatures(): self.input_full_bt5_table.deleteFeature(line.id()) def remove_points_layer(self): """ Remove the municipi's points from the database's layer Atenció: en alguns casos no esborra correctament les fites 3 termes. """ point_id_remove_list = self.get_points_to_remove() with edit(self.input_points_layer): for point_id in point_id_remove_list: self.input_points_layer.selectByExpression( f'"IdFita"=\'{point_id}\'', QgsVectorLayer.SetSelection) for feature in self.input_points_layer.getSelectedFeatures(): self.input_points_layer.deleteFeature(feature.id()) box = QMessageBox() box.setIcon(QMessageBox.Warning) box.setText( "Enrecorda't de revisar que s'han esborrat\ncorrectament totes les fites 3 termes." ) box.exec_() def get_points_to_remove(self): """ Get the points that the class has to remove, in order to avoid removing points that have to exists due they also pertain to another municipi that have MM. :return point_id_remove_list -> List with the ID of all the points to remove from the points layer """ fita_mem_layer = self.pg_adt.get_layer('v_fita_mem', 'id_fita') point_id_remove_list = [] delete_lines_list, edit_lines_dict = self.get_lines_to_manage() for line_id in delete_lines_list: fita_mem_layer.selectByExpression(f'"id_linia"=\'{line_id}\'', QgsVectorLayer.SetSelection) for feature in fita_mem_layer.getSelectedFeatures(): # Check that the point has correctly filled the coordinates fields if feature['point_x'] and feature['point_y']: point_id_fita = coordinates_to_id_fita( feature['point_x'], feature['point_y']) if feature['num_termes'] == 'F2T': point_id_remove_list.append(point_id_fita) elif feature['num_termes'] != 'F2T' and feature[ 'num_termes']: ''' No es pot fer una selecció espacial fita - linia de terme, de manera que no es pot comprovar de manera 100% fiable si una fita 3 termes té una línia veïna amb MM o no. El que es fa és el següent: 1. Per cada línia veïna, saber si cap dels municipis de la línia de terme té MM al MMC. 2. Si en té, no s'elimina la fita 3 termes. ''' neighbor_lines = self.get_neighbor_lines(line_id) neighbor_mm = False for neighbor_line in neighbor_lines: # Get neighbors municipis neighbor_municipi_1_codi_ine, neighbor_municipi_2_codi_ine = self.get_neighbors_ine( neighbor_line) # Check if any of neighbors has MM neighbor_municipi_1_mm = self.check_mm_exists( neighbor_municipi_1_codi_ine, 'input') neighbor_municipi_2_mm = self.check_mm_exists( neighbor_municipi_2_codi_ine, 'input') if (neighbor_municipi_1_mm or neighbor_municipi_2_mm ) and not neighbor_mm: neighbor_mm = True # If there is not any neighbor municipi with MM, remove the point if not neighbor_mm: point_id_remove_list.append(point_id_fita) return point_id_remove_list def remove_points_table(self): """ Remove the municipi's points from the database's table """ for line_id in self.municipi_lines: with edit(self.input_points_table): line_id_txt = line_id_2_txt(line_id) self.input_points_table.selectByExpression( f'"IdLinia"=\'{line_id_txt}\'', QgsVectorLayer.SetSelection) for feature in self.input_points_table.getSelectedFeatures(): self.input_points_table.deleteFeature(feature.id()) def get_neighbor_lines(self, line_id): """ Get the neighbor lines from the given boundary line :param line_id -> ID of the line to obtain its neighbor boundary lines :return neighbor_lines -> List with the line ID of the neighbor lines """ neighbor_lines = [] linia_veina_table = self.pg_adt.get_table('linia_veina') linia_veina_table.selectByExpression(f'"id_linia"=\'{line_id}\'', QgsVectorLayer.SetSelection) for line in linia_veina_table.getSelectedFeatures(): neighbor_lines.append(int(line['id_linia_veina'])) return neighbor_lines def remove_lines_layer(self): """ Remove the municipi's boundary lines from the database's layer """ # Remove boundary lines delete_lines_list, edit_lines_dict = self.get_lines_to_manage() if delete_lines_list: with edit(self.input_lines_layer): for line_id in delete_lines_list: self.input_lines_layer.selectByExpression( f'"IdLinia"=\'{line_id}\'', QgsVectorLayer.SetSelection) for line in self.input_lines_layer.getSelectedFeatures(): self.input_lines_layer.deleteFeature(line.id()) # Edit boundary lines if edit_lines_dict: with edit(self.input_lines_layer): for line_id, dates in edit_lines_dict.items(): neighbor_valid_de, neighbor_data_alta, neighbor_ine = dates self.input_lines_layer.selectByExpression( f'"IdLinia"=\'{line_id}\'', QgsVectorLayer.SetSelection) for line in self.input_lines_layer.getSelectedFeatures(): if line['ValidDe'] < neighbor_valid_de: line['ValidDe'] = neighbor_valid_de self.input_lines_layer.updateFeature(line) if line['DataAlta'] < neighbor_data_alta: line['DataAlta'] = neighbor_data_alta self.input_lines_layer.updateFeature(line) def get_lines_to_manage(self): """ Get a list with the line id of the lines to remove and a dict with the line id and some dates of the lines to edit :return delete_lines_list -> List with the line ID of the lines to remove :return edit_lines_dict -> List with the line ID and the Valid De, Data Alta and INE ID of the neighbor municipi """ delete_lines_list = [] edit_lines_dict = {} for line_id in self.municipi_lines: # Check if the other municipi has a considered MM neighbor_ine = self.get_neighbor_ine(line_id) neighbor_mm = self.check_mm_exists(neighbor_ine, 'input') line_id_txt = line_id_2_txt(line_id) # If the neighbor municipi doesn't have a considered MM, directly remove the boundary line # If it has, create a dictionary with its Data Alta and Valid De to check if it has to replace the dates if not neighbor_mm: delete_lines_list.append(line_id_txt) else: neighbor_data_alta, neighbor_valid_de = self.get_neighbor_dates( neighbor_ine) edit_lines_dict[line_id_txt] = [ neighbor_valid_de, neighbor_data_alta, neighbor_ine ] return delete_lines_list, edit_lines_dict def get_neighbor_municipi(self, line_id): """ Get the ID of the neighbor municipi :return neighbor_municipi_id -> ID of the neighbor municipi """ line_data = self.arr_lines_data[np.where( self.arr_lines_data['IDLINIA'] == line_id)] neighbor_municipi_id = '' if line_data['CODIMUNI1'] == self.municipi_id: neighbor_municipi_id = line_data['CODIMUNI2'][0] elif line_data['CODIMUNI2'] == self.municipi_id: neighbor_municipi_id = line_data['CODIMUNI1'][0] return neighbor_municipi_id def get_neighbors_municipis(self, line_id): """ Get the IDs of both municipis than share a boundary line :return neighbor_municipi_1_id -> ID of the first neighbor municipi :return neighbor_municipi_2_id -> ID of the second neighbor municipi """ line_data = self.arr_lines_data[np.where( self.arr_lines_data['IDLINIA'] == line_id)] neighbor_municipi_1_id, neighbor_municipi_2_id = line_data[ 'CODIMUNI1'][0], line_data['CODIMUNI2'][0] return neighbor_municipi_1_id, neighbor_municipi_2_id def get_neighbor_ine(self, line_id): """ Get the INE ID of the neighbor municipi :return neighbor_municipi_codi_ine -> INE ID of the neighbor municipi """ neighbor_municipi_id = self.get_neighbor_municipi(line_id) neighbor_municipi_codi_ine = self.get_municipi_codi_ine( neighbor_municipi_id) return neighbor_municipi_codi_ine def get_neighbors_ine(self, line_id): """ Get the INE IDs of both municipis than share a boundary line :return neighbor_municipi_1_codi_ine -> INE ID of the first neighbor municipi :return neighbor_municipi_2_codi_ine -> INE ID of the second neighbor municipi """ neighbor_municipi_1_id, neighbor_municipi_2_id = self.get_neighbors_municipis( line_id) neighbor_municipi_1_codi_ine = self.get_municipi_codi_ine( neighbor_municipi_1_id) neighbor_municipi_2_codi_ine = self.get_municipi_codi_ine( neighbor_municipi_2_id) return neighbor_municipi_1_codi_ine, neighbor_municipi_2_codi_ine def get_neighbor_dates(self, neighbor_ine): """ Get the Data Alta and Valid De dates of the neighbor municipi :return data_alta -> Data Alta of the neighbor municipi :return valid_de -> Valid De of the neighbor municipi """ self.input_polygons_layer.selectByExpression( f'"CodiMuni"=\'{neighbor_ine}\'', QgsVectorLayer.SetSelection) data_alta, valid_de = None, None for polygon in self.input_polygons_layer.getSelectedFeatures(): data_alta = polygon['DataAlta'] valid_de = polygon['ValidDe'] return data_alta, valid_de def remove_lines_table(self): """ Remove the municipi's boundary lines from the database's table """ with edit(self.input_line_table): for line in self.municipi_lines: line_txt = line_id_2_txt(line) self.input_line_table.selectByExpression( f'"IdLinia"={line_txt} and "CodiMuni" = \'{self.municipi_codi_ine}\'', QgsVectorLayer.SetSelection) for feature in self.input_line_table.getSelectedFeatures(): self.input_line_table.deleteFeature(feature.id()) def remove_coast_lines_table(self): """ Remove the municipi's boundary coast line from the database's table """ with edit(self.input_coast_line_table): self.input_coast_line_table.selectByExpression( f'"IdLinia"={self.municipi_coast_line}') for feature in self.input_coast_line_table.getSelectedFeatures(): self.input_coast_line_table.deleteFeature(feature.id())
def test_check_gaps_in_plots(self): gpkg_path = get_test_copy_path('db/static/gpkg/quality_validations.gpkg') uri = gpkg_path + '|layername={layername}'.format(layername='check_gaps_in_plots') test_plots_layer = QgsVectorLayer(uri, 'check_gaps_in_plots', 'ogr') print('\nINFO: Validating Gaps in Plots using roads and multiple geometries...') gaps = GeometryUtils.get_gaps_in_polygon_layer(test_plots_layer, True) geometries = [g.asWkt() for g in gaps] expected_list = [ 'Polygon ((1001839.42949045938439667 1013500.23419545334763825, 1001838.68766217899974436 1013479.83391774445772171, 1001839.42949045938439667 1013450.16078653128352016, 1001855.74971262644976377 1013449.78987239114940166, 1001858.3461116076214239 1013430.87325124291237444, 1001885.42284383939113468 1013430.87325124291237444, 1001901.72405463655013591 1013411.57209242216777056, 1001910.64500537037383765 1013418.26217047742102295, 1001917.32145989337004721 1013392.29818066605366766, 1001845.19794039404951036 1013415.08188382943626493, 1001851.47861975431442261 1013424.31817700632382184, 1001833.74493685469496995 1013433.92392191023100168, 1001829.49624199338722974 1013421.7320149167208001, 1001839.42949045938439667 1013500.23419545334763825))', 'Polygon ((1001935.86716690135654062 1013432.35690780356526375, 1001921.03060129494406283 1013446.08073098957538605, 1001920.28877301455941051 1013475.7538622027495876, 1001957.38018703076522797 1013429.01868054212536663, 1001935.86716690135654062 1013432.35690780356526375))', 'Polygon ((1001935.86716690135654062 1013432.35690780356526375, 1001921.03060129494406283 1013446.08073098957538605, 1001920.28877301455941051 1013475.7538622027495876, 1001957.38018703076522797 1013429.01868054212536663, 1001935.86716690135654062 1013432.35690780356526375))', 'Polygon ((1001920.28877301455941051 1013475.7538622027495876, 1001861.31342472892720252 1013477.9793470436707139, 1001862.05525300919543952 1013498.37962475256063044, 1001920.28877301455941051 1013475.7538622027495876))', 'Polygon ((1001895.43752562382724136 1013467.22283697873353958, 1001907.30677810893394053 1013464.25552385742776096, 1001907.67769224906805903 1013454.2408420731080696, 1001895.43752562382724136 1013454.2408420731080696, 1001895.43752562382724136 1013467.22283697873353958))', 'Polygon ((1001847.96051568305119872 1013470.1901501000393182, 1001867.98987925180699676 1013469.07740767952054739, 1001869.10262167232576758 1013455.72449863376095891, 1001847.58960154291708022 1013455.72449863376095891, 1001847.96051568305119872 1013470.1901501000393182))'] for expected in expected_list: self.assertIn(expected, geometries) self.assertEqual(len(geometries), 5) print('\nINFO: Validating Gaps in Plots using roads for one geometry...') test_plots_layer.startEditing() test_plots_layer.deleteFeature(2) gaps = GeometryUtils.get_gaps_in_polygon_layer(test_plots_layer, True) geometries = [g.asWkt() for g in gaps] self.assertIn( 'Polygon ((1001895.43752562382724136 1013467.22283697873353958, 1001907.30677810893394053 1013464.25552385742776096, 1001907.67769224906805903 1013454.2408420731080696, 1001895.43752562382724136 1013454.2408420731080696, 1001895.43752562382724136 1013467.22283697873353958))', geometries) self.assertIn( 'Polygon ((1001847.96051568305119872 1013470.1901501000393182, 1001867.98987925180699676 1013469.07740767952054739, 1001869.10262167232576758 1013455.72449863376095891, 1001847.58960154291708022 1013455.72449863376095891, 1001847.96051568305119872 1013470.1901501000393182))', geometries) self.assertEqual(len(geometries), 2) test_plots_layer.rollBack() print('\nINFO: Validating Gaps in Plots without using roads and multiple geometries...') gaps = GeometryUtils.get_gaps_in_polygon_layer(test_plots_layer, False) geometries = [g.asWkt() for g in gaps] self.assertIn( 'Polygon ((1001895.43752562382724136 1013467.22283697873353958, 1001907.30677810893394053 1013464.25552385742776096, 1001907.67769224906805903 1013454.2408420731080696, 1001895.43752562382724136 1013454.2408420731080696, 1001895.43752562382724136 1013467.22283697873353958))', geometries) self.assertIn( 'Polygon ((1001847.96051568305119872 1013470.1901501000393182, 1001867.98987925180699676 1013469.07740767952054739, 1001869.10262167232576758 1013455.72449863376095891, 1001847.58960154291708022 1013455.72449863376095891, 1001847.96051568305119872 1013470.1901501000393182))', geometries) self.assertEqual(len(geometries), 2) print('\nINFO: Validating Gaps in Plots without using roads for one geometry...') test_plots_layer.startEditing() test_plots_layer.deleteFeature(2) gaps = GeometryUtils.get_gaps_in_polygon_layer(test_plots_layer, False) geometries = [g.asWkt() for g in gaps] self.assertIn( 'Polygon ((1001895.43752562382724136 1013467.22283697873353958, 1001907.30677810893394053 1013464.25552385742776096, 1001907.67769224906805903 1013454.2408420731080696, 1001895.43752562382724136 1013454.2408420731080696, 1001895.43752562382724136 1013467.22283697873353958))', geometries) self.assertIn( 'Polygon ((1001847.96051568305119872 1013470.1901501000393182, 1001867.98987925180699676 1013469.07740767952054739, 1001869.10262167232576758 1013455.72449863376095891, 1001847.58960154291708022 1013455.72449863376095891, 1001847.96051568305119872 1013470.1901501000393182))', geometries) self.assertEqual(len(geometries), 2) test_plots_layer.rollBack() print('\nINFO: Validating Gaps in Plots using roads for only one geometry...') test_plots_layer.startEditing() test_plots_layer.deleteFeature(1) test_plots_layer.deleteFeature(2) test_plots_layer.deleteFeature(3) gaps = GeometryUtils.get_gaps_in_polygon_layer(test_plots_layer, True) geometries = [g.asWkt() for g in gaps] self.assertEqual([], geometries) self.assertEqual(len(geometries), 0) test_plots_layer.rollBack() print('\nINFO: Validating Gaps in Plots without using roads for only one geometry...') test_plots_layer.startEditing() test_plots_layer.deleteFeature(1) test_plots_layer.deleteFeature(2) test_plots_layer.deleteFeature(3) gaps = GeometryUtils.get_gaps_in_polygon_layer(test_plots_layer, False) geometries = [g.asWkt() for g in gaps] self.assertEqual([], geometries) self.assertEqual(len(geometries), 0) test_plots_layer.rollBack() print('\nINFO: Validating Gaps in Plots using roads for two geometries...') test_plots_layer.startEditing() test_plots_layer.deleteFeature(1) test_plots_layer.deleteFeature(3) gaps = GeometryUtils.get_gaps_in_polygon_layer(test_plots_layer, True) geometries = [g.asWkt() for g in gaps] self.assertIn( 'Polygon ((1001889.87381352134980261 1013447.93530169036239386, 1001885.42284383939113468 1013430.87325124291237444, 1001901.72405463655013591 1013411.57209242216777056, 1001845.19794039404951036 1013415.08188382943626493, 1001851.47861975431442261 1013424.31817700632382184, 1001833.74493685469496995 1013433.92392191023100168, 1001889.87381352134980261 1013447.93530169036239386))', geometries) self.assertEqual(len(geometries), 1) test_plots_layer.rollBack() print('\nINFO: Validating Gaps in Plots without using roads for two geometries...') test_plots_layer.startEditing() test_plots_layer.deleteFeature(1) test_plots_layer.deleteFeature(3 ) gaps = GeometryUtils.get_gaps_in_polygon_layer(test_plots_layer, False) geometries = [g.asWkt() for g in gaps] self.assertEqual([], geometries) self.assertEqual(len(geometries), 0) test_plots_layer.rollBack()
def testTransaction(self): tmpfile = os.path.join(self.basetestpath, 'testTransaction.gpkg') ds = ogr.GetDriverByName('GPKG').CreateDataSource(tmpfile) lyr = ds.CreateLayer('lyr1', geom_type=ogr.wkbPoint) f = ogr.Feature(lyr.GetLayerDefn()) f.SetGeometry(ogr.CreateGeometryFromWkt('POINT(0 1)')) lyr.CreateFeature(f) lyr = ds.CreateLayer('lyr2', geom_type=ogr.wkbPoint) f = ogr.Feature(lyr.GetLayerDefn()) f.SetGeometry(ogr.CreateGeometryFromWkt('POINT(2 3)')) lyr.CreateFeature(f) f = ogr.Feature(lyr.GetLayerDefn()) f.SetGeometry(ogr.CreateGeometryFromWkt('POINT(4 5)')) lyr.CreateFeature(f) ds = None vl1 = QgsVectorLayer(u'{}'.format(tmpfile) + "|layername=" + "lyr1", 'test', u'ogr') self.assertTrue(vl1.isValid()) vl2 = QgsVectorLayer(u'{}'.format(tmpfile) + "|layername=" + "lyr2", 'test', u'ogr') self.assertTrue(vl2.isValid()) # prepare a project with transactions enabled p = QgsProject() p.setAutoTransaction(True) p.addMapLayers([vl1, vl2]) self.assertTrue(vl1.startEditing()) self.assertIsNotNone(vl1.dataProvider().transaction()) self.assertTrue(vl1.deleteFeature(1)) # An iterator opened on the layer should see the feature deleted self.assertEqual( len([f for f in vl1.getFeatures(QgsFeatureRequest())]), 0) # But not if opened from another connection vl1_external = QgsVectorLayer( u'{}'.format(tmpfile) + "|layername=" + "lyr1", 'test', u'ogr') self.assertTrue(vl1_external.isValid()) self.assertEqual( len([f for f in vl1_external.getFeatures(QgsFeatureRequest())]), 1) del vl1_external self.assertTrue(vl1.commitChanges()) # Should still get zero features on vl1 self.assertEqual( len([f for f in vl1.getFeatures(QgsFeatureRequest())]), 0) self.assertEqual( len([f for f in vl2.getFeatures(QgsFeatureRequest())]), 2) # Test undo/redo self.assertTrue(vl2.startEditing()) self.assertIsNotNone(vl2.dataProvider().transaction()) self.assertTrue(vl2.editBuffer().deleteFeature(1)) self.assertEqual( len([f for f in vl2.getFeatures(QgsFeatureRequest())]), 1) self.assertTrue(vl2.editBuffer().deleteFeature(2)) self.assertEqual( len([f for f in vl2.getFeatures(QgsFeatureRequest())]), 0) vl2.undoStack().undo() self.assertEqual( len([f for f in vl2.getFeatures(QgsFeatureRequest())]), 1) vl2.undoStack().undo() self.assertEqual( len([f for f in vl2.getFeatures(QgsFeatureRequest())]), 2) vl2.undoStack().redo() self.assertEqual( len([f for f in vl2.getFeatures(QgsFeatureRequest())]), 1) self.assertTrue(vl2.commitChanges()) self.assertEqual( len([f for f in vl2.getFeatures(QgsFeatureRequest())]), 1) del vl1 del vl2 vl2_external = QgsVectorLayer( u'{}'.format(tmpfile) + "|layername=" + "lyr2", 'test', u'ogr') self.assertTrue(vl2_external.isValid()) self.assertEqual( len([f for f in vl2_external.getFeatures(QgsFeatureRequest())]), 1) del vl2_external
class ComponentAnalysisDialog(QWidget, FORM_CLASS): @wait_process def __init__(self, parent_view_widget, parent=None): QWidget.__init__(self, parent) self.setupUi(self) self.is_opened = False self.parent_view_widget = parent_view_widget self.render_widget.parent_view = self self.render_widget.crs = parent_view_widget.render_widget.crs # principal component ID self.id = parent_view_widget.id self.pc_id = parent_view_widget.pc_id # edit layer properties self.layerStyleEditor.clicked.connect( self.render_widget.layer_style_editor) # zoom to layer self.ZoomToLayer.clicked.connect(self.zoom_to_layer) # set layer self.render_widget.render_layer(parent_view_widget.render_widget.layer) self.pc_layer = self.render_widget.layer # set name self.pc_name = parent_view_widget.QLabel_ViewName.text() self.QLabel_ViewName.setText(self.pc_name) # picker pixel value widget self.PickerRangeFrom.clicked.connect( lambda: self.picker_mouse_value(self.RangeChangeFrom)) self.PickerRangeTo.clicked.connect( lambda: self.picker_mouse_value(self.RangeChangeTo)) # detection layer self.driver_detection_layer = None self.GenerateDetectionLayer.clicked.connect( self.generate_detection_layer) # active/deactive self.ShowHideChangeDetection.toggled.connect( self.detection_layer_toggled) # init temporal AOI layer self.aoi_features = QgsVectorLayer( "MultiPolygon?crs=" + self.pc_layer.crs().toWkt(), "aoi", "memory") # aoi self.rubber_bands = [] self.tmp_rubber_band = [] self.AOI_Picker.clicked.connect( lambda: self.render_widget.canvas.setMapTool( PickerAOIPointTool(self), clean=True)) self.UndoAOI.clicked.connect(self.undo_aoi) self.DeleteAllAOI.clicked.connect(self.delete_all_aoi) # set statistics from combobox self.QCBox_StatsLayer.addItems([self.pc_name, "Areas Of Interest"]) self.QCBox_StatsLayer.currentIndexChanged[str].connect( self.set_statistics) # init histogram plot self.hist_data = None self.hist_data_pc = { "auto": None, "doane": None, "scott": None, "rice": None } # store histogram done for principal components self.hist_bins = { "pc": { "type": "auto", "bins": None }, "aoi": { "type": "auto", "bins": None } } self.HistogramPlot.setTitle('Histogram', size='9pt') self.HistogramPlot.setBackground('w') self.HistogramPlot.showGrid(x=True, y=True, alpha=0.3) self.HistogramTypeBins.currentIndexChanged[str].connect( lambda value: self.histogram_plot(bins=value)) self.HistogramCustomBins.hide() self.HistogramCustomBins.valueChanged.connect( lambda value: self.histogram_plot(bins=value)) # init region and synchronize the region on plot with range values self.linear_region = pg.LinearRegionItem(brush=(255, 255, 0, 40)) self.HistogramPlot.addItem(self.linear_region) self.linear_region.sigRegionChanged.connect( self.update_region_from_plot) self.RangeChangeFrom.valueChanged.connect( self.update_region_from_values) self.RangeChangeTo.valueChanged.connect(self.update_region_from_values) # statistics for current principal component self.pc_gdal_ds = gdal.Open(str(get_file_path_of_layer(self.pc_layer)), GA_ReadOnly) self.pc_data = self.pc_gdal_ds.GetRasterBand(1).ReadAsArray() self.pc_data_flat = self.pc_data.flatten() self.stats_pc = None # store stats done for principal components from pca4cd.gui.main_analysis_dialog import MainAnalysisDialog if MainAnalysisDialog.nodata is not None: self.pc_data_flat = np.delete( self.pc_data_flat, np.where(self.pc_data_flat == MainAnalysisDialog.nodata)) self.set_statistics(stats_for=self.pc_name) # init aoi data self.aoi_data = np.array([np.nan]) def clean(self): for layer in self.render_widget.canvas.layers(): QgsProject.instance().removeMapLayer(layer.id()) self.render_widget.canvas.setLayers([]) self.render_widget.canvas.clearCache() # clear/reset all rubber bands for rubber_band in self.rubber_bands + self.tmp_rubber_band: rubber_band.reset(QgsWkbTypes.PolygonGeometry) self.rubber_bands = [] self.tmp_rubber_band = [] # remove all features in aoi self.aoi_features.dataProvider().truncate() del self.pc_data, self.pc_data_flat, self.aoi_data, self.HistogramPlot, self.hist_data, \ self.hist_data_pc, self.driver_detection_layer @pyqtSlot() def show(self): self.is_opened = True self.parent_view_widget.QPBtn_ComponentAnalysisDialog.setText( "Opened, click to show") super(ComponentAnalysisDialog, self).show() def closeEvent(self, event): self.is_opened = False self.parent_view_widget.QPBtn_ComponentAnalysisDialog.setText( "Change detection layer") super(ComponentAnalysisDialog, self).closeEvent(event) @pyqtSlot() def canvas_changed(self): new_extent = self.render_widget.canvas.extent() # update canvas for all view activated except this view from pca4cd.gui.main_analysis_dialog import MainAnalysisDialog for view_widget in MainAnalysisDialog.view_widgets: # for layer view widget in main analysis dialog if view_widget.is_active and view_widget != self: view_widget.render_widget.update_canvas_to(new_extent) # for components analysis opened if view_widget.component_analysis_dialog and view_widget.component_analysis_dialog.is_opened: view_widget.component_analysis_dialog.render_widget.update_canvas_to( new_extent) @pyqtSlot() def zoom_to_layer(self): self.render_widget.canvas.setExtent(self.render_widget.layer.extent()) self.canvas_changed() self.render_widget.canvas.refresh() @pyqtSlot() def detection_layer_toggled(self): if self.ShowHideChangeDetection.isChecked(): self.render_widget.show_detection_layer() else: self.render_widget.hide_detection_layer() @pyqtSlot() def picker_mouse_value(self, picker_widget): self.render_widget.canvas.setMapTool(PickerPixelPointTool( self.render_widget, picker_widget), clean=True) @pyqtSlot() @wait_process def generate_detection_layer(self): from pca4cd.pca4cd import PCA4CD as pca4cd detection_from = self.RangeChangeFrom.value() detection_to = self.RangeChangeTo.value() output_change_layer = Path(pca4cd.tmp_dir, self.pc_layer.name() + "_detection.tif") # compute the detection layer between range values da_pc = da.from_array(self.pc_data, chunks=(2000, 2000)) def calc(block, range_from, range_to): result = np.zeros_like(block) result[(block >= range_from) & (block <= range_to) & (block != 0)] = 1 return result # process map_blocks = da.map_blocks(calc, da_pc, range_from=detection_from, range_to=detection_to, dtype=np.int8) detection_layer_ds = map_blocks.compute(scheduler='threads', num_workers=cpu_count()) # save if self.driver_detection_layer is None: driver = gdal.GetDriverByName("GTiff") self.driver_detection_layer = driver.Create( str(output_change_layer), self.pc_gdal_ds.RasterXSize, self.pc_gdal_ds.RasterYSize, 1, gdal.GDT_Byte, ["NBITS=1", "COMPRESS=NONE"]) dl_band = self.driver_detection_layer.GetRasterBand(1) dl_band.SetNoDataValue(0) dl_band.WriteArray(detection_layer_ds) # set projection and geotransform if self.pc_gdal_ds.GetGeoTransform() is not None: self.driver_detection_layer.SetGeoTransform( self.pc_gdal_ds.GetGeoTransform()) if self.pc_gdal_ds.GetProjection() is not None: self.driver_detection_layer.SetProjection( self.pc_gdal_ds.GetProjection()) dl_band.FlushCache() dl_band = None self.driver_detection_layer.FlushCache() # necessary for fix flushing cache generating the detection layer the first time (Linux/Mac) # and not in Windows due to permission problems when the detection layer is overwritten if platform.system() != 'Windows': self.driver_detection_layer = None detection_layer = load_layer(output_change_layer, add_to_legend=False) apply_symbology(detection_layer, [("0", 0, (255, 255, 255, 0)), ("1", 1, (255, 255, 0, 255))]) self.render_widget.set_detection_layer(detection_layer) self.parent_view_widget.render_widget.set_detection_layer( detection_layer) self.parent_view_widget.EnableChangeDetection.setChecked(True) self.ShowHideChangeDetection.setEnabled(True) self.ShowHideChangeDetection.setChecked(True) def set_statistics(self, stats_for=None): if stats_for == self.pc_name: from pca4cd.gui.main_analysis_dialog import MainAnalysisDialog with block_signals_to(self.QCBox_StatsLayer): self.QCBox_StatsLayer.setCurrentIndex(0) self.statistics(self.pc_data_flat, MainAnalysisDialog.pca_stats) self.histogram_plot(data=self.pc_data_flat) if stats_for == "Areas Of Interest": with block_signals_to(self.QCBox_StatsLayer): self.QCBox_StatsLayer.setCurrentIndex(1) self.statistics(self.aoi_data) self.histogram_plot(data=self.aoi_data) @wait_process def statistics(self, data, pca_stats=None): # set headers if pca_stats: # for pca if pca_stats["eigenvals"] is not None: self.stats_header.setText("Eigenvalue: {} ({}%)".format( round(pca_stats["eigenvals"][self.pc_id - 1], 2), round(pca_stats["eigenvals_%"][self.pc_id - 1], 2))) self.stats_header.setToolTip( "It shows how are the dispersion of the data with respect to its component" ) else: self.stats_header.setText("Eigenvalue: --") self.stats_header.setToolTip( "Is only available when the components are computed with the plugin" ) else: # for aoi self.stats_header.setText("Pixels in AOI: {}".format( round(data.size if data.size > 1 else 0, 2))) self.stats_header.setToolTip("") # restore or compute the statistics if self.QCBox_StatsLayer.currentText( ) == self.pc_name and self.stats_pc is not None: min, max, std, p25, p50, p75 = self.stats_pc else: da_data = da.from_array(data, chunks=(8000000, )) min = da.min(da_data).compute() max = da.max(da_data).compute() std = da.std(da_data).compute() p25 = da.percentile(da_data, 25).compute()[0] p50 = da.percentile(da_data, 50).compute()[0] p75 = da.percentile(da_data, 75).compute()[0] if self.QCBox_StatsLayer.currentText() == self.pc_name: self.stats_pc = (min, max, std, p25, p50, p75) # set in dialog self.stats_min.setText(str(round(min, 2))) self.stats_max.setText(str(round(max, 2))) self.stats_std.setText(str(round(std, 2))) self.stats_p25.setText(str(round(p25, 2))) self.stats_p50.setText(str(round(p50, 2))) self.stats_p75.setText(str(round(p75, 2))) @pyqtSlot() @wait_process def histogram_plot(self, data=None, bins=None): # which plot stats_for = self.QCBox_StatsLayer.currentText() if stats_for == self.pc_name: hist_bins = self.hist_bins["pc"] if stats_for == "Areas Of Interest": hist_bins = self.hist_bins["aoi"] # check and set data if data is not None: self.hist_data = data if self.hist_data is None or self.hist_data.size <= 1: self.HistogramPlot.clear() return # histogram bins if bins is not None: if isinstance(bins, int): set_bins = bins elif bins == "custom": hist_bins["type"] = bins self.HistogramCustomBins.show() self.HistogramCustomBins.setValue(hist_bins["bins"]) return else: self.HistogramCustomBins.hide() hist_bins["type"] = bins set_bins = bins else: # from set_statistics functions if hist_bins["type"] == "custom": set_bins = hist_bins["bins"] self.HistogramCustomBins.show() with block_signals_to(self.HistogramTypeBins): self.HistogramTypeBins.setCurrentIndex( self.HistogramTypeBins.findText("custom")) with block_signals_to(self.HistogramCustomBins): self.HistogramCustomBins.setValue(hist_bins["bins"]) else: set_bins = hist_bins["type"] self.HistogramCustomBins.hide() with block_signals_to(self.HistogramTypeBins): self.HistogramTypeBins.setCurrentIndex( self.HistogramTypeBins.findText(hist_bins["type"])) # plot if stats_for == self.pc_name and set_bins in [ "auto", "doane", "scott", "rice" ]: if self.hist_data_pc[set_bins] is not None: y, x = self.hist_data_pc[set_bins] # restore histogram values else: try: bin_edges = np.histogram_bin_edges(self.hist_data, bins=set_bins) da_hist_data = da.from_array(self.hist_data, chunks=(8000000, )) y, x = da.histogram(da_hist_data, bins=bin_edges) y = y.compute(scheduler='threads', num_workers=cpu_count()) except: # TODO: after some time delete, compatibility to old numpy version Qgis < 3.4 y, x = np.histogram(self.hist_data, bins=set_bins) self.hist_data_pc[set_bins] = (y, x) else: try: bin_edges = np.histogram_bin_edges(self.hist_data, bins=set_bins) da_hist_data = da.from_array(self.hist_data, chunks=(8000000, )) y, x = da.histogram(da_hist_data, bins=bin_edges) y = y.compute(scheduler='threads', num_workers=cpu_count()) except: # TODO: after some time delete, compatibility to old numpy version Qgis < 3.4 y, x = np.histogram(self.hist_data, bins=set_bins) self.HistogramPlot.clear() self.HistogramPlot.plot(x, y, stepMode=True, fillLevel=0, brush=(80, 80, 80)) self.HistogramPlot.autoRange() self.HistogramPlot.addItem(self.linear_region) hist_bins["bins"] = len(y) # store bins @pyqtSlot() def update_region_from_plot(self): lower, upper = self.linear_region.getRegion() self.RangeChangeFrom.setValue(lower) self.RangeChangeTo.setValue(upper) @pyqtSlot() def update_region_from_values(self): lower = self.RangeChangeFrom.value() upper = self.RangeChangeTo.value() self.linear_region.setRegion((lower, upper)) @pyqtSlot() @wait_process def aoi_changes(self, new_feature=None): """Actions after added each polygon in the AOI""" from pca4cd.pca4cd import PCA4CD as pca4cd from pca4cd.gui.main_analysis_dialog import MainAnalysisDialog # update AOI if new_feature is not None: with edit(self.aoi_features): self.aoi_features.addFeature(new_feature) # clip the raster component in AOI for get only the pixel values inside it pc_aoi = Path(pca4cd.tmp_dir, self.pc_layer.name() + "_clip_aoi.tif") clip_raster_with_shape(self.pc_layer, self.aoi_features, str(pc_aoi)) dataset = gdal.Open(str(pc_aoi), GA_ReadOnly) band = dataset.GetRasterBand(1).ReadAsArray() self.aoi_data = band.flatten() self.aoi_data = np.delete(self.aoi_data, np.where(self.aoi_data == 0)) if MainAnalysisDialog.nodata is not None: self.aoi_data = np.delete( self.aoi_data, np.where(self.aoi_data == MainAnalysisDialog.nodata)) if self.aoi_data.size == 0: self.aoi_data = np.array([np.nan]) # update statistics and histogram plot self.set_statistics(stats_for="Areas Of Interest") # update range values using min/max of AOI with decimal adjusted for include in change layer range_from = np.floor(np.min(self.aoi_data) * 1000000000) / 1000000000 range_to = np.ceil(np.max(self.aoi_data) * 1000000000) / 1000000000 # double set due the synchronization values from plot min/max adjust self.RangeChangeFrom.setValue(range_from) self.RangeChangeTo.setValue(range_to) self.RangeChangeFrom.setValue(range_from) self.RangeChangeTo.setValue(range_to) # auto generate/update the detection layer self.generate_detection_layer() # enable undo and delete buttons self.UndoAOI.setEnabled(True) self.DeleteAllAOI.setEnabled(True) del dataset, band if pc_aoi.is_file(): os.remove(pc_aoi) @pyqtSlot() @wait_process def undo_aoi(self): # delete feature features_ids = [f.id() for f in self.aoi_features.getFeatures()] with edit(self.aoi_features): self.aoi_features.deleteFeature(features_ids[-1]) # delete rubber bands self.rubber_bands.pop().reset(QgsWkbTypes.PolygonGeometry) self.tmp_rubber_band.pop().reset(QgsWkbTypes.PolygonGeometry) # update if len(list(self.aoi_features.getFeatures())) > 0: self.aoi_changes() else: # empty self.delete_all_aoi() @pyqtSlot() @wait_process def delete_all_aoi(self): # clear/reset all rubber bands for rubber_band in self.rubber_bands + self.tmp_rubber_band: rubber_band.reset(QgsWkbTypes.PolygonGeometry) self.rubber_bands = [] self.tmp_rubber_band = [] # remove all features in aoi self.aoi_features.dataProvider().truncate() # update statistics and histogram plot self.aoi_data = np.array([np.nan]) self.set_statistics(stats_for="Areas Of Interest") # disable undo delete buttons self.UndoAOI.setEnabled(False) self.DeleteAllAOI.setEnabled(False) # clear the detection layer self.render_widget.set_detection_layer(None) self.parent_view_widget.render_widget.set_detection_layer(None)
def polygonize(layer, callback=None): """Polygonize a raster layer into a vector layer using GDAL. Issue https://github.com/inasafe/inasafe/issues/3183 :param layer: The layer to reproject. :type layer: QgsRasterLayer :param callback: A function to all to indicate progress. The function should accept params 'current' (int) and 'maximum' (int). Defaults to None. :type callback: function :return: Reprojected memory layer. :rtype: QgsRasterLayer .. versionadded:: 4.0 """ output_layer_name = polygonize_steps['output_layer_name'] processing_step = polygonize_steps['step_name'] output_layer_name = output_layer_name % layer.keywords['layer_purpose'] gdal_layer_name = polygonize_steps['gdal_layer_name'] if layer.keywords.get('layer_purpose') == 'exposure': output_field = exposure_type_field else: output_field = hazard_value_field input_raster = gdal.Open(layer.source(), gdal.GA_ReadOnly) srs = osr.SpatialReference() srs.ImportFromWkt(input_raster.GetProjectionRef()) temporary_dir = temp_dir(sub_dir='pre-process') out_shapefile = unique_filename( suffix='-%s.shp' % output_layer_name, dir=temporary_dir) driver = ogr.GetDriverByName("ESRI Shapefile") destination = driver.CreateDataSource(out_shapefile) output_layer = destination.CreateLayer(gdal_layer_name, srs) # We have no other way to use a shapefile. We need only the first 10 chars. field_name = output_field['field_name'][0:10] fd = ogr.FieldDefn(field_name, ogr.OFTInteger) output_layer.CreateField(fd) input_band = input_raster.GetRasterBand(1) # Fixme : add our own callback to Polygonize gdal.Polygonize(input_band, None, output_layer, 0, [], callback=None) destination.Destroy() vector_layer = QgsVectorLayer(out_shapefile, output_layer_name, 'ogr') # Let's remove polygons which were no data request = QgsFeatureRequest() expression = '"%s" = %s' % (field_name, no_data_value) request.setFilterExpression(expression) vector_layer.startEditing() for feature in vector_layer.getFeatures(request): vector_layer.deleteFeature(feature.id()) vector_layer.commitChanges() # We transfer keywords to the output. vector_layer.keywords = layer.keywords.copy() vector_layer.keywords[ layer_geometry['key']] = layer_geometry_polygon['key'] vector_layer.keywords['title'] = output_layer_name # We just polygonized the raster layer. inasafe_fields do not exist. vector_layer.keywords['inasafe_fields'] = { output_field['key']: field_name } check_layer(vector_layer) return vector_layer
def polygonize(layer): """Polygonize a raster layer into a vector layer using GDAL. Issue https://github.com/inasafe/inasafe/issues/3183 :param layer: The layer to reproject. :type layer: QgsRasterLayer :return: Reprojected memory layer. :rtype: QgsRasterLayer .. versionadded:: 4.0 """ output_layer_name = polygonize_steps['output_layer_name'] output_layer_name = output_layer_name % layer.keywords['layer_purpose'] gdal_layer_name = polygonize_steps['gdal_layer_name'] if layer.keywords.get('layer_purpose') == 'exposure': output_field = exposure_type_field else: output_field = hazard_value_field input_raster = gdal.Open(layer.source(), gdal.GA_ReadOnly) srs = osr.SpatialReference() srs.ImportFromWkt(input_raster.GetProjectionRef()) temporary_dir = temp_dir(sub_dir='pre-process') out_shapefile = unique_filename(suffix='-%s.shp' % output_layer_name, dir=temporary_dir) driver = ogr.GetDriverByName("ESRI Shapefile") destination = driver.CreateDataSource(out_shapefile) output_layer = destination.CreateLayer(gdal_layer_name, srs) # We have no other way to use a shapefile. We need only the first 10 chars. field_name = output_field['field_name'][0:10] fd = ogr.FieldDefn(field_name, ogr.OFTInteger) output_layer.CreateField(fd) active_band = layer.keywords.get('active_band', 1) input_band = input_raster.GetRasterBand(active_band) # Fixme : add our own callback to Polygonize gdal.Polygonize(input_band, None, output_layer, 0, [], callback=None) destination.Destroy() vector_layer = QgsVectorLayer(out_shapefile, output_layer_name, 'ogr') # Let's remove polygons which were no data request = QgsFeatureRequest() expression = '"%s" = %s' % (field_name, no_data_value) request.setFilterExpression(expression) vector_layer.startEditing() for feature in vector_layer.getFeatures(request): vector_layer.deleteFeature(feature.id()) vector_layer.commitChanges() # We transfer keywords to the output. vector_layer.keywords = layer.keywords.copy() vector_layer.keywords[ layer_geometry['key']] = layer_geometry_polygon['key'] vector_layer.keywords['title'] = output_layer_name # We just polygonized the raster layer. inasafe_fields do not exist. vector_layer.keywords['inasafe_fields'] = {output_field['key']: field_name} check_layer(vector_layer) return vector_layer
class VisuValideMultiCriteriaMatching: """QGIS Plugin Implementation.""" def __init__(self, iface): """Constructor.""" # Save reference to the QGIS interface self.iface = iface # initialize plugin directory self.plugin_dir = os.path.dirname(__file__) # Create the dialog (after translation) and keep reference self.pluginIsActive = False self.dockwidget = None # Declare instance attributes self.actions = [] self.menu = "Visualisation et validation de l'appariement" # We are going to let the user set this up in a future iteration self.toolbar = self.iface.addToolBar( u'VisuValideMultiCriteriaMatching') self.toolbar.setObjectName(u'VisuValideMultiCriteriaMatching') def add_action(self, icon_path, text, callback, enabled_flag=True, add_to_menu=True, add_to_toolbar=True, status_tip=None, whats_this=None, parent=None): """Add a toolbar icon to the toolbar.""" icon = QIcon(icon_path) action = QAction(icon, text, parent) action.triggered.connect(callback) action.setEnabled(enabled_flag) if status_tip is not None: action.setStatusTip(status_tip) if whats_this is not None: action.setWhatsThis(whats_this) if add_to_toolbar: self.toolbar.addAction(action) if add_to_menu: self.iface.addPluginToMenu(self.menu, action) self.actions.append(action) return action def initGui(self): """Create the menu entries and toolbar icons inside the QGIS GUI.""" icon_path = ':/plugins/VisuValideMultiCriteriaMatching/img/match.png' self.add_action( icon_path, text="Panneau de contrôle pour visualiser et valider l'appariement", callback=self.initWidget, parent=self.iface.mainWindow()) def unload(self): """Removes the plugin menu item and icon from QGIS GUI.""" for action in self.actions: self.iface.removePluginMenu('&VisuValideMultiCriteriaMatching', action) self.iface.removeToolBarIcon(action) # remove the toolbar del self.toolbar # remove layer ? def onClosePlugin(self): """Cleanup necessary items here when plugin dockwidget is closed""" # disconnects self.pluginIsActive = False # On recharge pour repartir #self.reload() # On supprime les layers de la fenêtre self.vider() # On ferme self.dockwidget.close() def vider(self): # ---------------------------------------------------------------------------- # supprime les layers if self.layerCOMP != None: QgsProject.instance().removeMapLayers([self.layerCOMP.id()]) if self.layerREF != None: QgsProject.instance().removeMapLayers([self.layerREF.id()]) def initWidget(self): """Run method that performs all the real work""" if not self.pluginIsActive: self.pluginIsActive = True if self.dockwidget == None: self.dockwidget = VisuResultatDialog() self.layerCOMP = None self.layerREF = None self.dockwidget.btPrec.setStyleSheet( 'color : black;font: 8pt MS Shell Dlg 2') self.dockwidget.btSuiv.setStyleSheet( 'color : black;font: 8pt MS Shell Dlg 2') self.dockwidget.currentId.setStyleSheet( 'color : black;font: 8pt MS Shell Dlg 2') self.dockwidget.fileResultat.setStyleSheet( 'color : black;font: 8pt MS Shell Dlg 2') self.dockwidget.btSuiv.clicked.connect(self.doSuivant) self.dockwidget.btPrec.clicked.connect(self.doPrecedent) self.dockwidget.currentId.setDisabled(True) self.dockwidget.currentId.setText("-1") self.dockwidget.btFermer.clicked.connect(self.onClosePlugin) self.dockwidget.btZoomGrille.clicked.connect(self.zoom) self.dockwidget.fileResultat.fileChanged.connect( self.importFichier) self.dockwidget.rbID.toggled.connect(self.visuDistance) self.dockwidget.rbD1.toggled.connect(self.visuDistance) self.dockwidget.rbD2.toggled.connect(self.visuDistance) self.dockwidget.rbD3.toggled.connect(self.visuDistance) self.dockwidget.rbD4.toggled.connect(self.visuDistance) self.dockwidget.rbD5.toggled.connect(self.visuDistance) self.iface.mapCanvas().refresh() # show the dockwidget self.iface.addDockWidget(Qt.RightDockWidgetArea, self.dockwidget) self.dockwidget.show() def importFichier(self): # On supprime les layers car on va les recreer self.vider() # On charge le chemin du fichier de résultat self.uriGrille = self.dockwidget.fileResultat.filePath().strip() # print (self.uriGrille) # On ouvre le fichier pour le type de la géométrie et les distances self.DISTANCE_NOM = [] self.ATTRS_NOM = [] self.CRITERE_SEUIL = [] self.TYPE_GEOM = '' self.seuilIndecision = -1 (DISTANCE_NOM, ATTRS_NOM, TYPE_GEOM, CRITERE_SEUIL, seuilIndecision) = util.entete(self.uriGrille) self.DISTANCE_NOM = DISTANCE_NOM self.ATTRS_NOM = ATTRS_NOM self.TYPE_GEOM = TYPE_GEOM self.CRITERE_SEUIL = CRITERE_SEUIL self.seuilIndecision = seuilIndecision self.dockwidget.seuilP.setText(str(self.seuilIndecision)) # self.dockwidget.seuilP.setEnabled(False) #self.dockwidget.pign1.setEnabled(False) #self.dockwidget.pign2.setEnabled(False) self.dockwidget.currentId.setText("-1") self.createLayerComp() self.createLayerRef() self.dockwidget.labelSeuil1.setEnabled(False) self.dockwidget.D1_T1.setEnabled(False) self.dockwidget.D1_T2.setEnabled(False) self.dockwidget.labelSeuil2.setEnabled(False) self.dockwidget.D2_T1.setEnabled(False) self.dockwidget.D2_T2.setEnabled(False) self.dockwidget.labelSeuil3.setEnabled(False) self.dockwidget.D3_T1.setEnabled(False) self.dockwidget.D3_T2.setEnabled(False) self.dockwidget.labelSeuil4.setEnabled(False) self.dockwidget.D4_T1.setEnabled(False) self.dockwidget.D4_T2.setEnabled(False) self.dockwidget.labelSeuil5.setEnabled(False) self.dockwidget.D5_T1.setEnabled(False) self.dockwidget.D5_T2.setEnabled(False) # print (self.DISTANCE_NOM) # print (self.CRITERE_SEUIL) self.dockwidget.rbD1.setEnabled(False) self.dockwidget.rbD2.setEnabled(False) self.dockwidget.rbD3.setEnabled(False) self.dockwidget.rbD4.setEnabled(False) self.dockwidget.rbD5.setEnabled(False) for i in range(len(self.DISTANCE_NOM)): if i == 0: self.dockwidget.labelSeuil1.setEnabled(True) self.dockwidget.D1_T1.setEnabled(True) self.dockwidget.D1_T2.setEnabled(True) seuils = self.CRITERE_SEUIL[i] if len(seuils) > 0: self.dockwidget.D1_T1.setText(seuils[0]) if len(seuils) > 1: self.dockwidget.D1_T2.setText(seuils[1]) else: self.dockwidget.D1_T2.setEnabled(False) self.dockwidget.rbD1.setEnabled(True) if i == 1: self.dockwidget.labelSeuil2.setEnabled(True) self.dockwidget.D2_T1.setEnabled(True) self.dockwidget.D2_T2.setEnabled(True) seuils = self.CRITERE_SEUIL[i] if len(seuils) > 0: self.dockwidget.D2_T1.setText(seuils[0]) if len(seuils) > 1: self.dockwidget.D2_T2.setText(seuils[1]) else: self.dockwidget.D2_T2.setEnabled(False) self.dockwidget.rbD2.setEnabled(True) if i == 2: self.dockwidget.labelSeuil3.setEnabled(True) self.dockwidget.D3_T1.setEnabled(True) self.dockwidget.D3_T2.setEnabled(True) seuils = self.CRITERE_SEUIL[i] if len(seuils) > 0: self.dockwidget.D3_T1.setText(seuils[0]) if len(seuils) > 1: self.dockwidget.D3_T2.setText(seuils[1]) else: self.dockwidget.D3_T2.setEnabled(False) self.dockwidget.rbD3.setEnabled(True) if i == 3: self.dockwidget.labelSeuil4.setEnabled(True) self.dockwidget.D4_T1.setEnabled(True) self.dockwidget.D4_T2.setEnabled(True) seuils = self.CRITERE_SEUIL[i] if len(seuils) > 0: self.dockwidget.D4_T1.setText(seuils[0]) if len(seuils) > 1: self.dockwidget.D4_T2.setText(seuils[1]) else: self.dockwidget.D4_T2.setEnabled(False) self.dockwidget.rbD4.setEnabled(True) if i == 4: self.dockwidget.labelSeuil5.setEnabled(True) self.dockwidget.D5_T1.setEnabled(True) self.dockwidget.D5_T2.setEnabled(True) seuils = self.CRITERE_SEUIL[i] if len(seuils) > 0: self.dockwidget.D5_T1.setText(seuils[0]) if len(seuils) > 1: self.dockwidget.D5_T2.setText(seuils[1]) else: self.dockwidget.D5_T2.setEnabled(False) self.dockwidget.rbD5.setEnabled(True) def createLayerRef(self): # ====================================================================================== # Layer IGN self.layerREF = QgsVectorLayer(self.TYPE_GEOM + "?crs=epsg:2154", "REF", "memory") if self.TYPE_GEOM == 'Polygon' or self.TYPE_GEOM == 'MultiPolygon': self.layerREF = style.getRefPolygoneStyle(self.layerREF) if self.TYPE_GEOM == 'Point' or self.TYPE_GEOM == 'MultiPoint': self.layerREF = style.getRefPointStyle(self.layerREF) QgsProject.instance().addMapLayer(self.layerREF) def createLayerComp(self): # ====================================================================================== # Layer OSM self.layerCOMP = QgsVectorLayer(self.TYPE_GEOM + "?crs=epsg:2154", "COMP", "memory") # Eventuellement si vous voulez ajouter des attributs pr = self.layerCOMP.dataProvider() pr.addAttributes([QgsField("position", QVariant.String)]) for i in range(len(self.ATTRS_NOM)): pr.addAttributes([QgsField(self.ATTRS_NOM[i], QVariant.String)]) for i in range(len(self.DISTANCE_NOM)): if i == 0: pr.addAttributes( [QgsField(self.DISTANCE_NOM[i], QVariant.Double)]) if i == 1: pr.addAttributes( [QgsField(self.DISTANCE_NOM[i], QVariant.Double)]) if i == 2: pr.addAttributes( [QgsField(self.DISTANCE_NOM[i], QVariant.Double)]) if i == 3: pr.addAttributes( [QgsField(self.DISTANCE_NOM[i], QVariant.Double)]) if i == 4: pr.addAttributes( [QgsField(self.DISTANCE_NOM[i], QVariant.Double)]) self.layerCOMP.commitChanges() if self.TYPE_GEOM == 'Polygon' or self.TYPE_GEOM == 'MultiPolygon': self.layerCOMP = style.getCompPolygoneStyle( self.layerCOMP, 'position') if self.TYPE_GEOM == 'Point' or self.TYPE_GEOM == 'MultiPoint': self.layerCOMP = style.getCompPointStyle(self.layerCOMP) QgsProject.instance().addMapLayer(self.layerCOMP) def afficheContexte(self, currId): self.removeFeatures() candList = util.getCandidat(self.uriGrille, currId, self.DISTANCE_NOM, self.ATTRS_NOM) # print (len(candList)) if len(candList) > 0: candidat = candList[1] # ====================================================================================== # Layer REF pr = self.layerREF.dataProvider() self.layerREF.startEditing() poly = QgsFeature() poly.setGeometry(QgsGeometry.fromWkt(candidat['geomref'])) pr.addFeatures([poly]) # Sauvegarde les changements self.layerREF.commitChanges() # ====================================================================================== # Layer OSM pr = self.layerCOMP.dataProvider() self.layerCOMP.startEditing() for i in range(len(candList)): if i > 0: poly = QgsFeature() candidat = candList[i] poly.setGeometry(QgsGeometry.fromWkt(candidat['geomcomp'])) attrs = [] attrs.append(str(candidat['id'])) for j in range(len(self.ATTRS_NOM)): nom = self.ATTRS_NOM[j] attrs.append(candidat[nom]) for i in range(len(self.DISTANCE_NOM)): nom = self.DISTANCE_NOM[i] s = float(candidat[nom]) #print (s) attrs.append(s) poly.setAttributes(attrs) pr.addFeatures([poly]) # Sauvegarde les changements self.layerCOMP.commitChanges() # Zoom self.zoom() # remplir le tableau self.initTable(candList) def zoom(self): # ZOOM extent = self.layerCOMP.extent() self.iface.mapCanvas().setExtent(extent) self.iface.mapCanvas().refresh() def doPrecedent(self): # On recupere l'id en cours currId = self.dockwidget.currentId.text() id = util.getLignePrec(self.uriGrille, currId) self.dockwidget.currentId.setText(str(id)) self.afficheContexte(id) def doSuivant(self): # On recupere l'id en cours currId = self.dockwidget.currentId.text() id = util.getLigneSuiv(self.uriGrille, currId) self.dockwidget.currentId.setText(str(id)) self.afficheContexte(id) def removeFeatures(self): self.layerREF.startEditing() for feature in self.layerREF.getFeatures(): self.layerREF.deleteFeature(feature.id()) # commit to stop editing the layer self.layerREF.commitChanges() # =========================================== self.layerCOMP.startEditing() for feature in self.layerCOMP.getFeatures(): self.layerCOMP.deleteFeature(feature.id()) # commit to stop editing the layer self.layerCOMP.commitChanges() def initTable(self, candList): self.vide(self.dockwidget.tableCoordFeu) if (len(candList)) == 0: self.dockwidget.tableCoordFeu.setRowCount(0) self.dockwidget.tableCoordFeu.setColumnCount(0) else: orange = QColor(255, 196, 109) vert = QColor(0, 255, 0) nbApp = 0 nbNonApp = 0 nbIndecis = 0 labelRes = '' pign1 = '' pign2 = '' for i in range(len(candList)): candidat = candList[i] # On ajoute les coordonnées au tableau n = self.dockwidget.tableCoordFeu.rowCount() self.dockwidget.tableCoordFeu.insertRow(n) item1 = QTableWidgetItem(str(candidat['id'])) self.dockwidget.tableCoordFeu.setItem(n, 0, item1) for i in range(len(self.ATTRS_NOM)): nom = self.ATTRS_NOM[i] itemDistance = QTableWidgetItem(str(candidat[nom])) self.dockwidget.tableCoordFeu.setItem( n, 1 + i, itemDistance) for i in range(len(self.DISTANCE_NOM)): nom = self.DISTANCE_NOM[i] itemDistance = QTableWidgetItem(str(candidat[nom])) self.dockwidget.tableCoordFeu.setItem( n, 1 + len(self.ATTRS_NOM) + i, itemDistance) s = float(candidat[nom]) if i == 0: if self.dockwidget.D1_T1.isEnabled(): seuil1 = float(self.dockwidget.D1_T1.text()) if self.dockwidget.D1_T2.isEnabled(): seuil2 = float(self.dockwidget.D1_T2.text()) if i == 1: if self.dockwidget.D2_T1.isEnabled(): seuil1 = float(self.dockwidget.D2_T1.text()) if self.dockwidget.D2_T2.isEnabled(): seuil2 = float(self.dockwidget.D2_T2.text()) if i == 2: if self.dockwidget.D3_T1.isEnabled(): seuil1 = float(self.dockwidget.D3_T1.text()) if self.dockwidget.D3_T2.isEnabled(): seuil2 = float(self.dockwidget.D3_T2.text()) if i == 3: if self.dockwidget.D4_T1.isEnabled(): seuil1 = float(self.dockwidget.D4_T1.text()) if self.dockwidget.D4_T2.isEnabled(): seuil2 = float(self.dockwidget.D4_T2.text()) if i == 4: if self.dockwidget.D5_T1.isEnabled(): seuil1 = float(self.dockwidget.D5_T1.text()) if self.dockwidget.D5_T2.isEnabled(): seuil2 = float(self.dockwidget.D5_T2.text()) # print (i) if s < seuil1 and i < 2: self.dockwidget.tableCoordFeu.item( n, 1 + i + len(self.ATTRS_NOM)).setBackground(vert) elif s < seuil2 and i < 2: self.dockwidget.tableCoordFeu.item( n, 1 + i + len(self.ATTRS_NOM)).setBackground(orange) isNA = False if candidat['nom'] == 'NA': isNA = True # print (candidat['decision']) # decision et resultat if (isNA and candidat['decision'] == 'true'): nbNonApp = nbNonApp + 1 if (not isNA and candidat['decision'] == 'true'): nbApp = nbApp + 1 labelRes = candidat['id'] pign1 = candidat['pign1'] pign2 = candidat['pign2'] if (isNA and candidat['decision'] == 'indécis'): nbIndecis = nbIndecis + 1 self.dockwidget.tableCoordFeu.scrollToBottom() if nbIndecis > 0: self.dockwidget.labelResultat.setText('INDECIS') self.dockwidget.labelResultat.setStyleSheet( 'color:black;font: 8pt MS Shell Dlg 2;background-color:orange' ) elif nbNonApp > 0: self.dockwidget.labelResultat.setText("Pas d'appariement") self.dockwidget.labelResultat.setStyleSheet( 'color:black;font: 8pt MS Shell Dlg 2;background-color:white' ) elif nbApp > 0: self.dockwidget.labelResultat.setText("Appariement avec " + labelRes) self.dockwidget.labelResultat.setStyleSheet( 'color:black;font: 8pt MS Shell Dlg 2;background-color:lightgreen' ) #else: # print ('Inconnu') self.dockwidget.pign1.setText(pign1) self.dockwidget.pign2.setText(pign2) # header = self.dockwidget.tableCoordFeu.horizontalHeader() # header.setSectionResizeMode(0, QtWidgets.QHeaderView.ResizeToContents) # header.setSectionResizeMode(1, QtWidgets.QHeaderView.ResizeToContents) # header.setSectionResizeMode(2, QtWidgets.QHeaderView.ResizeToContents) # header.setSectionResizeMode(3, QtWidgets.QHeaderView.ResizeToContents) # header.setSectionResizeMode(4, QtWidgets.QHeaderView.ResizeToContents) def vide(self, table): self.dockwidget.tableCoordFeu.setRowCount(0) self.dockwidget.tableCoordFeu.setColumnCount( len(self.ATTRS_NOM) + len(self.DISTANCE_NOM) + 1) colHearder = [] colHearder.append('id') for i in range(len(self.ATTRS_NOM)): nom = self.ATTRS_NOM[i] colHearder.append(nom) for i in range(len(self.DISTANCE_NOM)): nom = self.DISTANCE_NOM[i] nom = nom.replace('Distance', 'D').replace('distance', 'D') colHearder.append(nom) if i == 0: self.dockwidget.labelSeuil1.setText(nom) if i == 1: self.dockwidget.labelSeuil2.setText(nom) if i == 2: self.dockwidget.labelSeuil3.setText(nom) if i == 3: self.dockwidget.labelSeuil4.setText(nom) if i == 4: self.dockwidget.labelSeuil5.setText(nom) # table.setHorizontalHeaderLabels(colHearder) def visuDistance(self): # print (self.DISTANCE_NOM[0]) nomAttr = ''
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', 'test', 'memory') self.assertTrue(ml.isValid()) d = QTemporaryDir() options = QgsVectorFileWriter.SaveVectorOptions() options.driverName = 'GPKG' options.layerName = 'layer_a' err, _ = QgsVectorFileWriter.writeAsVectorFormatV2(ml, os.path.join(d.path(), 'transaction_test.gpkg'), QgsCoordinateTransformContext(), options) self.assertEqual(err, QgsVectorFileWriter.NoError) self.assertTrue(os.path.isfile(os.path.join(d.path(), 'transaction_test.gpkg'))) options.layerName = 'layer_b' options.actionOnExistingFile = QgsVectorFileWriter.CreateOrOverwriteLayer err, _ = QgsVectorFileWriter.writeAsVectorFormatV2(ml, os.path.join(d.path(), 'transaction_test.gpkg'), QgsCoordinateTransformContext(), options) layer_a = QgsVectorLayer(os.path.join(d.path(), 'transaction_test.gpkg|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(), {}) layer_a.changeAttributeValue(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) layer_a.undoStack().undo() 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 geometry f = next(layer_a.getFeatures()) self.assertTrue(layer_a.changeGeometry(f.id(), QgsGeometry.fromWkt('point(9 43)'))) _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 anothr 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)') self.assertTrue(layer_a.startEditing()) layer_a.changeAttributeValue(f.id(), 1, 321) buffer = layer_a.editBuffer() self.assertEqual(buffer.changedAttributeValues(), {1: {1: 321}}) layer_a.undoStack().undo() self.assertEqual(buffer.changedAttributeValues(), {}) # Change geometry self.assertTrue(layer_a.changeGeometry(f.id(), QgsGeometry.fromWkt('point(9 43)'))) f = next(layer_a.getFeatures()) self.assertEqual(f.geometry().asWkt().upper(), 'POINT (9 43)') self.assertEqual(buffer.changedGeometries()[1].asWkt().upper(), 'POINT (9 43)') layer_a.undoStack().undo() 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(), [2]) 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) layer_a.undoStack().redo() self.assertEqual(buffer.deletedAttributeIds(), [2]) self.assertEqual(layer_a.fields().lookupField(field.name()), -1) self.assertTrue(layer_a.rollBack()) ########################################### # Rename attribute self.assertTrue(layer_a.startEditing()) attr_idx = layer_a.fields().lookupField(field.name()) self.assertNotEqual(attr_idx, -1) 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)
def testTransaction(self): tmpfile = os.path.join(self.basetestpath, 'testTransaction.gpkg') ds = ogr.GetDriverByName('GPKG').CreateDataSource(tmpfile) lyr = ds.CreateLayer('lyr1', geom_type=ogr.wkbPoint) f = ogr.Feature(lyr.GetLayerDefn()) f.SetGeometry(ogr.CreateGeometryFromWkt('POINT(0 1)')) lyr.CreateFeature(f) lyr = ds.CreateLayer('lyr2', geom_type=ogr.wkbPoint) f = ogr.Feature(lyr.GetLayerDefn()) f.SetGeometry(ogr.CreateGeometryFromWkt('POINT(2 3)')) lyr.CreateFeature(f) f = ogr.Feature(lyr.GetLayerDefn()) f.SetGeometry(ogr.CreateGeometryFromWkt('POINT(4 5)')) lyr.CreateFeature(f) ds = None vl1 = QgsVectorLayer(u'{}'.format(tmpfile) + "|layername=" + "lyr1", 'test', u'ogr') self.assertTrue(vl1.isValid()) vl2 = QgsVectorLayer(u'{}'.format(tmpfile) + "|layername=" + "lyr2", 'test', u'ogr') self.assertTrue(vl2.isValid()) # prepare a project with transactions enabled p = QgsProject() p.setAutoTransaction(True) p.addMapLayers([vl1, vl2]) self.assertTrue(vl1.startEditing()) self.assertIsNotNone(vl1.dataProvider().transaction()) self.assertTrue(vl1.deleteFeature(1)) # An iterator opened on the layer should see the feature deleted self.assertEqual(len([f for f in vl1.getFeatures(QgsFeatureRequest())]), 0) # But not if opened from another connection vl1_external = QgsVectorLayer(u'{}'.format(tmpfile) + "|layername=" + "lyr1", 'test', u'ogr') self.assertTrue(vl1_external.isValid()) self.assertEqual(len([f for f in vl1_external.getFeatures(QgsFeatureRequest())]), 1) del vl1_external self.assertTrue(vl1.commitChanges()) # Should still get zero features on vl1 self.assertEqual(len([f for f in vl1.getFeatures(QgsFeatureRequest())]), 0) self.assertEqual(len([f for f in vl2.getFeatures(QgsFeatureRequest())]), 2) # Test undo/redo self.assertTrue(vl2.startEditing()) self.assertIsNotNone(vl2.dataProvider().transaction()) self.assertTrue(vl2.editBuffer().deleteFeature(1)) self.assertEqual(len([f for f in vl2.getFeatures(QgsFeatureRequest())]), 1) self.assertTrue(vl2.editBuffer().deleteFeature(2)) self.assertEqual(len([f for f in vl2.getFeatures(QgsFeatureRequest())]), 0) vl2.undoStack().undo() self.assertEqual(len([f for f in vl2.getFeatures(QgsFeatureRequest())]), 1) vl2.undoStack().undo() self.assertEqual(len([f for f in vl2.getFeatures(QgsFeatureRequest())]), 2) vl2.undoStack().redo() self.assertEqual(len([f for f in vl2.getFeatures(QgsFeatureRequest())]), 1) self.assertTrue(vl2.commitChanges()) self.assertEqual(len([f for f in vl2.getFeatures(QgsFeatureRequest())]), 1) del vl1 del vl2 vl2_external = QgsVectorLayer(u'{}'.format(tmpfile) + "|layername=" + "lyr2", 'test', u'ogr') self.assertTrue(vl2_external.isValid()) self.assertEqual(len([f for f in vl2_external.getFeatures(QgsFeatureRequest())]), 1) del vl2_external
def start_progress(self): import datetime start = datetime.datetime.now() # Check OS and dep if sys.platform == 'darwin': gdal_os_dep = '/Library/Frameworks/GDAL.framework/Versions/Current/Programs/' else: gdal_os_dep = '' if self.dlg.canvasButton.isChecked(): # Map Canvas extentCanvasCRS = self.iface.mapCanvas() srs = extentCanvasCRS.mapSettings().destinationCrs() crs = str(srs.authid()) # old_crs = osr.SpatialReference() # old_crs.ImportFromEPSG(int(crs[5:])) can_crs = QgsCoordinateReferenceSystem(int(crs[5:])) # can_wkt = extentCanvasCRS.mapRenderer().destinationCrs().toWkt() # can_crs = osr.SpatialReference() # can_crs.ImportFromWkt(can_wkt) # Raster Layer dem_layer = self.layerComboManagerDEM.currentLayer() dem_prov = dem_layer.dataProvider() dem_path = str(dem_prov.dataSourceUri()) dem_raster = gdal.Open(dem_path) projdsm = osr.SpatialReference(wkt=dem_raster.GetProjection()) projdsm.AutoIdentifyEPSG() projdsmepsg = int(projdsm.GetAttrValue('AUTHORITY', 1)) dem_crs = QgsCoordinateReferenceSystem(projdsmepsg) # dem_wkt = dem_raster.GetProjection() # dem_crs = osr.SpatialReference() # dem_crs.ImportFromWkt(dem_wkt) if can_crs != dem_crs: extentCanvas = self.iface.mapCanvas().extent() extentDEM = dem_layer.extent() transformExt = QgsCoordinateTransform(can_crs, dem_crs) # transformExt = osr.CoordinateTransformation(can_crs, dem_crs) canminx = extentCanvas.xMinimum() canmaxx = extentCanvas.xMaximum() canminy = extentCanvas.yMinimum() canmaxy = extentCanvas.yMaximum() canxymin = transformExt.TransformPoint(canminx, canminy) canxymax = transformExt.TransformPoint(canmaxx, canmaxy) extDiffminx = canxymin[0] - extentDEM.xMinimum( ) # If smaller than zero = warning extDiffminy = canxymin[1] - extentDEM.yMinimum( ) # If smaller than zero = warning extDiffmaxx = canxymax[0] - extentDEM.xMaximum( ) # If larger than zero = warning extDiffmaxy = canxymax[0] - extentDEM.yMaximum( ) # If larger than zero = warning if extDiffminx < 0 or extDiffminy < 0 or extDiffmaxx > 0 or extDiffmaxy > 0: QMessageBox.warning( None, "Warning! Extent of map canvas is larger than raster extent.", "Change to an extent equal to or smaller than the raster extent." ) return # Extent self.yMax = self.dlg.lineEditNorth.text() self.yMin = self.dlg.lineEditSouth.text() self.xMin = self.dlg.lineEditWest.text() self.xMax = self.dlg.lineEditEast.text() if not self.DSMoutputfile: QMessageBox.critical(None, "Error", "Specify a raster output file") return if self.dlg.checkBoxPolygon.isChecked() and not self.OSMoutputfile: QMessageBox.critical(None, "Error", "Specify an output file for OSM data") return # Acquiring geodata and attributes dem_layer = self.layerComboManagerDEM.currentLayer() if dem_layer is None: QMessageBox.critical(None, "Error", "No valid raster layer is selected") return else: provider = dem_layer.dataProvider() filepath_dem = str(provider.dataSourceUri()) demRaster = gdal.Open(filepath_dem) dem_layer_crs = osr.SpatialReference() dem_layer_crs.ImportFromWkt(demRaster.GetProjection()) self.dem_layer_unit = dem_layer_crs.GetAttrValue("UNIT") posUnits = [ 'metre', 'US survey foot', 'meter', 'm', 'ft', 'feet', 'foot', 'ftUS', 'International foot' ] # Possible units if not self.dem_layer_unit in posUnits: QMessageBox.critical( None, "Error", "Raster projection is not in metre or foot. Please reproject.") return polygon_layer = self.layerComboManagerPolygon.currentLayer() osm_layer = self.dlg.checkBoxOSM.isChecked() if polygon_layer is None and osm_layer is False: QMessageBox.critical(None, "Error", "No valid building height layer is selected") return elif polygon_layer: vlayer = QgsVectorLayer(polygon_layer.source(), "buildings", "ogr") fileInfo = QFileInfo(polygon_layer.source()) polygon_ln = fileInfo.baseName() polygon_field = self.layerComboManagerPolygonField.currentField() idx = vlayer.fieldNameIndex(polygon_field) flname = vlayer.attributeDisplayName(idx) if idx == -1: QMessageBox.critical( None, "Error", "An attribute with unique fields must be selected") return ### main code ### self.dlg.progressBar.setRange(0, 5) self.dlg.progressBar.setValue(1) if self.dlg.checkBoxOSM.isChecked(): # TODO replace osr.CoordinateTransformation with QgsCoordinateTransform dem_original = gdal.Open(filepath_dem) dem_wkt = dem_original.GetProjection() ras_crs = osr.SpatialReference() ras_crs.ImportFromWkt(dem_wkt) rasEPSG = ras_crs.GetAttrValue("PROJCS|AUTHORITY", 1) if self.dlg.layerButton.isChecked(): old_crs = ras_crs elif self.dlg.canvasButton.isChecked(): canvasCRS = self.iface.mapCanvas() outputWkt = canvasCRS.mapRenderer().destinationCrs().toWkt() old_crs = osr.SpatialReference() old_crs.ImportFromWkt(outputWkt) wgs84_wkt = """ GEOGCS["WGS 84", DATUM["WGS_1984", SPHEROID["WGS 84",6378137,298.257223563, AUTHORITY["EPSG","7030"]], AUTHORITY["EPSG","6326"]], PRIMEM["Greenwich",0, AUTHORITY["EPSG","8901"]], UNIT["degree",0.01745329251994328, AUTHORITY["EPSG","9122"]], AUTHORITY["EPSG","4326"]]""" new_crs = osr.SpatialReference() new_crs.ImportFromWkt(wgs84_wkt) transform = osr.CoordinateTransformation(old_crs, new_crs) minx = float(self.xMin) miny = float(self.yMin) maxx = float(self.xMax) maxy = float(self.yMax) lonlatmin = transform.TransformPoint(minx, miny) lonlatmax = transform.TransformPoint(maxx, maxy) if ras_crs != old_crs: rasTrans = osr.CoordinateTransformation(old_crs, ras_crs) raslonlatmin = rasTrans.TransformPoint(float(self.xMin), float(self.yMin)) raslonlatmax = rasTrans.TransformPoint(float(self.xMax), float(self.yMax)) #else: #raslonlatmin = [float(self.xMin), float(self.yMin)] #raslonlatmax = [float(self.xMax), float(self.yMax)] self.xMin = raslonlatmin[0] self.yMin = raslonlatmin[1] self.xMax = raslonlatmax[0] self.yMax = raslonlatmax[1] # Make data queries to overpass-api urlStr = 'http://overpass-api.de/api/map?bbox=' + str( lonlatmin[0]) + ',' + str(lonlatmin[1]) + ',' + str( lonlatmax[0]) + ',' + str(lonlatmax[1]) osmXml = urllib.urlopen(urlStr).read() #print urlStr # Make OSM building file osmPath = self.plugin_dir + '/temp/OSM_building.osm' osmFile = open(osmPath, 'w') osmFile.write(osmXml) if os.fstat(osmFile.fileno()).st_size < 1: urlStr = 'http://api.openstreetmap.org/api/0.6/map?bbox=' + str( lonlatmin[0]) + ',' + str(lonlatmin[1]) + ',' + str( lonlatmax[0]) + ',' + str(lonlatmax[1]) osmXml = urllib.urlopen(urlStr).read() osmFile.write(osmXml) #print 'Open Street Map' if os.fstat(osmFile.fileno()).st_size < 1: QMessageBox.critical(None, "Error", "No OSM data available") return osmFile.close() outputshp = self.plugin_dir + '/temp/' osmToShape = gdal_os_dep + 'ogr2ogr --config OSM_CONFIG_FILE "' + self.plugin_dir + '/osmconf.ini" -skipfailures -t_srs EPSG:' + str( rasEPSG ) + ' -overwrite -nlt POLYGON -f "ESRI Shapefile" "' + outputshp + '" "' + osmPath + '"' if sys.platform == 'win32': si = subprocess.STARTUPINFO() si.dwFlags |= subprocess.STARTF_USESHOWWINDOW subprocess.call(osmToShape, startupinfo=si) else: os.system(osmToShape) driver = ogr.GetDriverByName('ESRI Shapefile') driver.DeleteDataSource(outputshp + 'lines.shp') driver.DeleteDataSource(outputshp + 'multilinestrings.shp') driver.DeleteDataSource(outputshp + 'other_relations.shp') driver.DeleteDataSource(outputshp + 'points.shp') osmPolygonPath = outputshp + 'multipolygons.shp' vlayer = QgsVectorLayer(osmPolygonPath, 'multipolygons', 'ogr') polygon_layer = vlayer fileInfo = QFileInfo(polygon_layer.source()) polygon_ln = fileInfo.baseName() def renameField(srcLayer, oldFieldName, newFieldName): ds = gdal.OpenEx(srcLayer.source(), gdal.OF_VECTOR | gdal.OF_UPDATE) ds.ExecuteSQL('ALTER TABLE {} RENAME COLUMN {} TO {}'.format( srcLayer.name(), oldFieldName, newFieldName)) srcLayer.reload() vlayer.startEditing() renameField(vlayer, 'building_l', 'bld_levels') renameField(vlayer, 'building_h', 'bld_hght') renameField(vlayer, 'building_c', 'bld_colour') renameField(vlayer, 'building_m', 'bld_materi') renameField(vlayer, 'building_u', 'bld_use') vlayer.commitChanges() vlayer.startEditing() vlayer.dataProvider().addAttributes( [QgsField('bld_height', QVariant.Double, 'double', 3, 2)]) vlayer.updateFields() bld_lvl = vlayer.fieldNameIndex('bld_levels') hght = vlayer.fieldNameIndex('height') bld_hght = vlayer.fieldNameIndex('bld_hght') bld_height = vlayer.fieldNameIndex('bld_height') bldLvlHght = float(self.dlg.doubleSpinBoxBldLvl.value()) illegal_chars = string.ascii_letters + "!#$%&'*+^_`|~:" + " " counterNone = 0 counter = 0 #counterWeird = 0 for feature in vlayer.getFeatures(): if feature[hght]: try: #feature[bld_height] = float(re.sub("[^0-9]", ".", str(feature[hght]))) feature[bld_height] = float( str(feature[hght]).translate(None, illegal_chars)) except: counterNone += 1 elif feature[bld_hght]: try: #feature[bld_height] = float(re.sub("[^0-9]", ".", str(feature[bld_hght]))) feature[bld_height] = float( str(feature[bld_hght]).translate( None, illegal_chars)) except: counterNone += 1 elif feature[bld_lvl]: try: #feature[bld_height] = float(re.sub("[^0-9]", "", str(feature[bld_lvl])))*bldLvlHght feature[bld_height] = float( str(feature[bld_lvl]).translate( None, illegal_chars)) * bldLvlHght except: counterNone += 1 else: counterNone += 1 vlayer.updateFeature(feature) counter += 1 vlayer.commitChanges() flname = vlayer.attributeDisplayName(bld_height) counterDiff = counter - counterNone # Zonal statistics vlayer.startEditing() zoneStat = QgsZonalStatistics(vlayer, filepath_dem, "stat_", 1, QgsZonalStatistics.Mean) zoneStat.calculateStatistics(None) vlayer.dataProvider().addAttributes( [QgsField('height_asl', QVariant.Double)]) vlayer.updateFields() e = QgsExpression('stat_mean + ' + flname) e.prepare(vlayer.pendingFields()) idx = vlayer.fieldNameIndex('height_asl') for f in vlayer.getFeatures(): f[idx] = e.evaluate(f) vlayer.updateFeature(f) vlayer.commitChanges() vlayer.startEditing() idx2 = vlayer.fieldNameIndex('stat_mean') vlayer.dataProvider().deleteAttributes([idx2]) vlayer.updateFields() vlayer.commitChanges() self.dlg.progressBar.setValue(2) # Convert polygon layer to raster # Define pixel_size and NoData value of new raster pixel_size = self.dlg.spinBox.value() # half picture size # Create the destination data source gdalrasterize = gdal_os_dep + 'gdal_rasterize -a ' + 'height_asl' + ' -te ' + str(self.xMin) + ' ' + str(self.yMin) + ' ' + str(self.xMax) + ' ' + str(self.yMax) +\ ' -tr ' + str(pixel_size) + ' ' + str(pixel_size) + ' -l "' + str(polygon_ln) + '" "' \ + str(polygon_layer.source()) + '" "' + self.plugin_dir + '/temp/clipdsm.tif"' # gdalclipdem = gdal_os_dep + 'gdalwarp -dstnodata -9999 -q -overwrite -te ' + str(self.xMin) + ' ' + str(self.yMin) + ' ' + str(self.xMax) + ' ' + str(self.yMax) +\ # ' -tr ' + str(pixel_size) + ' ' + str(pixel_size) + \ # ' -of GTiff ' + '"' + filepath_dem + '" "' + self.plugin_dir + '/temp/clipdem.tif"' # Rasterize if sys.platform == 'win32': si = subprocess.STARTUPINFO() si.dwFlags |= subprocess.STARTF_USESHOWWINDOW subprocess.call(gdalrasterize, startupinfo=si) # subprocess.call(gdalclipdem, startupinfo=si) gdal.Warp(self.plugin_dir + '/temp/clipdem.tif', filepath_dem, xRes=pixel_size, yRes=pixel_size) else: os.system(gdalrasterize) # os.system(gdalclipdem) gdal.Warp(self.plugin_dir + '/temp/clipdem.tif', filepath_dem, xRes=pixel_size, yRes=pixel_size) # Remove gdalwarp with gdal.Translate # bigraster = gdal.Open(filepath_dem) # bbox = (self.xMin, self.yMax, self.xMax, self.yMin) # gdal.Translate(self.plugin_dir + '/data/clipdem.tif', bigraster, projWin=bbox) self.dlg.progressBar.setValue(3) # Adding DSM to DEM # Read DEM dem_raster = gdal.Open(self.plugin_dir + '/temp/clipdem.tif') dem_array = np.array(dem_raster.ReadAsArray().astype(np.float)) dsm_raster = gdal.Open(self.plugin_dir + '/temp/clipdsm.tif') dsm_array = np.array(dsm_raster.ReadAsArray().astype(np.float)) indx = dsm_array.shape for ix in range(0, int(indx[0])): for iy in range(0, int(indx[1])): if int(dsm_array[ix, iy]) == 0: dsm_array[ix, iy] = dem_array[ix, iy] if self.dlg.checkBoxPolygon.isChecked(): vlayer.startEditing() idxHght = vlayer.fieldNameIndex('height_asl') idxBld = vlayer.fieldNameIndex('building') features = vlayer.getFeatures() #for f in vlayer.getFeatures(): for f in features: geom = f.geometry() posUnitsMetre = ['metre', 'meter', 'm'] # Possible metre units posUnitsFt = [ 'US survey foot', 'ft', 'feet', 'foot', 'ftUS', 'International foot' ] # Possible foot units if self.dem_layer_unit in posUnitsMetre: sqUnit = 1 elif self.dem_layer_unit in posUnitsFt: sqUnit = 10.76 if int(geom.area()) > 50000 * sqUnit: vlayer.deleteFeature(f.id()) #if not f[idxHght]: #vlayer.deleteFeature(f.id()) #elif not f[idxBld]: #vlayer.deleteFeature(f.id()) vlayer.updateFields() vlayer.commitChanges() QgsVectorFileWriter.writeAsVectorFormat(vlayer, str(self.OSMoutputfile), "UTF-8", None, "ESRI Shapefile") else: vlayer.startEditing() idx3 = vlayer.fieldNameIndex('height_asl') vlayer.dataProvider().deleteAttributes([idx3]) vlayer.updateFields() vlayer.commitChanges() self.dlg.progressBar.setValue(4) # Save raster def saveraster( gdal_data, filename, raster ): # gdal_data = raster extent, filename = output filename, raster = numpy array (raster to be saved) rows = gdal_data.RasterYSize cols = gdal_data.RasterXSize outDs = gdal.GetDriverByName("GTiff").Create( filename, cols, rows, int(1), gdal.GDT_Float32) outBand = outDs.GetRasterBand(1) # write the data outBand.WriteArray(raster, 0, 0) # flush data to disk, set the NoData value and calculate stats outBand.FlushCache() outBand.SetNoDataValue(-9999) # georeference the image and set the projection outDs.SetGeoTransform(gdal_data.GetGeoTransform()) outDs.SetProjection(gdal_data.GetProjection()) saveraster(dsm_raster, self.DSMoutputfile, dsm_array) # Load result into canvas rlayer = self.iface.addRasterLayer(self.DSMoutputfile) # Trigger a repaint if hasattr(rlayer, "setCacheImage"): rlayer.setCacheImage(None) rlayer.triggerRepaint() self.dlg.progressBar.setValue(5) #runTime = datetime.datetime.now() - start if self.dlg.checkBoxOSM.isChecked(): QMessageBox.information( self.dlg, 'DSM Generator', 'Operation successful! ' + str(counterDiff) + ' building polygons out of ' + str(counter) + ' contained height values.') #self.iface.messageBar().pushMessage("DSM Generator. Operation successful! " + str(counterDiff) + " buildings out of " + str(counter) + " contained height values.", level=QgsMessageBar.INFO, duration=5) else: #self.iface.messageBar().pushMessage("DSM Generator. Operation successful!", level=QgsMessageBar.INFO, duration=5) QMessageBox.information(self.dlg, 'DSM Generator', 'Operation successful!') self.resetPlugin()