Пример #1
0
 def read_db_keywords(self, db_path):
     """Can we read sqlite keywords with the generic readKeywords method
     """
     # noinspection PyUnresolvedReferences
     local_path = os.path.join(
         os.path.dirname(__file__), '../../..///', 'jk.sqlite')
     self.keyword_io.set_keyword_db_path(db_path)
     # We need to make a local copy of the dataset so
     # that we can use a local path that will hash properly on the
     # database to return us the correct / valid keywords record.
     shutil.copy2(os.path.join(TESTDATA, 'jk.sqlite'), local_path)
     uri = QgsDataSourceURI()
     # always use relative path!
     uri.setDatabase('../jk.sqlite')
     uri.setDataSource('', 'osm_buildings', 'Geometry')
     # create a local version that has the relative url
     sqlite_layer = QgsVectorLayer(uri.uri(), 'OSM Buildings', 'spatialite')
     expected_source = (
         'dbname=\'../jk.sqlite\' table="osm_buildings" (Geometry) sql=')
     message = 'Got source: %s\n\nExpected %s\n' % (
         sqlite_layer.source(), expected_source)
     assert sqlite_layer.source() == expected_source, message
     keywords = self.keyword_io.read_keywords(sqlite_layer)
     expected_keywords = self.expected_sqlite_keywords
     message = 'Got: %s\n\nExpected %s\n\nSource: %s' % (
         keywords, expected_keywords, self.sqlite_layer.source())
     assert keywords == expected_keywords, message
     source = self.sqlite_layer.source()
     # delete sqlite_layer so that we can delete the file
     del sqlite_layer
     os.remove(local_path)
     message = 'Got: %s\n\nExpected %s\n\nSource: %s' % (
         keywords, expected_keywords, source)
     assert keywords == expected_keywords, message
Пример #2
0
    def read_db_keywords(self, db_path):
        """Can we read sqlite keywords with the generic readKeywords method
        """
        self.keyword_io.set_keyword_db_path(db_path)

        # We need to use relative path so that the hash from URI will match
        local_path = os.path.join(
            os.path.dirname(__file__), 'exposure.sqlite')
        sqlite_building_path = test_data_path('exposure', 'exposure.sqlite')
        shutil.copy2(sqlite_building_path, local_path)
        uri = QgsDataSourceURI()
        uri.setDatabase('exposure.sqlite')
        uri.setDataSource('', 'buildings_osm_4326', 'Geometry')
        sqlite_layer = QgsVectorLayer(uri.uri(), 'OSM Buildings', 'spatialite')

        expected_source = (
            'dbname=\'exposure.sqlite\' table="buildings_osm_4326" ('
            'Geometry) sql=')
        message = 'Got source: %s\n\nExpected %s\n' % (
            sqlite_layer.source(), expected_source)
        self.assertEqual(sqlite_layer.source(), expected_source, message)

        keywords = self.keyword_io.read_keywords(sqlite_layer)
        expected_keywords = self.expected_sqlite_keywords
        message = 'Got: %s\n\nExpected %s\n\nSource: %s' % (
            keywords, expected_keywords, self.sqlite_layer.source())
        self.assertDictEqual(keywords, expected_keywords, message)

        # Delete SQL Layer so that we can delete the file
        del sqlite_layer
        os.remove(local_path)
 def testUniqueSource(self):
     """
     Similar memory layers should have unique source - some code checks layer source to identify
     matching layers
     """
     layer = QgsVectorLayer("Point", "test", "memory")
     layer2 = QgsVectorLayer("Point", "test2", "memory")
     self.assertNotEqual(layer.source(), layer2.source())
Пример #4
0
def getObjectFromUri(uri, forceLoad=True):
    """Returns an object (layer/table) given a source definition.

    if forceLoad is true, it tries to load it if it is not currently open
    Otherwise, it will return the object only if it is loaded in QGIS.
    """

    if uri is None:
        return None
    if uri in _loadedLayers:
        return _loadedLayers[uri]
    layers = getRasterLayers()
    for layer in layers:
        if normalizeLayerSource(layer.source()) == normalizeLayerSource(uri):
            return layer
    layers = getVectorLayers()
    for layer in layers:
        if normalizeLayerSource(layer.source()) == normalizeLayerSource(uri):
            return layer
    tables = getTables()
    for table in tables:
        if normalizeLayerSource(table.source()) == normalizeLayerSource(uri):
            return table
    if forceLoad:
        settings = QSettings()
        prjSetting = settings.value('/Projections/defaultBehaviour')
        settings.setValue('/Projections/defaultBehaviour', '')

        # If is not opened, we open it
        layer = QgsVectorLayer(uri, uri, 'ogr')
        if layer.isValid():
            if prjSetting:
                settings.setValue('/Projections/defaultBehaviour', prjSetting)
            _loadedLayers[normalizeLayerSource(layer.source())] = layer
            return layer
        layer = QgsVectorLayer(uri, uri, 'postgres')
        if layer.isValid():
            if prjSetting:
                settings.setValue('/Projections/defaultBehaviour', prjSetting)
            _loadedLayers[normalizeLayerSource(layer.source())] = layer
            return layer
        layer = QgsRasterLayer(uri, uri)
        if layer.isValid():
            if prjSetting:
                settings.setValue('/Projections/defaultBehaviour', prjSetting)
            _loadedLayers[normalizeLayerSource(layer.source())] = layer
            return layer
        if prjSetting:
            settings.setValue('/Projections/defaultBehaviour', prjSetting)
    else:
        return None
Пример #5
0
 def test_readDBKeywords(self):
     """Can we read sqlite keywords with the generic readKeywords method
     """
     myLocalPath = os.path.join(os.path.dirname(__file__),
                                '..', 'jk.sqlite')
     myPath = os.path.join(TESTDATA, 'test_keywords.db')
     self.keywordIO.setKeywordDbPath(myPath)
     # We need to make a local copy of the dataset so
     # that we can use a local path that will hash properly on the
     # database to return us the correct / valid keywords record.
     shutil.copy2(os.path.join(TESTDATA, 'jk.sqlite'), myLocalPath)
     myUri = QgsDataSourceURI()
     # always use relative path!
     myUri.setDatabase('../jk.sqlite')
     myUri.setDataSource('', 'osm_buildings', 'Geometry')
     # create a local version that has the relative url
     mySqliteLayer = QgsVectorLayer(myUri.uri(), 'OSM Buildings',
                                    'spatialite')
     myExpectedSource = ('dbname=\'../jk.sqlite\' table="osm_buildings"'
          ' (Geometry) sql=')
     myMessage = 'Got source: %s\n\nExpected %s\n' % (
                 mySqliteLayer.source, myExpectedSource)
     assert mySqliteLayer.source() == myExpectedSource, myMessage
     myKeywords = self.keywordIO.readKeywords(mySqliteLayer)
     myExpectedKeywords = self.expectedSqliteKeywords
     assert myKeywords == myExpectedKeywords, myMessage
     mySource = self.sqliteLayer.source()
     os.remove(myLocalPath)
     myMessage = 'Got: %s\n\nExpected %s\n\nSource: %s' % (
                 myKeywords, myExpectedKeywords, mySource)
     assert myKeywords == myExpectedKeywords, myMessage
Пример #6
0
def load_wfs_layer(url, name):
    """Helper to load wfs layer and load it as QGIS layer.

    :param url: The complete URL to the WFS layer.
    :type url: str

    :param name: The layer name.
    :type name: str

    :returns: Layer instance.
    :rtype: QgsMapLayer
    """
    layer = QgsVectorLayer(url, name, 'WFS')
    # noinspection PyUnresolvedReferences
    message = 'Layer "%s" is not valid' % layer.source()
    # noinspection PyUnresolvedReferences
    if not layer.isValid():
        print message
    # noinspection PyUnresolvedReferences
    if not layer.isValid():
        raise Exception(message)
    return layer
Пример #7
0
class KeywordIOTest(unittest.TestCase):
    """Tests for reading and writing of raster and vector data
    """

    def setUp(self):
        self.keywordIO = KeywordIO()
        myUri = QgsDataSourceURI()
        myUri.setDatabase(os.path.join(TESTDATA, 'jk.sqlite'))
        myUri.setDataSource('', 'osm_buildings', 'Geometry')
        self.sqliteLayer = QgsVectorLayer(myUri.uri(), 'OSM Buildings',
                                       'spatialite')
        myHazardPath = os.path.join(HAZDATA, 'Shakemap_Padang_2009.asc')
        self.fileRasterLayer, myType = loadLayer(myHazardPath,
                                                 theDirectory=None)
        del myType
        self.fileVectorLayer, myType = loadLayer('Padang_WGS84.shp')
        del myType
        self.expectedSqliteKeywords = {'category': 'exposure',
                                       'datatype': 'OSM',
                                       'subcategory': 'building'}
        self.expectedVectorKeywords = {'category': 'exposure',
                                       'datatype': 'itb',
                                       'subcategory': 'structure'}
        self.expectedRasterKeywords = {'category': 'hazard',
                                       'source': 'USGS',
                                       'subcategory': 'earthquake',
                                       'unit': 'MMI',
                                       'title': ('An earthquake in Padang '
                                                 'like in 2009')}

    def tearDown(self):
        pass

    def test_getHashForDatasource(self):
        """Test we can reliably get a hash for a uri"""
        myHash = self.keywordIO.getHashForDatasource(PG_URI)
        myExpectedHash = '7cc153e1b119ca54a91ddb98a56ea95e'
        myMessage = "Got: %s\nExpected: %s" % (myHash, myExpectedHash)
        assert myHash == myExpectedHash, myMessage

    def test_writeReadKeywordFromUri(self):
        """Test we can set and get keywords for a non local datasource"""
        myHandle, myFilename = tempfile.mkstemp('.db', 'keywords_',
                                            temp_dir())

        # Ensure the file is deleted before we try to write to it
        # fixes windows specific issue where you get a message like this
        # ERROR 1: c:\temp\inasafe\clip_jpxjnt.shp is not a directory.
        # This is because mkstemp creates the file handle and leaves
        # the file open.
        os.close(myHandle)
        os.remove(myFilename)
        myExpectedKeywords = {'category': 'exposure',
                              'datatype': 'itb',
                              'subcategory': 'building'}
        # SQL insert test
        # On first write schema is empty and there is no matching hash
        self.keywordIO.setKeywordDbPath(myFilename)
        self.keywordIO.writeKeywordsForUri(PG_URI, myExpectedKeywords)
        # SQL Update test
        # On second write schema is populated and we update matching hash
        myExpectedKeywords = {'category': 'exposure',
                              'datatype': 'OSM',  # <--note the change here!
                              'subcategory': 'building'}
        self.keywordIO.writeKeywordsForUri(PG_URI, myExpectedKeywords)
        # Test getting all keywords
        myKeywords = self.keywordIO.readKeywordFromUri(PG_URI)
        myMessage = 'Got: %s\n\nExpected %s\n\nDB: %s' % (
                    myKeywords, myExpectedKeywords, myFilename)
        assert myKeywords == myExpectedKeywords, myMessage
        # Test getting just a single keyword
        myKeyword = self.keywordIO.readKeywordFromUri(PG_URI, 'datatype')
        myExpectedKeyword = 'OSM'
        myMessage = 'Got: %s\n\nExpected %s\n\nDB: %s' % (
                    myKeyword, myExpectedKeyword, myFilename)
        assert myKeyword == myExpectedKeyword, myMessage
        # Test deleting keywords actually does delete
        self.keywordIO.deleteKeywordsForUri(PG_URI)
        try:
            myKeyword = self.keywordIO.readKeywordFromUri(PG_URI, 'datatype')
            #if the above didnt cause an exception then bad
            myMessage = 'Expected a HashNotFoundException to be raised'
            assert myMessage
        except HashNotFoundException:
            #we expect this outcome so good!
            pass

    def test_areKeywordsFileBased(self):
        """Can we correctly determine if keywords should be written to file or
        to database?"""
        assert not self.keywordIO.areKeywordsFileBased(self.sqliteLayer)
        assert self.keywordIO.areKeywordsFileBased(self.fileRasterLayer)
        assert self.keywordIO.areKeywordsFileBased(self.fileVectorLayer)

    def test_readRasterFileKeywords(self):
        """Can we read raster file keywords using generic readKeywords method
        """
        myKeywords = self.keywordIO.readKeywords(self.fileRasterLayer)
        myExpectedKeywords = self.expectedRasterKeywords
        mySource = self.fileRasterLayer.source()
        myMessage = 'Got:\n%s\nExpected:\n%s\nSource:\n%s' % (
                    myKeywords, myExpectedKeywords, mySource)
        assert myKeywords == myExpectedKeywords, myMessage

    def test_readVectorFileKeywords(self):
        """Can we read vector file keywords with the generic readKeywords
        method """
        myKeywords = self.keywordIO.readKeywords(self.fileVectorLayer)
        myExpectedKeywords = self.expectedVectorKeywords
        mySource = self.fileVectorLayer.source()
        myMessage = 'Got: %s\n\nExpected %s\n\nSource: %s' % (
                    myKeywords, myExpectedKeywords, mySource)
        assert myKeywords == myExpectedKeywords, myMessage

    def test_appendKeywords(self):
        """Can we append file keywords with the generic readKeywords method."""
        myLayer, _ = makePadangLayerClone()
        myNewKeywords = {'category': 'exposure', 'test': 'TEST'}
        self.keywordIO.appendKeywords(myLayer, myNewKeywords)
        myKeywords = self.keywordIO.readKeywords(myLayer)

        for myKey, myValue in myNewKeywords.iteritems():
            myMessage = ('Layer keywords misses appended key: %s\n'
                        'Layer keywords:\n%s\n'
                        'Appended keywords:\n%s\n' %
                        (myKey,
                         myKeywords,
                         myNewKeywords))
            assert myKey in myKeywords, myMessage
            myMessage = ('Layer keywords misses appended value: %s\n'
                         'Layer keywords:\n%s\n'
                         'Appended keywords:\n%s\n' %
                         (myValue,
                          myKeywords,
                          myNewKeywords))
            assert myKeywords[myKey] == myValue, myMessage

    def test_readDBKeywords(self):
        """Can we read sqlite keywords with the generic readKeywords method
        """
        myLocalPath = os.path.join(os.path.dirname(__file__),
                                   '..', 'jk.sqlite')
        myPath = os.path.join(TESTDATA, 'test_keywords.db')
        self.keywordIO.setKeywordDbPath(myPath)
        # We need to make a local copy of the dataset so
        # that we can use a local path that will hash properly on the
        # database to return us the correct / valid keywords record.
        shutil.copy2(os.path.join(TESTDATA, 'jk.sqlite'), myLocalPath)
        myUri = QgsDataSourceURI()
        # always use relative path!
        myUri.setDatabase('../jk.sqlite')
        myUri.setDataSource('', 'osm_buildings', 'Geometry')
        # create a local version that has the relative url
        mySqliteLayer = QgsVectorLayer(myUri.uri(), 'OSM Buildings',
                                       'spatialite')
        myExpectedSource = ('dbname=\'../jk.sqlite\' table="osm_buildings"'
             ' (Geometry) sql=')
        myMessage = 'Got source: %s\n\nExpected %s\n' % (
                    mySqliteLayer.source, myExpectedSource)
        assert mySqliteLayer.source() == myExpectedSource, myMessage
        myKeywords = self.keywordIO.readKeywords(mySqliteLayer)
        myExpectedKeywords = self.expectedSqliteKeywords
        assert myKeywords == myExpectedKeywords, myMessage
        mySource = self.sqliteLayer.source()
        os.remove(myLocalPath)
        myMessage = 'Got: %s\n\nExpected %s\n\nSource: %s' % (
                    myKeywords, myExpectedKeywords, mySource)
        assert myKeywords == myExpectedKeywords, myMessage
