This is an automated email from the git hooks/post-receive script.

johanvdw-guest pushed a commit to branch master
in repository fiona.

commit 233acfc2810171908a26933e32585aa7ebfc3738
Author: Johan Van de Wauw <jo...@vandewauw.be>
Date:   Wed Sep 23 22:29:36 2015 +0200

    Imported Upstream version 1.6.2
---
 CHANGES.txt                   |  8 ++++++
 fiona/__init__.py             | 29 +++++++++++--------
 fiona/collection.py           | 35 +++++++++++++++--------
 fiona/ogrext.pyx              | 66 ++++++++++++++++++++++++++++++-------------
 tests/test_bytescollection.py |  2 +-
 tests/test_collection.py      |  2 +-
 tests/test_collection_crs.py  | 22 +++++++++++++++
 tests/test_profile.py         | 20 +++++++++++++
 tests/test_props.py           | 43 ++++++++++++++++++++++++++++
 9 files changed, 182 insertions(+), 45 deletions(-)

diff --git a/CHANGES.txt b/CHANGES.txt
index fbbf1ef..64e8d90 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -3,6 +3,14 @@ Changes
 
 All issue numbers are relative to https://github.com/Toblerity/Fiona/issues.
 
+1.6.2 (2015-09-22)
+------------------
+- Providing only PROJ4 representations in the dataset meta property resulted in
+  loss of CRS information when using the `fiona.open(..., **src.meta) as dst`
+  pattern (#265). This bug has been addressed by adding a crs_wkt item to the
+  meta property and extending the `fiona.open()` and the collection constructor
+  to look for and prioritize this keyword argument.
+
 1.6.1 (2015-08-12)
 ------------------
 - Bug fix: Fiona now deserializes JSON-encoded string properties provided by
diff --git a/fiona/__init__.py b/fiona/__init__.py
index 4c45226..ac0592c 100644
--- a/fiona/__init__.py
+++ b/fiona/__init__.py
@@ -63,7 +63,7 @@ writing modes) flush contents to disk when their ``with`` 
blocks end.
 """
 
 __all__ = ['bounds', 'listlayers', 'open', 'prop_type', 'prop_width']
-__version__ = "1.6.1"
+__version__ = "1.6.2"
 
 import logging
 import os
@@ -87,15 +87,16 @@ log.addHandler(NullHandler())
 
 
 def open(
-        path, 
-        mode='r', 
-        driver=None, 
-        schema=None, 
+        path,
+        mode='r',
+        driver=None,
+        schema=None,
         crs=None,
         encoding=None,
         layer=None,
         vfs=None,
-        enabled_drivers=None):
+        enabled_drivers=None,
+        crs_wkt=None):
     
     """Open file at ``path`` in ``mode`` "r" (read), "a" (append), or
     "w" (write) and return a ``Collection`` object.
