[python-shapely] 14/148: Imported Upstream version 1.2.1
Sebastiaan Couwenberg
sebastic at moszumanska.debian.org
Thu Aug 20 17:41:58 UTC 2015
This is an automated email from the git hooks/post-receive script.
sebastic pushed a commit to branch master
in repository python-shapely.
commit 0d703048a7bc834d3ba2d40999301d58654c417b
Author: Pietro Battiston <me at pietrobattiston.it>
Date: Thu Jun 24 15:04:08 2010 +0200
Imported Upstream version 1.2.1
---
._CHANGES.txt | Bin 186 -> 0 bytes
._CREDITS.txt | Bin 185 -> 0 bytes
._HISTORY.txt | Bin 184 -> 0 bytes
._LICENSE.txt | Bin 184 -> 0 bytes
._MANIFEST.in | Bin 186 -> 0 bytes
._README.txt | Bin 187 -> 0 bytes
CHANGES.txt | 89 ++-
CREDITS.txt | 33 +-
GEOS-C-API.txt | 55 --
HISTORY.txt | 24 -
MANIFEST.in | 8 +-
PKG-INFO | 201 +++--
README.txt | 191 +++--
Shapely.egg-info/PKG-INFO | 201 +++--
Shapely.egg-info/SOURCES.txt | 64 +-
Shapely.egg-info/requires.txt | 1 -
examples/._geoms.py | Bin 184 -> 0 bytes
examples/._world.py | Bin 184 -> 0 bytes
examples/dissolve.py | 53 ++
examples/geoms.py | 51 --
examples/intersect.py | 81 ++
examples/world.py | 21 -
manual/manual.txt | 875 ----------------------
setup.py | 63 +-
shapely/.___init__.py | Bin 185 -> 0 bytes
shapely/._ctypes_declarations.py | Bin 187 -> 0 bytes
shapely/._iterops.py | Bin 184 -> 0 bytes
shapely/._ops.py | Bin 184 -> 0 bytes
shapely/._predicates.py | Bin 186 -> 0 bytes
shapely/._topology.py | Bin 187 -> 0 bytes
shapely/._wkb.py | Bin 187 -> 0 bytes
shapely/__init__.py | 2 +-
shapely/coords.py | 167 +++++
shapely/ctypes_declarations.py | 100 ++-
shapely/geometry/._base.py | Bin 184 -> 0 bytes
shapely/geometry/._linestring.py | Bin 184 -> 0 bytes
shapely/geometry/__init__.py | 12 +
shapely/geometry/base.py | 786 ++++++++++---------
shapely/geometry/collection.py | 16 +-
shapely/geometry/geo.py | 8 +-
shapely/geometry/linestring.py | 257 +++----
shapely/geometry/multilinestring.py | 160 ++--
shapely/geometry/multipoint.py | 155 ++--
shapely/geometry/multipolygon.py | 153 ++--
shapely/geometry/point.py | 245 +++---
shapely/geometry/polygon.py | 450 ++++++-----
shapely/geometry/proxy.py | 32 +-
shapely/geos.py | 296 +++++++-
shapely/impl.py | 81 ++
shapely/iterops.py | 11 +-
shapely/linref.py | 26 +
shapely/ops.py | 110 ++-
shapely/predicates.py | 81 +-
shapely/prepared.py | 54 ++
{tests => shapely/tests}/Array.txt | 0
{tests => shapely/tests}/GeoInterface.txt | 0
{tests => shapely/tests}/IterOps.txt | 0
{tests => shapely/tests}/LineString.txt | 16 +-
{tests => shapely/tests}/MultiLineString.txt | 19 +-
{tests => shapely/tests}/MultiPoint.txt | 18 +-
{tests => shapely/tests}/MultiPolygon.txt | 18 +-
{tests => shapely/tests}/Operations.txt | 16 +
{tests => shapely/tests}/Persist.txt | 0
{tests => shapely/tests}/Point.txt | 20 +-
{tests => shapely/tests}/Polygon.txt | 10 +-
{tests => shapely/tests}/Predicates.txt | 0
shapely/tests/__init__.py | 18 +
{tests => shapely/tests}/attribute-chains.txt | 16 +-
{tests => shapely/tests}/binascii_hex.txt | 0
shapely/tests/cascaded_union.txt | 24 +
{tests => shapely/tests}/dimensions.txt | 0
{tests => shapely/tests}/invalid_intersection.txt | 6 +-
shapely/tests/linear-referencing.txt | 58 ++
shapely/tests/linemerge.txt | 61 ++
{tests => shapely/tests}/polygonize.txt | 0
shapely/tests/test_collection.py | 10 +
shapely/tests/test_doctests.py | 35 +
shapely/tests/test_emptiness.py | 19 +
shapely/tests/test_equality.py | 30 +
shapely/tests/test_geomseq.py | 11 +
shapely/tests/test_prepared.py | 15 +
shapely/tests/test_singularity.py | 15 +
shapely/tests/test_validation.py | 10 +
shapely/tests/test_xy.py | 14 +
shapely/tests/threading_test.py | 37 +
{tests => shapely/tests}/wkt_locale.txt | 0
shapely/topology.py | 120 ++-
shapely/validation.py | 7 +
shapely/wkb.py | 33 +-
shapely/wkt.py | 20 +-
tests/._Array.txt | Bin 184 -> 0 bytes
tests/._GeoInterface.txt | Bin 184 -> 0 bytes
tests/._Persist.txt | Bin 184 -> 0 bytes
tests/._Point.txt | Bin 184 -> 0 bytes
tests/._Predicates.txt | Bin 184 -> 0 bytes
tests/._binascii_hex.txt | Bin 184 -> 0 bytes
96 files changed, 3112 insertions(+), 2777 deletions(-)
diff --git a/._CHANGES.txt b/._CHANGES.txt
deleted file mode 100644
index e4be6ea..0000000
Binary files a/._CHANGES.txt and /dev/null differ
diff --git a/._CREDITS.txt b/._CREDITS.txt
deleted file mode 100644
index 2da06ff..0000000
Binary files a/._CREDITS.txt and /dev/null differ
diff --git a/._HISTORY.txt b/._HISTORY.txt
deleted file mode 100644
index 963bf98..0000000
Binary files a/._HISTORY.txt and /dev/null differ
diff --git a/._LICENSE.txt b/._LICENSE.txt
deleted file mode 100644
index 1dd305d..0000000
Binary files a/._LICENSE.txt and /dev/null differ
diff --git a/._MANIFEST.in b/._MANIFEST.in
deleted file mode 100644
index 74268d9..0000000
Binary files a/._MANIFEST.in and /dev/null differ
diff --git a/._README.txt b/._README.txt
deleted file mode 100644
index 5881da3..0000000
Binary files a/._README.txt and /dev/null differ
diff --git a/CHANGES.txt b/CHANGES.txt
index 0f3ef96..9e0bb95 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,15 +1,74 @@
All tickets are children of http://trac.gispython.org/lab/ticket.
-1.0.14 (2009-10-05)
+1.2.1 (2010-06-23)
+------------------
+- Fixed bounds of singular polygons.
+- Added shapely.validation.explain_validity function (#226).
+
+1.2 (2010-05-27)
+----------------
+- Final release.
+
+1.2rc2 (2010-05-26)
-------------------
-- Proper prototyping of WKB writer, and avoidance of errors on 64-bit systems
- (#191).
+- Add examples and tests to MANIFEST.in.
+- Release candidate 2.
-1.0.13 (2009-09-29)
+1.2rc1 (2010-05-25)
-------------------
+- Release candidate.
+
+1.2b7 (2010-04-22)
+------------------
+- Memory leak associated with new empty geometry state fixed.
+
+1.2b6 (2010-04-13)
+------------------
+- Broken GeometryCollection fixed.
+
+1.2b5 (2010-04-09)
+------------------
+- Objects can be constructed from others of the same type, thereby making
+ copies. Collections can be constructed from sequences of objects, also making
+ copies.
+- Collections are now iterators over their component objects.
+- New code for manual figures, using the descartes package.
+
+1.2b4 (2010-03-19)
+------------------
+- Adds support for the "sunos5" platform.
+
+1.2b3 (2010-02-28)
+------------------
+- Only provide simplification implementations for GEOS C API >= 1.5.
+
+1.2b2 (2010-02-19)
+------------------
+- Fix cascaded_union bug introduced in 1.2b1 (#212).
+
+1.2b1 (2010-02-18)
+------------------
+- Update the README. Remove cruft from setup.py. Add some version 1.2 metadata
+ regarding required Python version (>=2.5,<3) and external dependency
+ (libgeos_c >= 3.1).
+
+1.2a6 (2010-02-09)
+------------------
+- Add accessor for separate arrays of X and Y values (#210).
+
+TODO: fill gap here
+
+1.2a1 (2010-01-20)
+------------------
+- Proper prototyping of WKB writer, and avoidance of errors on 64-bit systems
+ (#191).
- Prototype libgeos_c functions in a way that lets py2exe apps import shapely
(#189).
-
+
+=========================
+1.2 Branched (2009-09-19)
+=========================
+
1.0.12 (2009-04-09)
-------------------
- Fix for references held by topology and predicate descriptors.
@@ -65,3 +124,23 @@ All tickets are children of http://trac.gispython.org/lab/ticket.
1.0.2 (2008-02-26)
------------------
- Fix loss of dimensionality in polygon rings (#155).
+
+1.0.1 (2008-02-08)
+------------------
+- Allow chaining expressions involving coordinate sequences and geometry parts
+ (#151).
+- Protect against abnormal use of coordinate accessors (#152).
+- Coordinate sequences now implement the numpy array protocol (#153).
+
+1.0 (2008-01-18)
+----------------
+- Final release.
+
+1.0 RC2 (2008-01-16)
+--------------------
+- Added temporary solution for #149.
+
+1.0 RC1 (2008-01-14)
+--------------------
+- First release candidate
+
diff --git a/CREDITS.txt b/CREDITS.txt
index 9f374e2..8967542 100644
--- a/CREDITS.txt
+++ b/CREDITS.txt
@@ -1,9 +1,24 @@
-Sean Gillies (Pleiades)
-Howard Butler (Hobu, Inc.)
-Kai Lautaportti (Hexagon IT)
-Frédéric Junod (Camptocamp SA)
-Eric Lemoine (Camptocamp SA)
-Justin Bronn (GeoDjango) for ctypes inspiration
-
-Some of this work was supported by a grant from the U.S. National Endowment
-for the Humanities (http://www.neh.gov).
+Shapely is written by:
+
+* Sean Gillies
+* Aron Bierbaum
+* Kai Lautaportti
+
+Patches contributed by:
+
+* Howard Butler
+* Fr |eaigue| d |eaigue| ric Junod
+* Eric Lemoine
+* Jonathan Tartley
+* Kristian Thy
+* Oliver Tonnhofer
+
+Additional help from:
+
+* Justin Bronn (GeoDjango) for ctypes inspiration
+* Martin Davis (JTS)
+* Jaakko Salli for the Windows distributions
+* Sandro Santilli, Mateusz Loskot, Paul Ramsey, et al (GEOS Project)
+
+Major portions of this work were supported by a grant (for Pleiades_) from the
+U.S. National Endowment for the Humanities (http://www.neh.gov).
diff --git a/GEOS-C-API.txt b/GEOS-C-API.txt
deleted file mode 100644
index c4b5ac3..0000000
--- a/GEOS-C-API.txt
+++ /dev/null
@@ -1,55 +0,0 @@
-Ctypes declarations for functions present in GEOS C API 1.4, but not present in
-1.3, are listed below:
-
-lgeos.GEOS_getWKBOutputDims.restype = ctypes.c_int
-lgeos.GEOS_getWKBByteOrder.restype = ctypes.c_int
-lgeos.GEOS_setWKBByteOrder.restype = ctypes.c_int
-lgeos.GEOS_setWKBByteOrder.argtypes = [ctypes.c_int]
-lgeos.GEOSGeomFromHEX_buf.restype = ctypes.c_void_p
-lgeos.GEOSGeomFromHEX_buf.argtypes = [ctypes.c_void_p, ctypes.c_size_t]
-lgeos.GEOSSimplify.restype = ctypes.c_void_p
-lgeos.GEOSSimplify.argtypes = [ctypes.c_void_p, ctypes.c_double]
-lgeos.GEOSTopologyPreserveSimplify.restype = ctypes.c_void_p
-lgeos.GEOSTopologyPreserveSimplify.argtypes = [ctypes.c_void_p, ctypes.c_double]
-lgeos.GEOSEqualsExact.restype = ctypes.c_int
-lgeos.GEOSEqualsExact.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_double]
-lgeos.GEOSNormalize.restype = ctypes.c_int
-lgeos.GEOSNormalize.argtypes = [ctypes.c_void_p]
-lgeos.GEOSWKTReader_create.restype = ctypes.c_void_p
-lgeos.GEOSWKTReader_destroy.restype = None
-lgeos.GEOSWKTReader_destroy.argtypes = [ctypes.c_void_p]
-lgeos.GEOSWKTReader_read.restype = ctypes.c_void_p
-lgeos.GEOSWKTReader_read.argtypes = [ctypes.c_void_p, ctypes.c_char_p]
-lgeos.GEOSWKTWriter_create.restype = ctypes.c_void_p
-lgeos.GEOSWKTWriter_destroy.restype = None
-lgeos.GEOSWKTWriter_destroy.argtypes = [ctypes.c_void_p]
-lgeos.GEOSWKTWriter_write.restype = ctypes.c_char_p
-lgeos.GEOSWKTWriter_write.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
-lgeos.GEOSWKBReader_create.restype = ctypes.c_void_p
-lgeos.GEOSWKBReader_destroy.restype = None
-lgeos.GEOSWKBReader_destroy.argtypes = [ctypes.c_void_p]
-lgeos.GEOSWKBReader_read.restype = ctypes.c_void_p
-lgeos.GEOSWKBReader_read.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_size_t]
-lgeos.GEOSWKBReader_readHEX.restype = ctypes.c_void_p
-lgeos.GEOSWKBReader_readHEX.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_size_t]
-lgeos.GEOSWKBWriter_create.restype = ctypes.c_void_p
-lgeos.GEOSWKBWriter_destroy.restype = None
-lgeos.GEOSWKBWriter_destroy.argtypes = [ctypes.c_void_p]
-lgeos.GEOSWKBWriter_getOutputDimension.restype = ctypes.c_int
-lgeos.GEOSWKBWriter_getOutputDimension.argtypes = [ctypes.c_void_p]
-lgeos.GEOSWKBWriter_setOutputDimension.restype = None
-lgeos.GEOSWKBWriter_setOutputDimension.argtypes = [ctypes.c_void_p, ctypes.c_int]
-lgeos.GEOSWKBWriter_getByteOrder.restype = ctypes.c_int
-lgeos.GEOSWKBWriter_getByteOrder.argtypes = [ctypes.c_void_p]
-lgeos.GEOSWKBWriter_setByteOrder.restype = None
-lgeos.GEOSWKBWriter_setByteOrder.argtypes = [ctypes.c_void_p, ctypes.c_int]
-lgeos.GEOSWKBWriter_getIncludeSRID.restype = ctypes.c_char
-lgeos.GEOSWKBWriter_getIncludeSRID.argtypes = [ctypes.c_void_p]
-lgeos.GEOSWKBWriter_setIncludeSRID.restype = None
-lgeos.GEOSWKBWriter_setIncludeSRID.argtypes = [ctypes.c_void_p, ctypes.c_char]
-
-Furthermore, the following unneeded declarations are removed to avoid problems
-with Debian's 2.2.3 package:
-
-lgeos.GEOSCoordSeq_getOrdinate.restype = ctypes.c_int
-lgeos.GEOSCoordSeq_getOrdinate.argtypes = [ctypes.c_void_p, ctypes.c_uint, ctypes.c_uint, ctypes.c_void_p]
diff --git a/HISTORY.txt b/HISTORY.txt
deleted file mode 100644
index d782205..0000000
--- a/HISTORY.txt
+++ /dev/null
@@ -1,24 +0,0 @@
-All ticket numbers are rooted at http://trac.gispython.org/projects/PCL/ticket/
-
-1.0.1: 8 February 2008
-----------------------
-- Allow chaining expressions involving coordinate sequences and geometry parts
- (#151).
-- Protect against abnormal use of coordinate accessors (#152).
-- Coordinate sequences now implement the numpy array protocol (#153).
-
-
-1.0: 18 January 2008
---------------------
-- Final release.
-
-
-1.0 RC2: 16 January 2008
-------------------------
-- Added temporary solution for #149.
-
-
-1.0 RC1: 14 January 2008
-------------------------
-- First release candidate
-
diff --git a/MANIFEST.in b/MANIFEST.in
index c4bf1f8..c34208b 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -1,2 +1,6 @@
-include *.txt
-recursive-include examples *.py
+exclude *.txt
+recursive-exclude manual *
+recursive-exclude debian *
+include CHANGES.txt CREDITS.txt LICENSE.txt README.txt
+recursive-include shapely/tests *.py *.txt
+recursive-include shapely/examples *.py
diff --git a/PKG-INFO b/PKG-INFO
index 6764f2a..38b4232 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,46 +1,39 @@
Metadata-Version: 1.0
Name: Shapely
-Version: 1.0.15
-Summary: Geospatial geometries, predicates, and operations
+Version: 1.2.1
+Summary: Geometric objects, predicates, and operations
Home-page: http://trac.gispython.org/lab/wiki/Shapely
Author: Sean Gillies
-Author-email: sgillies at frii.com
+Author-email: sean.gillies at gmail.com
License: BSD
-Description: Shapely
+Description: =======
+ Shapely
=======
- Shapely is a Python package for manipulation and analysis of 2D geospatial
- geometries. It is based on GEOS (http://geos.refractions.net). Shapely 1.0 is
- not concerned with data formats or coordinate reference systems.
- Responsibility for reading and writing data and projecting coordinates is left
- to other packages like WorldMill_ and pyproj_. For more information, see:
+ .. image:: http://farm3.static.flickr.com/2738/4511827859_b5822043b7_o_d.png
+ :width: 800
+ :height: 400
- * Shapely wiki_
- * Shapely manual_
-
- Shapely requires Python 2.4+. (I've also begun to port it to Python 3.0:
- http://sgillies.net/blog/564/shapely-for-python-3-0/.)
-
- .. note::
- We've switched to Windows GEOS DLLs based on MinGW in versions >= 1.0.6.
- Please contact us if you experience difficulties.
+ Shapely is a BSD-licensed Python package for manipulation and analysis of
+ planar geometric objects. It is not concerned with data formats or coordinate
+ systems. It is based on the widely deployed GEOS_ (the engine of PostGIS_) and
+ JTS_ (from which GEOS is ported) libraries. This C dependency is traded for the
+ ability to execute with blazing speed.
- See also CHANGES.txt_ and HISTORY.txt_.
-
- .. _CHANGES.txt: http://trac.gispython.org/lab/browser/Shapely/trunk/CHANGES.txt
- .. _HISTORY.txt: http://trac.gispython.org/lab/browser/Shapely/trunk/HISTORY.txt
- .. _WorldMill: http://pypi.python.org/pypi/WorldMill
- .. _pyproj: http://pypi.python.org/pypi/pyproj
+ In a nutshell: Shapely lets you do PostGIS-ish stuff outside the context of a
+ database using idiomatic Python. For more details, see:
+ * Shapely wiki_
+ * Shapely manual_
+ * Shapely `example apps`_
Dependencies
------------
- * libgeos_c (2.2.3 or 3.0.0+)
- * Python ctypes_ (standard in Python 2.5+)
-
- .. _ctypes: http://pypi.python.org/pypi/ctypes/
+ Shapely 1.2 depends on:
+ * Python >=2.5,<3
+ * libgeos_c >=3.1 (3.0 and below have not been tested, YMMV)
Installation
------------
@@ -49,123 +42,127 @@ Description: Shapely
GEOS DLL. Other users should acquire libgeos_c by any means, make sure that it
is on the system library path, and install from the Python package index::
- $ sudo easy_install Shapely
+ $ pip install Shapely
- or with the setup script::
-
- $ sudo python setup.py install
+ or from a source distribution with the setup script::
+ $ python setup.py install
Usage
-----
- To buffer a point::
+ Here is the canonical example of building an approximately circular patch by
+ buffering a point::
>>> from shapely.geometry import Point
- >>> point = Point(-106.0, 40.0) # longitude, latitude
- >>> point.buffer(10.0)
- <shapely.geometry.polygon.Polygon object at ...>
-
- See the manual_ for comprehensive examples of usage. See also Operations.txt
- and Predicates.txt under tests/ for more examples of the spatial operations and
- predicates provided by Shapely. See also Point.txt, LineString.txt, etc for
- examples of the geometry APIs.
+ >>> patch = Point(0.0, 0.0).buffer(10.0)
+ >>> patch
+ <shapely.geometry.polygon.Polygon object at 0x...>
+ >>> patch.area
+ 313.65484905459385
+ See the manual_ for comprehensive usage snippets and the dissolve.py and
+ intersect.py `example apps`_.
- Numpy integration
- -----------------
+ Integration
+ -----------
- All Shapely geometry instances provide the Numpy array interface::
-
- >>> from numpy import asarray
- >>> a = asarray(point)
- >>> a.size
- 3
- >>> a.shape
- (2,)
-
- Numpy arrays can also be adapted to Shapely points and linestrings::
-
- >>> from shapely.geometry import asLineString
- >>> a = array([[1.0, 2.0], [3.0, 4.0]])
- >>> line = asLineString(a)
- >>> line.wkt
- 'LINESTRING (1.0000000000000000 2.0000000000000000, 3.0000000000000000 4.0000000000000000)'
+ Shapely does not read or write data files, but it can serialize and deserialize
+ using several well known formats and protocols. The shapely.wkb and shapely.wkt
+ modules provide dumpers and loaders inspired by Python's pickle module.::
+ >>> from shapely.wkt import dumps, loads
+ >>> dumps(loads('POINT (0 0)'))
+ 'POINT (0.0000000000000000 0.0000000000000000)'
- Python Geo Interface
- --------------------
+ All linear objects, such as the rings of a polygon (like ``patch`` above),
+ provide the Numpy array interface.::
- Any object that provides the Python geo interface can be adapted to a Shapely
- geometry with the asShape factory::
+ >>> from numpy import asarray
+ >>> ag = asarray(patch.exterior)
+ >>> ag
+ array([[ 1.00000000e+01, 0.00000000e+00],
+ [ 9.95184727e+00, -9.80171403e-01],
+ [ 9.80785280e+00, -1.95090322e+00],
+ ...
+ [ 1.00000000e+01, 0.00000000e+00]])
- >>> d = {"type": "Point", "coordinates": (0.0, 0.0)}
- >>> from shapely.geometry import asShape
- >>> shape = asShape(d)
- >>> shape.geom_type
- 'Point'
- >>> tuple(shape.coords)
- ((0.0, 0.0),)
+ That yields a numpy array of [x, y] arrays. This is not always exactly what one
+ wants for plotting shapes with Matplotlib, so Shapely 1.2 adds a `xy` property
+ for getting separate arrays of coordinate x and y values.::
- >>> class GeoThing(object):
- ... def __init__(self, d):
- ... self.__geo_interface__ = d
- >>> thing = GeoThing({"type": "Point", "coordinates": (0.0, 0.0)})
- >>> shape = asShape(thing)
- >>> shape.geom_type
- 'Point'
- >>> tuple(shape.coords)
- ((0.0, 0.0),)
+ >>> x, y = patch.exterior.xy
+ >>> ax = asarray(x)
+ >>> ax
+ array([ 1.00000000e+01, 9.95184727e+00, 9.80785280e+00, ...])
- See http://trac.gispython.org/lab/wiki/PythonGeoInterface for more
- details on the interface.
+ Numpy arrays can also be adapted to Shapely linestrings::
+ >>> from shapely.geometry import asLineString
+ >>> asLineString(ag).length
+ 62.806623139095073
+ >>> asLineString(ag).wkt
+ 'LINESTRING (10.0000000000000000 0.0000000000000000, ...)'
Testing
-------
- Several of the modules have docstring doctests::
-
- $ cd shapely
- $ python point.py
-
- There are also two test runners under tests/. test_doctests.py requires
- zope.testing. runalldoctests.py does not. Perhaps the easiest way to run the
- tests is::
+ Shapely uses a Zope-stye suite of unittests and doctests, excercised via
+ setup.py.::
$ python setup.py test
+ Nosetests won't run the tests properly; Zope doctest suites are not currently
+ supported well by nose.
Support
-------
For current information about this project, see the wiki_.
- .. _wiki: http://trac.gispython.org/lab/wiki/Shapely
- .. _manual: http://gispython.org/shapely/manual.html
-
If you have questions, please consider joining our community list:
http://trac.gispython.org/projects/PCL/wiki/CommunityList
-
Credits
-------
- * Sean Gillies (Pleiades)
- * Howard Butler (Hobu, Inc.)
- * Kai Lautaportti (Hexagon IT)
- * Fr |eaigue| d |eaigue| ric Junod (Camptocamp SA)
- * Eric Lemoine (Camptocamp SA)
+ Shapely is written by:
+
+ * Sean Gillies
+ * Aron Bierbaum
+ * Kai Lautaportti
+
+ Patches contributed by:
+
+ * Howard Butler
+ * Fr |eaigue| d |eaigue| ric Junod
+ * Eric Lemoine
+ * Jonathan Tartley
+ * Kristian Thy
+ * Oliver Tonnhofer
+
+ Additional help from:
+
* Justin Bronn (GeoDjango) for ctypes inspiration
+ * Martin Davis (JTS)
+ * Jaakko Salli for the Windows distributions
+ * Sandro Santilli, Mateusz Loskot, Paul Ramsey, et al (GEOS Project)
+ Major portions of this work were supported by a grant (for Pleiades_) from the
+ U.S. National Endowment for the Humanities (http://www.neh.gov).
+
+ .. _JTS: http://www.vividsolutions.com/jts/jtshome.htm
+ .. _PostGIS: http://postgis.org
+ .. _GEOS: http://trac.osgeo.org/geos/
+ .. _example apps: http://trac.gispython.org/lab/wiki/Examples
+ .. _wiki: http://trac.gispython.org/lab/wiki/Shapely
+ .. _manual: http://gispython.org/shapely/docs/1.2
.. |eaigue| unicode:: U+00E9
:trim:
+ .. _Pleiades: http://pleiades.stoa.org
- Major portions of this work were supported by a grant (to Pleiades) from the
- U.S. National Endowment for the Humanities (http://www.neh.gov).
-
-Keywords: geometry topology
+Keywords: geometry topology gis
Platform: UNKNOWN
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
diff --git a/README.txt b/README.txt
index bc23ca1..e89f2f4 100644
--- a/README.txt
+++ b/README.txt
@@ -1,38 +1,31 @@
+=======
Shapely
=======
-Shapely is a Python package for manipulation and analysis of 2D geospatial
-geometries. It is based on GEOS (http://geos.refractions.net). Shapely 1.0 is
-not concerned with data formats or coordinate reference systems.
-Responsibility for reading and writing data and projecting coordinates is left
-to other packages like WorldMill_ and pyproj_. For more information, see:
-
-* Shapely wiki_
-* Shapely manual_
-
-Shapely requires Python 2.4+. (I've also begun to port it to Python 3.0:
-http://sgillies.net/blog/564/shapely-for-python-3-0/.)
-
-.. note::
- We've switched to Windows GEOS DLLs based on MinGW in versions >= 1.0.6.
- Please contact us if you experience difficulties.
+.. image:: http://farm3.static.flickr.com/2738/4511827859_b5822043b7_o_d.png
+ :width: 800
+ :height: 400
-See also CHANGES.txt_ and HISTORY.txt_.
+Shapely is a BSD-licensed Python package for manipulation and analysis of
+planar geometric objects. It is not concerned with data formats or coordinate
+systems. It is based on the widely deployed GEOS_ (the engine of PostGIS_) and
+JTS_ (from which GEOS is ported) libraries. This C dependency is traded for the
+ability to execute with blazing speed.
-.. _CHANGES.txt: http://trac.gispython.org/lab/browser/Shapely/trunk/CHANGES.txt
-.. _HISTORY.txt: http://trac.gispython.org/lab/browser/Shapely/trunk/HISTORY.txt
-.. _WorldMill: http://pypi.python.org/pypi/WorldMill
-.. _pyproj: http://pypi.python.org/pypi/pyproj
+In a nutshell: Shapely lets you do PostGIS-ish stuff outside the context of a
+database using idiomatic Python. For more details, see:
+* Shapely wiki_
+* Shapely manual_
+* Shapely `example apps`_
Dependencies
------------
-* libgeos_c (2.2.3 or 3.0.0+)
-* Python ctypes_ (standard in Python 2.5+)
-
-.. _ctypes: http://pypi.python.org/pypi/ctypes/
+Shapely 1.2 depends on:
+* Python >=2.5,<3
+* libgeos_c >=3.1 (3.0 and below have not been tested, YMMV)
Installation
------------
@@ -41,118 +34,122 @@ Windows users should use the executable installer, which contains the required
GEOS DLL. Other users should acquire libgeos_c by any means, make sure that it
is on the system library path, and install from the Python package index::
- $ sudo easy_install Shapely
-
-or with the setup script::
+ $ pip install Shapely
- $ sudo python setup.py install
+or from a source distribution with the setup script::
+ $ python setup.py install
Usage
-----
-To buffer a point::
+Here is the canonical example of building an approximately circular patch by
+buffering a point::
>>> from shapely.geometry import Point
- >>> point = Point(-106.0, 40.0) # longitude, latitude
- >>> point.buffer(10.0)
- <shapely.geometry.polygon.Polygon object at ...>
-
-See the manual_ for comprehensive examples of usage. See also Operations.txt
-and Predicates.txt under tests/ for more examples of the spatial operations and
-predicates provided by Shapely. See also Point.txt, LineString.txt, etc for
-examples of the geometry APIs.
-
+ >>> patch = Point(0.0, 0.0).buffer(10.0)
+ >>> patch
+ <shapely.geometry.polygon.Polygon object at 0x...>
+ >>> patch.area
+ 313.65484905459385
-Numpy integration
------------------
+See the manual_ for comprehensive usage snippets and the dissolve.py and
+intersect.py `example apps`_.
-All Shapely geometry instances provide the Numpy array interface::
+Integration
+-----------
- >>> from numpy import asarray
- >>> a = asarray(point)
- >>> a.size
- 3
- >>> a.shape
- (2,)
-
-Numpy arrays can also be adapted to Shapely points and linestrings::
-
- >>> from shapely.geometry import asLineString
- >>> a = array([[1.0, 2.0], [3.0, 4.0]])
- >>> line = asLineString(a)
- >>> line.wkt
- 'LINESTRING (1.0000000000000000 2.0000000000000000, 3.0000000000000000 4.0000000000000000)'
+Shapely does not read or write data files, but it can serialize and deserialize
+using several well known formats and protocols. The shapely.wkb and shapely.wkt
+modules provide dumpers and loaders inspired by Python's pickle module.::
+ >>> from shapely.wkt import dumps, loads
+ >>> dumps(loads('POINT (0 0)'))
+ 'POINT (0.0000000000000000 0.0000000000000000)'
-Python Geo Interface
---------------------
+All linear objects, such as the rings of a polygon (like ``patch`` above),
+provide the Numpy array interface.::
-Any object that provides the Python geo interface can be adapted to a Shapely
-geometry with the asShape factory::
+ >>> from numpy import asarray
+ >>> ag = asarray(patch.exterior)
+ >>> ag
+ array([[ 1.00000000e+01, 0.00000000e+00],
+ [ 9.95184727e+00, -9.80171403e-01],
+ [ 9.80785280e+00, -1.95090322e+00],
+ ...
+ [ 1.00000000e+01, 0.00000000e+00]])
- >>> d = {"type": "Point", "coordinates": (0.0, 0.0)}
- >>> from shapely.geometry import asShape
- >>> shape = asShape(d)
- >>> shape.geom_type
- 'Point'
- >>> tuple(shape.coords)
- ((0.0, 0.0),)
+That yields a numpy array of [x, y] arrays. This is not always exactly what one
+wants for plotting shapes with Matplotlib, so Shapely 1.2 adds a `xy` property
+for getting separate arrays of coordinate x and y values.::
- >>> class GeoThing(object):
- ... def __init__(self, d):
- ... self.__geo_interface__ = d
- >>> thing = GeoThing({"type": "Point", "coordinates": (0.0, 0.0)})
- >>> shape = asShape(thing)
- >>> shape.geom_type
- 'Point'
- >>> tuple(shape.coords)
- ((0.0, 0.0),)
+ >>> x, y = patch.exterior.xy
+ >>> ax = asarray(x)
+ >>> ax
+ array([ 1.00000000e+01, 9.95184727e+00, 9.80785280e+00, ...])
-See http://trac.gispython.org/lab/wiki/PythonGeoInterface for more
-details on the interface.
+Numpy arrays can also be adapted to Shapely linestrings::
+ >>> from shapely.geometry import asLineString
+ >>> asLineString(ag).length
+ 62.806623139095073
+ >>> asLineString(ag).wkt
+ 'LINESTRING (10.0000000000000000 0.0000000000000000, ...)'
Testing
-------
-Several of the modules have docstring doctests::
-
- $ cd shapely
- $ python point.py
-
-There are also two test runners under tests/. test_doctests.py requires
-zope.testing. runalldoctests.py does not. Perhaps the easiest way to run the
-tests is::
+Shapely uses a Zope-stye suite of unittests and doctests, excercised via
+setup.py.::
$ python setup.py test
+Nosetests won't run the tests properly; Zope doctest suites are not currently
+supported well by nose.
Support
-------
For current information about this project, see the wiki_.
-.. _wiki: http://trac.gispython.org/lab/wiki/Shapely
-.. _manual: http://gispython.org/shapely/manual.html
-
If you have questions, please consider joining our community list:
http://trac.gispython.org/projects/PCL/wiki/CommunityList
-
Credits
-------
-* Sean Gillies (Pleiades)
-* Howard Butler (Hobu, Inc.)
-* Kai Lautaportti (Hexagon IT)
-* Fr |eaigue| d |eaigue| ric Junod (Camptocamp SA)
-* Eric Lemoine (Camptocamp SA)
+Shapely is written by:
+
+* Sean Gillies
+* Aron Bierbaum
+* Kai Lautaportti
+
+Patches contributed by:
+
+* Howard Butler
+* Fr |eaigue| d |eaigue| ric Junod
+* Eric Lemoine
+* Jonathan Tartley
+* Kristian Thy
+* Oliver Tonnhofer
+
+Additional help from:
+
* Justin Bronn (GeoDjango) for ctypes inspiration
+* Martin Davis (JTS)
+* Jaakko Salli for the Windows distributions
+* Sandro Santilli, Mateusz Loskot, Paul Ramsey, et al (GEOS Project)
+Major portions of this work were supported by a grant (for Pleiades_) from the
+U.S. National Endowment for the Humanities (http://www.neh.gov).
+
+.. _JTS: http://www.vividsolutions.com/jts/jtshome.htm
+.. _PostGIS: http://postgis.org
+.. _GEOS: http://trac.osgeo.org/geos/
+.. _example apps: http://trac.gispython.org/lab/wiki/Examples
+.. _wiki: http://trac.gispython.org/lab/wiki/Shapely
+.. _manual: http://gispython.org/shapely/docs/1.2
.. |eaigue| unicode:: U+00E9
:trim:
-
-Major portions of this work were supported by a grant (to Pleiades) from the
-U.S. National Endowment for the Humanities (http://www.neh.gov).
+.. _Pleiades: http://pleiades.stoa.org
diff --git a/Shapely.egg-info/PKG-INFO b/Shapely.egg-info/PKG-INFO
index 6764f2a..38b4232 100644
--- a/Shapely.egg-info/PKG-INFO
+++ b/Shapely.egg-info/PKG-INFO
@@ -1,46 +1,39 @@
Metadata-Version: 1.0
Name: Shapely
-Version: 1.0.15
-Summary: Geospatial geometries, predicates, and operations
+Version: 1.2.1
+Summary: Geometric objects, predicates, and operations
Home-page: http://trac.gispython.org/lab/wiki/Shapely
Author: Sean Gillies
-Author-email: sgillies at frii.com
+Author-email: sean.gillies at gmail.com
License: BSD
-Description: Shapely
+Description: =======
+ Shapely
=======
- Shapely is a Python package for manipulation and analysis of 2D geospatial
- geometries. It is based on GEOS (http://geos.refractions.net). Shapely 1.0 is
- not concerned with data formats or coordinate reference systems.
- Responsibility for reading and writing data and projecting coordinates is left
- to other packages like WorldMill_ and pyproj_. For more information, see:
+ .. image:: http://farm3.static.flickr.com/2738/4511827859_b5822043b7_o_d.png
+ :width: 800
+ :height: 400
- * Shapely wiki_
- * Shapely manual_
-
- Shapely requires Python 2.4+. (I've also begun to port it to Python 3.0:
- http://sgillies.net/blog/564/shapely-for-python-3-0/.)
-
- .. note::
- We've switched to Windows GEOS DLLs based on MinGW in versions >= 1.0.6.
- Please contact us if you experience difficulties.
+ Shapely is a BSD-licensed Python package for manipulation and analysis of
+ planar geometric objects. It is not concerned with data formats or coordinate
+ systems. It is based on the widely deployed GEOS_ (the engine of PostGIS_) and
+ JTS_ (from which GEOS is ported) libraries. This C dependency is traded for the
+ ability to execute with blazing speed.
- See also CHANGES.txt_ and HISTORY.txt_.
-
- .. _CHANGES.txt: http://trac.gispython.org/lab/browser/Shapely/trunk/CHANGES.txt
- .. _HISTORY.txt: http://trac.gispython.org/lab/browser/Shapely/trunk/HISTORY.txt
- .. _WorldMill: http://pypi.python.org/pypi/WorldMill
- .. _pyproj: http://pypi.python.org/pypi/pyproj
+ In a nutshell: Shapely lets you do PostGIS-ish stuff outside the context of a
+ database using idiomatic Python. For more details, see:
+ * Shapely wiki_
+ * Shapely manual_
+ * Shapely `example apps`_
Dependencies
------------
- * libgeos_c (2.2.3 or 3.0.0+)
- * Python ctypes_ (standard in Python 2.5+)
-
- .. _ctypes: http://pypi.python.org/pypi/ctypes/
+ Shapely 1.2 depends on:
+ * Python >=2.5,<3
+ * libgeos_c >=3.1 (3.0 and below have not been tested, YMMV)
Installation
------------
@@ -49,123 +42,127 @@ Description: Shapely
GEOS DLL. Other users should acquire libgeos_c by any means, make sure that it
is on the system library path, and install from the Python package index::
- $ sudo easy_install Shapely
+ $ pip install Shapely
- or with the setup script::
-
- $ sudo python setup.py install
+ or from a source distribution with the setup script::
+ $ python setup.py install
Usage
-----
- To buffer a point::
+ Here is the canonical example of building an approximately circular patch by
+ buffering a point::
>>> from shapely.geometry import Point
- >>> point = Point(-106.0, 40.0) # longitude, latitude
- >>> point.buffer(10.0)
- <shapely.geometry.polygon.Polygon object at ...>
-
- See the manual_ for comprehensive examples of usage. See also Operations.txt
- and Predicates.txt under tests/ for more examples of the spatial operations and
- predicates provided by Shapely. See also Point.txt, LineString.txt, etc for
- examples of the geometry APIs.
+ >>> patch = Point(0.0, 0.0).buffer(10.0)
+ >>> patch
+ <shapely.geometry.polygon.Polygon object at 0x...>
+ >>> patch.area
+ 313.65484905459385
+ See the manual_ for comprehensive usage snippets and the dissolve.py and
+ intersect.py `example apps`_.
- Numpy integration
- -----------------
+ Integration
+ -----------
- All Shapely geometry instances provide the Numpy array interface::
-
- >>> from numpy import asarray
- >>> a = asarray(point)
- >>> a.size
- 3
- >>> a.shape
- (2,)
-
- Numpy arrays can also be adapted to Shapely points and linestrings::
-
- >>> from shapely.geometry import asLineString
- >>> a = array([[1.0, 2.0], [3.0, 4.0]])
- >>> line = asLineString(a)
- >>> line.wkt
- 'LINESTRING (1.0000000000000000 2.0000000000000000, 3.0000000000000000 4.0000000000000000)'
+ Shapely does not read or write data files, but it can serialize and deserialize
+ using several well known formats and protocols. The shapely.wkb and shapely.wkt
+ modules provide dumpers and loaders inspired by Python's pickle module.::
+ >>> from shapely.wkt import dumps, loads
+ >>> dumps(loads('POINT (0 0)'))
+ 'POINT (0.0000000000000000 0.0000000000000000)'
- Python Geo Interface
- --------------------
+ All linear objects, such as the rings of a polygon (like ``patch`` above),
+ provide the Numpy array interface.::
- Any object that provides the Python geo interface can be adapted to a Shapely
- geometry with the asShape factory::
+ >>> from numpy import asarray
+ >>> ag = asarray(patch.exterior)
+ >>> ag
+ array([[ 1.00000000e+01, 0.00000000e+00],
+ [ 9.95184727e+00, -9.80171403e-01],
+ [ 9.80785280e+00, -1.95090322e+00],
+ ...
+ [ 1.00000000e+01, 0.00000000e+00]])
- >>> d = {"type": "Point", "coordinates": (0.0, 0.0)}
- >>> from shapely.geometry import asShape
- >>> shape = asShape(d)
- >>> shape.geom_type
- 'Point'
- >>> tuple(shape.coords)
- ((0.0, 0.0),)
+ That yields a numpy array of [x, y] arrays. This is not always exactly what one
+ wants for plotting shapes with Matplotlib, so Shapely 1.2 adds a `xy` property
+ for getting separate arrays of coordinate x and y values.::
- >>> class GeoThing(object):
- ... def __init__(self, d):
- ... self.__geo_interface__ = d
- >>> thing = GeoThing({"type": "Point", "coordinates": (0.0, 0.0)})
- >>> shape = asShape(thing)
- >>> shape.geom_type
- 'Point'
- >>> tuple(shape.coords)
- ((0.0, 0.0),)
+ >>> x, y = patch.exterior.xy
+ >>> ax = asarray(x)
+ >>> ax
+ array([ 1.00000000e+01, 9.95184727e+00, 9.80785280e+00, ...])
- See http://trac.gispython.org/lab/wiki/PythonGeoInterface for more
- details on the interface.
+ Numpy arrays can also be adapted to Shapely linestrings::
+ >>> from shapely.geometry import asLineString
+ >>> asLineString(ag).length
+ 62.806623139095073
+ >>> asLineString(ag).wkt
+ 'LINESTRING (10.0000000000000000 0.0000000000000000, ...)'
Testing
-------
- Several of the modules have docstring doctests::
-
- $ cd shapely
- $ python point.py
-
- There are also two test runners under tests/. test_doctests.py requires
- zope.testing. runalldoctests.py does not. Perhaps the easiest way to run the
- tests is::
+ Shapely uses a Zope-stye suite of unittests and doctests, excercised via
+ setup.py.::
$ python setup.py test
+ Nosetests won't run the tests properly; Zope doctest suites are not currently
+ supported well by nose.
Support
-------
For current information about this project, see the wiki_.
- .. _wiki: http://trac.gispython.org/lab/wiki/Shapely
- .. _manual: http://gispython.org/shapely/manual.html
-
If you have questions, please consider joining our community list:
http://trac.gispython.org/projects/PCL/wiki/CommunityList
-
Credits
-------
- * Sean Gillies (Pleiades)
- * Howard Butler (Hobu, Inc.)
- * Kai Lautaportti (Hexagon IT)
- * Fr |eaigue| d |eaigue| ric Junod (Camptocamp SA)
- * Eric Lemoine (Camptocamp SA)
+ Shapely is written by:
+
+ * Sean Gillies
+ * Aron Bierbaum
+ * Kai Lautaportti
+
+ Patches contributed by:
+
+ * Howard Butler
+ * Fr |eaigue| d |eaigue| ric Junod
+ * Eric Lemoine
+ * Jonathan Tartley
+ * Kristian Thy
+ * Oliver Tonnhofer
+
+ Additional help from:
+
* Justin Bronn (GeoDjango) for ctypes inspiration
+ * Martin Davis (JTS)
+ * Jaakko Salli for the Windows distributions
+ * Sandro Santilli, Mateusz Loskot, Paul Ramsey, et al (GEOS Project)
+ Major portions of this work were supported by a grant (for Pleiades_) from the
+ U.S. National Endowment for the Humanities (http://www.neh.gov).
+
+ .. _JTS: http://www.vividsolutions.com/jts/jtshome.htm
+ .. _PostGIS: http://postgis.org
+ .. _GEOS: http://trac.osgeo.org/geos/
+ .. _example apps: http://trac.gispython.org/lab/wiki/Examples
+ .. _wiki: http://trac.gispython.org/lab/wiki/Shapely
+ .. _manual: http://gispython.org/shapely/docs/1.2
.. |eaigue| unicode:: U+00E9
:trim:
+ .. _Pleiades: http://pleiades.stoa.org
- Major portions of this work were supported by a grant (to Pleiades) from the
- U.S. National Endowment for the Humanities (http://www.neh.gov).
-
-Keywords: geometry topology
+Keywords: geometry topology gis
Platform: UNKNOWN
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
diff --git a/Shapely.egg-info/SOURCES.txt b/Shapely.egg-info/SOURCES.txt
index f13dc9e..01c238d 100644
--- a/Shapely.egg-info/SOURCES.txt
+++ b/Shapely.egg-info/SOURCES.txt
@@ -1,26 +1,28 @@
CHANGES.txt
CREDITS.txt
-GEOS-C-API.txt
-HISTORY.txt
LICENSE.txt
MANIFEST.in
README.txt
+setup.cfg
setup.py
Shapely.egg-info/PKG-INFO
Shapely.egg-info/SOURCES.txt
Shapely.egg-info/dependency_links.txt
-Shapely.egg-info/requires.txt
Shapely.egg-info/top_level.txt
-examples/geoms.py
-examples/world.py
-manual/manual.txt
+examples/dissolve.py
+examples/intersect.py
shapely/__init__.py
+shapely/coords.py
shapely/ctypes_declarations.py
shapely/geos.py
+shapely/impl.py
shapely/iterops.py
+shapely/linref.py
shapely/ops.py
shapely/predicates.py
+shapely/prepared.py
shapely/topology.py
+shapely/validation.py
shapely/wkb.py
shapely/wkt.py
shapely/geometry/__init__.py
@@ -34,21 +36,35 @@ shapely/geometry/multipolygon.py
shapely/geometry/point.py
shapely/geometry/polygon.py
shapely/geometry/proxy.py
-tests/Array.txt
-tests/GeoInterface.txt
-tests/IterOps.txt
-tests/LineString.txt
-tests/MultiLineString.txt
-tests/MultiPoint.txt
-tests/MultiPolygon.txt
-tests/Operations.txt
-tests/Persist.txt
-tests/Point.txt
-tests/Polygon.txt
-tests/Predicates.txt
-tests/attribute-chains.txt
-tests/binascii_hex.txt
-tests/dimensions.txt
-tests/invalid_intersection.txt
-tests/polygonize.txt
-tests/wkt_locale.txt
\ No newline at end of file
+shapely/tests/Array.txt
+shapely/tests/GeoInterface.txt
+shapely/tests/IterOps.txt
+shapely/tests/LineString.txt
+shapely/tests/MultiLineString.txt
+shapely/tests/MultiPoint.txt
+shapely/tests/MultiPolygon.txt
+shapely/tests/Operations.txt
+shapely/tests/Persist.txt
+shapely/tests/Point.txt
+shapely/tests/Polygon.txt
+shapely/tests/Predicates.txt
+shapely/tests/__init__.py
+shapely/tests/attribute-chains.txt
+shapely/tests/binascii_hex.txt
+shapely/tests/cascaded_union.txt
+shapely/tests/dimensions.txt
+shapely/tests/invalid_intersection.txt
+shapely/tests/linear-referencing.txt
+shapely/tests/linemerge.txt
+shapely/tests/polygonize.txt
+shapely/tests/test_collection.py
+shapely/tests/test_doctests.py
+shapely/tests/test_emptiness.py
+shapely/tests/test_equality.py
+shapely/tests/test_geomseq.py
+shapely/tests/test_prepared.py
+shapely/tests/test_singularity.py
+shapely/tests/test_validation.py
+shapely/tests/test_xy.py
+shapely/tests/threading_test.py
+shapely/tests/wkt_locale.txt
\ No newline at end of file
diff --git a/Shapely.egg-info/requires.txt b/Shapely.egg-info/requires.txt
deleted file mode 100644
index 8b6d003..0000000
--- a/Shapely.egg-info/requires.txt
+++ /dev/null
@@ -1 +0,0 @@
-setuptools
\ No newline at end of file
diff --git a/examples/._geoms.py b/examples/._geoms.py
deleted file mode 100644
index b703ef1..0000000
Binary files a/examples/._geoms.py and /dev/null differ
diff --git a/examples/._world.py b/examples/._world.py
deleted file mode 100644
index b255630..0000000
Binary files a/examples/._world.py and /dev/null differ
diff --git a/examples/dissolve.py b/examples/dissolve.py
new file mode 100644
index 0000000..3f1d624
--- /dev/null
+++ b/examples/dissolve.py
@@ -0,0 +1,53 @@
+# dissolve.py
+#
+# Demonstrate how Shapely can be used to build up a collection of patches by
+# dissolving circular regions and how Shapely supports plotting of the results.
+
+from functools import partial
+import random
+
+import pylab
+
+from shapely.geometry import Point
+from shapely.ops import cascaded_union
+
+# Use a partial function to make 100 points uniformly distributed in a 40x40
+# box centered on 0,0.
+r = partial(random.uniform, -20.0, 20.0)
+points = [Point(r(), r()) for i in range(100)]
+
+# Buffer the points, producing 100 polygon spots
+spots = [p.buffer(2.5) for p in points]
+
+# Perform a cascaded union of the polygon spots, dissolving them into a
+# collection of polygon patches
+patches = cascaded_union(spots)
+
+if __name__ == "__main__":
+ # Illustrate the results using matplotlib's pylab interface
+ pylab.figure(num=None, figsize=(4, 4), dpi=180)
+
+ for patch in patches.geoms:
+ assert patch.geom_type in ['Polygon']
+ assert patch.is_valid
+
+ # Fill and outline each patch
+ x, y = patch.exterior.xy
+ pylab.fill(x, y, color='#cccccc', aa=True)
+ pylab.plot(x, y, color='#666666', aa=True, lw=1.0)
+
+ # Do the same for the holes of the patch
+ for hole in patch.interiors:
+ x, y = hole.xy
+ pylab.fill(x, y, color='#ffffff', aa=True)
+ pylab.plot(x, y, color='#999999', aa=True, lw=1.0)
+
+ # Plot the original points
+ pylab.plot([p.x for p in points], [p.y for p in points], 'b,', alpha=0.75)
+
+ # Write the number of patches and the total patch area to the figure
+ pylab.text(-25, 25,
+ "Patches: %d, total area: %.2f" % (len(patches.geoms), patches.area))
+
+ pylab.savefig('dissolve.png')
+
diff --git a/examples/geoms.py b/examples/geoms.py
deleted file mode 100644
index 164ab72..0000000
--- a/examples/geoms.py
+++ /dev/null
@@ -1,51 +0,0 @@
-from numpy import asarray
-import pylab
-from shapely.geometry import Point, LineString, Polygon
-
-polygon = Polygon(((-1.0, -1.0), (-1.0, 1.0), (1.0, 1.0), (1.0, -1.0)))
-
-point_r = Point(-1.5, 1.2)
-point_g = Point(-1.0, 1.0)
-point_b = Point(-0.5, 0.5)
-
-line_r = LineString(((-0.5, 0.5), (0.5, 0.5)))
-line_g = LineString(((1.0, -1.0), (1.8, 0.5)))
-line_b = LineString(((-1.8, -1.2), (1.8, 0.5)))
-
-def plot_point(g, o, l):
- pylab.plot([g.x], [g.y], o, label=l)
-
-def plot_line(g, o):
- a = asarray(g)
- pylab.plot(a[:,0], a[:,1], o)
-
-def fill_polygon(g, o):
- a = asarray(g.exterior)
- pylab.fill(a[:,0], a[:,1], o, alpha=0.5)
-
-def fill_multipolygon(g, o):
- for g in g.geoms:
- fill_polygon(g, o)
-
-if __name__ == "__main__":
- from numpy import asarray
- import pylab
-
- fig = pylab.figure(1, figsize=(4, 3), dpi=150)
- #pylab.axis([-2.0, 2.0, -1.5, 1.5])
- pylab.axis('tight')
-
- a = asarray(polygon.exterior)
- pylab.fill(a[:,0], a[:,1], 'c')
-
- plot_point(point_r, 'ro', 'b')
- plot_point(point_g, 'go', 'c')
- plot_point(point_b, 'bo', 'd')
-
- plot_line(line_r, 'r')
- plot_line(line_g, 'g')
- plot_line(line_b, 'b')
-
- pylab.show()
-
-
diff --git a/examples/intersect.py b/examples/intersect.py
new file mode 100644
index 0000000..76fab15
--- /dev/null
+++ b/examples/intersect.py
@@ -0,0 +1,81 @@
+# intersect.py
+#
+# Demonstrate how Shapely can be used to analyze and plot the intersection of
+# a trajectory and regions in space.
+
+from functools import partial
+import random
+
+import pylab
+
+from shapely.geometry import LineString, Point
+from shapely.ops import cascaded_union
+
+# Build patches as in dissolved.py
+r = partial(random.uniform, -20.0, 20.0)
+points = [Point(r(), r()) for i in range(100)]
+spots = [p.buffer(2.5) for p in points]
+patches = cascaded_union(spots)
+
+# Represent the following geolocation parameters
+#
+# initial position: -25, -25
+# heading: 45.0
+# speed: 50*sqrt(2)
+#
+# as a line
+vector = LineString(((-25.0, -25.0), (25.0, 25.0)))
+
+# Find intercepted and missed patches. List the former so we can count them
+# later
+intercepts = [patch for patch in patches.geoms if vector.intersects(patch)]
+misses = (patch for patch in patches.geoms if not vector.intersects(patch))
+
+# Plot the intersection
+intersection = vector.intersection(patches)
+assert intersection.geom_type in ['MultiLineString']
+
+if __name__ == "__main__":
+ # Illustrate the results using matplotlib's pylab interface
+ pylab.figure(num=None, figsize=(4, 4), dpi=180)
+
+ # Plot the misses
+ for spot in misses:
+ x, y = spot.exterior.xy
+ pylab.fill(x, y, color='#cccccc', aa=True)
+ pylab.plot(x, y, color='#999999', aa=True, lw=1.0)
+
+ # Do the same for the holes of the patch
+ for hole in spot.interiors:
+ x, y = hole.xy
+ pylab.fill(x, y, color='#ffffff', aa=True)
+ pylab.plot(x, y, color='#999999', aa=True, lw=1.0)
+
+ # Plot the intercepts
+ for spot in intercepts:
+ x, y = spot.exterior.xy
+ pylab.fill(x, y, color='red', alpha=0.25, aa=True)
+ pylab.plot(x, y, color='red', alpha=0.5, aa=True, lw=1.0)
+
+ # Do the same for the holes of the patch
+ for hole in spot.interiors:
+ x, y = hole.xy
+ pylab.fill(x, y, color='#ffffff', aa=True)
+ pylab.plot(x, y, color='red', alpha=0.5, aa=True, lw=1.0)
+
+ # Draw the projected trajectory
+ pylab.arrow(-25, -25, 50, 50, color='#999999', aa=True,
+ head_width=1.0, head_length=1.0)
+
+ for segment in intersection.geoms:
+ x, y = segment.xy
+ pylab.plot(x, y, color='red', aa=True, lw=1.5)
+
+ # Write the number of patches and the total patch area to the figure
+ pylab.text(-28, 25,
+ "Patches: %d/%d (%d), total length: %.1f" \
+ % (len(intercepts), len(patches.geoms),
+ len(intersection.geoms), intersection.length))
+
+ pylab.savefig('intersect.png')
+
diff --git a/examples/world.py b/examples/world.py
deleted file mode 100644
index 81b9af8..0000000
--- a/examples/world.py
+++ /dev/null
@@ -1,21 +0,0 @@
-import ogr
-import pylab
-from numpy import asarray
-
-from shapely.wkb import loads
-
-source = ogr.Open("/var/gis/data/world/world_borders.shp")
-borders = source.GetLayerByName("world_borders")
-
-fig = pylab.figure(1, figsize=(4,2), dpi=300)
-
-while 1:
- feature = borders.GetNextFeature()
- if not feature:
- break
-
- geom = loads(feature.GetGeometryRef().ExportToWkb())
- a = asarray(geom)
- pylab.plot(a[:,0], a[:,1])
-
-pylab.show()
diff --git a/manual/manual.txt b/manual/manual.txt
deleted file mode 100644
index 10e2a2b..0000000
--- a/manual/manual.txt
+++ /dev/null
@@ -1,875 +0,0 @@
-==================
-The Shapely Manual
-==================
-
-:Author: Sean Gillies
-:address: sgillies at frii.com
-:revision: 1.0.5
-:date: 20 May 2008
-:copyright: This work is licensed under a `Creative Commons Attribution 3.0
- United States License`__.
-
-.. __: http://creativecommons.org/licenses/by/3.0/us/
-
-:abstract: This document describes the Shapely Python package for programming
- with geospatial geometries.
-
-.. sectnum::
-
-.. contents::
-
-
-Background
-==========
-
-Shapely is a Python package for programming with geospatial geometries. It is
-based on the GEOS_ library, a port of the `Java Topology Suite`_. Please refer
-to the JTS overview for definitions and illustrations of the various geometry
-types, operations, and predicates. See also the Shapely wiki_ and Python
-Package Index record_.
-
-.. _GEOS: http://geos.refractions.net
-.. _Java Topology Suite: http://www.jump-project.org/project.php?PID=JTS&SID=OVER
-.. _record: http://pypi.python.org/pypi/Shapely
-.. _wiki: http://trac.gispython.org/projects/PCL/wiki/Shapely
-
-
-Geometries
-==========
-
-The basic, standard GIS geometry model consists of single points, line strings,
-and polygons, homogeneous multi-point, multi-line string, and multi-polygon
-collections, and heterogeneous geometry collections. See the JTS illustration_.
-
-.. _illustration: http://www.jump-project.org/project.php?PID=JTS&SID=OVER#spatialdatatypes
-
-Shapely 1.0 does not provide circles, arcs, splines or the like.
-
-
-Factories
----------
-
-Geometries can be created in the typical Python fashion, using the geometry
-classes themselves as factories.
-
-Pseudo-code blocks in this section will use the following notation. Let **a**
-be a Cartesian *x*, *y*, and optional *z* coordinate sequence. The coordinates
-values must be numeric types. Let (**a**\ 1, ..., **a**\ M) and (**b**\ 1, ...,
-**b**\ N) be ordered sequences of *M* and *N* such coordinate sequences,
-defining lines or rings.
-
-Points
-++++++
-
-The point factory *Point* takes a coordinate sequence parameter
-
-.. code-block:: python
-
- >>> from shapely.geometry import Point
- >>> point = Point(a)
-
-The alternate form is to pass individual coordinate parameters
-
-.. code-block:: python
-
- >>> point = Point(x0, y0 [, z0])
-
-LineStrings
-+++++++++++
-
-To create a line string, pass in an ordered sequence of coordinate sequences:
-
-.. code-block:: python
-
- >>> from shapely.geometry import LineString
- >>> line = LineString((a1, ..., aM))
-
-Polygons
-++++++++
-
-A polygon with only an exterior boundary and no holes is created by passing the sequence representation of a closed ring
-
-.. code-block:: python
-
- >>> from shapely.geometry import Polygon
- >>> polygon = Polygon((a1, ..., aM))
-
-If **a**\ 1 is not exactly equal to **a**\ M, the factory will close the ring.
-The following (unit square) polygons are therefore topologically equal
-
-.. code-block:: python
-
- >>> polygon1 = Polygon(((0, 0), (0, 1), (1, 1), (1, 0), (0, 0)))
- >>> polygon2 = Polygon(((0, 0), (0, 1), (1, 1), (1, 0)))
-
-To create a polygon with interior boundaries pass a sequence of rings to the
-second parameter (*holes*)
-
-.. code-block:: python
-
- >>> polygon = Polygon((a1, ..., aM), [(b1, ..., bN), ...])
-
-Rings *must* be non-crossing, ordered coordinate sequences. The order may be
-clockwise or counter-clockwise. The resulting topology is independent of the
-order.
-
-
-Multipart Geometry Factories
-----------------------------
-
-MultiPoints
-+++++++++++
-
-An *N*\ -point geometry is created by passing an unordered sequence of
-coordinate sequences [**c**\ 1, ..., **c**\ N]
-
-.. code-block:: python
-
- >>> from shapely.geometry import MultiPoint
- >>> points = MultiPoint([c1, ..., cN])
-
-MultiLineStrings
-++++++++++++++++
-
-A multi-line geometry is created by passing a sequence of representations of
-lines
-
-.. code-block:: python
-
- >>> from shapely.geometry import MultiLineString
- >>> lines = MultiLineString([(a1, ..., aM), (b1, ..., bN), ...])
-
-MultiPolygons
-+++++++++++++
-
-A multi-polygon geometry is created by passing a sequence of exterior ring and
-hole list tuples
-
-.. code-block:: python
-
- >>> from shapely.geometry import MultiPolygon
- >>> lines = MultiPolygon([((a1, ..., aM), [(b1, ..., bN), ...]), ...])
-
-More explicit notation for the exterior and interior boundaries (or shells and
-holes) makes usage more clear
-
-.. code-block:: python
-
- >>> shell = (a1, ..., aM)
- >>> holes = [(b1, ..., bN), ...]
- >>> lines = MultiPolygon([(shell, holes), ...])
-
-
-Null Geometries
----------------
-
-Null geometries can be created by calling the factories with no arguments, but
-almost nothing can be done with a null geometry.
-
-.. code-block:: python
-
- >>> line_null = LineString()
- >>> line_null.length
- Traceback (most recent call last):
- ...
- ValueError: Null geometry supports no operations
-
-The coordinates of a null geometry *can* be set (see Section 3), after which
-the geometry is no longer null.
-
-.. code-block:: python
-
- >>> l_null.coords = [(0, 0), (1, 1)]
- >>> print l_null.length
- 1.414...
-
-
-Constructive Spatial Analysis Methods
--------------------------------------
-
-There are methods of geometry classes that also serve as factories for new
-geometries. It is important to note that these are topological and not
-point-wise operations, and therefore may produce results that are not what one
-might expect from operations on Python sets.
-
-See also the JTS |illustration2|_.
-
-.. |illustration2| replace:: illustration
-.. _illustration2: http://www.jump-project.org/project.php?PID=JTS&SID=OVER#spatialanalysismethods
-
-Example Geometries
-++++++++++++++++++
-
-.. code-block:: python
-
- >>> polygon = Polygon(((-1.0, -1.0), (-1.0, 1.0), (1.0, 1.0), (1.0, -1.0)))
- >>> point_r = Point(-1.5, 1.2)
- >>> point_g = Point(-1.0, 1.0)
- >>> point_b = Point(-0.5, 0.5)
- >>> line_r = LineString(((-0.5, 0.5), (0.5, 0.5)))
- >>> line_g = LineString(((1.0, -1.0), (1.8, 0.5)))
- >>> line_b = LineString(((-1.8, -1.2), (1.8, 0.5)))
-
-Buffer
-++++++
-
-.buffer(width, quadsegs=16) : geometry
- Returns a buffer region having the given width and with a specified number of
- segments used to approximate curves.
-
-The default result of buffering a point is an N-gon approximation of a circle:
-
-.. code-block:: python
-
- >>> buffered = point_r.buffer(1.0)
- >>> buffered
- <shapely.geometry.polygon.Polygon object at ...>
- >>> buffered.length
- 6.2806623139097271
- >>> buffered.area
- 3.1365484905463727
- >>> len(buffered.exterior.coords)
- 66
-
-Boundary
-++++++++
-
-.boundary : geometry
- Returns a lower dimension geometry. The boundary of a polygon is a line, the
- boundary of a line is a collection of points. The boundary of a point is an
- empty (null) collection.
-
-.. code-block:: python
-
- >>> polygon.boundary
- <shapely.geometry.linestring.LineString object at ...>
- >>> line_b.boundary
- <shapely.geometry.multipoint.MultiPoint object at ...>
- >>> point_r.boundary.is_empty
- True
-
-Centroid
-++++++++
-
-.centroid : geometry
- Returns the centroid, or geometric center of the polygon.
-
-.. code-block:: python
-
- >>> centroid_point = polygon.centroid
- >>> centroid_point.wkt
- 'POINT (-0.0000000000000000 -0.0000000000000000)'
-
-Convex Hull
-+++++++++++
-
-.convex_hull : geometry
- Imagine an elastic band stretched around the geometry: that's a convex hull,
- more or less.
-
-For example, collect the three points into a multi-point geometry, and get the
-triangular polygon that is their convex hull:
-
-.. code-block:: python
-
- >>> multi_point = point_r.union(point_g)
- >>> multi_point = multi_point.union(point_b)
- >>> multi_point.convex_hull
- <shapely.geometry.polygon.Polygon object at ...>
-
-Difference
-++++++++++
-
-.difference(other) : geometry
- Returns a geometry representing the points making up this geometry that do
- not make up *other*. Note that A.difference(B) is not necessarily equal to
- B.difference(A).
-
-.. code-block:: python
-
- >>> hull = multi_point.convex_hull
- >>> polygon.difference(hull)
- <shapely.geometry.polygon.Polygon object at ...>
-
-Envelope
-++++++++
-
-.envelope : geometry
- Returns the geometry's rectangular polygon envelope.
-
-.. code-block:: python
-
- >>> polygon.envelope
- <shapely.geometry.polygon.Polygon object at ...>
-
-Intersection
-++++++++++++
-
-.intersection(other) : geometry
- Returns the intersection of one geometry and the *other* geometry.
-
-.. code-block:: python
-
- >>> polygon.intersection(hull)
- <shapely.geometry.polygon.Polygon object at ...>
-
-Symmetric Difference
-++++++++++++++++++++
-
-.symmetric_difference(other) : geometry
- Returns a geometry combining the points in this geometry not in *other*, and
- the points in *other* not in this geometry.
-
-.. code-block:: python
-
- >>> polygon.symmetric_difference(hull)
- <shapely.geometry.multipolygon.MultiPolygon object at ...>
-
-Union
-+++++
-
-.union(other) : geometry
- Returns the union of one geometry and the *other* geometry.
-
-Point unions were demonstrated above under convex hull. The union of polygons
-will be a polygon or a multi-polygon depending on whether they intersect or
-not:
-
-.. code-block:: python
-
- >>> hull.union(polygon)
- <shapely.geometry.polygon.Polygon object at ...>
-
-
-Other Operations
-----------------
-
-Polygonization
-++++++++++++++
-
-shapely.ops.polygonize(lines) : iterator
- Returns an iterator over polygons constructed from the *lines* iterator. The
- elements of *lines* may be Shapely geometries, objects that provide the geo
- interface, or Numpy arrays or Python sequences shaped like LineStrings.
-
-.. code-block:: python
-
- >>> from shapely.ops import polygonize
- >>> lines = [
- ... ((0, 0), (1, 1)),
- ... ((0, 0), (0, 1)),
- ... ((0, 1), (1, 1)),
- ... ((1, 1), (1, 0)),
- ... ((1, 0), (0, 0))
- ... ]
- >>> result = polygonize(lines)
- >>> list(result.geoms)
- [<shapely.geometry.polygon.Polygon object at ...>, <shapely.geometry.polygon.Polygon object at ...>]
-
-
-Unary Spatial Predicates
-------------------------
-
-These are implemented as Python attributes.
-
-Is Empty
-++++++++
-
-.is_empty : bool
- True if the set of points in this geometry is empty, else False. For more
- details, see
- http://geos.refractions.net/ro/doxygen_docs/html/classgeos_1_1geom_1_1Geometry.html#a17.
-
-Is Valid
-++++++++
-
-.is_valid : bool
- True if the geometry is valid (definition depends on sub-class), else False.
- For more details, see
- http://geos.refractions.net/ro/doxygen_docs/html/classgeos_1_1geom_1_1Geometry.html#a16.
-
-Is Ring
-+++++++
-
-.is_ring : bool
- True if the geometry is a closed ring, else False.
-
-Has Z
-+++++
-
-.has_z : bool
- True if the geometry's coordinate sequence(s) have z values (are
- 3-dimensional)
-
-Examples
-++++++++
-
-.. code-block:: python
-
- >>> polygon.is_empty
- False
- >>> polygon.is_valid
- True
- >>> polygon.is_ring
- False
- >>> polygon.boundary.is_ring
- True
- >>> polygon.has_z
- False
-
-(Note: that last return value exposes a bug in GEOS 2.2.3.)
-
-
-Binary Spatial Predicates
--------------------------
-
-All of these methods take a single positional argument, an *other* geometry. It
-is important to note that these are topological and not point-wise operations,
-and therefore may produce results that are not what one might expect from
-operations on Python.
-
-Contains
-++++++++
-
-.contains(other) : bool
- True if the geometry is spatially within, without touching. Applies to all
- types of geometries.
-
-.. code-block:: python
-
- >>> polygon.contains(point_b)
- True
-
-Crosses
-+++++++
-
-.crosses(other) : bool
- Only linear geometries (lines, rings, polygon boundaries) may ever cross. No
- geometry may ever cross a point.
-
-.. code-block:: python
-
- >>> line_b.crosses(polygon)
- True
-
-Disjoint
-++++++++
-
-.disjoint(other) : bool
- True if geometries do not spatially relate in any way, else False. See the
- complementary *intersects*.
-
-.. code-block:: python
-
- >>> polygon.disjoint(point_r)
- True
-
-Equals
-++++++
-
-.equals(other) : bool
- Two geometries are topologically equal if their interiors intersect and no
- part of the interior or boundary of one geometry intersects the exterior of
- the other. Not to be confused with Python's *__equals__*.
-
-Intersects
-++++++++++
-
-.intersects(other) : bool
- This predicate is the complement of *disjoint*: geometries that do not
- intersect are disjoint. Intersects is the most inclusive predicate.
-
-.. code-block:: python
-
- >>> polygon.intersects(point_b)
- True
-
-Touches
-+++++++
-
-.touches(other) : bool
- True if geometries *only* touch. The least inclusive predicate.
-
-.. code-block:: python
-
- >>> polygon.touches(line_g)
- True
- >>> polygon.touches(line_b)
- False
-
-Within
-++++++
-
-.within(other): bool
- The inverse of *contains*.
-
-
-General Methods
----------------
-
-Distance
-++++++++
-
-.distance(other) : geometry
- The minimum distance from one geometry to the other.
-
-.. code-block:: python
-
- >>> Point(0,0).distance(Point(1,1))
- 1.4142135623730951
-
-
-Scalar Properties
------------------
-
-Area
-++++
-
-.area : float
- Area of the geometry, unitless. Non-zero only for surfaces (polygons,
- multi-polygons).
-
-Bounds
-++++++
-
-.bounds : tuple
- The geometry's (minx, miny, maxx, maxy) bounding box.
-
-Length
-++++++
-
-.length : float
- Length of the geometry, unitless. Non-zero only for linear geometries
- (line strings, rings, polygon boundaries)
-
-Examples
-++++++++
-
-.. code-block:: python
-
- >>> polygon.area
- 4.0
- >>> polygon.bounds
- (-1.0, -1.0, 1.0, 1.0)
- >>> polygon.length
- 8.0
- >>> line_r.length
- 1.0
- >>> line_b.length
- 3.9812058474788765
-
-
-Geometry Parts and Coordinates
-==============================
-
-Coordinate Sequences
---------------------
-
-The coordinates of points, line strings, and polygon rings can be accessed
-through the *coords* attribute of a geometry. *Coords* is an iterator over
-coordinate tuples.
-
-.. code-block:: python
-
- >>> point_r.coords
- <shapely.geometry.base.CoordinateSequence object at ...>
- >>> len(point_r.coords)
- 1
- >>> point_r.coords[0]
- (-1.5, 1.2)
- >>> list(point_r.coords)
- [(-1.5, 1.2)]
-
-The coordinate sequence can be modifed by assigning a sequence (**a**\ 1, ...,
-**a**\ M) to the coords attribute.
-
-.. code-block:: python
-
- >>> point_new = Point(0, 0)
- >>> point_new.coords = (1, 1)
- >>> list(point_new.coords)
- [(1.0, 1.0)]
-
-For line strings:
-
-.. code-block:: python
-
- >>> line_new = LineString([(0,0), (1,1)])
- >>> line_new.coords = [(1,1), (2,2)]
- >>> list(line_new.coords)
- [(1.0, 1.0), (2.0, 2.0)]
-
-
-Polygon Rings
--------------
-
-The exterior boundary of a polygon can be accessed through the *exterior*
-attribute of the geometry object.
-
-.. code-block:: python
-
- >>> polygon.exterior
- <shapely.geometry.polygon.LinearRing object at ...>
- >>> list(polygon.exterior.coords)
- [(-1.0, -1.0), (-1.0, 1.0), (1.0, 1.0), (1.0, -1.0), (-1.0, -1.0)]
-
-The interior boundaries (or holes) of a polygon can be accessed through the
-*interiors* attribute, which is a list of rings.
-
-
-Sub-geometries
---------------
-
-The parts of a multi-part geometry can be accessed through the *geoms*
-attribute of the geometry object, which is an iterator over the sub-geometries:
-
-.. code-block:: python
-
- >>> multi_point.geoms
- <shapely.geometry.base.GeometrySequence object at ...>
- >>> len(multi_point.geoms)
- 3
- >>> from pprint import pprint
- >>> pprint(list(multi_point.geoms))
- [<shapely.geometry.point.Point object at ...>,
- <shapely.geometry.point.Point object at ...>,
- <shapely.geometry.point.Point object at ...>]
-
-The coordinate sequences of these sub-geometries can then be accessed as
-described above.
-
-
-Point Coordinates
------------------
-
-For the sake of convenience the coordinate values of points can be accessed
-read-only via **x**, **y**, and **z** attributes:
-
-.. code-block:: python
-
- >>> point = Point(1.0, 1.0)
- >>> point.x
- 1.0
- >>> point.y
- 1.0
-
-
-Interoperation
-==============
-
-Shapely provides 4 avenues for interoperation with other Python and GIS
-software.
-
-Well-known Formats
-------------------
-
-Well-known Text (WKT)
-+++++++++++++++++++++
-
-The WKT representation of any geometry object can be had via the **wkt**
-attribute:
-
-.. code-block:: python
-
- >>> point_r.wkt
- 'POINT (-1.5000000000000000 1.2000000000000000)'
-
-Hex-encode that string and you have a value that can be conveniently inserted directly into PostGIS
-
-.. code-block:: python
-
- >>> point_r.wkt.encode('hex')
- '504f494e5420282d312e3530303030303030303030303030303020312e3230303030303030303030303030303029'
-
-New geometries can be created from WKT representations using the
-*shapely.wkt.loads* factory (inspired by the *pickle* module)
-
-.. code-block:: python
-
- >>> from shapely.wkt import loads
- >>> loads('POINT (0 0)')
- <shapely.geometry.point.Point object at ...>
-
-Well-known Binary (WKB)
-+++++++++++++++++++++++
-
-The WKB representation of any geometry object can be had via the **wkb**
-attribute. New geometries can be created from WKB data using the
-*shapely.wkb.loads* factory. Use this format to interoperate with ogr.py:
-
-.. code-block:: python
-
- >>> import ogr
- >>> from shapely.wkb import loads
- >>> source = ogr.Open("/tmp/world_borders.shp")
- >>> borders = source.GetLayerByName("world_borders")
- >>> feature = borders.GetNextFeature()
- >>> loads(feature.GetGeometryRef().ExportToWkb())
- <shapely.geometry.polygon.Polygon object at ...>
-
-
-Python Sequences
-----------------
-
-Python sequence data can be analyzed as Shapely geometries using the
-*shapely.geometry.as\** adapters while leaving the data in its original
-storage. A pair of float can be treated as a point with **asPoint**:
-
-.. code-block:: python
-
- >>> from shapely.geometry import asPoint
- >>> coords = [3.0, 4.0]
- >>> pa = asPoint(coords)
- >>> pa.wkt
- 'POINT (3.0000000000000000 4.0000000000000000)'
-
-Move the coordinates and watch the geometry adapter change
-
-.. code-block:: python
-
- >>> coords[0] = 1.0
- >>> pa.wkt
- 'POINT (1.0000000000000000 4.0000000000000000)'
-
-The **asLineString** adapter works much the same. The **asPolygon** adapter is
-used like
-
-.. code-block:: python
-
- >>> from shapely.geometry import asPolygon
- >>> coords = [[0.0, 0.0], [0.0, 1.0], [1.0, 1.0], [1.0, 0.0]]
- >>> hole_coords = [((0.1,0.1), (0.1,0.2), (0.2,0.2), (0.2,0.1))]
- >>> pa = asPolygon(coords, hole_coords)
- >>> len(pa.exterior.coords)
- 5
- >>> len(pa.interiors)
- 1
- >>> len(pa.interiors[0].coords)
- 5
-
-
-Numpy Array Interface
----------------------
-
-Shapely geometries provide the Numpy array interface which means that points,
-line strings, and polygon rings can be used as Numpy arrays:
-
-.. code-block:: python
-
- >>> from numpy import array
- >>> a = array(polygon.exterior)
- >>> a
- array([[-1., -1.],
- [-1., 1.],
- [ 1., 1.],
- [ 1., -1.],
- [-1., -1.]])
-
-The *numpy.asarray* function does not copy coordinate values at the price of
-slower numpy access to coordinates.
-
-The *shapely.geometry.as\** functions can also be used to wrap numpy arrays,
-which can then be analyzed using Shapely while maintaining their original
-storage. A 1 x 2 array can be adapted to a point
-
-.. code-block:: python
-
- >>> a = array([1.0, 2.0])
- >>> pa = asPoint(a)
- >>> pa.wkt
- 'POINT (1.0000000000000000 2.0000000000000000)'
-
-and a N x 2 array can be adapted to a line string
-
-.. code-block:: python
-
- >>> from shapely.geometry import asLineString
- >>> a = array([[1.0, 2.0], [3.0, 4.0]])
- >>> la = asLineString(a)
- >>> la.wkt
- 'LINESTRING (1.0000000000000000 2.0000000000000000, 3.0000000000000000 4.0000000000000000)'
-
-There is no Numpy array representation of a polygon.
-
-
-Python Geo Interface
---------------------
-
-Any object that provides the GeoJSON-like `Python geo interface`_ can be
-adapted and used as a Shapely geometry using the *shapely.geometry.asShape*
-function. For example, a dictionary:
-
-.. code-block:: python
-
- >>> from shapely.geometry import asShape
- >>> d = {"type": "Point", "coordinates": (0.0, 0.0)}
- >>> shape = asShape(d)
- >>> shape.geom_type
- 'Point'
- >>> list(shape.coords)
- [(0.0, 0.0)]
-
-Or a simple placemark-type object:
-
-.. code-block:: python
-
- >>> class GeoThing(object):
- ... def __init__(self, d):
- ... self.__geo_interface__ = d
- >>> thing = GeoThing({"type": "Point", "coordinates": (0.0, 0.0)})
- >>> shape = asShape(thing)
- >>> shape.geom_type
- 'Point'
- >>> list(shape.coords)
- [(0.0, 0.0)]
-
-If you want to copy coordinate data to a new geometry, use the
-*shapely.geometry.shape* function instead.
-
-.. _Python geo interface: http://trac.gispython.org/projects/PCL/wiki/PythonGeoInterface
-
-
-Advanced Features
-=================
-
-Iterative Operations
---------------------
-
-Shapely provides functions for efficient operations on large sets of
-geometries.
-
-Contains
-++++++++
-
-To find the subset of points that are contained within a polygon, use
-*shapely.iterops.contains*:
-
-.. code-block:: python
-
- >>> from shapely.geometry import Polygon
- >>> from shapely.geometry import Point
- >>> coords = ((0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (1.0, 0.0), (0.0, 0.0))
- >>> polygon = Polygon(coords)
- >>> points = [Point(0.5, 0.5), Point(2.0, 2.0)]
- >>> from shapely import iterops
- >>> list(iterops.contains(polygon, points, True))
- [<shapely.geometry.point.Point object at ...>]
-
-The second parameter to *iterops.contains* can be any kind of iterator, even a
-generator of objects. If it yields tuples, then the second element of the tuple
-will be ultimately yielded from *iterops.contains*.
-
-.. code-block:: python
-
- >>> list(iterops.contains(polygon, iter((p, p.wkt) for p in points)))
- ['POINT (0.5000000000000000 0.5000000000000000)']
-
-
-Credits
-=======
-
-Shapely is written by Sean Gillies with contributions from Howard Butler, Kai
-Lautaportti (Hexagon IT), Frédéric Junod (Camptocamp SA), Eric Lemoine
-(Camptocamp SA) and ctypes tips from Justin Bronn (GeoDjango).
-
-
diff --git a/setup.py b/setup.py
index 14e3b5e..0ed51f1 100644
--- a/setup.py
+++ b/setup.py
@@ -1,30 +1,37 @@
-from setuptools import setup, Extension
-from sys import version_info
+import warnings
-# Require ctypes egg only for Python < 2.5
-install_requires = ['setuptools']
-if version_info[:2] < (2,5):
- install_requires.append('ctypes')
+try:
+ from distribute_setup import use_setuptools
+ use_setuptools()
+except:
+ warnings.warn(
+ "Failed to import distribute_setup, continuing without distribute.",
+ Warning)
+
+from setuptools import setup, find_packages
+import sys
-# Get text from README.txt
readme_text = file('README.txt', 'rb').read()
-setup(name = 'Shapely',
- version = '1.0.15',
- description = 'Geospatial geometries, predicates, and operations',
- license = 'BSD',
- keywords = 'geometry topology',
- author = 'Sean Gillies',
- author_email = 'sgillies at frii.com',
- maintainer = 'Sean Gillies',
- maintainer_email = 'sgillies at frii.com',
- url = 'http://trac.gispython.org/lab/wiki/Shapely',
- long_description = readme_text,
- packages = ['shapely', 'shapely.geometry'],
- install_requires = install_requires,
- #tests_require = ['numpy'], -- not working with "tests" command
- test_suite = 'tests.test_suite',
- classifiers = [
+setup_args = dict(
+ metadata_version = '1.2',
+ name = 'Shapely',
+ version = '1.2.1',
+ requires_python = '>=2.5,<3',
+ requires_external = 'libgeos_c (>=3.1)',
+ description = 'Geometric objects, predicates, and operations',
+ license = 'BSD',
+ keywords = 'geometry topology gis',
+ author = 'Sean Gillies',
+ author_email = 'sean.gillies at gmail.com',
+ maintainer = 'Sean Gillies',
+ maintainer_email = 'sean.gillies at gmail.com',
+ url = 'http://trac.gispython.org/lab/wiki/Shapely',
+ long_description = readme_text,
+ packages = ['shapely', 'shapely.geometry'],
+ scripts = ['examples/dissolve.py', 'examples/intersect.py'],
+ test_suite = 'shapely.tests.test_suite',
+ classifiers = [
'Development Status :: 5 - Production/Stable',
'Intended Audience :: Developers',
'Intended Audience :: Science/Research',
@@ -33,4 +40,12 @@ setup(name = 'Shapely',
'Programming Language :: Python',
'Topic :: Scientific/Engineering :: GIS',
],
-)
+ )
+
+# Add DLLs for Windows
+if sys.platform == 'win32':
+ setup_args.update(
+ data_files=[('DLLs', ['DLLs/geos.dll', 'DLLs/libgeos-3-0-0.dll']),]
+ )
+
+setup(**setup_args)
diff --git a/shapely/.___init__.py b/shapely/.___init__.py
deleted file mode 100644
index a69f6cc..0000000
Binary files a/shapely/.___init__.py and /dev/null differ
diff --git a/shapely/._ctypes_declarations.py b/shapely/._ctypes_declarations.py
deleted file mode 100644
index 8078108..0000000
Binary files a/shapely/._ctypes_declarations.py and /dev/null differ
diff --git a/shapely/._iterops.py b/shapely/._iterops.py
deleted file mode 100644
index 69476e9..0000000
Binary files a/shapely/._iterops.py and /dev/null differ
diff --git a/shapely/._ops.py b/shapely/._ops.py
deleted file mode 100644
index 6011f43..0000000
Binary files a/shapely/._ops.py and /dev/null differ
diff --git a/shapely/._predicates.py b/shapely/._predicates.py
deleted file mode 100644
index b3329f2..0000000
Binary files a/shapely/._predicates.py and /dev/null differ
diff --git a/shapely/._topology.py b/shapely/._topology.py
deleted file mode 100644
index b7dcb87..0000000
Binary files a/shapely/._topology.py and /dev/null differ
diff --git a/shapely/._wkb.py b/shapely/._wkb.py
deleted file mode 100644
index 18c1290..0000000
Binary files a/shapely/._wkb.py and /dev/null differ
diff --git a/shapely/__init__.py b/shapely/__init__.py
index 792d600..5bb534f 100644
--- a/shapely/__init__.py
+++ b/shapely/__init__.py
@@ -1 +1 @@
-#
+# package
diff --git a/shapely/coords.py b/shapely/coords.py
new file mode 100644
index 0000000..005115e
--- /dev/null
+++ b/shapely/coords.py
@@ -0,0 +1,167 @@
+"""Coordinate sequence utilities
+"""
+
+from array import array
+from ctypes import string_at, byref, c_char_p, c_double, c_void_p
+from ctypes import c_int, c_size_t, c_uint
+import sys
+
+from shapely.geos import lgeos
+from shapely.topology import Validating
+
+
+class CoordinateSequence(object):
+ """
+ Iterative access to coordinate tuples from the parent geometry's coordinate
+ sequence.
+
+ Example:
+
+ >>> from shapely.wkt import loads
+ >>> g = loads('POINT (0.0 0.0)')
+ >>> list(g.coords)
+ [(0.0, 0.0)]
+
+ """
+
+ # Attributes
+ # ----------
+ # _cseq : c_void_p
+ # Ctypes pointer to GEOS coordinate sequence
+ # _ndim : int
+ # Number of dimensions (2 or 3, generally)
+ # __p__ : object
+ # Parent (Shapely) geometry
+ _cseq = None
+ _ndim = None
+ __p__ = None
+
+ def __init__(self, parent):
+ self.__p__ = parent
+
+ def _update(self):
+ self._ndim = self.__p__._ndim
+ self._cseq = lgeos.GEOSGeom_getCoordSeq(self.__p__._geom)
+
+ def __len__(self):
+ self._update()
+ cs_len = c_uint(0)
+ lgeos.GEOSCoordSeq_getSize(self._cseq, byref(cs_len))
+ return cs_len.value
+
+ def __iter__(self):
+ self._update()
+ dx = c_double()
+ dy = c_double()
+ dz = c_double()
+ for i in range(self.__len__()):
+ lgeos.GEOSCoordSeq_getX(self._cseq, i, byref(dx))
+ lgeos.GEOSCoordSeq_getY(self._cseq, i, byref(dy))
+ if self._ndim == 3: # TODO: use hasz
+ lgeos.GEOSCoordSeq_getZ(self._cseq, i, byref(dz))
+ yield (dx.value, dy.value, dz.value)
+ else:
+ yield (dx.value, dy.value)
+
+ def __getitem__(self, i):
+ self._update()
+ M = self.__len__()
+ if i + M < 0 or i >= M:
+ raise IndexError("index out of range")
+ if i < 0:
+ ii = M + i
+ else:
+ ii = i
+ dx = c_double()
+ dy = c_double()
+ dz = c_double()
+ lgeos.GEOSCoordSeq_getX(self._cseq, ii, byref(dx))
+ lgeos.GEOSCoordSeq_getY(self._cseq, ii, byref(dy))
+ if self._ndim == 3: # TODO: use hasz
+ lgeos.GEOSCoordSeq_getZ(self._cseq, ii, byref(dz))
+ return (dx.value, dy.value, dz.value)
+ else:
+ return (dx.value, dy.value)
+
+ @property
+ def ctypes(self):
+ self._update()
+ n = self._ndim
+ m = self.__len__()
+ array_type = c_double * (m * n)
+ data = array_type()
+ temp = c_double()
+ for i in xrange(m):
+ lgeos.GEOSCoordSeq_getX(self._cseq, i, byref(temp))
+ data[n*i] = temp.value
+ lgeos.GEOSCoordSeq_getY(self._cseq, i, byref(temp))
+ data[n*i+1] = temp.value
+ if n == 3: # TODO: use hasz
+ lgeos.GEOSCoordSeq_getZ(self._cseq, i, byref(temp))
+ data[n*i+2] = temp.value
+ return data
+
+ def array_interface(self):
+ """Provide the Numpy array protocol."""
+ if sys.byteorder == 'little':
+ typestr = '<f8'
+ elif sys.byteorder == 'big':
+ typestr = '>f8'
+ else:
+ raise ValueError(
+ "Unsupported byteorder: neither little nor big-endian")
+ ai = {
+ 'version': 3,
+ 'typestr': typestr,
+ 'data': self.ctypes,
+ }
+ ai.update({'shape': (len(self), self._ndim)})
+ return ai
+
+ __array_interface__ = property(array_interface)
+
+ @property
+ def xy(self):
+ """X and Y arrays"""
+ self._update()
+ m = self.__len__()
+ x = array('d')
+ y = array('d')
+ temp = c_double()
+ for i in xrange(m):
+ lgeos.GEOSCoordSeq_getX(self._cseq, i, byref(temp))
+ x.append(temp.value)
+ lgeos.GEOSCoordSeq_getY(self._cseq, i, byref(temp))
+ y.append(temp.value)
+ return x, y
+
+
+class BoundsOp(Validating):
+
+ def __init__(self, *args):
+ pass
+
+ def __call__(self, this):
+ self._validate(this)
+ env = this.envelope
+ if env.geom_type == 'Point':
+ return env.bounds
+ cs = lgeos.GEOSGeom_getCoordSeq(env.exterior._geom)
+ cs_len = c_uint(0)
+ lgeos.GEOSCoordSeq_getSize(cs, byref(cs_len))
+ minx = 1.e+20
+ maxx = -1e+20
+ miny = 1.e+20
+ maxy = -1e+20
+ temp = c_double()
+ for i in xrange(cs_len.value):
+ lgeos.GEOSCoordSeq_getX(cs, i, byref(temp))
+ x = temp.value
+ if x < minx: minx = x
+ if x > maxx: maxx = x
+ lgeos.GEOSCoordSeq_getY(cs, i, byref(temp))
+ y = temp.value
+ if y < miny: miny = y
+ if y > maxy: maxy = y
+ return (minx, miny, maxx, maxy)
+
diff --git a/shapely/ctypes_declarations.py b/shapely/ctypes_declarations.py
index 60e169c..6d1971c 100644
--- a/shapely/ctypes_declarations.py
+++ b/shapely/ctypes_declarations.py
@@ -3,7 +3,10 @@
import ctypes
-def prototype(lgeos):
+class allocated_c_char_p(ctypes.c_char_p):
+ pass
+
+def prototype(lgeos, geosVersion):
lgeos.initGEOS.restype = None
@@ -14,7 +17,7 @@ def prototype(lgeos):
lgeos.GEOSGeomFromWKT.restype = ctypes.c_void_p
lgeos.GEOSGeomFromWKT.argtypes = [ctypes.c_char_p]
- lgeos.GEOSGeomToWKT.restype = ctypes.c_char_p
+ lgeos.GEOSGeomToWKT.restype = allocated_c_char_p
lgeos.GEOSGeomToWKT.argtypes = [ctypes.c_void_p]
lgeos.GEOS_setWKBOutputDims.restype = ctypes.c_int
@@ -23,10 +26,9 @@ def prototype(lgeos):
lgeos.GEOSGeomFromWKB_buf.restype = ctypes.c_void_p
lgeos.GEOSGeomFromWKB_buf.argtypes = [ctypes.c_void_p, ctypes.c_size_t]
- lgeos.GEOSGeomToWKB_buf.restype = ctypes.c_void_p
+ lgeos.GEOSGeomToWKB_buf.restype = allocated_c_char_p
lgeos.GEOSGeomToWKB_buf.argtypes = [ctypes.c_void_p , ctypes.POINTER(ctypes.c_size_t)]
-
lgeos.GEOSCoordSeq_create.restype = ctypes.c_void_p
lgeos.GEOSCoordSeq_create.argtypes = [ctypes.c_uint, ctypes.c_uint]
@@ -93,6 +95,12 @@ def prototype(lgeos):
lgeos.GEOSBuffer.restype = ctypes.c_void_p
lgeos.GEOSBuffer.argtypes = [ctypes.c_void_p, ctypes.c_double, ctypes.c_int]
+ lgeos.GEOSSimplify.restype = ctypes.c_void_p
+ lgeos.GEOSSimplify.argtypes = [ctypes.c_void_p, ctypes.c_double]
+
+ lgeos.GEOSTopologyPreserveSimplify.restype = ctypes.c_void_p
+ lgeos.GEOSTopologyPreserveSimplify.argtypes = [ctypes.c_void_p, ctypes.c_double]
+
lgeos.GEOSConvexHull.restype = ctypes.c_void_p
lgeos.GEOSConvexHull.argtypes = [ctypes.c_void_p]
@@ -114,7 +122,7 @@ def prototype(lgeos):
lgeos.GEOSGetCentroid.restype = ctypes.c_void_p
lgeos.GEOSGetCentroid.argtypes = [ctypes.c_void_p]
- lgeos.GEOSRelate.restype = ctypes.c_char_p
+ lgeos.GEOSRelate.restype = allocated_c_char_p
lgeos.GEOSRelate.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
lgeos.GEOSPolygonize.restype = ctypes.c_void_p
@@ -126,43 +134,49 @@ def prototype(lgeos):
lgeos.GEOSRelatePattern.restype = ctypes.c_char
lgeos.GEOSRelatePattern.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_char_p]
- lgeos.GEOSDisjoint.restype = ctypes.c_int
+ lgeos.GEOSDisjoint.restype = ctypes.c_byte
lgeos.GEOSDisjoint.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
- lgeos.GEOSTouches.restype = ctypes.c_int
+ lgeos.GEOSTouches.restype = ctypes.c_byte
lgeos.GEOSTouches.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
- lgeos.GEOSIntersects.restype = ctypes.c_int
+ lgeos.GEOSIntersects.restype = ctypes.c_byte
lgeos.GEOSIntersects.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
- lgeos.GEOSCrosses.restype = ctypes.c_int
+ lgeos.GEOSCrosses.restype = ctypes.c_byte
lgeos.GEOSCrosses.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
- lgeos.GEOSWithin.restype = ctypes.c_int
+ lgeos.GEOSWithin.restype = ctypes.c_byte
lgeos.GEOSWithin.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
- lgeos.GEOSContains.restype = ctypes.c_int
+ lgeos.GEOSContains.restype = ctypes.c_byte
lgeos.GEOSContains.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
- lgeos.GEOSOverlaps.restype = ctypes.c_int
+ lgeos.GEOSOverlaps.restype = ctypes.c_byte
lgeos.GEOSOverlaps.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
- lgeos.GEOSEquals.restype = ctypes.c_int
+ lgeos.GEOSEquals.restype = ctypes.c_byte
lgeos.GEOSEquals.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
- lgeos.GEOSisEmpty.restype = ctypes.c_int
+ lgeos.GEOSEqualsExact.restype = ctypes.c_byte
+ lgeos.GEOSEqualsExact.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_double]
+
+ lgeos.GEOSisEmpty.restype = ctypes.c_byte
lgeos.GEOSisEmpty.argtypes = [ctypes.c_void_p]
- lgeos.GEOSisValid.restype = ctypes.c_int
+ lgeos.GEOSisValid.restype = ctypes.c_byte
lgeos.GEOSisValid.argtypes = [ctypes.c_void_p]
- lgeos.GEOSisSimple.restype = ctypes.c_int
+ lgeos.GEOSisValidReason.restype = allocated_c_char_p
+ lgeos.GEOSisValidReason.argtypes = [ctypes.c_void_p]
+
+ lgeos.GEOSisSimple.restype = ctypes.c_byte
lgeos.GEOSisSimple.argtypes = [ctypes.c_void_p]
- lgeos.GEOSisRing.restype = ctypes.c_int
+ lgeos.GEOSisRing.restype = ctypes.c_byte
lgeos.GEOSisRing.argtypes = [ctypes.c_void_p]
- lgeos.GEOSHasZ.restype = ctypes.c_int
+ lgeos.GEOSHasZ.restype = ctypes.c_byte
lgeos.GEOSHasZ.argtypes = [ctypes.c_void_p]
lgeos.GEOSGeomType.restype = ctypes.c_char_p
@@ -210,3 +224,53 @@ def prototype(lgeos):
lgeos.GEOSDistance.restype = ctypes.c_int
lgeos.GEOSDistance.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p]
+ if geosVersion >= (1, 5, 0):
+
+ if hasattr(lgeos, 'GEOSFree'):
+ lgeos.GEOSFree.restype = None
+ lgeos.GEOSFree.argtypes = [ctypes.c_void_p]
+
+ # Prepared geometry, GEOS C API 1.5.0+
+ lgeos.GEOSPrepare.restype = ctypes.c_void_p
+ lgeos.GEOSPrepare.argtypes = [ctypes.c_void_p]
+
+ lgeos.GEOSPreparedGeom_destroy.restype = None
+ lgeos.GEOSPreparedGeom_destroy.argtypes = [ctypes.c_void_p]
+
+ lgeos.GEOSPreparedIntersects.restype = ctypes.c_int
+ lgeos.GEOSPreparedIntersects.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
+
+ lgeos.GEOSPreparedContains.restype = ctypes.c_int
+ lgeos.GEOSPreparedContains.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
+
+ lgeos.GEOSPreparedContainsProperly.restype = ctypes.c_int
+ lgeos.GEOSPreparedContainsProperly.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
+
+ lgeos.GEOSPreparedCovers.restype = ctypes.c_int
+ lgeos.GEOSPreparedCovers.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
+
+ # Other, GEOS C API 1.5.0+
+ if geosVersion >= (1, 5, 0):
+ lgeos.GEOSUnionCascaded.restype = ctypes.c_void_p
+ lgeos.GEOSUnionCascaded.argtypes = [ctypes.c_void_p]
+
+ # 1.6.0
+ if geosVersion >= (1, 6, 0):
+ # Linear referencing features aren't found in versions 1.5,
+ # but not in all libs versioned 1.6.0 either!
+ if hasattr(lgeos, 'GEOSProject'):
+ lgeos.GEOSProject.restype = ctypes.c_double
+ lgeos.GEOSProject.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
+
+ lgeos.GEOSProjectNormalized.restype = ctypes.c_double
+ lgeos.GEOSProjectNormalized.argtypes = [ctypes.c_void_p,
+ ctypes.c_void_p]
+
+ lgeos.GEOSInterpolate.restype = ctypes.c_void_p
+ lgeos.GEOSInterpolate.argtypes = [ctypes.c_void_p,
+ ctypes.c_double]
+
+ lgeos.GEOSInterpolateNormalized.restype = ctypes.c_void_p
+ lgeos.GEOSInterpolateNormalized.argtypes = [ctypes.c_void_p,
+ ctypes.c_double]
+
diff --git a/shapely/geometry/._base.py b/shapely/geometry/._base.py
deleted file mode 100644
index 8fe3660..0000000
Binary files a/shapely/geometry/._base.py and /dev/null differ
diff --git a/shapely/geometry/._linestring.py b/shapely/geometry/._linestring.py
deleted file mode 100644
index 01bc948..0000000
Binary files a/shapely/geometry/._linestring.py and /dev/null differ
diff --git a/shapely/geometry/__init__.py b/shapely/geometry/__init__.py
index b66efc0..f39e02b 100644
--- a/shapely/geometry/__init__.py
+++ b/shapely/geometry/__init__.py
@@ -1,3 +1,6 @@
+"""Geometry classes and factories
+"""
+
from geo import shape, asShape
from point import Point, asPoint
from linestring import LineString, asLineString
@@ -6,3 +9,12 @@ from multipoint import MultiPoint, asMultiPoint
from multilinestring import MultiLineString, asMultiLineString
from multipolygon import MultiPolygon, asMultiPolygon
from collection import GeometryCollection
+
+__all__ = [
+ 'shape', 'asShape', 'Point', 'asPoint', 'LineString', 'asLineString',
+ 'Polygon', 'asPolygon', 'MultiPoint', 'asMultiPoint',
+ 'MultiLineString', 'asMultiLineString', 'MultiPolygon', 'asMultiPolygon',
+ 'GeometryCollection'
+ ]
+
+
diff --git a/shapely/geometry/base.py b/shapely/geometry/base.py
index 3cde024..828d3a8 100644
--- a/shapely/geometry/base.py
+++ b/shapely/geometry/base.py
@@ -1,14 +1,14 @@
-"""
-Base geometry class and utilities.
+"""Base geometry class and utilities
"""
-from ctypes import string_at, byref, c_int, c_size_t, c_char_p, c_double, c_void_p
+from functools import wraps
import sys
+import warnings
-from shapely.geos import lgeos, free, allocated_c_char_p
-from shapely.predicates import BinaryPredicate, UnaryPredicate
-from shapely.topology import BinaryTopologicalOp, UnaryTopologicalOp
-
+from shapely.coords import CoordinateSequence
+from shapely.geos import lgeos
+from shapely.impl import DefaultImplementation
+from shapely import wkb, wkt
GEOMETRY_TYPES = [
'Point',
@@ -23,14 +23,13 @@ GEOMETRY_TYPES = [
def geometry_type_name(g):
if g is None:
- raise ValueError, "Null geometry has no type"
+ raise ValueError("Null geometry has no type")
return GEOMETRY_TYPES[lgeos.GEOSGeomTypeId(g)]
-# Abstract geometry factory for use with topological methods below
-
def geom_factory(g, parent=None):
+ # Abstract geometry factory for use with topological methods below
if not g:
- raise ValueError, "No Shapely geometry can be created from this null value"
+ raise ValueError("No Shapely geometry can be created from null value")
ob = BaseGeometry()
geom_type = geometry_type_name(g)
# TODO: check cost of dynamic import by profiling
@@ -46,222 +45,63 @@ def geom_factory(g, parent=None):
ob._ndim = 2 # callers should be all from 2D worlds
return ob
-
-class CoordinateSequence(object):
-
- _geom = None
- _cseq = None
- _ndim = None
- _length = 0
- index = 0
- __p__ = None
-
- def __init__(self, parent):
- self.__p__ = parent
- self._geom = parent._geom
- self._ndim = parent._ndim
- self.update_cseq()
-
- def update_cseq(self):
- self._cseq = lgeos.GEOSGeom_getCoordSeq(self._geom)
-
- def __iter__(self):
- self.index = 0
- self.update_cseq()
- self._length = self.__len__()
- return self
-
- def next(self):
- dx = c_double()
- dy = c_double()
- dz = c_double()
- i = self.index
- if i < self._length:
- lgeos.GEOSCoordSeq_getX(self._cseq, i, byref(dx))
- lgeos.GEOSCoordSeq_getY(self._cseq, i, byref(dy))
- if self._ndim == 3: # TODO: use hasz
- lgeos.GEOSCoordSeq_getZ(self._cseq, i, byref(dz))
- self.index += 1
- return (dx.value, dy.value, dz.value)
- else:
- self.index += 1
- return (dx.value, dy.value)
- else:
- raise StopIteration
-
- def __len__(self):
- cs_len = c_int(0)
- lgeos.GEOSCoordSeq_getSize(self._cseq, byref(cs_len))
- return cs_len.value
-
- def __getitem__(self, i):
- self.update_cseq()
- M = self.__len__()
- if i + M < 0 or i >= M:
- raise IndexError, "index out of range"
- if i < 0:
- ii = M + i
- else:
- ii = i
- dx = c_double()
- dy = c_double()
- dz = c_double()
- lgeos.GEOSCoordSeq_getX(self._cseq, ii, byref(dx))
- lgeos.GEOSCoordSeq_getY(self._cseq, ii, byref(dy))
- if self._ndim == 3: # TODO: use hasz
- lgeos.GEOSCoordSeq_getZ(self._cseq, ii, byref(dz))
- return (dx.value, dy.value, dz.value)
- else:
- return (dx.value, dy.value)
-
- @property
- def ctypes(self):
- self.update_cseq()
- n = self._ndim
- m = self.__len__()
- array_type = c_double * (m * n)
- data = array_type()
- temp = c_double()
-
- for i in xrange(m):
- lgeos.GEOSCoordSeq_getX(self._cseq, i, byref(temp))
- data[n*i] = temp.value
- lgeos.GEOSCoordSeq_getY(self._cseq, i, byref(temp))
- data[n*i+1] = temp.value
- if n == 3: # TODO: use hasz
- lgeos.GEOSCoordSeq_getZ(self._cseq, i, byref(temp))
- data[n*i+2] = temp.value
- return data
-
- def array_interface(self):
- """Provide the Numpy array protocol."""
- if sys.byteorder == 'little':
- typestr = '<f8'
- elif sys.byteorder == 'big':
- typestr = '>f8'
- else:
- raise ValueError, \
- "Unsupported byteorder: neither little nor big-endian"
- ai = {
- 'version': 3,
- 'typestr': typestr,
- 'data': self.ctypes,
- }
- ai.update({'shape': (len(self), self._ndim)})
- return ai
- __array_interface__ = property(array_interface)
-
-
-class GeometrySequence(object):
-
- _factory = None
- _geom = None
- __p__ = None
- _ndim = None
- _index = 0
- _length = 0
-
- def __init__(self, parent, type):
- self._factory = type
- self.__p__ = parent
- self._geom = parent._geom
- self._ndim = parent._ndim
-
- def __iter__(self):
- self._index = 0
- self._length = self.__len__()
- return self
-
- def next(self):
- if self._index < self.__len__():
- g = self._factory()
- g._owned = True
- g._geom = lgeos.GEOSGetGeometryN(self._geom, self._index)
- self._index += 1
- return g
- else:
- raise StopIteration
-
- def __len__(self):
- return lgeos.GEOSGetNumGeometries(self._geom)
-
- def __getitem__(self, i):
- M = self.__len__()
- if i + M < 0 or i >= M:
- raise IndexError, "index out of range"
- if i < 0:
- ii = M + i
- else:
- ii = i
- g = self._factory()
- g._owned = True
- g._geom = lgeos.GEOSGetGeometryN(self._geom, ii)
- return g
-
- @property
- def _longest(self):
- max = 0
- for g in iter(self):
- l = len(g.coords)
- if l > max:
- max = l
-
-
-class HeterogeneousGeometrySequence(GeometrySequence):
-
- def __init__(self, parent):
- self.__p__ = parent
- self._geom = parent._geom
- self._ndim = parent._ndim
-
- def next(self):
- if self._index < self.__len__():
- sub = lgeos.GEOSGetGeometryN(self._geom, self._index)
- g = geom_factory(sub)
- g._owned = True
- self._index += 1
- return g
- else:
- raise StopIteration
-
-
def exceptNull(func):
"""Decorator which helps avoid GEOS operations on null pointers."""
+ @wraps(func)
def wrapper(*args, **kwargs):
- # self is the first arg
- if not args[0]._geom:
- raise ValueError, "Null geometry supports no operations"
- return func(*args, **kwargs)
- return wrapper
-
-def exceptEitherNull(func):
- """Decorator which avoids GEOS operations on one or more null pointers."""
- def wrapper(*args, **kwargs):
- # self is the first arg
- if not args[0]._geom or not args[1]._geom:
- raise ValueError, "Null geometry supports no operations"
+ if not args[0]._geom or args[0].is_empty:
+ raise ValueError("Null/empty geometry supports no operations")
return func(*args, **kwargs)
return wrapper
+EMPTY = wkb.deserialize('010700000000000000'.decode('hex'))
class BaseGeometry(object):
-
- """Provides GEOS spatial predicates and topological operations.
+ """
+ Provides GEOS spatial predicates and topological operations.
+
"""
- __geom__ = None # See _geom property below
+ # Attributes
+ # ----------
+ # __geom__ : c_void_p
+ # Cached ctypes pointer to GEOS geometry. Not to be accessed.
+ # _geom : c_void_p
+ # Property by which the GEOS geometry is accessed.
+ # __p__ : object
+ # Parent (Shapely) geometry
+ # _ctypes_data : object
+ # Cached ctypes data buffer
+ # _ndim : int
+ # Number of dimensions (2 or 3, generally)
+ # _crs : object
+ # Coordinate reference system. Available for Shapely extensions, but
+ # not implemented here.
+ # _owned : bool
+ # True if this object's GEOS geometry is owned by another as in the case
+ # of a multipart geometry member.
+ __geom__ = EMPTY
__p__ = None
_ctypes_data = None
_ndim = None
_crs = None
_owned = False
+
+ # Backend config
+ impl = DefaultImplementation
- def __init__(self):
- self.__geom__ = None
+ @property
+ def _is_empty(self):
+ return self.__geom__ in [EMPTY, None]
- def __del__(self):
- if self.__geom__ is not None and not self._owned:
+ def empty(self):
+ if not (self._owned or self._is_empty):
+ from shapely.geos import lgeos
lgeos.GEOSGeom_destroy(self.__geom__)
+ self.__geom__ = EMPTY
+
+ def __del__(self):
+ self.empty()
self.__geom__ = None
self.__p__ = None
@@ -269,34 +109,27 @@ class BaseGeometry(object):
return self.to_wkt()
# To support pickling
-
def __reduce__(self):
return (self.__class__, (), self.to_wkb())
def __setstate__(self, state):
- self.__geom__ = lgeos.GEOSGeomFromWKB_buf(
- c_char_p(state),
- c_size_t(len(state))
- )
-
- # _geom has been made a property with the GEOS geometry pointer stored
- # in __geom so that geometries and geometry adapters can share __del__
-
+ self.empty()
+ self.__geom__ = wkb.deserialize(state)
+
+ # The _geom property
def _get_geom(self):
return self.__geom__
-
def _set_geom(self, val):
+ self.empty()
self.__geom__ = val
-
_geom = property(_get_geom, _set_geom)
# Array and ctypes interfaces
+ # ---------------------------
@property
def ctypes(self):
- """Return a ctypes representation.
-
- To be overridden by extension classes."""
+ """Return ctypes buffer"""
raise NotImplementedError
@property
@@ -306,7 +139,8 @@ class BaseGeometry(object):
elif sys.byteorder == 'big':
typestr = '>f8'
else:
- raise ValueError, "Unsupported byteorder: neither little nor big-endian"
+ raise ValueError(
+ "Unsupported byteorder: neither little nor big-endian")
return {
'version': 3,
'typestr': typestr,
@@ -318,191 +152,429 @@ class BaseGeometry(object):
"""Provide the Numpy array protocol."""
raise NotImplementedError
+ # Coordinate access
+ # -----------------
+
@exceptNull
def _get_coords(self):
+ """Access to geometry's coordinates (CoordinateSequence)"""
return CoordinateSequence(self)
def _set_coords(self, ob):
- raise NotImplementedError, \
- "set_coords must be provided by derived classes"
+ raise NotImplementedError(
+ "set_coords must be provided by derived classes")
coords = property(_get_coords, _set_coords)
+ @property
+ def xy(self):
+ """Separate arrays of X and Y coordinate values"""
+ raise NotImplementedError
+
# Python feature protocol
@property
def __geo_interface__(self):
+ """Dictionary representation of the geometry"""
raise NotImplementedError
- @property
- def type(self):
- return self.geometryType()
-
# Type of geometry and its representations
+ # ----------------------------------------
@exceptNull
def geometryType(self):
- """Returns a string representing the geometry type, e.g. 'Polygon'."""
return geometry_type_name(self._geom)
+
+ @property
+ def type(self):
+ return self.geometryType()
- @exceptNull
def to_wkb(self):
- """Returns a WKB byte string representation of the geometry."""
- func = lgeos.GEOSGeomToWKB_buf
- size = c_size_t()
- def errcheck(result, func, argtuple):
- if not result: return None
- retval = string_at(result, size.value)[:]
- free(result)
- return retval
- func.errcheck = errcheck
- return func(c_void_p(self._geom), byref(size))
+ return wkb.dumps(self)
- @exceptNull
def to_wkt(self):
- """Returns a WKT string representation of the geometry."""
- func = lgeos.GEOSGeomToWKT
- def errcheck(result, func, argtuple):
- retval = result.value
- free(result)
- return retval
- func.restype = allocated_c_char_p
- func.errcheck = errcheck
- return lgeos.GEOSGeomToWKT(self._geom)
-
- geom_type = property(geometryType)
- wkt = property(to_wkt)
- wkb = property(to_wkb)
-
- # Basic geometry properties
+ return wkt.dumps(self)
+
+ geom_type = property(geometryType,
+ doc="""Name of the geometry's type, such as 'Point'"""
+ )
+ wkt = property(to_wkt,
+ doc="""WKT representation of the geometry""")
+ wkb = property(to_wkb,
+ doc="""WKB representation of the geometry""")
+
+ # Real-valued properties and methods
+ # ----------------------------------
@property
- @exceptNull
def area(self):
- a = c_double()
- retval = lgeos.GEOSArea(self._geom, byref(a))
- return a.value
+ """Unitless area of the geometry (float)"""
+ return self.impl['area'](self)
+
+ def distance(self, other):
+ """Unitless distance to other geometry (float)"""
+ return self.impl['distance'](self, other)
@property
- @exceptNull
def length(self):
- len = c_double()
- retval = lgeos.GEOSLength(self._geom, byref(len))
- return len.value
+ """Unitless length of the geometry (float)"""
+ return self.impl['length'](self)
- @exceptEitherNull
- def distance(self, other):
- d = c_double()
- retval = lgeos.GEOSDistance(self._geom, other._geom, byref(d))
- return d.value
-
- # Topology operations
- #
- # These use descriptors to reduce the amount of boilerplate.
-
- envelope = UnaryTopologicalOp(lgeos.GEOSEnvelope, geom_factory)
- intersection = BinaryTopologicalOp(lgeos.GEOSIntersection, geom_factory)
- convex_hull = UnaryTopologicalOp(lgeos.GEOSConvexHull, geom_factory)
- difference = BinaryTopologicalOp(lgeos.GEOSDifference, geom_factory)
- symmetric_difference = BinaryTopologicalOp(lgeos.GEOSSymDifference,
- geom_factory)
- boundary = UnaryTopologicalOp(lgeos.GEOSBoundary, geom_factory)
- union = BinaryTopologicalOp(lgeos.GEOSUnion, geom_factory)
- centroid = UnaryTopologicalOp(lgeos.GEOSGetCentroid, geom_factory)
-
- # Buffer has a unique distance argument, so not a descriptor
- @exceptNull
- def buffer(self, distance, quadsegs=16):
- return geom_factory(
- lgeos.GEOSBuffer(self._geom, c_double(distance), c_int(quadsegs))
- )
+ # Topological properties
+ # ----------------------
- # Relate has a unique string return value
- @exceptNull
- def relate(self, other):
- func = lgeos.GEOSRelate
- def errcheck(result, func, argtuple):
- retval = result.value
- free(result)
- return retval
- func.restype = allocated_c_char_p
- func.errcheck = errcheck
- return lgeos.GEOSRelate(self._geom, other._geom)
+ @property
+ def boundary(self):
+ """Returns a lower dimension geometry that bounds the object
+
+ The boundary of a polygon is a line, the boundary of a line is a
+ collection of points. The boundary of a point is an empty (null)
+ collection.
+ """
+ return geom_factory(self.impl['boundary'](self))
- # Binary predicates
- #
- # These use descriptors to reduce the amount of boilerplate.
-
- # TODO: Relate Pattern?
- disjoint = BinaryPredicate(lgeos.GEOSDisjoint)
- touches = BinaryPredicate(lgeos.GEOSTouches)
- intersects = BinaryPredicate(lgeos.GEOSIntersects)
- crosses = BinaryPredicate(lgeos.GEOSCrosses)
- within = BinaryPredicate(lgeos.GEOSWithin)
- contains = BinaryPredicate(lgeos.GEOSContains)
- overlaps = BinaryPredicate(lgeos.GEOSOverlaps)
- equals = BinaryPredicate(lgeos.GEOSEquals)
+ @property
+ def bounds(self):
+ """Returns minimum bounding region (minx, miny, maxx, maxy)"""
+ if self.is_empty:
+ return ()
+ else:
+ return self.impl['bounds'](self)
+
+ @property
+ def centroid(self):
+ """Returns the geometric center of the polygon"""
+ return geom_factory(self.impl['centroid'](self))
+
+ @property
+ def convex_hull(self):
+ """Imagine an elastic band stretched around the geometry: that's a
+ convex hull, more or less
+
+ The convex hull of a three member multipoint, for example, is a
+ triangular polygon.
+ """
+ return geom_factory(self.impl['convex_hull'](self))
+
+ @property
+ def envelope(self):
+ """A figure that envelopes the geometry"""
+ return geom_factory(self.impl['envelope'](self))
+
+ def buffer(self, distance, resolution=16, quadsegs=None):
+ """Returns a geometry with an envelope at a distance from the object's
+ envelope
+
+ A negative distance has a "shrink" effect. A zero distance may be used
+ to "tidy" a polygon. The resolution of the buffer around each vertex of
+ the object increases by increasing the resolution keyword parameter
+ or second positional parameter. Note: the use of a `quadsegs` parameter
+ is deprecated and will be gone from the next major release.
+
+ Example:
+
+ >>> from shapely.wkt import loads
+ >>> g = loads('POINT (0.0 0.0)')
+ >>> g.buffer(1.0).area # 16-gon approx of a unit radius circle
+ 3.1365484905459389
+ >>> g.buffer(1.0, 128).area # 128-gon approximation
+ 3.1415138011443009
+ >>> g.buffer(1.0, 3).area # triangle approximation
+ 3.0
+ """
+ if quadsegs is not None:
+ warnings.warn(
+ "The `quadsegs` argument is deprecated. Use `resolution`.",
+ DeprecationWarning)
+ res = quadsegs
+ else:
+ res = resolution
+ return geom_factory(self.impl['buffer'](self, distance, res))
+
+ def simplify(self, tolerance, preserve_topology=True):
+ """Returns a simplified geometry produced by the Douglas-Puecker
+ algorithm
+
+ Coordinates of the simplified geometry will be no more than the
+ tolerance distance from the original. Unless the topology preserving
+ option is used, the algorithm may produce self-intersecting or
+ otherwise invalid geometries.
+ """
+ if preserve_topology:
+ op = self.impl['topology_preserve_simplify']
+ else:
+ op = self.impl['simplify']
+ return geom_factory(op(self, tolerance))
+
+ # Binary operations
+ # -----------------
+
+ def difference(self, other):
+ """Returns the difference of the geometries"""
+ return geom_factory(self.impl['difference'](self, other))
+
+ def intersection(self, other):
+ """Returns the intersection of the geometries"""
+ return geom_factory(self.impl['intersection'](self, other))
+
+ def symmetric_difference(self, other):
+ """Returns the symmetric difference of the geometries
+ (Shapely geometry)"""
+ return geom_factory(self.impl['symmetric_difference'](self, other))
+
+ def union(self, other):
+ """Returns the union of the geometries (Shapely geometry)"""
+ return geom_factory(self.impl['union'](self, other))
# Unary predicates
- #
- # These use descriptors to reduce the amount of boilerplate.
+ # ----------------
+
+ @property
+ def has_z(self):
+ """True if the geometry's coordinate sequence(s) have z values (are
+ 3-dimensional)"""
+ return bool(self.impl['has_z'](self))
- is_empty = UnaryPredicate(lgeos.GEOSisEmpty)
- is_valid = UnaryPredicate(lgeos.GEOSisValid)
- is_simple = UnaryPredicate(lgeos.GEOSisSimple)
- is_ring = UnaryPredicate(lgeos.GEOSisRing)
- has_z = UnaryPredicate(lgeos.GEOSHasZ)
+ @property
+ def is_empty(self):
+ """True if the set of points in this geometry is empty, else False"""
+ return bool(self.impl['is_empty'](self)) or (self._geom is None)
@property
- @exceptNull
- def bounds(self):
- env = self.envelope
- if env.geom_type != 'Polygon':
- raise ValueError, env.wkt
- cs = lgeos.GEOSGeom_getCoordSeq(env.exterior._geom)
- cs_len = c_int(0)
- lgeos.GEOSCoordSeq_getSize(cs, byref(cs_len))
+ def is_ring(self):
+ """True if the geometry is a closed ring, else False"""
+ return bool(self.impl['is_ring'](self))
+
+ @property
+ def is_simple(self):
+ """True if the geometry is simple, meaning that any self-intersections
+ are only at boundary points, else False"""
+ return bool(self.impl['is_simple'](self))
+
+ @property
+ def is_valid(self):
+ """True if the geometry is valid (definition depends on sub-class),
+ else False"""
+ return bool(self.impl['is_valid'](self))
+
+ # Binary predicates
+ # -----------------
+
+ def relate(self, other):
+ """Returns the DE-9IM intersection matrix for the two geometries
+ (string)"""
+ return self.impl['relate'](self, other)
+
+ def contains(self, other):
+ """Returns True if the geometry contains the other, else False"""
+ return bool(self.impl['contains'](self, other))
+
+ def crosses(self, other):
+ """Returns True if the geometries cross, else False"""
+ return bool(self.impl['crosses'](self, other))
+
+ def disjoint(self, other):
+ """Returns True if geometries are disjoint, else False"""
+ return bool(self.impl['disjoint'](self, other))
+
+ def equals(self, other):
+ """Returns True if geometries are equal, else False"""
+ return bool(self.impl['equals'](self, other))
+
+ def intersects(self, other):
+ """Returns True if geometries intersect, else False"""
+ return bool(self.impl['intersects'](self, other))
+
+ def overlaps(self, other):
+ """Returns True if geometries overlap, else False"""
+ return bool(self.impl['overlaps'](self, other))
+
+ def touches(self, other):
+ """Returns True if geometries touch, else False"""
+ return bool(self.impl['touches'](self, other))
+
+ def within(self, other):
+ """Returns True if geometry is within the other, else False"""
+ return bool(self.impl['within'](self, other))
+
+ def equals_exact(self, other, tolerance):
+ """Returns True if geometries are equal to within a specified
+ tolerance"""
+ # return BinaryPredicateOp('equals_exact', self)(other, tolerance)
+ 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"""
+ return self.equals_exact(other, 0.5 * 10**(-decimal))
+
+ # Linear referencing
+ # ------------------
+
+ def project(self, other, normalized=False):
+ """Returns the distance along this geometry to a point nearest the
+ specified point
- minx = 1.e+20
- maxx = -1e+20
- miny = 1.e+20
- maxy = -1e+20
- temp = c_double()
- for i in xrange(cs_len.value):
- lgeos.GEOSCoordSeq_getX(cs, i, byref(temp))
- x = temp.value
- if x < minx: minx = x
- if x > maxx: maxx = x
- lgeos.GEOSCoordSeq_getY(cs, i, byref(temp))
- y = temp.value
- if y < miny: miny = y
- if y > maxy: maxy = y
+ If the normalized arg is True, return the distance normalized to the
+ length of the linear geometry.
+ """
+ if normalized:
+ op = self.impl['project_normalized']
+ else:
+ op = self.impl['project']
+ return op(self, other)
+
+ def interpolate(self, distance, normalized=False):
+ """Return a point at the specified distance along a linear geometry
- return (minx, miny, maxx, maxy)
+ If the normalized arg is True, the distance will be interpreted as a
+ fraction of the geometry's length.
+ """
+ if normalized:
+ op = self.impl['interpolate_normalized']
+ else:
+ op = self.impl['interpolate']
+ return geom_factory(op(self, distance))
+
+class BaseMultipartGeometry(BaseGeometry):
-class BaseMultiPartGeometry(BaseGeometry):
+ def shape_factory(self, *args):
+ # Factory for part instances, usually a geometry class
+ raise NotImplementedError("To be implemented by derived classes")
@property
def ctypes(self):
- raise NotImplementedError, \
- "Multi-part geometries have no ctypes representations"
+ raise NotImplementedError(
+ "Multi-part geometries have no ctypes representations")
@property
def __array_interface__(self):
"""Provide the Numpy array protocol."""
- raise NotImplementedError, \
- "Multi-part geometries do not themselves provide the array interface"
+ raise NotImplementedError(
+ "Multi-part geometries do not themselves provide the array interface")
def _get_coords(self):
- raise NotImplementedError, \
- "Sub-geometries may have coordinate sequences, but collections do not"
+ raise NotImplementedError(
+ "Sub-geometries may have coordinate sequences, but collections do not")
def _set_coords(self, ob):
- raise NotImplementedError, \
- "Sub-geometries may have coordinate sequences, but collections do not"
+ raise NotImplementedError(
+ "Sub-geometries may have coordinate sequences, but collections do not")
@property
def coords(self):
- raise NotImplementedError, \
- "Multi-part geometries do not provide a coordinate sequence"
+ raise NotImplementedError(
+ "Multi-part geometries do not provide a coordinate sequence")
+
+ @property
+ @exceptNull
+ def geoms(self):
+ return GeometrySequence(self, self.shape_factory)
+
+ def __iter__(self):
+ if not self.is_empty:
+ return iter(self.geoms)
+ else:
+ return iter([])
+
+ def __len__(self):
+ if not self.is_empty:
+ return len(self.geoms)
+ else:
+ return 0
+
+ def __getitem__(self, index):
+ if not self.is_empty:
+ return self.geoms[index]
+ else:
+ return ()[index]
+
+
+class GeometrySequence(object):
+ """
+ Iterative access to members of a homogeneous multipart geometry.
+ """
+
+ # Attributes
+ # ----------
+ # _factory : callable
+ # Returns instances of Shapely geometries
+ # _geom : c_void_p
+ # Ctypes pointer to the parent's GEOS geometry
+ # _ndim : int
+ # Number of dimensions (2 or 3, generally)
+ # __p__ : object
+ # Parent (Shapely) geometry
+ shape_factory = None
+ _geom = None
+ __p__ = None
+ _ndim = None
+
+ def __init__(self, parent, type):
+ self.shape_factory = type
+ self.__p__ = parent
+
+ def _update(self):
+ self._geom = self.__p__._geom
+ self._ndim = self.__p__._ndim
+
+ def _get_geom_item(self, i):
+ g = self.shape_factory()
+ g._owned = True
+ g._geom = lgeos.GEOSGetGeometryN(self._geom, i)
+ g._ndim = self._ndim
+ g.__p__ = self
+ return g
+
+ def __iter__(self):
+ self._update()
+ for i in range(self.__len__()):
+ yield self._get_geom_item(i)
+
+ def __len__(self):
+ self._update()
+ return lgeos.GEOSGetNumGeometries(self._geom)
+
+ def __getitem__(self, i):
+ self._update()
+ M = self.__len__()
+ if i + M < 0 or i >= M:
+ raise IndexError("index out of range")
+ if i < 0:
+ ii = M + i
+ else:
+ ii = i
+ return self._get_geom_item(i)
+
+ @property
+ def _longest(self):
+ max = 0
+ for g in iter(self):
+ l = len(g.coords)
+ if l > max:
+ max = l
+
+
+class HeterogeneousGeometrySequence(GeometrySequence):
+ """
+ Iterative access to a heterogeneous sequence of geometries.
+ """
+
+ def __init__(self, parent):
+ super(HeterogeneousGeometrySequence, self).__init__(parent, None)
+
+ def _get_geom_item(self, i):
+ sub = lgeos.GEOSGetGeometryN(self._geom, i)
+ g = geom_factory(sub)
+ g._owned = True
+ return g
+
+
+# Test runner
+def _test():
+ import doctest
+ doctest.testmod()
+
+if __name__ == "__main__":
+ _test()
diff --git a/shapely/geometry/collection.py b/shapely/geometry/collection.py
index fd66f5c..4afd80d 100644
--- a/shapely/geometry/collection.py
+++ b/shapely/geometry/collection.py
@@ -1,18 +1,22 @@
-"""
-Geometry collections.
+"""Multi-part collections of geometries
"""
-from shapely.geometry.base import BaseMultiPartGeometry
+from shapely.geometry.base import BaseMultipartGeometry
from shapely.geometry.base import HeterogeneousGeometrySequence, exceptNull
-class GeometryCollection(BaseMultiPartGeometry):
+class GeometryCollection(BaseMultipartGeometry):
+
+ """A heterogenous collection of geometries
- """A geometry collection.
+ Attributes
+ ----------
+ geoms : sequence
+ A sequence of Shapely geometry instances
"""
def __init__(self):
- BaseMultiPartGeometry.__init__(self)
+ BaseMultipartGeometry.__init__(self)
@property
def __geo_interface__(self):
diff --git a/shapely/geometry/geo.py b/shapely/geometry/geo.py
index 349ed4a..5c78d0c 100644
--- a/shapely/geometry/geo.py
+++ b/shapely/geometry/geo.py
@@ -1,5 +1,5 @@
"""
-Geometry factories based on the geo interface.
+Geometry factories based on the geo interface
"""
from point import Point, asPoint
@@ -32,7 +32,7 @@ def shape(context):
elif geom_type == "multipolygon":
return MultiPolygon(ob["coordinates"], context_type='geojson')
else:
- raise ValueError, "Unknown geometry type: %s" % geom_type
+ raise ValueError("Unknown geometry type: %s" % geom_type)
def asShape(context):
"""Adapts the context to a geometry interface. The coordinates remain
@@ -46,7 +46,7 @@ def asShape(context):
try:
geom_type = ob.get("type").lower()
except AttributeError:
- raise ValueError, "Context does not provide geo interface"
+ raise ValueError("Context does not provide geo interface")
if geom_type == "point":
return asPoint(ob["coordinates"])
@@ -61,4 +61,4 @@ def asShape(context):
elif geom_type == "multipolygon":
return MultiPolygonAdapter(ob["coordinates"], context_type='geojson')
else:
- raise ValueError, "Unknown geometry type: %s" % geom_type
+ raise ValueError("Unknown geometry type: %s" % geom_type)
diff --git a/shapely/geometry/linestring.py b/shapely/geometry/linestring.py
index 81820cb..44faf6c 100644
--- a/shapely/geometry/linestring.py
+++ b/shapely/geometry/linestring.py
@@ -1,14 +1,125 @@
-"""
-Line strings.
+"""Line strings and related utilities
"""
-from ctypes import byref, c_double, c_int, cast, POINTER, pointer
+from ctypes import c_double, cast, POINTER
from ctypes import ArgumentError
from shapely.geos import lgeos
-from shapely.geometry.base import BaseGeometry, exceptNull
+from shapely.geometry.base import BaseGeometry
from shapely.geometry.proxy import CachingGeometryProxy
+__all__ = ['LineString', 'asLineString']
+
+
+class LineString(BaseGeometry):
+ """
+ A one-dimensional figure comprising one or more line segments
+
+ A LineString has non-zero length and zero area. It may approximate a curve
+ and need not be straight. Unlike a LinearRing, a LineString is not closed.
+ """
+
+ def __init__(self, coordinates=None):
+ """
+ Parameters
+ ----------
+ coordinates : sequence
+ A sequence of (x, y [,z]) numeric coordinate pairs or triples or
+ an object that provides the numpy array interface, including another
+ instance of LineString.
+
+ Example
+ -------
+ Create a line with two segments
+
+ >>> a = LineString([[0, 0], [1, 0], [1, 1]])
+ >>> a.length
+ 2.0
+ """
+ BaseGeometry.__init__(self)
+ if coordinates is not None:
+ self._set_coords(coordinates)
+
+ @property
+ def __geo_interface__(self):
+ return {
+ 'type': 'LineString',
+ 'coordinates': tuple(self.coords)
+ }
+
+ @property
+ def ctypes(self):
+ if not self._ctypes_data:
+ self._ctypes_data = self.coords.ctypes
+ return self._ctypes_data
+
+ def array_interface(self):
+ """Provide the Numpy array protocol."""
+ return self.coords.array_interface()
+
+ __array_interface__ = property(array_interface)
+
+ # Coordinate access
+ def _set_coords(self, coordinates):
+ self.empty()
+ self._geom, self._ndim = geos_linestring_from_py(coordinates)
+
+ coords = property(BaseGeometry._get_coords, _set_coords)
+
+ @property
+ def xy(self):
+ """Separate arrays of X and Y coordinate values
+
+ Example:
+
+ >>> x, y = LineString(((0, 0), (1, 1))).xy
+ >>> list(x)
+ [0.0, 1.0]
+ >>> list(y)
+ [0.0, 1.0]
+ """
+ return self.coords.xy
+
+
+class LineStringAdapter(CachingGeometryProxy, LineString):
+
+ def __init__(self, context):
+ self.context = context
+ self.factory = geos_linestring_from_py
+
+ @property
+ def _ndim(self):
+ try:
+ # From array protocol
+ array = self.context.__array_interface__
+ n = array['shape'][1]
+ assert n == 2 or n == 3
+ return n
+ except AttributeError:
+ # Fall back on list
+ return len(self.context[0])
+
+ @property
+ def __array_interface__(self):
+ """Provide the Numpy array protocol."""
+ try:
+ return self.context.__array_interface__
+ except AttributeError:
+ return self.array_interface()
+
+ _get_coords = BaseGeometry._get_coords
+
+ def _set_coords(self, ob):
+ raise NotImplementedError(
+ "Adapters can not modify their coordinate sources")
+
+ coords = property(_get_coords)
+
+
+def asLineString(context):
+ """Adapt an object the LineString interface"""
+ return LineStringAdapter(context)
+
def geos_linestring_from_py(ob, update_geom=None, update_ndim=0):
try:
@@ -17,11 +128,13 @@ def geos_linestring_from_py(ob, update_geom=None, update_ndim=0):
assert len(array['shape']) == 2
m = array['shape'][0]
if m < 2:
- raise ValueError, "LineStrings must have at least 2 coordinate tuples"
+ raise ValueError(
+ "LineStrings must have at least 2 coordinate tuples")
try:
n = array['shape'][1]
except IndexError:
- raise ValueError, "Input %s is the wrong shape for a LineString" % str(ob)
+ raise ValueError(
+ "Input %s is the wrong shape for a LineString" % str(ob))
assert n == 2 or n == 3
# Make pointer to the coordinate array
@@ -34,9 +147,9 @@ def geos_linestring_from_py(ob, update_geom=None, update_ndim=0):
if update_geom is not None:
cs = lgeos.GEOSGeom_getCoordSeq(update_geom)
if n != update_ndim:
- raise ValueError, \
+ raise ValueError(
"Wrong coordinate dimensions; this geometry has dimensions: %d" \
- % update_ndim
+ % update_ndim)
else:
cs = lgeos.GEOSCoordSeq_create(m, n)
@@ -49,7 +162,7 @@ def geos_linestring_from_py(ob, update_geom=None, update_ndim=0):
try:
dz = c_double(cp[n*i+2])
except IndexError:
- raise ValueError, "Inconsistent coordinate dimensionality"
+ raise ValueError("Inconsistent coordinate dimensionality")
# Because of a bug in the GEOS C API,
# always set X before Y
@@ -62,20 +175,22 @@ def geos_linestring_from_py(ob, update_geom=None, update_ndim=0):
# Fall back on list
m = len(ob)
if m < 2:
- raise ValueError, "LineStrings must have at least 2 coordinate tuples"
+ raise ValueError(
+ "LineStrings must have at least 2 coordinate tuples")
try:
n = len(ob[0])
except TypeError:
- raise ValueError, "Input %s is the wrong shape for a LineString" % str(ob)
+ raise ValueError(
+ "Input %s is the wrong shape for a LineString" % str(ob))
assert n == 2 or n == 3
# Create a coordinate sequence
if update_geom is not None:
cs = lgeos.GEOSGeom_getCoordSeq(update_geom)
if n != update_ndim:
- raise ValueError, \
+ raise ValueError(
"Wrong coordinate dimensions; this geometry has dimensions: %d" \
- % update_ndim
+ % update_ndim)
else:
cs = lgeos.GEOSCoordSeq_create(m, n)
@@ -89,7 +204,7 @@ def geos_linestring_from_py(ob, update_geom=None, update_ndim=0):
try:
dz = c_double(coords[2])
except IndexError:
- raise ValueError, "Inconsistent coordinate dimensionality"
+ raise ValueError("Inconsistent coordinate dimensionality")
# Because of a bug in the GEOS C API,
# always set X before Y
@@ -106,124 +221,10 @@ def geos_linestring_from_py(ob, update_geom=None, update_ndim=0):
def update_linestring_from_py(geom, ob):
geos_linestring_from_py(ob, geom._geom, geom._ndim)
-
-class LineString(BaseGeometry):
-
- """A line string, also known as a polyline.
-
- """
-
- def __init__(self, coordinates=None):
- """Initialize.
-
- Parameters
- ----------
-
- coordinates : sequence or array
- This may be an object that satisfies the numpy array protocol,
- providing an M x 2 or M x 3 (with z) array, or it may be a sequence
- of x, y (,z) coordinate sequences.
-
- Example
- -------
-
- >>> line = LineString([[0.0, 0.0], [1.0, 2.0]])
- >>> line = LineString(array([[0.0, 0.0], [1.0, 2.0]]))
-
- Each result in a line string from (0.0, 0.0) to (1.0, 2.0).
- """
- BaseGeometry.__init__(self)
- self._init_geom(coordinates)
-
- def _init_geom(self, coordinates):
- if coordinates is None:
- # allow creation of null lines, to support unpickling
- pass
- else:
- self._geom, self._ndim = geos_linestring_from_py(coordinates)
-
- @property
- def __geo_interface__(self):
- return {
- 'type': 'LineString',
- 'coordinates': tuple(self.coords)
- }
-
- @property
- @exceptNull
- def ctypes(self):
- if not self._ctypes_data:
- self._ctypes_data = self.coords.ctypes
- return self._ctypes_data
-
- def array_interface(self):
- """Provide the Numpy array protocol."""
- return self.coords.array_interface()
-
- __array_interface__ = property(array_interface)
-
- # Coordinate access
-
- def _set_coords(self, coordinates):
- if self._geom is None:
- self._init_geom(coordinates)
- update_linestring_from_py(self, coordinates)
-
- coords = property(BaseGeometry._get_coords, _set_coords)
-
-
-class LineStringAdapter(CachingGeometryProxy, LineString):
-
- """Adapts a Python coordinate pair or a numpy array to the line string
- interface.
- """
-
- context = None
- _owned = False
-
- def __init__(self, context):
- self.context = context
- self.factory = geos_linestring_from_py
-
- @property
- def _ndim(self):
- try:
- # From array protocol
- array = self.context.__array_interface__
- n = array['shape'][1]
- assert n == 2 or n == 3
- return n
- except AttributeError:
- # Fall back on list
- return len(self.context[0])
-
- @property
- def __array_interface__(self):
- """Provide the Numpy array protocol."""
- try:
- return self.context.__array_interface__
- except AttributeError:
- return self.array_interface()
-
- _get_coords = BaseGeometry._get_coords
-
- def _set_coords(self, ob):
- raise NotImplementedError, \
- "Component rings have coordinate sequences, but the polygon does not"
-
- coords = property(_get_coords)
-
-
-def asLineString(context):
- """Factory for PointAdapter instances."""
- return LineStringAdapter(context)
-
-
# Test runner
def _test():
import doctest
doctest.testmod()
-
if __name__ == "__main__":
_test()
diff --git a/shapely/geometry/multilinestring.py b/shapely/geometry/multilinestring.py
index a831b58..ebf78a4 100644
--- a/shapely/geometry/multilinestring.py
+++ b/shapely/geometry/multilinestring.py
@@ -1,83 +1,53 @@
-"""
-Multi-part collection of linestrings.
+"""Collections of linestrings and related utilities
"""
-from ctypes import byref, c_double, c_int, c_void_p, cast, POINTER, pointer
+from ctypes import c_double, c_void_p, cast, POINTER
from shapely.geos import lgeos
-from shapely.geometry.base import BaseGeometry, GeometrySequence, exceptNull
+from shapely.geometry.base import BaseMultipartGeometry
from shapely.geometry.linestring import LineString, geos_linestring_from_py
from shapely.geometry.proxy import CachingGeometryProxy
-
-def geos_multilinestring_from_py(ob):
- """ob must be either a sequence or array of sequences or arrays."""
- try:
- # From array protocol
- array = ob.__array_interface__
- assert len(array['shape']) == 1
- L = array['shape'][0]
- assert L >= 1
-
- # Make pointer to the coordinate array
- cp = cast(array['data'][0], POINTER(c_double))
-
- # Array of pointers to sub-geometries
- subs = (c_void_p * L)()
-
- for l in xrange(L):
- geom, ndims = geos_linestring_from_py(array['data'][l])
- subs[i] = cast(geom, c_void_p)
- N = lgeos.GEOSGeom_getDimensions(subs[0])
-
- except AttributeError:
- # Fall back on list
- L = len(ob)
- N = len(ob[0][0])
- assert L >= 1
- assert N == 2 or N == 3
-
- # Array of pointers to point geometries
- subs = (c_void_p * L)()
-
- # add to coordinate sequence
- for l in xrange(L):
- geom, ndims = geos_linestring_from_py(ob[l])
- subs[l] = cast(geom, c_void_p)
-
- return (lgeos.GEOSGeom_createCollection(5, subs, L), N)
+__all__ = ['MultiLineString', 'asMultiLineString']
-class MultiLineString(BaseGeometry):
-
- """a multiple linestring geometry.
+class MultiLineString(BaseMultipartGeometry):
"""
+ A collection of one or more line strings
+
+ A MultiLineString has non-zero length and zero area.
- def __init__(self, coordinates=None):
- """Initialize.
+ Attributes
+ ----------
+ geoms : sequence
+ A sequence of LineStrings
+ """
+ def __init__(self, lines=None):
+ """
Parameters
----------
-
- coordinates : sequence
- Contains coordinate sequences or objects that provide the numpy
- array protocol, providing an M x 2 or M x 3 (with z) array.
+ lines : sequence
+ A sequence of line-like coordinate sequences or objects that
+ provide the numpy array interface, including instances of
+ LineString.
Example
-------
+ Construct a collection containing one line string.
- >>> geom = MultiLineString( [[[0.0, 0.0], [1.0, 2.0]]] )
- >>> geom = MultiLineString( [ array([[0.0, 0.0], [1.0, 2.0]]) ] )
-
- Each result in a collection containing one line string.
+ >>> lines = MultiLineString( [[[0.0, 0.0], [1.0, 2.0]]] )
"""
- BaseGeometry.__init__(self)
+ super(MultiLineString, self).__init__()
- if coordinates is None:
+ if lines is None:
# allow creation of null lines, to support unpickling
pass
else:
- self._geom, self._ndim = geos_multilinestring_from_py(coordinates)
+ self._geom, self._ndim = geos_multilinestring_from_py(lines)
+
+ def shape_factory(self, *args):
+ return LineString(*args)
@property
def __geo_interface__(self):
@@ -86,41 +56,8 @@ class MultiLineString(BaseGeometry):
'coordinates': tuple(tuple(c for c in g.coords) for g in self.geoms)
}
- @property
- def ctypes(self):
- raise NotImplementedError, \
- "Multi-part geometries have no ctypes representations"
-
- @property
- def __array_interface__(self):
- """Provide the Numpy array protocol."""
- raise NotImplementedError, \
- "Multi-part geometries do not themselves provide the array interface"
-
- def _get_coords(self):
- raise NotImplementedError, \
- "Component rings have coordinate sequences, but the polygon does not"
-
- def _set_coords(self, ob):
- raise NotImplementedError, \
- "Component rings have coordinate sequences, but the polygon does not"
-
- @property
- def coords(self):
- raise NotImplementedError, \
- "Multi-part geometries do not provide a coordinate sequence"
-
- @property
- @exceptNull
- def geoms(self):
- return GeometrySequence(self, LineString)
-
class MultiLineStringAdapter(CachingGeometryProxy, MultiLineString):
-
- """Adapts sequences of sequences or numpy arrays to the multilinestring
- interface.
- """
context = None
_owned = False
@@ -143,15 +80,54 @@ class MultiLineStringAdapter(CachingGeometryProxy, MultiLineString):
def asMultiLineString(context):
- """Factory for MultiLineStringAdapter instances."""
+ """Adapts a sequence of objects to the MultiLineString interface"""
return MultiLineStringAdapter(context)
+def geos_multilinestring_from_py(ob):
+ # ob must be either a sequence or array of sequences or arrays
+ try:
+ # From array protocol
+ array = ob.__array_interface__
+ assert len(array['shape']) == 1
+ L = array['shape'][0]
+ assert L >= 1
+
+ # Make pointer to the coordinate array
+ cp = cast(array['data'][0], POINTER(c_double))
+
+ # Array of pointers to sub-geometries
+ subs = (c_void_p * L)()
+
+ for l in xrange(L):
+ geom, ndims = geos_linestring_from_py(array['data'][l])
+ subs[i] = cast(geom, c_void_p)
+ N = lgeos.GEOSGeom_getDimensions(subs[0])
+ except (NotImplementedError, AttributeError):
+ obs = getattr(ob, 'geoms', ob)
+ L = len(obs)
+ exemplar = obs[0]
+ try:
+ N = len(exemplar[0])
+ except TypeError:
+ N = exemplar._ndim
+ assert L >= 1
+ assert N == 2 or N == 3
+
+ # Array of pointers to point geometries
+ subs = (c_void_p * L)()
+
+ # add to coordinate sequence
+ for l in xrange(L):
+ geom, ndims = geos_linestring_from_py(obs[l])
+ subs[l] = cast(geom, c_void_p)
+
+ return (lgeos.GEOSGeom_createCollection(5, subs, L), N)
+
# Test runner
def _test():
import doctest
doctest.testmod()
-
if __name__ == "__main__":
_test()
diff --git a/shapely/geometry/multipoint.py b/shapely/geometry/multipoint.py
index 9dbb887..6a36a92 100644
--- a/shapely/geometry/multipoint.py
+++ b/shapely/geometry/multipoint.py
@@ -1,88 +1,60 @@
-"""
-Multiple points.
+"""Collections of points and related utilities
"""
-from ctypes import byref, c_double, c_int, c_void_p, cast, POINTER, pointer
+from ctypes import byref, c_double, c_void_p, cast, POINTER
+from ctypes import ArgumentError
from shapely.geos import lgeos
-from shapely.geometry.base import BaseGeometry, GeometrySequence, exceptNull
+from shapely.geometry.base import BaseMultipartGeometry, exceptNull
from shapely.geometry.point import Point, geos_point_from_py
from shapely.geometry.proxy import CachingGeometryProxy
+__all__ = ['MultiPoint', 'asMultiPoint']
-def geos_multipoint_from_py(ob):
- try:
- # From array protocol
- array = ob.__array_interface__
- assert len(array['shape']) == 2
- m = array['shape'][0]
- n = array['shape'][1]
- assert m >= 1
- assert n == 2 or n == 3
-
- # Make pointer to the coordinate array
- cp = cast(array['data'][0], POINTER(c_double))
-
- # Array of pointers to sub-geometries
- subs = (c_void_p * m)()
-
- for i in xrange(m):
- geom, ndims = geos_point_from_py(cp[n*i:n*i+2])
- subs[i] = cast(geom, c_void_p)
-
- except AttributeError:
- # Fall back on list
- m = len(ob)
- n = len(ob[0])
- assert n == 2 or n == 3
- # Array of pointers to point geometries
- subs = (c_void_p * m)()
-
- # add to coordinate sequence
- for i in xrange(m):
- coords = ob[i]
- geom, ndims = geos_point_from_py(coords)
- subs[i] = cast(geom, c_void_p)
-
- return lgeos.GEOSGeom_createCollection(4, subs, m), n
+class MultiPoint(BaseMultipartGeometry):
+ """A collection of one or more points
-class MultiPoint(BaseGeometry):
+ A MultiPoint has zero area and zero length.
- """A multiple point geometry.
+ Attributes
+ ----------
+ geoms : sequence
+ A sequence of Points
"""
- def __init__(self, coordinates=None):
- """Initialize.
-
+ def __init__(self, points=None):
+ """
Parameters
----------
-
- coordinates : sequence or array
- This may be an object that satisfies the numpy array protocol,
- providing an M x 2 or M x 3 (with z) array, or it may be a sequence
- of x, y (,z) coordinate sequences.
+ points : sequence
+ A sequence of (x, y [,z]) numeric coordinate pairs or triples or a
+ sequence of objects that implement the numpy array interface,
+ including instaces of Point.
Example
-------
+ Construct a 2 point collection
- >>> geom = MultiPoint([[0.0, 0.0], [1.0, 2.0]])
- >>> geom = MultiPoint(array([[0.0, 0.0], [1.0, 2.0]]))
-
- Each result in a line string from (0.0, 0.0) to (1.0, 2.0).
+ >>> ob = MultiPoint([[0.0, 0.0], [1.0, 2.0]])
+ >>> len(ob.geoms)
+ 2
+ >>> type(ob.geoms[0]) == Point
+ True
"""
- BaseGeometry.__init__(self)
+ super(MultiPoint, self).__init__()
- if coordinates is None:
- # allow creation of null lines, to support unpickling
+ if points is None:
+ # allow creation of empty multipoints, to support unpickling
pass
else:
- self._geom, self._ndim = geos_multipoint_from_py(coordinates)
+ self._geom, self._ndim = geos_multipoint_from_py(points)
+ def shape_factory(self, *args):
+ return Point(*args)
@property
- @exceptNull
def __geo_interface__(self):
return {
'type': 'MultiPoint',
@@ -119,31 +91,9 @@ class MultiPoint(BaseGeometry):
return ai
__array_interface__ = property(array_interface)
- def _get_coords(self):
- raise NotImplementedError, \
- "Component rings have coordinate sequences, but the polygon does not"
-
- def _set_coords(self, ob):
- raise NotImplementedError, \
- "Component rings have coordinate sequences, but the polygon does not"
-
- @property
- def coords(self):
- raise NotImplementedError, \
- "Multipart geometries do not themselves provide coordinate sequences"
-
- @property
- @exceptNull
- def geoms(self):
- return GeometrySequence(self, Point)
-
class MultiPointAdapter(CachingGeometryProxy, MultiPoint):
- """Adapts a Python coordinate pair or a numpy array to the multipoint
- interface.
- """
-
context = None
_owned = False
@@ -173,10 +123,53 @@ class MultiPointAdapter(CachingGeometryProxy, MultiPoint):
def asMultiPoint(context):
- """Factory for MultiPointAdapter instances."""
+ """Adapt a sequence of objects to the MultiPoint interface"""
return MultiPointAdapter(context)
+def geos_multipoint_from_py(ob):
+ try:
+ # From array protocol
+ array = ob.__array_interface__
+ assert len(array['shape']) == 2
+ m = array['shape'][0]
+ n = array['shape'][1]
+ assert m >= 1
+ assert n == 2 or n == 3
+
+ # Make pointer to the coordinate array
+ try:
+ cp = cast(array['data'][0], POINTER(c_double))
+ except ArgumentError:
+ cp = array['data']
+
+ # Array of pointers to sub-geometries
+ subs = (c_void_p * m)()
+
+ for i in xrange(m):
+ geom, ndims = geos_point_from_py(cp[n*i:n*i+2])
+ subs[i] = cast(geom, c_void_p)
+
+ except AttributeError:
+ # Fall back on list
+ m = len(ob)
+ try:
+ n = len(ob[0])
+ except TypeError:
+ n = ob[0]._ndim
+ assert n == 2 or n == 3
+
+ # Array of pointers to point geometries
+ subs = (c_void_p * m)()
+
+ # add to coordinate sequence
+ for i in xrange(m):
+ coords = ob[i]
+ geom, ndims = geos_point_from_py(coords)
+ subs[i] = cast(geom, c_void_p)
+
+ return lgeos.GEOSGeom_createCollection(4, subs, m), n
+
# Test runner
def _test():
import doctest
diff --git a/shapely/geometry/multipolygon.py b/shapely/geometry/multipolygon.py
index dd78aed..6625d68 100644
--- a/shapely/geometry/multipolygon.py
+++ b/shapely/geometry/multipolygon.py
@@ -1,71 +1,54 @@
-"""
-Multi-part collection of polygons.
+"""Collections of polygons and related utilities
"""
-from ctypes import byref, c_double, c_int, c_void_p, cast, POINTER, pointer
+from ctypes import c_void_p, cast
from shapely.geos import lgeos
-from shapely.geometry.base import BaseGeometry, GeometrySequence, exceptNull
+from shapely.geometry.base import BaseMultipartGeometry
from shapely.geometry.polygon import Polygon, geos_polygon_from_py
from shapely.geometry.proxy import CachingGeometryProxy
-
-def geos_multipolygon_from_py(ob):
- """ob must provide Python geo interface coordinates."""
- L = len(ob)
- N = len(ob[0][0][0])
- assert L >= 1
- assert N == 2 or N == 3
-
- subs = (c_void_p * L)()
- for l in xrange(L):
- geom, ndims = geos_polygon_from_py(ob[l][0], ob[l][1:])
- subs[l] = cast(geom, c_void_p)
-
- return (lgeos.GEOSGeom_createCollection(6, subs, L), N)
-
-def geos_multipolygon_from_polygons(ob):
- """ob must be either a sequence or array of sequences or arrays."""
- L = len(ob)
- N = len(ob[0][0][0])
- assert L >= 1
- assert N == 2 or N == 3
-
- subs = (c_void_p * L)()
- for l in xrange(L):
- geom, ndims = geos_polygon_from_py(ob[l][0], ob[l][1])
- subs[l] = cast(geom, c_void_p)
-
- return (lgeos.GEOSGeom_createCollection(6, subs, L), N)
-
+__all__ = ['MultiPolygon', 'asMultiPolygon']
-class MultiPolygon(BaseGeometry):
+class MultiPolygon(BaseMultipartGeometry):
- """a multiple polygon geometry.
+ """A collection of one or more polygons
+
+ If component polygons overlap the collection is `invalid` and some
+ operations on it may fail.
+
+ Attributes
+ ----------
+ geoms : sequence
+ A sequence of `Polygon` instances
"""
def __init__(self, polygons=None, context_type='polygons'):
- """Initialize.
-
+ """
Parameters
----------
-
polygons : sequence
A sequence of (shell, holes) tuples where shell is the sequence
representation of a linear ring (see linearring.py) and holes is
- a sequence of such linear rings.
+ a sequence of such linear rings
Example
-------
- >>> geom = MultiPolygon( [
- ... (
- ... ((0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (1.0, 0.0)),
- ... [((0.1,0.1), (0.1,0.2), (0.2,0.2), (0.2,0.1))]
- ... )
- ... ] )
+ Construct a collection from a sequence of coordinate tuples
+
+ >>> ob = MultiPolygon( [
+ ... (
+ ... ((0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (1.0, 0.0)),
+ ... [((0.1,0.1), (0.1,0.2), (0.2,0.2), (0.2,0.1))]
+ ... )
+ ... ] )
+ >>> len(ob.geoms)
+ 1
+ >>> type(ob.geoms[0]) == Polygon
+ True
"""
- BaseGeometry.__init__(self)
+ super(MultiPolygon, self).__init__()
if polygons is None:
# allow creation of null collections, to support unpickling
@@ -75,6 +58,9 @@ class MultiPolygon(BaseGeometry):
elif context_type == 'geojson':
self._geom, self._ndim = geos_multipolygon_from_py(polygons)
+ def shape_factory(self, *args):
+ return Polygon(*args)
+
@property
def __geo_interface__(self):
allcoords = []
@@ -89,41 +75,8 @@ class MultiPolygon(BaseGeometry):
'coordinates': allcoords
}
- @property
- def ctypes(self):
- raise NotImplementedError, \
- "Multi-part geometries have no ctypes representations"
-
- @property
- def __array_interface__(self):
- """Provide the Numpy array protocol."""
- raise NotImplementedError, \
- "Multi-part geometries do not themselves provide the array interface"
-
- def _get_coords(self):
- raise NotImplementedError, \
- "Component rings have coordinate sequences, but the polygon does not"
-
- def _set_coords(self, ob):
- raise NotImplementedError, \
- "Component rings have coordinate sequences, but the polygon does not"
-
- @property
- def coords(self):
- raise NotImplementedError, \
- "Multi-part geometries do not provide a coordinate sequence"
-
- @property
- @exceptNull
- def geoms(self):
- return GeometrySequence(self, Polygon)
-
class MultiPolygonAdapter(CachingGeometryProxy, MultiPolygon):
-
- """Adapts sequences of sequences or numpy arrays to the multipolygon
- interface.
- """
context = None
_owned = False
@@ -149,15 +102,53 @@ class MultiPolygonAdapter(CachingGeometryProxy, MultiPolygon):
def asMultiPolygon(context):
- """Factory for MultiLineStringAdapter instances."""
+ """Adapts a sequence of objects to the MultiPolygon interface"""
return MultiPolygonAdapter(context)
+def geos_multipolygon_from_py(ob):
+ """ob must provide Python geo interface coordinates."""
+ L = len(ob)
+ N = len(ob[0][0][0])
+ assert L >= 1
+ assert N == 2 or N == 3
+
+ subs = (c_void_p * L)()
+ for l in xrange(L):
+ geom, ndims = geos_polygon_from_py(ob[l][0], ob[l][1:])
+ subs[l] = cast(geom, c_void_p)
+
+ return (lgeos.GEOSGeom_createCollection(6, subs, L), N)
+
+def geos_multipolygon_from_polygons(ob):
+ """ob must be either a sequence or array of sequences or arrays."""
+ obs = getattr(ob, 'geoms', None) or ob
+ L = len(obs)
+ exemplar = obs[0]
+ try:
+ N = len(exemplar[0][0])
+ except TypeError:
+ N = exemplar._ndim
+ assert L >= 1
+ assert N == 2 or N == 3
+
+ subs = (c_void_p * L)()
+ for l in xrange(L):
+ shell = getattr(obs[l], 'exterior', None)
+ if shell is None:
+ shell = obs[l][0]
+ holes = getattr(obs[l], 'interiors', None)
+ if holes is None:
+ holes = obs[l][1]
+ geom, ndims = geos_polygon_from_py(shell, holes)
+ subs[l] = cast(geom, c_void_p)
+
+ return (lgeos.GEOSGeom_createCollection(6, subs, L), N)
+
# Test runner
def _test():
import doctest
doctest.testmod()
-
if __name__ == "__main__":
_test()
diff --git a/shapely/geometry/point.py b/shapely/geometry/point.py
index af00131..9f5e8d4 100644
--- a/shapely/geometry/point.py
+++ b/shapely/geometry/point.py
@@ -1,80 +1,21 @@
-"""
-Points.
+"""Points and related utilities
"""
-from ctypes import string_at, create_string_buffer, \
- c_char_p, c_double, c_float, c_int, c_uint, c_size_t, c_ubyte, \
- c_void_p, byref
+from ctypes import c_double
from ctypes import cast, POINTER
from shapely.geos import lgeos, DimensionError
-from shapely.geometry.base import BaseGeometry, CoordinateSequence
-from shapely.geometry.base import exceptNull
+from shapely.geometry.base import BaseGeometry
from shapely.geometry.proxy import CachingGeometryProxy
-
-def geos_point_from_py(ob, update_geom=None, update_ndim=0):
- """Create a GEOS geom from an object that is a coordinate sequence
- or that provides the array interface.
-
- Returns the GEOS geometry and the number of its dimensions.
- """
- try:
- # From array protocol
- array = ob.__array_interface__
- assert len(array['shape']) == 1
- n = array['shape'][0]
- assert n == 2 or n == 3
-
- cdata = array['data'][0]
- cp = cast(cdata, POINTER(c_double))
- dx = c_double(cp[0])
- dy = c_double(cp[1])
- dz = None
- if n == 3:
- dz = c_double(cp[2])
- ndim = 3
- except AttributeError:
- # Fall back on the case of Python sequence data
- # Accept either (x, y) or [(x, y)]
- if type(ob[0]) == type(tuple()):
- coords = ob[0]
- else:
- coords = ob
- n = len(coords)
- dx = c_double(coords[0])
- dy = c_double(coords[1])
- dz = None
- if n == 3:
- dz = c_double(coords[2])
-
- if update_geom:
- cs = lgeos.GEOSGeom_getCoordSeq(update_geom)
- if n != update_ndim:
- raise ValueError, \
- "Wrong coordinate dimensions; this geometry has dimensions: %d" \
- % update_ndim
- else:
- cs = lgeos.GEOSCoordSeq_create(1, n)
-
- # Because of a bug in the GEOS C API, always set X before Y
- lgeos.GEOSCoordSeq_setX(cs, 0, dx)
- lgeos.GEOSCoordSeq_setY(cs, 0, dy)
- if n == 3:
- lgeos.GEOSCoordSeq_setZ(cs, 0, dz)
-
- if update_geom:
- return None
- else:
- return lgeos.GEOSGeom_createPoint(cs), n
-
-def update_point_from_py(geom, ob):
- geos_point_from_py(ob, geom._geom, geom._ndim)
+__all__ = ['Point', 'asPoint']
class Point(BaseGeometry):
+ """
+ A zero dimensional feature
- """A point geometry.
+ A point has zero length and zero area.
Attributes
----------
@@ -83,24 +24,20 @@ class Point(BaseGeometry):
Example
-------
- >>> p = Point(1.0, -1.0)
- >>> str(p)
- 'POINT (1.0000000000000000 -1.0000000000000000)'
- >>> p.y = 0.0
- >>> p.y
- 0.0
- >>> p.x
- 1.0
- >>> p.array
- [[1.0, 0.0]]
+
+ >>> p = Point(1.0, -1.0)
+ >>> print p
+ POINT (1.0000000000000000 -1.0000000000000000)
+ >>> p.y
+ -1.0
+ >>> p.x
+ 1.0
"""
def __init__(self, *args):
- """This *copies* the given data to a new GEOS geometry.
-
+ """
Parameters
----------
-
There are 2 cases:
1) 1 parameter: this must satisfy the numpy array protocol.
@@ -108,51 +45,29 @@ class Point(BaseGeometry):
Easting, northing, and elevation.
"""
BaseGeometry.__init__(self)
- self._init_geom(*args)
-
- def _init_geom(self, *args):
- if len(args) == 0:
- # allow creation of null points, to support unpickling
- pass
- else:
- if len(args) == 1:
- self._geom, self._ndim = geos_point_from_py(args[0])
- else:
- self._geom, self._ndim = geos_point_from_py(tuple(args))
+ if len(args) > 0:
+ self._set_coords(*args)
# Coordinate getters and setters
@property
- @exceptNull
def x(self):
"""Return x coordinate."""
- cs = lgeos.GEOSGeom_getCoordSeq(self._geom)
- d = c_double()
- lgeos.GEOSCoordSeq_getX(cs, 0, byref(d))
- return d.value
+ return self.coords[0][0]
@property
- @exceptNull
def y(self):
"""Return y coordinate."""
- cs = lgeos.GEOSGeom_getCoordSeq(self._geom)
- d = c_double()
- lgeos.GEOSCoordSeq_getY(cs, 0, byref(d))
- return d.value
+ return self.coords[0][1]
@property
- @exceptNull
def z(self):
"""Return z coordinate."""
if self._ndim != 3:
- raise DimensionError, "This point has no z coordinate."
- cs = lgeos.GEOSGeom_getCoordSeq(self._geom)
- d = c_double()
- lgeos.GEOSCoordSeq_getZ(cs, 0, byref(d))
- return d.value
+ raise DimensionError("This point has no z coordinate.")
+ return self.coords[0][2]
@property
- @exceptNull
def __geo_interface__(self):
return {
'type': 'Point',
@@ -160,15 +75,15 @@ class Point(BaseGeometry):
}
@property
- @exceptNull
def ctypes(self):
if not self._ctypes_data:
array_type = c_double * self._ndim
array = array_type()
- array[0] = self.x
- array[1] = self.y
+ xy = self.coords[0]
+ array[0] = xy[0]
+ array[1] = xy[1]
if self._ndim == 3:
- array[2] = self.z
+ array[2] = xy[2]
self._ctypes_data = array
return self._ctypes_data
@@ -180,30 +95,37 @@ class Point(BaseGeometry):
__array_interface__ = property(array_interface)
@property
- @exceptNull
def bounds(self):
- cs = lgeos.GEOSGeom_getCoordSeq(self._geom)
- x = c_double()
- y = c_double()
- lgeos.GEOSCoordSeq_getX(cs, 0, byref(x))
- lgeos.GEOSCoordSeq_getY(cs, 0, byref(y))
- return (x.value, y.value, x.value, y.value)
+ xy = self.coords[0]
+ return (xy[0], xy[1], xy[0], xy[1])
# Coordinate access
- def _set_coords(self, coordinates):
- if self._geom is None:
- self._init_geom(coordinates)
+ def _set_coords(self, *args):
+ self.empty()
+ if len(args) == 1:
+ self._geom, self._ndim = geos_point_from_py(args[0])
else:
- update_point_from_py(self, coordinates)
+ self._geom, self._ndim = geos_point_from_py(tuple(args))
coords = property(BaseGeometry._get_coords, _set_coords)
+ @property
+ def xy(self):
+ """Separate arrays of X and Y coordinate values
+
+ Example:
+
+ >>> x, y = Point(0, 0).xy
+ >>> list(x)
+ [0.0]
+ >>> list(y)
+ [0.0]
+ """
+ return self.coords.xy
-class PointAdapter(CachingGeometryProxy, Point):
- """Adapts a Python coordinate pair or a numpy array to the point interface.
- """
+class PointAdapter(CachingGeometryProxy, Point):
_owned = False
@@ -223,8 +145,6 @@ class PointAdapter(CachingGeometryProxy, Point):
# Fall back on list
return len(self.context)
- # TODO: reimplement x, y, z properties without calling invoking _geom
-
@property
def __array_interface__(self):
"""Provide the Numpy array protocol."""
@@ -236,17 +156,82 @@ class PointAdapter(CachingGeometryProxy, Point):
_get_coords = BaseGeometry._get_coords
def _set_coords(self, ob):
- raise NotImplementedError, \
- "Component rings have coordinate sequences, but the polygon does not"
+ raise NotImplementedError("Adapters can not modify their sources")
coords = property(_get_coords)
def asPoint(context):
- """Factory for PointAdapter instances."""
+ """Adapt an object to the Point interface"""
return PointAdapter(context)
+def geos_point_from_py(ob, update_geom=None, update_ndim=0):
+ """Create a GEOS geom from an object that is a coordinate sequence
+ or that provides the array interface.
+
+ Returns the GEOS geometry and the number of its dimensions.
+ """
+ try:
+ # From array protocol
+ array = ob.__array_interface__
+ assert len(array['shape']) == 1
+ n = array['shape'][0]
+ assert n == 2 or n == 3
+
+ dz = None
+ da = array['data']
+ if type(da) == type((0,)):
+ cdata = da[0]
+ cp = cast(cdata, POINTER(c_double))
+ dx = c_double(cp[0])
+ dy = c_double(cp[1])
+ if n == 3:
+ dz = c_double(cp[2])
+ ndim = 3
+ else:
+ dx, dy = da[0:2]
+ if n == 3:
+ dz = da[2]
+ ndim = 3
+
+ except AttributeError:
+ # Fall back on the case of Python sequence data
+ # Accept either (x, y) or [(x, y)]
+ if type(ob[0]) == type(tuple()):
+ coords = ob[0]
+ else:
+ coords = ob
+ n = len(coords)
+ dx = c_double(coords[0])
+ dy = c_double(coords[1])
+ dz = None
+ if n == 3:
+ dz = c_double(coords[2])
+
+ if update_geom:
+ cs = lgeos.GEOSGeom_getCoordSeq(update_geom)
+ if n != update_ndim:
+ raise ValueError(
+ "Wrong coordinate dimensions; this geometry has dimensions: %d" \
+ % update_ndim)
+ else:
+ cs = lgeos.GEOSCoordSeq_create(1, n)
+
+ # Because of a bug in the GEOS C API, always set X before Y
+ lgeos.GEOSCoordSeq_setX(cs, 0, dx)
+ lgeos.GEOSCoordSeq_setY(cs, 0, dy)
+ if n == 3:
+ lgeos.GEOSCoordSeq_setZ(cs, 0, dz)
+
+ if update_geom:
+ return None
+ else:
+ return lgeos.GEOSGeom_createPoint(cs), n
+
+def update_point_from_py(geom, ob):
+ geos_point_from_py(ob, geom._geom, geom._ndim)
+
# Test runner
def _test():
import doctest
diff --git a/shapely/geometry/polygon.py b/shapely/geometry/polygon.py
index 56eadb4..2a74021 100644
--- a/shapely/geometry/polygon.py
+++ b/shapely/geometry/polygon.py
@@ -1,181 +1,48 @@
-"""
-Polygons and their linear ring components.
+"""Polygons and their linear ring components
"""
-from ctypes import byref, c_double, c_int, c_void_p, cast, POINTER, pointer
+from ctypes import c_double, c_void_p, cast, POINTER
+from ctypes import ArgumentError
import weakref
from shapely.geos import lgeos
from shapely.geometry.base import BaseGeometry, exceptNull
from shapely.geometry.linestring import LineString, LineStringAdapter
from shapely.geometry.proxy import PolygonProxy
-
-def geos_linearring_from_py(ob, update_geom=None, update_ndim=0):
- try:
- # From array protocol
- array = ob.__array_interface__
- assert len(array['shape']) == 2
- m = array['shape'][0]
- n = array['shape'][1]
- if m < 3:
- raise ValueError, "A LinearRing must have at least 3 coordinate tuples"
- assert n == 2 or n == 3
-
- # Make pointer to the coordinate array
- try:
- cp = cast(array['data'][0], POINTER(c_double))
- except ArgumentError:
- cp = array['data']
-
- # Add closing coordinates to sequence?
- if cp[0] != cp[m*n-n] or cp[1] != cp[m*n-n+1]:
- M = m + 1
- else:
- M = m
-
- # Create a coordinate sequence
- if update_geom is not None:
- cs = lgeos.GEOSGeom_getCoordSeq(update_geom)
- if n != update_ndim:
- raise ValueError, \
- "Wrong coordinate dimensions; this geometry has dimensions: %d" \
- % update_ndim
- else:
- cs = lgeos.GEOSCoordSeq_create(M, n)
-
- # add to coordinate sequence
- for i in xrange(m):
- dx = c_double(cp[n*i])
- dy = c_double(cp[n*i+1])
- dz = None
- if n == 3:
- dz = c_double(cp[n*i+2])
-
- # Because of a bug in the GEOS C API,
- # always set X before Y
- lgeos.GEOSCoordSeq_setX(cs, i, dx)
- lgeos.GEOSCoordSeq_setY(cs, i, dy)
- if n == 3:
- lgeos.GEOSCoordSeq_setZ(cs, i, dz)
-
- # Add closing coordinates to sequence?
- if M > m:
- dx = c_double(cp[0])
- dy = c_double(cp[1])
- dz = None
- if n == 3:
- dz = c_double(cp[2])
-
- # Because of a bug in the GEOS C API,
- # always set X before Y
- lgeos.GEOSCoordSeq_setX(cs, M-1, dx)
- lgeos.GEOSCoordSeq_setY(cs, M-1, dy)
- if n == 3:
- lgeos.GEOSCoordSeq_setZ(cs, M-1, dz)
-
- except AttributeError:
- # Fall back on list
- m = len(ob)
- n = len(ob[0])
- if m < 3:
- raise ValueError, "A LinearRing must have at least 3 coordinate tuples"
- assert (n == 2 or n == 3)
-
- # Add closing coordinates if not provided
- if ob[0][0] != ob[-1][0] or ob[0][1] != ob[-1][1]:
- M = m + 1
- else:
- M = m
-
- # Create a coordinate sequence
- if update_geom is not None:
- cs = lgeos.GEOSGeom_getCoordSeq(update_geom)
- if n != update_ndim:
- raise ValueError, \
- "Wrong coordinate dimensions; this geometry has dimensions: %d" \
- % update_ndim
- else:
- cs = lgeos.GEOSCoordSeq_create(M, n)
-
- # add to coordinate sequence
- for i in xrange(m):
- coords = ob[i]
- dx = c_double(coords[0])
- dy = c_double(coords[1])
- dz = None
- if n == 3:
- dz = c_double(coords[2])
-
- # Because of a bug in the GEOS C API,
- # always set X before Y
- lgeos.GEOSCoordSeq_setX(cs, i, dx)
- lgeos.GEOSCoordSeq_setY(cs, i, dy)
- if n == 3:
- lgeos.GEOSCoordSeq_setZ(cs, i, dz)
-
- # Add closing coordinates to sequence?
- if M > m:
- coords = ob[0]
- dx = c_double(coords[0])
- dy = c_double(coords[1])
- dz = None
- if n == 3:
- dz = c_double(coords[2])
-
- # Because of a bug in the GEOS C API,
- # always set X before Y
- lgeos.GEOSCoordSeq_setX(cs, M-1, dx)
- lgeos.GEOSCoordSeq_setY(cs, M-1, dy)
- if n == 3:
- lgeos.GEOSCoordSeq_setZ(cs, M-1, dz)
-
- if update_geom is not None:
- return None
- else:
- return lgeos.GEOSGeom_createLinearRing(cs), n
-
-def update_linearring_from_py(geom, ob):
- geos_linearring_from_py(ob, geom._geom, geom._ndim)
+__all__ = ['Polygon', 'asPolygon', 'LinearRing', 'asLinearRing']
class LinearRing(LineString):
-
- """A linear ring.
"""
+ A closed one-dimensional feature comprising one or more line segments
- _ndim = None
- __geom__ = None
- __p__ = None
- _owned = False
-
+ A LinearRing that crosses itself or touches itself at a single point is
+ invalid and operations on it may fail.
+ """
+
def __init__(self, coordinates=None):
- """Initialize.
-
+ """
Parameters
----------
- coordinates : sequence or array
- This may be an object that satisfies the numpy array protocol,
- providing an M x 2 or M x 3 (with z) array, or it may be a sequence
- of x, y (,z) coordinate sequences.
+ coordinates : sequence
+ A sequence of (x, y [,z]) numeric coordinate pairs or triples
Rings are implicitly closed. There is no need to specific a final
coordinate pair identical to the first.
Example
-------
- >>> ring = LinearRing( ((0.,0.), (0.,1.), (1.,1.), (1.,0.)) )
+ Construct a square ring.
- Produces a 1x1 square.
+ >>> ring = LinearRing( ((0, 0), (0, 1), (1 ,1 ), (1 , 0)) )
+ >>> ring.is_closed
+ True
+ >>> ring.length
+ 4.0
"""
BaseGeometry.__init__(self)
- self._init_geom(coordinates)
-
- def _init_geom(self, coordinates):
- if coordinates is None:
- # allow creation of null lines, to support unpickling
- pass
- else:
- self._geom, self._ndim = geos_linearring_from_py(coordinates)
+ if coordinates is not None:
+ self._set_coords(coordinates)
@property
def __geo_interface__(self):
@@ -189,27 +56,19 @@ class LinearRing(LineString):
_get_coords = BaseGeometry._get_coords
def _set_coords(self, coordinates):
- if self._geom is None:
- self._init_geom(coordinates)
- update_linearring_from_py(self, coordinates)
+ self.empty()
+ self._geom, self._ndim = geos_linearring_from_py(coordinates)
coords = property(_get_coords, _set_coords)
class LinearRingAdapter(LineStringAdapter):
- context = None
- __geom__ = None
__p__ = None
- _owned = False
- @property
- def _geom(self):
- """Keeps the GEOS geometry in synch with the context."""
- if self.__geom__ is not None:
- lgeos.GEOSGeom_destroy(self.__geom)
- self.__geom, n = geos_linearring_from_py(self.context)
- return self.__geom
+ def __init__(self, context):
+ self.context = context
+ self.factory = geos_linearring_from_py
@property
def __geo_interface__(self):
@@ -222,6 +81,7 @@ class LinearRingAdapter(LineStringAdapter):
def asLinearRing(context):
+ """Adapt an object to the LinearRing interface"""
return LinearRingAdapter(context)
@@ -260,7 +120,7 @@ class InteriorRingSequence(object):
def __getitem__(self, i):
M = self.__len__()
if i + M < 0 or i >= M:
- raise IndexError, "index out of range"
+ raise IndexError("index out of range")
if i < 0:
ii = M + i
else:
@@ -293,62 +153,44 @@ class InteriorRingSequence(object):
return self.__rings__[i]()
-def geos_polygon_from_py(shell, holes=None):
- if shell is not None:
- geos_shell, ndim = geos_linearring_from_py(shell)
- if holes:
- ob = holes
- L = len(ob)
- N = len(ob[0][0])
- assert L >= 1
- assert N == 2 or N == 3
-
- # Array of pointers to ring geometries
- geos_holes = (c_void_p * L)()
-
- # add to coordinate sequence
- for l in xrange(L):
- geom, ndim = geos_linearring_from_py(ob[l])
- geos_holes[l] = cast(geom, c_void_p)
-
- else:
- geos_holes = POINTER(c_void_p)()
- L = 0
-
- return (
- lgeos.GEOSGeom_createPolygon(
- c_void_p(geos_shell),
- geos_holes,
- L
- ),
- ndim
- )
-
class Polygon(BaseGeometry):
-
- """A line string, also known as a polyline.
+ """
+ A two-dimensional figure bounded by a linear ring
+
+ A polygon has a non-zero area. It may have one or more negative-space
+ "holes" which are also bounded by linear rings. If any rings cross each
+ other, the feature is invalid and operations on it may fail.
+
+ Attributes
+ ----------
+ exterior : LinearRing
+ The ring which bounds the positive space of the polygon.
+ interiors : sequence
+ A sequence of rings which bound all existing holes.
"""
_exterior = None
_interiors = []
_ndim = 2
- __geom__ = None
- _owned = False
def __init__(self, shell=None, holes=None):
- """Initialize.
-
+ """
Parameters
----------
- exterior : sequence or array
- This may be an object that satisfies the numpy array protocol,
- providing an M x 2 or M x 3 (with z) array, or it may be a sequence
- of x, y (,z) coordinate sequences.
+ shell : sequence
+ A sequence of (x, y [,z]) numeric coordinate pairs or triples
+ holes : sequence
+ A sequence of objects which satisfy the same requirements as the
+ shell parameters above
Example
-------
- >>> coords = ((0., 0.), (0., 1.), (1., 1.), (1., 0.), (0., 0.))
- >>> polygon = Polygon(coords)
+ Create a square polygon with no holes
+
+ >>> coords = ((0., 0.), (0., 1.), (1., 1.), (1., 0.), (0., 0.))
+ >>> polygon = Polygon(coords)
+ >>> polygon.area
+ 1.0
"""
BaseGeometry.__init__(self)
@@ -381,21 +223,21 @@ class Polygon(BaseGeometry):
@property
def __array_interface__(self):
- raise NotImplementedError, \
- "A polygon does not itself provide the array interface. Its rings do."
+ raise NotImplementedError(
+ "A polygon does not itself provide the array interface. Its rings do.")
def _get_coords(self):
- raise NotImplementedError, \
- "Component rings have coordinate sequences, but the polygon does not"
+ raise NotImplementedError(
+ "Component rings have coordinate sequences, but the polygon does not")
def _set_coords(self, ob):
- raise NotImplementedError, \
- "Component rings have coordinate sequences, but the polygon does not"
+ raise NotImplementedError(
+ "Component rings have coordinate sequences, but the polygon does not")
@property
def coords(self):
- raise NotImplementedError, \
- "Component rings have coordinate sequences, but the polygon does not"
+ raise NotImplementedError(
+ "Component rings have coordinate sequences, but the polygon does not")
@property
def __geo_interface__(self):
@@ -409,15 +251,7 @@ class Polygon(BaseGeometry):
class PolygonAdapter(PolygonProxy, Polygon):
-
- """Adapts sequences of sequences or numpy arrays to the polygon
- interface.
- """
- context = None
- __geom__ = None
- _owned = False
-
def __init__(self, shell, holes=None):
self.shell = shell
self.holes = holes
@@ -438,10 +272,172 @@ class PolygonAdapter(PolygonProxy, Polygon):
def asPolygon(shell, holes=None):
- """Factory for PolygonAdapter instances."""
+ """Adapt objects to the Polygon interface"""
return PolygonAdapter(shell, holes)
+def geos_linearring_from_py(ob, update_geom=None, update_ndim=0):
+ try:
+ # From array protocol
+ array = ob.__array_interface__
+ assert len(array['shape']) == 2
+ m = array['shape'][0]
+ n = array['shape'][1]
+ if m < 3:
+ raise ValueError(
+ "A LinearRing must have at least 3 coordinate tuples")
+ assert n == 2 or n == 3
+
+ # Make pointer to the coordinate array
+ try:
+ cp = cast(array['data'][0], POINTER(c_double))
+ except ArgumentError:
+ cp = array['data']
+
+ # Add closing coordinates to sequence?
+ if cp[0] != cp[m*n-n] or cp[1] != cp[m*n-n+1]:
+ M = m + 1
+ else:
+ M = m
+
+ # Create a coordinate sequence
+ if update_geom is not None:
+ cs = lgeos.GEOSGeom_getCoordSeq(update_geom)
+ if n != update_ndim:
+ raise ValueError(
+ "Wrong coordinate dimensions; this geometry has dimensions: %d" \
+ % update_ndim)
+ else:
+ cs = lgeos.GEOSCoordSeq_create(M, n)
+
+ # add to coordinate sequence
+ for i in xrange(m):
+ dx = c_double(cp[n*i])
+ dy = c_double(cp[n*i+1])
+ dz = None
+ if n == 3:
+ dz = c_double(cp[n*i+2])
+
+ # Because of a bug in the GEOS C API,
+ # always set X before Y
+ lgeos.GEOSCoordSeq_setX(cs, i, dx)
+ lgeos.GEOSCoordSeq_setY(cs, i, dy)
+ if n == 3:
+ lgeos.GEOSCoordSeq_setZ(cs, i, dz)
+
+ # Add closing coordinates to sequence?
+ if M > m:
+ dx = c_double(cp[0])
+ dy = c_double(cp[1])
+ dz = None
+ if n == 3:
+ dz = c_double(cp[2])
+
+ # Because of a bug in the GEOS C API,
+ # always set X before Y
+ lgeos.GEOSCoordSeq_setX(cs, M-1, dx)
+ lgeos.GEOSCoordSeq_setY(cs, M-1, dy)
+ if n == 3:
+ lgeos.GEOSCoordSeq_setZ(cs, M-1, dz)
+
+ except AttributeError:
+ # Fall back on list
+ m = len(ob)
+ n = len(ob[0])
+ if m < 3:
+ raise ValueError(
+ "A LinearRing must have at least 3 coordinate tuples")
+ assert (n == 2 or n == 3)
+
+ # Add closing coordinates if not provided
+ if m == 3 or ob[0][0] != ob[-1][0] or ob[0][1] != ob[-1][1]:
+ M = m + 1
+ else:
+ M = m
+
+ # Create a coordinate sequence
+ if update_geom is not None:
+ cs = lgeos.GEOSGeom_getCoordSeq(update_geom)
+ if n != update_ndim:
+ raise ValueError(
+ "Wrong coordinate dimensions; this geometry has dimensions: %d" \
+ % update_ndim)
+ else:
+ cs = lgeos.GEOSCoordSeq_create(M, n)
+
+ # add to coordinate sequence
+ for i in xrange(m):
+ coords = ob[i]
+ dx = c_double(coords[0])
+ dy = c_double(coords[1])
+ dz = None
+ if n == 3:
+ dz = c_double(coords[2])
+
+ # Because of a bug in the GEOS C API,
+ # always set X before Y
+ lgeos.GEOSCoordSeq_setX(cs, i, dx)
+ lgeos.GEOSCoordSeq_setY(cs, i, dy)
+ if n == 3:
+ lgeos.GEOSCoordSeq_setZ(cs, i, dz)
+
+ # Add closing coordinates to sequence?
+ if M > m:
+ coords = ob[0]
+ dx = c_double(coords[0])
+ dy = c_double(coords[1])
+ dz = None
+ if n == 3:
+ dz = c_double(coords[2])
+
+ # Because of a bug in the GEOS C API,
+ # always set X before Y
+ lgeos.GEOSCoordSeq_setX(cs, M-1, dx)
+ lgeos.GEOSCoordSeq_setY(cs, M-1, dy)
+ if n == 3:
+ lgeos.GEOSCoordSeq_setZ(cs, M-1, dz)
+
+ if update_geom is not None:
+ return None
+ else:
+ return lgeos.GEOSGeom_createLinearRing(cs), n
+
+def update_linearring_from_py(geom, ob):
+ geos_linearring_from_py(ob, geom._geom, geom._ndim)
+
+def geos_polygon_from_py(shell, holes=None):
+ if shell is not None:
+ geos_shell, ndim = geos_linearring_from_py(shell)
+ if holes:
+ ob = holes
+ L = len(ob)
+ exemplar = ob[0]
+ try:
+ N = len(exemplar[0])
+ except TypeError:
+ N = exemplar._ndim
+ assert L >= 1
+ assert N == 2 or N == 3
+
+ # Array of pointers to ring geometries
+ geos_holes = (c_void_p * L)()
+
+ # add to coordinate sequence
+ for l in xrange(L):
+ geom, ndim = geos_linearring_from_py(ob[l])
+ geos_holes[l] = cast(geom, c_void_p)
+ else:
+ geos_holes = POINTER(c_void_p)()
+ L = 0
+ return (
+ lgeos.GEOSGeom_createPolygon(
+ c_void_p(geos_shell),
+ geos_holes,
+ L
+ ),
+ ndim
+ )
+
# Test runner
def _test():
import doctest
diff --git a/shapely/geometry/proxy.py b/shapely/geometry/proxy.py
index 27367d5..85ceb25 100644
--- a/shapely/geometry/proxy.py
+++ b/shapely/geometry/proxy.py
@@ -1,29 +1,38 @@
-"""
-Proxy for coordinates stored outside Shapely geometries.
+"""Proxy for coordinates stored outside Shapely geometries
"""
from shapely.geos import lgeos
+from shapely import wkb
+
+EMPTY = wkb.deserialize('010700000000000000'.decode('hex'))
class CachingGeometryProxy(object):
context = None
factory = None
- __geom__ = None
+ __geom__ = EMPTY
_gtag = None
def __init__(self, context):
self.context = context
@property
+ def _is_empty(self):
+ return self.__geom__ in [EMPTY, None]
+
+ def empty(self):
+ if not self._is_empty:
+ from shapely.geos import lgeos
+ lgeos.GEOSGeom_destroy(self.__geom__)
+ self.__geom__ = EMPTY
+
+ @property
def _geom(self):
"""Keeps the GEOS geometry in synch with the context."""
gtag = self.gtag()
- if gtag != self._gtag:
- if self.__geom__ is not None:
- lgeos.GEOSGeom_destroy(self.__geom__)
- self.__geom__, n = self.factory(self.context)
- elif self.__geom__ is None:
+ if gtag != self._gtag or self._is_empty:
+ self.empty()
self.__geom__, n = self.factory(self.context)
self._gtag = gtag
return self.__geom__
@@ -38,11 +47,8 @@ class PolygonProxy(CachingGeometryProxy):
def _geom(self):
"""Keeps the GEOS geometry in synch with the context."""
gtag = self.gtag()
- if gtag != self._gtag:
- if self.__geom__ is not None:
- lgeos.GEOSGeom_destroy(self.__geom__)
- self.__geom__, n = self.factory(self.context[0], self.context[1])
- elif self.__geom__ is None:
+ if gtag != self._gtag or self._is_empty:
+ self.empty()
self.__geom__, n = self.factory(self.context[0], self.context[1])
self._gtag = gtag
return self.__geom__
diff --git a/shapely/geos.py b/shapely/geos.py
index e3d518b..ef096ce 100644
--- a/shapely/geos.py
+++ b/shapely/geos.py
@@ -1,21 +1,32 @@
"""
-Exports the libgeos_c shared lib, GEOS-specific exceptions, and utilities.
+Proxies for the libgeos_c shared lib, GEOS-specific exceptions, and utilities
"""
import atexit
-from ctypes import cdll, CDLL, PyDLL, CFUNCTYPE, c_char_p, c_void_p
-from ctypes.util import find_library
+import functools
+import logging
import os
import sys
+import time
+import threading
+import ctypes
+from ctypes import cdll, CDLL, PyDLL, CFUNCTYPE, c_char_p, c_void_p, string_at
+from ctypes.util import find_library
from ctypes_declarations import prototype
+# Begin by creating a do-nothing handler and adding to this module's logger.
+class NullHandler(logging.Handler):
+ def emit(self, record):
+ pass
+LOG = logging.getLogger(__name__)
+LOG.addHandler(NullHandler())
# Find and load the GEOS and C libraries
# If this ever gets any longer, we'll break it into separate modules
if sys.platform == 'linux2':
- lgeos = PyDLL(find_library('geos_c'))
+ _lgeos = CDLL(find_library('geos_c'))
free = CDLL(find_library('c')).free
free.argtypes = [c_void_p]
free.restype = None
@@ -36,7 +47,7 @@ elif sys.platform == 'darwin':
break
if lib is None:
raise ImportError("Could not find geos_c library")
- lgeos = PyDLL(lib)
+ _lgeos = CDLL(lib)
free = CDLL(find_library('c')).free
free.argtypes = [c_void_p]
free.restype = None
@@ -46,7 +57,7 @@ elif sys.platform == 'win32':
local_dlls = os.path.abspath(os.__file__ + "../../../DLLs")
original_path = os.environ['PATH']
os.environ['PATH'] = "%s;%s" % (local_dlls, original_path)
- lgeos = PyDLL("geos.dll")
+ _lgeos = CDLL("geos.dll")
except (ImportError, WindowsError):
raise
def free(m):
@@ -60,9 +71,9 @@ elif sys.platform == 'sunos5':
# Try the major versioned name first, falling back on the unversioned
# name.
try:
- lgeos = PyDLL('libgeos_c.so.1')
+ _lgeos = CDLL('libgeos_c.so.1')
except (OSError, ImportError):
- lgeos = PyDLL('libgeos_c.so')
+ _lgeos = CDLL('libgeos_c.so')
except:
raise
free = CDLL('libc.so.1').free
@@ -73,22 +84,56 @@ else: # other *nix systems
# Try the major versioned name first, falling back on the unversioned
# name.
try:
- lgeos = PyDLL('libgeos_c.so.1')
+ _lgeos = CDLL('libgeos_c.so.1')
except (OSError, ImportError):
- lgeos = PyDLL('libgeos_c.so')
+ _lgeos = CDLL('libgeos_c.so')
except:
raise
free = CDLL('libc.so.6').free
free.argtypes = [c_void_p]
free.restype = None
-# Prototype the libgeos_c functions using new code from `tarley` in
-# http://trac.gispython.org/lab/ticket/189
-prototype(lgeos)
+def _geos_c_version():
+ func = _lgeos.GEOSversion
+ func.restype = c_char_p
+ v = func().split('-')[2]
+ return tuple(int(n) for n in v.split('.'))
+geos_capi_version = geos_c_version = _geos_c_version()
-class allocated_c_char_p(c_char_p):
- pass
+# If we have the new interface, then record a baseline so that we know what
+# additional functions are declared in ctypes_declarations.
+if geos_c_version >= (1,5,0):
+ start_set = set(_lgeos.__dict__)
+
+# Apply prototypes for the libgeos_c functions
+prototype(_lgeos, geos_c_version)
+
+# If we have the new interface, automatically detect all function
+# declarations, and declare their re-entrant counterpart.
+if geos_c_version >= (1,5,0):
+ end_set = set(_lgeos.__dict__)
+ new_func_names = end_set - start_set
+
+ for func_name in new_func_names:
+ new_func_name = "%s_r" % func_name
+ if hasattr(_lgeos, new_func_name):
+ new_func = getattr(_lgeos, new_func_name)
+ old_func = getattr(_lgeos, func_name)
+ new_func.restype = old_func.restype
+ if old_func.argtypes is None:
+ # Handle functions that didn't take an argument before,
+ # finishGEOS.
+ new_func.argtypes = [c_void_p]
+ else:
+ new_func.argtypes = [c_void_p] + old_func.argtypes
+ if old_func.errcheck is not None:
+ new_func.errcheck = old_func.errcheck
+
+ # Handle special case.
+ _lgeos.initGEOS_r.restype = c_void_p
+ _lgeos.initGEOS_r.argtypes = [c_void_p, c_void_p]
+ _lgeos.finishGEOS_r.argtypes = [c_void_p]
# Exceptions
@@ -104,23 +149,224 @@ class TopologicalError(Exception):
class PredicateError(Exception):
pass
-
-# GEOS error handlers, which currently do nothing.
-
def error_handler(fmt, list):
- pass
+ LOG.error("%s", list)
error_h = CFUNCTYPE(None, c_char_p, c_char_p)(error_handler)
def notice_handler(fmt, list):
- pass
+ LOG.warning("%s", list)
notice_h = CFUNCTYPE(None, c_char_p, c_char_p)(notice_handler)
-# Register a cleanup function
-
def cleanup():
- if lgeos is not None:
- lgeos.finishGEOS()
+ if _lgeos is not None :
+ _lgeos.finishGEOS()
atexit.register(cleanup)
-lgeos.initGEOS(notice_h, error_h)
+# Errcheck functions
+
+def errcheck_wkb(result, func, argtuple):
+ if not result:
+ return None
+ size_ref = argtuple[-1]
+ size = size_ref._obj
+ retval = ctypes.string_at(result, size.value)[:]
+ lgeos.GEOSFree(result)
+ return retval
+
+def errcheck_just_free(result, func, argtuple):
+ retval = string_at(result)
+ lgeos.GEOSFree(result)
+ return retval
+
+def errcheck_predicate(result, func, argtuple):
+ if result == 2:
+ raise PredicateError("Failed to evaluate %s" % repr(func))
+ return result
+
+
+class LGEOSBase(threading.local):
+ """Proxy for the GEOS_C DLL/SO
+
+ This is a base class. Do not instantiate.
+ """
+ methods = {}
+ def __init__(self, dll):
+ self._lgeos = dll
+ self.geos_handle = None
+
+
+class LGEOS14(LGEOSBase):
+ """Proxy for the GEOS_C DLL/SO API version 1.4
+ """
+ geos_capi_version = (1, 4, 0)
+ def __init__(self, dll):
+ super(LGEOS14, self).__init__(dll)
+ self.geos_handle = self._lgeos.initGEOS(notice_h, error_h)
+ keys = self._lgeos.__dict__.keys()
+ for key in keys:
+ setattr(self, key, getattr(self._lgeos, key))
+ self.GEOSFree = self._lgeos.free
+ self.GEOSGeomToWKB_buf.errcheck = errcheck_wkb
+ self.GEOSGeomToWKT.errcheck = errcheck_just_free
+ self.GEOSRelate.errcheck = errcheck_just_free
+ for pred in ( self.GEOSDisjoint,
+ self.GEOSTouches,
+ self.GEOSIntersects,
+ self.GEOSCrosses,
+ self.GEOSWithin,
+ self.GEOSContains,
+ self.GEOSOverlaps,
+ self.GEOSEquals,
+ self.GEOSEqualsExact,
+ self.GEOSisEmpty,
+ self.GEOSisValid,
+ self.GEOSisSimple,
+ self.GEOSisRing,
+ self.GEOSHasZ
+ ):
+ pred.errcheck = errcheck_predicate
+
+ self.methods['area'] = self.GEOSArea
+ self.methods['boundary'] = self.GEOSBoundary
+ self.methods['buffer'] = self.GEOSBuffer
+ self.methods['centroid'] = self.GEOSGetCentroid
+ self.methods['convex_hull'] = self.GEOSConvexHull
+ self.methods['distance'] = self.GEOSDistance
+ self.methods['envelope'] = self.GEOSEnvelope
+ self.methods['length'] = self.GEOSLength
+ self.methods['has_z'] = self.GEOSHasZ
+ self.methods['is_empty'] = self.GEOSisEmpty
+ self.methods['is_ring'] = self.GEOSisRing
+ self.methods['is_simple'] = self.GEOSisSimple
+ self.methods['is_valid'] = self.GEOSisValid
+ self.methods['disjoint'] = self.GEOSDisjoint
+ self.methods['touches'] = self.GEOSTouches
+ self.methods['intersects'] = self.GEOSIntersects
+ self.methods['crosses'] = self.GEOSCrosses
+ self.methods['within'] = self.GEOSWithin
+ self.methods['contains'] = self.GEOSContains
+ self.methods['overlaps'] = self.GEOSOverlaps
+ self.methods['equals'] = self.GEOSEquals
+ self.methods['equals_exact'] = self.GEOSEqualsExact
+ self.methods['relate'] = self.GEOSRelate
+ self.methods['difference'] = self.GEOSDifference
+ self.methods['symmetric_difference'] = self.GEOSSymDifference
+ self.methods['union'] = self.GEOSUnion
+ self.methods['intersection'] = self.GEOSIntersection
+ self.methods['simplify'] = self.GEOSSimplify
+ self.methods['topology_preserve_simplify'] = \
+ self.GEOSTopologyPreserveSimplify
+
+
+class LGEOS15(LGEOSBase):
+ """Proxy for the reentrant GEOS_C DLL/SO API version 1.5
+ """
+ geos_capi_version = (1, 5, 0)
+ def __init__(self, dll):
+ super(LGEOS15, self).__init__(dll)
+ self.geos_handle = self._lgeos.initGEOS_r(notice_h, error_h)
+ keys = self._lgeos.__dict__.keys()
+ for key in filter(lambda x: not x.endswith('_r'), keys):
+ if key + '_r' in keys:
+ reentr_func = getattr(self._lgeos, key + '_r')
+ attr = functools.partial(reentr_func, self.geos_handle)
+ attr.__name__ = reentr_func.__name__
+ setattr(self, key, attr)
+ else:
+ setattr(self, key, getattr(self._lgeos, key))
+ if not hasattr(self, 'GEOSFree'):
+ self.GEOSFree = self._lgeos.free
+ self.GEOSGeomToWKB_buf.func.errcheck = errcheck_wkb
+ self.GEOSGeomToWKT.func.errcheck = errcheck_just_free
+ self.GEOSRelate.func.errcheck = errcheck_just_free
+ for pred in ( self.GEOSDisjoint,
+ self.GEOSTouches,
+ self.GEOSIntersects,
+ self.GEOSCrosses,
+ self.GEOSWithin,
+ self.GEOSContains,
+ self.GEOSOverlaps,
+ self.GEOSEquals,
+ self.GEOSEqualsExact,
+ self.GEOSisEmpty,
+ self.GEOSisValid,
+ self.GEOSisSimple,
+ self.GEOSisRing,
+ self.GEOSHasZ
+ ):
+ pred.func.errcheck = errcheck_predicate
+
+ self.methods['area'] = self.GEOSArea
+ self.methods['boundary'] = self.GEOSBoundary
+ self.methods['buffer'] = self.GEOSBuffer
+ self.methods['centroid'] = self.GEOSGetCentroid
+ self.methods['convex_hull'] = self.GEOSConvexHull
+ self.methods['distance'] = self.GEOSDistance
+ self.methods['envelope'] = self.GEOSEnvelope
+ self.methods['length'] = self.GEOSLength
+ self.methods['has_z'] = self.GEOSHasZ
+ self.methods['is_empty'] = self.GEOSisEmpty
+ self.methods['is_ring'] = self.GEOSisRing
+ self.methods['is_simple'] = self.GEOSisSimple
+ self.methods['is_valid'] = self.GEOSisValid
+ self.methods['disjoint'] = self.GEOSDisjoint
+ self.methods['touches'] = self.GEOSTouches
+ self.methods['intersects'] = self.GEOSIntersects
+ self.methods['crosses'] = self.GEOSCrosses
+ self.methods['within'] = self.GEOSWithin
+ self.methods['contains'] = self.GEOSContains
+ self.methods['overlaps'] = self.GEOSOverlaps
+ self.methods['equals'] = self.GEOSEquals
+ self.methods['equals_exact'] = self.GEOSEqualsExact
+ self.methods['relate'] = self.GEOSRelate
+ self.methods['difference'] = self.GEOSDifference
+ self.methods['symmetric_difference'] = self.GEOSSymDifference
+ self.methods['union'] = self.GEOSUnion
+ self.methods['intersection'] = self.GEOSIntersection
+ self.methods['prepared_intersects'] = self.GEOSPreparedIntersects
+ self.methods['prepared_contains'] = self.GEOSPreparedContains
+ self.methods['prepared_contains_properly'] = \
+ self.GEOSPreparedContainsProperly
+ self.methods['prepared_covers'] = self.GEOSPreparedCovers
+ self.methods['simplify'] = self.GEOSSimplify
+ self.methods['topology_preserve_simplify'] = \
+ self.GEOSTopologyPreserveSimplify
+
+class LGEOS16(LGEOS15):
+ """Proxy for the reentrant GEOS_C DLL/SO API version 1.6
+ """
+ geos_capi_version = (1, 6, 0)
+ def __init__(self, dll):
+ super(LGEOS16, self).__init__(dll)
+
+
+class LGEOS16LR(LGEOS16):
+ """Proxy for the reentrant GEOS_C DLL/SO API version 1.6 with linear
+ referencing
+ """
+ geos_capi_version = geos_c_version
+ def __init__(self, dll):
+ super(LGEOS16LR, self).__init__(dll)
+
+ self.GEOSisValidReason.func.errcheck = errcheck_just_free
+
+ self.methods['project'] = self.GEOSProject
+ self.methods['project_normalized'] = self.GEOSProjectNormalized
+ self.methods['interpolate'] = self.GEOSInterpolate
+ self.methods['interpolate_normalized'] = \
+ self.GEOSInterpolateNormalized
+
+
+if geos_c_version >= (1, 6, 0):
+ if hasattr(_lgeos, 'GEOSProject'):
+ L = LGEOS16LR
+ else:
+ L = LGEOS16
+elif geos_c_version >= (1, 5, 0):
+ L = LGEOS15
+else:
+ L = LGEOS14
+
+lgeos = L(_lgeos)
+
diff --git a/shapely/impl.py b/shapely/impl.py
new file mode 100644
index 0000000..063925b
--- /dev/null
+++ b/shapely/impl.py
@@ -0,0 +1,81 @@
+"""Implementation of the intermediary layer between Shapely and GEOS
+"""
+
+from shapely.coords import BoundsOp
+from shapely.geos import lgeos
+from shapely.linref import ProjectOp, InterpolateOp
+from shapely.predicates import BinaryPredicate, UnaryPredicate
+from shapely.topology import BinaryRealProperty, BinaryTopologicalOp
+from shapely.topology import UnaryRealProperty, UnaryTopologicalOp
+
+
+# Map geometry methods to their GEOS delegates
+
+IMPL14 = {
+ 'area': (UnaryRealProperty, 'area'),
+ 'distance': (BinaryRealProperty, 'distance'),
+ 'length': (UnaryRealProperty, 'length'),
+ #
+ 'boundary': (UnaryTopologicalOp, 'boundary'),
+ 'bounds': (BoundsOp, None),
+ 'centroid': (UnaryTopologicalOp, 'centroid'),
+ 'envelope': (UnaryTopologicalOp, 'envelope'),
+ 'convex_hull': (UnaryTopologicalOp, 'convex_hull'),
+ 'buffer': (UnaryTopologicalOp, 'buffer'),
+ #
+ 'difference': (BinaryTopologicalOp, 'difference'),
+ 'intersection': (BinaryTopologicalOp, 'intersection'),
+ 'symmetric_difference': (BinaryTopologicalOp, 'symmetric_difference'),
+ 'union': (BinaryTopologicalOp, 'union'),
+ #
+ 'has_z': (UnaryPredicate, 'has_z'),
+ 'is_empty': (UnaryPredicate, 'is_empty'),
+ 'is_ring': (UnaryPredicate, 'is_ring'),
+ 'is_simple': (UnaryPredicate, 'is_simple'),
+ 'is_valid': (UnaryPredicate, 'is_valid'),
+ #
+ 'relate': (BinaryPredicate, 'relate'),
+ 'contains': (BinaryPredicate, 'contains'),
+ 'crosses': (BinaryPredicate, 'crosses'),
+ 'disjoint': (BinaryPredicate, 'disjoint'),
+ 'equals': (BinaryPredicate, 'equals'),
+ 'intersects': (BinaryPredicate, 'intersects'),
+ 'overlaps': (BinaryPredicate, 'overlaps'),
+ 'touches': (BinaryPredicate, 'touches'),
+ 'within': (BinaryPredicate, 'within'),
+ 'equals_exact': (BinaryPredicate, 'equals_exact'),
+ }
+
+IMPL15 = {
+ 'simplify': (UnaryTopologicalOp, 'simplify'),
+ 'topology_preserve_simplify':
+ (UnaryTopologicalOp, 'topology_preserve_simplify'),
+ 'prepared_intersects': (BinaryPredicate, 'prepared_intersects'),
+ 'prepared_contains': (BinaryPredicate, 'prepared_contains'),
+ 'prepared_contains_properly':
+ (BinaryPredicate, 'prepared_contains_properly'),
+ 'prepared_covers': (BinaryPredicate, 'prepared_covers'),
+ }
+
+IMPL16 = {
+ }
+
+IMPL16LR = {
+ 'project_normalized': (ProjectOp, 'project_normalized'),
+ 'project': (ProjectOp, 'project'),
+ 'interpolate_normalized': (InterpolateOp, 'interpolate_normalized'),
+ 'interpolate': (InterpolateOp, 'interpolate'),
+ }
+
+def build(defs):
+ return [(k, v[0](v[1])) for k, v in defs.items()]
+
+imp = dict(build(IMPL14))
+if lgeos.geos_capi_version >= (1, 5, 0):
+ imp.update(build(IMPL15))
+if lgeos.geos_capi_version >= (1, 6, 0):
+ imp.update(build(IMPL16))
+ if 'project' in lgeos.methods:
+ imp.update(build(IMPL16LR))
+
+DefaultImplementation = imp
diff --git a/shapely/iterops.py b/shapely/iterops.py
index e556632..70b6f5d 100644
--- a/shapely/iterops.py
+++ b/shapely/iterops.py
@@ -1,5 +1,5 @@
"""
-Iterative forms of operations.
+Iterative forms of operations
"""
from ctypes import c_char_p, c_size_t
@@ -24,7 +24,7 @@ class IterOp(object):
def __call__(self, context, iterator, value=True):
if context._geom is None:
- raise ValueError, "Null geometry supports no operations"
+ raise ValueError("Null geometry supports no operations")
for item in iterator:
try:
this_geom, ob = item
@@ -32,11 +32,11 @@ class IterOp(object):
this_geom = item
ob = this_geom
if not this_geom._geom:
- raise ValueError, "Null geometry supports no operations"
+ raise ValueError("Null geometry supports no operations")
retval = self.fn(context._geom, this_geom._geom)
if retval == 2:
- raise PredicateError, \
- "Failed to evaluate %s" % repr(self.fn)
+ raise PredicateError(
+ "Failed to evaluate %s" % repr(self.fn))
elif bool(retval) == value:
yield ob
@@ -50,3 +50,4 @@ within = IterOp(lgeos.GEOSWithin)
contains = IterOp(lgeos.GEOSContains)
overlaps = IterOp(lgeos.GEOSOverlaps)
equals = IterOp(lgeos.GEOSEquals)
+
diff --git a/shapely/linref.py b/shapely/linref.py
new file mode 100644
index 0000000..fc3d0f4
--- /dev/null
+++ b/shapely/linref.py
@@ -0,0 +1,26 @@
+"""Linear referencing
+"""
+
+from shapely.topology import Delegating
+
+
+class LinearRefBase(Delegating):
+ def _validate_line(self, ob):
+ super(LinearRefBase, self)._validate(ob)
+ try:
+ assert ob.geom_type in ['LineString', 'MultiLineString']
+ except AssertionError:
+ raise TypeError("Only linear types support this operation")
+
+class ProjectOp(LinearRefBase):
+ def __call__(self, this, other):
+ self._validate_line(this)
+ self._validate(other)
+ return self.fn(this._geom, other._geom)
+
+class InterpolateOp(LinearRefBase):
+ def __call__(self, this, distance):
+ self._validate_line(this)
+ return self.fn(this._geom, distance)
+
+
diff --git a/shapely/ops.py b/shapely/ops.py
index 3a2966b..71b77e8 100644
--- a/shapely/ops.py
+++ b/shapely/ops.py
@@ -1,29 +1,87 @@
+"""Support for various GEOS geometry operations
+"""
+
+from ctypes import byref, c_void_p
+
from shapely.geos import lgeos
from shapely.geometry.base import geom_factory, BaseGeometry
-from shapely.geometry import asShape, asLineString
-from ctypes import byref, c_void_p
+from shapely.geometry import asShape, asLineString, asMultiLineString
+
+__all__= ['operator', 'polygonize', 'linemerge', 'cascaded_union']
+
+
+class CollectionOperator(object):
+
+ def shapeup(self, ob):
+ if isinstance(ob, BaseGeometry):
+ return ob
+ else:
+ try:
+ return asShape(ob)
+ except ValueError:
+ return asLineString(ob)
+
+ def polygonize(self, lines):
+ """Creates polygons from a source of lines
+
+ The source may be a MultiLineString, a sequence of LineString objects,
+ or a sequence of objects than can be adapted to LineStrings.
+ """
+ source = getattr(lines, 'geoms', None) or lines
+ obs = [self.shapeup(l) for l in source]
+ geom_array_type = c_void_p * len(obs)
+ geom_array = geom_array_type()
+ for i, line in enumerate(obs):
+ geom_array[i] = line._geom
+ product = lgeos.GEOSPolygonize(byref(geom_array), len(obs))
+ collection = geom_factory(product)
+ for g in collection.geoms:
+ clone = lgeos.GEOSGeom_clone(g._geom)
+ g = geom_factory(clone)
+ g._owned = False
+ yield g
+
+ def linemerge(self, lines):
+ """Merges all connected lines from a source
+
+ The source may be a MultiLineString, a sequence of LineString objects,
+ or a sequence of objects than can be adapted to LineStrings. Returns a
+ LineString or MultiLineString when lines are not contiguous.
+ """
+ source = None
+ if hasattr(lines, 'type') and lines.type == 'MultiLineString':
+ source = lines
+ elif hasattr(lines, '__iter__'):
+ try:
+ source = asMultiLineString([ls.coords for ls in lines])
+ except AttributeError:
+ source = asMultiLineString(lines)
+ if source is None:
+ raise ValueError("Cannot linemerge %s" % lines)
+ result = lgeos.GEOSLineMerge(source._geom)
+ return geom_factory(result)
+
+ def cascaded_union(self, geoms):
+ """Returns the union of a sequence of geometries
+
+ This is the most efficient method of dissolving many polygons.
+ """
+ L = len(geoms)
+ subs = (c_void_p * L)()
+ for i, g in enumerate(geoms):
+ subs[i] = g._geom
+ collection = lgeos.GEOSGeom_createCollection(6, subs, L)
+ return geom_factory(lgeos.GEOSUnionCascaded(collection))
+
+
+operator = CollectionOperator()
+polygonize = operator.polygonize
+linemerge = operator.linemerge
+cascaded_union = operator.cascaded_union
+
+class ValidateOp(object):
+ def __call__(self, this):
+ return lgeos.GEOSisValidReason(this._geom)
+
+validate = ValidateOp()
-def shapeup(ob):
- if isinstance(ob, BaseGeometry):
- return ob
- else:
- try:
- return asShape(ob)
- except ValueError:
- return asLineString(ob)
-
-def polygonize(iterator):
- """Creates polygons from a list of LineString objects.
- """
- lines = [shapeup(ob) for ob in iterator]
- geom_array_type = c_void_p * len(lines)
- geom_array = geom_array_type()
- for i, line in enumerate(lines):
- geom_array[i] = line._geom
- product = lgeos.GEOSPolygonize(byref(geom_array), len(lines))
- collection = geom_factory(product)
- for g in collection.geoms:
- clone = lgeos.GEOSGeom_clone(g._geom)
- g = geom_factory(clone)
- g._owned = False
- yield g
diff --git a/shapely/predicates.py b/shapely/predicates.py
index ed12158..bc28ba7 100644
--- a/shapely/predicates.py
+++ b/shapely/predicates.py
@@ -1,65 +1,24 @@
"""
-Support for GEOS spatial predicates.
+Support for GEOS spatial predicates
"""
-from shapely.geos import PredicateError
-
-
-class OpWrapper(object):
-
- def __init__(self, fn, context):
- self.fn = fn
- self.context = context
-
- def __call__(self, other):
- if not other._geom:
- raise ValueError, "Null geometry can not be operated upon"
- return bool(self.fn(self.context._geom, other._geom))
-
-
-class BinaryPredicate(object):
-
- """A callable non-data descriptor.
- """
-
- fn = None
- context = None
-
- def __init__(self, fn):
- self.fn = fn
- def errcheck(result, func, argtuple):
- if result == 2:
- raise PredicateError, "Failed to evaluate %s" % repr(self.fn)
- return result
- self.fn.errcheck = errcheck
-
- def __get__(self, obj, objtype=None):
- if not obj._geom:
- raise ValueError, "Null geometry supports no operations"
- return OpWrapper(self.fn, obj)
-
-
-# A data descriptor
-class UnaryPredicate(object):
-
- """A data descriptor.
- """
-
- fn = None
-
- def __init__(self, fn):
- self.fn = fn
- def errcheck(result, func, argtuple):
- if result == 2:
- raise PredicateError, "Failed to evaluate %s" % repr(self.fn)
- return result
- self.fn.errcheck = errcheck
-
- def __get__(self, obj, objtype=None):
- if not obj._geom:
- raise ValueError, "Null geometry supports no operations"
- return bool(self.fn(obj._geom))
-
- def __set__(self, obj, value=None):
- raise AttributeError, "Attribute is read-only"
+from shapely.geos import PredicateError, lgeos
+from shapely.topology import Delegating
+
+class BinaryPredicate(Delegating):
+ def __call__(self, this, other, *args):
+ self._validate(this)
+ self._validate(other)
+ return self.fn(this._geom, other._geom, *args)
+
+class RelateOp(Delegating):
+ def __call__(self, this, other):
+ self._validate(this)
+ self._validate(other)
+ return self.fn(this._geom, other._geom)
+
+class UnaryPredicate(Delegating):
+ def __call__(self, this):
+ self._validate(this)
+ return self.fn(this._geom)
diff --git a/shapely/prepared.py b/shapely/prepared.py
new file mode 100644
index 0000000..0f41406
--- /dev/null
+++ b/shapely/prepared.py
@@ -0,0 +1,54 @@
+"""
+Support for GEOS prepared geometry operations.
+"""
+
+from shapely.geos import lgeos
+from shapely.impl import DefaultImplementation
+
+
+class PreparedGeometry(object):
+ """
+ A geometry prepared for efficient comparison to a set of other geometries.
+
+ Example:
+
+ >>> from shapely.geometry import Point, Polygon
+ >>> triangle = Polygon(((0.0, 0.0), (1.0, 1.0), (1.0, -1.0)))
+ >>> p = prep(triangle)
+ >>> p.intersects(Point(0.5, 0.5))
+ True
+ """
+
+ impl = DefaultImplementation
+
+ def __init__(self, context):
+ self.context = context
+ self.__geom__ = lgeos.GEOSPrepare(self.context._geom)
+
+ def __del__(self):
+ if self.__geom__ is not None:
+ lgeos.GEOSPreparedGeom_destroy(self.__geom__)
+ self.__geom__ = None
+ self.context = None
+
+ @property
+ def _geom(self):
+ return self.__geom__
+
+ def intersects(self, other):
+ return bool(self.impl['prepared_intersects'](self, other))
+
+ def contains(self, other):
+ return bool(self.impl['prepared_contains'](self, other))
+
+ def contains_properly(self, other):
+ return bool(self.impl['prepared_contains_properly'](self, other))
+
+ def covers(self, other):
+ return bool(self.impl['prepared_covers'](self, other))
+
+
+def prep(ob):
+ """Creates and returns a prepared geometric object."""
+ return PreparedGeometry(ob)
+
diff --git a/tests/Array.txt b/shapely/tests/Array.txt
similarity index 100%
rename from tests/Array.txt
rename to shapely/tests/Array.txt
diff --git a/tests/GeoInterface.txt b/shapely/tests/GeoInterface.txt
similarity index 100%
rename from tests/GeoInterface.txt
rename to shapely/tests/GeoInterface.txt
diff --git a/tests/IterOps.txt b/shapely/tests/IterOps.txt
similarity index 100%
rename from tests/IterOps.txt
rename to shapely/tests/IterOps.txt
diff --git a/tests/LineString.txt b/shapely/tests/LineString.txt
similarity index 91%
rename from tests/LineString.txt
rename to shapely/tests/LineString.txt
index 7e3cd7f..b6aeaf2 100644
--- a/tests/LineString.txt
+++ b/shapely/tests/LineString.txt
@@ -23,6 +23,14 @@ From coordinate tuples
>>> line.wkt
'LINESTRING (1.0000000000000000 2.0000000000000000, 3.0000000000000000 4.0000000000000000)'
+From lines
+
+ >>> copy = LineString(line)
+ >>> len(copy.coords)
+ 2
+ >>> print copy.wkt
+ LINESTRING (1.0000000000000000 2.0000000000000000, 3.0000000000000000 4.0000000000000000)
+
Numpy Support
-------------
@@ -113,14 +121,10 @@ Test Non-operability of Null geometry
>>> l_null = LineString()
>>> l_null.wkt # doctest: +ELLIPSIS
- Traceback (most recent call last):
- ...
- ValueError: Null geometry supports no operations
+ 'GEOMETRYCOLLECTION EMPTY'
>>> l_null.length # doctest: +ELLIPSIS
- Traceback (most recent call last):
- ...
- ValueError: Null geometry supports no operations
+ 0.0
Check that we can set coordinates of a null geometry
diff --git a/tests/MultiLineString.txt b/shapely/tests/MultiLineString.txt
similarity index 76%
rename from tests/MultiLineString.txt
rename to shapely/tests/MultiLineString.txt
index 3c41eee..71c1951 100644
--- a/tests/MultiLineString.txt
+++ b/shapely/tests/MultiLineString.txt
@@ -4,7 +4,7 @@ MultiLineStrings
Initialization
--------------
- >>> from shapely.geometry import MultiLineString
+ >>> from shapely.geometry import LineString, MultiLineString
From coordinate tuples
@@ -27,6 +27,23 @@ Construct from a numpy array
>>> geom.wkt
'MULTILINESTRING ((0.0000000000000000 0.0000000000000000, 1.0000000000000000 2.0000000000000000))'
+From lines
+
+ >>> a = LineString(((1.0, 2.0), (3.0, 4.0)))
+ >>> ml = MultiLineString([a])
+ >>> len(ml.geoms)
+ 1
+ >>> print ml.wkt
+ MULTILINESTRING ((1.0000000000000000 2.0000000000000000, 3.0000000000000000 4.0000000000000000))
+
+From another multi-line
+
+ >>> ml2 = MultiLineString(ml)
+ >>> len(ml2.geoms)
+ 1
+ >>> print ml2.wkt
+ MULTILINESTRING ((1.0000000000000000 2.0000000000000000, 3.0000000000000000 4.0000000000000000))
+
Sub-geometry Access
-------------------
diff --git a/tests/MultiPoint.txt b/shapely/tests/MultiPoint.txt
similarity index 80%
rename from tests/MultiPoint.txt
rename to shapely/tests/MultiPoint.txt
index bd0c5d5..4e5df0b 100644
--- a/tests/MultiPoint.txt
+++ b/shapely/tests/MultiPoint.txt
@@ -4,7 +4,7 @@ MultiPoints
Initialization
--------------
- >>> from shapely.geometry import MultiPoint
+ >>> from shapely.geometry import Point, MultiPoint
Construct from a numpy array
@@ -25,6 +25,22 @@ From coordinate tuples
>>> geom.wkt
'MULTIPOINT (1.0000000000000000 2.0000000000000000, 3.0000000000000000 4.0000000000000000)'
+From points
+
+ >>> geom = MultiPoint((Point(1.0, 2.0), Point(3.0, 4.0)))
+ >>> len(geom.geoms)
+ 2
+ >>> print geom.wkt
+ MULTIPOINT (1.0000000000000000 2.0000000000000000, 3.0000000000000000 4.0000000000000000)
+
+From another multi-point
+
+ >>> geom2 = MultiPoint(geom)
+ >>> len(geom2.geoms)
+ 2
+ >>> print geom2.wkt
+ MULTIPOINT (1.0000000000000000 2.0000000000000000, 3.0000000000000000 4.0000000000000000)
+
Sub-geometry Access
-------------------
diff --git a/tests/MultiPolygon.txt b/shapely/tests/MultiPolygon.txt
similarity index 65%
rename from tests/MultiPolygon.txt
rename to shapely/tests/MultiPolygon.txt
index 6edecaf..8b8723a 100644
--- a/tests/MultiPolygon.txt
+++ b/shapely/tests/MultiPolygon.txt
@@ -4,7 +4,7 @@ MultiPolygons
Initialization
--------------
- >>> from shapely.geometry import MultiPolygon
+ >>> from shapely.geometry import Polygon, MultiPolygon
From coordinate tuples
@@ -18,6 +18,22 @@ From coordinate tuples
>>> geom.wkt
'MULTIPOLYGON (((0.0000000000000000 0.0000000000000000, 0.0000000000000000 1.0000000000000000, 1.0000000000000000 1.0000000000000000, 1.0000000000000000 0.0000000000000000, 0.0000000000000000 0.0000000000000000), (0.1000000000000000 0.1000000000000000, 0.1000000000000000 0.2000000000000000, 0.2000000000000000 0.2000000000000000, 0.2000000000000000 0.1000000000000000, 0.1000000000000000 0.1000000000000000)))'
+Or from polygons
+
+ >>> p = Polygon(((0, 0), (0, 1), (1, 1), (1, 0)), [((0.1,0.1), (0.1,0.2), (0.2,0.2), (0.2,0.1))])
+ >>> geom = MultiPolygon([p])
+ >>> len(geom.geoms)
+ 1
+ >>> print geom.wkt
+ MULTIPOLYGON (((0.0000000000000000 0.0000000000000000, 0.0000000000000000 1.0000000000000000, 1.0000000000000000 1.0000000000000000, 1.0000000000000000 0.0000000000000000, 0.0000000000000000 0.0000000000000000), (0.1000000000000000 0.1000000000000000, 0.1000000000000000 0.2000000000000000, 0.2000000000000000 0.2000000000000000, 0.2000000000000000 0.1000000000000000, 0.1000000000000000 0.1000000000000000)))
+
+Or from another multi-polygon
+
+ >>> geom2 = MultiPolygon(geom)
+ >>> len(geom2.geoms)
+ 1
+ >>> print geom2.wkt
+ MULTIPOLYGON (((0.0000000000000000 0.0000000000000000, 0.0000000000000000 1.0000000000000000, 1.0000000000000000 1.0000000000000000, 1.0000000000000000 0.0000000000000000, 0.0000000000000000 0.0000000000000000), (0.1000000000000000 0.1000000000000000, 0.1000000000000000 0.2000000000000000, 0.2000000000000000 0.2000000000000000, 0.2000000000000000 0.1000000000000000, 0.1000000000000000 0.1000000000000000)))
Sub-geometry Access
-------------------
diff --git a/tests/Operations.txt b/shapely/tests/Operations.txt
similarity index 67%
rename from tests/Operations.txt
rename to shapely/tests/Operations.txt
index 1bfd325..8ff4c2b 100644
--- a/tests/Operations.txt
+++ b/shapely/tests/Operations.txt
@@ -37,6 +37,22 @@ Topology operations
>>> point.buffer(10.0, 32) #doctest: +ELLIPSIS
<shapely.geometry.polygon.Polygon object at ...>
+ Simplify
+
+ >>> from shapely.wkt import loads
+ >>> p = loads('POLYGON ((120 120, 121 121, 122 122, 220 120, 180 199, 160 200, 140 199, 120 120))')
+ >>> expected = loads('POLYGON ((120 120, 140 199, 160 200, 180 199, 220 120, 120 120))')
+ >>> s = p.simplify(10.0, preserve_topology = False)
+ >>> s.equals_exact(expected, 0.001)
+ True
+
+ >>> from shapely.wkt import loads
+ >>> p = loads('POLYGON ((80 200, 240 200, 240 60, 80 60, 80 200),(120 120, 220 120, 180 199, 160 200, 140 199, 120 120))')
+ >>> expected = loads('POLYGON ((80 200, 240 200, 240 60, 80 60, 80 200),(120 120, 220 120, 180 199, 160 200, 140 199, 120 120))')
+ >>> s = p.simplify(10.0, preserve_topology = True)
+ >>> s.equals_exact(expected, 0.001)
+ True
+
Convex Hull
>>> point.convex_hull #doctest: +ELLIPSIS
diff --git a/tests/Persist.txt b/shapely/tests/Persist.txt
similarity index 100%
rename from tests/Persist.txt
rename to shapely/tests/Persist.txt
diff --git a/tests/Point.txt b/shapely/tests/Point.txt
similarity index 90%
rename from tests/Point.txt
rename to shapely/tests/Point.txt
index 0a4596b..2325afe 100644
--- a/tests/Point.txt
+++ b/shapely/tests/Point.txt
@@ -1,8 +1,9 @@
+Testing Point
+=============
>>> from shapely.geometry import Point
Test 2D points
---------------
>>> p = Point(0.0, 0.0)
>>> str(p)
@@ -11,7 +12,6 @@ Test 2D points
'POINT (0.0000000000000000 0.0000000000000000)'
Check 3D
---------
>>> p = Point(0.0, 0.0, 1.0)
>>> p.z
@@ -22,7 +22,6 @@ Check 3D
'POINT (0.0000000000000000 0.0000000000000000)'
Construct from a numpy array
-----------------------------
>>> from numpy import array
>>> p = Point(array([1.0, 2.0]))
@@ -30,12 +29,17 @@ Construct from a numpy array
'POINT (1.0000000000000000 2.0000000000000000)'
From coordinate sequence
-------------------------
>>> p = Point((3.0, 4.0))
>>> p.wkt
'POINT (3.0000000000000000 4.0000000000000000)'
+From another point
+
+ >>> q = Point(p)
+ >>> print p.wkt
+ POINT (3.0000000000000000 4.0000000000000000)
+
Coordinate access
-----------------
@@ -131,14 +135,10 @@ Test Non-operability of Null geometry
>>> p_null = Point()
>>> p_null.wkt # doctest: +ELLIPSIS
- Traceback (most recent call last):
- ...
- ValueError: Null geometry supports no operations
+ 'GEOMETRYCOLLECTION EMPTY'
>>> p_null.area # doctest: +ELLIPSIS
- Traceback (most recent call last):
- ...
- ValueError: Null geometry supports no operations
+ 0.0
Check that we can set coordinates of a null geometry
diff --git a/tests/Polygon.txt b/shapely/tests/Polygon.txt
similarity index 94%
rename from tests/Polygon.txt
rename to shapely/tests/Polygon.txt
index 1b93d69..4a9e5dd 100644
--- a/tests/Polygon.txt
+++ b/shapely/tests/Polygon.txt
@@ -123,14 +123,10 @@ Test Non-operability of Null rings
>>> r_null = LinearRing()
>>> r_null.wkt # doctest: +ELLIPSIS
- Traceback (most recent call last):
- ...
- ValueError: Null geometry supports no operations
+ 'GEOMETRYCOLLECTION EMPTY'
- >>> r_null.length # doctest: +ELLIPSIS
- Traceback (most recent call last):
- ...
- ValueError: Null geometry supports no operations
+ >>> r_null.length
+ 0.0
Check that we can set coordinates of a null geometry
diff --git a/tests/Predicates.txt b/shapely/tests/Predicates.txt
similarity index 100%
rename from tests/Predicates.txt
rename to shapely/tests/Predicates.txt
diff --git a/shapely/tests/__init__.py b/shapely/tests/__init__.py
new file mode 100644
index 0000000..5f68983
--- /dev/null
+++ b/shapely/tests/__init__.py
@@ -0,0 +1,18 @@
+from unittest import TestSuite
+
+import test_doctests, test_prepared, test_equality, test_geomseq, test_xy
+import test_collection, test_emptiness, test_singularity, test_validation
+
+def test_suite():
+ suite = TestSuite()
+ suite.addTest(test_doctests.test_suite())
+ suite.addTest(test_prepared.test_suite())
+ suite.addTest(test_emptiness.test_suite())
+ suite.addTest(test_equality.test_suite())
+ suite.addTest(test_geomseq.test_suite())
+ suite.addTest(test_xy.test_suite())
+ suite.addTest(test_collection.test_suite())
+ suite.addTest(test_singularity.test_suite())
+ suite.addTest(test_validation.test_suite())
+ return suite
+
diff --git a/tests/attribute-chains.txt b/shapely/tests/attribute-chains.txt
similarity index 65%
rename from tests/attribute-chains.txt
rename to shapely/tests/attribute-chains.txt
index a853587..9b7933d 100644
--- a/tests/attribute-chains.txt
+++ b/shapely/tests/attribute-chains.txt
@@ -9,8 +9,8 @@ See also ticket #151.
[(0.0, 0.0), (0.0, 1.0), (-1.0, 1.0), (-1.0, 0.0), (0.0, 0.0)]
>>> from shapely.geometry import Point
- >>> print list(Point(0.0, 0.0).buffer(1.0).exterior.coords)
- [(0.0, -1.0), (-1.0, -1.0), ...
+ >>> print list(Point(0.0, 0.0).buffer(1.0, 1).exterior.coords)
+ [(1.0, 0.0), ..., (1.0, 0.0)]
Test chained access to interiors
@@ -20,10 +20,18 @@ Test chained access to interiors
... )
>>> p.area
0.75
+
+Not so much testing the exact values here, which are the responsibility of the
+geometry engine (GEOS), but that we can get chain functions and properties using
+anonymous references.
+
>>> print list(p.interiors[0].coords)
[(-0.25, 0.25), (-0.25, 0.75), (-0.75, 0.75), (-0.75, 0.25), (-0.25, 0.25)]
- >>> print list(p.interiors[0].buffer(1).exterior.coords)
- [(-0.25, -0.75), (-0.75, -0.75), ...
+ >>> xy = list(p.interiors[0].buffer(1).exterior.coords)[0]
+ >>> xy[0] + 0.25 <= 1.0e-7
+ True
+ >>> xy[1] + 0.75 <= 1.0e-7
+ True
Test multiple operators, boundary of a buffer
diff --git a/tests/binascii_hex.txt b/shapely/tests/binascii_hex.txt
similarity index 100%
rename from tests/binascii_hex.txt
rename to shapely/tests/binascii_hex.txt
diff --git a/shapely/tests/cascaded_union.txt b/shapely/tests/cascaded_union.txt
new file mode 100644
index 0000000..63adaed
--- /dev/null
+++ b/shapely/tests/cascaded_union.txt
@@ -0,0 +1,24 @@
+Cascaded Union
+==============
+
+ >>> from functools import partial
+ >>> import random
+ >>> from shapely.geometry import Point
+ >>> from shapely.ops import cascaded_union
+
+Use a partial function to make 100 points uniformly distributed in a 40x40
+box centered on 0,0.
+
+ >>> r = partial(random.uniform, -20.0, 20.0)
+ >>> points = [Point(r(), r()) for i in range(100)]
+
+Buffer the points, producing 100 polygon spots
+
+ >>> spots = [p.buffer(2.5) for p in points]
+
+Perform a cascaded union of the polygon spots, dissolving them into a
+collection of polygon patches
+
+ >>> cascaded_union(spots) # doctest: +ELLIPSIS
+ <shapely.geometry.multipolygon.MultiPolygon object at 0x...>
+
diff --git a/tests/dimensions.txt b/shapely/tests/dimensions.txt
similarity index 100%
rename from tests/dimensions.txt
rename to shapely/tests/dimensions.txt
diff --git a/tests/invalid_intersection.txt b/shapely/tests/invalid_intersection.txt
similarity index 63%
rename from tests/invalid_intersection.txt
rename to shapely/tests/invalid_intersection.txt
index 331377d..9c65d08 100644
--- a/tests/invalid_intersection.txt
+++ b/shapely/tests/invalid_intersection.txt
@@ -5,7 +5,7 @@ Test recovery from operation on invalid geometries
>>> from shapely.geometry import Polygon
>>> polygon_invalid = Polygon(((0, 0), (1, 1), (1, -1), (0, 1), (0, 0)))
- >>> polygon_invalid.is_valid
+ >>> print polygon_invalid.is_valid # doctest: +ELLIPSIS
False
Intersect with a valid polygon
@@ -18,12 +18,12 @@ Test recovery from operation on invalid geometries
>>> result = polygon_invalid.intersection(polygon) # doctest: +ELLIPSIS
Traceback (most recent call last):
...
- TopologicalError: The operation 'GEOSIntersection' produced a null geometry. Likely cause is invalidity of the geometry <shapely.geometry.polygon.Polygon object at ...>
+ TopologicalError: The operation 'GEOSIntersection_r' produced a null geometry. Likely cause is invalidity of the geometry <shapely.geometry.polygon.Polygon object at ...>
Check exception symmetry
>>> result = polygon.intersection(polygon_invalid) # doctest: +ELLIPSIS
Traceback (most recent call last):
...
- TopologicalError: The operation 'GEOSIntersection' produced a null geometry. Likely cause is invalidity of the 'other' geometry <shapely.geometry.polygon.Polygon object at ...>
+ TopologicalError: The operation 'GEOSIntersection_r' produced a null geometry. Likely cause is invalidity of the 'other' geometry <shapely.geometry.polygon.Polygon object at ...>
diff --git a/shapely/tests/linear-referencing.txt b/shapely/tests/linear-referencing.txt
new file mode 100644
index 0000000..3943915
--- /dev/null
+++ b/shapely/tests/linear-referencing.txt
@@ -0,0 +1,58 @@
+First, tests of projection
+
+ >>> from shapely.geometry import Point, LineString, MultiLineString
+
+ >>> point = Point(1, 1)
+ >>> line1 = LineString(([0, 0], [2, 0]))
+ >>> line1.project(point)
+ 1.0
+ >>> line1.project(point, normalized=True)
+ 0.5
+
+ >>> line2 = LineString(([3, 0], [3, 6]))
+ >>> line2.project(point)
+ 1.0
+ >>> line2.project(point, normalized=True)
+ 0.16666666666666666
+
+ >>> multiline = MultiLineString([list(line1.coords), list(line2.coords)])
+ >>> multiline.project(point)
+ 1.0
+ >>> multiline.project(point, normalized=True)
+ 0.125
+
+ >>> point.buffer(1.0).project(point) # doctest: +ELLIPSIS
+ Traceback (most recent call last):
+ ...
+ TypeError: Only linear types support this operation
+
+Points that aren't on the line project to 0.
+
+ >>> line1.project(Point(-10,-10))
+ 0.0
+
+Now tests of interpolation
+
+ >>> line1.interpolate(0.5).wkt
+ 'POINT (0.5000000000000000 0.0000000000000000)'
+ >>> line1.interpolate(0.5, normalized=True).wkt
+ 'POINT (1.0000000000000000 0.0000000000000000)'
+
+ >>> line2.interpolate(0.5).wkt
+ 'POINT (3.0000000000000000 0.5000000000000000)'
+ >>> line2.interpolate(0.5, normalized=True).wkt
+ 'POINT (3.0000000000000000 3.0000000000000000)'
+
+ >>> multiline.interpolate(0.5).wkt
+ 'POINT (0.5000000000000000 0.0000000000000000)'
+ >>> multiline.interpolate(0.5, normalized=True).wkt
+ 'POINT (3.0000000000000000 2.0000000000000000)'
+
+Distances greater than length of the line or less than zero yield the line's
+ends.
+
+ >>> line1.interpolate(-1000.0).wkt
+ 'POINT (0.0000000000000000 0.0000000000000000)'
+ >>> line1.interpolate(1000.0).wkt
+ 'POINT (2.0000000000000000 0.0000000000000000)'
+
diff --git a/shapely/tests/linemerge.txt b/shapely/tests/linemerge.txt
new file mode 100644
index 0000000..50c94af
--- /dev/null
+++ b/shapely/tests/linemerge.txt
@@ -0,0 +1,61 @@
+Linemerge
+=========
+
+ >>> from shapely.geometry import LineString, MultiLineString, Polygon
+ >>> from shapely.ops import linemerge
+
+ >>> lines = MultiLineString([
+ ... ((0, 0), (1, 1)),
+ ... ((2, 0), (2, 1), (1, 1))
+ ... ])
+ >>> result = linemerge(lines)
+ >>> result # docstring: +ELLIPSIS
+ <shapely.geometry.linestring.LineString object at 0x...>
+ >>> result.is_ring
+ False
+ >>> len(result.coords)
+ 4
+ >>> result.coords[0]
+ (0.0, 0.0)
+ >>> result.coords[3]
+ (2.0, 0.0)
+
+ >>> lines2 = MultiLineString([
+ ... ((0, 0), (1, 1)),
+ ... ((0, 0), (2, 0), (2, 1), (1, 1))
+ ... ])
+ >>> result = linemerge(lines2)
+ >>> result.is_ring
+ True
+ >>> len(result.coords)
+ 5
+
+ >>> lines3 = [
+ ... LineString(((0, 0), (1, 1))),
+ ... LineString(((0, 0), (0, 1))),
+ ... ]
+ >>> result = linemerge(lines3)
+ >>> result.is_ring
+ False
+ >>> len(result.coords)
+ 3
+ >>> result.coords[0]
+ (0.0, 1.0)
+ >>> result.coords[2]
+ (1.0, 1.0)
+
+ >>> lines4 = [
+ ... ((0, 0), (1, 1)),
+ ... ((0, 0), (0, 1)),
+ ... ]
+ >>> result.equals(linemerge(lines4))
+ True
+
+ >>> lines5 = [
+ ... ((0, 0), (1, 1)),
+ ... ((1, 0), (0, 1)),
+ ... ]
+ >>> result = linemerge(lines5)
+ >>> result.type == 'MultiLineString'
+ True
+
diff --git a/tests/polygonize.txt b/shapely/tests/polygonize.txt
similarity index 100%
rename from tests/polygonize.txt
rename to shapely/tests/polygonize.txt
diff --git a/shapely/tests/test_collection.py b/shapely/tests/test_collection.py
new file mode 100644
index 0000000..65b504f
--- /dev/null
+++ b/shapely/tests/test_collection.py
@@ -0,0 +1,10 @@
+import unittest
+from shapely.geometry.collection import GeometryCollection
+
+class CollectionTestCase(unittest.TestCase):
+ def test_array_interface(self):
+ m = GeometryCollection()
+ self.failUnlessEqual(len(m), 0)
+
+def test_suite():
+ return unittest.TestLoader().loadTestsFromTestCase(CollectionTestCase)
diff --git a/shapely/tests/test_doctests.py b/shapely/tests/test_doctests.py
new file mode 100644
index 0000000..25d5f7a
--- /dev/null
+++ b/shapely/tests/test_doctests.py
@@ -0,0 +1,35 @@
+import doctest
+import unittest
+import glob
+import os
+
+optionflags = (doctest.REPORT_ONLY_FIRST_FAILURE |
+ doctest.NORMALIZE_WHITESPACE |
+ doctest.ELLIPSIS)
+
+def list_doctests():
+ print __file__
+ return [filename
+ for filename
+ in glob.glob(os.path.join(os.path.dirname(__file__), '*.txt'))]
+
+def open_file(filename, mode='r'):
+ """Helper function to open files from within the tests package."""
+ return open(os.path.join(os.path.dirname(__file__), filename), mode)
+
+def setUp(test):
+ test.globs.update(dict(
+ open_file = open_file,
+ ))
+
+def test_suite():
+ return unittest.TestSuite(
+ [doctest.DocFileSuite(os.path.basename(filename),
+ optionflags=optionflags,
+ setUp=setUp)
+ for filename
+ in list_doctests()])
+
+if __name__ == "__main__":
+ runner = unittest.TextTestRunner(verbosity=1)
+ runner.run(test_suite())
diff --git a/shapely/tests/test_emptiness.py b/shapely/tests/test_emptiness.py
new file mode 100644
index 0000000..137f292
--- /dev/null
+++ b/shapely/tests/test_emptiness.py
@@ -0,0 +1,19 @@
+import unittest
+from shapely.geometry.base import BaseGeometry
+from shapely.geometry import Point
+
+class EmptinessTestCase(unittest.TestCase):
+ def test_empty_base(self):
+ g = BaseGeometry()
+ self.failUnless(g._is_empty, True)
+ def test_empty_point(self):
+ p = Point()
+ self.failUnless(p._is_empty, True)
+ def test_emptying_point(self):
+ p = Point(0, 0)
+ self.failIf(p._is_empty, False)
+ p.empty()
+ self.failUnless(p._is_empty, True)
+
+def test_suite():
+ return unittest.TestLoader().loadTestsFromTestCase(EmptinessTestCase)
diff --git a/shapely/tests/test_equality.py b/shapely/tests/test_equality.py
new file mode 100644
index 0000000..5ade663
--- /dev/null
+++ b/shapely/tests/test_equality.py
@@ -0,0 +1,30 @@
+import unittest
+from shapely import geometry
+
+
+class PointEqualityTestCase(unittest.TestCase):
+
+ def test_equals_exact(self):
+ p1 = geometry.Point(1.0, 1.0)
+ p2 = geometry.Point(2.0, 2.0)
+ self.failIf(p1.equals(p2))
+ self.failIf(p1.equals_exact(p2, 0.001))
+
+ def test_almost_equals_default(self):
+ p1 = geometry.Point(1.0, 1.0)
+ p2 = geometry.Point(1.0+1e-7, 1.0+1e-7) # almost equal to 6 places
+ p3 = geometry.Point(1.0+1e-6, 1.0+1e-6) # not almost equal
+ self.failUnless(p1.almost_equals(p2))
+ self.failIf(p1.almost_equals(p3))
+
+ def test_almost_equals(self):
+ p1 = geometry.Point(1.0, 1.0)
+ p2 = geometry.Point(1.1, 1.1)
+ self.failIf(p1.equals(p2))
+ self.failUnless(p1.almost_equals(p2, 0))
+ self.failIf(p1.almost_equals(p2, 1))
+
+def test_suite():
+ return unittest.TestLoader().loadTestsFromTestCase(
+ PointEqualityTestCase
+ )
diff --git a/shapely/tests/test_geomseq.py b/shapely/tests/test_geomseq.py
new file mode 100644
index 0000000..f384e62
--- /dev/null
+++ b/shapely/tests/test_geomseq.py
@@ -0,0 +1,11 @@
+import unittest
+from shapely import geometry
+
+class MultiLineTestCase(unittest.TestCase):
+ def test_array_interface(self):
+ m = geometry.MultiLineString([((0, 0), (1, 1)), ((2, 2), (3, 3))])
+ ai = m.geoms[0].__array_interface__
+ self.failUnlessEqual(ai['shape'], (2, 2))
+
+def test_suite():
+ return unittest.TestLoader().loadTestsFromTestCase(MultiLineTestCase)
diff --git a/shapely/tests/test_prepared.py b/shapely/tests/test_prepared.py
new file mode 100644
index 0000000..d8bbca4
--- /dev/null
+++ b/shapely/tests/test_prepared.py
@@ -0,0 +1,15 @@
+import unittest
+from shapely import prepared
+from shapely import geometry
+
+
+class PreparedGeometryTestCase(unittest.TestCase):
+
+ def test_prepared(self):
+ p = prepared.PreparedGeometry(geometry.Point(0.0, 0.0))
+
+
+def test_suite():
+ return unittest.TestLoader().loadTestsFromTestCase(
+ PreparedGeometryTestCase
+ )
diff --git a/shapely/tests/test_singularity.py b/shapely/tests/test_singularity.py
new file mode 100644
index 0000000..a2d2493
--- /dev/null
+++ b/shapely/tests/test_singularity.py
@@ -0,0 +1,15 @@
+import unittest
+from shapely.geometry import Polygon
+
+class PolygonTestCase(unittest.TestCase):
+ def test_polygon_3(self):
+ p = (1.0, 1.0)
+ poly = Polygon([p, p, p])
+ self.failUnlessEqual(poly.bounds, (1.0, 1.0, 1.0, 1.0))
+ def test_polygon_5(self):
+ p = (1.0, 1.0)
+ poly = Polygon([p, p, p, p, p])
+ self.failUnlessEqual(poly.bounds, (1.0, 1.0, 1.0, 1.0))
+
+def test_suite():
+ return unittest.TestLoader().loadTestsFromTestCase(PolygonTestCase)
diff --git a/shapely/tests/test_validation.py b/shapely/tests/test_validation.py
new file mode 100644
index 0000000..0a008de
--- /dev/null
+++ b/shapely/tests/test_validation.py
@@ -0,0 +1,10 @@
+import unittest
+from shapely.geometry import *
+from shapely.validation import explain_validity
+
+class ValidationTestCase(unittest.TestCase):
+ def test_valid(self):
+ self.failUnlessEqual(explain_validity(Point(0, 0)), 'Valid Geometry')
+
+def test_suite():
+ return unittest.TestLoader().loadTestsFromTestCase(ValidationTestCase)
diff --git a/shapely/tests/test_xy.py b/shapely/tests/test_xy.py
new file mode 100644
index 0000000..0bed93f
--- /dev/null
+++ b/shapely/tests/test_xy.py
@@ -0,0 +1,14 @@
+import unittest
+from shapely import geometry
+
+class XYTestCase(unittest.TestCase):
+ """New geometry/coordseq method 'xy' makes numpy interop easier"""
+ def test_arrays(self):
+ x, y = geometry.LineString(((0, 0), (1, 1))).xy
+ self.failUnless(len(x) == 2)
+ self.failUnless(list(x) == [0.0, 1.0])
+ self.failUnless(len(y) == 2)
+ self.failUnless(list(y) == [0.0, 1.0])
+
+def test_suite():
+ return unittest.TestLoader().loadTestsFromTestCase(XYTestCase)
diff --git a/shapely/tests/threading_test.py b/shapely/tests/threading_test.py
new file mode 100644
index 0000000..fd3b4bf
--- /dev/null
+++ b/shapely/tests/threading_test.py
@@ -0,0 +1,37 @@
+import threading
+
+def main():
+ num_threads = 10
+ use_threads = True
+
+ if not use_threads:
+ # Run core code
+ runShapelyBuilding()
+ else:
+ threads = [threading.Thread(target=runShapelyBuilding, name=str(i), args=(i,)) for i in range(num_threads)]
+ for t in threads:
+ t.start()
+ for t in threads:
+ t.join()
+
+def runShapelyBuilding(num):
+ print "%s: Running shapely tests on wkb" % num
+ import shapely.geos
+ print "%s GEOS Handle: %s" % (num, shapely.geos.lgeos.geos_handle)
+ import shapely.wkt
+ import shapely.wkb
+ p = shapely.wkt.loads("POINT (0 0)")
+ print "%s WKT: %s" % (num, shapely.wkt.dumps(p))
+ wkb = shapely.wkb.dumps(p)
+ print "%s WKB: %s" % (num, wkb.encode('hex'))
+
+ for i in xrange(10):
+ obj = shapely.wkb.loads(wkb)
+
+ print "%s GEOS Handle: %s" % (num, shapely.geos.lgeos.geos_handle)
+ print "Done %s" % num
+
+# Run main
+if __name__ == '__main__':
+ main()
+
diff --git a/tests/wkt_locale.txt b/shapely/tests/wkt_locale.txt
similarity index 100%
rename from tests/wkt_locale.txt
rename to shapely/tests/wkt_locale.txt
diff --git a/shapely/topology.py b/shapely/topology.py
index 97e94c1..5def1fa 100644
--- a/shapely/topology.py
+++ b/shapely/topology.py
@@ -1,74 +1,62 @@
"""
-Support for GEOS topological operations.
+Intermediaries supporting GEOS topological operations
+
+These methods all take Shapely geometries and other Python objects and delegate
+to GEOS functions via ctypes.
+
+These methods return ctypes objects that should be recast by the caller.
"""
-from shapely.geos import TopologicalError
+from ctypes import byref, c_double
+from shapely.geos import TopologicalError, lgeos
-class OpWrapper(object):
-
- def __init__(self, fn, context, factory):
- self.fn = fn
- self.context = context
- self.factory = factory
-
- def __call__(self, other):
- context = self.context
- if other._geom is None:
- raise ValueError, "Null geometry supports no operations"
- product = self.fn(context._geom, other._geom)
- if not product:
- # Check validity of geometries
- if not context.is_valid:
- raise TopologicalError, \
- "The operation '%s' produced a null geometry. Likely cause is invalidity of the geometry %s" % (self.fn.__name__, repr(context))
- elif not other.is_valid:
- raise TopologicalError, \
- "The operation '%s' produced a null geometry. Likely cause is invalidity of the 'other' geometry %s" % (self.fn.__name__, repr(other))
- return self.factory(product)
-
-
-class BinaryTopologicalOp(object):
-
- """A non-data descriptor that returns a callable.
-
- Wraps a GEOS function. The factory is a callable which wraps results in
- the appropriate shapely geometry class.
- """
-
- fn = None
- factory = None
-
- def __init__(self, fn, factory):
- self.fn = fn
- self.factory = factory
-
- def __get__(self, obj, objtype=None):
- if not obj._geom:
- raise ValueError, "Null geometry supports no operations"
- return OpWrapper(self.fn, obj, self.factory)
+class Validating(object):
+ def _validate(self, ob):
+ try:
+ assert ob is not None
+ assert ob._geom is not None
+ except AssertionError:
+ raise ValueError("Null geometry supports no operations")
+
+class Delegating(Validating):
+ def __init__(self, name):
+ self.fn = lgeos.methods[name]
+class BinaryRealProperty(Delegating):
+ def __call__(self, this, other):
+ self._validate(this)
+ self._validate(other)
+ d = c_double()
+ retval = self.fn(this._geom, other._geom, byref(d))
+ return d.value
+
+class UnaryRealProperty(Delegating):
+ def __call__(self, this):
+ self._validate(this)
+ d = c_double()
+ retval = self.fn(this._geom, byref(d))
+ return d.value
+
+class BinaryTopologicalOp(Delegating):
+ def __call__(self, this, other, *args):
+ self._validate(this)
+ self._validate(other)
+ product = self.fn(this._geom, other._geom, *args)
+ if product is None:
+ if not this.is_valid:
+ raise TopologicalError(
+ "The operation '%s' produced a null geometry. Likely cause is invalidity of the geometry %s" % (self.fn.__name__, repr(this)))
+ elif not other.is_valid:
+ raise TopologicalError(
+ "The operation '%s' produced a null geometry. Likely cause is invalidity of the 'other' geometry %s" % (self.fn.__name__, repr(other)))
+ else:
+ raise TopologicalError(
+ "This operation produced a null geometry. Reason: unknown")
+ return product
-class UnaryTopologicalOp(object):
-
- """A data descriptor.
-
- Wraps a GEOS function. The factory is a callable which wraps results in
- the appropriate shapely geometry class.
- """
-
- fn = None
- factory = None
-
- def __init__(self, fn, factory):
- self.fn = fn
- self.factory = factory
-
- def __get__(self, obj, objtype=None):
- if obj._geom is None:
- raise ValueError, "Null geometry supports no operations"
- return self.factory(self.fn(obj._geom), obj)
-
- def __set__(self, obj, value=None):
- raise AttributeError, "Attribute is read-only"
+class UnaryTopologicalOp(Delegating):
+ def __call__(self, this, *args):
+ self._validate(this)
+ return self.fn(this._geom, *args)
diff --git a/shapely/validation.py b/shapely/validation.py
new file mode 100644
index 0000000..69dde86
--- /dev/null
+++ b/shapely/validation.py
@@ -0,0 +1,7 @@
+#
+
+from shapely.geos import lgeos
+
+def explain_validity(ob):
+ return lgeos.GEOSisValidReason(ob._geom)
+
diff --git a/shapely/wkb.py b/shapely/wkb.py
index b6315c9..6e3869b 100644
--- a/shapely/wkb.py
+++ b/shapely/wkb.py
@@ -1,23 +1,25 @@
-"""
-Load/dump geometries using the well-known binary (WKB) format.
+"""Load/dump geometries using the well-known binary (WKB) format
"""
-from ctypes import byref, c_int, c_size_t, c_char_p, string_at
+from ctypes import byref, c_size_t, c_char_p, string_at
from ctypes import c_void_p, c_size_t
-from shapely.geos import lgeos, free, ReadingError
-from shapely.geometry.base import geom_factory
+from shapely.geos import lgeos, ReadingError
# Pickle-like convenience functions
-def loads(data):
- """Load a geometry from a WKB string."""
+def deserialize(data):
geom = lgeos.GEOSGeomFromWKB_buf(c_char_p(data), c_size_t(len(data)));
if not geom:
- raise ReadingError, \
- "Could not create geometry because of errors while reading input."
- return geom_factory(geom)
+ raise ReadingError(
+ "Could not create geometry because of errors while reading input.")
+ return geom
+
+def loads(data):
+ """Load a geometry from a WKB string."""
+ from shapely.geometry.base import geom_factory
+ return geom_factory(deserialize(data))
def load(fp):
"""Load a geometry from an open file."""
@@ -26,15 +28,10 @@ def load(fp):
def dumps(ob):
"""Dump a WKB representation of a geometry to a byte string."""
- func = lgeos.GEOSGeomToWKB_buf
+ if ob is None or ob._geom is None:
+ raise ValueError("Null geometry supports no operations")
size = c_size_t()
- def errcheck(result, func, argtuple):
- if not result: return None
- retval = string_at(result, size.value)[:]
- free(result)
- return retval
- func.errcheck = errcheck
- return func(c_void_p(ob._geom), byref(size))
+ return lgeos.GEOSGeomToWKB_buf(c_void_p(ob._geom), byref(size))
def dump(ob, fp):
"""Dump a geometry to an open file."""
diff --git a/shapely/wkt.py b/shapely/wkt.py
index 5c016a7..c5650ed 100644
--- a/shapely/wkt.py
+++ b/shapely/wkt.py
@@ -1,17 +1,16 @@
-"""
-Load/dump geometries using the well-known text (WKT) format.
+"""Load/dump geometries using the well-known text (WKT) format
"""
-from ctypes import byref, c_int, c_size_t, c_char_p, string_at
+from ctypes import c_char_p
-from shapely.geos import lgeos, free, allocated_c_char_p, ReadingError
-from shapely.geometry.base import geom_factory
+from shapely.geos import lgeos, ReadingError
# Pickle-like convenience functions
def loads(data):
"""Load a geometry from a WKT string."""
+ from shapely.geometry.base import geom_factory
geom = lgeos.GEOSGeomFromWKT(c_char_p(data))
if not geom:
raise ReadingError, \
@@ -25,14 +24,9 @@ def load(fp):
def dumps(ob):
"""Dump a WKB representation of a geometry to a byte string."""
- func = lgeos.GEOSGeomToWKT
- def errcheck(result, func, argtuple):
- retval = result.value
- free(result)
- return retval
- func.restype = allocated_c_char_p
- func.errcheck = errcheck
- return func(ob._geom)
+ if ob is None or ob._geom is None:
+ raise ValueError("Null geometry supports no operations")
+ return lgeos.GEOSGeomToWKT(ob._geom)
def dump(ob, fp):
"""Dump a geometry to an open file."""
diff --git a/tests/._Array.txt b/tests/._Array.txt
deleted file mode 100644
index 3c85056..0000000
Binary files a/tests/._Array.txt and /dev/null differ
diff --git a/tests/._GeoInterface.txt b/tests/._GeoInterface.txt
deleted file mode 100644
index 96f164d..0000000
Binary files a/tests/._GeoInterface.txt and /dev/null differ
diff --git a/tests/._Persist.txt b/tests/._Persist.txt
deleted file mode 100644
index 3ba76d6..0000000
Binary files a/tests/._Persist.txt and /dev/null differ
diff --git a/tests/._Point.txt b/tests/._Point.txt
deleted file mode 100644
index 2939fa5..0000000
Binary files a/tests/._Point.txt and /dev/null differ
diff --git a/tests/._Predicates.txt b/tests/._Predicates.txt
deleted file mode 100644
index 6a0a63d..0000000
Binary files a/tests/._Predicates.txt and /dev/null differ
diff --git a/tests/._binascii_hex.txt b/tests/._binascii_hex.txt
deleted file mode 100644
index 787b15c..0000000
Binary files a/tests/._binascii_hex.txt and /dev/null differ
--
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