[python-shapely] 01/148: Imported Upstream version 1.0.14
Sebastiaan Couwenberg
sebastic at moszumanska.debian.org
Thu Aug 20 17:41:57 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 3b2ae68e23c320fda279ee0735e614a0b94a8d86
Author: Pietro Battiston <toobaz at email.it>
Date: Fri Apr 30 12:06:11 2010 +0200
Imported Upstream version 1.0.14
---
CHANGES.txt | 67 +++
CREDITS.txt | 9 +
GEOS-C-API.txt | 55 +++
HISTORY.txt | 24 +
LICENSE.txt | 28 ++
MANIFEST.in | 2 +
PKG-INFO | 176 +++++++
README.txt | 158 ++++++
Shapely.egg-info/PKG-INFO | 176 +++++++
Shapely.egg-info/SOURCES.txt | 61 +++
Shapely.egg-info/dependency_links.txt | 1 +
Shapely.egg-info/requires.txt | 2 +
Shapely.egg-info/top_level.txt | 1 +
examples/geoms.py | 51 ++
examples/world.py | 21 +
manual/manual.txt | 875 ++++++++++++++++++++++++++++++++++
manual/style.css | 231 +++++++++
setup.cfg | 5 +
setup.py | 36 ++
setup_windows.py | 37 ++
shapely/__init__.py | 1 +
shapely/ctypes_declarations.py | 212 ++++++++
shapely/geometry/__init__.py | 8 +
shapely/geometry/base.py | 508 ++++++++++++++++++++
shapely/geometry/collection.py | 38 ++
shapely/geometry/geo.py | 64 +++
shapely/geometry/linestring.py | 229 +++++++++
shapely/geometry/multilinestring.py | 157 ++++++
shapely/geometry/multipoint.py | 187 ++++++++
shapely/geometry/multipolygon.py | 163 +++++++
shapely/geometry/point.py | 256 ++++++++++
shapely/geometry/polygon.py | 451 ++++++++++++++++++
shapely/geometry/proxy.py | 48 ++
shapely/geos.py | 101 ++++
shapely/iterops.py | 52 ++
shapely/ops.py | 29 ++
shapely/predicates.py | 65 +++
shapely/topology.py | 74 +++
shapely/wkb.py | 42 ++
shapely/wkt.py | 40 ++
tests/Array.txt | 86 ++++
tests/GeoInterface.txt | 69 +++
tests/IterOps.txt | 31 ++
tests/LineString.txt | 130 +++++
tests/MultiLineString.txt | 64 +++
tests/MultiPoint.txt | 85 ++++
tests/MultiPolygon.txt | 55 +++
tests/Operations.txt | 72 +++
tests/Persist.txt | 35 ++
tests/Point.txt | 148 ++++++
tests/Polygon.txt | 147 ++++++
tests/Predicates.txt | 51 ++
tests/__init__.py | 2 +
tests/attribute-chains.txt | 32 ++
tests/binascii_hex.txt | 47 ++
tests/dimensions.txt | 17 +
tests/invalid_intersection.txt | 29 ++
tests/polygonize.txt | 29 ++
tests/rungrind.dist | 4 +
tests/test_doctests.py | 35 ++
tests/test_threads.py | 33 ++
tests/valgrind-python.supp | 240 ++++++++++
tests/wkt_locale.txt | 28 ++
63 files changed, 6210 insertions(+)
diff --git a/CHANGES.txt b/CHANGES.txt
new file mode 100644
index 0000000..0f3ef96
--- /dev/null
+++ b/CHANGES.txt
@@ -0,0 +1,67 @@
+All tickets are children of http://trac.gispython.org/lab/ticket.
+
+1.0.14 (2009-10-05)
+-------------------
+- Proper prototyping of WKB writer, and avoidance of errors on 64-bit systems
+ (#191).
+
+1.0.13 (2009-09-29)
+-------------------
+- Prototype libgeos_c functions in a way that lets py2exe apps import shapely
+ (#189).
+
+1.0.12 (2009-04-09)
+-------------------
+- Fix for references held by topology and predicate descriptors.
+
+1.0.11 (2008-11-20)
+-------------------
+- Work around bug in GEOS 2.2.3, GEOSCoordSeq_getOrdinate not exported properly
+ (#178).
+
+1.0.10 (2008-11-17)
+-------------------
+- Fixed compatibility with GEOS 2.2.3 that was broken in 1.0.8 release (#176).
+
+1.0.9 (2008-11-16)
+------------------
+- Find and load MacPorts libgeos.
+
+1.0.8 (2008-11-01)
+------------------
+- Fill out GEOS function result and argument types to prevent faults on a
+ 64-bit arch.
+
+1.0.7 (2008-08-22)
+------------------
+- Polygon rings now have the same dimensions as parent (#168).
+- Eliminated reference cycles in polygons (#169).
+
+1.0.6 (2008-07-10)
+------------------
+- Fixed adaptation of multi polygon data.
+- Raise exceptions earlier from binary predicates.
+- Beginning distributing new windows DLLs (#166).
+
+1.0.5 (2008-05-20)
+------------------
+- Added access to GEOS polygonizer function.
+- Raise exception when insufficient coordinate tuples are passed to LinearRing
+ constructor (#164).
+
+1.0.4 (2008-05-01)
+------------------
+- Disentangle Python and topological equality (#163).
+- Add shape(), a factory that copies coordinates from a geo interface provider.
+ To be used instead of asShape() unless you really need to store coordinates
+ outside shapely for efficient use in other code.
+- Cache GEOS geometries in adapters (#163).
+
+1.0.3 (2008-04-09)
+------------------
+- Do not release GIL when calling GEOS functions (#158).
+- Prevent faults when chaining multiple GEOS operators (#159).
+
+1.0.2 (2008-02-26)
+------------------
+- Fix loss of dimensionality in polygon rings (#155).
diff --git a/CREDITS.txt b/CREDITS.txt
new file mode 100644
index 0000000..9f374e2
--- /dev/null
+++ b/CREDITS.txt
@@ -0,0 +1,9 @@
+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).
diff --git a/GEOS-C-API.txt b/GEOS-C-API.txt
new file mode 100644
index 0000000..c4b5ac3
--- /dev/null
+++ b/GEOS-C-API.txt
@@ -0,0 +1,55 @@
+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
new file mode 100644
index 0000000..d782205
--- /dev/null
+++ b/HISTORY.txt
@@ -0,0 +1,24 @@
+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/LICENSE.txt b/LICENSE.txt
new file mode 100644
index 0000000..7509168
--- /dev/null
+++ b/LICENSE.txt
@@ -0,0 +1,28 @@
+
+Copyright (c) 2007, Sean C. Gillies
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of Sean C. Gillies nor the names of
+ its contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
diff --git a/MANIFEST.in b/MANIFEST.in
new file mode 100644
index 0000000..c4bf1f8
--- /dev/null
+++ b/MANIFEST.in
@@ -0,0 +1,2 @@
+include *.txt
+recursive-include examples *.py
diff --git a/PKG-INFO b/PKG-INFO
new file mode 100644
index 0000000..ac76cbe
--- /dev/null
+++ b/PKG-INFO
@@ -0,0 +1,176 @@
+Metadata-Version: 1.0
+Name: Shapely
+Version: 1.0.14
+Summary: Geospatial geometries, predicates, and operations
+Home-page: http://trac.gispython.org/lab/wiki/Shapely
+Author: Sean Gillies
+Author-email: sgillies at frii.com
+License: BSD
+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:
+
+ * 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.
+
+ 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
+
+
+ Dependencies
+ ------------
+
+ * libgeos_c (2.2.3 or 3.0.0+)
+ * Python ctypes_ (standard in Python 2.5+)
+
+ .. _ctypes: http://pypi.python.org/pypi/ctypes/
+
+
+ Installation
+ ------------
+
+ 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::
+
+ $ sudo python setup.py install
+
+
+ Usage
+ -----
+
+ To buffer 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.
+
+
+ Numpy 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)'
+
+
+ Python Geo Interface
+ --------------------
+
+ Any object that provides the Python geo interface can be adapted to a Shapely
+ geometry with the asShape factory::
+
+ >>> 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),)
+
+ >>> 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),)
+
+ See http://trac.gispython.org/lab/wiki/PythonGeoInterface for more
+ details on the interface.
+
+
+ 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::
+
+ $ python setup.py test
+
+
+ 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)
+ * Justin Bronn (GeoDjango) for ctypes inspiration
+
+ .. |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).
+
+Keywords: geometry topology
+Platform: UNKNOWN
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Intended Audience :: Developers
+Classifier: Intended Audience :: Science/Research
+Classifier: License :: OSI Approved :: BSD License
+Classifier: Operating System :: OS Independent
+Classifier: Programming Language :: Python
+Classifier: Topic :: Scientific/Engineering :: GIS
diff --git a/README.txt b/README.txt
new file mode 100644
index 0000000..bc23ca1
--- /dev/null
+++ b/README.txt
@@ -0,0 +1,158 @@
+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.
+
+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
+
+
+Dependencies
+------------
+
+* libgeos_c (2.2.3 or 3.0.0+)
+* Python ctypes_ (standard in Python 2.5+)
+
+.. _ctypes: http://pypi.python.org/pypi/ctypes/
+
+
+Installation
+------------
+
+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::
+
+ $ sudo python setup.py install
+
+
+Usage
+-----
+
+To buffer 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.
+
+
+Numpy 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)'
+
+
+Python Geo Interface
+--------------------
+
+Any object that provides the Python geo interface can be adapted to a Shapely
+geometry with the asShape factory::
+
+ >>> 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),)
+
+ >>> 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),)
+
+See http://trac.gispython.org/lab/wiki/PythonGeoInterface for more
+details on the interface.
+
+
+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::
+
+ $ python setup.py test
+
+
+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)
+* Justin Bronn (GeoDjango) for ctypes inspiration
+
+.. |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).
diff --git a/Shapely.egg-info/PKG-INFO b/Shapely.egg-info/PKG-INFO
new file mode 100644
index 0000000..ac76cbe
--- /dev/null
+++ b/Shapely.egg-info/PKG-INFO
@@ -0,0 +1,176 @@
+Metadata-Version: 1.0
+Name: Shapely
+Version: 1.0.14
+Summary: Geospatial geometries, predicates, and operations
+Home-page: http://trac.gispython.org/lab/wiki/Shapely
+Author: Sean Gillies
+Author-email: sgillies at frii.com
+License: BSD
+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:
+
+ * 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.
+
+ 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
+
+
+ Dependencies
+ ------------
+
+ * libgeos_c (2.2.3 or 3.0.0+)
+ * Python ctypes_ (standard in Python 2.5+)
+
+ .. _ctypes: http://pypi.python.org/pypi/ctypes/
+
+
+ Installation
+ ------------
+
+ 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::
+
+ $ sudo python setup.py install
+
+
+ Usage
+ -----
+
+ To buffer 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.
+
+
+ Numpy 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)'
+
+
+ Python Geo Interface
+ --------------------
+
+ Any object that provides the Python geo interface can be adapted to a Shapely
+ geometry with the asShape factory::
+
+ >>> 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),)
+
+ >>> 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),)
+
+ See http://trac.gispython.org/lab/wiki/PythonGeoInterface for more
+ details on the interface.
+
+
+ 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::
+
+ $ python setup.py test
+
+
+ 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)
+ * Justin Bronn (GeoDjango) for ctypes inspiration
+
+ .. |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).
+
+Keywords: geometry topology
+Platform: UNKNOWN
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Intended Audience :: Developers
+Classifier: Intended Audience :: Science/Research
+Classifier: License :: OSI Approved :: BSD License
+Classifier: Operating System :: OS Independent
+Classifier: Programming Language :: Python
+Classifier: Topic :: Scientific/Engineering :: GIS
diff --git a/Shapely.egg-info/SOURCES.txt b/Shapely.egg-info/SOURCES.txt
new file mode 100644
index 0000000..3784d1b
--- /dev/null
+++ b/Shapely.egg-info/SOURCES.txt
@@ -0,0 +1,61 @@
+CHANGES.txt
+CREDITS.txt
+GEOS-C-API.txt
+HISTORY.txt
+LICENSE.txt
+MANIFEST.in
+README.txt
+setup.py
+setup_windows.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
+manual/style.css
+shapely/__init__.py
+shapely/ctypes_declarations.py
+shapely/geos.py
+shapely/iterops.py
+shapely/ops.py
+shapely/predicates.py
+shapely/topology.py
+shapely/wkb.py
+shapely/wkt.py
+shapely/geometry/__init__.py
+shapely/geometry/base.py
+shapely/geometry/collection.py
+shapely/geometry/geo.py
+shapely/geometry/linestring.py
+shapely/geometry/multilinestring.py
+shapely/geometry/multipoint.py
+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/__init__.py
+tests/attribute-chains.txt
+tests/binascii_hex.txt
+tests/dimensions.txt
+tests/invalid_intersection.txt
+tests/polygonize.txt
+tests/rungrind.dist
+tests/test_doctests.py
+tests/test_threads.py
+tests/valgrind-python.supp
+tests/wkt_locale.txt
\ No newline at end of file
diff --git a/Shapely.egg-info/dependency_links.txt b/Shapely.egg-info/dependency_links.txt
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/Shapely.egg-info/dependency_links.txt
@@ -0,0 +1 @@
+
diff --git a/Shapely.egg-info/requires.txt b/Shapely.egg-info/requires.txt
new file mode 100644
index 0000000..e0bd690
--- /dev/null
+++ b/Shapely.egg-info/requires.txt
@@ -0,0 +1,2 @@
+setuptools
+ctypes
\ No newline at end of file
diff --git a/Shapely.egg-info/top_level.txt b/Shapely.egg-info/top_level.txt
new file mode 100644
index 0000000..5cc3857
--- /dev/null
+++ b/Shapely.egg-info/top_level.txt
@@ -0,0 +1 @@
+shapely
diff --git a/examples/geoms.py b/examples/geoms.py
new file mode 100644
index 0000000..164ab72
--- /dev/null
+++ b/examples/geoms.py
@@ -0,0 +1,51 @@
+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/world.py b/examples/world.py
new file mode 100644
index 0000000..81b9af8
--- /dev/null
+++ b/examples/world.py
@@ -0,0 +1,21 @@
+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
new file mode 100644
index 0000000..10e2a2b
--- /dev/null
+++ b/manual/manual.txt
@@ -0,0 +1,875 @@
+==================
+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/manual/style.css b/manual/style.css
new file mode 100644
index 0000000..941516a
--- /dev/null
+++ b/manual/style.css
@@ -0,0 +1,231 @@
+/* css */
+
+body {
+ font: 90% 'Lucida Grande', Verdana, Geneva, Lucida, Arial, Helvetica, sans-serif;
+ background: #ffffff;
+ color: black;
+ margin: 2em;
+ padding: 2em;
+}
+
+a[href] {
+ color: #436976;
+ background-color: transparent;
+}
+
+a.toc-backref {
+ text-decoration: none;
+}
+
+h1 a[href] {
+ text-decoration: none;
+ color: #fcb100;
+ background-color: transparent;
+}
+
+a.strong {
+ font-weight: bold;
+}
+
+img {
+ margin: 0;
+ border: 0;
+}
+
+p {
+ margin: 0.5em 0 1em 0;
+ line-height: 1.5em;
+}
+p a {
+ text-decoration: underline;
+}
+p a:visited {
+ color: purple;
+ background-color: transparent;
+}
+p a:active {
+ color: red;
+ background-color: transparent;
+}
+a:hover {
+ text-decoration: none;
+}
+p img {
+ border: 0;
+ margin: 0;
+}
+
+h1, h2, h3, h4, h5, h6 {
+ color: #003a6b;
+ background-color: transparent;
+ font: 100% 'Lucida Grande', Verdana, Geneva, Lucida, Arial, Helvetica, sans-serif;
+ margin: 0;
+ padding-top: 0.5em;
+}
+
+h1 {
+ font-size: 160%;
+ margin-bottom: 0.5em;
+ border-bottom: 1px solid #fcb100;
+}
+h2 {
+ font-size: 140%;
+ margin-bottom: 0.5em;
+ border-bottom: 1px solid #aaa;
+}
+h3 {
+ font-size: 130%;
+ margin-bottom: 0.5em;
+ text-decoration: underline;
+}
+h4 {
+ font-size: 110%;
+ font-weight: bold;
+}
+h5 {
+ font-size: 100%;
+ font-weight: bold;
+}
+h6 {
+ font-size: 80%;
+ font-weight: bold;
+}
+
+ul a, ol a {
+ text-decoration: underline;
+}
+
+dt {
+ font-weight: bold;
+}
+dt a {
+ text-decoration: none;
+}
+
+dd {
+ line-height: 1.5em;
+ margin-bottom: 1em;
+}
+
+legend {
+ background: #ffffff;
+ padding: 0.5em;
+}
+
+form {
+ margin: 0;
+}
+
+
+dl.form {
+ margin: 0;
+ padding: 1em;
+}
+
+dl.form dt {
+ width: 30%;
+ float: left;
+ margin: 0;
+ padding: 0 0.5em 0.5em 0;
+ text-align: right;
+}
+
+input {
+ font: 100% 'Lucida Grande', Verdana, Geneva, Lucida, Arial, Helvetica, sans-serif;
+ color: black;
+ background-color: white;
+ vertical-align: middle;
+}
+
+abbr, acronym, .explain {
+ color: black;
+ background-color: transparent;
+}
+
+q, blockquote {
+}
+
+code, pre {
+ font-family: monospace;
+ font-size: 1.2em;
+ display: block;
+ padding: 10px;
+ border: 1px solid #838183;
+ background-color: #eee;
+ color: #000;
+ overflow: auto;
+ margin: 0.5em 1em;
+}
+
+tt.docutils {
+ background-color: #eeeeee;
+}
+
+.docinfo {
+ text-align: left;
+}
+
+div.highlight {
+ margin-left: 2em ;
+ margin-right: 2em ;
+ background-color: #eeeeee
+ }
+
+.c { color: #008800; font-style: italic } /* Comment */
+.err { border: 1px solid #FF0000 } /* Error */
+.k { color: #AA22FF; font-weight: bold } /* Keyword */
+.o { color: #666666 } /* Operator */
+.cm { color: #008800; font-style: italic } /* Comment.Multiline */
+.cp { color: #008800 } /* Comment.Preproc */
+.c1 { color: #008800; font-style: italic } /* Comment.Single */
+.gd { color: #A00000 } /* Generic.Deleted */
+.ge { font-style: italic } /* Generic.Emph */
+.gr { color: #FF0000 } /* Generic.Error */
+.gh { color: #000080; font-weight: bold } /* Generic.Heading */
+.gi { color: #00A000 } /* Generic.Inserted */
+.go { color: #808080 } /* Generic.Output */
+.gp { color: #000080; font-weight: bold } /* Generic.Prompt */
+.gs { font-weight: bold } /* Generic.Strong */
+.gu { color: #800080; font-weight: bold } /* Generic.Subheading */
+.gt { color: #0040D0 } /* Generic.Traceback */
+.kc { color: #AA22FF; font-weight: bold } /* Keyword.Constant */
+.kd { color: #AA22FF; font-weight: bold } /* Keyword.Declaration */
+.kp { color: #AA22FF } /* Keyword.Pseudo */
+.kr { color: #AA22FF; font-weight: bold } /* Keyword.Reserved */
+.kt { color: #AA22FF; font-weight: bold } /* Keyword.Type */
+.m { color: #666666 } /* Literal.Number */
+.s { color: #BB4444 } /* Literal.String */
+.na { color: #BB4444 } /* Name.Attribute */
+.nb { color: #AA22FF } /* Name.Builtin */
+.nc { color: #0000FF } /* Name.Class */
+.no { color: #880000 } /* Name.Constant */
+.nd { color: #AA22FF } /* Name.Decorator */
+.ni { color: #999999; font-weight: bold } /* Name.Entity */
+.ne { color: #D2413A; font-weight: bold } /* Name.Exception */
+.nf { color: #00A000 } /* Name.Function */
+.nl { color: #A0A000 } /* Name.Label */
+.nn { color: #0000FF; font-weight: bold } /* Name.Namespace */
+.nt { color: #008000; font-weight: bold } /* Name.Tag */
+.nv { color: #B8860B } /* Name.Variable */
+.ow { color: #AA22FF; font-weight: bold } /* Operator.Word */
+.mf { color: #666666 } /* Literal.Number.Float */
+.mh { color: #666666 } /* Literal.Number.Hex */
+.mi { color: #666666 } /* Literal.Number.Integer */
+.mo { color: #666666 } /* Literal.Number.Oct */
+.sb { color: #BB4444 } /* Literal.String.Backtick */
+.sc { color: #BB4444 } /* Literal.String.Char */
+.sd { color: #BB4444; font-style: italic } /* Literal.String.Doc */
+.s2 { color: #BB4444 } /* Literal.String.Double */
+.se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */
+.sh { color: #BB4444 } /* Literal.String.Heredoc */
+.si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */
+.sx { color: #008000 } /* Literal.String.Other */
+.sr { color: #BB6688 } /* Literal.String.Regex */
+.s1 { color: #BB4444 } /* Literal.String.Single */
+.ss { color: #B8860B } /* Literal.String.Symbol */
+.bp { color: #AA22FF } /* Name.Builtin.Pseudo */
+.vc { color: #B8860B } /* Name.Variable.Class */
+.vg { color: #B8860B } /* Name.Variable.Global */
+.vi { color: #B8860B } /* Name.Variable.Instance */
+.il { color: #666666 } /* Literal.Number.Integer.Long */
+
diff --git a/setup.cfg b/setup.cfg
new file mode 100644
index 0000000..861a9f5
--- /dev/null
+++ b/setup.cfg
@@ -0,0 +1,5 @@
+[egg_info]
+tag_build =
+tag_date = 0
+tag_svn_revision = 0
+
diff --git a/setup.py b/setup.py
new file mode 100644
index 0000000..5596735
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,36 @@
+from setuptools import setup, Extension
+from sys import version_info
+
+# Require ctypes egg only for Python < 2.5
+install_requires = ['setuptools']
+if version_info[:2] < (2,5):
+ install_requires.append('ctypes')
+
+# Get text from README.txt
+readme_text = file('README.txt', 'rb').read()
+
+setup(name = 'Shapely',
+ version = '1.0.14',
+ 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 = [
+ 'Development Status :: 5 - Production/Stable',
+ 'Intended Audience :: Developers',
+ 'Intended Audience :: Science/Research',
+ 'License :: OSI Approved :: BSD License',
+ 'Operating System :: OS Independent',
+ 'Programming Language :: Python',
+ 'Topic :: Scientific/Engineering :: GIS',
+ ],
+)
diff --git a/setup_windows.py b/setup_windows.py
new file mode 100644
index 0000000..08d8cc0
--- /dev/null
+++ b/setup_windows.py
@@ -0,0 +1,37 @@
+from setuptools import setup, Extension
+from sys import version_info
+
+# Require ctypes egg only for Python < 2.5
+install_requires = ['setuptools']
+if version_info[:2] < (2,5):
+ install_requires.append('ctypes')
+
+# Get text from README.txt
+readme_text = file('README.txt', 'rb').read()
+
+setup(name = 'Shapely',
+ version = '1.0.14',
+ 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'],
+ data_files=[('DLLs', ['DLLs/geos.dll', 'DLLs/libgeos-3-0-0.dll']),],
+ install_requires = install_requires,
+ #tests_require = ['numpy'], -- not working with "tests" command
+ test_suite = 'tests.test_suite',
+ classifiers = [
+ 'Development Status :: 5 - Production/Stable',
+ 'Intended Audience :: Developers',
+ 'Intended Audience :: Science/Research',
+ 'License :: OSI Approved :: BSD License',
+ 'Operating System :: OS Independent',
+ 'Programming Language :: Python',
+ 'Topic :: Scientific/Engineering :: GIS',
+ ],
+)
diff --git a/shapely/__init__.py b/shapely/__init__.py
new file mode 100644
index 0000000..792d600
--- /dev/null
+++ b/shapely/__init__.py
@@ -0,0 +1 @@
+#
diff --git a/shapely/ctypes_declarations.py b/shapely/ctypes_declarations.py
new file mode 100644
index 0000000..60e169c
--- /dev/null
+++ b/shapely/ctypes_declarations.py
@@ -0,0 +1,212 @@
+# Prototyping of libgeos_c functions, now using a function written by
+# `tartley`: http://trac.gispython.org/lab/ticket/189
+
+import ctypes
+
+def prototype(lgeos):
+
+ lgeos.initGEOS.restype = None
+
+ lgeos.finishGEOS.restype = None
+
+ lgeos.GEOSversion.restype = ctypes.c_char_p
+
+ lgeos.GEOSGeomFromWKT.restype = ctypes.c_void_p
+ lgeos.GEOSGeomFromWKT.argtypes = [ctypes.c_char_p]
+
+ lgeos.GEOSGeomToWKT.restype = ctypes.c_char_p
+ lgeos.GEOSGeomToWKT.argtypes = [ctypes.c_void_p]
+
+ lgeos.GEOS_setWKBOutputDims.restype = ctypes.c_int
+ lgeos.GEOS_setWKBOutputDims.argtypes = [ctypes.c_int]
+
+ 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.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]
+
+ lgeos.GEOSCoordSeq_clone.restype = ctypes.c_void_p
+ lgeos.GEOSCoordSeq_clone.argtypes = [ctypes.c_void_p]
+
+ lgeos.GEOSCoordSeq_destroy.restype = None
+ lgeos.GEOSCoordSeq_destroy.argtypes = [ctypes.c_void_p]
+
+ lgeos.GEOSCoordSeq_setX.restype = ctypes.c_int
+ lgeos.GEOSCoordSeq_setX.argtypes = [ctypes.c_void_p, ctypes.c_uint, ctypes.c_double]
+
+ lgeos.GEOSCoordSeq_setY.restype = ctypes.c_int
+ lgeos.GEOSCoordSeq_setY.argtypes = [ctypes.c_void_p, ctypes.c_uint, ctypes.c_double]
+
+ lgeos.GEOSCoordSeq_setZ.restype = ctypes.c_int
+ lgeos.GEOSCoordSeq_setZ.argtypes = [ctypes.c_void_p, ctypes.c_uint, ctypes.c_double]
+
+ lgeos.GEOSCoordSeq_setOrdinate.restype = ctypes.c_int
+ lgeos.GEOSCoordSeq_setOrdinate.argtypes = [ctypes.c_void_p, ctypes.c_uint, ctypes.c_uint, ctypes.c_double]
+
+ lgeos.GEOSCoordSeq_getX.restype = ctypes.c_int
+ lgeos.GEOSCoordSeq_getX.argtypes = [ctypes.c_void_p, ctypes.c_uint, ctypes.c_void_p]
+
+ lgeos.GEOSCoordSeq_getY.restype = ctypes.c_int
+ lgeos.GEOSCoordSeq_getY.argtypes = [ctypes.c_void_p, ctypes.c_uint, ctypes.c_void_p]
+
+ lgeos.GEOSCoordSeq_getZ.restype = ctypes.c_int
+ lgeos.GEOSCoordSeq_getZ.argtypes = [ctypes.c_void_p, ctypes.c_uint, ctypes.c_void_p]
+
+ lgeos.GEOSCoordSeq_getSize.restype = ctypes.c_int
+ lgeos.GEOSCoordSeq_getSize.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
+
+ lgeos.GEOSCoordSeq_getDimensions.restype = ctypes.c_int
+ lgeos.GEOSCoordSeq_getDimensions.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
+
+ lgeos.GEOSGeom_createPoint.restype = ctypes.c_void_p
+ lgeos.GEOSGeom_createPoint.argtypes = [ctypes.c_void_p]
+
+ lgeos.GEOSGeom_createLinearRing.restype = ctypes.c_void_p
+ lgeos.GEOSGeom_createLinearRing.argtypes = [ctypes.c_void_p]
+
+ lgeos.GEOSGeom_createLineString.restype = ctypes.c_void_p
+ lgeos.GEOSGeom_createLineString.argtypes = [ctypes.c_void_p]
+
+ lgeos.GEOSGeom_createPolygon.restype = ctypes.c_void_p
+ lgeos.GEOSGeom_createPolygon.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_uint]
+
+ lgeos.GEOSGeom_createCollection.restype = ctypes.c_void_p
+ lgeos.GEOSGeom_createCollection.argtypes = [ctypes.c_int, ctypes.c_void_p, ctypes.c_uint]
+
+ lgeos.GEOSGeom_clone.restype = ctypes.c_void_p
+ lgeos.GEOSGeom_clone.argtypes = [ctypes.c_void_p]
+
+ lgeos.GEOSGeom_destroy.restype = None
+ lgeos.GEOSGeom_destroy.argtypes = [ctypes.c_void_p]
+
+ lgeos.GEOSEnvelope.restype = ctypes.c_void_p
+ lgeos.GEOSEnvelope.argtypes = [ctypes.c_void_p]
+
+ lgeos.GEOSIntersection.restype = ctypes.c_void_p
+ lgeos.GEOSIntersection.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
+
+ lgeos.GEOSBuffer.restype = ctypes.c_void_p
+ lgeos.GEOSBuffer.argtypes = [ctypes.c_void_p, ctypes.c_double, ctypes.c_int]
+
+ lgeos.GEOSConvexHull.restype = ctypes.c_void_p
+ lgeos.GEOSConvexHull.argtypes = [ctypes.c_void_p]
+
+ lgeos.GEOSDifference.restype = ctypes.c_void_p
+ lgeos.GEOSDifference.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
+
+ lgeos.GEOSSymDifference.restype = ctypes.c_void_p
+ lgeos.GEOSSymDifference.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
+
+ lgeos.GEOSBoundary.restype = ctypes.c_void_p
+ lgeos.GEOSBoundary.argtypes = [ctypes.c_void_p]
+
+ lgeos.GEOSUnion.restype = ctypes.c_void_p
+ lgeos.GEOSUnion.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
+
+ lgeos.GEOSPointOnSurface.restype = ctypes.c_void_p
+ lgeos.GEOSPointOnSurface.argtypes = [ctypes.c_void_p]
+
+ lgeos.GEOSGetCentroid.restype = ctypes.c_void_p
+ lgeos.GEOSGetCentroid.argtypes = [ctypes.c_void_p]
+
+ lgeos.GEOSRelate.restype = ctypes.c_char_p
+ lgeos.GEOSRelate.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
+
+ lgeos.GEOSPolygonize.restype = ctypes.c_void_p
+ lgeos.GEOSPolygonize.argtypes = [ctypes.c_void_p, ctypes.c_uint]
+
+ lgeos.GEOSLineMerge.restype = ctypes.c_void_p
+ lgeos.GEOSLineMerge.argtypes = [ctypes.c_void_p]
+
+ 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.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
+
+ lgeos.GEOSTouches.restype = ctypes.c_int
+ lgeos.GEOSTouches.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
+
+ lgeos.GEOSIntersects.restype = ctypes.c_int
+ lgeos.GEOSIntersects.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
+
+ lgeos.GEOSCrosses.restype = ctypes.c_int
+ lgeos.GEOSCrosses.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
+
+ lgeos.GEOSWithin.restype = ctypes.c_int
+ lgeos.GEOSWithin.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
+
+ lgeos.GEOSContains.restype = ctypes.c_int
+ lgeos.GEOSContains.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
+
+ lgeos.GEOSOverlaps.restype = ctypes.c_int
+ lgeos.GEOSOverlaps.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
+
+ lgeos.GEOSEquals.restype = ctypes.c_int
+ lgeos.GEOSEquals.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
+
+ lgeos.GEOSisEmpty.restype = ctypes.c_int
+ lgeos.GEOSisEmpty.argtypes = [ctypes.c_void_p]
+
+ lgeos.GEOSisValid.restype = ctypes.c_int
+ lgeos.GEOSisValid.argtypes = [ctypes.c_void_p]
+
+ lgeos.GEOSisSimple.restype = ctypes.c_int
+ lgeos.GEOSisSimple.argtypes = [ctypes.c_void_p]
+
+ lgeos.GEOSisRing.restype = ctypes.c_int
+ lgeos.GEOSisRing.argtypes = [ctypes.c_void_p]
+
+ lgeos.GEOSHasZ.restype = ctypes.c_int
+ lgeos.GEOSHasZ.argtypes = [ctypes.c_void_p]
+
+ lgeos.GEOSGeomType.restype = ctypes.c_char_p
+ lgeos.GEOSGeomType.argtypes = [ctypes.c_void_p]
+
+ lgeos.GEOSGeomTypeId.restype = ctypes.c_int
+ lgeos.GEOSGeomTypeId.argtypes = [ctypes.c_void_p]
+
+ lgeos.GEOSGetSRID.restype = ctypes.c_int
+ lgeos.GEOSGetSRID.argtypes = [ctypes.c_void_p]
+
+ lgeos.GEOSSetSRID.restype = None
+ lgeos.GEOSSetSRID.argtypes = [ctypes.c_void_p, ctypes.c_int]
+
+ lgeos.GEOSGetNumGeometries.restype = ctypes.c_int
+ lgeos.GEOSGetNumGeometries.argtypes = [ctypes.c_void_p]
+
+ lgeos.GEOSGetGeometryN.restype = ctypes.c_void_p
+ lgeos.GEOSGetGeometryN.argtypes = [ctypes.c_void_p, ctypes.c_int]
+
+ lgeos.GEOSGetNumInteriorRings.restype = ctypes.c_int
+ lgeos.GEOSGetNumInteriorRings.argtypes = [ctypes.c_void_p]
+
+ lgeos.GEOSGetInteriorRingN.restype = ctypes.c_void_p
+ lgeos.GEOSGetInteriorRingN.argtypes = [ctypes.c_void_p, ctypes.c_int]
+
+ lgeos.GEOSGetExteriorRing.restype = ctypes.c_void_p
+ lgeos.GEOSGetExteriorRing.argtypes = [ctypes.c_void_p]
+
+ lgeos.GEOSGetNumCoordinates.restype = ctypes.c_int
+ lgeos.GEOSGetNumCoordinates.argtypes = [ctypes.c_void_p]
+
+ lgeos.GEOSGeom_getCoordSeq.restype = ctypes.c_void_p
+ lgeos.GEOSGeom_getCoordSeq.argtypes = [ctypes.c_void_p]
+
+ lgeos.GEOSGeom_getDimensions.restype = ctypes.c_int
+ lgeos.GEOSGeom_getDimensions.argtypes = [ctypes.c_void_p]
+
+ lgeos.GEOSArea.restype = ctypes.c_double
+ lgeos.GEOSArea.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
+
+ lgeos.GEOSLength.restype = ctypes.c_int
+ lgeos.GEOSLength.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
+
+ lgeos.GEOSDistance.restype = ctypes.c_int
+ lgeos.GEOSDistance.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p]
+
diff --git a/shapely/geometry/__init__.py b/shapely/geometry/__init__.py
new file mode 100644
index 0000000..b66efc0
--- /dev/null
+++ b/shapely/geometry/__init__.py
@@ -0,0 +1,8 @@
+from geo import shape, asShape
+from point import Point, asPoint
+from linestring import LineString, asLineString
+from polygon import Polygon, asPolygon
+from multipoint import MultiPoint, asMultiPoint
+from multilinestring import MultiLineString, asMultiLineString
+from multipolygon import MultiPolygon, asMultiPolygon
+from collection import GeometryCollection
diff --git a/shapely/geometry/base.py b/shapely/geometry/base.py
new file mode 100644
index 0000000..3cde024
--- /dev/null
+++ b/shapely/geometry/base.py
@@ -0,0 +1,508 @@
+"""
+Base geometry class and utilities.
+"""
+
+from ctypes import string_at, byref, c_int, c_size_t, c_char_p, c_double, c_void_p
+import sys
+
+from shapely.geos import lgeos, free, allocated_c_char_p
+from shapely.predicates import BinaryPredicate, UnaryPredicate
+from shapely.topology import BinaryTopologicalOp, UnaryTopologicalOp
+
+
+GEOMETRY_TYPES = [
+ 'Point',
+ 'LineString',
+ 'LinearRing',
+ 'Polygon',
+ 'MultiPoint',
+ 'MultiLineString',
+ 'MultiPolygon',
+ 'GeometryCollection'
+ ]
+
+def geometry_type_name(g):
+ if g is None:
+ 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):
+ if not g:
+ raise ValueError, "No Shapely geometry can be created from this null value"
+ ob = BaseGeometry()
+ geom_type = geometry_type_name(g)
+ # TODO: check cost of dynamic import by profiling
+ mod = __import__(
+ 'shapely.geometry',
+ globals(),
+ locals(),
+ [geom_type],
+ )
+ ob.__class__ = getattr(mod, geom_type)
+ ob.__geom__ = g
+ ob.__p__ = parent
+ 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."""
+ 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"
+ return func(*args, **kwargs)
+ return wrapper
+
+
+class BaseGeometry(object):
+
+ """Provides GEOS spatial predicates and topological operations.
+ """
+
+ __geom__ = None # See _geom property below
+ __p__ = None
+ _ctypes_data = None
+ _ndim = None
+ _crs = None
+ _owned = False
+
+ def __init__(self):
+ self.__geom__ = None
+
+ def __del__(self):
+ if self.__geom__ is not None and not self._owned:
+ lgeos.GEOSGeom_destroy(self.__geom__)
+ self.__geom__ = None
+ self.__p__ = None
+
+ def __str__(self):
+ 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__
+
+ def _get_geom(self):
+ return self.__geom__
+
+ def _set_geom(self, val):
+ 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."""
+ raise NotImplementedError
+
+ @property
+ def array_interface_base(self):
+ if sys.byteorder == 'little':
+ typestr = '<f8'
+ elif sys.byteorder == 'big':
+ typestr = '>f8'
+ else:
+ raise ValueError, "Unsupported byteorder: neither little nor big-endian"
+ return {
+ 'version': 3,
+ 'typestr': typestr,
+ 'data': self.ctypes,
+ }
+
+ @property
+ def __array_interface__(self):
+ """Provide the Numpy array protocol."""
+ raise NotImplementedError
+
+ @exceptNull
+ def _get_coords(self):
+ return CoordinateSequence(self)
+
+ def _set_coords(self, ob):
+ raise NotImplementedError, \
+ "set_coords must be provided by derived classes"
+
+ coords = property(_get_coords, _set_coords)
+
+ # Python feature protocol
+
+ @property
+ def __geo_interface__(self):
+ 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)
+
+ @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))
+
+ @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
+
+ @property
+ @exceptNull
+ def area(self):
+ a = c_double()
+ retval = lgeos.GEOSArea(self._geom, byref(a))
+ return a.value
+
+ @property
+ @exceptNull
+ def length(self):
+ len = c_double()
+ retval = lgeos.GEOSLength(self._geom, byref(len))
+ return len.value
+
+ @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))
+ )
+
+ # 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)
+
+ # 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)
+
+ # Unary predicates
+ #
+ # These use descriptors to reduce the amount of boilerplate.
+
+ 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
+ @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))
+
+ 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)
+
+
+class BaseMultiPartGeometry(BaseGeometry):
+
+ @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, \
+ "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"
+
+ @property
+ def coords(self):
+ raise NotImplementedError, \
+ "Multi-part geometries do not provide a coordinate sequence"
diff --git a/shapely/geometry/collection.py b/shapely/geometry/collection.py
new file mode 100644
index 0000000..fd66f5c
--- /dev/null
+++ b/shapely/geometry/collection.py
@@ -0,0 +1,38 @@
+"""
+Geometry collections.
+"""
+
+from shapely.geometry.base import BaseMultiPartGeometry
+from shapely.geometry.base import HeterogeneousGeometrySequence, exceptNull
+
+
+class GeometryCollection(BaseMultiPartGeometry):
+
+ """A geometry collection.
+ """
+
+ def __init__(self):
+ BaseMultiPartGeometry.__init__(self)
+
+ @property
+ def __geo_interface__(self):
+ geometries = []
+ for geom in self.geoms:
+ geometries.append(geom.__geo_interface__)
+ return dict(type='GeometryCollection', geometries=geometries)
+
+ @property
+ @exceptNull
+ def geoms(self):
+ return HeterogeneousGeometrySequence(self)
+
+
+# Test runner
+def _test():
+ import doctest
+ doctest.testmod()
+
+
+if __name__ == "__main__":
+ _test()
+
diff --git a/shapely/geometry/geo.py b/shapely/geometry/geo.py
new file mode 100644
index 0000000..349ed4a
--- /dev/null
+++ b/shapely/geometry/geo.py
@@ -0,0 +1,64 @@
+"""
+Geometry factories based on the geo interface.
+"""
+
+from point import Point, asPoint
+from linestring import LineString, asLineString
+from polygon import Polygon, asPolygon
+from multipoint import MultiPoint, asMultiPoint
+from multilinestring import MultiLineString, asMultiLineString
+from multipolygon import MultiPolygon, MultiPolygonAdapter, asMultiPolygon
+
+
+def shape(context):
+ """Return a new, independent geometry with coordinates *copied* from the
+ context.
+ """
+ if hasattr(context, "__geo_interface__"):
+ ob = context.__geo_interface__
+ else:
+ ob = context
+ geom_type = ob.get("type").lower()
+ if geom_type == "point":
+ return Point(ob["coordinates"])
+ elif geom_type == "linestring":
+ return LineString(ob["coordinates"])
+ elif geom_type == "polygon":
+ return Polygon(ob["coordinates"][0], ob["coordinates"][1:])
+ elif geom_type == "multipoint":
+ return MultiPoint(ob["coordinates"])
+ elif geom_type == "multilinestring":
+ return MultiLineString(ob["coordinates"])
+ elif geom_type == "multipolygon":
+ return MultiPolygon(ob["coordinates"], context_type='geojson')
+ else:
+ raise ValueError, "Unknown geometry type: %s" % geom_type
+
+def asShape(context):
+ """Adapts the context to a geometry interface. The coordinates remain
+ stored in the context.
+ """
+ if hasattr(context, "__geo_interface__"):
+ ob = context.__geo_interface__
+ else:
+ ob = context
+
+ try:
+ geom_type = ob.get("type").lower()
+ except AttributeError:
+ raise ValueError, "Context does not provide geo interface"
+
+ if geom_type == "point":
+ return asPoint(ob["coordinates"])
+ elif geom_type == "linestring":
+ return asLineString(ob["coordinates"])
+ elif geom_type == "polygon":
+ return asPolygon(ob["coordinates"][0], ob["coordinates"][1:])
+ elif geom_type == "multipoint":
+ return asMultiPoint(ob["coordinates"])
+ elif geom_type == "multilinestring":
+ return asMultiLineString(ob["coordinates"])
+ elif geom_type == "multipolygon":
+ return MultiPolygonAdapter(ob["coordinates"], context_type='geojson')
+ else:
+ raise ValueError, "Unknown geometry type: %s" % geom_type
diff --git a/shapely/geometry/linestring.py b/shapely/geometry/linestring.py
new file mode 100644
index 0000000..81820cb
--- /dev/null
+++ b/shapely/geometry/linestring.py
@@ -0,0 +1,229 @@
+"""
+Line strings.
+"""
+
+from ctypes import byref, c_double, c_int, cast, POINTER, pointer
+from ctypes import ArgumentError
+
+from shapely.geos import lgeos
+from shapely.geometry.base import BaseGeometry, exceptNull
+from shapely.geometry.proxy import CachingGeometryProxy
+
+
+def geos_linestring_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]
+ if m < 2:
+ 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)
+ 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']
+
+ # 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:
+ try:
+ dz = c_double(cp[n*i+2])
+ except IndexError:
+ raise ValueError, "Inconsistent coordinate dimensionality"
+
+ # 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)
+
+ except AttributeError:
+ # Fall back on list
+ m = len(ob)
+ if m < 2:
+ 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)
+ 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, \
+ "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:
+ try:
+ dz = c_double(coords[2])
+ except IndexError:
+ raise ValueError, "Inconsistent coordinate dimensionality"
+
+ # 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)
+
+ if update_geom is not None:
+ return None
+ else:
+ return lgeos.GEOSGeom_createLineString(cs), n
+
+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
new file mode 100644
index 0000000..a831b58
--- /dev/null
+++ b/shapely/geometry/multilinestring.py
@@ -0,0 +1,157 @@
+"""
+Multi-part collection of linestrings.
+"""
+
+from ctypes import byref, c_double, c_int, c_void_p, cast, POINTER, pointer
+
+from shapely.geos import lgeos
+from shapely.geometry.base import BaseGeometry, GeometrySequence, exceptNull
+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)
+
+
+class MultiLineString(BaseGeometry):
+
+ """a multiple linestring geometry.
+ """
+
+ def __init__(self, coordinates=None):
+ """Initialize.
+
+ 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.
+
+ Example
+ -------
+
+ >>> 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.
+ """
+ BaseGeometry.__init__(self)
+
+ if coordinates is None:
+ # allow creation of null lines, to support unpickling
+ pass
+ else:
+ self._geom, self._ndim = geos_multilinestring_from_py(coordinates)
+
+ @property
+ def __geo_interface__(self):
+ return {
+ 'type': 'MultiLineString',
+ '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
+
+ def __init__(self, context):
+ self.context = context
+ self.factory = geos_multilinestring_from_py
+
+ @property
+ def _ndim(self):
+ try:
+ # From array protocol
+ array = self.context[0].__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][0])
+
+
+def asMultiLineString(context):
+ """Factory for MultiLineStringAdapter instances."""
+ return MultiLineStringAdapter(context)
+
+
+# Test runner
+def _test():
+ import doctest
+ doctest.testmod()
+
+
+if __name__ == "__main__":
+ _test()
diff --git a/shapely/geometry/multipoint.py b/shapely/geometry/multipoint.py
new file mode 100644
index 0000000..9dbb887
--- /dev/null
+++ b/shapely/geometry/multipoint.py
@@ -0,0 +1,187 @@
+"""
+Multiple points.
+"""
+
+from ctypes import byref, c_double, c_int, c_void_p, cast, POINTER, pointer
+
+from shapely.geos import lgeos
+from shapely.geometry.base import BaseGeometry, GeometrySequence, exceptNull
+from shapely.geometry.point import Point, geos_point_from_py
+from shapely.geometry.proxy import CachingGeometryProxy
+
+
+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(BaseGeometry):
+
+ """A multiple point geometry.
+ """
+
+ 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
+ -------
+
+ >>> 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).
+ """
+ BaseGeometry.__init__(self)
+
+ if coordinates is None:
+ # allow creation of null lines, to support unpickling
+ pass
+ else:
+ self._geom, self._ndim = geos_multipoint_from_py(coordinates)
+
+
+ @property
+ @exceptNull
+ def __geo_interface__(self):
+ return {
+ 'type': 'MultiPoint',
+ 'coordinates': tuple([g.coords[0] for g in self.geoms])
+ }
+
+ @property
+ @exceptNull
+ def ctypes(self):
+ if not self._ctypes_data:
+ temp = c_double()
+ n = self._ndim
+ m = len(self.geoms)
+ array_type = c_double * (m * n)
+ data = array_type()
+ for i in xrange(m):
+ g = self.geoms[i]._geom
+ cs = lgeos.GEOSGeom_getCoordSeq(g)
+ lgeos.GEOSCoordSeq_getX(cs, 0, byref(temp))
+ data[n*i] = temp.value
+ lgeos.GEOSCoordSeq_getY(cs, 0, byref(temp))
+ data[n*i+1] = temp.value
+ if n == 3: # TODO: use hasz
+ lgeos.GEOSCoordSeq_getZ(cs, 0, byref(temp))
+ data[n*i+2] = temp.value
+ self._ctypes_data = data
+ return self._ctypes_data
+
+ @exceptNull
+ def array_interface(self):
+ """Provide the Numpy array protocol."""
+ ai = self.array_interface_base
+ ai.update({'shape': (len(self.geoms), self._ndim)})
+ 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
+
+ def __init__(self, context):
+ self.context = context
+ self.factory = geos_multipoint_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()
+
+
+def asMultiPoint(context):
+ """Factory for MultiPointAdapter instances."""
+ return MultiPointAdapter(context)
+
+
+# Test runner
+def _test():
+ import doctest
+ doctest.testmod()
+
+
+if __name__ == "__main__":
+ _test()
diff --git a/shapely/geometry/multipolygon.py b/shapely/geometry/multipolygon.py
new file mode 100644
index 0000000..dd78aed
--- /dev/null
+++ b/shapely/geometry/multipolygon.py
@@ -0,0 +1,163 @@
+"""
+Multi-part collection of polygons.
+"""
+
+from ctypes import byref, c_double, c_int, c_void_p, cast, POINTER, pointer
+
+from shapely.geos import lgeos
+from shapely.geometry.base import BaseGeometry, GeometrySequence, exceptNull
+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)
+
+
+
+class MultiPolygon(BaseGeometry):
+
+ """a multiple polygon geometry.
+ """
+
+ 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.
+
+ 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))]
+ ... )
+ ... ] )
+ """
+ BaseGeometry.__init__(self)
+
+ if polygons is None:
+ # allow creation of null collections, to support unpickling
+ pass
+ elif context_type == 'polygons':
+ self._geom, self._ndim = geos_multipolygon_from_polygons(polygons)
+ elif context_type == 'geojson':
+ self._geom, self._ndim = geos_multipolygon_from_py(polygons)
+
+ @property
+ def __geo_interface__(self):
+ allcoords = []
+ for geom in self.geoms:
+ coords = []
+ coords.append(tuple(geom.exterior.coords))
+ for hole in geom.interiors:
+ coords.append(tuple(hole.coords))
+ allcoords.append(coords)
+ return {
+ 'type': 'MultiPolygon',
+ '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
+
+ def __init__(self, context, context_type='polygons'):
+ self.context = context
+ if context_type == 'geojson':
+ self.factory = geos_multipolygon_from_py
+ elif context_type == 'polygons':
+ self.factory = geos_multipolygon_from_polygons
+
+ @property
+ def _ndim(self):
+ try:
+ # From array protocol
+ array = self.context[0][0].__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][0][0])
+
+
+def asMultiPolygon(context):
+ """Factory for MultiLineStringAdapter instances."""
+ return MultiPolygonAdapter(context)
+
+
+# Test runner
+def _test():
+ import doctest
+ doctest.testmod()
+
+
+if __name__ == "__main__":
+ _test()
diff --git a/shapely/geometry/point.py b/shapely/geometry/point.py
new file mode 100644
index 0000000..af00131
--- /dev/null
+++ b/shapely/geometry/point.py
@@ -0,0 +1,256 @@
+"""
+Points.
+"""
+
+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 cast, POINTER
+
+from shapely.geos import lgeos, DimensionError
+from shapely.geometry.base import BaseGeometry, CoordinateSequence
+from shapely.geometry.base import exceptNull
+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)
+
+
+class Point(BaseGeometry):
+
+ """A point geometry.
+
+ Attributes
+ ----------
+ x, y, z : float
+ Coordinate values
+
+ 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]]
+ """
+
+ 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.
+ 2) 2 or more parameters: x, y, z : float
+ 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))
+
+ # 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
+
+ @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
+
+ @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
+
+ @property
+ @exceptNull
+ def __geo_interface__(self):
+ return {
+ 'type': 'Point',
+ 'coordinates': self.coords[0]
+ }
+
+ @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
+ if self._ndim == 3:
+ array[2] = self.z
+ self._ctypes_data = array
+ return self._ctypes_data
+
+ def array_interface(self):
+ """Provide the Numpy array protocol."""
+ ai = self.array_interface_base
+ ai.update({'shape': (self._ndim,)})
+ return ai
+ __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)
+
+ # Coordinate access
+
+ def _set_coords(self, coordinates):
+ if self._geom is None:
+ self._init_geom(coordinates)
+ else:
+ update_point_from_py(self, coordinates)
+
+ coords = property(BaseGeometry._get_coords, _set_coords)
+
+
+class PointAdapter(CachingGeometryProxy, Point):
+
+ """Adapts a Python coordinate pair or a numpy array to the point interface.
+ """
+
+ _owned = False
+
+ def __init__(self, context):
+ self.context = context
+ self.factory = geos_point_from_py
+
+ @property
+ def _ndim(self):
+ try:
+ # From array protocol
+ array = self.context.__array_interface__
+ n = array['shape'][0]
+ assert n == 2 or n == 3
+ return n
+ except AttributeError:
+ # 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."""
+ 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 asPoint(context):
+ """Factory for PointAdapter instances."""
+ return PointAdapter(context)
+
+
+# Test runner
+def _test():
+ import doctest
+ doctest.testmod()
+
+if __name__ == "__main__":
+ _test()
diff --git a/shapely/geometry/polygon.py b/shapely/geometry/polygon.py
new file mode 100644
index 0000000..56eadb4
--- /dev/null
+++ b/shapely/geometry/polygon.py
@@ -0,0 +1,451 @@
+"""
+Polygons and their linear ring components.
+"""
+
+from ctypes import byref, c_double, c_int, c_void_p, cast, POINTER, pointer
+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)
+
+
+class LinearRing(LineString):
+
+ """A linear ring.
+ """
+
+ _ndim = None
+ __geom__ = None
+ __p__ = None
+ _owned = False
+
+ 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.
+
+ 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.)) )
+
+ Produces a 1x1 square.
+ """
+ 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)
+
+ @property
+ def __geo_interface__(self):
+ return {
+ 'type': 'LinearRing',
+ 'coordinates': tuple(self.coords)
+ }
+
+ # Coordinate access
+
+ _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)
+
+ 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
+
+ @property
+ def __geo_interface__(self):
+ return {
+ 'type': 'LinearRing',
+ 'coordinates': tuple(self.coords)
+ }
+
+ coords = property(BaseGeometry._get_coords)
+
+
+def asLinearRing(context):
+ return LinearRingAdapter(context)
+
+
+class InteriorRingSequence(object):
+
+ _factory = None
+ _geom = None
+ __p__ = None
+ _ndim = None
+ _index = 0
+ _length = 0
+ __rings__ = None
+ _gtag = None
+
+ def __init__(self, parent):
+ 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._length:
+ ring = self._get_ring(self._index)
+ self._index += 1
+ return ring
+ else:
+ raise StopIteration
+
+ def __len__(self):
+ return lgeos.GEOSGetNumInteriorRings(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
+ return self._get_ring(i)
+
+ @property
+ def _longest(self):
+ max = 0
+ for g in iter(self):
+ l = len(g.coords)
+ if l > max:
+ max = l
+
+ def gtag(self):
+ return hash(repr(self.__p__))
+
+ def _get_ring(self, i):
+ gtag = self.gtag()
+ if gtag != self._gtag:
+ self.__rings__ = {}
+ if i not in self.__rings__:
+ g = lgeos.GEOSGetInteriorRingN(self._geom, i)
+ ring = LinearRing()
+ ring.__geom__ = g
+ ring.__p__ = self
+ ring._owned = True
+ ring._ndim = self._ndim
+ self.__rings__[i] = weakref.ref(ring)
+ 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.
+ """
+
+ _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.
+
+ Example
+ -------
+ >>> coords = ((0., 0.), (0., 1.), (1., 1.), (1., 0.), (0., 0.))
+ >>> polygon = Polygon(coords)
+ """
+ BaseGeometry.__init__(self)
+
+ if shell is not None:
+ self._geom, self._ndim = geos_polygon_from_py(shell, holes)
+
+ @property
+ @exceptNull
+ def exterior(self):
+ if self._exterior is None or self._exterior() is None:
+ g = lgeos.GEOSGetExteriorRing(self._geom)
+ ring = LinearRing()
+ ring.__geom__ = g
+ ring.__p__ = self
+ ring._owned = True
+ ring._ndim = self._ndim
+ self._exterior = weakref.ref(ring)
+ return self._exterior()
+
+ @property
+ @exceptNull
+ def interiors(self):
+ return InteriorRingSequence(self)
+
+ @property
+ def ctypes(self):
+ if not self._ctypes_data:
+ self._ctypes_data = self.exterior.ctypes
+ return self._ctypes_data
+
+ @property
+ def __array_interface__(self):
+ 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"
+
+ def _set_coords(self, ob):
+ 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"
+
+ @property
+ def __geo_interface__(self):
+ coords = [tuple(self.exterior.coords)]
+ for hole in self.interiors:
+ coords.append(tuple(hole.coords))
+ return {
+ 'type': 'Polygon',
+ 'coordinates': tuple(coords)
+ }
+
+
+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
+ self.context = (shell, holes)
+ self.factory = geos_polygon_from_py
+
+ @property
+ def _ndim(self):
+ try:
+ # From array protocol
+ array = self.shell.__array_interface__
+ n = array['shape'][1]
+ assert n == 2 or n == 3
+ return n
+ except AttributeError:
+ # Fall back on list
+ return len(self.shell[0])
+
+
+def asPolygon(shell, holes=None):
+ """Factory for PolygonAdapter instances."""
+ return PolygonAdapter(shell, holes)
+
+
+# Test runner
+def _test():
+ import doctest
+ doctest.testmod()
+
+if __name__ == "__main__":
+ _test()
diff --git a/shapely/geometry/proxy.py b/shapely/geometry/proxy.py
new file mode 100644
index 0000000..27367d5
--- /dev/null
+++ b/shapely/geometry/proxy.py
@@ -0,0 +1,48 @@
+"""
+Proxy for coordinates stored outside Shapely geometries.
+"""
+
+from shapely.geos import lgeos
+
+
+class CachingGeometryProxy(object):
+
+ context = None
+ factory = None
+ __geom__ = None
+ _gtag = None
+
+ def __init__(self, context):
+ self.context = context
+
+ @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:
+ self.__geom__, n = self.factory(self.context)
+ self._gtag = gtag
+ return self.__geom__
+
+ def gtag(self):
+ return hash(repr(self.context))
+
+
+class PolygonProxy(CachingGeometryProxy):
+
+ @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[0], self.context[1])
+ elif self.__geom__ is None:
+ 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
new file mode 100644
index 0000000..75fd667
--- /dev/null
+++ b/shapely/geos.py
@@ -0,0 +1,101 @@
+"""
+Exports 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 os
+import sys
+
+from ctypes_declarations import prototype
+
+
+if sys.platform == 'win32':
+ try:
+ 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")
+ except (ImportError, WindowsError):
+ raise
+ def free(m):
+ try:
+ cdll.msvcrt.free(m)
+ except WindowsError:
+ # XXX: See http://trac.gispython.org/projects/PCL/ticket/149
+ pass
+elif sys.platform == 'darwin':
+ lib = find_library('geos_c')
+ if lib is None:
+ ## try a few more locations
+ lib_paths = [
+ # The Framework build from Kyng Chaos:
+ "/Library/Frameworks/GEOS.framework/Versions/Current/GEOS",
+ # macports
+ '/opt/local/lib/libgeos_c.dylib',
+ ]
+ for path in lib_paths:
+ if os.path.exists(path):
+ lib = path
+ break
+ if lib is None:
+ raise ImportError, "Could not find geos_c library"
+ lgeos = PyDLL(lib)
+ free = CDLL(find_library('libc')).free
+ free.argtypes=[c_void_p]
+ free.restype=None
+else:
+ # Try the major versioned name first, falling back on the unversioned name.
+ try:
+ lgeos = PyDLL('libgeos_c.so.1')
+ except (OSError, ImportError):
+ lgeos = PyDLL('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)
+
+
+class allocated_c_char_p(c_char_p):
+ pass
+
+# Exceptions
+
+class ReadingError(Exception):
+ pass
+
+class DimensionError(Exception):
+ pass
+
+class TopologicalError(Exception):
+ pass
+
+class PredicateError(Exception):
+ pass
+
+
+# GEOS error handlers, which currently do nothing.
+
+def error_handler(fmt, list):
+ pass
+error_h = CFUNCTYPE(None, c_char_p, c_char_p)(error_handler)
+
+def notice_handler(fmt, list):
+ pass
+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()
+
+atexit.register(cleanup)
+
+lgeos.initGEOS(notice_h, error_h)
diff --git a/shapely/iterops.py b/shapely/iterops.py
new file mode 100644
index 0000000..e556632
--- /dev/null
+++ b/shapely/iterops.py
@@ -0,0 +1,52 @@
+"""
+Iterative forms of operations.
+"""
+
+from ctypes import c_char_p, c_size_t
+from shapely.geos import lgeos, free
+
+
+def geos_from_geometry(geom):
+ data = geom.to_wkb()
+ return lgeos.GEOSGeomFromWKB_buf(
+ c_char_p(data),
+ c_size_t(len(data))
+ )
+
+
+class IterOp(object):
+
+ """A generating non-data descriptor.
+ """
+
+ def __init__(self, fn):
+ self.fn = fn
+
+ def __call__(self, context, iterator, value=True):
+ if context._geom is None:
+ raise ValueError, "Null geometry supports no operations"
+ for item in iterator:
+ try:
+ this_geom, ob = item
+ except TypeError:
+ this_geom = item
+ ob = this_geom
+ if not this_geom._geom:
+ 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)
+ elif bool(retval) == value:
+ yield ob
+
+
+# utilities
+disjoint = IterOp(lgeos.GEOSDisjoint)
+touches = IterOp(lgeos.GEOSTouches)
+intersects = IterOp(lgeos.GEOSIntersects)
+crosses = IterOp(lgeos.GEOSCrosses)
+within = IterOp(lgeos.GEOSWithin)
+contains = IterOp(lgeos.GEOSContains)
+overlaps = IterOp(lgeos.GEOSOverlaps)
+equals = IterOp(lgeos.GEOSEquals)
diff --git a/shapely/ops.py b/shapely/ops.py
new file mode 100644
index 0000000..3a2966b
--- /dev/null
+++ b/shapely/ops.py
@@ -0,0 +1,29 @@
+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
+
+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
new file mode 100644
index 0000000..ed12158
--- /dev/null
+++ b/shapely/predicates.py
@@ -0,0 +1,65 @@
+"""
+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"
+
diff --git a/shapely/topology.py b/shapely/topology.py
new file mode 100644
index 0000000..97e94c1
--- /dev/null
+++ b/shapely/topology.py
@@ -0,0 +1,74 @@
+"""
+Support for GEOS topological operations.
+"""
+
+from shapely.geos import TopologicalError
+
+
+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 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"
+
diff --git a/shapely/wkb.py b/shapely/wkb.py
new file mode 100644
index 0000000..b6315c9
--- /dev/null
+++ b/shapely/wkb.py
@@ -0,0 +1,42 @@
+"""
+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 c_void_p, c_size_t
+
+from shapely.geos import lgeos, free, ReadingError
+from shapely.geometry.base import geom_factory
+
+
+# Pickle-like convenience functions
+
+def loads(data):
+ """Load a geometry from a WKB string."""
+ 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)
+
+def load(fp):
+ """Load a geometry from an open file."""
+ data = fp.read()
+ return loads(data)
+
+def dumps(ob):
+ """Dump a WKB representation of a geometry to a byte string."""
+ 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(ob._geom), byref(size))
+
+def dump(ob, fp):
+ """Dump a geometry to an open file."""
+ fp.write(dumps(ob))
+
diff --git a/shapely/wkt.py b/shapely/wkt.py
new file mode 100644
index 0000000..5c016a7
--- /dev/null
+++ b/shapely/wkt.py
@@ -0,0 +1,40 @@
+"""
+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 shapely.geos import lgeos, free, allocated_c_char_p, ReadingError
+from shapely.geometry.base import geom_factory
+
+
+# Pickle-like convenience functions
+
+def loads(data):
+ """Load a geometry from a WKT string."""
+ geom = lgeos.GEOSGeomFromWKT(c_char_p(data))
+ if not geom:
+ raise ReadingError, \
+ "Could not create geometry because of errors while reading input."
+ return geom_factory(geom)
+
+def load(fp):
+ """Load a geometry from an open file."""
+ data = fp.read()
+ return loads(data)
+
+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)
+
+def dump(ob, fp):
+ """Dump a geometry to an open file."""
+ fp.write(dumps(ob))
+
diff --git a/tests/Array.txt b/tests/Array.txt
new file mode 100644
index 0000000..c30b687
--- /dev/null
+++ b/tests/Array.txt
@@ -0,0 +1,86 @@
+Test implementation of array interface
+======================================
+
+ >>> from shapely.geometry import Point
+
+ >>> p = Point(0.0, 0.0, 1.0)
+ >>> coords = p.coords[0]
+ >>> coords
+ (0.0, 0.0, 1.0)
+ >>> p.ctypes #doctest: +ELLIPSIS
+ <shapely.geometry.point.c_double_Array_3 object at ...>
+
+ Convert to Numpy array, passing through Python sequence
+
+ >>> from numpy import asarray
+ >>> a = asarray(coords)
+ >>> a.ndim
+ 1
+ >>> a.size
+ 3
+ >>> a.shape
+ (3,)
+
+ Convert to Numpy array, passing through a ctypes array
+
+ >>> b = asarray(p)
+ >>> b.size
+ 3
+ >>> b.shape
+ (3,)
+ >>> b[0]
+ 0.0
+ >>> b[1]
+ 0.0
+ >>> b[2]
+ 1.0
+
+ Make a point from a Numpy array
+
+ >>> a = asarray([1.0, 1.0, 0.0])
+ >>> p = Point(*list(a))
+ >>> p.wkt
+ 'POINT (1.0000000000000000 1.0000000000000000)'
+
+LineString
+----------
+
+ >>> a = asarray([[0.0, 0.0], [2.0, 2.0], [1.0, 1.0]])
+ >>> from shapely.geometry import LineString
+ >>> line = LineString(a)
+ >>> line.wkt
+ 'LINESTRING (0.0000000000000000 0.0000000000000000, 2.0000000000000000 2.0000000000000000, 1.0000000000000000 1.0000000000000000)'
+
+ >>> data = line.ctypes
+ >>> data[0]
+ 0.0
+ >>> data[5]
+ 1.0
+
+ >>> b = asarray(line)
+ >>> b.shape
+ (3, 2)
+ >>> b
+ array([[ 0., 0.],
+ [ 2., 2.],
+ [ 1., 1.]])
+
+Polygon
+-------
+
+ >>> a = asarray(((0., 0.), (0., 1.), (1., 1.), (1., 0.), (0., 0.)))
+ >>> from shapely.geometry import Polygon
+ >>> polygon = Polygon(a)
+ >>> polygon.wkt
+ 'POLYGON ((0.0000000000000000 0.0000000000000000, 0.0000000000000000 1.0000000000000000, 1.0000000000000000 1.0000000000000000, 1.0000000000000000 0.0000000000000000, 0.0000000000000000 0.0000000000000000))'
+
+ >>> b = asarray(polygon.exterior)
+ >>> b.shape
+ (5, 2)
+ >>> b
+ array([[ 0., 0.],
+ [ 0., 1.],
+ [ 1., 1.],
+ [ 1., 0.],
+ [ 0., 0.]])
+
diff --git a/tests/GeoInterface.txt b/tests/GeoInterface.txt
new file mode 100644
index 0000000..5c94030
--- /dev/null
+++ b/tests/GeoInterface.txt
@@ -0,0 +1,69 @@
+Test implementation of geo interface
+====================================
+
+ >>> from shapely.geometry import asShape
+
+ Adapt a dictionary
+
+ >>> d = {"type": "Point", "coordinates": (0.0, 0.0)}
+ >>> shape = asShape(d)
+ >>> shape.geom_type
+ 'Point'
+ >>> tuple(shape.coords)
+ ((0.0, 0.0),)
+
+ Adapt an object that implements the geo protocol
+
+ >>> class GeoThing(object):
+ ... def __init__(self, d):
+ ... self.__geo_interface__ = d
+
+ >>> shape = None
+ >>> thing = GeoThing({"type": "Point", "coordinates": (0.0, 0.0)})
+ >>> shape = asShape(thing)
+ >>> shape.geom_type
+ 'Point'
+ >>> tuple(shape.coords)
+ ((0.0, 0.0),)
+
+ Check line string
+
+ >>> shape = asShape({'type': 'LineString', 'coordinates': ((-1.0, -1.0), (1.0, 1.0))})
+ >>> shape # doctest: +ELLIPSIS
+ <shapely.geometry.linestring.LineStringAdapter object at ...>
+ >>> tuple(shape.coords)
+ ((-1.0, -1.0), (1.0, 1.0))
+
+ polygon
+
+ >>> shape = asShape({'type': 'Polygon', 'coordinates': (((0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (2.0, -1.0), (0.0, 0.0)), ((0.10000000000000001, 0.10000000000000001), (0.10000000000000001, 0.20000000000000001), (0.20000000000000001, 0.20000000000000001), (0.20000000000000001, 0.10000000000000001), (0.10000000000000001, 0.10000000000000001)))})
+ >>> shape # doctest: +ELLIPSIS
+ <shapely.geometry.polygon.PolygonAdapter object at ...>
+ >>> tuple(shape.exterior.coords)
+ ((0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (2.0, -1.0), (0.0, 0.0))
+ >>> len(shape.interiors)
+ 1
+
+ multi point
+ >>> shape = asShape({'type': 'MultiPoint', 'coordinates': ((1.0, 2.0), (3.0, 4.0))})
+ >>> shape # doctest: +ELLIPSIS
+ <shapely.geometry.multipoint.MultiPointAdapter object at ...>
+ >>> len(shape.geoms)
+ 2
+
+ multi line string
+
+ >>> shape = asShape({'type': 'MultiLineString', 'coordinates': (((0.0, 0.0), (1.0, 2.0)),)})
+ >>> shape # doctest: +ELLIPSIS
+ <shapely.geometry.multilinestring.MultiLineStringAdapter object at ...>
+ >>> len(shape.geoms)
+ 1
+
+ multi polygon
+
+ >>> shape = asShape({'type': 'MultiPolygon', 'coordinates': [(((0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (1.0, 0.0), (0.0, 0.0)), ((0.10000000000000001, 0.10000000000000001), (0.10000000000000001, 0.20000000000000001), (0.20000000000000001, 0.20000000000000001), (0.20000000000000001, 0.10000000000000001), (0.10000000000000001, 0.10000000000000001)))]})
+ >>> shape # doctest: +ELLIPSIS
+ <shapely.geometry.multipolygon.MultiPolygonAdapter object at ...>
+ >>> len(shape.geoms)
+ 1
+
diff --git a/tests/IterOps.txt b/tests/IterOps.txt
new file mode 100644
index 0000000..c71f9a3
--- /dev/null
+++ b/tests/IterOps.txt
@@ -0,0 +1,31 @@
+Test operator iterations
+========================
+
+ >>> 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 of the points contained by the polygon
+ >>> list(iterops.contains(polygon, points, True)) # doctest: +ELLIPSIS
+ [<shapely.geometry.point.Point object at ...>]
+
+ 'True' is the default value
+ >>> list(iterops.contains(polygon, points)) # doctest: +ELLIPSIS
+ [<shapely.geometry.point.Point object at ...>]
+
+ Test a false value
+ >>> list(iterops.contains(polygon, points, False)) # doctest: +ELLIPSIS
+ [<shapely.geometry.point.Point object at ...>]
+
+ If the provided iterator yields tuples, the second value will be yielded
+ >>> list(iterops.contains(polygon, [(p, p.wkt) for p in points], False))
+ ['POINT (2.0000000000000000 2.0000000000000000)']
+
+ Just to demonstrate that the important thing is that the second parameter
+ is an iterator:
+ >>> list(iterops.contains(polygon, iter((p, p.wkt) for p in points)))
+ ['POINT (0.5000000000000000 0.5000000000000000)']
diff --git a/tests/LineString.txt b/tests/LineString.txt
new file mode 100644
index 0000000..7e3cd7f
--- /dev/null
+++ b/tests/LineString.txt
@@ -0,0 +1,130 @@
+LineStrings
+===========
+
+Initialization
+--------------
+
+ >>> from shapely.geometry import LineString
+
+Construct from a numpy array
+
+ >>> from numpy import array
+ >>> line = LineString(array([[0.0, 0.0], [1.0, 2.0]]))
+ >>> len(line.coords)
+ 2
+ >>> line.wkt
+ 'LINESTRING (0.0000000000000000 0.0000000000000000, 1.0000000000000000 2.0000000000000000)'
+
+From coordinate tuples
+
+ >>> line = LineString(((1.0, 2.0), (3.0, 4.0)))
+ >>> len(line.coords)
+ 2
+ >>> line.wkt
+ 'LINESTRING (1.0000000000000000 2.0000000000000000, 3.0000000000000000 4.0000000000000000)'
+
+
+Numpy Support
+-------------
+
+ >>> from numpy import asarray
+ >>> la = asarray(line)
+ >>> la
+ array([[ 1., 2.],
+ [ 3., 4.]])
+ >>> la[0]
+ array([ 1., 2.])
+
+Coordinate sequences can be adapted as well
+
+ >>> asarray(line.coords)
+ array([[ 1., 2.],
+ [ 3., 4.]])
+
+Bounds
+------
+
+ >>> line.bounds
+ (1.0, 2.0, 3.0, 4.0)
+
+Coordinate Access
+-----------------
+
+ >>> tuple(line.coords)
+ ((1.0, 2.0), (3.0, 4.0))
+
+ >>> line.coords[0]
+ (1.0, 2.0)
+ >>> line.coords[1]
+ (3.0, 4.0)
+ >>> line.coords[2]
+ Traceback (most recent call last):
+ ...
+ IndexError: index out of range
+
+Geo interface
+-------------
+
+ >>> line.__geo_interface__
+ {'type': 'LineString', 'coordinates': ((1.0, 2.0), (3.0, 4.0))}
+
+
+Coordinate modification
+-----------------------
+
+ >>> line.coords = ((-1.0, -1.0), (1.0, 1.0))
+ >>> line.__geo_interface__
+ {'type': 'LineString', 'coordinates': ((-1.0, -1.0), (1.0, 1.0))}
+
+
+Adapter
+-------
+
+ >>> from shapely.geometry import asLineString
+
+ Adapt a Numpy array to a line string
+
+ >>> a = array([[1.0, 2.0], [3.0, 4.0]])
+ >>> la = asLineString(a)
+ >>> la.context
+ array([[ 1., 2.],
+ [ 3., 4.]])
+
+ >>> la.wkt
+ 'LINESTRING (1.0000000000000000 2.0000000000000000, 3.0000000000000000 4.0000000000000000)'
+
+Now, the inverse
+
+ >>> la.__array_interface__ == la.context.__array_interface__
+ True
+ >>> pas = asarray(la)
+ >>> pas
+ array([[ 1., 2.],
+ [ 3., 4.]])
+
+Adapt a coordinate list to a line string
+
+ >>> coords = [[5.0, 6.0], [7.0, 8.0]]
+ >>> la = asLineString(coords)
+ >>> la.wkt
+ 'LINESTRING (5.0000000000000000 6.0000000000000000, 7.0000000000000000 8.0000000000000000)'
+
+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
+
+ >>> l_null.length # doctest: +ELLIPSIS
+ Traceback (most recent call last):
+ ...
+ ValueError: Null geometry supports no operations
+
+Check that we can set coordinates of a null geometry
+
+ >>> l_null.coords = [(0, 0), (1, 1)]
+ >>> print l_null.length # doctest: +ELLIPSIS
+ 1.414...
+
diff --git a/tests/MultiLineString.txt b/tests/MultiLineString.txt
new file mode 100644
index 0000000..3c41eee
--- /dev/null
+++ b/tests/MultiLineString.txt
@@ -0,0 +1,64 @@
+MultiLineStrings
+================
+
+Initialization
+--------------
+
+ >>> from shapely.geometry import MultiLineString
+
+From coordinate tuples
+
+ >>> geom = MultiLineString( (((1.0, 2.0), (3.0, 4.0)),) )
+ >>> geom # doctest: +ELLIPSIS
+ <shapely.geometry.multilinestring.MultiLineString object at ...>
+ >>> len(geom.geoms)
+ 1
+ >>> geom.wkt
+ 'MULTILINESTRING ((1.0000000000000000 2.0000000000000000, 3.0000000000000000 4.0000000000000000))'
+
+Construct from a numpy array
+
+ >>> from numpy import array
+ >>> geom = MultiLineString( [array(((0.0, 0.0), (1.0, 2.0)))] )
+ >>> geom # doctest: +ELLIPSIS
+ <shapely.geometry.multilinestring.MultiLineString object at ...>
+ >>> len(geom.geoms)
+ 1
+ >>> geom.wkt
+ 'MULTILINESTRING ((0.0000000000000000 0.0000000000000000, 1.0000000000000000 2.0000000000000000))'
+
+
+Sub-geometry Access
+-------------------
+
+ >>> geom.geoms[0] # doctest: +ELLIPSIS
+ <shapely.geometry.linestring.LineString object at ...>
+ >>> geom.geoms[0].wkt
+ 'LINESTRING (0.0000000000000000 0.0000000000000000, 1.0000000000000000 2.0000000000000000)'
+ >>> geom.geoms[1]
+ Traceback (most recent call last):
+ ...
+ IndexError: index out of range
+
+Geo interface
+-------------
+
+ >>> geom.__geo_interface__
+ {'type': 'MultiLineString', 'coordinates': (((0.0, 0.0), (1.0, 2.0)),)}
+
+Adapter
+-------
+
+ >>> from shapely.geometry import asMultiLineString
+
+ Adapt a sequence of Numpy arrays to a multilinestring
+
+ >>> a = [ array(((1.0, 2.0), (3.0, 4.0))) ]
+ >>> geoma = asMultiLineString(a)
+ >>> geoma.context
+ [array([[ 1., 2.],
+ [ 3., 4.]])]
+
+ >>> geoma.wkt
+ 'MULTILINESTRING ((1.0000000000000000 2.0000000000000000, 3.0000000000000000 4.0000000000000000))'
+
diff --git a/tests/MultiPoint.txt b/tests/MultiPoint.txt
new file mode 100644
index 0000000..bd0c5d5
--- /dev/null
+++ b/tests/MultiPoint.txt
@@ -0,0 +1,85 @@
+MultiPoints
+===========
+
+Initialization
+--------------
+
+ >>> from shapely.geometry import MultiPoint
+
+Construct from a numpy array
+
+ >>> from numpy import array
+ >>> geom = MultiPoint(array([[0.0, 0.0], [1.0, 2.0]]))
+ >>> geom # doctest: +ELLIPSIS
+ <shapely.geometry.multipoint.MultiPoint object at ...>
+ >>> len(geom.geoms)
+ 2
+ >>> geom.wkt
+ 'MULTIPOINT (0.0000000000000000 0.0000000000000000, 1.0000000000000000 2.0000000000000000)'
+
+From coordinate tuples
+
+ >>> geom = MultiPoint(((1.0, 2.0), (3.0, 4.0)))
+ >>> len(geom.geoms)
+ 2
+ >>> geom.wkt
+ 'MULTIPOINT (1.0000000000000000 2.0000000000000000, 3.0000000000000000 4.0000000000000000)'
+
+
+Sub-geometry Access
+-------------------
+
+ >>> geom.geoms[0] # doctest: +ELLIPSIS
+ <shapely.geometry.point.Point object at ...>
+ >>> geom.geoms[0].x
+ 1.0
+ >>> geom.geoms[0].y
+ 2.0
+ >>> geom.geoms[2]
+ Traceback (most recent call last):
+ ...
+ IndexError: index out of range
+
+Geo interface
+-------------
+
+ >>> geom.__geo_interface__
+ {'type': 'MultiPoint', 'coordinates': ((1.0, 2.0), (3.0, 4.0))}
+
+ >>> array(geom)
+ array([[ 1., 2.],
+ [ 3., 4.]])
+
+Adapter
+-------
+
+ >>> from shapely.geometry import asMultiPoint
+
+ Adapt a Numpy array to a multipoint
+
+ >>> a = array([[1.0, 2.0], [3.0, 4.0]])
+ >>> geoma = asMultiPoint(a)
+ >>> geoma.context
+ array([[ 1., 2.],
+ [ 3., 4.]])
+
+ >>> geoma.wkt
+ 'MULTIPOINT (1.0000000000000000 2.0000000000000000, 3.0000000000000000 4.0000000000000000)'
+
+ Now, the inverse
+
+ >>> geoma.__array_interface__ == geoma.context.__array_interface__
+ True
+ >>> from numpy import asarray
+ >>> pas = asarray(geoma)
+ >>> pas
+ array([[ 1., 2.],
+ [ 3., 4.]])
+
+ Adapt a coordinate list to a line string
+
+ >>> coords = [[5.0, 6.0], [7.0, 8.0]]
+ >>> geoma = asMultiPoint(coords)
+ >>> geoma.wkt
+ 'MULTIPOINT (5.0000000000000000 6.0000000000000000, 7.0000000000000000 8.0000000000000000)'
+
diff --git a/tests/MultiPolygon.txt b/tests/MultiPolygon.txt
new file mode 100644
index 0000000..6edecaf
--- /dev/null
+++ b/tests/MultiPolygon.txt
@@ -0,0 +1,55 @@
+MultiPolygons
+=============
+
+Initialization
+--------------
+
+ >>> from shapely.geometry import MultiPolygon
+
+From coordinate tuples
+
+ >>> 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))])
+ ... ] )
+ >>> geom # doctest: +ELLIPSIS
+ <shapely.geometry.multipolygon.MultiPolygon object at ...>
+ >>> len(geom.geoms)
+ 1
+ >>> 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)))'
+
+
+Sub-geometry Access
+-------------------
+
+ >>> geom.geoms[0] # doctest: +ELLIPSIS
+ <shapely.geometry.polygon.Polygon object at ...>
+ >>> geom.geoms[0].wkt
+ 'POLYGON ((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))'
+ >>> geom.geoms[1]
+ Traceback (most recent call last):
+ ...
+ IndexError: index out of range
+
+Geo interface
+-------------
+
+ >>> geom.__geo_interface__
+ {'type': 'MultiPolygon', 'coordinates': [[((0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (1.0, 0.0), (0.0, 0.0)), ((0.10000000000000001, 0.10000000000000001), (0.10000000000000001, 0.20000000000000001), (0.20000000000000001, 0.20000000000000001), (0.20000000000000001, 0.10000000000000001), (0.10000000000000001, 0.10000000000000001))]]}
+
+Adapter
+-------
+
+ >>> from shapely.geometry import asMultiPolygon
+ >>> coords = ((0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (1.0, 0.0))
+ >>> holes_coords = [((0.1,0.1), (0.1,0.2), (0.2,0.2), (0.2,0.1))]
+ >>> mpa = asMultiPolygon([(coords, holes_coords)])
+ >>> len(mpa.geoms)
+ 1
+ >>> len(mpa.geoms[0].exterior.coords)
+ 5
+ >>> len(mpa.geoms[0].interiors)
+ 1
+ >>> len(mpa.geoms[0].interiors[0].coords)
+ 5
+
diff --git a/tests/Operations.txt b/tests/Operations.txt
new file mode 100644
index 0000000..1bfd325
--- /dev/null
+++ b/tests/Operations.txt
@@ -0,0 +1,72 @@
+Test GEOS operations
+====================
+
+ >>> from shapely.geometry import Point
+ >>> point = Point(0.0, 0.0)
+
+General geometry
+----------------
+
+ >>> point.area
+ 0.0
+
+ >>> point.length
+ 0.0
+
+ >>> point.distance(Point(-1.0, -1.0))
+ 1.4142135623730951
+
+Topology operations
+-------------------
+
+ Envelope
+
+ >>> point.envelope #doctest: +ELLIPSIS
+ <shapely.geometry.point.Point object at ...>
+
+ Intersection
+
+ >>> point.intersection(Point(-1,-1)) #doctest: +ELLIPSIS
+ <shapely.geometry.collection.GeometryCollection object at ...>
+
+ Buffer
+
+ >>> point.buffer(10.0) #doctest: +ELLIPSIS
+ <shapely.geometry.polygon.Polygon object at ...>
+
+ >>> point.buffer(10.0, 32) #doctest: +ELLIPSIS
+ <shapely.geometry.polygon.Polygon object at ...>
+
+ Convex Hull
+
+ >>> point.convex_hull #doctest: +ELLIPSIS
+ <shapely.geometry.point.Point object at ...>
+
+ Differences
+
+ >>> point.difference(Point(-1, 1)) #doctest: +ELLIPSIS
+ <shapely.geometry.point.Point object at ...>
+
+ >>> point.symmetric_difference(Point(-1, 1)) #doctest: +ELLIPSIS
+ <shapely.geometry.multipoint.MultiPoint object at ...>
+
+ Boundary
+
+ >>> point.boundary #doctest: +ELLIPSIS
+ <shapely.geometry.collection.GeometryCollection object at ...>
+
+ Union
+
+ >>> point.union(Point(-1, 1)) #doctest: +ELLIPSIS
+ <shapely.geometry.multipoint.MultiPoint object at ...>
+
+ Point on Surface (TODO?)
+
+ >>> point.centroid #doctest: +ELLIPSIS
+ <shapely.geometry.point.Point object at ...>
+
+ Relate
+
+ >>> point.relate(Point(-1, -1)) #doctest: +ELLIPSIS
+ 'FF0FFF0F2'
+
diff --git a/tests/Persist.txt b/tests/Persist.txt
new file mode 100644
index 0000000..be0b56f
--- /dev/null
+++ b/tests/Persist.txt
@@ -0,0 +1,35 @@
+Persistence tests
+=================
+
+Pickle
+------
+
+ >>> from shapely.geometry import Point
+ >>> import pickle
+ >>> p = Point(0.0, 0.0)
+ >>> data = pickle.dumps(p)
+ >>> q = pickle.loads(data)
+ >>> q.equals(p)
+ True
+
+WKB
+---
+
+ >>> from shapely import wkb
+ >>> bytes = wkb.dumps(p)
+ >>> pb = wkb.loads(bytes)
+ >>> pb.equals(p)
+ True
+
+WKT
+---
+
+ >>> from shapely import wkt
+ >>> text = wkt.dumps(p)
+ >>> text
+ 'POINT (0.0000000000000000 0.0000000000000000)'
+ >>> pt = wkt.loads(text)
+ >>> pt.equals(p)
+ True
+
+
diff --git a/tests/Point.txt b/tests/Point.txt
new file mode 100644
index 0000000..0a4596b
--- /dev/null
+++ b/tests/Point.txt
@@ -0,0 +1,148 @@
+
+ >>> from shapely.geometry import Point
+
+Test 2D points
+--------------
+
+ >>> p = Point(0.0, 0.0)
+ >>> str(p)
+ 'POINT (0.0000000000000000 0.0000000000000000)'
+ >>> p.wkt
+ 'POINT (0.0000000000000000 0.0000000000000000)'
+
+Check 3D
+--------
+
+ >>> p = Point(0.0, 0.0, 1.0)
+ >>> p.z
+ 1.0
+ >>> str(p)
+ 'POINT (0.0000000000000000 0.0000000000000000)'
+ >>> p.wkt
+ 'POINT (0.0000000000000000 0.0000000000000000)'
+
+Construct from a numpy array
+----------------------------
+
+ >>> from numpy import array
+ >>> p = Point(array([1.0, 2.0]))
+ >>> p.wkt
+ 'POINT (1.0000000000000000 2.0000000000000000)'
+
+From coordinate sequence
+------------------------
+
+ >>> p = Point((3.0, 4.0))
+ >>> p.wkt
+ 'POINT (3.0000000000000000 4.0000000000000000)'
+
+Coordinate access
+-----------------
+
+ >>> p.x
+ 3.0
+ >>> p.y
+ 4.0
+
+ >>> tuple(p.coords)
+ ((3.0, 4.0),)
+
+ >>> p.coords[0]
+ (3.0, 4.0)
+ >>> p.coords[1] # doctest: +ELLIPSIS
+ Traceback (most recent call last):
+ ...
+ IndexError: index out of range
+
+Bounds
+------
+
+ >>> p.bounds
+ (3.0, 4.0, 3.0, 4.0)
+
+Geo interface
+-------------
+
+ >>> p.__geo_interface__
+ {'type': 'Point', 'coordinates': (3.0, 4.0)}
+
+
+Modify coordinates
+------------------
+
+ >>> p.coords = (0.0, 0.0)
+ >>> p.__geo_interface__
+ {'type': 'Point', 'coordinates': (0.0, 0.0)}
+
+Alternate method
+
+ >>> p.coords = ((0.0, 0.0),)
+ >>> p.__geo_interface__
+ {'type': 'Point', 'coordinates': (0.0, 0.0)}
+
+Adapter
+-------
+
+ >>> from shapely.geometry import asPoint
+
+ Adapt a Numpy array to a point
+
+ >>> a = array([1.0, 2.0])
+ >>> pa = asPoint(a)
+ >>> pa.context
+ array([ 1., 2.])
+ >>> pa.wkt
+ 'POINT (1.0000000000000000 2.0000000000000000)'
+
+ Now, the inverse
+
+ >>> pa.__array_interface__ == pa.context.__array_interface__
+ True
+ >>> from numpy import asarray
+ >>> pas = asarray(pa)
+ >>> pas
+ array([ 1., 2.])
+
+ Adapt a coordinate list to a point
+
+ >>> coords = [3.0, 4.0]
+ >>> pa = asPoint(coords)
+ >>> pa.wkt
+ 'POINT (3.0000000000000000 4.0000000000000000)'
+ >>> pa.distance(p)
+ 5.0
+
+ Move the coordinates and watch the distance change
+
+ >>> coords[0] = 1.0
+ >>> pa.wkt
+ 'POINT (1.0000000000000000 4.0000000000000000)'
+ >>> pa.distance(p)
+ 4.1231056256176606
+
+ Now, the inverse
+
+ >>> ai = pa.__array_interface__
+ >>> pas = asarray(pa)
+ >>> pas
+ array([ 1., 4.])
+
+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
+
+ >>> p_null.area # doctest: +ELLIPSIS
+ Traceback (most recent call last):
+ ...
+ ValueError: Null geometry supports no operations
+
+Check that we can set coordinates of a null geometry
+
+ >>> p_null.coords = (0, 0)
+ >>> p_null.wkt
+ 'POINT (0.0000000000000000 0.0000000000000000)'
+
diff --git a/tests/Polygon.txt b/tests/Polygon.txt
new file mode 100644
index 0000000..1b93d69
--- /dev/null
+++ b/tests/Polygon.txt
@@ -0,0 +1,147 @@
+Polygons and Linear Rings
+=========================
+
+Initialization
+--------------
+
+ Linear rings won't usually be created by users, but by polygons
+
+ >>> coords = ((0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (1.0, 0.0))
+ >>> from shapely.geometry.polygon import LinearRing
+ >>> ring = LinearRing(coords)
+ >>> len(ring.coords)
+ 5
+ >>> ring.coords[0] == ring.coords[4]
+ True
+ >>> ring.coords[0] == ring.coords[-1]
+ True
+ >>> ring.is_ring
+ True
+
+ Coordinate modification
+ -----------------------
+
+ >>> ring.coords = ((0.0, 0.0), (0.0, 2.0), (2.0, 2.0), (2.0, 0.0))
+ >>> ring.__geo_interface__
+ {'type': 'LinearRing', 'coordinates': ((0.0, 0.0), (0.0, 2.0), (2.0, 2.0), (2.0, 0.0), (0.0, 0.0))}
+
+ Test ring adapter
+
+ >>> coords = [[0.0, 0.0], [0.0, 1.0], [1.0, 1.0], [1.0, 0.0]]
+ >>> from shapely.geometry.polygon import asLinearRing
+ >>> ra = asLinearRing(coords)
+ >>> ra.wkt
+ 'LINEARRING (0.0000000000000000 0.0000000000000000, 0.0000000000000000 1.0000000000000000, 1.0000000000000000 1.0000000000000000, 1.0000000000000000 0.0000000000000000, 0.0000000000000000 0.0000000000000000)'
+ >>> coords[3] = [2.0, -1.0]
+ >>> ra.wkt
+ 'LINEARRING (0.0000000000000000 0.0000000000000000, 0.0000000000000000 1.0000000000000000, 1.0000000000000000 1.0000000000000000, 2.0000000000000000 -1.0000000000000000, 0.0000000000000000 0.0000000000000000)'
+
+ Construct a polygon, exterior ring only
+
+ >>> from shapely.geometry import Polygon
+ >>> polygon = Polygon(coords)
+ >>> len(polygon.exterior.coords)
+ 5
+
+Ring Access
+-----------
+
+ >>> polygon.exterior # doctest: +ELLIPSIS
+ <shapely.geometry.polygon.LinearRing object at ...>
+ >>> ring = polygon.exterior
+ >>> len(ring.coords)
+ 5
+ >>> ring.coords[0] == ring.coords[4] == (0., 0.)
+ True
+ >>> ring.is_ring
+ True
+ >>> len(polygon.interiors)
+ 0
+
+ Create a new polygon from WKB
+
+ >>> data = polygon.wkb
+ >>> polygon = None
+ >>> ring = None
+ >>> from shapely.wkb import loads
+ >>> polygon = loads(data)
+ >>> ring = polygon.exterior
+ >>> len(ring.coords)
+ 5
+ >>> ring.coords[0] == ring.coords[4] == (0., 0.)
+ True
+ >>> ring.is_ring
+ True
+ >>> polygon = None
+
+Interior rings (holes)
+----------------------
+
+ >>> polygon = Polygon(coords, [((0.1,0.1), (0.1,0.2), (0.2,0.2), (0.2,0.1))])
+ >>> len(polygon.exterior.coords)
+ 5
+ >>> len(polygon.interiors[0].coords)
+ 5
+ >>> polygon.interiors[1] # doctest: +ELLIPSIS
+ Traceback (most recent call last):
+ ...
+ IndexError: index out of range
+
+Coordinate getters and setters raise exceptions
+
+ >>> polygon._get_coords()
+ Traceback (most recent call last):
+ ...
+ NotImplementedError: Component rings have coordinate sequences, but the polygon does not
+ >>> polygon.coords
+ Traceback (most recent call last):
+ ...
+ NotImplementedError: Component rings have coordinate sequences, but the polygon does not
+
+
+Geo interface
+-------------
+
+ >>> polygon.__geo_interface__
+ {'type': 'Polygon', 'coordinates': (((0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (2.0, -1.0), (0.0, 0.0)), ((0.10000000000000001, 0.10000000000000001), (0.10000000000000001, 0.20000000000000001), (0.20000000000000001, 0.20000000000000001), (0.20000000000000001, 0.10000000000000001), (0.10000000000000001, 0.10000000000000001)))}
+
+
+Adapter
+-------
+
+ >>> hole_coords = [((0.1,0.1), (0.1,0.2), (0.2,0.2), (0.2,0.1))]
+ >>> from shapely.geometry import asPolygon
+ >>> pa = asPolygon(coords, hole_coords)
+ >>> len(pa.exterior.coords)
+ 5
+ >>> len(pa.interiors)
+ 1
+ >>> len(pa.interiors[0].coords)
+ 5
+
+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
+
+ >>> r_null.length # doctest: +ELLIPSIS
+ Traceback (most recent call last):
+ ...
+ ValueError: Null geometry supports no operations
+
+Check that we can set coordinates of a null geometry
+
+ >>> r_null.coords = [(0, 0), (1, 1), (1, 0)]
+ >>> print r_null.length # doctest: +ELLIPSIS
+ 3.414...
+
+Error handling
+--------------
+
+ >>> p = Polygon([[1,2], [2, 3]])
+ Traceback (most recent call last):
+ ...
+ ValueError: A LinearRing must have at least 3 coordinate tuples
diff --git a/tests/Predicates.txt b/tests/Predicates.txt
new file mode 100644
index 0000000..beef7b1
--- /dev/null
+++ b/tests/Predicates.txt
@@ -0,0 +1,51 @@
+Test GEOS predicates
+====================
+
+ >>> from shapely.geometry import Point
+ >>> point = Point(0.0, 0.0)
+
+Binary Predicates
+-----------------
+
+ >>> point.disjoint(Point(-1.0, -1.0))
+ True
+
+ >>> point.touches(Point(-1.0, -1.0))
+ False
+
+ >>> point.crosses(Point(-1.0, -1.0))
+ False
+
+ >>> point.within(Point(-1.0, -1.0))
+ False
+
+ >>> point.contains(Point(-1.0, -1.0))
+ False
+
+ >>> point.overlaps(Point(-1.0, -1.0))
+ False
+
+ >>> point.equals(Point(-1.0, -1.0))
+ False
+
+ >>> point.equals(Point(0.0, 0.0))
+ True
+
+Unary Predicates
+----------------
+
+ >>> point.is_empty
+ False
+
+ >>> point.is_valid
+ True
+
+ >>> point.is_simple
+ True
+
+ >>> point.is_ring
+ False
+
+ >>> point.has_z
+ False
+
diff --git a/tests/__init__.py b/tests/__init__.py
new file mode 100644
index 0000000..4e9a837
--- /dev/null
+++ b/tests/__init__.py
@@ -0,0 +1,2 @@
+# package
+from test_doctests import test_suite
diff --git a/tests/attribute-chains.txt b/tests/attribute-chains.txt
new file mode 100644
index 0000000..a853587
--- /dev/null
+++ b/tests/attribute-chains.txt
@@ -0,0 +1,32 @@
+Attribute Chaining
+==================
+
+See also ticket #151.
+
+ >>> from shapely.geometry import Polygon
+ >>> p = Polygon(((0.0, 0.0), (0.0, 1.0), (-1.0, 1.0), (-1.0, 0.0)))
+ >>> list(p.boundary.coords)
+ [(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), ...
+
+Test chained access to interiors
+
+ >>> p = Polygon(
+ ... ((0.0, 0.0), (0.0, 1.0), (-1.0, 1.0), (-1.0, 0.0)),
+ ... [((-0.25, 0.25), (-0.25, 0.75), (-0.75, 0.75), (-0.75, 0.25))]
+ ... )
+ >>> p.area
+ 0.75
+ >>> 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), ...
+
+Test multiple operators, boundary of a buffer
+
+ >>> print list(p.buffer(1).boundary.coords)
+ [(0.0, -1.0), (-1.0, -1.0), ...
+
diff --git a/tests/binascii_hex.txt b/tests/binascii_hex.txt
new file mode 100644
index 0000000..c502a4f
--- /dev/null
+++ b/tests/binascii_hex.txt
@@ -0,0 +1,47 @@
+Round-tripping geometries through hex-encoded binary
+====================================================
+
+Hex-encoded binary is the PostGIS geometry representation, and so this is a
+test of the ability to store Shapely geometries in PostGIS.
+
+Point
+-----
+
+ >>> from shapely.geometry import Point
+ >>> point = Point(0.0, 0.0)
+ >>> import binascii
+ >>> x = binascii.b2a_hex(point.wkb)
+ >>> from shapely import wkb
+ >>> shape = wkb.loads(binascii.a2b_hex(x))
+ >>> shape # doctest: +ELLIPSIS
+ <shapely.geometry.point.Point object at ...>
+
+LineString
+----------
+
+ >>> from shapely.geometry import LineString
+ >>> line = LineString(((0.0, 0.0), (1.0, 1.0)))
+ >>> x = binascii.b2a_hex(line.wkb)
+ >>> shape = wkb.loads(binascii.a2b_hex(x))
+ >>> shape # doctest: +ELLIPSIS
+ <shapely.geometry.linestring.LineString object at ...>
+
+Polygon
+----------
+
+ >>> from shapely.geometry import Polygon
+ >>> polygon = Polygon(((0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (1.0, 0.0)))
+ >>> x = binascii.b2a_hex(polygon.wkb)
+ >>> shape = wkb.loads(binascii.a2b_hex(x))
+ >>> shape # doctest: +ELLIPSIS
+ <shapely.geometry.polygon.Polygon object at ...>
+
+Polygon with hole
+-----------------
+
+ >>> polygon = Polygon(((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))])
+ >>> x = binascii.b2a_hex(polygon.wkb)
+ >>> shape = wkb.loads(binascii.a2b_hex(x))
+ >>> shape # doctest: +ELLIPSIS
+ <shapely.geometry.polygon.Polygon object at ...>
+
diff --git a/tests/dimensions.txt b/tests/dimensions.txt
new file mode 100644
index 0000000..6b41c96
--- /dev/null
+++ b/tests/dimensions.txt
@@ -0,0 +1,17 @@
+Background: see http://trac.gispython.org/lab/ticket/168
+
+ >>> from shapely.geometry import Polygon
+ >>> coords = ((0.0, 0.0, 0.0), (0.0, 1.0, 0.0), (1.0, 1.0, 0.0), (1.0, 0.0, 0.0))
+ >>> polygon = Polygon(coords)
+ >>> polygon._ndim
+ 3
+ >>> gi = polygon.__geo_interface__
+ >>> gi['coordinates']
+ (((0.0, 0.0, 0.0), (0.0, 1.0, 0.0), (1.0, 1.0, 0.0), (1.0, 0.0, 0.0), (0.0, 0.0, 0.0)),)
+
+ >>> e = polygon.exterior
+ >>> e._ndim
+ 3
+ >>> gi = e.__geo_interface__
+ >>> gi['coordinates']
+ ((0.0, 0.0, 0.0), (0.0, 1.0, 0.0), (1.0, 1.0, 0.0), (1.0, 0.0, 0.0), (0.0, 0.0, 0.0))
diff --git a/tests/invalid_intersection.txt b/tests/invalid_intersection.txt
new file mode 100644
index 0000000..331377d
--- /dev/null
+++ b/tests/invalid_intersection.txt
@@ -0,0 +1,29 @@
+Test recovery from operation on invalid geometries
+==================================================
+
+ Make a self-intersecting polygon
+
+ >>> from shapely.geometry import Polygon
+ >>> polygon_invalid = Polygon(((0, 0), (1, 1), (1, -1), (0, 1), (0, 0)))
+ >>> polygon_invalid.is_valid
+ False
+
+ Intersect with a valid polygon
+
+ >>> polygon = Polygon(((-.5, -.5), (-.5, .5), (.5, .5), (.5, -5)))
+ >>> polygon.is_valid
+ True
+ >>> polygon_invalid.intersects(polygon)
+ True
+ >>> 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 ...>
+
+ 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 ...>
+
diff --git a/tests/polygonize.txt b/tests/polygonize.txt
new file mode 100644
index 0000000..50d1767
--- /dev/null
+++ b/tests/polygonize.txt
@@ -0,0 +1,29 @@
+Polygonizing
+============
+
+ >>> from shapely.geometry import LineString, Point
+ >>> from shapely.ops import polygonize
+ >>> lines = [
+ ... LineString(((0, 0), (1, 1))),
+ ... LineString(((0, 0), (0, 1))),
+ ... LineString(((0, 1), (1, 1))),
+ ... LineString(((1, 1), (1, 0))),
+ ... LineString(((1, 0), (0, 0))),
+ ... LineString(((5, 5), (6, 6))),
+ ... Point(0, 0),
+ ... ]
+ >>> result = polygonize(lines)
+ >>> list(result)
+ [<shapely.geometry.polygon.Polygon object at ...>, <shapely.geometry.polygon.Polygon object at ...>]
+
+ >>> lines2 = [
+ ... ((0, 0), (1, 1)),
+ ... ((0, 0), (0, 1)),
+ ... ((0, 1), (1, 1)),
+ ... ((1, 1), (1, 0)),
+ ... ((1, 0), (0, 0)),
+ ... ((5, 5), (6, 6)),
+ ... ]
+ >>> result2 = polygonize(lines2)
+ >>> list(result2)
+ [<shapely.geometry.polygon.Polygon object at ...>, <shapely.geometry.polygon.Polygon object at ...>]
diff --git a/tests/rungrind.dist b/tests/rungrind.dist
new file mode 100644
index 0000000..80f8005
--- /dev/null
+++ b/tests/rungrind.dist
@@ -0,0 +1,4 @@
+#!/bin/sh
+#export PYTHONPATH=YOUR_CUSTOM_PATH
+valgrind --tool=memcheck --leak-check=yes --suppressions=valgrind-python.supp python test_doctests.py
+
diff --git a/tests/test_doctests.py b/tests/test_doctests.py
new file mode 100644
index 0000000..25d5f7a
--- /dev/null
+++ b/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/tests/test_threads.py b/tests/test_threads.py
new file mode 100644
index 0000000..ac1a212
--- /dev/null
+++ b/tests/test_threads.py
@@ -0,0 +1,33 @@
+import threading
+
+def main():
+ num_threads = 4
+ use_threads = True
+
+ if not use_threads:
+ # Run core code
+ runShapelyBuilding()
+ else:
+ threads = [threading.Thread(target=runShapelyBuilding) for x in range(num_threads)]
+ for t in threads:
+ t.start()
+ for t in threads:
+ t.join()
+
+def runShapelyBuilding():
+ import shapely.wkt
+ import shapely.wkb
+
+ print "Running shapely tests on wkb"
+
+ wkb = shapely.wkt.loads("POINT (0 0)").wkb
+
+ for i in xrange(1000):
+ obj = shapely.wkb.loads(wkb)
+
+ print "Done"
+
+# Run main
+if __name__ == '__main__':
+ main()
+
diff --git a/tests/valgrind-python.supp b/tests/valgrind-python.supp
new file mode 100644
index 0000000..440fb4a
--- /dev/null
+++ b/tests/valgrind-python.supp
@@ -0,0 +1,240 @@
+#
+# This is a valgrind suppression file that should be used when using valgrind.
+#
+# Here's an example of running valgrind:
+#
+# cd python/dist/src
+# valgrind --tool=memcheck --suppressions=Misc/valgrind-python.supp \
+# ./python -E -tt ./Lib/test/regrtest.py -u bsddb,network
+#
+# You must edit Objects/obmalloc.c and uncomment Py_USING_MEMORY_DEBUGGER
+# to use the preferred suppressions with Py_ADDRESS_IN_RANGE.
+#
+# If you do not want to recompile Python, you can uncomment
+# suppressions for PyObject_Free and PyObject_Realloc.
+#
+# See Misc/README.valgrind for more information.
+
+# all tool names: Addrcheck,Memcheck,cachegrind,helgrind,massif
+{
+ ADDRESS_IN_RANGE/Invalid read of size 4
+ Memcheck:Addr4
+ fun:Py_ADDRESS_IN_RANGE
+}
+
+{
+ ADDRESS_IN_RANGE/Invalid read of size 4
+ Memcheck:Value4
+ fun:Py_ADDRESS_IN_RANGE
+}
+
+{
+ ADDRESS_IN_RANGE/Conditional jump or move depends on uninitialised value
+ Memcheck:Cond
+ fun:Py_ADDRESS_IN_RANGE
+}
+
+{
+ ADDRESS_IN_RANGE/Invalid read of size 4
+ Memcheck:Addr4
+ fun:PyObject_Free
+}
+
+{
+ ADDRESS_IN_RANGE/Invalid read of size 4
+ Memcheck:Value4
+ fun:PyObject_Free
+}
+
+{
+ ADDRESS_IN_RANGE/Conditional jump or move depends on uninitialised value
+ Memcheck:Cond
+ fun:PyObject_Free
+}
+
+{
+ ADDRESS_IN_RANGE/Invalid read of size 4
+ Memcheck:Addr4
+ fun:PyObject_Realloc
+}
+
+{
+ ADDRESS_IN_RANGE/Invalid read of size 4
+ Memcheck:Value4
+ fun:PyObject_Realloc
+}
+
+{
+ ADDRESS_IN_RANGE/Conditional jump or move depends on uninitialised value
+ Memcheck:Cond
+ fun:PyObject_Realloc
+}
+
+###
+### All the suppressions below are for errors that occur within libraries
+### that Python uses. The problems to not appear to be related to Python's
+### use of the libraries.
+###
+{
+ GDBM problems, see test_gdbm
+ Memcheck:Param
+ write(buf)
+ fun:write
+ fun:gdbm_open
+
+}
+
+###
+### These occur from somewhere within the SSL, when running
+### test_socket_sll. They are too general to leave on by default.
+###
+###{
+### somewhere in SSL stuff
+### Memcheck:Cond
+### fun:memset
+###}
+###{
+### somewhere in SSL stuff
+### Memcheck:Value4
+### fun:memset
+###}
+###
+###{
+### somewhere in SSL stuff
+### Memcheck:Cond
+### fun:MD5_Update
+###}
+###
+###{
+### somewhere in SSL stuff
+### Memcheck:Value4
+### fun:MD5_Update
+###}
+
+#
+# All of these problems come from using test_socket_ssl
+#
+{
+ from test_socket_ssl
+ Memcheck:Cond
+ fun:BN_bin2bn
+}
+
+{
+ from test_socket_ssl
+ Memcheck:Cond
+ fun:BN_num_bits_word
+}
+
+{
+ from test_socket_ssl
+ Memcheck:Value4
+ fun:BN_num_bits_word
+}
+
+{
+ from test_socket_ssl
+ Memcheck:Cond
+ fun:BN_mod_exp_mont_word
+}
+
+{
+ from test_socket_ssl
+ Memcheck:Cond
+ fun:BN_mod_exp_mont
+}
+
+{
+ from test_socket_ssl
+ Memcheck:Param
+ write(buf)
+ fun:write
+ obj:/usr/lib/libcrypto.so.0.9.7
+}
+
+{
+ from test_socket_ssl
+ Memcheck:Cond
+ fun:RSA_verify
+}
+
+{
+ from test_socket_ssl
+ Memcheck:Value4
+ fun:RSA_verify
+}
+
+{
+ from test_socket_ssl
+ Memcheck:Value4
+ fun:DES_set_key_unchecked
+}
+
+{
+ from test_socket_ssl
+ Memcheck:Value4
+ fun:DES_encrypt2
+}
+
+{
+ from test_socket_ssl
+ Memcheck:Cond
+ obj:/usr/lib/libssl.so.0.9.7
+}
+
+{
+ from test_socket_ssl
+ Memcheck:Value4
+ obj:/usr/lib/libssl.so.0.9.7
+}
+
+{
+ from test_socket_ssl
+ Memcheck:Cond
+ fun:BUF_MEM_grow_clean
+}
+
+{
+ from test_socket_ssl
+ Memcheck:Cond
+ fun:memcpy
+ fun:ssl3_read_bytes
+}
+
+{
+ from test_socket_ssl
+ Memcheck:Cond
+ fun:SHA1_Update
+}
+
+{
+ from test_socket_ssl
+ Memcheck:Value4
+ fun:SHA1_Update
+}
+
+
+# some extra lxml specific (?) suppressions..
+
+{
+ Test
+ Memcheck:Param
+ sigaction(act)
+ fun:__libc_sigaction
+}
+
+{
+ ld
+ Memcheck:Cond
+ obj:/lib/ld-2.6.so
+ obj:/lib/ld-2.6.so
+ obj:*
+}
+
+{
+ ld
+ Memcheck:Addr4
+ obj:/lib/ld-2.6.so
+ obj:/lib/ld-2.6.so
+ obj:*
+}
diff --git a/tests/wkt_locale.txt b/tests/wkt_locale.txt
new file mode 100644
index 0000000..bf6b431
--- /dev/null
+++ b/tests/wkt_locale.txt
@@ -0,0 +1,28 @@
+Test locale independence of WKT
+===============================
+
+Set locale to one that uses a comma as decimal seperator`
+
+ >>> import locale, sys
+ >>> if sys.platform == 'win32':
+ ... _ = locale.setlocale(locale.LC_ALL, 'portuguese-brazil')
+ ... else:
+ ... _ = locale.setlocale(locale.LC_ALL, 'pt_BR.UTF-8')
+
+Test reading and writing`
+
+ >>> from shapely.wkt import loads
+ >>> p = loads('POINT (0.0 0.0)')
+ >>> p.x == 0.0
+ True
+ >>> p.y == 0.0
+ True
+ >>> p.wkt
+ 'POINT (0.0...)'
+ >>> from shapely.wkt import dumps
+ >>> dumps(p)
+ 'POINT (0.0...)'
+
+Reset locale
+
+ >>> _ = locale.setlocale(locale.LC_ALL, '')
--
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