Skip to content
Commits on Source (5)
......@@ -3,6 +3,25 @@ Changes
All issue numbers are relative to https://github.com/Toblerity/Fiona/issues.
1.8.1 (2018-11-15)
------------------
Bug fixes:
- Add checks around OSRGetAuthorityName and OSRGetAuthorityCode calls that will
log problems with looking up these items.
- Opened data sources are now released before we raise exceptions in
WritingSession.start (#676). This fixes an issue with locked files on
Windows.
- We now ensure that an Env instance exists when getting the crs or crs_wkt
properties of a Collection (#673, #690). Otherwise, required GDAL and PROJ
data files included in Fiona wheels can not be found.
- GDAL and PROJ data search has been refactored to improve testability (#678).
- In the project's Cython code, void* pointers have been replaced with proper
GDAL types (#672).
- Pervasive warning level log messages about ENCODING creation options (#668)
have been eliminated.
1.8.0 (2018-10-31)
------------------
......@@ -74,8 +93,8 @@ Refactoring:
Deprecations:
- The ``rasterio.drivers()`` context manager is officially deprecated. All
users should switch to ``rasterio.Env()``, which registers format drivers and
- The ``fiona.drivers()`` context manager is officially deprecated. All
users should switch to ``fiona.Env()``, which registers format drivers and
manages GDAL configuration in a reversible manner.
Bug fixes:
......
fiona (1.8.1-1) unstable; urgency=medium
* Team upload.
* New upstream release.
* Refresh patches.
-- Bas Couwenberg <sebastic@debian.org> Fri, 16 Nov 2018 06:56:52 +0100
fiona (1.8.0-1) unstable; urgency=medium
* Team upload.
......
......@@ -9,7 +9,7 @@ There is already another package providing a binary "fio" (fio).
--- a/setup.py
+++ b/setup.py
@@ -296,7 +296,7 @@ setup_args = dict(
@@ -306,7 +306,7 @@ setup_args = dict(
packages=['fiona', 'fiona.fio'],
entry_points='''
[console_scripts]
......
......@@ -6,7 +6,7 @@ Author: Bas Couwenberg <sebastic@debian.org>
--- a/setup.py
+++ b/setup.py
@@ -278,11 +278,8 @@ extras_require['all'] = list(set(it.chai
@@ -288,11 +288,8 @@ extras_require['all'] = list(set(it.chai
setup_args = dict(
cmdclass={'sdist': sdist_multi_gdal},
......
......@@ -85,12 +85,12 @@ from fiona.drvsupport import supported_drivers
from fiona.env import ensure_env_with_credentials, Env
from fiona.errors import FionaDeprecationWarning
from fiona._env import driver_count
from fiona._env import (
calc_gdal_version_num, get_gdal_version_num, get_gdal_release_name,
get_gdal_version_tuple)
from fiona.compat import OrderedDict
from fiona.io import MemoryFile
from fiona.ogrext import _bounds, _listlayers, FIELD_TYPES_MAP, _remove, _remove_layer
from fiona.ogrext import (
calc_gdal_version_num, get_gdal_version_num, get_gdal_release_name,
get_gdal_version_tuple)
from fiona.path import ParsedPath, parse_path, vsi_path
from fiona.vfs import parse_paths as vfs_parse_paths
......@@ -101,12 +101,13 @@ import uuid
__all__ = ['bounds', 'listlayers', 'open', 'prop_type', 'prop_width']
__version__ = "1.8.0"
__version__ = "1.8.1"
__gdal_version__ = get_gdal_release_name()
gdal_version = get_gdal_version_tuple()
log = logging.getLogger(__name__)
log.addHandler(logging.NullHandler())
@ensure_env_with_credentials
......
......@@ -18,7 +18,8 @@ cdef extern from "cpl_conv.h":
cdef extern from "cpl_error.h":
void CPLSetErrorHandler (void *handler)
ctypedef void (*CPLErrorHandler)(int, int, const char*);
void CPLSetErrorHandler (CPLErrorHandler handler)
cdef extern from "gdal.h":
......@@ -105,7 +106,7 @@ cdef class GDALEnv(object):
GDALAllRegister()
if OGRGetDriverCount() == 0:
OGRRegisterAll()
CPLSetErrorHandler(<void *>errorHandler)
CPLSetErrorHandler(<CPLErrorHandler>errorHandler)
if OGRGetDriverCount() == 0:
raise ValueError("Drivers not registered")
......
......@@ -10,6 +10,7 @@ option is set to a new value inside the thread.
include "gdal.pxi"
from collections import namedtuple
import logging
import os
import os.path
......@@ -53,12 +54,59 @@ log = logging.getLogger(__name__)
cdef bint is_64bit = sys.maxsize > 2 ** 32
def calc_gdal_version_num(maj, min, rev):
"""Calculates the internal gdal version number based on major, minor and revision
GDAL Version Information macro changed with GDAL version 1.10.0 (April 2013)
"""
if (maj, min, rev) >= (1, 10, 0):
return int(maj * 1000000 + min * 10000 + rev * 100)
else:
return int(maj * 1000 + min * 100 + rev * 10)
def get_gdal_version_num():
"""Return current internal version number of gdal"""
return int(GDALVersionInfo("VERSION_NUM"))
def get_gdal_release_name():
"""Return release name of gdal"""
cdef const char *name_c = NULL
name_c = GDALVersionInfo("RELEASE_NAME")
name = name_c
return name
GDALVersion = namedtuple("GDALVersion", ["major", "minor", "revision"])
def get_gdal_version_tuple():
"""
Calculates gdal version tuple from gdal's internal version number.
GDAL Version Information macro changed with GDAL version 1.10.0 (April 2013)
"""
gdal_version_num = get_gdal_version_num()
if gdal_version_num >= calc_gdal_version_num(1, 10, 0):
major = gdal_version_num // 1000000
minor = (gdal_version_num - (major * 1000000)) // 10000
revision = (gdal_version_num - (major * 1000000) - (minor * 10000)) // 100
return GDALVersion(major, minor, revision)
else:
major = gdal_version_num // 1000
minor = (gdal_version_num - (major * 1000)) // 100
revision = (gdal_version_num - (major * 1000) - (minor * 100)) // 10
return GDALVersion(major, minor, revision)
cdef void log_error(CPLErr err_class, int err_no, const char* msg) with gil:
"""Send CPL debug messages and warnings to Python's logger."""
log = logging.getLogger(__name__)
if err_class < 3:
if err_no in code_map:
log.log(level_map[err_class], "%s in %s", code_map[err_no], msg)
log.log(level_map[err_class], "%s", msg)
else:
log.info("Unknown error number %r", err_no)
......@@ -186,6 +234,59 @@ cdef class ConfigEnv(object):
return {k: get_gdal_config(k) for k in self.options}
class GDALDataFinder(object):
"""Finds GDAL and PROJ data files"""
def search(self, prefix=None):
"""Returns GDAL_DATA location"""
path = self.search_wheel(prefix or __file__)
if not path:
path = self.search_prefix(prefix or sys.prefix)
if not path:
path = self.search_debian(prefix or sys.prefix)
return path
def search_wheel(self, prefix=None):
"""Check wheel location"""
if prefix is None:
prefix = __file__
datadir = os.path.abspath(os.path.join(os.path.dirname(prefix), "gdal_data"))
return datadir if os.path.exists(os.path.join(datadir, 'pcs.csv')) else None
def search_prefix(self, prefix=sys.prefix):
"""Check sys.prefix location"""
datadir = os.path.join(prefix, 'share', 'gdal')
return datadir if os.path.exists(os.path.join(datadir, 'pcs.csv')) else None
def search_debian(self, prefix=sys.prefix):
"""Check Debian locations"""
gdal_version = get_gdal_version_tuple()
datadir = os.path.join(prefix, 'share', 'gdal', '{}.{}'.format(gdal_version.major, gdal_version.minor))
return datadir if os.path.exists(os.path.join(datadir, 'pcs.csv')) else None
class PROJDataFinder(object):
def search(self, prefix=None):
"""Returns PROJ_LIB location"""
path = self.search_wheel(prefix or __file__)
if not path:
path = self.search_prefix(prefix or sys.prefix)
return path
def search_wheel(self, prefix=None):
"""Check wheel location"""
if prefix is None:
prefix = __file__
datadir = os.path.abspath(os.path.join(os.path.dirname(prefix), "proj_data"))
return datadir if os.path.exists(datadir) else None
def search_prefix(self, prefix=sys.prefix):
"""Check sys.prefix location"""
datadir = os.path.join(prefix, 'share', 'proj')
return datadir if os.path.exists(datadir) else None
cdef class GDALEnv(ConfigEnv):
"""Configuration and driver management"""
......@@ -210,40 +311,22 @@ cdef class GDALEnv(ConfigEnv):
if 'GDAL_DATA' not in os.environ:
# We will try a few well-known paths, starting with the
# official wheel path.
whl_datadir = os.path.abspath(
os.path.join(os.path.dirname(__file__), "gdal_data"))
fhs_share_datadir = os.path.join(sys.prefix, 'share/gdal')
path = GDALDataFinder().search()
# Debian supports multiple GDAL installs.
gdal_release_name = GDALVersionInfo("RELEASE_NAME")
deb_share_datadir = os.path.join(
fhs_share_datadir,
"{}.{}".format(*gdal_release_name.split('.')[:2]))
if path:
log.debug("GDAL data found in %r", path)
self.update_config_options(GDAL_DATA=path)
# If we find GDAL data at the well-known paths, we will
# add a GDAL_DATA key to the config options dict.
if os.path.exists(os.path.join(whl_datadir, 'pcs.csv')):
self.update_config_options(GDAL_DATA=whl_datadir)
elif os.path.exists(os.path.join(deb_share_datadir, 'pcs.csv')):
self.update_config_options(GDAL_DATA=deb_share_datadir)
elif os.path.exists(os.path.join(fhs_share_datadir, 'pcs.csv')):
self.update_config_options(GDAL_DATA=fhs_share_datadir)
else:
self.update_config_options(GDAL_DATA=os.environ['GDAL_DATA'])
if 'PROJ_LIB' not in os.environ:
whl_datadir = os.path.abspath(
os.path.join(os.path.dirname(__file__), 'proj_data'))
share_datadir = os.path.join(sys.prefix, 'share/proj')
if os.path.exists(whl_datadir):
os.environ['PROJ_LIB'] = whl_datadir
path = PROJDataFinder().search()
elif os.path.exists(share_datadir):
os.environ['PROJ_LIB'] = share_datadir
if path:
log.debug("PROJ data found in %r", path)
os.environ['PROJ_LIB'] = path
if driver_count() == 0:
CPLPopErrorHandler()
......@@ -274,8 +357,8 @@ cdef class GDALEnv(ConfigEnv):
result = {}
for i in range(OGRGetDriverCount()):
drv = OGRGetDriver(i)
key = OGR_Dr_GetName(drv)
val = OGR_Dr_GetName(drv)
key = <char *>OGR_Dr_GetName(drv)
val = <char *>OGR_Dr_GetName(drv)
result[key] = val
return result
......@@ -66,7 +66,7 @@ class CPLE_BaseError(Exception):
return self.__unicode__()
def __unicode__(self):
return "{}".format(self.errmsg)
return u"{}".format(self.errmsg)
@property
def args(self):
......
......@@ -3,6 +3,81 @@
ctypedef int OGRErr
cdef extern from "ogr_core.h":
ctypedef enum OGRwkbGeometryType:
wkbUnknown
wkbPoint
wkbLineString
wkbPolygon
wkbMultiPoint
wkbMultiLineString
wkbMultiPolygon
wkbGeometryCollection
wkbCircularString
wkbCompoundCurve
wkbCurvePolygon
wkbMultiCurve
wkbMultiSurface
wkbCurve
wkbSurface
wkbPolyhedralSurface
wkbTIN
wkbTriangle
wkbNone
wkbLinearRing
wkbCircularStringZ
wkbCompoundCurveZ
wkbCurvePolygonZ
wkbMultiCurveZ
wkbMultiSurfaceZ
wkbCurveZ
wkbSurfaceZ
wkbPolyhedralSurfaceZ
wkbTINZ
wkbTriangleZ
wkbPointM
wkbLineStringM
wkbPolygonM
wkbMultiPointM
wkbMultiLineStringM
wkbMultiPolygonM
wkbGeometryCollectionM
wkbCircularStringM
wkbCompoundCurveM
wkbCurvePolygonM
wkbMultiCurveM
wkbMultiSurfaceM
wkbCurveM
wkbSurfaceM
wkbPolyhedralSurfaceM
wkbTINM
wkbTriangleM
wkbPointZM
wkbLineStringZM
wkbPolygonZM
wkbMultiPointZM
wkbMultiLineStringZM
wkbMultiPolygonZM
wkbGeometryCollectionZM
wkbCircularStringZM
wkbCompoundCurveZM
wkbCurvePolygonZM
wkbMultiCurveZM
wkbMultiSurfaceZM
wkbCurveZM
wkbSurfaceZM
wkbPolyhedralSurfaceZM
wkbTINZM
wkbTriangleZM
wkbPoint25D
wkbLineString25D
wkbPolygon25D
wkbMultiPoint25D
wkbMultiLineString25D
wkbMultiPolygon25D
wkbGeometryCollection25D
ctypedef struct OGREnvelope:
double MinX
double MaxX
......@@ -15,7 +90,7 @@ cdef extern from "ogr_api.h":
void OGR_G_AddPoint (void *geometry, double x, double y, double z)
void OGR_G_AddPoint_2D (void *geometry, double x, double y)
void OGR_G_CloseRings (void *geometry)
void * OGR_G_CreateGeometry (int wkbtypecode)
void * OGR_G_CreateGeometry (OGRwkbGeometryType wkbtypecode)
void OGR_G_DestroyGeometry (void *geometry)
unsigned char * OGR_G_ExportToJson (void *geometry)
void OGR_G_ExportToWkb (void *geometry, int endianness, char *buffer)
......
......@@ -85,7 +85,7 @@ cdef void * _createOgrGeomFromWKB(object wkb) except NULL:
"""Make an OGR geometry from a WKB string"""
wkbtype = bytearray(wkb)[1]
cdef unsigned char *buffer = wkb
cdef void *cogr_geometry = OGR_G_CreateGeometry(wkbtype)
cdef void *cogr_geometry = OGR_G_CreateGeometry(<OGRwkbGeometryType>wkbtype)
if cogr_geometry is not NULL:
OGR_G_ImportFromWkb(cogr_geometry, buffer, len(wkb))
return cogr_geometry
......@@ -191,7 +191,7 @@ cdef class OGRGeomBuilder:
"""Builds OGR geometries from Fiona geometries.
"""
cdef void * _createOgrGeometry(self, int geom_type) except NULL:
cdef void *cogr_geometry = OGR_G_CreateGeometry(geom_type)
cdef void *cogr_geometry = OGR_G_CreateGeometry(<OGRwkbGeometryType>geom_type)
if cogr_geometry == NULL:
raise Exception("Could not create OGR Geometry of type: %i" % geom_type)
return cogr_geometry
......@@ -286,11 +286,6 @@ cdef class OGRGeomBuilder:
raise ValueError("Unsupported geometry type %s" % typename)
cdef geometry(void *geom):
"""Factory for Fiona geometries"""
return GeomBuilder().build(geom)
def geometryRT(geometry):
# For testing purposes only, leaks the JSON data
cdef void *cogr_geometry = OGRGeomBuilder().build(geometry)
......
......@@ -10,7 +10,7 @@ ctypedef enum OGRFieldSubType:
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(char* path_c, int mode, drivers, options) except NULL
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)
......
......@@ -3,7 +3,7 @@ include "ogrext2.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(char* path_c, int mode, drivers, options) except NULL
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)
......
......@@ -25,7 +25,7 @@ cdef void gdal_flush_cache(void *cogr_ds):
GDALFlushCache(cogr_ds)
cdef void* gdal_open_vector(char* path_c, int mode, drivers, options) except NULL:
cdef void* gdal_open_vector(const char* path_c, int mode, drivers, options) except NULL:
cdef void* cogr_ds = NULL
cdef char **drvs = NULL
cdef char **open_opts = NULL
......
......@@ -3,7 +3,7 @@ include "ogrext2.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(char* path_c, int mode, drivers, options) except NULL
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)
......
......@@ -30,7 +30,7 @@ cdef void gdal_flush_cache(void *cogr_ds):
GDALFlushCache(cogr_ds)
cdef void* gdal_open_vector(char* path_c, int mode, drivers, options) except NULL:
cdef void* gdal_open_vector(const char* path_c, int mode, drivers, options) except NULL:
cdef void* cogr_ds = NULL
cdef char **drvs = NULL
cdef void* drv = NULL
......@@ -64,7 +64,7 @@ cdef void* gdal_open_vector(char* path_c, int mode, drivers, options) except NUL
try:
cogr_ds = exc_wrap_pointer(
GDALOpenEx(path_c, flags, <const char *const *>drvs, open_opts, NULL)
GDALOpenEx(path_c, flags, <const char *const *>drvs, <const char *const *>open_opts, NULL)
)
return cogr_ds
except FionaNullPointerError:
......
......@@ -8,12 +8,12 @@ import warnings
from fiona import compat, vfs
from fiona.ogrext import Iterator, ItemsIterator, KeysIterator
from fiona.ogrext import Session, WritingSession
from fiona.ogrext import (
calc_gdal_version_num, get_gdal_version_num, get_gdal_release_name)
from fiona.ogrext import buffer_to_virtual_file, remove_virtual_file, GEOMETRY_TYPES
from fiona.errors import (DriverError, SchemaError, CRSError, UnsupportedGeometryTypeError, DriverSupportError)
from fiona.logutils import FieldSkipLogFilter
from fiona._env import driver_count
from fiona._env import (
calc_gdal_version_num, get_gdal_version_num, get_gdal_release_name)
from fiona.env import Env
from fiona.errors import FionaDeprecationWarning
from fiona.drvsupport import supported_drivers
......
"""Fiona's GDAL/AWS environment"""
from contextlib import contextmanager
from functools import wraps, total_ordering
import logging
import re
......@@ -9,7 +10,8 @@ import attr
from six import string_types
from fiona._env import (
GDALEnv, get_gdal_config, set_gdal_config)
GDALEnv, calc_gdal_version_num, get_gdal_version_num, get_gdal_config,
set_gdal_config, get_gdal_release_name)
from fiona.compat import getargspec
from fiona.errors import EnvError, GDALVersionError
from fiona.session import Session, DummySession
......@@ -160,15 +162,16 @@ class Env(object):
self.context_options = {}
@classmethod
def from_defaults(cls, *args, **kwargs):
def from_defaults(cls, session=None, **options):
"""Create an environment with default config options
Parameters
----------
args : optional
Positional arguments for Env()
kwargs : optional
Keyword arguments for Env()
session : optional
A Session object.
**options : optional
A mapping of GDAL configuration options, e.g.,
`CPL_DEBUG=True, CHECK_WITH_INVERT_PROJ=False`.
Returns
-------
......@@ -179,9 +182,9 @@ class Env(object):
The items in kwargs will be overlaid on the default values.
"""
options = Env.default_options()
options.update(**kwargs)
return Env(*args, **options)
opts = Env.default_options()
opts.update(**options)
return Env(session=session, **opts)
@property
def is_credentialized(self):
......@@ -313,16 +316,55 @@ def delenv():
local._env = None
class NullContextManager(object):
def __init__(self):
pass
def __enter__(self):
return self
def __exit__(self, *args):
pass
def env_ctx_if_needed():
"""Return an Env if one does not exist
Returns
-------
Env or a do-nothing context manager
"""
if local._env:
return NullContextManager()
else:
return Env.from_defaults()
def ensure_env(f):
"""A decorator that ensures an env exists before a function
calls any GDAL C functions."""
calls any GDAL C functions.
Parameters
----------
f : function
A function.
Returns
-------
A function wrapper.
Notes
-----
If there is already an existing environment, the wrapper does
nothing and immediately calls f with the given arguments.
"""
@wraps(f)
def wrapper(*args, **kwds):
def wrapper(*args, **kwargs):
if local._env:
return f(*args, **kwds)
return f(*args, **kwargs)
else:
with Env.from_defaults():
return f(*args, **kwds)
return f(*args, **kwargs)
return wrapper
......@@ -344,25 +386,23 @@ def ensure_env_with_credentials(f):
credentializes the environment if the first argument is a URI with
scheme "s3".
If there is already an existing environment, the wrapper does
nothing and immediately calls f with the given arguments.
"""
@wraps(f)
def wrapper(*args, **kwds):
def wrapper(*args, **kwargs):
if local._env:
env_ctor = Env
return f(*args, **kwargs)
else:
env_ctor = Env.from_defaults
if hascreds():
session = DummySession()
elif isinstance(args[0], str):
if isinstance(args[0], str):
session = Session.from_path(args[0])
else:
session = Session.from_path(None)
with env_ctor(session=session):
with Env.from_defaults(session=session):
log.debug("Credentialized: {!r}".format(getenv()))
return f(*args, **kwds)
return f(*args, **kwargs)
return wrapper
......@@ -425,7 +465,6 @@ class GDALVersion(object):
@classmethod
def runtime(cls):
"""Return GDALVersion of current GDAL runtime"""
from fiona.ogrext import get_gdal_release_name # to avoid circular import
return cls.parse(get_gdal_release_name())
def at_least(self, other):
......
......@@ -22,7 +22,9 @@ from fiona._geometry cimport (
from fiona._err cimport exc_wrap_int, exc_wrap_pointer, exc_wrap_vsilfile
import fiona
from fiona._err import cpl_errs, FionaNullPointerError, CPLE_BaseError
from fiona.env import env_ctx_if_needed
from fiona._env import GDALVersion, get_gdal_version_num
from fiona._err import cpl_errs, FionaNullPointerError, CPLE_BaseError, CPLE_OpenFailedError
from fiona._geometry import GEOMETRY_TYPES
from fiona import compat
from fiona.errors import (
......@@ -102,54 +104,8 @@ def _bounds(geometry):
return None
def calc_gdal_version_num(maj, min, rev):
"""Calculates the internal gdal version number based on major, minor and revision
GDAL Version Information macro changed with GDAL version 1.10.0 (April 2013)
"""
if (maj, min, rev) >= (1, 10, 0):
return int(maj * 1000000 + min * 10000 + rev * 100)
else:
return int(maj * 1000 + min * 100 + rev * 10)
def get_gdal_version_num():
"""Return current internal version number of gdal"""
return int(GDALVersionInfo("VERSION_NUM"))
def get_gdal_release_name():
"""Return release name of gdal"""
cdef const char *name_c = NULL
name_c = GDALVersionInfo("RELEASE_NAME")
name_b = name_c
return name_b.decode('utf-8')
cdef int GDAL_VERSION_NUM = get_gdal_version_num()
GDALVersion = namedtuple("GDALVersion", ["major", "minor", "revision"])
def get_gdal_version_tuple():
"""
Calculates gdal version tuple from gdal's internal version number.
GDAL Version Information macro changed with GDAL version 1.10.0 (April 2013)
"""
gdal_version_num = get_gdal_version_num()
if gdal_version_num >= calc_gdal_version_num(1, 10, 0):
major = gdal_version_num // 1000000
minor = (gdal_version_num - (major * 1000000)) // 10000
revision = (gdal_version_num - (major * 1000000) - (minor * 10000)) // 100
return GDALVersion(major, minor, revision)
else:
major = gdal_version_num // 1000
minor = (gdal_version_num - (major * 1000)) // 100
revision = (gdal_version_num - (major * 1000) - (minor * 100)) // 10
return GDALVersion(major, minor, revision)
# Feature extension classes and functions follow.
......@@ -520,7 +476,7 @@ cdef class Session:
OGR_L_TestCapability(
self.cogr_layer, OLC_STRINGSASUTF8) and
'utf-8') or (
self.get_driver() == "ESRI Shapefile" and
"Shapefile" in self.get_driver() and
'ISO-8859-1') or locale.getpreferredencoding().upper()
if collection.ignore_fields:
......@@ -531,7 +487,7 @@ cdef class Session:
except AttributeError:
raise TypeError("Ignored field \"{}\" has type \"{}\", expected string".format(name, name.__class__.__name__))
ignore_fields = CSLAddString(ignore_fields, <const char *>name)
OGR_L_SetIgnoredFields(self.cogr_layer, ignore_fields)
OGR_L_SetIgnoredFields(self.cogr_layer, <const char**>ignore_fields)
finally:
CSLDestroy(ignore_fields)
......@@ -650,23 +606,49 @@ cdef class Session:
return ret
def get_crs(self):
"""Get the layer's CRS
Returns
-------
CRS
"""
cdef char *proj_c = NULL
cdef const char *auth_key = NULL
cdef const char *auth_val = NULL
cdef void *cogr_crs = NULL
if self.cogr_layer == NULL:
raise ValueError("Null layer")
cogr_crs = OGR_L_GetSpatialRef(self.cogr_layer)
crs = {}
# We can't simply wrap a method in Python 2.7 so we
# bring the context manager inside like so.
with env_ctx_if_needed():
try:
cogr_crs = exc_wrap_pointer(OGR_L_GetSpatialRef(self.cogr_layer))
# TODO: we don't intend to use try/except for flow control
# this is a work around for a GDAL issue.
except FionaNullPointerError:
log.debug("Layer has no coordinate system")
if cogr_crs is not NULL:
log.debug("Got coordinate system")
crs = {}
try:
retval = OSRAutoIdentifyEPSG(cogr_crs)
if retval > 0:
log.info("Failed to auto identify EPSG: %d", retval)
auth_key = OSRGetAuthorityName(cogr_crs, NULL)
auth_val = OSRGetAuthorityCode(cogr_crs, NULL)
try:
auth_key = <const char *>exc_wrap_pointer(<void *>OSRGetAuthorityName(cogr_crs, NULL))
auth_val = <const char *>exc_wrap_pointer(<void *>OSRGetAuthorityCode(cogr_crs, NULL))
except CPLE_BaseError as exc:
log.debug("{}".format(exc))
if auth_key != NULL and auth_val != NULL:
key_b = auth_key
......@@ -675,6 +657,7 @@ cdef class Session:
val_b = auth_val
val = val_b.decode('utf-8')
crs['init'] = "epsg:" + val
else:
OSRExportToProj4(cogr_crs, &proj_c)
if proj_c == NULL:
......@@ -701,28 +684,57 @@ cdef class Session:
k = k.lstrip("+")
crs[k] = v
finally:
CPLFree(proj_c)
return crs
else:
log.debug("Projection not found (cogr_crs was NULL)")
return crs
return {}
def get_crs_wkt(self):
cdef char *proj_c = NULL
cdef void *cogr_crs = NULL
if self.cogr_layer == NULL:
raise ValueError("Null layer")
# We can't simply wrap a method in Python 2.7 so we
# bring the context manager inside like so.
with env_ctx_if_needed():
try:
cogr_crs = exc_wrap_pointer(OGR_L_GetSpatialRef(self.cogr_layer))
# TODO: we don't intend to use try/except for flow control
# this is a work around for a GDAL issue.
except FionaNullPointerError:
log.debug("Layer has no coordinate system")
except fiona._err.CPLE_OpenFailedError as exc:
log.debug("A support file wasn't opened. See the preceding ERROR level message.")
cogr_crs = OGR_L_GetSpatialRef(self.cogr_layer)
crs_wkt = ""
log.debug("Called OGR_L_GetSpatialRef() again without error checking.")
if cogr_crs == NULL:
raise exc
if cogr_crs is not NULL:
log.debug("Got coordinate system")
try:
OSRExportToWkt(cogr_crs, &proj_c)
if proj_c == NULL:
raise ValueError("Null projection")
proj_b = proj_c
crs_wkt = proj_b.decode('utf-8')
finally:
CPLFree(proj_c)
return crs_wkt
else:
log.debug("Projection not found (cogr_crs was NULL)")
return crs_wkt
return ""
def get_extent(self):
cdef OGREnvelope extent
......@@ -822,39 +834,41 @@ cdef class WritingSession(Session):
cdef const char *proj_c = NULL
cdef const char *fileencoding_c = NULL
cdef OGRFieldSubType field_subtype
cdef int ret
path = collection.path
self.collection = collection
userencoding = kwargs.get('encoding')
if collection.mode == 'a':
if os.path.exists(path):
if not os.path.exists(path):
raise OSError("No such file or directory %s" % path)
try:
path_b = path.encode('utf-8')
except UnicodeDecodeError:
path_b = path
path_c = path_b
self.cogr_ds = gdal_open_vector(path_c, 1, None, kwargs)
cogr_driver = GDALGetDatasetDriver(self.cogr_ds)
if cogr_driver == NULL:
raise ValueError("Null driver")
try:
self.cogr_ds = gdal_open_vector(path_c, 1, None, kwargs)
if isinstance(collection.name, string_types):
name_b = collection.name.encode()
name_c = name_b
self.cogr_layer = GDALDatasetGetLayerByName(
self.cogr_ds, name_c)
self.cogr_layer = exc_wrap_pointer(GDALDatasetGetLayerByName(self.cogr_ds, name_c))
elif isinstance(collection.name, int):
self.cogr_layer = GDALDatasetGetLayer(
self.cogr_ds, collection.name)
self.cogr_layer = exc_wrap_pointer(GDALDatasetGetLayer(self.cogr_ds, collection.name))
if self.cogr_layer == NULL:
raise RuntimeError(
"Failed to get layer %s" % collection.name)
else:
raise OSError("No such file or directory %s" % path)
except CPLE_BaseError as exc:
OGRReleaseDataSource(self.cogr_ds)
self.cogr_ds = NULL
self.cogr_layer = NULL
raise DriverError(u"{}".format(exc))
else:
self._fileencoding = (userencoding or (
OGR_L_TestCapability(self.cogr_layer, OLC_STRINGSASUTF8) and
'utf-8') or (
......@@ -862,6 +876,7 @@ cdef class WritingSession(Session):
'ISO-8859-1') or locale.getpreferredencoding()).upper()
elif collection.mode == 'w':
try:
path_b = path.encode('utf-8')
except UnicodeDecodeError:
......@@ -870,11 +885,7 @@ cdef class WritingSession(Session):
driver_b = collection.driver.encode()
driver_c = driver_b
cogr_driver = GDALGetDriverByName(driver_c)
if cogr_driver == NULL:
raise ValueError("Null driver")
cogr_driver = exc_wrap_pointer(GDALGetDriverByName(driver_c))
# Our most common use case is the creation of a new data
# file and historically we've assumed that it's a file on
......@@ -883,7 +894,7 @@ cdef class WritingSession(Session):
# TODO: remove the assumption.
if not os.path.exists(path):
log.debug("File doesn't exist. Creating a new one...")
cogr_ds = gdal_create(cogr_driver, path_c, kwargs)
cogr_ds = gdal_create(cogr_driver, path_c, {})
# TODO: revisit the logic in the following blocks when we
# change the assumption above.
......@@ -915,16 +926,19 @@ cdef class WritingSession(Session):
# collection constructor. We by-pass the crs_wkt and crs
# properties because they aren't accessible until the layer
# is constructed (later).
try:
col_crs = collection._crs_wkt or collection._crs
if col_crs:
cogr_srs = OSRNewSpatialReference(NULL)
if cogr_srs == NULL:
raise ValueError("NULL spatial reference")
cogr_srs = exc_wrap_pointer(OSRNewSpatialReference(NULL))
# First, check for CRS strings like "EPSG:3857".
if isinstance(col_crs, string_types):
proj_b = col_crs.encode('utf-8')
proj_c = proj_b
OSRSetFromUserInput(cogr_srs, proj_c)
elif isinstance(col_crs, compat.DICT_TYPES):
# EPSG is a special case.
init = col_crs.get('init')
......@@ -947,27 +961,33 @@ cdef class WritingSession(Session):
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)
except (ValueError, CPLE_BaseError) as exc:
OGRReleaseDataSource(self.cogr_ds)
self.cogr_ds = NULL
self.cogr_layer = NULL
raise CRSError(u"{}".format(exc))
# Figure out what encoding to use. The encoding parameter given
# to the collection constructor takes highest precedence, then
# 'iso-8859-1', then the system's default encoding as last resort.
sysencoding = locale.getpreferredencoding()
self._fileencoding = (userencoding or (
collection.driver == "ESRI Shapefile" and
"Shapefile" in collection.driver and
'ISO-8859-1') or sysencoding).upper()
# The ENCODING option makes no sense for some drivers and
# will result in a warning. Fixing is a TODO.
if "Shapefile" in collection.driver:
fileencoding = self.get_fileencoding()
if fileencoding:
fileencoding_b = fileencoding.encode('utf-8')
fileencoding_c = fileencoding_b
with cpl_errs:
options = CSLSetNameValue(options, "ENCODING", fileencoding_c)
# Does the layer exist already? If so, we delete it.
......@@ -995,7 +1015,14 @@ cdef class WritingSession(Session):
name_c = name_b
for k, v in kwargs.items():
# We need to remove encoding from the layer creation
# options if we're not creating a shapefile.
if k == 'encoding' and "Shapefile" not in collection.driver:
continue
k = k.upper().encode('utf-8')
if isinstance(v, bool):
v = ('ON' if v else 'OFF').encode('utf-8')
else:
......@@ -1018,9 +1045,13 @@ cdef class WritingSession(Session):
self.cogr_layer = exc_wrap_pointer(
GDALDatasetCreateLayer(
self.cogr_ds, name_c, cogr_srs,
geometry_code, options))
<OGRwkbGeometryType>geometry_code, options))
except Exception as exc:
raise DriverIOError(str(exc))
OGRReleaseDataSource(self.cogr_ds)
self.cogr_ds = NULL
raise DriverIOError(u"{}".format(exc))
finally:
if options != NULL:
CSLDestroy(options)
......@@ -1032,9 +1063,6 @@ cdef class WritingSession(Session):
if cogr_srs != NULL:
OSRRelease(cogr_srs)
if self.cogr_layer == NULL:
raise ValueError("Null layer")
log.debug("Created layer %s", collection.name)
# Next, make a layer definition from the given schema properties,
......@@ -1076,13 +1104,9 @@ cdef class WritingSession(Session):
field_type = FIELD_TYPES.index(value)
encoding = self.get_internalencoding()
try:
key_bytes = key.encode(encoding)
cogr_fielddefn = exc_wrap_pointer(OGR_Fld_Create(key_bytes, field_type))
if cogr_fielddefn == NULL:
raise ValueError("Field {} definition is NULL".format(key))
cogr_fielddefn = exc_wrap_pointer(OGR_Fld_Create(key_bytes, <OGRFieldType>field_type))
if width:
OGR_Fld_SetWidth(cogr_fielddefn, width)
if precision:
......@@ -1090,14 +1114,15 @@ cdef class WritingSession(Session):
if field_subtype != OFSTNone:
# subtypes are new in GDAL 2.x, ignored in 1.x
set_field_subtype(cogr_fielddefn, field_subtype)
try:
exc_wrap_int(OGR_L_CreateField(self.cogr_layer, cogr_fielddefn, 1))
except CPLE_BaseError as exc:
raise SchemaError(str(exc))
except (UnicodeEncodeError, CPLE_BaseError) as exc:
OGRReleaseDataSource(self.cogr_ds)
self.cogr_ds = NULL
self.cogr_layer = NULL
raise SchemaError(u"{}".format(exc))
else:
OGR_Fld_Destroy(cogr_fielddefn)
log.debug("End creating field %r", key)
# Mapping of the Python collection schema to the munged
......
......@@ -52,6 +52,94 @@ ctypedef struct OGREnvelope:
double MaxY
cdef extern from "ogr_core.h":
ctypedef enum OGRwkbGeometryType:
wkbUnknown
wkbPoint
wkbLineString
wkbPolygon
wkbMultiPoint
wkbMultiLineString
wkbMultiPolygon
wkbGeometryCollection
wkbCircularString
wkbCompoundCurve
wkbCurvePolygon
wkbMultiCurve
wkbMultiSurface
wkbCurve
wkbSurface
wkbPolyhedralSurface
wkbTIN
wkbTriangle
wkbNone
wkbLinearRing
wkbCircularStringZ
wkbCompoundCurveZ
wkbCurvePolygonZ
wkbMultiCurveZ
wkbMultiSurfaceZ
wkbCurveZ
wkbSurfaceZ
wkbPolyhedralSurfaceZ
wkbTINZ
wkbTriangleZ
wkbPointM
wkbLineStringM
wkbPolygonM
wkbMultiPointM
wkbMultiLineStringM
wkbMultiPolygonM
wkbGeometryCollectionM
wkbCircularStringM
wkbCompoundCurveM
wkbCurvePolygonM
wkbMultiCurveM
wkbMultiSurfaceM
wkbCurveM
wkbSurfaceM
wkbPolyhedralSurfaceM
wkbTINM
wkbTriangleM
wkbPointZM
wkbLineStringZM
wkbPolygonZM
wkbMultiPointZM
wkbMultiLineStringZM
wkbMultiPolygonZM
wkbGeometryCollectionZM
wkbCircularStringZM
wkbCompoundCurveZM
wkbCurvePolygonZM
wkbMultiCurveZM
wkbMultiSurfaceZM
wkbCurveZM
wkbSurfaceZM
wkbPolyhedralSurfaceZM
wkbTINZM
wkbTriangleZM
wkbPoint25D
wkbLineString25D
wkbPolygon25D
wkbMultiPoint25D
wkbMultiLineString25D
wkbMultiPolygon25D
wkbGeometryCollection25D
ctypedef enum OGRFieldType:
OFTInteger
OFTIntegerList
OFTReal
OFTRealList
OFTString
OFTStringList
OFTWideString
OFTWideStringList
OFTBinary
OFTDate
OFTTime
OFTDateTime
OFTMaxType
char * OGRGeometryTypeToName(int)
char * ODsCCreateLayer = "CreateLayer"
......@@ -79,7 +167,7 @@ cdef extern from "ogr_srs_api.h":
int OCTTransform (void *ct, int nCount, double *x, double *y, double *z)
cdef extern from "ogr_api.h":
char * OGR_Dr_GetName (void *driver)
const char * OGR_Dr_GetName (void *driver)
void * OGR_Dr_CreateDataSource (void *driver, const char *path, char **options)
int OGR_Dr_DeleteDataSource (void *driver, char *)
void * OGR_Dr_Open (void *driver, const char *path, int bupdate)
......@@ -119,7 +207,7 @@ cdef extern from "ogr_api.h":
void * OGR_FD_GetFieldDefn (void *featuredefn, int n)
int OGR_FD_GetGeomType (void *featuredefn)
char * OGR_FD_GetName (void *featuredefn)
void * OGR_Fld_Create (char *name, int fieldtype)
void * OGR_Fld_Create (char *name, OGRFieldType fieldtype)
void OGR_Fld_Destroy (void *fielddefn)
char * OGR_Fld_GetNameRef (void *fielddefn)
int OGR_Fld_GetPrecision (void *fielddefn)
......@@ -148,7 +236,7 @@ cdef extern from "ogr_api.h":
void OGR_G_ImportFromWkb (void *geometry, unsigned char *bytes, int nbytes)
int OGR_G_WkbSize (void *geometry)
OGRErr OGR_L_CreateFeature (void *layer, void *feature)
int OGR_L_CreateField (void *layer, void *fielddefn, int flexible)
OGRErr OGR_L_CreateField (void *layer, void *fielddefn, int flexible)
OGRErr OGR_L_GetExtent (void *layer, void *extent, int force)
void * OGR_L_GetFeature (void *layer, int n)
int OGR_L_GetFeatureCount (void *layer, int m)
......
......@@ -9,13 +9,102 @@ cdef extern from "ogr_core.h":
ctypedef int OGRErr
ctypedef enum OGRwkbGeometryType:
wkbUnknown
wkbPoint
wkbLineString
wkbPolygon
wkbMultiPoint
wkbMultiLineString
wkbMultiPolygon
wkbGeometryCollection
wkbCircularString
wkbCompoundCurve
wkbCurvePolygon
wkbMultiCurve
wkbMultiSurface
wkbCurve
wkbSurface
wkbPolyhedralSurface
wkbTIN
wkbTriangle
wkbNone
wkbLinearRing
wkbCircularStringZ
wkbCompoundCurveZ
wkbCurvePolygonZ
wkbMultiCurveZ
wkbMultiSurfaceZ
wkbCurveZ
wkbSurfaceZ
wkbPolyhedralSurfaceZ
wkbTINZ
wkbTriangleZ
wkbPointM
wkbLineStringM
wkbPolygonM
wkbMultiPointM
wkbMultiLineStringM
wkbMultiPolygonM
wkbGeometryCollectionM
wkbCircularStringM
wkbCompoundCurveM
wkbCurvePolygonM
wkbMultiCurveM
wkbMultiSurfaceM
wkbCurveM
wkbSurfaceM
wkbPolyhedralSurfaceM
wkbTINM
wkbTriangleM
wkbPointZM
wkbLineStringZM
wkbPolygonZM
wkbMultiPointZM
wkbMultiLineStringZM
wkbMultiPolygonZM
wkbGeometryCollectionZM
wkbCircularStringZM
wkbCompoundCurveZM
wkbCurvePolygonZM
wkbMultiCurveZM
wkbMultiSurfaceZM
wkbCurveZM
wkbSurfaceZM
wkbPolyhedralSurfaceZM
wkbTINZM
wkbTriangleZM
wkbPoint25D
wkbLineString25D
wkbPolygon25D
wkbMultiPoint25D
wkbMultiLineString25D
wkbMultiPolygon25D
wkbGeometryCollection25D
ctypedef enum OGRFieldType:
OFTInteger
OFTIntegerList
OFTReal
OFTRealList
OFTString
OFTStringList
OFTWideString
OFTWideStringList
OFTBinary
OFTDate
OFTTime
OFTDateTime
OFTInteger64
OFTInteger64List
OFTMaxType
ctypedef int OGRFieldSubType
ctypedef enum OGRFieldSubType:
OFSTNone = 0
OFSTBoolean = 1
OFSTInt16 = 2
OFSTFloat32 = 3
OFSTMaxSubType = 3
cdef int OFSTNone = 0
cdef int OFSTBoolean = 1
cdef int OFSTInt16 = 2
cdef int OFSTFloat32 = 3
cdef int OFSTMaxSubType = 3
ctypedef struct OGREnvelope:
double MinX
......@@ -35,9 +124,9 @@ cdef extern from "gdal.h":
void * GDALGetDriverByName(const char * pszName)
void * GDALOpenEx(const char * pszFilename,
unsigned int nOpenFlags,
const char ** papszAllowedDrivers,
const char ** papszOpenOptions,
const char *const *papszSibling1Files
const char *const *papszAllowedDrivers,
const char *const *papszOpenOptions,
const char *const *papszSiblingFiles
)
int GDAL_OF_UPDATE
int GDAL_OF_READONLY
......@@ -147,7 +236,7 @@ cdef extern from "ogr_srs_api.h":
cdef extern from "ogr_api.h":
char * OGR_Dr_GetName (void *driver)
const char * OGR_Dr_GetName (void *driver)
void * OGR_Dr_CreateDataSource (void *driver, const char *path, char **options)
int OGR_Dr_DeleteDataSource (void *driver, char *)
void * OGR_Dr_Open (void *driver, const char *path, int bupdate)
......@@ -178,7 +267,7 @@ cdef extern from "ogr_api.h":
void * OGR_FD_GetFieldDefn (void *featuredefn, int n)
int OGR_FD_GetGeomType (void *featuredefn)
char * OGR_FD_GetName (void *featuredefn)
void * OGR_Fld_Create (char *name, int fieldtype)
void * OGR_Fld_Create (char *name, OGRFieldType fieldtype)
void OGR_Fld_Destroy (void *fielddefn)
char * OGR_Fld_GetNameRef (void *fielddefn)
int OGR_Fld_GetPrecision (void *fielddefn)
......@@ -209,7 +298,7 @@ cdef extern from "ogr_api.h":
void OGR_G_ImportFromWkb (void *geometry, unsigned char *bytes, int nbytes)
int OGR_G_WkbSize (void *geometry)
OGRErr OGR_L_CreateFeature (void *layer, void *feature)
int OGR_L_CreateField (void *layer, void *fielddefn, int flexible)
OGRErr OGR_L_CreateField (void *layer, void *fielddefn, int flexible)
OGRErr OGR_L_GetExtent (void *layer, void *extent, int force)
void * OGR_L_GetFeature (void *layer, int n)
int OGR_L_GetFeatureCount (void *layer, int m)
......