Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add shim module and support for GDAL 3 #804

Merged
merged 17 commits into from
Oct 21, 2019
5 changes: 4 additions & 1 deletion CHANGES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@ Changes

All issue numbers are relative to https://github.com/Toblerity/Fiona/issues.

1.8.9 (TBD)
-----------
- A shim module and support for GDAL 3.0 has been added.

1.8.8 (2019-09-25)
------------------

- The schema of geopackage files with a geometry type code of 3000 could not be
reported using Fiona 1.8.7. This bug is fixed.

Expand Down
2 changes: 1 addition & 1 deletion fiona/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ class Path:


__all__ = ['bounds', 'listlayers', 'open', 'prop_type', 'prop_width']
__version__ = "1.8.8"
__version__ = "1.8.9dev"
__gdal_version__ = get_gdal_release_name()

gdal_version = get_gdal_version_tuple()
Expand Down
1 change: 0 additions & 1 deletion fiona/_crs.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ cdef extern from "ogr_srs_api.h":
int OSRImportFromProj4 (OGRSpatialReferenceH srs, char *proj)
int OSRSetFromUserInput (OGRSpatialReferenceH srs, char *input)
int OSRAutoIdentifyEPSG (OGRSpatialReferenceH srs)
int OSRFixup(OGRSpatialReferenceH srs)
const char * OSRGetAuthorityName (OGRSpatialReferenceH srs, const char *key)
const char * OSRGetAuthorityCode (OGRSpatialReferenceH srs, const char *key)
OGRSpatialReferenceH OSRNewSpatialReference (char *wkt)
Expand Down
12 changes: 8 additions & 4 deletions fiona/_crs.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,20 @@ import logging
from six import string_types

from fiona cimport _cpl
from fiona._shim cimport osr_get_name, osr_set_traditional_axis_mapping_strategy

from fiona.errors import CRSError


logger = logging.getLogger(__name__)

cdef int OAMS_TRADITIONAL_GIS_ORDER = 0


# Export a WKT string from input crs.
def crs_to_wkt(crs):
"""Convert a Fiona CRS object to WKT format"""
cdef void *cogr_srs = NULL
cdef OGRSpatialReferenceH cogr_srs = NULL
cdef char *proj_c = NULL

cogr_srs = OSRNewSpatialReference(NULL)
Expand All @@ -31,6 +35,7 @@ def crs_to_wkt(crs):
proj_b = crs.encode('utf-8')
proj_c = proj_b
OSRSetFromUserInput(cogr_srs, proj_c)

elif isinstance(crs, dict):
# EPSG is a special case.
init = crs.get('init')
Expand All @@ -53,12 +58,11 @@ def crs_to_wkt(crs):
proj_b = proj.encode('utf-8')
proj_c = proj_b
OSRImportFromProj4(cogr_srs, proj_c)

else:
raise ValueError("Invalid CRS")

# Fixup, export to WKT, and set the GDAL dataset's projection.
OSRFixup(cogr_srs)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this OSRFixup call should be still used in GDAL < 3.0. Supporting both version scenarios is probably complicated, however.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you see any harm in having osr_set_traditional_axis_mapping_strategy call OSRFixup for GDAL versions 1 and 2 @hobu ? That's one way to bring it back in.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No harm, but that's not quite where you want it. You want it right after any ImportFromWkt-type calls happen where the input might be ESRI content.


osr_set_traditional_axis_mapping_strategy(cogr_srs)
OSRExportToWkt(cogr_srs, &proj_c)

if proj_c == NULL:
Expand Down
2 changes: 2 additions & 0 deletions fiona/_shim1.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ cdef OGRFieldSubType get_field_subtype(void *fielddefn)
cdef void set_field_subtype(void *fielddefn, OGRFieldSubType subtype)
cdef bint check_capability_create_layer(void *cogr_ds)
cdef void *get_linear_geometry(void *geom)
cdef const char* osr_get_name(OGRSpatialReferenceH hSrs)
Copy link
Member Author

@sgillies sgillies Oct 18, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function is copied from rasterio. It isn't called in fiona yet, but it will be in a future version as we change our modules to be more like those in rasterio. Therefore, I'm going to leave it in.

cdef void osr_set_traditional_axis_mapping_strategy(OGRSpatialReferenceH hSrs)

from fiona._shim cimport OGR_F_GetFieldAsInteger as OGR_F_GetFieldAsInteger64
from fiona._shim cimport OGR_F_SetFieldInteger as OGR_F_SetFieldInteger64
Expand Down
8 changes: 8 additions & 0 deletions fiona/_shim1.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -123,3 +123,11 @@ cdef bint check_capability_create_layer(void *cogr_ds):