Пример #8
0
class KeywordIOTest(unittest.TestCase):
    """Tests for reading and writing of raster and vector data
    """

    def setUp(self):
        self.keyword_io = KeywordIO()

        # SQLite Layer
        uri = QgsDataSourceURI()
        sqlite_building_path = test_data_path('exposure', 'exposure.sqlite')
        uri.setDatabase(sqlite_building_path)
        uri.setDataSource('', 'buildings_osm_4326', 'Geometry')
        self.sqlite_layer = QgsVectorLayer(
            uri.uri(), 'OSM Buildings', 'spatialite')
        self.expected_sqlite_keywords = {
            'category': 'exposure',
            'datatype': 'OSM',
            'subcategory': 'building'}

        # Raster Layer keywords
        hazard_path = test_data_path('hazard', 'tsunami_wgs84.tif')
        self.raster_layer, _ = load_layer(hazard_path)
        self.expected_raster_keywords = {
            'hazard_category': 'single_event',
            'title': 'Tsunami',
            'hazard': 'tsunami',
            'continuous_hazard_unit': 'metres',
            'layer_geometry': 'raster',
            'layer_purpose': 'hazard',
            'layer_mode': 'continuous',
            'keyword_version': inasafe_keyword_version
        }

        # Vector Layer keywords
        vector_path = test_data_path('exposure', 'buildings_osm_4326.shp')
        self.vector_layer, _ = load_layer(vector_path)
        self.expected_vector_keywords = {
            'keyword_version': inasafe_keyword_version,
            'structure_class_field': 'FLOODED',
            'title': 'buildings_osm_4326',
            'layer_geometry': 'polygon',
            'layer_purpose': 'exposure',
            'layer_mode': 'classified',
            'exposure': 'structure'
        }
        # Keyword less layer
        keywordless_path = test_data_path('other', 'keywordless_layer.shp')
        self.keywordless_layer, _ = load_layer(keywordless_path)

    def tearDown(self):
        pass

    def test_get_hash_for_datasource(self):
        """Test we can reliably get a hash for a uri"""
        hash_value = self.keyword_io.hash_for_datasource(PG_URI)
        expected_hash = '7cc153e1b119ca54a91ddb98a56ea95e'
        message = "Got: %s\nExpected: %s" % (hash_value, expected_hash)
        self.assertEqual(hash_value, expected_hash, message)

    def test_write_read_keyword_from_uri(self):
        """Test we can set and get keywords for a non local datasource"""
        handle, filename = tempfile.mkstemp(
            '.db', 'keywords_', temp_dir())

        # Ensure the file is deleted before we try to write to it
        # fixes windows specific issue where you get a message like this
        # ERROR 1: c:\temp\inasafe\clip_jpxjnt.shp is not a directory.
        # This is because mkstemp creates the file handle and leaves
        # the file open.

        os.close(handle)
        os.remove(filename)
        expected_keywords = {
            'category': 'exposure',
            'datatype': 'itb',
            'subcategory': 'building'}
        # SQL insert test
        # On first write schema is empty and there is no matching hash
        self.keyword_io.set_keyword_db_path(filename)
        self.keyword_io.write_keywords_for_uri(PG_URI, expected_keywords)
        # SQL Update test
        # On second write schema is populated and we update matching hash
        expected_keywords = {
            'category': 'exposure',
            'datatype': 'OSM',  # <--note the change here!
            'subcategory': 'building'}
        self.keyword_io.write_keywords_for_uri(PG_URI, expected_keywords)
        # Test getting all keywords
        keywords = self.keyword_io.read_keyword_from_uri(PG_URI)
        message = 'Got: %s\n\nExpected %s\n\nDB: %s' % (
            keywords, expected_keywords, filename)
        self.assertDictEqual(keywords, expected_keywords, message)
        # Test getting just a single keyword
        keyword = self.keyword_io.read_keyword_from_uri(PG_URI, 'datatype')
        expected_keyword = 'OSM'
        message = 'Got: %s\n\nExpected %s\n\nDB: %s' % (
            keyword, expected_keyword, filename)
        self.assertDictEqual(keywords, expected_keywords, message)
        # Test deleting keywords actually does delete
        self.keyword_io.delete_keywords_for_uri(PG_URI)
        try:
            _ = self.keyword_io.read_keyword_from_uri(PG_URI, 'datatype')
            # if the above didn't cause an exception then bad
            message = 'Expected a HashNotFoundError to be raised'
            assert message
        except HashNotFoundError:
            # we expect this outcome so good!
            pass

    def test_are_keywords_file_based(self):
        """Can we correctly determine if keywords should be written to file or
        to database?"""
        assert not self.keyword_io.are_keywords_file_based(self.sqlite_layer)
        assert self.keyword_io.are_keywords_file_based(self.raster_layer)
        assert self.keyword_io.are_keywords_file_based(self.vector_layer)

    def test_read_raster_file_keywords(self):
        """Can we read raster file keywords using generic readKeywords method
        """
        keywords = self.keyword_io.read_keywords(self.raster_layer)
        expected_keywords = self.expected_raster_keywords
        source = self.raster_layer.source()
        message = 'Got:\n%s\nExpected:\n%s\nSource:\n%s' % (
            keywords, expected_keywords, source)
        self.assertDictEqual(keywords, expected_keywords, message)

    def test_read_vector_file_keywords(self):
        """Test read vector file keywords with the generic readKeywords method.
         """
        keywords = self.keyword_io.read_keywords(self.vector_layer)
        expected_keywords = self.expected_vector_keywords
        source = self.vector_layer.source()
        message = 'Got: %s\n\nExpected %s\n\nSource: %s' % (
            keywords, expected_keywords, source)
        self.assertDictEqual(keywords, expected_keywords, message)

    def test_read_keywordless_layer(self):
        """Test read 'keyword' file from keywordless layer.
        """
        self.assertRaises(
            NoKeywordsFoundError,
            self.keyword_io.read_keywords,
            self.keywordless_layer,
            )

    def test_update_keywords(self):
        """Test append file keywords with update_keywords method."""
        layer = clone_raster_layer(
            name='tsunami_wgs84',
            extension='.tif',
            include_keywords=True,
            source_directory=test_data_path('hazard'))
        new_keywords = {'category': 'exposure', 'test': 'TEST'}
        self.keyword_io.update_keywords(layer, new_keywords)
        keywords = self.keyword_io.read_keywords(layer)
        expected_keywords = {
            'category': 'exposure',
            'hazard_category': 'single_event',
            'title': 'Tsunami',
            'hazard': 'tsunami',
            'continuous_hazard_unit': 'metres',
            'test': 'TEST',
            'layer_geometry': 'raster',
            'layer_purpose': 'hazard',
            'layer_mode': 'continuous',
            'keyword_version': inasafe_keyword_version
        }
        message = 'Got:\n%s\nExpected:\n%s' % (keywords, expected_keywords)
        self.assertDictEqual(keywords, expected_keywords, message)

    def test_read_db_keywords(self):
        """Can we read sqlite kw with the generic read_keywords method
        """
        db_path = test_data_path('other', 'test_keywords.db')
        self.read_db_keywords(db_path)

    def read_db_keywords(self, db_path):
        """Can we read sqlite keywords with the generic readKeywords method
        """
        self.keyword_io.set_keyword_db_path(db_path)

        # We need to use relative path so that the hash from URI will match
        local_path = os.path.join(
            os.path.dirname(__file__), 'exposure.sqlite')
        sqlite_building_path = test_data_path('exposure', 'exposure.sqlite')
        shutil.copy2(sqlite_building_path, local_path)
        uri = QgsDataSourceURI()
        uri.setDatabase('exposure.sqlite')
        uri.setDataSource('', 'buildings_osm_4326', 'Geometry')
        sqlite_layer = QgsVectorLayer(uri.uri(), 'OSM Buildings', 'spatialite')

        expected_source = (
            'dbname=\'exposure.sqlite\' table="buildings_osm_4326" ('
            'Geometry) sql=')
        message = 'Got source: %s\n\nExpected %s\n' % (
            sqlite_layer.source(), expected_source)
        self.assertEqual(sqlite_layer.source(), expected_source, message)

        keywords = self.keyword_io.read_keywords(sqlite_layer)
        expected_keywords = self.expected_sqlite_keywords
        message = 'Got: %s\n\nExpected %s\n\nSource: %s' % (
            keywords, expected_keywords, self.sqlite_layer.source())
        self.assertDictEqual(keywords, expected_keywords, message)

        # Delete SQL Layer so that we can delete the file
        del sqlite_layer
        os.remove(local_path)

    def test_copy_keywords(self):
        """Test we can copy the keywords."""
        out_path = unique_filename(
            prefix='test_copy_keywords', suffix='.keywords')
        self.keyword_io.copy_keywords(self.raster_layer, out_path)
        copied_keywords = read_file_keywords(out_path)
        expected_keywords = self.expected_raster_keywords
        message = 'Got:\n%s\nExpected:\n%s\nSource:\n%s' % (
            copied_keywords, expected_keywords, out_path)
        self.assertDictEqual(copied_keywords, expected_keywords, message)

    def test_definition(self):
        """Test we can get definitions for keywords.

        .. versionadded:: 3.2

        """
        keyword = 'hazards'
        definition = self.keyword_io.definition(keyword)
        self.assertTrue('description' in definition)

    def test_to_message(self):
        """Test we can convert keywords to a message object.

        .. versionadded:: 3.2

        """
        keywords = self.keyword_io.read_keywords(self.vector_layer)
        message = self.keyword_io.to_message(keywords).to_text()
        self.assertIn('*Exposure*, structure------', message)

    def test_dict_to_row(self):
        """Test the dict to row helper works.

        .. versionadded:: 3.2
        """
        keyword_value = (
            "{'high': ['Kawasan Rawan Bencana III'], "
            "'medium': ['Kawasan Rawan Bencana II'], "
            "'low': ['Kawasan Rawan Bencana I']}")
        table = self.keyword_io._dict_to_row(keyword_value)
        self.assertIn(
            u'\n---\n*high*, Kawasan Rawan Bencana III------',
            table.to_text())
        # should also work passing a dict
        keyword_value = {
            'high': ['Kawasan Rawan Bencana III'],
            'medium': ['Kawasan Rawan Bencana II'],
            'low': ['Kawasan Rawan Bencana I']}
        table = self.keyword_io._dict_to_row(keyword_value)
        self.assertIn(
            u'\n---\n*high*, Kawasan Rawan Bencana III------',
            table.to_text())
Пример #9
0
class KeywordIOTest(unittest.TestCase):
    """Tests for reading and writing of raster and vector data
    """

    def setUp(self):
        self.keyword_io = KeywordIO()
        uri = QgsDataSourceURI()
        uri.setDatabase(os.path.join(TESTDATA, 'jk.sqlite'))
        uri.setDataSource('', 'osm_buildings', 'Geometry')
        self.sqlite_layer = QgsVectorLayer(
            uri.uri(), 'OSM Buildings', 'spatialite')
        hazard_path = os.path.join(HAZDATA, 'Shakemap_Padang_2009.asc')
        self.raster_layer, layer_type = load_layer(
            hazard_path, directory=None)
        del layer_type
        self.vector_layer, layer_type = load_layer('Padang_WGS84.shp')
        del layer_type
        self.expected_sqlite_keywords = {
            'category': 'exposure',
            'datatype': 'OSM',
            'subcategory': 'building'}
        self.expected_vector_keywords = {
            'category': 'exposure',
            'datatype': 'itb',
            'subcategory': 'structure',
            'title': 'Padang WGS84'}
        self.expected_raster_keywords = {
            'category': 'hazard',
            'source': 'USGS',
            'subcategory': 'earthquake',
            'unit': 'MMI',
            'title': ('An earthquake in Padang '
            'like in 2009')}

    def tearDown(self):
        pass

    def test_get_hash_for_datasource(self):
        """Test we can reliably get a hash for a uri"""
        hash_value = self.keyword_io.hash_for_datasource(PG_URI)
        expected_hash = '7cc153e1b119ca54a91ddb98a56ea95e'
        message = "Got: %s\nExpected: %s" % (hash_value, expected_hash)
        assert hash_value == expected_hash, message

    def test_write_read_keyword_from_uri(self):
        """Test we can set and get keywords for a non local datasource"""
        handle, filename = tempfile.mkstemp(
            '.db', 'keywords_', temp_dir())

        # Ensure the file is deleted before we try to write to it
        # fixes windows specific issue where you get a message like this
        # ERROR 1: c:\temp\inasafe\clip_jpxjnt.shp is not a directory.
        # This is because mkstemp creates the file handle and leaves
        # the file open.
        os.close(handle)
        os.remove(filename)
        expected_keywords = {
            'category': 'exposure',
            'datatype': 'itb',
            'subcategory': 'building'}
        # SQL insert test
        # On first write schema is empty and there is no matching hash
        self.keyword_io.set_keyword_db_path(filename)
        self.keyword_io.write_keywords_for_uri(PG_URI, expected_keywords)
        # SQL Update test
        # On second write schema is populated and we update matching hash
        expected_keywords = {
            'category': 'exposure',
            'datatype': 'OSM',  # <--note the change here!
            'subcategory': 'building'}
        self.keyword_io.write_keywords_for_uri(PG_URI, expected_keywords)
        # Test getting all keywords
        keywords = self.keyword_io.read_keyword_from_uri(PG_URI)
        message = 'Got: %s\n\nExpected %s\n\nDB: %s' % (
            keywords, expected_keywords, filename)
        assert keywords == expected_keywords, message
        # Test getting just a single keyword
        keyword = self.keyword_io.read_keyword_from_uri(PG_URI, 'datatype')
        expected_keyword = 'OSM'
        message = 'Got: %s\n\nExpected %s\n\nDB: %s' % (
            keyword, expected_keyword, filename)
        assert keyword == expected_keyword, message
        # Test deleting keywords actually does delete
        self.keyword_io.delete_keywords_for_uri(PG_URI)
        try:
            _ = self.keyword_io.read_keyword_from_uri(PG_URI, 'datatype')
            #if the above didnt cause an exception then bad
            message = 'Expected a HashNotFoundError to be raised'
            assert message
        except HashNotFoundError:
            #we expect this outcome so good!
            pass

    def test_are_keywords_file_based(self):
        """Can we correctly determine if keywords should be written to file or
        to database?"""
        assert not self.keyword_io.are_keywords_file_based(self.sqlite_layer)
        assert self.keyword_io.are_keywords_file_based(self.raster_layer)
        assert self.keyword_io.are_keywords_file_based(self.vector_layer)

    def test_read_raster_file_keywords(self):
        """Can we read raster file keywords using generic readKeywords method
        """
        keywords = self.keyword_io.read_keywords(self.raster_layer)
        expected_keywords = self.expected_raster_keywords
        source = self.raster_layer.source()
        message = 'Got:\n%s\nExpected:\n%s\nSource:\n%s' % (
            keywords, expected_keywords, source)
        self.assertEquals(keywords, expected_keywords, message)

    def test_read_vector_file_keywords(self):
        """Test read vector file keywords with the generic readKeywords method.
         """
        keywords = self.keyword_io.read_keywords(self.vector_layer)
        expected_keywords = self.expected_vector_keywords
        source = self.vector_layer.source()
        message = 'Got: %s\n\nExpected %s\n\nSource: %s' % (
            keywords, expected_keywords, source)
        assert keywords == expected_keywords, message

    def test_append_keywords(self):
        """Can we append file keywords with the generic readKeywords method."""
        layer, _ = clone_padang_layer()
        new_keywords = {'category': 'exposure', 'test': 'TEST'}
        self.keyword_io.update_keywords(layer, new_keywords)
        keywords = self.keyword_io.read_keywords(layer)

        for key, value in new_keywords.iteritems():
            message = (
                'Layer keywords misses appended key: %s\n'
                'Layer keywords:\n%s\n'
                'Appended keywords:\n%s\n' %
                (key,
                keywords,
                new_keywords))
            assert key in keywords, message
            message = (
                'Layer keywords misses appended value: %s\n'
                'Layer keywords:\n%s\n'
                'Appended keywords:\n%s\n' %
                (value,
                keywords,
                new_keywords))
            assert keywords[key] == value, message

    def test_read_db_keywords(self):
        """Can we read sqlite keywords with the generic readKeywords method
        """
        # noinspection PyUnresolvedReferences
        local_path = os.path.join(
            os.path.dirname(__file__), '../../..///', 'jk.sqlite')
        path = os.path.join(TESTDATA, 'test_keywords.db')
        self.keyword_io.set_keyword_db_path(path)
        # We need to make a local copy of the dataset so
        # that we can use a local path that will hash properly on the
        # database to return us the correct / valid keywords record.
        shutil.copy2(os.path.join(TESTDATA, 'jk.sqlite'), local_path)
        uri = QgsDataSourceURI()
        # always use relative path!
        uri.setDatabase('../jk.sqlite')
        uri.setDataSource('', 'osm_buildings', 'Geometry')
        # create a local version that has the relative url
        sqlite_layer = QgsVectorLayer(uri.uri(), 'OSM Buildings', 'spatialite')
        expected_source = (
            'dbname=\'../jk.sqlite\' table="osm_buildings" (Geometry) sql=')
        message = 'Got source: %s\n\nExpected %s\n' % (
            sqlite_layer.source, expected_source)
        assert sqlite_layer.source() == expected_source, message
        keywords = self.keyword_io.read_keywords(sqlite_layer)
        expected_keywords = self.expected_sqlite_keywords
        assert keywords == expected_keywords, message
        source = self.sqlite_layer.source()
        # delete sqlite_layer so that we can delete the file
        del sqlite_layer
        os.remove(local_path)
        message = 'Got: %s\n\nExpected %s\n\nSource: %s' % (
            keywords, expected_keywords, source)
        assert keywords == expected_keywords, message

    def test_copy_keywords(self):
        """Test we can copy the keywords."""
        out_path = unique_filename(
            prefix='test_copy_keywords', suffix='.keywords')
        self.keyword_io.copy_keywords(self.raster_layer, out_path)
        copied_keywords = read_file_keywords(out_path)
        expected_keywords = self.expected_raster_keywords
        message = 'Got:\n%s\nExpected:\n%s\nSource:\n%s' % (
            copied_keywords, expected_keywords, out_path)
        self.assertEquals(copied_keywords, expected_keywords, message)
