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

Sebastiaan Couwenberg sebastic at xs4all.nl
Fri Oct 23 21:10:11 UTC 2015


Control: tags -1 patch

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.

Kind Regards,

Bas
-------------- next part --------------
diff -Nru fiona-1.6.2/debian/changelog fiona-1.6.2/debian/changelog
--- fiona-1.6.2/debian/changelog	2015-10-16 13:31:36.000000000 +0200
+++ fiona-1.6.2/debian/changelog	2015-10-23 22:48:33.000000000 +0200
@@ -1,3 +1,12 @@
+fiona (1.6.2-2) UNRELEASED; urgency=medium
+
+  * Team upload.
+  * Add patch for GDAL 2.0 support.
+  * Add patch to not cythonize in the clean target.
+  * Add patch to fix builtins ImportError with Python 2.
+
+ -- Bas Couwenberg <sebastic at debian.org>  Fri, 23 Oct 2015 21:50:46 +0200
+
 fiona (1.6.2-1) unstable; urgency=medium
 
   * Imported Upstream version 1.6.2
diff -Nru fiona-1.6.2/debian/patches/0003-GDAL-2.0.patch fiona-1.6.2/debian/patches/0003-GDAL-2.0.patch
--- fiona-1.6.2/debian/patches/0003-GDAL-2.0.patch	1970-01-01 01:00:00.000000000 +0100
+++ fiona-1.6.2/debian/patches/0003-GDAL-2.0.patch	2015-10-23 22:02:31.000000000 +0200
@@ -0,0 +1,4432 @@
+Origin: https://github.com/Toblerity/Fiona/pull/275
+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/10] 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,1233 +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
+-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 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
+-                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 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
+-                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 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/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,1307 @@
++# 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
++
++from libc.stdlib cimport malloc, free
++from libc.string cimport strcmp
++from builtins import int
++
++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
+@@ -72,11 +72,11 @@ 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():
+@@ -138,6 +138,16 @@ if os.path.exists("MANIFEST.in"):
+             "Cython.Build.cythonize not found. "
+             "Cython is required to build from a repo.")
+         sys.exit(1)
++
++    if gdal_output[3][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')
++
+     ext_modules = cythonize([
+         Extension('fiona._geometry', ['fiona/_geometry.pyx'], **ext_options),
+         Extension('fiona._transform', ['fiona/_transform.pyx'], **ext_options),
+--- a/requirements.txt
++++ b/requirements.txt
+@@ -2,3 +2,4 @@ argparse
+ cligj
+ six
+ ordereddict
++future
+--- /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
+@@ -6,7 +6,7 @@ import tempfile
+ import fiona
+ from fiona import prop_type, prop_width
+ from fiona.rfc3339 import FionaDateType
+-
++from builtins import int
+ 
+ def test_width_str():
+     assert prop_width('str:254') == 254
+@@ -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
+ 
+ 
diff -Nru fiona-1.6.2/debian/patches/0004-clean-setup.patch fiona-1.6.2/debian/patches/0004-clean-setup.patch
--- fiona-1.6.2/debian/patches/0004-clean-setup.patch	1970-01-01 01:00:00.000000000 +0100
+++ fiona-1.6.2/debian/patches/0004-clean-setup.patch	2015-10-23 22:12:29.000000000 +0200
@@ -0,0 +1,15 @@
+Description: Don't cythonize in clean target.
+ Just like [rasterio](https://github.com/mapbox/rasterio/blob/master/setup.py#L141)
+Author: Bas Couwenberg <sebastic at debian.org>
+
+--- a/setup.py
++++ b/setup.py
+@@ -131,7 +131,7 @@ 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(
diff -Nru fiona-1.6.2/debian/patches/0005-builtins.patch fiona-1.6.2/debian/patches/0005-builtins.patch
--- fiona-1.6.2/debian/patches/0005-builtins.patch	1970-01-01 01:00:00.000000000 +0100
+++ fiona-1.6.2/debian/patches/0005-builtins.patch	2015-10-23 22:48:06.000000000 +0200
@@ -0,0 +1,40 @@
+Description: Use __builtin__ module for Python 2.
+ Fixes exception:
+  ImportError: No module named builtins
+Author: Bas Couwenberg <sebastic at debian.org>
+
+--- a/fiona/ogrext2.pyx
++++ b/fiona/ogrext2.pyx
+@@ -23,7 +23,11 @@ from fiona.rfc3339 import FionaDateType,
+ 
+ from libc.stdlib cimport malloc, free
+ from libc.string cimport strcmp
+-from builtins import int
++
++if sys.version_info > (3,):
++    from builtins import int
++else:
++    from __builtin__ import int
+ 
+ log = logging.getLogger("Fiona")
+ class NullHandler(logging.Handler):
+--- a/tests/test_props.py
++++ b/tests/test_props.py
+@@ -2,11 +2,16 @@ import json
+ import os.path
+ from six import text_type
+ import tempfile
++import sys
+ 
+ import fiona
+ from fiona import prop_type, prop_width
+ from fiona.rfc3339 import FionaDateType
+-from builtins import int
++
++if sys.version_info > (3,):
++    from builtins import int
++else:
++    from __builtin__ import int
+ 
+ def test_width_str():
+     assert prop_width('str:254') == 254
diff -Nru fiona-1.6.2/debian/patches/series fiona-1.6.2/debian/patches/series
--- fiona-1.6.2/debian/patches/series	2015-08-16 22:38:51.000000000 +0200
+++ fiona-1.6.2/debian/patches/series	2015-10-23 22:40:32.000000000 +0200
@@ -1,2 +1,5 @@
 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-clean-setup.patch
+0005-builtins.patch


More information about the Pkg-grass-devel mailing list