[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