Пример #10
0
class KeywordIOTest(unittest.TestCase):
    """Tests for reading and writing of raster and vector data
    """

    def setUp(self):
        self.keyword_io = KeywordIO()
        uri = QgsDataSourceURI()
        uri.setDatabase(os.path.join(TESTDATA, 'jk.sqlite'))
        uri.setDataSource('', 'osm_buildings', 'Geometry')
        self.sqlite_layer = QgsVectorLayer(
            uri.uri(), 'OSM Buildings', 'spatialite')
        hazard_path = os.path.join(HAZDATA, 'Shakemap_Padang_2009.asc')
        self.raster_layer, layer_type = load_layer(
            hazard_path, directory=None)
        del layer_type
        self.vector_layer, layer_type = load_layer('Padang_WGS84.shp')
        del layer_type
        self.expected_sqlite_keywords = {
            'category': 'exposure',
            'datatype': 'OSM',
            'subcategory': 'building'}
        self.expected_vector_keywords = {
            'category': 'exposure',
            'datatype': 'itb',
            'subcategory': 'structure',
            'title': 'Padang WGS84'}
        self.expected_raster_keywords = {
            'category': 'hazard',
            'source': 'USGS',
            'subcategory': 'earthquake',
            'unit': 'MMI',
            'title': ('An earthquake in Padang '
                      'like in 2009')}

    def tearDown(self):
        pass

    def test_get_hash_for_datasource(self):
        """Test we can reliably get a hash for a uri"""
        hash_value = self.keyword_io.hash_for_datasource(PG_URI)
        expected_hash = '7cc153e1b119ca54a91ddb98a56ea95e'
        message = "Got: %s\nExpected: %s" % (hash_value, expected_hash)
        assert hash_value == expected_hash, message

    def test_write_read_keyword_from_uri(self):
        """Test we can set and get keywords for a non local datasource"""
        handle, filename = tempfile.mkstemp(
            '.db', 'keywords_', temp_dir())

        # Ensure the file is deleted before we try to write to it
        # fixes windows specific issue where you get a message like this
        # ERROR 1: c:\temp\inasafe\clip_jpxjnt.shp is not a directory.
        # This is because mkstemp creates the file handle and leaves
        # the file open.

        os.close(handle)
        os.remove(filename)
        expected_keywords = {
            'category': 'exposure',
            'datatype': 'itb',
            'subcategory': 'building'}
        # SQL insert test
        # On first write schema is empty and there is no matching hash
        self.keyword_io.set_keyword_db_path(filename)
        self.keyword_io.write_keywords_for_uri(PG_URI, expected_keywords)
        # SQL Update test
        # On second write schema is populated and we update matching hash
        expected_keywords = {
            'category': 'exposure',
            'datatype': 'OSM',  # <--note the change here!
            'subcategory': 'building'}
        self.keyword_io.write_keywords_for_uri(PG_URI, expected_keywords)
        # Test getting all keywords
        keywords = self.keyword_io.read_keyword_from_uri(PG_URI)
        message = 'Got: %s\n\nExpected %s\n\nDB: %s' % (
            keywords, expected_keywords, filename)
        assert keywords == expected_keywords, message
        # Test getting just a single keyword
        keyword = self.keyword_io.read_keyword_from_uri(PG_URI, 'datatype')
        expected_keyword = 'OSM'
        message = 'Got: %s\n\nExpected %s\n\nDB: %s' % (
            keyword, expected_keyword, filename)
        assert keyword == expected_keyword, message
        # Test deleting keywords actually does delete
        self.keyword_io.delete_keywords_for_uri(PG_URI)
        try:
            _ = self.keyword_io.read_keyword_from_uri(PG_URI, 'datatype')
            # if the above didnt cause an exception then bad
            message = 'Expected a HashNotFoundError to be raised'
            assert message
        except HashNotFoundError:
            # we expect this outcome so good!
            pass

    def test_are_keywords_file_based(self):
        """Can we correctly determine if keywords should be written to file or
        to database?"""
        assert not self.keyword_io.are_keywords_file_based(self.sqlite_layer)
        assert self.keyword_io.are_keywords_file_based(self.raster_layer)
        assert self.keyword_io.are_keywords_file_based(self.vector_layer)

    def test_read_raster_file_keywords(self):
        """Can we read raster file keywords using generic readKeywords method
        """
        keywords = self.keyword_io.read_keywords(self.raster_layer)
        expected_keywords = self.expected_raster_keywords
        source = self.raster_layer.source()
        message = 'Got:\n%s\nExpected:\n%s\nSource:\n%s' % (
            keywords, expected_keywords, source)
        self.assertEquals(keywords, expected_keywords, message)

    def test_read_vector_file_keywords(self):
        """Test read vector file keywords with the generic readKeywords method.
         """
        keywords = self.keyword_io.read_keywords(self.vector_layer)
        expected_keywords = self.expected_vector_keywords
        source = self.vector_layer.source()
        message = 'Got: %s\n\nExpected %s\n\nSource: %s' % (
            keywords, expected_keywords, source)
        assert keywords == expected_keywords, message

    def test_append_keywords(self):
        """Can we append file keywords with the generic readKeywords method."""
        layer, _ = clone_padang_layer()
        new_keywords = {'category': 'exposure', 'test': 'TEST'}
        self.keyword_io.update_keywords(layer, new_keywords)
        keywords = self.keyword_io.read_keywords(layer)

        for key, value in new_keywords.iteritems():
            message = (
                'Layer keywords misses appended key: %s\n'
                'Layer keywords:\n%s\n'
                'Appended keywords:\n%s\n' %
                (key,
                 keywords,
                 new_keywords))
            assert key in keywords, message
            message = (
                'Layer keywords misses appended value: %s\n'
                'Layer keywords:\n%s\n'
                'Appended keywords:\n%s\n' %
                (value,
                 keywords,
                 new_keywords))
            assert keywords[key] == value, message

    def test_read_db_keywords(self):
        """Can we read sqlite kw with the generic readKeywords method
        """
        db_path = os.path.join(TESTDATA, 'test_keywords.db')
        self.read_db_keywords(db_path)

    def test_read_legacy_db_keywords(self):
        """Can we read legacy sqlite kw with the generic readKeywords method
        """
        db_path = os.path.join(TESTDATA, 'test_keywords_legacy.db')
        self.read_db_keywords(db_path)

    def read_db_keywords(self, db_path):
        """Can we read sqlite keywords with the generic readKeywords method
        """
        # noinspection PyUnresolvedReferences
        local_path = os.path.join(
            os.path.dirname(__file__), '../../..///', 'jk.sqlite')
        self.keyword_io.set_keyword_db_path(db_path)
        # We need to make a local copy of the dataset so
        # that we can use a local path that will hash properly on the
        # database to return us the correct / valid keywords record.
        shutil.copy2(os.path.join(TESTDATA, 'jk.sqlite'), local_path)
        uri = QgsDataSourceURI()
        # always use relative path!
        uri.setDatabase('../jk.sqlite')
        uri.setDataSource('', 'osm_buildings', 'Geometry')
        # create a local version that has the relative url
        sqlite_layer = QgsVectorLayer(uri.uri(), 'OSM Buildings', 'spatialite')
        expected_source = (
            'dbname=\'../jk.sqlite\' table="osm_buildings" (Geometry) sql=')
        message = 'Got source: %s\n\nExpected %s\n' % (
            sqlite_layer.source(), expected_source)
        assert sqlite_layer.source() == expected_source, message
        keywords = self.keyword_io.read_keywords(sqlite_layer)
        expected_keywords = self.expected_sqlite_keywords
        message = 'Got: %s\n\nExpected %s\n\nSource: %s' % (
            keywords, expected_keywords, self.sqlite_layer.source())
        assert keywords == expected_keywords, message
        source = self.sqlite_layer.source()
        # delete sqlite_layer so that we can delete the file
        del sqlite_layer
        os.remove(local_path)
        message = 'Got: %s\n\nExpected %s\n\nSource: %s' % (
            keywords, expected_keywords, source)
        assert keywords == expected_keywords, message

    def test_copy_keywords(self):
        """Test we can copy the keywords."""
        out_path = unique_filename(
            prefix='test_copy_keywords', suffix='.keywords')
        self.keyword_io.copy_keywords(self.raster_layer, out_path)
        copied_keywords = read_file_keywords(out_path)
        expected_keywords = self.expected_raster_keywords
        message = 'Got:\n%s\nExpected:\n%s\nSource:\n%s' % (
            copied_keywords, expected_keywords, out_path)
        self.assertEquals(copied_keywords, expected_keywords, message)
Пример #11
0
class KeywordIOTest(unittest.TestCase):
    """Tests for reading and writing of raster and vector data
    """

    def setUp(self):
        self.keywordIO = KeywordIO()
        myUri = QgsDataSourceURI()
        myUri.setDatabase(os.path.join(TESTDATA, 'jk.sqlite'))
        myUri.setDataSource('', 'osm_buildings', 'Geometry')
        self.sqliteLayer = QgsVectorLayer(myUri.uri(), 'OSM Buildings',
                                          'spatialite')
        myHazardPath = os.path.join(HAZDATA, 'Shakemap_Padang_2009.asc')
        self.fileRasterLayer, myType = load_layer(
            myHazardPath, directory=None)
        del myType
        self.fileVectorLayer, myType = load_layer('Padang_WGS84.shp')
        del myType
        self.expectedSqliteKeywords = {
            'category': 'exposure',
            'datatype': 'OSM',
            'subcategory': 'building'}
        self.expectedVectorKeywords = {
            'category': 'exposure',
            'datatype': 'itb',
            'subcategory': 'structure',
            'title': 'Padang WGS84'}
        self.expectedRasterKeywords = {
            'category': 'hazard',
            'source': 'USGS',
            'subcategory': 'earthquake',
            'unit': 'MMI',
            'title': ('An earthquake in Padang '
            'like in 2009')}

    def tearDown(self):
        pass

    def test_getHashForDatasource(self):
        """Test we can reliably get a hash for a uri"""
        myHash = self.keywordIO.hash_for_datasource(PG_URI)
        myExpectedHash = '7cc153e1b119ca54a91ddb98a56ea95e'
        myMessage = "Got: %s\nExpected: %s" % (myHash, myExpectedHash)
        assert myHash == myExpectedHash, myMessage

    def test_writeReadKeywordFromUri(self):
        """Test we can set and get keywords for a non local datasource"""
        myHandle, myFilename = tempfile.mkstemp('.db', 'keywords_',
                                                temp_dir())

        # Ensure the file is deleted before we try to write to it
        # fixes windows specific issue where you get a message like this
        # ERROR 1: c:\temp\inasafe\clip_jpxjnt.shp is not a directory.
        # This is because mkstemp creates the file handle and leaves
        # the file open.
        os.close(myHandle)
        os.remove(myFilename)
        myExpectedKeywords = {'category': 'exposure',
                              'datatype': 'itb',
                              'subcategory': 'building'}
        # SQL insert test
        # On first write schema is empty and there is no matching hash
        self.keywordIO.set_keyword_db_path(myFilename)
        self.keywordIO.write_keywords_for_uri(PG_URI, myExpectedKeywords)
        # SQL Update test
        # On second write schema is populated and we update matching hash
        myExpectedKeywords = {'category': 'exposure',
                              'datatype': 'OSM',  # <--note the change here!
                              'subcategory': 'building'}
        self.keywordIO.write_keywords_for_uri(PG_URI, myExpectedKeywords)
        # Test getting all keywords
        myKeywords = self.keywordIO.read_keyword_from_uri(PG_URI)
        myMessage = 'Got: %s\n\nExpected %s\n\nDB: %s' % (
                    myKeywords, myExpectedKeywords, myFilename)
        assert myKeywords == myExpectedKeywords, myMessage
        # Test getting just a single keyword
        myKeyword = self.keywordIO.read_keyword_from_uri(PG_URI, 'datatype')
        myExpectedKeyword = 'OSM'
        myMessage = 'Got: %s\n\nExpected %s\n\nDB: %s' % (
                    myKeyword, myExpectedKeyword, myFilename)
        assert myKeyword == myExpectedKeyword, myMessage
        # Test deleting keywords actually does delete
        self.keywordIO.delete_keywords_for_uri(PG_URI)
        try:
            myKeyword = self.keywordIO.read_keyword_from_uri(PG_URI, 'datatype')
            #if the above didnt cause an exception then bad
            myMessage = 'Expected a HashNotFoundError to be raised'
            assert myMessage
        except HashNotFoundError:
            #we expect this outcome so good!
            pass

    def test_areKeywordsFileBased(self):
        """Can we correctly determine if keywords should be written to file or
        to database?"""
        assert not self.keywordIO.are_keywords_file_based(self.sqliteLayer)
        assert self.keywordIO.are_keywords_file_based(self.fileRasterLayer)
        assert self.keywordIO.are_keywords_file_based(self.fileVectorLayer)

    def test_readRasterFileKeywords(self):
        """Can we read raster file keywords using generic readKeywords method
        """
        myKeywords = self.keywordIO.read_keywords(self.fileRasterLayer)
        myExpectedKeywords = self.expectedRasterKeywords
        mySource = self.fileRasterLayer.source()
        myMessage = 'Got:\n%s\nExpected:\n%s\nSource:\n%s' % (
                    myKeywords, myExpectedKeywords, mySource)
        assert myKeywords == myExpectedKeywords, myMessage

    def test_readVectorFileKeywords(self):
        """Test read vector file keywords with the generic readKeywords method.
         """
        myKeywords = self.keywordIO.read_keywords(self.fileVectorLayer)
        myExpectedKeywords = self.expectedVectorKeywords
        mySource = self.fileVectorLayer.source()
        myMessage = 'Got: %s\n\nExpected %s\n\nSource: %s' % (
                    myKeywords, myExpectedKeywords, mySource)
        assert myKeywords == myExpectedKeywords, myMessage

    def test_appendKeywords(self):
        """Can we append file keywords with the generic readKeywords method."""
        myLayer, _ = makePadangLayerClone()
        myNewKeywords = {'category': 'exposure', 'test': 'TEST'}
        self.keywordIO.update_keywords(myLayer, myNewKeywords)
        myKeywords = self.keywordIO.read_keywords(myLayer)

        for myKey, myValue in myNewKeywords.iteritems():
            myMessage = (
                'Layer keywords misses appended key: %s\n'
                'Layer keywords:\n%s\n'
                'Appended keywords:\n%s\n' %
                (myKey,
                myKeywords,
                myNewKeywords))
            assert myKey in myKeywords, myMessage
            myMessage = (
                'Layer keywords misses appended value: %s\n'
                'Layer keywords:\n%s\n'
                'Appended keywords:\n%s\n' %
                (myValue,
                myKeywords,
                myNewKeywords))
            assert myKeywords[myKey] == myValue, myMessage

    def test_readDBKeywords(self):
        """Can we read sqlite keywords with the generic readKeywords method
        """
        myLocalPath = os.path.join(os.path.dirname(__file__),
                                   '../../..///', 'jk.sqlite')
        myPath = os.path.join(TESTDATA, 'test_keywords.db')
        self.keywordIO.set_keyword_db_path(myPath)
        # We need to make a local copy of the dataset so
        # that we can use a local path that will hash properly on the
        # database to return us the correct / valid keywords record.
        shutil.copy2(os.path.join(TESTDATA, 'jk.sqlite'), myLocalPath)
        myUri = QgsDataSourceURI()
        # always use relative path!
        myUri.setDatabase('../jk.sqlite')
        myUri.setDataSource('', 'osm_buildings', 'Geometry')
        # create a local version that has the relative url
        mySqliteLayer = QgsVectorLayer(myUri.uri(), 'OSM Buildings',
                                       'spatialite')
        myExpectedSource = ('dbname=\'../jk.sqlite\' table="osm_buildings"'
                            ' (Geometry) sql=')
        myMessage = 'Got source: %s\n\nExpected %s\n' % (
                    mySqliteLayer.source, myExpectedSource)
        assert mySqliteLayer.source() == myExpectedSource, myMessage
        myKeywords = self.keywordIO.read_keywords(mySqliteLayer)
        myExpectedKeywords = self.expectedSqliteKeywords
        assert myKeywords == myExpectedKeywords, myMessage
        mySource = self.sqliteLayer.source()
        # delete mySqliteLayer so that we can delete the file
        del mySqliteLayer
        os.remove(myLocalPath)
        myMessage = 'Got: %s\n\nExpected %s\n\nSource: %s' % (
                    myKeywords, myExpectedKeywords, mySource)
        assert myKeywords == myExpectedKeywords, myMessage
