Bug#802808: fiona: Fails to build with GDAL 2.0 (patch)

Sebastiaan Couwenberg sebastic at xs4all.nl
Sun Feb 7 22:28:08 UTC 2016


On 23-10-15 23:10, Sebastiaan Couwenberg wrote:
> It was pointed out in the upstream 'GDAL 2.0' issue that work is in
> progress to support GDAL 2.0 in fiona:
> 
> https://github.com/Toblerity/Fiona/pull/275
> 
> With the changes from PR #275 and a couple of additional patches fiona
> builds successfully with GDAL 2.0. See the attached debdiff.
> 
> Keep an eye on the upstream issues for further changes we should
> incorporate in the Debian package too.

The upstream changes for GDAL 2.0 differ somewhat from those initially
posted to this bugreport. With the changes from the attached debdiff,
fiona builds successfully with GDAL 2.0. It is already included in the
fiona packaging in git.

Kind Regards,

Bas

-- 
 GPG Key ID: 4096R/6750F10AE88D4AF1
Fingerprint: 8182 DE41 7056 408D 6146  50D1 6750 F10A E88D 4AF1
-------------- next part --------------
diff -Nru fiona-1.6.3/debian/changelog fiona-1.6.3/debian/changelog
--- fiona-1.6.3/debian/changelog	2015-12-25 02:25:23.000000000 +0100
+++ fiona-1.6.3/debian/changelog	2016-02-06 22:53:35.000000000 +0100
@@ -1,3 +1,11 @@
+fiona (1.6.3-2) UNRELEASED; urgency=medium
+
+  * Team upload.
+  * Update Vcs-Git URL to use HTTPS.
+  * Add patches for GDAL 2.0 support.
+
+ -- Bas Couwenberg <sebastic at debian.org>  Fri, 05 Feb 2016 18:10:31 +0100
+
 fiona (1.6.3-1) unstable; urgency=medium
 
   * Team upload.
diff -Nru fiona-1.6.3/debian/control fiona-1.6.3/debian/control
--- fiona-1.6.3/debian/control	2015-12-25 01:58:09.000000000 +0100
+++ fiona-1.6.3/debian/control	2016-02-06 22:52:59.000000000 +0100
@@ -28,7 +28,7 @@
                gdal-bin
 Standards-Version: 3.9.6
 Vcs-Browser: https://anonscm.debian.org/cgit/pkg-grass/fiona.git
-Vcs-Git: git://anonscm.debian.org/pkg-grass/fiona.git
+Vcs-Git: https://anonscm.debian.org/git/pkg-grass/fiona.git
 Homepage: https://github.com/Toblerity/Fiona
 X-Python-Version: >= 2.5
 
