[python-shapely] 111/148: Imported Upstream version 1.4.1

Sebastiaan Couwenberg sebastic at moszumanska.debian.org
Thu Aug 20 17:42:09 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 0e09ebf016dadca96d49f089af02d0dd73f8411f
Author: Pietro Battiston <me at pietrobattiston.it>
Date:   Fri Sep 26 14:36:42 2014 +0200

    Imported Upstream version 1.4.1
---
 .gitignore                                         |   4 +-
 .travis.yml                                        |  12 +-
 CHANGES.txt                                        |  34 ++
 CREDITS.txt                                        |   5 +
 MANIFEST.in                                        |   4 +-
 README.rst                                         |  93 ++++--
 docs/code/triangulate.py                           |  24 ++
 docs/conf.py                                       |   6 +-
 docs/{design.txt => design.rst}                    |   0
 docs/{index.txt => index.rst}                      |   0
 docs/{manual.txt => manual.rst}                    | 343 +++++++++++++--------
 docs/{project.txt => project.rst}                  |   2 +
 requirements-dev.txt                               |   7 +-
 setup.py                                           | 109 +++----
 shapely/__init__.py                                |   2 +-
 shapely/_geos.pxi                                  |  58 ++++
 shapely/affinity.py                                |   2 +-
 shapely/coords.py                                  |   9 +-
 shapely/ctypes_declarations.py                     |  36 ++-
 shapely/examples/world.py                          |  21 --
 shapely/geometry/base.py                           |  86 +++++-
 shapely/geometry/linestring.py                     |  50 ++-
 shapely/geometry/multilinestring.py                |  47 ++-
 shapely/geometry/multipoint.py                     |  40 ++-
 shapely/geometry/multipolygon.py                   |  46 ++-
 shapely/geometry/point.py                          |  29 +-
 shapely/geometry/polygon.py                        |  38 ++-
 shapely/geometry/proxy.py                          |   6 +-
 shapely/geos.py                                    |  55 +++-
 shapely/impl.py                                    |   1 +
 shapely/ops.py                                     |  84 ++++-
 shapely/speedups/__init__.py                       |  10 +
 shapely/speedups/_speedups.pyx                     | 160 +++++++---
 shapely/strtree.py                                 |  59 ++++
 shapely/tests/__init__.py                          |  81 -----
 shapely/tests/test_speedups.py                     |  39 ---
 shapely/vectorized/__init__.py                     |   3 +
 shapely/vectorized/_vectorized.pyx                 | 120 +++++++
 shapely/wkt.py                                     |   4 +-
 tests/__init__.py                                  |  25 ++
 {shapely/tests => tests}/binascii_hex.txt          |   0
 tests/conftest.py                                  |  16 +
 {shapely/tests => tests}/rungrind.dist             |   0
 {shapely/tests => tests}/test_affinity.py          |   4 +
 {shapely/tests => tests}/test_box.py               |   0
 {shapely/tests => tests}/test_cga.py               |   0
 {shapely/tests => tests}/test_collection.py        |   0
 tests/test_delaunay.py                             |  43 +++
 {shapely/tests => tests}/test_delegated.py         |   0
 {shapely/tests => tests}/test_dlls.py              |   0
 {shapely/tests => tests}/test_doctests.py          |   0
 {shapely/tests => tests}/test_emptiness.py         |   0
 {shapely/tests => tests}/test_equality.py          |   0
 {shapely/tests => tests}/test_geointerface.py      |   0
 {shapely/tests => tests}/test_geomseq.py           |   0
 {shapely/tests => tests}/test_getitem.py           |   0
 .../tests => tests}/test_invalid_geometries.py     |   0
 {shapely/tests => tests}/test_iterops.py           |   0
 .../tests => tests}/test_linear_referencing.py     |   0
 {shapely/tests => tests}/test_linemerge.py         |   0
 {shapely/tests => tests}/test_linestring.py        |  46 ++-
 {shapely/tests => tests}/test_locale.py            |   0
 {shapely/tests => tests}/test_mapping.py           |   0
 {shapely/tests => tests}/test_multilinestring.py   |  19 +-
 {shapely/tests => tests}/test_multipoint.py        |   0
 {shapely/tests => tests}/test_multipolygon.py      |   0
 {shapely/tests => tests}/test_ndarrays.py          |   0
 tests/test_nearest.py                              |  22 ++
 {shapely/tests => tests}/test_operations.py        |   0
 {shapely/tests => tests}/test_operators.py         |   0
 {shapely/tests => tests}/test_persist.py           |   0
 {shapely/tests => tests}/test_pickle.py            |   0
 {shapely/tests => tests}/test_point.py             |   0
 {shapely/tests => tests}/test_polygon.py           |  31 +-
 {shapely/tests => tests}/test_polygonize.py        |   0
 {shapely/tests => tests}/test_predicates.py        |   0
 {shapely/tests => tests}/test_prepared.py          |   0
 {shapely/tests => tests}/test_products_z.py        |   0
 {shapely/tests => tests}/test_singularity.py       |   0
 tests/test_strtree.py                              |  23 ++
 {shapely/tests => tests}/test_styles.py            |   0
 {shapely/tests => tests}/test_transform.py         |   6 +
 {shapely/tests => tests}/test_union.py             |   0
 {shapely/tests => tests}/test_validation.py        |   0
 tests/test_vectorized.py                           | 107 +++++++
 {shapely/tests => tests}/test_xy.py                |   0
 {shapely/tests => tests}/threading_test.py         |   0
 {shapely/tests => tests}/valgrind-python.supp      |   0
 88 files changed, 1580 insertions(+), 491 deletions(-)

diff --git a/.gitignore b/.gitignore
index 8be8256..7662bd5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,10 +1,12 @@
 *.pyc
+*.c
+*.so
+
 VERSION.txt
 Shapely.egg-info/
 build/
 dist/
 docs/_build/
-shapely/speedups/_speedups.*
 docs/shapely.*.txt
 docs/shapely.txt
 docs/modules.txt
diff --git a/.travis.yml b/.travis.yml
index 0cdaccf..f92f690 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -3,20 +3,24 @@ language: python
 python:
   - "2.6"
   - "2.7"
-  - "3.2"
   - "3.3"
+  - "3.4"
+
+env:
+  - "TRAVIS_SPEEDUP_OPTS=--with-speedups"
+  - "TRAVIS_SPEEDUP_OPTS="
 
 before_install:
   - sudo add-apt-repository -y ppa:ubuntugis/ppa
   - sudo apt-get update -qq
-  - sudo apt-get install -qq libgeos-dev
+  - sudo apt-get install -qq libgeos-dev python-numpy cython
   - if [[ $TRAVIS_PYTHON_VERSION == "2.6" ]]; then pip install unittest2; fi
   - pip install -r requirements-dev.txt
 
 install:
-  - python setup.py build
+  - pip install -e .
 
-script: "python setup.py test"
+script: "py.test tests ${TRAVIS_SPEEDUP_OPTS}"
 
 notifications:
     email: false
diff --git a/CHANGES.txt b/CHANGES.txt
index 99eb0fb..47399f9 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,6 +1,40 @@
 Changes
 =======
 
