[python-shapely] 01/05: New upstream version 1.6.3

Bas Couwenberg sebastic at debian.org
Sun Dec 10 10:51:06 UTC 2017


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

sebastic pushed a commit to branch master
in repository python-shapely.

commit ee596a49f29dcfeb53aba22f50fc6f816c1b0d53
Author: Bas Couwenberg <sebastic at xs4all.nl>
Date:   Sun Dec 10 11:33:43 2017 +0100

    New upstream version 1.6.3
---
 CHANGES.txt                    | 17 +++++++++++++++++
 docs/manual.rst                | 18 +++++++++++++++---
 shapely/__init__.py            |  2 +-
 shapely/geometry/base.py       | 18 +++++++++++++++---
 shapely/geometry/polygon.py    | 21 ++++++++++++++++-----
 shapely/geometry/proxy.py      |  3 ++-
 shapely/speedups/_speedups.pyx |  3 +++
 shapely/strtree.py             |  4 +++-
 tests/test_emptiness.py        | 10 ++++++++++
 tests/test_geointerface.py     | 18 +++++++++++++++++-
 tests/test_pickle.py           | 36 +++++++++++++++++++++---------------
 11 files changed, 120 insertions(+), 30 deletions(-)

diff --git a/CHANGES.txt b/CHANGES.txt
index 8dd19ad..393f8c2 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,6 +1,23 @@
 Changes
 =======
 
