This is an automated email from the git hooks/post-receive script. johanvdw-guest pushed a commit to branch master in repository fiona.
commit 70a9a0d22f1abb630812e4dbf5a499dbdad69291 Author: Johan Van de Wauw <[email protected]> Date: Fri May 19 22:46:35 2017 +0200 Imported Upstream version 1.7.6 --- .travis.yml | 2 + CHANGES.txt | 14 ++++ CREDITS.txt | 1 + fiona/__init__.py | 8 +-- fiona/_crs.pxd | 30 ++++---- fiona/_drivers.pyx | 27 +++++-- fiona/_err.pxd | 1 + fiona/_err.pyx | 191 ++++++++++++++++++++++++++++++++++++++++++++++---- fiona/_transform.pyx | 31 ++++---- fiona/collection.py | 42 ++++++----- fiona/compat.py | 2 +- fiona/errors.py | 5 ++ fiona/ogrext1.pxd | 30 ++++---- fiona/ogrext1.pyx | 114 +++++++++++++++++------------- fiona/ogrext2.pxd | 48 +++++++------ fiona/ogrext2.pyx | 184 +++++++++++++++++++++++++----------------------- setup.py | 7 +- tests/test_drivers.py | 2 +- tests/test_geojson.py | 26 ++++++- 19 files changed, 519 insertions(+), 246 deletions(-) diff --git a/.travis.yml b/.travis.yml index 724cf8a..774f1ad 100644 --- a/.travis.yml +++ b/.travis.yml @@ -35,6 +35,8 @@ before_install: - . ./scripts/travis_gdal_install.sh - export PATH=$GDALINST/gdal-$GDALVERSION/bin:$PATH - export LD_LIBRARY_PATH=$GDALINST/gdal-$GDALVERSION/lib:$LD_LIBRARY_PATH + - export GDAL_DATA=$GDALINST/gdal-$GDALVERSION/share/gdal + - export PROJ_LIB=/usr/share/proj - gdal-config --version install: - "if [ $(gdal-config --version) == \"$GDALVERSION\" ]; then echo \"Using gdal $GDALVERSION\"; else echo \"NOT using gdal $GDALVERSION as expected; aborting\"; exit 1; fi" diff --git a/CHANGES.txt b/CHANGES.txt index ea759be..d5f451a 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -3,6 +3,20 @@ Changes All issue numbers are relative to https://github.com/Toblerity/Fiona/issues. +1.7.6 (2017-04-26) +------------------ + +Bug fixes: + +- Fall back to `share/proj` for PROJ_LIB (#440). +- Replace every call to `OSRDestroySpatialReference()` with `OSRRelease()`, + fixing the GPKG driver crasher reported in #441 (#443). +- Add a `DriverIOError` derived from `IOError` to use for driver-specific + errors such as the GeoJSON driver's refusal to overwrite existing files. + Also we now ensure that when this error is raised by `fiona.open()` any + created read or write session is deleted, this eliminates spurious + exceptions on teardown of broken `Collection` objects (#437, #444). + 1.7.5 (2017-03-20) ------------------ diff --git a/CREDITS.txt b/CREDITS.txt index de41ed5..4012f96 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -32,6 +32,7 @@ Fiona is written by: - Stefano Costa <[email protected]> - dimlev <[email protected]> - wilsaj <[email protected]> +- Jesse Crocker <[email protected]> Fiona would not be possible without the great work of Frank Warmerdam and other GDAL/OGR developers. diff --git a/fiona/__init__.py b/fiona/__init__.py index 2ea320f..7c11959 100644 --- a/fiona/__init__.py +++ b/fiona/__init__.py @@ -81,14 +81,10 @@ import uuid __all__ = ['bounds', 'listlayers', 'open', 'prop_type', 'prop_width'] -__version__ = "1.7.5" +__version__ = "1.7.6" __gdal_version__ = get_gdal_release_name().decode('utf-8') -log = logging.getLogger('Fiona') -class NullHandler(logging.Handler): - def emit(self, record): - pass -log.addHandler(NullHandler()) +log = logging.getLogger(__name__) def open( diff --git a/fiona/_crs.pxd b/fiona/_crs.pxd index 7329cb0..9646a89 100644 --- a/fiona/_crs.pxd +++ b/fiona/_crs.pxd @@ -1,20 +1,22 @@ # Coordinate system and transform API functions. cdef extern from "ogr_srs_api.h": + + ctypedef void * OGRSpatialReferenceH + void OSRCleanup () - void * OSRClone (void *srs) - void OSRDestroySpatialReference (void *srs) - int OSRExportToProj4 (void *srs, char **params) - int OSRExportToWkt (void *srs, char **params) - int OSRImportFromEPSG (void *srs, int code) - int OSRImportFromProj4 (void *srs, char *proj) - int OSRSetFromUserInput (void *srs, char *input) - int OSRAutoIdentifyEPSG (void *srs) - int OSRFixup(void *srs) - const char * OSRGetAuthorityName (void *srs, const char *key) - const char * OSRGetAuthorityCode (void *srs, const char *key) - void * OSRNewSpatialReference (char *wkt) - void OSRRelease (void *srs) - void * OCTNewCoordinateTransformation (void *source, void *dest) + OGRSpatialReferenceH OSRClone (OGRSpatialReferenceH srs) + int OSRExportToProj4 (OGRSpatialReferenceH srs, char **params) + int OSRExportToWkt (OGRSpatialReferenceH srs, char **params) + int OSRImportFromEPSG (OGRSpatialReferenceH srs, int code) + 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) + void OSRRelease (OGRSpatialReferenceH srs) + void * OCTNewCoordinateTransformation (OGRSpatialReferenceH source, OGRSpatialReferenceH dest) void OCTDestroyCoordinateTransformation (void *source) int OCTTransform (void *ct, int nCount, double *x, double *y, double *z) diff --git a/fiona/_drivers.pyx b/fiona/_drivers.pyx index 7c49231..79d5b73 100644 --- a/fiona/_drivers.pyx +++ b/fiona/_drivers.pyx @@ -106,18 +106,35 @@ cdef class GDALEnv(object): if OGRGetDriverCount() == 0: raise ValueError("Drivers not registered") - if 'GDAL_DATA' not in os.environ: + if 'GDAL_DATA' in os.environ: + log.debug("GDAL_DATA: %s", os.environ['GDAL_DATA']) + else: whl_datadir = os.path.abspath( os.path.join(os.path.dirname(__file__), "gdal_data")) share_datadir = os.path.join(sys.prefix, 'share/gdal') if os.path.exists(os.path.join(whl_datadir, 'pcs.csv')): os.environ['GDAL_DATA'] = whl_datadir + log.debug("Set GDAL_DATA = %r", whl_datadir) elif os.path.exists(os.path.join(share_datadir, 'pcs.csv')): os.environ['GDAL_DATA'] = share_datadir - if 'PROJ_LIB' not in os.environ: + log.debug("Set GDAL_DATA = %r", share_datadir) + else: + log.warn("GDAL data files not located, GDAL_DATA not set") + + if 'PROJ_LIB' in os.environ: + log.debug("PROJ_LIB: %s", os.environ['PROJ_LIB']) + else: whl_datadir = os.path.abspath( os.path.join(os.path.dirname(__file__), "proj_data")) - os.environ['PROJ_LIB'] = whl_datadir + share_datadir = os.path.join(sys.prefix, 'share/proj') + if os.path.exists(whl_datadir): + os.environ['PROJ_LIB'] = whl_datadir + log.debug("Set PROJ_LIB = %r", whl_datadir) + elif os.path.exists(share_datadir): + os.environ['PROJ_LIB'] = share_datadir + log.debug("Set PROJ_LIB = %r", share_datadir) + else: + log.warn("PROJ data files not located, PROJ_LIB not set") for key, val in self.options.items(): key_b = key.upper().encode('utf-8') @@ -141,8 +158,8 @@ cdef class GDALEnv(object): def drivers(self): cdef void *drv = NULL - cdef char *key = NULL - cdef char *val = NULL + cdef const char *key = NULL + cdef const char *val = NULL cdef int i result = {} for i in range(OGRGetDriverCount()): diff --git a/fiona/_err.pxd b/fiona/_err.pxd new file mode 100644 index 0000000..5818e0e --- /dev/null +++ b/fiona/_err.pxd @@ -0,0 +1 @@ +cdef void *exc_wrap_pointer(void *ptr) except NULL diff --git a/fiona/_err.pyx b/fiona/_err.pyx index 4ab0fdc..467e8ed 100644 --- a/fiona/_err.pyx +++ b/fiona/_err.pyx @@ -31,24 +31,140 @@ manager raises a more useful and informative error: # CPL function declarations. cdef extern from "cpl_error.h": + + ctypedef enum CPLErr: + CE_None + CE_Debug + CE_Warning + CE_Failure + CE_Fatal + int CPLGetLastErrorNo() const char* CPLGetLastErrorMsg() int CPLGetLastErrorType() void CPLErrorReset() -# Map GDAL error numbers to Python exceptions. + +from enum import IntEnum + +# Python exceptions expressing the CPL error numbers. + +class CPLE_BaseError(Exception): + """Base CPL error class + Exceptions deriving from this class are intended for use only in + Rasterio's Cython code. Let's not expose API users to them. + """ + + def __init__(self, error, errno, errmsg): + self.error = error + self.errno = errno + self.errmsg = errmsg + + def __str__(self): + return self.__unicode__() + + def __unicode__(self): + return "{}".format(self.errmsg) + + @property + def args(self): + return self.error, self.errno, self.errmsg + + +class CPLE_AppDefinedError(CPLE_BaseError): + pass + + +class CPLE_OutOfMemoryError(CPLE_BaseError): + pass + + +class CPLE_FileIOError(CPLE_BaseError): + pass + + +class CPLE_OpenFailedError(CPLE_BaseError): + pass + + +class CPLE_IllegalArgError(CPLE_BaseError): + pass + + +class CPLE_NotSupportedError(CPLE_BaseError): + pass + + +class CPLE_AssertionFailedError(CPLE_BaseError): + pass + + +class CPLE_NoWriteAccessError(CPLE_BaseError): + pass + + +class CPLE_UserInterruptError(CPLE_BaseError): + pass + + +class ObjectNullError(CPLE_BaseError): + pass + + +class CPLE_HttpResponseError(CPLE_BaseError): + pass + + +class CPLE_AWSBucketNotFoundError(CPLE_BaseError): + pass + + +class CPLE_AWSObjectNotFoundError(CPLE_BaseError): + pass + + +class CPLE_AWSAccessDeniedError(CPLE_BaseError): + pass + + +class CPLE_AWSInvalidCredentialsError(CPLE_BaseError): + pass + + +class CPLE_AWSSignatureDoesNotMatchError(CPLE_BaseError): + pass + + +# Map of GDAL error numbers to the Python exceptions. exception_map = { - 1: RuntimeError, # CPLE_AppDefined - 2: MemoryError, # CPLE_OutOfMemory - 3: IOError, # CPLE_FileIO - 4: IOError, # CPLE_OpenFailed - 5: TypeError, # CPLE_IllegalArg - 6: ValueError, # CPLE_NotSupported - 7: AssertionError, # CPLE_AssertionFailed - 8: IOError, # CPLE_NoWriteAccess - 9: KeyboardInterrupt, # CPLE_UserInterrupt - 10: ValueError # ObjectNull - } + 1: CPLE_AppDefinedError, + 2: CPLE_OutOfMemoryError, + 3: CPLE_FileIOError, + 4: CPLE_OpenFailedError, + 5: CPLE_IllegalArgError, + 6: CPLE_NotSupportedError, + 7: CPLE_AssertionFailedError, + 8: CPLE_NoWriteAccessError, + 9: CPLE_UserInterruptError, + 10: ObjectNullError, + + # error numbers 11-16 are introduced in GDAL 2.1. See + # https://github.com/OSGeo/gdal/pull/98. + 11: CPLE_HttpResponseError, + 12: CPLE_AWSBucketNotFoundError, + 13: CPLE_AWSObjectNotFoundError, + 14: CPLE_AWSAccessDeniedError, + 15: CPLE_AWSInvalidCredentialsError, + 16: CPLE_AWSSignatureDoesNotMatchError} + + +# CPL Error types as an enum. +class GDALError(IntEnum): + none = CE_None + debug = CE_Debug + warning = CE_Warning + failure = CE_Failure + fatal = CE_Fatal cdef class GDALErrCtxManager: @@ -61,10 +177,55 @@ cdef class GDALErrCtxManager: def __exit__(self, exc_type=None, exc_val=None, exc_tb=None): cdef int err_type = CPLGetLastErrorType() cdef int err_no = CPLGetLastErrorNo() - cdef char *msg = CPLGetLastErrorMsg() + cdef const char *msg = CPLGetLastErrorMsg() # TODO: warn for err_type 2? - if err_type >= 3: + if err_type >= 2: raise exception_map[err_no](msg) -cpl_errs = GDALErrCtxManager() +cdef inline object exc_check(): + """Checks GDAL error stack for fatal or non-fatal errors + + Returns + ------- + An Exception, SystemExit, or None + """ + cdef const char *msg_c = NULL + + err_type = CPLGetLastErrorType() + err_no = CPLGetLastErrorNo() + err_msg = CPLGetLastErrorMsg() + + if err_msg == NULL: + msg = "No error message." + else: + # Reformat messages. + msg_b = err_msg + msg = msg_b.decode('utf-8') + msg = msg.replace("`", "'") + msg = msg.replace("\n", " ") + + if err_type == 3: + CPLErrorReset() + return exception_map.get( + err_no, CPLE_BaseError)(err_type, err_no, msg) + + if err_type == 4: + return SystemExit("Fatal error: {0}".format((err_type, err_no, msg))) + + else: + return + + +cdef void *exc_wrap_pointer(void *ptr) except NULL: + """Wrap a GDAL/OGR function that returns GDALDatasetH etc (void *) + Raises a Rasterio exception if a non-fatal error has be set. + """ + if ptr == NULL: + exc = exc_check() + if exc: + raise exc + return NULL + return ptr + +cpl_errs = GDALErrCtxManager() \ No newline at end of file diff --git a/fiona/_transform.pyx b/fiona/_transform.pyx index 6c5460b..bafdba5 100644 --- a/fiona/_transform.pyx +++ b/fiona/_transform.pyx @@ -9,6 +9,8 @@ cimport _crs cimport _csl cimport _geometry +from _crs cimport OGRSpatialReferenceH + cdef extern from "ogr_geometry.h" nogil: @@ -34,7 +36,7 @@ log.addHandler(NullHandler()) cdef void *_crs_from_crs(object crs): cdef char *proj_c = NULL - cdef void *osr = NULL + cdef OGRSpatialReferenceH osr = NULL osr = _crs.OSRNewSpatialReference(NULL) if osr == NULL: raise ValueError("NULL spatial reference") @@ -68,10 +70,12 @@ cdef void *_crs_from_crs(object crs): def _transform(src_crs, dst_crs, xs, ys): - cdef double *x, *y + cdef double *x + cdef double *y cdef char *proj_c = NULL - cdef void *src, *dst - cdef void *transform + cdef OGRSpatialReferenceH src = NULL + cdef OGRSpatialReferenceH dst = NULL + cdef void *transform = NULL cdef int i assert len(xs) == len(ys) @@ -99,8 +103,8 @@ def _transform(src_crs, dst_crs, xs, ys): _cpl.CPLFree(x) _cpl.CPLFree(y) _crs.OCTDestroyCoordinateTransformation(transform) - _crs.OSRDestroySpatialReference(src) - _crs.OSRDestroySpatialReference(dst) + _crs.OSRRelease(src) + _crs.OSRRelease(dst) return res_xs, res_ys @@ -112,11 +116,12 @@ def _transform_geom( cdef char *key_c = NULL cdef char *val_c = NULL cdef char **options = NULL - cdef void *src, *dst - cdef void *transform - cdef OGRGeometryFactory *factory - cdef void *src_ogr_geom - cdef void *dst_ogr_geom + cdef OGRSpatialReferenceH src = NULL + cdef OGRSpatialReferenceH dst = NULL + cdef void *transform = NULL + cdef OGRGeometryFactory *factory = NULL + cdef void *src_ogr_geom = NULL + cdef void *dst_ogr_geom = NULL cdef int i if src_crs and dst_crs: @@ -144,8 +149,8 @@ def _transform_geom( _crs.OCTDestroyCoordinateTransformation(transform) if options != NULL: _csl.CSLDestroy(options) - _crs.OSRDestroySpatialReference(src) - _crs.OSRDestroySpatialReference(dst) + _crs.OSRRelease(src) + _crs.OSRRelease(dst) else: g = geom if precision >= 0: diff --git a/fiona/collection.py b/fiona/collection.py index f19c1a7..ada8d6d 100644 --- a/fiona/collection.py +++ b/fiona/collection.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # Collections provide file-like access to feature data - +import logging import os import warnings @@ -17,6 +17,9 @@ from fiona.drvsupport import supported_drivers from six import string_types, binary_type +log = logging.getLogger(__name__) + + class Collection(object): """A file-like interface to features of a vector dataset @@ -141,25 +144,24 @@ class Collection(object): self.env = GDALEnv() self.env.__enter__() - if self.mode == "r": - self._driver = driver - self.encoding = encoding - self.session = Session() - self.session.start(self) - - # If encoding param is None, we'll use what the session - # suggests. - self.encoding = encoding or self.session.get_fileencoding().lower() - - elif self.mode in ("a", "w"): - self._driver = driver - self.encoding = encoding - self.session = WritingSession() - self.session.start(self, **kwargs) - self.encoding = encoding or self.session.get_fileencoding().lower() + self._driver = driver + self.encoding = encoding + + try: + if self.mode == 'r': + self.session = Session() + self.session.start(self) + elif self.mode in ('a', 'w'): + self.session = WritingSession() + self.session.start(self, **kwargs) + except IOError: + self.session = None + raise - if self.session: + if self.session is not None: self.guard_driver_mode() + if not self.encoding: + self.encoding = self.session.get_fileencoding().lower() def __repr__(self): return "<%s Collection '%s', mode '%s' at %s>" % ( @@ -399,7 +401,9 @@ class Collection(object): if self.session is not None: if self.mode in ('a', 'w'): self.flush() + log.debug("Flushed buffer") self.session.stop() + log.debug("Stopped session") self.session = None self.iterator = None if self.env: @@ -419,7 +423,7 @@ class Collection(object): def __del__(self): # Note: you can't count on this being called. Call close() explicitly # or use the context manager protocol ("with"). - self.__exit__(None, None, None) + self.close() def get_filetype(bytesbuf): diff --git a/fiona/compat.py b/fiona/compat.py index 28d761f..72b2791 100644 --- a/fiona/compat.py +++ b/fiona/compat.py @@ -2,7 +2,7 @@ import collections from six.moves import UserDict try: from collections import OrderedDict -except ImportError: +except ImportError: from ordereddict import OrderedDict diff --git a/fiona/errors.py b/fiona/errors.py index b868cea..489d2ea 100644 --- a/fiona/errors.py +++ b/fiona/errors.py @@ -21,8 +21,13 @@ class DataIOError(IOError): """IO errors involving driver registration or availability.""" +class DriverIOError(IOError): + """A format specific driver error.""" + + class FieldNameEncodeError(UnicodeEncodeError): """Failure to encode a field name.""" + class UnsupportedGeometryTypeError(KeyError): """When a OGR geometry type isn't supported by Fiona.""" diff --git a/fiona/ogrext1.pxd b/fiona/ogrext1.pxd index 752c85e..9a2ca4f 100644 --- a/fiona/ogrext1.pxd +++ b/fiona/ogrext1.pxd @@ -40,21 +40,23 @@ cdef extern from "ogr_core.h": char * OGRGeometryTypeToName(int) cdef extern from "ogr_srs_api.h": + + ctypedef void * OGRSpatialReferenceH + void OSRCleanup () - void * OSRClone (void *srs) - void OSRDestroySpatialReference (void *srs) - int OSRExportToProj4 (void *srs, char **params) - int OSRExportToWkt (void *srs, char **params) - int OSRImportFromEPSG (void *srs, int code) - int OSRImportFromProj4 (void *srs, char *proj) - int OSRSetFromUserInput (void *srs, char *input) - int OSRAutoIdentifyEPSG (void *srs) - int OSRFixup(void *srs) - const char * OSRGetAuthorityName (void *srs, const char *key) - const char * OSRGetAuthorityCode (void *srs, const char *key) - void * OSRNewSpatialReference (char *wkt) - void OSRRelease (void *srs) - void * OCTNewCoordinateTransformation (void *source, void *dest) + OGRSpatialReferenceH OSRClone (OGRSpatialReferenceH srs) + int OSRExportToProj4 (OGRSpatialReferenceH srs, char **params) + int OSRExportToWkt (OGRSpatialReferenceH srs, char **params) + int OSRImportFromEPSG (OGRSpatialReferenceH, int code) + 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) + void OSRRelease (OGRSpatialReferenceH srs) + void * OCTNewCoordinateTransformation (OGRSpatialReferenceH source, OGRSpatialReferenceH dest) void OCTDestroyCoordinateTransformation (void *source) int OCTTransform (void *ct, int nCount, double *x, double *y, double *z) diff --git a/fiona/ogrext1.pyx b/fiona/ogrext1.pyx index 442c3db..57d6c43 100644 --- a/fiona/ogrext1.pyx +++ b/fiona/ogrext1.pyx @@ -16,11 +16,13 @@ cimport ogrext1 from _geometry cimport ( GeomBuilder, OGRGeomBuilder, geometry_type_code, normalize_geometry_type_code) +from fiona._err cimport exc_wrap_pointer + from fiona._err import cpl_errs from fiona._geometry import GEOMETRY_TYPES from fiona import compat from fiona.errors import ( - DriverError, SchemaError, CRSError, FionaValueError) + DriverError, DriverIOError, SchemaError, CRSError, FionaValueError) from fiona.compat import OrderedDict from fiona.rfc3339 import parse_date, parse_datetime, parse_time from fiona.rfc3339 import FionaDateType, FionaDateTimeType, FionaTimeType @@ -66,25 +68,25 @@ FIELD_TYPES_MAP = { } # OGR Driver capability -ODrCCreateDataSource = b"CreateDataSource" -ODrCDeleteDataSource = b"DeleteDataSource" +cdef const char * ODrCCreateDataSource = "CreateDataSource" +cdef const char * ODrCDeleteDataSource = "DeleteDataSource" # OGR Layer capability -OLC_RANDOMREAD = b"RandomRead" -OLC_SEQUENTIALWRITE = b"SequentialWrite" -OLC_RANDOMWRITE = b"RandomWrite" -OLC_FASTSPATIALFILTER = b"FastSpatialFilter" -OLC_FASTFEATURECOUNT = b"FastFeatureCount" -OLC_FASTGETEXTENT = b"FastGetExtent" -OLC_FASTSETNEXTBYINDEX = b"FastSetNextByIndex" -OLC_CREATEFIELD = b"CreateField" -OLC_CREATEGEOMFIELD = b"CreateGeomField" -OLC_DELETEFIELD = b"DeleteField" -OLC_REORDERFIELDS = b"ReorderFields" -OLC_ALTERFIELDDEFN = b"AlterFieldDefn" -OLC_DELETEFEATURE = b"DeleteFeature" -OLC_STRINGSASUTF8 = b"StringsAsUTF8" -OLC_TRANSACTIONS = b"Transactions" +cdef const char * OLC_RANDOMREAD = "RandomRead" +cdef const char * OLC_SEQUENTIALWRITE = "SequentialWrite" +cdef const char * OLC_RANDOMWRITE = "RandomWrite" +cdef const char * OLC_FASTSPATIALFILTER = "FastSpatialFilter" +cdef const char * OLC_FASTFEATURECOUNT = "FastFeatureCount" +cdef const char * OLC_FASTGETEXTENT = "FastGetExtent" +cdef const char * OLC_FASTSETNEXTBYINDEX = "FastSetNextByIndex" +cdef const char * OLC_CREATEFIELD = "CreateField" +cdef const char * OLC_CREATEGEOMFIELD = "CreateGeomField" +cdef const char * OLC_DELETEFIELD = "DeleteField" +cdef const char * OLC_REORDERFIELDS = "ReorderFields" +cdef const char * OLC_ALTERFIELDDEFN = "AlterFieldDefn" +cdef const char * OLC_DELETEFEATURE = "DeleteFeature" +cdef const char * OLC_STRINGSASUTF8 = "StringsAsUTF8" +cdef const char * OLC_TRANSACTIONS = "Transactions" # OGR integer error types. @@ -217,7 +219,7 @@ cdef class FeatureBuilder: props[key] = None cdef void *cogr_geometry = ogrext1.OGR_F_GetGeometryRef(feature) - if cogr_geometry is not NULL: + if cogr_geometry != NULL: geom = GeomBuilder().build(cogr_geometry) else: geom = None @@ -334,7 +336,7 @@ cdef class OGRFeatureBuilder: cdef _deleteOgrFeature(void *cogr_feature): """Delete an OGR feature""" - if cogr_feature is not NULL: + if cogr_feature != NULL: ogrext1.OGR_F_Destroy(cogr_feature) cogr_feature = NULL @@ -360,7 +362,7 @@ def featureRT(feature, collection): # Collection-related extension classes and functions cdef class Session: - + cdef void *cogr_ds cdef void *cogr_layer cdef object _fileencoding @@ -451,7 +453,7 @@ cdef class Session: def stop(self): self.cogr_layer = NULL - if self.cogr_ds is not NULL: + if self.cogr_ds != NULL: ogrext1.OGR_DS_Destroy(self.cogr_ds) self.cogr_ds = NULL @@ -552,7 +554,7 @@ cdef class Session: raise ValueError("Null layer") cogr_crs = ogrext1.OGR_L_GetSpatialRef(self.cogr_layer) crs = {} - if cogr_crs is not NULL: + if cogr_crs != NULL: log.debug("Got coordinate system") retval = ogrext1.OSRAutoIdentifyEPSG(cogr_crs) @@ -606,7 +608,7 @@ cdef class Session: raise ValueError("Null layer") cogr_crs = ogrext1.OGR_L_GetSpatialRef(self.cogr_layer) crs_wkt = "" - if cogr_crs is not NULL: + if cogr_crs != NULL: log.debug("Got coordinate system") ogrext1.OSRExportToWkt(cogr_crs, &proj_c) if proj_c == NULL: @@ -696,8 +698,8 @@ cdef class WritingSession(Session): def start(self, collection): cdef void *cogr_fielddefn cdef void *cogr_driver - cdef void *cogr_ds - cdef void *cogr_layer + cdef void *cogr_ds = NULL + cdef void *cogr_layer = NULL cdef void *cogr_srs = NULL cdef char **options = NULL self.collection = collection @@ -751,29 +753,40 @@ cdef class WritingSession(Session): except UnicodeError: path_b = path path_c = path_b + driver_b = collection.driver.encode() driver_c = driver_b + # TODO: use exc_wrap_pointer() cogr_driver = ogrext1.OGRGetDriverByName(driver_c) if cogr_driver == NULL: raise ValueError("Null driver") + # Our most common use case is the creation of a new data + # file and historically we've assumed that it's a file on + # the local filesystem and queryable via os.path. + # + # TODO: remove the assumption. + # TODO: use exc_wrap_pointer(). if not os.path.exists(path): cogr_ds = ogrext1.OGR_Dr_CreateDataSource( cogr_driver, path_c, NULL) else: - with cpl_errs: - cogr_ds = ogrext1.OGROpen(path_c, 1, NULL) + cogr_ds = ogrext1.OGROpen(path_c, 1, NULL) if cogr_ds == NULL: - cogr_ds = ogrext1.OGR_Dr_CreateDataSource( - cogr_driver, path_c, NULL) + try: + cogr_ds = exc_wrap_pointer( + ogrext1.OGR_Dr_CreateDataSource( + cogr_driver, path_c, NULL)) + except Exception as exc: + raise DriverIOError(str(exc)) elif collection.name is None: ogrext1.OGR_DS_Destroy(cogr_ds) - cogr_ds == NULL + cogr_ds = NULL log.debug("Deleted pre-existing data at %s", path) - + cogr_ds = ogrext1.OGR_Dr_CreateDataSource( cogr_driver, path_c, NULL) @@ -866,21 +879,26 @@ cdef class WritingSession(Session): # Create the named layer in the datasource. name_b = collection.name.encode('utf-8') name_c = name_b - self.cogr_layer = ogrext1.OGR_DS_CreateLayer( - self.cogr_ds, - name_c, - cogr_srs, - geometry_type_code( - collection.schema.get('geometry', 'Unknown')), - options) - - if cogr_srs != NULL: - ogrext1.OSRDestroySpatialReference(cogr_srs) - if options != NULL: - ogrext1.CSLDestroy(options) - - if self.cogr_layer == NULL: - raise ValueError("Null layer") + + try: + self.cogr_layer = exc_wrap_pointer( + ogrext1.OGR_DS_CreateLayer( + self.cogr_ds, name_c, cogr_srs, + geometry_type_code( + collection.schema.get('geometry', 'Unknown')), + options)) + except Exception as exc: + raise DriverError(str(exc)) + finally: + # Shapefile layers make a copy of the passed srs. GPKG + # layers, on the other hand, increment its reference + # count. OSRRelease() is the safe way to release + # OGRSpatialReferenceH. + if cogr_srs != NULL: + ogrext1.OSRRelease(cogr_srs) + if options != NULL: + ogrext1.CSLDestroy(options) + log.debug("Created layer") # Next, make a layer definition from the given schema properties, @@ -1240,7 +1258,7 @@ def _listlayers(path): layer_names.append(name_b.decode('utf-8')) # Close up data source. - if cogr_ds is not NULL: + if cogr_ds != NULL: ogrext1.OGR_DS_Destroy(cogr_ds) cogr_ds = NULL diff --git a/fiona/ogrext2.pxd b/fiona/ogrext2.pxd index 0a3aa29..f3579f9 100644 --- a/fiona/ogrext2.pxd +++ b/fiona/ogrext2.pxd @@ -19,7 +19,6 @@ cdef extern from "gdal.h": void * GDALDatasetGetLayer(void * hDS, int iLayer) void * GDALDatasetGetLayerByName(void * hDS, char * pszName) void GDALClose(void * hDS) - void * GDALGetDatasetDriver(void * hDataset) void * GDALCreate(void * hDriver, const char * pszFilename, int nXSize, @@ -80,36 +79,43 @@ cdef extern from "cpl_vsi.h": int take_ownership) int VSIUnlink (const char * pathname) -ctypedef int OGRErr -ctypedef struct OGREnvelope: - double MinX - double MaxX - double MinY - double MaxY cdef extern from "ogr_core.h": + + ctypedef int OGRErr + + ctypedef struct OGREnvelope: + double MinX + double MaxX + double MinY + double MaxY + char * OGRGeometryTypeToName(int) + cdef extern from "ogr_srs_api.h": + + ctypedef void * OGRSpatialReferenceH + void OSRCleanup () - void * OSRClone (void *srs) - void OSRDestroySpatialReference (void *srs) - int OSRExportToProj4 (void *srs, char **params) - int OSRExportToWkt (void *srs, char **params) - int OSRImportFromEPSG (void *srs, int code) - int OSRImportFromProj4 (void *srs, char *proj) - int OSRSetFromUserInput (void *srs, char *input) - int OSRAutoIdentifyEPSG (void *srs) - int OSRFixup(void *srs) - const char * OSRGetAuthorityName (void *srs, const char *key) - const char * OSRGetAuthorityCode (void *srs, const char *key) - void * OSRNewSpatialReference (char *wkt) - void OSRRelease (void *srs) - void * OCTNewCoordinateTransformation (void *source, void *dest) + OGRSpatialReferenceH OSRClone (OGRSpatialReferenceH srs) + int OSRExportToProj4 (OGRSpatialReferenceH srs, char **params) + int OSRExportToWkt (OGRSpatialReferenceH srs, char **params) + int OSRImportFromEPSG (OGRSpatialReferenceH, int code) + 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) + void OSRRelease (OGRSpatialReferenceH srs) + void * OCTNewCoordinateTransformation (OGRSpatialReferenceH source, OGRSpatialReferenceH dest) void OCTDestroyCoordinateTransformation (void *source) int OCTTransform (void *ct, int nCount, double *x, double *y, double *z) cdef extern from "ogr_api.h": + char * OGR_Dr_GetName (void *driver) void * OGR_Dr_CreateDataSource (void *driver, const char *path, char **options) int OGR_Dr_DeleteDataSource (void *driver, char *) diff --git a/fiona/ogrext2.pyx b/fiona/ogrext2.pyx index 82eaec4..3a0d416 100644 --- a/fiona/ogrext2.pyx +++ b/fiona/ogrext2.pyx @@ -13,13 +13,17 @@ import uuid from six import integer_types, string_types, text_type cimport ogrext2 +from ogrext2 cimport OGREnvelope from _geometry cimport ( GeomBuilder, OGRGeomBuilder, geometry_type_code, normalize_geometry_type_code) +from fiona._err cimport exc_wrap_pointer + from fiona._err import cpl_errs from fiona._geometry import GEOMETRY_TYPES from fiona import compat -from fiona.errors import DriverError, SchemaError, CRSError, FionaValueError +from fiona.errors import ( + DriverError, DriverIOError, SchemaError, CRSError, FionaValueError) from fiona.compat import OrderedDict from fiona.rfc3339 import parse_date, parse_datetime, parse_time from fiona.rfc3339 import FionaDateType, FionaDateTimeType, FionaTimeType @@ -29,11 +33,6 @@ from libc.string cimport strcmp log = logging.getLogger("Fiona") -class NullHandler(logging.Handler): - def emit(self, record): - pass -log.addHandler(NullHandler()) - # Mapping of OGR integer field types to Fiona field type names. # @@ -68,25 +67,25 @@ FIELD_TYPES_MAP = { } # OGR Driver capability -ODrCCreateDataSource = b"CreateDataSource" -ODrCDeleteDataSource = b"DeleteDataSource" +cdef const char * ODrCCreateDataSource = "CreateDataSource" +cdef const char * ODrCDeleteDataSource = "DeleteDataSource" # OGR Layer capability -OLC_RANDOMREAD = b"RandomRead" -OLC_SEQUENTIALWRITE = b"SequentialWrite" -OLC_RANDOMWRITE = b"RandomWrite" -OLC_FASTSPATIALFILTER = b"FastSpatialFilter" -OLC_FASTFEATURECOUNT = b"FastFeatureCount" -OLC_FASTGETEXTENT = b"FastGetExtent" -OLC_FASTSETNEXTBYINDEX = b"FastSetNextByIndex" -OLC_CREATEFIELD = b"CreateField" -OLC_CREATEGEOMFIELD = b"CreateGeomField" -OLC_DELETEFIELD = b"DeleteField" -OLC_REORDERFIELDS = b"ReorderFields" -OLC_ALTERFIELDDEFN = b"AlterFieldDefn" -OLC_DELETEFEATURE = b"DeleteFeature" -OLC_STRINGSASUTF8 = b"StringsAsUTF8" -OLC_TRANSACTIONS = b"Transactions" +cdef const char * OLC_RANDOMREAD = "RandomRead" +cdef const char * OLC_SEQUENTIALWRITE = "SequentialWrite" +cdef const char * OLC_RANDOMWRITE = "RandomWrite" +cdef const char * OLC_FASTSPATIALFILTER = "FastSpatialFilter" +cdef const char * OLC_FASTFEATURECOUNT = "FastFeatureCount" +cdef const char * OLC_FASTGETEXTENT = "FastGetExtent" +cdef const char * OLC_FASTSETNEXTBYINDEX = "FastSetNextByIndex" +cdef const char * OLC_CREATEFIELD = "CreateField" +cdef const char * OLC_CREATEGEOMFIELD = "CreateGeomField" +cdef const char * OLC_DELETEFIELD = "DeleteField" +cdef const char * OLC_REORDERFIELDS = "ReorderFields" +cdef const char * OLC_ALTERFIELDDEFN = "AlterFieldDefn" +cdef const char * OLC_DELETEFEATURE = "DeleteFeature" +cdef const char * OLC_STRINGSASUTF8 = "StringsAsUTF8" +cdef const char * OLC_TRANSACTIONS = "Transactions" # OGR integer error types. @@ -156,7 +155,7 @@ cdef class FeatureBuilder: cdef int ss = 0 cdef int tz = 0 cdef int retval - cdef char *key_c + cdef const char *key_c = NULL props = OrderedDict() for i in range(ogrext2.OGR_F_GetFieldCount(feature)): fdefn = ogrext2.OGR_F_GetFieldDefnRef(feature, i) @@ -239,7 +238,7 @@ cdef class OGRFeatureBuilder: cdef void * build(self, feature, collection) except NULL: cdef void *cogr_geometry = NULL - cdef char *string_c + cdef const char *string_c = NULL cdef WritingSession session session = collection.session cdef void *cogr_layer = session.cogr_layer @@ -417,7 +416,7 @@ cdef class Session: flags = ogrext2.GDAL_OF_VECTOR | ogrext2.GDAL_OF_READONLY try: self.cogr_ds = ogrext2.GDALOpenEx( - path_c, flags, drvs, NULL, NULL) + path_c, flags, <const char *const *>drvs, NULL, NULL) finally: ogrext2.CSLDestroy(drvs) @@ -458,7 +457,7 @@ cdef class Session: def stop(self): self.cogr_layer = NULL - if self.cogr_ds is not NULL: + if self.cogr_ds != NULL: ogrext2.GDALClose(self.cogr_ds) self.cogr_ds = NULL @@ -483,7 +482,7 @@ cdef class Session: cdef void *cogr_driver = ogrext2.GDALGetDatasetDriver(self.cogr_ds) if cogr_driver == NULL: raise ValueError("Null driver") - cdef char *name = ogrext2.OGR_Dr_GetName(cogr_driver) + cdef const char *name = ogrext2.OGR_Dr_GetName(cogr_driver) driver_name = name return driver_name.decode() @@ -492,7 +491,7 @@ cdef class Session: cdef int n cdef void *cogr_featuredefn cdef void *cogr_fielddefn - cdef char *key_c + cdef const char *key_c props = [] if self.cogr_layer == NULL: @@ -552,8 +551,8 @@ cdef class Session: def get_crs(self): cdef char *proj_c = NULL - cdef char *auth_key = NULL - cdef char *auth_val = 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") @@ -626,9 +625,11 @@ cdef class Session: return crs_wkt def get_extent(self): + cdef OGREnvelope extent + if self.cogr_layer == NULL: raise ValueError("Null layer") - cdef ogrext2.OGREnvelope extent + result = ogrext2.OGR_L_GetExtent(self.cogr_layer, &extent, 1) return (extent.MinX, extent.MinY, extent.MaxX, extent.MaxY) @@ -701,18 +702,18 @@ cdef class WritingSession(Session): cdef object _schema_mapping def start(self, collection): - cdef void *cogr_fielddefn - cdef void *cogr_driver - cdef void *cogr_ds - cdef void *cogr_layer + cdef void *cogr_fielddefn = NULL + cdef void *cogr_driver = NULL + cdef void *cogr_ds = NULL + cdef void *cogr_layer = NULL cdef void *cogr_srs = NULL cdef char **options = NULL self.collection = collection - cdef char *path_c - cdef char *driver_c - cdef char *name_c - cdef char *proj_c - cdef char *fileencoding_c + cdef const char *path_c = NULL + cdef const char *driver_c = NULL + cdef const char *name_c = NULL + cdef const char *proj_c = NULL + cdef const char *fileencoding_c = NULL path = collection.path if collection.mode == 'a': @@ -722,15 +723,10 @@ cdef class WritingSession(Session): except UnicodeDecodeError: path_b = path path_c = path_b - with cpl_errs: - self.cogr_ds = ogrext2.GDALOpenEx(path_c, - ogrext2.GDAL_OF_VECTOR | ogrext2.GDAL_OF_UPDATE, - NULL, - NULL, - NULL) -# self.cogr_ds = ogrext2.OGROpen(path_c, 1, NULL) - if self.cogr_ds == NULL: - raise RuntimeError("Failed to open %s" % path) + self.cogr_ds = ogrext2.GDALOpenEx(path_c, + ogrext2.GDAL_OF_VECTOR | ogrext2.GDAL_OF_UPDATE, + NULL, NULL, NULL) + cogr_driver = ogrext2.GDALGetDatasetDriver(self.cogr_ds) if cogr_driver == NULL: raise ValueError("Null driver") @@ -763,6 +759,7 @@ cdef class WritingSession(Session): except UnicodeDecodeError: path_b = path path_c = path_b + driver_b = collection.driver.encode() driver_c = driver_b @@ -770,27 +767,32 @@ cdef class WritingSession(Session): if cogr_driver == NULL: raise ValueError("Null driver") + # Our most common use case is the creation of a new data + # file and historically we've assumed that it's a file on + # the local filesystem and queryable via os.path. + # + # TODO: remove the assumption. if not os.path.exists(path): -# cogr_ds = ogrext2.OGR_Dr_CreateDataSource( -# cogr_driver, path_c, NULL) - cogr_ds = ogrext2.GDALCreate( + cogr_ds = exc_wrap_pointer(ogrext2.GDALCreate( cogr_driver, path_c, 0, 0, 0, ogrext2.GDT_Unknown, - NULL) - pass + NULL)) + # TODO: revisit the logic in the following blocks when we + # change the assumption above. + # TODO: use exc_wrap_pointer() else: - with cpl_errs: - cogr_ds = ogrext2.GDALOpenEx(path_c, - ogrext2.GDAL_OF_VECTOR | ogrext2.GDAL_OF_UPDATE, - NULL, - NULL, - NULL) -# cogr_ds = ogrext2.OGROpen(path_c, 1, NULL) + cogr_ds = ogrext2.GDALOpenEx(path_c, + ogrext2.GDAL_OF_VECTOR | ogrext2.GDAL_OF_UPDATE, + NULL, + NULL, + NULL) + + # TODO: use exc_wrap_pointer() if cogr_ds == NULL: cogr_ds = ogrext2.GDALCreate( cogr_driver, @@ -800,12 +802,10 @@ cdef class WritingSession(Session): 0, ogrext2.GDT_Unknown, NULL) -# cogr_ds = ogrext2.OGR_Dr_CreateDataSource( -# cogr_driver, path_c, NULL) elif collection.name is None: ogrext2.GDALClose(cogr_ds) - cogr_ds == NULL + cogr_ds = NULL log.debug("Deleted pre-existing data at %s", path) cogr_ds = ogrext2.GDALCreate( cogr_driver, @@ -815,8 +815,6 @@ cdef class WritingSession(Session): 0, ogrext2.GDT_Unknown, NULL) -# cogr_ds = ogrext2.OGR_Dr_CreateDataSource( -# cogr_driver, path_c, NULL) else: pass @@ -877,11 +875,14 @@ cdef class WritingSession(Session): collection.driver == "ESRI Shapefile" 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. fileencoding = self.get_fileencoding() if fileencoding: fileencoding_b = fileencoding.encode() fileencoding_c = fileencoding_b - options = ogrext2.CSLSetNameValue(options, "ENCODING", fileencoding_c) + with cpl_errs: + options = ogrext2.CSLSetNameValue(options, "ENCODING", fileencoding_c) # Does the layer exist already? If so, we delete it. layer_count = ogrext2.GDALDatasetGetLayerCount(self.cogr_ds) @@ -906,22 +907,31 @@ cdef class WritingSession(Session): # Create the named layer in the datasource. name_b = collection.name.encode('utf-8') name_c = name_b - self.cogr_layer = ogrext2.GDALDatasetCreateLayer( - self.cogr_ds, - name_c, - cogr_srs, - geometry_type_code( - collection.schema.get('geometry', 'Unknown')), - options) - - if cogr_srs != NULL: - ogrext2.OSRDestroySpatialReference(cogr_srs) - if options != NULL: - ogrext2.CSLDestroy(options) + + try: + self.cogr_layer = exc_wrap_pointer( + ogrext2.GDALDatasetCreateLayer( + self.cogr_ds, name_c, cogr_srs, + geometry_type_code( + collection.schema.get('geometry', 'Unknown')), + options)) + except Exception as exc: + raise DriverIOError(str(exc)) + finally: + if options != NULL: + ogrext2.CSLDestroy(options) + + # Shapefile layers make a copy of the passed srs. GPKG + # layers, on the other hand, increment its reference + # count. OSRRelease() is the safe way to release + # OGRSpatialReferenceH. + if cogr_srs != NULL: + ogrext2.OSRRelease(cogr_srs) if self.cogr_layer == NULL: raise ValueError("Null layer") - log.debug("Created layer") + + log.debug("Created layer %s", collection.name) # Next, make a layer definition from the given schema properties, # which are an ordered dict since Fiona 1.0.1. @@ -1028,10 +1038,12 @@ cdef class WritingSession(Session): cdef void *cogr_layer = self.cogr_layer if cogr_ds == NULL: raise ValueError("Null data source") - log.debug("Syncing OGR to disk") - ogrext2.GDALFlushCache(cogr_ds) + with cpl_errs: + ogrext2.GDALFlushCache(cogr_ds) + + log.debug("Flushed data source cache") cdef class Iterator: @@ -1261,8 +1273,8 @@ def _listlayers(path): cdef void *cogr_ds cdef void *cogr_layer - cdef char *path_c - cdef char *name_c + cdef const char *path_c + cdef const char *name_c # Open OGR data source. try: @@ -1290,7 +1302,7 @@ def _listlayers(path): layer_names.append(name_b.decode('utf-8')) # Close up data source. - if cogr_ds is not NULL: + if cogr_ds != NULL: ogrext2.GDALClose(cogr_ds) cogr_ds = NULL diff --git a/setup.py b/setup.py index e3fac13..f794f60 100644 --- a/setup.py +++ b/setup.py @@ -222,12 +222,15 @@ requirements = [ 'cligj', 'click-plugins', 'six', - 'munch' -] + 'munch'] + if sys.version_info < (2, 7): requirements.append('argparse') requirements.append('ordereddict') +if sys.version_info < (3, 4): + requirements.append('enum34') + setup_args = dict( cmdclass={'sdist': sdist_multi_gdal}, metadata_version='1.2', diff --git a/tests/test_drivers.py b/tests/test_drivers.py index 63f3e28..9d88aa9 100644 --- a/tests/test_drivers.py +++ b/tests/test_drivers.py @@ -8,7 +8,7 @@ import unittest import fiona -logging.basicConfig(stream=sys.stderr, level=logging.INFO) +logging.basicConfig(stream=sys.stderr, level=logging.DEBUG) FIXME_WINDOWS = sys.platform.startswith('win') diff --git a/tests/test_geojson.py b/tests/test_geojson.py index 60d95cd..b7d2d7f 100644 --- a/tests/test_geojson.py +++ b/tests/test_geojson.py @@ -10,7 +10,10 @@ import fiona from fiona.collection import supported_drivers from fiona.errors import FionaValueError, DriverError, SchemaError, CRSError -logging.basicConfig(stream=sys.stderr, level=logging.INFO) + +# logging.basicConfig(stream=sys.stderr, level=logging.DEBUG) + +log = logging.getLogger(__name__) class ReadingTest(unittest.TestCase): @@ -46,3 +49,24 @@ class WritingTest(unittest.TestCase): with fiona.open(path) as c: self.assertEqual(c.schema['geometry'], 'Unknown') self.assertEqual(len(c), 2) + + def test_json_overwrite(self): + path = os.path.join(self.tempdir, 'foo.json') + + with fiona.drivers(), fiona.open(path, 'w', + driver='GeoJSON', + schema={'geometry': 'Unknown', 'properties': [('title', 'str')]}) as c: + c.writerecords([{ + 'geometry': {'type': 'Point', 'coordinates': [0.0, 0.0]}, + 'properties': {'title': 'One'}}]) + c.writerecords([{ + 'geometry': {'type': 'MultiPoint', 'coordinates': [[0.0, 0.0]]}, + 'properties': {'title': 'Two'}}]) + + # Overwrite should raise DriverIOError. + try: + with fiona.drivers(), fiona.open(path, 'w', driver='GeoJSON', + schema={'geometry': 'Unknown', 'properties': [('title', 'str')]}) as c: + pass + except IOError: + pass -- Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-grass/fiona.git _______________________________________________ Pkg-grass-devel mailing list [email protected] http://lists.alioth.debian.org/cgi-bin/mailman/listinfo/pkg-grass-devel