Пример #12
0
class KeywordIOTest(unittest.TestCase):
    """Tests for reading and writing of raster and vector data
    """

    def setUp(self):
        self.keyword_io = KeywordIO()

        # SQLite Layer
        uri = QgsDataSourceURI()
        sqlite_building_path = test_data_path('exposure', 'exposure.sqlite')
        uri.setDatabase(sqlite_building_path)
        uri.setDataSource('', 'buildings_osm_4326', 'Geometry')
        self.sqlite_layer = QgsVectorLayer(
            uri.uri(), 'OSM Buildings', 'spatialite')
        self.expected_sqlite_keywords = {
            'category': 'exposure',
            'datatype': 'OSM',
            'subcategory': 'building'}

        # Raster Layer keywords
        hazard_path = test_data_path('hazard', 'tsunami_wgs84.tif')
        self.raster_layer, _ = load_layer(hazard_path)
        self.expected_raster_keywords = {
            'category': 'hazard',
            'subcategory': 'tsunami',
            'data_type': 'continuous',
            'unit': 'metres_depth',
            'title': 'Tsunami'}

        # Vector Layer keywords
        vector_path = test_data_path('exposure', 'buildings_osm_4326.shp')
        self.vector_layer, _ = load_layer(vector_path)
        self.expected_vector_keywords = {
            'category': 'exposure',
            'datatype': 'osm',
            'subcategory': 'structure',
            'title': 'buildings_osm_4326',
            'purpose': 'dki'}

        # Keyword less layer
        keywordless_path = test_data_path('other', 'keywordless_layer.shp')
        self.keywordless_layer, _ = load_layer(keywordless_path)

    def tearDown(self):
        pass

    def test_get_hash_for_datasource(self):
        """Test we can reliably get a hash for a uri"""
        hash_value = self.keyword_io.hash_for_datasource(PG_URI)
        expected_hash = '7cc153e1b119ca54a91ddb98a56ea95e'
        message = "Got: %s\nExpected: %s" % (hash_value, expected_hash)
        assert hash_value == expected_hash, message

    def test_write_read_keyword_from_uri(self):
        """Test we can set and get keywords for a non local datasource"""
        handle, filename = tempfile.mkstemp(
            '.db', 'keywords_', temp_dir())

        # Ensure the file is deleted before we try to write to it
        # fixes windows specific issue where you get a message like this
        # ERROR 1: c:\temp\inasafe\clip_jpxjnt.shp is not a directory.
        # This is because mkstemp creates the file handle and leaves
        # the file open.

        os.close(handle)
        os.remove(filename)
        expected_keywords = {
            'category': 'exposure',
            'datatype': 'itb',
            'subcategory': 'building'}
        # SQL insert test
        # On first write schema is empty and there is no matching hash
        self.keyword_io.set_keyword_db_path(filename)
        self.keyword_io.write_keywords_for_uri(PG_URI, expected_keywords)
        # SQL Update test
        # On second write schema is populated and we update matching hash
        expected_keywords = {
            'category': 'exposure',
            'datatype': 'OSM',  # <--note the change here!
            'subcategory': 'building'}
        self.keyword_io.write_keywords_for_uri(PG_URI, expected_keywords)
        # Test getting all keywords
        keywords = self.keyword_io.read_keyword_from_uri(PG_URI)
        message = 'Got: %s\n\nExpected %s\n\nDB: %s' % (
            keywords, expected_keywords, filename)
        assert keywords == expected_keywords, message
        # Test getting just a single keyword
        keyword = self.keyword_io.read_keyword_from_uri(PG_URI, 'datatype')
        expected_keyword = 'OSM'
        message = 'Got: %s\n\nExpected %s\n\nDB: %s' % (
            keyword, expected_keyword, filename)
        assert keyword == expected_keyword, message
        # Test deleting keywords actually does delete
        self.keyword_io.delete_keywords_for_uri(PG_URI)
        try:
            _ = self.keyword_io.read_keyword_from_uri(PG_URI, 'datatype')
            # if the above didnt cause an exception then bad
            message = 'Expected a HashNotFoundError to be raised'
            assert message
        except HashNotFoundError:
            # we expect this outcome so good!
            pass

    def test_are_keywords_file_based(self):
        """Can we correctly determine if keywords should be written to file or
        to database?"""
        assert not self.keyword_io.are_keywords_file_based(self.sqlite_layer)
        assert self.keyword_io.are_keywords_file_based(self.raster_layer)
        assert self.keyword_io.are_keywords_file_based(self.vector_layer)

    def test_read_raster_file_keywords(self):
        """Can we read raster file keywords using generic readKeywords method
        """
        keywords = self.keyword_io.read_keywords(self.raster_layer)
        expected_keywords = self.expected_raster_keywords
        source = self.raster_layer.source()
        message = 'Got:\n%s\nExpected:\n%s\nSource:\n%s' % (
            keywords, expected_keywords, source)
        self.assertEquals(keywords, expected_keywords, message)

    def test_read_vector_file_keywords(self):
        """Test read vector file keywords with the generic readKeywords method.
         """
        keywords = self.keyword_io.read_keywords(self.vector_layer)
        expected_keywords = self.expected_vector_keywords
        source = self.vector_layer.source()
        message = 'Got: %s\n\nExpected %s\n\nSource: %s' % (
            keywords, expected_keywords, source)
        assert keywords == expected_keywords, message

    def test_read_keywordless_layer(self):
        """Test read 'keyword' file from keywordless layer.
        """
        self.assertRaises(
            NoKeywordsFoundError,
            self.keyword_io.read_keywords,
            self.keywordless_layer,
            )

    def test_update_keywords(self):
        """Test append file keywords with update_keywords method."""
        layer = clone_raster_layer(
            name='tsunami_wgs84',
            extension='.tif',
            include_keywords=True,
            source_directory=test_data_path('hazard'))
        new_keywords = {'category': 'exposure', 'test': 'TEST'}
        self.keyword_io.update_keywords(layer, new_keywords)
        keywords = self.keyword_io.read_keywords(layer)
        expected_keywords = {
            'category': 'exposure',
            'subcategory': 'tsunami',
            'data_type': 'continuous',
            'title': 'Tsunami',
            'test': 'TEST',
            'unit': 'metres_depth'
        }
        message = 'Keywords: %s. Expected: %s' % (keywords, expected_keywords)
        self.assertEqual(keywords, expected_keywords, message)

    def test_read_db_keywords(self):
        """Can we read sqlite kw with the generic read_keywords method
        """
        db_path = test_data_path('other', 'test_keywords.db')
        self.read_db_keywords(db_path)

    def read_db_keywords(self, db_path):
        """Can we read sqlite keywords with the generic readKeywords method
        """
        self.keyword_io.set_keyword_db_path(db_path)

        # We need to use relative path so that the hash from URI will match
        local_path = os.path.join(
            os.path.dirname(__file__), 'exposure.sqlite')
        sqlite_building_path = test_data_path('exposure', 'exposure.sqlite')
        shutil.copy2(sqlite_building_path, local_path)
        uri = QgsDataSourceURI()
        uri.setDatabase('exposure.sqlite')
        uri.setDataSource('', 'buildings_osm_4326', 'Geometry')
        sqlite_layer = QgsVectorLayer(uri.uri(), 'OSM Buildings', 'spatialite')

        expected_source = (
            'dbname=\'exposure.sqlite\' table="buildings_osm_4326" ('
            'Geometry) sql=')
        message = 'Got source: %s\n\nExpected %s\n' % (
            sqlite_layer.source(), expected_source)
        self.assertEqual(sqlite_layer.source(), expected_source, message)

        keywords = self.keyword_io.read_keywords(sqlite_layer)
        expected_keywords = self.expected_sqlite_keywords
        message = 'Got: %s\n\nExpected %s\n\nSource: %s' % (
            keywords, expected_keywords, self.sqlite_layer.source())
        self.assertEqual(keywords, expected_keywords, message)

        # Delete SQL Layer so that we can delete the file
        del sqlite_layer
        os.remove(local_path)

    def test_copy_keywords(self):
        """Test we can copy the keywords."""
        out_path = unique_filename(
            prefix='test_copy_keywords', suffix='.keywords')
        self.keyword_io.copy_keywords(self.raster_layer, out_path)
        copied_keywords = read_file_keywords(out_path)
        expected_keywords = self.expected_raster_keywords
        message = 'Got:\n%s\nExpected:\n%s\nSource:\n%s' % (
            copied_keywords, expected_keywords, out_path)
        self.assertEquals(copied_keywords, expected_keywords, message)
class Dialog(QDialog, Ui_Dialog):

 sampItems = {}  # {name1 : [layer1, [field_src,field_dsn,Active?], [field_src,field_dsn,Active?], ...] , name2 : [layer2, ...] }
 polyItems = {}  # {name1 : [layer1, [field_src,field_dsn,Active?], [field_src,field_dsn,Active?], ...] , name2 : [layer2, ...] }
 rastItems = {}  # {name1 : [layer1, [band_name,field_dsn,Active?], [band_name,field_dsn,Active?], ...] , name2 : [layer2, ...] }
 fields = []     # [[type,layer,field],[type,layer,field],[type,layer,field]...] list of adresses of output fields

 def __init__(self, iface):
  QDialog.__init__(self)
  self.iface = iface
  self.setupUi(self)
  self.outButton.clicked.connect(self.outFile)
  self.inSample.currentIndexChanged.connect(self.updateFieldsList)
  self.inData.itemSelectionChanged.connect(self.updateFieldsTable)
  self.fieldsTable.cellChanged.connect(self.fieldNameChanged)
  self.addToMapCanvas.setCheckState(Qt.Checked)
  mapCanvas = self.iface.mapCanvas()
  # init dictionaries of items:
  self.sampItems = {}
  self.polyItems = {}
  self.rastItems = {}
  for i in range(mapCanvas.layerCount()):
   layer = mapCanvas.layer(i)
   if ( layer.type() == layer.VectorLayer ) and ( layer.geometryType() == QgsWkbTypes.PointGeometry ):
    # read point layers
    provider = layer.dataProvider()
    fields = provider.fields()
    theItem = [layer]
    for j in fields:
     theItem += [[str(j.name()), str(j.name()), False]]
    self.sampItems[str(layer.name())] = theItem
    self.inSample.addItem(layer.name())
   elif ( layer.type() == layer.VectorLayer ) and ( layer.geometryType() == QgsWkbTypes.PolygonGeometry ):
    # read polygon layers
    provider = layer.dataProvider()
    fields = provider.fields()
    theItem = [layer]
    for j in fields:
     theItem += [[str(j.name()), str(j.name()), False]]
    self.polyItems[str(layer.name())] = theItem
   elif layer.type() == layer.RasterLayer:
    # read raster layers
    theItem = [layer]
    for j in range(layer.bandCount()):
     if layer.bandCount() == 1:
      name1 = layer.bandName(j+1)
      name2 = layer.name()[:10]
     else:
      name1 = layer.bandName(j+1)
      name2 = layer.name()[:8] + "_" + str(j+1)
     theItem += [[name1, name2, False]]
    self.rastItems[str(layer.name())] = theItem
  self.updateFieldsList()


 def updateFieldsList(self):
  self.inData.clear()
  if not self.inSample.count(): return
  i = self.inSample.currentText()
  for j in range(1, len(self.sampItems[i])):
    #clear previously enabled fields (as they aren't selected in the widget)
    self.sampItems[i][j][2] = False
    self.inData.addItem(self.sampItems[i][0].name() + " : " + self.sampItems[i][j][0] + " (source point)")