cdef void *get_linear_geometry(void *geom):
return geom


cdef const char* osr_get_name(OGRSpatialReferenceH hSrs):
return ''


cdef void osr_set_traditional_axis_mapping_strategy(OGRSpatialReferenceH hSrs):
pass
2 changes: 2 additions & 0 deletions fiona/_shim2.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,5 @@ cdef OGRFieldSubType get_field_subtype(void *fielddefn)
cdef void set_field_subtype(void *fielddefn, OGRFieldSubType subtype)
cdef bint check_capability_create_layer(void *cogr_ds)
cdef void *get_linear_geometry(void *geom)
cdef const char* osr_get_name(OGRSpatialReferenceH hSrs)
cdef void osr_set_traditional_axis_mapping_strategy(OGRSpatialReferenceH hSrs)
5 changes: 5 additions & 0 deletions fiona/_shim2.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -125,3 +125,8 @@ cdef bint check_capability_create_layer(void *cogr_ds):
cdef void *get_linear_geometry(void *geom):
return OGR_G_GetLinearGeometry(geom, 0.0, NULL)

cdef const char* osr_get_name(OGRSpatialReferenceH hSrs):
return ''

cdef void osr_set_traditional_axis_mapping_strategy(OGRSpatialReferenceH hSrs):
pass
2 changes: 2 additions & 0 deletions fiona/_shim22.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,5 @@ cdef OGRFieldSubType get_field_subtype(void *fielddefn)
cdef void set_field_subtype(void *fielddefn, OGRFieldSubType subtype)
cdef bint check_capability_create_layer(void *cogr_ds)
cdef void *get_linear_geometry(void *geom)
cdef const char* osr_get_name(OGRSpatialReferenceH hSrs)
cdef void osr_set_traditional_axis_mapping_strategy(OGRSpatialReferenceH hSrs)
6 changes: 6 additions & 0 deletions fiona/_shim22.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -132,3 +132,9 @@ cdef bint check_capability_create_layer(void *cogr_ds):

cdef void *get_linear_geometry(void *geom):
return OGR_G_GetLinearGeometry(geom, 0.0, NULL)

cdef const char* osr_get_name(OGRSpatialReferenceH hSrs):
return ''

cdef void osr_set_traditional_axis_mapping_strategy(OGRSpatialReferenceH hSrs):
pass
16 changes: 16 additions & 0 deletions fiona/_shim3.pxd
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
include "ogrext3.pxd"

cdef bint is_field_null(void *feature, int n)
cdef void set_field_null(void *feature, int n)
cdef void gdal_flush_cache(void *cogr_ds)
cdef void* gdal_open_vector(const char *path_c, int mode, drivers, options) except NULL
cdef void* gdal_create(void* cogr_driver, const char *path_c, options) except NULL
cdef OGRErr gdal_start_transaction(void *cogr_ds, int force)
cdef OGRErr gdal_commit_transaction(void *cogr_ds)
cdef OGRErr gdal_rollback_transaction(void *cogr_ds)
cdef OGRFieldSubType get_field_subtype(void *fielddefn)
cdef void set_field_subtype(void *fielddefn, OGRFieldSubType subtype)
cdef bint check_capability_create_layer(void *cogr_ds)
cdef void *get_linear_geometry(void *geom)
cdef const char* osr_get_name(OGRSpatialReferenceH hSrs)
cdef void osr_set_traditional_axis_mapping_strategy(OGRSpatialReferenceH hSrs)
149 changes: 149 additions & 0 deletions fiona/_shim3.pyx
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
"""Shims on top of ogrext for GDAL versions >= 2.2"""
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

>= 3.0


cdef extern from "ogr_api.h":

int OGR_F_IsFieldNull(void *feature, int n)


cdef extern from "ogr_srs_api.h" nogil:

ctypedef enum OSRAxisMappingStrategy:
OAMS_TRADITIONAL_GIS_ORDER

const char* OSRGetName(OGRSpatialReferenceH hSRS)
void OSRSetAxisMappingStrategy(OGRSpatialReferenceH hSRS, OSRAxisMappingStrategy)


from fiona.ogrext2 cimport *
from fiona._err cimport exc_wrap_pointer
from fiona._err import cpl_errs, CPLE_BaseError, FionaNullPointerError
from fiona.errors import DriverError

import logging


log = logging.getLogger(__name__)


cdef bint is_field_null(void *feature, int n):
if OGR_F_IsFieldNull(feature, n):
return True
elif not OGR_F_IsFieldSet(feature, n):
return True
else:
return False