diff -Nru fiona-1.6.3/debian/patches/0003-GDAL-2.0.patch fiona-1.6.3/debian/patches/0003-GDAL-2.0.patch
--- fiona-1.6.3/debian/patches/0003-GDAL-2.0.patch	1970-01-01 01:00:00.000000000 +0100
+++ fiona-1.6.3/debian/patches/0003-GDAL-2.0.patch	2016-02-07 22:52:45.000000000 +0100
@@ -0,0 +1,4578 @@
+Origin: https://github.com/Toblerity/Fiona/commit/3ebef78f2d1af1e306ad2a1df043511bfc55c13b
+Bug: https://github.com/Toblerity/Fiona/issues/239
+Bug-Debian: https://bugs.debian.org/802808
+From 84e7ce2786e3d881d0a88156bb1e26167bb2ce0d Mon Sep 17 00:00:00 2001
+From: Rene Buffat <buffat at gmail.com>
+Date: Wed, 23 Sep 2015 22:40:37 +0200
+Subject: [PATCH 01/16] sperate implementation for gdal2
+
+---
+ .travis.yml       |    2 +-
+ fiona/ograpi.pxd  |  145 --
+ fiona/ograpi1.pxd |  145 ++
+ fiona/ograpi2.pxd |  183 ++
+ fiona/ogrext.pyx  | 1233 -----------
+ fiona/ogrext1.pyx | 1233 +++++++++++
+ fiona/ogrext2.pyx | 6240 +++++++++++++++++++++++++++++++++++++++++++++++++++++
+ setup.py          |   14 +-
+ 8 files changed, 7814 insertions(+), 1381 deletions(-)
+ delete mode 100644 fiona/ograpi.pxd
+ create mode 100644 fiona/ograpi1.pxd
+ create mode 100644 fiona/ograpi2.pxd
+ delete mode 100644 fiona/ogrext.pyx
+ create mode 100644 fiona/ogrext1.pyx
+ create mode 100644 fiona/ogrext2.pyx
+
+--- a/.travis.yml
++++ b/.travis.yml
+@@ -8,6 +8,7 @@ before_install:
+   - sudo apt-get update -qq
+   - sudo apt-get install -y libgdal1h gdal-bin libgdal-dev
+ install:
++  - "pip install -r requirements.txt"
+   - "pip install -r requirements-dev.txt"
+   - "pip install pytest"
+   - "pip install coveralls"
+--- a/fiona/ograpi.pxd
++++ /dev/null
+@@ -1,145 +0,0 @@
+-# Copyright (c) 2007, Sean C. Gillies
+-# All rights reserved.
+-# See ../LICENSE.txt
+-
+-cdef extern from "gdal.h":
+-    char * GDALVersionInfo (char *pszRequest)
+-
+-cdef extern from "gdal_version.h":
+-    int    GDAL_COMPUTE_VERSION(int maj, int min, int rev)
+-
+-cdef extern from "cpl_conv.h":
+-    void *  CPLMalloc (size_t)
+-    void    CPLFree (void *ptr)
+-    void    CPLSetThreadLocalConfigOption (char *key, char *val)
+-    const char *CPLGetConfigOption (char *, char *)
+-
+-cdef extern from "cpl_string.h":
+-    char ** CSLSetNameValue (char **list, char *name, char *value)
+-    void    CSLDestroy (char **list)
+-
+-cdef extern from "cpl_vsi.h":
+-    ctypedef struct VSILFILE:
+-        pass
+-    int VSIFCloseL (VSILFILE *)
+-    VSILFILE * VSIFileFromMemBuffer (const char * filename,
+-                                     unsigned char * data,
+-                                     int data_len,
+-                                     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":
+-    char *  OGRGeometryTypeToName(int)
+-
+-cdef extern from "ogr_srs_api.h":
+-    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)
+-    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 *)
+-    void *  OGR_Dr_Open (void *driver, const char *path, int bupdate)
+-    int     OGR_DS_DeleteLayer (void *datasource, int n)
+-    void *  OGR_DS_CreateLayer (void *datasource, char *name, void *crs, int geomType, char **options)
+-    void *  OGR_DS_ExecuteSQL (void *datasource, char *name, void *filter, char *dialext)
+-    void    OGR_DS_Destroy (void *datasource)
+-    void *  OGR_DS_GetDriver (void *layer_defn)
+-    void *  OGR_DS_GetLayerByName (void *datasource, char *name)
+-    int     OGR_DS_GetLayerCount (void *datasource)
+-    void *  OGR_DS_GetLayer (void *datasource, int n)
+-    void    OGR_DS_ReleaseResultSet (void *datasource, void *results)
+-    int     OGR_DS_SyncToDisk (void *datasource)
+-    void *  OGR_F_Create (void *featuredefn)
+-    void    OGR_F_Destroy (void *feature)
+-    long    OGR_F_GetFID (void *feature)
+-    int     OGR_F_IsFieldSet (void *feature, int n)
+-    int     OGR_F_GetFieldAsDateTime (void *feature, int n, int *y, int *m, int *d, int *h, int *m, int *s, int *z)
+-    double  OGR_F_GetFieldAsDouble (void *feature, int n)
+-    int     OGR_F_GetFieldAsInteger (void *feature, int n)
+-    char *  OGR_F_GetFieldAsString (void *feature, int n)
+-    int     OGR_F_GetFieldCount (void *feature)
+-    void *  OGR_F_GetFieldDefnRef (void *feature, int n)
+-    int     OGR_F_GetFieldIndex (void *feature, char *name)
+-    void *  OGR_F_GetGeometryRef (void *feature)
+-    void    OGR_F_SetFieldDateTime (void *feature, int n, int y, int m, int d, int hh, int mm, int ss, int tz)
+-    void    OGR_F_SetFieldDouble (void *feature, int n, double value)
+-    void    OGR_F_SetFieldInteger (void *feature, int n, int value)
+-    void    OGR_F_SetFieldString (void *feature, int n, char *value)
+-    int     OGR_F_SetGeometryDirectly (void *feature, void *geometry)
+-    void *  OGR_FD_Create (char *name)
+-    int     OGR_FD_GetFieldCount (void *featuredefn)
+-    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_Destroy (void *fielddefn)
+-    char *  OGR_Fld_GetNameRef (void *fielddefn)
+-    int     OGR_Fld_GetPrecision (void *fielddefn)
+-    int     OGR_Fld_GetType (void *fielddefn)
+-    int     OGR_Fld_GetWidth (void *fielddefn)
+-    void    OGR_Fld_Set (void *fielddefn, char *name, int fieldtype, int width, int precision, int justification)
+-    void    OGR_Fld_SetPrecision (void *fielddefn, int n)
+-    void    OGR_Fld_SetWidth (void *fielddefn, int n)
+-    OGRErr  OGR_G_AddGeometryDirectly (void *geometry, void *part)
+-    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_DestroyGeometry (void *geometry)
+-    unsigned char *  OGR_G_ExportToJson (void *geometry)
+-    void    OGR_G_ExportToWkb (void *geometry, int endianness, char *buffer)
+-    int     OGR_G_GetCoordinateDimension (void *geometry)
+-    int     OGR_G_GetGeometryCount (void *geometry)
+-    unsigned char *  OGR_G_GetGeometryName (void *geometry)
+-    int     OGR_G_GetGeometryType (void *geometry)
+-    void *  OGR_G_GetGeometryRef (void *geometry, int n)
+-    int     OGR_G_GetPointCount (void *geometry)
+-    double  OGR_G_GetX (void *geometry, int n)
+-    double  OGR_G_GetY (void *geometry, int n)
+-    double  OGR_G_GetZ (void *geometry, int n)
+-    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_GetExtent (void *layer, void *extent, int force)
+-    void *  OGR_L_GetFeature (void *layer, int n)
+-    int     OGR_L_GetFeatureCount (void *layer, int m)
+-    void *  OGR_L_GetLayerDefn (void *layer)
+-    char *  OGR_L_GetName (void *layer)
+-    void *  OGR_L_GetNextFeature (void *layer)
+-    void *  OGR_L_GetSpatialFilter (void *layer)
+-    void *  OGR_L_GetSpatialRef (void *layer)
+-    void    OGR_L_ResetReading (void *layer)
+-    void    OGR_L_SetSpatialFilter (void *layer, void *geometry)
+-    void    OGR_L_SetSpatialFilterRect (
+-                void *layer, double minx, double miny, double maxx, double maxy
+-                )
+-    int     OGR_L_TestCapability (void *layer, char *name)
+-    void *  OGRGetDriverByName (char *)
+-    void *  OGROpen (char *path, int mode, void *x)
+-    void *  OGROpenShared (char *path, int mode, void *x)
+-    int     OGRReleaseDataSource (void *datasource)
+-    OGRErr  OGR_L_SetNextByIndex (void *layer, long nIndex)
+--- /dev/null
++++ b/fiona/ograpi1.pxd
+@@ -0,0 +1,145 @@
++# Copyright (c) 2007, Sean C. Gillies
++# All rights reserved.
++# See ../LICENSE.txt
++
++cdef extern from "gdal.h":
++    char * GDALVersionInfo (char *pszRequest)
++
++cdef extern from "gdal_version.h":
++    int    GDAL_COMPUTE_VERSION(int maj, int min, int rev)
++
++cdef extern from "cpl_conv.h":
++    void *  CPLMalloc (size_t)
++    void    CPLFree (void *ptr)
++    void    CPLSetThreadLocalConfigOption (char *key, char *val)
++    const char *CPLGetConfigOption (char *, char *)
++
++cdef extern from "cpl_string.h":
++    char ** CSLSetNameValue (char **list, char *name, char *value)
++    void    CSLDestroy (char **list)
++
++cdef extern from "cpl_vsi.h":
++    ctypedef struct VSILFILE:
++        pass
++    int VSIFCloseL (VSILFILE *)
++    VSILFILE * VSIFileFromMemBuffer (const char * filename,
++                                     unsigned char * data,
++                                     int data_len,
++                                     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":
++    char *  OGRGeometryTypeToName(int)
++
++cdef extern from "ogr_srs_api.h":
++    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)
++    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 *)
++    void *  OGR_Dr_Open (void *driver, const char *path, int bupdate)
++    int     OGR_DS_DeleteLayer (void *datasource, int n)
++    void *  OGR_DS_CreateLayer (void *datasource, char *name, void *crs, int geomType, char **options)
++    void *  OGR_DS_ExecuteSQL (void *datasource, char *name, void *filter, char *dialext)
++    void    OGR_DS_Destroy (void *datasource)
++    void *  OGR_DS_GetDriver (void *layer_defn)
++    void *  OGR_DS_GetLayerByName (void *datasource, char *name)
++    int     OGR_DS_GetLayerCount (void *datasource)
++    void *  OGR_DS_GetLayer (void *datasource, int n)
++    void    OGR_DS_ReleaseResultSet (void *datasource, void *results)
++    int     OGR_DS_SyncToDisk (void *datasource)
++    void *  OGR_F_Create (void *featuredefn)
++    void    OGR_F_Destroy (void *feature)
++    long    OGR_F_GetFID (void *feature)
++    int     OGR_F_IsFieldSet (void *feature, int n)
++    int     OGR_F_GetFieldAsDateTime (void *feature, int n, int *y, int *m, int *d, int *h, int *m, int *s, int *z)
++    double  OGR_F_GetFieldAsDouble (void *feature, int n)
++    int     OGR_F_GetFieldAsInteger (void *feature, int n)
++    char *  OGR_F_GetFieldAsString (void *feature, int n)
++    int     OGR_F_GetFieldCount (void *feature)
++    void *  OGR_F_GetFieldDefnRef (void *feature, int n)
++    int     OGR_F_GetFieldIndex (void *feature, char *name)
++    void *  OGR_F_GetGeometryRef (void *feature)
++    void    OGR_F_SetFieldDateTime (void *feature, int n, int y, int m, int d, int hh, int mm, int ss, int tz)
++    void    OGR_F_SetFieldDouble (void *feature, int n, double value)
++    void    OGR_F_SetFieldInteger (void *feature, int n, int value)
++    void    OGR_F_SetFieldString (void *feature, int n, char *value)
++    int     OGR_F_SetGeometryDirectly (void *feature, void *geometry)
++    void *  OGR_FD_Create (char *name)
++    int     OGR_FD_GetFieldCount (void *featuredefn)
++    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_Destroy (void *fielddefn)
++    char *  OGR_Fld_GetNameRef (void *fielddefn)
++    int     OGR_Fld_GetPrecision (void *fielddefn)
++    int     OGR_Fld_GetType (void *fielddefn)
++    int     OGR_Fld_GetWidth (void *fielddefn)
++    void    OGR_Fld_Set (void *fielddefn, char *name, int fieldtype, int width, int precision, int justification)
++    void    OGR_Fld_SetPrecision (void *fielddefn, int n)
++    void    OGR_Fld_SetWidth (void *fielddefn, int n)
++    OGRErr  OGR_G_AddGeometryDirectly (void *geometry, void *part)
++    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_DestroyGeometry (void *geometry)
++    unsigned char *  OGR_G_ExportToJson (void *geometry)
++    void    OGR_G_ExportToWkb (void *geometry, int endianness, char *buffer)
++    int     OGR_G_GetCoordinateDimension (void *geometry)
++    int     OGR_G_GetGeometryCount (void *geometry)
++    unsigned char *  OGR_G_GetGeometryName (void *geometry)
++    int     OGR_G_GetGeometryType (void *geometry)
++    void *  OGR_G_GetGeometryRef (void *geometry, int n)
++    int     OGR_G_GetPointCount (void *geometry)
++    double  OGR_G_GetX (void *geometry, int n)
++    double  OGR_G_GetY (void *geometry, int n)
++    double  OGR_G_GetZ (void *geometry, int n)
++    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_GetExtent (void *layer, void *extent, int force)
++    void *  OGR_L_GetFeature (void *layer, int n)
++    int     OGR_L_GetFeatureCount (void *layer, int m)
++    void *  OGR_L_GetLayerDefn (void *layer)
++    char *  OGR_L_GetName (void *layer)
++    void *  OGR_L_GetNextFeature (void *layer)
++    void *  OGR_L_GetSpatialFilter (void *layer)
++    void *  OGR_L_GetSpatialRef (void *layer)
++    void    OGR_L_ResetReading (void *layer)
++    void    OGR_L_SetSpatialFilter (void *layer, void *geometry)
++    void    OGR_L_SetSpatialFilterRect (
++                void *layer, double minx, double miny, double maxx, double maxy
++                )
++    int     OGR_L_TestCapability (void *layer, char *name)
++    void *  OGRGetDriverByName (char *)
++    void *  OGROpen (char *path, int mode, void *x)
++    void *  OGROpenShared (char *path, int mode, void *x)
++    int     OGRReleaseDataSource (void *datasource)
++    OGRErr  OGR_L_SetNextByIndex (void *layer, long nIndex)
+--- /dev/null
++++ b/fiona/ograpi2.pxd
+@@ -0,0 +1,185 @@
++# Copyright (c) 2007, Sean C. Gillies
++# All rights reserved.
++# See ../LICENSE.txt
++
++cdef extern from "gdal.h":
++    char * GDALVersionInfo (char *pszRequest)
++    void * GDALGetDriverByName(const char * pszName)
++    void * GDALOpenEx(const char * pszFilename,
++                      unsigned int nOpenFlags,
++                      const char ** papszAllowedDrivers,
++                      const char ** papszOpenOptions,
++                      const char *const *papszSibling1Files
++                      )
++    int GDAL_OF_UPDATE
++    int GDAL_OF_READONLY
++    int GDAL_OF_VECTOR
++    int GDAL_OF_VERBOSE_ERROR
++    int GDALDatasetGetLayerCount(void * hds)
++    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,
++                      int     nYSize,
++                      int     nBands,
++                      GDALDataType eBandType,
++                      char ** papszOptions)
++    void * GDALDatasetCreateLayer(void * hDS,
++                                  const char * pszName,
++                                  void * hSpatialRef,
++                                  int eType,
++                                  char ** papszOptions)
++    int GDALDatasetDeleteLayer(void * hDS, int iLayer)
++    void GDALFlushCache(void * hDS)
++    char * GDALGetDriverShortName(void * hDriver)
++    char * GDALGetDatasetDriver (void * hDataset)
++
++
++    ctypedef enum GDALDataType:
++        GDT_Unknown
++        GDT_Byte
++        GDT_UInt16
++        GDT_Int16
++        GDT_UInt32
++        GDT_Int32
++        GDT_Float32
++        GDT_Float64
++        GDT_CInt16
++        GDT_CInt32
++        GDT_CFloat32
++        GDT_CFloat64
++        GDT_TypeCount
++
++cdef extern from "gdal_version.h":
++    int    GDAL_COMPUTE_VERSION(int maj, int min, int rev)
++
++cdef extern from "cpl_conv.h":
++    void *  CPLMalloc (size_t)
++    void    CPLFree (void *ptr)
++    void    CPLSetThreadLocalConfigOption (char *key, char *val)
++    const char *CPLGetConfigOption (char *, char *)
++
++cdef extern from "cpl_string.h":
++    char ** CSLSetNameValue (char **list, char *name, char *value)
++    void    CSLDestroy (char **list)
++
++cdef extern from "cpl_vsi.h":
++    ctypedef struct VSILFILE:
++        pass
++    int VSIFCloseL (VSILFILE *)
++    VSILFILE * VSIFileFromMemBuffer (const char * filename,
++                                     unsigned char * data,
++                                     int data_len,
++                                     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":
++    char *  OGRGeometryTypeToName(int)
++
++cdef extern from "ogr_srs_api.h":
++    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)
++    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 *)
++    void *  OGR_Dr_Open (void *driver, const char *path, int bupdate)
++    void *  OGR_F_Create (void *featuredefn)
++    void    OGR_F_Destroy (void *feature)
++    long    OGR_F_GetFID (void *feature)
++    int     OGR_F_IsFieldSet (void *feature, int n)
++    int     OGR_F_GetFieldAsDateTime (void *feature, int n, int *y, int *m, int *d, int *h, int *m, int *s, int *z)
++    double  OGR_F_GetFieldAsDouble (void *feature, int n)
++    int     OGR_F_GetFieldAsInteger (void *feature, int n)
++    char *  OGR_F_GetFieldAsString (void *feature, int n)
++    int     OGR_F_GetFieldCount (void *feature)
++    void *  OGR_F_GetFieldDefnRef (void *feature, int n)
++    int     OGR_F_GetFieldIndex (void *feature, char *name)
++    void *  OGR_F_GetGeometryRef (void *feature)
++    void    OGR_F_SetFieldDateTime (void *feature, int n, int y, int m, int d, int hh, int mm, int ss, int tz)
++    void    OGR_F_SetFieldDouble (void *feature, int n, double value)
++    void    OGR_F_SetFieldInteger (void *feature, int n, int value)
++    void    OGR_F_SetFieldString (void *feature, int n, char *value)
++    int     OGR_F_SetGeometryDirectly (void *feature, void *geometry)
++    void *  OGR_FD_Create (char *name)
++    int     OGR_FD_GetFieldCount (void *featuredefn)
++    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_Destroy (void *fielddefn)
++    char *  OGR_Fld_GetNameRef (void *fielddefn)
++    int     OGR_Fld_GetPrecision (void *fielddefn)
++    int     OGR_Fld_GetType (void *fielddefn)
++    int     OGR_Fld_GetWidth (void *fielddefn)
++    void    OGR_Fld_Set (void *fielddefn, char *name, int fieldtype, int width, int precision, int justification)
++    void    OGR_Fld_SetPrecision (void *fielddefn, int n)
++    void    OGR_Fld_SetWidth (void *fielddefn, int n)
++    OGRErr  OGR_G_AddGeometryDirectly (void *geometry, void *part)
++    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_DestroyGeometry (void *geometry)
++    unsigned char *  OGR_G_ExportToJson (void *geometry)
++    void    OGR_G_ExportToWkb (void *geometry, int endianness, char *buffer)
++    int     OGR_G_GetCoordinateDimension (void *geometry)
++    int     OGR_G_GetGeometryCount (void *geometry)
++    unsigned char *  OGR_G_GetGeometryName (void *geometry)
++    int     OGR_G_GetGeometryType (void *geometry)
++    void *  OGR_G_GetGeometryRef (void *geometry, int n)
++    int     OGR_G_GetPointCount (void *geometry)
++    double  OGR_G_GetX (void *geometry, int n)
++    double  OGR_G_GetY (void *geometry, int n)
++    double  OGR_G_GetZ (void *geometry, int n)
++    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_GetExtent (void *layer, void *extent, int force)
++    void *  OGR_L_GetFeature (void *layer, int n)
++    int     OGR_L_GetFeatureCount (void *layer, int m)
++    void *  OGR_L_GetLayerDefn (void *layer)
++    char *  OGR_L_GetName (void *layer)
++    void *  OGR_L_GetNextFeature (void *layer)
++    void *  OGR_L_GetSpatialFilter (void *layer)
++    void *  OGR_L_GetSpatialRef (void *layer)
++    void    OGR_L_ResetReading (void *layer)
++    void    OGR_L_SetSpatialFilter (void *layer, void *geometry)
++    void    OGR_L_SetSpatialFilterRect (
++                void *layer, double minx, double miny, double maxx, double maxy
++                )
++    int     OGR_L_TestCapability (void *layer, char *name)
++    void *  OGRGetDriverByName (char *)
++    void *  OGROpen (char *path, int mode, void *x)
++    void *  OGROpenShared (char *path, int mode, void *x)
++    int     OGRReleaseDataSource (void *datasource)
++    OGRErr  OGR_L_SetNextByIndex (void *layer, long nIndex)
++    long long OGR_F_GetFieldAsInteger64 (void *feature, int n)
++    void    OGR_F_SetFieldInteger64 (void *feature, int n, long long value)
+--- a/fiona/ogrext.pyx
++++ /dev/null
+@@ -1,1243 +0,0 @@
+-# These are extension functions and classes using the OGR C API.
+-
+-import datetime
+-import json
+-import locale
+-import logging
+-import os
+-import sys
+-import warnings
+-import math
+-import uuid
+-
+-from six import integer_types, string_types, text_type
+-
+-from fiona cimport ograpi
+-from fiona._geometry cimport GeomBuilder, OGRGeomBuilder
+-from fiona._err import cpl_errs
+-from fiona._geometry import GEOMETRY_TYPES
+-from fiona.errors import (
+-    DriverError, SchemaError, CRSError, FionaValueError, FieldNameEncodeError,
+-    StringFieldEncodeError, StringFieldDecodeError)
+-from fiona.odict import OrderedDict
+-from fiona.rfc3339 import parse_date, parse_datetime, parse_time
+-from fiona.rfc3339 import FionaDateType, FionaDateTimeType, FionaTimeType
+-
+-
+-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.
+-#
+-# Lists are currently unsupported in this version, but might be done as
+-# arrays in a future version.
+-
+-FIELD_TYPES = [
+-    'int',          # OFTInteger, Simple 32bit integer
+-    None,           # OFTIntegerList, List of 32bit integers
+-    'float',        # OFTReal, Double Precision floating point
+-    None,           # OFTRealList, List of doubles
+-    'str',          # OFTString, String of ASCII chars
+-    None,           # OFTStringList, Array of strings
+-    None,           # OFTWideString, deprecated
+-    None,           # OFTWideStringList, deprecated
+-    None,           # OFTBinary, Raw Binary data
+-    'date',         # OFTDate, Date
+-    'time',         # OFTTime, Time
+-    'datetime',     # OFTDateTime, Date and Time
+-    ]
+-
+-# Mapping of Fiona field type names to Python types.
+-FIELD_TYPES_MAP = {
+-    'int':      int,
+-    'float':    float,
+-    'str':      text_type,
+-    'date':     FionaDateType,
+-    'time':     FionaTimeType,
+-    'datetime': FionaDateTimeType
+-   }
+-
+-# 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"
+-
+-# OGR integer error types.
+-
+-OGRERR_NONE = 0
+-OGRERR_NOT_ENOUGH_DATA = 1    # not enough data to deserialize */
+-OGRERR_NOT_ENOUGH_MEMORY = 2
+-OGRERR_UNSUPPORTED_GEOMETRY_TYPE = 3
+-OGRERR_UNSUPPORTED_OPERATION = 4
+-OGRERR_CORRUPT_DATA = 5
+-OGRERR_FAILURE = 6
+-OGRERR_UNSUPPORTED_SRS = 7
+-OGRERR_INVALID_HANDLE = 8
+-
+-# Recent versions of OGR can sometimes detect file encoding, but don't
+-# provide access yet to the detected encoding. Hence this variable.
+-OGR_DETECTED_ENCODING = '-ogr-detected-encoding'
+-
+-
+-def _explode(coords):
+-    """Explode a GeoJSON geometry's coordinates object and yield
+-    coordinate tuples. As long as the input is conforming, the type of
+-    the geometry doesn't matter."""
+-    for e in coords:
+-        if isinstance(e, (float, int)):
+-            yield coords
+-            break
+-        else:
+-            for f in _explode(e):
+-                yield f
+-
+-
+-def _bounds(geometry):
+-    """Bounding box of a GeoJSON geometry"""
+-    try:
+-        xyz = tuple(zip(*list(_explode(geometry['coordinates']))))
+-        return min(xyz[0]), min(xyz[1]), max(xyz[0]), max(xyz[1])
+-    except (KeyError, TypeError):
+-        return None
+-
+-def calc_gdal_version_num(maj, min, rev):
+-    """Calculates the internal gdal version number based on major, minor and revision"""
+-    return int(maj * 1000000 + min * 10000 + rev*100)
+-
+-def get_gdal_version_num():
+-    """Return current internal version number of gdal"""
+-    return int(ograpi.GDALVersionInfo("VERSION_NUM"))
+-
+-def get_gdal_release_name():
+-    """Return release name of gdal"""
+-    return ograpi.GDALVersionInfo("RELEASE_NAME")
+-
+-
+-# Feature extension classes and functions follow.
+-
+-cdef class FeatureBuilder:
+-    """Build Fiona features from OGR feature pointers.
+-
+-    No OGR objects are allocated by this function and the feature
+-    argument is not destroyed.
+-    """
+-
+-    cdef build(self, void *feature, encoding='utf-8', bbox=False, driver=None):
+-        # The only method anyone ever needs to call
+-        cdef void *fdefn
+-        cdef int i
+-        cdef int y = 0
+-        cdef int m = 0
+-        cdef int d = 0
+-        cdef int hh = 0
+-        cdef int mm = 0
+-        cdef int ss = 0
+-        cdef int tz = 0
+-        cdef int retval
+-        cdef char *key_c
+-        props = OrderedDict()
+-        for i in range(ograpi.OGR_F_GetFieldCount(feature)):
+-            fdefn = ograpi.OGR_F_GetFieldDefnRef(feature, i)
+-            if fdefn == NULL:
+-                raise ValueError("Null feature definition")
+-            key_c = ograpi.OGR_Fld_GetNameRef(fdefn)
+-            if key_c == NULL:
+-                raise ValueError("Null field name reference")
+-            key_b = key_c
+-            key = key_b.decode(encoding)
+-            fieldtypename = FIELD_TYPES[ograpi.OGR_Fld_GetType(fdefn)]
+-            if not fieldtypename:
+-                log.warn(
+-                    "Skipping field %s: invalid type %s", 
+-                    key,
+-                    ograpi.OGR_Fld_GetType(fdefn))
+-                continue
+-
+-            # TODO: other types
+-            fieldtype = FIELD_TYPES_MAP[fieldtypename]
+-            if not ograpi.OGR_F_IsFieldSet(feature, i):
+-                props[key] = None
+-            elif fieldtype is int:
+-                props[key] = ograpi.OGR_F_GetFieldAsInteger(feature, i)
+-            elif fieldtype is float:
+-                props[key] = ograpi.OGR_F_GetFieldAsDouble(feature, i)
+-
+-            elif fieldtype is text_type:
+-                try:
+-                    val = ograpi.OGR_F_GetFieldAsString(feature, i)
+-                    val = val.decode(encoding)
+-                except UnicodeError as exc:
+-                    raise StringFieldDecodeError(
+-                        "Failed to decode {0} using {1} codec: {2}".format(
+-                            val, encoding, str(exc)))
+-
+-                # Does the text contain a JSON object? Let's check.
+-                # Let's check as cheaply as we can.
+-                if driver == 'GeoJSON' and val.startswith('{'):
+-                    try:
+-                        val = json.loads(val)
+-                    except ValueError as err:
+-                        log.warn(str(err))
+-
+-                # Now add to the properties object.
+-                props[key] = val
+-
+-            elif fieldtype in (FionaDateType, FionaTimeType, FionaDateTimeType):
+-                retval = ograpi.OGR_F_GetFieldAsDateTime(
+-                    feature, i, &y, &m, &d, &hh, &mm, &ss, &tz)
+-                if fieldtype is FionaDateType:
+-                    props[key] = datetime.date(y, m, d).isoformat()
+-                elif fieldtype is FionaTimeType:
+-                    props[key] = datetime.time(hh, mm, ss).isoformat()
+-                else:
+-                    props[key] = datetime.datetime(
+-                        y, m, d, hh, mm, ss).isoformat()
+-            else:
+-                log.debug("%s: None, fieldtype: %r, %r" % (key, fieldtype, fieldtype in string_types))
+-                props[key] = None
+-
+-        cdef void *cogr_geometry = ograpi.OGR_F_GetGeometryRef(feature)
+-        if cogr_geometry is not NULL:
+-            geom = GeomBuilder().build(cogr_geometry)
+-        else:
+-            geom = None
+-        return {
+-            'type': 'Feature',
+-            'id': str(ograpi.OGR_F_GetFID(feature)),
+-            'geometry': geom,
+-            'properties': props }
+-
+-
+-cdef class OGRFeatureBuilder:
+-    
+-    """Builds an OGR Feature from a Fiona feature mapping.
+-
+-    Allocates one OGR Feature which should be destroyed by the caller.
+-    Borrows a layer definition from the collection.
+-    """
+-    
+-    cdef void * build(self, feature, collection) except NULL:
+-        cdef void *cogr_geometry = NULL
+-        cdef char *string_c
+-        cdef WritingSession session
+-        session = collection.session
+-        cdef void *cogr_layer = session.cogr_layer
+-        if cogr_layer == NULL:
+-            raise ValueError("Null layer")
+-        cdef void *cogr_featuredefn = ograpi.OGR_L_GetLayerDefn(cogr_layer)
+-        if cogr_featuredefn == NULL:
+-            raise ValueError("Null feature definition")
+-        cdef void *cogr_feature = ograpi.OGR_F_Create(cogr_featuredefn)
+-        if cogr_feature == NULL:
+-            raise ValueError("Null feature")
+-        
+-        if feature['geometry'] is not None:
+-            cogr_geometry = OGRGeomBuilder().build(
+-                                feature['geometry'])
+-        ograpi.OGR_F_SetGeometryDirectly(cogr_feature, cogr_geometry)
+-        
+-        # OGR_F_SetFieldString takes UTF-8 encoded strings ('bytes' in 
+-        # Python 3).
+-        encoding = session.get_internalencoding()
+-
+-        for key, value in feature['properties'].items():
+-            log.debug(
+-                "Looking up %s in %s", key, repr(session._schema_mapping))
+-            ogr_key = session._schema_mapping[key]
+-            schema_type = collection.schema['properties'][key]
+-
+-            # Catch and re-raise unicode encoding errors.
+-            try:
+-                key_bytes = ogr_key.encode(encoding)
+-            except UnicodeError as exc:
+-                raise FieldNameEncodeError(
+-                    "Failed to encode {0} using {1} codec: {2}".format(
+-                        key, encoding, str(exc)))
+-
+-            key_c = key_bytes
+-            i = ograpi.OGR_F_GetFieldIndex(cogr_feature, key_c)
+-            if i < 0:
+-                continue
+-
+-            # Special case: serialize dicts to assist OGR.
+-            if isinstance(value, dict):
+-                value = json.dumps(value)
+-
+-            # Continue over the standard OGR types.
+-            if isinstance(value, integer_types):
+-                ograpi.OGR_F_SetFieldInteger(cogr_feature, i, value)
+-            elif isinstance(value, float):
+-                ograpi.OGR_F_SetFieldDouble(cogr_feature, i, value)
+-            elif (isinstance(value, string_types) 
+-            and schema_type in ['date', 'time', 'datetime']):
+-                if schema_type == 'date':
+-                    y, m, d, hh, mm, ss, ff = parse_date(value)
+-                elif schema_type == 'time':
+-                    y, m, d, hh, mm, ss, ff = parse_time(value)
+-                else:
+-                    y, m, d, hh, mm, ss, ff = parse_datetime(value)
+-                ograpi.OGR_F_SetFieldDateTime(
+-                    cogr_feature, i, y, m, d, hh, mm, ss, 0)
+-            elif (isinstance(value, datetime.date)
+-            and schema_type == 'date'):
+-                y, m, d = value.year, value.month, value.day
+-                ograpi.OGR_F_SetFieldDateTime(
+-                    cogr_feature, i, y, m, d, 0, 0, 0, 0)
+-            elif (isinstance(value, datetime.datetime)
+-            and schema_type == 'datetime'):
+-                y, m, d = value.year, value.month, value.day
+-                hh, mm, ss = value.hour, value.minute, value.second
+-                ograpi.OGR_F_SetFieldDateTime(
+-                    cogr_feature, i, y, m, d, hh, mm, ss, 0)
+-            elif (isinstance(value, datetime.time)
+-            and schema_type == 'time'):
+-                hh, mm, ss = value.hour, value.minute, value.second
+-                ograpi.OGR_F_SetFieldDateTime(
+-                    cogr_feature, i, 0, 0, 0, hh, mm, ss, 0)
+-            elif isinstance(value, string_types):
+-                
+-                # Catch and re-raise string field value encoding errors.
+-                try:
+-                    value_bytes = value.encode(encoding)
+-                except UnicodeError as exc:
+-                    raise StringFieldEncodeError(
+-                        "Failed to encode {0} using {1} codec: {2}".format(
+-                            value, encoding, str(exc)))
+-
+-                string_c = value_bytes
+-                ograpi.OGR_F_SetFieldString(cogr_feature, i, string_c)
+-            elif value is None:
+-                pass # keep field unset/null
+-            else:
+-                raise ValueError("Invalid field type %s" % type(value))
+-            log.debug("Set field %s: %s" % (key, value))
+-        return cogr_feature
+-
+-
+-cdef _deleteOgrFeature(void *cogr_feature):
+-    """Delete an OGR feature"""
+-    if cogr_feature is not NULL:
+-        ograpi.OGR_F_Destroy(cogr_feature)
+-    cogr_feature = NULL
+-
+-
+-def featureRT(feature, collection):
+-    # For testing purposes only, leaks the JSON data
+-    cdef void *cogr_feature = OGRFeatureBuilder().build(feature, collection)
+-    cdef void *cogr_geometry = ograpi.OGR_F_GetGeometryRef(cogr_feature)
+-    if cogr_geometry == NULL:
+-        raise ValueError("Null geometry")
+-    log.debug("Geometry: %s" % ograpi.OGR_G_ExportToJson(cogr_geometry))
+-    encoding = collection.encoding or 'utf-8'
+-    result = FeatureBuilder().build(
+-        cogr_feature,
+-        bbox=False,
+-        encoding=encoding,
+-        driver=collection.driver
+-    )
+-    _deleteOgrFeature(cogr_feature)
+-    return result
+-
+-
+-# Collection-related extension classes and functions
+-
+-cdef class Session:
+-    
+-    cdef void *cogr_ds
+-    cdef void *cogr_layer
+-    cdef object _fileencoding
+-    cdef object _encoding
+-    cdef object collection
+-
+-    def __cinit__(self):
+-        self.cogr_ds = NULL
+-        self.cogr_layer = NULL
+-        self._fileencoding = None
+-        self._encoding = None
+-
+-    def __dealloc__(self):
+-        self.stop()
+-
+-    def start(self, collection):
+-        cdef const char *path_c = NULL
+-        cdef const char *name_c = NULL
+-        cdef void *drv = NULL
+-        cdef void *ds = NULL
+-
+-        if collection.path == '-':
+-            path = '/vsistdin/'
+-        else:
+-            path = collection.path
+-        try:
+-            path_b = path.encode('utf-8')
+-        except UnicodeError:
+-            # Presume already a UTF-8 encoded string
+-            path_b = path
+-        path_c = path_b
+-        
+-        with cpl_errs:
+-            drivers = []
+-            if collection._driver:
+-                drivers = [collection._driver]
+-            elif collection.enabled_drivers:
+-                drivers = collection.enabled_drivers
+-            if drivers:
+-                for name in drivers:
+-                    name_b = name.encode()
+-                    name_c = name_b
+-                    log.debug("Trying driver: %s", name)
+-                    drv = ograpi.OGRGetDriverByName(name_c)
+-                    if drv != NULL:
+-                        ds = ograpi.OGR_Dr_Open(drv, path_c, 0)
+-                    if ds != NULL:
+-                        self.cogr_ds = ds
+-                        collection._driver = name
+-                        break
+-            else:
+-                self.cogr_ds = ograpi.OGROpen(path_c, 0, NULL)
+-
+-        if self.cogr_ds == NULL:
+-            raise FionaValueError(
+-                "No dataset found at path '%s' using drivers: %s" % (
+-                    collection.path,
+-                    drivers or '*'))
+-        
+-        if isinstance(collection.name, string_types):
+-            name_b = collection.name.encode('utf-8')
+-            name_c = name_b
+-            self.cogr_layer = ograpi.OGR_DS_GetLayerByName(
+-                                self.cogr_ds, name_c)
+-        elif isinstance(collection.name, int):
+-            self.cogr_layer = ograpi.OGR_DS_GetLayer(
+-                                self.cogr_ds, collection.name)
+-            name_c = ograpi.OGR_L_GetName(self.cogr_layer)
+-            name_b = name_c
+-            collection.name = name_b.decode('utf-8')
+-
+-        if self.cogr_layer == NULL:
+-            raise ValueError("Null layer: " + repr(collection.name))
+-        
+-        self.collection = collection
+-        
+-        userencoding = self.collection.encoding
+-        if userencoding:
+-            ograpi.CPLSetThreadLocalConfigOption('SHAPE_ENCODING', '')
+-            self._fileencoding = userencoding.upper()
+-        else:
+-            self._fileencoding = (
+-                ograpi.OGR_L_TestCapability(
+-                    self.cogr_layer, OLC_STRINGSASUTF8) and
+-                OGR_DETECTED_ENCODING) or (
+-                self.get_driver() == "ESRI Shapefile" and
+-                'ISO-8859-1') or locale.getpreferredencoding().upper()
+-
+-    def stop(self):
+-        self.cogr_layer = NULL
+-        if self.cogr_ds is not NULL:
+-            ograpi.OGR_DS_Destroy(self.cogr_ds)
+-        self.cogr_ds = NULL
+-
+-    def get_fileencoding(self):
+-        return self._fileencoding
+-
+-    def get_internalencoding(self):
+-        if not self._encoding:
+-            fileencoding = self.get_fileencoding()
+-            self._encoding = (
+-                ograpi.OGR_L_TestCapability(
+-                    self.cogr_layer, OLC_STRINGSASUTF8) and
+-                'utf-8') or fileencoding
+-        return self._encoding
+-
+-    def get_length(self):
+-        if self.cogr_layer == NULL:
+-            raise ValueError("Null layer")
+-        return ograpi.OGR_L_GetFeatureCount(self.cogr_layer, 0)
+-
+-    def get_driver(self):
+-        cdef void *cogr_driver = ograpi.OGR_DS_GetDriver(self.cogr_ds)
+-        if cogr_driver == NULL:
+-            raise ValueError("Null driver")
+-        cdef char *name = ograpi.OGR_Dr_GetName(cogr_driver)
+-        driver_name = name
+-        return driver_name.decode()
+- 
+-    def get_schema(self):
+-        cdef int i
+-        cdef int n
+-        cdef void *cogr_featuredefn
+-        cdef void *cogr_fielddefn
+-        cdef char *key_c
+-        props = []
+-        
+-        if self.cogr_layer == NULL:
+-            raise ValueError("Null layer")
+-
+-        cogr_featuredefn = ograpi.OGR_L_GetLayerDefn(self.cogr_layer)
+-        if cogr_featuredefn == NULL:
+-            raise ValueError("Null feature definition")
+-        n = ograpi.OGR_FD_GetFieldCount(cogr_featuredefn)
+-        for i from 0 <= i < n:
+-            cogr_fielddefn = ograpi.OGR_FD_GetFieldDefn(cogr_featuredefn, i)
+-            if cogr_fielddefn == NULL:
+-                raise ValueError("Null field definition")
+-            key_c = ograpi.OGR_Fld_GetNameRef(cogr_fielddefn)
+-            key_b = key_c
+-            if not bool(key_b):
+-                raise ValueError("Invalid field name ref: %s" % key)
+-            key = key_b.decode(self.get_internalencoding())
+-            fieldtypename = FIELD_TYPES[ograpi.OGR_Fld_GetType(cogr_fielddefn)]
+-            if not fieldtypename:
+-                log.warn(
+-                    "Skipping field %s: invalid type %s", 
+-                    key,
+-                    ograpi.OGR_Fld_GetType(cogr_fielddefn))
+-                continue
+-            val = fieldtypename
+-            if fieldtypename == 'float':
+-                fmt = ""
+-                width = ograpi.OGR_Fld_GetWidth(cogr_fielddefn)
+-                if width: # and width != 24:
+-                    fmt = ":%d" % width
+-                precision = ograpi.OGR_Fld_GetPrecision(cogr_fielddefn)
+-                if precision: # and precision != 15:
+-                    fmt += ".%d" % precision
+-                val = "float" + fmt
+-            elif fieldtypename == 'int':
+-                fmt = ""
+-                width = ograpi.OGR_Fld_GetWidth(cogr_fielddefn)
+-                if width: # and width != 11:
+-                    fmt = ":%d" % width
+-                val = fieldtypename + fmt
+-            elif fieldtypename == 'str':
+-                fmt = ""
+-                width = ograpi.OGR_Fld_GetWidth(cogr_fielddefn)
+-                if width: # and width != 80:
+-                    fmt = ":%d" % width
+-                val = fieldtypename + fmt
+-
+-            props.append((key, val))
+-
+-        cdef unsigned int geom_type = ograpi.OGR_FD_GetGeomType(
+-            cogr_featuredefn)
+-        return {
+-            'properties': OrderedDict(props), 
+-            'geometry': GEOMETRY_TYPES[geom_type]}
+-
+-    def get_crs(self):
+-        cdef char *proj_c = NULL
+-        cdef char *auth_key = NULL
+-        cdef char *auth_val = NULL
+-        cdef void *cogr_crs = NULL
+-        if self.cogr_layer == NULL:
+-            raise ValueError("Null layer")
+-        cogr_crs = ograpi.OGR_L_GetSpatialRef(self.cogr_layer)
+-        crs = {}
+-        if cogr_crs is not NULL:
+-            log.debug("Got coordinate system")
+-
+-            retval = ograpi.OSRAutoIdentifyEPSG(cogr_crs)
+-            if retval > 0:
+-                log.info("Failed to auto identify EPSG: %d", retval)
+-            
+-            auth_key = ograpi.OSRGetAuthorityName(cogr_crs, NULL)
+-            auth_val = ograpi.OSRGetAuthorityCode(cogr_crs, NULL)
+-
+-            if auth_key != NULL and auth_val != NULL:
+-                key_b = auth_key
+-                key = key_b.decode('utf-8')
+-                if key == 'EPSG':
+-                    val_b = auth_val
+-                    val = val_b.decode('utf-8')
+-                    crs['init'] = "epsg:" + val
+-            else:
+-                ograpi.OSRExportToProj4(cogr_crs, &proj_c)
+-                if proj_c == NULL:
+-                    raise ValueError("Null projection")
+-                proj_b = proj_c
+-                log.debug("Params: %s", proj_b)
+-                value = proj_b.decode()
+-                value = value.strip()
+-                for param in value.split():
+-                    kv = param.split("=")
+-                    if len(kv) == 2:
+-                        k, v = kv
+-                        try:
+-                            v = float(v)
+-                            if v % 1 == 0:
+-                                v = int(v)
+-                        except ValueError:
+-                            # Leave v as a string
+-                            pass
+-                    elif len(kv) == 1:
+-                        k, v = kv[0], True
+-                    else:
+-                        raise ValueError("Unexpected proj parameter %s" % param)
+-                    k = k.lstrip("+")
+-                    crs[k] = v
+-
+-            ograpi.CPLFree(proj_c)
+-        else:
+-            log.debug("Projection not found (cogr_crs was NULL)")
+-        return crs
+-
+-    def get_crs_wkt(self):
+-        cdef char *proj_c = NULL
+-        if self.cogr_layer == NULL:
+-            raise ValueError("Null layer")
+-        cogr_crs = ograpi.OGR_L_GetSpatialRef(self.cogr_layer)
+-        crs_wkt = ""
+-        if cogr_crs is not NULL:
+-            log.debug("Got coordinate system")
+-            ograpi.OSRExportToWkt(cogr_crs, &proj_c)
+-            if proj_c == NULL:
+-                raise ValueError("Null projection")
+-            proj_b = proj_c
+-            crs_wkt = proj_b.decode('utf-8')
+-            ograpi.CPLFree(proj_c)
+-        else:
+-            log.debug("Projection not found (cogr_crs was NULL)")
+-        return crs_wkt
+-
+-    def get_extent(self):
+-        if self.cogr_layer == NULL:
+-            raise ValueError("Null layer")
+-        cdef ograpi.OGREnvelope extent
+-        result = ograpi.OGR_L_GetExtent(self.cogr_layer, &extent, 1)
+-        return (extent.MinX, extent.MinY, extent.MaxX, extent.MaxY)
+-
+-    def has_feature(self, fid):
+-        """Provides access to feature data by FID.
+-
+-        Supports Collection.__contains__().
+-        """
+-        cdef void * cogr_feature
+-        fid = int(fid)
+-        cogr_feature = ograpi.OGR_L_GetFeature(self.cogr_layer, fid)
+-        if cogr_feature != NULL:
+-            _deleteOgrFeature(cogr_feature)
+-            return True
+-        else:
+-            return False
+-
+-    def get_feature(self, fid):
+-        """Provides access to feature data by FID.
+-
+-        Supports Collection.__contains__().
+-        """
+-        cdef void * cogr_feature
+-        fid = int(fid)
+-        cogr_feature = ograpi.OGR_L_GetFeature(self.cogr_layer, fid)
+-        if cogr_feature != NULL:
+-            _deleteOgrFeature(cogr_feature)
+-            return True
+-        else:
+-            return False
+-
+-
+-    def __getitem__(self, item):
+-        cdef void * cogr_feature
+-        if isinstance(item, slice):
+-            itr = Iterator(self.collection, item.start, item.stop, item.step)
+-            log.debug("Slice: %r", item)
+-            return list(itr)
+-        elif isinstance(item, int):
+-            index = item
+-            # from the back
+-            if index < 0:
+-                ftcount = ograpi.OGR_L_GetFeatureCount(self.cogr_layer, 0)
+-                if ftcount == -1:
+-                    raise IndexError(
+-                        "collection's dataset does not support negative indexes")
+-                index += ftcount
+-            cogr_feature = ograpi.OGR_L_GetFeature(self.cogr_layer, index)
+-            if cogr_feature == NULL:
+-                return None
+-            feature = FeatureBuilder().build(
+-                cogr_feature,
+-                bbox=False,
+-                encoding=self.get_internalencoding(),
+-                driver=self.collection.driver
+-            )
+-            _deleteOgrFeature(cogr_feature)
+-            return feature
+-
+-
+-    def isactive(self):
+-        if self.cogr_layer != NULL and self.cogr_ds != NULL:
+-            return 1
+-        else:
+-            return 0
+-
+-
+-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_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
+-        path = collection.path
+-
+-        if collection.mode == 'a':
+-            if os.path.exists(path):
+-                try:
+-                    path_b = path.encode('utf-8')
+-                except UnicodeError:
+-                    path_b = path
+-                path_c = path_b
+-                with cpl_errs:
+-                    self.cogr_ds = ograpi.OGROpen(path_c, 1, NULL)
+-                if self.cogr_ds == NULL:
+-                    raise RuntimeError("Failed to open %s" % path)
+-                cogr_driver = ograpi.OGR_DS_GetDriver(self.cogr_ds)
+-                if cogr_driver == NULL:
+-                    raise ValueError("Null driver")
+-
+-                if isinstance(collection.name, string_types):
+-                    name_b = collection.name.encode()
+-                    name_c = name_b
+-                    self.cogr_layer = ograpi.OGR_DS_GetLayerByName(
+-                                        self.cogr_ds, name_c)
+-                elif isinstance(collection.name, int):
+-                    self.cogr_layer = ograpi.OGR_DS_GetLayer(
+-                                        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)
+-
+-            userencoding = self.collection.encoding
+-            self._fileencoding = (userencoding or (
+-                ograpi.OGR_L_TestCapability(self.cogr_layer, OLC_STRINGSASUTF8) and
+-                OGR_DETECTED_ENCODING) or (
+-                self.get_driver() == "ESRI Shapefile" and
+-                'ISO-8859-1') or locale.getpreferredencoding()).upper()
+-
+-        elif collection.mode == 'w':
+-            try:
+-                path_b = path.encode('utf-8')
+-            except UnicodeError:
+-                path_b = path
+-            path_c = path_b
+-            driver_b = collection.driver.encode()
+-            driver_c = driver_b
+-
+-            cogr_driver = ograpi.OGRGetDriverByName(driver_c)
+-            if cogr_driver == NULL:
+-                raise ValueError("Null driver")
+-
+-            if not os.path.exists(path):
+-                cogr_ds = ograpi.OGR_Dr_CreateDataSource(
+-                    cogr_driver, path_c, NULL)
+-
+-            else:
+-                with cpl_errs:
+-                    cogr_ds = ograpi.OGROpen(path_c, 1, NULL)
+-                if cogr_ds == NULL:
+-                    cogr_ds = ograpi.OGR_Dr_CreateDataSource(
+-                        cogr_driver, path_c, NULL)
+-
+-                elif collection.name is None:
+-                    ograpi.OGR_DS_Destroy(cogr_ds)
+-                    cogr_ds == NULL
+-                    log.debug("Deleted pre-existing data at %s", path)
+-                    
+-                    cogr_ds = ograpi.OGR_Dr_CreateDataSource(
+-                        cogr_driver, path_c, NULL)
+-
+-                else:
+-                    pass
+-
+-            if cogr_ds == NULL:
+-                raise RuntimeError("Failed to open %s" % path)
+-            else:
+-                self.cogr_ds = cogr_ds
+-
+-            # Set the spatial reference system from the crs given to the
+-            # collection constructor. We by-pass the crs_wkt and crs
+-            # properties because they aren't accessible until the layer
+-            # is constructed (later).
+-            col_crs = collection._crs_wkt or collection._crs
+-            if col_crs:
+-                cogr_srs = ograpi.OSRNewSpatialReference(NULL)
+-                if cogr_srs == NULL:
+-                    raise ValueError("NULL spatial reference")
+-                # 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
+-                    ograpi.OSRSetFromUserInput(cogr_srs, proj_c)
+-                elif isinstance(col_crs, dict):
+-                    # EPSG is a special case.
+-                    init = col_crs.get('init')
+-                    if init:
+-                        log.debug("Init: %s", init)
+-                        auth, val = init.split(':')
+-                        if auth.upper() == 'EPSG':
+-                            log.debug("Setting EPSG: %s", val)
+-                            ograpi.OSRImportFromEPSG(cogr_srs, int(val))
+-                    else:
+-                        params = []
+-                        col_crs['wktext'] = True
+-                        for k, v in col_crs.items():
+-                            if v is True or (k in ('no_defs', 'wktext') and v):
+-                                params.append("+%s" % k)
+-                            else:
+-                                params.append("+%s=%s" % (k, v))
+-                        proj = " ".join(params)
+-                        log.debug("PROJ.4 to be imported: %r", proj)
+-                        proj_b = proj.encode('utf-8')
+-                        proj_c = proj_b
+-                        ograpi.OSRImportFromProj4(cogr_srs, proj_c)
+-                else:
+-                    raise ValueError("Invalid CRS")
+-
+-                # Fixup, export to WKT, and set the GDAL dataset's projection.
+-                ograpi.OSRFixup(cogr_srs)
+-
+-            # 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()
+-            userencoding = collection.encoding
+-            self._fileencoding = (userencoding or (
+-                collection.driver == "ESRI Shapefile" and
+-                'ISO-8859-1') or sysencoding).upper()
+-
+-            fileencoding = self.get_fileencoding()
+-            if fileencoding:
+-                fileencoding_b = fileencoding.encode()
+-                fileencoding_c = fileencoding_b
+-                options = ograpi.CSLSetNameValue(options, "ENCODING", fileencoding_c)
+-
+-            # Does the layer exist already? If so, we delete it.
+-            layer_count = ograpi.OGR_DS_GetLayerCount(self.cogr_ds)
+-            layer_names = []
+-            for i in range(layer_count):
+-                cogr_layer = ograpi.OGR_DS_GetLayer(cogr_ds, i)
+-                name_c = ograpi.OGR_L_GetName(cogr_layer)
+-                name_b = name_c
+-                layer_names.append(name_b.decode('utf-8'))
+-
+-            idx = -1
+-            if isinstance(collection.name, string_types):
+-                if collection.name in layer_names:
+-                    idx = layer_names.index(collection.name)
+-            elif isinstance(collection.name, int):
+-                if collection.name >= 0 and collection.name < layer_count:
+-                    idx = collection.name
+-            if idx >= 0:
+-                log.debug("Deleted pre-existing layer at %s", collection.name)
+-                ograpi.OGR_DS_DeleteLayer(self.cogr_ds, idx)
+-            
+-            # Create the named layer in the datasource.
+-            name_b = collection.name.encode('utf-8')
+-            name_c = name_b
+-            self.cogr_layer = ograpi.OGR_DS_CreateLayer(
+-                self.cogr_ds, 
+-                name_c,
+-                cogr_srs,
+-                <unsigned int>[k for k,v in GEOMETRY_TYPES.items() if 
+-                    v == collection.schema.get('geometry', 'Unknown')][0],
+-                options
+-                )
+-
+-            if cogr_srs != NULL:
+-                ograpi.OSRDestroySpatialReference(cogr_srs)
+-            if options != NULL:
+-                ograpi.CSLDestroy(options)
+-
+-            if self.cogr_layer == NULL:
+-                raise ValueError("Null layer")
+-            log.debug("Created layer")
+-            
+-            # Next, make a layer definition from the given schema properties,
+-            # which are an ordered dict since Fiona 1.0.1.
+-            for key, value in collection.schema['properties'].items():
+-                log.debug("Creating field: %s %s", key, value)
+-
+-                # Convert 'long' to 'int'. See
+-                # https://github.com/Toblerity/Fiona/issues/101.
+-                if value == 'long':
+-                    value = 'int'
+-                
+-                # Is there a field width/precision?
+-                width = precision = None
+-                if ':' in value:
+-                    value, fmt = value.split(':')
+-                    if '.' in fmt:
+-                        width, precision = map(int, fmt.split('.'))
+-                    else:
+-                        width = int(fmt)
+-                
+-                encoding = self.get_internalencoding()
+-                key_bytes = key.encode(encoding)
+-                cogr_fielddefn = ograpi.OGR_Fld_Create(
+-                    key_bytes, 
+-                    FIELD_TYPES.index(value) )
+-                if cogr_fielddefn == NULL:
+-                    raise ValueError("Null field definition")
+-                if width:
+-                    ograpi.OGR_Fld_SetWidth(cogr_fielddefn, width)
+-                if precision:
+-                    ograpi.OGR_Fld_SetPrecision(cogr_fielddefn, precision)
+-                ograpi.OGR_L_CreateField(self.cogr_layer, cogr_fielddefn, 1)
+-                ograpi.OGR_Fld_Destroy(cogr_fielddefn)
+-            log.debug("Created fields")
+-
+-        # Mapping of the Python collection schema to the munged 
+-        # OGR schema.
+-        ogr_schema = self.get_schema()
+-        self._schema_mapping = dict(zip(
+-            collection.schema['properties'].keys(), 
+-            ogr_schema['properties'].keys() ))
+-
+-        log.debug("Writing started")
+-
+-    def writerecs(self, records, collection):
+-        """Writes buffered records to OGR."""
+-        cdef void *cogr_driver
+-        cdef void *cogr_feature
+-
+-        cdef void *cogr_layer = self.cogr_layer
+-        if cogr_layer == NULL:
+-            raise ValueError("Null layer")
+-    
+-        schema_geom_type = collection.schema['geometry']
+-        cogr_driver = ograpi.OGR_DS_GetDriver(self.cogr_ds)
+-        if ograpi.OGR_Dr_GetName(cogr_driver) == b"GeoJSON":
+-            def validate_geometry_type(rec):
+-                return True
+-        elif ograpi.OGR_Dr_GetName(cogr_driver) == b"ESRI Shapefile" \
+-                and "Point" not in collection.schema['geometry']:
+-            schema_geom_type = collection.schema['geometry'].lstrip(
+-                "3D ").lstrip("Multi")
+-            def validate_geometry_type(rec):
+-                return rec['geometry'] is None or \
+-                rec['geometry']['type'].lstrip(
+-                    "3D ").lstrip("Multi") == schema_geom_type
+-        else:
+-            schema_geom_type = collection.schema['geometry'].lstrip("3D ")
+-            def validate_geometry_type(rec):
+-                return rec['geometry'] is None or \
+-                       rec['geometry']['type'].lstrip("3D ") == schema_geom_type
+-
+-        schema_props_keys = set(collection.schema['properties'].keys())
+-        for record in records:
+-            log.debug("Creating feature in layer: %s" % record)
+-            # Validate against collection's schema.
+-            if set(record['properties'].keys()) != schema_props_keys:
+-                raise ValueError(
+-                    "Record does not match collection schema: %r != %r" % (
+-                        record['properties'].keys(), 
+-                        list(schema_props_keys) ))
+-            if not validate_geometry_type(record):
+-                raise ValueError(
+-                    "Record's geometry type does not match "
+-                    "collection schema's geometry type: %r != %r" % (
+-                         record['geometry']['type'],
+-                         collection.schema['geometry'] ))
+-
+-            cogr_feature = OGRFeatureBuilder().build(record, collection)
+-            result = ograpi.OGR_L_CreateFeature(cogr_layer, cogr_feature)
+-            if result != OGRERR_NONE:
+-                raise RuntimeError("Failed to write record: %s" % record)
+-            _deleteOgrFeature(cogr_feature)
+-
+-    def sync(self, collection):
+-        """Syncs OGR to disk."""
+-        cdef void *cogr_ds = self.cogr_ds
+-        cdef void *cogr_layer = self.cogr_layer
+-        if cogr_ds == NULL:
+-            raise ValueError("Null data source")
+-        log.debug("Syncing OGR to disk")
+-        retval = ograpi.OGR_DS_SyncToDisk(cogr_ds)
+-        if retval != OGRERR_NONE:
+-            raise RuntimeError("Failed to sync to disk")
+-
+-
+-cdef class Iterator:
+-
+-    """Provides iterated access to feature data.
+-    """
+-
+-    # Reference to its Collection
+-    cdef collection
+-    cdef encoding
+-    cdef int next_index
+-    cdef stop
+-    cdef start
+-    cdef step
+-    cdef fastindex
+-    cdef stepsign
+-
+-    def __init__(self, collection, 
+-            start=None, stop=None, step=None, bbox=None, mask=None):
+-        if collection.session is None:
+-            raise ValueError("I/O operation on closed collection")
+-        self.collection = collection
+-        cdef Session session
+-        cdef void *cogr_geometry
+-        session = self.collection.session
+-        cdef void *cogr_layer = session.cogr_layer
+-        if cogr_layer == NULL:
+-            raise ValueError("Null layer")
+-        ograpi.OGR_L_ResetReading(cogr_layer)
+-        
+-        if bbox and mask:
+-            raise ValueError("mask and bbox can not be set together")
+-        
+-        if bbox:
+-            ograpi.OGR_L_SetSpatialFilterRect(
+-                cogr_layer, bbox[0], bbox[1], bbox[2], bbox[3])
+-        elif mask:
+-            cogr_geometry = OGRGeomBuilder().build(mask)
+-            ograpi.OGR_L_SetSpatialFilter(cogr_layer, cogr_geometry)
+-            ograpi.OGR_G_DestroyGeometry(cogr_geometry)
+-            
+-        else:
+-            ograpi.OGR_L_SetSpatialFilter(
+-                cogr_layer, NULL)
+-        self.encoding = session.get_internalencoding()
+-
+-        self.fastindex = ograpi.OGR_L_TestCapability(
+-            session.cogr_layer, OLC_FASTSETNEXTBYINDEX)
+-
+-        ftcount = ograpi.OGR_L_GetFeatureCount(session.cogr_layer, 0)
+-        if ftcount == -1 and ((start is not None and start < 0) or
+-                              (stop is not None and stop < 0)):
+-            raise IndexError(
+-                "collection's dataset does not support negative slice indexes")
+-
+-        if stop is not None and stop < 0:
+-            stop += ftcount
+-
+-        if start is None:
+-            start = 0
+-        if start is not None and start < 0:
+-            start += ftcount
+-
+-        # step size
+-        if step is None:
+-            step = 1
+-        if step == 0:
+-            raise ValueError("slice step cannot be zero")
+-        if step < 0 and not self.fastindex:
+-            warnings.warn("Layer does not support" \
+-                    "OLCFastSetNextByIndex, negative step size may" \
+-                    " be slow", RuntimeWarning)
+-        self.stepsign = int(math.copysign(1, step))
+-        self.stop = stop
+-        self.start = start
+-        self.step = step
+-
+-        self.next_index = start
+-        log.debug("Index: %d", self.next_index)
+-        ograpi.OGR_L_SetNextByIndex(session.cogr_layer, self.next_index)
+-
+-
+-    def __iter__(self):
+-        return self
+-
+-
+-    def _next(self):
+-        """Internal method to set read cursor to next item"""
+-
+-        cdef Session session
+-        session = self.collection.session
+-
+-        # Check if next_index is valid
+-        if self.next_index < 0:
+-            raise StopIteration
+-        
+-        if self.stepsign == 1:
+-            if self.next_index < self.start or (self.stop is not None and self.next_index >= self.stop):
+-                raise StopIteration
+-        else:
+-            if self.next_index > self.start or (self.stop is not None and self.next_index <= self.stop):
+-                raise StopIteration
+-
+-
+-        # Set read cursor to next_item position
+-        if self.step > 1 and self.fastindex:
+-            ograpi.OGR_L_SetNextByIndex(session.cogr_layer, self.next_index)
+-
+-        elif self.step > 1 and not self.fastindex and not self.next_index == self.start:
+-            for _ in range(self.step - 1):
+-                # TODO rbuffat add test -> OGR_L_GetNextFeature increments cursor by 1, therefore self.step - 1 as one increment was performed when feature is read
+-                cogr_feature = ograpi.OGR_L_GetNextFeature(session.cogr_layer)
+-                if cogr_feature == NULL:
+-                    raise StopIteration
+-        elif self.step > 1 and not self.fastindex and self.next_index == self.start:
+-            ograpi.OGR_L_SetNextByIndex(session.cogr_layer, self.next_index)
+-
+-        elif self.step == 0:
+-            # ograpi.OGR_L_GetNextFeature increments read cursor by one
+-            pass
+-        elif self.step < 0:
+-            ograpi.OGR_L_SetNextByIndex(session.cogr_layer, self.next_index)
+-            
+-        # set the next index
+-        self.next_index += self.step
+-
+-
+-    def __next__(self):
+-        cdef void * cogr_feature
+-        cdef Session session
+-        session = self.collection.session
+-
+-        #Update read cursor
+-        self._next()
+-
+-        # Get the next feature.
+-        cogr_feature = ograpi.OGR_L_GetNextFeature(session.cogr_layer)
+-        if cogr_feature == NULL:
+-            raise StopIteration
+-
+-        feature = FeatureBuilder().build(
+-            cogr_feature,
+-            bbox=False,
+-            encoding=self.encoding,
+-            driver=self.collection.driver
+-        )
+-        _deleteOgrFeature(cogr_feature)
+-        return feature
+-
+-
+-cdef class ItemsIterator(Iterator):
+-
+-    def __next__(self):
+-
+-        cdef long fid
+-        cdef void * cogr_feature
+-        cdef Session session
+-        session = self.collection.session
+-
+-        #Update read cursor
+-        self._next()
+-
+-        # Get the next feature.
+-        cogr_feature = ograpi.OGR_L_GetNextFeature(session.cogr_layer)
+-        if cogr_feature == NULL:
+-            raise StopIteration
+-
+-
+-        fid = ograpi.OGR_F_GetFID(cogr_feature)
+-        feature = FeatureBuilder().build(
+-            cogr_feature,
+-            bbox=False,
+-            encoding=self.encoding,
+-            driver=self.collection.driver
+-        )
+-        _deleteOgrFeature(cogr_feature)
+-
+-        return fid, feature
+-
+-
+-cdef class KeysIterator(Iterator):
+-
+-    def __next__(self):
+-        cdef long fid
+-        cdef void * cogr_feature
+-        cdef Session session
+-        session = self.collection.session
+-
+-        #Update read cursor
+-        self._next()
+-
+-        # Get the next feature.
+-        cogr_feature = ograpi.OGR_L_GetNextFeature(session.cogr_layer)
+-        if cogr_feature == NULL:
+-            raise StopIteration
+-
+-        fid = ograpi.OGR_F_GetFID(cogr_feature)
+-        _deleteOgrFeature(cogr_feature)
+-
+-        return fid
+-
+-
+-def _listlayers(path):
+-
+-    """Provides a list of the layers in an OGR data source.
+-    """
+-    
+-    cdef void *cogr_ds
+-    cdef void *cogr_layer
+-    cdef char *path_c
+-    cdef char *name_c
+-    
+-    # Open OGR data source.
+-    try:
+-        path_b = path.encode('utf-8')
+-    except UnicodeError:
+-        path_b = path
+-    path_c = path_b
+-    with cpl_errs:
+-        cogr_ds = ograpi.OGROpen(path_c, 0, NULL)
+-    if cogr_ds == NULL:
+-        raise ValueError("No data available at path '%s'" % path)
+-    
+-    # Loop over the layers to get their names.
+-    layer_count = ograpi.OGR_DS_GetLayerCount(cogr_ds)
+-    layer_names = []
+-    for i in range(layer_count):
+-        cogr_layer = ograpi.OGR_DS_GetLayer(cogr_ds, i)
+-        name_c = ograpi.OGR_L_GetName(cogr_layer)
+-        name_b = name_c
+-        layer_names.append(name_b.decode('utf-8'))
+-    
+-    # Close up data source.
+-    if cogr_ds is not NULL:
+-        ograpi.OGR_DS_Destroy(cogr_ds)
+-    cogr_ds = NULL
+-
+-    return layer_names
+-
+-def buffer_to_virtual_file(bytesbuf):
+-    """Maps a bytes buffer to a virtual file.
+-    """
+-    vsi_filename = os.path.join('/vsimem', uuid.uuid4().hex)
+-    vsi_cfilename = vsi_filename if not isinstance(vsi_filename, string_types) else vsi_filename.encode('utf-8')
+-
+-    vsi_handle = ograpi.VSIFileFromMemBuffer(vsi_cfilename, bytesbuf, len(bytesbuf), 0)
+-    if vsi_handle == NULL:
+-        raise OSError('failed to map buffer to file')
+-    if ograpi.VSIFCloseL(vsi_handle) != 0:
+-        raise OSError('failed to close mapped file handle')
+-
+-    return vsi_filename
+-
+-def remove_virtual_file(vsi_filename):
+-    vsi_cfilename = vsi_filename if not isinstance(vsi_filename, string_types) else vsi_filename.encode('utf-8')
+-    return ograpi.VSIUnlink(vsi_cfilename)
+-
+-
+--- /dev/null
++++ b/fiona/ogrext1.pyx
+@@ -0,0 +1,1229 @@
++# These are extension functions and classes using the OGR C API.
++
++import datetime
++import json
++import locale
++import logging
++import os
++import sys
++import warnings
++import math
++import uuid
++
++from six import integer_types, string_types, text_type
++
++from fiona cimport ograpi
++from fiona._geometry cimport GeomBuilder, OGRGeomBuilder
++from fiona._err import cpl_errs
++from fiona._geometry import GEOMETRY_TYPES
++from fiona.errors import DriverError, SchemaError, CRSError, FionaValueError
++from fiona.odict import OrderedDict
++from fiona.rfc3339 import parse_date, parse_datetime, parse_time
++from fiona.rfc3339 import FionaDateType, FionaDateTimeType, FionaTimeType
++
++
++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.
++#
++# Lists are currently unsupported in this version, but might be done as
++# arrays in a future version.
++
++FIELD_TYPES = [
++    'int',          # OFTInteger, Simple 32bit integer
++    None,           # OFTIntegerList, List of 32bit integers
++    'float',        # OFTReal, Double Precision floating point
++    None,           # OFTRealList, List of doubles
++    'str',          # OFTString, String of ASCII chars
++    None,           # OFTStringList, Array of strings
++    None,           # OFTWideString, deprecated
++    None,           # OFTWideStringList, deprecated
++    None,           # OFTBinary, Raw Binary data
++    'date',         # OFTDate, Date
++    'time',         # OFTTime, Time
++    'datetime',     # OFTDateTime, Date and Time
++    ]
++
++# Mapping of Fiona field type names to Python types.
++FIELD_TYPES_MAP = {
++    'int':      int,
++    'float':    float,
++    'str':      text_type,
++    'date':     FionaDateType,
++    'time':     FionaTimeType,
++    'datetime': FionaDateTimeType
++   }
++
++# 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"
++
++# OGR integer error types.
++
++OGRERR_NONE = 0
++OGRERR_NOT_ENOUGH_DATA = 1    # not enough data to deserialize */
++OGRERR_NOT_ENOUGH_MEMORY = 2
++OGRERR_UNSUPPORTED_GEOMETRY_TYPE = 3
++OGRERR_UNSUPPORTED_OPERATION = 4
++OGRERR_CORRUPT_DATA = 5
++OGRERR_FAILURE = 6
++OGRERR_UNSUPPORTED_SRS = 7
++OGRERR_INVALID_HANDLE = 8
++
++
++def _explode(coords):
++    """Explode a GeoJSON geometry's coordinates object and yield
++    coordinate tuples. As long as the input is conforming, the type of
++    the geometry doesn't matter."""
++    for e in coords:
++        if isinstance(e, (float, int)):
++            yield coords
++            break
++        else:
++            for f in _explode(e):
++                yield f
++
++
++def _bounds(geometry):
++    """Bounding box of a GeoJSON geometry"""
++    try:
++        xyz = tuple(zip(*list(_explode(geometry['coordinates']))))
++        return min(xyz[0]), min(xyz[1]), max(xyz[0]), max(xyz[1])
++    except (KeyError, TypeError):
++        return None
++
++def calc_gdal_version_num(maj, min, rev):
++    """Calculates the internal gdal version number based on major, minor and revision"""
++    return int(maj * 1000000 + min * 10000 + rev*100)
++
++def get_gdal_version_num():
++    """Return current internal version number of gdal"""
++    return int(ograpi.GDALVersionInfo("VERSION_NUM"))
++
++def get_gdal_release_name():
++    """Return release name of gdal"""
++    return ograpi.GDALVersionInfo("RELEASE_NAME")
++
++
++# Feature extension classes and functions follow.
++
++cdef class FeatureBuilder:
++    """Build Fiona features from OGR feature pointers.
++
++    No OGR objects are allocated by this function and the feature
++    argument is not destroyed.
++    """
++
++    cdef build(self, void *feature, encoding='utf-8', bbox=False, driver=None):
++        # The only method anyone ever needs to call
++        cdef void *fdefn
++        cdef int i
++        cdef int y = 0
++        cdef int m = 0
++        cdef int d = 0
++        cdef int hh = 0
++        cdef int mm = 0
++        cdef int ss = 0
++        cdef int tz = 0
++        cdef int retval
++        cdef char *key_c
++        props = OrderedDict()
++        for i in range(ograpi.OGR_F_GetFieldCount(feature)):
++            fdefn = ograpi.OGR_F_GetFieldDefnRef(feature, i)
++            if fdefn == NULL:
++                raise ValueError("Null feature definition")
++            key_c = ograpi.OGR_Fld_GetNameRef(fdefn)
++            if key_c == NULL:
++                raise ValueError("Null field name reference")
++            key_b = key_c
++            key = key_b.decode(encoding)
++            fieldtypename = FIELD_TYPES[ograpi.OGR_Fld_GetType(fdefn)]
++            if not fieldtypename:
++                log.warn(
++                    "Skipping field %s: invalid type %s", 
++                    key,
++                    ograpi.OGR_Fld_GetType(fdefn))
++                continue
++
++            # TODO: other types
++            fieldtype = FIELD_TYPES_MAP[fieldtypename]
++            if not ograpi.OGR_F_IsFieldSet(feature, i):
++                props[key] = None
++            elif fieldtype is int:
++                props[key] = ograpi.OGR_F_GetFieldAsInteger(feature, i)
++            elif fieldtype is float:
++                props[key] = ograpi.OGR_F_GetFieldAsDouble(feature, i)
++
++            elif fieldtype is text_type:
++                try:
++                    val = ograpi.OGR_F_GetFieldAsString(feature, i)
++                    val = val.decode(encoding)
++                except UnicodeDecodeError:
++                    log.warn(
++                        "Failed to decode %s using %s codec", val, encoding)
++
++                # Does the text contain a JSON object? Let's check.
++                # Let's check as cheaply as we can.
++                if driver == 'GeoJSON' and val.startswith('{'):
++                    try:
++                        val = json.loads(val)
++                    except ValueError as err:
++                        log.warn(str(err))
++
++                # Now add to the properties object.
++                props[key] = val
++
++            elif fieldtype in (FionaDateType, FionaTimeType, FionaDateTimeType):
++                retval = ograpi.OGR_F_GetFieldAsDateTime(
++                    feature, i, &y, &m, &d, &hh, &mm, &ss, &tz)
++                if fieldtype is FionaDateType:
++                    props[key] = datetime.date(y, m, d).isoformat()
++                elif fieldtype is FionaTimeType:
++                    props[key] = datetime.time(hh, mm, ss).isoformat()
++                else:
++                    props[key] = datetime.datetime(
++                        y, m, d, hh, mm, ss).isoformat()
++            else:
++                log.debug("%s: None, fieldtype: %r, %r" % (key, fieldtype, fieldtype in string_types))
++                props[key] = None
++
++        cdef void *cogr_geometry = ograpi.OGR_F_GetGeometryRef(feature)
++        if cogr_geometry is not NULL:
++            geom = GeomBuilder().build(cogr_geometry)
++        else:
++            geom = None
++        return {
++            'type': 'Feature',
++            'id': str(ograpi.OGR_F_GetFID(feature)),
++            'geometry': geom,
++            'properties': props }
++
++
++cdef class OGRFeatureBuilder:
++    
++    """Builds an OGR Feature from a Fiona feature mapping.
++
++    Allocates one OGR Feature which should be destroyed by the caller.
++    Borrows a layer definition from the collection.
++    """
++    
++    cdef void * build(self, feature, collection) except NULL:
++        cdef void *cogr_geometry = NULL
++        cdef char *string_c
++        cdef WritingSession session
++        session = collection.session
++        cdef void *cogr_layer = session.cogr_layer
++        if cogr_layer == NULL:
++            raise ValueError("Null layer")
++        cdef void *cogr_featuredefn = ograpi.OGR_L_GetLayerDefn(cogr_layer)
++        if cogr_featuredefn == NULL:
++            raise ValueError("Null feature definition")
++        cdef void *cogr_feature = ograpi.OGR_F_Create(cogr_featuredefn)
++        if cogr_feature == NULL:
++            raise ValueError("Null feature")
++        
++        if feature['geometry'] is not None:
++            cogr_geometry = OGRGeomBuilder().build(
++                                feature['geometry'])
++        ograpi.OGR_F_SetGeometryDirectly(cogr_feature, cogr_geometry)
++        
++        # OGR_F_SetFieldString takes UTF-8 encoded strings ('bytes' in 
++        # Python 3).
++        encoding = session.get_internalencoding()
++
++        for key, value in feature['properties'].items():
++            log.debug(
++                "Looking up %s in %s", key, repr(session._schema_mapping))
++            ogr_key = session._schema_mapping[key]
++            schema_type = collection.schema['properties'][key]
++            try:
++                key_bytes = ogr_key.encode(encoding)
++            except UnicodeDecodeError:
++                log.warn("Failed to encode %s using %s codec", key, encoding)
++                key_bytes = ogr_key
++            key_c = key_bytes
++            i = ograpi.OGR_F_GetFieldIndex(cogr_feature, key_c)
++            if i < 0:
++                continue
++
++            # Special case: serialize dicts to assist OGR.
++            if isinstance(value, dict):
++                value = json.dumps(value)
++
++            # Continue over the standard OGR types.
++            if isinstance(value, integer_types):
++                ograpi.OGR_F_SetFieldInteger(cogr_feature, i, value)
++            elif isinstance(value, float):
++                ograpi.OGR_F_SetFieldDouble(cogr_feature, i, value)
++            elif (isinstance(value, string_types) 
++            and schema_type in ['date', 'time', 'datetime']):
++                if schema_type == 'date':
++                    y, m, d, hh, mm, ss, ff = parse_date(value)
++                elif schema_type == 'time':
++                    y, m, d, hh, mm, ss, ff = parse_time(value)
++                else:
++                    y, m, d, hh, mm, ss, ff = parse_datetime(value)
++                ograpi.OGR_F_SetFieldDateTime(
++                    cogr_feature, i, y, m, d, hh, mm, ss, 0)
++            elif (isinstance(value, datetime.date)
++            and schema_type == 'date'):
++                y, m, d = value.year, value.month, value.day
++                ograpi.OGR_F_SetFieldDateTime(
++                    cogr_feature, i, y, m, d, 0, 0, 0, 0)
++            elif (isinstance(value, datetime.datetime)
++            and schema_type == 'datetime'):
++                y, m, d = value.year, value.month, value.day
++                hh, mm, ss = value.hour, value.minute, value.second
++                ograpi.OGR_F_SetFieldDateTime(
++                    cogr_feature, i, y, m, d, hh, mm, ss, 0)
++            elif (isinstance(value, datetime.time)
++            and schema_type == 'time'):
++                hh, mm, ss = value.hour, value.minute, value.second
++                ograpi.OGR_F_SetFieldDateTime(
++                    cogr_feature, i, 0, 0, 0, hh, mm, ss, 0)
++            elif isinstance(value, string_types):
++                try:
++                    value_bytes = value.encode(encoding)
++                except UnicodeDecodeError:
++                    log.warn(
++                        "Failed to encode %s using %s codec", value, encoding)
++                    value_bytes = value
++                string_c = value_bytes
++                ograpi.OGR_F_SetFieldString(cogr_feature, i, string_c)
++            elif value is None:
++                pass # keep field unset/null
++            else:
++                raise ValueError("Invalid field type %s" % type(value))
++            log.debug("Set field %s: %s" % (key, value))
++        return cogr_feature
++
++
++cdef _deleteOgrFeature(void *cogr_feature):
++    """Delete an OGR feature"""
++    if cogr_feature is not NULL:
++        ograpi.OGR_F_Destroy(cogr_feature)
++    cogr_feature = NULL
++
++
++def featureRT(feature, collection):
++    # For testing purposes only, leaks the JSON data
++    cdef void *cogr_feature = OGRFeatureBuilder().build(feature, collection)
++    cdef void *cogr_geometry = ograpi.OGR_F_GetGeometryRef(cogr_feature)
++    if cogr_geometry == NULL:
++        raise ValueError("Null geometry")
++    log.debug("Geometry: %s" % ograpi.OGR_G_ExportToJson(cogr_geometry))
++    encoding = collection.encoding or 'utf-8'
++    result = FeatureBuilder().build(
++        cogr_feature,
++        bbox=False,
++        encoding=encoding,
++        driver=collection.driver
++    )
++    _deleteOgrFeature(cogr_feature)
++    return result
++
++
++# Collection-related extension classes and functions
++
++cdef class Session:
++    
++    cdef void *cogr_ds
++    cdef void *cogr_layer
++    cdef object _fileencoding
++    cdef object _encoding
++    cdef object collection
++
++    def __cinit__(self):
++        self.cogr_ds = NULL
++        self.cogr_layer = NULL
++        self._fileencoding = None
++        self._encoding = None
++
++    def __dealloc__(self):
++        self.stop()
++
++    def start(self, collection):
++        cdef const char *path_c = NULL
++        cdef const char *name_c = NULL
++        cdef void *drv = NULL
++        cdef void *ds = NULL
++
++        if collection.path == '-':
++            path = '/vsistdin/'
++        else:
++            path = collection.path
++        try:
++            path_b = path.encode('utf-8')
++        except UnicodeDecodeError:
++            # Presume already a UTF-8 encoded string
++            path_b = path
++        path_c = path_b
++        
++        with cpl_errs:
++            drivers = []
++            if collection._driver:
++                drivers = [collection._driver]
++            elif collection.enabled_drivers:
++                drivers = collection.enabled_drivers
++            if drivers:
++                for name in drivers:
++                    name_b = name.encode()
++                    name_c = name_b
++                    log.debug("Trying driver: %s", name)
++                    drv = ograpi.OGRGetDriverByName(name_c)
++                    if drv != NULL:
++                        ds = ograpi.OGR_Dr_Open(drv, path_c, 0)
++                    if ds != NULL:
++                        self.cogr_ds = ds
++                        collection._driver = name
++                        break
++            else:
++                self.cogr_ds = ograpi.OGROpen(path_c, 0, NULL)
++
++        if self.cogr_ds == NULL:
++            raise FionaValueError(
++                "No dataset found at path '%s' using drivers: %s" % (
++                    collection.path,
++                    drivers or '*'))
++        
++        if isinstance(collection.name, string_types):
++            name_b = collection.name.encode('utf-8')
++            name_c = name_b
++            self.cogr_layer = ograpi.OGR_DS_GetLayerByName(
++                                self.cogr_ds, name_c)
++        elif isinstance(collection.name, int):
++            self.cogr_layer = ograpi.OGR_DS_GetLayer(
++                                self.cogr_ds, collection.name)
++            name_c = ograpi.OGR_L_GetName(self.cogr_layer)
++            name_b = name_c
++            collection.name = name_b.decode('utf-8')
++
++        if self.cogr_layer == NULL:
++            raise ValueError("Null layer: " + repr(collection.name))
++        
++        self.collection = collection
++        
++        userencoding = self.collection.encoding
++        if userencoding:
++            ograpi.CPLSetThreadLocalConfigOption('SHAPE_ENCODING', '')
++            self._fileencoding = userencoding.upper()
++        else:
++            self._fileencoding = (
++                ograpi.OGR_L_TestCapability(
++                    self.cogr_layer, OLC_STRINGSASUTF8) and
++                'utf-8') or (
++                self.get_driver() == "ESRI Shapefile" and
++                'ISO-8859-1') or locale.getpreferredencoding().upper()
++
++    def stop(self):
++        self.cogr_layer = NULL
++        if self.cogr_ds is not NULL:
++            ograpi.OGR_DS_Destroy(self.cogr_ds)
++        self.cogr_ds = NULL
++
++    def get_fileencoding(self):
++        return self._fileencoding
++
++    def get_internalencoding(self):
++        if not self._encoding:
++            fileencoding = self.get_fileencoding()
++            self._encoding = (
++                ograpi.OGR_L_TestCapability(
++                    self.cogr_layer, OLC_STRINGSASUTF8) and
++                'utf-8') or fileencoding
++        return self._encoding
++
++    def get_length(self):
++        if self.cogr_layer == NULL:
++            raise ValueError("Null layer")
++        return ograpi.OGR_L_GetFeatureCount(self.cogr_layer, 0)
++
++    def get_driver(self):
++        cdef void *cogr_driver = ograpi.OGR_DS_GetDriver(self.cogr_ds)
++        if cogr_driver == NULL:
++            raise ValueError("Null driver")
++        cdef char *name = ograpi.OGR_Dr_GetName(cogr_driver)
++        driver_name = name
++        return driver_name.decode()
++ 
++    def get_schema(self):
++        cdef int i
++        cdef int n
++        cdef void *cogr_featuredefn
++        cdef void *cogr_fielddefn
++        cdef char *key_c
++        props = []
++        
++        if self.cogr_layer == NULL:
++            raise ValueError("Null layer")
++
++        cogr_featuredefn = ograpi.OGR_L_GetLayerDefn(self.cogr_layer)
++        if cogr_featuredefn == NULL:
++            raise ValueError("Null feature definition")
++        n = ograpi.OGR_FD_GetFieldCount(cogr_featuredefn)
++        for i from 0 <= i < n:
++            cogr_fielddefn = ograpi.OGR_FD_GetFieldDefn(cogr_featuredefn, i)
++            if cogr_fielddefn == NULL:
++                raise ValueError("Null field definition")
++            key_c = ograpi.OGR_Fld_GetNameRef(cogr_fielddefn)
++            key_b = key_c
++            if not bool(key_b):
++                raise ValueError("Invalid field name ref: %s" % key)
++            key = key_b.decode(self.get_internalencoding())
++            fieldtypename = FIELD_TYPES[ograpi.OGR_Fld_GetType(cogr_fielddefn)]
++            if not fieldtypename:
++                log.warn(
++                    "Skipping field %s: invalid type %s", 
++                    key,
++                    ograpi.OGR_Fld_GetType(cogr_fielddefn))
++                continue
++            val = fieldtypename
++            if fieldtypename == 'float':
++                fmt = ""
++                width = ograpi.OGR_Fld_GetWidth(cogr_fielddefn)
++                if width: # and width != 24:
++                    fmt = ":%d" % width
++                precision = ograpi.OGR_Fld_GetPrecision(cogr_fielddefn)
++                if precision: # and precision != 15:
++                    fmt += ".%d" % precision
++                val = "float" + fmt
++            elif fieldtypename == 'int':
++                fmt = ""
++                width = ograpi.OGR_Fld_GetWidth(cogr_fielddefn)
++                if width: # and width != 11:
++                    fmt = ":%d" % width
++                val = fieldtypename + fmt
++            elif fieldtypename == 'str':
++                fmt = ""
++                width = ograpi.OGR_Fld_GetWidth(cogr_fielddefn)
++                if width: # and width != 80:
++                    fmt = ":%d" % width
++                val = fieldtypename + fmt
++
++            props.append((key, val))
++
++        cdef unsigned int geom_type = ograpi.OGR_FD_GetGeomType(
++            cogr_featuredefn)
++        return {
++            'properties': OrderedDict(props), 
++            'geometry': GEOMETRY_TYPES[geom_type]}
++
++    def get_crs(self):
++        cdef char *proj_c = NULL
++        cdef char *auth_key = NULL
++        cdef char *auth_val = NULL
++        cdef void *cogr_crs = NULL
++        if self.cogr_layer == NULL:
++            raise ValueError("Null layer")
++        cogr_crs = ograpi.OGR_L_GetSpatialRef(self.cogr_layer)
++        crs = {}
++        if cogr_crs is not NULL:
++            log.debug("Got coordinate system")
++
++            retval = ograpi.OSRAutoIdentifyEPSG(cogr_crs)
++            if retval > 0:
++                log.info("Failed to auto identify EPSG: %d", retval)
++            
++            auth_key = ograpi.OSRGetAuthorityName(cogr_crs, NULL)
++            auth_val = ograpi.OSRGetAuthorityCode(cogr_crs, NULL)
++
++            if auth_key != NULL and auth_val != NULL:
++                key_b = auth_key
++                key = key_b.decode('utf-8')
++                if key == 'EPSG':
++                    val_b = auth_val
++                    val = val_b.decode('utf-8')
++                    crs['init'] = "epsg:" + val
++            else:
++                ograpi.OSRExportToProj4(cogr_crs, &proj_c)
++                if proj_c == NULL:
++                    raise ValueError("Null projection")
++                proj_b = proj_c
++                log.debug("Params: %s", proj_b)
++                value = proj_b.decode()
++                value = value.strip()
++                for param in value.split():
++                    kv = param.split("=")
++                    if len(kv) == 2:
++                        k, v = kv
++                        try:
++                            v = float(v)
++                            if v % 1 == 0:
++                                v = int(v)
++                        except ValueError:
++                            # Leave v as a string
++                            pass
++                    elif len(kv) == 1:
++                        k, v = kv[0], True
++                    else:
++                        raise ValueError("Unexpected proj parameter %s" % param)
++                    k = k.lstrip("+")
++                    crs[k] = v
++
++            ograpi.CPLFree(proj_c)
++        else:
++            log.debug("Projection not found (cogr_crs was NULL)")
++        return crs
++
++    def get_crs_wkt(self):
++        cdef char *proj_c = NULL
++        if self.cogr_layer == NULL:
++            raise ValueError("Null layer")
++        cogr_crs = ograpi.OGR_L_GetSpatialRef(self.cogr_layer)
++        crs_wkt = ""
++        if cogr_crs is not NULL:
++            log.debug("Got coordinate system")
++            ograpi.OSRExportToWkt(cogr_crs, &proj_c)
++            if proj_c == NULL:
++                raise ValueError("Null projection")
++            proj_b = proj_c
++            crs_wkt = proj_b.decode('utf-8')
++            ograpi.CPLFree(proj_c)
++        else:
++            log.debug("Projection not found (cogr_crs was NULL)")
++        return crs_wkt
++
++    def get_extent(self):
++        if self.cogr_layer == NULL:
++            raise ValueError("Null layer")
++        cdef ograpi.OGREnvelope extent
++        result = ograpi.OGR_L_GetExtent(self.cogr_layer, &extent, 1)
++        return (extent.MinX, extent.MinY, extent.MaxX, extent.MaxY)
++
++    def has_feature(self, fid):
++        """Provides access to feature data by FID.
++
++        Supports Collection.__contains__().
++        """
++        cdef void * cogr_feature
++        fid = int(fid)
++        cogr_feature = ograpi.OGR_L_GetFeature(self.cogr_layer, fid)
++        if cogr_feature != NULL:
++            _deleteOgrFeature(cogr_feature)
++            return True
++        else:
++            return False
++
++    def get_feature(self, fid):
++        """Provides access to feature data by FID.
++
++        Supports Collection.__contains__().
++        """
++        cdef void * cogr_feature
++        fid = int(fid)
++        cogr_feature = ograpi.OGR_L_GetFeature(self.cogr_layer, fid)
++        if cogr_feature != NULL:
++            _deleteOgrFeature(cogr_feature)
++            return True
++        else:
++            return False
++
++
++    def __getitem__(self, item):
++        cdef void * cogr_feature
++        if isinstance(item, slice):
++            itr = Iterator(self.collection, item.start, item.stop, item.step)
++            log.debug("Slice: %r", item)
++            return list(itr)
++        elif isinstance(item, int):
++            index = item
++            # from the back
++            if index < 0:
++                ftcount = ograpi.OGR_L_GetFeatureCount(self.cogr_layer, 0)
++                if ftcount == -1:
++                    raise IndexError(
++                        "collection's dataset does not support negative indexes")
++                index += ftcount
++            cogr_feature = ograpi.OGR_L_GetFeature(self.cogr_layer, index)
++            if cogr_feature == NULL:
++                return None
++            feature = FeatureBuilder().build(
++                cogr_feature,
++                bbox=False,
++                encoding=self.get_internalencoding(),
++                driver=self.collection.driver
++            )
++            _deleteOgrFeature(cogr_feature)
++            return feature
++
++
++    def isactive(self):
++        if self.cogr_layer != NULL and self.cogr_ds != NULL:
++            return 1
++        else:
++            return 0
++
++
++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_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
++        path = collection.path
++
++        if collection.mode == 'a':
++            if os.path.exists(path):
++                try:
++                    path_b = path.encode('utf-8')
++                except UnicodeDecodeError:
++                    path_b = path
++                path_c = path_b
++                with cpl_errs:
++                    self.cogr_ds = ograpi.OGROpen(path_c, 1, NULL)
++                if self.cogr_ds == NULL:
++                    raise RuntimeError("Failed to open %s" % path)
++                cogr_driver = ograpi.OGR_DS_GetDriver(self.cogr_ds)
++                if cogr_driver == NULL:
++                    raise ValueError("Null driver")
++
++                if isinstance(collection.name, string_types):
++                    name_b = collection.name.encode()
++                    name_c = name_b
++                    self.cogr_layer = ograpi.OGR_DS_GetLayerByName(
++                                        self.cogr_ds, name_c)
++                elif isinstance(collection.name, int):
++                    self.cogr_layer = ograpi.OGR_DS_GetLayer(
++                                        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)
++
++            userencoding = self.collection.encoding
++            self._fileencoding = (userencoding or (
++                ograpi.OGR_L_TestCapability(self.cogr_layer, OLC_STRINGSASUTF8) and
++                'utf-8') or (
++                self.get_driver() == "ESRI Shapefile" and
++                'ISO-8859-1') or locale.getpreferredencoding()).upper()
++
++        elif collection.mode == 'w':
++            try:
++                path_b = path.encode('utf-8')
++            except UnicodeDecodeError:
++                path_b = path
++            path_c = path_b
++            driver_b = collection.driver.encode()
++            driver_c = driver_b
++
++            cogr_driver = ograpi.OGRGetDriverByName(driver_c)
++            if cogr_driver == NULL:
++                raise ValueError("Null driver")
++
++            if not os.path.exists(path):
++                cogr_ds = ograpi.OGR_Dr_CreateDataSource(
++                    cogr_driver, path_c, NULL)
++
++            else:
++                with cpl_errs:
++                    cogr_ds = ograpi.OGROpen(path_c, 1, NULL)
++                if cogr_ds == NULL:
++                    cogr_ds = ograpi.OGR_Dr_CreateDataSource(
++                        cogr_driver, path_c, NULL)
++
++                elif collection.name is None:
++                    ograpi.OGR_DS_Destroy(cogr_ds)
++                    cogr_ds == NULL
++                    log.debug("Deleted pre-existing data at %s", path)
++                    
++                    cogr_ds = ograpi.OGR_Dr_CreateDataSource(
++                        cogr_driver, path_c, NULL)
++
++                else:
++                    pass
++
++            if cogr_ds == NULL:
++                raise RuntimeError("Failed to open %s" % path)
++            else:
++                self.cogr_ds = cogr_ds
++
++            # Set the spatial reference system from the crs given to the
++            # collection constructor. We by-pass the crs_wkt and crs
++            # properties because they aren't accessible until the layer
++            # is constructed (later).
++            col_crs = collection._crs_wkt or collection._crs
++            if col_crs:
++                cogr_srs = ograpi.OSRNewSpatialReference(NULL)
++                if cogr_srs == NULL:
++                    raise ValueError("NULL spatial reference")
++                # 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
++                    ograpi.OSRSetFromUserInput(cogr_srs, proj_c)
++                elif isinstance(col_crs, dict):
++                    # EPSG is a special case.
++                    init = col_crs.get('init')
++                    if init:
++                        log.debug("Init: %s", init)
++                        auth, val = init.split(':')
++                        if auth.upper() == 'EPSG':
++                            log.debug("Setting EPSG: %s", val)
++                            ograpi.OSRImportFromEPSG(cogr_srs, int(val))
++                    else:
++                        params = []
++                        col_crs['wktext'] = True
++                        for k, v in col_crs.items():
++                            if v is True or (k in ('no_defs', 'wktext') and v):
++                                params.append("+%s" % k)
++                            else:
++                                params.append("+%s=%s" % (k, v))
++                        proj = " ".join(params)
++                        log.debug("PROJ.4 to be imported: %r", proj)
++                        proj_b = proj.encode('utf-8')
++                        proj_c = proj_b
++                        ograpi.OSRImportFromProj4(cogr_srs, proj_c)
++                else:
++                    raise ValueError("Invalid CRS")
++
++                # Fixup, export to WKT, and set the GDAL dataset's projection.
++                ograpi.OSRFixup(cogr_srs)
++
++            # 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()
++            userencoding = collection.encoding
++            self._fileencoding = (userencoding or (
++                collection.driver == "ESRI Shapefile" and
++                'ISO-8859-1') or sysencoding).upper()
++
++            fileencoding = self.get_fileencoding()
++            if fileencoding:
++                fileencoding_b = fileencoding.encode()
++                fileencoding_c = fileencoding_b
++                options = ograpi.CSLSetNameValue(options, "ENCODING", fileencoding_c)
++
++            # Does the layer exist already? If so, we delete it.
++            layer_count = ograpi.OGR_DS_GetLayerCount(self.cogr_ds)
++            layer_names = []
++            for i in range(layer_count):
++                cogr_layer = ograpi.OGR_DS_GetLayer(cogr_ds, i)
++                name_c = ograpi.OGR_L_GetName(cogr_layer)
++                name_b = name_c
++                layer_names.append(name_b.decode('utf-8'))
++
++            idx = -1
++            if isinstance(collection.name, string_types):
++                if collection.name in layer_names:
++                    idx = layer_names.index(collection.name)
++            elif isinstance(collection.name, int):
++                if collection.name >= 0 and collection.name < layer_count:
++                    idx = collection.name
++            if idx >= 0:
++                log.debug("Deleted pre-existing layer at %s", collection.name)
++                ograpi.OGR_DS_DeleteLayer(self.cogr_ds, idx)
++            
++            # Create the named layer in the datasource.
++            name_b = collection.name.encode('utf-8')
++            name_c = name_b
++            self.cogr_layer = ograpi.OGR_DS_CreateLayer(
++                self.cogr_ds, 
++                name_c,
++                cogr_srs,
++                <unsigned int>[k for k,v in GEOMETRY_TYPES.items() if 
++                    v == collection.schema.get('geometry', 'Unknown')][0],
++                options
++                )
++
++            if cogr_srs != NULL:
++                ograpi.OSRDestroySpatialReference(cogr_srs)
++            if options != NULL:
++                ograpi.CSLDestroy(options)
++
++            if self.cogr_layer == NULL:
++                raise ValueError("Null layer")
++            log.debug("Created layer")
++            
++            # Next, make a layer definition from the given schema properties,
++            # which are an ordered dict since Fiona 1.0.1.
++            for key, value in collection.schema['properties'].items():
++                log.debug("Creating field: %s %s", key, value)
++
++                # Convert 'long' to 'int'. See
++                # https://github.com/Toblerity/Fiona/issues/101.
++                if value == 'long':
++                    value = 'int'
++                
++                # Is there a field width/precision?
++                width = precision = None
++                if ':' in value:
++                    value, fmt = value.split(':')
++                    if '.' in fmt:
++                        width, precision = map(int, fmt.split('.'))
++                    else:
++                        width = int(fmt)
++                
++                encoding = self.get_internalencoding()
++                key_bytes = key.encode(encoding)
++                cogr_fielddefn = ograpi.OGR_Fld_Create(
++                    key_bytes, 
++                    FIELD_TYPES.index(value) )
++                if cogr_fielddefn == NULL:
++                    raise ValueError("Null field definition")
++                if width:
++                    ograpi.OGR_Fld_SetWidth(cogr_fielddefn, width)
++                if precision:
++                    ograpi.OGR_Fld_SetPrecision(cogr_fielddefn, precision)
++                ograpi.OGR_L_CreateField(self.cogr_layer, cogr_fielddefn, 1)
++                ograpi.OGR_Fld_Destroy(cogr_fielddefn)
++            log.debug("Created fields")
++
++        # Mapping of the Python collection schema to the munged 
++        # OGR schema.
++        ogr_schema = self.get_schema()
++        self._schema_mapping = dict(zip(
++            collection.schema['properties'].keys(), 
++            ogr_schema['properties'].keys() ))
++
++        log.debug("Writing started")
++
++    def writerecs(self, records, collection):
++        """Writes buffered records to OGR."""
++        cdef void *cogr_driver
++        cdef void *cogr_feature
++
++        cdef void *cogr_layer = self.cogr_layer
++        if cogr_layer == NULL:
++            raise ValueError("Null layer")
++    
++        schema_geom_type = collection.schema['geometry']
++        cogr_driver = ograpi.OGR_DS_GetDriver(self.cogr_ds)
++        if ograpi.OGR_Dr_GetName(cogr_driver) == b"GeoJSON":
++            def validate_geometry_type(rec):
++                return True
++        elif ograpi.OGR_Dr_GetName(cogr_driver) == b"ESRI Shapefile" \
++                and "Point" not in collection.schema['geometry']:
++            schema_geom_type = collection.schema['geometry'].lstrip(
++                "3D ").lstrip("Multi")
++            def validate_geometry_type(rec):
++                return rec['geometry'] is None or \
++                rec['geometry']['type'].lstrip(
++                    "3D ").lstrip("Multi") == schema_geom_type
++        else:
++            schema_geom_type = collection.schema['geometry'].lstrip("3D ")
++            def validate_geometry_type(rec):
++                return rec['geometry'] is None or \
++                       rec['geometry']['type'].lstrip("3D ") == schema_geom_type
++
++        schema_props_keys = set(collection.schema['properties'].keys())
++        for record in records:
++            log.debug("Creating feature in layer: %s" % record)
++            # Validate against collection's schema.
++            if set(record['properties'].keys()) != schema_props_keys:
++                raise ValueError(
++                    "Record does not match collection schema: %r != %r" % (
++                        record['properties'].keys(), 
++                        list(schema_props_keys) ))
++            if not validate_geometry_type(record):
++                raise ValueError(
++                    "Record's geometry type does not match "
++                    "collection schema's geometry type: %r != %r" % (
++                         record['geometry']['type'],
++                         collection.schema['geometry'] ))
++
++            cogr_feature = OGRFeatureBuilder().build(record, collection)
++            result = ograpi.OGR_L_CreateFeature(cogr_layer, cogr_feature)
++            if result != OGRERR_NONE:
++                raise RuntimeError("Failed to write record: %s" % record)
++            _deleteOgrFeature(cogr_feature)
++
++    def sync(self, collection):
++        """Syncs OGR to disk."""
++        cdef void *cogr_ds = self.cogr_ds
++        cdef void *cogr_layer = self.cogr_layer
++        if cogr_ds == NULL:
++            raise ValueError("Null data source")
++        log.debug("Syncing OGR to disk")
++        retval = ograpi.OGR_DS_SyncToDisk(cogr_ds)
++        if retval != OGRERR_NONE:
++            raise RuntimeError("Failed to sync to disk")
++
++
++cdef class Iterator:
++
++    """Provides iterated access to feature data.
++    """
++
++    # Reference to its Collection
++    cdef collection
++    cdef encoding
++    cdef int next_index
++    cdef stop
++    cdef start
++    cdef step
++    cdef fastindex
++    cdef stepsign
++
++    def __init__(self, collection, 
++            start=None, stop=None, step=None, bbox=None, mask=None):
++        if collection.session is None:
++            raise ValueError("I/O operation on closed collection")
++        self.collection = collection
++        cdef Session session
++        cdef void *cogr_geometry
++        session = self.collection.session
++        cdef void *cogr_layer = session.cogr_layer
++        if cogr_layer == NULL:
++            raise ValueError("Null layer")
++        ograpi.OGR_L_ResetReading(cogr_layer)
++        
++        if bbox and mask:
++            raise ValueError("mask and bbox can not be set together")
++        
++        if bbox:
++            ograpi.OGR_L_SetSpatialFilterRect(
++                cogr_layer, bbox[0], bbox[1], bbox[2], bbox[3])
++        elif mask:
++            cogr_geometry = OGRGeomBuilder().build(mask)
++            ograpi.OGR_L_SetSpatialFilter(cogr_layer, cogr_geometry)
++            ograpi.OGR_G_DestroyGeometry(cogr_geometry)
++            
++        else:
++            ograpi.OGR_L_SetSpatialFilter(
++                cogr_layer, NULL)
++        self.encoding = session.get_internalencoding()
++
++        self.fastindex = ograpi.OGR_L_TestCapability(
++            session.cogr_layer, OLC_FASTSETNEXTBYINDEX)
++
++        ftcount = ograpi.OGR_L_GetFeatureCount(session.cogr_layer, 0)
++        if ftcount == -1 and ((start is not None and start < 0) or
++                              (stop is not None and stop < 0)):
++            raise IndexError(
++                "collection's dataset does not support negative slice indexes")
++
++        if stop is not None and stop < 0:
++            stop += ftcount
++
++        if start is None:
++            start = 0
++        if start is not None and start < 0:
++            start += ftcount
++
++        # step size
++        if step is None:
++            step = 1
++        if step == 0:
++            raise ValueError("slice step cannot be zero")
++        if step < 0 and not self.fastindex:
++            warnings.warn("Layer does not support" \
++                    "OLCFastSetNextByIndex, negative step size may" \
++                    " be slow", RuntimeWarning)
++        self.stepsign = int(math.copysign(1, step))
++        self.stop = stop
++        self.start = start
++        self.step = step
++
++        self.next_index = start
++        log.debug("Index: %d", self.next_index)
++        ograpi.OGR_L_SetNextByIndex(session.cogr_layer, self.next_index)
++
++
++    def __iter__(self):
++        return self
++
++
++    def _next(self):
++        """Internal method to set read cursor to next item"""
++
++        cdef Session session
++        session = self.collection.session
++
++        # Check if next_index is valid
++        if self.next_index < 0:
++            raise StopIteration
++        
++        if self.stepsign == 1:
++            if self.next_index < self.start or (self.stop is not None and self.next_index >= self.stop):
++                raise StopIteration
++        else:
++            if self.next_index > self.start or (self.stop is not None and self.next_index <= self.stop):
++                raise StopIteration
++
++
++        # Set read cursor to next_item position
++        if self.step > 1 and self.fastindex:
++            ograpi.OGR_L_SetNextByIndex(session.cogr_layer, self.next_index)
++
++        elif self.step > 1 and not self.fastindex and not self.next_index == self.start:
++            for _ in range(self.step - 1):
++                # TODO rbuffat add test -> OGR_L_GetNextFeature increments cursor by 1, therefore self.step - 1 as one increment was performed when feature is read
++                cogr_feature = ograpi.OGR_L_GetNextFeature(session.cogr_layer)
++                if cogr_feature == NULL:
++                    raise StopIteration
++        elif self.step > 1 and not self.fastindex and self.next_index == self.start:
++            ograpi.OGR_L_SetNextByIndex(session.cogr_layer, self.next_index)
++
++        elif self.step == 0:
++            # ograpi.OGR_L_GetNextFeature increments read cursor by one
++            pass
++        elif self.step < 0:
++            ograpi.OGR_L_SetNextByIndex(session.cogr_layer, self.next_index)
++            
++        # set the next index
++        self.next_index += self.step
++
++
++    def __next__(self):
++        cdef void * cogr_feature
++        cdef Session session
++        session = self.collection.session
++
++        #Update read cursor
++        self._next()
++
++        # Get the next feature.
++        cogr_feature = ograpi.OGR_L_GetNextFeature(session.cogr_layer)
++        if cogr_feature == NULL:
++            raise StopIteration
++
++        feature = FeatureBuilder().build(
++            cogr_feature,
++            bbox=False,
++            encoding=self.encoding,
++            driver=self.collection.driver
++        )
++        _deleteOgrFeature(cogr_feature)
++        return feature
++
++
++cdef class ItemsIterator(Iterator):
++
++    def __next__(self):
++
++        cdef long fid
++        cdef void * cogr_feature
++        cdef Session session
++        session = self.collection.session
++
++        #Update read cursor
++        self._next()
++
++        # Get the next feature.
++        cogr_feature = ograpi.OGR_L_GetNextFeature(session.cogr_layer)
++        if cogr_feature == NULL:
++            raise StopIteration
++
++
++        fid = ograpi.OGR_F_GetFID(cogr_feature)
++        feature = FeatureBuilder().build(
++            cogr_feature,
++            bbox=False,
++            encoding=self.encoding,
++            driver=self.collection.driver
++        )
++        _deleteOgrFeature(cogr_feature)
++
++        return fid, feature
++
++
++cdef class KeysIterator(Iterator):
++
++    def __next__(self):
++        cdef long fid
++        cdef void * cogr_feature
++        cdef Session session
++        session = self.collection.session
++
++        #Update read cursor
++        self._next()
++
++        # Get the next feature.
++        cogr_feature = ograpi.OGR_L_GetNextFeature(session.cogr_layer)
++        if cogr_feature == NULL:
++            raise StopIteration
++
++        fid = ograpi.OGR_F_GetFID(cogr_feature)
++        _deleteOgrFeature(cogr_feature)
++
++        return fid
++
++
++def _listlayers(path):
++
++    """Provides a list of the layers in an OGR data source.
++    """
++    
++    cdef void *cogr_ds
++    cdef void *cogr_layer
++    cdef char *path_c
++    cdef char *name_c
++    
++    # Open OGR data source.
++    try:
++        path_b = path.encode('utf-8')
++    except UnicodeDecodeError:
++        path_b = path
++    path_c = path_b
++    with cpl_errs:
++        cogr_ds = ograpi.OGROpen(path_c, 0, NULL)
++    if cogr_ds == NULL:
++        raise ValueError("No data available at path '%s'" % path)
++    
++    # Loop over the layers to get their names.
++    layer_count = ograpi.OGR_DS_GetLayerCount(cogr_ds)
++    layer_names = []
++    for i in range(layer_count):
++        cogr_layer = ograpi.OGR_DS_GetLayer(cogr_ds, i)
++        name_c = ograpi.OGR_L_GetName(cogr_layer)
++        name_b = name_c
++        layer_names.append(name_b.decode('utf-8'))
++    
++    # Close up data source.
++    if cogr_ds is not NULL:
++        ograpi.OGR_DS_Destroy(cogr_ds)
++    cogr_ds = NULL
++
++    return layer_names
++
++def buffer_to_virtual_file(bytesbuf):
++    """Maps a bytes buffer to a virtual file.
++    """
++    vsi_filename = os.path.join('/vsimem', uuid.uuid4().hex)
++    vsi_cfilename = vsi_filename if not isinstance(vsi_filename, string_types) else vsi_filename.encode('utf-8')
++
++    vsi_handle = ograpi.VSIFileFromMemBuffer(vsi_cfilename, bytesbuf, len(bytesbuf), 0)
++    if vsi_handle == NULL:
++        raise OSError('failed to map buffer to file')
++    if ograpi.VSIFCloseL(vsi_handle) != 0:
++        raise OSError('failed to close mapped file handle')
++
++    return vsi_filename
++
++def remove_virtual_file(vsi_filename):
++    vsi_cfilename = vsi_filename if not isinstance(vsi_filename, string_types) else vsi_filename.encode('utf-8')
++    return ograpi.VSIUnlink(vsi_cfilename)
++
++
+--- /dev/null
++++ b/fiona/ogrext2.pyx
+@@ -0,0 +1,1312 @@
++# These are extension functions and classes using the OGR C API.
++
++import datetime
++import json
++import locale
++import logging
++import os
++import sys
++import warnings
++import math
++import uuid
++
++if sys.version_info > (3,):
++    from builtins import int
++else:
++    from __builtin__ import int
++
++from six import integer_types, string_types, text_type
++
++from fiona cimport ograpi
++from fiona._geometry cimport GeomBuilder, OGRGeomBuilder
++from fiona._err import cpl_errs
++from fiona._geometry import GEOMETRY_TYPES
++from fiona.errors import DriverError, SchemaError, CRSError, FionaValueError
++from fiona.odict import OrderedDict
++from fiona.rfc3339 import parse_date, parse_datetime, parse_time
++from fiona.rfc3339 import FionaDateType, FionaDateTimeType, FionaTimeType
++
++from libc.stdlib cimport malloc, free
++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.
++#
++# Lists are currently unsupported in this version, but might be done as
++# arrays in a future version.
++
++FIELD_TYPES = [
++    'int',          # OFTInteger, Simple 32bit integer
++    None,           # OFTIntegerList, List of 32bit integers
++    'float',        # OFTReal, Double Precision floating point
++    None,           # OFTRealList, List of doubles
++    'str',          # OFTString, String of ASCII chars
++    None,           # OFTStringList, Array of strings
++    None,           # OFTWideString, deprecated
++    None,           # OFTWideStringList, deprecated
++    None,           # OFTBinary, Raw Binary data
++    'date',         # OFTDate, Date
++    'time',         # OFTTime, Time
++    'datetime',     # OFTDateTime, Date and Time
++    'int',          # OFTInteger64, Single 64bit integer
++    None,           # OFTInteger64List, List of 64bit integers
++    ]
++
++# Mapping of Fiona field type names to Python types.
++FIELD_TYPES_MAP = {
++    'int':      int,
++    'float':    float,
++    'str':      text_type,
++    'date':     FionaDateType,
++    'time':     FionaTimeType,
++    'datetime': FionaDateTimeType
++   }
++
++# 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"
++
++# OGR integer error types.
++
++OGRERR_NONE = 0
++OGRERR_NOT_ENOUGH_DATA = 1    # not enough data to deserialize */
++OGRERR_NOT_ENOUGH_MEMORY = 2
++OGRERR_UNSUPPORTED_GEOMETRY_TYPE = 3
++OGRERR_UNSUPPORTED_OPERATION = 4
++OGRERR_CORRUPT_DATA = 5
++OGRERR_FAILURE = 6
++OGRERR_UNSUPPORTED_SRS = 7
++OGRERR_INVALID_HANDLE = 8
++
++
++cdef char ** string_list(list_str):
++    """
++    Function by Stackoverflow User falsetru
++    https://stackoverflow.com/questions/17511309/fast-string-array-cython
++    """
++    cdef char* s
++    cdef char **ret = <char **>malloc(len(list_str) * sizeof(char *))
++    for i in range(len(list_str)):
++        s = list_str[i]
++        ret[i] = s
++    ret[i + 1] = NULL
++    return ret
++
++def _explode(coords):
++    """Explode a GeoJSON geometry's coordinates object and yield
++    coordinate tuples. As long as the input is conforming, the type of
++    the geometry doesn't matter."""
++    for e in coords:
++        if isinstance(e, (float, int)):
++            yield coords
++            break
++        else:
++            for f in _explode(e):
++                yield f
++
++
++def _bounds(geometry):
++    """Bounding box of a GeoJSON geometry"""
++    try:
++        xyz = tuple(zip(*list(_explode(geometry['coordinates']))))
++        return min(xyz[0]), min(xyz[1]), max(xyz[0]), max(xyz[1])
++    except (KeyError, TypeError):
++        return None
++
++def calc_gdal_version_num(maj, min, rev):
++    """Calculates the internal gdal version number based on major, minor and revision"""
++    return int(maj * 1000000 + min * 10000 + rev*100)
++
++def get_gdal_version_num():
++    """Return current internal version number of gdal"""
++    return int(ograpi.GDALVersionInfo("VERSION_NUM"))
++
++def get_gdal_release_name():
++    """Return release name of gdal"""
++    return ograpi.GDALVersionInfo("RELEASE_NAME")
++
++
++# Feature extension classes and functions follow.
++
++cdef class FeatureBuilder:
++    """Build Fiona features from OGR feature pointers.
++
++    No OGR objects are allocated by this function and the feature
++    argument is not destroyed.
++    """
++
++    cdef build(self, void *feature, encoding='utf-8', bbox=False, driver=None):
++        # The only method anyone ever needs to call
++        cdef void *fdefn
++        cdef int i
++        cdef int y = 0
++        cdef int m = 0
++        cdef int d = 0
++        cdef int hh = 0
++        cdef int mm = 0
++        cdef int ss = 0
++        cdef int tz = 0
++        cdef int retval
++        cdef char *key_c
++        props = OrderedDict()
++        for i in range(ograpi.OGR_F_GetFieldCount(feature)):
++            fdefn = ograpi.OGR_F_GetFieldDefnRef(feature, i)
++            if fdefn == NULL:
++                raise ValueError("Null feature definition")
++            key_c = ograpi.OGR_Fld_GetNameRef(fdefn)
++            if key_c == NULL:
++                raise ValueError("Null field name reference")
++            key_b = key_c
++            key = key_b.decode(encoding)
++            fieldtypename = FIELD_TYPES[ograpi.OGR_Fld_GetType(fdefn)]
++            if not fieldtypename:
++                log.warn(
++                    "Skipping field %s: invalid type %s", 
++                    key,
++                    ograpi.OGR_Fld_GetType(fdefn))
++                continue
++
++            # TODO: other types
++            fieldtype = FIELD_TYPES_MAP[fieldtypename]
++            if not ograpi.OGR_F_IsFieldSet(feature, i):
++                props[key] = None
++            elif fieldtype is int:
++                props[key] = ograpi.OGR_F_GetFieldAsInteger64(feature, i)
++            elif fieldtype is float:
++                props[key] = ograpi.OGR_F_GetFieldAsDouble(feature, i)
++
++            elif fieldtype is text_type:
++                try:
++                    val = ograpi.OGR_F_GetFieldAsString(feature, i)
++                    val = val.decode(encoding)
++                except UnicodeDecodeError:
++                    log.warn(
++                        "Failed to decode %s using %s codec", val, encoding)
++
++                # Does the text contain a JSON object? Let's check.
++                # Let's check as cheaply as we can.
++                if driver == 'GeoJSON' and val.startswith('{'):
++                    try:
++                        val = json.loads(val)
++                    except ValueError as err:
++                        log.warn(str(err))
++
++                # Now add to the properties object.
++                props[key] = val
++
++            elif fieldtype in (FionaDateType, FionaTimeType, FionaDateTimeType):
++                retval = ograpi.OGR_F_GetFieldAsDateTime(
++                    feature, i, &y, &m, &d, &hh, &mm, &ss, &tz)
++                if fieldtype is FionaDateType:
++                    props[key] = datetime.date(y, m, d).isoformat()
++                elif fieldtype is FionaTimeType:
++                    props[key] = datetime.time(hh, mm, ss).isoformat()
++                else:
++                    props[key] = datetime.datetime(
++                        y, m, d, hh, mm, ss).isoformat()
++            else:
++                log.debug("%s: None, fieldtype: %r, %r" % (key, fieldtype, fieldtype in string_types))
++                props[key] = None
++
++        cdef void *cogr_geometry = ograpi.OGR_F_GetGeometryRef(feature)
++        if cogr_geometry is not NULL:
++            geom = GeomBuilder().build(cogr_geometry)
++        else:
++            geom = None
++        return {
++            'type': 'Feature',
++            'id': str(ograpi.OGR_F_GetFID(feature)),
++            'geometry': geom,
++            'properties': props }
++
++
++cdef class OGRFeatureBuilder:
++    
++    """Builds an OGR Feature from a Fiona feature mapping.
++
++    Allocates one OGR Feature which should be destroyed by the caller.
++    Borrows a layer definition from the collection.
++    """
++    
++    cdef void * build(self, feature, collection) except NULL:
++        cdef void *cogr_geometry = NULL
++        cdef char *string_c
++        cdef WritingSession session
++        session = collection.session
++        cdef void *cogr_layer = session.cogr_layer
++        if cogr_layer == NULL:
++            raise ValueError("Null layer")
++        cdef void *cogr_featuredefn = ograpi.OGR_L_GetLayerDefn(cogr_layer)
++        if cogr_featuredefn == NULL:
++            raise ValueError("Null feature definition")
++        cdef void *cogr_feature = ograpi.OGR_F_Create(cogr_featuredefn)
++        if cogr_feature == NULL:
++            raise ValueError("Null feature")
++        
++        if feature['geometry'] is not None:
++            cogr_geometry = OGRGeomBuilder().build(
++                                feature['geometry'])
++        ograpi.OGR_F_SetGeometryDirectly(cogr_feature, cogr_geometry)
++        
++        # OGR_F_SetFieldString takes UTF-8 encoded strings ('bytes' in 
++        # Python 3).
++        encoding = session.get_internalencoding()
++
++        for key, value in feature['properties'].items():
++            log.debug(
++                "Looking up %s in %s", key, repr(session._schema_mapping))
++            ogr_key = session._schema_mapping[key]
++            schema_type = collection.schema['properties'][key]
++            try:
++                key_bytes = ogr_key.encode(encoding)
++            except UnicodeDecodeError:
++                log.warn("Failed to encode %s using %s codec", key, encoding)
++                key_bytes = ogr_key
++            key_c = key_bytes
++            i = ograpi.OGR_F_GetFieldIndex(cogr_feature, key_c)
++            if i < 0:
++                continue
++
++            # Special case: serialize dicts to assist OGR.
++            if isinstance(value, dict):
++                value = json.dumps(value)
++
++            # Continue over the standard OGR types.
++            if isinstance(value, integer_types):
++                ograpi.OGR_F_SetFieldInteger64(cogr_feature, i, value)
++            elif isinstance(value, float):
++                ograpi.OGR_F_SetFieldDouble(cogr_feature, i, value)
++            elif (isinstance(value, string_types) 
++            and schema_type in ['date', 'time', 'datetime']):
++                if schema_type == 'date':
++                    y, m, d, hh, mm, ss, ff = parse_date(value)
++                elif schema_type == 'time':
++                    y, m, d, hh, mm, ss, ff = parse_time(value)
++                else:
++                    y, m, d, hh, mm, ss, ff = parse_datetime(value)
++                ograpi.OGR_F_SetFieldDateTime(
++                    cogr_feature, i, y, m, d, hh, mm, ss, 0)
++            elif (isinstance(value, datetime.date)
++            and schema_type == 'date'):
++                y, m, d = value.year, value.month, value.day
++                ograpi.OGR_F_SetFieldDateTime(
++                    cogr_feature, i, y, m, d, 0, 0, 0, 0)
++            elif (isinstance(value, datetime.datetime)
++            and schema_type == 'datetime'):
++                y, m, d = value.year, value.month, value.day
++                hh, mm, ss = value.hour, value.minute, value.second
++                ograpi.OGR_F_SetFieldDateTime(
++                    cogr_feature, i, y, m, d, hh, mm, ss, 0)
++            elif (isinstance(value, datetime.time)
++            and schema_type == 'time'):
++                hh, mm, ss = value.hour, value.minute, value.second
++                ograpi.OGR_F_SetFieldDateTime(
++                    cogr_feature, i, 0, 0, 0, hh, mm, ss, 0)
++            elif isinstance(value, string_types):
++                try:
++                    value_bytes = value.encode(encoding)
++                except UnicodeDecodeError:
++                    log.warn(
++                        "Failed to encode %s using %s codec", value, encoding)
++                    value_bytes = value
++                string_c = value_bytes
++                ograpi.OGR_F_SetFieldString(cogr_feature, i, string_c)
++            elif value is None:
++                pass # keep field unset/null
++            else:
++                raise ValueError("Invalid field type %s" % type(value))
++            log.debug("Set field %s: %s" % (key, value))
++        return cogr_feature
++
++
++cdef _deleteOgrFeature(void *cogr_feature):
++    """Delete an OGR feature"""
++    if cogr_feature is not NULL:
++        ograpi.OGR_F_Destroy(cogr_feature)
++    cogr_feature = NULL
++
++
++def featureRT(feature, collection):
++    # For testing purposes only, leaks the JSON data
++    cdef void *cogr_feature = OGRFeatureBuilder().build(feature, collection)
++    cdef void *cogr_geometry = ograpi.OGR_F_GetGeometryRef(cogr_feature)
++    if cogr_geometry == NULL:
++        raise ValueError("Null geometry")
++    log.debug("Geometry: %s" % ograpi.OGR_G_ExportToJson(cogr_geometry))
++    encoding = collection.encoding or 'utf-8'
++    result = FeatureBuilder().build(
++        cogr_feature,
++        bbox=False,
++        encoding=encoding,
++        driver=collection.driver
++    )
++    _deleteOgrFeature(cogr_feature)
++    return result
++
++
++# Collection-related extension classes and functions
++
++cdef class Session:
++    
++    cdef void *cogr_ds
++    cdef void *cogr_layer
++    cdef object _fileencoding
++    cdef object _encoding
++    cdef object collection
++
++    def __cinit__(self):
++        self.cogr_ds = NULL
++        self.cogr_layer = NULL
++        self._fileencoding = None
++        self._encoding = None
++
++    def __dealloc__(self):
++        self.stop()
++
++    def start(self, collection):
++        cdef const char *path_c = NULL
++        cdef const char *name_c = NULL
++        cdef void *drv = NULL
++        cdef void *ds = NULL
++        cdef char ** drvs = NULL
++        if collection.path == '-':
++            path = '/vsistdin/'
++        else:
++            path = collection.path
++        try:
++            path_b = path.encode('utf-8')
++        except UnicodeDecodeError:
++            # Presume already a UTF-8 encoded string
++            path_b = path
++        path_c = path_b
++        
++        with cpl_errs:
++            drivers = []
++            if collection._driver:
++                drivers = [collection._driver]
++            elif collection.enabled_drivers:
++                drivers = collection.enabled_drivers
++            if drivers:
++                for name in drivers:
++                    name_b = name.encode()
++                    name_c = name_b
++                    log.debug("Trying driver: %s", name)
++                    drv = ograpi.GDALGetDriverByName(name_c)
++                    if drv != NULL:
++                        drvs = string_list([name_b])
++
++                        flags = ograpi.GDAL_OF_VECTOR | ograpi.GDAL_OF_READONLY
++                        log.debug("GDALOpenEx({}, {}, {})".format(path_c, flags, [name_b]))
++                        ds = ograpi.GDALOpenEx(path_c,
++                                               flags,
++                                               drvs,
++                                               NULL,
++                                               NULL)
++                    if ds != NULL:
++                        self.cogr_ds = ds
++                        collection._driver = name
++                        _driver = ograpi.GDALGetDatasetDriver(ds)
++                        drv_name = ograpi.GDALGetDriverShortName(_driver)
++                        log.debug("Driver: {} Success".format(drv_name))
++
++                        break
++            else:
++                self.cogr_ds = ograpi.GDALOpenEx(path_c,
++                                                 ograpi.GDAL_OF_VECTOR | ograpi.GDAL_OF_READONLY,
++                                                 NULL,
++                                                 NULL,
++                                                 NULL)
++
++        if self.cogr_ds == NULL:
++            raise FionaValueError(
++                "No dataset found at path '%s' using drivers: %s" % (
++                    collection.path,
++                    drivers or '*'))
++        
++        if isinstance(collection.name, string_types):
++            name_b = collection.name.encode('utf-8')
++            name_c = name_b
++            self.cogr_layer = ograpi.GDALDatasetGetLayerByName(
++                                self.cogr_ds, name_c)
++        elif isinstance(collection.name, int):
++            self.cogr_layer = ograpi.GDALDatasetGetLayer(
++                                self.cogr_ds, collection.name)
++            name_c = ograpi.OGR_L_GetName(self.cogr_layer)
++            name_b = name_c
++            collection.name = name_b.decode('utf-8')
++
++        if self.cogr_layer == NULL:
++            raise ValueError("Null layer: " + repr(collection.name))
++        
++        self.collection = collection
++        
++        userencoding = self.collection.encoding
++        if userencoding:
++            ograpi.CPLSetThreadLocalConfigOption('SHAPE_ENCODING', '')
++            self._fileencoding = userencoding.upper()
++        else:
++            self._fileencoding = (
++                ograpi.OGR_L_TestCapability(
++                    self.cogr_layer, OLC_STRINGSASUTF8) and
++                'utf-8') or (
++                self.get_driver() == "ESRI Shapefile" and
++                'ISO-8859-1') or locale.getpreferredencoding().upper()
++
++    def stop(self):
++        self.cogr_layer = NULL
++        if self.cogr_ds is not NULL:
++            ograpi.GDALClose(self.cogr_ds)
++        self.cogr_ds = NULL
++
++    def get_fileencoding(self):
++        return self._fileencoding
++
++    def get_internalencoding(self):
++        if not self._encoding:
++            fileencoding = self.get_fileencoding()
++            self._encoding = (
++                ograpi.OGR_L_TestCapability(
++                    self.cogr_layer, OLC_STRINGSASUTF8) and
++                'utf-8') or fileencoding
++        return self._encoding
++
++    def get_length(self):
++        if self.cogr_layer == NULL:
++            raise ValueError("Null layer")
++        return ograpi.OGR_L_GetFeatureCount(self.cogr_layer, 0)
++
++    def get_driver(self):
++        cdef void *cogr_driver = ograpi.GDALGetDatasetDriver(self.cogr_ds)
++        if cogr_driver == NULL:
++            raise ValueError("Null driver")
++        cdef char *name = ograpi.OGR_Dr_GetName(cogr_driver)
++        driver_name = name
++        return driver_name.decode()
++ 
++    def get_schema(self):
++        cdef int i
++        cdef int n
++        cdef void *cogr_featuredefn
++        cdef void *cogr_fielddefn
++        cdef char *key_c
++        props = []
++        
++        if self.cogr_layer == NULL:
++            raise ValueError("Null layer")
++
++        cogr_featuredefn = ograpi.OGR_L_GetLayerDefn(self.cogr_layer)
++        if cogr_featuredefn == NULL:
++            raise ValueError("Null feature definition")
++        n = ograpi.OGR_FD_GetFieldCount(cogr_featuredefn)
++        for i from 0 <= i < n:
++            cogr_fielddefn = ograpi.OGR_FD_GetFieldDefn(cogr_featuredefn, i)
++            if cogr_fielddefn == NULL:
++                raise ValueError("Null field definition")
++            key_c = ograpi.OGR_Fld_GetNameRef(cogr_fielddefn)
++            key_b = key_c
++            if not bool(key_b):
++                raise ValueError("Invalid field name ref: %s" % key)
++            key = key_b.decode(self.get_internalencoding())
++            fieldtypename = FIELD_TYPES[ograpi.OGR_Fld_GetType(cogr_fielddefn)]
++            if not fieldtypename:
++                log.warn(
++                    "Skipping field %s: invalid type %s", 
++                    key,
++                    ograpi.OGR_Fld_GetType(cogr_fielddefn))
++                continue
++            val = fieldtypename
++            if fieldtypename == 'float':
++                fmt = ""
++                width = ograpi.OGR_Fld_GetWidth(cogr_fielddefn)
++                if width: # and width != 24:
++                    fmt = ":%d" % width
++                precision = ograpi.OGR_Fld_GetPrecision(cogr_fielddefn)
++                if precision: # and precision != 15:
++                    fmt += ".%d" % precision
++                val = "float" + fmt
++            elif fieldtypename == 'int':
++                fmt = ""
++                width = ograpi.OGR_Fld_GetWidth(cogr_fielddefn)
++                if width: # and width != 11:
++                    fmt = ":%d" % width
++                val = fieldtypename + fmt
++            elif fieldtypename == 'str':
++                fmt = ""
++                width = ograpi.OGR_Fld_GetWidth(cogr_fielddefn)
++                if width: # and width != 80:
++                    fmt = ":%d" % width
++                val = fieldtypename + fmt
++
++            props.append((key, val))
++
++        cdef unsigned int geom_type = ograpi.OGR_FD_GetGeomType(
++            cogr_featuredefn)
++        return {
++            'properties': OrderedDict(props), 
++            'geometry': GEOMETRY_TYPES[geom_type]}
++
++    def get_crs(self):
++        cdef char *proj_c = NULL
++        cdef char *auth_key = NULL
++        cdef char *auth_val = NULL
++        cdef void *cogr_crs = NULL
++        if self.cogr_layer == NULL:
++            raise ValueError("Null layer")
++        cogr_crs = ograpi.OGR_L_GetSpatialRef(self.cogr_layer)
++        crs = {}
++        if cogr_crs is not NULL:
++            log.debug("Got coordinate system")
++
++            retval = ograpi.OSRAutoIdentifyEPSG(cogr_crs)
++            if retval > 0:
++                log.info("Failed to auto identify EPSG: %d", retval)
++            
++            auth_key = ograpi.OSRGetAuthorityName(cogr_crs, NULL)
++            auth_val = ograpi.OSRGetAuthorityCode(cogr_crs, NULL)
++
++            if auth_key != NULL and auth_val != NULL:
++                key_b = auth_key
++                key = key_b.decode('utf-8')
++                if key == 'EPSG':
++                    val_b = auth_val
++                    val = val_b.decode('utf-8')
++                    crs['init'] = "epsg:" + val
++            else:
++                ograpi.OSRExportToProj4(cogr_crs, &proj_c)
++                if proj_c == NULL:
++                    raise ValueError("Null projection")
++                proj_b = proj_c
++                log.debug("Params: %s", proj_b)
++                value = proj_b.decode()
++                value = value.strip()
++                for param in value.split():
++                    kv = param.split("=")
++                    if len(kv) == 2:
++                        k, v = kv
++                        try:
++                            v = float(v)
++                            if v % 1 == 0:
++                                v = int(v)
++                        except ValueError:
++                            # Leave v as a string
++                            pass
++                    elif len(kv) == 1:
++                        k, v = kv[0], True
++                    else:
++                        raise ValueError("Unexpected proj parameter %s" % param)
++                    k = k.lstrip("+")
++                    crs[k] = v
++
++            ograpi.CPLFree(proj_c)
++        else:
++            log.debug("Projection not found (cogr_crs was NULL)")
++        return crs
++
++    def get_crs_wkt(self):
++        cdef char *proj_c = NULL
++        if self.cogr_layer == NULL:
++            raise ValueError("Null layer")
++        cogr_crs = ograpi.OGR_L_GetSpatialRef(self.cogr_layer)
++        crs_wkt = ""
++        if cogr_crs is not NULL:
++            log.debug("Got coordinate system")
++            ograpi.OSRExportToWkt(cogr_crs, &proj_c)
++            if proj_c == NULL:
++                raise ValueError("Null projection")
++            proj_b = proj_c
++            crs_wkt = proj_b.decode('utf-8')
++            ograpi.CPLFree(proj_c)
++        else:
++            log.debug("Projection not found (cogr_crs was NULL)")        
++        return crs_wkt
++
++    def get_extent(self):
++        if self.cogr_layer == NULL:
++            raise ValueError("Null layer")
++        cdef ograpi.OGREnvelope extent
++        result = ograpi.OGR_L_GetExtent(self.cogr_layer, &extent, 1)
++        return (extent.MinX, extent.MinY, extent.MaxX, extent.MaxY)
++
++    def has_feature(self, fid):
++        """Provides access to feature data by FID.
++
++        Supports Collection.__contains__().
++        """
++        cdef void * cogr_feature
++        fid = int(fid)
++        cogr_feature = ograpi.OGR_L_GetFeature(self.cogr_layer, fid)
++        if cogr_feature != NULL:
++            _deleteOgrFeature(cogr_feature)
++            return True
++        else:
++            return False
++
++    def get_feature(self, fid):
++        """Provides access to feature data by FID.
++
++        Supports Collection.__contains__().
++        """
++        cdef void * cogr_feature
++        fid = int(fid)
++        cogr_feature = ograpi.OGR_L_GetFeature(self.cogr_layer, fid)
++        if cogr_feature != NULL:
++            _deleteOgrFeature(cogr_feature)
++            return True
++        else:
++            return False
++
++
++    def __getitem__(self, item):
++        cdef void * cogr_feature
++        if isinstance(item, slice):
++            itr = Iterator(self.collection, item.start, item.stop, item.step)
++            log.debug("Slice: %r", item)
++            return list(itr)
++        elif isinstance(item, int):
++            index = item
++            # from the back
++            if index < 0:
++                ftcount = ograpi.OGR_L_GetFeatureCount(self.cogr_layer, 0)
++                if ftcount == -1:
++                    raise IndexError(
++                        "collection's dataset does not support negative indexes")
++                index += ftcount
++            cogr_feature = ograpi.OGR_L_GetFeature(self.cogr_layer, index)
++            if cogr_feature == NULL:
++                return None
++            feature = FeatureBuilder().build(
++                cogr_feature,
++                bbox=False,
++                encoding=self.get_internalencoding(),
++                driver=self.collection.driver
++            )
++            _deleteOgrFeature(cogr_feature)
++            return feature
++
++
++    def isactive(self):
++        if self.cogr_layer != NULL and self.cogr_ds != NULL:
++            return 1
++        else:
++            return 0
++
++
++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_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
++        path = collection.path
++
++        if collection.mode == 'a':
++            if os.path.exists(path):
++                try:
++                    path_b = path.encode('utf-8')
++                except UnicodeDecodeError:
++                    path_b = path
++                path_c = path_b
++                with cpl_errs:
++                    self.cogr_ds = ograpi.GDALOpenEx(path_c,
++                                                 ograpi.GDAL_OF_VECTOR | ograpi.GDAL_OF_UPDATE,
++                                                 NULL,
++                                                 NULL,
++                                                 NULL)
++#                     self.cogr_ds = ograpi.OGROpen(path_c, 1, NULL)
++                if self.cogr_ds == NULL:
++                    raise RuntimeError("Failed to open %s" % path)
++                cogr_driver = ograpi.GDALGetDatasetDriver(self.cogr_ds)
++                if cogr_driver == NULL:
++                    raise ValueError("Null driver")
++
++                if isinstance(collection.name, string_types):
++                    name_b = collection.name.encode()
++                    name_c = name_b
++                    self.cogr_layer = ograpi.GDALDatasetGetLayerByName(
++                                        self.cogr_ds, name_c)
++                elif isinstance(collection.name, int):
++                    self.cogr_layer = ograpi.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)
++
++            userencoding = self.collection.encoding
++            self._fileencoding = (userencoding or (
++                ograpi.OGR_L_TestCapability(self.cogr_layer, OLC_STRINGSASUTF8) and
++                'utf-8') or (
++                self.get_driver() == "ESRI Shapefile" and
++                'ISO-8859-1') or locale.getpreferredencoding()).upper()
++
++        elif collection.mode == 'w':
++            try:
++                path_b = path.encode('utf-8')
++            except UnicodeDecodeError:
++                path_b = path
++            path_c = path_b
++            driver_b = collection.driver.encode()
++            driver_c = driver_b
++
++            cogr_driver = ograpi.GDALGetDriverByName(driver_c)
++            if cogr_driver == NULL:
++                raise ValueError("Null driver")
++
++            if not os.path.exists(path):
++#                 cogr_ds = ograpi.OGR_Dr_CreateDataSource(
++#                     cogr_driver, path_c, NULL)
++                cogr_ds = ograpi.GDALCreate(
++                    cogr_driver,
++                    path_c,
++                    0,
++                    0,
++                    0,
++                    ograpi.GDT_Unknown,
++                    NULL)
++                pass
++
++            else:
++                with cpl_errs:
++                    cogr_ds = ograpi.GDALOpenEx(path_c,
++                                     ograpi.GDAL_OF_VECTOR | ograpi.GDAL_OF_UPDATE,
++                                     NULL,
++                                     NULL,
++                                     NULL)
++#                     cogr_ds = ograpi.OGROpen(path_c, 1, NULL)
++                if cogr_ds == NULL:
++                    cogr_ds = ograpi.GDALCreate(
++                        cogr_driver,
++                        path_c,
++                        0,
++                        0,
++                        0,
++                        ograpi.GDT_Unknown,
++                        NULL)
++#                     cogr_ds = ograpi.OGR_Dr_CreateDataSource(
++#                         cogr_driver, path_c, NULL)
++
++                elif collection.name is None:
++                    ograpi.GDALClose(cogr_ds)
++                    cogr_ds == NULL
++                    log.debug("Deleted pre-existing data at %s", path)
++                    cogr_ds = ograpi.GDALCreate(
++                        cogr_driver,
++                        path_c,
++                        0,
++                        0,
++                        0,
++                        ograpi.GDT_Unknown,
++                        NULL)
++#                     cogr_ds = ograpi.OGR_Dr_CreateDataSource(
++#                         cogr_driver, path_c, NULL)
++
++                else:
++                    pass
++
++            if cogr_ds == NULL:
++                raise RuntimeError("Failed to open %s" % path)
++            else:
++                self.cogr_ds = cogr_ds
++
++            # Set the spatial reference system from the crs given to the
++            # collection constructor. We by-pass the crs_wkt and crs
++            # properties because they aren't accessible until the layer
++            # is constructed (later).
++            col_crs = collection._crs_wkt or collection._crs
++            if col_crs:
++                cogr_srs = ograpi.OSRNewSpatialReference(NULL)
++                if cogr_srs == NULL:
++                    raise ValueError("NULL spatial reference")
++                # 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
++                    ograpi.OSRSetFromUserInput(cogr_srs, proj_c)
++                elif isinstance(col_crs, dict):
++                    # EPSG is a special case.
++                    init = col_crs.get('init')
++                    if init:
++                        log.debug("Init: %s", init)
++                        auth, val = init.split(':')
++                        if auth.upper() == 'EPSG':
++                            log.debug("Setting EPSG: %s", val)
++                            ograpi.OSRImportFromEPSG(cogr_srs, int(val))
++                    else:
++                        params = []
++                        col_crs['wktext'] = True
++                        for k, v in col_crs.items():
++                            if v is True or (k in ('no_defs', 'wktext') and v):
++                                params.append("+%s" % k)
++                            else:
++                                params.append("+%s=%s" % (k, v))
++                        proj = " ".join(params)
++                        log.debug("PROJ.4 to be imported: %r", proj)
++                        proj_b = proj.encode('utf-8')
++                        proj_c = proj_b
++                        ograpi.OSRImportFromProj4(cogr_srs, proj_c)
++                else:
++                    raise ValueError("Invalid CRS")
++
++                # Fixup, export to WKT, and set the GDAL dataset's projection.
++                ograpi.OSRFixup(cogr_srs)
++
++            # 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()
++            userencoding = collection.encoding
++            self._fileencoding = (userencoding or (
++                collection.driver == "ESRI Shapefile" and
++                'ISO-8859-1') or sysencoding).upper()
++
++            fileencoding = self.get_fileencoding()
++            if fileencoding:
++                fileencoding_b = fileencoding.encode()
++                fileencoding_c = fileencoding_b
++                options = ograpi.CSLSetNameValue(options, "ENCODING", fileencoding_c)
++
++            # Does the layer exist already? If so, we delete it.
++            layer_count = ograpi.GDALDatasetGetLayerCount(self.cogr_ds)
++            layer_names = []
++            for i in range(layer_count):
++                cogr_layer = ograpi.GDALDatasetGetLayer(cogr_ds, i)
++                name_c = ograpi.OGR_L_GetName(cogr_layer)
++                name_b = name_c
++                layer_names.append(name_b.decode('utf-8'))
++
++            idx = -1
++            if isinstance(collection.name, string_types):
++                if collection.name in layer_names:
++                    idx = layer_names.index(collection.name)
++            elif isinstance(collection.name, int):
++                if collection.name >= 0 and collection.name < layer_count:
++                    idx = collection.name
++            if idx >= 0:
++                log.debug("Deleted pre-existing layer at %s", collection.name)
++                ograpi.GDALDatasetDeleteLayer(self.cogr_ds, idx)
++            
++            # Create the named layer in the datasource.
++            name_b = collection.name.encode('utf-8')
++            name_c = name_b
++            self.cogr_layer = ograpi.GDALDatasetCreateLayer(
++                self.cogr_ds, 
++                name_c,
++                cogr_srs,
++                <unsigned int>[k for k,v in GEOMETRY_TYPES.items() if 
++                    v == collection.schema.get('geometry', 'Unknown')][0],
++                options
++                )
++
++            if cogr_srs != NULL:
++                ograpi.OSRDestroySpatialReference(cogr_srs)
++            if options != NULL:
++                ograpi.CSLDestroy(options)
++
++            if self.cogr_layer == NULL:
++                raise ValueError("Null layer")
++            log.debug("Created layer")
++            
++            # Next, make a layer definition from the given schema properties,
++            # which are an ordered dict since Fiona 1.0.1.
++            for key, value in collection.schema['properties'].items():
++                log.debug("Creating field: %s %s", key, value)
++
++                # Convert 'long' to 'int'. See
++                # https://github.com/Toblerity/Fiona/issues/101.
++                if value == 'long':
++                    value = 'int'
++
++                # Is there a field width/precision?
++                width = precision = None
++                if ':' in value:
++                    value, fmt = value.split(':')
++                    if '.' in fmt:
++                        width, precision = map(int, fmt.split('.'))
++                    else:
++                        width = int(fmt)
++
++                field_type = FIELD_TYPES.index(value)
++                # See https://trac.osgeo.org/gdal/wiki/rfc31_ogr_64
++                if value == 'int' and (width is not None and width >= 10):
++                    field_type = 12
++
++                encoding = self.get_internalencoding()
++                key_bytes = key.encode(encoding)
++
++                cogr_fielddefn = ograpi.OGR_Fld_Create(
++                    key_bytes,
++                    field_type)
++                if cogr_fielddefn == NULL:
++                    raise ValueError("Null field definition")
++                if width:
++                    ograpi.OGR_Fld_SetWidth(cogr_fielddefn, width)
++                if precision:
++                    ograpi.OGR_Fld_SetPrecision(cogr_fielddefn, precision)
++                ograpi.OGR_L_CreateField(self.cogr_layer, cogr_fielddefn, 1)
++                ograpi.OGR_Fld_Destroy(cogr_fielddefn)
++            log.debug("Created fields")
++
++        # Mapping of the Python collection schema to the munged 
++        # OGR schema.
++        ogr_schema = self.get_schema()
++        self._schema_mapping = dict(zip(
++            collection.schema['properties'].keys(), 
++            ogr_schema['properties'].keys() ))
++
++        log.debug("Writing started")
++
++    def writerecs(self, records, collection):
++        """Writes buffered records to OGR."""
++        cdef void *cogr_driver
++        cdef void *cogr_feature
++
++        cdef void *cogr_layer = self.cogr_layer
++        if cogr_layer == NULL:
++            raise ValueError("Null layer")
++    
++        schema_geom_type = collection.schema['geometry']
++        cogr_driver = ograpi.GDALGetDatasetDriver(self.cogr_ds)
++        if ograpi.OGR_Dr_GetName(cogr_driver) == b"GeoJSON":
++            def validate_geometry_type(rec):
++                return True
++        elif ograpi.OGR_Dr_GetName(cogr_driver) == b"ESRI Shapefile" \
++                and "Point" not in collection.schema['geometry']:
++            schema_geom_type = collection.schema['geometry'].lstrip(
++                "3D ").lstrip("Multi")
++            def validate_geometry_type(rec):
++                return rec['geometry'] is None or \
++                rec['geometry']['type'].lstrip(
++                    "3D ").lstrip("Multi") == schema_geom_type
++        else:
++            schema_geom_type = collection.schema['geometry'].lstrip("3D ")
++            def validate_geometry_type(rec):
++                return rec['geometry'] is None or \
++                       rec['geometry']['type'].lstrip("3D ") == schema_geom_type
++
++        schema_props_keys = set(collection.schema['properties'].keys())
++        for record in records:
++            log.debug("Creating feature in layer: %s" % record)
++            # Validate against collection's schema.
++            if set(record['properties'].keys()) != schema_props_keys:
++                raise ValueError(
++                    "Record does not match collection schema: %r != %r" % (
++                        record['properties'].keys(), 
++                        list(schema_props_keys) ))
++            if not validate_geometry_type(record):
++                raise ValueError(
++                    "Record's geometry type does not match "
++                    "collection schema's geometry type: %r != %r" % (
++                         record['geometry']['type'],
++                         collection.schema['geometry'] ))
++
++            cogr_feature = OGRFeatureBuilder().build(record, collection)
++            result = ograpi.OGR_L_CreateFeature(cogr_layer, cogr_feature)
++            if result != OGRERR_NONE:
++                raise RuntimeError("Failed to write record: %s" % record)
++            _deleteOgrFeature(cogr_feature)
++
++    def sync(self, collection):
++        """Syncs OGR to disk."""
++        cdef void *cogr_ds = self.cogr_ds
++        cdef void *cogr_layer = self.cogr_layer
++        if cogr_ds == NULL:
++            raise ValueError("Null data source")
++        log.debug("Syncing OGR to disk")
++
++        ograpi.GDALFlushCache(cogr_ds)
++
++
++cdef class Iterator:
++
++    """Provides iterated access to feature data.
++    """
++
++    # Reference to its Collection
++    cdef collection
++    cdef encoding
++    cdef int next_index
++    cdef stop
++    cdef start
++    cdef step
++    cdef fastindex
++    cdef stepsign
++
++    def __init__(self, collection, 
++            start=None, stop=None, step=None, bbox=None, mask=None):
++        if collection.session is None:
++            raise ValueError("I/O operation on closed collection")
++        self.collection = collection
++        cdef Session session
++        cdef void *cogr_geometry
++        session = self.collection.session
++        cdef void *cogr_layer = session.cogr_layer
++        if cogr_layer == NULL:
++            raise ValueError("Null layer")
++        ograpi.OGR_L_ResetReading(cogr_layer)
++        
++        if bbox and mask:
++            raise ValueError("mask and bbox can not be set together")
++        
++        if bbox:
++            ograpi.OGR_L_SetSpatialFilterRect(
++                cogr_layer, bbox[0], bbox[1], bbox[2], bbox[3])
++        elif mask:
++            cogr_geometry = OGRGeomBuilder().build(mask)
++            ograpi.OGR_L_SetSpatialFilter(cogr_layer, cogr_geometry)
++            ograpi.OGR_G_DestroyGeometry(cogr_geometry)
++            
++        else:
++            ograpi.OGR_L_SetSpatialFilter(
++                cogr_layer, NULL)
++        self.encoding = session.get_internalencoding()
++
++        self.fastindex = ograpi.OGR_L_TestCapability(
++            session.cogr_layer, OLC_FASTSETNEXTBYINDEX)
++
++        ftcount = ograpi.OGR_L_GetFeatureCount(session.cogr_layer, 0)
++        if ftcount == -1 and ((start is not None and start < 0) or
++                              (stop is not None and stop < 0)):
++            raise IndexError(
++                "collection's dataset does not support negative slice indexes")
++
++        if stop is not None and stop < 0:
++            stop += ftcount
++
++        if start is None:
++            start = 0
++        if start is not None and start < 0:
++            start += ftcount
++
++        # step size
++        if step is None:
++            step = 1
++        if step == 0:
++            raise ValueError("slice step cannot be zero")
++        if step < 0 and not self.fastindex:
++            warnings.warn("Layer does not support" \
++                    "OLCFastSetNextByIndex, negative step size may" \
++                    " be slow", RuntimeWarning)
++        self.stepsign = int(math.copysign(1, step))
++        self.stop = stop
++        self.start = start
++        self.step = step
++
++        self.next_index = start
++        log.debug("Index: %d", self.next_index)
++        ograpi.OGR_L_SetNextByIndex(session.cogr_layer, self.next_index)
++
++
++    def __iter__(self):
++        return self
++
++
++    def _next(self):
++        """Internal method to set read cursor to next item"""
++
++        cdef Session session
++        session = self.collection.session
++
++        # Check if next_index is valid
++        if self.next_index < 0:
++            raise StopIteration
++        
++        if self.stepsign == 1:
++            if self.next_index < self.start or (self.stop is not None and self.next_index >= self.stop):
++                raise StopIteration
++        else:
++            if self.next_index > self.start or (self.stop is not None and self.next_index <= self.stop):
++                raise StopIteration
++
++
++        # Set read cursor to next_item position
++        if self.step > 1 and self.fastindex:
++            ograpi.OGR_L_SetNextByIndex(session.cogr_layer, self.next_index)
++
++        elif self.step > 1 and not self.fastindex and not self.next_index == self.start:
++            for _ in range(self.step - 1):
++                # TODO rbuffat add test -> OGR_L_GetNextFeature increments cursor by 1, therefore self.step - 1 as one increment was performed when feature is read
++                cogr_feature = ograpi.OGR_L_GetNextFeature(session.cogr_layer)
++                if cogr_feature == NULL:
++                    raise StopIteration
++        elif self.step > 1 and not self.fastindex and self.next_index == self.start:
++            ograpi.OGR_L_SetNextByIndex(session.cogr_layer, self.next_index)
++
++        elif self.step == 0:
++            # ograpi.OGR_L_GetNextFeature increments read cursor by one
++            pass
++        elif self.step < 0:
++            ograpi.OGR_L_SetNextByIndex(session.cogr_layer, self.next_index)
++            
++        # set the next index
++        self.next_index += self.step
++
++
++    def __next__(self):
++        cdef void * cogr_feature
++        cdef Session session
++        session = self.collection.session
++
++        #Update read cursor
++        self._next()
++
++        # Get the next feature.
++        cogr_feature = ograpi.OGR_L_GetNextFeature(session.cogr_layer)
++        if cogr_feature == NULL:
++            raise StopIteration
++
++        feature = FeatureBuilder().build(
++            cogr_feature,
++            bbox=False,
++            encoding=self.encoding,
++            driver=self.collection.driver
++        )
++        _deleteOgrFeature(cogr_feature)
++        return feature
++
++
++cdef class ItemsIterator(Iterator):
++
++    def __next__(self):
++
++        cdef long fid
++        cdef void * cogr_feature
++        cdef Session session
++        session = self.collection.session
++
++        #Update read cursor
++        self._next()
++
++        # Get the next feature.
++        cogr_feature = ograpi.OGR_L_GetNextFeature(session.cogr_layer)
++        if cogr_feature == NULL:
++            raise StopIteration
++
++
++        fid = ograpi.OGR_F_GetFID(cogr_feature)
++        feature = FeatureBuilder().build(
++            cogr_feature,
++            bbox=False,
++            encoding=self.encoding,
++            driver=self.collection.driver
++        )
++        _deleteOgrFeature(cogr_feature)
++
++        return fid, feature
++
++
++cdef class KeysIterator(Iterator):
++
++    def __next__(self):
++        cdef long fid
++        cdef void * cogr_feature
++        cdef Session session
++        session = self.collection.session
++
++        #Update read cursor
++        self._next()
++
++        # Get the next feature.
++        cogr_feature = ograpi.OGR_L_GetNextFeature(session.cogr_layer)
++        if cogr_feature == NULL:
++            raise StopIteration
++
++        fid = ograpi.OGR_F_GetFID(cogr_feature)
++        _deleteOgrFeature(cogr_feature)
++
++        return fid
++
++
++def _listlayers(path):
++
++    """Provides a list of the layers in an OGR data source.
++    """
++    
++    cdef void *cogr_ds
++    cdef void *cogr_layer
++    cdef char *path_c
++    cdef char *name_c
++    
++    # Open OGR data source.
++    try:
++        path_b = path.encode('utf-8')
++    except UnicodeDecodeError:
++        path_b = path
++    path_c = path_b
++    with cpl_errs:
++        cogr_ds = ograpi.GDALOpenEx(path_c,
++             ograpi.GDAL_OF_VECTOR | ograpi.GDAL_OF_READONLY,
++             NULL,
++             NULL,
++             NULL)
++#         cogr_ds = ograpi.OGROpen(path_c, 0, NULL)
++    if cogr_ds == NULL:
++        raise ValueError("No data available at path '%s'" % path)
++    
++    # Loop over the layers to get their names.
++    layer_count = ograpi.GDALDatasetGetLayerCount(cogr_ds)
++    layer_names = []
++    for i in range(layer_count):
++        cogr_layer = ograpi.GDALDatasetGetLayer(cogr_ds, i)
++        name_c = ograpi.OGR_L_GetName(cogr_layer)
++        name_b = name_c
++        layer_names.append(name_b.decode('utf-8'))
++    
++    # Close up data source.
++    if cogr_ds is not NULL:
++        ograpi.GDALClose(cogr_ds)
++    cogr_ds = NULL
++
++    return layer_names
++
++def buffer_to_virtual_file(bytesbuf):
++    """Maps a bytes buffer to a virtual file.
++    """
++    vsi_filename = os.path.join('/vsimem', uuid.uuid4().hex)
++    vsi_cfilename = vsi_filename if not isinstance(vsi_filename, string_types) else vsi_filename.encode('utf-8')
++
++    vsi_handle = ograpi.VSIFileFromMemBuffer(vsi_cfilename, bytesbuf, len(bytesbuf), 0)
++    if vsi_handle == NULL:
++        raise OSError('failed to map buffer to file')
++    if ograpi.VSIFCloseL(vsi_handle) != 0:
++        raise OSError('failed to close mapped file handle')
++
++    return vsi_filename
++
++def remove_virtual_file(vsi_filename):
++    vsi_cfilename = vsi_filename if not isinstance(vsi_filename, string_types) else vsi_filename.encode('utf-8')
++    return ograpi.VSIUnlink(vsi_cfilename)
++
++
+--- a/setup.py
++++ b/setup.py
+@@ -66,17 +66,34 @@ with open('CREDITS.txt', **open_kwds) as
+ with open('CHANGES.txt', **open_kwds) as f:
+     changes = f.read()
+ 
++
++def copy_gdalapi(gdalversion):
++    if gdalversion[0] == u'1':
++        log.info("Building Fiona for gdal 1.x: {}".format(gdal_output[3]))
++        shutil.copy('fiona/ogrext1.pyx', 'fiona/ogrext.pyx')
++        shutil.copy('fiona/ograpi1.pxd', 'fiona/ograpi.pxd')
++    else:
++        log.info("Building Fiona for gdal 2.x: {}".format(gdal_output[3]))
++        shutil.copy('fiona/ogrext2.pyx', 'fiona/ogrext.pyx')
++        shutil.copy('fiona/ograpi2.pxd', 'fiona/ograpi.pxd')
++ 
++if '--gdalversion' in sys.argv:
++    index = sys.argv.index('--gdalversion')
++    sys.argv.pop(index)
++    gdalversion = sys.argv.pop(index)
++    copy_gdalapi(gdalversion)
++
+ # By default we'll try to get options via gdal-config. On systems without,
+ # options will need to be set in setup.cfg or on the setup command line.
+ include_dirs = []
+ library_dirs = []
+ libraries = []
+ extra_link_args = []
+-gdal_output = [None]*3
++gdal_output = [None] * 4
+ 
+ try:
+     gdal_config = os.environ.get('GDAL_CONFIG', 'gdal-config')
+-    for i, flag in enumerate(("--cflags", "--libs", "--datadir")):
++    for i, flag in enumerate(("--cflags", "--libs", "--datadir", "--version")):
+         gdal_output[i] = check_output([gdal_config, flag]).strip()
+ 
+     for item in gdal_output[0].split():
+@@ -91,6 +108,8 @@ try:
+             # e.g. -framework GDAL
+             extra_link_args.append(item)
+ 
++    copy_gdalapi(gdal_output[3])
++
+ except Exception as e:
+     if os.name == "nt":
+         log.info(("Building on Windows requires extra options to setup.py to locate needed GDAL files.\n"
+@@ -131,13 +150,14 @@ ext_options = dict(
+     extra_link_args=extra_link_args)
+ 
+ # When building from a repo, Cython is required.
+-if os.path.exists("MANIFEST.in"):
++if os.path.exists("MANIFEST.in") and "clean" not in sys.argv:
+     log.info("MANIFEST.in found, presume a repo, cythonizing...")
+     if not cythonize:
+         log.critical(
+             "Cython.Build.cythonize not found. "
+             "Cython is required to build from a repo.")
+         sys.exit(1)
++
+     ext_modules = cythonize([
+         Extension('fiona._geometry', ['fiona/_geometry.pyx'], **ext_options),
+         Extension('fiona._transform', ['fiona/_transform.pyx'], **ext_options),
+@@ -166,8 +186,8 @@ setup_args = dict(
+     metadata_version='1.2',
+     name='Fiona',
+     version=version,
+-    requires_python = '>=2.6',
+-    requires_external = 'GDAL (>=1.8)',
++    requires_python='>=2.6',
++    requires_external='GDAL (>=1.8)',
+     description="Fiona reads and writes spatial data files",
+     license='BSD',
+     keywords='gis vector feature data',
+--- a/requirements.txt
++++ b/requirements.txt
+@@ -2,3 +2,4 @@ argparse
+ cligj
+ six
+ ordereddict
++munch
+--- /dev/null
++++ b/tests/test_bigint.py
+@@ -0,0 +1,69 @@
++import fiona
++import os
++import shutil
++import tempfile
++import unittest
++from fiona.ogrext import calc_gdal_version_num, get_gdal_version_num
++
++"""
++
++OGR 54bit handling: https://trac.osgeo.org/gdal/wiki/rfc31_ogr_64
++
++Shapefile: OFTInteger fields are created by default with a width of 9
++characters, so to be unambiguously read as OFTInteger (and if specifying
++integer that require 10 or 11 characters. the field is dynamically extended
++like managed since a few versions). OFTInteger64 fields are created by default
++with a width of 18 digits, so to be unambiguously read as OFTInteger64, and
++extented to 19 or 20 if needed. Integer fields of width between 10 and 18
++will be read as OFTInteger64. Above they will be treated as OFTReal. In
++previous GDAL versions, Integer fields were created with a default with of 10,
++and thus will be now read as OFTInteger64. An open option, DETECT_TYPE=YES, can
++be specified so as OGR does a full scan of the DBF file to see if integer
++fields of size 10 or 11 hold 32 bit or 64 bit values and adjust the type
++accordingly (and same for integer fields of size 19 or 20, in case of overflow
++of 64 bit integer, OFTReal is chosen)
++"""
++class TestBigInt(unittest.TestCase):
++
++    def setUp(self):
++        self.tempdir = tempfile.mkdtemp()
++
++    def tearDown(self):
++        shutil.rmtree(self.tempdir)
++
++    def testCreateBigIntSchema(self):
++        name = os.path.join(self.tempdir, 'output1.shp')
++
++        a_bigint = 10 ** 18 - 1
++        fieldname = 'abigint'
++
++        kwargs = {
++            'driver': 'ESRI Shapefile',
++            'crs': 'EPSG:4326',
++            'schema': {
++                'geometry': 'Point',
++                'properties': [(fieldname, 'int:10')]}}
++        if get_gdal_version_num() < calc_gdal_version_num(2, 0, 0):
++            with self.assertRaises(OverflowError):
++                with fiona.open(name, 'w', **kwargs) as dst:
++                    rec = {}
++                    rec['geometry'] = {'type': 'Point', 'coordinates': (0, 0)}
++                    rec['properties'] = {fieldname: a_bigint}
++                    dst.write(rec)
++        else:
++
++            with fiona.open(name, 'w', **kwargs) as dst:
++                rec = {}
++                rec['geometry'] = {'type': 'Point', 'coordinates': (0, 0)}
++                rec['properties'] = {fieldname: a_bigint}
++                dst.write(rec)
++
++            with fiona.open(name) as src:
++                if get_gdal_version_num() >= calc_gdal_version_num(2, 0, 0):
++                    first = next(src)
++                    self.assertEqual(first['properties'][fieldname], a_bigint)
++
++
++if __name__ == "__main__":
++    # import sys;sys.argv = ['', 'Test.testName']
++    unittest.main()
+--- a/tests/test_props.py
++++ b/tests/test_props.py
+@@ -22,8 +22,8 @@ def test_width_other():
+ def test_types():
+     assert prop_type('str:254') == text_type
+     assert prop_type('str') == text_type
+-    assert prop_type('int') == type(0)
+-    assert prop_type('float') == type(0.0)
++    assert isinstance(0, prop_type('int'))
++    assert isinstance(0.0, prop_type('float'))
+     assert prop_type('date') == FionaDateType
+ 
+ 
+--- /dev/null
++++ b/.gitignore
+@@ -0,0 +1,69 @@
++# Byte-compiled / optimized / DLL files
++__pycache__/
++*.py[cod]
++
++# C extensions
++*.so
++
++# Distribution / packaging
++.Python
++env/
++build/
++develop-eggs/
++dist/
++downloads/
++eggs/
++.eggs/
++lib/
++lib64/
++parts/
++sdist/
++var/
++*.egg-info/
++.installed.cfg
++*.egg
++
++# PyInstaller
++#  Usually these files are written by a python script from a template
++#  before PyInstaller builds the exe, so as to inject date/other infos into it.
++*.manifest
++*.spec
++
++# Installer logs
++pip-log.txt
++pip-delete-this-directory.txt
++
++# Unit test / coverage reports
++htmlcov/
++.tox/
++.coverage
++.coverage.*
++.cache
++nosetests.xml
++coverage.xml
++*,cover
++
++# Translations
++*.mo
++*.pot
++
++# Django stuff:
++*.log
++
++# Sphinx documentation
++docs/_build/
++
++# PyBuilder
++target/
++
++# IDE's etc.
++.idea/
++venv/
++venv2/
++
++#fiona
++fiona/ogrext.c
++fiona/_drivers.c
++fiona/_err.c
++fiona/_geometry.c
++fiona/_transform.cpp
+--- a/README.rst
++++ b/README.rst
+@@ -236,7 +236,7 @@ gdal``).
+ Python Requirements
+ -------------------
+ 
+-Fiona depends on the modules ``six``, ``cligj``, ``argparse``, and
++Fiona depends on the modules ``six``, ``cligj``,  ``future``,  ``munch``, ``argparse``, and
+ ``ordereddict`` (the two latter modules are standard in Python 2.7+). Pip will
+ fetch these requirements for you, but users installing Fiona from a Windows
+ installer must get them separately.
+@@ -314,7 +314,7 @@ locations on your system (via your syste
+ If you have a non-standard environment, you'll need to specify the include and
+ lib dirs and GDAL library on the command line::
+ 
+-  (fiona_env)$ python setup.py build_ext -I/path/to/gdal/include -L/path/to/gdal/lib -lgdal develop
++  (fiona_env)$ python setup.py build_ext -I/path/to/gdal/include -L/path/to/gdal/lib -lgdal --gdalversion 2.0.1 develop
+   (fiona_env)$ nosetests
+ 
+ .. _OGR: http://www.gdal.org/ogr
+--- a/requirements-dev.txt
++++ b/requirements-dev.txt
+@@ -1,3 +1,4 @@
++-r requirements.txt
+ cython>=0.21.2
+ nose
+ pytest
diff -Nru fiona-1.6.3/debian/patches/0004-Just-use-int-as-a-plain-old-builtin.patch fiona-1.6.3/debian/patches/0004-Just-use-int-as-a-plain-old-builtin.patch
--- fiona-1.6.3/debian/patches/0004-Just-use-int-as-a-plain-old-builtin.patch	1970-01-01 01:00:00.000000000 +0100
+++ fiona-1.6.3/debian/patches/0004-Just-use-int-as-a-plain-old-builtin.patch	2016-02-06 22:37:56.000000000 +0100
@@ -0,0 +1,36 @@
+Origin: https://github.com/Toblerity/Fiona/commit/805bb6224df389d493cee5b933a65f5b9e4026c9
+From c1b7189bfd5f1613eea017e474034a6a5f56827a Mon Sep 17 00:00:00 2001
+From: Sean Gillies <sean at mapbox.com>
+Date: Tue, 27 Oct 2015 13:04:39 -0600
+Subject: [PATCH] Just use int as a plain old builtin
+
+---
+ README.rst        | 2 +-
+ fiona/ogrext2.pyx | 5 -----
+ 2 files changed, 1 insertion(+), 6 deletions(-)
+
+--- a/README.rst
++++ b/README.rst
+@@ -236,7 +236,7 @@ gdal``).
+ Python Requirements
+ -------------------
+ 
+-Fiona depends on the modules ``six``, ``cligj``,  ``future``,  ``munch``, ``argparse``, and
++Fiona depends on the modules ``six``, ``cligj``,  ``munch``, ``argparse``, and
+ ``ordereddict`` (the two latter modules are standard in Python 2.7+). Pip will
+ fetch these requirements for you, but users installing Fiona from a Windows
+ installer must get them separately.
+--- a/fiona/ogrext2.pyx
++++ b/fiona/ogrext2.pyx
+@@ -10,11 +10,6 @@ import warnings
+ import math
+ import uuid
+ 
+-if sys.version_info > (3,):
+-    from builtins import int
+-else:
+-    from __builtin__ import int
+-
+ from six import integer_types, string_types, text_type
+ 
+ from fiona cimport ograpi
diff -Nru fiona-1.6.3/debian/patches/0005-Initial-attempt-at-fiona.remove-for-deleting-data-sources.patch fiona-1.6.3/debian/patches/0005-Initial-attempt-at-fiona.remove-for-deleting-data-sources.patch
--- fiona-1.6.3/debian/patches/0005-Initial-attempt-at-fiona.remove-for-deleting-data-sources.patch	1970-01-01 01:00:00.000000000 +0100
+++ fiona-1.6.3/debian/patches/0005-Initial-attempt-at-fiona.remove-for-deleting-data-sources.patch	2016-02-06 22:37:56.000000000 +0100
@@ -0,0 +1,246 @@
+From d8fcdeb95e10de9a1c8a6d2fbdcd5b8e861ae725 Mon Sep 17 00:00:00 2001
+From: Joshua Arnott <josh at snorfalorpagus.net>
+Date: Tue, 27 Oct 2015 23:40:38 +0000
+Subject: [PATCH 1/6] Initial attempt at fiona.remove from deleting data
+ sources
+
+---
+ fiona/__init__.py | 12 +++++++++++-
+ fiona/ogrext1.pyx | 18 ++++++++++++++++++
+ 2 files changed, 29 insertions(+), 1 deletion(-)
+
+--- a/fiona/__init__.py
++++ b/fiona/__init__.py
+@@ -72,7 +72,7 @@ from six import string_types
+ from fiona.collection import Collection, BytesCollection, vsi_path
+ from fiona._drivers import driver_count, GDALEnv, supported_drivers
+ from fiona.odict import OrderedDict
+-from fiona.ogrext import _bounds, _listlayers, FIELD_TYPES_MAP
++from fiona.ogrext import _bounds, _listlayers, FIELD_TYPES_MAP, _remove
+ 
+ # These modules are imported by fiona.ogrext, but are also import here to
+ # help tools like cx_Freeze find them automatically
+@@ -182,6 +182,33 @@ def open(
+ collection = open
+ 
+ 
++def remove(path_or_collection, driver=None):
++    """Deletes an OGR data source
++
++    The required ``path`` argument may be an absolute or relative file path.
++    Alternatively, a Collection can be passed instead in which case the path
++    and driver are automatically determined. Otherwise the ``driver`` argument
++    must be specified.
++
++    Raises a ``RuntimeError`` if the data source cannot be deleted.
++
++    Example usage:
++
++      fiona.remove('test.shp', 'ESRI Shapefile')
++
++    """
++    if isinstance(path_or_collection, Collection):
++        collection = path_or_collection
++        path = collection.path
++        driver = collection.driver
++        collection.close()
++    else:
++        path = path_or_collection
++        if driver is None:
++            raise ValueError("The driver argument is required when removing a path")
++    _remove(path, driver)
++
++
+ def listlayers(path, vfs=None):
+     """Returns a list of layer names in their index order.
+     
+--- a/fiona/ogrext1.pyx
++++ b/fiona/ogrext1.pyx
+@@ -59,6 +59,10 @@ FIELD_TYPES_MAP = {
+     'datetime': FionaDateTimeType
+    }
+ 
++# OGR Driver capability
++ODrCCreateDataSource = b"CreateDataSource"
++ODrCDeleteDataSource = b"DeleteDataSource"
++
+ # OGR Layer capability
+ OLC_RANDOMREAD = b"RandomRead"
+ OLC_SEQUENTIALWRITE = b"SequentialWrite"
+@@ -1171,6 +1175,27 @@ cdef class KeysIterator(Iterator):
+         return fid
+ 
+ 
++def _remove(path, driver=None):
++    """Deletes an OGR data source
++    """
++    cdef void *cogr_driver
++    cdef int result
++
++    if driver is None:
++        driver = 'ESRI Shapefile'
++
++    cogr_driver = ograpi.OGRGetDriverByName(driver.encode('utf-8'))
++    if cogr_driver == NULL:
++        raise ValueError("Null driver")
++
++    if not ograpi.OGR_Dr_TestCapability(cogr_driver, ODrCDeleteDataSource):
++        raise RuntimeError("Driver does not support dataset removal operation")
++
++    result = ograpi.OGR_Dr_DeleteDataSource(cogr_driver, path.encode('utf-8'))
++    if result != OGRERR_NONE:
++        raise RuntimeError("Failed to remove data source {}".format(path))
++
++
+ def _listlayers(path):
+ 
+     """Provides a list of the layers in an OGR data source.
+--- /dev/null
++++ b/tests/test_remove.py
+@@ -0,0 +1,77 @@
++import logging
++import sys
++import os
++
++import tempfile
++import pytest
++
++import fiona
++
++
++logging.basicConfig(stream=sys.stderr, level=logging.DEBUG)
++
++
++def create_sample_data(filename, driver):
++    meta = {
++        'driver': driver,
++        'schema': {
++            'geometry': 'Point',
++            'properties': {}
++        }
++    }
++    with fiona.open(filename, 'w', **meta) as dst:
++        dst.write({
++            'geometry': {
++                'type': 'Point',
++                'coordinates': (0, 0),
++            },
++            'properties': {},
++        })
++    assert(os.path.exists(filename))
++
++
++def test_remove(tmpdir=None):
++    if tmpdir is None:
++        tmpdir = tempfile.mkdtemp()
++    filename_shp = os.path.join(tmpdir, 'test.shp')
++    
++    create_sample_data(filename_shp, driver='ESRI Shapefile')
++    fiona.remove(filename_shp, driver='ESRI Shapefile')
++    assert(not os.path.exists(filename_shp))
++    
++    with pytest.raises(RuntimeError):
++        fiona.remove(filename_shp, driver='ESRI Shapefile')
++
++def test_remove_driver(tmpdir=None):
++    if tmpdir is None:
++        tmpdir = tempfile.mkdtemp()
++    filename_shp = os.path.join(tmpdir, 'test.shp')
++    filename_json = os.path.join(tmpdir, 'test.json')
++        
++    create_sample_data(filename_shp, driver='ESRI Shapefile')
++    create_sample_data(filename_json, driver='GeoJSON')
++    fiona.remove(filename_json, driver='GeoJSON')
++    assert(not os.path.exists(filename_json))
++    assert(os.path.exists(filename_shp))
++
++def test_remove_collection(tmpdir=None):
++    if tmpdir is None:
++        tmpdir = tempfile.mkdtemp()
++    filename_shp = os.path.join(tmpdir, 'test.shp')
++    
++    create_sample_data(filename_shp, driver='ESRI Shapefile')
++    collection = fiona.open(filename_shp, 'r')
++    fiona.remove(collection)
++    assert(not os.path.exists(filename_shp))
++
++def test_remove_path_without_driver(tmpdir=None):
++    if tmpdir is None:
++        tmpdir = tempfile.mkdtemp()
++    filename_shp = os.path.join(tmpdir, 'test.shp')
++
++    create_sample_data(filename_shp, driver='ESRI Shapefile')
++
++    with pytest.raises(Exception):
++        fiona.remove(filename_shp)
++
++    assert(os.path.exists(filename_shp))
+--- a/fiona/ogrext2.pyx
++++ b/fiona/ogrext2.pyx
+@@ -64,6 +64,10 @@ FIELD_TYPES_MAP = {
+     'datetime': FionaDateTimeType
+    }
+ 
++# OGR Driver capability
++ODrCCreateDataSource = b"CreateDataSource"
++ODrCDeleteDataSource = b"DeleteDataSource"
++
+ # OGR Layer capability
+ OLC_RANDOMREAD = b"RandomRead"
+ OLC_SEQUENTIALWRITE = b"SequentialWrite"
+@@ -1244,6 +1248,27 @@ cdef class KeysIterator(Iterator):
+         return fid
+ 
+ 
++def _remove(path, driver=None):
++    """Deletes an OGR data source
++    """
++    cdef void *cogr_driver
++    cdef int result
++
++    if driver is None:
++        driver = 'ESRI Shapefile'
++
++    cogr_driver = ograpi.OGRGetDriverByName(driver.encode('utf-8'))
++    if cogr_driver == NULL:
++        raise ValueError("Null driver")
++
++    if not ograpi.OGR_Dr_TestCapability(cogr_driver, ODrCDeleteDataSource):
++        raise RuntimeError("Driver does not support dataset removal operation")
++
++    result = ograpi.GDALDeleteDataset(cogr_driver, path.encode('utf-8'))
++    if result != OGRERR_NONE:
++        raise RuntimeError("Failed to remove data source {}".format(path))
++
++
+ def _listlayers(path):
+ 
+     """Provides a list of the layers in an OGR data source.
+--- a/fiona/ograpi2.pxd
++++ b/fiona/ograpi2.pxd
+@@ -36,6 +36,7 @@ cdef extern from "gdal.h":
+     void GDALFlushCache(void * hDS)
+     char * GDALGetDriverShortName(void * hDriver)
+     char * GDALGetDatasetDriver (void * hDataset)
++    int GDALDeleteDataset(void * hDriver, const char * pszFilename)
+ 
+ 
+     ctypedef enum GDALDataType:
+@@ -110,6 +111,7 @@ cdef extern from "ogr_api.h":
+     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)
++    int     OGR_Dr_TestCapability (void *driver, const char *)
+     void *  OGR_F_Create (void *featuredefn)
+     void    OGR_F_Destroy (void *feature)
+     long    OGR_F_GetFID (void *feature)
+--- a/fiona/ograpi1.pxd
++++ b/fiona/ograpi1.pxd
+@@ -62,6 +62,7 @@ cdef extern from "ogr_api.h":
+     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)
++    int     OGR_Dr_TestCapability (void *driver, const char *)
+     int     OGR_DS_DeleteLayer (void *datasource, int n)
+     void *  OGR_DS_CreateLayer (void *datasource, char *name, void *crs, int geomType, char **options)
+     void *  OGR_DS_ExecuteSQL (void *datasource, char *name, void *filter, char *dialext)
diff -Nru fiona-1.6.3/debian/patches/0006-Remove-unknown-distribution-options.patch fiona-1.6.3/debian/patches/0006-Remove-unknown-distribution-options.patch
--- fiona-1.6.3/debian/patches/0006-Remove-unknown-distribution-options.patch	1970-01-01 01:00:00.000000000 +0100
+++ fiona-1.6.3/debian/patches/0006-Remove-unknown-distribution-options.patch	2016-02-06 22:37:56.000000000 +0100
@@ -0,0 +1,20 @@
+Description: Remove unknown distribution options.
+ UserWarning: Unknown distribution option: 'metadata_version'
+ UserWarning: Unknown distribution option: 'requires_python'
+ UserWarning: Unknown distribution option: 'requires_external'
+Author: Bas Couwenberg <sebastic at debian.org>
+
+--- a/setup.py
++++ b/setup.py
+@@ -183,11 +183,8 @@ if sys.version_info < (2, 7):
+     requirements.append('ordereddict')
+ 
+ setup_args = dict(
+-    metadata_version='1.2',
+     name='Fiona',
+     version=version,
+-    requires_python='>=2.6',
+-    requires_external='GDAL (>=1.8)',
+     description="Fiona reads and writes spatial data files",
+     license='BSD',
+     keywords='gis vector feature data',
diff -Nru fiona-1.6.3/debian/patches/0007-clean.patch fiona-1.6.3/debian/patches/0007-clean.patch
--- fiona-1.6.3/debian/patches/0007-clean.patch	1970-01-01 01:00:00.000000000 +0100
+++ fiona-1.6.3/debian/patches/0007-clean.patch	2016-02-07 23:01:25.000000000 +0100
@@ -0,0 +1,121 @@
+Description: Don't copy gdalapi files in clean target.
+Author: Bas Couwenberg <sebastic at debian.org>
+
+--- a/setup.py
++++ b/setup.py
+@@ -77,7 +77,7 @@ def copy_gdalapi(gdalversion):
+         shutil.copy('fiona/ogrext2.pyx', 'fiona/ogrext.pyx')
+         shutil.copy('fiona/ograpi2.pxd', 'fiona/ograpi.pxd')
+  
+-if '--gdalversion' in sys.argv:
++if '--gdalversion' in sys.argv and "clean" not in sys.argv:
+     index = sys.argv.index('--gdalversion')
+     sys.argv.pop(index)
+     gdalversion = sys.argv.pop(index)
+@@ -91,57 +91,58 @@ libraries = []
+ extra_link_args = []
+ gdal_output = [None] * 4
+ 
+-try:
+-    gdal_config = os.environ.get('GDAL_CONFIG', 'gdal-config')
+-    for i, flag in enumerate(("--cflags", "--libs", "--datadir", "--version")):
+-        gdal_output[i] = check_output([gdal_config, flag]).strip()
+-
+-    for item in gdal_output[0].split():
+-        if item.startswith("-I"):
+-            include_dirs.extend(item[2:].split(":"))
+-    for item in gdal_output[1].split():
+-        if item.startswith("-L"):
+-            library_dirs.extend(item[2:].split(":"))
+-        elif item.startswith("-l"):
+-            libraries.append(item[2:])
++if "clean" not in sys.argv:
++    try:
++        gdal_config = os.environ.get('GDAL_CONFIG', 'gdal-config')
++        for i, flag in enumerate(("--cflags", "--libs", "--datadir", "--version")):
++            gdal_output[i] = check_output([gdal_config, flag]).strip()
++
++        for item in gdal_output[0].split():
++            if item.startswith("-I"):
++                include_dirs.extend(item[2:].split(":"))
++        for item in gdal_output[1].split():
++            if item.startswith("-L"):
++                library_dirs.extend(item[2:].split(":"))
++            elif item.startswith("-l"):
++                libraries.append(item[2:])
++            else:
++                # e.g. -framework GDAL
++                extra_link_args.append(item)
++
++        copy_gdalapi(gdal_output[3])
++
++    except Exception as e:
++        if os.name == "nt":
++            log.info(("Building on Windows requires extra options to setup.py to locate needed GDAL files.\n"
++                       "More information is available in the README."))
+         else:
+-            # e.g. -framework GDAL
+-            extra_link_args.append(item)
++            log.warning("Failed to get options via gdal-config: %s", str(e))
+ 
+-    copy_gdalapi(gdal_output[3])
+-
+-except Exception as e:
+-    if os.name == "nt":
+-        log.info(("Building on Windows requires extra options to setup.py to locate needed GDAL files.\n"
+-                   "More information is available in the README."))
+-    else:
+-        log.warning("Failed to get options via gdal-config: %s", str(e))
+-
+-    # Conditionally copy the GDAL data. To be used in conjunction with
+-    # the bdist_wheel command to make self-contained binary wheels.
++        # Conditionally copy the GDAL data. To be used in conjunction with
++        # the bdist_wheel command to make self-contained binary wheels.
++        if os.environ.get('PACKAGE_DATA'):
++            try:
++                shutil.rmtree('fiona/gdal_data')
++            except OSError:
++                pass
++            shutil.copytree(datadir, 'fiona/gdal_data')
+     if os.environ.get('PACKAGE_DATA'):
+-        try:
+-            shutil.rmtree('fiona/gdal_data')
+-        except OSError:
+-            pass
+-        shutil.copytree(datadir, 'fiona/gdal_data')
+-if os.environ.get('PACKAGE_DATA'):
+-    destdir = 'fiona/gdal_data'
+-    if gdal_output[2]:
+-        log.info("Copying gdal data from %s" % gdal_output[2])
+-        copy_data_tree(gdal_output[2], destdir)
+-    else:
+-        # check to see if GDAL_DATA is defined
+-        gdal_data = os.environ.get('GDAL_DATA', None)
+-        if gdal_data:
+-            log.info("Copying gdal data from %s" % gdal_data)
+-            copy_data_tree(gdal_data, destdir)
+-
+-    # Conditionally copy PROJ.4 data. 
+-    projdatadir = os.environ.get('PROJ_LIB', '/usr/local/share/proj')
+-    if os.path.exists(projdatadir):
+-        log.info("Copying proj data from %s" % projdatadir)
+-        copy_data_tree(projdatadir, 'fiona/proj_data')
++        destdir = 'fiona/gdal_data'
++        if gdal_output[2]:
++            log.info("Copying gdal data from %s" % gdal_output[2])
++            copy_data_tree(gdal_output[2], destdir)
++        else:
++            # check to see if GDAL_DATA is defined
++            gdal_data = os.environ.get('GDAL_DATA', None)
++            if gdal_data:
++                log.info("Copying gdal data from %s" % gdal_data)
++                copy_data_tree(gdal_data, destdir)
++
++        # Conditionally copy PROJ.4 data. 
++        projdatadir = os.environ.get('PROJ_LIB', '/usr/local/share/proj')
++        if os.path.exists(projdatadir):
++            log.info("Copying proj data from %s" % projdatadir)
++            copy_data_tree(projdatadir, 'fiona/proj_data')
+ 
+ ext_options = dict(
+     include_dirs=include_dirs,
diff -Nru fiona-1.6.3/debian/patches/series fiona-1.6.3/debian/patches/series
--- fiona-1.6.3/debian/patches/series	2015-12-25 01:56:16.000000000 +0100
+++ fiona-1.6.3/debian/patches/series	2016-02-07 22:53:27.000000000 +0100
@@ -1,2 +1,7 @@
 0001-Rename-fio-command-to-fiona-to-avoid-name-clash.patch
 0002-Remove-outside-reference-possible-privacy-breach.patch
+0003-GDAL-2.0.patch
+0004-Just-use-int-as-a-plain-old-builtin.patch
+0005-Initial-attempt-at-fiona.remove-for-deleting-data-sources.patch
+0006-Remove-unknown-distribution-options.patch
+0007-clean.patch


More information about the Pkg-grass-devel mailing list