+1.6.3 (2017-12-09)
+------------------
+
+- AttributeError is no longer raised when accessing __geo_interface__ of an
+  empty polygon (#450).
+- ``asShape`` now handles empty coordinates in mappings as ``shape`` does
+  (#542). Please note that ``asShape`` is likely to be deprecated in a future
+  version of Shapely.
+- Check for length of LineString coordinates in speed mode, preventing crashes
+  when using LineStrings with only one coordinate (#546).
+
+1.6.2 (2017-10-30)
+------------------
+
+- A 1.6.2.post1 release has been made to fix a problem with macosx wheels
+  uploaded to PyPI.
+
 1.6.2 (2017-10-26)
 ------------------
 
diff --git a/docs/manual.rst b/docs/manual.rst
index 8ad4665..5d934e3 100644
--- a/docs/manual.rst
+++ b/docs/manual.rst
@@ -157,7 +157,7 @@ themselves as instance factories. A few of their intrinsic properties will be
 discussed in this sections, others in the following sections on operations and
 serializations.
 
-Instances of `Point`, `LineString`, and `LinearRing` have as their most
+Instances of ``Point``, ``LineString``, and ``LinearRing`` have as their most
 important attribute a finite sequence of coordinates that determines their
 interior, boundary, and exterior point sets. A line string can be determined by
 as few as 2 points, but contains an infinite number of points. Coordinate
@@ -169,8 +169,20 @@ In all constructors, numeric values are converted to type ``float``. In other
 words, ``Point(0, 0)`` and ``Point(0.0, 0.0)`` produce geometrically equivalent
 instances. Shapely does not check the topological simplicity or validity of
 instances when they are constructed as the cost is unwarranted in most cases.
-Validating factories are trivially implemented, using the :attr:`is_valid`
-predicate, by users that require them.
+Validating factories are easily implemented using the :attr:``is_valid``
+predicate by users that require them.
+
+.. note::
+
+   Shapely is a planar geometry library and `z`, the height
+   above or below the plane, is ignored in geometric analysis. There is
+   a potential pitfall for users here: coordinate tuples that differ only in
+   `z` are not distinguished from each other and their application can result
+   in suprisingly invalid geometry objects. For example, ``LineString([(0, 0,
+   0), (0, 0, 1)])`` does not return a vertical line of unit length, but an invalid line
+   in the plane with zero length. Similarly, ``Polygon([(0, 0, 0), (0, 0, 1),
+   (1, 1, 1)])`` is not bounded by a closed ring and is invalid.
+
 
 General Attributes and Methods
 ------------------------------
diff --git a/shapely/__init__.py b/shapely/__init__.py
index 51bbb3f..31e744e 100644
--- a/shapely/__init__.py
+++ b/shapely/__init__.py
@@ -1 +1 @@
-__version__ = "1.6.2"
+__version__ = "1.6.3"
diff --git a/shapely/geometry/base.py b/shapely/geometry/base.py
index 202d923..4931295 100644
--- a/shapely/geometry/base.py
+++ b/shapely/geometry/base.py
@@ -699,7 +699,11 @@ class BaseGeometry(object):
         return bool(self.impl['disjoint'](self, other))
 
     def equals(self, other):
-        """Returns True if geometries are equal, else False"""
+        """Returns True if geometries are equal, else False
+        
+        Refers to point-set equality (or topological equality), and is equivalent to
+        (self.within(other) & self.contains(other))
+        """
         return bool(self.impl['equals'](self, other))
 
     def intersects(self, other):
@@ -720,12 +724,20 @@ class BaseGeometry(object):
 
     def equals_exact(self, other, tolerance):
         """Returns True if geometries are equal to within a specified
-        tolerance"""
+        tolerance
+        
+        Refers to coordinate equality, which requires coordinates to be equal 
+        and in the same order for all components of a geometry
+        """
         return bool(self.impl['equals_exact'](self, other, tolerance))
 
     def almost_equals(self, other, decimal=6):
         """Returns True if geometries are equal at all coordinates to a
-        specified decimal place"""
+        specified decimal place
+
+        Refers to approximate coordinate equality, which requires coordinates be
+        approximately equal and in the same order for all components of a geometry.
+        """
         return self.equals_exact(other, 0.5 * 10**(-decimal))
 
     def relate_pattern(self, other, pattern):
diff --git a/shapely/geometry/polygon.py b/shapely/geometry/polygon.py
index 9602df5..b619878 100644
--- a/shapely/geometry/polygon.py
+++ b/shapely/geometry/polygon.py
@@ -71,6 +71,15 @@ class LinearRing(LineString):
 
     coords = property(_get_coords, _set_coords)
 
+    def __setstate__(self, state):
+        """WKB doesn't differentiate between LineString and LinearRing so we
+        need to move the coordinate sequence into the correct geometry type"""
+        super(LinearRing, self).__setstate__(state)
+        cs = lgeos.GEOSGeom_getCoordSeq(self.__geom__)
+        cs_clone = lgeos.GEOSCoordSeq_clone(cs)
+        lgeos.GEOSGeom_destroy(self.__geom__)
+        self.__geom__ = lgeos.GEOSGeom_createLinearRing(cs_clone)
+
     @property
     def is_ccw(self):
         """True is the ring is oriented counter clock-wise"""
@@ -303,13 +312,15 @@ class Polygon(BaseGeometry):
 
     @property
     def __geo_interface__(self):
-        coords = [tuple(self.exterior.coords)]
-        for hole in self.interiors:
-            coords.append(tuple(hole.coords))
+        if not self.exterior:
+            coords = []
+        else:
+            coords = [tuple(self.exterior.coords)]
+            for hole in self.interiors:
+                coords.append(tuple(hole.coords))
         return {
             'type': 'Polygon',
-            'coordinates': tuple(coords)
-            }
+            'coordinates': tuple(coords)}
 
     def svg(self, scale_factor=1., fill_color=None):
         """Returns SVG path element for the Polygon geometry.
diff --git a/shapely/geometry/proxy.py b/shapely/geometry/proxy.py
index c9cad3a..28598e0 100644
--- a/shapely/geometry/proxy.py
+++ b/shapely/geometry/proxy.py
@@ -30,7 +30,8 @@ class CachingGeometryProxy(object):
         gtag = self.gtag()
         if gtag != self._gtag or self._is_empty:
             self.empty()
-            self.__geom__, n = self.factory(self.context)
+            if len(self.context) > 0:
+                self.__geom__, n = self.factory(self.context)
         self._gtag = gtag
         return self.__geom__
         
diff --git a/shapely/speedups/_speedups.pyx b/shapely/speedups/_speedups.pyx
index 8754c1b..5faac47 100644
--- a/shapely/speedups/_speedups.pyx
+++ b/shapely/speedups/_speedups.pyx
@@ -148,6 +148,9 @@ def geos_linestring_from_py(ob, update_geom=None, update_ndim=0):
 
         if m == 0:
             return None
+        elif m < 2:
+            raise ValueError(
+                "LineStrings must have at least 2 coordinate tuples")
 
         def _coords(o):
             if isinstance(o, Point):
diff --git a/shapely/strtree.py b/shapely/strtree.py
index 161554d..121b740 100644
--- a/shapely/strtree.py
+++ b/shapely/strtree.py
@@ -43,7 +43,9 @@ class STRtree:
         self._geoms = list(geoms)
 
     def __del__(self):
-        lgeos.GEOSSTRtree_destroy(self._tree_handle)
+        if self._tree_handle is not None:
+            lgeos.GEOSSTRtree_destroy(self._tree_handle)
+            self._tree_handle = None
 
     def query(self, geom):
         if self._n_geoms == 0:
diff --git a/tests/test_emptiness.py b/tests/test_emptiness.py
index 91d5ec2..91b6a01 100644
--- a/tests/test_emptiness.py
+++ b/tests/test_emptiness.py
@@ -3,6 +3,9 @@ from shapely.geometry.base import BaseGeometry, EmptyGeometry
 import shapely.geometry as sgeom
 from shapely.geometry.polygon import LinearRing
 
+from shapely.geometry import MultiPolygon, mapping, shape, asShape
+
+
 empty_generator = lambda: iter([])
 
 class EmptinessTestCase(unittest.TestCase):
@@ -60,6 +63,13 @@ class EmptinessTestCase(unittest.TestCase):
         self.assertTrue(LinearRing(empty_generator()).is_empty)
 
 
+def test_asshape_empty():
+    empty_mp = MultiPolygon()
+    empty_json = mapping(empty_mp)
+    empty_asShape = asShape(empty_json)
+    assert empty_asShape.is_empty
+
+
 def test_suite():
     return unittest.TestLoader().loadTestsFromTestCase(EmptinessTestCase)
 
diff --git a/tests/test_geointerface.py b/tests/test_geointerface.py
index 34130a3..b9514b9 100644
--- a/tests/test_geointerface.py
+++ b/tests/test_geointerface.py
@@ -1,10 +1,12 @@
 from . import unittest
+
 from shapely.geometry import asShape
 from shapely.geometry.multipoint import MultiPointAdapter
 from shapely.geometry.linestring import LineStringAdapter
 from shapely.geometry.multilinestring import MultiLineStringAdapter
-from shapely.geometry.polygon import PolygonAdapter
+from shapely.geometry.polygon import Polygon, PolygonAdapter
 from shapely.geometry.multipolygon import MultiPolygonAdapter
+from shapely import wkt
 
 
 class GeoThing(object):
@@ -70,5 +72,19 @@ class GeoInterfaceTestCase(unittest.TestCase):
         self.assertEqual(len(shape.geoms), 1)
 
 
+def test_empty_wkt_polygon():
+    """Confirm fix for issue #450"""
+    g = wkt.loads('POLYGON EMPTY')
+    assert g.__geo_interface__['type'] == 'Polygon'
+    assert g.__geo_interface__['coordinates'] == ()
+
+
+def test_empty_polygon():
+    """Confirm fix for issue #450"""
+    g = Polygon()
+    assert g.__geo_interface__['type'] == 'Polygon'
+    assert g.__geo_interface__['coordinates'] == ()
+
+
 def test_suite():
     return unittest.TestLoader().loadTestsFromTestCase(GeoInterfaceTestCase)
diff --git a/tests/test_pickle.py b/tests/test_pickle.py
index 0c0692a..182ee93 100644
--- a/tests/test_pickle.py
+++ b/tests/test_pickle.py
@@ -1,5 +1,5 @@
-from . import unittest
-from shapely import geometry
+import pytest
+from shapely.geometry import Point, LineString, LinearRing, Polygon, MultiPoint
 
 import sys
 if sys.version_info[0] >= 3:
@@ -7,16 +7,22 @@ if sys.version_info[0] >= 3:
 else:
     from cPickle import dumps, loads, HIGHEST_PROTOCOL
 
-
-class TwoDeeTestCase(unittest.TestCase):
-
-    def test_linestring(self):
-        l = geometry.LineString(((0.0, 0.0), (0.0, 1.0), (1.0, 1.0)))
-        self.assertEqual(l._ndim, 2)
-        s = dumps(l, HIGHEST_PROTOCOL)
-        t = loads(s)
-        self.assertEqual(t._ndim, 2)
-
-
-def test_suite():
-    return unittest.TestLoader().loadTestsFromTestCase(TwoDeeTestCase)
+TEST_DATA = {
+    "point2d": (Point, [(1.0, 2.0)]),
+    "point3d": (Point, [(1.0, 2.0, 3.0)]),
+    "linestring": (LineString, [(0.0, 0.0), (0.0, 1.0), (1.0, 1.0)]),
+    "linearring": (LinearRing, [(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 0.0)]),
+    "polygon": (Polygon, [(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 0.0)]),
+    "multipoint": (MultiPoint, [(1.0, 2.0), (3.0, 4.0), (5.0, 6.0)]),
+}
+TEST_NAMES, TEST_DATA = zip(*TEST_DATA.items())
+ at pytest.mark.parametrize("cls,coords", TEST_DATA, ids=TEST_NAMES)
+def test_pickle_round_trip(cls, coords):
+    geom1 = cls(coords)
+    assert geom1.has_z == (len(coords[0]) == 3)
+    data = dumps(geom1, HIGHEST_PROTOCOL)
+    geom2 = loads(data)
+    assert geom2.has_z == geom1.has_z
+    assert type(geom2) is type(geom1)
+    assert geom2.type == geom1.type
+    assert geom2.wkt == geom1.wkt

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



More information about the Pkg-grass-devel mailing list