cdef void set_field_null(void *feature, int n):
OGR_F_SetFieldNull(feature, n)


cdef void gdal_flush_cache(void *cogr_ds):
with cpl_errs:
GDALFlushCache(cogr_ds)


cdef void* gdal_open_vector(char* path_c, int mode, drivers, options) except NULL:
cdef void* cogr_ds = NULL
cdef char **drvs = NULL
cdef void* drv = NULL
cdef char **open_opts = NULL

flags = GDAL_OF_VECTOR | GDAL_OF_VERBOSE_ERROR
if mode == 1:
flags |= GDAL_OF_UPDATE
else:
flags |= GDAL_OF_READONLY

if drivers:
for name in drivers:
name_b = name.encode()
name_c = name_b
drv = GDALGetDriverByName(name_c)
if drv != NULL:
drvs = CSLAddString(drvs, name_c)

for k, v in options.items():

if v is None:
continue

k = k.upper().encode('utf-8')
if isinstance(v, bool):
v = ('ON' if v else 'OFF').encode('utf-8')
else:
v = str(v).encode('utf-8')
log.debug("Set option %r: %r", k, v)
open_opts = CSLAddNameValue(open_opts, <const char *>k, <const char *>v)

open_opts = CSLAddNameValue(open_opts, "VALIDATE_OPEN_OPTIONS", "NO")

try:
cogr_ds = exc_wrap_pointer(
GDALOpenEx(path_c, flags, <const char *const *>drvs, open_opts, NULL)
)
return cogr_ds
except FionaNullPointerError:
raise DriverError("Failed to open dataset (mode={}): {}".format(mode, path_c.decode("utf-8")))
except CPLE_BaseError as exc:
raise DriverError(str(exc))
finally:
CSLDestroy(drvs)
CSLDestroy(open_opts)


cdef void* gdal_create(void* cogr_driver, const char *path_c, options) except NULL:
cdef char **creation_opts = NULL
cdef void *cogr_ds = NULL

for k, v in options.items():
k = k.upper().encode('utf-8')
if isinstance(v, bool):
v = ('ON' if v else 'OFF').encode('utf-8')
else:
v = str(v).encode('utf-8')
log.debug("Set option %r: %r", k, v)
creation_opts = CSLAddNameValue(creation_opts, <const char *>k, <const char *>v)

try:
return exc_wrap_pointer(GDALCreate(cogr_driver, path_c, 0, 0, 0, GDT_Unknown, creation_opts))
except FionaNullPointerError:
raise DriverError("Failed to create dataset: {}".format(path_c.decode("utf-8")))
except CPLE_BaseError as exc:
raise DriverError(str(exc))
finally:
CSLDestroy(creation_opts)


cdef OGRErr gdal_start_transaction(void* cogr_ds, int force):
return GDALDatasetStartTransaction(cogr_ds, force)


cdef OGRErr gdal_commit_transaction(void* cogr_ds):
return GDALDatasetCommitTransaction(cogr_ds)


cdef OGRErr gdal_rollback_transaction(void* cogr_ds):
return GDALDatasetRollbackTransaction(cogr_ds)


cdef OGRFieldSubType get_field_subtype(void *fielddefn):
return OGR_Fld_GetSubType(fielddefn)


cdef void set_field_subtype(void *fielddefn, OGRFieldSubType subtype):
OGR_Fld_SetSubType(fielddefn, subtype)


cdef bint check_capability_create_layer(void *cogr_ds):
return GDALDatasetTestCapability(cogr_ds, ODsCCreateLayer)


cdef void *get_linear_geometry(void *geom):
return OGR_G_GetLinearGeometry(geom, 0.0, NULL)

cdef const char* osr_get_name(OGRSpatialReferenceH hSrs):
return OSRGetName(hSrs)

cdef void osr_set_traditional_axis_mapping_strategy(OGRSpatialReferenceH hSrs):
OSRSetAxisMappingStrategy(hSrs, OAMS_TRADITIONAL_GIS_ORDER)
3 changes: 3 additions & 0 deletions fiona/_transform.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import logging

from fiona cimport _cpl, _crs, _csl, _geometry
from fiona._crs cimport OGRSpatialReferenceH
from fiona._shim cimport osr_set_traditional_axis_mapping_strategy

from fiona.compat import UserDict