+1.4.1 (2014-09-23)
+------------------
+- Return of support for GEOS 3.2 (#176, #178).
+
+1.4.0 (2014-09-08)
+------------------
+- SVG representations for IPython's inline image protocol.
+- Efficient and fast vectorized contains().
+- Change mitre_limit default to 5.0; raise ValueError with 0.0 (#139).
+- Allow mix of tuples and Points in sped-up LineString ctor (#152).
+- New STRtree class (#73).
+- Add ops.nearest_points() (#147).
+- Faster creation of geometric objects from others (cloning) (#165).
+- Removal of tests from package.
+
+1.3.3 (2014-07-23)
+------------------
+- Allow single-part geometries as argument to ops.cacaded_union() (#135).
+- Support affine transformations of LinearRings (#112).
+
+1.3.3 (2014-07-23)
+------------------
+- Allow single-part geometries as argument to ops.cacaded_union() (#135).
+- Support affine transformations of LinearRings (#112).
+
+1.3.2 (2014-05-13)
+------------------
+- Let LineString() take a sequence of Points (#130).
+
+1.3.1 (2014-04-22)
+------------------
+- More reliable proxy cleanup on exit (#106).
+- More robust DLL loading on all platforms (#114).
+
 1.3.0 (2013-12-31)
 ------------------
 - Include support for Python 3.2 and 3.3 (#56), minimum version is now 2.6.
diff --git a/CREDITS.txt b/CREDITS.txt
index 569280f..a838183 100644
--- a/CREDITS.txt
+++ b/CREDITS.txt
@@ -11,6 +11,7 @@ Shapely is written by:
 Patches contributed by:
 
 * Allan Adair (https://github.com/allanadair)
+* Joshua Arnott (https://github.com/snorfalorpagus)
 * Howard Butler
 * Gabi Davar (https://github.com/mindw)
 * Phil Elson (https://github.com/pelson)
@@ -20,8 +21,10 @@ Patches contributed by:
 * Kelsey Jordahl (https://github.com/kjordahl)
 * Fr |eaigue| d |eaigue| ric Junod
 * Thomas Kluyver (https://github.com/takluyver)
+* William Kyngesburye (https://github.com/kyngchaos)
 * Eric Lemoine
 * Naveen Michaud-Agrawal (https://github.com/nmichaud)
+* om-henners (https://github.com/om-henners)
 * psagers https://github.com/psagers
 * Jeethu Rao (https://github.com/jeethu)
 * Benjamin Root (https://github.com/WeatherGod)
@@ -31,6 +34,8 @@ Patches contributed by:
 * Kristian Thy
 * Mike Toews (https://github.com/mwtoews)
 * Maarten Vermeyen (https://github.com/maarten-vermeyen)
+* Jacob Wasserman (https://github.com/jwass)
+* Brandon Wood (https://github.com/woodb)
 
 See also: https://github.com/Toblerity/Shapely/graphs/contributors.
 
diff --git a/MANIFEST.in b/MANIFEST.in
index 7e709ac..86e4a78 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -5,8 +5,8 @@ recursive-exclude docs *
 recursive-exclude DLLs_AMD64 *
 recursive-exclude DLLs_x86 *
 include CHANGES.txt CREDITS.txt LICENSE.txt README.rst VERSION.txt
-recursive-include shapely/tests *.py *.txt
+recursive-include tests *.py *.txt
 recursive-include shapely/examples *.py
 recursive-exclude shapely/speedups *.pyx
-include docs/manual.txt
+include docs/*.rst
 exclude MANIFEST.in
diff --git a/README.rst b/README.rst
index effd748..3495165 100644
--- a/README.rst
+++ b/README.rst
@@ -4,6 +4,9 @@ Shapely
 
 Manipulation and analysis of geometric objects in the Cartesian plane.
 
+.. image:: https://travis-ci.org/Toblerity/Shapely.png?branch=master
+   :target: https://travis-ci.org/Toblerity/Shapely
+
 .. image:: http://farm3.staticflickr.com/2738/4511827859_b5822043b7_o_d.png
    :width: 800
    :height: 400
@@ -20,7 +23,7 @@ integrated with packages that are. For more details, see:
 Requirements
 ============
 
-Shapely 1.3 requires
+Shapely 1.4 requires
 
 * Python >=2.6 (including Python 3.x)
 * libgeos_c >=3.1 (3.0 and below have not been tested, YMMV)
@@ -30,13 +33,14 @@ 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::
+is on the system library path, and install from the Python package index.
 
-  $ pip install Shapely
+.. code-block:: console
 
-or from a source distribution with the setup script::
+  $ pip install shapely
 
-  $ python setup.py install
+Shapely is also provided by popular Python distributions like Enthought Canopy
+and Continuum Analytics Anaconda. 
 
 .. warning:: Windows users:
    do not under any circumstances use pip (or easy_install) to uninstall
@@ -48,7 +52,9 @@ Usage
 =====
 
 Here is the canonical example of building an approximately circular patch by
-buffering a point::
+buffering a point.
+
+.. code-block:: pycon
 
   >>> from shapely.geometry import Point
   >>> patch = Point(0.0, 0.0).buffer(10.0)
@@ -65,40 +71,51 @@ Integration
 
 Shapely does not read or write data files, but it can serialize and deserialize
 using several well known formats and protocols. The shapely.wkb and shapely.wkt
-modules provide dumpers and loaders inspired by Python's pickle module.::
+modules provide dumpers and loaders inspired by Python's pickle module.
+
+.. code-block:: pycon
 
   >>> from shapely.wkt import dumps, loads
   >>> dumps(loads('POINT (0 0)'))
   'POINT (0.0000000000000000 0.0000000000000000)'
 
 All linear objects, such as the rings of a polygon (like ``patch`` above),
-provide the Numpy array interface.::
+provide the Numpy array interface.
+
+.. code-block:: pycon
 
-  >>> from numpy import asarray
-  >>> ag = asarray(patch.exterior)
-  >>> ag
+  >>> import numpy as np
+  >>> np.array(patch.exterior)
   array([[  1.00000000e+01,   0.00000000e+00],
          [  9.95184727e+00,  -9.80171403e-01],
          [  9.80785280e+00,  -1.95090322e+00],
          ...
          [  1.00000000e+01,   0.00000000e+00]])
 
-That yields a Numpy array of [x, y] arrays. This is not always exactly what one
-wants for plotting shapes with Matplotlib (for example), so Shapely 1.2 adds
-a `xy` property for obtaining separate arrays of coordinate x and y values.::
+That yields a Numpy array of `[x, y]` arrays. This is not always exactly what one
+wants for plotting shapes with Matplotlib (for example), so Shapely adds
+a `xy` property for obtaining separate arrays of coordinate x and y values.
+
+.. code-block:: pycon
 
   >>> x, y = patch.exterior.xy
-  >>> ax = asarray(x)
-  >>> ax
+  >>> np.array(x)
   array([  1.00000000e+01,   9.95184727e+00,   9.80785280e+00,  ...])
 
-Numpy arrays can also be adapted to Shapely linestrings::
+Numpy arrays of `[x, y]` arrays can also be adapted to Shapely linestrings.
+
+.. code-block:: pycon
 
-  >>> from shapely.geometry import asLineString
-  >>> asLineString(ag).length
+  >>> from shapely.geometry import LineString
+  >>> LineString(np.array(patch.exterior)).length
   62.806623139095073
-  >>> asLineString(ag).wkt
-  'LINESTRING (10.0000000000000000 0.0000000000000000, ...)'
+
+Numpy arrays of x and y must be transposed.
+
+.. code-block::
+
+  >>> LineString(np.transpose(np.array(patch.exterior.xy))).length
+  62.80662313909507
 
 Shapely can also integrate with other Python GIS packages using data modeled
 after GeoJSON.
@@ -116,22 +133,36 @@ after GeoJSON.
 Development and Testing
 =======================
 
-Dependecies for developing Shapely are listed in requirements-dev.txt. Cython
+Dependencies for developing Shapely are listed in requirements-dev.txt. Cython
 and Numpy are not required for production installations, only for development.
-Use of a virtual environment is strongly recommended.::
+Use of a virtual environment is strongly recommended.
+
+.. code-block:: console
 
   $ virtualenv .
   $ source bin/activate
   (env)$ pip install -r requirements-dev.txt
-  (env)$ python setup.py develop
+  (env)$ pip install -e .
+
+We use py.test to run Shapely's suite of unittests and doctests.
+
+.. code-block:: console
+
+  (env)$ py.test tests
+
+Roadmap and Maintenance
+=======================
 
-Shapely uses a Zope-stye suite of unittests and doctests, exercised via
-setup.py.::
+Shapely 1.2.x is a maintenance-only branch which supports Python 2.4-2.6, but
+not Python 3+. There will be no new features in Shapely 1.2.x and only fixes
+for major bugs.
 
-  (env)$ python setup.py test
+Shapely 1.3.x is a maintenance-only branch supporting Pythons 2.7 and 3+.
 
-Nosetests won't run the tests properly; Zope doctest suites are not currently
-supported well by nose.
+"Shapely 3000" is the name of the next milestone. New features will include
+vectorized operations, better integration with IPython Notebook, support for
+fixed precision models, and more. Less ctypes and more Cython is another theme
+in this branch. A 1.4 release should come out of this by Summer, 2014.
 
 Support
 =======
@@ -141,12 +172,12 @@ http://lists.gispython.org/mailman/listinfo/community.
 
 Bugs may be reported at https://github.com/Toblerity/Shapely.
 
-.. include:: ../CREDITS.txt
+.. include:: CREDITS.txt
 
 .. _JTS: http://www.vividsolutions.com/jts/jtshome.htm
 .. _PostGIS: http://postgis.org
 .. _GEOS: http://trac.osgeo.org/geos/
-.. _example apps: https://github.com/sgillies/shapely/tree/master/shapely/examples
+.. _example apps: https://github.com/Toblerity/shapely/tree/master/shapely/examples
 .. _manual: http://toblerity.github.com/shapely/manual.html
 .. _Pleiades: http://pleiades.stoa.org
 
diff --git a/docs/code/triangulate.py b/docs/code/triangulate.py
new file mode 100644
index 0000000..29d5002
--- /dev/null
+++ b/docs/code/triangulate.py
@@ -0,0 +1,24 @@
+from shapely.geometry import MultiPoint
+from shapely.ops import triangulate
+
+from matplotlib import pyplot
+from descartes.patch import PolygonPatch
+from figures import SIZE, BLUE, GRAY
+
+points = MultiPoint([(0, 0), (1, 1), (0, 2), (2, 2), (3, 1), (1, 0)])
+triangles = triangulate(points)
+
+fig = pyplot.figure(1, figsize=SIZE, dpi=90)
+fig.set_frameon(True)
+ax = fig.add_subplot(111)
+
+for triangle in triangles:
+    patch = PolygonPatch(triangle, facecolor=BLUE, edgecolor=BLUE, alpha=0.5, zorder=2)
+    ax.add_patch(patch)
+
+for point in points:
+    pyplot.plot(point.x, point.y, 'o', color=GRAY)
+
+pyplot.xlim(-0.5, 3.5)
+pyplot.ylim(-0.5, 2.5)
+pyplot.show()
diff --git a/docs/conf.py b/docs/conf.py
index e66ac12..fe3b911 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -35,15 +35,15 @@ extensions = [
     'matplotlib.sphinxext.only_directives',
     'matplotlib.sphinxext.plot_directive',
     'sphinx.ext.autodoc',
-    'sphinx.ext.pngmath', # <----- pick one, not both
-    #'sphinx.ext.mathjax', # <--/
+    #'sphinx.ext.pngmath', # <----- pick one, not both
+    'sphinx.ext.mathjax', # <--/
 ]
 
 # Add any paths that contain templates here, relative to this directory.
 #templates_path = ['_templates']
 
 # The suffix of source filenames.
-source_suffix = '.txt'
+source_suffix = '.rst'
 
 # The encoding of source files.
 #source_encoding = 'utf-8'
diff --git a/docs/design.txt b/docs/design.rst
similarity index 100%
rename from docs/design.txt
rename to docs/design.rst
diff --git a/docs/index.txt b/docs/index.rst
similarity index 100%
rename from docs/index.txt
rename to docs/index.rst
diff --git a/docs/manual.txt b/docs/manual.rst
similarity index 91%
rename from docs/manual.txt
rename to docs/manual.rst
index 8ca9f41..87933db 100644
--- a/docs/manual.txt
+++ b/docs/manual.rst
@@ -193,7 +193,7 @@ General Attributes and Methods
   Returns a string specifying the `Geometry Type` of the object in accordance
   with [1]_.
 
-.. sourcecode:: pycon
+.. code-block:: pycon
 
   >>> print Point(0, 0).geom_type
   Point
@@ -202,7 +202,7 @@ General Attributes and Methods
 
   Returns the minimum distance (``float``) to the `other` geometric object.
 
-.. sourcecode:: pycon
+.. code-block:: pycon
 
   >>> Point(0,0).distance(Point(1,1))
   1.4142135623730951
@@ -215,7 +215,7 @@ General Attributes and Methods
 .. note::
   This is not in general the same as the centroid.
 
-.. sourcecode:: pycon
+.. code-block:: pycon
 
   >>> donut = Point(0, 0).buffer(2.0).difference(Point(0, 0).buffer(1.0))
   >>> donut.centroid.wkt
@@ -233,7 +233,7 @@ Points
   The `Point` constructor takes positional coordinate values or point tuple
   parameters.
 
-.. sourcecode:: pycon
+.. code-block:: pycon
 
   >>> from shapely.geometry import Point
   >>> point = Point(0.0, 0.0)
@@ -241,7 +241,7 @@ Points
 
 A `Point` has zero area and zero length.
 
-.. sourcecode:: pycon
+.. code-block:: pycon
 
   >>> point.area
   0.0
@@ -250,14 +250,14 @@ A `Point` has zero area and zero length.
 
 Its `x-y` bounding box is a ``(minx, miny, maxx, maxy)`` tuple.
 
-.. sourcecode:: pycon
+.. code-block:: pycon
 
   >>> point.bounds
   (0.0, 0.0, 0.0, 0.0)
 
 Coordinate values are accessed via `coords`, `x`, `y`, and `z` properties.
 
-.. sourcecode:: pycon
+.. code-block:: pycon
 
   >>> list(point.coords)
   [(0.0, 0.0)]
@@ -268,7 +268,7 @@ Coordinate values are accessed via `coords`, `x`, `y`, and `z` properties.
 
 Coordinates may also be sliced. `New in version 1.2.14`.
 
-.. sourcecode:: pycon
+.. code-block:: pycon
 
   >>> point.coords[:]
   [(0.0, 0.0)]
@@ -276,7 +276,7 @@ Coordinates may also be sliced. `New in version 1.2.14`.
 The `Point` constructor also accepts another `Point` instance, thereby making
 a copy.
 
-.. sourcecode:: pycon
+.. code-block:: pycon
 
   >>> Point(point)
   <shapely.geometry.point.Point object at 0x...>
@@ -304,7 +304,7 @@ that describe the lines are shown in grey.
 
 A `LineString` has zero area and non-zero length.
 
-.. sourcecode:: pycon
+.. code-block:: pycon
 
   >>> from shapely.geometry import LineString
   >>> line = LineString([(0, 0), (1, 1)])
@@ -315,14 +315,14 @@ A `LineString` has zero area and non-zero length.
 
 Its `x-y` bounding box is a ``(minx, miny, maxx, maxy)`` tuple.
 
-.. sourcecode:: pycon
+.. code-block:: pycon
 
   >>> line.bounds
   (0.0, 0.0, 1.0, 1.0)
 
 The defining coordinate values are accessed via the `coords` property.
 
-.. sourcecode:: pycon
+.. code-block:: pycon
 
   >>> len(line.coords)
   2
@@ -331,7 +331,7 @@ The defining coordinate values are accessed via the `coords` property.
 
 Coordinates may also be sliced. `New in version 1.2.14`.
 
-.. sourcecode:: pycon
+.. code-block:: pycon
 
   >>> point.coords[:]
   [(0.0, 0.0), (1.0, 1.0)]
@@ -341,13 +341,19 @@ Coordinates may also be sliced. `New in version 1.2.14`.
 The constructor also accepts another `LineString` instance, thereby making a
 copy.
 
-.. sourcecode:: pycon
+.. code-block:: pycon
 
   >>> LineString(line)
   <shapely.geometry.linestring.LineString object at 0x...>
 
-A sequence of `Point` instances is not a valid constructor parameter. A
-`LineString` is described by points, but is not composed of `Point` instances.
+A `LineString` may also be constructed using a a sequence of mixed `Point`
+instances or coordinate tuples. The individual coordinates are copied into
+the new object.
+
+.. code-block:: pycon
+
+  >>> LineString([Point(0.0, 1.0), (2.0, 3.0), Point(4.0, 5.0)])
+  <shapely.geometry.linestring.LineString object at 0x...>
 
 .. _linearrings:
 
@@ -378,7 +384,7 @@ grey. A ring's boundary is `empty`.
 
 A `LinearRing` has zero area and non-zero length.
 
-.. sourcecode:: pycon
+.. code-block:: pycon
 
   >>> from shapely.geometry.polygon import LinearRing
   >>> ring = LinearRing([(0, 0), (1, 1), (1, 0)])
@@ -389,14 +395,14 @@ A `LinearRing` has zero area and non-zero length.
 
 Its `x-y` bounding box is a ``(minx, miny, maxx, maxy)`` tuple.
 
-.. sourcecode:: pycon
+.. code-block:: pycon
 
   >>> ring.bounds
   (0.0, 0.0, 1.0, 1.0)
 
 Defining coordinate values are accessed via the `coords` property.
 
-.. sourcecode:: pycon
+.. code-block:: pycon
 
   >>> len(ring.coords)
   4
@@ -406,7 +412,7 @@ Defining coordinate values are accessed via the `coords` property.
 The `LinearRing` constructor also accepts another `LineString` or `LinearRing`
 instance, thereby making a copy.
 
-.. sourcecode:: pycon
+.. code-block:: pycon
 
   >>> LinearRring(ring)
   <shapely.geometry.polygon.LinearRing object at 0x...>
@@ -446,7 +452,7 @@ interior rings touch along a line, and on the right, a `Polygon` that is
 
 A `Polygon` has non-zero area and non-zero length.
 
-.. sourcecode:: pycon
+.. code-block:: pycon
 
   >>> from shapely.geometry import Polygon
   >>> polygon = Polygon([(0, 0), (1, 1), (1, 0)])
@@ -457,14 +463,14 @@ A `Polygon` has non-zero area and non-zero length.
 
 Its `x-y` bounding box is a ``(minx, miny, maxx, maxy)`` tuple.
 
-.. sourcecode:: pycon
+.. code-block:: pycon
 
   >>> polygon.bounds
   (0.0, 0.0, 1.0, 1.0)
 
 Component rings are accessed via `exterior` and `interiors` properties.
 
-.. sourcecode:: pycon
+.. code-block:: pycon
 
   >>> list(polygon.exterior.coords)
   [(0.0, 0.0), (1.0, 1.0), (1.0, 0.0), (0.0, 0.0)]
@@ -474,7 +480,7 @@ Component rings are accessed via `exterior` and `interiors` properties.
 The `Polygon` constructor also accepts instances of `LineString` and
 `LinearRing`.
 
-.. sourcecode:: pycon
+.. code-block:: pycon
 
   >>> coords = [(0, 0), (1, 1), (1, 0)]
   >>> r = LinearRing(coords)
@@ -497,7 +503,7 @@ the :func:`shapely.geometry.box()` function.
 
 For example:
 
-.. sourcecode:: pycon
+.. code-block:: pycon
 
   >>> from shapely.geometry import box
   >>> b = box(0.0, 0.0, 1.0, 1.0)
@@ -530,7 +536,7 @@ point. To represent these kind of results, Shapely provides frozenset_-like,
 immutable collections of geometric objects.  The collections may be homogeneous
 (`MultiPoint` etc.) or heterogeneous.
 
-.. sourcecode:: python
+.. code-block:: python
 
   >>> a = LineString([(0, 0), (1, 1), (1,2), (2,2)])
   >>> b = LineString([(0, 0), (1, 1), (2,1), (2,2)])
@@ -552,7 +558,7 @@ single point; b) the intersection (in blue) is a collection containing one
 Members of a `GeometryCollection` are accessed via the `geoms` property or via
 the iterator protocol using ``in``  or ``list()``.
 
-.. sourcecode:: pycon
+.. code-block:: pycon
 
   >>> pprint(list(x.geoms))
   [<shapely.geometry.point.Point object at 0x...>,
@@ -564,7 +570,7 @@ the iterator protocol using ``in``  or ``list()``.
 Homogeneous collections can also be sliced, resulting in a new object of the
 same type.
 
-.. sourcecode:: pycon
+.. code-block:: pycon
 
   >>> from shapely.geometry import MultiPoint
   >>> m = MultiPoint([(0, 0), (1, 1), (1,2), (2,2)])
@@ -594,7 +600,7 @@ Collections of Points
 
 A `MultiPoint` has zero area and zero length.
 
-.. sourcecode:: pycon
+.. code-block:: pycon
 
   >>> from shapely.geometry import MultiPoint
   >>> points = MultiPoint([(0.0, 0.0), (1.0, 1.0)])
@@ -605,7 +611,7 @@ A `MultiPoint` has zero area and zero length.
 
 Its `x-y` bounding box is a ``(minx, miny, maxx, maxy)`` tuple.
 
-.. sourcecode:: pycon
+.. code-block:: pycon
 
   >>> points.bounds
   (0.0, 0.0, 1.0, 1.0)
@@ -613,7 +619,7 @@ Its `x-y` bounding box is a ``(minx, miny, maxx, maxy)`` tuple.
 Members of a multi-point collection are accessed via the ``geoms`` property or
 via the iterator protocol using ``in`` or :func:`list`.
 
-.. sourcecode:: pycon
+.. code-block:: pycon
 
   >>> import pprint
   >>> pprint.pprint(list(points.geoms))
@@ -626,7 +632,7 @@ via the iterator protocol using ``in`` or :func:`list`.
 The constructor also accepts another `MultiPoint` instance or an unordered
 sequence of `Point` instances, thereby making copies.
 
-.. sourcecode:: pycon
+.. code-block:: pycon
 
   >>> MultiPoint([Point(0, 0), Point(1, 1)])
   <shapely.geometry.multipoint.MultiPoint object at 0x...>
@@ -649,7 +655,7 @@ shown in gray, the boundaries of the objects in black.
 
 A `MultiLineString` has zero area and non-zero length.
 
-.. sourcecode:: pycon
+.. code-block:: pycon
 
   >>> from shapely.geometry import MultiLineString
   >>> coords = [((0, 0), (1, 1)), ((-1, 0), (1, 0))]
@@ -661,7 +667,7 @@ A `MultiLineString` has zero area and non-zero length.
 
 Its `x-y` bounding box is a ``(minx, miny, maxx, maxy)`` tuple.
 
-.. sourcecode:: pycon
+.. code-block:: pycon
 
   >>> lines.bounds
   (-1.0, 0.0, 1.0, 1.0)
@@ -669,7 +675,7 @@ Its `x-y` bounding box is a ``(minx, miny, maxx, maxy)`` tuple.
 Its members are instances of `LineString` and are accessed via the ``geoms``
 property or via the iterator protocol using ``in`` or ``list()``.
 
-.. sourcecode:: pycon
+.. code-block:: pycon
 
   >>> len(lines.geoms)
   2
@@ -683,7 +689,7 @@ property or via the iterator protocol using ``in`` or ``list()``.
 The constructor also accepts another instance of `MultiLineString` or an
 unordered sequence of `LineString` instances, thereby making copies.
 
-.. sourcecode:: pycon
+.. code-block:: pycon
 
   >>> MultiLineString(lines)
   <shapely.geometry.multilinestring.MultiLineString object at 0x...>
@@ -703,7 +709,7 @@ Collections of Polygons
 More clearly, the constructor also accepts an unordered sequence of `Polygon`
 instances, thereby making copies.
 
-.. sourcecode:: pycon
+.. code-block:: pycon
 
   >>> polygons = MultiPolygon([polygon, s, t])
   >>> len(polygons.geoms)
@@ -711,13 +717,13 @@ instances, thereby making copies.
 
 .. plot:: code/multipolygon.py
 
-Figure 7. On the right, a `valid` `MultiPolygon` with 2 members, and on the
+Figure 7. On the left, a `valid` `MultiPolygon` with 2 members, and on the
 right, a `MultiPolygon` that is invalid because its members touch at an
 infinite number of points (along a line).
 
 Its `x-y` bounding box is a ``(minx, miny, maxx, maxy)`` tuple.
 
-.. sourcecode:: pycon
+.. code-block:: pycon
 
   >>> polygons.bounds
   (-1.0, -1.0, 2.0, 2.0)
@@ -725,7 +731,7 @@ Its `x-y` bounding box is a ``(minx, miny, maxx, maxy)`` tuple.
 Its members are instances of `Polygon` and are accessed via the ``geoms``
 property or via the iterator protocol using ``in`` or ``list()``.
 
-.. sourcecode:: pycon
+.. code-block:: pycon
 
   >>> len(polygons.geoms)
   3
@@ -742,7 +748,7 @@ not ``None``, but like ``set([])``. Empty features can be created by calling
 the various constructors with no arguments. Almost no operations are supported
 by empty features.
 
-.. sourcecode:: pycon
+.. code-block:: pycon
 
   >>> line = LineString()
   >>> line.is_empty
@@ -757,7 +763,7 @@ by empty features.
 The coordinates of a empty feature can be set, after which the geometry is no
 longer empty.
 
-.. sourcecode:: pycon
+.. code-block:: pycon
 
   >>> line.coords = [(0, 0), (1, 1)]
   >>> line.is_empty
@@ -787,7 +793,7 @@ point at a given distance along the object.
 If the `normalized` arg is ``True``, the distance will be interpreted as a
 fraction of the geometric object's length.
 
-.. sourcecode:: pycon
+.. code-block:: pycon
 
   >>> ip = LineString([(0, 0), (0, 1), (1, 1)]).interpolate(1.5)
   >>> ip
@@ -806,7 +812,7 @@ If the `normalized` arg is ``True``, return the distance normalized to the
 length of the object. The :meth:`project` method is the inverse of
 :meth:`interpolate`.
 
-.. sourcecode:: pycon
+.. code-block:: pycon
 
   >>> LineString([(0, 0), (0, 1), (1, 1)]).project(ip)
   1.5
@@ -816,7 +822,7 @@ length of the object. The :meth:`project` method is the inverse of
 For example, the linear referencing methods might be used to cut lines at a
 specified distance.
 
-.. sourcecode:: python
+.. code-block:: python
 
   def cut(line, distance):
       # Cuts a line in two at a distance from its starting point
@@ -835,7 +841,7 @@ specified distance.
                   LineString(coords[:i] + [(cp.x, cp.y)]),
                   LineString([(cp.x, cp.y)] + coords[i:])]
 
-.. sourcecode:: pycon
+.. code-block:: pycon
 
   >>> line = LineString([(0, 0), (1, 0), (2, 0), (3, 0), (4, 0), (5, 0)])
   >>> pprint([list(x.coords) for x in cut(line, 1.0)])
@@ -867,7 +873,7 @@ example will be shown for each.
   Returns ``True`` if the feature has not only `x` and `y`, but also `z`
   coordinates for 3D (or so-called, 2.5D) geometries.
 
-.. sourcecode:: pycon
+.. code-block:: pycon
 
   >>> Point(0, 0).has_z
   False
@@ -882,14 +888,14 @@ example will be shown for each.
 
   `New in version 1.2.10`.
 
-.. sourcecode:: pycon
+.. code-block:: pycon
 
   >>> LinearRing([(1,0), (1,1), (0,0)]).is_ccw
   True
 
 A ring with an undesired orientation can be reversed like this:
 
-.. sourcecode:: pycon
+.. code-block:: pycon
 
   >>> ring = LinearRing([(0,0), (1,1), (1,0)])
   >>> ring.is_ccw
@@ -903,7 +909,7 @@ A ring with an undesired orientation can be reversed like this:
   Returns ``True`` if the feature's `interior` and `boundary` (in point set
   terms) coincide with the empty set.
 
-.. sourcecode:: pycon
+.. code-block:: pycon
 
   >>> Point().is_empty
   True
@@ -916,7 +922,7 @@ A ring with an undesired orientation can be reversed like this:
    unary predicates such as ``is_empty`` can be easily used as predicates for
    the built in :func:`filter` or :func:`itertools.ifilter`.
 
-.. sourcecode:: pycon
+.. code-block:: pycon
 
   >>> from operator import attrgetter
   >>> empties = filter(attrgetter('is_empty'), [Point(), Point(0, 0)])
@@ -928,7 +934,7 @@ A ring with an undesired orientation can be reversed like this:
   Returns ``True`` if the feature is closed. A closed feature's `boundary`
   coincides with the empty set.
 
-.. sourcecode:: pycon
+.. code-block:: pycon
 
   >>> LineString([(0, 0), (1, 1), (1, -1)]).is_ring
   False
@@ -946,7 +952,7 @@ meaningless for others.
 
    The simplicity test is meaningful only for `LineStrings` and `LinearRings`.
 
-.. sourcecode:: pycon
+.. code-block:: pycon
 
   >>> LineString([(0, 0), (1, 1), (1, -1), (0, 1)]).is_simple
   False
@@ -962,7 +968,7 @@ valid `Polygon` may not possess any overlapping exterior or interior rings. A
 valid `MultiPolygon` may not collect any overlapping polygons. Operations on
 invalid features may fail.
 
-.. sourcecode:: pycon
+.. code-block:: pycon
 
   >>> MultiPolygon([Point(0, 0).buffer(2.0), Point(1, 1).buffer(2.0)]).is_valid
   False
@@ -976,7 +982,7 @@ buffer operations (explained in a following section) overlap.
   could ensure that only valid objects are returned from a constructor
   function.
 
-.. sourcecode:: python
+.. code-block:: python
 
   from functools import wraps
   def validate(func):
@@ -989,7 +995,7 @@ buffer operations (explained in a following section) overlap.
           return ob
       return wrapper
 
-.. sourcecode:: pycon
+.. code-block:: pycon
 
   >>> @validate
   ... def ring(coordinates):
@@ -1027,7 +1033,7 @@ See also :meth:`equals`.
 This predicate applies to all types, and is inverse to :meth:`within`. The
 expression ``a.contains(b) == b.within(a)`` always evaluates to ``True``.
 
-.. sourcecode:: pycon
+.. code-block:: pycon
 
   >>> coords = [(0, 0), (1, 1)]
   >>> LineString(coords).contains(Point(0.5, 0.5))
@@ -1037,7 +1043,7 @@ expression ``a.contains(b) == b.within(a)`` always evaluates to ``True``.
 
 A line's endpoints are part of its `boundary` and are therefore not contained.
 
-.. sourcecode:: pycon
+.. code-block:: pycon
 
   >>> LineString(coords).contains(Point(1.0, 1.0))
   False
@@ -1047,7 +1053,7 @@ A line's endpoints are part of its `boundary` and are therefore not contained.
   Binary predicates can be used directly as predicates for ``filter()`` or
   ``itertools.ifilter()``.
 
-.. sourcecode:: pycon
+.. code-block:: pycon
 
   >>> line = LineString(coords)
   >>> contained = filter(line.contains, [Point(), Point(0.5, 0.5)])
@@ -1062,14 +1068,14 @@ A line's endpoints are part of its `boundary` and are therefore not contained.
   the other but does not contain it, and the dimension of the intersection is
   less than the dimension of the one or the other.
 
-.. sourcecode:: pycon
+.. code-block:: pycon
 
   >>> LineString(coords).crosses(LineString([(0, 1), (1, 0)]))
   True
 
 A line does not cross a point that it contains.
 
-.. sourcecode:: pycon
+.. code-block:: pycon
 
   >>> LineString(coords).crosses(Point(0.5, 0.5))
   False
@@ -1079,7 +1085,7 @@ A line does not cross a point that it contains.
   Returns ``True`` if the `boundary` and `interior` of the object do not
   intersect at all with those of the other.
 
-.. sourcecode:: pycon
+.. code-block:: pycon
 
   >>> Point(0, 0).disjoint(Point(1, 1))
   True
@@ -1096,7 +1102,7 @@ determine them, but are not the entirety of the sets. This is a potential
 "gotcha" for new users.  Equivalent lines, for example, can be constructed
 differently.
 
-.. sourcecode:: pycon
+.. code-block:: pycon
 
   >>> a = LineString([(0, 0), (1, 1)])
   >>> b = LineString([(0, 0), (0.5, 0.5), (1, 1)])
@@ -1125,7 +1131,7 @@ This predicate is equivalent to the OR-ing of :meth:`contains`, :meth:`crosses`,
 Overlapping features do not therefore `touch`, another potential "gotcha". For
 example, the following lines touch at ``(1, 1)``, but do not overlap.
 
-.. sourcecode:: pycon
+.. code-block:: pycon
 
   >>> a = LineString([(0, 0), (1, 1)])
   >>> b = LineString([(1, 1), (2, 2)])
@@ -1144,7 +1150,7 @@ objects. Let's say we have 4 stereotypic features: a point that is contained by
 a polygon which is itself contained by another polygon, and a free spirited
 point contained by none
 
-.. sourcecode:: pycon
+.. code-block:: pycon
 
   >>> a = Point(2, 2)
   >>> b = Polygon([[1, 1], [1, 3], [3, 3], [3, 1]])
@@ -1153,7 +1159,7 @@ point contained by none
 
 and that copies of these are collected into a list
 
-.. sourcecode:: pycon
+.. code-block:: pycon
 
   >>> features = [c, a, d, b, c]
 
@@ -1163,9 +1169,7 @@ function that operates on each list element and returns a value for comparison.
 Our key function will be a wrapper class that implements ``__lt__()`` using
 Shapely's binary :meth:`within` predicate.
 
-.. sourcecode:: python
-
-  from shapely.geometry import asShape
+.. code-block:: python
 
   class Within(object):
       def __init__(self, o):
@@ -1178,7 +1182,7 @@ sorting. That's what we'll rely on to spatially sort, and the reason why we use
 :meth:`within` in reverse instead of :meth:`contains`. Trying it out on features
 `d` and `c`, we see that it works.
 
-.. sourcecode:: pycon
+.. code-block:: pycon
 
   >>> d < c
   True
@@ -1187,7 +1191,7 @@ sorting. That's what we'll rely on to spatially sort, and the reason why we use
 
 It also works on the list of features, producing the order we want.
 
-.. sourcecode:: pycon
+.. code-block:: pycon
 
   >>> [d, c, c, b, a] == sorted(features, key=Within, reverse=True)
   True
@@ -1213,7 +1217,7 @@ object (the rest of the plane). The intersection of the `interior` of one with
 the `exterior` of the other is a ``0`` dimensional object (3rd and 7th elements
 of the matrix).
 
-.. sourcecode:: pycon
+.. code-block:: pycon
 
   >>> Point(0, 0).relate(Point(1, 1))
   'FF0FFF0F2'
@@ -1221,7 +1225,7 @@ of the matrix).
 The matrix for a line and a point on the line has more "true" (not ``F``)
 elements.
 
-.. sourcecode:: pycon
+.. code-block:: pycon
 
   >>> Point(0, 0).relate(LineString([(0, 0), (1, 1)]))
   'F0FFFF102'
@@ -1254,7 +1258,7 @@ available as a read-only attribute.
 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.
 
-.. sourcecode:: pycon
+.. code-block:: pycon
 
   >> coords = [((0, 0), (1, 1)), ((-1, 0), (1, 0))]
   >>> lines = MultiLineString(coords)
@@ -1277,7 +1281,7 @@ illustration of lines and their boundaries.
 
   Returns a representation of the object's geometric centroid (point).
 
-.. sourcecode:: pycon
+.. code-block:: pycon
 
   >>> LineString([(0, 0), (1, 1)]).centroid
   <shapely.geometry.point.Point object at 0x...>
@@ -1294,7 +1298,7 @@ illustration of lines and their boundaries.
   Returns a representation of the points making up this geometric object that
   do not make up the *other* object.
 
-.. sourcecode:: pycon
+.. code-block:: pycon
 
   >>> a = Point(1, 1).buffer(1.5)
   >>> b = Point(2, 1).buffer(1.5)
@@ -1323,7 +1327,7 @@ Figure 8. Differences between two approximately circular polygons.
   Returns a representation of the intersection of this object with the `other`
   geometric object.
 
-.. sourcecode:: pycon
+.. code-block:: pycon
 
   >>> a = Point(1, 1).buffer(1.5)
   >>> b = Point(2, 1).buffer(1.5)
@@ -1337,7 +1341,7 @@ See the figure under :meth:`symmetric_difference` below.
   Returns a representation of the points in this object not in the `other`
   geometric object, and the points in the `other` not in this geometric object.
 
-.. sourcecode:: pycon
+.. code-block:: pycon
 
   >>> a = Point(1, 1).buffer(1.5)
   >>> b = Point(2, 1).buffer(1.5)
@@ -1355,7 +1359,7 @@ The type of object returned depends on the relationship between the operands.
 The union of polygons (for example) will be a polygon or a multi-polygon
 depending on whether they intersect or not.
 
-.. sourcecode:: pycon
+.. code-block:: pycon
 
   >>> a = Point(1, 1).buffer(1.5)
   >>> b = Point(2, 1).buffer(1.5)
@@ -1366,7 +1370,7 @@ The semantics of these operations vary with type of geometric object.  For
 example, compare the boundary of the union of polygons to the union of their
 boundaries.
 
-.. sourcecode:: pycon
+.. code-block:: pycon
 
   >>> a.union(b).boundary
   <shapely.geometry.polygon.LinearRing object at 0x...>
@@ -1387,7 +1391,7 @@ Constructive Methods
 Shapely geometric object have several methods that yield new objects not
 derived from set-theoretic analysis.
 
-.. method:: object.buffer(distance, resolution=16, cap_style=1, join_style=1, mitre_limit=1.0)
+.. method:: object.buffer(distance, resolution=16, cap_style=1, join_style=1, mitre_limit=5.0)
 
   Returns an approximate representation of all points within a given `distance`
   of the this geometric object.
@@ -1395,7 +1399,7 @@ derived from set-theoretic analysis.
   The styles of caps are specified by integer values: 1 (round), 2 (flat),
   3 (square). These values are also enumerated by the object
   :class:`shapely.geometry.CAP_STYLE` (see below).
-  
+
   The styles of joins between offset segments are specified by integer values:
   1 (round), 2 (mitre), and 3 (bevel). These values are also enumerated by the
   object :class:`shapely.geometry.JOIN_STYLE` (see below).
@@ -1420,7 +1424,7 @@ derived from set-theoretic analysis.
    bevel         3
    ========= =====
 
-.. sourcecode:: pycon
+.. code-block:: pycon
 
   >>> from shapely.geometry import CAP_STYLE, JOIN_STYLE
   >>> CAP_STYLE.flat
@@ -1432,7 +1436,7 @@ A positive distance has an effect of dilation; a negative distance, erosion.
 The optional `resolution` argument determines the number of segments used to
 approximate a quarter circle around a point.
 
-.. sourcecode:: pycon
+.. code-block:: pycon
 
   >>> line = LineString([(0, 0), (1, 1), (0, 2), (2, 2), (3, 1), (1, 0)])
   >>> dilated = line.buffer(0.5)
@@ -1446,7 +1450,7 @@ object is shown in blue.
 The default (`resolution` of 16) buffer of a point is a polygonal patch with
 99.8% of the area of the circular disk it approximates.
 
-.. sourcecode:: pycon
+.. code-block:: pycon
 
   >>> p = Point(0, 0).buffer(10.0)
   >>> len(p.exterior.coords)
@@ -1456,7 +1460,7 @@ The default (`resolution` of 16) buffer of a point is a polygonal patch with
 
 With a `resolution` of 1, the buffer is a square patch.
 
-.. sourcecode:: pycon
+.. code-block:: pycon
 
   >>> q = Point(0, 0).buffer(10.0, 1)
   >>> len(q.exterior.coords)
@@ -1467,7 +1471,7 @@ With a `resolution` of 1, the buffer is a square patch.
 Passed a `distance` of 0, :meth:`buffer` can be used to "clean" self-touching
 or self-crossing polygons such as the classic "bowtie".
 
-.. sourcecode:: pycon
+.. code-block:: pycon
 
   >>> coords = [(0, 0), (0, 2), (1, 1), (2, 2), (2, 0), (1, 1), (0, 0)]
   >>> bowtie = Polygon(coords)
@@ -1494,7 +1498,7 @@ Buffering splits the polygon in two at the point where they touch.
   three. For two points, the convex hull collapses to a `LineString`; for 1, a
   `Point`.
 
-.. sourcecode:: pycon
+.. code-block:: pycon
 
   >>> Point(0, 0).convex_hull
   <shapely.geometry.point.Point object at 0x...>
@@ -1512,14 +1516,14 @@ Figure 10. Convex hull (blue) of 2 points (left) and of 6 points (right).
   Returns a representation of the point or smallest rectangular polygon (with
   sides parallel to the coordinate axes) that contains the object.
 
-.. sourcecode:: pycon
+.. code-block:: pycon
 
   >>> Point(0, 0).envelope
   <shapely.geometry.point.Point object at 0x...>
   >>> MultiPoint([(0, 0), (1, 1)]).envelope
   <shapely.geometry.polygon.Polygon object at 0x...>
 
-.. method:: object.parallel_offset(distance, side, resolution=16, join_style=1, mitre_limit=1.0)
+.. method:: object.parallel_offset(distance, side, resolution=16, join_style=1, mitre_limit=5.0)
 
   Returns a LineString or MultiLineString geometry at a distance from the
   object on its right or its left side.
@@ -1562,7 +1566,7 @@ the original geometry. By default a slower algorithm is used that preserves
 topology. If preserve topology is set to ``False`` the much quicker
 Douglas-Peucker algorithm [6]_ is used.
 
-.. sourcecode:: pycon
+.. code-block:: pycon
 
   >>> p = Point(0.0, 0.0)
   >>> x = p.buffer(1.0)
@@ -1677,7 +1681,7 @@ preserved or supported by 3D affine transformations.
     x_\mathrm{off} &= x_0 - x_0 \cos{\theta} + y_0 \sin{\theta} \\
     y_\mathrm{off} &= y_0 - x_0 \sin{\theta} - y_0 \cos{\theta}
 
-  .. sourcecode:: pycon
+  .. code-block:: pycon
 
     >>> from shapely import affinity
     >>> line = LineString([(1, 3), (1, 1), (4, 1)])
@@ -1716,7 +1720,7 @@ preserved or supported by 3D affine transformations.
     y_\mathrm{off} &= y_0 - y_0 y_\mathrm{fact} \\
     z_\mathrm{off} &= z_0 - z_0 z_\mathrm{fact}
 
-  .. sourcecode:: pycon
+  .. code-block:: pycon
 
     >>> triangle = Polygon([(1, 1), (2, 3), (3, 1)])
     >>> triangle_a = affinity.scale(triangle, xfact=1.5, yfact=-1)
@@ -1799,7 +1803,7 @@ Shapely supports map projections and other arbitrary transformations of geometri
 For example, here is an identity function applicable to both types of input
 (scalar or array).
 
-.. sourcecode:: python
+.. code-block:: python
 
     def id_func(x, y, z=None):
         return tuple(filter(None, [x, y, z]))
@@ -1809,21 +1813,22 @@ For example, here is an identity function applicable to both types of input
 A partially applied transform function from pyproj satisfies the requirements
 for `func`.
 
-.. sourcecode:: python
+.. code-block:: python
 
+    from shapely.ops import transform
     from functools import partial
     import pyproj
 
     project = partial(
         pyproj.transform,
-        pyproj.Proj(init='espg:4326'),
+        pyproj.Proj(init='epsg:4326'),
         pyproj.Proj(init='epsg:26913'))
 
     g2 = transform(project, g1)
 
 Lambda expressions such as the one in
 
-.. sourcecode:: python
+.. code-block:: python
 
     g2 = transform(lambda x, y, z=None: (x+1.0, y+1.0), g1)
 
@@ -1846,7 +1851,7 @@ using functions in the :mod:`shapely.ops` module.
   As with the :class:`MultiLineString` constructor, the input elements may be
   any line-like object.
 
-  .. sourcecode:: pycon
+  .. code-block:: pycon
 
     >>> from shapely.ops import polygonize
     >>> lines = [
@@ -1878,7 +1883,7 @@ using functions in the :mod:`shapely.ops` module.
 
   `New in version 1.2.18.`
 
-  .. sourcecode:: pycon
+  .. code-block:: pycon
 
     >>> lines = [
     ...     ((0, 0), (1, 1)),
@@ -1905,7 +1910,7 @@ using functions in the :mod:`shapely.ops` module.
   As with :func:`shapely.ops.polygonize`, the input elements may be any
   line-like object.
 
-.. sourcecode:: python
+.. code-block:: python
 
     >>> from shapely.ops import linemerge
     >>> linemerge(lines)
@@ -1927,7 +1932,7 @@ efficient than accumulating with :meth:`union`.
 
   Returns a representation of the union of the given geometric objects.
 
-  .. sourcecode:: pycon
+  .. code-block:: pycon
 
     >>> from shapely.ops import cascaded_union
     >>> polygons = [Point(i, 0).buffer(0.7) for i in range(5)]
@@ -1936,7 +1941,7 @@ efficient than accumulating with :meth:`union`.
 
   The function is particularly useful in dissolving `MultiPolygons`.
 
-  .. sourcecode:: pycon
+  .. code-block:: pycon
 
     >>> m = MultiPolygon(polygons)
     >>> m.area
@@ -1955,6 +1960,65 @@ efficient than accumulating with :meth:`union`.
 
   Returns a representation of the union of the given geometric objects.
 
+Delaunay triangulation
+----------------------
+
+The :func:`~shapely.ops.triangulate` function in `shapely.ops` calculates a
+Delaunay triangulation from a collection of points.
+
+.. plot:: code/triangulate.py
+
+.. function:: shapely.ops.triangulate(geom, tolerance=0.0, edges=False)
+
+   Returns a Delaunary triangulation of the vertices of the input geometry.
+
+   The source may be any geometry type. All vertices of the geometry will be
+   used as the points of the triangulation.
+
+   The `tolerance` keyword argument sets the snapping tolerance used to improve
+   the robustness of the triangulation computation. A tolerance of 0.0 specifies
+   that no snapping will take place.
+
+   If the `edges` keyword argument is `False` a list of `Polygon` triangles
+   will be returned. Otherwise a list of `LineString` edges is returned.
+
+   `New in version  1.4.0`
+
+.. code-block:: pycon
+
+  >>> from shapely.ops import triangulate
+  >>> points = MultiPoint([(0, 0), (1, 1), (0, 2), (2, 2), (3, 1), (1, 0)])
+  >>> triangles = triangulate(points)
+  >>> pprint([triangle.wkt for triangle in triangles])
+  ['POLYGON ((0 2, 0 0, 1 1, 0 2))',
+   'POLYGON ((0 2, 1 1, 2 2, 0 2))',
+   'POLYGON ((2 2, 1 1, 3 1, 2 2))',
+   'POLYGON ((3 1, 1 1, 1 0, 3 1))',
+   'POLYGON ((1 0, 1 1, 0 0, 1 0))']
+
+Nearest points
+--------------
+
+The :func:`~shapely.ops.nearest_points` function in `shapely.ops` calculates
+the nearest points in a pair of geometries.
+
+.. function:: shapely.ops.nearest_points(geom1, geom2)
+
+   Returns a tuple of the nearest points in the input geometries. The points are
+   returned in the same order as the input geometries.
+
+   `New in version 1.4.0`.
+
+.. code-block:: pycon
+
+  >>> from shapely.ops import nearest_points
+  >>> triangle = Polygon([(0, 0), (1, 0), (0.5, 1), (0, 0)])
+  >>> square = Polygon([(0, 2), (1, 2), (1, 3), (0, 3), (0, 2)])
+  >>> [o.wkt for o in nearest_points(triangle, square)]
+  ['POINT (0.5 1)', 'POINT (0.5 2)']
+
+Note that the nearest points may not be existing vertices in the geometries.
+
 Prepared Geometry Operations
 ----------------------------
 
@@ -1968,7 +2032,7 @@ batches of operations.
 To test one polygon containment against a large batch of points, one should
 first use the :func:`prepared.prep` function.
 
-.. sourcecode:: pycon
+.. code-block:: pycon
 
   >>> from shapely.geometry import Point
   >>> from shapely.prepared import prep
@@ -1996,7 +2060,7 @@ Diagnostics
 The messages may or may not have a representation of a problem point that can
 be parsed out.
 
-.. sourcecode:: pycon
+.. code-block:: pycon
 
   >>> coords = [(0, 0), (0, 2), (1, 1), (2, 2), (2, 0), (1, 1), (0, 0)]
   >>> p = Polygon(coords)
@@ -2009,7 +2073,7 @@ accessible via :attr:`shapely.__version__`,
 :attr:`shapely.geos.geos_version_string`, and
 :attr:`shapely.geos.geos_capi_version`.
 
-.. sourcecode:: pycon
+.. code-block:: pycon
 
   >>> import shapely
   >>> shapely.__version__
@@ -2021,6 +2085,37 @@ accessible via :attr:`shapely.__version__`,
   '3.3.0-CAPI-1.7.0'
 
 
+STR-packed R-tree
+=================
+
+Shapely provides an interface to the query-only GEOS R-tree packed using the
+Sort-Tile-Recursive algorithm. Pass a list of geometry objects to the STRtree
+constructor to create an R-tree that you can query with another geometric object.
+
+.. class:: strtree.STRtree(geometries)
+
+  The `STRtree` constructor takes a sequence of geometric objects.
+
+  These are copied and stored in the R-tree.
+
+  `New in version 1.4.0`.
+
+Query-only means in this case that the R-tree, once created, is immutable. You
+cannot add or remove geometries.
+
+.. code-block:: pycon
+
+  >>> from shapely.geometry import Point
+  >>> from shapely.strtree import STRtree
+  >>> points = [Point(i, i) for i in range(10)]
+  >>> tree = STRtree(points)
+  >>> tree.query(Point(2,2).buffer(0.99))
+  >>> [o.wkt for o in tree.query(Point(2,2).buffer(0.99))]
+  ['POINT (2 2)']
+  >>> [o.wkt for o in tree.query(Point(2,2).buffer(1.0))]
+  ['POINT (1 1)', 'POINT (2 2)', 'POINT (3 3)']
+
+
 Interoperation
 ==============
 
@@ -2034,7 +2129,7 @@ any geometric object can be had via its :attr:`wkt` or :attr:`wkb` attribute.
 These representations allow interchange with many GIS programs. PostGIS, for
 example, trades in hex-encoded WKB.
 
-.. sourcecode:: pycon
+.. code-block:: pycon
 
   >>> Point(0, 0).wkt
   'POINT (0.0000000000000000 0.0000000000000000)'
@@ -2055,7 +2150,7 @@ appropriate type, use ``loads()``.
 
   Returns a geometric object from a WKB representation `wkb`.
 
-.. sourcecode:: pycon
+.. code-block:: pycon
 
   >> from shapely.wkb import dumps, loads
   >>> wkb = dumps(Point(0, 0))
@@ -2074,7 +2169,7 @@ All of Shapely's geometry types are supported by these functions.
 
   Returns a geometric object from a WKT representation `wkt`.
 
-.. sourcecode:: pycon
+.. code-block:: pycon
 
   >> wkt = dumps(Point(0, 0))
   >>> print wkt
@@ -2089,7 +2184,7 @@ All geometric objects with coordinate sequences (`Point`, `LinearRing`,
 `LineString`) provide the Numpy array interface and can thereby be converted or
 adapted to Numpy arrays.
 
-.. sourcecode:: pycon
+.. code-block:: pycon
 
   >>> from numpy import array
   >>> array(Point(0, 0))
@@ -2108,7 +2203,7 @@ price of slower Numpy access to the coordinates of Shapely objects.
 The coordinates of the same types of geometric objects can be had as standard
 Python arrays of `x` and `y` values via the :attr:`xy` attribute.
 
-.. sourcecode:: pycon
+.. code-block:: pycon
 
   >>> Point(0, 0).xy
   (array('d', [0.0]), array('d', [0.0]))
@@ -2119,7 +2214,7 @@ The :func:`shapely.geometry.asShape` family of functions can be used to wrap
 Numpy coordinate arrays so that they can then be analyzed using Shapely while
 maintaining their original storage. A 1 x 2 array can be adapted to a point
 
-.. sourcecode:: pycon
+.. code-block:: pycon
 
   >>> from shapely.geometry import asPoint
   >>> pa = asPoint(array([0.0, 0.0]))
@@ -2128,7 +2223,7 @@ maintaining their original storage. A 1 x 2 array can be adapted to a point
 
 and a N x 2 array can be adapted to a line string
 
-.. sourcecode:: pycon
+.. code-block:: pycon
 
   >>> from shapely.geometry import asLineString
   >>> la = asLineString(array([[1.0, 2.0], [3.0, 4.0]]))
@@ -2156,28 +2251,28 @@ adapted and used as a Shapely geometry using the
 
 For example, a dictionary:
 
-.. sourcecode:: pycon
+.. code-block:: pycon
 
-  >>> from shapely.geometry import asShape
-  >>> d = {"type": "Point", "coordinates": (0.0, 0.0)}
-  >>> shape = asShape(d)
-  >>> shape.geom_type
+  >>> from shapely.geometry import shape
+  >>> data = {"type": "Point", "coordinates": (0.0, 0.0)}
+  >>> geom = shape(data)
+  >>> geom.geom_type
   'Point'
-  >>> list(shape.coords)
+  >>> list(geom.coords)
   [(0.0, 0.0)]
 
 Or a simple placemark-type object:
 
-.. sourcecode:: pycon
+.. code-block:: pycon
 
   >>> 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
+  >>> geom = shape(thing)
+  >>> geom.geom_type
   'Point'
-  >>> list(shape.coords)
+  >>> list(geom.coords)
   [(0.0, 0.0)]
 
 The GeoJSON-like mapping of a geometric object can be obtained using
@@ -2192,7 +2287,7 @@ The GeoJSON-like mapping of a geometric object can be obtained using
 
   For example, using the same `GeoThing` class:
 
-.. sourcecode:: pycon
+.. code-block:: pycon
 
   >>> from shapely.geometry import mapping
   >>> thing = GeoThing({"type": "Point", "coordinates": (0.0, 0.0)})
@@ -2222,7 +2317,7 @@ attribute. The constructor speedups are disabled by default. To enable the
 speedups call :func:`enable`. You can revert to the default implementation with
 :func:`disable`.
 
-.. sourcecode:: pycon
+.. code-block:: pycon
 
   >>> from shapely import speedups
   >>> speedups.available
diff --git a/docs/project.txt b/docs/project.rst
similarity index 66%
rename from docs/project.txt
rename to docs/project.rst
index cba758f..ce70278 100644
--- a/docs/project.txt
+++ b/docs/project.rst
@@ -1,4 +1,6 @@
 .. include:: ../README.rst
 
+.. include:: ../CREDITS.txt
+
 .. include:: ../CHANGES.txt
 
diff --git a/requirements-dev.txt b/requirements-dev.txt
index 6f1d903..b16d71b 100644
--- a/requirements-dev.txt
+++ b/requirements-dev.txt
@@ -1,4 +1,5 @@
-setuptools >= 0.9.8
-Numpy==1.8.0
-Cython==0.19.2
+setuptools
+Numpy>=1.8.0
+Cython>=0.19
 descartes==1.0.1
+pytest
diff --git a/setup.py b/setup.py
index 413a474..ea428c0 100755
--- a/setup.py
+++ b/setup.py
@@ -14,6 +14,7 @@ except ImportError:
 from distutils.cmd import Command
 from distutils.errors import CCompilerError, DistutilsExecError, \
     DistutilsPlatformError
+from distutils.sysconfig import get_config_var
 import errno
 import glob
 import os
@@ -22,40 +23,6 @@ import shutil
 import subprocess
 import sys
 
-class test(Command):
-    """Run unit tests after in-place build"""
-    description = __doc__
-    user_options = []
-
-    def initialize_options(self):
-        pass
-
-    def finalize_options(self):
-        pass
-
-    def run(self):
-        # Force an in-place build for testing speedups
-        cmd = self.reinitialize_command('build_ext')
-        setattr(cmd, 'inplace', 1)
-        self.run_command('build_ext')
-
-        if sys.version_info[0:2] <= (2, 6):
-            try:
-                from unittest2 import TextTestRunner, TestLoader
-            except ImportError:
-                raise ImportError(
-                    "unittest2 is required to run tests with python-%d.%d" %
-                    sys.version_info[0:2])
-        else:
-            from unittest import TextTestRunner, TestLoader
-
-        import shapely.tests
-        tests = TestLoader().loadTestsFromName('test_suite', shapely.tests)
-        runner = TextTestRunner(verbosity=2)
-        result = runner.run(tests)
-
-        if not result.wasSuccessful():
-            sys.exit(1)
 
 # Get the version from the shapely module
 version = None
@@ -67,17 +34,22 @@ with open('shapely/__init__.py', 'r') as fp:
 if version is None:
     raise ValueError("Could not determine Shapely's version")
 
-with open('VERSION.txt', 'w') as fp:
+# Handle UTF-8 encoding of certain text files.
+open_kwds = {}
+if sys.version_info > (3,):
+    open_kwds['encoding'] = 'utf-8'
+
+with open('VERSION.txt', 'w', **open_kwds) as fp:
     fp.write(version)
 
-with open('README.rst', 'r') as fp:
+with open('README.rst', 'r', **open_kwds) as fp:
     readme_text = fp.read()
 readme_text = readme_text.replace(".. include:: CREDITS.txt", "")
 
-with open('CREDITS.txt', 'r') as fp:
+with open('CREDITS.txt', 'r', **open_kwds) as fp:
     credits = fp.read()
 
-with open('CHANGES.txt', 'r') as fp:
+with open('CHANGES.txt', 'r', **open_kwds) as fp:
     changes_text = fp.read()
 
 setup_args = dict(
@@ -99,9 +71,9 @@ setup_args = dict(
         'shapely.algorithms',
         'shapely.examples',
         'shapely.speedups',
-        'shapely.tests',
+        'shapely.vectorized',
     ],
-    cmdclass            = {'test': test},
+    cmdclass = {},
     classifiers         = [
         'Development Status :: 5 - Production/Stable',
         'Intended Audience :: Developers',
@@ -113,6 +85,7 @@ setup_args = dict(
         'Programming Language :: Python :: 3',
         'Topic :: Scientific/Engineering :: GIS',
     ],
+    data_files         = [('shapely', ['shapely/_geos.pxi'])]
 )
 
 # Add DLLs for Windows
@@ -152,20 +125,23 @@ class BuildFailed(Exception):
     pass
 
 
-class build_ext(distutils_build_ext):
-    # This class allows C extension building to fail.
+def construct_build_ext(build_ext):
+    class WrappedBuildExt(build_ext):
+        # This class allows C extension building to fail.
+
+        def run(self):
+            try:
+                build_ext.run(self)
+            except DistutilsPlatformError as x:
+                raise BuildFailed(x)
 
-    def run(self):
-        try:
-            distutils_build_ext.run(self)
-        except DistutilsPlatformError as x:
-            raise BuildFailed(x)
+        def build_extension(self, ext):
+            try:
+                build_ext.build_extension(self, ext)
+            except ext_errors as x:
+                raise BuildFailed(x)
+    return WrappedBuildExt
 
-    def build_extension(self, ext):
-        try:
-            distutils_build_ext.build_extension(self, ext)
-        except ext_errors as x:
-            raise BuildFailed(x)
 
 if (hasattr(platform, 'python_implementation')
         and platform.python_implementation() == 'PyPy'):
@@ -177,6 +153,7 @@ elif sys.platform == 'win32':
 else:
     libraries = ['geos_c']
 
+
 if os.path.exists("MANIFEST.in"):
     pyx_file = "shapely/speedups/_speedups.pyx"
     c_file = "shapely/speedups/_speedups.c"
@@ -202,12 +179,36 @@ ext_modules = [
     Extension(
         "shapely.speedups._speedups",
         ["shapely/speedups/_speedups.c"],
-        libraries=libraries)
+        libraries=libraries,
+        include_dirs=[get_config_var('INCLUDEDIR')],),
 ]
 
 try:
+    import numpy as np
+    from Cython.Distutils import build_ext as cython_build_ext
+    from distutils.extension import Extension as DistutilsExtension
+
+    cmd_classes = setup_args.setdefault('cmdclass', {})
+    if 'build_ext' in cmd_classes:
+        raise ValueError('We need to put the Cython build_ext in '
+                         'cmd_classes, but it is already defined.')
+    cmd_classes['build_ext'] = cython_build_ext
+
+    ext_modules.append(DistutilsExtension("shapely.vectorized._vectorized",
+                                 sources=["shapely/vectorized/_vectorized.pyx"],
+                                 libraries=libraries + [np.get_include()],
+                                 include_dirs=[get_config_var('INCLUDEDIR'),
+                                               np.get_include()],
+                                 ))
+except ImportError:
+    print("Numpy or Cython not available, shapely.vectorized submodule not "
+          "being built.")
+
+
+try:
     # try building with speedups
-    setup_args['cmdclass']['build_ext'] = build_ext
+    existing_build_ext = setup_args['cmdclass'].get('build_ext', distutils_build_ext)
+    setup_args['cmdclass']['build_ext'] = construct_build_ext(existing_build_ext)
     setup(
         ext_modules=ext_modules,
         **setup_args
diff --git a/shapely/__init__.py b/shapely/__init__.py
index 67bc602..bf25615 100644
--- a/shapely/__init__.py
+++ b/shapely/__init__.py
@@ -1 +1 @@
-__version__ = "1.3.0"
+__version__ = "1.4.1"
diff --git a/shapely/_geos.pxi b/shapely/_geos.pxi
new file mode 100644
index 0000000..fc22a57
--- /dev/null
+++ b/shapely/_geos.pxi
@@ -0,0 +1,58 @@
+# The beginnings of a Cython definition of GEOS. In the future much of this
+# could be auto-generated.
+
+ctypedef long ptr
+
+
+cdef extern from "geos_c.h":
+    ctypedef void *GEOSContextHandle_t
+    ctypedef struct GEOSGeometry
+    ctypedef struct GEOSCoordSequence
+    ctypedef struct GEOSPreparedGeometry
+    
+    GEOSCoordSequence *GEOSCoordSeq_create_r(GEOSContextHandle_t, unsigned int, unsigned int) nogil
+    GEOSCoordSequence *GEOSGeom_getCoordSeq_r(GEOSContextHandle_t, GEOSGeometry *) nogil
+
+    int GEOSCoordSeq_getSize_r(GEOSContextHandle_t, GEOSCoordSequence *, int *) nogil
+    int GEOSCoordSeq_setX_r(GEOSContextHandle_t, GEOSCoordSequence *, int, double) nogil
+    int GEOSCoordSeq_setY_r(GEOSContextHandle_t, GEOSCoordSequence *, int, double) nogil
+    int GEOSCoordSeq_setZ_r(GEOSContextHandle_t, GEOSCoordSequence *, int, double) nogil
+    int GEOSCoordSeq_getX_r(GEOSContextHandle_t, GEOSCoordSequence *, int, double *) nogil
+    int GEOSCoordSeq_getY_r(GEOSContextHandle_t, GEOSCoordSequence *, int, double *) nogil
+    int GEOSCoordSeq_getZ_r(GEOSContextHandle_t, GEOSCoordSequence *, int, double *) nogil
+
+    GEOSGeometry *GEOSGeom_createPoint_r(GEOSContextHandle_t, GEOSCoordSequence *) nogil
+    GEOSGeometry *GEOSGeom_createLineString_r(GEOSContextHandle_t, GEOSCoordSequence *) nogil
+    GEOSGeometry *GEOSGeom_createLinearRing_r(GEOSContextHandle_t, GEOSCoordSequence *) nogil
+    GEOSGeometry *GEOSGeom_clone_r(GEOSContextHandle_t, GEOSGeometry *) nogil
+    GEOSCoordSequence *GEOSCoordSeq_clone_r(GEOSContextHandle_t, GEOSCoordSequence *) nogil
+
+    void GEOSGeom_destroy_r(GEOSContextHandle_t, GEOSGeometry *) nogil
+
+    char GEOSPreparedContains_r(GEOSContextHandle_t, const GEOSPreparedGeometry*, const GEOSGeometry*) nogil
+    char GEOSPreparedContainsProperly_r(GEOSContextHandle_t, const GEOSPreparedGeometry*, const GEOSGeometry*) nogil
+    char GEOSPreparedCoveredBy_r(GEOSContextHandle_t, const GEOSPreparedGeometry*, const GEOSGeometry*) nogil
+    char GEOSPreparedCovers_r(GEOSContextHandle_t, const GEOSPreparedGeometry*, const GEOSGeometry*) nogil
+    char GEOSPreparedCrosses_r(GEOSContextHandle_t, const GEOSPreparedGeometry*, const GEOSGeometry*) nogil
+    char GEOSPreparedDisjoint_r(GEOSContextHandle_t, const GEOSPreparedGeometry*, const GEOSGeometry*) nogil
+    char GEOSPreparedIntersects_r(GEOSContextHandle_t, const GEOSPreparedGeometry*, const GEOSGeometry*) nogil
+    char GEOSPreparedOverlaps_r(GEOSContextHandle_t, const GEOSPreparedGeometry*, const GEOSGeometry*) nogil
+    char GEOSPreparedTouches_r(GEOSContextHandle_t, const GEOSPreparedGeometry*, const GEOSGeometry*) nogil
+    char GEOSPreparedWithin_r(GEOSContextHandle_t, const GEOSPreparedGeometry*, const GEOSGeometry*) nogil
+
+    char GEOSHasZ_r(GEOSContextHandle_t, GEOSGeometry *) nogil
+    char GEOSisRing_r(GEOSContextHandle_t, GEOSGeometry *) nogil
+    char GEOSisClosed_r(GEOSContextHandle_t, GEOSGeometry *) nogil
+
+
+cdef GEOSContextHandle_t get_geos_context_handle():
+    # Note: This requires that lgeos is defined, so needs to be imported as:
+    from shapely.geos import lgeos
+    cdef ptr handle = lgeos.geos_handle
+    return <GEOSContextHandle_t>handle
+
+
+cdef GEOSPreparedGeometry *geos_from_prepared(shapely_geom) except *:
+    """Get the Prepared GEOS geometry pointer from the given shapely geometry."""
+    cdef ptr geos_geom = shapely_geom._geom
+    return <GEOSPreparedGeometry *>geos_geom
diff --git a/shapely/affinity.py b/shapely/affinity.py
index 794bb30..a45ca3c 100755
--- a/shapely/affinity.py
+++ b/shapely/affinity.py
@@ -77,7 +77,7 @@ def affine_transform(geom, matrix):
                 yield (xp, yp, zp)
 
     # Process coordinates from each supported geometry type
-    if geom.type in ('Point', 'LineString'):
+    if geom.type in ('Point', 'LineString', 'LinearRing'):
         return type(geom)(list(affine_pts(geom.coords)))
     elif geom.type == 'Polygon':
         ring = geom.exterior
diff --git a/shapely/coords.py b/shapely/coords.py
index b2c5dda..c37ffc0 100644
--- a/shapely/coords.py
+++ b/shapely/coords.py
@@ -21,8 +21,13 @@ def required(ob):
     """Return an object that meets Shapely requirements for self-owned
     C-continguous data, copying if necessary, or just return the original
     object."""
-    if has_numpy and hasattr(ob, '__array_interface__'):
-        return numpy.require(ob, numpy.float64, ["C", "OWNDATA"])
+    if (hasattr(ob, '__array_interface__')
+            and ob.__array_interface__.get('strides')):
+        if has_numpy:
+            return numpy.require(ob, numpy.float64, ["C", "OWNDATA"])
+        else:
+            # raise an error if strided. See issue #52.
+            raise ValueError("C-contiguous data is required")
     else:
         return ob
 
diff --git a/shapely/ctypes_declarations.py b/shapely/ctypes_declarations.py
index c41e922..2c0a305 100644
--- a/shapely/ctypes_declarations.py
+++ b/shapely/ctypes_declarations.py
@@ -4,7 +4,7 @@ See header file: geos-x.y.z/capi/geos_c.h
 '''
 
 from ctypes import CFUNCTYPE, POINTER, c_void_p, c_char_p, \
-    c_size_t, c_byte, c_char, c_uint, c_int, c_double
+    c_size_t, c_byte, c_char, c_uint, c_int, c_double, py_object
 
 # Derived pointer types
 c_size_t_p = POINTER(c_size_t)
@@ -66,6 +66,9 @@ def prototype(lgeos, geos_version):
     lgeos.GEOSCoordSeq_clone.restype = c_void_p
     lgeos.GEOSCoordSeq_clone.argtypes = [c_void_p]
 
+    lgeos.GEOSGeom_clone.restype = c_void_p
+    lgeos.GEOSGeom_clone.argtypes = [c_void_p]
+
     lgeos.GEOSCoordSeq_destroy.restype = None
     lgeos.GEOSCoordSeq_destroy.argtypes = [c_void_p]
 
@@ -206,6 +209,11 @@ def prototype(lgeos, geos_version):
         lgeos.GEOSPolygonize_full.restype = c_void_p
         lgeos.GEOSPolygonize_full.argtypes = [c_void_p, c_void_p, c_void_p, c_void_p]
 
+    if geos_version >= (3, 4, 0):
+        lgeos.GEOSDelaunayTriangulation.restype = c_void_p
+        lgeos.GEOSDelaunayTriangulation.argtypes = [c_void_p, c_double, c_int]
+
+
     lgeos.GEOSLineMerge.restype = c_void_p
     lgeos.GEOSLineMerge.argtypes = [c_void_p]
 
@@ -268,6 +276,9 @@ def prototype(lgeos, geos_version):
     lgeos.GEOSisRing.restype = c_byte
     lgeos.GEOSisRing.argtypes = [c_void_p]
 
+    lgeos.GEOSisClosed.restype = c_byte
+    lgeos.GEOSisClosed.argtypes = [c_void_p]
+
     lgeos.GEOSHasZ.restype = c_byte
     lgeos.GEOSHasZ.argtypes = [c_void_p]
 
@@ -453,3 +464,26 @@ def prototype(lgeos, geos_version):
 
         lgeos.GEOSFree.restype = None
         lgeos.GEOSFree.argtypes = [c_void_p]
+
+    if geos_version >= (3, 4, 0):
+        lgeos.GEOSNearestPoints.restype = c_void_p
+        lgeos.GEOSNearestPoints.argtypes = [c_void_p, c_void_p]
+
+    if geos_version >= (3, 4, 2):
+        lgeos.GEOSQueryCallback = CFUNCTYPE(None, c_void_p, c_void_p)
+
+        lgeos.GEOSSTRtree_query.argtypes = [c_void_p, c_void_p, lgeos.GEOSQueryCallback, py_object]
+        lgeos.GEOSSTRtree_query.restype = None
+
+        lgeos.GEOSSTRtree_create.argtypes = [c_int]
+        lgeos.GEOSSTRtree_create.restype = c_void_p
+
+        lgeos.GEOSSTRtree_insert.argtypes = [c_void_p, c_void_p, py_object]
+        lgeos.GEOSSTRtree_insert.restype = None
+
+        lgeos.GEOSSTRtree_remove.argtypes = [c_void_p, c_void_p, py_object]
+        lgeos.GEOSSTRtree_remove.restype = None
+
+        lgeos.GEOSSTRtree_destroy.argtypes = [c_void_p]
+        lgeos.GEOSSTRtree_destroy.restype = None
+
diff --git a/shapely/examples/world.py b/shapely/examples/world.py
deleted file mode 100644
index 81b9af8..0000000
--- a/shapely/examples/world.py
+++ /dev/null
@@ -1,21 +0,0 @@
-import ogr
-import pylab
-from numpy import asarray
-
-from shapely.wkb import loads
-
-source = ogr.Open("/var/gis/data/world/world_borders.shp")
-borders = source.GetLayerByName("world_borders")
-
-fig = pylab.figure(1, figsize=(4,2), dpi=300)
-
-while 1:
-    feature = borders.GetNextFeature()
-    if not feature:
-        break
-    
-    geom = loads(feature.GetGeometryRef().ExportToWkb())
-    a = asarray(geom)
-    pylab.plot(a[:,0], a[:,1])
-
-pylab.show()
diff --git a/shapely/geometry/base.py b/shapely/geometry/base.py
index ed1f4cf..a62b40f 100644
--- a/shapely/geometry/base.py
+++ b/shapely/geometry/base.py
@@ -115,6 +115,25 @@ def geom_to_wkb(ob):
     return lgeos.GEOSGeomToWKB_buf(c_void_p(ob._geom), pointer(size))
 
 
+def geos_geom_from_py(ob, create_func=None):
+    """Helper function for geos_*_from_py functions in each geom type.
+
+    If a create_func is specified the coodinate sequence is cloned and a new
+    geometry is created with it, otherwise the geometry is cloned directly.
+    This behaviour is useful for converting between LineString and LinearRing
+    objects.
+    """
+    if create_func is None:
+        geom = lgeos.GEOSGeom_clone(ob._geom)
+    else:
+        cs = lgeos.GEOSGeom_getCoordSeq(ob._geom)
+        cs = lgeos.GEOSCoordSeq_clone(cs)
+        geom = create_func(cs)
+
+    N = ob._ndim
+
+    return geom, N
+
 def exceptNull(func):
     """Decorator which helps avoid GEOS operations on null pointers."""
     @wraps(func)
@@ -160,7 +179,7 @@ class BaseGeometry(object):
     # _crs : object
     #     Coordinate reference system. Available for Shapely extensions, but
     #     not implemented here.
-    # _owned : bool
+    # _other_owned : bool
     #     True if this object's GEOS geometry is owned by another as in the
     #     case of a multipart geometry member.
     __geom__ = EMPTY
@@ -168,7 +187,7 @@ class BaseGeometry(object):
     _ctypes_data = None
     _ndim = None
     _crs = None
-    _owned = False
+    _other_owned = False
 
     # Backend config
     impl = DefaultImplementation
@@ -180,19 +199,18 @@ class BaseGeometry(object):
     # a reference to the so/dll proxy to preserve access during clean up
     _lgeos = lgeos
 
-    def empty(self):
+    def empty(self, val=EMPTY):
         # TODO: defer cleanup to the implementation. We shouldn't be
         # explicitly calling a lgeos method here.
-        if not (self._owned or self._is_empty):
+        if not self._is_empty and not self._other_owned and self.__geom__:
             try:
                 self._lgeos.GEOSGeom_destroy(self.__geom__)
             except AttributeError:
                 pass  # _lgeos might be empty on shutdown
-        self.__geom__ = EMPTY
+        self.__geom__ = val
 
     def __del__(self):
-        self.empty()
-        self.__geom__ = None
+        self.empty(val=None)
         self.__p__ = None
 
     def __str__(self):
@@ -324,6 +342,36 @@ class BaseGeometry(object):
         """WKB hex representation of the geometry"""
         return WKBWriter(lgeos).write_hex(self)
 
+    def svg(self, scale_factor=1.):
+        """
+        SVG representation of the geometry. Scale factor is multiplied by
+        the size of the SVG symbol so it can be scaled consistently for a
+        consistent appearance based on the canvas size.
+        """
+        raise NotImplementedError
+
+    def _repr_svg_(self):
+        """SVG representation for iPython notebook"""
+        #Pick an arbitrary size for the SVG canvas
+
+
+        xmin, ymin, xmax, ymax = self.buffer(1).bounds
+        x_size = min([max([100., xmax - xmin]), 300])
+        y_size = min([max([100., ymax - ymin]), 300])
+        try:
+            scale_factor = max([xmax - xmin, ymax - ymin]) / max([x_size, y_size])
+        except ZeroDivisionError:
+            scale_factor = 1
+        buffered_box = "{0} {1} {2} {3}".format(xmin, ymin, xmax - xmin, ymax - ymin)
+        return """<svg
+            preserveAspectRatio="xMinYMin meet"
+            viewBox="{0}"
+            width="{1}"
+            height="{2}"
+            transform="translate(0, {1}),scale(1, -1)">
+            {3}
+            </svg>""".format(buffered_box, x_size, y_size, self.svg(scale_factor))
+
     @property
     def geom_type(self):
         """Name of the geometry's type, such as 'Point'"""
@@ -394,7 +442,7 @@ class BaseGeometry(object):
 
     def buffer(self, distance, resolution=16, quadsegs=None,
                cap_style=CAP_STYLE.round, join_style=JOIN_STYLE.round,
-               mitre_limit=0):
+               mitre_limit=5.0):
         """Returns a geometry with an envelope at a distance from the object's
         envelope
 
@@ -433,7 +481,6 @@ class BaseGeometry(object):
           >>> g.buffer(1.0, cap_style='square').area
           4.0
         """
-
         if quadsegs is not None:
             warn(
                 "The `quadsegs` argument is deprecated. Use `resolution`.",
@@ -441,7 +488,9 @@ class BaseGeometry(object):
             res = quadsegs
         else:
             res = resolution
-
+        if mitre_limit == 0.0:
+            raise ValueError(
+                'Cannot compute offset from zero-length line segment')
         if cap_style == CAP_STYLE.round and join_style == JOIN_STYLE.round:
             return geom_factory(self.impl['buffer'](self, distance, res))
 
@@ -510,6 +559,11 @@ class BaseGeometry(object):
         return bool(self.impl['is_ring'](self))
 
     @property
+    def is_closed(self):
+        """True if the geometry is closed, else False"""
+        return bool(self.impl['is_closed'](self))
+
+    @property
     def is_simple(self):
         """True if the geometry is simple, meaning that any self-intersections
         are only at boundary points, else False"""
@@ -657,6 +711,14 @@ class BaseMultipartGeometry(BaseGeometry):
         else:
             return ()[index]
 
+    def svg(self, scale_factor=1.):
+        """
+        SVG representation of the geometry. Scale factor is multiplied by
+        the size of the SVG symbol so it can be scaled consistently for a
+        consistent appearance based on the canvas size.
+        """
+        return "\n".join([g.svg(scale_factor) for g in self])
+
 
 class GeometrySequence(object):
     """
@@ -688,7 +750,7 @@ class GeometrySequence(object):
 
     def _get_geom_item(self, i):
         g = self.shape_factory()
-        g._owned = True
+        g._other_owned = True
         g._geom = lgeos.GEOSGetGeometryN(self._geom, i)
         g._ndim = self._ndim
         g.__p__ = self
@@ -746,7 +808,7 @@ class HeterogeneousGeometrySequence(GeometrySequence):
     def _get_geom_item(self, i):
         sub = lgeos.GEOSGetGeometryN(self._geom, i)
         g = geom_factory(sub, parent=self)
-        g._owned = True
+        g._other_owned = True
         return g
 
 
diff --git a/shapely/geometry/linestring.py b/shapely/geometry/linestring.py
index 3af8197..dc39d8b 100644
--- a/shapely/geometry/linestring.py
+++ b/shapely/geometry/linestring.py
@@ -10,8 +10,11 @@ from ctypes import c_double, cast, POINTER
 
 from shapely.coords import required
 from shapely.geos import lgeos, TopologicalError
-from shapely.geometry.base import BaseGeometry, geom_factory, JOIN_STYLE
+from shapely.geometry.base import (
+    BaseGeometry, geom_factory, JOIN_STYLE, geos_geom_from_py
+)
 from shapely.geometry.proxy import CachingGeometryProxy
+from shapely.geometry.point import Point
 
 __all__ = ['LineString', 'asLineString']
 
@@ -52,6 +55,24 @@ class LineString(BaseGeometry):
             'coordinates': tuple(self.coords)
             }
 
+    def svg(self, scale_factor=1.):
+        """
+        SVG representation of the geometry. Scale factor is multiplied by
+        the size of the SVG symbol so it can be scaled consistently for a
+        consistent appearance based on the canvas size.
+        """
+        pnt_format = " ".join(["{0},{1}".format(*c) for c in self.coords])
+        return """<polyline
+            fill="none"
+            stroke="{2}"
+            stroke-width={1}
+            points="{0}"
+            opacity=".8"
+            />""".format(
+                pnt_format,
+                2.*scale_factor, 
+                "#66cc99" if self.is_valid else "#ff3333")
+
     @property
     def ctypes(self):
         if not self._ctypes_data:
@@ -87,7 +108,7 @@ class LineString(BaseGeometry):
 
     def parallel_offset(
             self, distance, side,
-            resolution=16, join_style=JOIN_STYLE.round, mitre_limit=1.0):
+            resolution=16, join_style=JOIN_STYLE.round, mitre_limit=5.0):
 
         """Returns a LineString or MultiLineString geometry at a distance from
         the object on its right or its left side.
@@ -107,12 +128,14 @@ class LineString(BaseGeometry):
         far beyond the original geometry. To prevent unreasonable geometry, the
         mitre limit allows controlling the maximum length of the join corner.
         Corners with a ratio which exceed the limit will be beveled."""
-
+        if mitre_limit == 0.0:
+            raise ValueError(
+                'Cannot compute offset from zero-length line segment')
         try:
             return geom_factory(self.impl['parallel_offset'](
                 self, distance, resolution, join_style, mitre_limit,
                 bool(side == 'left')))
-        except WindowsError:
+        except OSError:
             raise TopologicalError()
 
 
@@ -157,6 +180,14 @@ def asLineString(context):
 
 
 def geos_linestring_from_py(ob, update_geom=None, update_ndim=0):
+    # If a LineString is passed in, clone it and return
+    # If a LinearRing is passed in, clone the coord seq and return a LineString
+    if isinstance(ob, LineString):
+        if type(ob) == LineString:
+            return geos_geom_from_py(ob)
+        else:
+            return geos_geom_from_py(ob, lgeos.GEOSGeom_createLineString)
+
     # If numpy is present, we use numpy.require to ensure that we have a
     # C-continguous array that owns its data. View data will be copied.
     ob = required(ob)
@@ -221,8 +252,15 @@ def geos_linestring_from_py(ob, update_geom=None, update_ndim=0):
         if m < 2:
             raise ValueError(
                 "LineStrings must have at least 2 coordinate tuples")
+
+        def _coords(o):
+            if isinstance(o, Point):
+                return o.coords[0]
+            else:
+                return o
+
         try:
-            n = len(ob[0])
+            n = len(_coords(ob[0]))
         except TypeError:
             raise ValueError(
                 "Input %s is the wrong shape for a LineString" % str(ob))
@@ -240,7 +278,7 @@ def geos_linestring_from_py(ob, update_geom=None, update_ndim=0):
 
         # add to coordinate sequence
         for i in range(m):
-            coords = ob[i]
+            coords = _coords(ob[i])
             # Because of a bug in the GEOS C API,
             # always set X before Y
             lgeos.GEOSCoordSeq_setX(cs, i, coords[0])
diff --git a/shapely/geometry/multilinestring.py b/shapely/geometry/multilinestring.py
index b78d174..497a405 100644
--- a/shapely/geometry/multilinestring.py
+++ b/shapely/geometry/multilinestring.py
@@ -9,8 +9,8 @@ if sys.version_info[0] < 3:
 from ctypes import c_double, c_void_p, cast, POINTER
 
 from shapely.geos import lgeos
-from shapely.geometry.base import BaseMultipartGeometry
-from shapely.geometry.linestring import LineString, geos_linestring_from_py
+from shapely.geometry.base import BaseMultipartGeometry, geos_geom_from_py
+from shapely.geometry import linestring
 from shapely.geometry.proxy import CachingGeometryProxy
 
 __all__ = ['MultiLineString', 'asMultiLineString']
@@ -52,7 +52,7 @@ class MultiLineString(BaseMultipartGeometry):
             self._geom, self._ndim = geos_multilinestring_from_py(lines)
 
     def shape_factory(self, *args):
-        return LineString(*args)
+        return linestring.LineString(*args)
 
     @property
     def __geo_interface__(self):
@@ -61,11 +61,32 @@ class MultiLineString(BaseMultipartGeometry):
             'coordinates': tuple(tuple(c for c in g.coords) for g in self.geoms)
             }
 
+    def svg(self, scale_factor=1.):
+        """
+        SVG representation of the geometry. Scale factor is multiplied by
+        the size of the SVG symbol so it can be scaled consistently for a
+        consistent appearance based on the canvas size.
+        """
+        parts = []
+        for part in self.geoms:
+            pnt_format = " ".join(["{0},{1}".format(*c) for c in part.coords])
+            parts.append("""<polyline
+                fill="none"
+                stroke="{2}"
+                stroke-width={1}
+                points="{0}"
+                opacity=".8"
+                />""".format(
+                    pnt_format,
+                    2.*scale_factor,
+                    "#66cc99" if self.is_valid else "#ff3333"))
+        return "\n".join(parts)
+
 
 class MultiLineStringAdapter(CachingGeometryProxy, MultiLineString):
     
     context = None
-    _owned = False
+    _other_owned = False
 
     def __init__(self, context):
         self.context = context
@@ -90,7 +111,12 @@ def asMultiLineString(context):
 
 
 def geos_multilinestring_from_py(ob):
-    # ob must be either a sequence or array of sequences or arrays
+    # ob must be either a MultiLineString, a sequence, or 
+    # array of sequences or arrays
+    
+    if isinstance(ob, MultiLineString):
+         return geos_geom_from_py(ob)
+
     try:
         # From array protocol
         array = ob.__array_interface__
@@ -102,9 +128,14 @@ def geos_multilinestring_from_py(ob):
         subs = (c_void_p * L)()
 
         for l in range(L):
-            geom, ndims = geos_linestring_from_py(array['data'][l])
+            geom, ndims = linestring.geos_linestring_from_py(array['data'][l])
             subs[i] = cast(geom, c_void_p)
-        N = lgeos.GEOSGeom_getDimensions(subs[0])
+
+        if lgeos.GEOSHasZ(subs[0]):
+            N = 3
+        else:
+            N = 2
+
     except (NotImplementedError, AttributeError):
         obs = getattr(ob, 'geoms', ob)
         L = len(obs)
@@ -121,7 +152,7 @@ def geos_multilinestring_from_py(ob):
         
         # add to coordinate sequence
         for l in range(L):
-            geom, ndims = geos_linestring_from_py(obs[l])
+            geom, ndims = linestring.geos_linestring_from_py(obs[l])
             subs[l] = cast(geom, c_void_p)
             
     return (lgeos.GEOSGeom_createCollection(5, subs, L), N)
diff --git a/shapely/geometry/multipoint.py b/shapely/geometry/multipoint.py
index 1e43117..633c65b 100644
--- a/shapely/geometry/multipoint.py
+++ b/shapely/geometry/multipoint.py
@@ -11,8 +11,10 @@ from ctypes import ArgumentError
 
 from shapely.coords import required
 from shapely.geos import lgeos
-from shapely.geometry.base import BaseMultipartGeometry, exceptNull
-from shapely.geometry.point import Point, geos_point_from_py
+from shapely.geometry.base import (
+    BaseMultipartGeometry, exceptNull, geos_geom_from_py
+)
+from shapely.geometry import point
 from shapely.geometry.proxy import CachingGeometryProxy
 
 __all__ = ['MultiPoint', 'asMultiPoint']
@@ -58,7 +60,7 @@ class MultiPoint(BaseMultipartGeometry):
             self._geom, self._ndim = geos_multipoint_from_py(points)
 
     def shape_factory(self, *args):
-        return Point(*args)
+        return point.Point(*args)
 
     @property
     def __geo_interface__(self):
@@ -67,6 +69,29 @@ class MultiPoint(BaseMultipartGeometry):
             'coordinates': tuple([g.coords[0] for g in self.geoms])
             }
 
+    def svg(self, scale_factor=1.):
+        """
+        SVG representation of the geometry. Scale factor is multiplied by
+        the size of the SVG symbol so it can be scaled consistently for a
+        consistent appearance based on the canvas size.
+        """
+        
+        parts = []
+        for part in self.geoms:
+            parts.append("""<circle
+            cx="{0.x}"
+            cy="{0.y}"
+            r="{1}"
+            stroke="#555555"
+            stroke-width="{2}"
+            fill="{3}"
+            opacity=".6"
+            />""".format(
+                part,
+                3*scale_factor,
+                1*scale_factor, "#66cc99" if self.is_valid else "#ff3333"))
+        return "\n".join(parts)
+
     @property
     @exceptNull
     def ctypes(self):
@@ -101,7 +126,7 @@ class MultiPoint(BaseMultipartGeometry):
 class MultiPointAdapter(CachingGeometryProxy, MultiPoint):
 
     context = None
-    _owned = False
+    _other_owned = False
 
     def __init__(self, context):
         self.context = context
@@ -134,6 +159,9 @@ def asMultiPoint(context):
 
 
 def geos_multipoint_from_py(ob):
+    if isinstance(ob, MultiPoint):
+        return geos_geom_from_py(ob)
+
     # If numpy is present, we use numpy.require to ensure that we have a
     # C-continguous array that owns its data. View data will be copied.
     ob = required(ob)
@@ -157,7 +185,7 @@ def geos_multipoint_from_py(ob):
         subs = (c_void_p * m)()
 
         for i in range(m):
-            geom, ndims = geos_point_from_py(cp[n*i:n*i+2])
+            geom, ndims = point.geos_point_from_py(cp[n*i:n*i+2])
             subs[i] = cast(geom, c_void_p)
 
     except AttributeError:
@@ -175,7 +203,7 @@ def geos_multipoint_from_py(ob):
         # add to coordinate sequence
         for i in range(m):
             coords = ob[i]
-            geom, ndims = geos_point_from_py(coords)
+            geom, ndims = point.geos_point_from_py(coords)
             subs[i] = cast(geom, c_void_p)
             
     return lgeos.GEOSGeom_createCollection(4, subs, m), n
diff --git a/shapely/geometry/multipolygon.py b/shapely/geometry/multipolygon.py
index e2d0f58..3e645de 100644
--- a/shapely/geometry/multipolygon.py
+++ b/shapely/geometry/multipolygon.py
@@ -9,8 +9,8 @@ if sys.version_info[0] < 3:
 from ctypes import c_void_p, cast
 
 from shapely.geos import lgeos
-from shapely.geometry.base import BaseMultipartGeometry
-from shapely.geometry.polygon import Polygon, geos_polygon_from_py
+from shapely.geometry.base import BaseMultipartGeometry, geos_geom_from_py
+from shapely.geometry import polygon
 from shapely.geometry.proxy import CachingGeometryProxy
 
 __all__ = ['MultiPolygon', 'asMultiPolygon']
@@ -64,7 +64,7 @@ class MultiPolygon(BaseMultipartGeometry):
             self._geom, self._ndim = geos_multipolygon_from_py(polygons)
 
     def shape_factory(self, *args):
-        return Polygon(*args)
+        return polygon.Polygon(*args)
 
     @property
     def __geo_interface__(self):
@@ -80,11 +80,35 @@ class MultiPolygon(BaseMultipartGeometry):
             'coordinates': allcoords
             }
 
+    def svg(self, scale_factor=1.):
+        """
+        SVG representation of the geometry. Scale factor is multiplied by
+        the size of the SVG symbol so it can be scaled consistently for a
+        consistent appearance based on the canvas size.
+        """
+        parts = []
+        for part in self.geoms:
+            exterior_coords = [["{0},{1}".format(*c) for c in part.exterior.coords]]
+            interior_coords = [
+                ["{0},{1}".format(*c) for c in interior.coords]
+                for interior in part.interiors ]
+            path = " ".join([
+                "M {0} L {1} z".format(coords[0], " L ".join(coords[1:]))
+                for coords in exterior_coords + interior_coords ])
+            parts.append(
+                """<g fill-rule="evenodd" fill="{2}" stroke="#555555"
+                stroke-width="{0}" opacity="0.6">
+                <path d="{1}" /></g>""".format(
+                    2. * scale_factor,
+                    path,
+                    "#66cc99" if self.is_valid else "#ff3333"))
+        return "\n".join(parts)
+
 
 class MultiPolygonAdapter(CachingGeometryProxy, MultiPolygon):
     
     context = None
-    _owned = False
+    _other_owned = False
 
     def __init__(self, context, context_type='polygons'):
         self.context = context
@@ -121,13 +145,21 @@ def geos_multipolygon_from_py(ob):
 
     subs = (c_void_p * L)()
     for l in range(L):
-        geom, ndims = geos_polygon_from_py(ob[l][0], ob[l][1:])
+        geom, ndims = polygon.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."""
+    """
+    ob must be either a MultiPolygon, sequence or array of sequences 
+    or arrays.
+    
+    """
+    if isinstance(ob, MultiPolygon):
+        return geos_geom_from_py(ob)
+
     obs = getattr(ob, 'geoms', None) or ob
     L = len(obs)
     assert L >= 1
@@ -148,7 +180,7 @@ def geos_multipolygon_from_polygons(ob):
         holes = getattr(obs[l], 'interiors', None)
         if holes is None:
             holes =  obs[l][1]
-        geom, ndims = geos_polygon_from_py(shell, holes)
+        geom, ndims = polygon.geos_polygon_from_py(shell, holes)
         subs[l] = cast(geom, c_void_p)
             
     return (lgeos.GEOSGeom_createCollection(6, subs, L), N)
diff --git a/shapely/geometry/point.py b/shapely/geometry/point.py
index e8f0605..d7660d6 100644
--- a/shapely/geometry/point.py
+++ b/shapely/geometry/point.py
@@ -6,7 +6,7 @@ from ctypes import cast, POINTER
 
 from shapely.coords import required
 from shapely.geos import lgeos, DimensionError
-from shapely.geometry.base import BaseGeometry
+from shapely.geometry.base import BaseGeometry, geos_geom_from_py
 from shapely.geometry.proxy import CachingGeometryProxy
 
 __all__ = ['Point', 'asPoint']
@@ -74,6 +74,26 @@ class Point(BaseGeometry):
             'coordinates': self.coords[0]
             }
 
+    def svg(self, scale_factor=1.):
+        """
+        SVG representation of the geometry. Scale factor is multiplied by
+        the size of the SVG symbol so it can be scaled consistently for a
+        consistent appearance based on the canvas size.
+        """
+        return """<circle
+            cx="{0.x}"
+            cy="{0.y}"
+            r="{1}"
+            stroke="#555555"
+            stroke-width="{2}"
+            fill="{3}"
+            opacity=".6"
+            />""".format(
+                self,
+                3 * scale_factor,
+                1 * scale_factor,
+                "#66cc99" if self.is_valid else "#ff3333")
+
     @property
     def ctypes(self):
         if not self._ctypes_data:
@@ -126,7 +146,7 @@ class Point(BaseGeometry):
 
 class PointAdapter(CachingGeometryProxy, Point):
 
-    _owned = False
+    _other_owned = False
 
     def __init__(self, context):
         self.context = context
@@ -166,11 +186,14 @@ def asPoint(context):
 
 
 def geos_point_from_py(ob, update_geom=None, update_ndim=0):
-    """Create a GEOS geom from an object that is a coordinate sequence
+    """Create a GEOS geom from an object that is a Point, a coordinate sequence
     or that provides the array interface.
 
     Returns the GEOS geometry and the number of its dimensions.
     """
+    if isinstance(ob, Point):
+        return geos_geom_from_py(ob)
+
     # If numpy is present, we use numpy.require to ensure that we have a
     # C-continguous array that owns its data. View data will be copied.
     ob = required(ob)
diff --git a/shapely/geometry/polygon.py b/shapely/geometry/polygon.py
index d4e1f43..4e6355b 100644
--- a/shapely/geometry/polygon.py
+++ b/shapely/geometry/polygon.py
@@ -13,7 +13,7 @@ import weakref
 from shapely.algorithms.cga import signed_area
 from shapely.coords import required
 from shapely.geos import lgeos
-from shapely.geometry.base import BaseGeometry
+from shapely.geometry.base import BaseGeometry, geos_geom_from_py
 from shapely.geometry.linestring import LineString, LineStringAdapter
 from shapely.geometry.proxy import PolygonProxy
 
@@ -178,7 +178,7 @@ class InteriorRingSequence(object):
             ring = LinearRing()
             ring.__geom__ = g
             ring.__p__ = self
-            ring._owned = True
+            ring._other_owned = True
             ring._ndim = self._ndim
             self.__rings__[i] = weakref.ref(ring)
         return self.__rings__[i]()
@@ -237,7 +237,7 @@ class Polygon(BaseGeometry):
             ring = LinearRing()
             ring.__geom__ = g
             ring.__p__ = self
-            ring._owned = True
+            ring._other_owned = True
             ring._ndim = self._ndim
             self._exterior = weakref.ref(ring)
         return self._exterior()
@@ -282,6 +282,26 @@ class Polygon(BaseGeometry):
             'coordinates': tuple(coords)
             }
 
+    def svg(self, scale_factor=1.):
+        """
+        SVG representation of the geometry. Scale factor is multiplied by
+        the size of the SVG symbol so it can be scaled consistently for a
+        consistent appearance based on the canvas size.
+        """
+        exterior_coords = [["{0},{1}".format(*c) for c in self.exterior.coords]]
+        interior_coords = [
+            ["{0},{1}".format(*c) for c in interior.coords]
+            for interior in self.interiors ]
+        path = " ".join([
+            "M {0} L {1} z".format(coords[0], " L ".join(coords[1:]))
+            for coords in exterior_coords + interior_coords ])
+        return """
+            <g fill-rule="evenodd" fill="{2}" stroke="#555555" 
+            stroke-width="{0}" opacity="0.6">
+            <path d="{1}" />
+            </g>""".format(
+                2.*scale_factor, path, "#66cc99" if self.is_valid else "#ff3333")
+
 
 class PolygonAdapter(PolygonProxy, Polygon):
     
@@ -324,6 +344,15 @@ def orient(polygon, sign=1.0):
     return Polygon(rings[0], rings[1:])
 
 def geos_linearring_from_py(ob, update_geom=None, update_ndim=0):
+    # If a LinearRing is passed in, clone it and return
+    # If a LineString is passed in, clone the coord seq and return a LinearRing
+    if isinstance(ob, LineString):
+        if type(ob) == LinearRing:
+            return geos_geom_from_py(ob)
+        else:
+            if ob.is_closed and len(ob.coords) >= 4:
+                return geos_geom_from_py(ob, lgeos.GEOSGeom_createLinearRing)
+
     # If numpy is present, we use numpy.require to ensure that we have a
     # C-continguous array that owns its data. View data will be copied.
     ob = required(ob)
@@ -441,6 +470,9 @@ def update_linearring_from_py(geom, ob):
     geos_linearring_from_py(ob, geom._geom, geom._ndim)
 
 def geos_polygon_from_py(shell, holes=None):
+    if isinstance(shell, Polygon):
+        return geos_geom_from_py(shell)
+
     if shell is not None:
         geos_shell, ndim = geos_linearring_from_py(shell)
         if holes is not None and len(holes) > 0:
diff --git a/shapely/geometry/proxy.py b/shapely/geometry/proxy.py
index a89c690..c9cad3a 100644
--- a/shapely/geometry/proxy.py
+++ b/shapely/geometry/proxy.py
@@ -19,10 +19,10 @@ class CachingGeometryProxy(object):
     def _is_empty(self):
         return self.__geom__ in [EMPTY, None]
 
-    def empty(self):
-        if not self._is_empty:
+    def empty(self, val=EMPTY):
+        if not self._is_empty and self.__geom__:
             lgeos.GEOSGeom_destroy(self.__geom__)
-        self.__geom__ = EMPTY
+        self.__geom__ = val
 
     @property
     def _geom(self):
diff --git a/shapely/geos.py b/shapely/geos.py
index 5cb15a4..6061ed3 100644
--- a/shapely/geos.py
+++ b/shapely/geos.py
@@ -36,19 +36,22 @@ else:
 def load_dll(libname, fallbacks=None):
     lib = find_library(libname)
     if lib is not None:
-        return CDLL(lib)
-    else:
-        if fallbacks is not None:
-            for name in fallbacks:
-                try:
-                    return CDLL(name)
-                except OSError:
-                    # move on to the next fallback
-                    pass
-        # the end
-        raise OSError(
-            "Could not find library %s or load any of its variants %s" % (
-                libname, fallbacks or []))
+        try:
+            return CDLL(lib)
+        except OSError:
+            pass
+    if fallbacks is not None:
+        for name in fallbacks:
+            try:
+                return CDLL(name)
+            except OSError:
+                # move on to the next fallback
+                pass
+    # No shared library was loaded. Raise OSError.
+    raise OSError(
+        "Could not find library %s or load any of its variants %s" % (
+            libname, fallbacks or []))
+
 
 if sys.platform.startswith('linux'):
     _lgeos = load_dll('geos_c', fallbacks=['libgeos_c.so.1', 'libgeos_c.so'])
@@ -542,6 +545,7 @@ class LGEOS300(LGEOSBase):
                 self.GEOSisValid,
                 self.GEOSisSimple,
                 self.GEOSisRing,
+                self.GEOSisClosed,
                 self.GEOSHasZ):
             pred.errcheck = errcheck_predicate
 
@@ -557,6 +561,7 @@ class LGEOS300(LGEOSBase):
         self.methods['has_z'] = self.GEOSHasZ
         self.methods['is_empty'] = self.GEOSisEmpty
         self.methods['is_ring'] = self.GEOSisRing
+        self.methods['is_closed'] = self.GEOSisClosed
         self.methods['is_simple'] = self.GEOSisSimple
         self.methods['is_valid'] = self.GEOSisValid
         self.methods['disjoint'] = self.GEOSDisjoint
@@ -617,6 +622,7 @@ class LGEOS310(LGEOSBase):
                 self.GEOSisValid,
                 self.GEOSisSimple,
                 self.GEOSisRing,
+                self.GEOSisClosed,
                 self.GEOSHasZ):
             pred.func.errcheck = errcheck_predicate
 
@@ -634,6 +640,7 @@ class LGEOS310(LGEOSBase):
         self.methods['has_z'] = self.GEOSHasZ
         self.methods['is_empty'] = self.GEOSisEmpty
         self.methods['is_ring'] = self.GEOSisRing
+        self.methods['is_closed'] = self.GEOSisClosed
         self.methods['is_simple'] = self.GEOSisSimple
         self.methods['is_valid'] = self.GEOSisValid
         self.methods['disjoint'] = self.GEOSDisjoint
@@ -710,7 +717,21 @@ class LGEOS330(LGEOS320):
         self.methods['cascaded_union'] = self.methods['unary_union']
 
 
-if geos_version >= (3, 3, 0):
+class LGEOS340(LGEOS330):
+    """Proxy for GEOS 3.4.0-CAPI-1.8.0
+    """
+    geos_version = (3, 4, 0)
+    geos_capi_version = (1, 8, 0)
+
+    def __init__(self, dll):
+        super(LGEOS340, self).__init__(dll)
+        self.methods['delaunay_triangulation'] = self.GEOSDelaunayTriangulation
+        self.methods['nearest_points'] = self.GEOSNearestPoints
+
+
+if geos_version >= (3, 4, 0):
+    L = LGEOS340
+elif geos_version >= (3, 3, 0):
     L = LGEOS330
 elif geos_version >= (3, 2, 0):
     L = LGEOS320
@@ -723,7 +744,7 @@ else:
 
 lgeos = L(_lgeos)
 
+def cleanup(proxy):
+    del proxy
 
- at atexit.register
-def cleanup():
-    lgeos.__del__()
+atexit.register(cleanup, lgeos)
diff --git a/shapely/impl.py b/shapely/impl.py
index c71518e..bc804b7 100644
--- a/shapely/impl.py
+++ b/shapely/impl.py
@@ -71,6 +71,7 @@ IMPL300 = {
     'has_z': (UnaryPredicate, 'has_z'),
     'is_empty': (UnaryPredicate, 'is_empty'),
     'is_ring': (UnaryPredicate, 'is_ring'),
+    'is_closed': (UnaryPredicate, 'is_closed'),
     'is_simple': (UnaryPredicate, 'is_simple'),
     'is_valid': (UnaryPredicate, 'is_valid'),
     #
diff --git a/shapely/ops.py b/shapely/ops.py
index 1f2387a..fa5be14 100644
--- a/shapely/ops.py
+++ b/shapely/ops.py
@@ -8,14 +8,14 @@ if sys.version_info[0] < 3:
 else:
     izip = zip
 
-from ctypes import byref, c_void_p
+from ctypes import byref, c_void_p, c_double
 
 from shapely.geos import lgeos
 from shapely.geometry.base import geom_factory, BaseGeometry
-from shapely.geometry import asShape, asLineString, asMultiLineString
+from shapely.geometry import asShape, asLineString, asMultiLineString, Point
 
 __all__ = ['cascaded_union', 'linemerge', 'operator', 'polygonize',
-           'polygonize_full', 'transform', 'unary_union']
+           'polygonize_full', 'transform', 'unary_union', 'triangulate']
 
 
 class CollectionOperator(object):
@@ -36,7 +36,12 @@ class CollectionOperator(object):
         or a sequence of objects than can be adapted to LineStrings.
         """
         source = getattr(lines, 'geoms', None) or lines
-        obs = [self.shapeup(l) for l in source]
+        try:
+            source = iter(source)
+        except TypeError:
+            source = [source]
+        finally:
+            obs = [self.shapeup(l) for l in source]
         geom_array_type = c_void_p * len(obs)
         geom_array = geom_array_type()
         for i, line in enumerate(obs):
@@ -65,8 +70,12 @@ class CollectionOperator(object):
         (bowties, etc).
         """
         source = getattr(lines, 'geoms', None) or lines
-        obs = [self.shapeup(l) for l in source]
-
+        try:
+            source = iter(source)
+        except TypeError:
+            source = [source]
+        finally:
+            obs = [self.shapeup(l) for l in source]
         L = len(obs)
         subs = (c_void_p * L)()
         for i, g in enumerate(obs):
@@ -109,7 +118,11 @@ class CollectionOperator(object):
 
         This is the most efficient method of dissolving many polygons.
         """
-        L = len(geoms)
+        try:
+            L = len(geoms)
+        except TypeError:
+            geoms = [geoms]
+            L = 1
         subs = (c_void_p * L)()
         for i, g in enumerate(geoms):
             subs[i] = g._geom
@@ -123,7 +136,11 @@ class CollectionOperator(object):
         prefered method for dissolving many polygons.
 
         """
-        L = len(geoms)
+        try:
+            L = len(geoms)
+        except TypeError:
+            geoms = [geoms]
+            L = 1
         subs = (c_void_p * L)()
         for i, g in enumerate(geoms):
             subs[i] = g._geom
@@ -138,6 +155,25 @@ cascaded_union = operator.cascaded_union
 unary_union = operator.unary_union
 
 
+def triangulate(geom, tolerance=0.0, edges=False):
+    """Creates the Delaunay triangulation and returns a list of geometries
+
+    The source may be any geometry type. All vertices of the geometry will be
+    used as the points of the triangulation.
+
+    From the GEOS documentation:
+    tolerance is the snapping tolerance used to improve the robustness of
+    the triangulation computation. A tolerance of 0.0 specifies that no
+    snapping will take place.
+
+    If edges is False, a list of Polygons (triangles) will be returned.
+    Otherwise the list of LineString edges is returned.
+
+    """
+    func = lgeos.methods['delaunay_triangulation']
+    gc = geom_factory(func(geom._geom, tolerance, int(edges)))
+    return [g for g in gc.geoms]
+
 class ValidateOp(object):
     def __call__(self, this):
         return lgeos.GEOSisValidReason(this._geom)
@@ -170,7 +206,7 @@ def transform(func, geom):
 
       project = partial(
           pyproj.transform,
-          pyproj.Proj(init='espg:4326'),
+          pyproj.Proj(init='epsg:4326'),
           pyproj.Proj(init='epsg:26913'))
 
       g2 = transform(project, g1)
@@ -183,14 +219,14 @@ def transform(func, geom):
     """
     if geom.is_empty:
         return geom
-    if geom.type in ('Point', 'LineString', 'Polygon'):
+    if geom.type in ('Point', 'LineString', 'LinearRing', 'Polygon'):
 
         # First we try to apply func to x, y, z sequences. When func is
         # optimized for sequences, this is the fastest, though zipping
         # the results up to go back into the geometry constructors adds
         # extra cost.
         try:
-            if geom.type in ('Point', 'LineString'):
+            if geom.type in ('Point', 'LineString', 'LinearRing'):
                 return type(geom)(zip(*func(*izip(*geom.coords))))
             elif geom.type == 'Polygon':
                 shell = type(geom.exterior)(
@@ -202,7 +238,7 @@ def transform(func, geom):
         # A func that assumes x, y, z are single values will likely raise a
         # TypeError, in which case we'll try again.
         except TypeError:
-            if geom.type in ('Point', 'LineString'):
+            if geom.type in ('Point', 'LineString', 'LinearRing'):
                 return type(geom)([func(*c) for c in geom.coords])
             elif geom.type == 'Polygon':
                 shell = type(geom.exterior)(
@@ -215,3 +251,27 @@ def transform(func, geom):
         return type(geom)([transform(func, part) for part in geom.geoms])
     else:
         raise ValueError('Type %r not recognized' % geom.type)
+
+
+def nearest_points(g1, g2):
+    """Returns the calculated nearest points in the input geometries
+    
+    The points are returned in the same order as the input geometries.
+    """
+    seq = lgeos.methods['nearest_points'](g1._geom, g2._geom)
+    if seq is None:
+        if g1.is_empty:
+            raise ValueError('The first input geometry is empty')
+        else:
+            raise ValueError('The second input geometry is empty')
+    x1 = c_double()
+    y1 = c_double()
+    x2 = c_double()
+    y2 = c_double()
+    lgeos.GEOSCoordSeq_getX(seq, 0, byref(x1))
+    lgeos.GEOSCoordSeq_getY(seq, 0, byref(y1))
+    lgeos.GEOSCoordSeq_getX(seq, 1, byref(x2))
+    lgeos.GEOSCoordSeq_getY(seq, 1, byref(y2))
+    p1 = Point(x1.value, y1.value)
+    p2 = Point(x2.value, y2.value)
+    return (p1, p2)
diff --git a/shapely/speedups/__init__.py b/shapely/speedups/__init__.py
index a3d3e5f..a472aaf 100644
--- a/shapely/speedups/__init__.py
+++ b/shapely/speedups/__init__.py
@@ -13,6 +13,12 @@ except ImportError:
     # TODO: This does not appear to do anything useful
     import_error_msg = sys.exc_info()[1]
 
+from ..ftools import wraps
+def method_wrapper(f):
+    def wrapper(*args, **kwargs):
+        return f(*args, **kwargs)
+    return wraps(f)(wrapper)
+
 __all__ = ['available', 'enable', 'disable']
 _orig = {}
 
@@ -27,6 +33,9 @@ def enable():
     _orig['CoordinateSequence.ctypes'] = coords.CoordinateSequence.ctypes
     coords.CoordinateSequence.ctypes = property(_speedups.coordseq_ctypes)
     
+    _orig['CoordinateSequence.__iter__'] = coords.CoordinateSequence.__iter__
+    coords.CoordinateSequence.__iter__ = method_wrapper(_speedups.coordseq_iter)
+
     _orig['geos_linestring_from_py'] = linestring.geos_linestring_from_py
     linestring.geos_linestring_from_py = _speedups.geos_linestring_from_py
 
@@ -38,6 +47,7 @@ def disable():
         return
 
     coords.CoordinateSequence.ctypes = _orig['CoordinateSequence.ctypes']
+    coords.CoordinateSequence.__iter__ = _orig['CoordinateSequence.__iter__']
     linestring.geos_linestring_from_py = _orig['geos_linestring_from_py']
     polygon.geos_linearring_from_py = _orig['geos_linearring_from_py']
     _orig.clear()
diff --git a/shapely/speedups/_speedups.pyx b/shapely/speedups/_speedups.pyx
index 6ba3623..bf3d505 100644
--- a/shapely/speedups/_speedups.pyx
+++ b/shapely/speedups/_speedups.pyx
@@ -7,29 +7,16 @@
 
 import ctypes
 from shapely.geos import lgeos
+from shapely.geometry import Point, LineString, LinearRing
 
-cdef extern from "geos_c.h":
-    ctypedef struct GEOSCoordSequence
-    ctypedef struct GEOSGeometry
-    cdef struct GEOSContextHandle_HS
-    GEOSCoordSequence *GEOSCoordSeq_create_r(GEOSContextHandle_HS *,double, double)
-    GEOSCoordSequence *GEOSGeom_getCoordSeq_r(GEOSContextHandle_HS *, GEOSGeometry *)
-    int GEOSCoordSeq_getSize_r(GEOSContextHandle_HS *, GEOSCoordSequence *, int *)
-    int GEOSCoordSeq_setX_r(GEOSContextHandle_HS *, GEOSCoordSequence *, int, double)
-    int GEOSCoordSeq_setY_r(GEOSContextHandle_HS *, GEOSCoordSequence *, int, double)
-    int GEOSCoordSeq_setZ_r(GEOSContextHandle_HS *, GEOSCoordSequence *, int, double)
-    int GEOSCoordSeq_getX_r(GEOSContextHandle_HS *, GEOSCoordSequence *, int, double *)
-    int GEOSCoordSeq_getY_r(GEOSContextHandle_HS *, GEOSCoordSequence *, int, double *)
-    int GEOSCoordSeq_getZ_r(GEOSContextHandle_HS *, GEOSCoordSequence *, int, double *)
-    GEOSGeometry *GEOSGeom_createLineString_r(GEOSContextHandle_HS *, GEOSCoordSequence *)
-    GEOSGeometry *GEOSGeom_createLinearRing_r(GEOSContextHandle_HS *, GEOSCoordSequence *)
-    void GEOSGeom_destroy_r(GEOSContextHandle_HS *, GEOSGeometry *)
+include "../_geos.pxi"
+    
 
 cdef inline GEOSGeometry *cast_geom(unsigned long geom_addr):
     return <GEOSGeometry *>geom_addr
 
-cdef inline GEOSContextHandle_HS *cast_handle(unsigned long handle_addr):
-    return <GEOSContextHandle_HS *>handle_addr
+cdef inline GEOSContextHandle_t cast_handle(unsigned long handle_addr):
+    return <GEOSContextHandle_t>handle_addr
 
 cdef inline GEOSCoordSequence *cast_seq(unsigned long handle_addr):
     return <GEOSCoordSequence *>handle_addr
@@ -39,10 +26,28 @@ def destroy(geom):
 
 def geos_linestring_from_py(ob, update_geom=None, update_ndim=0):
     cdef double *cp
-    cdef GEOSContextHandle_HS *handle = cast_handle(lgeos.geos_handle)
+    cdef GEOSContextHandle_t handle = cast_handle(lgeos.geos_handle)
     cdef GEOSCoordSequence *cs
+    cdef GEOSGeometry *g
     cdef double dx, dy, dz
-    cdef int i, n, m
+    cdef int i, n, m, sm, sn
+
+    # If a LineString is passed in, just clone it and return
+    # If a LinearRing is passed in, clone the coord seq and return a LineString
+    if isinstance(ob, LineString):
+        g = cast_geom(ob._geom)
+        if GEOSHasZ_r(handle, g):
+            n = 3
+        else:
+            n = 2
+
+        if type(ob) == LineString:
+            return <unsigned long>GEOSGeom_clone_r(handle, g), n
+        else:
+            cs = GEOSGeom_getCoordSeq_r(handle, g)
+            cs = GEOSCoordSeq_clone_r(handle, cs)
+            return <unsigned long>GEOSGeom_createLineString_r(handle, cs), n
+
     try:
         # From array protocol
         array = ob.__array_interface__
@@ -64,6 +69,17 @@ def geos_linestring_from_py(ob, update_geom=None, update_ndim=0):
         else:
             cp = <double *><unsigned long>array['data'][0]
 
+        # Use strides to properly index into cp
+        # ob[i, j] == cp[sm*i + sn*j]
+        # Just to avoid a referenced before assignment warning.
+        dx = 0
+        if array.get('strides', None):
+            sm = array['strides'][0]/sizeof(dx)
+            sn = array['strides'][1]/sizeof(dx)
+        else:
+            sm = n
+            sn = 1
+
         # Create a coordinate sequence
         if update_geom is not None:
             cs = GEOSGeom_getCoordSeq_r(handle, cast_geom(update_geom))
@@ -76,11 +92,11 @@ def geos_linestring_from_py(ob, update_geom=None, update_ndim=0):
 
         # add to coordinate sequence
         for i in xrange(m):
-            dx = cp[n*i]
-            dy = cp[n*i+1]
+            dx = cp[sm*i]
+            dy = cp[sm*i+sn]
             dz = 0
             if n == 3:
-                dz = cp[n*i+2]
+                dz = cp[sm*i+2*sn]
                 
             # Because of a bug in the GEOS C API, 
             # always set X before Y
@@ -88,15 +104,26 @@ def geos_linestring_from_py(ob, update_geom=None, update_ndim=0):
             GEOSCoordSeq_setY_r(handle, cs, i, dy)
             if n == 3:
                 GEOSCoordSeq_setZ_r(handle, cs, i, dz)
-    
+
     except AttributeError:
         # Fall back on list
-        m = len(ob)
+        try:
+            m = len(ob)
+        except TypeError:  # Iterators, e.g. Python 3 zip
+            ob = list(ob)
+            m = len(ob)
         if m < 2:
             raise ValueError(
                 "LineStrings must have at least 2 coordinate tuples")
+
+        def _coords(o):
+            if isinstance(o, Point):
+                return o.coords[0]
+            else:
+                return o
+
         try:
-            n = len(ob[0])
+            n = len(_coords(ob[0]))
         except TypeError:
             raise ValueError(
                 "Input %s is the wrong shape for a LineString" % str(ob))
@@ -114,7 +141,7 @@ def geos_linestring_from_py(ob, update_geom=None, update_ndim=0):
 
         # add to coordinate sequence
         for i in xrange(m):
-            coords = ob[i]
+            coords = _coords(ob[i])
             dx = coords[0]
             dy = coords[1]
             dz = 0
@@ -138,10 +165,30 @@ def geos_linestring_from_py(ob, update_geom=None, update_ndim=0):
 
 def geos_linearring_from_py(ob, update_geom=None, update_ndim=0):
     cdef double *cp
-    cdef GEOSContextHandle_HS *handle = cast_handle(lgeos.geos_handle)
+    cdef GEOSContextHandle_t handle = cast_handle(lgeos.geos_handle)
+    cdef GEOSGeometry *g
     cdef GEOSCoordSequence *cs
     cdef double dx, dy, dz
-    cdef int i, n, m, M
+    cdef int i, n, m, M, sm, sn
+
+    # If a LinearRing is passed in, just clone it and return
+    # If a LineString is passed in, clone the coord seq and return a LinearRing
+    if isinstance(ob, LineString):
+        g = cast_geom(ob._geom)
+        if GEOSHasZ_r(handle, g):
+            n = 3
+        else:
+            n = 2
+
+        if type(ob) == LinearRing:
+            return <unsigned long>GEOSGeom_clone_r(handle, g), n
+        else:
+            cs = GEOSGeom_getCoordSeq_r(handle, g)
+            GEOSCoordSeq_getSize_r(handle, cs, &m)
+            if GEOSisClosed_r(handle, g) and m >= 4:
+                cs = GEOSCoordSeq_clone_r(handle, cs)
+                return <unsigned long>GEOSGeom_createLinearRing_r(handle, cs), n
+
     try:
         # From array protocol
         array = ob.__array_interface__
@@ -159,8 +206,21 @@ def geos_linearring_from_py(ob, update_geom=None, update_ndim=0):
         else:
             cp = <double *><unsigned long>array['data'][0]
 
+        # Use strides to properly index into cp
+        # ob[i, j] == cp[sm*i + sn*j]
+        dx = 0  # Just to avoid a referenced before assignment warning.
+        if array.get('strides', None):
+            sm = array['strides'][0]/sizeof(dx)
+            sn = array['strides'][1]/sizeof(dx)
+        else:
+            sm = n
+            sn = 1
+
         # Add closing coordinates to sequence?
-        if cp[0] != cp[m*n-n] or cp[1] != cp[m*n-n+1]:
+        # Check whether the first set of coordinates matches the last.
+        # If not, we'll have to close the ring later
+        if (cp[0] != cp[sm*(m-1)] or cp[sn] != cp[sm*(m-1)+sn] or
+            (n == 3 and cp[2*sn] != cp[sm*(m-1)+2*sn])):
             M = m + 1
         else:
             M = m
@@ -177,11 +237,11 @@ def geos_linearring_from_py(ob, update_geom=None, update_ndim=0):
 
         # add to coordinate sequence
         for i in xrange(m):
-            dx = cp[n*i]
-            dy = cp[n*i+1]
+            dx = cp[sm*i]
+            dy = cp[sm*i+sn]
             dz = 0
             if n == 3:
-                dz = cp[n*i+2]
+                dz = cp[sm*i+2*sn]
         
             # Because of a bug in the GEOS C API, 
             # always set X before Y
@@ -193,10 +253,10 @@ def geos_linearring_from_py(ob, update_geom=None, update_ndim=0):
         # Add closing coordinates to sequence?
         if M > m:
             dx = cp[0]
-            dy = cp[1]
+            dy = cp[sn]
             dz = 0
             if n == 3:
-                dz = cp[2]
+                dz = cp[2*sn]
         
             # Because of a bug in the GEOS C API, 
             # always set X before Y
@@ -207,7 +267,11 @@ def geos_linearring_from_py(ob, update_geom=None, update_ndim=0):
             
     except AttributeError:
         # Fall back on list
-        m = len(ob)
+        try:
+            m = len(ob)
+        except TypeError:  # Iterators, e.g. Python 3 zip
+            ob = list(ob)
+            m = len(ob)
         n = len(ob[0])
         if m < 3:
             raise ValueError(
@@ -271,7 +335,7 @@ def geos_linearring_from_py(ob, update_geom=None, update_ndim=0):
 def coordseq_ctypes(self):
     cdef int i, n, m
     cdef double temp = 0
-    cdef GEOSContextHandle_HS *handle = cast_handle(lgeos.geos_handle)
+    cdef GEOSContextHandle_t handle = cast_handle(lgeos.geos_handle)
     cdef GEOSCoordSequence *cs
     cdef double *data_p
     self._update()
@@ -293,3 +357,25 @@ def coordseq_ctypes(self):
             data_p[n*i+2] = temp
     return data
 
+def coordseq_iter(self):
+    cdef int i
+    cdef double dx
+    cdef double dy
+    cdef double dz
+    cdef int has_z
+
+    self._update()
+
+    cdef GEOSContextHandle_t handle = cast_handle(lgeos.geos_handle)
+    cdef GEOSCoordSequence *cs
+    cs = cast_seq(self._cseq)
+
+    has_z = self._ndim == 3
+    for i in range(self.__len__()):
+        GEOSCoordSeq_getX_r(handle, cs, i, &dx)
+        GEOSCoordSeq_getY_r(handle, cs, i, &dy)
+        if has_z == 1:
+            GEOSCoordSeq_getZ_r(handle, cs, i, &dz)
+            yield (dx, dy, dz)
+        else:
+            yield (dx, dy)
diff --git a/shapely/strtree.py b/shapely/strtree.py
new file mode 100644
index 0000000..3c0235e
--- /dev/null
+++ b/shapely/strtree.py
@@ -0,0 +1,59 @@
+from shapely.geos import lgeos
+import ctypes
+
+class STRtree:
+    """
+    STRtree is an R-tree that is created using the Sort-Tile-Recursive
+    algorithm. STRtree takes a sequence of geometry objects as initialization
+    parameter. After initialization the query method can be used to make a
+    spatial query over those objects.
+
+    >>> from shapely.geometry import Polygon
+    >>> polys = [ Polygon(((0, 0), (1, 0), (1, 1))), Polygon(((0, 1), (0, 0), (1, 0))), Polygon(((100, 100), (101, 100), (101, 101))) ]
+    >>> s = STRtree(polys)
+    >>> query_geom = Polygon(((-1, -1), (2, 0), (2, 2), (-1, 2)))
+    >>> result = s.query(query_geom)
+    >>> polys[0] in result
+    True
+    >>> polys[1] in result
+    True
+    >>> polys[2] in result
+    False
+    >>> # Test empty tree
+    >>> s = STRtree([])
+    >>> s.query(query_geom)
+    []
+    >>> # Test tree with one object
+    >>> s = STRtree([polys[0]])
+    >>> result = s.query(query_geom)
+    >>> polys[0] in result
+    True
+    """
+
+    def __init__(self, geoms):
+        self._n_geoms = len(geoms)
+        # GEOS STRtree capacity has to be > 1
+        self._tree_handle = lgeos.GEOSSTRtree_create(max(2, len(geoms)))
+        for geom in geoms:
+            lgeos.GEOSSTRtree_insert(self._tree_handle, geom._geom, ctypes.py_object(geom))
+
+    def __del__(self):
+        lgeos.GEOSSTRtree_destroy(self._tree_handle)
+
+    def query(self, geom):
+        if self._n_geoms == 0:
+            return []
+
+        result = []
+
+        def callback(item, userdata):
+            geom = ctypes.cast(item, ctypes.py_object).value
+            result.append(geom)
+
+        lgeos.GEOSSTRtree_query(self._tree_handle, geom._geom, lgeos.GEOSQueryCallback(callback), None)
+
+        return result
+
+if __name__ == "__main__":
+    import doctest
+    doctest.testmod()
diff --git a/shapely/tests/__init__.py b/shapely/tests/__init__.py
deleted file mode 100755
index a714388..0000000
--- a/shapely/tests/__init__.py
+++ /dev/null
@@ -1,81 +0,0 @@
-import sys
-from shapely.geos import geos_version_string, lgeos, WKTWriter
-from shapely import speedups
-
-try:
-    import numpy
-    numpy_version = numpy.version.version
-except ImportError:
-    numpy = False
-    numpy_version = 'not available'
-
-# Show some diagnostic information; handy for Travis CI
-print('Python version: ' + sys.version.replace('\n', ' '))
-print('GEOS version: ' + geos_version_string)
-print('Numpy version: ' + numpy_version)
-print('Cython speedups: ' + str(speedups.available))
-
-if lgeos.geos_version >= (3, 3, 0):
-    # Remove any WKT writer defaults to pass tests for all versions of GEOS
-    WKTWriter.defaults = {}
-
-if sys.version_info[0:2] <= (2, 6):
-    import unittest2 as unittest
-else:
-    import unittest
-
-from . import test_doctests, test_prepared, test_equality, test_geomseq, \
-    test_point, test_linestring, test_polygon, test_multipoint, \
-    test_multilinestring, test_multipolygon, test_geointerface, test_locale, \
-    test_xy, test_persist, test_collection, test_emptiness, test_singularity, \
-    test_validation, test_mapping, test_delegated, test_dlls, \
-    test_linear_referencing, test_products_z, test_box, test_speedups, \
-    test_cga, test_getitem, test_ndarrays, test_pickle, \
-    test_affinity, test_transform, test_invalid_geometries, test_styles, \
-    test_operations, test_operators, test_iterops, test_predicates, \
-    test_linemerge, test_polygonize, test_union
-
-
-def test_suite():
-    suite = unittest.TestSuite()
-    suite.addTest(test_point.test_suite())
-    suite.addTest(test_linestring.test_suite())
-    suite.addTest(test_polygon.test_suite())
-    suite.addTest(test_multipoint.test_suite())
-    suite.addTest(test_multilinestring.test_suite())
-    suite.addTest(test_multipolygon.test_suite())
-    suite.addTest(test_doctests.test_suite())
-    suite.addTest(test_prepared.test_suite())
-    suite.addTest(test_emptiness.test_suite())
-    suite.addTest(test_equality.test_suite())
-    suite.addTest(test_geomseq.test_suite())
-    suite.addTest(test_geointerface.test_suite())
-    suite.addTest(test_locale.test_suite())
-    suite.addTest(test_xy.test_suite())
-    suite.addTest(test_persist.test_suite())
-    suite.addTest(test_collection.test_suite())
-    suite.addTest(test_singularity.test_suite())
-    suite.addTest(test_validation.test_suite())
-    suite.addTest(test_mapping.test_suite())
-    suite.addTest(test_delegated.test_suite())
-    suite.addTest(test_dlls.test_suite())
-    suite.addTest(test_linear_referencing.test_suite())
-    suite.addTest(test_products_z.test_suite())
-    suite.addTest(test_box.test_suite())
-    suite.addTest(test_speedups.test_suite())
-    suite.addTest(test_cga.test_suite())
-    suite.addTest(test_getitem.test_suite())
-    suite.addTest(test_ndarrays.test_suite())
-    suite.addTest(test_pickle.test_suite())
-    suite.addTest(test_affinity.test_suite())
-    suite.addTest(test_transform.test_suite())
-    suite.addTest(test_invalid_geometries.test_suite())
-    suite.addTest(test_styles.test_suite())
-    suite.addTest(test_operations.test_suite())
-    suite.addTest(test_operators.test_suite())
-    suite.addTest(test_iterops.test_suite())
-    suite.addTest(test_predicates.test_suite())
-    suite.addTest(test_linemerge.test_suite())
-    suite.addTest(test_polygonize.test_suite())
-    suite.addTest(test_union.test_suite())
-    return suite
diff --git a/shapely/tests/test_speedups.py b/shapely/tests/test_speedups.py
deleted file mode 100644
index 705d0ab..0000000
--- a/shapely/tests/test_speedups.py
+++ /dev/null
@@ -1,39 +0,0 @@
-from . import unittest
-
-from shapely import speedups
-from shapely.geometry import LineString, Polygon
-
-
-class SpeedupsTestCase(unittest.TestCase):
-
-    def setUp(self):
-        self.assertFalse(speedups._orig)
-        if speedups.available:
-            speedups.enable()
-            self.assertTrue(speedups._orig)
-
-    def tearDown(self):
-        if speedups.available:
-            self.assertTrue(speedups._orig)
-        speedups.disable()
-        self.assertFalse(speedups._orig)
-
-    @unittest.skipIf(not speedups.available, 'speedups not available')
-    def test_create_linestring(self):
-        ls = LineString([(0, 0), (1, 0), (1, 2)])
-        self.assertEqual(ls.length, 3)
-
-    @unittest.skipIf(not speedups.available, 'speedups not available')
-    def test_create_polygon(self):
-        p = Polygon([(0, 0), (2, 0), (2, 2), (0, 2)])
-        self.assertEqual(p.length, 8)
-
-    @unittest.skipIf(not speedups.available, 'speedups not available')
-    def test_create_polygon_from_linestring(self):
-        ls = LineString([(0, 0), (2, 0), (2, 2), (0, 2)])
-        p = Polygon(ls)
-        self.assertEqual(p.length, 8)
-
-
-def test_suite():
-    return unittest.TestLoader().loadTestsFromTestCase(SpeedupsTestCase)
diff --git a/shapely/vectorized/__init__.py b/shapely/vectorized/__init__.py
new file mode 100644
index 0000000..8898712
--- /dev/null
+++ b/shapely/vectorized/__init__.py
@@ -0,0 +1,3 @@
+"""Provides multi-point element-wise operations such as ``contains``."""
+
+from ._vectorized import (contains, touches)
diff --git a/shapely/vectorized/_vectorized.pyx b/shapely/vectorized/_vectorized.pyx
new file mode 100644
index 0000000..4586823
--- /dev/null
+++ b/shapely/vectorized/_vectorized.pyx
@@ -0,0 +1,120 @@
+import cython
+cimport cpython.array
+
+import numpy as np
+cimport numpy as np
+
+import shapely.prepared
+
+
+include "../_geos.pxi"
+
+
+# Define the predicate function, which returns True/False from functions such as GEOSPreparedContains_r
+# and GEOSPreparedWithin_r.
+ctypedef char (* predicate)(GEOSContextHandle_t, const GEOSPreparedGeometry *, const GEOSGeometry *) nogil
+
+
+def contains(geometry, x, y):
+    """
+    Vectorized (element-wise) version of `contains` which checks whether
+    multiple points are contained by a single geometry.
+
+    Parameters
+    ----------
+    geometry : PreparedGeometry or subclass of BaseGeometry
+        The geometry which is to be checked to see whether each point is
+        contained within. The geometry will be "prepared" if it is not already
+        a PreparedGeometry instance.
+    x : array
+        The x coordinates of the points to check. 
+    y : array
+        The y coordinates of the points to check.
+
+    Returns
+    -------
+    Mask of points contained by the given `geometry`.
+
+    """
+    return _predicated_elementwise(geometry, x, y, GEOSPreparedContains_r)
+
+
+def touches(geometry, x, y):
+    """
+    Vectorized (element-wise) version of `touches` which checks whether
+    multiple points touch the exterior of a single geometry.
+
+    Parameters
+    ----------
+    geometry : PreparedGeometry or subclass of BaseGeometry
+        The geometry which is to be checked to see whether each point is
+        contained within. The geometry will be "prepared" if it is not already
+        a PreparedGeometry instance.
+    x : array
+        The x coordinates of the points to check. 
+    y : array
+        The y coordinates of the points to check.
+
+    Returns
+    -------
+    Mask of points which touch the exterior of the given `geometry`.
+
+    """
+    return _predicated_elementwise(geometry, x, y, GEOSPreparedTouches_r)
+
+
+cdef _predicated_elementwise(geometry, x, y, predicate fn):
+    """
+    Implements elementwise True/False testing, given a predicate function
+    such as "contains" or "touches". x and y arrays can be of any type, order
+    and dtype, and will be cast for appropriate calling to "_predicated_1d".
+
+    """
+    # Support coordinate sequences and other array like objects.
+    x, y = np.asanyarray(x), np.asanyarray(y)
+    if x.shape != y.shape:
+        raise ValueError('X and Y shapes must be equivalent.')
+
+    x_1d = x.astype(np.float64, copy=False).ravel()
+    y_1d = y.astype(np.float64, copy=False).ravel()
+
+    result = _predicated_1d(geometry, x_1d, y_1d, fn)
+    return result.reshape(x.shape)
+
+
+
+ at cython.boundscheck(False)
+ at cython.wraparound(False)
+cdef _predicated_1d(geometry, np.double_t[:] x, np.double_t[:] y, predicate fn):
+    
+    cdef Py_ssize_t idx
+    cdef unsigned int n = x.size
+    cdef np.ndarray[np.uint8_t, ndim=1, cast=True] result = np.empty(n, dtype=np.bool)
+    cdef GEOSContextHandle_t geos_handle
+    cdef GEOSPreparedGeometry *geos_prepared_geom
+    cdef GEOSCoordSequence *cs
+    cdef GEOSGeometry *point
+
+    # Prepare the geometry if it hasn't already been prepared.
+    if not isinstance(geometry, shapely.prepared.PreparedGeometry):
+        geometry = shapely.prepared.prep(geometry)
+
+    geos_h = get_geos_context_handle()
+    geos_geom = geos_from_prepared(geometry)
+
+    with nogil:
+        for idx in xrange(n):
+            # Construct a coordinate sequence with our x, y values.
+            cs = GEOSCoordSeq_create_r(geos_h, 1, 2)
+            GEOSCoordSeq_setX_r(geos_h, cs, 0, x[idx])
+            GEOSCoordSeq_setY_r(geos_h, cs, 0, y[idx])
+            
+            # Construct a point with this sequence.
+            p = GEOSGeom_createPoint_r(geos_h, cs)
+            
+            # Put the result of whether the point is "contained" by the
+            # prepared geometry into the result array. 
+            result[idx] = <np.uint8_t> fn(geos_h, geos_geom, p)
+            GEOSGeom_destroy_r(geos_h, p)
+
+    return result
diff --git a/shapely/wkt.py b/shapely/wkt.py
index 51ffbfa..a6f3a46 100644
--- a/shapely/wkt.py
+++ b/shapely/wkt.py
@@ -14,12 +14,12 @@ def load(fp):
     data = fp.read()
     return loads(data)
 
-def dumps(ob, **kw):
+def dumps(ob, trim=False, **kw):
     """Dump a WKT representation of a geometry to a string.
 
     See available keyword output settings in ``shapely.geos.WKTWriter``.
     """
-    return geos.WKTWriter(geos.lgeos, **kw).write(ob)
+    return geos.WKTWriter(geos.lgeos, trim=trim, **kw).write(ob)
 
 def dump(ob, fp, **settings):
     """Dump a geometry to an open file."""
diff --git a/tests/__init__.py b/tests/__init__.py
new file mode 100755
index 0000000..b4cf8ab
--- /dev/null
+++ b/tests/__init__.py
@@ -0,0 +1,25 @@
+import sys
+from shapely.geos import geos_version_string, lgeos, WKTWriter
+from shapely import speedups
+
+try:
+    import numpy
+    numpy_version = numpy.version.version
+except ImportError:
+    numpy = False
+    numpy_version = 'not available'
+
+# Show some diagnostic information; handy for Travis CI
+print('Python version: ' + sys.version.replace('\n', ' '))
+print('GEOS version: ' + geos_version_string)
+print('Numpy version: ' + numpy_version)
+print('Cython speedups: ' + str(speedups.available))
+
+if lgeos.geos_version >= (3, 3, 0):
+    # Remove any WKT writer defaults to pass tests for all versions of GEOS
+    WKTWriter.defaults = {}
+
+if sys.version_info[0:2] <= (2, 6):
+    import unittest2 as unittest
+else:
+    import unittest
diff --git a/shapely/tests/binascii_hex.txt b/tests/binascii_hex.txt
similarity index 100%
rename from shapely/tests/binascii_hex.txt
rename to tests/binascii_hex.txt
diff --git a/tests/conftest.py b/tests/conftest.py
new file mode 100644
index 0000000..3fe9d4e
--- /dev/null
+++ b/tests/conftest.py
@@ -0,0 +1,16 @@
+import sys
+
+import pytest
+
+def pytest_addoption(parser):
+    parser.addoption("--with-speedups", action="store_true", default=False,
+        help="Run tests with speedups.")
+
+def pytest_runtest_setup(item):
+    if item.config.getoption("--with-speedups"):
+        import shapely.speedups
+        if not shapely.speedups.available:
+            print("Speedups have been demanded but are unavailable")
+            sys.exit(1)
+        shapely.speedups.enable()
+        print("Speedups enabled for %s." % item.name)
diff --git a/shapely/tests/rungrind.dist b/tests/rungrind.dist
similarity index 100%
rename from shapely/tests/rungrind.dist
rename to tests/rungrind.dist
diff --git a/shapely/tests/test_affinity.py b/tests/test_affinity.py
similarity index 98%
rename from shapely/tests/test_affinity.py
rename to tests/test_affinity.py
index 1f3b9ba..f997e0a 100755
--- a/shapely/tests/test_affinity.py
+++ b/tests/test_affinity.py
@@ -51,6 +51,10 @@ class AffineTestCase(unittest.TestCase):
         ls2d = load_wkt('LINESTRING(0.9 3.4, 0.7 2, 2.5 2.7)')
         ls3d = load_wkt('LINESTRING(0.9 3.4 3.3, 0.7 2 2.3, 2.5 2.7 5.5)')
         test_geom(ls2d, ls3d)
+        lr2d = load_wkt('LINEARRING(0.9 3.4, 0.7 2, 2.5 2.7, 0.9 3.4)')
+        lr3d = load_wkt(
+            'LINEARRING(0.9 3.4 3.3, 0.7 2 2.3, 2.5 2.7 5.5, 0.9 3.4 3.3)')
+        test_geom(lr2d, lr3d)
         test_geom(load_wkt('POLYGON((0.9 2.3, 0.5 1.1, 2.4 0.8, 0.9 2.3), '
                            '(1.1 1.7, 0.9 1.3, 1.4 1.2, 1.1 1.7), '
                            '(1.6 1.3, 1.7 1, 1.9 1.1, 1.6 1.3))'))
diff --git a/shapely/tests/test_box.py b/tests/test_box.py
similarity index 100%
rename from shapely/tests/test_box.py
rename to tests/test_box.py
diff --git a/shapely/tests/test_cga.py b/tests/test_cga.py
similarity index 100%
rename from shapely/tests/test_cga.py
rename to tests/test_cga.py
diff --git a/shapely/tests/test_collection.py b/tests/test_collection.py
similarity index 100%
rename from shapely/tests/test_collection.py
rename to tests/test_collection.py
diff --git a/tests/test_delaunay.py b/tests/test_delaunay.py
new file mode 100644
index 0000000..ef91780
--- /dev/null
+++ b/tests/test_delaunay.py
@@ -0,0 +1,43 @@
+try:
+    import unittest2 as unittest
+except ImportError:
+    import unittest
+
+from shapely.geometry import Polygon, LineString, Point
+from shapely.ops import triangulate
+from shapely.geos import geos_version
+
+ at unittest.skipIf(geos_version < (3, 4, 0), 
+                 "Delaunay triangulation not supported")
+class DelaunayTriangulation(unittest.TestCase):
+    """
+    Only testing the number of triangles and their type here.
+    This doesn't actually test the points in the resulting geometries.
+
+    """
+    def setUp(self):
+        self.p = Polygon([(0,0), (1,0), (1,1), (0,1)])
+
+    def test_polys(self):
+        polys = triangulate(self.p)
+        self.assertEqual(len(polys), 2)
+        for p in polys:
+            self.assert_(isinstance(p, Polygon))
+
+    def test_lines(self):
+        polys = triangulate(self.p, edges=True)
+        self.assertEqual(len(polys), 5)
+        for p in polys:
+            self.assert_(isinstance(p, LineString))
+
+    def test_point(self):
+        p = Point(1,1)
+        polys = triangulate(p)
+        self.assertEqual(len(polys), 0)
+
+
+def test_suite():
+    return unittest.TestLoader().loadTestsFromTestCase(DelaunayTriangulation)
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/shapely/tests/test_delegated.py b/tests/test_delegated.py
similarity index 100%
rename from shapely/tests/test_delegated.py
rename to tests/test_delegated.py
diff --git a/shapely/tests/test_dlls.py b/tests/test_dlls.py
similarity index 100%
rename from shapely/tests/test_dlls.py
rename to tests/test_dlls.py
diff --git a/shapely/tests/test_doctests.py b/tests/test_doctests.py
similarity index 100%
rename from shapely/tests/test_doctests.py
rename to tests/test_doctests.py
diff --git a/shapely/tests/test_emptiness.py b/tests/test_emptiness.py
similarity index 100%
rename from shapely/tests/test_emptiness.py
rename to tests/test_emptiness.py
diff --git a/shapely/tests/test_equality.py b/tests/test_equality.py
similarity index 100%
rename from shapely/tests/test_equality.py
rename to tests/test_equality.py
diff --git a/shapely/tests/test_geointerface.py b/tests/test_geointerface.py
similarity index 100%
rename from shapely/tests/test_geointerface.py
rename to tests/test_geointerface.py
diff --git a/shapely/tests/test_geomseq.py b/tests/test_geomseq.py
similarity index 100%
rename from shapely/tests/test_geomseq.py
rename to tests/test_geomseq.py
diff --git a/shapely/tests/test_getitem.py b/tests/test_getitem.py
similarity index 100%
rename from shapely/tests/test_getitem.py
rename to tests/test_getitem.py
diff --git a/shapely/tests/test_invalid_geometries.py b/tests/test_invalid_geometries.py
similarity index 100%
rename from shapely/tests/test_invalid_geometries.py
rename to tests/test_invalid_geometries.py
diff --git a/shapely/tests/test_iterops.py b/tests/test_iterops.py
similarity index 100%
rename from shapely/tests/test_iterops.py
rename to tests/test_iterops.py
diff --git a/shapely/tests/test_linear_referencing.py b/tests/test_linear_referencing.py
similarity index 100%
rename from shapely/tests/test_linear_referencing.py
rename to tests/test_linear_referencing.py
diff --git a/shapely/tests/test_linemerge.py b/tests/test_linemerge.py
similarity index 100%
rename from shapely/tests/test_linemerge.py
rename to tests/test_linemerge.py
diff --git a/shapely/tests/test_linestring.py b/tests/test_linestring.py
similarity index 69%
rename from shapely/tests/test_linestring.py
rename to tests/test_linestring.py
index ab9fc47..d7c7d4e 100644
--- a/shapely/tests/test_linestring.py
+++ b/tests/test_linestring.py
@@ -1,5 +1,6 @@
 from . import unittest, numpy
-from shapely.geometry import LineString, asLineString
+from shapely.geos import lgeos
+from shapely.geometry import LineString, asLineString, Point, LinearRing
 
 
 class LineStringTestCase(unittest.TestCase):
@@ -11,10 +12,15 @@ class LineStringTestCase(unittest.TestCase):
         self.assertEqual(len(line.coords), 2)
         self.assertEqual(line.coords[:], [(1.0, 2.0), (3.0, 4.0)])
 
-        # From lines
-        copy = LineString(line)
-        self.assertEqual(len(copy.coords), 2)
-        self.assertEqual(copy.coords[:], [(1.0, 2.0), (3.0, 4.0)])
+        # From Points
+        line2 = LineString((Point(1.0, 2.0), Point(3.0, 4.0)))
+        self.assertEqual(len(line2.coords), 2)
+        self.assertEqual(line2.coords[:], [(1.0, 2.0), (3.0, 4.0)])
+
+        # From mix of tuples and Points
+        line3 = LineString((Point(1.0, 2.0), (2.0, 3.0), Point(3.0, 4.0)))
+        self.assertEqual(len(line3.coords), 3)
+        self.assertEqual(line3.coords[:], [(1.0, 2.0), (2.0, 3.0), (3.0, 4.0)])
 
         # Bounds
         self.assertEqual(line.bounds, (1.0, 2.0, 3.0, 4.0))
@@ -51,6 +57,36 @@ class LineStringTestCase(unittest.TestCase):
         l_null.coords = [(0, 0), (1, 1)]
         self.assertAlmostEqual(l_null.length, 1.4142135623730951)
 
+
+    def test_from_linestring(self):
+        line = LineString(((1.0, 2.0), (3.0, 4.0)))
+        copy = LineString(line)
+        self.assertEqual(len(copy.coords), 2)
+        self.assertEqual(copy.coords[:], [(1.0, 2.0), (3.0, 4.0)])
+        self.assertEqual('LineString',
+                         lgeos.GEOSGeomType(copy._geom).decode('ascii'))
+
+
+    def test_from_linestring_z(self):
+        coords = [(1.0, 2.0, 3.0), (4.0, 5.0, 6.0)]
+        line = LineString(coords)
+        copy = LineString(line)
+        self.assertEqual(len(copy.coords), 2)
+        self.assertEqual(copy.coords[:], coords)
+        self.assertEqual('LineString',
+                         lgeos.GEOSGeomType(copy._geom).decode('ascii'))
+
+
+    def test_from_linearring(self):
+        coords = [(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 0.0)]
+        ring = LinearRing(coords)
+        copy = LineString(ring)
+        self.assertEqual(len(copy.coords), 4)
+        self.assertEqual(copy.coords[:], coords)
+        self.assertEqual('LineString',
+                         lgeos.GEOSGeomType(copy._geom).decode('ascii'))
+
+
     @unittest.skipIf(not numpy, 'Numpy required')
     def test_numpy(self):
 
diff --git a/shapely/tests/test_locale.py b/tests/test_locale.py
similarity index 100%
rename from shapely/tests/test_locale.py
rename to tests/test_locale.py
diff --git a/shapely/tests/test_mapping.py b/tests/test_mapping.py
similarity index 100%
rename from shapely/tests/test_mapping.py
rename to tests/test_mapping.py
diff --git a/shapely/tests/test_multilinestring.py b/tests/test_multilinestring.py
similarity index 76%
rename from shapely/tests/test_multilinestring.py
rename to tests/test_multilinestring.py
index f4930b9..61b6bc3 100644
--- a/shapely/tests/test_multilinestring.py
+++ b/tests/test_multilinestring.py
@@ -1,11 +1,12 @@
 from . import unittest, numpy
+from shapely.geos import lgeos
 from shapely.geometry import LineString, MultiLineString, asMultiLineString
 from shapely.geometry.base import dump_coords
 
 
 class MultiLineStringTestCase(unittest.TestCase):
 
-    def test_multipoint(self):
+    def test_multilinestring(self):
 
         # From coordinate tuples
         geom = MultiLineString((((1.0, 2.0), (3.0, 4.0)),))
@@ -36,6 +37,22 @@ class MultiLineStringTestCase(unittest.TestCase):
                          {'type': 'MultiLineString',
                           'coordinates': (((0.0, 0.0), (1.0, 2.0)),)})
 
+
+    def test_from_multilinestring_z(self):
+        coords1 = [(0.0, 1.0, 2.0), (3.0, 4.0, 5.0)]
+        coords2 = [(6.0, 7.0, 8.0), (9.0, 10.0, 11.0)]
+
+        # From coordinate tuples
+        ml = MultiLineString([coords1, coords2])
+        copy = MultiLineString(ml)
+        self.assertIsInstance(copy, MultiLineString)
+        self.assertEqual('MultiLineString',
+                         lgeos.GEOSGeomType(copy._geom).decode('ascii'))
+        self.assertEqual(len(copy.geoms), 2)
+        self.assertEqual(dump_coords(copy.geoms[0]), coords1)
+        self.assertEqual(dump_coords(copy.geoms[1]), coords2)
+
+
     @unittest.skipIf(not numpy, 'Numpy required')
     def test_numpy(self):
 
diff --git a/shapely/tests/test_multipoint.py b/tests/test_multipoint.py
similarity index 100%
rename from shapely/tests/test_multipoint.py
rename to tests/test_multipoint.py
diff --git a/shapely/tests/test_multipolygon.py b/tests/test_multipolygon.py
similarity index 100%
rename from shapely/tests/test_multipolygon.py
rename to tests/test_multipolygon.py
diff --git a/shapely/tests/test_ndarrays.py b/tests/test_ndarrays.py
similarity index 100%
rename from shapely/tests/test_ndarrays.py
rename to tests/test_ndarrays.py
diff --git a/tests/test_nearest.py b/tests/test_nearest.py
new file mode 100644
index 0000000..0d3f13f
--- /dev/null
+++ b/tests/test_nearest.py
@@ -0,0 +1,22 @@
+from . import unittest
+
+from shapely.geometry import Point
+from shapely.geos import geos_version
+from shapely.ops import nearest_points
+
+ at unittest.skipIf(geos_version < (3, 4, 0), 'GEOS 3.4.0 required')
+class Nearest(unittest.TestCase):
+    def test_nearest(self):
+        first, second = nearest_points(
+                        Point(0, 0).buffer(1.0), Point(3, 0).buffer(1.0))
+        self.assertAlmostEqual(first.x, 1.0, 7)
+        self.assertAlmostEqual(second.x, 2.0, 7)
+        self.assertAlmostEqual(first.y, 0.0, 7)
+        self.assertAlmostEqual(second.y, 0.0, 7)
+
+
+def test_suite():
+    return unittest.TestLoader().loadTestsFromTestCase(Nearest)
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/shapely/tests/test_operations.py b/tests/test_operations.py
similarity index 100%
rename from shapely/tests/test_operations.py
rename to tests/test_operations.py
diff --git a/shapely/tests/test_operators.py b/tests/test_operators.py
similarity index 100%
rename from shapely/tests/test_operators.py
rename to tests/test_operators.py
diff --git a/shapely/tests/test_persist.py b/tests/test_persist.py
similarity index 100%
rename from shapely/tests/test_persist.py
rename to tests/test_persist.py
diff --git a/shapely/tests/test_pickle.py b/tests/test_pickle.py
similarity index 100%
rename from shapely/tests/test_pickle.py
rename to tests/test_pickle.py
diff --git a/shapely/tests/test_point.py b/tests/test_point.py
similarity index 100%
rename from shapely/tests/test_point.py
rename to tests/test_point.py
diff --git a/shapely/tests/test_polygon.py b/tests/test_polygon.py
similarity index 85%
rename from shapely/tests/test_polygon.py
rename to tests/test_polygon.py
index d973786..2d07a3a 100644
--- a/shapely/tests/test_polygon.py
+++ b/tests/test_polygon.py
@@ -2,8 +2,9 @@
 """
 from . import unittest, numpy
 from shapely.wkb import loads as load_wkb
+from shapely.geos import lgeos
 from shapely.geometry import Point, Polygon, asPolygon
-from shapely.geometry.polygon import LinearRing, asLinearRing
+from shapely.geometry.polygon import LinearRing, LineString, asLinearRing
 from shapely.geometry.base import dump_coords
 
 
@@ -73,6 +74,13 @@ class PolygonTestCase(unittest.TestCase):
         with self.assertRaises(IndexError):  # index out of range
             polygon.interiors[1]
 
+        # Test from another Polygon
+        copy = Polygon(polygon)
+        self.assertEqual(len(polygon.exterior.coords), 5)
+        self.assertEqual(len(polygon.interiors[0].coords), 5)
+        with self.assertRaises(IndexError):  # index out of range
+            polygon.interiors[1]
+
         # Coordinate getters and setters raise exceptions
         self.assertRaises(NotImplementedError, polygon._get_coords)
         with self.assertRaises(NotImplementedError):
@@ -107,6 +115,27 @@ class PolygonTestCase(unittest.TestCase):
             # A LinearRing must have at least 3 coordinate tuples
             Polygon([[1, 2], [2, 3]])
 
+
+    def test_linearring_from_closed_linestring(self):
+        coords = [(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 0.0)]
+        line = LineString(coords)
+        ring = LinearRing(line)
+        self.assertEqual(len(ring.coords), 4)
+        self.assertEqual(ring.coords[:], coords)
+        self.assertEqual('LinearRing',
+                         lgeos.GEOSGeomType(ring._geom).decode('ascii'))
+
+
+    def test_linearring_from_unclosed_linestring(self):
+        coords = [(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 0.0)]
+        line = LineString(coords[:-1])  # Pass in unclosed line
+        ring = LinearRing(line)
+        self.assertEqual(len(ring.coords), 4)
+        self.assertEqual(ring.coords[:], coords)
+        self.assertEqual('LinearRing',
+                         lgeos.GEOSGeomType(ring._geom).decode('ascii'))
+
+
     @unittest.skipIf(not numpy, 'Numpy required')
     def test_numpy(self):
 
diff --git a/shapely/tests/test_polygonize.py b/tests/test_polygonize.py
similarity index 100%
rename from shapely/tests/test_polygonize.py
rename to tests/test_polygonize.py
diff --git a/shapely/tests/test_predicates.py b/tests/test_predicates.py
similarity index 100%
rename from shapely/tests/test_predicates.py
rename to tests/test_predicates.py
diff --git a/shapely/tests/test_prepared.py b/tests/test_prepared.py
similarity index 100%
rename from shapely/tests/test_prepared.py
rename to tests/test_prepared.py
diff --git a/shapely/tests/test_products_z.py b/tests/test_products_z.py
similarity index 100%
rename from shapely/tests/test_products_z.py
rename to tests/test_products_z.py
diff --git a/shapely/tests/test_singularity.py b/tests/test_singularity.py
similarity index 100%
rename from shapely/tests/test_singularity.py
rename to tests/test_singularity.py
diff --git a/tests/test_strtree.py b/tests/test_strtree.py
new file mode 100644
index 0000000..7de3921
--- /dev/null
+++ b/tests/test_strtree.py
@@ -0,0 +1,23 @@
+from . import unittest
+
+from shapely.strtree import STRtree
+from shapely.geometry import Point
+from shapely.geos import geos_version
+
+
+ at unittest.skipIf(geos_version < (3, 4, 2), 'GEOS 3.4.2 required')
+class STRTestCase(unittest.TestCase):
+    def test_query(self):
+        points = [Point(i, i) for i in range(10)]
+        tree = STRtree(points)
+        results = tree.query(Point(2,2).buffer(0.99))
+        self.assertEqual(len(results), 1)
+        results = tree.query(Point(2,2).buffer(1.0))
+        self.assertEqual(len(results), 3)
+
+
+def test_suite():
+    return unittest.TestLoader().loadTestsFromTestCase(STRTestCase)
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/shapely/tests/test_styles.py b/tests/test_styles.py
similarity index 100%
rename from shapely/tests/test_styles.py
rename to tests/test_styles.py
diff --git a/shapely/tests/test_transform.py b/tests/test_transform.py
similarity index 91%
rename from shapely/tests/test_transform.py
rename to tests/test_transform.py
index c1b8d94..8c8cb55 100644
--- a/shapely/tests/test_transform.py
+++ b/tests/test_transform.py
@@ -26,6 +26,12 @@ class IdentityTestCase(unittest.TestCase):
         self.assertEqual(h.geom_type, 'LineString')
         self.assertEqual(list(h.coords), [(0, 1), (2, 3)])
 
+    def test_linearring(self):
+        g = geometry.LinearRing(((0, 1), (2, 3), (2, 2), (0, 1)))
+        h = transform(self.func, g)
+        self.assertEqual(h.geom_type, 'LinearRing')
+        self.assertEqual(list(h.coords), [(0, 1), (2, 3), (2, 2), (0, 1)])
+
     def test_polygon(self):
         g = geometry.Point(0, 1).buffer(1.0)
         h = transform(self.func, g)
diff --git a/shapely/tests/test_union.py b/tests/test_union.py
similarity index 100%
rename from shapely/tests/test_union.py
rename to tests/test_union.py
diff --git a/shapely/tests/test_validation.py b/tests/test_validation.py
similarity index 100%
rename from shapely/tests/test_validation.py
rename to tests/test_validation.py
diff --git a/tests/test_vectorized.py b/tests/test_vectorized.py
new file mode 100644
index 0000000..00673ad
--- /dev/null
+++ b/tests/test_vectorized.py
@@ -0,0 +1,107 @@
+from . import unittest, numpy
+from shapely.geometry import Point, box, MultiPolygon
+from shapely.vectorized import contains, touches
+
+try:
+    import numpy as np
+except ImportError:
+    pass
+
+
+ at unittest.skipIf(not numpy, 'numpy required')
+class VectorizedContainsTestCase(unittest.TestCase):
+    def assertContainsResults(self, geom, x, y):
+        result = contains(geom, x, y)
+        x = np.asanyarray(x)
+        y = np.asanyarray(y) 
+
+        self.assertIsInstance(result, np.ndarray)
+        self.assertEqual(result.dtype, np.bool)
+
+        result_flat = result.flat
+        x_flat, y_flat = x.flat, y.flat
+
+        # Do the equivalent operation, only slowly, comparing the result
+        # as we go.
+        for idx in range(x.size):
+            self.assertEqual(result_flat[idx], geom.contains(Point(x_flat[idx],
+                                                                   y_flat[idx])))
+        return result
+
+    def construct_torus(self):
+        point = Point(0, 0)
+        return point.buffer(5).symmetric_difference(point.buffer(2.5))
+
+    def test_contains_poly(self):
+        y, x = np.mgrid[-10:10:5j], np.mgrid[-5:15:5j]
+        self.assertContainsResults(self.construct_torus(), x, y)
+
+    def test_contains_point(self):
+        y, x = np.mgrid[-10:10:5j], np.mgrid[-5:15:5j]
+        self.assertContainsResults(Point(x[0], y[0]), x, y)
+    
+    def test_contains_linestring(self):
+        y, x = np.mgrid[-10:10:5j], np.mgrid[-5:15:5j]
+        self.assertContainsResults(Point(x[0], y[0]), x, y)
+    
+    def test_contains_multipoly(self):
+        y, x = np.mgrid[-10:10:5j], np.mgrid[-5:15:5j]
+        # Construct a geometry of the torus cut in half vertically.
+        cut_poly = box(-1, -10, -2.5, 10)
+        geom = self.construct_torus().difference(cut_poly)
+        self.assertIsInstance(geom, MultiPolygon)
+        self.assertContainsResults(geom, x, y)
+
+    def test_y_array_order(self):
+        y, x = np.mgrid[-10:10:5j, -5:15:5j]
+        y = y.copy(order='f')
+        self.assertContainsResults(self.construct_torus(), x, y)
+    
+    def test_x_array_order(self):
+        y, x = np.mgrid[-10:10:5j, -5:15:5j]
+        x = x.copy(order='f')
+        self.assertContainsResults(self.construct_torus(), x, y)
+    
+    def test_xy_array_order(self):
+        y, x = np.mgrid[-10:10:5j, -5:15:5j]
+        x = x.copy(order='f')
+        y = y.copy(order='f')
+        result = self.assertContainsResults(self.construct_torus(), x, y)
+        # We always return a C_CONTIGUOUS array.
+        self.assertTrue(result.flags['C_CONTIGUOUS'])
+    
+    def test_array_dtype(self):
+        y, x = np.mgrid[-10:10:5j], np.mgrid[-5:15:5j]
+        x = x.astype(np.int16)
+        self.assertContainsResults(self.construct_torus(), x, y)
+    
+    def test_array_2d(self):
+        y, x = np.mgrid[-10:10:15j, -5:15:16j]
+        result = self.assertContainsResults(self.construct_torus(), x, y)
+        self.assertEqual(result.shape, x.shape)
+
+    def test_shapely_xy_attr_contains(self):
+        g = Point(0, 0).buffer(10.0)
+        self.assertContainsResults(self.construct_torus(), *g.exterior.xy)
+
+
+ at unittest.skipIf(not numpy, 'numpy required')
+class VectorizedTouchesTestCase(unittest.TestCase):
+    def test_touches(self):
+        y, x = np.mgrid[-2:3:6j, -1:3:5j]
+        geom = box(0, -1, 2, 2)
+        result = touches(geom, x, y)
+        expected = np.array([[False, False, False, False, False],
+                             [False,  True,  True,  True, False],
+                             [False,  True, False,  True, False],
+                             [False,  True, False,  True, False],
+                             [False,  True,  True,  True, False],
+                             [False, False, False, False, False]], dtype=bool)
+        from numpy.testing import assert_array_equal
+        assert_array_equal(result, expected)
+        
+        
+
+
+def test_suite():
+    return unittest.TestLoader().loadTestsFromTestCase(VectorizedContainsTestCase)
diff --git a/shapely/tests/test_xy.py b/tests/test_xy.py
similarity index 100%
rename from shapely/tests/test_xy.py
rename to tests/test_xy.py
diff --git a/shapely/tests/threading_test.py b/tests/threading_test.py
similarity index 100%
rename from shapely/tests/threading_test.py
rename to tests/threading_test.py
diff --git a/shapely/tests/valgrind-python.supp b/tests/valgrind-python.supp
similarity index 100%
rename from shapely/tests/valgrind-python.supp
rename to tests/valgrind-python.supp

-- 
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