[fiona] 01/06: Imported Upstream version 1.6.2
Johan Van de Wauw
johanvdw-guest at moszumanska.debian.org
Wed Sep 23 21:56:40 UTC 2015
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 <johan at 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
More information about the Pkg-grass-devel
mailing list