Expand Down Expand Up @@ -52,6 +53,7 @@ cdef void *_crs_from_crs(object crs):
auth, val = init.split(':')
if auth.upper() == 'EPSG':
_crs.OSRImportFromEPSG(osr, int(val))
osr_set_traditional_axis_mapping_strategy(osr)
else:
crs['wktext'] = True
for k, v in crs.items():
Expand All @@ -64,6 +66,7 @@ cdef void *_crs_from_crs(object crs):
proj_b = proj.encode('utf-8')
proj_c = proj_b
_crs.OSRImportFromProj4(osr, proj_c)
osr_set_traditional_axis_mapping_strategy(osr)
# Fall back for CRS strings like "EPSG:3857."
else:
proj_b = crs.encode('utf-8')
Expand Down
1 change: 0 additions & 1 deletion fiona/gdal.pxi
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,6 @@ cdef extern from "ogr_srs_api.h" nogil:
OGRSpatialReferenceH OSRClone(OGRSpatialReferenceH srs)
int OSRExportToProj4(OGRSpatialReferenceH srs, char **params)
int OSRExportToWkt(OGRSpatialReferenceH srs, char **params)
int OSRFixup(OGRSpatialReferenceH srs)
const char *OSRGetAuthorityName(OGRSpatialReferenceH srs, const char *key)
const char *OSRGetAuthorityCode(OGRSpatialReferenceH srs, const char *key)
int OSRImportFromEPSG(OGRSpatialReferenceH srs, int code)
Expand Down
9 changes: 4 additions & 5 deletions fiona/ogrext.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ from fiona.rfc3339 import FionaDateType, FionaDateTimeType, FionaTimeType
from fiona.schema import FIELD_TYPES, FIELD_TYPES_MAP, normalize_field_type
from fiona.path import vsi_path

from fiona._shim cimport is_field_null
from fiona._shim cimport is_field_null, osr_get_name, osr_set_traditional_axis_mapping_strategy

from libc.stdlib cimport malloc, free
from libc.string cimport strcmp
Expand Down Expand Up @@ -877,7 +877,7 @@ cdef class WritingSession(Session):
cdef object _schema_mapping

def start(self, collection, **kwargs):
cdef void *cogr_srs = NULL
cdef OGRSpatialReferenceH cogr_srs = NULL
cdef char **options = NULL
cdef const char *path_c = NULL
cdef const char *driver_c = NULL
Expand Down Expand Up @@ -995,6 +995,7 @@ cdef class WritingSession(Session):
if auth.upper() == 'EPSG':
log.debug("Setting EPSG: %s", val)
OSRImportFromEPSG(cogr_srs, int(val))
osr_set_traditional_axis_mapping_strategy(cogr_srs)
else:
params = []
col_crs['wktext'] = True
Expand All @@ -1008,13 +1009,11 @@ cdef class WritingSession(Session):
proj_b = proj.encode('utf-8')
proj_c = proj_b
OSRImportFromProj4(cogr_srs, proj_c)
osr_set_traditional_axis_mapping_strategy(cogr_srs)

else:
raise ValueError("Invalid CRS")

# Fixup, export to WKT, and set the GDAL dataset's projection.
OSRFixup(cogr_srs)

except (ValueError, CPLE_BaseError) as exc:
OGRReleaseDataSource(self.cogr_ds)
self.cogr_ds = NULL
Expand Down
1 change: 0 additions & 1 deletion fiona/ogrext1.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,6 @@ cdef extern from "ogr_srs_api.h":
int OSRImportFromProj4 (OGRSpatialReferenceH srs, const char *proj)
int OSRSetFromUserInput (OGRSpatialReferenceH srs, const char *input)
int OSRAutoIdentifyEPSG (OGRSpatialReferenceH srs)
int OSRFixup(OGRSpatialReferenceH srs)
const char * OSRGetAuthorityName (OGRSpatialReferenceH srs, const char *key)
const char * OSRGetAuthorityCode (OGRSpatialReferenceH srs, const char *key)
OGRSpatialReferenceH OSRNewSpatialReference (char *wkt)
Expand Down
1 change: 0 additions & 1 deletion fiona/ogrext2.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,6 @@ cdef extern from "ogr_srs_api.h":
int OSRImportFromProj4 (OGRSpatialReferenceH srs, const char *proj)
int OSRSetFromUserInput (OGRSpatialReferenceH srs, const char *input)
int OSRAutoIdentifyEPSG (OGRSpatialReferenceH srs)
int OSRFixup(OGRSpatialReferenceH srs)
const char * OSRGetAuthorityName (OGRSpatialReferenceH srs, const char *key)
const char * OSRGetAuthorityCode (OGRSpatialReferenceH srs, const char *key)
OGRSpatialReferenceH OSRNewSpatialReference (char *wkt)
Expand Down