Esempio n. 1
0
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
Esempio n. 2
0
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
Esempio n. 3
0
    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)])
Esempio n. 4
0
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))
Esempio n. 5
0
 def readableVersion():
     return gdal.VersionInfo('RELEASE_NAME')
Esempio n. 6
0
 def version(self):
     return Version(gdal.VersionInfo("RELEASE_NAME"))
Esempio n. 7
0
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)
Esempio n. 10
0
    def version(self):
        '''Get GDAL version.

        Return version of installed GDAL.
        '''
        return gdal.VersionInfo('RELEASE_NAME')
Esempio n. 11
0
    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
Esempio n. 12
0
                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')
Esempio n. 13
0
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
Esempio n. 14
0
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
Esempio n. 15
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))
Esempio n. 16
0
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)
Esempio n. 17
0
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))
Esempio n. 18
0
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 -
Esempio n. 19
0
 def versionNum(self):
     return int(gdal.VersionInfo("VERSION_NUM"))
Esempio n. 20
0
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
Esempio n. 21
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())
Esempio n. 22
0
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')
Esempio n. 23
0
 def version():
     return int(gdal.VersionInfo('VERSION_NUM'))
Esempio n. 24
0
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')
Esempio n. 25
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.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
Esempio n. 26
0
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:
Esempio n. 27
0
    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)
Esempio n. 28
0
__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.
Esempio n. 29
0
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'
Esempio n. 30
0
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