@@ -118,7 +119,13 @@ def open(
     
       {'proj': 'longlat', 'ellps': 'WGS84', 'datum': 'WGS84', 
        'no_defs': True}
-    
+
+    short hand strings like
+
+      EPSG:4326
+
+    or WKT representations of coordinate reference systems.
+
     The drivers used by Fiona will try to detect the encoding of data
     files. If they fail, you may provide the proper ``encoding``, such
     as 'Windows-1252' for the Natural Earth datasets.
@@ -163,10 +170,10 @@ def open(
             this_schema['properties'] = OrderedDict(schema['properties'])
         else:
             this_schema = None
-        c = Collection(path, mode, 
-                crs=crs, driver=driver, schema=this_schema, 
+        c = Collection(path, mode,
+                crs=crs, driver=driver, schema=this_schema,
                 encoding=encoding, layer=layer, vsi=vsi, archive=archive,
-                enabled_drivers=enabled_drivers)
+                enabled_drivers=enabled_drivers, crs_wkt=crs_wkt)
     else:
         raise ValueError(
             "mode string must be one of 'r', 'w', or 'a', not %s" % mode)
@@ -174,6 +181,7 @@ def open(
 
 collection = open
 
+
 def listlayers(path, vfs=None):
     """Returns a list of layer names in their index order.
     
@@ -257,4 +265,3 @@ def bounds(ob):
     The ``ob`` may be a feature record or geometry."""
     geom = ob.get('geometry') or ob
     return _bounds(geom)
-
diff --git a/fiona/collection.py b/fiona/collection.py
index e284242..d553582 100644
--- a/fiona/collection.py
+++ b/fiona/collection.py
@@ -15,8 +15,12 @@ from six import string_types, binary_type
 
 class Collection(object):
 
-    """A file-like interface to features in the form of GeoJSON-like
-    mappings."""
+    """A file-like interface to features of a vector dataset
+    
+    Python text file objects are iterators over lines of a file. Fiona
+    Collections are similar iterators (not lists!) over features
+    represented as GeoJSON-like mappings.
+    """
 
     def __init__(
             self, path, mode='r', 
@@ -26,6 +30,7 @@ class Collection(object):
             vsi=None,
             archive=None,
             enabled_drivers=None,
+            crs_wkt=None,
             **kwargs):
         
         """The required ``path`` is the absolute or relative path to
@@ -35,7 +40,9 @@ class Collection(object):
         a file.
         
         In ``mode`` 'w', an OGR ``driver`` name and a ``schema`` are
-        required. A Proj4 ``crs`` string is recommended.
+        required. A Proj4 ``crs`` string is recommended. If both ``crs``
+        and ``crs_wkt`` keyword arguments are passed, the latter will 
+        trump the former.
         
         In 'w' mode, kwargs will be mapped to OGR layer creation
         options.
@@ -51,6 +58,8 @@ class Collection(object):
             raise TypeError("invalid schema: %r" % schema)
         if crs and not isinstance(crs, (dict,) + string_types):
             raise TypeError("invalid crs: %r" % crs)
+        if crs_wkt and not isinstance(crs_wkt, string_types):
+            raise TypeError("invalid crs_wkt: %r" % crs_wkt)
         if encoding and not isinstance(encoding, string_types):
             raise TypeError("invalid encoding: %r" % encoding)
         if layer and not isinstance(layer, tuple(list(string_types) + [int])):
@@ -119,13 +128,15 @@ class Collection(object):
             elif 'geometry' not in schema:
                 raise SchemaError("schema lacks: geometry")
             self._schema = schema
-            
-            if crs:
+
+            if crs_wkt:
+                self._crs_wkt = crs_wkt
+            elif crs:
                 if 'init' in crs or 'proj' in crs or 'epsg' in crs.lower():
                     self._crs = crs
                 else:
                     raise CRSError("crs lacks init or proj parameter")
-        
+
         if driver_count == 0:
             # create a local manager and enter
             self.env = GDALEnv()
@@ -189,24 +200,24 @@ class Collection(object):
     @property
     def crs(self):
         """Returns a Proj4 string."""
-        if self._crs is None and self.mode in ("a", "r") and self.session:
+        if self._crs is None and self.session:
             self._crs = self.session.get_crs()
         return self._crs
 
     @property
     def crs_wkt(self):
         """Returns a WKT string."""
-        if self._crs_wkt is None and self.mode in ("a", "r") and self.session:
+        if self._crs_wkt is None and self.session:
             self._crs_wkt = self.session.get_crs_wkt()
         return self._crs_wkt
 
     @property
     def meta(self):
-        """Returns a mapping with the driver, schema, and crs properties."""
+        """Returns a mapping with the driver, schema, crs, and additional
+        properties."""
         return {
-            'driver': self.driver, 
-            'schema': self.schema, 
-            'crs': self.crs }
+            'driver': self.driver, 'schema': self.schema, 'crs': self.crs,
+            'crs_wkt': self.crs_wkt}
 
     def filter(self, *args, **kwds):
         """Returns an iterator over records, but filtered by a test for
diff --git a/fiona/ogrext.pyx b/fiona/ogrext.pyx
index 4defcb1..85b9b02 100644
--- a/fiona/ogrext.pyx
+++ b/fiona/ogrext.pyx
@@ -136,7 +136,7 @@ cdef class FeatureBuilder:
     argument is not destroyed.
     """
 
-    cdef build(self, void *feature, encoding='utf-8', bbox=False):
+    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
@@ -186,7 +186,7 @@ cdef class FeatureBuilder:
 
                 # Does the text contain a JSON object? Let's check.
                 # Let's check as cheaply as we can.
-                if val.startswith('{'):
+                if driver == 'GeoJSON' and val.startswith('{'):
                     try:
                         val = json.loads(val)
                     except ValueError as err:
@@ -335,7 +335,12 @@ def featureRT(feature, collection):
         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, encoding)
+    result = FeatureBuilder().build(
+        cogr_feature,
+        bbox=False,
+        encoding=encoding,
+        driver=collection.driver
+    )
     _deleteOgrFeature(cogr_feature)
     return result
 
@@ -587,14 +592,17 @@ cdef class Session:
         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)
+            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):
@@ -652,7 +660,11 @@ cdef class Session:
             if cogr_feature == NULL:
                 return None
             feature = FeatureBuilder().build(
-                        cogr_feature, self.get_internalencoding())
+                cogr_feature,
+                bbox=False,
+                encoding=self.get_internalencoding(),
+                driver=self.collection.driver
+            )
             _deleteOgrFeature(cogr_feature)
             return feature
 
@@ -760,19 +772,23 @@ cdef class WritingSession(Session):
             else:
                 self.cogr_ds = cogr_ds
 
-            # Set the spatial reference system from the given crs.
-            if collection.crs:
+            # 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(collection.crs, string_types):
-                    proj_b = collection.crs.encode('utf-8')
+                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(collection.crs, dict):
+                elif isinstance(col_crs, dict):
                     # EPSG is a special case.
-                    init = collection.crs.get('init')
+                    init = col_crs.get('init')
                     if init:
                         log.debug("Init: %s", init)
                         auth, val = init.split(':')
@@ -781,8 +797,8 @@ cdef class WritingSession(Session):
                             ograpi.OSRImportFromEPSG(cogr_srs, int(val))
                     else:
                         params = []
-                        collection.crs['wktext'] = True
-                        for k, v in collection.crs.items():
+                        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:
@@ -1097,7 +1113,12 @@ cdef class Iterator:
         if cogr_feature == NULL:
             raise StopIteration
 
-        feature = FeatureBuilder().build(cogr_feature, self.encoding)
+        feature = FeatureBuilder().build(
+            cogr_feature,
+            bbox=False,
+            encoding=self.encoding,
+            driver=self.collection.driver
+        )
         _deleteOgrFeature(cogr_feature)
         return feature
 
@@ -1121,7 +1142,12 @@ cdef class ItemsIterator(Iterator):
 
 
         fid = ograpi.OGR_F_GetFID(cogr_feature)
-        feature = FeatureBuilder().build(cogr_feature, self.encoding)
+        feature = FeatureBuilder().build(
+            cogr_feature,
+            bbox=False,
+            encoding=self.encoding,
+            driver=self.collection.driver
+        )
         _deleteOgrFeature(cogr_feature)
 
         return fid, feature
diff --git a/tests/test_bytescollection.py b/tests/test_bytescollection.py
index e9eaccf..3aecd30 100644
--- a/tests/test_bytescollection.py
+++ b/tests/test_bytescollection.py
@@ -130,7 +130,7 @@ class ReadingTest(unittest.TestCase):
     def test_meta(self):
         self.failUnlessEqual(
             sorted(self.c.meta.keys()),
-            ['crs', 'driver', 'schema'])
+            ['crs', 'crs_wkt', 'driver', 'schema'])
 
     def test_bounds(self):
         self.failUnlessAlmostEqual(self.c.bounds[0], -113.564247, 6)
diff --git a/tests/test_collection.py b/tests/test_collection.py
index ebd45e0..f49608e 100644
--- a/tests/test_collection.py
+++ b/tests/test_collection.py
@@ -185,7 +185,7 @@ class ReadingTest(unittest.TestCase):
     def test_meta(self):
         self.failUnlessEqual(
             sorted(self.c.meta.keys()), 
-            ['crs', 'driver', 'schema'])
+            ['crs', 'crs_wkt', 'driver', 'schema'])
 
     def test_bounds(self):
         self.failUnlessAlmostEqual(self.c.bounds[0], -113.564247, 6)
diff --git a/tests/test_collection_crs.py b/tests/test_collection_crs.py
new file mode 100644
index 0000000..ce483ad
--- /dev/null
+++ b/tests/test_collection_crs.py
@@ -0,0 +1,22 @@
+import os
+import tempfile
+
+import fiona
+
+
+def test_collection_crs_wkt():
+    with fiona.open('tests/data/coutwildrnp.shp') as src:
+        assert 
src.crs_wkt.startswith('GEOGCS["GCS_WGS_1984",DATUM["WGS_1984",SPHEROID["WGS_84"')
+
+
+def test_collection_no_crs_wkt():
+    """crs members of a dataset with no crs can be accessed safely."""
+    tmpdir = tempfile.gettempdir()
+    filename = os.path.join(tmpdir, 'test.shp')
+    with fiona.open('tests/data/coutwildrnp.shp') as src:
+        profile = src.meta
+    del profile['crs']
+    del profile['crs_wkt']
+    with fiona.open(filename, 'w', **profile) as dst:
+        assert dst.crs_wkt == ""
+        assert dst.crs == {}
diff --git a/tests/test_profile.py b/tests/test_profile.py
new file mode 100644
index 0000000..294ca67
--- /dev/null
+++ b/tests/test_profile.py
@@ -0,0 +1,20 @@
+import os
+import tempfile
+
+import fiona
+
+
+def test_profile():
+    with fiona.open('tests/data/coutwildrnp.shp') as src:
+        assert src.meta['crs_wkt'] == 
'GEOGCS["GCS_WGS_1984",DATUM["WGS_1984",SPHEROID["WGS_84",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295],AUTHORITY["EPSG","4326"]]'
+
+
+def test_profile_creation_wkt():
+    tmpdir = tempfile.mkdtemp()
+    outfilename = os.path.join(tmpdir, 'test.shp')
+    with fiona.open('tests/data/coutwildrnp.shp') as src:
+        profile = src.meta
+        profile['crs'] = 'bogus'
+        with fiona.open(outfilename, 'w', **profile) as dst:
+            assert dst.crs == {'init': 'epsg:4326'}
+            assert dst.crs_wkt == 
'GEOGCS["GCS_WGS_1984",DATUM["WGS_1984",SPHEROID["WGS_84",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295],AUTHORITY["EPSG","4326"]]'
diff --git a/tests/test_props.py b/tests/test_props.py
index 7f36a3b..2f562ac 100644
--- a/tests/test_props.py
+++ b/tests/test_props.py
@@ -151,3 +151,46 @@ def test_write_json_object_properties():
         assert props['upperLeftCoordinate']['latitude'] == 45.66894
         assert props['upperLeftCoordinate']['longitude'] == 87.91166
         assert props['tricky'] == "{gotcha"
+
+
+def test_json_prop_decode_non_geojson_driver():
+    feature = {
+        "type": "Feature",
+        "properties": {
+            "ulc": {
+                "latitude": 45.66894,
+                "longitude": 87.91166
+            },
+            "tricky": "{gotcha"
+        },
+        "geometry": {
+            "type": "Point",
+            "coordinates": [10, 15]
+        }
+    }
+
+    meta = {
+        'crs': 'EPSG:4326',
+        'driver': 'ESRI Shapefile',
+        'schema': {
+            'geometry': 'Point',
+            'properties': {
+                'ulc': 'str:255',
+                'tricky': 'str:255'
+            }
+        }
+    }
+
+    tmpdir = tempfile.mkdtemp()
+    filename = os.path.join(tmpdir, 'test.json')
+    with fiona.open(filename, 'w', **meta) as dst:
+        dst.write(feature)
+
+    with fiona.open(filename) as src:
+        actual = next(src)
+
+    assert isinstance(actual['properties']['ulc'], text_type)
+    a = json.loads(actual['properties']['ulc'])
+    e = json.loads(actual['properties']['ulc'])
+    assert e == a
+    assert actual['properties']['tricky'].startswith('{')

-- 
Alioth's /usr/local/bin/git-commit-notice on 
/srv/git.debian.org/git/pkg-grass/fiona.git

_______________________________________________
Pkg-grass-devel mailing list
Pkg-grass-devel@lists.alioth.debian.org
http://lists.alioth.debian.org/cgi-bin/mailman/listinfo/pkg-grass-devel

Reply via email to