def _write_to_ogr(destination_path, new_layer_name, driverName=None): """Writes features to new or existing OGR layer""" tmp_dir = QTemporaryDir() tmp_path = os.path.join(tmp_dir.path(), 'isochrone.json') with open(tmp_path, 'w+') as f: f.write(geojson) tmp_layer = QgsVectorLayer(tmp_path, 'tmp_isochrone', 'ogr') if not tmp_layer.isValid(): raise Exception( _('Cannot create temporary layer for isochrone result.')) # Note: shp attribute names are max 10 chars long save_options = QgsVectorFileWriter.SaveVectorOptions() if driverName is not None: save_options.driverName = driverName save_options.layerName = new_layer_name save_options.fileEncoding = 'utf-8' # This is nonsense to me: if the file does not exist the actionOnExistingFile # should be ignored instead of raising an error, probable QGIS bug if os.path.exists(destination_path): # Check if the layer already exists layer_exists = QgsVectorFileWriter.targetLayerExists( destination_path, new_layer_name) if layer_exists: raise Exception( _('Cannot save isochrone result to destination layer: layer already exists (use "qgis_layer_id" instead)!' )) save_options.actionOnExistingFile = QgsVectorFileWriter.CreateOrOverwriteLayer error_code, error_message = QgsVectorFileWriter.writeAsVectorFormatV2( tmp_layer, destination_path, project.qgis_project.transformContext(), save_options) if error_code != QgsVectorFileWriter.NoError: raise Exception( _('Cannot save isochrone result to destination layer: ') + error_message) layer_uri = destination_path if driverName != 'ESRI Shapefile': layer_uri += '|layername=' + new_layer_name provider = 'ogr' return layer_uri, provider
def testOverwriteLayer(self): """Tests writing a layer with a field value converter.""" ml = QgsVectorLayer('Point?field=firstfield:int', 'test', 'memory') provider = ml.dataProvider() ft = QgsFeature() ft.setAttributes([1]) provider.addFeatures([ft]) options = QgsVectorFileWriter.SaveVectorOptions() options.driverName = 'GPKG' options.layerName = 'test' filename = '/vsimem/out.gpkg' write_result, error_message = QgsVectorFileWriter.writeAsVectorFormat( ml, filename, options) self.assertEqual(write_result, QgsVectorFileWriter.NoError, error_message) ds = ogr.Open(filename, update=1) lyr = ds.GetLayerByName('test') self.assertIsNotNone(lyr) f = lyr.GetNextFeature() self.assertEqual(f['firstfield'], 1) ds.CreateLayer('another_layer') del f del lyr del ds caps = QgsVectorFileWriter.editionCapabilities(filename) self.assertTrue((caps & QgsVectorFileWriter.CanAddNewLayer)) self.assertTrue((caps & QgsVectorFileWriter.CanAppendToExistingLayer)) self.assertTrue( (caps & QgsVectorFileWriter.CanAddNewFieldsToExistingLayer)) self.assertTrue((caps & QgsVectorFileWriter.CanDeleteLayer)) self.assertTrue(QgsVectorFileWriter.targetLayerExists( filename, 'test')) self.assertFalse( QgsVectorFileWriter.areThereNewFieldsToCreate( filename, 'test', ml, [0])) # Test CreateOrOverwriteLayer ml = QgsVectorLayer('Point?field=firstfield:int', 'test', 'memory') provider = ml.dataProvider() ft = QgsFeature() ft.setAttributes([2]) provider.addFeatures([ft]) options = QgsVectorFileWriter.SaveVectorOptions() options.driverName = 'GPKG' options.layerName = 'test' options.actionOnExistingFile = QgsVectorFileWriter.CreateOrOverwriteLayer filename = '/vsimem/out.gpkg' write_result, error_message = QgsVectorFileWriter.writeAsVectorFormat( ml, filename, options) self.assertEqual(write_result, QgsVectorFileWriter.NoError, error_message) ds = ogr.Open(filename) lyr = ds.GetLayerByName('test') self.assertIsNotNone(lyr) f = lyr.GetNextFeature() self.assertEqual(f['firstfield'], 2) # another_layer should still exist self.assertIsNotNone(ds.GetLayerByName('another_layer')) del f del lyr del ds # Test CreateOrOverwriteFile ml = QgsVectorLayer('Point?field=firstfield:int', 'test', 'memory') provider = ml.dataProvider() ft = QgsFeature() ft.setAttributes([3]) provider.addFeatures([ft]) options = QgsVectorFileWriter.SaveVectorOptions() options.driverName = 'GPKG' options.layerName = 'test' filename = '/vsimem/out.gpkg' write_result, error_message = QgsVectorFileWriter.writeAsVectorFormat( ml, filename, options) self.assertEqual(write_result, QgsVectorFileWriter.NoError, error_message) ds = ogr.Open(filename) lyr = ds.GetLayerByName('test') self.assertIsNotNone(lyr) f = lyr.GetNextFeature() self.assertEqual(f['firstfield'], 3) # another_layer should no longer exist self.assertIsNone(ds.GetLayerByName('another_layer')) del f del lyr del ds # Test AppendToLayerNoNewFields ml = QgsVectorLayer('Point?field=firstfield:int&field=secondfield:int', 'test', 'memory') provider = ml.dataProvider() ft = QgsFeature() ft.setAttributes([4, -10]) provider.addFeatures([ft]) self.assertTrue( QgsVectorFileWriter.areThereNewFieldsToCreate( filename, 'test', ml, [0, 1])) options = QgsVectorFileWriter.SaveVectorOptions() options.driverName = 'GPKG' options.layerName = 'test' options.actionOnExistingFile = QgsVectorFileWriter.AppendToLayerNoNewFields filename = '/vsimem/out.gpkg' write_result, error_message = QgsVectorFileWriter.writeAsVectorFormat( ml, filename, options) self.assertEqual(write_result, QgsVectorFileWriter.NoError, error_message) ds = ogr.Open(filename) lyr = ds.GetLayerByName('test') self.assertEqual(lyr.GetLayerDefn().GetFieldCount(), 1) self.assertIsNotNone(lyr) f = lyr.GetNextFeature() self.assertEqual(f['firstfield'], 3) f = lyr.GetNextFeature() self.assertEqual(f['firstfield'], 4) del f del lyr del ds # Test AppendToLayerAddFields ml = QgsVectorLayer('Point?field=firstfield:int&field=secondfield:int', 'test', 'memory') provider = ml.dataProvider() ft = QgsFeature() ft.setAttributes([5, -1]) provider.addFeatures([ft]) self.assertTrue( QgsVectorFileWriter.areThereNewFieldsToCreate( filename, 'test', ml, [0, 1])) options = QgsVectorFileWriter.SaveVectorOptions() options.driverName = 'GPKG' options.layerName = 'test' options.actionOnExistingFile = QgsVectorFileWriter.AppendToLayerAddFields filename = '/vsimem/out.gpkg' write_result, error_message = QgsVectorFileWriter.writeAsVectorFormat( ml, filename, options) self.assertEqual(write_result, QgsVectorFileWriter.NoError, error_message) ds = ogr.Open(filename) lyr = ds.GetLayerByName('test') self.assertEqual(lyr.GetLayerDefn().GetFieldCount(), 2) self.assertIsNotNone(lyr) f = lyr.GetNextFeature() self.assertEqual(f['firstfield'], 3) if hasattr(f, "IsFieldSetAndNotNull"): # GDAL >= 2.2 self.assertFalse(f.IsFieldSetAndNotNull('secondfield')) else: self.assertFalse(f.IsFieldSet('secondfield')) f = lyr.GetNextFeature() self.assertEqual(f['firstfield'], 4) if hasattr(f, "IsFieldSetAndNotNull"): self.assertFalse(f.IsFieldSetAndNotNull('secondfield')) else: self.assertFalse(f.IsFieldSet('secondfield')) f = lyr.GetNextFeature() self.assertEqual(f['firstfield'], 5) self.assertEqual(f['secondfield'], -1) del f del lyr del ds gdal.Unlink(filename)
def testOverwriteLayer(self): """Tests writing a layer with a field value converter.""" ml = QgsVectorLayer('Point?field=firstfield:int', 'test', 'memory') provider = ml.dataProvider() ft = QgsFeature() ft.setAttributes([1]) provider.addFeatures([ft]) options = QgsVectorFileWriter.SaveVectorOptions() options.driverName = 'GPKG' options.layerName = 'test' filename = '/vsimem/out.gpkg' write_result, error_message = QgsVectorFileWriter.writeAsVectorFormat( ml, filename, options) self.assertEqual(write_result, QgsVectorFileWriter.NoError, error_message) ds = ogr.Open(filename, update=1) lyr = ds.GetLayerByName('test') self.assertIsNotNone(lyr) f = lyr.GetNextFeature() self.assertEqual(f['firstfield'], 1) ds.CreateLayer('another_layer') del f del lyr del ds caps = QgsVectorFileWriter.editionCapabilities(filename) self.assertTrue((caps & QgsVectorFileWriter.CanAddNewLayer)) self.assertTrue((caps & QgsVectorFileWriter.CanAppendToExistingLayer)) self.assertTrue((caps & QgsVectorFileWriter.CanAddNewFieldsToExistingLayer)) self.assertTrue((caps & QgsVectorFileWriter.CanDeleteLayer)) self.assertTrue(QgsVectorFileWriter.targetLayerExists(filename, 'test')) self.assertFalse(QgsVectorFileWriter.areThereNewFieldsToCreate(filename, 'test', ml, [0])) # Test CreateOrOverwriteLayer ml = QgsVectorLayer('Point?field=firstfield:int', 'test', 'memory') provider = ml.dataProvider() ft = QgsFeature() ft.setAttributes([2]) provider.addFeatures([ft]) options = QgsVectorFileWriter.SaveVectorOptions() options.driverName = 'GPKG' options.layerName = 'test' options.actionOnExistingFile = QgsVectorFileWriter.CreateOrOverwriteLayer filename = '/vsimem/out.gpkg' write_result, error_message = QgsVectorFileWriter.writeAsVectorFormat( ml, filename, options) self.assertEqual(write_result, QgsVectorFileWriter.NoError, error_message) ds = ogr.Open(filename) lyr = ds.GetLayerByName('test') self.assertIsNotNone(lyr) f = lyr.GetNextFeature() self.assertEqual(f['firstfield'], 2) # another_layer should still exist self.assertIsNotNone(ds.GetLayerByName('another_layer')) del f del lyr del ds # Test CreateOrOverwriteFile ml = QgsVectorLayer('Point?field=firstfield:int', 'test', 'memory') provider = ml.dataProvider() ft = QgsFeature() ft.setAttributes([3]) provider.addFeatures([ft]) options = QgsVectorFileWriter.SaveVectorOptions() options.driverName = 'GPKG' options.layerName = 'test' filename = '/vsimem/out.gpkg' write_result, error_message = QgsVectorFileWriter.writeAsVectorFormat( ml, filename, options) self.assertEqual(write_result, QgsVectorFileWriter.NoError, error_message) ds = ogr.Open(filename) lyr = ds.GetLayerByName('test') self.assertIsNotNone(lyr) f = lyr.GetNextFeature() self.assertEqual(f['firstfield'], 3) # another_layer should no longer exist self.assertIsNone(ds.GetLayerByName('another_layer')) del f del lyr del ds # Test AppendToLayerNoNewFields ml = QgsVectorLayer('Point?field=firstfield:int&field=secondfield:int', 'test', 'memory') provider = ml.dataProvider() ft = QgsFeature() ft.setAttributes([4, -10]) provider.addFeatures([ft]) self.assertTrue(QgsVectorFileWriter.areThereNewFieldsToCreate(filename, 'test', ml, [0, 1])) options = QgsVectorFileWriter.SaveVectorOptions() options.driverName = 'GPKG' options.layerName = 'test' options.actionOnExistingFile = QgsVectorFileWriter.AppendToLayerNoNewFields filename = '/vsimem/out.gpkg' write_result, error_message = QgsVectorFileWriter.writeAsVectorFormat( ml, filename, options) self.assertEqual(write_result, QgsVectorFileWriter.NoError, error_message) ds = ogr.Open(filename) lyr = ds.GetLayerByName('test') self.assertEqual(lyr.GetLayerDefn().GetFieldCount(), 1) self.assertIsNotNone(lyr) f = lyr.GetNextFeature() self.assertEqual(f['firstfield'], 3) f = lyr.GetNextFeature() self.assertEqual(f['firstfield'], 4) del f del lyr del ds # Test AppendToLayerAddFields ml = QgsVectorLayer('Point?field=firstfield:int&field=secondfield:int', 'test', 'memory') provider = ml.dataProvider() ft = QgsFeature() ft.setAttributes([5, -1]) provider.addFeatures([ft]) self.assertTrue(QgsVectorFileWriter.areThereNewFieldsToCreate(filename, 'test', ml, [0, 1])) options = QgsVectorFileWriter.SaveVectorOptions() options.driverName = 'GPKG' options.layerName = 'test' options.actionOnExistingFile = QgsVectorFileWriter.AppendToLayerAddFields filename = '/vsimem/out.gpkg' write_result, error_message = QgsVectorFileWriter.writeAsVectorFormat( ml, filename, options) self.assertEqual(write_result, QgsVectorFileWriter.NoError, error_message) ds = ogr.Open(filename) lyr = ds.GetLayerByName('test') self.assertEqual(lyr.GetLayerDefn().GetFieldCount(), 2) self.assertIsNotNone(lyr) f = lyr.GetNextFeature() self.assertEqual(f['firstfield'], 3) if hasattr(f, "IsFieldSetAndNotNull"): # GDAL >= 2.2 self.assertFalse(f.IsFieldSetAndNotNull('secondfield')) else: self.assertFalse(f.IsFieldSet('secondfield')) f = lyr.GetNextFeature() self.assertEqual(f['firstfield'], 4) if hasattr(f, "IsFieldSetAndNotNull"): self.assertFalse(f.IsFieldSetAndNotNull('secondfield')) else: self.assertFalse(f.IsFieldSet('secondfield')) f = lyr.GetNextFeature() self.assertEqual(f['firstfield'], 5) self.assertEqual(f['secondfield'], -1) del f del lyr del ds gdal.Unlink(filename)
def testOverwriteLayer(self): """Tests writing a layer with a field value converter.""" ml = QgsVectorLayer("Point?field=firstfield:int", "test", "memory") provider = ml.dataProvider() ft = QgsFeature() ft.setAttributes([1]) provider.addFeatures([ft]) options = QgsVectorFileWriter.SaveVectorOptions() options.driverName = "GPKG" options.layerName = "test" filename = "/vsimem/out.gpkg" write_result = QgsVectorFileWriter.writeAsVectorFormat(ml, filename, options) self.assertEqual(write_result, QgsVectorFileWriter.NoError) ds = ogr.Open(filename, update=1) lyr = ds.GetLayerByName("test") self.assertIsNotNone(lyr) f = lyr.GetNextFeature() self.assertEqual(f["firstfield"], 1) ds.CreateLayer("another_layer") del f del lyr del ds caps = QgsVectorFileWriter.editionCapabilities(filename) self.assertTrue((caps & QgsVectorFileWriter.CanAddNewLayer)) self.assertTrue((caps & QgsVectorFileWriter.CanAppendToExistingLayer)) self.assertTrue((caps & QgsVectorFileWriter.CanAddNewFieldsToExistingLayer)) self.assertTrue((caps & QgsVectorFileWriter.CanDeleteLayer)) self.assertTrue(QgsVectorFileWriter.targetLayerExists(filename, "test")) self.assertFalse(QgsVectorFileWriter.areThereNewFieldsToCreate(filename, "test", ml, [0])) # Test CreateOrOverwriteLayer ml = QgsVectorLayer("Point?field=firstfield:int", "test", "memory") provider = ml.dataProvider() ft = QgsFeature() ft.setAttributes([2]) provider.addFeatures([ft]) options = QgsVectorFileWriter.SaveVectorOptions() options.driverName = "GPKG" options.layerName = "test" options.actionOnExistingFile = QgsVectorFileWriter.CreateOrOverwriteLayer filename = "/vsimem/out.gpkg" write_result = QgsVectorFileWriter.writeAsVectorFormat(ml, filename, options) self.assertEqual(write_result, QgsVectorFileWriter.NoError) ds = ogr.Open(filename) lyr = ds.GetLayerByName("test") self.assertIsNotNone(lyr) f = lyr.GetNextFeature() self.assertEqual(f["firstfield"], 2) # another_layer should still exist self.assertIsNotNone(ds.GetLayerByName("another_layer")) del f del lyr del ds # Test CreateOrOverwriteFile ml = QgsVectorLayer("Point?field=firstfield:int", "test", "memory") provider = ml.dataProvider() ft = QgsFeature() ft.setAttributes([3]) provider.addFeatures([ft]) options = QgsVectorFileWriter.SaveVectorOptions() options.driverName = "GPKG" options.layerName = "test" filename = "/vsimem/out.gpkg" write_result = QgsVectorFileWriter.writeAsVectorFormat(ml, filename, options) self.assertEqual(write_result, QgsVectorFileWriter.NoError) ds = ogr.Open(filename) lyr = ds.GetLayerByName("test") self.assertIsNotNone(lyr) f = lyr.GetNextFeature() self.assertEqual(f["firstfield"], 3) # another_layer should no longer exist self.assertIsNone(ds.GetLayerByName("another_layer")) del f del lyr del ds # Test AppendToLayerNoNewFields ml = QgsVectorLayer("Point?field=firstfield:int&field=secondfield:int", "test", "memory") provider = ml.dataProvider() ft = QgsFeature() ft.setAttributes([4, -10]) provider.addFeatures([ft]) self.assertTrue(QgsVectorFileWriter.areThereNewFieldsToCreate(filename, "test", ml, [0, 1])) options = QgsVectorFileWriter.SaveVectorOptions() options.driverName = "GPKG" options.layerName = "test" options.actionOnExistingFile = QgsVectorFileWriter.AppendToLayerNoNewFields filename = "/vsimem/out.gpkg" write_result = QgsVectorFileWriter.writeAsVectorFormat(ml, filename, options) self.assertEqual(write_result, QgsVectorFileWriter.NoError) ds = ogr.Open(filename) lyr = ds.GetLayerByName("test") self.assertEqual(lyr.GetLayerDefn().GetFieldCount(), 1) self.assertIsNotNone(lyr) f = lyr.GetNextFeature() self.assertEqual(f["firstfield"], 3) f = lyr.GetNextFeature() self.assertEqual(f["firstfield"], 4) del f del lyr del ds # Test AppendToLayerAddFields ml = QgsVectorLayer("Point?field=firstfield:int&field=secondfield:int", "test", "memory") provider = ml.dataProvider() ft = QgsFeature() ft.setAttributes([5, -1]) provider.addFeatures([ft]) self.assertTrue(QgsVectorFileWriter.areThereNewFieldsToCreate(filename, "test", ml, [0, 1])) options = QgsVectorFileWriter.SaveVectorOptions() options.driverName = "GPKG" options.layerName = "test" options.actionOnExistingFile = QgsVectorFileWriter.AppendToLayerAddFields filename = "/vsimem/out.gpkg" write_result = QgsVectorFileWriter.writeAsVectorFormat(ml, filename, options) self.assertEqual(write_result, QgsVectorFileWriter.NoError) ds = ogr.Open(filename) lyr = ds.GetLayerByName("test") self.assertEqual(lyr.GetLayerDefn().GetFieldCount(), 2) self.assertIsNotNone(lyr) f = lyr.GetNextFeature() self.assertEqual(f["firstfield"], 3) self.assertFalse(f.IsFieldSet("secondfield")) f = lyr.GetNextFeature() self.assertEqual(f["firstfield"], 4) self.assertFalse(f.IsFieldSet("secondfield")) f = lyr.GetNextFeature() self.assertEqual(f["firstfield"], 5) self.assertEqual(f["secondfield"], -1) del f del lyr del ds gdal.Unlink(filename)
def convert_vector_layer( layer, # pylint: disable=too-many-locals,too-many-branches,too-many-statements project, data_folder, feedback, conversion_results: ConversionResults, change_source_on_error: bool = False, verbose_log=False): """ Converts a vector layer to a standard format """ if layer.customProperty('original_uri'): uri = layer.customProperty('original_uri') if verbose_log: feedback.pushDebugInfo( f'Original layer URI from custom properties is {uri}') else: uri = layer.source() if verbose_log: feedback.pushDebugInfo( 'Original layer URI not found in custom properties') source = QgsProviderRegistry.instance().decodeUri( layer.providerType(), uri) if verbose_log: feedback.pushInfo('') # older versions of QGIS didn't correctly strip out the subset from the layerName: if 'subset' not in source: if '|subset=' in source['layerName']: if verbose_log: feedback.pushDebugInfo( 'Stripping out subset string from layerName: {}'. format(source['layerName'])) layer_name = source['layerName'] parts = layer_name.split('|subset=') if len(parts) == 2: source['layerName'] = parts[0] if verbose_log: feedback.pushDebugInfo('Cleaned layer name: {}'.format( source['layerName'])) elif verbose_log: feedback.reportError('Failed to strip subset string!') elif '|subset=' in source['path']: path = source['path'] if verbose_log: feedback.pushDebugInfo( 'Stripping out subset string from path: {}'.format( source['path'])) parts = path.split('|subset=') if len(parts) == 2: source['path'] = parts[0] if verbose_log: feedback.pushDebugInfo('Cleaned path: {}'.format( source['path'])) elif verbose_log: feedback.reportError('Failed to strip subset string!') # convert to Geopackage source_uri = QgsProviderRegistry.instance().encodeUri( layer.providerType(), { 'path': source['path'], 'layerName': source['layerName'] }) # Sometimes the case varies in ArcMap documents, so when comparing to previously converted layers # we use a case-insensitive path/layername which is normalized result_key = QgsProviderRegistry.instance().encodeUri( layer.providerType(), { 'path': pathlib.Path( source['path']).resolve().as_posix().lower(), 'layerName': source['layerName'].lower() }) if verbose_log: feedback.pushDebugInfo('Converting layer: {} ( {} )'.format( source['path'], source['layerName'])) feedback.pushDebugInfo(f'Cached result key: {result_key}') provider_options = QgsDataProvider.ProviderOptions() provider_options.transformContext = project.transformContext() subset = layer.subsetString() # have we maybe already converted this layer?? if result_key in conversion_results.layer_map: previous_results = conversion_results.layer_map[result_key] if previous_results.get('error'): if verbose_log: feedback.pushDebugInfo( 'Already tried to convert this layer, but failed last time, skipping...' ) feedback.pushDebugInfo('Restoring stored URI') layer.setDataSource(uri, layer.name(), 'ogr', provider_options) else: if verbose_log: feedback.pushDebugInfo( 'Already converted this layer, reusing previous converted path: {} layername: {}' .format(previous_results['destPath'], previous_results['destLayer'])) layer.setDataSource( QgsProviderRegistry.instance().encodeUri( 'ogr', { 'path': previous_results['destPath'], 'layerName': previous_results['destLayer'] }), layer.name(), 'ogr', provider_options) if verbose_log: feedback.pushDebugInfo('new source {}'.format( layer.dataProvider().dataSourceUri())) if subset: if verbose_log: feedback.pushDebugInfo( 'Resetting subset string: {}'.format(subset)) layer.setSubsetString(subset) return previous_results source_layer = QgsVectorLayer(source_uri, '', layer.providerType()) path = pathlib.Path(source['path']) dest_file_name = ((pathlib.Path(data_folder) / path.stem).with_suffix('.gpkg')).as_posix() if dest_file_name not in conversion_results.created_databases: # about to use a new file -- let's double-check that it doesn't already exist. We don't want # to put layers into a database which we didn't make for this project counter = 1 while pathlib.Path(dest_file_name).exists(): counter += 1 dest_file_name = ((pathlib.Path(data_folder) / (path.stem + '_' + str(counter)) ).with_suffix('.gpkg')).as_posix() if dest_file_name in conversion_results.created_databases: break if verbose_log: feedback.pushDebugInfo( 'Creating new destination file {}'.format(dest_file_name)) elif verbose_log: feedback.pushDebugInfo( 'Reusing existing destination file {}'.format(dest_file_name)) # now this filename is ok for other layers to be stored in for this conversion conversion_results.created_databases.add(dest_file_name) layer_name_candidate = source['layerName'] counter = 1 while QgsVectorFileWriter.targetLayerExists(dest_file_name, layer_name_candidate): counter += 1 layer_name_candidate = '{}_{}'.format(source['layerName'], counter) if verbose_log: feedback.pushDebugInfo( 'Target layer name is {}'.format(layer_name_candidate)) if not source_layer.isValid(): if verbose_log: feedback.reportError('Source layer is not valid') if path.exists(): if verbose_log: feedback.pushDebugInfo('File path DOES exist') test_layer = QgsVectorLayer(path.as_posix()) sub_layers = test_layer.dataProvider().subLayers() feedback.pushDebugInfo( f'Readable layers from "{path.as_posix()}" are:') for sub_layer in sub_layers: _, name, count, geom_type, _, _ = sub_layer.split( QgsDataProvider.sublayerSeparator()) feedback.pushDebugInfo( f'- "{name}" ({count} features, geometry type {geom_type})' ) if path.exists() and path.suffix.lower() == '.mdb': try: source['layerName'].encode('ascii') except UnicodeDecodeError: error = f'''MDB layers with unicode names are not supported by QGIS -- cannot convert "{source['layerName']}"''' if verbose_log: feedback.reportError(error) feedback.pushDebugInfo('Restoring stored URI') layer.setDataSource(uri, layer.name(), 'ogr', provider_options) if subset: if verbose_log: feedback.pushDebugInfo( 'Resetting subset string: {}'.format(subset)) layer.setSubsetString(subset) conversion_results.layer_map[result_key] = {'error': error} return conversion_results.layer_map[result_key] # maybe a non-spatial table, which can't be read with GDAL < 3.2 source_layer = None if verbose_log: feedback.pushDebugInfo('Layer type is {}'.format( QgsWkbTypes.displayString(layer.wkbType()))) if layer.wkbType() == QgsWkbTypes.NoGeometry: if verbose_log: feedback.pushDebugInfo( 'Attempting fallback for non-spatial tables') try: source_layer = ConversionUtils.convert_mdb_table_to_memory_layer( str(path), source['layerName']) if verbose_log: feedback.pushDebugInfo('Fallback succeeded!') except Exception as e: # nopep8, pylint: disable=broad-except if verbose_log: feedback.reportError('Fallback failed: {}'.format( str(e))) source_layer = None elif verbose_log: feedback.reportError( 'Nothing else to try, conversion failed') if not source_layer: # here we fake things. We don't leave the original path to the mdb layer intact in the converted # project, as this can cause massive issues with QGIS as it attempts to re-read this path constantly # rather we "pretend" that the conversion was ok and set the broken layer's path to what the gpkg # converted version WOULD have been! It'll still be broken in the converted project (obviously), # but QGIS will no longer try endless to read the MDB and get all hung up on this... conversion_results.layer_map[result_key] = { 'sourcePath': source['path'], 'sourceLayer': source['layerName'], 'destPath': dest_file_name, 'destLayer': layer_name_candidate, 'error': 'Could not open {} ({}) for conversion'.format( source_uri, source['layerName']) } if change_source_on_error: if verbose_log: feedback.pushDebugInfo('Restoring stored URI') layer.setDataSource(uri, layer.name(), 'ogr', provider_options) if subset: if verbose_log: feedback.pushDebugInfo( 'Resetting subset string: {}'.format( subset)) layer.setSubsetString(subset) if verbose_log: feedback.pushDebugInfo('new source {}'.format( layer.dataProvider().dataSourceUri())) return conversion_results.layer_map[result_key] else: if not path.exists(): error = 'The referenced file {} does NOT exist!'.format( str(path)) else: error = 'The referenced file exists, but could not open {} ({}) for conversion'.format( source_uri, source['layerName']) if verbose_log: feedback.reportError(error) feedback.pushDebugInfo('Restoring stored URI') layer.setDataSource(uri, layer.name(), 'ogr', provider_options) if subset: if verbose_log: feedback.pushDebugInfo( 'Resetting subset string: {}'.format(subset)) layer.setSubsetString(subset) conversion_results.layer_map[result_key] = {'error': error} return conversion_results.layer_map[result_key] if verbose_log: feedback.pushDebugInfo('Source is valid, converting') options = QgsVectorFileWriter.SaveVectorOptions() options.layerName = layer_name_candidate options.actionOnExistingFile = QgsVectorFileWriter.CreateOrOverwriteLayer if pathlib.Path( dest_file_name).exists( ) else QgsVectorFileWriter.CreateOrOverwriteFile options.feedback = feedback error, error_message = QgsVectorFileWriter.writeAsVectorFormatV2( source_layer, dest_file_name, project.transformContext(), options) if error != QgsVectorFileWriter.NoError: if verbose_log: feedback.reportError('Failed: {}'.format(error_message)) feedback.pushDebugInfo('Restoring stored URI') layer.setDataSource(uri, layer.name(), 'ogr', provider_options) if subset: if verbose_log: feedback.pushDebugInfo( 'Resetting subset string: {}'.format(subset)) layer.setSubsetString(subset) conversion_results.layer_map[result_key] = {'error': error_message} return conversion_results.layer_map[result_key] if verbose_log: feedback.pushDebugInfo('Success!') provider_options = QgsDataProvider.ProviderOptions() provider_options.transformContext = project.transformContext() subset = layer.subsetString() layer.setDataSource( QgsProviderRegistry.instance().encodeUri( 'ogr', { 'path': dest_file_name, 'layerName': options.layerName }), layer.name(), 'ogr', provider_options) if subset: if verbose_log: feedback.pushDebugInfo( 'Resetting subset string: {}'.format(subset)) layer.setSubsetString(subset) if verbose_log: feedback.pushDebugInfo('new source {}'.format( layer.dataProvider().dataSourceUri())) conversion_results.layer_map[result_key] = { 'sourcePath': source['path'], 'sourceLayer': source['layerName'], 'destPath': dest_file_name, 'destLayer': options.layerName } return conversion_results.layer_map[result_key]