def create_osr(projname, **kwargs): """Conveniently supports the construction of osr spatial reference objects Currently, the following projection names (argument *projname*) are supported: **"aeqd": Azimuthal Equidistant** needs the following keyword arguments: - *lat_0* (latitude at projection center), - *lon_0* (longitude at projection center), - *x_0* (false Easting, also known as x-offset), - *y_0* (false Northing, also known as y-offset) **"dwd-radolan" : RADOLAN Composite Coordinate System** - no additional arguments needed. Polar stereographic projection used by the German Weather Service (DWD) for all Radar composite products. See the final report on the RADOLAN project :cite:`DWD2004` for details. Parameters ---------- projname : string "aeqd" or "dwd-radolan" kwargs : depends on projname - see above! Returns ------- output : osr.SpatialReference GDAL/OSR object defining projection Examples -------- See :ref:`/notebooks/basics/wradlib_workflow.ipynb#\ Georeferencing-and-Projection`. """ aeqd_wkt = ('PROJCS["unnamed",' 'GEOGCS["WGS 84",' 'DATUM["unknown",' 'SPHEROID["WGS84",6378137,298.257223563]],' 'PRIMEM["Greenwich",0],UNIT["degree",0.0174532925199433]],' 'PROJECTION["Azimuthal_Equidistant"],' 'PARAMETER["latitude_of_center", {0:-f}],' 'PARAMETER["longitude_of_center", {1:-f}],' 'PARAMETER["false_easting", {2:-f}],' 'PARAMETER["false_northing", {3:-f}],' 'UNIT["Meter",1]]') aeqd_wkt3 = ('PROJCS["unnamed",' 'GEOGCS["WGS 84",' 'DATUM["unknown",' 'SPHEROID["WGS84",6378137,298.257223563]],' 'PRIMEM["Greenwich",0],' 'UNIT["degree",0.0174532925199433]],' 'PROJECTION["Azimuthal_Equidistant"],' 'PARAMETER["latitude_of_center",{0:-f}],' 'PARAMETER["longitude_of_center",{1:-f}],' 'PARAMETER["false_easting",{2:-f}],' 'PARAMETER["false_northing",{3:-f}],' 'UNIT["Meter",1]]') radolan_wkt3 = ('PROJCS["Radolan Projection",' 'GEOGCS["Radolan Coordinate System",' 'DATUM["Radolan_Kugel",' 'SPHEROID["Erdkugel", 6370040, 0]],' 'PRIMEM["Greenwich", 0,' 'AUTHORITY["EPSG","8901"]],' 'UNIT["degree", 0.017453292519943295,' 'AUTHORITY["EPSG","9122"]]],' 'PROJECTION["Polar_Stereographic"],' 'PARAMETER["latitude_of_origin", 90],' 'PARAMETER["central_meridian", 10],' 'PARAMETER["scale_factor", {0:8.12f}],' 'PARAMETER["false_easting", 0],' 'PARAMETER["false_northing", 0],' 'UNIT["kilometre", 1000,' 'AUTHORITY["EPSG","9036"]],' 'AXIS["Easting",SOUTH],' 'AXIS["Northing",SOUTH]]') radolan_wkt = ('PROJCS["Radolan projection",' 'GEOGCS["Radolan Coordinate System",' 'DATUM["Radolan Kugel",' 'SPHEROID["Erdkugel", 6370040.0, 0.0]],' 'PRIMEM["Greenwich", 0.0, AUTHORITY["EPSG","8901"]],' 'UNIT["degree", 0.017453292519943295],' 'AXIS["Longitude", EAST],' 'AXIS["Latitude", NORTH]],' 'PROJECTION["polar_stereographic"],' 'PARAMETER["central_meridian", 10.0],' 'PARAMETER["latitude_of_origin", 90.0],' 'PARAMETER["scale_factor", {0:8.10f}],' 'PARAMETER["false_easting", 0.0],' 'PARAMETER["false_northing", 0.0],' 'UNIT["m*1000.0", 1000.0],' 'AXIS["X", EAST],' 'AXIS["Y", NORTH]]') proj = osr.SpatialReference() if projname == "aeqd": # Azimuthal Equidistant if gdal.VersionInfo()[0] >= '3': aeqd_wkt = aeqd_wkt3 if "x_0" in kwargs: proj.ImportFromWkt( aeqd_wkt.format(kwargs["lat_0"], kwargs["lon_0"], kwargs["x_0"], kwargs["y_0"])) else: proj.ImportFromWkt( aeqd_wkt.format(kwargs["lat_0"], kwargs["lon_0"], 0., 0.)) elif projname == "dwd-radolan": # DWD-RADOLAN polar stereographic projection scale = (1. + np.sin(np.radians(60.))) / (1. + np.sin(np.radians(90.))) if gdal.VersionInfo()[0] >= '3': radolan_wkt = radolan_wkt3.format(scale) else: radolan_wkt = radolan_wkt.format(scale) proj.ImportFromWkt(radolan_wkt) else: raise ValueError("No convenience support for projection %r, " "yet.\nYou need to create projection by using " "other means..." % projname) return proj
def _validate(ds, check_tiled=True, full_check=False): """Check if a file is a (Geo)TIFF with cloud optimized compatible structure. Args: ds: GDAL Dataset for the file to inspect. check_tiled: Set to False to ignore missing tiling. full_check: Set to TRUe to check tile/strip leader/trailer bytes. Might be slow on remote files Returns: A tuple, whose first element is an array of error messages (empty if there is no error), and the second element, a dictionary with the structure of the GeoTIFF file. Raises: ValidateCloudOptimizedGeoTIFFException: Unable to open the file or the file is not a Tiff. """ if int(gdal.VersionInfo('VERSION_NUM')) < 2020000: raise ValidateCloudOptimizedGeoTIFFException( 'GDAL 2.2 or above required') unicode_type = type(''.encode('utf-8').decode('utf-8')) if isinstance(ds, (str, unicode_type)): gdal.PushErrorHandler() ds = gdal.Open(ds) gdal.PopErrorHandler() if ds is None: raise ValidateCloudOptimizedGeoTIFFException( 'Invalid file : %s' % gdal.GetLastErrorMsg()) if ds.GetDriver().ShortName != 'GTiff': raise ValidateCloudOptimizedGeoTIFFException( 'The file is not a GeoTIFF') details = {} errors = [] warnings = [] filename = ds.GetDescription() main_band = ds.GetRasterBand(1) ovr_count = main_band.GetOverviewCount() filelist = ds.GetFileList() if filelist is not None and filename + '.ovr' in filelist: errors += [ 'Overviews found in external .ovr file. They should be internal' ] if main_band.XSize > 512 or main_band.YSize > 512: if check_tiled: block_size = main_band.GetBlockSize() if block_size[0] == main_band.XSize and block_size[0] > 1024: errors += [ 'The file is greater than 512xH or Wx512, but is not tiled' ] if ovr_count == 0: warnings += [ 'The file is greater than 512xH or Wx512, it is recommended ' 'to include internal overviews' ] ifd_offset = int(main_band.GetMetadataItem('IFD_OFFSET', 'TIFF')) ifd_offsets = [ifd_offset] block_order_row_major = False block_leader_size_as_uint4 = False block_trailer_last_4_bytes_repeated = False mask_interleaved_with_imagery = False if ifd_offset not in (8, 16): # Check if there is GDAL hidden structural metadata f = gdal.VSIFOpenL(filename, 'rb') if not f: raise ValidateCloudOptimizedGeoTIFFException("Cannot open file") signature = struct.unpack('B' * 4, gdal.VSIFReadL(4, 1, f)) bigtiff = signature in ((0x49, 0x49, 0x2B, 0x00), (0x4D, 0x4D, 0x00, 0x2B)) if bigtiff: expected_ifd_pos = 16 else: expected_ifd_pos = 8 gdal.VSIFSeekL(f, expected_ifd_pos, 0) pattern = "GDAL_STRUCTURAL_METADATA_SIZE=%06d bytes\n" % 0 got = gdal.VSIFReadL(len(pattern), 1, f).decode('LATIN1') if len(got) == len(pattern) and got.startswith( 'GDAL_STRUCTURAL_METADATA_SIZE='): size = int(got[len('GDAL_STRUCTURAL_METADATA_SIZE='):][0:6]) extra_md = gdal.VSIFReadL(size, 1, f).decode('LATIN1') block_order_row_major = 'BLOCK_ORDER=ROW_MAJOR' in extra_md block_leader_size_as_uint4 = 'BLOCK_LEADER=SIZE_AS_UINT4' in extra_md block_trailer_last_4_bytes_repeated = 'BLOCK_TRAILER=LAST_4_BYTES_REPEATED' in extra_md mask_interleaved_with_imagery = 'MASK_INTERLEAVED_WITH_IMAGERY=YES' in extra_md if 'KNOWN_INCOMPATIBLE_EDITION=YES' in extra_md: errors += [ "KNOWN_INCOMPATIBLE_EDITION=YES is declared in the file" ] expected_ifd_pos += len(pattern) + size expected_ifd_pos += expected_ifd_pos % 2 # IFD offset starts on a 2-byte boundary gdal.VSIFCloseL(f) if expected_ifd_pos != ifd_offsets[0]: errors += [ 'The offset of the main IFD should be %d. It is %d instead' % (expected_ifd_pos, ifd_offsets[0]) ] details['ifd_offsets'] = {} details['ifd_offsets']['main'] = ifd_offset for i in range(ovr_count): # Check that overviews are by descending sizes ovr_band = ds.GetRasterBand(1).GetOverview(i) if i == 0: if (ovr_band.XSize > main_band.XSize or ovr_band.YSize > main_band.YSize): errors += [ 'First overview has larger dimension than main band' ] else: prev_ovr_band = ds.GetRasterBand(1).GetOverview(i - 1) if (ovr_band.XSize > prev_ovr_band.XSize or ovr_band.YSize > prev_ovr_band.YSize): errors += [ 'Overview of index %d has larger dimension than ' 'overview of index %d' % (i, i - 1) ] if check_tiled: block_size = ovr_band.GetBlockSize() if block_size[0] == ovr_band.XSize and block_size[0] > 1024: errors += ['Overview of index %d is not tiled' % i] # Check that the IFD of descending overviews are sorted by increasing # offsets ifd_offset = int(ovr_band.GetMetadataItem('IFD_OFFSET', 'TIFF')) ifd_offsets.append(ifd_offset) details['ifd_offsets']['overview_%d' % i] = ifd_offset if ifd_offsets[-1] < ifd_offsets[-2]: if i == 0: errors += [ 'The offset of the IFD for overview of index %d is %d, ' 'whereas it should be greater than the one of the main ' 'image, which is at byte %d' % (i, ifd_offsets[-1], ifd_offsets[-2]) ] else: errors += [ 'The offset of the IFD for overview of index %d is %d, ' 'whereas it should be greater than the one of index %d, ' 'which is at byte %d' % (i, ifd_offsets[-1], i - 1, ifd_offsets[-2]) ] # Check that the imagery starts by the smallest overview and ends with # the main resolution dataset def _get_block_offset(band): blockxsize, blockysize = band.GetBlockSize() for y in range(int((band.YSize + blockysize - 1) / blockysize)): for x in range(int((band.XSize + blockxsize - 1) / blockxsize)): block_offset = band.GetMetadataItem( 'BLOCK_OFFSET_%d_%d' % (x, y), 'TIFF') if block_offset: return int(block_offset) return 0 block_offset = _get_block_offset(main_band) data_offsets = [block_offset] details['data_offsets'] = {} details['data_offsets']['main'] = block_offset for i in range(ovr_count): ovr_band = ds.GetRasterBand(1).GetOverview(i) block_offset = _get_block_offset(ovr_band) data_offsets.append(block_offset) details['data_offsets']['overview_%d' % i] = block_offset if data_offsets[-1] != 0 and data_offsets[-1] < ifd_offsets[-1]: if ovr_count > 0: errors += [ 'The offset of the first block of the smallest overview ' 'should be after its IFD' ] else: errors += [ 'The offset of the first block of the image should ' 'be after its IFD' ] for i in range(len(data_offsets) - 2, 0, -1): if data_offsets[i] != 0 and data_offsets[i] < data_offsets[i + 1]: errors += [ 'The offset of the first block of overview of index %d should ' 'be after the one of the overview of index %d' % (i - 1, i) ] if len(data_offsets) >= 2 and data_offsets[0] != 0 and data_offsets[ 0] < data_offsets[1]: errors += [ 'The offset of the first block of the main resolution image ' 'should be after the one of the overview of index %d' % (ovr_count - 1) ] if full_check and (block_order_row_major or block_leader_size_as_uint4 or block_trailer_last_4_bytes_repeated or mask_interleaved_with_imagery): f = gdal.VSIFOpenL(filename, 'rb') if not f: raise ValidateCloudOptimizedGeoTIFFException("Cannot open file") _full_check_band(f, 'Main resolution image', main_band, errors, block_order_row_major, block_leader_size_as_uint4, block_trailer_last_4_bytes_repeated, mask_interleaved_with_imagery) if main_band.GetMaskFlags() == gdal.GMF_PER_DATASET and \ (filename + '.msk') not in ds.GetFileList(): _full_check_band(f, 'Mask band of main resolution image', main_band.GetMaskBand(), errors, block_order_row_major, block_leader_size_as_uint4, block_trailer_last_4_bytes_repeated, False) for i in range(ovr_count): ovr_band = ds.GetRasterBand(1).GetOverview(i) _full_check_band(f, 'Overview %d' % i, ovr_band, errors, block_order_row_major, block_leader_size_as_uint4, block_trailer_last_4_bytes_repeated, mask_interleaved_with_imagery) if ovr_band.GetMaskFlags() == gdal.GMF_PER_DATASET and \ (filename + '.msk') not in ds.GetFileList(): _full_check_band(f, 'Mask band of overview %d' % i, ovr_band.GetMaskBand(), errors, block_order_row_major, block_leader_size_as_uint4, block_trailer_last_4_bytes_repeated, False) gdal.VSIFCloseL(f) return warnings, errors, details
def testFidSupport(self): # We do not use @unittest.expectedFailure since the test might actually succeed # on Linux 64bit with GDAL 1.11, where "long" is 64 bit... # GDAL 2.0 is guaranteed to properly support it on all platforms version_num = int(gdal.VersionInfo('VERSION_NUM')) if version_num < GDAL_COMPUTE_VERSION(2, 0, 0): return tmpfile = os.path.join(self.basetestpath, 'testFidSupport.sqlite') ds = ogr.GetDriverByName('SQLite').CreateDataSource(tmpfile) lyr = ds.CreateLayer('test', geom_type=ogr.wkbPoint, options=['FID=fid']) lyr.CreateField(ogr.FieldDefn('strfield', ogr.OFTString)) lyr.CreateField(ogr.FieldDefn('intfield', ogr.OFTInteger)) f = ogr.Feature(lyr.GetLayerDefn()) f.SetFID(12) f.SetField(0, 'foo') f.SetField(1, 123) lyr.CreateFeature(f) f = None ds = None vl = QgsVectorLayer('{}'.format(tmpfile), 'test', 'ogr') self.assertEqual(len(vl.fields()), 3) got = [(f.attribute('fid'), f.attribute('strfield'), f.attribute('intfield')) for f in vl.getFeatures()] self.assertEqual(got, [(12, 'foo', 123)]) got = [(f.attribute('fid'), f.attribute('strfield')) for f in vl.getFeatures(QgsFeatureRequest().setFilterExpression("strfield = 'foo'"))] self.assertEqual(got, [(12, 'foo')]) got = [(f.attribute('fid'), f.attribute('strfield')) for f in vl.getFeatures(QgsFeatureRequest().setFilterExpression("fid = 12"))] self.assertEqual(got, [(12, 'foo')]) result = [f['strfield'] for f in vl.dataProvider().getFeatures(QgsFeatureRequest().setSubsetOfAttributes(['strfield'], vl.dataProvider().fields()))] self.assertEqual(result, ['foo']) result = [f['fid'] for f in vl.dataProvider().getFeatures(QgsFeatureRequest().setSubsetOfAttributes(['fid'], vl.dataProvider().fields()))] self.assertEqual(result, [12]) # Test that when the 'fid' field is not set, regular insertion is done f = QgsFeature() f.setFields(vl.fields()) f.setAttributes([None, 'automatic_id']) (res, out_f) = vl.dataProvider().addFeatures([f]) self.assertEqual(out_f[0].id(), 13) self.assertEqual(out_f[0].attribute('fid'), 13) self.assertEqual(out_f[0].attribute('strfield'), 'automatic_id') # Test that when the 'fid' field is set, it is really used to set the id f = QgsFeature() f.setFields(vl.fields()) f.setAttributes([9876543210, 'bar']) (res, out_f) = vl.dataProvider().addFeatures([f]) self.assertEqual(out_f[0].id(), 9876543210) self.assertEqual(out_f[0].attribute('fid'), 9876543210) self.assertEqual(out_f[0].attribute('strfield'), 'bar') got = [(f.attribute('fid'), f.attribute('strfield')) for f in vl.getFeatures(QgsFeatureRequest().setFilterExpression("fid = 9876543210"))] self.assertEqual(got, [(9876543210, 'bar')]) self.assertTrue(vl.dataProvider().changeAttributeValues({9876543210: {1: 'baz'}})) got = [(f.attribute('fid'), f.attribute('strfield')) for f in vl.getFeatures(QgsFeatureRequest().setFilterExpression("fid = 9876543210"))] self.assertEqual(got, [(9876543210, 'baz')]) self.assertTrue(vl.dataProvider().changeAttributeValues({9876543210: {0: 9876543210, 1: 'baw'}})) got = [(f.attribute('fid'), f.attribute('strfield')) for f in vl.getFeatures(QgsFeatureRequest().setFilterExpression("fid = 9876543210"))] self.assertEqual(got, [(9876543210, 'baw')]) # Not allowed: changing the fid regular field self.assertTrue(vl.dataProvider().changeAttributeValues({9876543210: {0: 12, 1: 'baw'}})) got = [(f.attribute('fid'), f.attribute('strfield')) for f in vl.getFeatures(QgsFeatureRequest().setFilterExpression("fid = 9876543210"))] self.assertEqual(got, [(9876543210, 'baw')]) # Cannot delete fid self.assertFalse(vl.dataProvider().deleteAttributes([0])) # Delete first "genuine" attribute self.assertTrue(vl.dataProvider().deleteAttributes([1])) got = [(f.attribute('fid'), f.attribute('intfield')) for f in vl.dataProvider().getFeatures(QgsFeatureRequest().setFilterExpression("fid = 12"))] self.assertEqual(got, [(12, 123)])
class PyQgsOGRProvider(unittest.TestCase): @classmethod def setUpClass(cls): """Run before all tests""" # Create test layer cls.basetestpath = tempfile.mkdtemp() cls.datasource = os.path.join(cls.basetestpath, 'test.csv') with open(cls.datasource, 'wt') as f: f.write('id,WKT\n') f.write('1,POINT(2 49)\n') cls.dirs_to_cleanup = [cls.basetestpath] @classmethod def tearDownClass(cls): """Run after all tests""" for dirname in cls.dirs_to_cleanup: shutil.rmtree(dirname, True) def testUpdateMode(self): vl = QgsVectorLayer('{}|layerid=0'.format(self.datasource), 'test', 'ogr') self.assertTrue(vl.isValid()) caps = vl.dataProvider().capabilities() self.assertTrue(caps & QgsVectorDataProvider.AddFeatures) self.assertEqual(vl.dataProvider().property("_debug_open_mode"), "read-write") # No-op self.assertTrue(vl.dataProvider().enterUpdateMode()) self.assertEqual(vl.dataProvider().property("_debug_open_mode"), "read-write") # No-op self.assertTrue(vl.dataProvider().leaveUpdateMode()) self.assertEqual(vl.dataProvider().property("_debug_open_mode"), "read-write") def testGeometryTypeKnownAtSecondFeature(self): datasource = os.path.join(self.basetestpath, 'testGeometryTypeKnownAtSecondFeature.csv') with open(datasource, 'wt') as f: f.write('id,WKT\n') f.write('1,\n') f.write('2,POINT(2 49)\n') vl = QgsVectorLayer('{}|layerid=0'.format(datasource), 'test', 'ogr') self.assertTrue(vl.isValid()) self.assertEqual(vl.wkbType(), QgsWkbTypes.Point) @unittest.expectedFailure(int(gdal.VersionInfo('VERSION_NUM')) < GDAL_COMPUTE_VERSION(2, 0, 0)) def testMixOfPolygonCurvePolygon(self): datasource = os.path.join(self.basetestpath, 'testMixOfPolygonCurvePolygon.csv') with open(datasource, 'wt') as f: f.write('id,WKT\n') f.write('1,"POLYGON((0 0,0 1,1 1,0 0))"\n') f.write('2,"CURVEPOLYGON((0 0,0 1,1 1,0 0))"\n') f.write('3,"MULTIPOLYGON(((0 0,0 1,1 1,0 0)))"\n') f.write('4,"MULTISURFACE(((0 0,0 1,1 1,0 0)))"\n') vl = QgsVectorLayer('{}|layerid=0'.format(datasource), 'test', 'ogr') self.assertTrue(vl.isValid()) self.assertEqual(len(vl.dataProvider().subLayers()), 1) self.assertEqual(vl.dataProvider().subLayers()[0], '0:testMixOfPolygonCurvePolygon:4:CurvePolygon') @unittest.expectedFailure(int(gdal.VersionInfo('VERSION_NUM')) < GDAL_COMPUTE_VERSION(2, 0, 0)) def testMixOfLineStringCompoundCurve(self): datasource = os.path.join(self.basetestpath, 'testMixOfLineStringCompoundCurve.csv') with open(datasource, 'wt') as f: f.write('id,WKT\n') f.write('1,"LINESTRING(0 0,0 1)"\n') f.write('2,"COMPOUNDCURVE((0 0,0 1))"\n') f.write('3,"MULTILINESTRING((0 0,0 1))"\n') f.write('4,"MULTICURVE((0 0,0 1))"\n') f.write('5,"CIRCULARSTRING(0 0,1 1,2 0)"\n') vl = QgsVectorLayer('{}|layerid=0'.format(datasource), 'test', 'ogr') self.assertTrue(vl.isValid()) self.assertEqual(len(vl.dataProvider().subLayers()), 1) self.assertEqual(vl.dataProvider().subLayers()[0], '0:testMixOfLineStringCompoundCurve:5:CompoundCurve') def testGpxElevation(self): # GPX without elevation data datasource = os.path.join(TEST_DATA_DIR, 'noelev.gpx') vl = QgsVectorLayer('{}|layername=routes'.format(datasource), 'test', 'ogr') self.assertTrue(vl.isValid()) f = next(vl.getFeatures()) self.assertEqual(f.geometry().geometry().wkbType(), QgsWkbTypes.LineString) # GPX with elevation data datasource = os.path.join(TEST_DATA_DIR, 'elev.gpx') vl = QgsVectorLayer('{}|layername=routes'.format(datasource), 'test', 'ogr') self.assertTrue(vl.isValid()) f = next(vl.getFeatures()) self.assertEqual(f.geometry().geometry().wkbType(), QgsWkbTypes.LineString25D) self.assertEqual(f.geometry().geometry().pointN(0).z(), 1) self.assertEqual(f.geometry().geometry().pointN(1).z(), 2) self.assertEqual(f.geometry().geometry().pointN(2).z(), 3) def testNoDanglingFileDescriptorAfterCloseVariant1(self): ''' Test that when closing the provider all file handles are released ''' datasource = os.path.join(self.basetestpath, 'testNoDanglingFileDescriptorAfterCloseVariant1.csv') with open(datasource, 'wt') as f: f.write('id,WKT\n') f.write('1,\n') f.write('2,POINT(2 49)\n') vl = QgsVectorLayer('{}|layerid=0'.format(datasource), 'test', 'ogr') self.assertTrue(vl.isValid()) # The iterator will take one extra connection myiter = vl.getFeatures() # Consume one feature but the iterator is still opened f = next(myiter) self.assertTrue(f.isValid()) if sys.platform.startswith('linux'): self.assertEqual(count_opened_filedescriptors(datasource), 2) # Should release one file descriptor del vl # Non portable, but Windows testing is done with trying to unlink if sys.platform.startswith('linux'): self.assertEqual(count_opened_filedescriptors(datasource), 1) f = next(myiter) self.assertTrue(f.isValid()) # Should release one file descriptor del myiter # Non portable, but Windows testing is done with trying to unlink if sys.platform.startswith('linux'): self.assertEqual(count_opened_filedescriptors(datasource), 0) # Check that deletion works well (can only fail on Windows) os.unlink(datasource) self.assertFalse(os.path.exists(datasource)) def testNoDanglingFileDescriptorAfterCloseVariant2(self): ''' Test that when closing the provider all file handles are released ''' datasource = os.path.join(self.basetestpath, 'testNoDanglingFileDescriptorAfterCloseVariant2.csv') with open(datasource, 'wt') as f: f.write('id,WKT\n') f.write('1,\n') f.write('2,POINT(2 49)\n') vl = QgsVectorLayer('{}|layerid=0'.format(datasource), 'test', 'ogr') self.assertTrue(vl.isValid()) # Consume all features. myiter = vl.getFeatures() for feature in myiter: pass # The iterator is closed, but the corresponding connection still not closed if sys.platform.startswith('linux'): self.assertEqual(count_opened_filedescriptors(datasource), 2) # Should release one file descriptor del vl # Non portable, but Windows testing is done with trying to unlink if sys.platform.startswith('linux'): self.assertEqual(count_opened_filedescriptors(datasource), 0) # Check that deletion works well (can only fail on Windows) os.unlink(datasource) self.assertFalse(os.path.exists(datasource)) def testGeometryCollection(self): ''' Test that we can at least retrieves attribute of features with geometry collection ''' datasource = os.path.join(self.basetestpath, 'testGeometryCollection.csv') with open(datasource, 'wt') as f: f.write('id,WKT\n') f.write('1,POINT Z(2 49 0)\n') f.write('2,GEOMETRYCOLLECTION Z (POINT Z (2 49 0))\n') vl = QgsVectorLayer('{}|layerid=0|geometrytype=GeometryCollection'.format(datasource), 'test', 'ogr') self.assertTrue(vl.isValid()) self.assertTrue(vl.featureCount(), 1) values = [f['id'] for f in vl.getFeatures()] self.assertEqual(values, ['2']) del vl os.unlink(datasource) self.assertFalse(os.path.exists(datasource))
def readableVersion(): return gdal.VersionInfo('RELEASE_NAME')
def version(self): return Version(gdal.VersionInfo("RELEASE_NAME"))
class TestPyQgsProviderConnectionGpkg(unittest.TestCase, TestPyQgsProviderConnectionBase): # Provider test cases must define the string URI for the test uri = '' # Provider test cases must define the provider name (e.g. "postgres" or "ogr") providerKey = 'ogr' # Provider test cases can define a slowQuery for executeSql cancellation test # Note: GDAL does not support GDALDatasetExecuteSQL interruption, so # let's disable this test for the time being slowQuery___disabled = """ WITH RECURSIVE r(i) AS ( VALUES(0) UNION ALL SELECT i FROM r LIMIT 10000000 ) SELECT i FROM r WHERE i = 1; """ # Provider test cases can define a schema and table name for SQL query layers test sqlVectorLayerSchema = '' sqlVectorLayerTable = 'cdb_lines' @classmethod def setUpClass(cls): """Run before all tests""" TestPyQgsProviderConnectionBase.setUpClass() gpkg_original_path = '{}/qgis_server/test_project_wms_grouped_layers.gpkg'.format( TEST_DATA_DIR) cls.temp_dir = QTemporaryDir() cls.gpkg_path = '{}/test_project_wms_grouped_layers.gpkg'.format( cls.temp_dir.path()) shutil.copy(gpkg_original_path, cls.gpkg_path) gpkg_domains_original_path = '{}/domains.gpkg'.format(TEST_DATA_DIR) cls.gpkg_domains_path = '{}/domains.gpkg'.format(cls.temp_dir.path()) shutil.copy(gpkg_domains_original_path, cls.gpkg_domains_path) vl = QgsVectorLayer('{}|layername=cdb_lines'.format(cls.gpkg_path), 'test', 'ogr') assert vl.isValid() cls.uri = cls.gpkg_path def test_gpkg_connections_from_uri(self): """Create a connection from a layer uri and retrieve it""" md = QgsProviderRegistry.instance().providerMetadata('ogr') vl = QgsVectorLayer('{}|layername=cdb_lines'.format(self.gpkg_path), 'test', 'ogr') conn = md.createConnection(vl.dataProvider().dataSourceUri(), {}) self.assertEqual(conn.uri(), self.gpkg_path) def test_gpkg_table_uri(self): """Create a connection from a layer uri and create a table URI""" md = QgsProviderRegistry.instance().providerMetadata('ogr') conn = md.createConnection(self.uri, {}) self.assertEqual(conn.tableUri('', 'cdb_lines'), '{}|layername=cdb_lines'.format(self.gpkg_path)) vl = QgsVectorLayer(conn.tableUri('', 'cdb_lines'), 'lines', 'ogr') self.assertTrue(vl.isValid()) # Test table(), throws if not found conn.table('', 'osm') conn.table('', 'cdb_lines') self.assertEqual(conn.tableUri('', 'osm'), "GPKG:%s:osm" % self.uri) rl = QgsRasterLayer(conn.tableUri('', 'osm'), 'r', 'gdal') self.assertTrue(rl.isValid()) def test_gpkg_connections(self): """Create some connections and retrieve them""" md = QgsProviderRegistry.instance().providerMetadata('ogr') conn = md.createConnection(self.uri, {}) md.saveConnection(conn, 'qgis_test1') # Retrieve capabilities capabilities = conn.capabilities() self.assertTrue( bool(capabilities & QgsAbstractDatabaseProviderConnection.Tables)) self.assertFalse( bool(capabilities & QgsAbstractDatabaseProviderConnection.Schemas)) self.assertTrue( bool(capabilities & QgsAbstractDatabaseProviderConnection.CreateVectorTable)) self.assertTrue( bool(capabilities & QgsAbstractDatabaseProviderConnection.DropVectorTable)) self.assertTrue( bool(capabilities & QgsAbstractDatabaseProviderConnection.RenameVectorTable)) self.assertFalse( bool(capabilities & QgsAbstractDatabaseProviderConnection.RenameRasterTable)) crs = QgsCoordinateReferenceSystem.fromEpsgId(3857) typ = QgsWkbTypes.LineString conn.createVectorTable('', 'myNewAspatialTable', QgsFields(), QgsWkbTypes.NoGeometry, crs, True, {}) conn.createVectorTable('', 'myNewTable', QgsFields(), typ, crs, True, {}) # Check filters and special cases table_names = self._table_names( conn.tables('', QgsAbstractDatabaseProviderConnection.Raster)) self.assertTrue('osm' in table_names) self.assertFalse('myNewTable' in table_names) self.assertFalse('myNewAspatialTable' in table_names) table_names = self._table_names( conn.tables('', QgsAbstractDatabaseProviderConnection.View)) self.assertFalse('osm' in table_names) self.assertFalse('myNewTable' in table_names) self.assertFalse('myNewAspatialTable' in table_names) table_names = self._table_names( conn.tables('', QgsAbstractDatabaseProviderConnection.Aspatial)) self.assertFalse('osm' in table_names) self.assertFalse('myNewTable' in table_names) self.assertTrue('myNewAspatialTable' in table_names) def test_gpkg_fields(self): """Test fields""" md = QgsProviderRegistry.instance().providerMetadata('ogr') conn = md.createConnection(self.uri, {}) fields = conn.fields('', 'cdb_lines') table_info = conn.table('', 'cdb_lines') self.assertIn(table_info.geometryColumn(), fields.names()) self.assertIn(table_info.primaryKeyColumns()[0], fields.names()) self.assertEqual( fields.names(), ['fid', 'id', 'typ', 'name', 'ortsrat', 'id_long', 'geom']) @unittest.skipIf( int(gdal.VersionInfo('VERSION_NUM')) < GDAL_COMPUTE_VERSION(3, 5, 0), "GDAL 3.5 required") def test_gpkg_field_domain_names(self): """ Test retrieving field domain names """ md = QgsProviderRegistry.instance().providerMetadata('ogr') conn = md.createConnection(self.gpkg_domains_path, {}) domain_names = conn.fieldDomainNames() self.assertCountEqual(domain_names, [ 'enum_domain', 'glob_domain', 'range_domain_int', 'range_domain_int64', 'range_domain_real', 'range_domain_real_inf' ]) @unittest.skipIf( int(gdal.VersionInfo('VERSION_NUM')) < GDAL_COMPUTE_VERSION(3, 3, 0), "GDAL 3.3 required") def test_gpkg_field_domain(self): """ Test retrieving field domain """ md = QgsProviderRegistry.instance().providerMetadata('ogr') conn = md.createConnection(self.gpkg_domains_path, {}) domain = conn.fieldDomain('enum_domain') self.assertEqual(domain.type(), Qgis.FieldDomainType.Coded) self.assertEqual(domain.name(), 'enum_domain') domain = conn.fieldDomain('range_domain_int') self.assertEqual(domain.type(), Qgis.FieldDomainType.Range) self.assertEqual(domain.name(), 'range_domain_int') self.assertEqual(domain.minimum(), 1) self.assertEqual(domain.maximum(), 2) domain = conn.fieldDomain('glob_domain') self.assertEqual(domain.type(), Qgis.FieldDomainType.Glob) self.assertEqual(domain.name(), 'glob_domain') self.assertEqual(domain.glob(), '*') @unittest.skipIf( int(gdal.VersionInfo('VERSION_NUM')) < GDAL_COMPUTE_VERSION(3, 3, 0), "GDAL 3.3 required") def test_gpkg_field_domain_create(self): """ Test creating field domains """ gpkg_domains_original_path = '{}/domains.gpkg'.format(TEST_DATA_DIR) temp_domains_path = '{}/domains_create.gpkg'.format( self.temp_dir.path()) shutil.copy(gpkg_domains_original_path, temp_domains_path) md = QgsProviderRegistry.instance().providerMetadata('ogr') conn = md.createConnection(temp_domains_path, {}) domain = QgsRangeFieldDomain('my new domain', 'my new domain desc', QVariant.Int, 5, True, 15, True) conn.addFieldDomain(domain, '') # try retrieving result del conn conn = md.createConnection(temp_domains_path, {}) res = conn.fieldDomain('my new domain') self.assertEqual(res.type(), Qgis.FieldDomainType.Range) self.assertEqual(res.name(), 'my new domain') self.assertEqual(res.minimum(), 5) self.assertEqual(res.maximum(), 15) # try adding another with a duplicate name, should fail with self.assertRaises(QgsProviderConnectionException) as e: conn.addFieldDomain(domain, '') self.assertEqual( str(e.exception), 'Could not create field domain: A domain of identical name already exists' ) domain = QgsGlobFieldDomain('my new glob domain', 'my new glob desc', QVariant.String, '*aaabc*') conn.addFieldDomain(domain, '') # try retrieving result del conn conn = md.createConnection(temp_domains_path, {}) res = conn.fieldDomain('my new glob domain') self.assertEqual(res.type(), Qgis.FieldDomainType.Glob) self.assertEqual(res.name(), 'my new glob domain') self.assertEqual(res.description(), 'my new glob desc') self.assertEqual(res.glob(), '*aaabc*') # coded value domain = QgsCodedFieldDomain( 'my new coded domain', 'my new coded desc', QVariant.String, [QgsCodedValue('a', 'aa'), QgsCodedValue('b', 'bb')]) conn.addFieldDomain(domain, '') # try retrieving result del conn conn = md.createConnection(temp_domains_path, {}) res = conn.fieldDomain('my new coded domain') self.assertEqual(res.type(), Qgis.FieldDomainType.Coded) self.assertEqual(res.name(), 'my new coded domain') self.assertEqual(res.description(), 'my new coded desc') self.assertCountEqual( res.values(), [QgsCodedValue('a', 'aa'), QgsCodedValue('b', 'bb')]) @unittest.skipIf( int(gdal.VersionInfo('VERSION_NUM')) < GDAL_COMPUTE_VERSION(3, 3, 0), "GDAL 3.3 required") def test_gpkg_field_domain_set(self): """ Test setting field domains """ gpkg_domains_original_path = '{}/bug_17878.gpkg'.format(TEST_DATA_DIR) temp_domains_path = '{}/domain_set.gpkg'.format(self.temp_dir.path()) shutil.copy(gpkg_domains_original_path, temp_domains_path) md = QgsProviderRegistry.instance().providerMetadata('ogr') conn = md.createConnection(temp_domains_path, {}) domain = QgsRangeFieldDomain('my new domain', 'my new domain desc', QVariant.Int, 5, True, 15, True) conn.addFieldDomain(domain, '') # field doesn't exist with self.assertRaises(QgsProviderConnectionException): conn.setFieldDomainName('xxx', '', 'bug_17878', 'my new domain') conn.setFieldDomainName('int_field', '', 'bug_17878', 'my new domain') # try retrieving result del conn conn = md.createConnection(temp_domains_path, {}) fields = conn.fields('', 'bug_17878') field = fields.field('int_field') self.assertEqual(field.constraints().domainName(), 'my new domain') def test_create_vector_layer(self): """Test query layers""" md = QgsProviderRegistry.instance().providerMetadata('ogr') conn = md.createConnection(self.uri, {}) options = QgsAbstractDatabaseProviderConnection.SqlVectorLayerOptions() options.sql = 'SELECT fid, name, geom FROM cdb_lines WHERE name LIKE \'S%\' LIMIT 2' vl = conn.createSqlVectorLayer(options) self.assertTrue(vl.isValid()) self.assertEqual(vl.geometryType(), QgsWkbTypes.PolygonGeometry) features = [f for f in vl.getFeatures()] self.assertEqual(len(features), 2) self.assertEqual(features[0].attributes(), [8, 'Sülfeld']) def test_execute_sql_pk_geoms(self): """OGR hides fid and geom from attributes, check if we can still get them""" md = QgsProviderRegistry.instance().providerMetadata('ogr') conn = md.createConnection(self.uri, {}) # Check errors with self.assertRaises(QgsProviderConnectionException): sql = 'SELECT not_exists, name, geom FROM cdb_lines WHERE name LIKE \'S%\' LIMIT 2' results = conn.executeSql(sql) sql = 'SELECT fid, name, geom FROM cdb_lines WHERE name LIKE \'S%\' LIMIT 2' results = conn.executeSql(sql) self.assertEqual(results[0][:2], [8, 'Sülfeld']) self.assertEqual(results[1][:2], [16, 'Steimker Berg']) self.assertEqual(results[0][2][:20], 'Polygon ((612694.674') self.assertEqual(results[1][2][:20], 'Polygon ((622042.427') sql = 'SELECT name, st_astext(geom) FROM cdb_lines WHERE name LIKE \'S%\' LIMIT 2' results = conn.executeSql(sql) self.assertEqual(results[0], [ 'Sülfeld', 'POLYGON((612694.674 5807839.658, 612668.715 5808176.815, 612547.354 5808414.452, 612509.527 5808425.73, 612522.932 5808473.02, 612407.901 5808519.082, 612505.836 5808632.763, 612463.449 5808781.115, 612433.57 5808819.061, 612422.685 5808980.281999, 612473.423 5808995.424999, 612333.856 5809647.731, 612307.316 5809781.446, 612267.099 5809852.803, 612308.221 5810040.995, 613920.397 5811079.478, 613947.16 5811129.3, 614022.726 5811154.456, 614058.436 5811260.36, 614194.037 5811331.972, 614307.176 5811360.06, 614343.842 5811323.238, 614443.449 5811363.03, 614526.199 5811059.031, 614417.83 5811057.603, 614787.296 5809648.422, 614772.062 5809583.246, 614981.93 5809245.35, 614811.885 5809138.271, 615063.452 5809100.954, 615215.476 5809029.413, 615469.441 5808883.282, 615569.846 5808829.522, 615577.239 5808806.242, 615392.964 5808736.873, 615306.34 5808662.171, 615335.445 5808290.588, 615312.192 5808290.397, 614890.582 5808077.956, 615018.854 5807799.895, 614837.326 5807688.363, 614435.698 5807646.847, 614126.351 5807661.841, 613555.813 5807814.801, 612826.66 5807964.828, 612830.113 5807856.315, 612694.674 5807839.658))' ])
class TestPyQgsProviderConnectionPostgres(unittest.TestCase, TestPyQgsProviderConnectionBase): # Provider test cases must define the string URI for the test uri = '' # Provider test cases must define the provider name (e.g. "postgres" or "ogr") providerKey = 'postgres' @classmethod def setUpClass(cls): """Run before all tests""" TestPyQgsProviderConnectionBase.setUpClass() cls.postgres_conn = 'dbname=\'qgis_test\'' if 'QGIS_PGTEST_DB' in os.environ: cls.postgres_conn = os.environ['QGIS_PGTEST_DB'] # Create test layers vl = QgsVectorLayer( cls.postgres_conn + ' sslmode=disable key=\'"key1","key2"\' srid=4326 type=POINT table="qgis_test"."someData" (geom) sql=', 'test', 'postgres') assert vl.isValid() cls.uri = cls.postgres_conn + ' port=5432 sslmode=disable ' def test_postgis_connections(self): """Create some connections and retrieve them""" md = QgsProviderRegistry.instance().providerMetadata('postgres') conn = md.createConnection(self.uri, {}) md.saveConnection(conn, 'qgis_test1') # Retrieve capabilities capabilities = conn.capabilities() self.assertTrue( bool(capabilities & QgsAbstractDatabaseProviderConnection.Tables)) self.assertTrue( bool(capabilities & QgsAbstractDatabaseProviderConnection.Schemas)) self.assertTrue( bool(capabilities & QgsAbstractDatabaseProviderConnection.CreateVectorTable)) self.assertTrue( bool(capabilities & QgsAbstractDatabaseProviderConnection.DropVectorTable)) self.assertTrue( bool(capabilities & QgsAbstractDatabaseProviderConnection.RenameVectorTable)) self.assertTrue( bool(capabilities & QgsAbstractDatabaseProviderConnection.RenameRasterTable)) # Check filters and special cases table_names = self._table_names( conn.tables('qgis_test', QgsAbstractDatabaseProviderConnection.Raster)) self.assertTrue('Raster1' in table_names) self.assertFalse('geometryless_table' in table_names) self.assertFalse('geometries_table' in table_names) self.assertFalse('geometries_view' in table_names) table_names = self._table_names( conn.tables('qgis_test', QgsAbstractDatabaseProviderConnection.View)) self.assertFalse('Raster1' in table_names) self.assertFalse('geometryless_table' in table_names) self.assertFalse('geometries_table' in table_names) self.assertTrue('geometries_view' in table_names) table_names = self._table_names( conn.tables('qgis_test', QgsAbstractDatabaseProviderConnection.Aspatial)) self.assertFalse('Raster1' in table_names) self.assertTrue('geometryless_table' in table_names) self.assertFalse('geometries_table' in table_names) self.assertFalse('geometries_view' in table_names) geometries_table = self._table_by_name(conn.tables('qgis_test'), 'geometries_table') srids = [ t.crs.postgisSrid() for t in geometries_table.geometryColumnTypes() ] self.assertEqual(srids, [0, 0, 0, 3857, 4326, 0]) types = [t.wkbType for t in geometries_table.geometryColumnTypes()] self.assertEqual(types, [7, 2, 1, 1, 1, 3]) # error: ERROR: relation "qgis_test.raster1" does not exist @unittest.skipIf(gdal.VersionInfo() < '2040000', 'This test requires GDAL >= 2.4.0') def test_postgis_raster_rename(self): """Test raster rename""" md = QgsProviderRegistry.instance().providerMetadata('postgres') conn = md.createConnection(self.uri, {}) md.saveConnection(conn, 'qgis_test1') table = self._table_by_name( conn.tables('qgis_test', QgsAbstractDatabaseProviderConnection.Raster), 'Raster1') self.assertTrue( QgsRasterLayer( "PG: %s schema='qgis_test' column='%s' table='%s'" % (conn.uri(), table.geometryColumn(), table.tableName()), 'r1', 'gdal').isValid()) conn.renameRasterTable('qgis_test', table.tableName(), 'Raster2') table = self._table_by_name( conn.tables('qgis_test', QgsAbstractDatabaseProviderConnection.Raster), 'Raster2') self.assertTrue( QgsRasterLayer( "PG: %s schema='qgis_test' column='%s' table='%s'" % (conn.uri(), table.geometryColumn(), table.tableName()), 'r1', 'gdal').isValid()) table_names = self._table_names( conn.tables('qgis_test', QgsAbstractDatabaseProviderConnection.Raster)) self.assertFalse('Raster1' in table_names) self.assertTrue('Raster2' in table_names) conn.renameRasterTable('qgis_test', table.tableName(), 'Raster1') table_names = self._table_names( conn.tables('qgis_test', QgsAbstractDatabaseProviderConnection.Raster)) self.assertFalse('Raster2' in table_names) self.assertTrue('Raster1' in table_names)
def loadAlgorithms(self): self.algs = [ AssignProjection(), aspect(), buildvrt(), ClipRasterByExtent(), ClipRasterByMask(), ColorRelief(), contour(), contour_polygon(), Datasources2Vrt(), fillnodata(), gdalinfo(), gdal2tiles(), gdal2xyz(), gdaladdo(), gdalcalc(), gdaltindex(), GridAverage(), GridDataMetrics(), GridInverseDistance(), GridInverseDistanceNearestNeighbor(), GridLinear(), GridNearestNeighbor(), hillshade(), merge(), nearblack(), pct2rgb(), polygonize(), proximity(), rasterize(), rearrange_bands(), retile(), rgb2pct(), roughness(), sieve(), slope(), translate(), tpi(), tri(), warp(), pansharp(), # rasterize(), ExtractProjection(), rasterize_over(), rasterize_over_fixed_value(), # ----- OGR tools ----- Buffer(), ClipVectorByExtent(), ClipVectorByMask(), Dissolve(), ExecuteSql(), OffsetCurve(), ogr2ogr(), ogrinfo(), OgrToPostGis(), Ogr2OgrToPostGisList(), OneSideBuffer(), PointsAlongLines(), # Ogr2OgrTableToPostGisList(), ] if int(gdal.VersionInfo()) > 3010000: self.algs.append(viewshed()) for a in self.algs: self.addAlgorithm(a)
def version(self): '''Get GDAL version. Return version of installed GDAL. ''' return gdal.VersionInfo('RELEASE_NAME')
def qgis_session_info(self): # import re # from processing.algs.saga.SagaAlgorithmProvider import SagaAlgorithmProvider # from processing.algs.saga import SagaUtils # from processing.algs.grass.GrassUtils import GrassUtils # from processing.algs.grass7.Grass7Utils import Grass7Utils # from processing.algs.otb.OTBAlgorithmProvider import OTBAlgorithmProvider # from processing.algs.otb.OTBUtils import getInstalledVersion # from processing.algs.taudem.TauDEMUtils import TauDEMUtils # from osgeo import gdal # from processing.tools.system import isWindows, isMac # QGIS version qgis = QGis.QGIS_VERSION # GRASS versions # grassPath returns "" if called under Linux and if there is no GRASS # installation GrassUtils.checkGrassIsInstalled() g6 = GrassUtils.isGrassInstalled if g6 is True and isWindows(): g6 = GrassUtils.grassPath() # extract everything followed by grass-, i.e. extract the version number g6 = re.findall("grass-(.*)", g6) if g6 is True and isMac(): g6 = GrassUtils.grassPath()[0:21] g6 = os.listdir(g6) delim = ';' g6 = delim.join(g6) g6 = re.findall('[0-9].[0-9].[0-9]', g6) Grass7Utils.checkGrass7IsInstalled() g7 = Grass7Utils.isGrass7Installed if g7 is True and isWindows(): g7 = Grass7Utils.grassPath() g7 = re.findall('grass-(.*)', g7) if g7 is True and isMac(): g7 = Grass7Utils.grassPath()[0:21] g7 = os.listdir(g7) delim = ';' g7 = delim.join(g7) #g7 = re.findall(';(grass[0-9].);', g7) g7 = re.findall('[0-9].[0-9].[0-9]', g7) # installed SAGA version usable with QGIS saga = SagaUtils.getSagaInstalledVersion() # supported SAGA versions try: # supportedVersions were deleted from SagaAlgorithmProvider since # QGIS 2.18.10. At least this is the case with custom applications... my_dict = SagaAlgorithmProvider.supportedVersions saga_versions = my_dict.keys() saga_versions.sort() except: # with QGIS 2.18.10 only SAGA 2.3.0 and 2.3.1 is suppported # well, observe next QGIS releases and try to avoid the hard-coding! saga_versions = ["2.3.0", "2.3.1"] # GDAL gdal_v = gdal.VersionInfo('VERSION_NUM') gdal_v = '.'.join([gdal_v[0], gdal_v[2], gdal_v[4]]) ## this is good to have for the future, but so far, I would not report ## these software versions since we don't know if they actually work ## with QGIS (without additional functions such as run_taudem...) ## OTB versions # otb = getInstalledVersion() # otb = OTBUtils.getInstalledVersion() ## TauDEM versions (currently not in use because no function to extract ## Taudem version in 'TauDEMUtils') # TauDEMUtils.taudemMultifilePath() # finally, put it all into a named dictionary keys = ["qgis_version", "gdal", "grass6", "grass7", "saga",\ "supported_saga_versions"] values = [qgis, gdal_v, g6, g7, saga, saga_versions] info = dict(zip(keys, values)) return info
inRasters.extend(files) else: for rasterPaths in rasterPaths2: for f in formats: # look for supported rasters in directory files = glob.glob(rasterPath + f) inRasters.extend(files) if len(inRasters) == 0: print('No input rasters selected.') usage() # convert filenames to fileinfos fileInfos, bands = fileNamesToFileInfos(inRasters) # try to open source shapefile if int(gdal.VersionInfo()) > 1990000: inShape = ogr.Open(inShapeName, gdal.OF_VECTOR) else: inShape = ogr.Open(inShapeName, 1) if inShape is None: print('Unable to open shapefile', inShapeName) sys.exit(1) inLayer = inShape.GetLayer(0) if fields_descript: layerDefinition = inLayer.GetLayerDefn() for i in range(layerDefinition.GetFieldCount()): fields_csv.write( layerDefinition.GetFieldDefn(i).GetName() + ';' + layerDefinition.GetFieldDefn(i).GetName() + ';1\n')
def main( argv = None ): bReportHistograms = False bApproxStats = False scale = 1.0 offset = 0.0 bComputeMinMax = False bSample = False bShowGCPs = True bShowMetadata = True bShowRAT=True bStats = False bScale = False bShowColorTable = True pszFilename = None papszExtraMDDomains = [ ] pszProjection = None hTransform = None if argv is None: argv = sys.argv argv = gdal.GeneralCmdLineProcessor( argv ) if argv is None: return 1 nArgc = len(argv) #/* -------------------------------------------------------------------- */ #/* Parse arguments. */ #/* -------------------------------------------------------------------- */ i = 1 while i < nArgc: if EQUAL(argv[i], "--utility_version"): print("%s is running against GDAL %s" % (argv[0], gdal.VersionInfo("RELEASE_NAME"))) return 0 elif EQUAL(argv[i], "-mm"): bComputeMinMax = True elif EQUAL(argv[i], "-unscale"): bScale = True elif EQUAL(argv[i], "-stats"): bStats = True elif EQUAL(argv[i], "-hist"): bReportHistograms = True elif argv[i][0] == '-': return Usage() elif pszFilename is None: pszFilename = argv[i] else: return Usage() i = i + 1 if pszFilename is None: return Usage() if not (bComputeMinMax or bScale or bStats or bReportHistograms): return Usage() #/* -------------------------------------------------------------------- */ #/* Open dataset. */ #/* -------------------------------------------------------------------- */ hDataset = gdal.Open( pszFilename, gdal.GA_ReadOnly ) if hDataset is None: print("gdalinfo failed - unable to open '%s'." % pszFilename ) return 1 #/* ==================================================================== */ #/* Loop over bands. */ #/* ==================================================================== */ for iBand in range(hDataset.RasterCount): hBand = hDataset.GetRasterBand(iBand+1 ) (nBlockXSize, nBlockYSize) = hBand.GetBlockSize() if bScale: offset = hBand.GetOffset() if offset is None: offset = 0.0 scale = hBand.GetScale() if scale is None: scale = 1.0 if (hDataset.RasterCount > 1): print( "Band %d Block=%dx%d Type=%s, ColorInterp=%s" % ( iBand+1, \ nBlockXSize, nBlockYSize, \ gdal.GetDataTypeName(hBand.DataType), \ gdal.GetColorInterpretationName( \ hBand.GetRasterColorInterpretation()) )) dfMin = hBand.GetMinimum() dfMax = hBand.GetMaximum() if dfMin is not None or dfMax is not None or bComputeMinMax: line = "" if dfMin is not None: dfMin = (dfMin * scale) + offset line = line + ("Min=%.3f " % (dfMin)) if dfMax is not None: dfMax = (dfMax * scale) + offset line = line + ("Max=%.3f " % (dfMax)) if bComputeMinMax: gdal.ErrorReset() adfCMinMax = hBand.ComputeRasterMinMax(True) if gdal.GetLastErrorType() == gdal.CE_None: line = line + ( " Computed Min/Max=%.3f,%.3f" % ( \ ((adfCMinMax[0] * scale) + offset), \ ((adfCMinMax[1] * scale) + offset) )) print( line ) #if bStats: # print( line ) stats = hBand.GetStatistics( bApproxStats, bStats) #inType = gdal.GetDataTypeName(hBand.DataType) # Dirty hack to recognize if stats are valid. If invalid, the returned # stddev is negative if stats[3] >= 0.0: if bStats: mean = (stats[2] * scale) + offset; stdev = (stats[3] * scale) + offset; rms = math.sqrt((mean * mean) + ( stdev * stdev)) print( "Min=%.2f, Max=%.2f, Mean=%.2f, StdDev=%.2f, RMS=%.2f" \ % ((stats[0] * scale) + offset, (stats[1] * scale) + offset,\ mean, stdev, rms )) if bReportHistograms: print ("level\tvalue\tcount\tcumulative") #Histogram call not returning exact min and max. #...Workaround run gdalinfo -stats and then use min/max from above hist = hBand.GetDefaultHistogram(force = True) #hist = hBand.GetDefaultHistogram(force = True, callback = gdal.TermProgress) cnt = 0 sum = 0 sumTotal = 0 if hist is not None: #use dfMin and dfMax from previous calls when possible if dfMin is None: dfMin = (hist[0] * scale) + offset if dfMax is None: dfMax = (hist[1] * scale) + offset nBucketCount = hist[2] panHistogram = hist[3] #print( " %d buckets from %g to %g:" % ( \ # nBucketCount, dfMin, dfMax )) #print ( "scale: %g, offset: %g" % (scale, offset)) increment = round(((dfMax - dfMin) / nBucketCount),2) value = dfMin #get total to normalize (below) for bucket in panHistogram: sumTotal = sumTotal + bucket for bucket in panHistogram: sum = sum + bucket #normalize cumulative nsum = sum / float(sumTotal) line = "%d\t%0.2f\t%d\t%0.6f" % (cnt, value, bucket, nsum) print(line) cnt = cnt + 1 value = value + increment return True
def main(): global TMPLOC, SRCGISRC, TGTGISRC, GISDBASE overwrite = grass.overwrite() # list formats and exit if flags['f']: grass.run_command('v.in.ogr', flags='f') return 0 # list layers and exit if flags['l']: try: grass.run_command('v.in.ogr', flags='l', input=options['input']) except CalledModuleError: return 1 return 0 OGRdatasource = options['input'] output = options['output'] layers = options['layer'] vflags = '' if options['extent'] == 'region': vflags += 'r' if flags['o']: vflags += 'o' vopts = {} if options['encoding']: vopts['encoding'] = options['encoding'] if options['datum_trans'] and options['datum_trans'] == '-1': # list datum transform parameters if not options['epsg']: grass.fatal(_("Missing value for parameter <%s>") % 'epsg') return grass.run_command('g.proj', epsg=options['epsg'], datum_trans=options['datum_trans']) if layers: vopts['layer'] = layers if output: vopts['output'] = output vopts['snap'] = options['snap'] # try v.in.ogr directly if flags['o'] or grass.run_command('v.in.ogr', input=OGRdatasource, flags='j', errors='status', quiet=True, overwrite=overwrite, **vopts) == 0: try: grass.run_command('v.in.ogr', input=OGRdatasource, flags=vflags, overwrite=overwrite, **vopts) grass.message( _("Input <%s> successfully imported without reprojection") % OGRdatasource) return 0 except CalledModuleError: grass.fatal(_("Unable to import <%s>") % OGRdatasource) grassenv = grass.gisenv() tgtloc = grassenv['LOCATION_NAME'] # make sure target is not xy if grass.parse_command('g.proj', flags='g')['name'] == 'xy_location_unprojected': grass.fatal( _("Coordinate reference system not available for current location <%s>" ) % tgtloc) tgtmapset = grassenv['MAPSET'] GISDBASE = grassenv['GISDBASE'] TGTGISRC = os.environ['GISRC'] SRCGISRC = grass.tempfile() TMPLOC = 'temp_import_location_' + str(os.getpid()) f = open(SRCGISRC, 'w') f.write('MAPSET: PERMANENT\n') f.write('GISDBASE: %s\n' % GISDBASE) f.write('LOCATION_NAME: %s\n' % TMPLOC) f.write('GUI: text\n') f.close() tgtsrs = grass.read_command('g.proj', flags='j', quiet=True) # create temp location from input without import grass.verbose(_("Creating temporary location for <%s>...") % OGRdatasource) try: if OGRdatasource.lower().endswith("gml"): try: from osgeo import gdal except: grass.fatal( _("Unable to load GDAL Python bindings (requires package 'python-gdal' being installed)" )) if int(gdal.VersionInfo('VERSION_NUM')) < GDAL_COMPUTE_VERSION( 2, 4, 1): fix_gfsfile(OGRdatasource) grass.run_command('v.in.ogr', input=OGRdatasource, location=TMPLOC, flags='i', quiet=True, overwrite=overwrite, **vopts) except CalledModuleError: grass.fatal( _("Unable to create location from OGR datasource <%s>") % OGRdatasource) # switch to temp location os.environ['GISRC'] = str(SRCGISRC) if options['epsg']: # force given EPSG kwargs = {} if options['datum_trans']: kwargs['datum_trans'] = options['datum_trans'] grass.run_command('g.proj', flags='c', epsg=options['epsg'], **kwargs) # print projection at verbose level grass.verbose(grass.read_command('g.proj', flags='p').rstrip(os.linesep)) # make sure input is not xy if grass.parse_command('g.proj', flags='g')['name'] == 'xy_location_unprojected': grass.fatal( _("Coordinate reference system not available for input <%s>") % OGRdatasource) if options['extent'] == 'region': # switch to target location os.environ['GISRC'] = str(TGTGISRC) # v.in.region in tgt vreg = 'vreg_' + str(os.getpid()) grass.run_command('v.in.region', output=vreg, quiet=True) # reproject to src # switch to temp location os.environ['GISRC'] = str(SRCGISRC) try: grass.run_command('v.proj', input=vreg, output=vreg, location=tgtloc, mapset=tgtmapset, quiet=True, overwrite=overwrite) except CalledModuleError: grass.fatal(_("Unable to reproject to source location")) # set region from region vector grass.run_command('g.region', res='1') grass.run_command('g.region', vector=vreg) # import into temp location grass.message(_("Importing <%s> ...") % OGRdatasource) try: if OGRdatasource.lower().endswith("gml"): try: from osgeo import gdal except: grass.fatal( _("Unable to load GDAL Python bindings (requires package 'python-gdal' being installed)" )) if int(gdal.VersionInfo('VERSION_NUM')) < GDAL_COMPUTE_VERSION( 2, 4, 1): fix_gfsfile(OGRdatasource) grass.run_command('v.in.ogr', input=OGRdatasource, flags=vflags, overwrite=overwrite, **vopts) except CalledModuleError: grass.fatal(_("Unable to import OGR datasource <%s>") % OGRdatasource) # if output is not define check source mapset if not output: output = grass.list_grouped('vector')['PERMANENT'][0] # switch to target location os.environ['GISRC'] = str(TGTGISRC) # check if map exists if not grass.overwrite() and \ grass.find_file(output, element='vector', mapset='.')['mapset']: grass.fatal(_("option <%s>: <%s> exists.") % ('output', output)) if options['extent'] == 'region': grass.run_command('g.remove', type='vector', name=vreg, flags='f', quiet=True) # v.proj grass.message(_("Reprojecting <%s>...") % output) try: grass.run_command('v.proj', location=TMPLOC, mapset='PERMANENT', input=output, overwrite=overwrite) except CalledModuleError: grass.fatal(_("Unable to to reproject vector <%s>") % output) return 0
op = getattr(input_lyr, op_str) if quiet_flag == 0: ret = op(method_lyr, output_lyr, options=opt, callback=gdal.TermProgress_nocb) else: ret = op(method_lyr, output_lyr, options=opt) input_ds = None method_ds = None output_ds = None if ret != 0: print('An error occurred during %s operation' % op_str) return 1 return 0 ############################################################################### if __name__ == '__main__': version_num = int(gdal.VersionInfo('VERSION_NUM')) if version_num < 1100000: print('ERROR: Python bindings of GDAL 1.10 or later required') sys.exit(1) sys.exit(main(sys.argv))
class TestPyQgsOGRProviderSqlite(unittest.TestCase): @classmethod def setUpClass(cls): """Run before all tests""" # Create test layer cls.basetestpath = tempfile.mkdtemp() @classmethod def tearDownClass(cls): """Run after all tests""" shutil.rmtree(cls.basetestpath, True) def testFidSupport(self): # We do not use @unittest.expectedFailure since the test might actually succeed # on Linux 64bit with GDAL 1.11, where "long" is 64 bit... # GDAL 2.0 is guaranteed to properly support it on all platforms version_num = int(gdal.VersionInfo('VERSION_NUM')) if version_num < GDAL_COMPUTE_VERSION(2, 0, 0): return tmpfile = os.path.join(self.basetestpath, 'testFidSupport.sqlite') ds = ogr.GetDriverByName('SQLite').CreateDataSource(tmpfile) lyr = ds.CreateLayer('test', geom_type=ogr.wkbPoint, options=['FID=fid']) lyr.CreateField(ogr.FieldDefn('strfield', ogr.OFTString)) lyr.CreateField(ogr.FieldDefn('intfield', ogr.OFTInteger)) f = ogr.Feature(lyr.GetLayerDefn()) f.SetFID(12) f.SetField(0, 'foo') f.SetField(1, 123) lyr.CreateFeature(f) f = None ds = None vl = QgsVectorLayer('{}'.format(tmpfile), 'test', 'ogr') self.assertEqual(len(vl.fields()), 3) got = [(f.attribute('fid'), f.attribute('strfield'), f.attribute('intfield')) for f in vl.getFeatures()] self.assertEqual(got, [(12, 'foo', 123)]) got = [(f.attribute('fid'), f.attribute('strfield')) for f in vl.getFeatures(QgsFeatureRequest().setFilterExpression( "strfield = 'foo'"))] self.assertEqual(got, [(12, 'foo')]) got = [(f.attribute('fid'), f.attribute('strfield')) for f in vl.getFeatures(QgsFeatureRequest().setFilterExpression( "fid = 12"))] self.assertEqual(got, [(12, 'foo')]) result = [ f['strfield'] for f in vl.dataProvider().getFeatures(QgsFeatureRequest( ).setSubsetOfAttributes(['strfield'], vl.dataProvider().fields())) ] self.assertEqual(result, ['foo']) result = [ f['fid'] for f in vl.dataProvider().getFeatures(QgsFeatureRequest( ).setSubsetOfAttributes(['fid'], vl.dataProvider().fields())) ] self.assertEqual(result, [12]) # Test that when the 'fid' field is not set, regular insertion is done f = QgsFeature() f.setFields(vl.fields()) f.setAttributes([None, 'automatic_id']) (res, out_f) = vl.dataProvider().addFeatures([f]) self.assertEqual(out_f[0].id(), 13) self.assertEqual(out_f[0].attribute('fid'), 13) self.assertEqual(out_f[0].attribute('strfield'), 'automatic_id') # Test that when the 'fid' field is set, it is really used to set the id f = QgsFeature() f.setFields(vl.fields()) f.setAttributes([9876543210, 'bar']) (res, out_f) = vl.dataProvider().addFeatures([f]) self.assertEqual(out_f[0].id(), 9876543210) self.assertEqual(out_f[0].attribute('fid'), 9876543210) self.assertEqual(out_f[0].attribute('strfield'), 'bar') got = [(f.attribute('fid'), f.attribute('strfield')) for f in vl.getFeatures(QgsFeatureRequest().setFilterExpression( "fid = 9876543210"))] self.assertEqual(got, [(9876543210, 'bar')]) self.assertTrue(vl.dataProvider().changeAttributeValues( {9876543210: { 1: 'baz' }})) got = [(f.attribute('fid'), f.attribute('strfield')) for f in vl.getFeatures(QgsFeatureRequest().setFilterExpression( "fid = 9876543210"))] self.assertEqual(got, [(9876543210, 'baz')]) self.assertTrue(vl.dataProvider().changeAttributeValues( {9876543210: { 0: 9876543210, 1: 'baw' }})) got = [(f.attribute('fid'), f.attribute('strfield')) for f in vl.getFeatures(QgsFeatureRequest().setFilterExpression( "fid = 9876543210"))] self.assertEqual(got, [(9876543210, 'baw')]) # Not allowed: changing the fid regular field self.assertTrue(vl.dataProvider().changeAttributeValues( {9876543210: { 0: 12, 1: 'baw' }})) got = [(f.attribute('fid'), f.attribute('strfield')) for f in vl.getFeatures(QgsFeatureRequest().setFilterExpression( "fid = 9876543210"))] self.assertEqual(got, [(9876543210, 'baw')]) # Cannot delete fid self.assertFalse(vl.dataProvider().deleteAttributes([0])) # Delete first "genuine" attribute self.assertTrue(vl.dataProvider().deleteAttributes([1])) got = [(f.attribute('fid'), f.attribute('intfield')) for f in vl.dataProvider().getFeatures( QgsFeatureRequest().setFilterExpression("fid = 12"))] self.assertEqual(got, [(12, 123)]) @unittest.expectedFailure( int(gdal.VersionInfo('VERSION_NUM')) < GDAL_COMPUTE_VERSION(2, 0, 0)) def testNotNullConstraint(self): """ test detection of not null constraint on OGR layer """ tmpfile = os.path.join(self.basetestpath, 'testNotNullConstraint.sqlite') ds = ogr.GetDriverByName('SQLite').CreateDataSource(tmpfile) lyr = ds.CreateLayer('test', geom_type=ogr.wkbPoint, options=['FID=fid']) lyr.CreateField(ogr.FieldDefn('field1', ogr.OFTInteger)) fld2 = ogr.FieldDefn('field2', ogr.OFTInteger) fld2.SetNullable(False) lyr.CreateField(fld2) ds = None vl = QgsVectorLayer('{}'.format(tmpfile), 'test', 'ogr') self.assertTrue(vl.isValid()) # test some bad indexes self.assertEqual(vl.dataProvider().fieldConstraints(-1), QgsFieldConstraints.Constraints()) self.assertEqual(vl.dataProvider().fieldConstraints(1001), QgsFieldConstraints.Constraints()) self.assertFalse(vl.dataProvider().fieldConstraints(0) & QgsFieldConstraints.ConstraintNotNull) self.assertFalse(vl.dataProvider().fieldConstraints(1) & QgsFieldConstraints.ConstraintNotNull) self.assertTrue(vl.dataProvider().fieldConstraints(2) & QgsFieldConstraints.ConstraintNotNull) # test that constraints have been saved to fields correctly fields = vl.fields() self.assertFalse( fields.at(0).constraints().constraints() & QgsFieldConstraints.ConstraintNotNull) self.assertFalse( fields.at(1).constraints().constraints() & QgsFieldConstraints.ConstraintNotNull) self.assertTrue( fields.at(2).constraints().constraints() & QgsFieldConstraints.ConstraintNotNull) self.assertEqual( fields.at(2).constraints().constraintOrigin( QgsFieldConstraints.ConstraintNotNull), QgsFieldConstraints.ConstraintOriginProvider) @unittest.expectedFailure( int(gdal.VersionInfo('VERSION_NUM')) < GDAL_COMPUTE_VERSION(2, 0, 0)) def testDefaultValues(self): """ test detection of defaults on OGR layer """ tmpfile = os.path.join(self.basetestpath, 'testDefaults.sqlite') ds = ogr.GetDriverByName('SQLite').CreateDataSource(tmpfile) lyr = ds.CreateLayer('test', geom_type=ogr.wkbPoint, options=['FID=fid']) lyr.CreateField(ogr.FieldDefn('field1', ogr.OFTInteger)) fld2 = ogr.FieldDefn('field2', ogr.OFTInteger) fld2.SetDefault('5') lyr.CreateField(fld2) fld3 = ogr.FieldDefn('field3', ogr.OFTString) fld3.SetDefault("'some ''default'") lyr.CreateField(fld3) fld4 = ogr.FieldDefn('field4', ogr.OFTDate) fld4.SetDefault("CURRENT_DATE") lyr.CreateField(fld4) fld5 = ogr.FieldDefn('field5', ogr.OFTTime) fld5.SetDefault("CURRENT_TIME") lyr.CreateField(fld5) fld6 = ogr.FieldDefn('field6', ogr.OFTDateTime) fld6.SetDefault("CURRENT_TIMESTAMP") lyr.CreateField(fld6) ds = None vl = QgsVectorLayer('{}'.format(tmpfile), 'test', 'ogr') self.assertTrue(vl.isValid()) # test some bad indexes self.assertFalse(vl.dataProvider().defaultValue(-1)) self.assertFalse(vl.dataProvider().defaultValue(1001)) # test default self.assertEqual(vl.dataProvider().defaultValue(1), NULL) self.assertEqual(vl.dataProvider().defaultValue(2), 5) self.assertEqual(vl.dataProvider().defaultValue(3), "some 'default") self.assertEqual(vl.dataProvider().defaultValue(4), QDate.currentDate()) # time may pass, so we allow 1 second difference here self.assertTrue( vl.dataProvider().defaultValue(5).secsTo(QTime.currentTime()) < 1) self.assertTrue(vl.dataProvider().defaultValue(6).secsTo( QDateTime.currentDateTime()) < 1)
class PyQgsGdalProvider(unittest.TestCase): def checkBlockContents(self, block, expected): res = [] for r in range(block.height()): res.extend([block.value(r, c) for c in range(block.width())]) self.assertEqual(res, expected) def testRasterBlock(self): """Test raster block with extent""" path = os.path.join(unitTestDataPath(), 'landsat_4326.tif') raster_layer = QgsRasterLayer(path, 'test') self.assertTrue(raster_layer.isValid()) extent = QgsRectangle(17.94284482577178252, 30.23021770271909503, 17.94407867909909626, 30.23154272264058307) block = raster_layer.dataProvider().block(1, extent, 2, 3) self.checkBlockContents(block, [ 125.0, 125.0, 125.0, 125.0, 125.0, 124.0, ]) full_content = [ 125.0, 125.0, 125.0, 125.0, 125.0, 125.0, 125.0, 124.0, 125.0, 126.0, 127.0, 127.0, ] extent = raster_layer.extent() block = raster_layer.dataProvider().block(1, extent, 3, 4) self.checkBlockContents(block, full_content) extent = raster_layer.extent() extent.grow(-0.0001) block = raster_layer.dataProvider().block(1, extent, 3, 4) self.checkBlockContents(block, full_content) row_height = raster_layer.extent().height() / raster_layer.height() for row in range(raster_layer.height()): extent = raster_layer.extent() extent.setYMaximum(extent.yMaximum() - row_height * row) extent.setYMinimum(extent.yMaximum() - row_height) block = raster_layer.dataProvider().block(1, extent, 3, 1) self.checkBlockContents(block, full_content[row * 3:row * 3 + 3]) def testDecodeEncodeUriGpkg(self): """Test decodeUri/encodeUri geopackage support""" uri = '/my/raster.gpkg' parts = QgsProviderRegistry.instance().decodeUri('gdal', uri) self.assertEqual(parts, {'path': '/my/raster.gpkg', 'layerName': None}) encodedUri = QgsProviderRegistry.instance().encodeUri('gdal', parts) self.assertEqual(encodedUri, uri) uri = 'GPKG:/my/raster.gpkg' parts = QgsProviderRegistry.instance().decodeUri('gdal', uri) self.assertEqual(parts, {'path': '/my/raster.gpkg', 'layerName': None}) encodedUri = QgsProviderRegistry.instance().encodeUri('gdal', parts) self.assertEqual(encodedUri, '/my/raster.gpkg') uri = 'GPKG:/my/raster.gpkg:mylayer' parts = QgsProviderRegistry.instance().decodeUri('gdal', uri) self.assertEqual(parts, { 'path': '/my/raster.gpkg', 'layerName': 'mylayer' }) encodedUri = QgsProviderRegistry.instance().encodeUri('gdal', parts) self.assertEqual(encodedUri, uri) def testDecodeEncodeUriOptions(self): """Test decodeUri/encodeUri options support""" uri = '/my/raster.pdf|option:DPI=300|option:GIVEME=TWO' parts = QgsProviderRegistry.instance().decodeUri('gdal', uri) self.assertEqual( parts, { 'path': '/my/raster.pdf', 'layerName': None, 'openOptions': ['DPI=300', 'GIVEME=TWO'] }) encodedUri = QgsProviderRegistry.instance().encodeUri('gdal', parts) self.assertEqual(encodedUri, uri) def testDecodeEncodeUriVsizip(self): """Test decodeUri/encodeUri for /vsizip/ prefixed URIs""" uri = '/vsizip//my/file.zip/image.tif' parts = QgsProviderRegistry.instance().decodeUri('gdal', uri) self.assertEqual( parts, { 'path': '/my/file.zip', 'layerName': None, 'vsiPrefix': '/vsizip/', 'vsiSuffix': '/image.tif' }) encodedUri = QgsProviderRegistry.instance().encodeUri('gdal', parts) self.assertEqual(encodedUri, uri) def test_provider_sidecar_files_for_uri(self): """ Test retrieving sidecar files for uris """ metadata = QgsProviderRegistry.instance().providerMetadata('gdal') self.assertEqual(metadata.sidecarFilesForUri(''), []) self.assertEqual( metadata.sidecarFilesForUri('/home/me/some_file.asc'), [ '/home/me/some_file.aux.xml', '/home/me/some_file.asc.aux.xml', '/home/me/some_file.vat.dbf', '/home/me/some_file.asc.vat.dbf', '/home/me/some_file.ovr', '/home/me/some_file.asc.ovr', '/home/me/some_file.wld', '/home/me/some_file.asc.wld' ]) self.assertEqual(metadata.sidecarFilesForUri('/home/me/special.jpg'), [ '/home/me/special.jpw', '/home/me/special.jgw', '/home/me/special.jpgw', '/home/me/special.jpegw', '/home/me/special.aux.xml', '/home/me/special.jpg.aux.xml', '/home/me/special.vat.dbf', '/home/me/special.jpg.vat.dbf', '/home/me/special.ovr', '/home/me/special.jpg.ovr', '/home/me/special.wld', '/home/me/special.jpg.wld' ]) self.assertEqual(metadata.sidecarFilesForUri('/home/me/special.img'), [ '/home/me/special.ige', '/home/me/special.aux.xml', '/home/me/special.img.aux.xml', '/home/me/special.vat.dbf', '/home/me/special.img.vat.dbf', '/home/me/special.ovr', '/home/me/special.img.ovr', '/home/me/special.wld', '/home/me/special.img.wld' ]) self.assertEqual(metadata.sidecarFilesForUri('/home/me/special.sid'), [ '/home/me/special.j2w', '/home/me/special.aux.xml', '/home/me/special.sid.aux.xml', '/home/me/special.vat.dbf', '/home/me/special.sid.vat.dbf', '/home/me/special.ovr', '/home/me/special.sid.ovr', '/home/me/special.wld', '/home/me/special.sid.wld' ]) self.assertEqual(metadata.sidecarFilesForUri('/home/me/special.tif'), [ '/home/me/special.tifw', '/home/me/special.tfw', '/home/me/special.aux.xml', '/home/me/special.tif.aux.xml', '/home/me/special.vat.dbf', '/home/me/special.tif.vat.dbf', '/home/me/special.ovr', '/home/me/special.tif.ovr', '/home/me/special.wld', '/home/me/special.tif.wld' ]) self.assertEqual(metadata.sidecarFilesForUri('/home/me/special.bil'), [ '/home/me/special.bilw', '/home/me/special.blw', '/home/me/special.aux.xml', '/home/me/special.bil.aux.xml', '/home/me/special.vat.dbf', '/home/me/special.bil.vat.dbf', '/home/me/special.ovr', '/home/me/special.bil.ovr', '/home/me/special.wld', '/home/me/special.bil.wld' ]) self.assertEqual( metadata.sidecarFilesForUri('/home/me/special.raster'), [ '/home/me/special.rasterw', '/home/me/special.aux.xml', '/home/me/special.raster.aux.xml', '/home/me/special.vat.dbf', '/home/me/special.raster.vat.dbf', '/home/me/special.ovr', '/home/me/special.raster.ovr', '/home/me/special.wld', '/home/me/special.raster.wld' ]) self.assertEqual(metadata.sidecarFilesForUri('/home/me/special.bt'), [ '/home/me/special.btw', '/home/me/special.aux.xml', '/home/me/special.bt.aux.xml', '/home/me/special.vat.dbf', '/home/me/special.bt.vat.dbf', '/home/me/special.ovr', '/home/me/special.bt.ovr', '/home/me/special.wld', '/home/me/special.bt.wld' ]) self.assertEqual(metadata.sidecarFilesForUri('/home/me/special.rst'), [ '/home/me/special.rdc', '/home/me/special.smp', '/home/me/special.ref', '/home/me/special.vct', '/home/me/special.vdc', '/home/me/special.avl', '/home/me/special.aux.xml', '/home/me/special.rst.aux.xml', '/home/me/special.vat.dbf', '/home/me/special.rst.vat.dbf', '/home/me/special.ovr', '/home/me/special.rst.ovr', '/home/me/special.wld', '/home/me/special.rst.wld' ]) self.assertEqual( metadata.sidecarFilesForUri('/home/me/special.sdat'), [ '/home/me/special.sgrd', '/home/me/special.mgrd', '/home/me/special.prj', '/home/me/special.aux.xml', '/home/me/special.sdat.aux.xml', '/home/me/special.vat.dbf', '/home/me/special.sdat.vat.dbf', '/home/me/special.ovr', '/home/me/special.sdat.ovr', '/home/me/special.wld', '/home/me/special.sdat.wld' ]) @unittest.skipIf( int(gdal.VersionInfo('VERSION_NUM')) < GDAL_COMPUTE_VERSION(3, 5, 0), "GDAL 3.5.0 required") def testInt64(self): """Test Int64 support""" tmp_dir = QTemporaryDir() tmpfile = os.path.join(tmp_dir.path(), 'testInt64.tif') ds = gdal.GetDriverByName('GTiff').Create(tmpfile, 2, 2, 1, gdal.GDT_Int64) ds.WriteRaster( 0, 0, 2, 2, struct.pack('q' * 4, -1234567890123, 1234567890123, -(1 << 63), (1 << 63) - 1)) ds = None raster_layer = QgsRasterLayer(tmpfile, 'test') self.assertTrue(raster_layer.isValid()) self.assertEqual(raster_layer.dataProvider().dataType(1), Qgis.Float64) extent = raster_layer.extent() block = raster_layer.dataProvider().block(1, extent, 2, 2) full_content = [ -1234567890123, 1234567890123, float(-(1 << 63)), float((1 << 63) - 1) ] self.checkBlockContents(block, full_content) pos = QgsPointXY(0, 0) value_sample = raster_layer.dataProvider().sample(pos, 1)[0] self.assertEqual(value_sample, full_content[0]) pos = QgsPointXY(1, 0) value_sample = raster_layer.dataProvider().sample(pos, 1)[0] self.assertEqual(value_sample, full_content[1]) pos = QgsPointXY(0, -1) value_sample = raster_layer.dataProvider().sample(pos, 1)[0] self.assertTrue(value_sample, full_content[2]) pos = QgsPointXY(1, -1) value_sample = raster_layer.dataProvider().sample(pos, 1)[0] self.assertTrue(math.isnan( value_sample)) # (1 << 63) - 1 not exactly representable as double @unittest.skipIf( int(gdal.VersionInfo('VERSION_NUM')) < GDAL_COMPUTE_VERSION(3, 5, 0), "GDAL 3.5.0 required") def testUInt64(self): """Test Int64 support""" tmp_dir = QTemporaryDir() tmpfile = os.path.join(tmp_dir.path(), 'testUInt64.tif') ds = gdal.GetDriverByName('GTiff').Create(tmpfile, 2, 2, 1, gdal.GDT_UInt64) ds.WriteRaster( 0, 0, 2, 2, struct.pack('Q' * 4, 1, 1234567890123, 0, (1 << 64) - 1)) ds = None raster_layer = QgsRasterLayer(tmpfile, 'test') self.assertTrue(raster_layer.isValid()) self.assertEqual(raster_layer.dataProvider().dataType(1), Qgis.Float64) extent = raster_layer.extent() block = raster_layer.dataProvider().block(1, extent, 2, 2) full_content = [1, 1234567890123, 0, float((1 << 64) - 1)] self.checkBlockContents(block, full_content) pos = QgsPointXY(0, 0) value_sample = raster_layer.dataProvider().sample(pos, 1)[0] self.assertEqual(value_sample, full_content[0]) pos = QgsPointXY(1, 0) value_sample = raster_layer.dataProvider().sample(pos, 1)[0] self.assertEqual(value_sample, full_content[1]) pos = QgsPointXY(0, -1) value_sample = raster_layer.dataProvider().sample(pos, 1)[0] self.assertEqual(value_sample, full_content[2]) pos = QgsPointXY(1, -1) value_sample = raster_layer.dataProvider().sample(pos, 1)[0] self.assertTrue(math.isnan(value_sample))
TIFFTAG_GEOPIXELSCALE = 33550 TIFFTAG_GEOTIEPOINTS = 33922 TIFFTAG_GEOTRANSMATRIX = 34264 TIFFTAG_GEOKEYDIRECTORY = 34735 TIFFTAG_GEODOUBLEPARAMS = 34736 TIFFTAG_GEOASCIIPARAMS = 34737 geotiff_tagids = [ TIFFTAG_GEOPIXELSCALE, TIFFTAG_GEOTIEPOINTS, TIFFTAG_GEOTRANSMATRIX, TIFFTAG_GEOKEYDIRECTORY, TIFFTAG_GEODOUBLEPARAMS, TIFFTAG_GEOASCIIPARAMS ] TIFFTAG_GDAL_METADATA = 42112 TIFFTAG_GDAL_NODATA = 42113 gdal.UseExceptions() gdal_3_3 = int(gdal.VersionInfo('VERSION_NUM')) >= 3030000 class Raster: def __init__(self, ds): self.ds = ds self.width = self.ds.RasterXSize self.height = self.ds.RasterYSize self.datatype = self.ds.GetRasterBand(1).DataType self.bitspersample = gdal.GetDataTypeSize(self.datatype) self.num_bands = self.ds.RasterCount self.tile_width = 512 self.tile_height = 512 self.tile_x_count = (self.width + self.tile_width - 1) // self.tile_width self.tile_y_count = (self.height + self.tile_height -
def versionNum(self): return int(gdal.VersionInfo("VERSION_NUM"))
def main( argv = None ): bComputeMinMax = False bShowGCPs = True bShowMetadata = True bShowRAT=True bStats = False bApproxStats = True bShowColorTable = True bComputeChecksum = False bReportHistograms = False pszFilename = None papszExtraMDDomains = [ ] pszProjection = None hTransform = None bShowFileList = True # Must process GDAL_SKIP before GDALAllRegister(), but we can't call # GDALGeneralCmdLineProcessor before it needs the drivers to be registered # for the --format or --formats options #for( i = 1; i < argc; i++ ) #{ # if EQUAL(argv[i],"--config") and i + 2 < argc and EQUAL(argv[i + 1], "GDAL_SKIP"): # { # CPLSetConfigOption( argv[i+1], argv[i+2] ); # # i += 2; # } #} # #GDALAllRegister(); if argv is None: argv = sys.argv argv = gdal.GeneralCmdLineProcessor( argv ) if argv is None: return 1 nArgc = len(argv) # -------------------------------------------------------------------- # Parse arguments. # -------------------------------------------------------------------- i = 1 while i < nArgc: if EQUAL(argv[i], "--utility_version"): print("%s is running against GDAL %s" % (argv[0], gdal.VersionInfo("RELEASE_NAME"))) return 0 elif EQUAL(argv[i], "-mm"): bComputeMinMax = True elif EQUAL(argv[i], "-hist"): bReportHistograms = True elif EQUAL(argv[i], "-stats"): bStats = True bApproxStats = False elif EQUAL(argv[i], "-approx_stats"): bStats = True bApproxStats = True elif EQUAL(argv[i], "-checksum"): bComputeChecksum = True elif EQUAL(argv[i], "-nogcp"): bShowGCPs = False elif EQUAL(argv[i], "-nomd"): bShowMetadata = False elif EQUAL(argv[i], "-norat"): bShowRAT = False elif EQUAL(argv[i], "-noct"): bShowColorTable = False elif EQUAL(argv[i], "-mdd") and i < nArgc-1: i = i + 1 papszExtraMDDomains.append( argv[i] ) elif EQUAL(argv[i], "-nofl"): bShowFileList = False elif argv[i][0] == '-': return Usage() elif pszFilename is None: pszFilename = argv[i] else: return Usage() i = i + 1 if pszFilename is None: return Usage() # -------------------------------------------------------------------- # Open dataset. # -------------------------------------------------------------------- hDataset = gdal.Open( pszFilename, gdal.GA_ReadOnly ) if hDataset is None: print("gdalinfo failed - unable to open '%s'." % pszFilename ) return 1 # -------------------------------------------------------------------- # Report general info. # -------------------------------------------------------------------- hDriver = hDataset.GetDriver(); print( "Driver: %s/%s" % ( \ hDriver.ShortName, \ hDriver.LongName )) papszFileList = hDataset.GetFileList(); if papszFileList is None or len(papszFileList) == 0: print( "Files: none associated" ) else: print( "Files: %s" % papszFileList[0] ) if bShowFileList: for i in range(1, len(papszFileList)): print( " %s" % papszFileList[i] ) print( "Size is %d, %d" % (hDataset.RasterXSize, hDataset.RasterYSize)) # -------------------------------------------------------------------- # Report projection. # -------------------------------------------------------------------- pszProjection = hDataset.GetProjectionRef() if pszProjection is not None: hSRS = osr.SpatialReference() if hSRS.ImportFromWkt(pszProjection ) == gdal.CE_None: pszPrettyWkt = hSRS.ExportToPrettyWkt(False) print( "Coordinate System is:\n%s" % pszPrettyWkt ) else: print( "Coordinate System is `%s'" % pszProjection ) # -------------------------------------------------------------------- # Report Geotransform. # -------------------------------------------------------------------- adfGeoTransform = hDataset.GetGeoTransform(can_return_null = True) if adfGeoTransform is not None: if adfGeoTransform[2] == 0.0 and adfGeoTransform[4] == 0.0: print( "Origin = (%.15f,%.15f)" % ( \ adfGeoTransform[0], adfGeoTransform[3] )) print( "Pixel Size = (%.15f,%.15f)" % ( \ adfGeoTransform[1], adfGeoTransform[5] )) else: print( "GeoTransform =\n" \ " %.16g, %.16g, %.16g\n" \ " %.16g, %.16g, %.16g" % ( \ adfGeoTransform[0], \ adfGeoTransform[1], \ adfGeoTransform[2], \ adfGeoTransform[3], \ adfGeoTransform[4], \ adfGeoTransform[5] )) # -------------------------------------------------------------------- # Report GCPs. # -------------------------------------------------------------------- if bShowGCPs and hDataset.GetGCPCount() > 0: pszProjection = hDataset.GetGCPProjection() if pszProjection is not None: hSRS = osr.SpatialReference() if hSRS.ImportFromWkt(pszProjection ) == gdal.CE_None: pszPrettyWkt = hSRS.ExportToPrettyWkt(False) print( "GCP Projection = \n%s" % pszPrettyWkt ) else: print( "GCP Projection = %s" % \ pszProjection ) gcps = hDataset.GetGCPs() i = 0 for gcp in gcps: print( "GCP[%3d]: Id=%s, Info=%s\n" \ " (%.15g,%.15g) -> (%.15g,%.15g,%.15g)" % ( \ i, gcp.Id, gcp.Info, \ gcp.GCPPixel, gcp.GCPLine, \ gcp.GCPX, gcp.GCPY, gcp.GCPZ )) i = i + 1 # -------------------------------------------------------------------- # Report metadata. # -------------------------------------------------------------------- if bShowMetadata: papszMetadata = hDataset.GetMetadata_List() else: papszMetadata = None if bShowMetadata and papszMetadata is not None and len(papszMetadata) > 0 : print( "Metadata:" ) for metadata in papszMetadata: print( " %s" % metadata ) if bShowMetadata: for extra_domain in papszExtraMDDomains: papszMetadata = hDataset.GetMetadata_List(extra_domain) if papszMetadata is not None and len(papszMetadata) > 0 : print( "Metadata (%s):" % extra_domain) for metadata in papszMetadata: print( " %s" % metadata ) # -------------------------------------------------------------------- # Report "IMAGE_STRUCTURE" metadata. # -------------------------------------------------------------------- if bShowMetadata: papszMetadata = hDataset.GetMetadata_List("IMAGE_STRUCTURE") else: papszMetadata = None if bShowMetadata and papszMetadata is not None and len(papszMetadata) > 0 : print( "Image Structure Metadata:" ) for metadata in papszMetadata: print( " %s" % metadata ) # -------------------------------------------------------------------- # Report subdatasets. # -------------------------------------------------------------------- papszMetadata = hDataset.GetMetadata_List("SUBDATASETS") if papszMetadata is not None and len(papszMetadata) > 0 : print( "Subdatasets:" ) for metadata in papszMetadata: print( " %s" % metadata ) # -------------------------------------------------------------------- # Report geolocation. # -------------------------------------------------------------------- if bShowMetadata: papszMetadata = hDataset.GetMetadata_List("GEOLOCATION") else: papszMetadata = None if bShowMetadata and papszMetadata is not None and len(papszMetadata) > 0 : print( "Geolocation:" ) for metadata in papszMetadata: print( " %s" % metadata ) # -------------------------------------------------------------------- # Report RPCs # -------------------------------------------------------------------- if bShowMetadata: papszMetadata = hDataset.GetMetadata_List("RPC") else: papszMetadata = None if bShowMetadata and papszMetadata is not None and len(papszMetadata) > 0 : print( "RPC Metadata:" ) for metadata in papszMetadata: print( " %s" % metadata ) # -------------------------------------------------------------------- # Setup projected to lat/long transform if appropriate. # -------------------------------------------------------------------- if pszProjection is not None and len(pszProjection) > 0: hProj = osr.SpatialReference( pszProjection ) if hProj is not None: hLatLong = hProj.CloneGeogCS() if hLatLong is not None: gdal.PushErrorHandler( 'CPLQuietErrorHandler' ) hTransform = osr.CoordinateTransformation( hProj, hLatLong ) gdal.PopErrorHandler() if gdal.GetLastErrorMsg().find( 'Unable to load PROJ.4 library' ) != -1: hTransform = None # -------------------------------------------------------------------- # Report corners. # -------------------------------------------------------------------- print( "Corner Coordinates:" ) GDALInfoReportCorner( hDataset, hTransform, "Upper Left", \ 0.0, 0.0 ); GDALInfoReportCorner( hDataset, hTransform, "Lower Left", \ 0.0, hDataset.RasterYSize); GDALInfoReportCorner( hDataset, hTransform, "Upper Right", \ hDataset.RasterXSize, 0.0 ); GDALInfoReportCorner( hDataset, hTransform, "Lower Right", \ hDataset.RasterXSize, \ hDataset.RasterYSize ); GDALInfoReportCorner( hDataset, hTransform, "Center", \ hDataset.RasterXSize/2.0, \ hDataset.RasterYSize/2.0 ); # ==================================================================== # Loop over bands. # ==================================================================== for iBand in range(hDataset.RasterCount): hBand = hDataset.GetRasterBand(iBand+1 ) #if( bSample ) #{ # float afSample[10000]; # int nCount; # # nCount = GDALGetRandomRasterSample( hBand, 10000, afSample ); # print( "Got %d samples.\n", nCount ); #} (nBlockXSize, nBlockYSize) = hBand.GetBlockSize() print( "Band %d Block=%dx%d Type=%s, ColorInterp=%s" % ( iBand+1, \ nBlockXSize, nBlockYSize, \ gdal.GetDataTypeName(hBand.DataType), \ gdal.GetColorInterpretationName( \ hBand.GetRasterColorInterpretation()) )) if hBand.GetDescription() is not None \ and len(hBand.GetDescription()) > 0 : print( " Description = %s" % hBand.GetDescription() ) dfMin = hBand.GetMinimum() dfMax = hBand.GetMaximum() if dfMin is not None or dfMax is not None or bComputeMinMax: line = " " if dfMin is not None: line = line + ("Min=%.3f " % dfMin) if dfMax is not None: line = line + ("Max=%.3f " % dfMax) if bComputeMinMax: gdal.ErrorReset() adfCMinMax = hBand.ComputeRasterMinMax(False) if gdal.GetLastErrorType() == gdal.CE_None: line = line + ( " Computed Min/Max=%.3f,%.3f" % ( \ adfCMinMax[0], adfCMinMax[1] )) print( line ) stats = hBand.GetStatistics( bApproxStats, bStats) # Dirty hack to recognize if stats are valid. If invalid, the returned # stddev is negative if stats[3] >= 0.0: print( " Minimum=%.3f, Maximum=%.3f, Mean=%.3f, StdDev=%.3f" % ( \ stats[0], stats[1], stats[2], stats[3] )) if bReportHistograms: hist = hBand.GetDefaultHistogram(force = True, callback = gdal.TermProgress) if hist is not None: dfMin = hist[0] dfMax = hist[1] nBucketCount = hist[2] panHistogram = hist[3] print( " %d buckets from %g to %g:" % ( \ nBucketCount, dfMin, dfMax )) line = ' ' for bucket in panHistogram: line = line + ("%d " % bucket) print(line) if bComputeChecksum: print( " Checksum=%d" % hBand.Checksum()) dfNoData = hBand.GetNoDataValue() if dfNoData is not None: if dfNoData != dfNoData: print( " NoData Value=nan" ) else: print( " NoData Value=%.18g" % dfNoData ) if hBand.GetOverviewCount() > 0: line = " Overviews: " for iOverview in range(hBand.GetOverviewCount()): if iOverview != 0 : line = line + ", " hOverview = hBand.GetOverview( iOverview ); if hOverview is not None: line = line + ( "%dx%d" % (hOverview.XSize, hOverview.YSize)) pszResampling = \ hOverview.GetMetadataItem( "RESAMPLING", "" ) if pszResampling is not None \ and len(pszResampling) >= 12 \ and EQUAL(pszResampling[0:12],"AVERAGE_BIT2"): line = line + "*" else: line = line + "(null)" print(line) if bComputeChecksum: line = " Overviews checksum: " for iOverview in range(hBand.GetOverviewCount()): if iOverview != 0: line = line + ", " hOverview = hBand.GetOverview( iOverview ); if hOverview is not None: line = line + ( "%d" % hOverview.Checksum()) else: line = line + "(null)" print(line) if hBand.HasArbitraryOverviews(): print( " Overviews: arbitrary" ) nMaskFlags = hBand.GetMaskFlags() if (nMaskFlags & (gdal.GMF_NODATA|gdal.GMF_ALL_VALID)) == 0: hMaskBand = hBand.GetMaskBand() line = " Mask Flags: " if (nMaskFlags & gdal.GMF_PER_DATASET) != 0: line = line + "PER_DATASET " if (nMaskFlags & gdal.GMF_ALPHA) != 0: line = line + "ALPHA " if (nMaskFlags & gdal.GMF_NODATA) != 0: line = line + "NODATA " if (nMaskFlags & gdal.GMF_ALL_VALID) != 0: line = line + "ALL_VALID " print(line) if hMaskBand is not None and \ hMaskBand.GetOverviewCount() > 0: line = " Overviews of mask band: " for iOverview in range(hMaskBand.GetOverviewCount()): if iOverview != 0: line = line + ", " hOverview = hMaskBand.GetOverview( iOverview ); if hOverview is not None: line = line + ( "%d" % hOverview.Checksum()) else: line = line + "(null)" if len(hBand.GetUnitType()) > 0: print( " Unit Type: %s" % hBand.GetUnitType()) papszCategories = hBand.GetRasterCategoryNames() if papszCategories is not None: print( " Categories:" ); i = 0 for category in papszCategories: print( " %3d: %s" % (i, category) ) i = i + 1 if hBand.GetScale() != 1.0 or hBand.GetOffset() != 0.0: print( " Offset: %.15g, Scale:%.15g" % \ ( hBand.GetOffset(), hBand.GetScale())) if bShowMetadata: papszMetadata = hBand.GetMetadata_List() else: papszMetadata = None if bShowMetadata and papszMetadata is not None and len(papszMetadata) > 0 : print( " Metadata:" ) for metadata in papszMetadata: print( " %s" % metadata ) if bShowMetadata: papszMetadata = hBand.GetMetadata_List("IMAGE_STRUCTURE") else: papszMetadata = None if bShowMetadata and papszMetadata is not None and len(papszMetadata) > 0 : print( " Image Structure Metadata:" ) for metadata in papszMetadata: print( " %s" % metadata ) hTable = hBand.GetRasterColorTable() if hBand.GetRasterColorInterpretation() == gdal.GCI_PaletteIndex \ and hTable is not None: print( " Color Table (%s with %d entries)" % (\ gdal.GetPaletteInterpretationName( \ hTable.GetPaletteInterpretation( )), \ hTable.GetCount() )) if bShowColorTable: for i in range(hTable.GetCount()): sEntry = hTable.GetColorEntry(i) print( " %3d: %d,%d,%d,%d" % ( \ i, \ sEntry[0],\ sEntry[1],\ sEntry[2],\ sEntry[3] )) if bShowRAT: pass #hRAT = hBand.GetDefaultRAT() #GDALRATDumpReadable( hRAT, None ); return 0
class TestPyQgsOGRProviderGpkg(unittest.TestCase): @classmethod def setUpClass(cls): """Run before all tests""" QCoreApplication.setOrganizationName("QGIS_Test") QCoreApplication.setOrganizationDomain("TestPyQgsOGRProviderGpkg.com") QCoreApplication.setApplicationName("TestPyQgsOGRProviderGpkg") QgsSettings().clear() start_app() # Create test layer cls.basetestpath = tempfile.mkdtemp() @classmethod def tearDownClass(cls): """Run after all tests""" shutil.rmtree(cls.basetestpath, True) QgsSettings().clear() def testSingleToMultiPolygonPromotion(self): tmpfile = os.path.join(self.basetestpath, 'testSingleToMultiPolygonPromotion.gpkg') ds = ogr.GetDriverByName('GPKG').CreateDataSource(tmpfile) ds.CreateLayer('test', geom_type=ogr.wkbMultiPolygon) ds = None vl = QgsVectorLayer('{}|layerid=0'.format(tmpfile), 'test', 'ogr') f = QgsFeature() f.setGeometry(QgsGeometry.fromWkt('POLYGON ((0 0,0 1,1 1,0 0))')) vl.dataProvider().addFeatures([f]) got = [feat for feat in vl.getFeatures()][0] got_geom = got.geometry() reference = QgsGeometry.fromWkt( 'MultiPolygon (((0 0, 0 1, 1 1, 0 0)))') # The geometries must be binarily identical self.assertEqual( got_geom.asWkb(), reference.asWkb(), 'Expected {}, got {}'.format(reference.asWkt(), got_geom.asWkt())) def testCurveGeometryType(self): tmpfile = os.path.join(self.basetestpath, 'testCurveGeometryType.gpkg') ds = ogr.GetDriverByName('GPKG').CreateDataSource(tmpfile) ds.CreateLayer('test', geom_type=ogr.wkbCurvePolygon) ds = None vl = QgsVectorLayer('{}'.format(tmpfile), 'test', 'ogr') self.assertEqual(vl.dataProvider().subLayers(), [ QgsDataProvider.SUBLAYER_SEPARATOR.join( ['0', 'test', '0', 'CurvePolygon', 'geom']) ]) f = QgsFeature() f.setGeometry(QgsGeometry.fromWkt('POLYGON ((0 0,0 1,1 1,0 0))')) vl.dataProvider().addFeatures([f]) got = [feat for feat in vl.getFeatures()][0] got_geom = got.geometry() reference = QgsGeometry.fromWkt( 'CurvePolygon (((0 0, 0 1, 1 1, 0 0)))') # The geometries must be binarily identical self.assertEqual( got_geom.asWkb(), reference.asWkb(), 'Expected {}, got {}'.format(reference.asWkt(), got_geom.asWkt())) def internalTestBug15351(self, orderClosing): tmpfile = os.path.join(self.basetestpath, 'testBug15351.gpkg') ds = ogr.GetDriverByName('GPKG').CreateDataSource(tmpfile) lyr = ds.CreateLayer('test', geom_type=ogr.wkbPoint) f = ogr.Feature(lyr.GetLayerDefn()) f.SetGeometry(ogr.CreateGeometryFromWkt('POINT(0 0)')) lyr.CreateFeature(f) f = None ds = None vl = QgsVectorLayer(u'{}'.format(tmpfile), u'test', u'ogr') self.assertTrue(vl.startEditing()) self.assertTrue( vl.changeGeometry(1, QgsGeometry.fromWkt('Point (3 50)'))) # Iterate over features (will open a new OGR connection), but do not # close the iterator for now it = vl.getFeatures() f = QgsFeature() it.nextFeature(f) if orderClosing == 'closeIter_commit_closeProvider': it = None # Commit changes cbk = ErrorReceiver() vl.dataProvider().raiseError.connect(cbk.receiveError) self.assertTrue(vl.commitChanges()) self.assertIsNone(cbk.msg) # Close layer and iterator in different orders if orderClosing == 'closeIter_commit_closeProvider': vl = None elif orderClosing == 'commit_closeProvider_closeIter': vl = None it = None else: assert orderClosing == 'commit_closeIter_closeProvider' it = None vl = None # Test that we succeeded restoring default journal mode, and we # are not let in WAL mode. ds = ogr.Open(tmpfile) lyr = ds.ExecuteSQL('PRAGMA journal_mode') f = lyr.GetNextFeature() res = f.GetField(0) ds.ReleaseResultSet(lyr) ds = None self.assertEqual(res, 'delete') # We need GDAL 2.0 to issue PRAGMA journal_mode # Note: for that case, we don't strictly need turning on WAL def testBug15351_closeIter_commit_closeProvider(self): self.internalTestBug15351('closeIter_commit_closeProvider') # We need GDAL 2.0 to issue PRAGMA journal_mode def testBug15351_commit_closeProvider_closeIter(self): self.internalTestBug15351('commit_closeProvider_closeIter') # We need GDAL 2.0 to issue PRAGMA journal_mode def testBug15351_commit_closeIter_closeProvider(self): self.internalTestBug15351('commit_closeIter_closeProvider') @unittest.skip( int(gdal.VersionInfo('VERSION_NUM')) < GDAL_COMPUTE_VERSION(2, 1, 2)) def testGeopackageExtentUpdate(self): ''' test https://issues.qgis.org/issues/15273 ''' tmpfile = os.path.join(self.basetestpath, 'testGeopackageExtentUpdate.gpkg') ds = ogr.GetDriverByName('GPKG').CreateDataSource(tmpfile) lyr = ds.CreateLayer('test', geom_type=ogr.wkbPoint) f = ogr.Feature(lyr.GetLayerDefn()) f.SetGeometry(ogr.CreateGeometryFromWkt('POINT(0 0)')) lyr.CreateFeature(f) f = ogr.Feature(lyr.GetLayerDefn()) f.SetGeometry(ogr.CreateGeometryFromWkt('POINT(1 1)')) lyr.CreateFeature(f) f = None f = ogr.Feature(lyr.GetLayerDefn()) f.SetGeometry(ogr.CreateGeometryFromWkt('POINT(1 0.5)')) lyr.CreateFeature(f) f = None gdal.ErrorReset() ds.ExecuteSQL('RECOMPUTE EXTENT ON test') has_error = gdal.GetLastErrorMsg() != '' ds = None if has_error: print('Too old GDAL trunk version. Please update') return vl = QgsVectorLayer(u'{}'.format(tmpfile), u'test', u'ogr') # Test moving a geometry that touches the bbox self.assertTrue(vl.startEditing()) self.assertTrue( vl.changeGeometry(1, QgsGeometry.fromWkt('Point (0.5 0)'))) self.assertTrue(vl.commitChanges()) reference = QgsGeometry.fromRect(QgsRectangle(0.5, 0.0, 1.0, 1.0)) provider_extent = QgsGeometry.fromRect(vl.extent()) self.assertTrue( QgsGeometry.compare(provider_extent.asPolygon()[0], reference.asPolygon()[0], 0.00001), provider_extent.asPolygon()[0]) # Test deleting a geometry that touches the bbox self.assertTrue(vl.startEditing()) self.assertTrue(vl.deleteFeature(2)) self.assertTrue(vl.commitChanges()) reference = QgsGeometry.fromRect(QgsRectangle(0.5, 0.0, 1.0, 0.5)) provider_extent = QgsGeometry.fromRect(vl.extent()) self.assertTrue( QgsGeometry.compare(provider_extent.asPolygon()[0], reference.asPolygon()[0], 0.00001), provider_extent.asPolygon()[0]) def testSelectSubsetString(self): tmpfile = os.path.join(self.basetestpath, 'testSelectSubsetString.gpkg') ds = ogr.GetDriverByName('GPKG').CreateDataSource(tmpfile) lyr = ds.CreateLayer('test', geom_type=ogr.wkbMultiPolygon) lyr.CreateField(ogr.FieldDefn('foo', ogr.OFTString)) f = ogr.Feature(lyr.GetLayerDefn()) f['foo'] = 'bar' lyr.CreateFeature(f) f = None f = ogr.Feature(lyr.GetLayerDefn()) f['foo'] = 'baz' lyr.CreateFeature(f) f = None ds = None vl = QgsVectorLayer('{}|layerid=0'.format(tmpfile), 'test', 'ogr') vl.setSubsetString("SELECT fid, foo FROM test WHERE foo = 'baz'") got = [feat for feat in vl.getFeatures()] self.assertEqual(len(got), 1) def testStyle(self): # First test with invalid URI vl = QgsVectorLayer('/idont/exist.gpkg', 'test', 'ogr') self.assertFalse( vl.dataProvider().isSaveAndLoadStyleToDatabaseSupported()) related_count, idlist, namelist, desclist, errmsg = vl.listStylesInDatabase( ) self.assertEqual(related_count, -1) self.assertEqual(idlist, []) self.assertEqual(namelist, []) self.assertEqual(desclist, []) self.assertNotEqual(errmsg, "") qml, errmsg = vl.getStyleFromDatabase("1") self.assertEqual(qml, "") self.assertNotEqual(errmsg, "") qml, success = vl.loadNamedStyle('/idont/exist.gpkg') self.assertFalse(success) errorMsg = vl.saveStyleToDatabase("name", "description", False, "") self.assertNotEqual(errorMsg, "") # Now with valid URI tmpfile = os.path.join(self.basetestpath, 'testStyle.gpkg') ds = ogr.GetDriverByName('GPKG').CreateDataSource(tmpfile) lyr = ds.CreateLayer('test', geom_type=ogr.wkbMultiPolygon) lyr.CreateField(ogr.FieldDefn('foo', ogr.OFTString)) f = ogr.Feature(lyr.GetLayerDefn()) f['foo'] = 'bar' lyr.CreateFeature(f) f = None lyr = ds.CreateLayer('test2', geom_type=ogr.wkbMultiPolygon) lyr.CreateField(ogr.FieldDefn('foo', ogr.OFTString)) f = ogr.Feature(lyr.GetLayerDefn()) f['foo'] = 'bar' lyr.CreateFeature(f) f = None ds = None vl = QgsVectorLayer('{}|layername=test'.format(tmpfile), 'test', 'ogr') self.assertTrue(vl.isValid()) vl2 = QgsVectorLayer('{}|layername=test2'.format(tmpfile), 'test2', 'ogr') self.assertTrue(vl2.isValid()) self.assertTrue( vl.dataProvider().isSaveAndLoadStyleToDatabaseSupported()) related_count, idlist, namelist, desclist, errmsg = vl.listStylesInDatabase( ) self.assertEqual(related_count, 0) self.assertEqual(idlist, []) self.assertEqual(namelist, []) self.assertEqual(desclist, []) self.assertNotEqual(errmsg, "") qml, errmsg = vl.getStyleFromDatabase("not_existing") self.assertEqual(qml, "") self.assertNotEqual(errmsg, "") qml, success = vl.loadNamedStyle('{}|layerid=0'.format(tmpfile)) self.assertFalse(success) errorMsg = vl.saveStyleToDatabase("name", "description", False, "") self.assertEqual(errorMsg, "") qml, errmsg = vl.getStyleFromDatabase("not_existing") self.assertEqual(qml, "") self.assertNotEqual(errmsg, "") related_count, idlist, namelist, desclist, errmsg = vl.listStylesInDatabase( ) self.assertEqual(related_count, 1) self.assertEqual(errmsg, "") self.assertEqual(idlist, ['1']) self.assertEqual(namelist, ['name']) self.assertEqual(desclist, ['description']) qml, errmsg = vl.getStyleFromDatabase("100") self.assertEqual(qml, "") self.assertNotEqual(errmsg, "") qml, errmsg = vl.getStyleFromDatabase("1") self.assertTrue(qml.startswith('<!DOCTYPE qgis'), qml) self.assertEqual(errmsg, "") # Try overwrite it but simulate answer no settings = QgsSettings() settings.setValue("/qgis/overwriteStyle", False) errorMsg = vl.saveStyleToDatabase("name", "description_bis", False, "") self.assertNotEqual(errorMsg, "") related_count, idlist, namelist, desclist, errmsg = vl.listStylesInDatabase( ) self.assertEqual(related_count, 1) self.assertEqual(errmsg, "") self.assertEqual(idlist, ['1']) self.assertEqual(namelist, ['name']) self.assertEqual(desclist, ['description']) # Try overwrite it and simulate answer yes settings = QgsSettings() settings.setValue("/qgis/overwriteStyle", True) errorMsg = vl.saveStyleToDatabase("name", "description_bis", False, "") self.assertEqual(errorMsg, "") related_count, idlist, namelist, desclist, errmsg = vl.listStylesInDatabase( ) self.assertEqual(related_count, 1) self.assertEqual(errmsg, "") self.assertEqual(idlist, ['1']) self.assertEqual(namelist, ['name']) self.assertEqual(desclist, ['description_bis']) errorMsg = vl2.saveStyleToDatabase("name_test2", "description_test2", True, "") self.assertEqual(errorMsg, "") errorMsg = vl.saveStyleToDatabase("name2", "description2", True, "") self.assertEqual(errorMsg, "") errorMsg = vl.saveStyleToDatabase("name3", "description3", True, "") self.assertEqual(errorMsg, "") related_count, idlist, namelist, desclist, errmsg = vl.listStylesInDatabase( ) self.assertEqual(related_count, 3) self.assertEqual(errmsg, "") self.assertEqual(idlist, ['1', '3', '4', '2']) self.assertEqual(namelist, ['name', 'name2', 'name3', 'name_test2']) self.assertEqual( desclist, ['description_bis', 'description2', 'description3', 'name_test2']) # Check that layers_style table is not list in subLayers() vl = QgsVectorLayer(tmpfile, 'test', 'ogr') sublayers = vl.dataProvider().subLayers() self.assertEqual(len(sublayers), 2, sublayers) def testDisablewalForSqlite3(self): ''' Test disabling walForSqlite3 setting ''' QgsSettings().setValue("/qgis/walForSqlite3", False) tmpfile = os.path.join(self.basetestpath, 'testDisablewalForSqlite3.gpkg') ds = ogr.GetDriverByName('GPKG').CreateDataSource(tmpfile) lyr = ds.CreateLayer('test', geom_type=ogr.wkbPoint) lyr.CreateField(ogr.FieldDefn('attr0', ogr.OFTInteger)) lyr.CreateField(ogr.FieldDefn('attr1', ogr.OFTInteger)) f = ogr.Feature(lyr.GetLayerDefn()) f.SetGeometry(ogr.CreateGeometryFromWkt('POINT(0 0)')) lyr.CreateFeature(f) f = None ds = None vl = QgsVectorLayer(u'{}'.format(tmpfile), u'test', u'ogr') # Test that we are using default delete mode and not WAL ds = ogr.Open(tmpfile) lyr = ds.ExecuteSQL('PRAGMA journal_mode') f = lyr.GetNextFeature() res = f.GetField(0) ds.ReleaseResultSet(lyr) ds = None self.assertEqual(res, 'delete') self.assertTrue(vl.startEditing()) feature = next(vl.getFeatures()) self.assertTrue(vl.changeAttributeValue(feature.id(), 1, 1001)) # Commit changes cbk = ErrorReceiver() vl.dataProvider().raiseError.connect(cbk.receiveError) self.assertTrue(vl.commitChanges()) self.assertIsNone(cbk.msg) vl = None QgsSettings().setValue("/qgis/walForSqlite3", None) def testSimulatedDBManagerImport(self): uri = 'point?field=f1:int' uri += '&field=f2:double(6,4)' uri += '&field=f3:string(20)' lyr = QgsVectorLayer(uri, "x", "memory") self.assertTrue(lyr.isValid()) f = QgsFeature(lyr.fields()) f['f1'] = 1 f['f2'] = 123.456 f['f3'] = '12345678.90123456789' f2 = QgsFeature(lyr.fields()) f2['f1'] = 2 lyr.dataProvider().addFeatures([f, f2]) tmpfile = os.path.join(self.basetestpath, 'testSimulatedDBManagerImport.gpkg') ds = ogr.GetDriverByName('GPKG').CreateDataSource(tmpfile) ds = None options = {} options['update'] = True options['driverName'] = 'GPKG' options['layerName'] = 'my_out_table' err = QgsVectorLayerExporter.exportLayer(lyr, tmpfile, "ogr", lyr.crs(), False, options) self.assertEqual(err[0], QgsVectorLayerExporter.NoError, 'unexpected import error {0}'.format(err)) lyr = QgsVectorLayer(tmpfile + "|layername=my_out_table", "y", "ogr") self.assertTrue(lyr.isValid()) features = lyr.getFeatures() f = next(features) self.assertEqual(f['f1'], 1) self.assertEqual(f['f2'], 123.456) self.assertEqual(f['f3'], '12345678.90123456789') f = next(features) self.assertEqual(f['f1'], 2) features = None # Test overwriting without overwrite option err = QgsVectorLayerExporter.exportLayer(lyr, tmpfile, "ogr", lyr.crs(), False, options) self.assertEqual(err[0], QgsVectorLayerExporter.ErrCreateDataSource) # Test overwriting lyr = QgsVectorLayer(uri, "x", "memory") self.assertTrue(lyr.isValid()) f = QgsFeature(lyr.fields()) f['f1'] = 3 lyr.dataProvider().addFeatures([f]) options['overwrite'] = True err = QgsVectorLayerExporter.exportLayer(lyr, tmpfile, "ogr", lyr.crs(), False, options) self.assertEqual(err[0], QgsVectorLayerExporter.NoError, 'unexpected import error {0}'.format(err)) lyr = QgsVectorLayer(tmpfile + "|layername=my_out_table", "y", "ogr") self.assertTrue(lyr.isValid()) features = lyr.getFeatures() f = next(features) self.assertEqual(f['f1'], 3) features = None def testExportLayerToExistingDatabase(self): fields = QgsFields() fields.append(QgsField('f1', QVariant.Int)) tmpfile = os.path.join(self.basetestpath, 'testCreateNewGeopackage.gpkg') options = {} options['update'] = True options['driverName'] = 'GPKG' options['layerName'] = 'table1' exporter = QgsVectorLayerExporter(tmpfile, "ogr", fields, QgsWkbTypes.Polygon, QgsCoordinateReferenceSystem(3111), False, options) self.assertFalse( exporter.errorCode(), 'unexpected export error {}: {}'.format(exporter.errorCode(), exporter.errorMessage())) options['layerName'] = 'table2' exporter = QgsVectorLayerExporter(tmpfile, "ogr", fields, QgsWkbTypes.Point, QgsCoordinateReferenceSystem(3113), False, options) self.assertFalse( exporter.errorCode(), 'unexpected export error {} : {}'.format(exporter.errorCode(), exporter.errorMessage())) del exporter # make sure layers exist lyr = QgsVectorLayer('{}|layername=table1'.format(tmpfile), "lyr1", "ogr") self.assertTrue(lyr.isValid()) self.assertEqual(lyr.crs().authid(), 'EPSG:3111') self.assertEqual(lyr.wkbType(), QgsWkbTypes.Polygon) lyr2 = QgsVectorLayer('{}|layername=table2'.format(tmpfile), "lyr2", "ogr") self.assertTrue(lyr2.isValid()) self.assertEqual(lyr2.crs().authid(), 'EPSG:3113') self.assertEqual(lyr2.wkbType(), QgsWkbTypes.Point) def testGeopackageTwoLayerEdition(self): ''' test https://issues.qgis.org/issues/17034 ''' tmpfile = os.path.join(self.basetestpath, 'testGeopackageTwoLayerEdition.gpkg') ds = ogr.GetDriverByName('GPKG').CreateDataSource(tmpfile) lyr = ds.CreateLayer('layer1', geom_type=ogr.wkbPoint) lyr.CreateField(ogr.FieldDefn('attr', ogr.OFTInteger)) f = ogr.Feature(lyr.GetLayerDefn()) f.SetGeometry(ogr.CreateGeometryFromWkt('POINT(0 0)')) lyr.CreateFeature(f) f = None lyr = ds.CreateLayer('layer2', geom_type=ogr.wkbPoint) lyr.CreateField(ogr.FieldDefn('attr', ogr.OFTInteger)) f = ogr.Feature(lyr.GetLayerDefn()) f.SetGeometry(ogr.CreateGeometryFromWkt('POINT(1 1)')) lyr.CreateFeature(f) f = None ds = None vl1 = QgsVectorLayer(u'{}'.format(tmpfile) + "|layername=layer1", u'layer1', u'ogr') vl2 = QgsVectorLayer(u'{}'.format(tmpfile) + "|layername=layer2", u'layer2', u'ogr') # Edit vl1, vl2 multiple times self.assertTrue(vl1.startEditing()) self.assertTrue(vl2.startEditing()) self.assertTrue( vl1.changeGeometry(1, QgsGeometry.fromWkt('Point (2 2)'))) self.assertTrue( vl2.changeGeometry(1, QgsGeometry.fromWkt('Point (3 3)'))) self.assertTrue(vl1.commitChanges()) self.assertTrue(vl2.commitChanges()) self.assertTrue(vl1.startEditing()) self.assertTrue(vl2.startEditing()) self.assertTrue(vl1.changeAttributeValue(1, 1, 100)) self.assertTrue(vl2.changeAttributeValue(1, 1, 101)) self.assertTrue(vl1.commitChanges()) self.assertTrue(vl2.commitChanges()) self.assertTrue(vl1.startEditing()) self.assertTrue(vl2.startEditing()) self.assertTrue( vl1.changeGeometry(1, QgsGeometry.fromWkt('Point (4 4)'))) self.assertTrue( vl2.changeGeometry(1, QgsGeometry.fromWkt('Point (5 5)'))) self.assertTrue(vl1.commitChanges()) self.assertTrue(vl2.commitChanges()) vl1 = None vl2 = None # Check everything is as expected after re-opening vl1 = QgsVectorLayer(u'{}'.format(tmpfile) + "|layername=layer1", u'layer1', u'ogr') vl2 = QgsVectorLayer(u'{}'.format(tmpfile) + "|layername=layer2", u'layer2', u'ogr') got = [feat for feat in vl1.getFeatures()][0] got_geom = got.geometry() self.assertEqual(got['attr'], 100) reference = QgsGeometry.fromWkt('Point (4 4)') self.assertEqual( got_geom.asWkb(), reference.asWkb(), 'Expected {}, got {}'.format(reference.asWkt(), got_geom.asWkt())) got = [feat for feat in vl2.getFeatures()][0] got_geom = got.geometry() self.assertEqual(got['attr'], 101) reference = QgsGeometry.fromWkt('Point (5 5)') self.assertEqual( got_geom.asWkb(), reference.asWkb(), 'Expected {}, got {}'.format(reference.asWkt(), got_geom.asWkt())) def testReplaceLayerWhileOpen(self): ''' Replace an existing geopackage layer whilst it's open in the project''' tmpfile = os.path.join(self.basetestpath, 'testGeopackageReplaceOpenLayer.gpkg') ds = ogr.GetDriverByName('GPKG').CreateDataSource(tmpfile) lyr = ds.CreateLayer('layer1', geom_type=ogr.wkbPoint) lyr.CreateField(ogr.FieldDefn('attr', ogr.OFTInteger)) lyr.CreateField(ogr.FieldDefn('attr2', ogr.OFTInteger)) f = ogr.Feature(lyr.GetLayerDefn()) f.SetGeometry(ogr.CreateGeometryFromWkt('POINT(0 0)')) lyr.CreateFeature(f) f = None vl1 = QgsVectorLayer(u'{}'.format(tmpfile) + "|layername=layer1", u'layer1', u'ogr') p = QgsProject() p.addMapLayer(vl1) request = QgsFeatureRequest().setSubsetOfAttributes([0]) features = [f for f in vl1.getFeatures(request)] self.assertEqual(len(features), 1) # now, overwrite the layer with a different geometry type and fields ds.DeleteLayer('layer1') lyr = ds.CreateLayer('layer1', geom_type=ogr.wkbLineString) lyr.CreateField(ogr.FieldDefn('attr', ogr.OFTString)) f = ogr.Feature(lyr.GetLayerDefn()) f.SetGeometry(ogr.CreateGeometryFromWkt('LineString(0 0, 1 1)')) lyr.CreateFeature(f) f = None vl2 = QgsVectorLayer(u'{}'.format(tmpfile) + "|layername=layer1", u'layer2', u'ogr') p.addMapLayer(vl2) features = [f for f in vl1.getFeatures(request)] self.assertEqual(len(features), 1) def testSublayerWithComplexLayerName(self): ''' Test reading a gpkg with a sublayer name containing : ''' tmpfile = os.path.join(self.basetestpath, 'testGeopackageComplexLayerName.gpkg') ds = ogr.GetDriverByName('GPKG').CreateDataSource(tmpfile) lyr = ds.CreateLayer('layer1:', geom_type=ogr.wkbPoint, options=['GEOMETRY_NAME=geom:']) lyr.CreateField(ogr.FieldDefn('attr', ogr.OFTInteger)) f = ogr.Feature(lyr.GetLayerDefn()) f.SetGeometry(ogr.CreateGeometryFromWkt('POINT(0 0)')) lyr.CreateFeature(f) f = None vl = QgsVectorLayer(u'{}'.format(tmpfile), u'layer', u'ogr') self.assertEqual(vl.dataProvider().subLayers(), [ QgsDataProvider.SUBLAYER_SEPARATOR.join( ['0', 'layer1:', '1', 'Point', 'geom:']) ]) def testGeopackageManyLayers(self): ''' test opening more than 64 layers without running out of Spatialite connections ''' tmpfile = os.path.join(self.basetestpath, 'testGeopackageManyLayers.gpkg') ds = ogr.GetDriverByName('GPKG').CreateDataSource(tmpfile) for i in range(70): lyr = ds.CreateLayer('layer%d' % i, geom_type=ogr.wkbPoint) f = ogr.Feature(lyr.GetLayerDefn()) f.SetGeometry(ogr.CreateGeometryFromWkt('POINT(%d 0)' % i)) lyr.CreateFeature(f) f = None ds = None vl_tab = [] for i in range(70): layername = 'layer%d' % i vl = QgsVectorLayer( u'{}'.format(tmpfile) + "|layername=" + layername, layername, u'ogr') self.assertTrue(vl.isValid()) vl_tab += [vl] count = count_opened_filedescriptors(tmpfile) if count > 0: self.assertEqual(count, 1) for i in range(70): got = [feat for feat in vl.getFeatures()] self.assertTrue(len(got) == 1) # We shouldn't have more than 2 file handles opened: # one shared by the QgsOgrProvider object # one shared by the feature iterators count = count_opened_filedescriptors(tmpfile) if count > 0: self.assertEqual(count, 2) # Re-open an already opened layers. We should get a new handle layername = 'layer%d' % 0 vl_extra0 = QgsVectorLayer( u'{}'.format(tmpfile) + "|layername=" + layername, layername, u'ogr') self.assertTrue(vl_extra0.isValid()) countNew = count_opened_filedescriptors(tmpfile) if countNew > 0: self.assertLessEqual(countNew, 4) # for some reason we get 4 and not 3 layername = 'layer%d' % 1 vl_extra1 = QgsVectorLayer( u'{}'.format(tmpfile) + "|layername=" + layername, layername, u'ogr') self.assertTrue(vl_extra1.isValid()) countNew2 = count_opened_filedescriptors(tmpfile) self.assertEqual(countNew2, countNew) def testGeopackageRefreshIfTableListUpdated(self): ''' test that creating/deleting a layer is reflected when opening a new layer ''' tmpfile = os.path.join(self.basetestpath, 'testGeopackageRefreshIfTableListUpdated.gpkg') ds = ogr.GetDriverByName('GPKG').CreateDataSource(tmpfile) ds.CreateLayer('test', geom_type=ogr.wkbPoint) ds = None vl = QgsVectorLayer(u'{}'.format(tmpfile) + "|layername=" + "test", 'test', u'ogr') self.assertTrue(vl.extent().isNull()) time.sleep(1) # so timestamp gets updated ds = ogr.Open(tmpfile, update=1) ds.CreateLayer('test2', geom_type=ogr.wkbPoint) ds = None vl2 = QgsVectorLayer(u'{}'.format(tmpfile), 'test', u'ogr') vl2.subLayers() self.assertEqual(vl2.dataProvider().subLayers(), [ QgsDataProvider.SUBLAYER_SEPARATOR.join( ['0', 'test', '0', 'Point', 'geom']), QgsDataProvider.SUBLAYER_SEPARATOR.join( ['1', 'test2', '0', 'Point', 'geom']) ]) def testGeopackageLargeFID(self): tmpfile = os.path.join(self.basetestpath, 'testGeopackageLargeFID.gpkg') ds = ogr.GetDriverByName('GPKG').CreateDataSource(tmpfile) lyr = ds.CreateLayer('test', geom_type=ogr.wkbPoint) lyr.CreateField(ogr.FieldDefn('str_field', ogr.OFTString)) ds = None vl = QgsVectorLayer(u'{}'.format(tmpfile) + "|layername=" + "test", 'test', u'ogr') f = QgsFeature() f.setAttributes([1234567890123, None]) self.assertTrue(vl.startEditing()) self.assertTrue(vl.dataProvider().addFeatures([f])) self.assertTrue(vl.commitChanges()) got = [feat for feat in vl.getFeatures()][0] self.assertEqual(got['fid'], 1234567890123) self.assertTrue(vl.startEditing()) self.assertTrue( vl.changeGeometry(1234567890123, QgsGeometry.fromWkt('Point (3 50)'))) self.assertTrue(vl.changeAttributeValue(1234567890123, 1, 'foo')) self.assertTrue(vl.commitChanges()) got = [feat for feat in vl.getFeatures()][0] self.assertEqual(got['str_field'], 'foo') got_geom = got.geometry() self.assertIsNotNone(got_geom) self.assertTrue(vl.startEditing()) self.assertTrue(vl.deleteFeature(1234567890123)) self.assertTrue(vl.commitChanges()) def test_AddFeatureNullFid(self): """Test gpkg feature with NULL fid can be added""" tmpfile = os.path.join(self.basetestpath, 'testGeopackageSplitFeatures.gpkg') ds = ogr.GetDriverByName('GPKG').CreateDataSource(tmpfile) lyr = ds.CreateLayer('test', geom_type=ogr.wkbPolygon) lyr.CreateField(ogr.FieldDefn('str_field', ogr.OFTString)) ds = None layer = QgsVectorLayer(u'{}'.format(tmpfile) + "|layername=" + "test", 'test', u'ogr') # Check that pk field has unique constraint fields = layer.fields() pkfield = fields.at(0) self.assertTrue(pkfield.constraints().constraints() & QgsFieldConstraints.ConstraintUnique) # Test add feature with default Fid (NULL) layer.startEditing() f = QgsFeature() feat = QgsFeature(layer.fields()) feat.setGeometry( QgsGeometry.fromWkt('Polygon ((0 0, 0 1, 1 1, 1 0, 0 0))')) feat.setAttribute(1, 'test_value') layer.addFeature(feat) self.assertTrue(layer.commitChanges()) self.assertEqual(layer.featureCount(), 1) def test_SplitFeature(self): """Test gpkg feature can be split""" tmpfile = os.path.join(self.basetestpath, 'testGeopackageSplitFeatures.gpkg') ds = ogr.GetDriverByName('GPKG').CreateDataSource(tmpfile) lyr = ds.CreateLayer('test', geom_type=ogr.wkbPolygon) lyr.CreateField(ogr.FieldDefn('str_field', ogr.OFTString)) f = ogr.Feature(lyr.GetLayerDefn()) f.SetGeometry( ogr.CreateGeometryFromWkt('POLYGON ((0 0,0 1,1 1,1 0,0 0))')) lyr.CreateFeature(f) f = None ds = None # Split features layer = QgsVectorLayer(u'{}'.format(tmpfile) + "|layername=" + "test", 'test', u'ogr') self.assertTrue(layer.isValid()) self.assertTrue(layer.isSpatial()) self.assertEqual([f for f in layer.getFeatures()][0].geometry().asWkt(), 'Polygon ((0 0, 0 1, 1 1, 1 0, 0 0))') layer.startEditing() self.assertEqual( layer.splitFeatures( [QgsPointXY(0.5, 0), QgsPointXY(0.5, 1)], 0), 0) self.assertTrue(layer.commitChanges()) self.assertEqual(layer.featureCount(), 2) layer = QgsVectorLayer(u'{}'.format(tmpfile) + "|layername=" + "test", 'test', u'ogr') self.assertEqual(layer.featureCount(), 2) self.assertEqual([f for f in layer.getFeatures()][0].geometry().asWkt(), 'Polygon ((0.5 0, 0.5 1, 1 1, 1 0, 0.5 0))') self.assertEqual([f for f in layer.getFeatures()][1].geometry().asWkt(), 'Polygon ((0.5 1, 0.5 0, 0 0, 0 1, 0.5 1))') def testCreateAttributeIndex(self): tmpfile = os.path.join(self.basetestpath, 'testGeopackageAttributeIndex.gpkg') ds = ogr.GetDriverByName('GPKG').CreateDataSource(tmpfile) lyr = ds.CreateLayer('test', geom_type=ogr.wkbPolygon) lyr.CreateField(ogr.FieldDefn('str_field', ogr.OFTString)) lyr.CreateField(ogr.FieldDefn('str_field2', ogr.OFTString)) f = None ds = None vl = QgsVectorLayer(u'{}'.format(tmpfile) + "|layername=" + "test", 'test', u'ogr') self.assertTrue(vl.isValid()) self.assertTrue(vl.dataProvider().capabilities() & QgsVectorDataProvider.CreateAttributeIndex) self.assertFalse(vl.dataProvider().createAttributeIndex(-1)) self.assertFalse(vl.dataProvider().createAttributeIndex(100)) # should not be allowed - there's already a index on the primary key self.assertFalse(vl.dataProvider().createAttributeIndex(0)) self.assertTrue(vl.dataProvider().createAttributeIndex(1)) con = spatialite_connect(tmpfile, isolation_level=None) cur = con.cursor() rs = cur.execute( "SELECT * FROM sqlite_master WHERE type='index' AND tbl_name='test'" ) res = [row for row in rs] self.assertEqual(len(res), 1) index_name = res[0][1] rs = cur.execute("PRAGMA index_info({})".format(index_name)) res = [row for row in rs] self.assertEqual(len(res), 1) self.assertEqual(res[0][2], 'str_field') # second index self.assertTrue(vl.dataProvider().createAttributeIndex(2)) rs = cur.execute( "SELECT * FROM sqlite_master WHERE type='index' AND tbl_name='test'" ) res = [row for row in rs] self.assertEqual(len(res), 2) indexed_columns = [] for row in res: index_name = row[1] rs = cur.execute("PRAGMA index_info({})".format(index_name)) res = [row for row in rs] self.assertEqual(len(res), 1) indexed_columns.append(res[0][2]) self.assertCountEqual(indexed_columns, ['str_field', 'str_field2']) con.close() def testCreateSpatialIndex(self): tmpfile = os.path.join(self.basetestpath, 'testGeopackageSpatialIndex.gpkg') ds = ogr.GetDriverByName('GPKG').CreateDataSource(tmpfile) lyr = ds.CreateLayer('test', geom_type=ogr.wkbPolygon, options=['SPATIAL_INDEX=NO']) lyr.CreateField(ogr.FieldDefn('str_field', ogr.OFTString)) lyr.CreateField(ogr.FieldDefn('str_field2', ogr.OFTString)) f = None ds = None vl = QgsVectorLayer(u'{}'.format(tmpfile) + "|layername=" + "test", 'test', u'ogr') self.assertTrue(vl.isValid()) self.assertTrue(vl.dataProvider().capabilities() & QgsVectorDataProvider.CreateSpatialIndex) self.assertTrue(vl.dataProvider().createSpatialIndex())
class TestPyQgsProviderConnectionPostgres(unittest.TestCase, TestPyQgsProviderConnectionBase): # Provider test cases must define the string URI for the test uri = '' # Provider test cases must define the provider name (e.g. "postgres" or "ogr") providerKey = 'postgres' @classmethod def setUpClass(cls): """Run before all tests""" TestPyQgsProviderConnectionBase.setUpClass() cls.postgres_conn = "service='qgis_test'" if 'QGIS_PGTEST_DB' in os.environ: cls.postgres_conn = os.environ['QGIS_PGTEST_DB'] # Create test layers vl = QgsVectorLayer( cls.postgres_conn + ' sslmode=disable key=\'"key1","key2"\' srid=4326 type=POINT table="qgis_test"."someData" (geom) sql=', 'test', 'postgres') assert vl.isValid() cls.uri = cls.postgres_conn + ' sslmode=disable' def test_postgis_connections_from_uri(self): """Create a connection from a layer uri and retrieve it""" md = QgsProviderRegistry.instance().providerMetadata('postgres') vl = QgsVectorLayer( self.postgres_conn + ' sslmode=disable key=\'"key1","key2"\' srid=4326 type=POINT table="qgis_test"."someData" (geom) sql=', 'test', 'postgres') conn = md.createConnection(vl.dataProvider().uri().uri(), {}) self.assertEqual(conn.uri(), self.uri) # Test table(), throws if not found table_info = conn.table('qgis_test', 'someData') table_info = conn.table('qgis_test', 'Raster1') # Test raster self.assertEqual(conn.tableUri('qgis_test', 'Raster1'), '%s table="qgis_test"."Raster1"' % self.uri) rl = QgsRasterLayer(conn.tableUri('qgis_test', 'Raster1'), 'r1', 'postgresraster') self.assertTrue(rl.isValid()) def test_postgis_table_uri(self): """Create a connection from a layer uri and create a table URI""" md = QgsProviderRegistry.instance().providerMetadata('postgres') conn = md.createConnection(self.uri, {}) vl = QgsVectorLayer(conn.tableUri('qgis_test', 'geometries_table'), 'my', 'postgres') self.assertTrue(vl.isValid()) def test_postgis_connections(self): """Create some connections and retrieve them""" md = QgsProviderRegistry.instance().providerMetadata('postgres') conn = md.createConnection(self.uri, {}) md.saveConnection(conn, 'qgis_test1') # Retrieve capabilities capabilities = conn.capabilities() self.assertTrue( bool(capabilities & QgsAbstractDatabaseProviderConnection.Tables)) self.assertTrue( bool(capabilities & QgsAbstractDatabaseProviderConnection.Schemas)) self.assertTrue( bool(capabilities & QgsAbstractDatabaseProviderConnection.CreateVectorTable)) self.assertTrue( bool(capabilities & QgsAbstractDatabaseProviderConnection.DropVectorTable)) self.assertTrue( bool(capabilities & QgsAbstractDatabaseProviderConnection.RenameVectorTable)) self.assertTrue( bool(capabilities & QgsAbstractDatabaseProviderConnection.RenameRasterTable)) # Check filters and special cases table_names = self._table_names( conn.tables('qgis_test', QgsAbstractDatabaseProviderConnection.Raster)) self.assertTrue('Raster1' in table_names) self.assertFalse('geometryless_table' in table_names) self.assertFalse('geometries_table' in table_names) self.assertFalse('geometries_view' in table_names) table_names = self._table_names( conn.tables('qgis_test', QgsAbstractDatabaseProviderConnection.View)) self.assertFalse('Raster1' in table_names) self.assertFalse('geometryless_table' in table_names) self.assertFalse('geometries_table' in table_names) self.assertTrue('geometries_view' in table_names) table_names = self._table_names( conn.tables('qgis_test', QgsAbstractDatabaseProviderConnection.Aspatial)) self.assertFalse('Raster1' in table_names) self.assertTrue('geometryless_table' in table_names) self.assertFalse('geometries_table' in table_names) self.assertFalse('geometries_view' in table_names) tables = conn.tables( 'qgis_test', QgsAbstractDatabaseProviderConnection.Aspatial | QgsAbstractDatabaseProviderConnection.View) table_names = self._table_names(tables) b32523_view = self._table_by_name(tables, 'b32523') self.assertTrue(b32523_view) pks = b32523_view.primaryKeyColumns() self.assertTrue('pk' in pks) self.assertTrue('random' in pks) geometries_table = self._table_by_name(conn.tables('qgis_test'), 'geometries_table') srids_and_types = [[t.crs.postgisSrid(), t.wkbType] for t in geometries_table.geometryColumnTypes()] srids_and_types.sort() self.assertEqual( srids_and_types, [[0, 1], [0, 2], [0, 3], [0, 7], [3857, 1], [4326, 1]]) # Check TopoGeometry and Pointcloud layers are found in vector table names tables = conn.tables('qgis_test', QgsAbstractDatabaseProviderConnection.Vector) table_names = self._table_names(tables) self.assertTrue('TopoLayer1' in table_names) self.assertTrue('PointCloudPointLayer' in table_names) self.assertTrue('PointCloudPatchLayer' in table_names) self.assertTrue('geometries_table' in table_names) # Revoke select permissions on topology.topology from qgis_test_user conn.executeSql( 'REVOKE SELECT ON topology.topology FROM qgis_test_user') # Revoke select permissions on pointcloud_format from qgis_test_user conn.executeSql( 'REVOKE SELECT ON pointcloud_formats FROM qgis_test_user') # Revoke select permissions on pointcloud_format from qgis_test_user conn.executeSql('REVOKE SELECT ON raster_columns FROM public') conn.executeSql('REVOKE SELECT ON raster_columns FROM qgis_test_user') # Re-connect as the qgis_test_role role newuri = self.uri + ' user=qgis_test_user password=qgis_test_user_password' newconn = md.createConnection(newuri, {}) # Check TopoGeometry and Pointcloud layers are not found in vector table names tableTypes = QgsAbstractDatabaseProviderConnection.Vector | QgsAbstractDatabaseProviderConnection.Raster tables = newconn.tables('qgis_test', tableTypes) table_names = self._table_names(tables) self.assertFalse('TopoLayer1' in table_names) self.assertFalse('PointCloudPointLayer' in table_names) self.assertFalse('PointCloudPatchLayer' in table_names) self.assertFalse('Raster1' in table_names) self.assertTrue('geometries_table' in table_names) # TODO: only revoke select permission on topology.layer, grant # on topology.topology # TODO: only revoke usage permission on topology, grant # all on topology.layer and topology.topology # TODO: only revoke select permission the actual topology # schema associated with TopoLayer1 # TODO: only revoke select permission the pointcloud_columns # table # Grant select permissions back on topology.topology to qgis_test_user conn.executeSql('GRANT SELECT ON topology.topology TO qgis_test_user') # Grant select permissions back on pointcloud_formats to qgis_test_user conn.executeSql('GRANT SELECT ON pointcloud_formats TO qgis_test_user') # Grant select permissions back on raster_columns to qgis_test_user conn.executeSql('GRANT SELECT ON raster_columns TO public') conn.executeSql('GRANT SELECT ON raster_columns TO qgis_test_user') # error: ERROR: relation "qgis_test.raster1" does not exist @unittest.skipIf(gdal.VersionInfo() < '2040000', 'This test requires GDAL >= 2.4.0') def test_postgis_raster_rename(self): """Test raster rename""" md = QgsProviderRegistry.instance().providerMetadata('postgres') conn = md.createConnection(self.uri, {}) md.saveConnection(conn, 'qgis_test1') table = self._table_by_name( conn.tables('qgis_test', QgsAbstractDatabaseProviderConnection.Raster), 'Raster1') self.assertTrue( QgsRasterLayer( "PG: %s schema='qgis_test' column='%s' table='%s'" % (conn.uri(), table.geometryColumn(), table.tableName()), 'r1', 'gdal').isValid()) conn.renameRasterTable('qgis_test', table.tableName(), 'Raster2') table = self._table_by_name( conn.tables('qgis_test', QgsAbstractDatabaseProviderConnection.Raster), 'Raster2') self.assertTrue( QgsRasterLayer( "PG: %s schema='qgis_test' column='%s' table='%s'" % (conn.uri(), table.geometryColumn(), table.tableName()), 'r1', 'gdal').isValid()) table_names = self._table_names( conn.tables('qgis_test', QgsAbstractDatabaseProviderConnection.Raster)) self.assertFalse('Raster1' in table_names) self.assertTrue('Raster2' in table_names) conn.renameRasterTable('qgis_test', table.tableName(), 'Raster1') table_names = self._table_names( conn.tables('qgis_test', QgsAbstractDatabaseProviderConnection.Raster)) self.assertFalse('Raster2' in table_names) self.assertTrue('Raster1' in table_names) def test_true_false(self): """Test returned values from BOOL queries""" md = QgsProviderRegistry.instance().providerMetadata(self.providerKey) conn = md.createConnection(self.uri, {}) self.assertEqual(conn.executeSql('SELECT FALSE'), [[False]]) self.assertEqual(conn.executeSql('SELECT TRUE'), [[True]]) def test_nulls(self): """Test returned values from typed NULL queries""" md = QgsProviderRegistry.instance().providerMetadata(self.providerKey) conn = md.createConnection(self.uri, {}) self.assertEqual(conn.executeSql('SELECT NULL::bool'), [[None]]) self.assertEqual(conn.executeSql('SELECT NULL::text'), [[None]]) self.assertEqual(conn.executeSql('SELECT NULL::bytea'), [[None]]) self.assertEqual(conn.executeSql('SELECT NULL::char'), [[None]]) def test_pk_cols_order(self): """Test that PKs are returned in consistent order: see GH #34167""" md = QgsProviderRegistry.instance().providerMetadata(self.providerKey) conn = md.createConnection(self.uri, {}) self.assertEqual( conn.table('qgis_test', 'bikes_view').primaryKeyColumns(), ['pk', 'name']) self.assertEqual( conn.table('qgis_test', 'some_poly_data_view').primaryKeyColumns(), ['pk', 'geom']) def test_char_type_conversion(self): """Test char types: see GH #34806""" md = QgsProviderRegistry.instance().providerMetadata(self.providerKey) conn = md.createConnection(self.uri, {}) self.assertEqual( conn.executeSql( "SELECT relname, relkind FROM pg_class c, pg_namespace n WHERE n.oid = c.relnamespace AND relname = 'bikes_view' AND c.relkind IN ('t', 'v', 'm')" ), [['bikes_view', 'v']]) def test_foreign_table_csv(self): """Test foreign table""" md = QgsProviderRegistry.instance().providerMetadata(self.providerKey) conn = md.createConnection(self.uri, {}) temp_dir = QTemporaryDir() csv_path = os.path.join(temp_dir.path(), 'test.csv') csv = """id,description,geom_x,geom_y 1,Basic point,10.5,20.82 2,Integer point,11,22 3,Final point,13.0,23.0 """ with open(csv_path, 'w') as f: f.write(csv) os.chmod(temp_dir.path(), 0o777) os.chmod(csv_path, 0o777) foreign_table_definition = """ CREATE EXTENSION IF NOT EXISTS file_fdw; CREATE SERVER IF NOT EXISTS file_fdw_test_server FOREIGN DATA WRAPPER file_fdw; CREATE FOREIGN TABLE IF NOT EXISTS points_csv ( id integer not null, name text, x numeric, y numeric ) SERVER file_fdw_test_server OPTIONS ( filename '%s', format 'csv', header 'true' ); """ % csv_path conn.executeSql(foreign_table_definition) self.assertNotEquals( conn.tables( 'public', QgsAbstractDatabaseProviderConnection.Foreign | QgsAbstractDatabaseProviderConnection.Aspatial), []) @unittest.skipIf( os.environ.get('TRAVIS', '') == 'true', 'Disabled on Travis') def test_foreign_table_server(self): """Test foreign table with server""" md = QgsProviderRegistry.instance().providerMetadata(self.providerKey) conn = md.createConnection(self.uri, {}) uri = QgsDataSourceUri(conn.uri()) host = uri.host() port = uri.port() user = uri.username() dbname = uri.database() password = uri.password() service = uri.service() foreign_table_definition = """ CREATE EXTENSION IF NOT EXISTS postgres_fdw; CREATE SERVER IF NOT EXISTS postgres_fdw_test_server FOREIGN DATA WRAPPER postgres_fdw OPTIONS (service '{service}', dbname '{dbname}', host '{host}', port '{port}'); DROP SCHEMA IF EXISTS foreign_schema CASCADE; CREATE SCHEMA IF NOT EXISTS foreign_schema; CREATE USER MAPPING IF NOT EXISTS FOR CURRENT_USER SERVER postgres_fdw_test_server OPTIONS (user '{user}', password '{password}'); IMPORT FOREIGN SCHEMA qgis_test LIMIT TO ( "someData" ) FROM SERVER postgres_fdw_test_server INTO foreign_schema; """.format(host=host, user=user, port=port, dbname=dbname, password=password, service=service) conn.executeSql(foreign_table_definition) self.assertEquals( conn.tables( 'foreign_schema', QgsAbstractDatabaseProviderConnection.Foreign)[0].tableName(), 'someData')
def version(): return int(gdal.VersionInfo('VERSION_NUM'))
class TestPyQgsProviderConnectionPostgres(unittest.TestCase, TestPyQgsProviderConnectionBase): # Provider test cases must define the string URI for the test uri = '' # Provider test cases must define the provider name (e.g. "postgres" or "ogr") providerKey = 'postgres' # Provider test cases can define a slowQuery for executeSql cancellation test slowQuery = "select pg_sleep(30)" @classmethod def setUpClass(cls): """Run before all tests""" TestPyQgsProviderConnectionBase.setUpClass() cls.postgres_conn = "service='qgis_test'" if 'QGIS_PGTEST_DB' in os.environ: cls.postgres_conn = os.environ['QGIS_PGTEST_DB'] # Create test layers vl = QgsVectorLayer( cls.postgres_conn + ' sslmode=disable key=\'"key1","key2"\' srid=4326 type=POINT table="qgis_test"."someData" (geom) sql=', 'test', 'postgres') assert vl.isValid() cls.uri = cls.postgres_conn + ' sslmode=disable' def test_postgis_connections_from_uri(self): """Create a connection from a layer uri and retrieve it""" md = QgsProviderRegistry.instance().providerMetadata('postgres') vl = QgsVectorLayer( self.postgres_conn + ' sslmode=disable key=\'"key1","key2"\' srid=4326 type=POINT table="qgis_test"."someData" (geom) sql=', 'test', 'postgres') conn = md.createConnection(vl.dataProvider().uri().uri(), {}) self.assertEqual(conn.uri(), self.uri) # Test table(), throws if not found table_info = conn.table('qgis_test', 'someData') table_info = conn.table('qgis_test', 'Raster1') # Test raster self.assertEqual(conn.tableUri('qgis_test', 'Raster1'), '%s table="qgis_test"."Raster1"' % self.uri) rl = QgsRasterLayer(conn.tableUri('qgis_test', 'Raster1'), 'r1', 'postgresraster') self.assertTrue(rl.isValid()) def test_sslmode_store(self): """Test that sslmode is stored as a string in the settings""" md = QgsProviderRegistry.instance().providerMetadata('postgres') conn = md.createConnection( 'database=\'mydb\' username=\'myuser\' password=\'mypasswd\' sslmode=verify-ca', {}) conn.store('my_sslmode_test') settings = QgsSettings() settings.beginGroup('/PostgreSQL/connections/my_sslmode_test') self.assertEqual(settings.value("sslmode"), 'SslVerifyCa') self.assertEqual( settings.enumValue("sslmode", QgsDataSourceUri.SslPrefer), QgsDataSourceUri.SslVerifyCa) def test_postgis_geometry_filter(self): """Make sure the postgres provider only returns one matching geometry record and no polygons etc.""" vl = QgsVectorLayer( self.postgres_conn + ' srid=4326 type=POINT table="qgis_test"."geometries_table" (geom) sql=', 'test', 'postgres') ids = [f.id() for f in vl.getFeatures()] self.assertEqual(ids, [2]) def test_postgis_table_uri(self): """Create a connection from a layer uri and create a table URI""" md = QgsProviderRegistry.instance().providerMetadata('postgres') conn = md.createConnection(self.uri, {}) vl = QgsVectorLayer(conn.tableUri('qgis_test', 'geometries_table'), 'my', 'postgres') self.assertTrue(vl.isValid()) def test_postgis_connections(self): """Create some connections and retrieve them""" md = QgsProviderRegistry.instance().providerMetadata('postgres') conn = md.createConnection(self.uri, {}) md.saveConnection(conn, 'qgis_test1') # Retrieve capabilities capabilities = conn.capabilities() self.assertTrue( bool(capabilities & QgsAbstractDatabaseProviderConnection.Tables)) self.assertTrue( bool(capabilities & QgsAbstractDatabaseProviderConnection.Schemas)) self.assertTrue( bool(capabilities & QgsAbstractDatabaseProviderConnection.CreateVectorTable)) self.assertTrue( bool(capabilities & QgsAbstractDatabaseProviderConnection.DropVectorTable)) self.assertTrue( bool(capabilities & QgsAbstractDatabaseProviderConnection.RenameVectorTable)) self.assertTrue( bool(capabilities & QgsAbstractDatabaseProviderConnection.RenameRasterTable)) # Check filters and special cases table_names = self._table_names( conn.tables('qgis_test', QgsAbstractDatabaseProviderConnection.Raster)) self.assertTrue('Raster1' in table_names) self.assertFalse('geometryless_table' in table_names) self.assertFalse('geometries_table' in table_names) self.assertFalse('geometries_view' in table_names) table_names = self._table_names( conn.tables('qgis_test', QgsAbstractDatabaseProviderConnection.View)) self.assertFalse('Raster1' in table_names) self.assertFalse('geometryless_table' in table_names) self.assertFalse('geometries_table' in table_names) self.assertTrue('geometries_view' in table_names) table_names = self._table_names( conn.tables('qgis_test', QgsAbstractDatabaseProviderConnection.Aspatial)) self.assertFalse('Raster1' in table_names) self.assertTrue('geometryless_table' in table_names) self.assertFalse('geometries_table' in table_names) self.assertFalse('geometries_view' in table_names) tables = conn.tables( 'qgis_test', QgsAbstractDatabaseProviderConnection.Aspatial | QgsAbstractDatabaseProviderConnection.View) table_names = self._table_names(tables) b32523_view = self._table_by_name(tables, 'b32523') self.assertTrue(b32523_view) pks = b32523_view.primaryKeyColumns() self.assertTrue('pk' in pks) self.assertTrue('random' in pks) geometries_table = self._table_by_name(conn.tables('qgis_test'), 'geometries_table') srids_and_types = [[t.crs.postgisSrid(), t.wkbType] for t in geometries_table.geometryColumnTypes()] srids_and_types.sort() self.assertEqual( srids_and_types, [[0, 1], [0, 2], [0, 3], [0, 7], [3857, 1], [4326, 1]]) # Check TopoGeometry and Pointcloud layers are found in vector table names tables = conn.tables('qgis_test', QgsAbstractDatabaseProviderConnection.Vector) table_names = self._table_names(tables) self.assertTrue('TopoLayer1' in table_names) self.assertTrue('PointCloudPointLayer' in table_names) self.assertTrue('PointCloudPatchLayer' in table_names) self.assertTrue('geometries_table' in table_names) # Revoke select permissions on topology.topology from qgis_test_user conn.executeSql( 'REVOKE SELECT ON topology.topology FROM qgis_test_user') # Revoke select permissions on pointcloud_format from qgis_test_user conn.executeSql( 'REVOKE SELECT ON pointcloud_formats FROM qgis_test_user') # Revoke select permissions on pointcloud_format from qgis_test_user conn.executeSql('REVOKE SELECT ON raster_columns FROM public') conn.executeSql('REVOKE SELECT ON raster_columns FROM qgis_test_user') # Re-connect as the qgis_test_role role newuri = self.uri + ' user=qgis_test_user password=qgis_test_user_password' newconn = md.createConnection(newuri, {}) # Check TopoGeometry and Pointcloud layers are not found in vector table names tableTypes = QgsAbstractDatabaseProviderConnection.Vector | QgsAbstractDatabaseProviderConnection.Raster tables = newconn.tables('qgis_test', tableTypes) table_names = self._table_names(tables) self.assertFalse('TopoLayer1' in table_names) self.assertFalse('PointCloudPointLayer' in table_names) self.assertFalse('PointCloudPatchLayer' in table_names) self.assertFalse('Raster1' in table_names) self.assertTrue('geometries_table' in table_names) # TODO: only revoke select permission on topology.layer, grant # on topology.topology # TODO: only revoke usage permission on topology, grant # all on topology.layer and topology.topology # TODO: only revoke select permission the actual topology # schema associated with TopoLayer1 # TODO: only revoke select permission the pointcloud_columns # table # Grant select permissions back on topology.topology to qgis_test_user conn.executeSql('GRANT SELECT ON topology.topology TO qgis_test_user') # Grant select permissions back on pointcloud_formats to qgis_test_user conn.executeSql('GRANT SELECT ON pointcloud_formats TO qgis_test_user') # Grant select permissions back on raster_columns to qgis_test_user conn.executeSql('GRANT SELECT ON raster_columns TO public') conn.executeSql('GRANT SELECT ON raster_columns TO qgis_test_user') # error: ERROR: relation "qgis_test.raster1" does not exist @unittest.skipIf(gdal.VersionInfo() < '2040000', 'This test requires GDAL >= 2.4.0') def test_postgis_raster_rename(self): """Test raster rename""" md = QgsProviderRegistry.instance().providerMetadata('postgres') conn = md.createConnection(self.uri, {}) md.saveConnection(conn, 'qgis_test1') table = self._table_by_name( conn.tables('qgis_test', QgsAbstractDatabaseProviderConnection.Raster), 'Raster1') self.assertTrue( QgsRasterLayer( "PG: %s schema='qgis_test' column='%s' table='%s'" % (conn.uri(), table.geometryColumn(), table.tableName()), 'r1', 'gdal').isValid()) conn.renameRasterTable('qgis_test', table.tableName(), 'Raster2') table = self._table_by_name( conn.tables('qgis_test', QgsAbstractDatabaseProviderConnection.Raster), 'Raster2') self.assertTrue( QgsRasterLayer( "PG: %s schema='qgis_test' column='%s' table='%s'" % (conn.uri(), table.geometryColumn(), table.tableName()), 'r1', 'gdal').isValid()) table_names = self._table_names( conn.tables('qgis_test', QgsAbstractDatabaseProviderConnection.Raster)) self.assertFalse('Raster1' in table_names) self.assertTrue('Raster2' in table_names) conn.renameRasterTable('qgis_test', table.tableName(), 'Raster1') table_names = self._table_names( conn.tables('qgis_test', QgsAbstractDatabaseProviderConnection.Raster)) self.assertFalse('Raster2' in table_names) self.assertTrue('Raster1' in table_names) def test_true_false(self): """Test returned values from BOOL queries""" md = QgsProviderRegistry.instance().providerMetadata(self.providerKey) conn = md.createConnection(self.uri, {}) self.assertEqual(conn.executeSql('SELECT FALSE'), [[False]]) self.assertEqual(conn.executeSql('SELECT TRUE'), [[True]]) def test_nulls(self): """Test returned values from typed NULL queries""" md = QgsProviderRegistry.instance().providerMetadata(self.providerKey) conn = md.createConnection(self.uri, {}) self.assertEqual(conn.executeSql('SELECT NULL::bool'), [[None]]) self.assertEqual(conn.executeSql('SELECT NULL::text'), [[None]]) self.assertEqual(conn.executeSql('SELECT NULL::bytea'), [[None]]) self.assertEqual(conn.executeSql('SELECT NULL::char'), [[None]]) def test_pk_cols_order(self): """Test that PKs are returned in consistent order: see GH #34167""" md = QgsProviderRegistry.instance().providerMetadata(self.providerKey) conn = md.createConnection(self.uri, {}) self.assertEqual( conn.table('qgis_test', 'bikes_view').primaryKeyColumns(), ['pk', 'name']) self.assertEqual( conn.table('qgis_test', 'some_poly_data_view').primaryKeyColumns(), ['pk', 'geom']) def test_char_type_conversion(self): """Test char types: see GH #34806""" md = QgsProviderRegistry.instance().providerMetadata(self.providerKey) conn = md.createConnection(self.uri, {}) self.assertEqual( conn.executeSql( "SELECT relname, relkind FROM pg_class c, pg_namespace n WHERE n.oid = c.relnamespace AND relname = 'bikes_view' AND c.relkind IN ('t', 'v', 'm')" ), [['bikes_view', 'v']]) def test_foreign_table_csv(self): """Test foreign table""" md = QgsProviderRegistry.instance().providerMetadata(self.providerKey) conn = md.createConnection(self.uri, {}) temp_dir = QTemporaryDir() csv_path = os.path.join(temp_dir.path(), 'test.csv') csv = """id,description,geom_x,geom_y 1,Basic point,10.5,20.82 2,Integer point,11,22 3,Final point,13.0,23.0 """ with open(csv_path, 'w') as f: f.write(csv) os.chmod(temp_dir.path(), 0o777) os.chmod(csv_path, 0o777) foreign_table_definition = """ CREATE EXTENSION IF NOT EXISTS file_fdw; CREATE SERVER IF NOT EXISTS file_fdw_test_server FOREIGN DATA WRAPPER file_fdw; CREATE FOREIGN TABLE IF NOT EXISTS points_csv ( id integer not null, name text, x numeric, y numeric ) SERVER file_fdw_test_server OPTIONS ( filename '%s', format 'csv', header 'true' ); """ % csv_path conn.executeSql(foreign_table_definition) self.assertNotEquals( conn.tables( 'public', QgsAbstractDatabaseProviderConnection.Foreign | QgsAbstractDatabaseProviderConnection.Aspatial), []) @unittest.skipIf( os.environ.get('TRAVIS', '') == 'true', 'Disabled on Travis') def test_foreign_table_server(self): """Test foreign table with server""" md = QgsProviderRegistry.instance().providerMetadata(self.providerKey) conn = md.createConnection(self.uri, {}) uri = QgsDataSourceUri(conn.uri()) host = uri.host() port = uri.port() user = uri.username() dbname = uri.database() password = uri.password() service = uri.service() foreign_table_definition = """ CREATE EXTENSION IF NOT EXISTS postgres_fdw; CREATE SERVER IF NOT EXISTS postgres_fdw_test_server FOREIGN DATA WRAPPER postgres_fdw OPTIONS (service '{service}', dbname '{dbname}', host '{host}', port '{port}'); DROP SCHEMA IF EXISTS foreign_schema CASCADE; CREATE SCHEMA IF NOT EXISTS foreign_schema; CREATE USER MAPPING IF NOT EXISTS FOR CURRENT_USER SERVER postgres_fdw_test_server OPTIONS (user '{user}', password '{password}'); IMPORT FOREIGN SCHEMA qgis_test LIMIT TO ( "someData" ) FROM SERVER postgres_fdw_test_server INTO foreign_schema; """.format(host=host, user=user, port=port, dbname=dbname, password=password, service=service) conn.executeSql(foreign_table_definition) self.assertEquals( conn.tables( 'foreign_schema', QgsAbstractDatabaseProviderConnection.Foreign)[0].tableName(), 'someData') def test_fields(self): """Test fields""" md = QgsProviderRegistry.instance().providerMetadata('postgres') conn = md.createConnection(self.uri, {}) fields = conn.fields('qgis_test', 'someData') self.assertEqual(fields.names(), [ 'pk', 'cnt', 'name', 'name2', 'num_char', 'dt', 'date', 'time', 'geom' ]) sql = """ DROP TABLE IF EXISTS qgis_test.gh_37666; CREATE TABLE qgis_test.gh_37666 (id SERIAL PRIMARY KEY); ALTER TABLE qgis_test.gh_37666 ADD COLUMN geom geometry(POINT,4326); ALTER TABLE qgis_test.gh_37666 ADD COLUMN geog geography(POINT,4326); INSERT INTO qgis_test.gh_37666 (id, geom) VALUES (221, ST_GeomFromText('point(9 45)', 4326)); UPDATE qgis_test.gh_37666 SET geog = ST_GeogFromWKB(st_asewkb(geom)); """ conn.executeSql(sql) fields = conn.fields('qgis_test', 'gh_37666') self.assertEqual([f.name() for f in fields], ['id', 'geom', 'geog']) self.assertEqual([f.typeName() for f in fields], ['int4', 'geometry', 'geography']) table = conn.table('qgis_test', 'gh_37666') self.assertEqual(table.primaryKeyColumns(), ['id']) def test_fields_no_pk(self): """Test issue: no fields are exposed for raster_columns""" md = QgsProviderRegistry.instance().providerMetadata('postgres') conn = md.createConnection(self.uri, {}) self.assertTrue(conn.tableExists('public', 'raster_columns')) fields = conn.fields("public", "raster_columns") self.assertTrue( set(fields.names()).issuperset({ 'r_table_catalog', 'r_table_schema', 'r_table_name', 'r_raster_column', 'srid', 'scale_x', 'scale_y', 'blocksize_x', 'blocksize_y', 'same_alignment', 'regular_blocking', 'num_bands', 'pixel_types', 'nodata_values', 'out_db', 'spatial_index' })) def test_exceptions(self): """Test that exception are converted to Python QgsProviderConnectionException""" md = QgsProviderRegistry.instance().providerMetadata('postgres') conn = md.createConnection(self.uri, {}) with self.assertRaises(QgsProviderConnectionException): conn.table('my_not_existent_schema', 'my_not_existent_table')
class TestPyQgsOGRProviderGpkg(unittest.TestCase): @classmethod def setUpClass(cls): """Run before all tests""" QCoreApplication.setOrganizationName("QGIS_Test") QCoreApplication.setOrganizationDomain("TestPyQgsOGRProviderGpkg.com") QCoreApplication.setApplicationName("TestPyQgsOGRProviderGpkg") QgsSettings().clear() start_app() # Create test layer cls.basetestpath = tempfile.mkdtemp() @classmethod def tearDownClass(cls): """Run after all tests""" shutil.rmtree(cls.basetestpath, True) QgsSettings().clear() def testSingleToMultiPolygonPromotion(self): tmpfile = os.path.join(self.basetestpath, 'testSingleToMultiPolygonPromotion.gpkg') ds = ogr.GetDriverByName('GPKG').CreateDataSource(tmpfile) ds.CreateLayer('test', geom_type=ogr.wkbMultiPolygon) ds = None vl = QgsVectorLayer('{}|layerid=0'.format(tmpfile), 'test', 'ogr') f = QgsFeature() f.setGeometry(QgsGeometry.fromWkt('POLYGON ((0 0,0 1,1 1,0 0))')) vl.dataProvider().addFeatures([f]) got = [feat for feat in vl.getFeatures()][0] got_geom = got.geometry() reference = QgsGeometry.fromWkt( 'MultiPolygon (((0 0, 0 1, 1 1, 0 0)))') # The geometries must be binarily identical self.assertEqual( got_geom.exportToWkb(), reference.exportToWkb(), 'Expected {}, got {}'.format(reference.exportToWkt(), got_geom.exportToWkt())) def testCurveGeometryType(self): tmpfile = os.path.join(self.basetestpath, 'testCurveGeometryType.gpkg') ds = ogr.GetDriverByName('GPKG').CreateDataSource(tmpfile) ds.CreateLayer('test', geom_type=ogr.wkbCurvePolygon) ds = None vl = QgsVectorLayer('{}'.format(tmpfile), 'test', 'ogr') self.assertEqual(vl.dataProvider().subLayers(), ['0:test:0:CurvePolygon']) f = QgsFeature() f.setGeometry(QgsGeometry.fromWkt('POLYGON ((0 0,0 1,1 1,0 0))')) vl.dataProvider().addFeatures([f]) got = [feat for feat in vl.getFeatures()][0] got_geom = got.geometry() reference = QgsGeometry.fromWkt( 'CurvePolygon (((0 0, 0 1, 1 1, 0 0)))') # The geometries must be binarily identical self.assertEqual( got_geom.exportToWkb(), reference.exportToWkb(), 'Expected {}, got {}'.format(reference.exportToWkt(), got_geom.exportToWkt())) def internalTestBug15351(self, orderClosing): tmpfile = os.path.join(self.basetestpath, 'testBug15351.gpkg') ds = ogr.GetDriverByName('GPKG').CreateDataSource(tmpfile) lyr = ds.CreateLayer('test', geom_type=ogr.wkbPoint) f = ogr.Feature(lyr.GetLayerDefn()) f.SetGeometry(ogr.CreateGeometryFromWkt('POINT(0 0)')) lyr.CreateFeature(f) f = None ds = None vl = QgsVectorLayer(u'{}'.format(tmpfile), u'test', u'ogr') self.assertTrue(vl.startEditing()) self.assertTrue( vl.changeGeometry(1, QgsGeometry.fromWkt('Point (3 50)'))) # Iterate over features (will open a new OGR connection), but do not # close the iterator for now it = vl.getFeatures() f = QgsFeature() it.nextFeature(f) if orderClosing == 'closeIter_commit_closeProvider': it = None # Commit changes cbk = ErrorReceiver() vl.dataProvider().raiseError.connect(cbk.receiveError) self.assertTrue(vl.commitChanges()) self.assertIsNone(cbk.msg) # Close layer and iterator in different orders if orderClosing == 'closeIter_commit_closeProvider': vl = None elif orderClosing == 'commit_closeProvider_closeIter': vl = None it = None else: assert orderClosing == 'commit_closeIter_closeProvider' it = None vl = None # Test that we succeeded restoring default journal mode, and we # are not let in WAL mode. ds = ogr.Open(tmpfile) lyr = ds.ExecuteSQL('PRAGMA journal_mode') f = lyr.GetNextFeature() res = f.GetField(0) ds.ReleaseResultSet(lyr) ds = None self.assertEqual(res, 'delete') # We need GDAL 2.0 to issue PRAGMA journal_mode # Note: for that case, we don't strictly need turning on WAL def testBug15351_closeIter_commit_closeProvider(self): self.internalTestBug15351('closeIter_commit_closeProvider') # We need GDAL 2.0 to issue PRAGMA journal_mode def testBug15351_commit_closeProvider_closeIter(self): self.internalTestBug15351('commit_closeProvider_closeIter') # We need GDAL 2.0 to issue PRAGMA journal_mode def testBug15351_commit_closeIter_closeProvider(self): self.internalTestBug15351('commit_closeIter_closeProvider') @unittest.skip( int(gdal.VersionInfo('VERSION_NUM')) < GDAL_COMPUTE_VERSION(2, 1, 2)) def testGeopackageExtentUpdate(self): ''' test https://issues.qgis.org/issues/15273 ''' tmpfile = os.path.join(self.basetestpath, 'testGeopackageExtentUpdate.gpkg') ds = ogr.GetDriverByName('GPKG').CreateDataSource(tmpfile) lyr = ds.CreateLayer('test', geom_type=ogr.wkbPoint) f = ogr.Feature(lyr.GetLayerDefn()) f.SetGeometry(ogr.CreateGeometryFromWkt('POINT(0 0)')) lyr.CreateFeature(f) f = ogr.Feature(lyr.GetLayerDefn()) f.SetGeometry(ogr.CreateGeometryFromWkt('POINT(1 1)')) lyr.CreateFeature(f) f = None f = ogr.Feature(lyr.GetLayerDefn()) f.SetGeometry(ogr.CreateGeometryFromWkt('POINT(1 0.5)')) lyr.CreateFeature(f) f = None gdal.ErrorReset() ds.ExecuteSQL('RECOMPUTE EXTENT ON test') has_error = gdal.GetLastErrorMsg() != '' ds = None if has_error: print('Too old GDAL trunk version. Please update') return vl = QgsVectorLayer(u'{}'.format(tmpfile), u'test', u'ogr') # Test moving a geometry that touches the bbox self.assertTrue(vl.startEditing()) self.assertTrue( vl.changeGeometry(1, QgsGeometry.fromWkt('Point (0.5 0)'))) self.assertTrue(vl.commitChanges()) reference = QgsGeometry.fromRect(QgsRectangle(0.5, 0.0, 1.0, 1.0)) provider_extent = QgsGeometry.fromRect(vl.extent()) self.assertTrue( QgsGeometry.compare(provider_extent.asPolygon()[0], reference.asPolygon()[0], 0.00001), provider_extent.asPolygon()[0]) # Test deleting a geometry that touches the bbox self.assertTrue(vl.startEditing()) self.assertTrue(vl.deleteFeature(2)) self.assertTrue(vl.commitChanges()) reference = QgsGeometry.fromRect(QgsRectangle(0.5, 0.0, 1.0, 0.5)) provider_extent = QgsGeometry.fromRect(vl.extent()) self.assertTrue( QgsGeometry.compare(provider_extent.asPolygon()[0], reference.asPolygon()[0], 0.00001), provider_extent.asPolygon()[0]) def testSelectSubsetString(self): tmpfile = os.path.join(self.basetestpath, 'testSelectSubsetString.gpkg') ds = ogr.GetDriverByName('GPKG').CreateDataSource(tmpfile) lyr = ds.CreateLayer('test', geom_type=ogr.wkbMultiPolygon) lyr.CreateField(ogr.FieldDefn('foo', ogr.OFTString)) f = ogr.Feature(lyr.GetLayerDefn()) f['foo'] = 'bar' lyr.CreateFeature(f) f = None f = ogr.Feature(lyr.GetLayerDefn()) f['foo'] = 'baz' lyr.CreateFeature(f) f = None ds = None vl = QgsVectorLayer('{}|layerid=0'.format(tmpfile), 'test', 'ogr') vl.setSubsetString("SELECT fid, foo FROM test WHERE foo = 'baz'") got = [feat for feat in vl.getFeatures()] self.assertEqual(len(got), 1) def testStyle(self): # First test with invalid URI vl = QgsVectorLayer('/idont/exist.gpkg', 'test', 'ogr') self.assertFalse( vl.dataProvider().isSaveAndLoadStyleToDatabaseSupported()) related_count, idlist, namelist, desclist, errmsg = vl.listStylesInDatabase( ) self.assertEqual(related_count, -1) self.assertEqual(idlist, []) self.assertEqual(namelist, []) self.assertEqual(desclist, []) self.assertNotEqual(errmsg, "") qml, errmsg = vl.getStyleFromDatabase("1") self.assertEqual(qml, "") self.assertNotEqual(errmsg, "") qml, success = vl.loadNamedStyle('/idont/exist.gpkg') self.assertFalse(success) errorMsg = vl.saveStyleToDatabase("name", "description", False, "") self.assertNotEqual(errorMsg, "") # Now with valid URI tmpfile = os.path.join(self.basetestpath, 'testStyle.gpkg') ds = ogr.GetDriverByName('GPKG').CreateDataSource(tmpfile) lyr = ds.CreateLayer('test', geom_type=ogr.wkbMultiPolygon) lyr.CreateField(ogr.FieldDefn('foo', ogr.OFTString)) f = ogr.Feature(lyr.GetLayerDefn()) f['foo'] = 'bar' lyr.CreateFeature(f) f = None lyr = ds.CreateLayer('test2', geom_type=ogr.wkbMultiPolygon) lyr.CreateField(ogr.FieldDefn('foo', ogr.OFTString)) f = ogr.Feature(lyr.GetLayerDefn()) f['foo'] = 'bar' lyr.CreateFeature(f) f = None ds = None vl = QgsVectorLayer('{}|layername=test'.format(tmpfile), 'test', 'ogr') self.assertTrue(vl.isValid()) vl2 = QgsVectorLayer('{}|layername=test2'.format(tmpfile), 'test2', 'ogr') self.assertTrue(vl2.isValid()) self.assertTrue( vl.dataProvider().isSaveAndLoadStyleToDatabaseSupported()) related_count, idlist, namelist, desclist, errmsg = vl.listStylesInDatabase( ) self.assertEqual(related_count, 0) self.assertEqual(idlist, []) self.assertEqual(namelist, []) self.assertEqual(desclist, []) self.assertNotEqual(errmsg, "") qml, errmsg = vl.getStyleFromDatabase("not_existing") self.assertEqual(qml, "") self.assertNotEqual(errmsg, "") qml, success = vl.loadNamedStyle('{}|layerid=0'.format(tmpfile)) self.assertFalse(success) errorMsg = vl.saveStyleToDatabase("name", "description", False, "") self.assertEqual(errorMsg, "") qml, errmsg = vl.getStyleFromDatabase("not_existing") self.assertEqual(qml, "") self.assertNotEqual(errmsg, "") related_count, idlist, namelist, desclist, errmsg = vl.listStylesInDatabase( ) self.assertEqual(related_count, 1) self.assertEqual(errmsg, "") self.assertEqual(idlist, ['1']) self.assertEqual(namelist, ['name']) self.assertEqual(desclist, ['description']) qml, errmsg = vl.getStyleFromDatabase("100") self.assertEqual(qml, "") self.assertNotEqual(errmsg, "") qml, errmsg = vl.getStyleFromDatabase("1") self.assertTrue(qml.startswith('<!DOCTYPE qgis'), qml) self.assertEqual(errmsg, "") # Try overwrite it but simulate answer no settings = QgsSettings() settings.setValue("/qgis/overwriteStyle", False) errorMsg = vl.saveStyleToDatabase("name", "description_bis", False, "") self.assertNotEqual(errorMsg, "") related_count, idlist, namelist, desclist, errmsg = vl.listStylesInDatabase( ) self.assertEqual(related_count, 1) self.assertEqual(errmsg, "") self.assertEqual(idlist, ['1']) self.assertEqual(namelist, ['name']) self.assertEqual(desclist, ['description']) # Try overwrite it and simulate answer yes settings = QgsSettings() settings.setValue("/qgis/overwriteStyle", True) errorMsg = vl.saveStyleToDatabase("name", "description_bis", False, "") self.assertEqual(errorMsg, "") related_count, idlist, namelist, desclist, errmsg = vl.listStylesInDatabase( ) self.assertEqual(related_count, 1) self.assertEqual(errmsg, "") self.assertEqual(idlist, ['1']) self.assertEqual(namelist, ['name']) self.assertEqual(desclist, ['description_bis']) errorMsg = vl2.saveStyleToDatabase("name_test2", "description_test2", True, "") self.assertEqual(errorMsg, "") errorMsg = vl.saveStyleToDatabase("name2", "description2", True, "") self.assertEqual(errorMsg, "") errorMsg = vl.saveStyleToDatabase("name3", "description3", True, "") self.assertEqual(errorMsg, "") related_count, idlist, namelist, desclist, errmsg = vl.listStylesInDatabase( ) self.assertEqual(related_count, 3) self.assertEqual(errmsg, "") self.assertEqual(idlist, ['1', '3', '4', '2']) self.assertEqual(namelist, ['name', 'name2', 'name3', 'name_test2']) self.assertEqual( desclist, ['description_bis', 'description2', 'description3', 'name_test2']) # Check that layers_style table is not list in subLayers() vl = QgsVectorLayer(tmpfile, 'test', 'ogr') sublayers = vl.dataProvider().subLayers() self.assertEqual(len(sublayers), 2, sublayers) def testDisablewalForSqlite3(self): ''' Test disabling walForSqlite3 setting ''' QgsSettings().setValue("/qgis/walForSqlite3", False) tmpfile = os.path.join(self.basetestpath, 'testDisablewalForSqlite3.gpkg') ds = ogr.GetDriverByName('GPKG').CreateDataSource(tmpfile) lyr = ds.CreateLayer('test', geom_type=ogr.wkbPoint) lyr.CreateField(ogr.FieldDefn('attr0', ogr.OFTInteger)) lyr.CreateField(ogr.FieldDefn('attr1', ogr.OFTInteger)) f = ogr.Feature(lyr.GetLayerDefn()) f.SetGeometry(ogr.CreateGeometryFromWkt('POINT(0 0)')) lyr.CreateFeature(f) f = None ds = None vl = QgsVectorLayer(u'{}'.format(tmpfile), u'test', u'ogr') # Test that we are using default delete mode and not WAL ds = ogr.Open(tmpfile) lyr = ds.ExecuteSQL('PRAGMA journal_mode') f = lyr.GetNextFeature() res = f.GetField(0) ds.ReleaseResultSet(lyr) ds = None self.assertEqual(res, 'delete') self.assertTrue(vl.startEditing()) feature = next(vl.getFeatures()) self.assertTrue(vl.changeAttributeValue(feature.id(), 1, 1001)) # Commit changes cbk = ErrorReceiver() vl.dataProvider().raiseError.connect(cbk.receiveError) self.assertTrue(vl.commitChanges()) self.assertIsNone(cbk.msg) vl = None QgsSettings().setValue("/qgis/walForSqlite3", None) def testSimulatedDBManagerImport(self): uri = 'point?field=f1:int' uri += '&field=f2:double(6,4)' uri += '&field=f3:string(20)' lyr = QgsVectorLayer(uri, "x", "memory") self.assertTrue(lyr.isValid()) f = QgsFeature(lyr.fields()) f['f1'] = 1 f['f2'] = 123.456 f['f3'] = '12345678.90123456789' f2 = QgsFeature(lyr.fields()) f2['f1'] = 2 lyr.dataProvider().addFeatures([f, f2]) tmpfile = os.path.join(self.basetestpath, 'testSimulatedDBManagerImport.gpkg') ds = ogr.GetDriverByName('GPKG').CreateDataSource(tmpfile) ds = None options = {} options['update'] = True options['driverName'] = 'GPKG' options['layerName'] = 'my_out_table' err = QgsVectorLayerExporter.exportLayer(lyr, tmpfile, "ogr", lyr.crs(), False, options) self.assertEqual(err[0], QgsVectorLayerExporter.NoError, 'unexpected import error {0}'.format(err)) lyr = QgsVectorLayer(tmpfile + "|layername=my_out_table", "y", "ogr") self.assertTrue(lyr.isValid()) features = lyr.getFeatures() f = next(features) self.assertEqual(f['f1'], 1) self.assertEqual(f['f2'], 123.456) self.assertEqual(f['f3'], '12345678.90123456789') f = next(features) self.assertEqual(f['f1'], 2) features = None # Test overwriting without overwrite option err = QgsVectorLayerExporter.exportLayer(lyr, tmpfile, "ogr", lyr.crs(), False, options) self.assertEqual(err[0], QgsVectorLayerExporter.ErrCreateDataSource) # Test overwriting lyr = QgsVectorLayer(uri, "x", "memory") self.assertTrue(lyr.isValid()) f = QgsFeature(lyr.fields()) f['f1'] = 3 lyr.dataProvider().addFeatures([f]) options['overwrite'] = True err = QgsVectorLayerExporter.exportLayer(lyr, tmpfile, "ogr", lyr.crs(), False, options) self.assertEqual(err[0], QgsVectorLayerExporter.NoError, 'unexpected import error {0}'.format(err)) lyr = QgsVectorLayer(tmpfile + "|layername=my_out_table", "y", "ogr") self.assertTrue(lyr.isValid()) features = lyr.getFeatures() f = next(features) self.assertEqual(f['f1'], 3) features = None
EX_FAILURE = 1 GDAL_STATS_KEYS = ('STATISTICS_MINIMUM', 'STATISTICS_MAXIMUM', 'STATISTICS_MEAN', 'STATISTICS_STDDEV') # @NOTE: the band.GetStatistics method called with the second argument # set to False (no image rescanning) has been fixed in # r19666_ (1.6 branch) and r19665_ (1.7 branch) # see `ticket #3572` on `GDAL Trac`_. # # .. _r19666: http://trac.osgeo.org/gdal/changeset/19666 # .. _r19665: http://trac.osgeo.org/gdal/changeset/19665 # .. _`ticket #3572`: http://trac.osgeo.org/gdal/ticket/3572 # .. _`GDAL Trac`: http://trac.osgeo.org/gdal HAS_GETSTATS_FORCE_BUG = (('1640' <= gdal.VersionInfo() < '1700') or (gdal.VersionInfo() > '1720')) def SafeGetStatistics(band, approxok, force): '''Retriewe statistics form a GDAL raster band in a safe way. If it is not possible to get statistics (e.g. because the force flag is set to false and statistics are not available, or because the approxok is set and there are too many nodata elements in the raster band) then a tuple of four None is returned. :param approxok: if True statistics may be computed based on overviews or a subset of all tiles :param force:
def test(self): # Skip if GDAL python bindings are not available try: from osgeo import gdal, ogr except: return version_num = int(gdal.VersionInfo('VERSION_NUM')) if version_num < GDAL_COMPUTE_VERSION(1, 11, 0): return dialog = QgsNewGeoPackageLayerDialog() dialog.setProperty("hideDialogs", True) mDatabase = dialog.findChild(QgsFileWidget, "mDatabase") buttonBox = dialog.findChild(QDialogButtonBox, "buttonBox") ok_button = buttonBox.button(QDialogButtonBox.Ok) mTableNameEdit = dialog.findChild(QLineEdit, "mTableNameEdit") mLayerIdentifierEdit = dialog.findChild(QLineEdit, "mLayerIdentifierEdit") mLayerDescriptionEdit = dialog.findChild(QLineEdit, "mLayerDescriptionEdit") mFeatureIdColumnEdit = dialog.findChild(QLineEdit, "mFeatureIdColumnEdit") mGeometryTypeBox = dialog.findChild(QComboBox, "mGeometryTypeBox") mGeometryColumnEdit = dialog.findChild(QLineEdit, "mGeometryColumnEdit") mFieldNameEdit = dialog.findChild(QLineEdit, "mFieldNameEdit") mFieldTypeBox = dialog.findChild(QComboBox, "mFieldTypeBox") mFieldLengthEdit = dialog.findChild(QLineEdit, "mFieldLengthEdit") mAddAttributeButton = dialog.findChild(QToolButton, "mAddAttributeButton") mRemoveAttributeButton = dialog.findChild(QToolButton, "mRemoveAttributeButton") mAttributeView = dialog.findChild(QTreeWidget, "mAttributeView") dialog.accepted.connect(self.accepted_slot) mGeometryTypeBox.setCurrentIndex(mGeometryTypeBox.findData(ogr.wkbPoint)) self.assertEqual(mGeometryTypeBox.currentText(), "Point") self.assertFalse(ok_button.isEnabled()) dbname = os.path.join(self.basetestpath, 'test.gpkg') mDatabase.setFilePath(dbname) self.assertEqual(mTableNameEdit.text(), 'test') self.assertEqual(mLayerIdentifierEdit.text(), 'test') self.assertTrue(ok_button.isEnabled()) mGeometryColumnEdit.setText('my_geom') mFeatureIdColumnEdit.setText('my_fid') self.assertFalse(mAddAttributeButton.isEnabled()) self.assertFalse(mRemoveAttributeButton.isEnabled()) mFieldNameEdit.setText('strfield') self.assertTrue(mAddAttributeButton.isEnabled()) mFieldLengthEdit.setText('10') QTest.mouseClick(mAddAttributeButton, Qt.LeftButton) mFieldNameEdit.setText('intfield') mFieldTypeBox.setCurrentIndex(mFieldTypeBox.findData('integer')) self.assertFalse(mFieldLengthEdit.isEnabled()) QTest.mouseClick(mAddAttributeButton, Qt.LeftButton) mFieldNameEdit.setText('realfield') mFieldTypeBox.setCurrentIndex(mFieldTypeBox.findData('real')) self.assertFalse(mFieldLengthEdit.isEnabled()) QTest.mouseClick(mAddAttributeButton, Qt.LeftButton) mFieldNameEdit.setText('datefield') mFieldTypeBox.setCurrentIndex(mFieldTypeBox.findData('date')) self.assertFalse(mFieldLengthEdit.isEnabled()) QTest.mouseClick(mAddAttributeButton, Qt.LeftButton) mFieldNameEdit.setText('datetimefield') mFieldTypeBox.setCurrentIndex(mFieldTypeBox.findData('datetime')) self.assertFalse(mFieldLengthEdit.isEnabled()) QTest.mouseClick(mAddAttributeButton, Qt.LeftButton) if version_num >= GDAL_COMPUTE_VERSION(2, 0, 0): mFieldNameEdit.setText('int64field') mFieldTypeBox.setCurrentIndex(mFieldTypeBox.findData('integer64')) self.assertFalse(mFieldLengthEdit.isEnabled()) QTest.mouseClick(mAddAttributeButton, Qt.LeftButton) # Add and remove field mFieldNameEdit.setText('dummy') self.assertFalse(mFieldLengthEdit.isEnabled()) QTest.mouseClick(mAddAttributeButton, Qt.LeftButton) index = mAttributeView.model().index(mAttributeView.model().rowCount() - 1, 0) mAttributeView.setCurrentIndex(index) QTest.mouseClick(mRemoveAttributeButton, Qt.LeftButton) self.accepted = False QTest.mouseClick(ok_button, Qt.LeftButton) self.assertTrue(self.accepted) layers = QgsProject.instance().mapLayers() self.assertEqual(len(layers), 1) layer = layers[list(layers.keys())[0]] self.assertEqual(layer.name(), 'test') self.assertEqual(layer.geometryType(), QgsWkbTypes.PointGeometry) QgsProject.instance().removeAllMapLayers() ds = ogr.Open(dbname) lyr = ds.GetLayer(0) self.assertEqual(lyr.GetFIDColumn(), 'my_fid') self.assertEqual(lyr.GetGeometryColumn(), 'my_geom') self.assertEqual(lyr.GetGeomType(), ogr.wkbPoint) if version_num >= GDAL_COMPUTE_VERSION(2, 0, 0): self.assertEqual(lyr.GetLayerDefn().GetFieldCount(), 6) else: self.assertEqual(lyr.GetLayerDefn().GetFieldCount(), 5) self.assertEqual(lyr.GetLayerDefn().GetFieldDefn(0).GetNameRef(), 'strfield') self.assertEqual(lyr.GetLayerDefn().GetFieldDefn(0).GetType(), ogr.OFTString) # Only GDAL 2.0 recognizes string field width if version_num >= GDAL_COMPUTE_VERSION(2, 0, 0): self.assertEqual(lyr.GetLayerDefn().GetFieldDefn(0).GetWidth(), 10) self.assertEqual(lyr.GetLayerDefn().GetFieldDefn(1).GetNameRef(), 'intfield') self.assertEqual(lyr.GetLayerDefn().GetFieldDefn(1).GetType(), ogr.OFTInteger) self.assertEqual(lyr.GetLayerDefn().GetFieldDefn(1).GetWidth(), 0) self.assertEqual(lyr.GetLayerDefn().GetFieldDefn(2).GetNameRef(), 'realfield') self.assertEqual(lyr.GetLayerDefn().GetFieldDefn(2).GetType(), ogr.OFTReal) self.assertEqual(lyr.GetLayerDefn().GetFieldDefn(2).GetWidth(), 0) self.assertEqual(lyr.GetLayerDefn().GetFieldDefn(3).GetNameRef(), 'datefield') self.assertEqual(lyr.GetLayerDefn().GetFieldDefn(3).GetType(), ogr.OFTDate) self.assertEqual(lyr.GetLayerDefn().GetFieldDefn(3).GetWidth(), 0) self.assertEqual(lyr.GetLayerDefn().GetFieldDefn(4).GetNameRef(), 'datetimefield') if version_num >= GDAL_COMPUTE_VERSION(2, 0, 0): self.assertEqual(lyr.GetLayerDefn().GetFieldDefn(4).GetType(), ogr.OFTDateTime) else: # There's a bug in OGR 1.11. The field is probably declared as DATETIME in SQL # but OGR detects it as OFTDate self.assertEqual(lyr.GetLayerDefn().GetFieldDefn(4).GetType(), ogr.OFTDate) self.assertEqual(lyr.GetLayerDefn().GetFieldDefn(4).GetWidth(), 0) if version_num >= GDAL_COMPUTE_VERSION(2, 0, 0): self.assertEqual(lyr.GetLayerDefn().GetFieldDefn(5).GetNameRef(), 'int64field') self.assertEqual(lyr.GetLayerDefn().GetFieldDefn(5).GetType(), ogr.OFTInteger64) self.assertEqual(lyr.GetLayerDefn().GetFieldDefn(5).GetWidth(), 0) ds = None # Try re-adding with different table. It should ask if we want to # overwrite the DB, and we'll implicitly answer cancel, hence failure mTableNameEdit.setText('table2') self.accepted = False QTest.mouseClick(ok_button, Qt.LeftButton) self.assertFalse(self.accepted) # Retry, and ask to keep the DB self.accepted = False dialog.setProperty('question_existing_db_answer_add_new_layer', True) QTest.mouseClick(ok_button, Qt.LeftButton) dialog.setProperty('question_existing_db_answer_add_new_layer', None) self.assertTrue(self.accepted) QgsProject.instance().removeAllMapLayers() ds = ogr.Open(dbname) self.assertEqual(ds.GetLayerCount(), 2) ds = None # Retry, and ask to overwrite the DB self.accepted = False dialog.setProperty('question_existing_db_answer_overwrite', True) QTest.mouseClick(ok_button, Qt.LeftButton) dialog.setProperty('question_existing_db_answer_overwrite', None) self.assertTrue(self.accepted) QgsProject.instance().removeAllMapLayers() ds = ogr.Open(dbname) self.assertEqual(ds.GetLayerCount(), 1) ds = None # Try re-adding with same parameters. It should ask if we want to # overwrite the layer, and we'll implicitly answer no, hence failure # since it already exists with that name self.accepted = False dialog.setProperty('question_existing_db_answer_add_new_layer', True) QTest.mouseClick(ok_button, Qt.LeftButton) dialog.setProperty('question_existing_db_answer_add_new_layer', None) self.assertFalse(self.accepted) # Now answer yes, and change a few things mLayerIdentifierEdit.setText('my_identifier') mLayerDescriptionEdit.setText('my_description') dialog.setProperty('question_existing_db_answer_add_new_layer', True) dialog.setProperty('question_existing_layer_answer_overwrite', True) self.accepted = False QTest.mouseClick(ok_button, Qt.LeftButton) dialog.setProperty('question_existing_db_answer_add_new_layer', None) dialog.setProperty('question_existing_layer_answer_overwrite', None) self.assertTrue(self.accepted) # Only check with OGR 2.0 since the IDENTIFIER and DESCRIPTION creation options don't exist in OGR 1.11 if version_num >= GDAL_COMPUTE_VERSION(2, 0, 0): layers = QgsProject.instance().mapLayers() self.assertEqual(len(layers), 1) layer = layers[list(layers.keys())[0]] self.assertEqual(layer.name(), 'my_identifier') QgsProject.instance().removeAllMapLayers() ds = ogr.Open(dbname) sql_lyr = ds.ExecuteSQL('SELECT * FROM gpkg_contents') self.assertEqual(sql_lyr.GetFeatureCount(), 1) f = sql_lyr.GetNextFeature() identifier = f.GetField('identifier') description = f.GetField('description') f = None ds.ReleaseResultSet(sql_lyr) ds = None self.assertEqual(identifier, 'my_identifier') self.assertEqual(description, 'my_description') else: QgsProject.instance().removeAllMapLayers() # Try invalid path mDatabase.setFilePath('/this/is/invalid/test.gpkg') self.accepted = False QTest.mouseClick(ok_button, Qt.LeftButton) self.assertFalse(self.accepted)
__revision__ = '$Format:%H$' __date__ = '14/06/2016' def _run_tests(test_suite, package_name): """Core function to test a test suite.""" count = test_suite.countTestCases() # fix_print_with_import # fix_print_with_import print('########') # fix_print_with_import # fix_print_with_import print('%s tests has been discovered in %s' % (count, package_name)) # fix_print_with_import # fix_print_with_import print('Python GDAL : %s' % gdal.VersionInfo('VERSION_NUM')) # fix_print_with_import # fix_print_with_import print('########') currentdir = dirname(abspath(inspect.getfile(inspect.currentframe()))) parentdir = dirname(currentdir) sys.path.insert(0, parentdir) sys.path.insert(0, '/root/.qgis2/python/plugins/GeoPublicHealth') unittest.TextTestRunner(verbosity=3, stream=sys.stdout).run(test_suite) def test_package(package='src'): """Test package. This function is called by travis without arguments. :param package: The package to test.
def misc_12(): if int(gdal.VersionInfo('VERSION_NUM')) < 1900: gdaltest.post_reason('would crash') return 'skip' import test_cli_utilities gdal_translate_path = test_cli_utilities.get_gdal_translate_path() for i in range(gdal.GetDriverCount()): drv = gdal.GetDriver(i) #if drv.ShortName == 'ECW' or drv.ShortName == 'JP2ECW': # continue md = drv.GetMetadata() if 'DCAP_CREATECOPY' in md or 'DCAP_CREATE' in md and 'DCAP_RASTER' in md: ext = '' if drv.ShortName == 'GTX': ext = '.gtx' elif drv.ShortName == 'RST': ext = '.rst' elif drv.ShortName == 'SAGA': ext = '.sdat' elif drv.ShortName == 'ECW': ext = '.ecw' elif drv.ShortName == 'KMLSUPEROVERLAY': ext = '.kmz' elif drv.ShortName == 'ADRG': ext = '/ABCDEF01.GEN' elif drv.ShortName == 'SRTMHGT': ext = '/N48E002.HGT' nbands = 1 if drv.ShortName == 'WEBP' or drv.ShortName == 'ADRG': nbands = 3 datatype = gdal.GDT_Byte if drv.ShortName == 'BT' or drv.ShortName == 'BLX': datatype = gdal.GDT_Int16 elif drv.ShortName == 'GTX' or drv.ShortName == 'NTv2' or drv.ShortName == 'Leveller' : datatype = gdal.GDT_Float32 size = 1201 if drv.ShortName == 'BLX': size = 128 src_ds = gdal.GetDriverByName('GTiff').Create('/vsimem/misc_12_src.tif', size, size, nbands, datatype) set_gt = (2,1.0/size,0,49,0,-1.0/size) src_ds.SetGeoTransform(set_gt) src_ds.SetProjection('GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["degree",0.01745329251994328]]') # Test to detect crashes gdal.PushErrorHandler('CPLQuietErrorHandler') ds = drv.CreateCopy('/nonexistingpath/nonexistingfile' + ext, src_ds) gdal.PopErrorHandler() if ds is None and gdal.GetLastErrorMsg() == '': gdaltest.post_reason('failure') print('CreateCopy() into non existing dir fails without error message for driver %s' % drv.ShortName) return 'fail' ds = None if gdal_translate_path is not None: # Test to detect memleaks ds = gdal.GetDriverByName('VRT').CreateCopy('tmp/misc_12.vrt', src_ds) (out, err) = gdaltest.runexternal_out_and_err(gdal_translate_path + ' -of ' + drv.ShortName + ' tmp/misc_12.vrt /nonexistingpath/nonexistingfile' + ext, check_memleak = False) del ds gdal.Unlink('tmp/misc_12.vrt') # If DEBUG_VSIMALLOC_STATS is defined, this is an easy way # to catch some memory leaks if out.find('VSIMalloc + VSICalloc - VSIFree') != -1 and \ out.find('VSIMalloc + VSICalloc - VSIFree : 0') == -1: if drv.ShortName == 'Rasterlite' and out.find('VSIMalloc + VSICalloc - VSIFree : 1') != -1: pass else: print('memleak detected for driver %s' % drv.ShortName) src_ds = None gdal.Unlink('/vsimem/misc_12_src.tif') return 'success'
def reproject(*args, **kwargs): """Transform coordinates from a source projection to a target projection. Call signatures:: reproject(C, **kwargs) reproject(X, Y, **kwargs) reproject(X, Y, Z, **kwargs) *C* is the np array of source coordinates. *X*, *Y* and *Z* specify arrays of x, y, and z coordinate values Parameters ---------- C : multidimensional :class:`numpy:numpy.ndarray` Array of shape (...,2) or (...,3) with coordinates (x,y) or (x,y,z) respectively X : :class:`numpy:numpy.ndarray` Array of x coordinates Y : :class:`numpy:numpy.ndarray` Array of y coordinates Z : :class:`numpy:numpy.ndarray` Array of z coordinates Keyword Arguments ----------------- projection_source : osr object defaults to EPSG(4326) projection_target : osr object defaults to EPSG(4326) area_of_interest : tuple tuple of floats (WestLongitudeDeg, SouthLatitudeDeg, EastLongitudeDeg, dfNorthLatitudeDeg), only gdal>=3 Returns ------- trans : :class:`numpy:numpy.ndarray` Array of reprojected coordinates x,y (...,2) or x,y,z (...,3) depending on input array. X, Y : :class:`numpy:numpy.ndarray` Arrays of reprojected x,y coordinates, shape depending on input array X, Y, Z: :class:`numpy:numpy.ndarray` Arrays of reprojected x,y,z coordinates, shape depending on input array Examples -------- See :ref:`/notebooks/georeferencing/wradlib_georef_example.ipynb`. """ if len(args) == 1: C = np.asanyarray(args[0]) cshape = C.shape numCols = C.shape[-1] C = C.reshape(-1, numCols) if numCols < 2 or numCols > 3: raise TypeError('Input Array column mismatch ' 'to %s' % ('reproject')) else: if len(args) == 2: X, Y = (np.asanyarray(arg) for arg in args) numCols = 2 elif len(args) == 3: X, Y, Z = (np.asanyarray(arg) for arg in args) zshape = Z.shape numCols = 3 else: raise TypeError('Illegal arguments to %s' % ('reproject')) xshape = X.shape yshape = Y.shape if xshape != yshape: raise TypeError('Incompatible X, Y inputs to %s' % ('reproject')) if 'Z' in locals(): if xshape != zshape: raise TypeError('Incompatible Z input to %s' % ('reproject')) C = np.concatenate( [X.ravel()[:, None], Y.ravel()[:, None], Z.ravel()[:, None]], axis=1) else: C = np.concatenate( [X.ravel()[:, None], Y.ravel()[:, None]], axis=1) projection_source = kwargs.get('projection_source', get_default_projection()) projection_target = kwargs.get('projection_target', get_default_projection()) area_of_interest = kwargs.get( 'area_of_interest', (np.float(C[..., 0].min()), np.float(C[..., 1].min()), np.float(C[..., 0].max()), np.float(C[..., 1].max()))) if gdal.VersionInfo()[0] >= '3': axis_order = osr.OAMS_TRADITIONAL_GIS_ORDER projection_source.SetAxisMappingStrategy(axis_order) projection_target.SetAxisMappingStrategy(axis_order) options = osr.CoordinateTransformationOptions() options.SetAreaOfInterest(*area_of_interest) ct = osr.CreateCoordinateTransformation(projection_source, projection_target, options) else: ct = osr.CoordinateTransformation(projection_source, projection_target) trans = np.array(ct.TransformPoints(C)) if len(args) == 1: # here we could do this one # return(np.array(ct.TransformPoints(C))[...,0:numCols])) # or this one trans = trans[:, 0:numCols].reshape(cshape) return trans else: X = trans[:, 0].reshape(xshape) Y = trans[:, 1].reshape(yshape) if len(args) == 2: return X, Y if len(args) == 3: Z = trans[:, 2].reshape(zshape) return X, Y, Z