Ejemplo n.º 1
0
    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())
Ejemplo n.º 2
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()
        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
Ejemplo n.º 3
0
    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']))
Ejemplo n.º 4
0
    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
Ejemplo n.º 6
0
    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)
Ejemplo n.º 7
0
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))
Ejemplo n.º 8
0
    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
Ejemplo n.º 9
0
    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)
Ejemplo n.º 10
0
    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],
        )
Ejemplo n.º 11
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
Ejemplo n.º 12
0
    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
Ejemplo n.º 13
0
    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
Ejemplo n.º 14
0
    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])
Ejemplo n.º 15
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)
Ejemplo n.º 16
0
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())
Ejemplo n.º 17
0
    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()
Ejemplo n.º 18
0
    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
Ejemplo n.º 19
0
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)
Ejemplo n.º 20
0
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
Ejemplo n.º 21
0
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)
Ejemplo n.º 24
0
    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
Ejemplo n.º 25
0
    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()