#NOT YET FINISHED - to be switched to tree rather
#  self.inData.addItem(str(self.sampItems[i][0].name()) + " (X coordinate)")
#  self.inData.addItem(str(self.sampItems[i][0].name()) + " (Y coordinate)")

  for i in self.polyItems:
   for j in range(1, len(self.polyItems[i])):
    self.inData.addItem(str(self.polyItems[i][0].name()) + " : " + str(self.polyItems[i][j][0]) + " (polygon)")
  for i in self.rastItems:
   for j in range(1, len(self.rastItems[i])):
    self.inData.addItem(str(self.rastItems[i][0].name()) + " : "+ str(self.rastItems[i][j][0]) + " (raster)")
  self.updateFieldsTable()
  self.repaint()




 def updateFieldsTable(self): # called after selection changing
  # mark selected point items
  n=0
  i = self.inSample.currentText()
  for j in range(1, len(self.sampItems[i])):
    if self.inData.item(n) and self.inData.item(n).isSelected():
      self.sampItems[i][j][2] = True
    else:
      self.sampItems[i][j][2] = False
    n += 1
  # mark selected polygon items
  for i in self.polyItems:
   for j in range(1, len(self.polyItems[i])):
    if self.inData.item(n) and self.inData.item(n).isSelected():
     self.polyItems[i][j][2] = True
    else:
     self.polyItems[i][j][2] = False
    n += 1
  # mark selected raster items (don't zero n; it's one list)
  for i in self.rastItems:
   for j in range(1, len(self.rastItems[i])):
    if self.inData.item(n) and self.inData.item(n).isSelected():
     self.rastItems[i][j][2] = True
    else:
     self.rastItems[i][j][2] = False
    n += 1
  # fill the fieldsTable with point, then polygon and then raster items:
  self.fields = []
  n = 0
  self.fieldsTable.setRowCount(0)
  i = self.inSample.currentText()
  for j in range(1, len(self.sampItems[i])):
   if self.sampItems[i][j][2]:
    self.fields += [["point",i,j]]
    self.fieldsTable.setRowCount(n+1)
    cell = QTableWidgetItem(str(self.sampItems[i][0].name()) + " : " + str(self.sampItems[i][j][0]))
    cell.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
    self.fieldsTable.setItem(n,0,cell)
    self.fieldsTable.setItem(n,1,QTableWidgetItem(str(self.sampItems[i][j][1])))
    n += 1
  for i in self.polyItems:
   for j in range(1, len(self.polyItems[i])):
    if self.polyItems[i][j][2]:
     self.fields += [["poly",i,j]]
     self.fieldsTable.setRowCount(n+1)
     cell = QTableWidgetItem(str(self.polyItems[i][0].name()) + " : " + str(self.polyItems[i][j][0]))
     cell.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
     self.fieldsTable.setItem(n,0,cell)
     self.fieldsTable.setItem(n,1,QTableWidgetItem(str(self.polyItems[i][j][1])))
     n += 1
  for i in self.rastItems:
   for j in range(1, len(self.rastItems[i])):
    if self.rastItems[i][j][2]:
     self.fields += [["rast",i,j]]
     self.fieldsTable.setRowCount(n+1)
     cell = QTableWidgetItem(str(self.rastItems[i][0].name()) + " : " + str(self.rastItems[i][j][0]))
     cell.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
     self.fieldsTable.setItem(n,0,cell)
     self.fieldsTable.setItem(n,1,QTableWidgetItem(str(self.rastItems[i][j][1])))
     n += 1
  self.fieldsTable.resizeColumnsToContents()



 def fieldNameChanged(self, n): # called when any cell of the fieldsTable was modyfied
  # exit when false alarm
  if len(self.fields) == 0: return 0
  if self.fieldsTable.rowCount() == 0: return 0
  updatedItem = self.fieldsTable.item(n,1)
  if updatedItem == None: return 0
  # update items dictionaries
  updatedText = str(updatedItem.text())
  if self.fields[n][0] == "point":
   self.sampItems[self.fields[n][1]][self.fields[n][2]][1] = updatedText[:10]
  elif self.fields[n][0] == "poly":
   self.polyItems[self.fields[n][1]][self.fields[n][2]][1] = updatedText[:10]
  else:
   self.rastItems[self.fields[n][1]][self.fields[n][2]][1] = updatedText[:10]
  # cut to 10 characters if exceed
  if len(updatedText) > 10:
   self.updateFieldsTable()
   QMessageBox.information(self, self.tr("Point Sampling Tool"), self.tr("Name length can't exceed 10 chars, so it has been truncated."))
   # This message box may to make some confusion, if user press OK while "too long name" is still under edition.
   # In this case, the message box pops up (correctly) and then the OK button becomes pressed, but the self.accept method is not called.
   # This pressed button with no action may to look like a hang up.
   # I've no idea either
   #  how to pull this button up, or
   #  how to forse execution the self.accept method, or
   #  how to don't allow user to exceed 10 chars limit in QTableWidget cell. (THIS OPTION WOULD BE THE BEST!)
  self.fieldsTable.resizeColumnsToContents()



 def outFile(self): # by Carson Farmer 2008
  # display file dialog for output file
  self.outShape.clear()
  outName, _ = QFileDialog().getSaveFileName(self, self.tr("Output file"), ".",
                                             self.tr("GeoPackages(*.gpkg);;Comma separated values (*.csv);;Shapefiles (*.shp)"),
                                             options = QFileDialog.DontConfirmOverwrite)
  outPath = QFileInfo(outName).absoluteFilePath()
  if not outPath.upper().endswith('.GPKG') and not outPath.upper().endswith('.CSV') and not outPath.upper().endswith('.SHP'):
   outPath += '.gpkg'
  if outName:
   self.outShape.clear()
   self.outShape.insert(outPath)



 def accept(self): # Called when "OK" button pressed (based on the Carson Farmer's PointsInPoly Plugin, 2008)
  # check if all fields are filled up
  self.statusLabel.setText(self.tr("Check input values, please!"))
  nothingSelected = True
  for i in self.polyItems:
   for j in range(1, len(self.polyItems[i])):
    if self.polyItems[i][j][2]:
     nothingSelected = False
  for i in self.rastItems:
   for j in range(1, len(self.rastItems[i])):
    if self.rastItems[i][j][2]:
     nothingSelected = False

  if self.inSample.currentText() == "":
   self.tabWidget.setCurrentIndex(0)
   QMessageBox.information(self, self.tr("Point Sampling Tool"), self.tr("Please select vector layer containing the sampling points"))
   return
  if nothingSelected:
   self.tabWidget.setCurrentIndex(0)
   QMessageBox.information(self, self.tr("Point Sampling Tool"), self.tr("Please select at least one polygon attribute or raster band"))
   return
  if self.outShape.text() == "":
   self.tabWidget.setCurrentIndex(0)
   QMessageBox.information(self, self.tr("Point Sampling Tool"), self.tr("Please specify output file name"))
   return
  # check if destination field names are unique
  if not self.testFieldsNames(self.fields):
   self.updateFieldsTable()
   self.tabWidget.setCurrentIndex(1)
   QMessageBox.warning(self, self.tr("Point Sampling Tool"), self.tr("At least two field names are the same!\nPlease type unique names."))
   return

  # Check if there a CRS mismatch

  pointLayerSrid = list(self.sampItems.values())[0][0].crs().postgisSrid()
  msg = self.tr('''<html>All layers must have the same coordinate refere system. The <b>%s</b> layer seems to have different CRS id (<b>%d</b>)
                   than the point layer (<b>%d</b>). If they are two different CRSes, you need to reproject one of the layers first,
                   otherwise results will be wrong.<br/>
                   However, if you are sure both CRSes are the same, and they are just improperly recognized, you can safely continue.
                   Do you want to continue?</html>''')
  for i in self.polyItems:
   for j in range(1, len(self.polyItems[i])):
    if self.polyItems[i][j][2]:
     layerSrid = self.polyItems[i][0].crs().postgisSrid()
     if layerSrid != pointLayerSrid:
      if QMessageBox.question(self, self.tr("Point Sampling Tool: layer CRS mismatch!"), msg % (i, layerSrid, pointLayerSrid), QMessageBox.Yes | QMessageBox.No) != QMessageBox.Yes:
       return
  for i in self.rastItems:
   for j in range(1, len(self.rastItems[i])):
    if self.rastItems[i][j][2]:
     layerSrid = self.rastItems[i][0].crs().postgisSrid()
     if layerSrid != pointLayerSrid:
      if QMessageBox.question(self, self.tr("Point Sampling Tool: layer CRS mismatch!"), msg % (i, layerSrid, pointLayerSrid), QMessageBox.Yes | QMessageBox.No) != QMessageBox.Yes:
       return

  if True:
   # all tests passed! Let's go on
   self.statusLabel.setText(self.tr("Processing the output file name..."))
   self.repaint()
   outPath = self.outShape.text()
   outPath = outPath.replace("\\","/")
   if not outPath.upper().endswith('.GPKG') and not outPath.upper().endswith('.CSV') and not outPath.upper().endswith('.SHP'):
    outPath += '.gpkg'
   outName = QFileInfo(outPath).fileName()
   tableName = None
   oldFile = QFile(outPath)
   if oldFile.exists():
    if not outPath.upper().endswith('.GPKG'):
     if QMessageBox.question(self, self.tr("Point Sampling Tool"), self.tr("File %s already exists. Do you want to overwrite?") % outName) == QMessageBox.No:
      # return to filling the input fields
      self.outShape.clear()
      self.statusLabel.setText(self.tr("Fill up the input fields, please."))
      self.repaint()
      return
    else:
     msg = self.tr("""Please provide <b>table name</b> for your layer.<br/>
                      <b>WARNING: </b>Database %s already exists. If you select a table existing in it, the table will be overwritten.""") % outName
     tableName, result = QInputDialog.getText(self, "Point Sampling Tool", msg, text=outName[:-5])
     if not result:
      # return to filling the input fields
      self.outShape.clear()
      self.statusLabel.setText(self.tr("Fill up the input fields, please."))
      self.repaint()
      return
   self.statusLabel.setText(self.tr("Processing..."))
   self.repaint()
   # execute main function
   if not self.sampling(outPath, tableName):
    return
   self.outShape.clear()
   if self.addToMapCanvas.checkState() == Qt.Checked:
    uri = outPath
    layerName =  outName
    if tableName:
     uri += "|layername=%s" % tableName
     layerName += ": %s" % tableName
    self.vlayer = QgsVectorLayer(uri, layerName, "ogr")
    if self.vlayer.isValid():
     # Add the layer to the map, but first remove it if already present
     for l in QgsProject.instance().mapLayers().values():
      if hasattr(l, 'source') and l.source() == self.vlayer.source():
       QgsProject.instance().removeMapLayer(l)
     QgsProject.instance().addMapLayer(self.vlayer)
     self.statusLabel.setText(self.tr("OK. The new layer has been added to the map."))
    else:
     self.statusLabel.setText(self.tr("Error loading the created layer"))
     QMessageBox.warning(self, self.tr("Point Sampling Tool"), self.tr("The new layer seems to be created, but is invalid.\nIt won't be loaded."))



 def sampling(self, outPath, tableName): # main process
    # open sampling points layer
    pointLayer = self.sampItems[str(self.inSample.currentText())][0]
    pointProvider = pointLayer.dataProvider()
    allAttrs = pointProvider.attributeIndexes()
    sRs = pointLayer.crs()
    # create destination layer: first create list of selected fields
    fieldList = QgsFields()
    for i in range(len(self.fields)):
        if self.fields[i][0] == "point": #copying fields from source layer
            field = pointProvider.fields()[pointProvider.fieldNameIndex(self.sampItems[self.fields[i][1]][self.fields[i][2]][0])]
            field.setName(self.sampItems[self.fields[i][1]][self.fields[i][2]][1])
        elif self.fields[i][0] == "poly": #copying fields from polygon layers
            polyLayer = self.polyItems[self.fields[i][1]][0]
            polyProvider = polyLayer.dataProvider()
            field = polyProvider.fields()[polyProvider.fieldNameIndex(self.polyItems[self.fields[i][1]][self.fields[i][2]][0])]
            field.setName(self.polyItems[self.fields[i][1]][self.fields[i][2]][1])
        else: #creating fields for raster layers
            field = QgsField(self.rastItems[self.fields[i][1]][self.fields[i][2]][1], QVariant.Double, "real", 20, 5, "")
            ##### Better data type fit will be implemented in next versions
        fieldList.append(field)
    # create temporary memory layer (as it's currently impossible to set GPKG table name when writting features to QgsVectorFileWriter directly)
    memLayer = QgsVectorLayer("Point?crs=epsg:%d" % sRs.postgisSrid(), 'temp layer', 'memory')
    memLayer.startEditing()
    for field in fieldList:
        memLayer.addAttribute(field)
    memLayer.commitChanges()

    self.statusLabel.setText(self.tr("Writing data to the new layer..."))
    self.repaint()
    # process point after point...
    pointFeat = QgsFeature()
    np = 0
    snp = pointProvider.featureCount()
    for pointFeat in pointProvider.getFeatures():
        np += 1
        if snp<100 or ( snp<5000 and ( np // 10.0 == np / 10.0 ) ) or ( np // 100.0 == np / 100.0 ): # display each or every 10th or every 100th point:
            self.statusLabel.setText(self.tr("Processing point %s of %s") % (np, snp))
            self.repaint()
        # convert multipoint[0] to point
        pointGeom = pointFeat.geometry()
        if pointGeom.wkbType() == QgsWkbTypes.MultiPoint:
            pointPoint = pointGeom.asMultiPoint()[0]
        else:
            pointPoint = pointGeom.asPoint()
        outFeat = QgsFeature()
        outFeat.setGeometry(pointGeom)
        # ...and next loop inside: field after field
        bBox = QgsRectangle(pointPoint.x()-0.001,pointPoint.y()-0.001,pointPoint.x()+0.001,pointPoint.y()+0.001) # reuseable rectangle buffer around the point feature
        previousPolyLayer = None  # reuse previous feature if it's still the same layer
        previousPolyFeat = None   # reuse previous feature if it's still the same layer
        previousRastLayer = None  # reuse previous raster multichannel sample if it's still the same layer
        previousRastSample = None # reuse previous raster multichannel sample if it's still the same layer
        attrs = []
        for i in range(len(self.fields)):
            field = self.fields[i]
            if field[0] == "point":
                attr = pointFeat.attributes()[pointProvider.fieldNameIndex(self.sampItems[field[1]][field[2]][0])]
                attrs += [attr]
            elif field[0] == "poly":
                polyLayer = self.polyItems[field[1]][0]
                polyProvider = polyLayer.dataProvider()
                if polyLayer == previousPolyLayer:
                    polyFeat = previousPolyFeat
                else:
                    polyFeat = None
                    pointGeom = QgsGeometry().fromPointXY(pointPoint)
                    for iFeat in polyProvider.getFeatures(QgsFeatureRequest().setFilterRect(bBox)):
                        if pointGeom.intersects(iFeat.geometry()):
                            polyFeat = iFeat
                if polyFeat:
                    attr = polyFeat.attributes()[polyProvider.fieldNameIndex(self.polyItems[field[1]][field[2]][0])]
                else:
                    attr = None
                attrs += [attr] #only last one if more polygons overlaps!! This way we avoid attribute list overflow
                previousPolyLayer = polyLayer
                previousPolyFeat = polyFeat
            else: # field source is raster
                rastLayer = self.rastItems[field[1]][0]
                if rastLayer == previousRastLayer:
                    rastSample = previousRastSample
                else:
                    rastSample = rastLayer.dataProvider().identify(pointPoint, QgsRaster.IdentifyFormatValue).results()
                try:
                    #bandName = self.rastItems[field[1]][field[2]][0] #depreciated
                    bandNo = field[2]
                    attr = float(rastSample[bandNo]) ##### !! float() - I HAVE TO IMPLEMENT RASTER TYPE HANDLING!!!!
                except: # point is out of raster extent
                    attr = None
                attrs += [attr]
                previousRastLayer = rastLayer
                previousRastSample = rastSample
        outFeat.initAttributes(len(attrs))
        outFeat.setAttributes(attrs)
        memLayer.dataProvider().addFeature(outFeat)

    # write the memlayer to the output file
    so=QgsVectorFileWriter.SaveVectorOptions()
    so.fileEncoding = 'UTF-8'
    if outPath.upper().endswith('SHP'):
        so.driverName = "ESRI Shapefile"
    elif outPath.upper().endswith('CSV'):
        so.driverName = "CSV"
    else:
        so.driverName = "GPKG"
        if tableName:
            so.actionOnExistingFile = QgsVectorFileWriter.CreateOrOverwriteLayer
            so.layerName = tableName
    result, errMsg = QgsVectorFileWriter.writeAsVectorFormat(memLayer, outPath, so)
    if result:
     QMessageBox.critical(self, self.tr("Point sampling tool"), errMsg)
     return False
    else:
     del memLayer
     self.statusLabel.setText(self.tr("The new layer has been created."))
     return True



 def testFieldsNames(self, fields): #tests uniquity of field names
  ok = True
  if len(fields) > 1:
   for field1 in fields:
    for field2 in fields:
     if field1[0] == "point": name1 = self.sampItems[field1[1]][field1[2]][1]
     elif field1[0] == "poly": name1 = self.polyItems[field1[1]][field1[2]][1]
     else: name1 = self.rastItems[field1[1]][field1[2]][1]
     if field2[0] == "point": name2 = self.sampItems[field2[1]][field2[2]][1]
     elif field2[0] == "poly": name2 = self.polyItems[field2[1]][field2[2]][1]
     else: name2 = self.rastItems[field2[1]][field2[2]][1]
     if (name1 == name2) and (field1 != field2):
      ok = False
  return ok
Пример #14
0
class Dialog(QDialog, Ui_Dialog):

    sampItems = {
    }  # {name1 : [layer1, [field_src,field_dsn,Active?], [field_src,field_dsn,Active?], ...] , name2 : [layer2, ...] }
    polyItems = {
    }  # {name1 : [layer1, [field_src,field_dsn,Active?], [field_src,field_dsn,Active?], ...] , name2 : [layer2, ...] }
    rastItems = {
    }  # {name1 : [layer1, [band_name,field_dsn,Active?], [band_name,field_dsn,Active?], ...] , name2 : [layer2, ...] }
    fields = [
    ]  # [[type,layer,field],[type,layer,field],[type,layer,field]...] list of adresses of output fields

    def __init__(self, iface):
        QDialog.__init__(self)
        self.iface = iface
        self.setupUi(self)
        self.outButton.clicked.connect(self.outFile)
        self.inSample.currentIndexChanged.connect(self.updateFieldsList)
        self.inData.itemSelectionChanged.connect(self.updateFieldsTable)
        self.fieldsTable.cellChanged.connect(self.fieldNameChanged)
        self.addToMapCanvas.setCheckState(Qt.Checked)
        mapCanvas = self.iface.mapCanvas()
        # init dictionaries of items:
        self.sampItems = {}
        self.polyItems = {}
        self.rastItems = {}
        for i in range(mapCanvas.layerCount()):
            layer = mapCanvas.layer(i)
            if (layer.type()
                    == layer.VectorLayer) and (layer.geometryType()
                                               == QgsWkbTypes.PointGeometry):
                # read point layers
                provider = layer.dataProvider()
                fields = provider.fields()
                theItem = [layer]
                for j in fields:
                    theItem += [[str(j.name()), str(j.name()), False]]
                self.sampItems[str(layer.name())] = theItem
                self.inSample.addItem(layer.name())
            elif (layer.type()
                  == layer.VectorLayer) and (layer.geometryType()
                                             == QgsWkbTypes.PolygonGeometry):
                # read polygon layers
                provider = layer.dataProvider()
                fields = provider.fields()
                theItem = [layer]
                for j in fields:
                    theItem += [[str(j.name()), str(j.name()), False]]
                self.polyItems[str(layer.name())] = theItem
            elif layer.type() == layer.RasterLayer:
                # read raster layers
                theItem = [layer]
                for j in range(layer.bandCount()):
                    if layer.bandCount() == 1:
                        name1 = layer.bandName(j + 1)
                        name2 = layer.name()[:10]
                    else:
                        name1 = layer.bandName(j + 1)
                        name2 = layer.name()[:8] + "_" + str(j + 1)
                    theItem += [[name1, name2, False]]
                self.rastItems[str(layer.name())] = theItem
        self.updateFieldsList()

    def updateFieldsList(self):
        self.inData.clear()
        if not self.inSample.count(): return
        i = self.inSample.currentText()
        for j in range(1, len(self.sampItems[i])):
            #clear previously enabled fields (as they aren't selected in the widget)
            self.sampItems[i][j][2] = False
            self.inData.addItem(self.sampItems[i][0].name() + " : " +
                                self.sampItems[i][j][0] + " (source point)")


#NOT YET FINISHED - to be switched to tree rather
#  self.inData.addItem(str(self.sampItems[i][0].name()) + " (X coordinate)")
#  self.inData.addItem(str(self.sampItems[i][0].name()) + " (Y coordinate)")

        for i in self.polyItems:
            for j in range(1, len(self.polyItems[i])):
                self.inData.addItem(
                    str(self.polyItems[i][0].name()) + " : " +
                    str(self.polyItems[i][j][0]) + " (polygon)")
        for i in self.rastItems:
            for j in range(1, len(self.rastItems[i])):
                self.inData.addItem(
                    str(self.rastItems[i][0].name()) + " : " +
                    str(self.rastItems[i][j][0]) + " (raster)")
        self.updateFieldsTable()
        self.repaint()

    def updateFieldsTable(self):  # called after selection changing
        # mark selected point items
        n = 0
        i = self.inSample.currentText()
        for j in range(1, len(self.sampItems[i])):
            if self.inData.item(n) and self.inData.item(n).isSelected():
                self.sampItems[i][j][2] = True
            else:
                self.sampItems[i][j][2] = False
            n += 1
        # mark selected polygon items
        for i in self.polyItems:
            for j in range(1, len(self.polyItems[i])):
                if self.inData.item(n) and self.inData.item(n).isSelected():
                    self.polyItems[i][j][2] = True
                else:
                    self.polyItems[i][j][2] = False
                n += 1
        # mark selected raster items (don't zero n; it's one list)
        for i in self.rastItems:
            for j in range(1, len(self.rastItems[i])):
                if self.inData.item(n) and self.inData.item(n).isSelected():
                    self.rastItems[i][j][2] = True
                else:
                    self.rastItems[i][j][2] = False
                n += 1
        # fill the fieldsTable with point, then polygon and then raster items:
        self.fields = []
        n = 0
        self.fieldsTable.setRowCount(0)
        i = self.inSample.currentText()
        for j in range(1, len(self.sampItems[i])):
            if self.sampItems[i][j][2]:
                self.fields += [["point", i, j]]
                self.fieldsTable.setRowCount(n + 1)
                cell = QTableWidgetItem(
                    str(self.sampItems[i][0].name()) + " : " +
                    str(self.sampItems[i][j][0]))
                cell.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
                self.fieldsTable.setItem(n, 0, cell)
                self.fieldsTable.setItem(
                    n, 1, QTableWidgetItem(str(self.sampItems[i][j][1])))
                n += 1
        for i in self.polyItems:
            for j in range(1, len(self.polyItems[i])):
                if self.polyItems[i][j][2]:
                    self.fields += [["poly", i, j]]
                    self.fieldsTable.setRowCount(n + 1)
                    cell = QTableWidgetItem(
                        str(self.polyItems[i][0].name()) + " : " +
                        str(self.polyItems[i][j][0]))
                    cell.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
                    self.fieldsTable.setItem(n, 0, cell)
                    self.fieldsTable.setItem(
                        n, 1, QTableWidgetItem(str(self.polyItems[i][j][1])))
                    n += 1
        for i in self.rastItems:
            for j in range(1, len(self.rastItems[i])):
                if self.rastItems[i][j][2]:
                    self.fields += [["rast", i, j]]
                    self.fieldsTable.setRowCount(n + 1)
                    cell = QTableWidgetItem(
                        str(self.rastItems[i][0].name()) + " : " +
                        str(self.rastItems[i][j][0]))
                    cell.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
                    self.fieldsTable.setItem(n, 0, cell)
                    self.fieldsTable.setItem(
                        n, 1, QTableWidgetItem(str(self.rastItems[i][j][1])))
                    n += 1
        self.fieldsTable.resizeColumnsToContents()

    def fieldNameChanged(
            self, n):  # called when any cell of the fieldsTable was modyfied
        # exit when false alarm
        if len(self.fields) == 0: return 0
        if self.fieldsTable.rowCount() == 0: return 0
        updatedItem = self.fieldsTable.item(n, 1)
        if updatedItem == None: return 0
        # update items dictionaries
        updatedText = str(updatedItem.text())
        if self.fields[n][0] == "point":
            self.sampItems[self.fields[n][1]][self.fields[n]
                                              [2]][1] = updatedText[:10]
        elif self.fields[n][0] == "poly":
            self.polyItems[self.fields[n][1]][self.fields[n]
                                              [2]][1] = updatedText[:10]
        else:
            self.rastItems[self.fields[n][1]][self.fields[n]
                                              [2]][1] = updatedText[:10]
        # cut to 10 characters if exceed
        if len(updatedText) > 10:
            self.updateFieldsTable()
            QMessageBox.information(
                self, "Point Sampling Tool",
                "Name length can't exceed 10 chars, so it has been truncated.")
            # This message box may to make some confusion, if user press OK while "too long name" is still under edition.
            # In this case, the message box pops up (correctly) and then the OK button becomes pressed, but the self.accept method is not called.
            # This pressed button with no action may to look like a hang up.
            # I've no idea either
            #  how to pull this button up, or
            #  how to forse execution the self.accept method, or
            #  how to don't allow user to exceed 10 chars limit in QTableWidget cell. (THIS OPTION WOULD BE THE BEST!)
        self.fieldsTable.resizeColumnsToContents()

    def outFile(self):  # by Carson Farmer 2008
        # display file dialog for output file
        self.outShape.clear()
        outName, _ = QFileDialog().getSaveFileName(
            self,
            "Output file",
            ".",
            "GeoPackages(*.gpkg);;Comma separated values (*.csv);;Shapefiles (*.shp)",
            options=QFileDialog.DontConfirmOverwrite)
        outPath = QFileInfo(outName).absoluteFilePath()
        if not outPath.upper().endswith('.GPKG') and not outPath.upper(
        ).endswith('.CSV') and not outPath.upper().endswith('.SHP'):
            outPath += '.gpkg'
        if outName:
            self.outShape.clear()
            self.outShape.insert(outPath)

    def accept(
        self
    ):  # Called when "OK" button pressed (based on the Carson Farmer's PointsInPoly Plugin, 2008)
        # check if all fields are filled up
        self.statusLabel.setText("Check input values, please!")
        nothingSelected = True
        for i in self.polyItems:
            for j in range(1, len(self.polyItems[i])):
                if self.polyItems[i][j][2]:
                    nothingSelected = False
        for i in self.rastItems:
            for j in range(1, len(self.rastItems[i])):
                if self.rastItems[i][j][2]:
                    nothingSelected = False

        if self.inSample.currentText() == "":
            self.tabWidget.setCurrentIndex(0)
            QMessageBox.information(
                self, "Point Sampling Tool",
                "Please select vector layer containing the sampling points")
            return
        if nothingSelected:
            self.tabWidget.setCurrentIndex(0)
            QMessageBox.information(
                self, "Point Sampling Tool",
                "Please select at least one polygon attribute or raster band")
            return
        if self.outShape.text() == "":
            self.tabWidget.setCurrentIndex(0)
            QMessageBox.information(self, "Point Sampling Tool",
                                    "Please specify output file name")
            return
        # check if destination field names are unique
        if not self.testFieldsNames(self.fields):
            self.updateFieldsTable()
            self.tabWidget.setCurrentIndex(1)
            QMessageBox.warning(
                self, "Point Sampling Tool",
                "At least two field names are the same!\nPlease type unique names."
            )
            return

        # Check if there a CRS mismatch

        pointLayerSrid = list(
            self.sampItems.values())[0][0].crs().postgisSrid()
        msg = '''<html>All layers must have the same coordinate refere system. The <b>%s</b> layer seems to have different CRS id (<b>%d</b>)
            than the point layer (<b>%d</b>). If they are two different CRSes, you need to reproject one of the layers first,
            otherwise results will be wrong.<br/>
            However, if you are sure both CRSes are the same, and they are just improperly recognized, you can safely continue.
            Do you want to continue?</html>'''
        for i in self.polyItems:
            for j in range(1, len(self.polyItems[i])):
                if self.polyItems[i][j][2]:
                    layerSrid = self.polyItems[i][0].crs().postgisSrid()
                    if layerSrid != pointLayerSrid:
                        if QMessageBox.question(
                                self,
                                "Point Sampling Tool: layer CRS mismatch!",
                                msg %
                            (i, layerSrid, pointLayerSrid), QMessageBox.Yes
                                | QMessageBox.No) != QMessageBox.Yes:
                            return
        for i in self.rastItems:
            for j in range(1, len(self.rastItems[i])):
                if self.rastItems[i][j][2]:
                    layerSrid = self.rastItems[i][0].crs().postgisSrid()
                    if layerSrid != pointLayerSrid:
                        if QMessageBox.question(
                                self,
                                "Point Sampling Tool: layer CRS mismatch!",
                                msg %
                            (i, layerSrid, pointLayerSrid), QMessageBox.Yes
                                | QMessageBox.No) != QMessageBox.Yes:
                            return

        if True:
            # all tests passed! Let's go on
            self.statusLabel.setText("Processing the output file name...")
            self.repaint()
            outPath = self.outShape.text()
            outPath = outPath.replace("\\", "/")
            if not outPath.upper().endswith('.GPKG') and not outPath.upper(
            ).endswith('.CSV') and not outPath.upper().endswith('.SHP'):
                outPath += '.gpkg'
            outName = QFileInfo(outPath).fileName()
            tableName = None
            oldFile = QFile(outPath)
            if oldFile.exists():
                if not outPath.upper().endswith('.GPKG'):
                    if QMessageBox.question(
                            self, "Point Sampling Tool",
                            "File %s already exists. Do you want to overwrite?"
                            % outName) == QMessageBox.No:
                        # return to filling the input fields
                        self.outShape.clear()
                        self.statusLabel.setText(
                            "Fill up the input fields, please.")
                        self.repaint()
                        return
                else:
                    msg = """Please provide <b>table name</b> for your layer.<br/>
              <b>WARNING: </b>Database %s already exists. If you select a table existing in it, the table will be overwritten.""" % outName
                    tableName, result = QInputDialog.getText(
                        self, "Point Sampling Tool", msg, text=outName[:-5])
                    if not result:
                        # return to filling the input fields
                        self.outShape.clear()
                        self.statusLabel.setText(
                            "Fill up the input fields, please.")
                        self.repaint()
                        return
            self.statusLabel.setText("Processing...")
            self.repaint()
            # execute main function
            if not self.sampling(outPath, tableName):
                return
            self.outShape.clear()
            if self.addToMapCanvas.checkState() == Qt.Checked:
                uri = outPath
                layerName = outName
                if tableName:
                    uri += "|layername=%s" % tableName
                    layerName += ": %s" % tableName
                self.vlayer = QgsVectorLayer(uri, layerName, "ogr")
                if self.vlayer.isValid():
                    # Add the layer to the map, but first remove it if already present
                    for l in QgsProject.instance().mapLayers().values():
                        if hasattr(l, 'source') and l.source(
                        ) == self.vlayer.source():
                            QgsProject.instance().removeMapLayer(l)
                    QgsProject.instance().addMapLayer(self.vlayer)
                    self.statusLabel.setText(
                        "OK. The new layer has been added to the map.")
                else:
                    self.statusLabel.setText("Error loading the created layer")
                    QMessageBox.warning(
                        self, "Point Sampling Tool",
                        "The new layer seems to be created, but is invalid.\nIt won't be loaded."
                    )

    def sampling(self, outPath, tableName):  # main process
        # open sampling points layer
        pointLayer = self.sampItems[str(self.inSample.currentText())][0]
        pointProvider = pointLayer.dataProvider()
        allAttrs = pointProvider.attributeIndexes()
        sRs = pointLayer.crs()
        # create destination layer: first create list of selected fields
        fieldList = QgsFields()
        for i in range(len(self.fields)):
            if self.fields[i][0] == "point":  #copying fields from source layer
                field = pointProvider.fields()[pointProvider.fieldNameIndex(
                    self.sampItems[self.fields[i][1]][self.fields[i][2]][0])]
                field.setName(
                    self.sampItems[self.fields[i][1]][self.fields[i][2]][1])
            elif self.fields[i][
                    0] == "poly":  #copying fields from polygon layers
                polyLayer = self.polyItems[self.fields[i][1]][0]
                polyProvider = polyLayer.dataProvider()
                field = polyProvider.fields()[polyProvider.fieldNameIndex(
                    self.polyItems[self.fields[i][1]][self.fields[i][2]][0])]
                field.setName(
                    self.polyItems[self.fields[i][1]][self.fields[i][2]][1])
            else:  #creating fields for raster layers
                field = QgsField(
                    self.rastItems[self.fields[i][1]][self.fields[i][2]][1],
                    QVariant.Double, "real", 20, 5, "")
                ##### Better data type fit will be implemented in next versions
            fieldList.append(field)
        # create temporary memory layer (as it's currently impossible to set GPKG table name when writting features to QgsVectorFileWriter directly)
        memLayer = QgsVectorLayer("Point?crs=epsg:%d" % sRs.postgisSrid(),
                                  'temp layer', 'memory')
        memLayer.startEditing()
        for field in fieldList:
            memLayer.addAttribute(field)
        memLayer.commitChanges()

        self.statusLabel.setText("Writing data to the new layer...")
        self.repaint()
        # process point after point...
        pointFeat = QgsFeature()
        np = 0
        snp = pointProvider.featureCount()
        for pointFeat in pointProvider.getFeatures():
            np += 1
            if snp < 100 or (snp < 5000 and (np // 10.0 == np / 10.0)) or (
                    np // 100.0 == np /
                    100.0):  # display each or every 10th or every 100th point:
                self.statusLabel.setText("Processing point %s of %s" %
                                         (np, snp))
                self.repaint()
            # convert multipoint[0] to point
            pointGeom = pointFeat.geometry()
            if pointGeom.wkbType() == QgsWkbTypes.MultiPoint:
                pointPoint = pointGeom.asMultiPoint()[0]
            else:
                pointPoint = pointGeom.asPoint()
            outFeat = QgsFeature()
            outFeat.setGeometry(pointGeom)
            # ...and next loop inside: field after field
            bBox = QgsRectangle(
                pointPoint.x() - 0.001,
                pointPoint.y() - 0.001,
                pointPoint.x() + 0.001,
                pointPoint.y() +
                0.001)  # reuseable rectangle buffer around the point feature
            previousPolyLayer = None  # reuse previous feature if it's still the same layer
            previousPolyFeat = None  # reuse previous feature if it's still the same layer
            previousRastLayer = None  # reuse previous raster multichannel sample if it's still the same layer
            previousRastSample = None  # reuse previous raster multichannel sample if it's still the same layer
            attrs = []
            for i in range(len(self.fields)):
                field = self.fields[i]
                if field[0] == "point":
                    attr = pointFeat.attributes()[pointProvider.fieldNameIndex(
                        self.sampItems[field[1]][field[2]][0])]
                    attrs += [attr]
                elif field[0] == "poly":
                    polyLayer = self.polyItems[field[1]][0]
                    polyProvider = polyLayer.dataProvider()
                    if polyLayer == previousPolyLayer:
                        polyFeat = previousPolyFeat
                    else:
                        polyFeat = None
                        pointGeom = QgsGeometry().fromPointXY(pointPoint)
                        for iFeat in polyProvider.getFeatures(
                                QgsFeatureRequest().setFilterRect(bBox)):
                            if pointGeom.intersects(iFeat.geometry()):
                                polyFeat = iFeat
                    if polyFeat:
                        attr = polyFeat.attributes()[
                            polyProvider.fieldNameIndex(
                                self.polyItems[field[1]][field[2]][0])]
                    else:
                        attr = None
                    attrs += [
                        attr
                    ]  #only last one if more polygons overlaps!! This way we avoid attribute list overflow
                    previousPolyLayer = polyLayer
                    previousPolyFeat = polyFeat
                else:  # field source is raster
                    rastLayer = self.rastItems[field[1]][0]
                    if rastLayer == previousRastLayer:
                        rastSample = previousRastSample
                    else:
                        rastSample = rastLayer.dataProvider().identify(
                            pointPoint,
                            QgsRaster.IdentifyFormatValue).results()
                    try:
                        #bandName = self.rastItems[field[1]][field[2]][0] #depreciated
                        bandNo = field[2]
                        attr = float(
                            rastSample[bandNo]
                        )  ##### !! float() - I HAVE TO IMPLEMENT RASTER TYPE HANDLING!!!!
                    except:  # point is out of raster extent
                        attr = None
                    attrs += [attr]
                    previousRastLayer = rastLayer
                    previousRastSample = rastSample
            outFeat.initAttributes(len(attrs))
            outFeat.setAttributes(attrs)
            memLayer.dataProvider().addFeature(outFeat)

        # write the memlayer to the output file
        so = QgsVectorFileWriter.SaveVectorOptions()
        so.fileEncoding = 'UTF-8'
        if outPath.upper().endswith('SHP'):
            so.driverName = "ESRI Shapefile"
        elif outPath.upper().endswith('CSV'):
            so.driverName = "CSV"
        else:
            so.driverName = "GPKG"
            if tableName:
                so.actionOnExistingFile = QgsVectorFileWriter.CreateOrOverwriteLayer
                so.layerName = tableName
        result, errMsg = QgsVectorFileWriter.writeAsVectorFormat(
            memLayer, outPath, so)
        if result:
            QMessageBox.critical(self, "Point sampling tool", errMsg)
            return False
        else:
            del memLayer
            self.statusLabel.setText("The new layer has been created.")
            return True

    def testFieldsNames(self, fields):  #tests uniquity of field names
        ok = True
        if len(fields) > 1:
            for field1 in fields:
                for field2 in fields:
                    if field1[0] == "point":
                        name1 = self.sampItems[field1[1]][field1[2]][1]
                    elif field1[0] == "poly":
                        name1 = self.polyItems[field1[1]][field1[2]][1]
                    else:
                        name1 = self.rastItems[field1[1]][field1[2]][1]
                    if field2[0] == "point":
                        name2 = self.sampItems[field2[1]][field2[2]][1]
                    elif field2[0] == "poly":
                        name2 = self.polyItems[field2[1]][field2[2]][1]
                    else:
                        name2 = self.rastItems[field2[1]][field2[2]][1]
                    if (name1 == name2) and (field1 != field2):
                        ok = False
        return ok
Пример #15
0
class ExtractToPointsDialog(QDialog, UI_Dialog):
    
    targetItems = {}
    polygonItems = {}
    rasterItems = {}
    fields = []
    
    def __init__(self, iface):
        QDialog.__init__(self)
        self.iface = iface
        self.setupUi(self)
        
        #connect
        self.BrowseButton.clicked.connect(self.browseFile)
        self.TargetLayer.currentIndexChanged.connect(self.updateFieldsList)
        self.AddToMap.setCheckState(Qt.Checked)
        self.SourceField.itemSelectionChanged.connect(self.updateFieldsTable)
        self.FieldsTable.cellChanged.connect(self.fieldNameChanged)
        self.buttonBox.accepted.connect(self.accept)
        self.buttonBox.rejected.connect(self.reject)
        
        mapCanvas = self.iface.mapCanvas()
        
        # initial dictionaries of items:
        self.targetItems = {}
        self.polygonItems = {}
        self.rasterItems = {}
        
        for i in range(mapCanvas.layerCount()):
            layer = mapCanvas.layer(i)
            
            if (layer.type() == layer.VectorLayer) and (layer.geometryType() == QgsWkbTypes.PointGeometry):
                # identify and get point layer
                provider = layer.dataProvider()
                fields = provider.fields()
                allItem = [layer]
                for j in fields:
                    allItem += [[str(j.name()), str(j.name()), False]]
                self.targetItems[str(layer.name())] = allItem
                self.TargetLayer.addItem(layer.name())
                
            elif (layer.type() == layer.VectorLayer) and (layer.geometryType() == QgsWkbTypes.PolygonGeometry):
                # identify and get polygon layer
                provider = layer.dataProvider()
                fields = provider.fields()
                allItem = [layer]
                for j in fields:
                    allItem += [[str(j.name()), str(j.name()), False]]
                self.polygonItems[str(layer.name())] = allItem
                
            elif layer.type() == layer.RasterLayer:
                # identify and get raster layer
                allItem = [layer]
                for j in range(layer.bandCount()):
                    if layer.bandCount() == 1:
                        name1 = layer.bandName(j+1) 
                        name2 = layer.name()[:10]
                    else:
                        name1 = layer.bandName(j+1)
                        name2 = layer.name()[:8] + "_" + str(j+1)
                    allItem += [[name1, name2, False]]
                self.rasterItems[str(layer.name())] = allItem
        
        self.updateFieldsList()
        
    def updateFieldsList(self):
        
        self.SourceField.clear()
        if not self.TargetLayer.count():
            return
        # i:layer j:fields
        i = self.TargetLayer.currentText()
        for j in range(1, len(self.targetItems[i])):
            #clear previously enabled fields (as they aren't selected in the widget)
            self.targetItems[i][j][2] = False
            self.SourceField.addItem(self.targetItems[i][0].name() + " : " + self.targetItems[i][j][0] + "  (source layer field)")
            
        for i in self.polygonItems:
            for j in range(1, len(self.polygonItems[i])):
                self.SourceField.addItem(str(self.polygonItems[i][0].name()) + " : " + str(self.polygonItems[i][i][0]) + "  (polygon)")
            
        for i in self.rasterItems:
            for j in range(1, len(self.rasterItems[i])):
                self.SourceField.addItem(str(self.rasterItems[i][0].name()) + " : " + str(self.rasterItems[i][j][0]) + "  (raster)")
        
        self.updateFieldsTable()
        self.repaint()
        
        
    def updateFieldsTable(self):
        # after selection, update the table
        
        # mark selected point items
        n = 0
        i = self.TargetLayer.currentText()
        for j in range(1, len(self.targetItems[i])):
            if self.SourceField.item(n) and self.SourceField.item(n).isSelected():
                self.targetItems[i][j][2] = True
            else:
                self.targetItems[i][j][2] = False
            n += 1
            
        # mark selected polygon items
        for i in self.polygonItems:
            for j in range(1, len(self.polygonItems[i])):
                if self.SourceField.item(n) and self.SourceField.item(n).isSelected():
                    self.polygonItems[i][j][2] = True
                else:
                    self.polygonItems[i][j][2] = False
                n += 1
        
        # mark selected raster items
        for i in self.rasterItems:
            for j in range(1, len(self.rasterItems[i])):
                if self.SourceField.item(n) and self.SourceField.item(n).isSelected():
                    self.rasterItems[i][j][2] = True
                else:
                    self.rasterItems[i][j][2] = False
                n += 1
        
        # fill FieldsTable with point, polygon and raster items
        self.fields = []
        n = 0
        self.FieldsTable.setRowCount(0)
        # point
        i = self.TargetLayer.currentText()
        for j in range(1, len(self.targetItems[i])):
            if self.targetItems[i][j][2]:
                self.fields += [["point",i,j]]
                self.FieldsTable.setRowCount(n+1)
                cell = QTableWidgetItem(str(self.targetItems[i][0].name()) + " : " + str(self.targetItems[i][j][0]))
                cell.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
                self.FieldsTable.setItem(n, 0, cell)
                self.FieldsTable.setItem(n, 1, QTableWidgetItem(str(self.targetItems[i][j][1])))
                n += 1
        #polygon
        for i in self.polygonItems:
            for j in range(1, len(self.polygonItems[i])):
                if self.polygonItems[i][j][2]:
                    self.fields += [["polygon",i,j]]
                    self.FieldsTable.setRowCount(n+1)
                    cell = QTableWidgetItem(str(self.polygonItems[i][0].name()) + " : " +str(self.polygonItems[i][j][0]))
                    cell.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
                    self.FieldsTable.setItem(n, 0, cell)
                    self.FieldsTable.setItem(n, 1, QTableWidgetItem(str(self.polygonItems[i][j][1])))
                    n += 1
        #raster
        for i in self.rasterItems:
            for j in range(1, len(self.rasterItems[i])):
                if self.rasterItems[i][j][2]:
                    self.fields += [["raster",i,j]]
                    self.FieldsTable.setRowCount(n+1)
                    cell = QTableWidgetItem(str(self.rasterItems[i][0].name()) + " : " +str(self.rasterItems[i][j][0]))
                    cell.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
                    self.FieldsTable.setItem(n, 0, cell)
                    self.FieldsTable.setItem(n, 1, QTableWidgetItem(str(self.rasterItems[i][j][1])))
                    n += 1
        
        self.FieldsTable.resizeColumnsToContents()
        
    def fieldNameChanged(self, n):
        # when cell of the FieldsTable was modified
        
        # exit when false alarm
        if len(self.fields) == 0:
            return 0
        if self.FieldsTable.rowCount() == 0:
            return 0
        
        updatedItem = self.FieldsTable.item(n,1)
        if updatedItem == None:
            return 0
        
        # update
        updatedText = str(updatedItem.text())
        if self.fields[n][0] == "point":
            self.targetItems[self.fields[n][1]][self.fields[n][2]][1] = updatedText[:10]
        
        elif self.fields[n][0] == "polygon":
            self.polygonItems[self.fields[n][1]][self.fields[n][2]][1] = updatedText[:10]
        
        else:
            self.rasterItems[self.fields[n][1]][self.fields[n][2]][1] = updatedText[:10]
        
        # limit 10 characters
        if len(updatedText) > 10:
            self.updateFieldsTable()
            QMessageBox.information(self, self.tr("Extract Tool"), self.tr("Limit 10 Characters."))
            
        self.FieldsTable.resizeColumnsToContents()
        
    def browseFile(self):
        # call dialog for output file
        self.OutLayer.clear()
        outName, _ = QFileDialog().getSaveFileName(self, self.tr("Output file"), '.', 
                                                   self.tr("Shapefiles (*.shp);;Comma separated values (*.csv);;GeoPackages(*.gpkg)"), 
                                                   options = QFileDialog.DontConfirmOverwrite)
        outPath = QFileInfo(outName).absoluteFilePath()
        if not outPath.upper().endswith('.SHP') and not outPath.upper().endswith('.CSV') and not outPath.upper().endswith('.GPKG'):
            outPath += '.gpkg'
        if outName:
            self.OutLayer.clear()
            self.OutLayer.insert(outPath)
            
    def accept(self):
        # "OK" button
        
        # check if all fields are filled up
        nothingSelected = True
        for i in self.polygonItems:
            for j in range(1, len(self.polygonItems[i])):
                if self.polygonItems[i][j][2]:
                    nothingSelected = False
        
        for i in self.rasterItems:
            for j in range(1, len(self.rasterItems[i])):
                if self.rasterItems[i][j][2]:
                    nothingSelected = False
                    
        if self.TargetLayer.currentText() == "":
            QMessageBox.information(self, self.tr("Extract Tool"), self.tr("Please select target point layer"))
            return
        if nothingSelected:
            QMessageBox.information(self, self.tr("Extract Tool"), self.tr("Please select at least one field"))
            return 
        if self.OutLayer.text() == "":
            QMessageBox.information(self, self.tr("Extract Tool"), self.tr("Please specify output file name"))
            return
        
        # check if target field names are unique
        if not self.testFieldNames(self.fields):
            self.updateFieldsTable()
            QMessageBox.warning(self, self.tr("Extract Tool"), self.tr("At least two field names are the same!\nPlease type unique names."))
            return
        
        # check if there a CRS mismatch
        targetLayerSrid = list(self.targetItems.values())[0][0].crs().postgisSrid()
        msg = self.tr('''<html>All layers must have the same coordinate reference system. The <b>%s</b> layer seems to have different CRS id (<b>%d</b>)
                   than the point layer (<b>%d</b>). If they are two different CRSes, you need to reproject one of the layers first,
                   otherwise results will be wrong.<br/>
                   However, if you are sure both CRSes are the same, and they are just improperly recognized, you can safely continue.
                   Do you want to continue?</html>''')
        
        for i in self.polygonItems:
            for j in range(1, len(self.polygonItems[i])):
                if self.polygonItems[i][j][2]:
                    layerSrid = self.polygonItems[i][0].crs().postgisSrid()
                    if layerSrid != targetLayerSrid:
                        if QMessageBox.question(self, self.tr("Extract Tool: layer CRS mismatch!"), msg % (i, layerSrid, targetLayerSrid), QMessageBox.Yes | QMessageBox.No) != QMessageBox.Yes:
                            return
        
        for i in self.rasterItems:
            for j in range(1, len(self.rasterItems[i])):
                if self.rasterItems[i][j][2]:
                    layerSrid = self.rasterItems[i][0].crs().postgisSrid()
                    if layerSrid != targetLayerSrid:
                        if QMessageBox.question(self, self.tr("Extract Tool: layer CRS mismatch!"), msg % (i, layerSrid, targetLayerSrid), QMessageBox.Yes | QMessageBox.No) != QMessageBox.Yes:
                            return
        
        if True:
            # all tests passed, go on
            
            self.repaint()
            outPath = self.OutLayer.text()
            outPath = outPath.replace("\\","/")
            if not outPath.upper().endswith('.SHP') and not outPath.upper().endswith('.CSV') and not outPath.upper().endswith('.GPKG'):
                outPath += '.gpkg'
            outName = QFileInfo(outPath).fileName()
            tableName = None
            oldFile = QFile(outPath)
            if oldFile.exists():
                if not outPath.upper().endswith('.GPKG'):
                    if QMessageBox.question(self, self.tr("Extract Tool"), self.tr("File %s already exists. Do you want to overwrite?") % outName) == QMessageBox.No:
                        self.OutLayer.clear()
                        self.repaint()
                        return
                    
                else:
                    msg = self.tr("""Please provide <b>table name</b> for your layer.<br/>
                      <b>WARNING: </b>Database %s already exists. If you select a table existing in it, the table will be overwritten.""") % outName
                    tableName, result = QInputDialog.getText(self, "Extract Tool", msg, text = outName[:-5])
                    
                    if not result:
                        self.OutLayer.clear()
                        self.repaint()
                        return
            
            self.repaint()
            # execute main function
            if not self.extract(outPath, tableName):
                return 
        
            self.OutLayer.clear()
            if self.AddToMap.checkState() == Qt.Checked:
                uri = outPath
                layerName = outName
                if tableName:
                    uri += "|layername=%s" % tableName
                    layerName += ": %s" % tableName
                self.vlayer = QgsVectorLayer(uri, layerName, "ogr")
                
                if self.vlayer.isValid():
                    # Add layer to the map, but first remove it if already present
                    for l in QgsProject.instance().mapLayers().values():
                        if hasattr(l, 'source') and l.source() == self.vlayer.source():
                            QgsProject.instance().removeMapLayer(l)
                            
                    QgsProject.instance().addMapLayer(self.vlayer)
                
                else:
                    QMessageBox.warning(self, self.tr("Extract Tool"), self.tr("The new layer seems to be created, but is invalid.\nIt won't be loaded."))
                
    def extract(self, outPath, tableName):
        # main process
        
        # open target point layer
        targetLayer = self.targetItems[str(self.TargetLayer.currentText())][0]
        targetprovider = targetLayer.dataProvider()
        allAttrs = targetprovider.attributeIndexes()
        sRs = targetLayer.crs()
        
        # create output layer: first create list of selected fields
        fieldList = QgsFields()
        for i in range(len(self.fields)):
            if self.fields[i][0] == "point":
                # copying fields from source layer
                field = targetprovider.fields()[targetprovider.fieldNameIndex(self.targetItems[self.fields[i][1]][self.fields[i][2]][0])]
                field.setName(self.targetItems[self.fields[i][1]][self.fields[i][2]][1])
                
            elif self.fields[i][0] == "polygon": 
                # copying fields from polygon layers
                polyLayer = self.polygonItems[self.fields[i][1]][0]
                polyProvider = polyLayer.dataProvider()
                field = polyProvider.fields()[polyProvider.fieldNameIndex(self.polygonItems[self.fields[i][1]][self.fields[i][2]][0])]
                field.setName(self.polygonItems[self.fields[i][1]][self.fields[i][2]][1])
            
            else: 
                # creating fields for raster layers
                field = QgsField(self.rasterItems[self.fields[i][1]][self.fields[i][2]][1], QVariant.Double, "real", 20, 5, "")
            
            fieldList.append(field)
            
        # create temporary memory layer 
        memLayer = QgsVectorLayer("Point?crs=epsg:%d" % sRs.postgisSrid(), 'temp layer', 'memory')
        memLayer.startEditing()
        for field in fieldList:
            memLayer.addAttribute(field)
        memLayer.commitChanges()
        
        self.repaint()
        # process point after point
        pointFeat = QgsFeature()
        np = 0
        snp = targetprovider.featureCount()
        for pointFeat in targetprovider.getFeatures():
            np += 1
            # convert multipoint[0] to point
            pointGeom = pointFeat.geometry()
            if pointGeom.wkbType() == QgsWkbTypes.MultiPoint:
                pointPoint = pointGeom.asMultiPoint()[0]
            else:
                pointPoint = pointGeom.asPoint()
            outFeat = QgsFeature()
            outFeat.setGeometry(pointGeom)
            
            # and next loop inside: field after field
            bBox = QgsRectangle(pointPoint.x()-0.001,pointPoint.y()-0.001,pointPoint.x()+0.001,pointPoint.y()+0.001) 
            # reusable rectangle buffer around the point feature
            previousPolyLayer = None  # reuse previous feature if it's still the same layer
            previousPolyFeat = None   # reuse previous feature if it's still the same layer
            previousRastLayer = None  # reuse previous raster multichannel sample if it's still the same layer
            previousRastSample = None # reuse previous raster multichannel sample if it's still the same layer
            attrs = []
            for i in range(len(self.fields)):
                field = self.fields[i]
                
                if field[0] == "point":
                    attr = pointFeat.attributes()[targetprovider.fieldNameIndex(self.targetItems[field[1]][field[2]][0])]
                    attrs += [attr]
                    
                elif field[0] == "polygon":
                    polyLayer = self.polygonItems[field[1]][0]
                    polyProvider = polyLayer.dataProvider()
                    
                    if polyLayer == previousPolyLayer:
                        polyFeat = previousPolyFeat
                        
                    else:
                        polyFeat = None
                        pointGeom = QgsGeometry().fromPointXY(pointPoint)
                        for iFeat in polyProvider.getFeatures(QgsFeatureRequest().setFilterRect(bBox)):
                            if pointGeom.intersects(iFeat.geometry()):
                                polyFeat = iFeat
                                
                    if polyFeat:
                        attr = polyFeat.attributes()[polyProvider.fieldNameIndex(self.polygonItems[field[1]][field[2]][0])]
                    
                    else:
                        attr = None
                    attrs += [attr] 
                    #only last one if more polygons overlaps
                    previousPolyLayer = polyLayer
                    previousPolyFeat = polyFeat
                    
                else:
                    # raster
                    rastLayer = self.rasterItems[field[1]][0]
                    if rastLayer == previousRastLayer:
                        rastSample = previousRastSample
                        
                    else:
                        rastSample = rastLayer.dataProvider().identify(pointPoint, QgsRaster.IdentifyFormatValue).results()
                    
                    try:
                        bandNo = field[2]
                        attr = float(rastSample[bandNo])
                    except: # point is out of raster extent
                        attr = None
                    
                    attrs += [attr]
                    previousRastLayer = rastLayer
                    previousRastSample = rastSample
                    
            outFeat.initAttributes(len(attrs))
            outFeat.setAttributes(attrs)
            memLayer.dataProvider().addFeature(outFeat)
        
        # write memlayer to the output file
        so = QgsVectorFileWriter.SaveVectorOptions()
        so.fileEncoding = 'UTF-8'
        
        if outPath.upper().endswith('SHP'):
            so.driverName = "ESRI Shapefile"
        elif outPath.upper().endswith('CSV'):
            so.driverName = "CSV"
        else:
            so.driverName = "GPKG"
            if tableName:
                so.actionOnExistingFile = QgsVectorFileWriter.CreateOrOverwriteLayer
                so.layerName = tableName
        result, errMsg = QgsVectorFileWriter.writeAsVectorFormat(memLayer, outPath, so)
        if result:
            QMessageBox.critical(self, self.tr("Extract tool"), errMsg)
            return False
        else:
            del memLayer
            return True
        
    def testFieldNames(self, fields):
        #tests uniqueness of field names
        ok = True
        if len(fields) > 1:
            for field1 in fields:
                for field2 in fields:
                    if field1[0] == "point": 
                        name1 = self.targetItems[field1[1]][field1[2]][1]
                    elif field1[0] == "polygon": 
                        name1 = self.polygonItems[field1[1]][field1[2]][1]
                    else: 
                        name1 = self.rasterItems[field1[1]][field1[2]][1]
                        
                    if field2[0] == "point": 
                        name2 = self.targetItems[field2[1]][field2[2]][1]
                    elif field2[0] == "polygon": 
                        name2 = self.polygonItems[field2[1]][field2[2]][1]
                    else: 
                        name2 = self.rasterItems[field2[1]][field2[2]][1]
                        
                    if (name1 == name2) and (field1 != field2):
                        ok = False
                        
        return ok
Пример #16
0
def calc(progress_bars, receiver_layer, source_pts_layer, source_roads_layer,
         settings, level_field_index, obstacles_layer, rays_writer,
         diff_rays_writer):

    research_ray = int(settings['research_ray'])
    temperature = int(settings['temperature'])
    humidity = int(settings['humidity'])

    time = datetime.now()
    ## create diffraction points

    if obstacles_layer is not None:
        bar = progress_bars['create_dif']['bar']

        diffraction_points_layer_path = os.path.abspath(
            os.path.join(temp_dir + os.sep + "diffraction_pts.shp"))

        on_CreateDiffractionPoints.run(bar, obstacles_layer.source(),
                                       diffraction_points_layer_path)
        diffraction_layer_name = 'diff'
        diffraction_layer = QgsVectorLayer(diffraction_points_layer_path,
                                           diffraction_layer_name, "ogr")

        progress_bars['create_dif']['label'].setText(
            'Done in ' + duration(time, datetime.now()))
        #        print 'crea diffraction points ',datetime.now() - time
        time = datetime.now()

    # Create emission layer that will contain all the emission pts from source_pts and source_roads
    emission_pts_layer_path = os.path.abspath(
        os.path.join(temp_dir + os.sep + "emission_pts.shp"))
    # emission_pts_fields = [QgsField("type", QVariant.String),
    #                        QgsField("id_source", QVariant.Int),
    #                        QgsField("segment", QVariant.String),]
    emission_pts_fields = QgsFields()
    emission_pts_fields.append(QgsField("type", QVariant.String))
    emission_pts_fields.append(QgsField("id_source", QVariant.Int))
    emission_pts_fields.append(QgsField("segment", QVariant.String))

    emission_pts_writer = QgsVectorFileWriter(emission_pts_layer_path,
                                              "System", emission_pts_fields,
                                              QgsWkbTypes.Point,
                                              receiver_layer.crs(),
                                              "ESRI Shapefile")

    bar = progress_bars['prepare_emi']['bar']
    bar.setValue(1)

    source_feat_all_dict = {}

    # pts source layer to emission pts layer
    if source_pts_layer is not None:
        # get emission levels and add feat to emission pts layer
        source_pts_levels_dict = {}
        source_pts_feat_all = source_pts_layer.dataProvider().getFeatures()
        for source_feat in source_pts_feat_all:
            # get emission values
            levels = get_levels(settings, source_pts_layer, source_feat)
            source_pts_levels_dict[source_feat.id()] = levels

            # add feat to emission pts layer
            source_feat.setAttributes(['pt', source_feat.id(), None])
            emission_pts_writer.addFeature(source_feat)

    # roads source layer to emission pts layer
    if source_roads_layer is not None:

        ## create emission points from roads source
        emission_pts_roads_layer_path = os.path.abspath(
            os.path.join(temp_dir + os.sep + "emission_pts_roads.shp"))
        on_CreateEmissionPoints.run(source_roads_layer.source(),
                                    receiver_layer.source(),
                                    emission_pts_roads_layer_path,
                                    research_ray)
        emission_pts_roads_layer = QgsVectorLayer(
            emission_pts_roads_layer_path, 'emission_pts_roads', "ogr")

        # get levels from the road source
        source_roads_levels_dict = {}
        source_roads_feat_all = source_roads_layer.dataProvider().getFeatures()
        for source_feat in source_roads_feat_all:
            levels = get_levels(settings, source_roads_layer, source_feat)
            source_roads_levels_dict[source_feat.id()] = levels

        # add roads pts to emission pts layer
        source_pts_roads_feat_all = emission_pts_roads_layer.dataProvider(
        ).getFeatures()

        for source_feat in source_pts_roads_feat_all:
            id_source = source_feat['id_source']
            segment_source = source_feat[
                'd_rTOe']  # Pierluigi -- corretto perchè chiamava segment

            # add feat to emission pts layer
            source_feat.setAttributes(['road', id_source, segment_source])
            emission_pts_writer.addFeature(source_feat)

    del emission_pts_writer

    # Create dict with all the data
    source_feat_all_dict = {}
    source_layer = QgsVectorLayer(emission_pts_layer_path, 'emission pts',
                                  "ogr")
    source_feat_all = source_layer.dataProvider().getFeatures()
    source_feat_total = source_layer.dataProvider().featureCount()
    source_feat_number = 0

    for source_feat in source_feat_all:

        source_feat_number = source_feat_number + 1
        barValue = source_feat_number / float(source_feat_total) * 100
        bar.setValue(barValue)

        type_source = source_feat['type']
        id_source = source_feat['id_source']
        segment = source_feat['segment']

        if type_source == 'pt':
            levels = source_pts_levels_dict[id_source]

        if type_source == 'road':
            levels = source_roads_levels_dict[id_source]

        value = {}
        value['type'] = type_source
        value['feat'] = source_feat
        value['global'] = levels['global']
        value['bands'] = levels['bands']
        value['segment'] = segment
        source_feat_all_dict[source_feat.id()] = value

    ## get data
    # receiver layer
    receiver_feat_all = receiver_layer.dataProvider().getFeatures()
    receiver_feat_total = receiver_layer.dataProvider().featureCount()
    receiver_feat_number = 0

    # obstacles layer
    if obstacles_layer is not None:
        obstacles_feat_all = obstacles_layer.dataProvider().getFeatures()
        obstacles_feat_all_dict = {}
        for obstacles_feat in obstacles_feat_all:
            obstacles_feat_all_dict[obstacles_feat.id()] = obstacles_feat

        # diffraction layer
        diff_feat_all = diffraction_layer.dataProvider().getFeatures()
        diff_feat_all_dict = {}
        for diff_feat in diff_feat_all:
            diff_feat_all_dict[diff_feat.id()] = diff_feat

    progress_bars['prepare_emi']['label'].setText(
        'Done in ' + duration(time, datetime.now()))
    # fix_print_with_import
    print('get acoustic data', datetime.now() - time)
    time = datetime.now()

    if obstacles_layer is None:

        bar = progress_bars['recTOsou']['bar']

        recTOsource_dict = on_RaysSearch.run(bar, receiver_layer.source(),
                                             source_layer.source(), None,
                                             research_ray)

        progress_bars['recTOsou']['label'].setText(
            'Done in ' + duration(time, datetime.now()))

        # fix_print_with_import
        print('find connections receivers sources ', datetime.now() - time)
        time = datetime.now()
        diffTOsource_dict = {}
        recTOdiff_dict = {}

    else:
        ### recTOsou
        bar = progress_bars['recTOsou']['bar']
        recTOsource_dict = on_RaysSearch.run(bar, receiver_layer.source(),
                                             source_layer.source(),
                                             obstacles_layer.source(),
                                             research_ray)

        progress_bars['recTOsou']['label'].setText(
            'Done in ' + duration(time, datetime.now()))

        # fix_print_with_import
        print('find connections receceivers sources ', datetime.now() - time)
        time = datetime.now()

        ### difTOsou
        bar = progress_bars['difTOsou']['bar']

        diffTOsource_dict = on_RaysSearch.run(bar, diffraction_layer.source(),
                                              source_layer.source(),
                                              obstacles_layer.source(),
                                              research_ray)

        progress_bars['difTOsou']['label'].setText(
            'Done in ' + duration(time, datetime.now()))

        # fix_print_with_import
        print('find connections diffraction points sources',
              datetime.now() - time)
        time = datetime.now()

        ### recTOdif
        bar = progress_bars['recTOdif']['bar']

        #        recTOdiff_dict = on_RaysSearch.run_selection_distance(bar,receiver_layer.source(),diffraction_layer.source(),obstacles_layer.source(),research_ray,diffTOsource_dict,source_layer.source())
        recTOdiff_dict = on_RaysSearch.run_selection(
            bar, receiver_layer.source(), diffraction_layer.source(),
            obstacles_layer.source(), research_ray, diffTOsource_dict)

        progress_bars['recTOdif']['label'].setText(
            'Done in ' + duration(time, datetime.now()))

        # fix_print_with_import
        print('find connectino receivers diffraction points',
              datetime.now() - time)
        time = datetime.now()

    ray_id = 0
    diff_ray_id = 0

    receiver_feat_all_new_fields = {}

    bar = progress_bars['calculate']['bar']

    for receiver_feat in receiver_feat_all:

        receiver_feat_number = receiver_feat_number + 1
        barValue = receiver_feat_number / float(receiver_feat_total) * 100
        bar.setValue(barValue)

        receiver_feat_new_fields = {}

        # initializes the receiver point lin level
        receiver_point_lin_level = {}
        receiver_point_lin_level['gen'] = 0
        receiver_point_lin_level['day'] = 0
        receiver_point_lin_level['eve'] = 0
        receiver_point_lin_level['nig'] = 0

        if receiver_feat.id() in recTOsource_dict:

            source_ids = recTOsource_dict[receiver_feat.id()]

            for source_id in source_ids:

                source_feat_value = source_feat_all_dict[source_id]

                source_feat = source_feat_value['feat']

                ray_geometry = QgsGeometry.fromPolylineXY([
                    receiver_feat.geometry().asPoint(),
                    source_feat.geometry().asPoint()
                ])

                d_recTOsource = compute_distance(
                    receiver_feat.geometry().asPoint(),
                    source_feat.geometry().asPoint())
                # length with receiver points height fixed to 4 m
                d_recTOsource_4m = sqrt(d_recTOsource**2 + 16)

                feat_type = source_feat_value['type']
                level_emi = source_feat_value['global']
                level_emi_bands = source_feat_value['bands']
                segment = source_feat_value['segment']

                level_dir = {}
                level_atm_bands = {}

                geo_attenuation = on_Acoustics.GeometricalAttenuation(
                    'spherical', d_recTOsource_4m)

                for key in list(level_emi.keys()):
                    if level_emi[key] > 0:
                        level_atm_bands[
                            key] = on_Acoustics.AtmosphericAbsorption(
                                d_recTOsource, temperature, humidity,
                                level_emi_bands[key]).level()
                        level_dir[key] = on_Acoustics.OctaveBandsToGlobal(
                            level_atm_bands[key]) - geo_attenuation

                        # correction for the segment lenght
                        if feat_type == 'road':
                            if (settings['implementation_roads'] == 'POWER_R'
                                    or settings['implementation_roads']
                                    == 'NMPB'):
                                level_dir[key] = level_dir[
                                    key] + 20 + 10 * log10(float(segment)) + 3
                            if settings['implementation_roads'] == 'CNOSSOS':
                                level_dir[key] = level_dir[key] + 10 * log10(
                                    float(segment)) + 3

                        receiver_point_lin_level[
                            key] = receiver_point_lin_level[key] + 10**(
                                level_dir[key] / float(10))
                    else:
                        #
                        level_dir[key] = -1

                if rays_writer is not None:
                    ray = QgsFeature()
                    ray.setGeometry(ray_geometry)
                    attributes = [
                        ray_id,
                        receiver_feat.id(),
                        source_feat.id(), d_recTOsource, d_recTOsource_4m
                    ]

                    if settings['period_pts_gen'] == "True" or settings[
                            'period_roads_gen'] == "True":
                        if 'gen' in level_emi:
                            attributes.append(level_emi['gen'])
                            attributes.append(level_dir['gen'])
                        else:
                            attributes.append(None)
                            attributes.append(None)
                    if settings['period_pts_day'] == "True" or settings[
                            'period_roads_day'] == "True":
                        if 'day' in level_emi:
                            attributes.append(level_emi['day'])
                            attributes.append(level_dir['day'])
                        else:
                            attributes.append(None)
                            attributes.append(None)
                    if settings['period_pts_eve'] == "True" or settings[
                            'period_roads_eve'] == "True":
                        if 'eve' in level_emi:
                            attributes.append(level_emi['eve'])
                            attributes.append(level_dir['eve'])
                        else:
                            attributes.append(None)
                            attributes.append(None)
                    if settings['period_pts_nig'] == "True" or settings[
                            'period_roads_nig'] == "True":
                        if 'nig' in level_emi:
                            attributes.append(level_emi['nig'])
                            attributes.append(level_dir['nig'])
                        else:
                            attributes.append(None)
                            attributes.append(None)

                    ray.setAttributes(attributes)
                    rays_writer.addFeature(ray)
                    ray_id = ray_id + 1

        if receiver_feat.id() in recTOdiff_dict:

            diff_ids = recTOdiff_dict[receiver_feat.id()]

            for diff_id in diff_ids:

                diff_feat = diff_feat_all_dict[diff_id]

                if diff_feat.id() in diffTOsource_dict:

                    source_ids = diffTOsource_dict[diff_feat.id()]

                    for source_id in source_ids:

                        source_feat_value = source_feat_all_dict[source_id]

                        source_feat = source_feat_value['feat']

                        if receiver_feat.id() in recTOsource_dict:
                            source_ids = recTOsource_dict[receiver_feat.id()]
                            if source_feat.id() in source_ids:
                                shadow = 0
                            else:
                                shadow = 1
                        else:
                            shadow = 1

                        if shadow == 1:

                            ray_geometry = QgsGeometry.fromPolylineXY([
                                receiver_feat.geometry().asPoint(),
                                diff_feat.geometry().asPoint(),
                                source_feat.geometry().asPoint()
                            ])

                            d_recTOdiff = compute_distance(
                                receiver_feat.geometry().asPoint(),
                                diff_feat.geometry().asPoint())
                            d_diffTOsource = compute_distance(
                                diff_feat.geometry().asPoint(),
                                source_feat.geometry().asPoint())
                            d_recTOsource = compute_distance(
                                receiver_feat.geometry().asPoint(),
                                source_feat.geometry().asPoint())
                            d_recPLUSsource = d_recTOdiff + d_diffTOsource

                            if d_recPLUSsource <= research_ray:

                                feat_type = source_feat_value['type']
                                level_emi = source_feat_value['global']
                                level_emi_bands = source_feat_value['bands']
                                segment = source_feat_value['segment']

                                level_dif = {}
                                level_dif_bands = {}
                                level_atm_bands = {}

                                for key in list(level_emi_bands.keys()):
                                    if level_emi[key] > 0:

                                        level_dif_bands[
                                            key] = on_Acoustics.Diffraction(
                                                'CNOSSOS',
                                                level_emi_bands[key],
                                                d_diffTOsource, d_recTOsource,
                                                d_recTOdiff).level()
                                        level_atm_bands[
                                            key] = on_Acoustics.AtmosphericAbsorption(
                                                d_recPLUSsource, temperature,
                                                humidity, level_emi_bands[key]
                                            ).attenuation()
                                        level_dif_bands[
                                            key] = on_Acoustics.DiffBands(
                                                level_dif_bands[key],
                                                level_atm_bands[key])
                                        level_dif[
                                            key] = on_Acoustics.OctaveBandsToGlobal(
                                                level_dif_bands[key])

                                        # correction for the segment lenght
                                        if feat_type == 'road':
                                            if (settings['implementation_roads']
                                                    == 'POWER_R' or settings[
                                                        'implementation_roads']
                                                    == 'NMPB'):
                                                level_dif[key] = level_dif[
                                                    key] + 20 + 10 * log10(
                                                        float(segment)) + 3
                                            if settings[
                                                    'implementation_roads'] == 'CNOSSOS':
                                                level_dif[key] = level_dif[
                                                    key] + 10 * log10(
                                                        float(segment)) + 3

                                        receiver_point_lin_level[
                                            key] = receiver_point_lin_level[
                                                key] + 10**(level_dif[key] /
                                                            float(10))
                                    else:
                                        level_dif[key] = -1

                                if diff_rays_writer is not None:
                                    ray = QgsFeature()
                                    ray.setGeometry(ray_geometry)
                                    attributes = [
                                        diff_ray_id,
                                        receiver_feat.id(),
                                        diff_feat.id(),
                                        source_feat.id(), d_recTOdiff,
                                        d_diffTOsource, d_recTOsource
                                    ]

                                    if settings[
                                            'period_pts_gen'] == "True" or settings[
                                                'period_roads_gen'] == "True":
                                        if 'gen' in level_emi:
                                            attributes.append(level_emi['gen'])
                                            attributes.append(level_dif['gen'])
                                        else:
                                            attributes.append(None)
                                            attributes.append(None)
                                    if settings[
                                            'period_pts_day'] == "True" or settings[
                                                'period_roads_day'] == "True":
                                        if 'day' in level_emi:
                                            attributes.append(level_emi['day'])
                                            attributes.append(level_dif['day'])
                                        else:
                                            attributes.append(None)
                                            attributes.append(None)
                                    if settings[
                                            'period_pts_eve'] == "True" or settings[
                                                'period_roads_eve'] == "True":
                                        if 'eve' in level_emi:
                                            attributes.append(level_emi['eve'])
                                            attributes.append(level_dif['eve'])
                                        else:
                                            attributes.append(None)
                                            attributes.append(None)
                                    if settings[
                                            'period_pts_nig'] == "True" or settings[
                                                'period_roads_nig'] == "True":
                                        if 'nig' in level_emi:
                                            attributes.append(level_emi['nig'])
                                            attributes.append(level_dif['nig'])
                                        else:
                                            attributes.append(None)
                                            attributes.append(None)

                                    ray.setAttributes(attributes)
                                    diff_rays_writer.addFeature(ray)
                                    diff_ray_id = diff_ray_id + 1

        if settings['period_pts_gen'] == "True" or settings[
                'period_roads_gen'] == "True":
            if receiver_point_lin_level['gen'] > 0:
                Lgen = 10 * log10(receiver_point_lin_level['gen'])
                if Lgen < 0:
                    Lgen = 0
                receiver_feat_new_fields[level_field_index['gen']] = Lgen
            else:
                receiver_feat_new_fields[level_field_index['gen']] = -99

        Lday = 0
        Leve = 0
        Lnig = 0

        #addec contron on final data if negative set to zero
        if settings['period_pts_day'] == "True" or settings[
                'period_roads_day'] == "True":
            if receiver_point_lin_level['day'] > 0:
                Lday = 10 * log10(receiver_point_lin_level['day'])
                if Lday < 0:
                    Lday = 0
                receiver_feat_new_fields[level_field_index['day']] = Lday
            else:
                receiver_feat_new_fields[level_field_index['day']] = -99

        if settings['period_pts_eve'] == "True" or settings[
                'period_roads_eve'] == "True":
            if receiver_point_lin_level['eve'] > 0:
                Leve = 10 * log10(receiver_point_lin_level['eve'])
                if Leve < 0:
                    Leve = 0
                receiver_feat_new_fields[level_field_index['eve']] = Leve
            else:
                receiver_feat_new_fields[level_field_index['eve']] = -99

        if settings['period_pts_nig'] == "True" or settings[
                'period_roads_nig'] == "True":
            if receiver_point_lin_level['nig'] > 0:
                Lnig = 10 * log10(receiver_point_lin_level['nig'])
                if Lnig < 0:
                    Lnig = 0
                receiver_feat_new_fields[level_field_index['nig']] = Lnig

            else:
                receiver_feat_new_fields[level_field_index['nig']] = -99

        if settings['period_den'] == "True":
            receiver_feat_new_fields[
                level_field_index['den']] = on_Acoustics.Lden(
                    Lday, Leve, Lnig, int(settings['day_hours']),
                    int(settings['eve_hours']), int(settings['nig_hours']),
                    int(settings['day_penalty']), int(settings['eve_penalty']),
                    int(settings['nig_penalty']))
        receiver_feat_all_new_fields[
            receiver_feat.id()] = receiver_feat_new_fields

    progress_bars['calculate']['label'].setText('Done in ' +
                                                duration(time, datetime.now()))

    # fix_print_with_import
    print('calculate levels and, if selected, draw rays',
          datetime.now() - time)
    time = datetime.now()

    return receiver_feat_all_new_fields