[python-shapely] 95/148: Imported Upstream version 1.3.0

Sebastiaan Couwenberg sebastic at moszumanska.debian.org
Thu Aug 20 17:42:07 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 99bac80bd72216c4b0cec4e367bcc1f204527d2d
Author: Pietro Battiston <me at pietrobattiston.it>
Date:   Mon Jan 20 10:31:51 2014 +0100

    Imported Upstream version 1.3.0
---
 .gitignore                                     |   10 +
 .travis.yml                                    |   22 +
 CHANGES.txt                                    |   52 +-
 CREDITS.txt                                    |   22 +
 DLLs_AMD64_VC9/README.txt                      |    1 +
 DLLs_x86_VC7/README.txt                        |    1 +
 DLLs_x86_VC9/README.txt                        |    1 +
 GEOS-C-API.txt                                 |   55 +
 MANIFEST.in                                    |   12 +
 PKG-INFO                                       |  396 --
 README.rst                                     |   94 +-
 Shapely.egg-info/PKG-INFO                      |  396 --
 Shapely.egg-info/SOURCES.txt                   |   87 -
 Shapely.egg-info/dependency_links.txt          |    1 -
 Shapely.egg-info/top_level.txt                 |    1 -
 docs/Makefile                                  |   95 +
 docs/code/buffer.py                            |   63 +
 docs/code/cascaded_union.py                    |   47 +
 docs/code/convex_hull.py                       |   54 +
 docs/code/difference.py                        |   55 +
 docs/code/figures.py                           |    9 +
 docs/code/geometrycollection.py                |   67 +
 docs/code/intersection-sym-difference.py       |   61 +
 docs/code/linearring.py                        |   59 +
 docs/code/linestring.py                        |   63 +
 docs/code/multilinestring.py                   |   69 +
 docs/code/multipolygon.py                      |   68 +
 docs/code/parallel_offset.py                   |   80 +
 docs/code/parallel_offset_mitre.py             |   80 +
 docs/code/polygon.py                           |   67 +
 docs/code/polygon2.py                          |   71 +
 docs/code/rotate.py                            |   57 +
 docs/code/scale.py                             |   64 +
 docs/code/simplify.py                          |   55 +
 docs/code/skew.py                              |   77 +
 docs/code/union.py                             |   61 +
 docs/conf.py                                   |  212 ++
 docs/design.txt                                |   30 +
 docs/images/4511827859_b5822043b7_o.png        |  Bin 0 -> 70577 bytes
 docs/images/buffer.png                         |  Bin 0 -> 19096 bytes
 docs/images/convex-hull.png                    |  Bin 0 -> 4050 bytes
 docs/images/simplify.png                       |  Bin 0 -> 19883 bytes
 docs/index.txt                                 |   19 +
 docs/manual.txt                                |  637 +++-
 docs/project.txt                               |    4 +
 docs/sphinxext/apigen.py                       |  427 +++
 docs/sphinxext/docscrape.py                    |  497 +++
 docs/sphinxext/docscrape_sphinx.py             |  136 +
 docs/sphinxext/inheritance_diagram.py          |  407 +++
 docs/sphinxext/ipython_console_highlighting.py |  114 +
 docs/sphinxext/numpydoc.py                     |  116 +
 requirements-dev.txt                           |    4 +
 setup.cfg                                      |    5 -
 setup.py                                       |  237 +-
 shapely/__init__.py                            |    3 +-
 shapely/affinity.py                            |  255 ++
 shapely/algorithms/cga.py                      |    1 -
 shapely/coords.py                              |   30 +-
 shapely/ctypes_declarations.py                 |  537 ++-
 shapely/geometry/__init__.py                   |   28 +-
 shapely/geometry/base.py                       |  287 +-
 shapely/geometry/geo.py                        |   16 +-
 shapely/geometry/linestring.py                 |   76 +-
 shapely/geometry/multilinestring.py            |   11 +-
 shapely/geometry/multipoint.py                 |   15 +-
 shapely/geometry/multipolygon.py               |   20 +-
 shapely/geometry/point.py                      |   42 +-
 shapely/geometry/polygon.py                    |   45 +-
 shapely/geometry/proxy.py                      |    4 +-
 shapely/geos.py                                |  566 ++-
 shapely/impl.py                                |   49 +-
 shapely/iterops.py                             |    4 +-
 shapely/ops.py                                 |  168 +-
 shapely/speedups/__init__.py                   |    5 +-
 shapely/speedups/_speedups.c                   | 4560 ------------------------
 shapely/tests/Array.txt                        |   86 -
 shapely/tests/GeoInterface.txt                 |   69 -
 shapely/tests/IterOps.txt                      |   31 -
 shapely/tests/LineString.txt                   |  134 -
 shapely/tests/MultiLineString.txt              |   81 -
 shapely/tests/MultiPoint.txt                   |  101 -
 shapely/tests/MultiPolygon.txt                 |   71 -
 shapely/tests/Operations.txt                   |   89 -
 shapely/tests/Persist.txt                      |   35 -
 shapely/tests/Point.txt                        |  148 -
 shapely/tests/Polygon.txt                      |  143 -
 shapely/tests/Predicates.txt                   |   51 -
 shapely/tests/__init__.py                      |   66 +-
 shapely/tests/attribute-chains.txt             |   40 -
 shapely/tests/binascii_hex.txt                 |   18 +-
 shapely/tests/cascaded_union.txt               |   24 -
 shapely/tests/dimensions.txt                   |   17 -
 shapely/tests/invalid_intersection.txt         |   29 -
 shapely/tests/linemerge.txt                    |   61 -
 shapely/tests/polygonize.txt                   |   29 -
 shapely/tests/rungrind.dist                    |    4 +
 shapely/tests/test_affinity.py                 |  251 ++
 shapely/tests/test_box.py                      |    7 +-
 shapely/tests/test_cga.py                      |   34 +-
 shapely/tests/test_collection.py               |   24 +-
 shapely/tests/test_delegated.py                |   12 +-
 shapely/tests/test_dlls.py                     |   12 +-
 shapely/tests/test_doctests.py                 |   21 +-
 shapely/tests/test_emptiness.py                |   52 +-
 shapely/tests/test_equality.py                 |   27 +-
 shapely/tests/test_geointerface.py             |   74 +
 shapely/tests/test_geomseq.py                  |    6 +-
 shapely/tests/test_getitem.py                  |   56 +-
 shapely/tests/test_invalid_geometries.py       |   30 +
 shapely/tests/test_iterops.py                  |   47 +
 shapely/tests/test_linear_referencing.py       |   63 +-
 shapely/tests/test_linemerge.py                |   52 +
 shapely/tests/test_linestring.py               |  101 +
 shapely/tests/test_locale.py                   |   56 +
 shapely/tests/test_mapping.py                  |    8 +-
 shapely/tests/test_multilinestring.py          |   61 +
 shapely/tests/test_multipoint.py               |   73 +
 shapely/tests/test_multipolygon.py             |   72 +
 shapely/tests/test_ndarrays.py                 |   54 +
 shapely/tests/test_operations.py               |   70 +
 shapely/tests/test_operators.py                |   18 +
 shapely/tests/test_persist.py                  |   34 +
 shapely/tests/test_pickle.py                   |   22 +
 shapely/tests/test_point.py                    |  139 +
 shapely/tests/test_polygon.py                  |  186 +
 shapely/tests/test_polygonize.py               |   49 +
 shapely/tests/test_predicates.py               |   34 +
 shapely/tests/test_prepared.py                 |   13 +-
 shapely/tests/test_products_z.py               |   15 +-
 shapely/tests/test_singularity.py              |   10 +-
 shapely/tests/test_speedups.py                 |   25 +-
 shapely/tests/test_styles.py                   |   20 +
 shapely/tests/test_transform.py                |   78 +
 shapely/tests/test_union.py                    |   72 +
 shapely/tests/test_validation.py               |    8 +-
 shapely/tests/test_xy.py                       |   13 +-
 shapely/tests/threading_test.py                |   37 +-
 shapely/tests/valgrind-python.supp             |  240 ++
 shapely/tests/wkt_locale.txt                   |   28 -
 shapely/validation.py                          |    3 +-
 shapely/wkb.py                                 |   53 +-
 shapely/wkt.py                                 |   28 +-
 tools/README.txt                               |    0
 143 files changed, 8062 insertions(+), 7693 deletions(-)

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..8be8256
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,10 @@
+*.pyc
+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
new file mode 100644
index 0000000..0cdaccf
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,22 @@
+language: python
+
+python:
+  - "2.6"
+  - "2.7"
+  - "3.2"
+  - "3.3"
+
+before_install:
+  - sudo add-apt-repository -y ppa:ubuntugis/ppa
+  - sudo apt-get update -qq
+  - sudo apt-get install -qq libgeos-dev
+  - if [[ $TRAVIS_PYTHON_VERSION == "2.6" ]]; then pip install unittest2; fi
+  - pip install -r requirements-dev.txt
+
+install:
+  - python setup.py build
+
+script: "python setup.py test"
+
+notifications:
+    email: false
diff --git a/CHANGES.txt b/CHANGES.txt
index ba2db87..99eb0fb 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,7 +1,57 @@
-=======
 Changes
 =======
 
+1.3.0 (2013-12-31)
+------------------
+- Include support for Python 3.2 and 3.3 (#56), minimum version is now 2.6.
+- Switch to GEOS WKT/WKB Reader/Writer API, with defaults changed to enable 3D
+  output dimensions, and to 'trim' WKT output for GEOS >=3.3.0.
+- Use GEOS version instead of GEOS C API version to determine library
+  capabilities (#65).
+
+1.2.19 (2013-12-30)
+-------------------
+- Add buffering style options (#55).
+
+1.2.18 (2013-07-23)
+--------------------
+- Add shapely.ops.transform.
+- Permit empty sequences in collection constructors (#49, #50).
+- Individual polygons in MultiPolygon.__geo_interface__ are changed to tuples
+  to match Polygon.__geo_interface__ (#51).
+- Add shapely.ops.polygonize_full (#57).
+
+1.2.17 (2013-01-27)
+-------------------
+- Avoid circular import between wkt/wkb and geometry.base by moving calls
+  to GEOS serializers to the latter module.
+- Set _ndim when unpickling (issue #6).
+- Don't install DLLs to Python's DLL directory (#37).
+- Add affinity module of affine transformation (#31).
+- Fix NameError that blocked installation with PyPy (#40, #41).
+
+1.2.16 (2012-09-18)
+-------------------
+- Add ops.unary_union function.
+- Alias ops.cascaded_union to ops.unary_union when GEOS CAPI >= (1,7,0).
+- Add geos_version_string attribute to shapely.geos.
+- Ensure parent is set when child geometry is accessed.
+- Generate _speedups.c using Cython when building from repo when missing,
+  stale, or the build target is "sdist".
+- The is_simple predicate of invalid, self-intersecting linear rings now
+  returns ``False``.
+- Remove VERSION.txt from repo, it's now written by the distutils setup script
+  with value of shapely.__version__.
+
+1.2.15 (2012-06-27)
+-------------------
+- Eliminate numerical sensitivity in a method chaining test (Debian bug
+  #663210).
+- Account for cascaded union of random buffered test points being a polygon
+  or multipolygon (Debian bug #666655).
+- Use Cython to build speedups if it is installed.
+- Avoid stumbling over SVN revision numbers in GEOS C API version strings.
+
 1.2.14 (2012-01-23)
 -------------------
 - A geometry's coords property is now sliceable, yielding a list of coordinate
diff --git a/CREDITS.txt b/CREDITS.txt
index 94eeee9..569280f 100644
--- a/CREDITS.txt
+++ b/CREDITS.txt
@@ -1,3 +1,6 @@
+Credits
+=======
+
 Shapely is written by:
 
 * Sean Gillies
@@ -7,12 +10,29 @@ Shapely is written by:
 
 Patches contributed by:
 
+* Allan Adair (https://github.com/allanadair)
 * Howard Butler
+* Gabi Davar (https://github.com/mindw)
+* Phil Elson (https://github.com/pelson)
+* Johan Euphrosine (https://github.com/proppy)
+* Bertrand Gervais (https://github.com/BertrandGervais)
+* Marc Jansen (https://github.com/marcjansen)
+* Kelsey Jordahl (https://github.com/kjordahl)
 * Fr |eaigue| d |eaigue| ric Junod
+* Thomas Kluyver (https://github.com/takluyver)
 * Eric Lemoine
+* Naveen Michaud-Agrawal (https://github.com/nmichaud)
+* psagers https://github.com/psagers
+* Jeethu Rao (https://github.com/jeethu)
+* Benjamin Root (https://github.com/WeatherGod)
+* Jason Sanford (https://github.com/JasonSanford)
+* Johannes Schönberger (https://github.com/ahojnnes)
 * Jonathan Tartley
 * Kristian Thy
 * Mike Toews (https://github.com/mwtoews)
+* Maarten Vermeyen (https://github.com/maarten-vermeyen)
+
+See also: https://github.com/Toblerity/Shapely/graphs/contributors.
 
 Additional help from:
 
@@ -24,4 +44,6 @@ Additional help from:
 Major portions of this work were supported by a grant (for Pleiades_) from the
 U.S. National Endowment for the Humanities (http://www.neh.gov).
 
+.. |eaigue| unicode:: U+00E9
+   :trim:
 .. _Pleiades: http://pleiades.stoa.org
diff --git a/DLLs_AMD64_VC9/README.txt b/DLLs_AMD64_VC9/README.txt
new file mode 100644
index 0000000..51f1fc5
--- /dev/null
+++ b/DLLs_AMD64_VC9/README.txt
@@ -0,0 +1 @@
+AMD64 DLLs
diff --git a/DLLs_x86_VC7/README.txt b/DLLs_x86_VC7/README.txt
new file mode 100644
index 0000000..74a7706
--- /dev/null
+++ b/DLLs_x86_VC7/README.txt
@@ -0,0 +1 @@
+x86 DLLs
diff --git a/DLLs_x86_VC9/README.txt b/DLLs_x86_VC9/README.txt
new file mode 100644
index 0000000..74a7706
--- /dev/null
+++ b/DLLs_x86_VC9/README.txt
@@ -0,0 +1 @@
+x86 DLLs
diff --git a/GEOS-C-API.txt b/GEOS-C-API.txt
new file mode 100644
index 0000000..c4b5ac3
--- /dev/null
+++ b/GEOS-C-API.txt
@@ -0,0 +1,55 @@
+Ctypes declarations for functions present in GEOS C API 1.4, but not present in
+1.3, are listed below:
+
+lgeos.GEOS_getWKBOutputDims.restype = ctypes.c_int
+lgeos.GEOS_getWKBByteOrder.restype = ctypes.c_int
+lgeos.GEOS_setWKBByteOrder.restype = ctypes.c_int
+lgeos.GEOS_setWKBByteOrder.argtypes = [ctypes.c_int]
+lgeos.GEOSGeomFromHEX_buf.restype = ctypes.c_void_p
+lgeos.GEOSGeomFromHEX_buf.argtypes = [ctypes.c_void_p, ctypes.c_size_t]
+lgeos.GEOSSimplify.restype = ctypes.c_void_p
+lgeos.GEOSSimplify.argtypes = [ctypes.c_void_p, ctypes.c_double]
+lgeos.GEOSTopologyPreserveSimplify.restype = ctypes.c_void_p
+lgeos.GEOSTopologyPreserveSimplify.argtypes = [ctypes.c_void_p, ctypes.c_double]
+lgeos.GEOSEqualsExact.restype = ctypes.c_int
+lgeos.GEOSEqualsExact.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_double]
+lgeos.GEOSNormalize.restype = ctypes.c_int
+lgeos.GEOSNormalize.argtypes = [ctypes.c_void_p]
+lgeos.GEOSWKTReader_create.restype = ctypes.c_void_p
+lgeos.GEOSWKTReader_destroy.restype = None
+lgeos.GEOSWKTReader_destroy.argtypes = [ctypes.c_void_p]
+lgeos.GEOSWKTReader_read.restype = ctypes.c_void_p
+lgeos.GEOSWKTReader_read.argtypes = [ctypes.c_void_p, ctypes.c_char_p]
+lgeos.GEOSWKTWriter_create.restype = ctypes.c_void_p
+lgeos.GEOSWKTWriter_destroy.restype = None
+lgeos.GEOSWKTWriter_destroy.argtypes = [ctypes.c_void_p]
+lgeos.GEOSWKTWriter_write.restype = ctypes.c_char_p
+lgeos.GEOSWKTWriter_write.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
+lgeos.GEOSWKBReader_create.restype = ctypes.c_void_p
+lgeos.GEOSWKBReader_destroy.restype = None
+lgeos.GEOSWKBReader_destroy.argtypes = [ctypes.c_void_p]
+lgeos.GEOSWKBReader_read.restype = ctypes.c_void_p
+lgeos.GEOSWKBReader_read.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_size_t]
+lgeos.GEOSWKBReader_readHEX.restype = ctypes.c_void_p
+lgeos.GEOSWKBReader_readHEX.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_size_t]
+lgeos.GEOSWKBWriter_create.restype = ctypes.c_void_p
+lgeos.GEOSWKBWriter_destroy.restype = None
+lgeos.GEOSWKBWriter_destroy.argtypes = [ctypes.c_void_p]
+lgeos.GEOSWKBWriter_getOutputDimension.restype = ctypes.c_int
+lgeos.GEOSWKBWriter_getOutputDimension.argtypes = [ctypes.c_void_p]
+lgeos.GEOSWKBWriter_setOutputDimension.restype = None
+lgeos.GEOSWKBWriter_setOutputDimension.argtypes = [ctypes.c_void_p, ctypes.c_int]
+lgeos.GEOSWKBWriter_getByteOrder.restype = ctypes.c_int
+lgeos.GEOSWKBWriter_getByteOrder.argtypes = [ctypes.c_void_p]
+lgeos.GEOSWKBWriter_setByteOrder.restype = None
+lgeos.GEOSWKBWriter_setByteOrder.argtypes = [ctypes.c_void_p, ctypes.c_int]
+lgeos.GEOSWKBWriter_getIncludeSRID.restype = ctypes.c_char
+lgeos.GEOSWKBWriter_getIncludeSRID.argtypes = [ctypes.c_void_p]
+lgeos.GEOSWKBWriter_setIncludeSRID.restype = None
+lgeos.GEOSWKBWriter_setIncludeSRID.argtypes = [ctypes.c_void_p, ctypes.c_char]
+
+Furthermore, the following unneeded declarations are removed to avoid problems
+with Debian's 2.2.3 package:
+
+lgeos.GEOSCoordSeq_getOrdinate.restype = ctypes.c_int
+lgeos.GEOSCoordSeq_getOrdinate.argtypes = [ctypes.c_void_p, ctypes.c_uint, ctypes.c_uint, ctypes.c_void_p]
diff --git a/MANIFEST.in b/MANIFEST.in
new file mode 100644
index 0000000..7e709ac
--- /dev/null
+++ b/MANIFEST.in
@@ -0,0 +1,12 @@
+exclude *.txt
+recursive-exclude manual *
+recursive-exclude debian *
+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 shapely/examples *.py
+recursive-exclude shapely/speedups *.pyx
+include docs/manual.txt
+exclude MANIFEST.in
diff --git a/PKG-INFO b/PKG-INFO
deleted file mode 100644
index 25243cd..0000000
--- a/PKG-INFO
+++ /dev/null
@@ -1,396 +0,0 @@
-Metadata-Version: 1.0
-Name: Shapely
-Version: 1.2.14
-Summary: Geometric objects, predicates, and operations
-Home-page: https://github.com/sgillies/shapely
-Author: Sean Gillies
-Author-email: sean.gillies at gmail.com
-License: BSD
-Description: ======
-        README
-        ======
-        
-        PostGIS-ish operations outside a database context for Pythoneers and
-        Pythonistas.
-        
-        .. image:: http://farm3.static.flickr.com/2738/4511827859_b5822043b7_o_d.png
-        :width: 800
-        :height: 400
-        
-        Shapely is a BSD-licensed Python package for manipulation and analysis of
-        planar geometric objects. It is based on the widely deployed GEOS_ (the engine
-        of PostGIS_) and JTS_ (from which GEOS is ported) libraries. This C dependency
-        is traded for the ability to execute with blazing speed. Shapely is not
-        concerned with data formats or coordinate systems, but can be readily
-        integrated with packages that are. For more details, see:
-        
-        * Shapely wiki_
-        * Shapely manual_
-        * Shapely `example apps`_
-        
-        Dependencies
-        ============
-        
-        Shapely 1.2 depends on:
-        
-        * Python >=2.5,<3
-        * libgeos_c >=3.1 (3.0 and below have not been tested, YMMV)
-        
-        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::
-        
-        $ pip install Shapely
-        
-        or from a source distribution with the setup script::
-        
-        $ python setup.py install
-        
-        Usage
-        =====
-        
-        Here is the canonical example of building an approximately circular patch by
-        buffering a point::
-        
-        >>> from shapely.geometry import Point
-        >>> patch = Point(0.0, 0.0).buffer(10.0)
-        >>> patch
-        <shapely.geometry.polygon.Polygon object at 0x...>
-        >>> patch.area
-        313.65484905459385
-        
-        See the manual_ for comprehensive usage snippets and the dissolve.py and
-        intersect.py `example apps`_.
-        
-        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.::
-        
-        >>> 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.::
-        
-        >>> from numpy import asarray
-        >>> ag = asarray(patch.exterior)
-        >>> ag
-        array([[  1.00000000e+01,   0.00000000e+00],
-        [  9.95184727e+00,  -9.80171403e-01],
-        [  9.80785280e+00,  -1.95090322e+00],
-        ...
-        [  1.00000000e+01,   0.00000000e+00]])
-        
-        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.::
-        
-        >>> x, y = patch.exterior.xy
-        >>> ax = asarray(x)
-        >>> ax
-        array([  1.00000000e+01,   9.95184727e+00,   9.80785280e+00,  ...])
-        
-        Numpy arrays can also be adapted to Shapely linestrings::
-        
-        >>> from shapely.geometry import asLineString
-        >>> asLineString(ag).length
-        62.806623139095073
-        >>> asLineString(ag).wkt
-        'LINESTRING (10.0000000000000000 0.0000000000000000, ...)'
-        
-        Testing
-        =======
-        
-        Shapely uses a Zope-stye suite of unittests and doctests, exercised via
-        setup.py.::
-        
-        $ python setup.py test
-        
-        Nosetests won't run the tests properly; Zope doctest suites are not currently
-        supported well by nose.
-        
-        Support
-        =======
-        
-        Bugs may be reported and questions asked via https://github.com/Toblerity/Shapely.
-        
-        Credits
-        =======
-        
-        Shapely is written by:
-        
-        * Sean Gillies
-        * Aron Bierbaum
-        * Kai Lautaportti
-        * Oliver Tonnhofer
-        
-        Patches contributed by:
-        
-        * Howard Butler
-        * Frédéric Junod
-        * Éric Lemoine
-        * Jonathan Tartley
-        * Kristian Thy
-        * Mike Toews (https://github.com/mwtoews)
-        
-        Additional help from:
-        
-        * Justin Bronn (GeoDjango) for ctypes inspiration
-        * Martin Davis (JTS)
-        * Jaakko Salli for the Windows distributions
-        * Sandro Santilli, Mateusz Loskot, Paul Ramsey, et al (GEOS_ Project)
-        
-        Major portions of this work were supported by a grant (for Pleiades_) from the
-        U.S. National Endowment for the Humanities (http://www.neh.gov).
-        
-        .. _JTS: http://www.vividsolutions.com/jts/jtshome.htm
-        .. _PostGIS: http://postgis.org
-        .. _GEOS: http://trac.osgeo.org/geos/
-        .. _example apps: http://trac.gispython.org/lab/wiki/Examples
-        .. _wiki: http://trac.gispython.org/lab/wiki/Shapely
-        .. _manual: http://toblerity.github.com/shapely/manual.html
-        .. _Pleiades: http://pleiades.stoa.org
-        
-        
-        Changes
-        =======
-        
-        1.2.14 (2012-01-23)
-        -------------------
-        - A geometry's coords property is now sliceable, yielding a list of coordinate
-        values.
-        - Homogeneous collections are now sliceable, yielding a new collection of the
-        same type.
-        
-        1.2.13 (2011-09-16)
-        -------------------
-        - Fixed errors in speedups on 32bit systems when GEOS references memory above
-        2GB.
-        - Add shapely.__version__ attribute.
-        - Update the manual.
-        
-        1.2.12 (2011-08-15)
-        -------------------
-        - Build Windows distributions with VC7 or VC9 as appropriate.
-        - More verbose report on failure to speed up.
-        - Fix for prepared geometries broken in 1.2.11.
-        - DO NOT INSTALL 1.2.11
-        
-        1.2.11 (2011-08-04)
-        -------------------
-        - Ignore AttributeError during exit.
-        - PyPy 1.5 support.
-        - Prevent operation on prepared geometry crasher (#12).
-        - Optional Cython speedups for Windows.
-        - Linux 3 platform support.
-        
-        1.2.10 (2011-05-09)
-        -------------------
-        - Add optional Cython speedups.
-        - Add is_cww predicate to LinearRing.
-        - Add function that forces orientation of Polygons.
-        - Disable build of speedups on Windows pending packaging work.
-        
-        1.2.9 (2011-03-31)
-        ------------------
-        - Remove extra glob import.
-        - Move examples to shapely.examples.
-        - Add box() constructor for rectangular polygons.
-        - Fix extraneous imports.
-        
-        1.2.8 (2011-12-03)
-        ------------------
-        - New parallel_offset method (#6).
-        - Support for Python 2.4.
-        
-        1.2.7 (2010-11-05)
-        ------------------
-        - Support for Windows eggs.
-        
-        1.2.6 (2010-10-21)
-        ------------------
-        - The geoms property of an empty collection yields [] instead of a ValueError
-        (#3).
-        - The coords and geometry type sproperties have the same behavior as above.
-        - Ensure that z values carry through into products of operations (#4).
-        
-        1.2.5 (2010-09-19)
-        ------------------
-        - Stop distributing docs/_build.
-        - Include library fallbacks in test_dlls.py for linux platform.
-        
-        1.2.4 (2010-09-09)
-        ------------------
-        - Raise AttributeError when there's no backend support for a method.
-        - Raise OSError if libgeos_c.so (or variants) can't be found and loaded.
-        - Add geos_c DLL loading support for linux platforms where find_library doesn't
-        work.
-        
-        1.2.3 (2010-08-17)
-        ------------------
-        - Add mapping function.
-        - Fix problem with GEOSisValidReason symbol for GEOS < 3.1.
-        
-        1.2.2 (2010-07-23)
-        ------------------
-        - Add representative_point method.
-        
-        1.2.1 (2010-06-23)
-        ------------------
-        - Fixed bounds of singular polygons.
-        - Added shapely.validation.explain_validity function (#226).
-        
-        1.2 (2010-05-27)
-        ----------------
-        - Final release.
-        
-        1.2rc2 (2010-05-26)
-        -------------------
-        - Add examples and tests to MANIFEST.in.
-        - Release candidate 2.
-        
-        1.2rc1 (2010-05-25)
-        -------------------
-        - Release candidate.
-        
-        1.2b7 (2010-04-22)
-        ------------------
-        - Memory leak associated with new empty geometry state fixed.
-        
-        1.2b6 (2010-04-13)
-        ------------------
-        - Broken GeometryCollection fixed.
-        
-        1.2b5 (2010-04-09)
-        ------------------
-        - Objects can be constructed from others of the same type, thereby making
-        copies. Collections can be constructed from sequences of objects, also making
-        copies.
-        - Collections are now iterators over their component objects.
-        - New code for manual figures, using the descartes package.
-        
-        1.2b4 (2010-03-19)
-        ------------------
-        - Adds support for the "sunos5" platform.
-        
-        1.2b3 (2010-02-28)
-        ------------------
-        - Only provide simplification implementations for GEOS C API >= 1.5.
-        
-        1.2b2 (2010-02-19)
-        ------------------
-        - Fix cascaded_union bug introduced in 1.2b1 (#212).
-        
-        1.2b1 (2010-02-18)
-        ------------------
-        - Update the README. Remove cruft from setup.py. Add some version 1.2 metadata
-        regarding required Python version (>=2.5,<3) and external dependency
-        (libgeos_c >= 3.1).
-        
-        1.2a6 (2010-02-09)
-        ------------------
-        - Add accessor for separate arrays of X and Y values (#210).
-        
-        TODO: fill gap here
-        
-        1.2a1 (2010-01-20)
-        ------------------
-        - Proper prototyping of WKB writer, and avoidance of errors on 64-bit systems
-        (#191).
-        - Prototype libgeos_c functions in a way that lets py2exe apps import shapely
-        (#189).
-        
-        1.2 Branched (2009-09-19)
-        
-        1.0.12 (2009-04-09)
-        -------------------
-        - Fix for references held by topology and predicate descriptors.
-        
-        1.0.11 (2008-11-20)
-        -------------------
-        - Work around bug in GEOS 2.2.3, GEOSCoordSeq_getOrdinate not exported properly
-        (#178).
-        
-        1.0.10 (2008-11-17)
-        -------------------
-        - Fixed compatibility with GEOS 2.2.3 that was broken in 1.0.8 release (#176).
-        
-        1.0.9 (2008-11-16)
-        ------------------
-        - Find and load MacPorts libgeos.
-        
-        1.0.8 (2008-11-01)
-        ------------------
-        - Fill out GEOS function result and argument types to prevent faults on a
-        64-bit arch.
-        
-        1.0.7 (2008-08-22)
-        ------------------
-        - Polygon rings now have the same dimensions as parent (#168).
-        - Eliminated reference cycles in polygons (#169).
-        
-        1.0.6 (2008-07-10)
-        ------------------
-        - Fixed adaptation of multi polygon data.
-        - Raise exceptions earlier from binary predicates.
-        - Beginning distributing new windows DLLs (#166).
-        
-        1.0.5 (2008-05-20)
-        ------------------
-        - Added access to GEOS polygonizer function.
-        - Raise exception when insufficient coordinate tuples are passed to LinearRing
-        constructor (#164).
-        
-        1.0.4 (2008-05-01)
-        ------------------
-        - Disentangle Python and topological equality (#163).
-        - Add shape(), a factory that copies coordinates from a geo interface provider.
-        To be used instead of asShape() unless you really need to store coordinates
-        outside shapely for efficient use in other code.
-        - Cache GEOS geometries in adapters (#163).
-        
-        1.0.3 (2008-04-09)
-        ------------------
-        - Do not release GIL when calling GEOS functions (#158).
-        - Prevent faults when chaining multiple GEOS operators (#159).
-        
-        1.0.2 (2008-02-26)
-        ------------------
-        - Fix loss of dimensionality in polygon rings (#155).
-        
-        1.0.1 (2008-02-08)
-        ------------------
-        - Allow chaining expressions involving coordinate sequences and geometry parts
-        (#151).
-        - Protect against abnormal use of coordinate accessors (#152).
-        - Coordinate sequences now implement the numpy array protocol (#153).
-        
-        1.0 (2008-01-18)
-        ----------------
-        - Final release.
-        
-        1.0 RC2 (2008-01-16)
-        --------------------
-        - Added temporary solution for #149.
-        
-        1.0 RC1 (2008-01-14)
-        --------------------
-        - First release candidate
-        
-        
-Keywords: geometry topology gis
-Platform: UNKNOWN
-Classifier: Development Status :: 5 - Production/Stable
-Classifier: Intended Audience :: Developers
-Classifier: Intended Audience :: Science/Research
-Classifier: License :: OSI Approved :: BSD License
-Classifier: Operating System :: OS Independent
-Classifier: Programming Language :: Python
-Classifier: Topic :: Scientific/Engineering :: GIS
diff --git a/README.rst b/README.rst
index b336670..effd748 100644
--- a/README.rst
+++ b/README.rst
@@ -1,31 +1,28 @@
-======
-README
-======
+=======
+Shapely
+=======
 
-PostGIS-ish operations outside a database context for Pythoneers and
-Pythonistas.
+Manipulation and analysis of geometric objects in the Cartesian plane.
 
-.. image:: http://farm3.static.flickr.com/2738/4511827859_b5822043b7_o_d.png
+.. image:: http://farm3.staticflickr.com/2738/4511827859_b5822043b7_o_d.png
    :width: 800
    :height: 400
 
 Shapely is a BSD-licensed Python package for manipulation and analysis of
 planar geometric objects. It is based on the widely deployed GEOS_ (the engine
-of PostGIS_) and JTS_ (from which GEOS is ported) libraries. This C dependency
-is traded for the ability to execute with blazing speed. Shapely is not
+of PostGIS_) and JTS_ (from which GEOS is ported) libraries. Shapely is not
 concerned with data formats or coordinate systems, but can be readily
 integrated with packages that are. For more details, see:
 
-* Shapely wiki_
 * Shapely manual_
 * Shapely `example apps`_
 
-Dependencies
+Requirements
 ============
 
-Shapely 1.2 depends on:
+Shapely 1.3 requires
 
-* Python >=2.5,<3
+* Python >=2.6 (including Python 3.x)
 * libgeos_c >=3.1 (3.0 and below have not been tested, YMMV)
 
 Installation
@@ -41,6 +38,12 @@ or from a source distribution with the setup script::
 
   $ python setup.py install
 
+.. warning:: Windows users:
+   do not under any circumstances use pip (or easy_install) to uninstall
+   Shapely versions < 1.2.17. Due to the way Shapely used to install its GEOS
+   DLL and a distribute or setuptools bug, your Python installation may be
+   broken by an uninstall command. Shapely 1.2.17 will uninstall safely.
+
 Usage
 =====
 
@@ -57,7 +60,7 @@ buffering a point::
 See the manual_ for comprehensive usage snippets and the dissolve.py and
 intersect.py `example apps`_.
 
-Integration 
+Integration
 ===========
 
 Shapely does not read or write data files, but it can serialize and deserialize
@@ -80,7 +83,7 @@ provide the Numpy array interface.::
          ...
          [  1.00000000e+01,   0.00000000e+00]])
 
-That yields a numpy array of [x, y] arrays. This is not always exactly what one
+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.::
 
@@ -97,13 +100,35 @@ Numpy arrays can also be adapted to Shapely linestrings::
   >>> asLineString(ag).wkt
   'LINESTRING (10.0000000000000000 0.0000000000000000, ...)'
 
-Testing
-=======
+Shapely can also integrate with other Python GIS packages using data modeled
+after GeoJSON.
+
+.. sourcecode:: pycon
+
+  >>> import json
+  >>> from shapely.geometry import mapping, shape
+  >>> s = shape(json.loads('{"type": "Point", "coordinates": [0.0, 0.0]}'))
+  >>> s
+  <shapely.geometry.point.Point object at 0x...>
+  >>> print(json.dumps(mapping(s)))
+  {"type": "Point", "coordinates": [0.0, 0.0]}
+
+Development and Testing
+=======================
+
+Dependecies 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.::
+
+  $ virtualenv .
+  $ source bin/activate
+  (env)$ pip install -r requirements-dev.txt
+  (env)$ python setup.py develop
 
 Shapely uses a Zope-stye suite of unittests and doctests, exercised via
 setup.py.::
 
-  $ python setup.py test
+  (env)$ python setup.py test
 
 Nosetests won't run the tests properly; Zope doctest suites are not currently
 supported well by nose.
@@ -111,42 +136,17 @@ supported well by nose.
 Support
 =======
 
-Bugs may be reported and questions asked via https://github.com/Toblerity/Shapely.
-
-Credits
-=======
-
-Shapely is written by:
-
-* Sean Gillies
-* Aron Bierbaum
-* Kai Lautaportti
-* Oliver Tonnhofer
-
-Patches contributed by:
-
-* Howard Butler
-* Frédéric Junod
-* Éric Lemoine
-* Jonathan Tartley
-* Kristian Thy
-* Mike Toews (https://github.com/mwtoews)
-
-Additional help from:
+Please discuss Shapely with us at
+http://lists.gispython.org/mailman/listinfo/community.
 
-* Justin Bronn (GeoDjango) for ctypes inspiration
-* Martin Davis (JTS)
-* Jaakko Salli for the Windows distributions
-* Sandro Santilli, Mateusz Loskot, Paul Ramsey, et al (GEOS_ Project)
+Bugs may be reported at https://github.com/Toblerity/Shapely.
 
-Major portions of this work were supported by a grant (for Pleiades_) from the
-U.S. National Endowment for the Humanities (http://www.neh.gov).
+.. include:: ../CREDITS.txt
 
 .. _JTS: http://www.vividsolutions.com/jts/jtshome.htm
 .. _PostGIS: http://postgis.org
 .. _GEOS: http://trac.osgeo.org/geos/
-.. _example apps: http://trac.gispython.org/lab/wiki/Examples
-.. _wiki: http://trac.gispython.org/lab/wiki/Shapely
+.. _example apps: https://github.com/sgillies/shapely/tree/master/shapely/examples
 .. _manual: http://toblerity.github.com/shapely/manual.html
 .. _Pleiades: http://pleiades.stoa.org
 
diff --git a/Shapely.egg-info/PKG-INFO b/Shapely.egg-info/PKG-INFO
deleted file mode 100644
index 25243cd..0000000
--- a/Shapely.egg-info/PKG-INFO
+++ /dev/null
@@ -1,396 +0,0 @@
-Metadata-Version: 1.0
-Name: Shapely
-Version: 1.2.14
-Summary: Geometric objects, predicates, and operations
-Home-page: https://github.com/sgillies/shapely
-Author: Sean Gillies
-Author-email: sean.gillies at gmail.com
-License: BSD
-Description: ======
-        README
-        ======
-        
-        PostGIS-ish operations outside a database context for Pythoneers and
-        Pythonistas.
-        
-        .. image:: http://farm3.static.flickr.com/2738/4511827859_b5822043b7_o_d.png
-        :width: 800
-        :height: 400
-        
-        Shapely is a BSD-licensed Python package for manipulation and analysis of
-        planar geometric objects. It is based on the widely deployed GEOS_ (the engine
-        of PostGIS_) and JTS_ (from which GEOS is ported) libraries. This C dependency
-        is traded for the ability to execute with blazing speed. Shapely is not
-        concerned with data formats or coordinate systems, but can be readily
-        integrated with packages that are. For more details, see:
-        
-        * Shapely wiki_
-        * Shapely manual_
-        * Shapely `example apps`_
-        
-        Dependencies
-        ============
-        
-        Shapely 1.2 depends on:
-        
-        * Python >=2.5,<3
-        * libgeos_c >=3.1 (3.0 and below have not been tested, YMMV)
-        
-        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::
-        
-        $ pip install Shapely
-        
-        or from a source distribution with the setup script::
-        
-        $ python setup.py install
-        
-        Usage
-        =====
-        
-        Here is the canonical example of building an approximately circular patch by
-        buffering a point::
-        
-        >>> from shapely.geometry import Point
-        >>> patch = Point(0.0, 0.0).buffer(10.0)
-        >>> patch
-        <shapely.geometry.polygon.Polygon object at 0x...>
-        >>> patch.area
-        313.65484905459385
-        
-        See the manual_ for comprehensive usage snippets and the dissolve.py and
-        intersect.py `example apps`_.
-        
-        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.::
-        
-        >>> 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.::
-        
-        >>> from numpy import asarray
-        >>> ag = asarray(patch.exterior)
-        >>> ag
-        array([[  1.00000000e+01,   0.00000000e+00],
-        [  9.95184727e+00,  -9.80171403e-01],
-        [  9.80785280e+00,  -1.95090322e+00],
-        ...
-        [  1.00000000e+01,   0.00000000e+00]])
-        
-        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.::
-        
-        >>> x, y = patch.exterior.xy
-        >>> ax = asarray(x)
-        >>> ax
-        array([  1.00000000e+01,   9.95184727e+00,   9.80785280e+00,  ...])
-        
-        Numpy arrays can also be adapted to Shapely linestrings::
-        
-        >>> from shapely.geometry import asLineString
-        >>> asLineString(ag).length
-        62.806623139095073
-        >>> asLineString(ag).wkt
-        'LINESTRING (10.0000000000000000 0.0000000000000000, ...)'
-        
-        Testing
-        =======
-        
-        Shapely uses a Zope-stye suite of unittests and doctests, exercised via
-        setup.py.::
-        
-        $ python setup.py test
-        
-        Nosetests won't run the tests properly; Zope doctest suites are not currently
-        supported well by nose.
-        
-        Support
-        =======
-        
-        Bugs may be reported and questions asked via https://github.com/Toblerity/Shapely.
-        
-        Credits
-        =======
-        
-        Shapely is written by:
-        
-        * Sean Gillies
-        * Aron Bierbaum
-        * Kai Lautaportti
-        * Oliver Tonnhofer
-        
-        Patches contributed by:
-        
-        * Howard Butler
-        * Frédéric Junod
-        * Éric Lemoine
-        * Jonathan Tartley
-        * Kristian Thy
-        * Mike Toews (https://github.com/mwtoews)
-        
-        Additional help from:
-        
-        * Justin Bronn (GeoDjango) for ctypes inspiration
-        * Martin Davis (JTS)
-        * Jaakko Salli for the Windows distributions
-        * Sandro Santilli, Mateusz Loskot, Paul Ramsey, et al (GEOS_ Project)
-        
-        Major portions of this work were supported by a grant (for Pleiades_) from the
-        U.S. National Endowment for the Humanities (http://www.neh.gov).
-        
-        .. _JTS: http://www.vividsolutions.com/jts/jtshome.htm
-        .. _PostGIS: http://postgis.org
-        .. _GEOS: http://trac.osgeo.org/geos/
-        .. _example apps: http://trac.gispython.org/lab/wiki/Examples
-        .. _wiki: http://trac.gispython.org/lab/wiki/Shapely
-        .. _manual: http://toblerity.github.com/shapely/manual.html
-        .. _Pleiades: http://pleiades.stoa.org
-        
-        
-        Changes
-        =======
-        
-        1.2.14 (2012-01-23)
-        -------------------
-        - A geometry's coords property is now sliceable, yielding a list of coordinate
-        values.
-        - Homogeneous collections are now sliceable, yielding a new collection of the
-        same type.
-        
-        1.2.13 (2011-09-16)
-        -------------------
-        - Fixed errors in speedups on 32bit systems when GEOS references memory above
-        2GB.
-        - Add shapely.__version__ attribute.
-        - Update the manual.
-        
-        1.2.12 (2011-08-15)
-        -------------------
-        - Build Windows distributions with VC7 or VC9 as appropriate.
-        - More verbose report on failure to speed up.
-        - Fix for prepared geometries broken in 1.2.11.
-        - DO NOT INSTALL 1.2.11
-        
-        1.2.11 (2011-08-04)
-        -------------------
-        - Ignore AttributeError during exit.
-        - PyPy 1.5 support.
-        - Prevent operation on prepared geometry crasher (#12).
-        - Optional Cython speedups for Windows.
-        - Linux 3 platform support.
-        
-        1.2.10 (2011-05-09)
-        -------------------
-        - Add optional Cython speedups.
-        - Add is_cww predicate to LinearRing.
-        - Add function that forces orientation of Polygons.
-        - Disable build of speedups on Windows pending packaging work.
-        
-        1.2.9 (2011-03-31)
-        ------------------
-        - Remove extra glob import.
-        - Move examples to shapely.examples.
-        - Add box() constructor for rectangular polygons.
-        - Fix extraneous imports.
-        
-        1.2.8 (2011-12-03)
-        ------------------
-        - New parallel_offset method (#6).
-        - Support for Python 2.4.
-        
-        1.2.7 (2010-11-05)
-        ------------------
-        - Support for Windows eggs.
-        
-        1.2.6 (2010-10-21)
-        ------------------
-        - The geoms property of an empty collection yields [] instead of a ValueError
-        (#3).
-        - The coords and geometry type sproperties have the same behavior as above.
-        - Ensure that z values carry through into products of operations (#4).
-        
-        1.2.5 (2010-09-19)
-        ------------------
-        - Stop distributing docs/_build.
-        - Include library fallbacks in test_dlls.py for linux platform.
-        
-        1.2.4 (2010-09-09)
-        ------------------
-        - Raise AttributeError when there's no backend support for a method.
-        - Raise OSError if libgeos_c.so (or variants) can't be found and loaded.
-        - Add geos_c DLL loading support for linux platforms where find_library doesn't
-        work.
-        
-        1.2.3 (2010-08-17)
-        ------------------
-        - Add mapping function.
-        - Fix problem with GEOSisValidReason symbol for GEOS < 3.1.
-        
-        1.2.2 (2010-07-23)
-        ------------------
-        - Add representative_point method.
-        
-        1.2.1 (2010-06-23)
-        ------------------
-        - Fixed bounds of singular polygons.
-        - Added shapely.validation.explain_validity function (#226).
-        
-        1.2 (2010-05-27)
-        ----------------
-        - Final release.
-        
-        1.2rc2 (2010-05-26)
-        -------------------
-        - Add examples and tests to MANIFEST.in.
-        - Release candidate 2.
-        
-        1.2rc1 (2010-05-25)
-        -------------------
-        - Release candidate.
-        
-        1.2b7 (2010-04-22)
-        ------------------
-        - Memory leak associated with new empty geometry state fixed.
-        
-        1.2b6 (2010-04-13)
-        ------------------
-        - Broken GeometryCollection fixed.
-        
-        1.2b5 (2010-04-09)
-        ------------------
-        - Objects can be constructed from others of the same type, thereby making
-        copies. Collections can be constructed from sequences of objects, also making
-        copies.
-        - Collections are now iterators over their component objects.
-        - New code for manual figures, using the descartes package.
-        
-        1.2b4 (2010-03-19)
-        ------------------
-        - Adds support for the "sunos5" platform.
-        
-        1.2b3 (2010-02-28)
-        ------------------
-        - Only provide simplification implementations for GEOS C API >= 1.5.
-        
-        1.2b2 (2010-02-19)
-        ------------------
-        - Fix cascaded_union bug introduced in 1.2b1 (#212).
-        
-        1.2b1 (2010-02-18)
-        ------------------
-        - Update the README. Remove cruft from setup.py. Add some version 1.2 metadata
-        regarding required Python version (>=2.5,<3) and external dependency
-        (libgeos_c >= 3.1).
-        
-        1.2a6 (2010-02-09)
-        ------------------
-        - Add accessor for separate arrays of X and Y values (#210).
-        
-        TODO: fill gap here
-        
-        1.2a1 (2010-01-20)
-        ------------------
-        - Proper prototyping of WKB writer, and avoidance of errors on 64-bit systems
-        (#191).
-        - Prototype libgeos_c functions in a way that lets py2exe apps import shapely
-        (#189).
-        
-        1.2 Branched (2009-09-19)
-        
-        1.0.12 (2009-04-09)
-        -------------------
-        - Fix for references held by topology and predicate descriptors.
-        
-        1.0.11 (2008-11-20)
-        -------------------
-        - Work around bug in GEOS 2.2.3, GEOSCoordSeq_getOrdinate not exported properly
-        (#178).
-        
-        1.0.10 (2008-11-17)
-        -------------------
-        - Fixed compatibility with GEOS 2.2.3 that was broken in 1.0.8 release (#176).
-        
-        1.0.9 (2008-11-16)
-        ------------------
-        - Find and load MacPorts libgeos.
-        
-        1.0.8 (2008-11-01)
-        ------------------
-        - Fill out GEOS function result and argument types to prevent faults on a
-        64-bit arch.
-        
-        1.0.7 (2008-08-22)
-        ------------------
-        - Polygon rings now have the same dimensions as parent (#168).
-        - Eliminated reference cycles in polygons (#169).
-        
-        1.0.6 (2008-07-10)
-        ------------------
-        - Fixed adaptation of multi polygon data.
-        - Raise exceptions earlier from binary predicates.
-        - Beginning distributing new windows DLLs (#166).
-        
-        1.0.5 (2008-05-20)
-        ------------------
-        - Added access to GEOS polygonizer function.
-        - Raise exception when insufficient coordinate tuples are passed to LinearRing
-        constructor (#164).
-        
-        1.0.4 (2008-05-01)
-        ------------------
-        - Disentangle Python and topological equality (#163).
-        - Add shape(), a factory that copies coordinates from a geo interface provider.
-        To be used instead of asShape() unless you really need to store coordinates
-        outside shapely for efficient use in other code.
-        - Cache GEOS geometries in adapters (#163).
-        
-        1.0.3 (2008-04-09)
-        ------------------
-        - Do not release GIL when calling GEOS functions (#158).
-        - Prevent faults when chaining multiple GEOS operators (#159).
-        
-        1.0.2 (2008-02-26)
-        ------------------
-        - Fix loss of dimensionality in polygon rings (#155).
-        
-        1.0.1 (2008-02-08)
-        ------------------
-        - Allow chaining expressions involving coordinate sequences and geometry parts
-        (#151).
-        - Protect against abnormal use of coordinate accessors (#152).
-        - Coordinate sequences now implement the numpy array protocol (#153).
-        
-        1.0 (2008-01-18)
-        ----------------
-        - Final release.
-        
-        1.0 RC2 (2008-01-16)
-        --------------------
-        - Added temporary solution for #149.
-        
-        1.0 RC1 (2008-01-14)
-        --------------------
-        - First release candidate
-        
-        
-Keywords: geometry topology gis
-Platform: UNKNOWN
-Classifier: Development Status :: 5 - Production/Stable
-Classifier: Intended Audience :: Developers
-Classifier: Intended Audience :: Science/Research
-Classifier: License :: OSI Approved :: BSD License
-Classifier: Operating System :: OS Independent
-Classifier: Programming Language :: Python
-Classifier: Topic :: Scientific/Engineering :: GIS
diff --git a/Shapely.egg-info/SOURCES.txt b/Shapely.egg-info/SOURCES.txt
deleted file mode 100644
index 1f8cd3f..0000000
--- a/Shapely.egg-info/SOURCES.txt
+++ /dev/null
@@ -1,87 +0,0 @@
-CHANGES.txt
-CREDITS.txt
-LICENSE.txt
-README.rst
-setup.cfg
-setup.py
-Shapely.egg-info/PKG-INFO
-Shapely.egg-info/SOURCES.txt
-Shapely.egg-info/dependency_links.txt
-Shapely.egg-info/top_level.txt
-docs/manual.txt
-shapely/__init__.py
-shapely/coords.py
-shapely/ctypes_declarations.py
-shapely/ftools.py
-shapely/geos.py
-shapely/impl.py
-shapely/iterops.py
-shapely/linref.py
-shapely/ops.py
-shapely/predicates.py
-shapely/prepared.py
-shapely/topology.py
-shapely/validation.py
-shapely/wkb.py
-shapely/wkt.py
-shapely/algorithms/__init__.py
-shapely/algorithms/cga.py
-shapely/examples/__init__.py
-shapely/examples/dissolve.py
-shapely/examples/geoms.py
-shapely/examples/intersect.py
-shapely/examples/world.py
-shapely/geometry/__init__.py
-shapely/geometry/base.py
-shapely/geometry/collection.py
-shapely/geometry/geo.py
-shapely/geometry/linestring.py
-shapely/geometry/multilinestring.py
-shapely/geometry/multipoint.py
-shapely/geometry/multipolygon.py
-shapely/geometry/point.py
-shapely/geometry/polygon.py
-shapely/geometry/proxy.py
-shapely/speedups/__init__.py
-shapely/speedups/_speedups.c
-shapely/speedups/_speedups.pyx
-shapely/tests/Array.txt
-shapely/tests/GeoInterface.txt
-shapely/tests/IterOps.txt
-shapely/tests/LineString.txt
-shapely/tests/MultiLineString.txt
-shapely/tests/MultiPoint.txt
-shapely/tests/MultiPolygon.txt
-shapely/tests/Operations.txt
-shapely/tests/Persist.txt
-shapely/tests/Point.txt
-shapely/tests/Polygon.txt
-shapely/tests/Predicates.txt
-shapely/tests/__init__.py
-shapely/tests/attribute-chains.txt
-shapely/tests/binascii_hex.txt
-shapely/tests/cascaded_union.txt
-shapely/tests/dimensions.txt
-shapely/tests/invalid_intersection.txt
-shapely/tests/linemerge.txt
-shapely/tests/polygonize.txt
-shapely/tests/test_box.py
-shapely/tests/test_cga.py
-shapely/tests/test_collection.py
-shapely/tests/test_delegated.py
-shapely/tests/test_dlls.py
-shapely/tests/test_doctests.py
-shapely/tests/test_emptiness.py
-shapely/tests/test_equality.py
-shapely/tests/test_geomseq.py
-shapely/tests/test_getitem.py
-shapely/tests/test_linear_referencing.py
-shapely/tests/test_mapping.py
-shapely/tests/test_prepared.py
-shapely/tests/test_products_z.py
-shapely/tests/test_singularity.py
-shapely/tests/test_speedups.py
-shapely/tests/test_validation.py
-shapely/tests/test_xy.py
-shapely/tests/threading_test.py
-shapely/tests/wkt_locale.txt
\ No newline at end of file
diff --git a/Shapely.egg-info/dependency_links.txt b/Shapely.egg-info/dependency_links.txt
deleted file mode 100644
index 8b13789..0000000
--- a/Shapely.egg-info/dependency_links.txt
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/Shapely.egg-info/top_level.txt b/Shapely.egg-info/top_level.txt
deleted file mode 100644
index 5cc3857..0000000
--- a/Shapely.egg-info/top_level.txt
+++ /dev/null
@@ -1 +0,0 @@
-shapely
diff --git a/docs/Makefile b/docs/Makefile
new file mode 100644
index 0000000..4f165a8
--- /dev/null
+++ b/docs/Makefile
@@ -0,0 +1,95 @@
+# Makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line.
+SPHINXOPTS    =
+SPHINXBUILD   = sphinx-build
+PAPER         =
+BUILDDIR      = _build
+
+# Internal variables.
+PAPEROPT_a4     = -D latex_paper_size=a4
+PAPEROPT_letter = -D latex_paper_size=letter
+ALLSPHINXOPTS   = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+
+.PHONY: help clean html dirhtml pickle json htmlhelp qthelp latex changes linkcheck doctest
+
+help:
+	@echo "Please use \`make <target>' where <target> is one of"
+	@echo "  html      to make standalone HTML files"
+	@echo "  dirhtml   to make HTML files named index.html in directories"
+	@echo "  pickle    to make pickle files"
+	@echo "  json      to make JSON files"
+	@echo "  htmlhelp  to make HTML files and a HTML help project"
+	@echo "  qthelp    to make HTML files and a qthelp project"
+	@echo "  latex     to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
+	@echo "  changes   to make an overview of all changed/added/deprecated items"
+	@echo "  linkcheck to check all external links for integrity"
+	@echo "  doctest   to run all doctests embedded in the documentation (if enabled)"
+
+clean:
+	-rm -rf $(BUILDDIR)/* build/plot_directive/*
+
+html: apidocs
+	$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
+	@echo
+	@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
+
+dirhtml:
+	$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
+	@echo
+	@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
+
+pickle:
+	$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
+	@echo
+	@echo "Build finished; now you can process the pickle files."
+
+json:
+	$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
+	@echo
+	@echo "Build finished; now you can process the JSON files."
+
+htmlhelp: apidocs
+	$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
+	@echo
+	@echo "Build finished; now you can run HTML Help Workshop with the" \
+	      ".hhp project file in $(BUILDDIR)/htmlhelp."
+
+qthelp:
+	$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
+	@echo
+	@echo "Build finished; now you can run "qcollectiongenerator" with the" \
+	      ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
+	@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Shapely.qhcp"
+	@echo "To view the help file:"
+	@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Shapely.qhc"
+
+latex:
+	$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+	@echo
+	@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
+	@echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \
+	      "run these through (pdf)latex."
+
+changes:
+	$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
+	@echo
+	@echo "The overview file is in $(BUILDDIR)/changes."
+
+linkcheck:
+	$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
+	@echo
+	@echo "Link check complete; look for any errors in the above output " \
+	      "or in $(BUILDDIR)/linkcheck/output.txt."
+
+doctest:
+	$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
+	@echo "Testing of doctests in the sources finished, look at the " \
+	      "results in $(BUILDDIR)/doctest/output.txt."
+
+apidocs:
+	sphinx-apidoc -f -s txt -o . ../shapely
+	@echo
+	@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
+
diff --git a/docs/code/buffer.py b/docs/code/buffer.py
new file mode 100644
index 0000000..376be92
--- /dev/null
+++ b/docs/code/buffer.py
@@ -0,0 +1,63 @@
+from matplotlib import pyplot
+from shapely.geometry import LineString
+from descartes import PolygonPatch
+
+from figures import SIZE, BLUE, GRAY
+
+def plot_line(ax, ob):
+    x, y = ob.xy
+    ax.plot(x, y, color=GRAY, linewidth=3, solid_capstyle='round', zorder=1)
+
+line = LineString([(0, 0), (1, 1), (0, 2), (2, 2), (3, 1), (1, 0)])
+
+fig = pyplot.figure(1, figsize=SIZE, dpi=90)
+
+# 1
+ax = fig.add_subplot(121)
+
+plot_line(ax, line)
+
+dilated = line.buffer(0.5, cap_style=3)
+patch1 = PolygonPatch(dilated, fc=BLUE, ec=BLUE, alpha=0.5, zorder=2)
+ax.add_patch(patch1)
+
+ax.set_title('a) dilation, cap_style=3')
+
+xrange = [-1, 4]
+yrange = [-1, 3]
+ax.set_xlim(*xrange)
+ax.set_xticks(range(*xrange) + [xrange[-1]])
+ax.set_ylim(*yrange)
+ax.set_yticks(range(*yrange) + [yrange[-1]])
+ax.set_aspect(1)
+
+#2
+ax = fig.add_subplot(122)
+
+patch2a = PolygonPatch(dilated, fc=GRAY, ec=GRAY, alpha=0.5, zorder=1)
+ax.add_patch(patch2a)
+
+eroded = dilated.buffer(-0.3)
+
+# GeoJSON-like data works as well
+
+polygon = eroded.__geo_interface__
+# >>> geo['type']
+# 'Polygon'
+# >>> geo['coordinates'][0][:2]
+# ((0.50502525316941682, 0.78786796564403572), (0.5247963548222736, 0.8096820147509064))
+patch2b = PolygonPatch(polygon, fc=BLUE, ec=BLUE, alpha=0.5, zorder=2)
+ax.add_patch(patch2b)
+
+ax.set_title('b) erosion, join_style=1')
+
+xrange = [-1, 4]
+yrange = [-1, 3]
+ax.set_xlim(*xrange)
+ax.set_xticks(range(*xrange) + [xrange[-1]])
+ax.set_ylim(*yrange)
+ax.set_yticks(range(*yrange) + [yrange[-1]])
+ax.set_aspect(1)
+
+pyplot.show()
+
diff --git a/docs/code/cascaded_union.py b/docs/code/cascaded_union.py
new file mode 100644
index 0000000..4c4989a
--- /dev/null
+++ b/docs/code/cascaded_union.py
@@ -0,0 +1,47 @@
+from matplotlib import pyplot
+from shapely.geometry import Point
+from shapely.ops import cascaded_union
+from descartes import PolygonPatch
+
+from figures import SIZE, BLUE, GRAY
+
+polygons = [Point(i, 0).buffer(0.7) for i in range(5)]
+
+fig = pyplot.figure(1, figsize=SIZE, dpi=90)
+
+# 1
+ax = fig.add_subplot(121)
+
+for ob in polygons:
+    p = PolygonPatch(ob, fc=GRAY, ec=GRAY, alpha=0.5, zorder=1)
+    ax.add_patch(p)
+
+ax.set_title('a) polygons')
+
+xrange = [-2, 6]
+yrange = [-2, 2]
+ax.set_xlim(*xrange)
+ax.set_xticks(range(*xrange) + [xrange[-1]])
+ax.set_ylim(*yrange)
+ax.set_yticks(range(*yrange) + [yrange[-1]])
+ax.set_aspect(1)
+
+#2
+ax = fig.add_subplot(122)
+
+u = cascaded_union(polygons)
+patch2b = PolygonPatch(u, fc=BLUE, ec=BLUE, alpha=0.5, zorder=2)
+ax.add_patch(patch2b)
+
+ax.set_title('b) union')
+
+xrange = [-2, 6]
+yrange = [-2, 2]
+ax.set_xlim(*xrange)
+ax.set_xticks(range(*xrange) + [xrange[-1]])
+ax.set_ylim(*yrange)
+ax.set_yticks(range(*yrange) + [yrange[-1]])
+ax.set_aspect(1)
+
+pyplot.show()
+
diff --git a/docs/code/convex_hull.py b/docs/code/convex_hull.py
new file mode 100644
index 0000000..437264a
--- /dev/null
+++ b/docs/code/convex_hull.py
@@ -0,0 +1,54 @@
+from matplotlib import pyplot
+from shapely.geometry import MultiPoint
+
+from descartes.patch import PolygonPatch
+
+from figures import SIZE
+
+fig = pyplot.figure(1, figsize=SIZE, dpi=90)
+fig.set_frameon(True)
+
+# 1
+ax = fig.add_subplot(121)
+
+points2 = MultiPoint([(0, 0), (2, 2)])
+for p in points2:
+    ax.plot(p.x, p.y, 'o', color='#999999')
+hull2 = points2.convex_hull
+x, y = hull2.xy
+ax.plot(x, y, color='#6699cc', linewidth=3, alpha=0.5, zorder=2)
+
+ax.set_title('a) N = 2')
+
+xrange = [-1, 4]
+yrange = [-1, 3]
+ax.set_xlim(*xrange)
+ax.set_xticks(range(*xrange) + [xrange[-1]])
+ax.set_ylim(*yrange)
+ax.set_yticks(range(*yrange) + [yrange[-1]])
+ax.set_aspect(1)
+
+#2
+ax = fig.add_subplot(122)
+
+points1 = MultiPoint([(0, 0), (1, 1), (0, 2), (2, 2), (3, 1), (1, 0)])
+
+for p in points1:
+    ax.plot(p.x, p.y, 'o', color='#999999')
+hull1 = points1.convex_hull
+patch1 = PolygonPatch(hull1, facecolor='#6699cc', edgecolor='#6699cc', alpha=0.5, zorder=2)
+ax.add_patch(patch1)
+
+ax.set_title('b) N > 2')
+
+xrange = [-1, 4]
+yrange = [-1, 3]
+ax.set_xlim(*xrange)
+ax.set_xticks(range(*xrange) + [xrange[-1]])
+ax.set_ylim(*yrange)
+ax.set_yticks(range(*yrange) + [yrange[-1]])
+ax.set_aspect(1)
+
+pyplot.show()
+
+
diff --git a/docs/code/difference.py b/docs/code/difference.py
new file mode 100644
index 0000000..479878e
--- /dev/null
+++ b/docs/code/difference.py
@@ -0,0 +1,55 @@
+from matplotlib import pyplot
+from shapely.geometry import Point
+from descartes import PolygonPatch
+
+from figures import SIZE, BLUE, GRAY
+
+fig = pyplot.figure(1, figsize=SIZE, dpi=90)
+
+a = Point(1, 1).buffer(1.5)
+b = Point(2, 1).buffer(1.5)
+
+# 1
+ax = fig.add_subplot(121)
+
+patch1 = PolygonPatch(a, fc=GRAY, ec=GRAY, alpha=0.2, zorder=1)
+ax.add_patch(patch1)
+patch2 = PolygonPatch(b, fc=GRAY, ec=GRAY, alpha=0.2, zorder=1)
+ax.add_patch(patch2)
+c = a.difference(b)
+patchc = PolygonPatch(c, fc=BLUE, ec=BLUE, alpha=0.5, zorder=2)
+ax.add_patch(patchc)
+
+ax.set_title('a.difference(b)')
+
+xrange = [-1, 4]
+yrange = [-1, 3]
+ax.set_xlim(*xrange)
+ax.set_xticks(range(*xrange) + [xrange[-1]])
+ax.set_ylim(*yrange)
+ax.set_yticks(range(*yrange) + [yrange[-1]])
+ax.set_aspect(1)
+
+#2
+ax = fig.add_subplot(122)
+
+patch1 = PolygonPatch(a, fc=GRAY, ec=GRAY, alpha=0.2, zorder=1)
+ax.add_patch(patch1)
+patch2 = PolygonPatch(b, fc=GRAY, ec=GRAY, alpha=0.2, zorder=1)
+ax.add_patch(patch2)
+c = b.difference(a)
+patchc = PolygonPatch(c, fc=BLUE, ec=BLUE, alpha=0.5, zorder=2)
+ax.add_patch(patchc)
+
+ax.set_title('b.difference(a)')
+
+xrange = [-1, 4]
+yrange = [-1, 3]
+ax.set_xlim(*xrange)
+ax.set_xticks(range(*xrange) + [xrange[-1]])
+ax.set_ylim(*yrange)
+ax.set_yticks(range(*yrange) + [yrange[-1]])
+ax.set_aspect(1)
+
+pyplot.show()
+
diff --git a/docs/code/figures.py b/docs/code/figures.py
new file mode 100644
index 0000000..c0c7924
--- /dev/null
+++ b/docs/code/figures.py
@@ -0,0 +1,9 @@
+from math import sqrt
+
+GM = (sqrt(5)-1.0)/2.0
+W = 8.0
+H = W*GM
+SIZE = (W, H)
+
+BLUE = '#6699cc'
+GRAY = '#999999'
diff --git a/docs/code/geometrycollection.py b/docs/code/geometrycollection.py
new file mode 100644
index 0000000..b5b8b7c
--- /dev/null
+++ b/docs/code/geometrycollection.py
@@ -0,0 +1,67 @@
+from matplotlib import pyplot
+from shapely.geometry import LineString
+from figures import SIZE
+
+BLUE =   '#6699cc'
+YELLOW = '#ffcc33'
+GREEN =  '#339933'
+GRAY =   '#999999'
+
+def plot_coords(ax, ob):
+    x, y = ob.xy
+    ax.plot(x, y, 'o', color=GRAY, zorder=1)
+
+fig = pyplot.figure(1, figsize=SIZE, dpi=90) #1, figsize=(10, 4), dpi=180)
+
+a = LineString([(0, 0), (1, 1), (1,2), (2,2)])
+b = LineString([(0, 0), (1, 1), (2,1), (2,2)])
+
+# 1: disconnected multilinestring
+ax = fig.add_subplot(121)
+
+plot_coords(ax, a)
+plot_coords(ax, b)
+
+x, y = a.xy
+ax.plot(x, y, color=YELLOW, alpha=0.5, linewidth=3, solid_capstyle='round', zorder=2)
+
+x, y = b.xy
+ax.plot(x, y, color=GREEN, alpha=0.5, linewidth=3, solid_capstyle='round', zorder=2)
+
+ax.set_title('a) lines')
+
+xrange = [-1, 3]
+yrange = [-1, 3]
+ax.set_xlim(*xrange)
+ax.set_xticks(range(*xrange) + [xrange[-1]])
+ax.set_ylim(*yrange)
+ax.set_yticks(range(*yrange) + [yrange[-1]])
+ax.set_aspect(1)
+
+#2: invalid self-touching ring
+ax = fig.add_subplot(122)
+
+x, y = a.xy
+ax.plot(x, y, color=GRAY, alpha=0.7, linewidth=1, solid_capstyle='round', zorder=1)
+x, y = b.xy
+ax.plot(x, y, color=GRAY, alpha=0.7, linewidth=1, solid_capstyle='round', zorder=1)
+
+for ob in a.intersection(b):
+    x, y = ob.xy
+    if len(x) == 1:
+        ax.plot(x, y, 'o', color=BLUE, zorder=2)
+    else:
+        ax.plot(x, y, color=BLUE, alpha=0.7, linewidth=3, solid_capstyle='round', zorder=2)
+
+ax.set_title('b) collection')
+
+xrange = [-1, 3]
+yrange = [-1, 3]
+ax.set_xlim(*xrange)
+ax.set_xticks(range(*xrange) + [xrange[-1]])
+ax.set_ylim(*yrange)
+ax.set_yticks(range(*yrange) + [yrange[-1]])
+ax.set_aspect(1)
+
+pyplot.show()
+
diff --git a/docs/code/intersection-sym-difference.py b/docs/code/intersection-sym-difference.py
new file mode 100644
index 0000000..b673e34
--- /dev/null
+++ b/docs/code/intersection-sym-difference.py
@@ -0,0 +1,61 @@
+from matplotlib import pyplot
+from shapely.geometry import Point
+from descartes import PolygonPatch
+
+from figures import SIZE, BLUE, GRAY
+
+fig = pyplot.figure(1, figsize=SIZE, dpi=90)
+
+a = Point(1, 1).buffer(1.5)
+b = Point(2, 1).buffer(1.5)
+
+# 1
+ax = fig.add_subplot(121)
+
+patch1 = PolygonPatch(a, fc=GRAY, ec=GRAY, alpha=0.2, zorder=1)
+ax.add_patch(patch1)
+patch2 = PolygonPatch(b, fc=GRAY, ec=GRAY, alpha=0.2, zorder=1)
+ax.add_patch(patch2)
+c = a.intersection(b)
+patchc = PolygonPatch(c, fc=BLUE, ec=BLUE, alpha=0.5, zorder=2)
+ax.add_patch(patchc)
+
+ax.set_title('a.intersection(b)')
+
+xrange = [-1, 4]
+yrange = [-1, 3]
+ax.set_xlim(*xrange)
+ax.set_xticks(range(*xrange) + [xrange[-1]])
+ax.set_ylim(*yrange)
+ax.set_yticks(range(*yrange) + [yrange[-1]])
+ax.set_aspect(1)
+
+#2
+ax = fig.add_subplot(122)
+
+patch1 = PolygonPatch(a, fc=GRAY, ec=GRAY, alpha=0.2, zorder=1)
+ax.add_patch(patch1)
+patch2 = PolygonPatch(b, fc=GRAY, ec=GRAY, alpha=0.2, zorder=1)
+ax.add_patch(patch2)
+c = a.symmetric_difference(b)
+
+if c.geom_type == 'Polygon':
+    patchc = PolygonPatch(c, fc=BLUE, ec=BLUE, alpha=0.5, zorder=2)
+    ax.add_patch(patchc)
+elif c.geom_type == 'MultiPolygon':
+    for p in c:
+        patchp = PolygonPatch(p, fc=BLUE, ec=BLUE, alpha=0.5, zorder=2)
+        ax.add_patch(patchp)
+
+ax.set_title('a.symmetric_difference(b)')
+
+xrange = [-1, 4]
+yrange = [-1, 3]
+ax.set_xlim(*xrange)
+ax.set_xticks(range(*xrange) + [xrange[-1]])
+ax.set_ylim(*yrange)
+ax.set_yticks(range(*yrange) + [yrange[-1]])
+ax.set_aspect(1)
+
+pyplot.show()
+
diff --git a/docs/code/linearring.py b/docs/code/linearring.py
new file mode 100644
index 0000000..788fe0b
--- /dev/null
+++ b/docs/code/linearring.py
@@ -0,0 +1,59 @@
+from matplotlib import pyplot
+from shapely.geometry.polygon import LinearRing
+
+from figures import SIZE
+
+COLOR = {
+    True:  '#6699cc',
+    False: '#ff3333'
+    }
+
+def v_color(ob):
+    return COLOR[ob.is_valid]
+
+def plot_coords(ax, ob):
+    x, y = ob.xy
+    ax.plot(x, y, 'o', color='#999999', zorder=1)
+
+def plot_line(ax, ob):
+    x, y = ob.xy
+    ax.plot(x, y, color=v_color(ob), alpha=0.7, linewidth=3, solid_capstyle='round', zorder=2)
+
+fig = pyplot.figure(1, figsize=SIZE, dpi=90)
+
+# 1: valid ring
+ax = fig.add_subplot(121)
+ring = LinearRing([(0, 0), (0, 2), (1, 1), (2, 2), (2, 0), (1, 0.8), (0, 0)])
+
+plot_coords(ax, ring)
+plot_line(ax, ring)
+
+ax.set_title('a) valid')
+
+xrange = [-1, 3]
+yrange = [-1, 3]
+ax.set_xlim(*xrange)
+ax.set_xticks(range(*xrange) + [xrange[-1]])
+ax.set_ylim(*yrange)
+ax.set_yticks(range(*yrange) + [yrange[-1]])
+ax.set_aspect(1)
+
+#2: invalid self-touching ring
+ax = fig.add_subplot(122)
+ring2 = LinearRing([(0, 0), (0, 2), (1, 1), (2, 2), (2, 0), (1, 1), (0, 0)])
+
+plot_coords(ax, ring2)
+plot_line(ax, ring2)
+
+ax.set_title('b) invalid')
+
+xrange = [-1, 3]
+yrange = [-1, 3]
+ax.set_xlim(*xrange)
+ax.set_xticks(range(*xrange) + [xrange[-1]])
+ax.set_ylim(*yrange)
+ax.set_yticks(range(*yrange) + [yrange[-1]])
+ax.set_aspect(1)
+
+pyplot.show()
+
diff --git a/docs/code/linestring.py b/docs/code/linestring.py
new file mode 100644
index 0000000..ff69687
--- /dev/null
+++ b/docs/code/linestring.py
@@ -0,0 +1,63 @@
+from matplotlib import pyplot
+from shapely.geometry import LineString
+
+from figures import SIZE
+
+COLOR = {
+    True:  '#6699cc',
+    False: '#ffcc33'
+    }
+
+def v_color(ob):
+    return COLOR[ob.is_simple]
+
+def plot_coords(ax, ob):
+    x, y = ob.xy
+    ax.plot(x, y, 'o', color='#999999', zorder=1)
+
+def plot_bounds(ax, ob):
+    x, y = zip(*list((p.x, p.y) for p in ob.boundary))
+    ax.plot(x, y, 'o', color='#000000', zorder=1)
+
+def plot_line(ax, ob):
+    x, y = ob.xy
+    ax.plot(x, y, color=v_color(ob), alpha=0.7, linewidth=3, solid_capstyle='round', zorder=2)
+
+fig = pyplot.figure(1, figsize=SIZE, dpi=90)
+
+# 1: simple line
+ax = fig.add_subplot(121)
+line = LineString([(0, 0), (1, 1), (0, 2), (2, 2), (3, 1), (1, 0)])
+
+plot_coords(ax, line)
+plot_bounds(ax, line)
+plot_line(ax, line)
+
+ax.set_title('a) simple')
+
+xrange = [-1, 4]
+yrange = [-1, 3]
+ax.set_xlim(*xrange)
+ax.set_ylim(*yrange)
+ax.set_yticks(list(range(*yrange)) + [yrange[-1]])
+ax.set_aspect(1)
+
+#2: complex line
+ax = fig.add_subplot(122)
+line2 = LineString([(0, 0), (1, 1), (0, 2), (2, 2), (-1, 1), (1, 0)])
+
+plot_coords(ax, line2)
+plot_bounds(ax, line2)
+plot_line(ax, line2)
+
+ax.set_title('b) complex')
+
+xrange = [-2, 3]
+yrange = [-1, 3]
+ax.set_xlim(*xrange)
+ax.set_ylim(*yrange)
+ax.set_yticks(list(range(*yrange)) + [yrange[-1]])
+ax.set_aspect(1)
+
+pyplot.show()
+
diff --git a/docs/code/multilinestring.py b/docs/code/multilinestring.py
new file mode 100644
index 0000000..68fe8f4
--- /dev/null
+++ b/docs/code/multilinestring.py
@@ -0,0 +1,69 @@
+from matplotlib import pyplot
+from shapely.geometry import MultiLineString
+
+from figures import SIZE
+
+COLOR = {
+    True:  '#6699cc',
+    False: '#ffcc33'
+    }
+
+def v_color(ob):
+    return COLOR[ob.is_simple]
+
+def plot_coords(ax, ob):
+    for line in ob:
+        x, y = line.xy
+        ax.plot(x, y, 'o', color='#999999', zorder=1)
+
+def plot_bounds(ax, ob):
+    x, y = zip(*list((p.x, p.y) for p in ob.boundary))
+    ax.plot(x, y, 'o', color='#000000', zorder=1)
+
+def plot_lines(ax, ob):
+    for line in ob:
+        x, y = line.xy
+        ax.plot(x, y, color=v_color(ob), alpha=0.7, linewidth=3, solid_capstyle='round', zorder=2)
+
+fig = pyplot.figure(1, figsize=SIZE, dpi=90)
+
+# 1: disconnected multilinestring
+ax = fig.add_subplot(121)
+
+mline1 = MultiLineString([((0, 0), (1, 1)), ((0, 2),  (1, 1.5), (1.5, 1), (2, 0))])
+
+plot_coords(ax, mline1)
+plot_bounds(ax, mline1)
+plot_lines(ax, mline1)
+
+ax.set_title('a) simple')
+
+xrange = [-1, 3]
+yrange = [-1, 3]
+ax.set_xlim(*xrange)
+ax.set_xticks(range(*xrange) + [xrange[-1]])
+ax.set_ylim(*yrange)
+ax.set_yticks(range(*yrange) + [yrange[-1]])
+ax.set_aspect(1)
+
+#2: invalid self-touching ring
+ax = fig.add_subplot(122)
+
+mline2 = MultiLineString([((0, 0), (1, 1), (1.5, 1)), ((0, 2), (1, 1.5), (1.5, 1), (2, 0))])
+
+plot_coords(ax, mline2)
+plot_bounds(ax, mline2)
+plot_lines(ax, mline2)
+
+ax.set_title('b) complex')
+
+xrange = [-1, 3]
+yrange = [-1, 3]
+ax.set_xlim(*xrange)
+ax.set_xticks(range(*xrange) + [xrange[-1]])
+ax.set_ylim(*yrange)
+ax.set_yticks(range(*yrange) + [yrange[-1]])
+ax.set_aspect(1)
+
+pyplot.show()
+
diff --git a/docs/code/multipolygon.py b/docs/code/multipolygon.py
new file mode 100644
index 0000000..ebdd141
--- /dev/null
+++ b/docs/code/multipolygon.py
@@ -0,0 +1,68 @@
+from matplotlib import pyplot
+from shapely.geometry import MultiPolygon
+from descartes.patch import PolygonPatch
+
+from figures import SIZE
+
+COLOR = {
+    True:  '#6699cc',
+    False: '#ff3333'
+    }
+
+def v_color(ob):
+    return COLOR[ob.is_valid]
+
+def plot_coords(ax, ob):
+    x, y = ob.xy
+    ax.plot(x, y, 'o', color='#999999', zorder=1)
+    
+fig = pyplot.figure(1, figsize=SIZE, dpi=90)
+
+# 1: valid multi-polygon
+ax = fig.add_subplot(121)
+
+a = [(0, 0), (0, 1), (1, 1), (1, 0), (0, 0)]
+b = [(1, 1), (1, 2), (2, 2), (2, 1), (1, 1)]
+
+multi1 = MultiPolygon([[a, []], [b, []]])
+
+for polygon in multi1:
+    plot_coords(ax, polygon.exterior)
+    patch = PolygonPatch(polygon, facecolor=v_color(multi1), edgecolor=v_color(multi1), alpha=0.5, zorder=2)
+    ax.add_patch(patch)
+
+ax.set_title('a) valid')
+
+xrange = [-1, 3]
+yrange = [-1, 3]
+ax.set_xlim(*xrange)
+ax.set_xticks(range(*xrange) + [xrange[-1]])
+ax.set_ylim(*yrange)
+ax.set_yticks(range(*yrange) + [yrange[-1]])
+ax.set_aspect(1)
+
+#2: invalid self-touching ring
+ax = fig.add_subplot(122)
+
+c = [(0, 0), (0, 1.5), (1, 1.5), (1, 0), (0, 0)]
+d = [(1, 0.5), (1, 2), (2, 2), (2, 0.5), (1, 0.5)]
+
+multi2 = MultiPolygon([[c, []], [d, []]])
+
+for polygon in multi2:
+    plot_coords(ax, polygon.exterior)
+    patch = PolygonPatch(polygon, facecolor=v_color(multi2), edgecolor=v_color(multi2), alpha=0.5, zorder=2)
+    ax.add_patch(patch)
+
+ax.set_title('b) invalid')
+
+xrange = [-1, 3]
+yrange = [-1, 3]
+ax.set_xlim(*xrange)
+ax.set_xticks(range(*xrange) + [xrange[-1]])
+ax.set_ylim(*yrange)
+ax.set_yticks(range(*yrange) + [yrange[-1]])
+ax.set_aspect(1)
+
+pyplot.show()
+
diff --git a/docs/code/parallel_offset.py b/docs/code/parallel_offset.py
new file mode 100644
index 0000000..87caa78
--- /dev/null
+++ b/docs/code/parallel_offset.py
@@ -0,0 +1,80 @@
+from matplotlib import pyplot
+from shapely.geometry import LineString
+from descartes import PolygonPatch
+
+from figures import SIZE, BLUE, GRAY
+
+def plot_coords(ax, x, y, color='#999999', zorder=1):
+    ax.plot(x, y, 'o', color=color, zorder=zorder)
+
+def plot_line(ax, ob, color=GRAY):
+    parts = hasattr(ob, 'geoms') and ob or [ob]
+    for part in parts:
+        x, y = part.xy
+        ax.plot(x, y, color=color, linewidth=3, solid_capstyle='round', zorder=1)
+
+def set_limits(ax, x_range, y_range):
+    ax.set_xlim(*x_range)
+    ax.set_xticks(range(*x_range) + [x_range[-1]])
+    ax.set_ylim(*y_range)
+    ax.set_yticks(range(*y_range) + [y_range[-1]])
+    ax.set_aspect(1)
+
+line = LineString([(0, 0), (1, 1), (0, 2), (2, 2), (3, 1), (1, 0)])
+line_bounds = line.bounds
+ax_range = [int(line_bounds[0] - 1.0), int(line_bounds[2] + 1.0)]
+ay_range = [int(line_bounds[1] - 1.0), int(line_bounds[3] + 1.0)]
+
+fig = pyplot.figure(1, figsize=(SIZE[0], 2 * SIZE[1]), dpi=90)
+
+# 1
+ax = fig.add_subplot(221)
+
+plot_line(ax, line)
+x, y = list(line.coords)[0]
+plot_coords(ax, x, y)
+offset = line.parallel_offset(0.5, 'left', join_style=1)
+plot_line(ax, offset, color=BLUE)
+
+ax.set_title('a) left, round')
+set_limits(ax, ax_range, ay_range)
+
+#2
+ax = fig.add_subplot(222)
+
+plot_line(ax, line)
+x, y = list(line.coords)[0]
+plot_coords(ax, x, y)
+
+offset = line.parallel_offset(0.5, 'left', join_style=2)
+plot_line(ax, offset, color=BLUE)
+
+ax.set_title('b) left, mitred')
+set_limits(ax, ax_range, ay_range)
+
+#3
+ax = fig.add_subplot(223)
+
+plot_line(ax, line)
+x, y = list(line.coords)[0]
+plot_coords(ax, x, y)
+offset = line.parallel_offset(0.5, 'left', join_style=3)
+plot_line(ax, offset, color=BLUE)
+
+ax.set_title('c) left, beveled')
+set_limits(ax, ax_range, ay_range)
+
+#4
+ax = fig.add_subplot(224)
+
+plot_line(ax, line)
+x, y = list(line.coords)[0]
+plot_coords(ax, x, y)
+offset = line.parallel_offset(0.5, 'right', join_style=1)
+plot_line(ax, offset, color=BLUE)
+
+ax.set_title('d) right, round')
+set_limits(ax, ax_range, ay_range)
+
+pyplot.show()
+
diff --git a/docs/code/parallel_offset_mitre.py b/docs/code/parallel_offset_mitre.py
new file mode 100644
index 0000000..d0b5f25
--- /dev/null
+++ b/docs/code/parallel_offset_mitre.py
@@ -0,0 +1,80 @@
+from matplotlib import pyplot
+from shapely.geometry import LineString
+from descartes import PolygonPatch
+
+from figures import SIZE, BLUE, GRAY
+
+def plot_coords(ax, x, y, color='#999999', zorder=1):
+    ax.plot(x, y, 'o', color=color, zorder=zorder)
+
+def plot_line(ax, ob, color=GRAY):
+    parts = hasattr(ob, 'geoms') and ob or [ob]
+    for part in parts:
+        x, y = part.xy
+        ax.plot(x, y, color=color, linewidth=3, solid_capstyle='round', zorder=1)
+
+def set_limits(ax, x_range, y_range):
+    ax.set_xlim(*x_range)
+    ax.set_xticks(range(*x_range) + [x_range[-1]])
+    ax.set_ylim(*y_range)
+    ax.set_yticks(range(*y_range) + [y_range[-1]])
+    ax.set_aspect(1)
+
+line = LineString([(0, 0), (1, 1), (0, 2), (2, 2), (3, 1), (1, 0)])
+line_bounds = line.bounds
+ax_range = [int(line_bounds[0] - 1.0), int(line_bounds[2] + 1.0)]
+ay_range = [int(line_bounds[1] - 1.0), int(line_bounds[3] + 1.0)]
+
+fig = pyplot.figure(1, figsize=(SIZE[0], 2 * SIZE[1]), dpi=90)
+
+# 1
+ax = fig.add_subplot(221)
+
+plot_line(ax, line)
+x, y = list(line.coords)[0]
+plot_coords(ax, x, y)
+offset = line.parallel_offset(0.5, 'left', join_style=2, mitre_limit=0.1)
+plot_line(ax, offset, color=BLUE)
+
+ax.set_title('a) left, limit=0.1')
+set_limits(ax, ax_range, ay_range)
+
+#2
+ax = fig.add_subplot(222)
+
+plot_line(ax, line)
+x, y = list(line.coords)[0]
+plot_coords(ax, x, y)
+
+offset = line.parallel_offset(0.5, 'left', join_style=2, mitre_limit=10.0)
+plot_line(ax, offset, color=BLUE)
+
+ax.set_title('b) left, limit=10.0')
+set_limits(ax, ax_range, ay_range)
+
+#3
+ax = fig.add_subplot(223)
+
+plot_line(ax, line)
+x, y = list(line.coords)[0]
+plot_coords(ax, x, y)
+offset = line.parallel_offset(0.5, 'right', join_style=2, mitre_limit=0.1)
+plot_line(ax, offset, color=BLUE)
+
+ax.set_title('c) right, limit=0.1')
+set_limits(ax, ax_range, ay_range)
+
+#4
+ax = fig.add_subplot(224)
+
+plot_line(ax, line)
+x, y = list(line.coords)[0]
+plot_coords(ax, x, y)
+offset = line.parallel_offset(0.5, 'right', join_style=2, mitre_limit=10.0)
+plot_line(ax, offset, color=BLUE)
+
+ax.set_title('d) right, limit=10.0')
+set_limits(ax, ax_range, ay_range)
+
+pyplot.show()
+
diff --git a/docs/code/polygon.py b/docs/code/polygon.py
new file mode 100644
index 0000000..8ca26bf
--- /dev/null
+++ b/docs/code/polygon.py
@@ -0,0 +1,67 @@
+from matplotlib import pyplot
+from shapely.geometry import Polygon
+from descartes.patch import PolygonPatch
+
+from figures import SIZE
+
+COLOR = {
+    True:  '#6699cc',
+    False: '#ff3333'
+    }
+
+def v_color(ob):
+    return COLOR[ob.is_valid]
+
+def plot_coords(ax, ob):
+    x, y = ob.xy
+    ax.plot(x, y, 'o', color='#999999', zorder=1)
+    
+fig = pyplot.figure(1, figsize=SIZE, dpi=90)
+
+# 1: valid polygon
+ax = fig.add_subplot(121)
+
+ext = [(0, 0), (0, 2), (2, 2), (2, 0), (0, 0)]
+int = [(1, 0), (0.5, 0.5), (1, 1), (1.5, 0.5), (1, 0)][::-1]
+polygon = Polygon(ext, [int])
+
+plot_coords(ax, polygon.interiors[0])
+plot_coords(ax, polygon.exterior)
+
+patch = PolygonPatch(polygon, facecolor=v_color(polygon), edgecolor=v_color(polygon), alpha=0.5, zorder=2)
+ax.add_patch(patch)
+
+ax.set_title('a) valid')
+
+xrange = [-1, 3]
+yrange = [-1, 3]
+ax.set_xlim(*xrange)
+ax.set_xticks(range(*xrange) + [xrange[-1]])
+ax.set_ylim(*yrange)
+ax.set_yticks(range(*yrange) + [yrange[-1]])
+ax.set_aspect(1)
+
+#2: invalid self-touching ring
+ax = fig.add_subplot(122)
+ext = [(0, 0), (0, 2), (2, 2), (2, 0), (0, 0)]
+int = [(1, 0), (0, 1), (0.5, 1.5), (1.5, 0.5), (1, 0)][::-1]
+polygon = Polygon(ext, [int])
+
+plot_coords(ax, polygon.interiors[0])
+plot_coords(ax, polygon.exterior)
+
+patch = PolygonPatch(polygon, facecolor=v_color(polygon), edgecolor=v_color(polygon), alpha=0.5, zorder=2)
+ax.add_patch(patch)
+
+ax.set_title('b) invalid')
+
+xrange = [-1, 3]
+yrange = [-1, 3]
+ax.set_xlim(*xrange)
+ax.set_xticks(range(*xrange) + [xrange[-1]])
+ax.set_ylim(*yrange)
+ax.set_yticks(range(*yrange) + [yrange[-1]])
+ax.set_aspect(1)
+
+pyplot.show()
+
diff --git a/docs/code/polygon2.py b/docs/code/polygon2.py
new file mode 100644
index 0000000..e170727
--- /dev/null
+++ b/docs/code/polygon2.py
@@ -0,0 +1,71 @@
+from matplotlib import pyplot
+from matplotlib.patches import Circle
+from shapely.geometry import Polygon
+from descartes.patch import PolygonPatch
+
+from figures import SIZE
+
+COLOR = {
+    True:  '#6699cc',
+    False: '#ff3333'
+    }
+
+def v_color(ob):
+    return COLOR[ob.is_valid]
+
+def plot_coords(ax, ob):
+    x, y = ob.xy
+    ax.plot(x, y, 'o', color='#999999', zorder=1)
+    
+fig = pyplot.figure(1, figsize=SIZE, dpi=90)
+
+# 3: invalid polygon, ring touch along a line
+ax = fig.add_subplot(121)
+
+ext = [(0, 0), (0, 2), (2, 2), (2, 0), (0, 0)]
+int = [(0.5, 0), (1.5, 0), (1.5, 1), (0.5, 1), (0.5, 0)]
+polygon = Polygon(ext, [int])
+
+plot_coords(ax, polygon.interiors[0])
+plot_coords(ax, polygon.exterior)
+
+patch = PolygonPatch(polygon, facecolor=v_color(polygon), edgecolor=v_color(polygon), alpha=0.5, zorder=2)
+ax.add_patch(patch)
+
+ax.set_title('c) invalid')
+
+xrange = [-1, 3]
+yrange = [-1, 3]
+ax.set_xlim(*xrange)
+ax.set_xticks(range(*xrange) + [xrange[-1]])
+ax.set_ylim(*yrange)
+ax.set_yticks(range(*yrange) + [yrange[-1]])
+ax.set_aspect(1)
+
+#4: invalid self-touching ring
+ax = fig.add_subplot(122)
+ext = [(0, 0), (0, 2), (2, 2), (2, 0), (0, 0)]
+int_1 = [(0.5, 0.25), (1.5, 0.25), (1.5, 1.25), (0.5, 1.25), (0.5, 0.25)]
+int_2 = [(0.5, 1.25), (1, 1.25), (1, 1.75), (0.5, 1.75)]
+# int_2 = [
+polygon = Polygon(ext, [int_1, int_2])
+
+plot_coords(ax, polygon.interiors[0])
+plot_coords(ax, polygon.interiors[1])
+plot_coords(ax, polygon.exterior)
+
+patch = PolygonPatch(polygon, facecolor=v_color(polygon), edgecolor=v_color(polygon), alpha=0.5, zorder=2)
+ax.add_patch(patch)
+
+ax.set_title('d) invalid')
+
+xrange = [-1, 3]
+yrange = [-1, 3]
+ax.set_xlim(*xrange)
+ax.set_xticks(range(*xrange) + [xrange[-1]])
+ax.set_ylim(*yrange)
+ax.set_yticks(range(*yrange) + [yrange[-1]])
+ax.set_aspect(1)
+
+pyplot.show()
+
diff --git a/docs/code/rotate.py b/docs/code/rotate.py
new file mode 100644
index 0000000..8ac565a
--- /dev/null
+++ b/docs/code/rotate.py
@@ -0,0 +1,57 @@
+from matplotlib import pyplot
+from shapely.geometry import LineString
+from shapely import affinity
+
+from figures import SIZE, BLUE, GRAY
+
+
+def add_origin(ax, geom, origin):
+    x, y = xy = affinity.interpret_origin(geom, origin, 2)
+    ax.plot(x, y, 'o', color=GRAY, zorder=1)
+    ax.annotate(str(xy), xy=xy, ha='center',
+                textcoords='offset points', xytext=(0, 8))
+
+
+def plot_line(ax, ob, color):
+    x, y = ob.xy
+    ax.plot(x, y, color=color, alpha=0.7, linewidth=3,
+            solid_capstyle='round', zorder=2)
+
+fig = pyplot.figure(1, figsize=SIZE, dpi=90)
+
+line = LineString([(1, 3), (1, 1), (4, 1)])
+
+xrange = [0, 5]
+yrange = [0, 4]
+
+# 1
+ax = fig.add_subplot(121)
+
+plot_line(ax, line, GRAY)
+plot_line(ax, affinity.rotate(line, 90, 'center'), BLUE)
+add_origin(ax, line, 'center')
+
+ax.set_title(u"90\N{DEGREE SIGN}, default origin (center)")
+
+ax.set_xlim(*xrange)
+ax.set_xticks(range(*xrange) + [xrange[-1]])
+ax.set_ylim(*yrange)
+ax.set_yticks(range(*yrange) + [yrange[-1]])
+ax.set_aspect(1)
+
+# 2
+ax = fig.add_subplot(122)
+
+plot_line(ax, line, GRAY)
+plot_line(ax, affinity.rotate(line, 90, 'centroid'), BLUE)
+add_origin(ax, line, 'centroid')
+
+ax.set_title(u"90\N{DEGREE SIGN}, origin='centroid'")
+
+ax.set_xlim(*xrange)
+ax.set_xticks(range(*xrange) + [xrange[-1]])
+ax.set_ylim(*yrange)
+ax.set_yticks(range(*yrange) + [yrange[-1]])
+ax.set_aspect(1)
+
+pyplot.show()
diff --git a/docs/code/scale.py b/docs/code/scale.py
new file mode 100644
index 0000000..df51fc8
--- /dev/null
+++ b/docs/code/scale.py
@@ -0,0 +1,64 @@
+from matplotlib import pyplot
+from shapely.geometry import Polygon
+from shapely import affinity
+from descartes.patch import PolygonPatch
+
+from figures import SIZE, BLUE, GRAY
+
+
+def add_origin(ax, geom, origin):
+    x, y = xy = affinity.interpret_origin(geom, origin, 2)
+    ax.plot(x, y, 'o', color=GRAY, zorder=1)
+    ax.annotate(str(xy), xy=xy, ha='center',
+                textcoords='offset points', xytext=(0, 8))
+
+fig = pyplot.figure(1, figsize=SIZE, dpi=90)
+
+triangle = Polygon([(1, 1), (2, 3), (3, 1)])
+
+xrange = [0, 5]
+yrange = [0, 4]
+
+# 1
+ax = fig.add_subplot(121)
+
+patch = PolygonPatch(triangle, facecolor=GRAY, edgecolor=GRAY,
+                     alpha=0.5, zorder=1)
+triangle_a = affinity.scale(triangle, xfact=1.5, yfact=-1)
+patch_a = PolygonPatch(triangle_a, facecolor=BLUE, edgecolor=BLUE,
+                       alpha=0.5, zorder=2)
+ax.add_patch(patch)
+ax.add_patch(patch_a)
+
+add_origin(ax, triangle, 'center')
+
+ax.set_title("a) xfact=1.5, yfact=-1")
+
+ax.set_xlim(*xrange)
+ax.set_xticks(range(*xrange) + [xrange[-1]])
+ax.set_ylim(*yrange)
+ax.set_yticks(range(*yrange) + [yrange[-1]])
+ax.set_aspect(1)
+
+# 2
+ax = fig.add_subplot(122)
+
+patch = PolygonPatch(triangle, facecolor=GRAY, edgecolor=GRAY,
+                     alpha=0.5, zorder=1)
+triangle_b = affinity.scale(triangle, xfact=2, origin=(1, 1))
+patch_b = PolygonPatch(triangle_b, facecolor=BLUE, edgecolor=BLUE,
+                       alpha=0.5, zorder=2)
+ax.add_patch(patch)
+ax.add_patch(patch_b)
+
+add_origin(ax, triangle, (1, 1))
+
+ax.set_title("b) xfact=2, origin=(1, 1)")
+
+ax.set_xlim(*xrange)
+ax.set_xticks(range(*xrange) + [xrange[-1]])
+ax.set_ylim(*yrange)
+ax.set_yticks(range(*yrange) + [yrange[-1]])
+ax.set_aspect(1)
+
+pyplot.show()
diff --git a/docs/code/simplify.py b/docs/code/simplify.py
new file mode 100644
index 0000000..b4ea5c2
--- /dev/null
+++ b/docs/code/simplify.py
@@ -0,0 +1,55 @@
+from matplotlib import pyplot
+from shapely.geometry import MultiPoint, Point
+from descartes.patch import PolygonPatch
+
+from figures import SIZE, BLUE, GRAY
+
+fig = pyplot.figure(1, figsize=SIZE, dpi=90) #1, figsize=SIZE, dpi=90)
+
+p = Point(1, 1).buffer(1.5)
+
+# 1
+ax = fig.add_subplot(121)
+
+q = p.simplify(0.2)
+
+patch1a = PolygonPatch(p, facecolor=GRAY, edgecolor=GRAY, alpha=0.5, zorder=1)
+ax.add_patch(patch1a)
+
+patch1b = PolygonPatch(q, facecolor=BLUE, edgecolor=BLUE, alpha=0.5, zorder=2)
+ax.add_patch(patch1b)
+
+ax.set_title('a) tolerance 0.2')
+
+xrange = [-1, 3]
+yrange = [-1, 3]
+ax.set_xlim(*xrange)
+ax.set_xticks(range(*xrange) + [xrange[-1]])
+ax.set_ylim(*yrange)
+ax.set_yticks(range(*yrange) + [yrange[-1]])
+ax.set_aspect(1)
+
+#2
+ax = fig.add_subplot(122)
+
+r = p.simplify(0.5)
+
+patch2a = PolygonPatch(p, facecolor=GRAY, edgecolor=GRAY, alpha=0.5, zorder=1)
+ax.add_patch(patch2a)
+
+patch2b = PolygonPatch(r, facecolor=BLUE, edgecolor=BLUE, alpha=0.5, zorder=2)
+ax.add_patch(patch2b)
+
+ax.set_title('b) tolerance 0.5')
+
+xrange = [-1, 3]
+yrange = [-1, 3]
+ax.set_xlim(*xrange)
+ax.set_xticks(range(*xrange) + [xrange[-1]])
+ax.set_ylim(*yrange)
+ax.set_yticks(range(*yrange) + [yrange[-1]])
+ax.set_aspect(1)
+
+pyplot.show()
+
+
diff --git a/docs/code/skew.py b/docs/code/skew.py
new file mode 100644
index 0000000..d5aa0a1
--- /dev/null
+++ b/docs/code/skew.py
@@ -0,0 +1,77 @@
+from matplotlib import pyplot
+from shapely.wkt import loads as load_wkt
+from shapely import affinity
+from descartes.patch import PolygonPatch
+
+from figures import SIZE, BLUE, GRAY
+
+
+def add_origin(ax, geom, origin):
+    x, y = xy = affinity.interpret_origin(geom, origin, 2)
+    ax.plot(x, y, 'o', color=GRAY, zorder=1)
+    ax.annotate(str(xy), xy=xy, ha='center',
+                textcoords='offset points', xytext=(0, 8))
+
+fig = pyplot.figure(1, figsize=SIZE, dpi=90)
+
+# Geometry from JTS TestBuilder with fixed precision model of 100.0
+# Using CreateShape > FontGlyphSanSerif and A = triangle.wkt from scale.py
+R = load_wkt('''\
+POLYGON((2.218 2.204, 2.273 2.18, 2.328 2.144, 2.435 2.042, 2.541 1.895,
+  2.647 1.702, 3 1, 2.626 1, 2.298 1.659, 2.235 1.777, 2.173 1.873,
+  2.112 1.948, 2.051 2.001, 1.986 2.038, 1.91 2.064, 1.823 2.08, 1.726 2.085,
+  1.347 2.085, 1.347 1, 1 1, 1 3.567, 1.784 3.567, 1.99 3.556, 2.168 3.521,
+  2.319 3.464, 2.441 3.383, 2.492 3.334, 2.536 3.279, 2.604 3.152,
+  2.644 3.002, 2.658 2.828, 2.651 2.712, 2.63 2.606, 2.594 2.51, 2.545 2.425,
+  2.482 2.352, 2.407 2.29, 2.319 2.241, 2.218 2.204),
+ (1.347 3.282, 1.347 2.371, 1.784 2.371, 1.902 2.378, 2.004 2.4, 2.091 2.436,
+  2.163 2.487, 2.219 2.552, 2.259 2.63, 2.283 2.722, 2.291 2.828, 2.283 2.933,
+  2.259 3.025, 2.219 3.103, 2.163 3.167, 2.091 3.217, 2.004 3.253, 1.902 3.275,
+  1.784 3.282, 1.347 3.282))''')
+
+xrange = [0, 5]
+yrange = [0, 4]
+
+# 1
+ax = fig.add_subplot(121)
+
+patch1a = PolygonPatch(R, facecolor=GRAY, edgecolor=GRAY,
+                       alpha=0.5, zorder=1)
+skewR = affinity.skew(R, xs=20, origin=(1, 1))
+patch1b = PolygonPatch(skewR, facecolor=BLUE, edgecolor=BLUE,
+                       alpha=0.5, zorder=2)
+ax.add_patch(patch1a)
+ax.add_patch(patch1b)
+
+add_origin(ax, R, (1, 1))
+
+ax.set_title("a) xs=20, origin(1, 1)")
+
+ax.set_xlim(*xrange)
+ax.set_xticks(range(*xrange) + [xrange[-1]])
+ax.set_ylim(*yrange)
+ax.set_yticks(range(*yrange) + [yrange[-1]])
+ax.set_aspect(1)
+
+# 2
+ax = fig.add_subplot(122)
+
+patch2a = PolygonPatch(R, facecolor=GRAY, edgecolor=GRAY,
+                       alpha=0.5, zorder=1)
+skewR = affinity.skew(R, ys=30)
+patch2b = PolygonPatch(skewR, facecolor=BLUE, edgecolor=BLUE,
+                       alpha=0.5, zorder=2)
+ax.add_patch(patch2a)
+ax.add_patch(patch2b)
+
+add_origin(ax, R, 'center')
+
+ax.set_title("b) ys=30")
+
+ax.set_xlim(*xrange)
+ax.set_xticks(range(*xrange) + [xrange[-1]])
+ax.set_ylim(*yrange)
+ax.set_yticks(range(*yrange) + [yrange[-1]])
+ax.set_aspect(1)
+
+pyplot.show()
diff --git a/docs/code/union.py b/docs/code/union.py
new file mode 100644
index 0000000..e3d5cba
--- /dev/null
+++ b/docs/code/union.py
@@ -0,0 +1,61 @@
+from matplotlib import pyplot
+from shapely.geometry import Point
+from descartes import PolygonPatch
+
+from figures import SIZE, BLUE, GRAY
+
+fig = pyplot.figure(1, figsize=SIZE, dpi=90)
+
+a = Point(1, 1).buffer(1.5)
+b = Point(2, 1).buffer(1.5)
+
+# 1
+ax = fig.add_subplot(121)
+
+patch1 = PolygonPatch(a, fc=GRAY, ec=GRAY, alpha=0.2, zorder=1)
+ax.add_patch(patch1)
+patch2 = PolygonPatch(b, fc=GRAY, ec=GRAY, alpha=0.2, zorder=1)
+ax.add_patch(patch2)
+c = a.union(b)
+patchc = PolygonPatch(c, fc=BLUE, ec=BLUE, alpha=0.5, zorder=2)
+ax.add_patch(patchc)
+
+ax.set_title('a.union(b)')
+
+xrange = [-1, 4]
+yrange = [-1, 3]
+ax.set_xlim(*xrange)
+ax.set_xticks(range(*xrange) + [xrange[-1]])
+ax.set_ylim(*yrange)
+ax.set_yticks(range(*yrange) + [yrange[-1]])
+ax.set_aspect(1)
+
+def plot_line(ax, ob, color=GRAY):
+    x, y = ob.xy
+    ax.plot(x, y, color, linewidth=3, solid_capstyle='round', zorder=1)
+
+#2
+ax = fig.add_subplot(122)
+
+plot_line(ax, a.exterior)
+plot_line(ax, b.exterior)
+
+u = a.exterior.union(b.exterior)
+if u.geom_type in ['LineString', 'LinearRing', 'Point']:
+    plot_line(ax, u, color=BLUE)
+elif u.geom_type is 'MultiLineString':
+    for p in u:
+        plot_line(ax, p, color=BLUE)
+
+ax.set_title('a.boundary.union(b.boundary)')
+
+xrange = [-1, 4]
+yrange = [-1, 3]
+ax.set_xlim(*xrange)
+ax.set_xticks(range(*xrange) + [xrange[-1]])
+ax.set_ylim(*yrange)
+ax.set_yticks(range(*yrange) + [yrange[-1]])
+ax.set_aspect(1)
+
+pyplot.show()
+
diff --git a/docs/conf.py b/docs/conf.py
new file mode 100644
index 0000000..e66ac12
--- /dev/null
+++ b/docs/conf.py
@@ -0,0 +1,212 @@
+# -*- coding: utf-8 -*-
+#
+# Shapely documentation build configuration file, created by
+# sphinx-quickstart on Mon Apr 12 11:07:08 2010.
+#
+# This file is execfile()d with the current directory set to its containing dir.
+#
+# Note that not all possible configuration values are present in this
+# autogenerated file.
+#
+# All configuration values have a default; values that are commented out
+# serve to show the default.
+
+import sys, os
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+sys.path.append(os.path.abspath('sphinxext'))
+
+# Load latest source tree
+sys.path.insert(0, os.path.abspath('..'))
+
+import shapely
+
+# For pyplots in code/, load functions here first, so they are visible
+from shapely import geometry, affinity, wkt, wkb
+from shapely.ops import cascaded_union
+
+# -- General configuration -----------------------------------------------------
+
+# Add any Sphinx extension module names here, as strings. They can be extensions
+# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
+extensions = [
+    'matplotlib.sphinxext.only_directives',
+    'matplotlib.sphinxext.plot_directive',
+    'sphinx.ext.autodoc',
+    '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'
+
+# The encoding of source files.
+#source_encoding = 'utf-8'
+
+# The master toctree document.
+master_doc = 'index'
+
+# General information about the project.
+project = 'Shapely'
+copyright = '2011-2013, Sean Gillies, Aron Bierbaum, Kai Lautaportti ' \
+            'and others'
+
+# The version info for the project you're documenting, acts as replacement for
+# |version| and |release|, also used in various other places throughout the
+# built documents.
+#
+# The short X.Y version.
+version = shapely.__version__
+# The full version, including alpha/beta/rc tags.
+release = shapely.__version__
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+#language = None
+
+# There are two options for replacing |today|: either, you set today to some
+# non-false value, then it is used:
+#today = ''
+# Else, today_fmt is used as the format for a strftime call.
+#today_fmt = '%B %d, %Y'
+
+# List of documents that shouldn't be included in the build.
+#unused_docs = []
+
+# List of directories, relative to source directory, that shouldn't be searched
+# for source files.
+exclude_trees = ['_build']
+
+# The reST default role (used for this markup: `text`) to use for all documents.
+#default_role = None
+
+# If true, '()' will be appended to :func: etc. cross-reference text.
+#add_function_parentheses = True
+
+# If true, the current module name will be prepended to all description
+# unit titles (such as .. function::).
+#add_module_names = True
+
+# If true, sectionauthor and moduleauthor directives will be shown in the
+# output. They are ignored by default.
+#show_authors = False
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'sphinx'
+
+# A list of ignored prefixes for module index sorting.
+#modindex_common_prefix = []
+
+
+# -- Options for HTML output ---------------------------------------------------
+
+# The theme to use for HTML and HTML Help pages.  Major themes that come with
+# Sphinx are currently 'default' and 'sphinxdoc'.
+# html_theme = 'haiku'
+html_theme = 'sphinxdoc'
+# html_theme = 'shapely'
+
+# Theme options are theme-specific and customize the look and feel of a theme
+# further.  For a list of options available for each theme, see the
+# documentation.
+#html_theme_options = {}
+
+# Add any paths that contain custom themes here, relative to this directory.
+html_theme_path = ['themes']
+
+# The name for this set of Sphinx documents.  If None, it defaults to
+# "<project> v<release> documentation".
+html_title = "Shapely 1.2 and 1.3 documentation"
+
+# A shorter title for the navigation bar.  Default is the same as html_title.
+#html_short_title = None
+
+# The name of an image file (relative to this directory) to place at the top
+# of the sidebar.
+#html_logo = None
+
+# The name of an image file (within the static path) to use as favicon of the
+# docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
+# pixels large.
+#html_favicon = None
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+#html_static_path = ['_static']
+
+# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
+# using the given strftime format.
+#html_last_updated_fmt = '%b %d, %Y'
+
+# If true, SmartyPants will be used to convert quotes and dashes to
+# typographically correct entities.
+#html_use_smartypants = True
+
+# Custom sidebar templates, maps document names to template names.
+#html_sidebars = {}
+
+# Additional templates that should be rendered to pages, maps page names to
+# template names.
+#html_additional_pages = {}
+
+# If false, no module index is generated.
+#html_use_modindex = True
+
+# If false, no index is generated.
+#html_use_index = True
+
+# If true, the index is split into individual pages for each letter.
+#html_split_index = False
+
+# If true, links to the reST sources are added to the pages.
+#html_show_sourcelink = True
+
+# If true, an OpenSearch description file will be output, and all pages will
+# contain a <link> tag referring to it.  The value of this option must be the
+# base URL from which the finished HTML is served.
+#html_use_opensearch = ''
+
+# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
+#html_file_suffix = ''
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'Shapelydoc'
+
+
+# -- Options for LaTeX output --------------------------------------------------
+
+# The paper size ('letter' or 'a4').
+#latex_paper_size = 'letter'
+
+# The font size ('10pt', '11pt' or '12pt').
+#latex_font_size = '10pt'
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title, author, documentclass [howto/manual]).
+latex_documents = [
+  ('index', 'Shapely.tex', 'Shapely Documentation',
+   'Sean Gillies', 'manual'),
+]
+
+# The name of an image file (relative to this directory) to place at the top of
+# the title page.
+#latex_logo = None
+
+# For "manual" documents, if this is true, then toplevel headings are parts,
+# not chapters.
+#latex_use_parts = False
+
+# Additional stuff for the LaTeX preamble.
+#latex_preamble = ''
+
+# Documents to append as an appendix to all manuals.
+#latex_appendices = []
+
+# If false, no module index is generated.
+#latex_use_modindex = True
diff --git a/docs/design.txt b/docs/design.txt
new file mode 100644
index 0000000..0c768f0
--- /dev/null
+++ b/docs/design.txt
@@ -0,0 +1,30 @@
+============
+Design Notes
+============
+
+Shapely provides classes that implement, more or less, the interfaces in the
+OGC's simple feature acess specification [1]_. The classes are defined in
+similarly named modules under ``shapely.geometry``: ``Point`` is in
+``shapely.geometry.point``, ``MultiPolygon`` is in
+``shapely.geometry.multipolygon``. These classes derive from
+``shapely.geometry.base.BaseGeometry``. The simple features methods of
+``BaseGeometry`` call functions registered in a class variable ``impl``. For
+example, ``BaseGeometry.area`` calls ``BaseGeometry.impl['area']``.
+
+The default registry is in the ``shapely.impl`` module. Its items are classes
+that operate on single geometric objects or pairs of geometric objects.
+Pluggability is a goal of this design, but we're not there yet. Some work needs
+to be done before anybody can use CGAL as a Shapely backend.
+
+In sum, Shapely's stack is 4 layers:
+
+* Python geometry classes in ``shapely.geometry``
+* An implementation registry: an abstraction that permits alternate geometry
+  engines, even a mix of geometry engines. The default is in ``shapely.impl``.
+* The GEOS implementations of methods for the registry in ``shapely.geos``.
+* libgeos: algorithms written in C++.
+
+.. [1] John R. Herring, Ed.,
+   “OpenGIS Implementation Specification for Geographic information - Simple
+   feature access - Part 1: Common architecture,” Oct. 2006.
+
diff --git a/docs/images/4511827859_b5822043b7_o.png b/docs/images/4511827859_b5822043b7_o.png
new file mode 100644
index 0000000..a53eb0d
Binary files /dev/null and b/docs/images/4511827859_b5822043b7_o.png differ
diff --git a/docs/images/buffer.png b/docs/images/buffer.png
new file mode 100644
index 0000000..3fe7b5c
Binary files /dev/null and b/docs/images/buffer.png differ
diff --git a/docs/images/convex-hull.png b/docs/images/convex-hull.png
new file mode 100644
index 0000000..ef3265a
Binary files /dev/null and b/docs/images/convex-hull.png differ
diff --git a/docs/images/simplify.png b/docs/images/simplify.png
new file mode 100644
index 0000000..15f0050
Binary files /dev/null and b/docs/images/simplify.png differ
diff --git a/docs/index.txt b/docs/index.txt
new file mode 100644
index 0000000..e509d6a
--- /dev/null
+++ b/docs/index.txt
@@ -0,0 +1,19 @@
+=======
+Shapely
+=======
+
+Documentation Contents
+======================
+
+.. toctree::
+   :maxdepth: 2
+
+   The Project <project>
+   User Manual <manual>
+   API Documentation <modules>
+
+Indices and tables
+==================
+
+* :ref:`genindex`
+* :ref:`search`
diff --git a/docs/manual.txt b/docs/manual.txt
index 5ad9db7..8ca9f41 100644
--- a/docs/manual.txt
+++ b/docs/manual.txt
@@ -5,15 +5,15 @@ The Shapely User Manual
 =======================
 
 :Author: Sean Gillies, <sean.gillies at gmail.com>
-:Revision: 1.2.14
-:Date: 21 January 2012
-:Copyright: 
+:Version: 1.2 and 1.3
+:Date: |today|
+:Copyright:
   This work is licensed under a `Creative Commons Attribution 3.0
   United States License`__.
 
 .. __: http://creativecommons.org/licenses/by/3.0/us/
 
-:Abstract: 
+:Abstract:
   This document explains how to use the Shapely Python package for
   computational geometry.
 
@@ -54,9 +54,9 @@ serialization, and map projection of features are significant, but orthogonal
 problems. You may not need a hundred GIS format readers and writers or the
 multitude of State Plane projections, and Shapely doesn't burden you with them.
 The third premise is that Python idioms trump GIS (or Java, in this case, since
-the GEOS library is derived from JTS, a Java project) idioms. 
+the GEOS library is derived from JTS, a Java project) idioms.
 
-If you enjoy and profit from idiommatic Python, appreciate packages that do one
+If you enjoy and profit from idiomatic Python, appreciate packages that do one
 thing well, and agree that a spatially enabled RDBMS is often enough the wrong
 tool for your computational geometry job, Shapely might be for you.
 
@@ -78,7 +78,7 @@ feature are mutually exclusive and their union coincides with the entire plane
 * A `Curve` has an `interior` set consisting of the infinitely many points
   along its length (imagine a `Point` dragged in space), a `boundary` set
   consisting of its two end points, and an `exterior` set of all other points.
-  A `Curve` has a topological dimension of 1. 
+  A `Curve` has a topological dimension of 1.
 
 * A `Surface` has an `interior` set consisting of the infinitely many points
   within (imagine a `Curve` dragged in space to cover an area), a `boundary`
@@ -117,7 +117,7 @@ Relationships
 
 The spatial data model is accompanied by a group of natural language
 relationships between geometric objects – `contains`, `intersects`, `overlaps`,
-`touches`, etc – and a theoretical framework for understanding them using the
+`touches`, etc. – and a theoretical framework for understanding them using the
 3x3 matrix of the mutual intersections of their component point sets [2]_: the
 DE-9IM. A comprehensive review of the relationships in terms of the DE-9IM is
 found in [4]_ and will not be reiterated in this manual.
@@ -129,7 +129,7 @@ Operations
 
 Following the JTS technical specs [5]_, this manual will make a distinction
 between constructive (`buffer`, `convex hull`) and set-theoretic operations
-(`intersection`, `union`, etc). The individual operations will be fully
+(`intersection`, `union`, etc.). The individual operations will be fully
 described in a following section of the manual.
 
 .. _intro-coordinate-systems:
@@ -155,15 +155,14 @@ Geometric Objects
 Geometric objects are created in the typical Python fashion, using the classes
 themselves as instance factories. A few of their intrinsic properties will be
 discussed in this sections, others in the following sections on operations and
-serializations. 
+serializations.
 
 Instances of `Point`, `LineString`, and `LinearRing` have as their most
 important attribute a finite sequence of coordinates that determines their
 interior, boundary, and exterior point sets. A line string can be determined by
 as few as 2 points, but contains an infinite number of points. Coordinate
-sequences are immutable. Their parent features are mutable in that they can be
-assigned new coordinate sequences. A third `z` coordinate value may be used
-when constructing instances, but has no effect on geometric analysis.  All
+sequences are immutable. A third `z` coordinate value may be used when
+constructing instances, but has no effect on geometric analysis.  All
 operations are performed in the `x-y` plane.
 
 In all constructors, numeric values are converted to type ``float``. In other
@@ -210,7 +209,7 @@ General Attributes and Methods
 
 .. method:: object.representative_point()
 
-  Returns a cheaply computed point that is guaranteed to be within the 
+  Returns a cheaply computed point that is guaranteed to be within the
   geometric object.
 
 .. note::
@@ -289,7 +288,7 @@ LineStrings
 
 .. class:: LineString(coordinates)
 
-  The `LineString` constructor takes an ordered sequence of 2 or more 
+  The `LineString` constructor takes an ordered sequence of 2 or more
   ``(x, y[, z])`` point tuples.
 
 The constructed `LineString` object represents one or more connected linear
@@ -299,7 +298,7 @@ allowed, but may incur performance penalties and should be avoided. A
 
 .. plot:: code/linestring.py
 
-Figure 1: a simple `LineString` on the left, a complex `LineString` on the
+Figure 1. A simple `LineString` on the left, a complex `LineString` on the
 right. The (`MultiPoint`) boundary of each is shown in black, the other points
 that describe the lines are shown in grey.
 
@@ -359,7 +358,7 @@ LinearRings
 
   The `LinearRing` constructor takes an ordered sequence of ``(x, y[, z])``
   point tuples.
-  
+
 The sequence may be explicitly closed by passing identical values in the first
 and last indices. Otherwise, the sequence will be implicitly closed by copying
 the first tuple to the last index. As with a `LineString`, repeated points in
@@ -369,11 +368,11 @@ itself at a single point.
 
 .. plot:: code/linearring.py
 
-Figure 1: a valid `LinearRing` on the left, an invalid self-touching
+Figure 2. A valid `LinearRing` on the left, an invalid self-touching
 `LinearRing` on the right. The points that describe the rings are shown in
 grey. A ring's boundary is `empty`.
 
-.. note:: 
+.. note::
    Shapely will not prevent the creation of such rings, but exceptions will be
    raised when they are operated on.
 
@@ -411,7 +410,7 @@ instance, thereby making a copy.
 
   >>> LinearRring(ring)
   <shapely.geometry.polygon.LinearRing object at 0x...>
-  
+
 As with `LineString`, a sequence of `Point` instances is not a valid
 constructor parameter.
 
@@ -434,14 +433,14 @@ features, but exceptions will be raised when they are operated on.
 
 .. plot:: code/polygon.py
 
-Figure 1: On the left, a valid `Polygon` with one interior ring that touches
+Figure 3. On the left, a valid `Polygon` with one interior ring that touches
 the exterior ring at one point, and on the right a `Polygon` that is `invalid`
 because its interior ring touches the exterior ring at more than one point. The
 points that describe the rings are shown in grey.
 
 .. plot:: code/polygon2.py
 
-Figure 1: On the left, a `Polygon` that is `invalid` because its exterior and
+Figure 4. On the left, a `Polygon` that is `invalid` because its exterior and
 interior rings touch along a line, and on the right, a `Polygon` that is
 `invalid` because its interior rings touch along a line.
 
@@ -481,7 +480,7 @@ The `Polygon` constructor also accepts instances of `LineString` and
   >>> r = LinearRing(coords)
   >>> s = Polygon(r)
   >>> s.area
-  0.5  
+  0.5
   >>> t = Polygon(s.buffer(1.0).exterior, [r])
   >>> t.area
   6.5507620529190334
@@ -509,14 +508,14 @@ For example:
 
 This is the first appearance of an explicit polygon handedness in Shapely.
 
-To obtain a polygon with a known orientation, use 
+To obtain a polygon with a known orientation, use
 :func:`shapely.geometry.polygon.orient()`:
 
 .. function:: shapely.geometry.polygon.orient(polygon, sign=1.0)
 
   Returns a properly oriented copy of the given polygon. The signed area of the
-  result will have the given sign. A sign of 1.0 means that the coordinates of the
-  product's exterior ring will be oriented counter-clockwise.
+  result will have the given sign. A sign of 1.0 means that the coordinates of
+  the product's exterior ring will be oriented counter-clockwise.
 
   `New in version 1.2.10`.
 
@@ -546,7 +545,7 @@ immutable collections of geometric objects.  The collections may be homogeneous
 .. plot:: code/geometrycollection.py
    :class: figure
 
-Figure 1: a) a green and a yellow line that intersect along a line and at a
+Figure 5. a) a green and a yellow line that intersect along a line and at a
 single point; b) the intersection (in blue) is a collection containing one
 `LineString` and one `Point`.
 
@@ -565,7 +564,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 
+.. sourcecode:: pycon
 
   >>> from shapely.geometry import MultiPoint
   >>> m = MultiPoint([(0, 0), (1, 1), (1,2), (2,2)])
@@ -579,7 +578,7 @@ same type.
 `New in version 1.2.14`.
 
 .. note::
-  
+
   When possible, it is better to use one of the homogeneous collection types
   described below.
 
@@ -590,7 +589,7 @@ Collections of Points
 
 .. class:: MultiPoint(points)
 
-  The `MultiPoint` constructor takes a sequence of ``(x, y[,z ])`` point
+  The `MultiPoint` constructor takes a sequence of ``(x, y[, z ])`` point
   tuples.
 
 A `MultiPoint` has zero area and zero length.
@@ -619,7 +618,7 @@ via the iterator protocol using ``in`` or :func:`list`.
   >>> import pprint
   >>> pprint.pprint(list(points.geoms))
   [<shapely.geometry.point.Point object at 0x...>,
-   <shapely.geometry.point.Point object at 0x...>]  
+   <shapely.geometry.point.Point object at 0x...>]
   >>> pprint.pprint(list(points))
   [<shapely.geometry.point.Point object at 0x...>,
    <shapely.geometry.point.Point object at 0x...>]
@@ -644,9 +643,9 @@ Collections of Lines
 
 .. plot:: code/multilinestring.py
 
-Figure 1: On the left, a `simple`, disconnected `MultiLineString`, and on the
+Figure 6. On the left, a `simple`, disconnected `MultiLineString`, and on the
 right, a non-simple `MultiLineString`. The points defining the objects are
-shownb in gray, the boundaries of the objects in black.
+shown in gray, the boundaries of the objects in black.
 
 A `MultiLineString` has zero area and non-zero length.
 
@@ -680,7 +679,7 @@ property or via the iterator protocol using ``in`` or ``list()``.
   >>> pprint.pprint(list(lines))
   [<shapely.geometry.linestring.LineString object at 0x...>,
    <shapely.geometry.linestring.LineString object at 0x...>]
-  
+
 The constructor also accepts another instance of `MultiLineString` or an
 unordered sequence of `LineString` instances, thereby making copies.
 
@@ -712,7 +711,7 @@ instances, thereby making copies.
 
 .. plot:: code/multipolygon.py
 
-Figure 1: On the right, a `valid` `MultiPolygon` with 2 members, and on the
+Figure 7. On the right, 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).
 
@@ -771,17 +770,15 @@ longer empty.
 Linear Referencing Methods
 --------------------------
 
-It can useful to specify position along linear features such as `LineStrings`
+It can be useful to specify position along linear features such as `LineStrings`
 and `MultiLineStrings` with a 1-dimensional referencing system. Shapely
 supports linear referencing based on length or distance, evaluating the
 distance along a geometric object to the projection of a given point, or the
 point at a given distance along the object.
 
-.. note:: 
+.. note::
 
-  Linear referencing methods require the support of GEOS version >= 3.2 or more
-  specifically, a GEOS C API version > (1, 6, 0). This version tuple is
-  surfaced in Shapely as :data:`shapely.geos.geos_capi_version`.
+  Linear referencing methods require GEOS 3.2.0 or later.
 
 .. method:: object.interpolate(distance[, normalized=False])
 
@@ -803,7 +800,7 @@ fraction of the geometric object's length.
 .. method:: object.project(other[, normalized=False])
 
   Returns the distance along this geometric object to a point nearest the
-  `other` object`.
+  `other` object.
 
 If the `normalized` arg is ``True``, return the distance normalized to the
 length of the object. The :meth:`project` method is the inverse of
@@ -835,7 +832,7 @@ specified distance.
           if pd > distance:
               cp = line.interpolate(distance)
               return [
-                  LineString(coords[:i] + [(cp.x, cp.y)]), 
+                  LineString(coords[:i] + [(cp.x, cp.y)]),
                   LineString([(cp.x, cp.y)] + coords[i:])]
 
 .. sourcecode:: pycon
@@ -868,7 +865,7 @@ example will be shown for each.
 .. attribute:: object.has_z
 
   Returns ``True`` if the feature has not only `x` and `y`, but also `z`
-  coordinates.
+  coordinates for 3D (or so-called, 2.5D) geometries.
 
 .. sourcecode:: pycon
 
@@ -879,8 +876,9 @@ example will be shown for each.
 
 .. attribute:: object.is_ccw
 
-  Returns ``True`` if coordinates are in counter-clockwise order (bounding a region
-  with positive signed area). This method applies to `LinearRing` objects only.
+  Returns ``True`` if coordinates are in counter-clockwise order (bounding a
+  region with positive signed area). This method applies to `LinearRing`
+  objects only.
 
   `New in version 1.2.10`.
 
@@ -929,7 +927,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
 
   >>> LineString([(0, 0), (1, 1), (1, -1)]).is_ring
@@ -943,18 +941,22 @@ meaningless for others.
 .. attribute:: object.is_simple
 
   Returns ``True`` if the feature does not cross itself.
-  
+
+.. note::
+
+   The simplicity test is meaningful only for `LineStrings` and `LinearRings`.
+
 .. sourcecode:: pycon
 
   >>> LineString([(0, 0), (1, 1), (1, -1), (0, 1)]).is_simple
   False
 
-Operations on non-simple `LineStrings` are fully supported by Shapely. 
+Operations on non-simple `LineStrings` are fully supported by Shapely.
 
 .. attribute:: object.is_valid
 
   Returns ``True`` if a feature is "valid" in the sense of [1]_.
-  
+
 A valid `LinearRing` may not cross itself or touch itself at a single point. A
 valid `Polygon` may not possess any overlapping exterior or interior rings. A
 valid `MultiPolygon` may not collect any overlapping polygons. Operations on
@@ -993,7 +995,7 @@ buffer operations (explained in a following section) overlap.
   ... def ring(coordinates):
   ...     return LinearRing(coordinates)
   ...
-  >>> coords = [(0, 0), (1, 1), (1, -1), (0, 1)]  
+  >>> coords = [(0, 0), (1, 1), (1, -1), (0, 1)]
   >>> ring(coords)
   Traceback (most recent call last):
     File "<stdin>", line 1, in <module>
@@ -1040,7 +1042,7 @@ A line's endpoints are part of its `boundary` and are therefore not contained.
   >>> LineString(coords).contains(Point(1.0, 1.0))
   False
 
-.. note:: 
+.. note::
 
   Binary predicates can be used directly as predicates for ``filter()`` or
   ``itertools.ifilter()``.
@@ -1110,16 +1112,15 @@ constructions.
 .. method:: object.intersects(other)
 
   Returns ``True`` if the `boundary` and `interior` of the object intersect in
-  any way with those of the other. 
+  any way with those of the other.
 
 This predicate is equivalent to the OR-ing of :meth:`contains`, :meth:`crosses`,
-:meth:`equals`, :meth:`touches`, and :meth:`within`. 
+:meth:`equals`, :meth:`touches`, and :meth:`within`.
 
 .. method:: object.touches(other)
 
-  Returns ``True`` if the `boundary` of the object intersects only the
-  `boundary` of the other, and their interiors do not intersect with any part
-  of the other. 
+  Returns ``True`` if the objects have at least one point in common and their
+  interiors do not intersect with any part of the other.
 
 Overlapping features do not therefore `touch`, another potential "gotcha". For
 example, the following lines touch at ``(1, 1)``, but do not overlap.
@@ -1165,7 +1166,7 @@ Shapely's binary :meth:`within` predicate.
 .. sourcecode:: python
 
   from shapely.geometry import asShape
-  
+
   class Within(object):
       def __init__(self, o):
           self.o = o
@@ -1226,7 +1227,7 @@ elements.
   'F0FFFF102'
 
 Further discussion of the DE-9IM matrix is beyond the scope of this manual. See
-[4]_ and http://pypi.python.org/pypi/de9im/0.1.
+[4]_ and http://pypi.python.org/pypi/de9im.
 
 .. _analysis-methods:
 
@@ -1236,7 +1237,7 @@ Spatial Analysis Methods
 As well as boolean attributes and methods, Shapely provides analysis methods
 that return new geometric objects.
 
-.. _set-theortic-methods:
+.. _set-theoretic-methods:
 
 Set-theoretic Methods
 ---------------------
@@ -1249,7 +1250,7 @@ available as a read-only attribute.
 
   Returns a lower dimensional object representing the object's set-theoretic
   `boundary`.
-  
+
 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.
 
@@ -1283,7 +1284,7 @@ illustration of lines and their boundaries.
   >>> LineString([(0, 0), (1, 1)]).centroid.wkt
   'POINT (0.5000000000000000 0.5000000000000000)'
 
-.. note:: 
+.. note::
 
   The centroid of an object might be one of its points, but this is not
   guaranteed.
@@ -1308,7 +1309,7 @@ illustration of lines and their boundaries.
 
 .. plot:: code/difference.py
 
-Figure 1: differences between two approximately circular polygons.
+Figure 8. Differences between two approximately circular polygons.
 
 .. note::
 
@@ -1346,7 +1347,7 @@ See the figure under :meth:`symmetric_difference` below.
 .. plot:: code/intersection-sym-difference.py
 
 .. method:: object.union(other)
-  
+
   Returns a representation of the union of points from this object and the
   `other` geometric object.
 
@@ -1386,14 +1387,50 @@ Constructive Methods
 Shapely geometric object have several methods that yield new objects not
 derived from set-theoretic analysis.
 
-.. method:: object.buffer(distance, resolution=16)
+.. method:: object.buffer(distance, resolution=16, cap_style=1, join_style=1, mitre_limit=1.0)
 
   Returns an approximate representation of all points within a given `distance`
   of the this geometric object.
 
+  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).
+
+.. data:: shapely.geometry.CAP_STYLE
+
+   ========= =====
+   Attribute Value
+   ========= =====
+   round        1
+   flat         2
+   square       3
+   ========= =====
+
+.. data:: shapely.geometry.JOIN_STYLE
+
+   ========= =====
+   Attribute Value
+   ========= =====
+   round         1
+   mitre         2
+   bevel         3
+   ========= =====
+
+.. sourcecode:: pycon
+
+  >>> from shapely.geometry import CAP_STYLE, JOIN_STYLE
+  >>> CAP_STYLE.flat
+  2
+  >>> JOIN_STYLE.bevel
+  3
+
 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. 
+approximate a quarter circle around a point.
 
 .. sourcecode:: pycon
 
@@ -1403,7 +1440,7 @@ approximate a quarter circle around a point.
 
 .. plot:: code/buffer.py
 
-Figure 1. Dilation of a line (left) and erosion of a polygon (right). New
+Figure 9. Dilation of a line (left) and erosion of a polygon (right). New
 object is shown in blue.
 
 The default (`resolution` of 16) buffer of a point is a polygonal patch with
@@ -1468,7 +1505,7 @@ Buffering splits the polygon in two at the point where they touch.
 
 .. plot:: code/convex_hull.py
 
-Figure 1. Convex hull (blue) of 2 points (left) and of 6 points (right).
+Figure 10. Convex hull (blue) of 2 points (left) and of 6 points (right).
 
 .. attribute:: object.envelope
 
@@ -1482,14 +1519,48 @@ Figure 1. Convex hull (blue) of 2 points (left) and of 6 points (right).
   >>> 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)
+
+  Returns a LineString or MultiLineString geometry at a distance from the
+  object on its right or its left side.
+
+  Distance must be a positive float value. The side parameter may be 'left' or
+  'right'. The resolution of the offset around each vertex of the object is
+  parameterized as in the buffer method.
+
+  The join style is for outside corners between line segments. Accepted integer
+  values are 1 (round), 2 (mitre), and 3 (bevel). See also
+  :data:`shapely.geometry.JOIN_STYLE`.
+
+  Severely mitered corners can be controlled by the mitre_limit parameter
+  (spelled in British English, en-gb). The ratio of the distance from the
+  corner to the end of the mitred offset corner is the miter ratio. Corners
+  with a ratio which exceed the limit will be beveled.
+
+.. note::
+
+  This method is only available for `LinearRing` and `LineString`  objects.
+
+.. plot:: code/parallel_offset.py
+
+Figure 11. Three styles of parallel offset lines on the left side of a simple
+line string (its starting point shown as a circle) and one offset on the right
+side, a multipart.
+
+The effect of the `mitre_limit` parameter is shown below.
+
+.. plot:: code/parallel_offset_mitre.py
+
+Figure 12. Large and small mitre_limit values for left and right offsets.
+
 .. method:: object.simplify(tolerance, preserve_topology=True)
 
   Returns a simplified representation of the geometric object.
 
 All points in the simplified object will be within the `tolerance` distance of
 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.
+topology. If preserve topology is set to ``False`` the much quicker
+Douglas-Peucker algorithm [6]_ is used.
 
 .. sourcecode:: pycon
 
@@ -1507,7 +1578,7 @@ algorithm [6]_ is used.
 
 .. plot:: code/simplify.py
 
-Figure 1. Simplification of a nearly circular polygon using a tolerance of 0.2
+Figure 13. Simplification of a nearly circular polygon using a tolerance of 0.2
 (left) and 0.5 (right).
 
 .. note::
@@ -1515,58 +1586,340 @@ Figure 1. Simplification of a nearly circular polygon using a tolerance of 0.2
   `Invalid` geometric objects may result from simplification that does not
   preserve topology.
 
+
+Affine Transformations
+======================
+
+A collection of affine transform functions are in the :mod:`shapely.affinity`
+module, which return transformed geometries by either directly supplying
+coefficients to an affine transformation matrix, or by using a specific, named
+transform (`rotate`, `scale`, etc.). The functions can be used with all
+geometry types (except `GeometryCollection`), and 3D types are either
+preserved or supported by 3D affine transformations.
+
+`New in version 1.2.17`.
+
+.. function:: shapely.affinity.affine_transform(geom, matrix)
+
+  Returns a transformed geometry using an affine transformation matrix.
+
+  The coefficient ``matrix`` is provided as a list or tuple with 6 or 12 items
+  for 2D or 3D transformations, respectively.
+
+  For 2D affine transformations, the 6 parameter ``matrix`` is:
+
+    ``[a, b, d, e, xoff, yoff]``
+
+  which represents the augmented matrix:
+
+  .. math::
+    \begin{bmatrix} x' & y' & 1 \end{bmatrix} =
+    \begin{bmatrix} x  & y  & 1 \end{bmatrix}
+    \begin{bmatrix}
+      a & b & x_\mathrm{off} \\
+      d & e & y_\mathrm{off} \\
+      0 & 0 & 1
+    \end{bmatrix}
+
+  or the equations for the transformed coordinates:
+
+  .. math::
+    x' &= a x + b y + x_\mathrm{off} \\
+    y' &= d x + e y + y_\mathrm{off}.
+
+  For 3D affine transformations, the 12 parameter ``matrix`` is:
+
+    ``[a, b, c, d, e, f, g, h, i, xoff, yoff, zoff]``
+
+  which represents the augmented matrix:
+
+  .. math::
+    \begin{bmatrix} x' & y' & z' & 1 \end{bmatrix} =
+    \begin{bmatrix} x  & y  & z  & 1 \end{bmatrix}
+    \begin{bmatrix}
+      a & b & c & x_\mathrm{off} \\
+      d & e & f & y_\mathrm{off} \\
+      g & h & i & z_\mathrm{off} \\
+      0 & 0 & 0 & 1
+    \end{bmatrix}
+
+  or the equations for the transformed coordinates:
+
+  .. math::
+    x' &= a x + b y + c z + x_\mathrm{off} \\
+    y' &= d x + e y + f z + y_\mathrm{off} \\
+    z' &= g x + h y + i z + z_\mathrm{off}.
+
+.. function:: shapely.affinity.rotate(geom, angle, origin='center', use_radians=False)
+
+  Returns a rotated geometry on a 2D plane.
+
+  The angle of rotation can be specified in either degrees (default) or
+  radians by setting ``use_radians=True``. Positive angles are
+  counter-clockwise and negative are clockwise rotations.
+
+  The point of origin can be a keyword ``'center'`` for the bounding box
+  center (default), ``'centroid'`` for the geometry's centroid, a `Point` object
+  or a coordinate tuple ``(x0, y0)``.
+
+  The affine transformation matrix for 2D rotation with angle :math:`\theta` is:
+
+  .. math::
+    \begin{bmatrix}
+      \cos{\theta} & -\sin{\theta} & x_\mathrm{off} \\
+      \sin{\theta} &  \cos{\theta} & y_\mathrm{off} \\
+            0      &        0      & 1
+    \end{bmatrix}
+
+  where the offsets are calculated from the origin :math:`(x_0, y_0)`:
+
+  .. math::
+    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
+
+    >>> from shapely import affinity
+    >>> line = LineString([(1, 3), (1, 1), (4, 1)])
+    >>> rotated_a = affinity.rotate(line, 90)
+    >>> rotated_b = affinity.rotate(line, 90, origin='centroid')
+
+  .. plot:: code/rotate.py
+
+  Figure 14. Rotation of a `LineString` (gray) by an angle of 90°
+  counter-clockwise (blue) using different origins.
+
+.. function:: shapely.affinity.scale(geom, xfact=1.0, yfact=1.0, zfact=1.0, origin='center')
+
+  Returns a scaled geometry, scaled by factors along each dimension.
+
+  The point of origin can be a keyword ``'center'`` for the 2D bounding box
+  center (default), ``'centroid'`` for the geometry's 2D centroid, a `Point`
+  object or a coordinate tuple ``(x0, y0, z0)``.
+
+  Negative scale factors will mirror or reflect coordinates.
+
+  The general 3D affine transformation matrix for scaling is:
+
+  .. math::
+    \begin{bmatrix}
+      x_\mathrm{fact} & 0               & 0               & x_\mathrm{off} \\
+      0               & y_\mathrm{fact} & 0               & y_\mathrm{off} \\
+      0               & 0               & z_\mathrm{fact} & z_\mathrm{off} \\
+      0               & 0               & 0               & 1
+    \end{bmatrix}
+
+  where the offsets are calculated from the origin :math:`(x_0, y_0, z_0)`:
+
+  .. math::
+    x_\mathrm{off} &= x_0 - x_0 x_\mathrm{fact} \\
+    y_\mathrm{off} &= y_0 - y_0 y_\mathrm{fact} \\
+    z_\mathrm{off} &= z_0 - z_0 z_\mathrm{fact}
+
+  .. sourcecode:: pycon
+
+    >>> triangle = Polygon([(1, 1), (2, 3), (3, 1)])
+    >>> triangle_a = affinity.scale(triangle, xfact=1.5, yfact=-1)
+    >>> triangle_a.exterior.coords[:]
+    [(0.5, 3.0), (2.0, 1.0), (3.5, 3.0), (0.5, 3.0)]
+    >>> triangle_b = affinity.scale(triangle, xfact=2, origin=(1,1))
+    >>> triangle_b.exterior.coords[:]
+    [(1.0, 1.0), (3.0, 3.0), (5.0, 1.0), (1.0, 1.0)]
+
+  .. plot:: code/scale.py
+
+  Figure 15. Scaling of a gray triangle to blue result: a) by a factor of 1.5
+  along x-direction, with reflection across y-axis; b) by a factor of 2 along
+  x-direction with custom origin at (1, 1).
+
+.. function:: shapely.affinity.skew(geom, xs=0.0, ys=0.0, origin='center', use_radians=False)
+
+  Returns a skewed geometry, sheared by angles along x and y dimensions.
+
+  The shear angle can be specified in either degrees (default) or radians
+  by setting ``use_radians=True``.
+
+  The point of origin can be a keyword ``'center'`` for the bounding box
+  center (default), ``'centroid'`` for the geometry's centroid, a `Point`
+  object or a coordinate tuple ``(x0, y0)``.
+
+  The general 2D affine transformation matrix for skewing is:
+
+  .. math::
+    \begin{bmatrix}
+      1 & \tan{x_s} & x_\mathrm{off} \\
+      \tan{y_s} & 1 & y_\mathrm{off} \\
+      0 & 0 & 1
+    \end{bmatrix}
+
+  where the offsets are calculated from the origin :math:`(x_0, y_0)`:
+
+  .. math::
+    x_\mathrm{off} &= -y_0 \tan{x_s} \\
+    y_\mathrm{off} &= -x_0 \tan{y_s}
+
+  .. plot:: code/skew.py
+
+  Figure 16. Skewing of a gray "R" to blue result: a) by a shear angle of 20°
+  along the x-direction and an origin at (1, 1); b) by a shear angle of 30°
+  along the y-direction, using default origin.
+
+.. function:: shapely.affinity.translate(geom, xoff=0.0, yoff=0.0, zoff=0.0)
+
+  Returns a translated geometry shifted by offsets along each dimension.
+
+  The general 3D affine transformation matrix for translation is:
+
+  .. math::
+    \begin{bmatrix}
+      1 & 0 & 0 & x_\mathrm{off} \\
+      0 & 1 & 0 & y_\mathrm{off} \\
+      0 & 0 & 1 & z_\mathrm{off} \\
+      0 & 0 & 0 & 1
+    \end{bmatrix}
+
+
+Other Transformations
+=====================
+
+Shapely supports map projections and other arbitrary transformations of geometric objects.
+
+.. function:: shapely.ops.transform(func, geom)
+
+  Applies `func` to all coordinates of `geom` and returns a new
+  geometry of the same type from the transformed coordinates.
+
+  `func` maps x, y, and optionally z to output xp, yp, zp. The input
+  parameters may iterable types like lists or arrays or single values.
+  The output shall be of the same type: scalars in, scalars out;
+  lists in, lists out.
+
+  `New in version 1.2.18`.
+
+For example, here is an identity function applicable to both types of input
+(scalar or array).
+
+.. sourcecode:: python
+
+    def id_func(x, y, z=None):
+        return tuple(filter(None, [x, y, z]))
+
+    g2 = transform(id_func, g1)
+
+A partially applied transform function from pyproj satisfies the requirements
+for `func`.
+
+.. sourcecode:: python
+
+    from functools import partial
+    import pyproj
+
+    project = partial(
+        pyproj.transform,
+        pyproj.Proj(init='espg:4326'),
+        pyproj.Proj(init='epsg:26913'))
+
+    g2 = transform(project, g1)
+
+Lambda expressions such as the one in
+
+.. sourcecode:: python
+
+    g2 = transform(lambda x, y, z=None: (x+1.0, y+1.0), g1)
+
+also satisfy the requirements for `func`.
+
+
 Other Operations
 ================
 
 Merging Linear Features
 -----------------------
 
-Sequences of touching lines can be merged into `MultiLineStrings` or `Polygons` using functions in the :mod:`shapely.ops` module.
+Sequences of touching lines can be merged into `MultiLineStrings` or `Polygons`
+using functions in the :mod:`shapely.ops` module.
 
 .. function:: shapely.ops.polygonize(lines)
 
   Returns an iterator over polygons constructed from the input `lines`.
 
-As with the :class:`MultiLineString` constructor, the input elements may be any
-line-like object.
-  
-.. sourcecode:: python
-
-  >>> from shapely.ops import polygonize
-  >>> lines = [
-  ...     ((0, 0), (1, 1)),
-  ...     ((0, 0), (0, 1)),
-  ...     ((0, 1), (1, 1)),
-  ...     ((1, 1), (1, 0)),
-  ...     ((1, 0), (0, 0))
-  ...     ]
-  >>> pprint(list(polygonize(lines)))
-  [<shapely.geometry.polygon.Polygon object at 0x...>, 
-   <shapely.geometry.polygon.Polygon object at 0x...>]
+  As with the :class:`MultiLineString` constructor, the input elements may be
+  any line-like object.
+
+  .. sourcecode:: pycon
+
+    >>> from shapely.ops import polygonize
+    >>> lines = [
+    ...     ((0, 0), (1, 1)),
+    ...     ((0, 0), (0, 1)),
+    ...     ((0, 1), (1, 1)),
+    ...     ((1, 1), (1, 0)),
+    ...     ((1, 0), (0, 0))
+    ...     ]
+    >>> pprint(list(polygonize(lines)))
+    [<shapely.geometry.polygon.Polygon object at 0x...>,
+     <shapely.geometry.polygon.Polygon object at 0x...>]
+
+.. function:: shapely.ops.polygonize_full(lines)
+
+  Creates polygons from a source of lines, returning the polygons
+  and leftover geometries.
+
+  The source may be a MultiLineString, a sequence of LineString objects,
+  or a sequence of objects than can be adapted to LineStrings.
+
+  Returns a tuple of objects: (polygons, dangles, cut edges, invalid ring
+  lines). Each are a geometry collection.
+
+  Dangles are edges which have one or both ends which are not incident on
+  another edge endpoint. Cut edges are connected at both ends but do not
+  form part of polygon. Invalid ring lines form rings which are invalid
+  (bowties, etc).
+
+  `New in version 1.2.18.`
+
+  .. sourcecode:: pycon
+
+    >>> lines = [
+    ...     ((0, 0), (1, 1)),
+    ...     ((0, 0), (0, 1)),
+    ...     ((0, 1), (1, 1)),
+    ...     ((1, 1), (1, 0)),
+    ...     ((1, 0), (0, 0)),
+    ...     ((5, 5), (6, 6)),
+    ...     ((1, 1), (100, 100)),
+    ...     ]
+    >>> result, dangles, cuts, invalids = polygonize_full(lines)
+    >>> len(result)
+    2
+    >>> list(result.geoms)
+    [<shapely.geometry.polygon.Polygon object at ...>, <shapely.geometry.polygon.Polygon object at ...>]
+    >>> list(cuts.geoms)
+    [<shapely.geometry.linestring.LineString object at ...>, <shapely.geometry.linestring.LineString object at ...>]
 
 .. function:: shapely.ops.linemerge(lines)
 
   Returns a `LineString` or `MultiLineString` representing the merger of all
   contiguous elements of `lines`.
 
-As with :func:`shapely.ops.polygonize`, the input elements may be any line-like
-object.
+  As with :func:`shapely.ops.polygonize`, the input elements may be any
+  line-like object.
 
 .. sourcecode:: python
 
-  >>> from shapely.ops import linemerge
-  >>> linemerge(lines)
-  <shapely.geometry.multilinestring.MultiLineString object at 0x...>
-  >>> pprint(list(linemerge(lines)))
-  [<shapely.geometry.linestring.LineString object at 0x...>,
-   <shapely.geometry.linestring.LineString object at 0x...>,
-   <shapely.geometry.linestring.LineString object at 0x...>]
+    >>> from shapely.ops import linemerge
+    >>> linemerge(lines)
+    <shapely.geometry.multilinestring.MultiLineString object at 0x...>
+    >>> pprint(list(linemerge(lines)))
+    [<shapely.geometry.linestring.LineString object at 0x...>,
+     <shapely.geometry.linestring.LineString object at 0x...>,
+     <shapely.geometry.linestring.LineString object at 0x...>]
 
 Cascading Unions
 ----------------
 
-The :func:`cascaded_union` function in `shapely.ops` is more efficient than
-accumulating with :meth:`union`.
+The :func:`~shapely.ops.cascaded_union` function in `shapely.ops` is more
+efficient than accumulating with :meth:`union`.
 
 .. plot:: code/cascaded_union.py
 
@@ -1574,22 +1927,33 @@ accumulating with :meth:`union`.
 
   Returns a representation of the union of the given geometric objects.
 
-.. sourcecode:: pycon
+  .. sourcecode:: pycon
 
-  >>> from shapely.ops import cascaded_union
-  >>> polygons = [Point(i, 0).buffer(0.7) for i in range(5)]
-  >>> cascaded_union(polygons)
-  <shapely.geometry.polygon.Polygon object at 0x...>
+    >>> from shapely.ops import cascaded_union
+    >>> polygons = [Point(i, 0).buffer(0.7) for i in range(5)]
+    >>> cascaded_union(polygons)
+    <shapely.geometry.polygon.Polygon object at 0x...>
 
-The function is particularly useful in dissolving `MultiPolygons`.
+  The function is particularly useful in dissolving `MultiPolygons`.
 
-.. sourcecode:: pycon
+  .. sourcecode:: pycon
+
+    >>> m = MultiPolygon(polygons)
+    >>> m.area
+    7.6845438018375516
+    >>> cascaded_union(m).area
+    6.6103013551167971
+
+  .. note::
 
-  >>> m = MultiPolygon(polygons)
-  >>> m.area
-  7.6845438018375516
-  >>> cascaded_union(m).area
-  6.6103013551167971
+     In 1.2.16 :func:`shapely.ops.cascaded_union` is superceded by
+     :func:`shapely.ops.unary_union` if GEOS 3.2+ is used. The unary union
+     function can operate on different geometry types, not only polygons as is
+     the case for the older cascaded unions.
+
+.. function:: shapely.ops.unary_union(geoms)
+
+  Returns a representation of the union of the given geometric objects.
 
 Prepared Geometry Operations
 ----------------------------
@@ -1632,7 +1996,7 @@ Diagnostics
 The messages may or may not have a representation of a problem point that can
 be parsed out.
 
-.. sourcecode:: python
+.. sourcecode:: pycon
 
   >>> coords = [(0, 0), (0, 2), (1, 1), (2, 2), (2, 0), (1, 1), (0, 0)]
   >>> p = Polygon(coords)
@@ -1640,12 +2004,29 @@ be parsed out.
   >>> explain_validity(p)
   'Ring Self-intersection[1 1]'
 
+The Shapely version, GEOS library version, and GEOS C API version are
+accessible via :attr:`shapely.__version__`,
+:attr:`shapely.geos.geos_version_string`, and
+:attr:`shapely.geos.geos_capi_version`.
+
+.. sourcecode:: pycon
+
+  >>> import shapely
+  >>> shapely.__version__
+  '1.3.0'
+  >>> import shapely.geos
+  >>> shapely.geos.geos_version
+  (3, 3, 0)
+  >>> shapely.geos.geos_version_string
+  '3.3.0-CAPI-1.7.0'
+
+
 Interoperation
 ==============
 
 Shapely provides 4 avenues for interoperation with other software.
 
-Well-known Formats
+Well-Known Formats
 ------------------
 
 A `Well Known Text` (WKT) or `Well Known Binary` (WKB) representation [1]_ of
@@ -1683,7 +2064,7 @@ appropriate type, use ``loads()``.
   >>> loads(wkb).wkt
   'POINT (0.0000000000000000 0.0000000000000000)'
 
-All of Shapely's geometry types are supported by these functions. 
+All of Shapely's geometry types are supported by these functions.
 
 .. function:: shapely.wkt.dumps(ob)
 
@@ -1718,7 +2099,7 @@ adapted to Numpy arrays.
          [ 1.,  1.]])
 
 The :func:`numpy.asarray` function does not copy coordinate values – at the
-price of slower numpy access to the coordinates of Shapely objects.
+price of slower Numpy access to the coordinates of Shapely objects.
 
 .. note::
 
@@ -1735,7 +2116,7 @@ Python arrays of `x` and `y` values via the :attr:`xy` attribute.
   (array('d', [0.0, 1.0]), array('d', [0.0, 1.0]))
 
 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
+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
@@ -1770,7 +2151,7 @@ adapted and used as a Shapely geometry using the
 
 .. function:: shapely.geometry.shape(context)
 
-   Return a new, independent geometry with coordinates `copied` from the
+   Returns a new, independent geometry with coordinates `copied` from the
    context.
 
 For example, a dictionary:
@@ -1804,12 +2185,12 @@ The GeoJSON-like mapping of a geometric object can be obtained using
 
 .. function:: shapely.geometry.mapping(ob)
 
-   Return a new, independent geometry with coordinates `copied` from the
-   context.
+  Returns a new, independent geometry with coordinates `copied` from the
+  context.
 
-   `New in version 1.2.3`.
+  `New in version 1.2.3`.
 
-For example, using the same `GeoThing` class:
+  For example, using the same `GeoThing` class:
 
 .. sourcecode:: pycon
 
@@ -1833,8 +2214,8 @@ involves some overhead that might slow down your code.
 .. versionadded:: 1.2.10
 
 The :mod:`shapely.speedups` module contains performance enhancements written in
-C. They are automaticaly installed when Python has access to a compiler during
-installation.
+C. They are automaticaly installed when Python has access to a compiler and
+GEOS development headers during installation.
 
 You can check if the speedups are installed with the :attr:`available`
 attribute. The constructor speedups are disabled by default. To enable the
@@ -1855,7 +2236,7 @@ Conclusion
 We hope that you will enjoy and profit from using Shapely. Questions and
 comments are welcome on the GIS-Python email list_. This manual will be updated
 and improved regularly. Its source is available at
-http://github.com/Toblerity/Shapely/tree/master/docs/. 
+http://github.com/Toblerity/Shapely/tree/master/docs/.
 
 
 References
@@ -1865,11 +2246,11 @@ References
    “OpenGIS Implementation Specification for Geographic information - Simple
    feature access - Part 1: Common architecture,” Oct. 2006.
 
-.. [2] M.J. Egenhofer and John R. Herring, 
+.. [2] M.J. Egenhofer and John R. Herring,
    Categorizing Binary Topological Relations Between Regions, Lines, and Points
    in Geographic Databases,  Orono, ME: University of Maine, 1991.
 
-.. [3] E. Clementini, P. Di Felice, and P. van OOsterom,
+.. [3] E. Clementini, P. Di Felice, and P. van Oosterom,
    “A Small Set of Formal Topological Relationships Suitable for End-User
    Interaction,” Third International Symposium on Large Spatial Databases
    (SSD). Lecture Notes in Computer Science no. 692, David Abel and Beng Chin
@@ -1881,7 +2262,7 @@ References
 
 .. [5] Martin Davis, “JTS Technical Specifications,” Mar. 2003. [|JTS-PDF|_]
 
-.. [6] David H. Douglas and Thomas K. Peucker, 
+.. [6] David H. Douglas and Thomas K. Peucker,
    “Algorithms for the Reduction of the Number of Points Required to Represent
    a Digitized Line or its Caricature,” Cartographica: The International
    Journal for Geographic Information and Geovisualization,  vol. 10, Dec.
@@ -1897,11 +2278,11 @@ References
 .. _Open Geospatial Consortium: http://www.opengeospatial.org/
 .. _Davis: http://lin-ear-th-inking.blogspot.com/2007/06/subtleties-of-ogc-covers-spatial.html
 .. _Understanding spatial relations: http://edndoc.esri.com/arcsde/9.1/general_topics/understand_spatial_relations.htm
-.. _Strobl-PDF: http://gis.hsr.ch/wiki/images/3/3d/9dem_springer.pdf
+.. _Strobl-PDF: http://giswiki.hsr.ch/images/3/3d/9dem_springer.pdf
 .. |Strobl-PDF| replace:: PDF
 .. _JTS-PDF: http://www.vividsolutions.com/jts/bin/JTS%20Technical%20Specs.pdf
 .. |JTS-PDF| replace:: PDF
 .. _frozenset: http://docs.python.org/library/stdtypes.html#frozenset
 .. _Sorting HowTo: http://wiki.python.org/moin/HowTo/Sorting/
-.. _Python geo interface: http://trac.gispython.org/projects/PCL/wiki/PythonGeoInterface
+.. _Python geo interface: http://gist.github.com/2217756
 .. _list: http://lists.gispython.org/mailman/listinfo/community
diff --git a/docs/project.txt b/docs/project.txt
new file mode 100644
index 0000000..cba758f
--- /dev/null
+++ b/docs/project.txt
@@ -0,0 +1,4 @@
+.. include:: ../README.rst
+
+.. include:: ../CHANGES.txt
+
diff --git a/docs/sphinxext/apigen.py b/docs/sphinxext/apigen.py
new file mode 100644
index 0000000..1237409
--- /dev/null
+++ b/docs/sphinxext/apigen.py
@@ -0,0 +1,427 @@
+"""Attempt to generate templates for module reference with Sphinx
+
+XXX - we exclude extension modules
+
+To include extension modules, first identify them as valid in the
+``_uri2path`` method, then handle them in the ``_parse_module`` script.
+
+We get functions and classes by parsing the text of .py files.
+Alternatively we could import the modules for discovery, and we'd have
+to do that for extension modules.  This would involve changing the
+``_parse_module`` method to work via import and introspection, and
+might involve changing ``discover_modules`` (which determines which
+files are modules, and therefore which module URIs will be passed to
+``_parse_module``).
+
+NOTE: this is a modified version of a script originally shipped with the
+PyMVPA project, which we've adapted for NIPY use.  PyMVPA is an MIT-licensed
+project."""
+
+# Stdlib imports
+import os
+import re
+
+# Functions and classes
+class ApiDocWriter(object):
+    ''' Class for automatic detection and parsing of API docs
+    to Sphinx-parsable reST format'''
+
+    # only separating first two levels
+    rst_section_levels = ['*', '=', '-', '~', '^']
+
+    def __init__(self,
+                 package_name,
+                 rst_extension='.rst',
+                 package_skip_patterns=None,
+                 module_skip_patterns=None,
+                 ):
+        ''' Initialize package for parsing
+
+        Parameters
+        ----------
+        package_name : string
+            Name of the top-level package.  *package_name* must be the
+            name of an importable package
+        rst_extension : string, optional
+            Extension for reST files, default '.rst'
+        package_skip_patterns : None or sequence of {strings, regexps}
+            Sequence of strings giving URIs of packages to be excluded
+            Operates on the package path, starting at (including) the
+            first dot in the package path, after *package_name* - so,
+            if *package_name* is ``sphinx``, then ``sphinx.util`` will
+            result in ``.util`` being passed for earching by these
+            regexps.  If is None, gives default. Default is:
+            ['\.tests$']
+        module_skip_patterns : None or sequence
+            Sequence of strings giving URIs of modules to be excluded
+            Operates on the module name including preceding URI path,
+            back to the first dot after *package_name*.  For example
+            ``sphinx.util.console`` results in the string to search of
+            ``.util.console``
+            If is None, gives default. Default is:
+            ['\.setup$', '\._']
+        '''
+        if package_skip_patterns is None:
+            package_skip_patterns = ['\\.tests$']
+        if module_skip_patterns is None:
+            module_skip_patterns = ['\\.setup$', '\\._']
+        self.package_name = package_name
+        self.rst_extension = rst_extension
+        self.package_skip_patterns = package_skip_patterns
+        self.module_skip_patterns = module_skip_patterns
+
+    def get_package_name(self):
+        return self._package_name
+
+    def set_package_name(self, package_name):
+        ''' Set package_name
+
+        >>> docwriter = ApiDocWriter('sphinx')
+        >>> import sphinx
+        >>> docwriter.root_path == sphinx.__path__[0]
+        True
+        >>> docwriter.package_name = 'docutils'
+        >>> import docutils
+        >>> docwriter.root_path == docutils.__path__[0]
+        True
+        '''
+        # It's also possible to imagine caching the module parsing here
+        self._package_name = package_name
+        self.root_module = __import__(package_name)
+        self.root_path = self.root_module.__path__[0]
+        self.written_modules = None
+
+    package_name = property(get_package_name, set_package_name, None,
+                            'get/set package_name')
+
+    def _get_object_name(self, line):
+        ''' Get second token in line
+        >>> docwriter = ApiDocWriter('sphinx')
+        >>> docwriter._get_object_name("  def func():  ")
+        'func'
+        >>> docwriter._get_object_name("  class Klass(object):  ")
+        'Klass'
+        >>> docwriter._get_object_name("  class Klass:  ")
+        'Klass'
+        '''
+        name = line.split()[1].split('(')[0].strip()
+        # in case we have classes which are not derived from object
+        # ie. old style classes
+        return name.rstrip(':')
+
+    def _uri2path(self, uri):
+        ''' Convert uri to absolute filepath
+
+        Parameters
+        ----------
+        uri : string
+            URI of python module to return path for
+
+        Returns
+        -------
+        path : None or string
+            Returns None if there is no valid path for this URI
+            Otherwise returns absolute file system path for URI
+
+        Examples
+        --------
+        >>> docwriter = ApiDocWriter('sphinx')
+        >>> import sphinx
+        >>> modpath = sphinx.__path__[0]
+        >>> res = docwriter._uri2path('sphinx.builder')
+        >>> res == os.path.join(modpath, 'builder.py')
+        True
+        >>> res = docwriter._uri2path('sphinx')
+        >>> res == os.path.join(modpath, '__init__.py')
+        True
+        >>> docwriter._uri2path('sphinx.does_not_exist')
+
+        '''
+        if uri == self.package_name:
+            return os.path.join(self.root_path, '__init__.py')
+        path = uri.replace('.', os.path.sep)
+        path = path.replace(self.package_name + os.path.sep, '')
+        path = os.path.join(self.root_path, path)
+        # XXX maybe check for extensions as well?
+        if os.path.exists(path + '.py'): # file
+            path += '.py'
+        elif os.path.exists(os.path.join(path, '__init__.py')):
+            path = os.path.join(path, '__init__.py')
+        else:
+            return None
+        return path
+
+    def _path2uri(self, dirpath):
+        ''' Convert directory path to uri '''
+        relpath = dirpath.replace(self.root_path, self.package_name)
+        if relpath.startswith(os.path.sep):
+            relpath = relpath[1:]
+        return relpath.replace(os.path.sep, '.')
+
+    def _parse_module(self, uri):
+        ''' Parse module defined in *uri* '''
+        filename = self._uri2path(uri)
+        if filename is None:
+            # nothing that we could handle here.
+            return ([],[])
+        f = open(filename, 'rt')
+        functions, classes = self._parse_lines(f)
+        f.close()
+        return functions, classes
+    
+    def _parse_lines(self, linesource):
+        ''' Parse lines of text for functions and classes '''
+        functions = []
+        classes = []
+        for line in linesource:
+            if line.startswith('def ') and line.count('('):
+                # exclude private stuff
+                name = self._get_object_name(line)
+                if not name.startswith('_'):
+                    functions.append(name)
+            elif line.startswith('class '):
+                # exclude private stuff
+                name = self._get_object_name(line)
+                if not name.startswith('_'):
+                    classes.append(name)
+            else:
+                pass
+        functions.sort()
+        classes.sort()
+        return functions, classes
+
+    def generate_api_doc(self, uri):
+        '''Make autodoc documentation template string for a module
+
+        Parameters
+        ----------
+        uri : string
+            python location of module - e.g 'sphinx.builder'
+
+        Returns
+        -------
+        S : string
+            Contents of API doc
+        '''
+        # get the names of all classes and functions
+        functions, classes = self._parse_module(uri)
+        if not len(functions) and not len(classes):
+            print 'WARNING: Empty -',uri  # dbg
+            return ''
+
+        # Make a shorter version of the uri that omits the package name for
+        # titles 
+        uri_short = re.sub(r'^%s\.' % self.package_name,'',uri)
+        
+        ad = '.. AUTO-GENERATED FILE -- DO NOT EDIT!\n\n'
+
+        chap_title = uri_short
+        ad += (chap_title+'\n'+ self.rst_section_levels[1] * len(chap_title)
+               + '\n\n')
+
+        # Set the chapter title to read 'module' for all modules except for the
+        # main packages
+        if '.' in uri:
+            title = 'Module: :mod:`' + uri_short + '`'
+        else:
+            title = ':mod:`' + uri_short + '`'
+        ad += title + '\n' + self.rst_section_levels[2] * len(title)
+
+        if len(classes):
+            ad += '\nInheritance diagram for ``%s``:\n\n' % uri
+            ad += '.. inheritance-diagram:: %s \n' % uri
+            ad += '   :parts: 3\n'
+
+        ad += '\n.. automodule:: ' + uri + '\n'
+        ad += '\n.. currentmodule:: ' + uri + '\n'
+        multi_class = len(classes) > 1
+        multi_fx = len(functions) > 1
+        if multi_class:
+            ad += '\n' + 'Classes' + '\n' + \
+                  self.rst_section_levels[2] * 7 + '\n'
+        elif len(classes) and multi_fx:
+            ad += '\n' + 'Class' + '\n' + \
+                  self.rst_section_levels[2] * 5 + '\n'
+        for c in classes:
+            ad += '\n:class:`' + c + '`\n' \
+                  + self.rst_section_levels[multi_class + 2 ] * \
+                  (len(c)+9) + '\n\n'
+            ad += '\n.. autoclass:: ' + c + '\n'
+            # must NOT exclude from index to keep cross-refs working
+            ad += '  :members:\n' \
+                  '  :undoc-members:\n' \
+                  '  :show-inheritance:\n' \
+                  '  :inherited-members:\n' \
+                  '\n' \
+                  '  .. automethod:: __init__\n'
+        if multi_fx:
+            ad += '\n' + 'Functions' + '\n' + \
+                  self.rst_section_levels[2] * 9 + '\n\n'
+        elif len(functions) and multi_class:
+            ad += '\n' + 'Function' + '\n' + \
+                  self.rst_section_levels[2] * 8 + '\n\n'
+        for f in functions:
+            # must NOT exclude from index to keep cross-refs working
+            ad += '\n.. autofunction:: ' + uri + '.' + f + '\n\n'
+        return ad
+
+    def _survives_exclude(self, matchstr, match_type):
+        ''' Returns True if *matchstr* does not match patterns
+
+        ``self.package_name`` removed from front of string if present
+
+        Examples
+        --------
+        >>> dw = ApiDocWriter('sphinx')
+        >>> dw._survives_exclude('sphinx.okpkg', 'package')
+        True
+        >>> dw.package_skip_patterns.append('^\\.badpkg$')
+        >>> dw._survives_exclude('sphinx.badpkg', 'package')
+        False
+        >>> dw._survives_exclude('sphinx.badpkg', 'module')
+        True
+        >>> dw._survives_exclude('sphinx.badmod', 'module')
+        True
+        >>> dw.module_skip_patterns.append('^\\.badmod$')
+        >>> dw._survives_exclude('sphinx.badmod', 'module')
+        False
+        '''
+        if match_type == 'module':
+            patterns = self.module_skip_patterns
+        elif match_type == 'package':
+            patterns = self.package_skip_patterns
+        else:
+            raise ValueError('Cannot interpret match type "%s"' 
+                             % match_type)
+        # Match to URI without package name
+        L = len(self.package_name)
+        if matchstr[:L] == self.package_name:
+            matchstr = matchstr[L:]
+        for pat in patterns:
+            try:
+                pat.search
+            except AttributeError:
+                pat = re.compile(pat)
+            if pat.search(matchstr):
+                return False
+        return True
+
+    def discover_modules(self):
+        ''' Return module sequence discovered from ``self.package_name`` 
+
+
+        Parameters
+        ----------
+        None
+
+        Returns
+        -------
+        mods : sequence
+            Sequence of module names within ``self.package_name``
+
+        Examples
+        --------
+        >>> dw = ApiDocWriter('sphinx')
+        >>> mods = dw.discover_modules()
+        >>> 'sphinx.util' in mods
+        True
+        >>> dw.package_skip_patterns.append('\.util$')
+        >>> 'sphinx.util' in dw.discover_modules()
+        False
+        >>> 
+        '''
+        modules = [self.package_name]
+        # raw directory parsing
+        for dirpath, dirnames, filenames in os.walk(self.root_path):
+            # Check directory names for packages
+            root_uri = self._path2uri(os.path.join(self.root_path,
+                                                   dirpath))
+            for dirname in dirnames[:]: # copy list - we modify inplace
+                package_uri = '.'.join((root_uri, dirname))
+                if (self._uri2path(package_uri) and
+                    self._survives_exclude(package_uri, 'package')):
+                    modules.append(package_uri)
+                else:
+                    dirnames.remove(dirname)
+            # Check filenames for modules
+            for filename in filenames:
+                module_name = filename[:-3]
+                module_uri = '.'.join((root_uri, module_name))
+                if (self._uri2path(module_uri) and
+                    self._survives_exclude(module_uri, 'module')):
+                    modules.append(module_uri)
+        return sorted(modules)
+    
+    def write_modules_api(self, modules,outdir):
+        # write the list
+        written_modules = []
+        for m in modules:
+            api_str = self.generate_api_doc(m)
+            if not api_str:
+                continue
+            # write out to file
+            outfile = os.path.join(outdir,
+                                   m + self.rst_extension)
+            fileobj = open(outfile, 'wt')
+            fileobj.write(api_str)
+            fileobj.close()
+            written_modules.append(m)
+        self.written_modules = written_modules
+
+    def write_api_docs(self, outdir):
+        """Generate API reST files.
+
+        Parameters
+        ----------
+        outdir : string
+            Directory name in which to store files
+            We create automatic filenames for each module
+            
+        Returns
+        -------
+        None
+
+        Notes
+        -----
+        Sets self.written_modules to list of written modules
+        """
+        if not os.path.exists(outdir):
+            os.mkdir(outdir)
+        # compose list of modules
+        modules = self.discover_modules()
+        self.write_modules_api(modules,outdir)
+        
+    def write_index(self, outdir, froot='gen', relative_to=None):
+        """Make a reST API index file from written files
+
+        Parameters
+        ----------
+        path : string
+            Filename to write index to
+        outdir : string
+            Directory to which to write generated index file
+        froot : string, optional
+            root (filename without extension) of filename to write to
+            Defaults to 'gen'.  We add ``self.rst_extension``.
+        relative_to : string
+            path to which written filenames are relative.  This
+            component of the written file path will be removed from
+            outdir, in the generated index.  Default is None, meaning,
+            leave path as it is.
+        """
+        if self.written_modules is None:
+            raise ValueError('No modules written')
+        # Get full filename path
+        path = os.path.join(outdir, froot+self.rst_extension)
+        # Path written into index is relative to rootpath
+        if relative_to is not None:
+            relpath = outdir.replace(relative_to + os.path.sep, '')
+        else:
+            relpath = outdir
+        idx = open(path,'wt')
+        w = idx.write
+        w('.. AUTO-GENERATED FILE -- DO NOT EDIT!\n\n')
+        w('.. toctree::\n\n')
+        for f in self.written_modules:
+            w('   %s\n' % os.path.join(relpath,f))
+        idx.close()
diff --git a/docs/sphinxext/docscrape.py b/docs/sphinxext/docscrape.py
new file mode 100644
index 0000000..f374b3d
--- /dev/null
+++ b/docs/sphinxext/docscrape.py
@@ -0,0 +1,497 @@
+"""Extract reference documentation from the NumPy source tree.
+
+"""
+
+import inspect
+import textwrap
+import re
+import pydoc
+from StringIO import StringIO
+from warnings import warn
+4
+class Reader(object):
+    """A line-based string reader.
+
+    """
+    def __init__(self, data):
+        """
+        Parameters
+        ----------
+        data : str
+           String with lines separated by '\n'.
+
+        """
+        if isinstance(data,list):
+            self._str = data
+        else:
+            self._str = data.split('\n') # store string as list of lines
+
+        self.reset()
+
+    def __getitem__(self, n):
+        return self._str[n]
+
+    def reset(self):
+        self._l = 0 # current line nr
+
+    def read(self):
+        if not self.eof():
+            out = self[self._l]
+            self._l += 1
+            return out
+        else:
+            return ''
+
+    def seek_next_non_empty_line(self):
+        for l in self[self._l:]:
+            if l.strip():
+                break
+            else:
+                self._l += 1
+
+    def eof(self):
+        return self._l >= len(self._str)
+
+    def read_to_condition(self, condition_func):
+        start = self._l
+        for line in self[start:]:
+            if condition_func(line):
+                return self[start:self._l]
+            self._l += 1
+            if self.eof():
+                return self[start:self._l+1]
+        return []
+
+    def read_to_next_empty_line(self):
+        self.seek_next_non_empty_line()
+        def is_empty(line):
+            return not line.strip()
+        return self.read_to_condition(is_empty)
+
+    def read_to_next_unindented_line(self):
+        def is_unindented(line):
+            return (line.strip() and (len(line.lstrip()) == len(line)))
+        return self.read_to_condition(is_unindented)
+
+    def peek(self,n=0):
+        if self._l + n < len(self._str):
+            return self[self._l + n]
+        else:
+            return ''
+
+    def is_empty(self):
+        return not ''.join(self._str).strip()
+
+
+class NumpyDocString(object):
+    def __init__(self,docstring):
+        docstring = textwrap.dedent(docstring).split('\n')
+
+        self._doc = Reader(docstring)
+        self._parsed_data = {
+            'Signature': '',
+            'Summary': [''],
+            'Extended Summary': [],
+            'Parameters': [],
+            'Returns': [],
+            'Raises': [],
+            'Warns': [],
+            'Other Parameters': [],
+            'Attributes': [],
+            'Methods': [],
+            'See Also': [],
+            'Notes': [],
+            'Warnings': [],
+            'References': '',
+            'Examples': '',
+            'index': {}
+            }
+
+        self._parse()
+
+    def __getitem__(self,key):
+        return self._parsed_data[key]
+
+    def __setitem__(self,key,val):
+        if not self._parsed_data.has_key(key):
+            warn("Unknown section %s" % key)
+        else:
+            self._parsed_data[key] = val
+
+    def _is_at_section(self):
+        self._doc.seek_next_non_empty_line()
+
+        if self._doc.eof():
+            return False
+
+        l1 = self._doc.peek().strip()  # e.g. Parameters
+
+        if l1.startswith('.. index::'):
+            return True
+
+        l2 = self._doc.peek(1).strip() #    ---------- or ==========
+        return l2.startswith('-'*len(l1)) or l2.startswith('='*len(l1))
+
+    def _strip(self,doc):
+        i = 0
+        j = 0
+        for i,line in enumerate(doc):
+            if line.strip(): break
+
+        for j,line in enumerate(doc[::-1]):
+            if line.strip(): break
+
+        return doc[i:len(doc)-j]
+
+    def _read_to_next_section(self):
+        section = self._doc.read_to_next_empty_line()
+
+        while not self._is_at_section() and not self._doc.eof():
+            if not self._doc.peek(-1).strip(): # previous line was empty
+                section += ['']
+
+            section += self._doc.read_to_next_empty_line()
+
+        return section
+
+    def _read_sections(self):
+        while not self._doc.eof():
+            data = self._read_to_next_section()
+            name = data[0].strip()
+
+            if name.startswith('..'): # index section
+                yield name, data[1:]
+            elif len(data) < 2:
+                yield StopIteration
+            else:
+                yield name, self._strip(data[2:])
+
+    def _parse_param_list(self,content):
+        r = Reader(content)
+        params = []
+        while not r.eof():
+            header = r.read().strip()
+            if ' : ' in header:
+                arg_name, arg_type = header.split(' : ')[:2]
+            else:
+                arg_name, arg_type = header, ''
+
+            desc = r.read_to_next_unindented_line()
+            desc = dedent_lines(desc)
+
+            params.append((arg_name,arg_type,desc))
+
+        return params
+
+    
+    _name_rgx = re.compile(r"^\s*(:(?P<role>\w+):`(?P<name>[a-zA-Z0-9_.-]+)`|"
+                           r" (?P<name2>[a-zA-Z0-9_.-]+))\s*", re.X)
+    def _parse_see_also(self, content):
+        """
+        func_name : Descriptive text
+            continued text
+        another_func_name : Descriptive text
+        func_name1, func_name2, :meth:`func_name`, func_name3
+
+        """
+        items = []
+
+        def parse_item_name(text):
+            """Match ':role:`name`' or 'name'"""
+            m = self._name_rgx.match(text)
+            if m:
+                g = m.groups()
+                if g[1] is None:
+                    return g[3], None
+                else:
+                    return g[2], g[1]
+            raise ValueError("%s is not a item name" % text)
+
+        def push_item(name, rest):
+            if not name:
+                return
+            name, role = parse_item_name(name)
+            items.append((name, list(rest), role))
+            del rest[:]
+
+        current_func = None
+        rest = []
+        
+        for line in content:
+            if not line.strip(): continue
+
+            m = self._name_rgx.match(line)
+            if m and line[m.end():].strip().startswith(':'):
+                push_item(current_func, rest)
+                current_func, line = line[:m.end()], line[m.end():]
+                rest = [line.split(':', 1)[1].strip()]
+                if not rest[0]:
+                    rest = []
+            elif not line.startswith(' '):
+                push_item(current_func, rest)
+                current_func = None
+                if ',' in line:
+                    for func in line.split(','):
+                        push_item(func, [])
+                elif line.strip():
+                    current_func = line
+            elif current_func is not None:
+                rest.append(line.strip())
+        push_item(current_func, rest)
+        return items
+
+    def _parse_index(self, section, content):
+        """
+        .. index: default
+           :refguide: something, else, and more
+
+        """
+        def strip_each_in(lst):
+            return [s.strip() for s in lst]
+
+        out = {}
+        section = section.split('::')
+        if len(section) > 1:
+            out['default'] = strip_each_in(section[1].split(','))[0]
+        for line in content:
+            line = line.split(':')
+            if len(line) > 2:
+                out[line[1]] = strip_each_in(line[2].split(','))
+        return out
+    
+    def _parse_summary(self):
+        """Grab signature (if given) and summary"""
+        if self._is_at_section():
+            return
+
+        summary = self._doc.read_to_next_empty_line()
+        summary_str = " ".join([s.strip() for s in summary]).strip()
+        if re.compile('^([\w., ]+=)?\s*[\w\.]+\(.*\)$').match(summary_str):
+            self['Signature'] = summary_str
+            if not self._is_at_section():
+                self['Summary'] = self._doc.read_to_next_empty_line()
+        else:
+            self['Summary'] = summary
+
+        if not self._is_at_section():
+            self['Extended Summary'] = self._read_to_next_section()
+    
+    def _parse(self):
+        self._doc.reset()
+        self._parse_summary()
+
+        for (section,content) in self._read_sections():
+            if not section.startswith('..'):
+                section = ' '.join([s.capitalize() for s in section.split(' ')])
+            if section in ('Parameters', 'Attributes', 'Methods',
+                           'Returns', 'Raises', 'Warns'):
+                self[section] = self._parse_param_list(content)
+            elif section.startswith('.. index::'):
+                self['index'] = self._parse_index(section, content)
+            elif section == 'See Also':
+                self['See Also'] = self._parse_see_also(content)
+            else:
+                self[section] = content
+
+    # string conversion routines
+
+    def _str_header(self, name, symbol='-'):
+        return [name, len(name)*symbol]
+
+    def _str_indent(self, doc, indent=4):
+        out = []
+        for line in doc:
+            out += [' '*indent + line]
+        return out
+
+    def _str_signature(self):
+        if self['Signature']:
+            return [self['Signature'].replace('*','\*')] + ['']
+        else:
+            return ['']
+
+    def _str_summary(self):
+        if self['Summary']:
+            return self['Summary'] + ['']
+        else:
+            return []
+
+    def _str_extended_summary(self):
+        if self['Extended Summary']:
+            return self['Extended Summary'] + ['']
+        else:
+            return []
+
+    def _str_param_list(self, name):
+        out = []
+        if self[name]:
+            out += self._str_header(name)
+            for param,param_type,desc in self[name]:
+                out += ['%s : %s' % (param, param_type)]
+                out += self._str_indent(desc)
+            out += ['']
+        return out
+
+    def _str_section(self, name):
+        out = []
+        if self[name]:
+            out += self._str_header(name)
+            out += self[name]
+            out += ['']
+        return out
+
+    def _str_see_also(self, func_role):
+        if not self['See Also']: return []
+        out = []
+        out += self._str_header("See Also")
+        last_had_desc = True
+        for func, desc, role in self['See Also']:
+            if role:
+                link = ':%s:`%s`' % (role, func)
+            elif func_role:
+                link = ':%s:`%s`' % (func_role, func)
+            else:
+                link = "`%s`_" % func
+            if desc or last_had_desc:
+                out += ['']
+                out += [link]
+            else:
+                out[-1] += ", %s" % link
+            if desc:
+                out += self._str_indent([' '.join(desc)])
+                last_had_desc = True
+            else:
+                last_had_desc = False
+        out += ['']
+        return out
+
+    def _str_index(self):
+        idx = self['index']
+        out = []
+        out += ['.. index:: %s' % idx.get('default','')]
+        for section, references in idx.iteritems():
+            if section == 'default':
+                continue
+            out += ['   :%s: %s' % (section, ', '.join(references))]
+        return out
+
+    def __str__(self, func_role=''):
+        out = []
+        out += self._str_signature()
+        out += self._str_summary()
+        out += self._str_extended_summary()
+        for param_list in ('Parameters','Returns','Raises'):
+            out += self._str_param_list(param_list)
+        out += self._str_section('Warnings')
+        out += self._str_see_also(func_role)
+        for s in ('Notes','References','Examples'):
+            out += self._str_section(s)
+        out += self._str_index()
+        return '\n'.join(out)
+
+
+def indent(str,indent=4):
+    indent_str = ' '*indent
+    if str is None:
+        return indent_str
+    lines = str.split('\n')
+    return '\n'.join(indent_str + l for l in lines)
+
+def dedent_lines(lines):
+    """Deindent a list of lines maximally"""
+    return textwrap.dedent("\n".join(lines)).split("\n")
+
+def header(text, style='-'):
+    return text + '\n' + style*len(text) + '\n'
+
+
+class FunctionDoc(NumpyDocString):
+    def __init__(self, func, role='func', doc=None):
+        self._f = func
+        self._role = role # e.g. "func" or "meth"
+        if doc is None:
+            doc = inspect.getdoc(func) or ''
+        try:
+            NumpyDocString.__init__(self, doc)
+        except ValueError, e:
+            print '*'*78
+            print "ERROR: '%s' while parsing `%s`" % (e, self._f)
+            print '*'*78
+            #print "Docstring follows:"
+            #print doclines
+            #print '='*78
+
+        if not self['Signature']:
+            func, func_name = self.get_func()
+            try:
+                # try to read signature
+                argspec = inspect.getargspec(func)
+                argspec = inspect.formatargspec(*argspec)
+                argspec = argspec.replace('*','\*')
+                signature = '%s%s' % (func_name, argspec)
+            except TypeError, e:
+                signature = '%s()' % func_name
+            self['Signature'] = signature
+
+    def get_func(self):
+        func_name = getattr(self._f, '__name__', self.__class__.__name__)
+        if inspect.isclass(self._f):
+            func = getattr(self._f, '__call__', self._f.__init__)
+        else:
+            func = self._f
+        return func, func_name
+            
+    def __str__(self):
+        out = ''
+
+        func, func_name = self.get_func()
+        signature = self['Signature'].replace('*', '\*')
+
+        roles = {'func': 'function',
+                 'meth': 'method'}
+
+        if self._role:
+            if not roles.has_key(self._role):
+                print "Warning: invalid role %s" % self._role
+            out += '.. %s:: %s\n    \n\n' % (roles.get(self._role,''),
+                                             func_name)
+
+        out += super(FunctionDoc, self).__str__(func_role=self._role)
+        return out
+
+
+class ClassDoc(NumpyDocString):
+    def __init__(self,cls,modulename='',func_doc=FunctionDoc,doc=None):
+        if not inspect.isclass(cls):
+            raise ValueError("Initialise using a class. Got %r" % cls)
+        self._cls = cls
+
+        if modulename and not modulename.endswith('.'):
+            modulename += '.'
+        self._mod = modulename
+        self._name = cls.__name__
+        self._func_doc = func_doc
+
+        if doc is None:
+            doc = pydoc.getdoc(cls)
+
+        NumpyDocString.__init__(self, doc)
+
+    @property
+    def methods(self):
+        return [name for name,func in inspect.getmembers(self._cls)
+                if not name.startswith('_') and callable(func)]
+
+    def __str__(self):
+        out = ''
+        out += super(ClassDoc, self).__str__()
+        out += "\n\n"
+
+        #for m in self.methods:
+        #    print "Parsing `%s`" % m
+        #    out += str(self._func_doc(getattr(self._cls,m), 'meth')) + '\n\n'
+        #    out += '.. index::\n   single: %s; %s\n\n' % (self._name, m)
+
+        return out
+
+
diff --git a/docs/sphinxext/docscrape_sphinx.py b/docs/sphinxext/docscrape_sphinx.py
new file mode 100644
index 0000000..77ed271
--- /dev/null
+++ b/docs/sphinxext/docscrape_sphinx.py
@@ -0,0 +1,136 @@
+import re, inspect, textwrap, pydoc
+from docscrape import NumpyDocString, FunctionDoc, ClassDoc
+
+class SphinxDocString(NumpyDocString):
+    # string conversion routines
+    def _str_header(self, name, symbol='`'):
+        return ['.. rubric:: ' + name, '']
+
+    def _str_field_list(self, name):
+        return [':' + name + ':']
+
+    def _str_indent(self, doc, indent=4):
+        out = []
+        for line in doc:
+            out += [' '*indent + line]
+        return out
+
+    def _str_signature(self):
+        return ['']
+        if self['Signature']:
+            return ['``%s``' % self['Signature']] + ['']
+        else:
+            return ['']
+
+    def _str_summary(self):
+        return self['Summary'] + ['']
+
+    def _str_extended_summary(self):
+        return self['Extended Summary'] + ['']
+
+    def _str_param_list(self, name):
+        out = []
+        if self[name]:
+            out += self._str_field_list(name)
+            out += ['']
+            for param,param_type,desc in self[name]:
+                out += self._str_indent(['**%s** : %s' % (param.strip(),
+                                                          param_type)])
+                out += ['']
+                out += self._str_indent(desc,8)
+                out += ['']
+        return out
+
+    def _str_section(self, name):
+        out = []
+        if self[name]:
+            out += self._str_header(name)
+            out += ['']
+            content = textwrap.dedent("\n".join(self[name])).split("\n")
+            out += content
+            out += ['']
+        return out
+
+    def _str_see_also(self, func_role):
+        out = []
+        if self['See Also']:
+            see_also = super(SphinxDocString, self)._str_see_also(func_role)
+            out = ['.. seealso::', '']
+            out += self._str_indent(see_also[2:])
+        return out
+
+    def _str_warnings(self):
+        out = []
+        if self['Warnings']:
+            out = ['.. warning::', '']
+            out += self._str_indent(self['Warnings'])
+        return out
+
+    def _str_index(self):
+        idx = self['index']
+        out = []
+        if len(idx) == 0:
+            return out
+
+        out += ['.. index:: %s' % idx.get('default','')]
+        for section, references in idx.iteritems():
+            if section == 'default':
+                continue
+            elif section == 'refguide':
+                out += ['   single: %s' % (', '.join(references))]
+            else:
+                out += ['   %s: %s' % (section, ','.join(references))]
+        return out
+
+    def _str_references(self):
+        out = []
+        if self['References']:
+            out += self._str_header('References')
+            if isinstance(self['References'], str):
+                self['References'] = [self['References']]
+            out.extend(self['References'])
+            out += ['']
+        return out
+
+    def __str__(self, indent=0, func_role="obj"):
+        out = []
+        out += self._str_signature()
+        out += self._str_index() + ['']
+        out += self._str_summary()
+        out += self._str_extended_summary()
+        for param_list in ('Parameters', 'Attributes', 'Methods',
+                           'Returns','Raises'):
+            out += self._str_param_list(param_list)
+        out += self._str_warnings()
+        out += self._str_see_also(func_role)
+        out += self._str_section('Notes')
+        out += self._str_references()
+        out += self._str_section('Examples')
+        out = self._str_indent(out,indent)
+        return '\n'.join(out)
+
+class SphinxFunctionDoc(SphinxDocString, FunctionDoc):
+    pass
+
+class SphinxClassDoc(SphinxDocString, ClassDoc):
+    pass
+
+def get_doc_object(obj, what=None, doc=None):
+    if what is None:
+        if inspect.isclass(obj):
+            what = 'class'
+        elif inspect.ismodule(obj):
+            what = 'module'
+        elif callable(obj):
+            what = 'function'
+        else:
+            what = 'object'
+    if what == 'class':
+        return SphinxClassDoc(obj, '', func_doc=SphinxFunctionDoc, doc=doc)
+    elif what in ('function', 'method'):
+        return SphinxFunctionDoc(obj, '', doc=doc)
+    else:
+        if doc is None:
+            doc = pydoc.getdoc(obj)
+        return SphinxDocString(doc)
+
diff --git a/docs/sphinxext/inheritance_diagram.py b/docs/sphinxext/inheritance_diagram.py
new file mode 100644
index 0000000..407fc13
--- /dev/null
+++ b/docs/sphinxext/inheritance_diagram.py
@@ -0,0 +1,407 @@
+"""
+Defines a docutils directive for inserting inheritance diagrams.
+
+Provide the directive with one or more classes or modules (separated
+by whitespace).  For modules, all of the classes in that module will
+be used.
+
+Example::
+
+   Given the following classes:
+
+   class A: pass
+   class B(A): pass
+   class C(A): pass
+   class D(B, C): pass
+   class E(B): pass
+
+   .. inheritance-diagram: D E
+
+   Produces a graph like the following:
+
+               A
+              / \
+             B   C
+            / \ /
+           E   D
+
+The graph is inserted as a PNG+image map into HTML and a PDF in
+LaTeX.
+"""
+
+import inspect
+import os
+import re
+import subprocess
+try:
+    from hashlib import md5
+except ImportError:
+    from md5 import md5
+
+from docutils.nodes import Body, Element
+from docutils.parsers.rst import directives
+from sphinx.roles import xfileref_role
+
+def my_import(name):
+    """Module importer - taken from the python documentation.
+
+    This function allows importing names with dots in them."""
+    
+    mod = __import__(name)
+    components = name.split('.')
+    for comp in components[1:]:
+        mod = getattr(mod, comp)
+    return mod
+
+class DotException(Exception):
+    pass
+
+class InheritanceGraph(object):
+    """
+    Given a list of classes, determines the set of classes that
+    they inherit from all the way to the root "object", and then
+    is able to generate a graphviz dot graph from them.
+    """
+    def __init__(self, class_names, show_builtins=False):
+        """
+        *class_names* is a list of child classes to show bases from.
+
+        If *show_builtins* is True, then Python builtins will be shown
+        in the graph.
+        """
+        self.class_names = class_names
+        self.classes = self._import_classes(class_names)
+        self.all_classes = self._all_classes(self.classes)
+        if len(self.all_classes) == 0:
+            raise ValueError("No classes found for inheritance diagram")
+        self.show_builtins = show_builtins
+
+    py_sig_re = re.compile(r'''^([\w.]*\.)?    # class names
+                           (\w+)  \s* $        # optionally arguments
+                           ''', re.VERBOSE)
+
+    def _import_class_or_module(self, name):
+        """
+        Import a class using its fully-qualified *name*.
+        """
+        try:
+            path, base = self.py_sig_re.match(name).groups()
+        except:
+            raise ValueError(
+                "Invalid class or module '%s' specified for inheritance diagram" % name)
+        fullname = (path or '') + base
+        path = (path and path.rstrip('.'))
+        if not path:
+            path = base
+        try:
+            module = __import__(path, None, None, [])
+            # We must do an import of the fully qualified name.  Otherwise if a
+            # subpackage 'a.b' is requested where 'import a' does NOT provide
+            # 'a.b' automatically, then 'a.b' will not be found below.  This
+            # second call will force the equivalent of 'import a.b' to happen
+            # after the top-level import above.
+            my_import(fullname)
+            
+        except ImportError:
+            raise ValueError(
+                "Could not import class or module '%s' specified for inheritance diagram" % name)
+
+        try:
+            todoc = module
+            for comp in fullname.split('.')[1:]:
+                todoc = getattr(todoc, comp)
+        except AttributeError:
+            raise ValueError(
+                "Could not find class or module '%s' specified for inheritance diagram" % name)
+
+        # If a class, just return it
+        if inspect.isclass(todoc):
+            return [todoc]
+        elif inspect.ismodule(todoc):
+            classes = []
+            for cls in todoc.__dict__.values():
+                if inspect.isclass(cls) and cls.__module__ == todoc.__name__:
+                    classes.append(cls)
+            return classes
+        raise ValueError(
+            "'%s' does not resolve to a class or module" % name)
+
+    def _import_classes(self, class_names):
+        """
+        Import a list of classes.
+        """
+        classes = []
+        for name in class_names:
+            classes.extend(self._import_class_or_module(name))
+        return classes
+
+    def _all_classes(self, classes):
+        """
+        Return a list of all classes that are ancestors of *classes*.
+        """
+        all_classes = {}
+
+        def recurse(cls):
+            all_classes[cls] = None
+            for c in cls.__bases__:
+                if c not in all_classes:
+                    recurse(c)
+
+        for cls in classes:
+            recurse(cls)
+
+        return all_classes.keys()
+
+    def class_name(self, cls, parts=0):
+        """
+        Given a class object, return a fully-qualified name.  This
+        works for things I've tested in matplotlib so far, but may not
+        be completely general.
+        """
+        module = cls.__module__
+        if module == '__builtin__':
+            fullname = cls.__name__
+        else:
+            fullname = "%s.%s" % (module, cls.__name__)
+        if parts == 0:
+            return fullname
+        name_parts = fullname.split('.')
+        return '.'.join(name_parts[-parts:])
+
+    def get_all_class_names(self):
+        """
+        Get all of the class names involved in the graph.
+        """
+        return [self.class_name(x) for x in self.all_classes]
+
+    # These are the default options for graphviz
+    default_graph_options = {
+        "rankdir": "LR",
+        "size": '"8.0, 12.0"'
+        }
+    default_node_options = {
+        "shape": "box",
+        "fontsize": 10,
+        "height": 0.25,
+        "fontname": "Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans",
+        "style": '"setlinewidth(0.5)"'
+        }
+    default_edge_options = {
+        "arrowsize": 0.5,
+        "style": '"setlinewidth(0.5)"'
+        }
+
+    def _format_node_options(self, options):
+        return ','.join(["%s=%s" % x for x in options.items()])
+    def _format_graph_options(self, options):
+        return ''.join(["%s=%s;\n" % x for x in options.items()])
+
+    def generate_dot(self, fd, name, parts=0, urls={},
+                     graph_options={}, node_options={},
+                     edge_options={}):
+        """
+        Generate a graphviz dot graph from the classes that
+        were passed in to __init__.
+
+        *fd* is a Python file-like object to write to.
+
+        *name* is the name of the graph
+
+        *urls* is a dictionary mapping class names to http urls
+
+        *graph_options*, *node_options*, *edge_options* are
+        dictionaries containing key/value pairs to pass on as graphviz
+        properties.
+        """
+        g_options = self.default_graph_options.copy()
+        g_options.update(graph_options)
+        n_options = self.default_node_options.copy()
+        n_options.update(node_options)
+        e_options = self.default_edge_options.copy()
+        e_options.update(edge_options)
+
+        fd.write('digraph %s {\n' % name)
+        fd.write(self._format_graph_options(g_options))
+
+        for cls in self.all_classes:
+            if not self.show_builtins and cls in __builtins__.values():
+                continue
+
+            name = self.class_name(cls, parts)
+
+            # Write the node
+            this_node_options = n_options.copy()
+            url = urls.get(self.class_name(cls))
+            if url is not None:
+                this_node_options['URL'] = '"%s"' % url
+            fd.write('  "%s" [%s];\n' %
+                     (name, self._format_node_options(this_node_options)))
+
+            # Write the edges
+            for base in cls.__bases__:
+                if not self.show_builtins and base in __builtins__.values():
+                    continue
+
+                base_name = self.class_name(base, parts)
+                fd.write('  "%s" -> "%s" [%s];\n' %
+                         (base_name, name,
+                          self._format_node_options(e_options)))
+        fd.write('}\n')
+
+    def run_dot(self, args, name, parts=0, urls={},
+                graph_options={}, node_options={}, edge_options={}):
+        """
+        Run graphviz 'dot' over this graph, returning whatever 'dot'
+        writes to stdout.
+
+        *args* will be passed along as commandline arguments.
+
+        *name* is the name of the graph
+
+        *urls* is a dictionary mapping class names to http urls
+
+        Raises DotException for any of the many os and
+        installation-related errors that may occur.
+        """
+        try:
+            dot = subprocess.Popen(['dot'] + list(args),
+                                   stdin=subprocess.PIPE, stdout=subprocess.PIPE,
+                                   close_fds=True)
+        except OSError:
+            raise DotException("Could not execute 'dot'.  Are you sure you have 'graphviz' installed?")
+        except ValueError:
+            raise DotException("'dot' called with invalid arguments")
+        except:
+            raise DotException("Unexpected error calling 'dot'")
+
+        self.generate_dot(dot.stdin, name, parts, urls, graph_options,
+                          node_options, edge_options)
+        dot.stdin.close()
+        result = dot.stdout.read()
+        returncode = dot.wait()
+        if returncode != 0:
+            raise DotException("'dot' returned the errorcode %d" % returncode)
+        return result
+
+class inheritance_diagram(Body, Element):
+    """
+    A docutils node to use as a placeholder for the inheritance
+    diagram.
+    """
+    pass
+
+def inheritance_diagram_directive(name, arguments, options, content, lineno,
+                                  content_offset, block_text, state,
+                                  state_machine):
+    """
+    Run when the inheritance_diagram directive is first encountered.
+    """
+    node = inheritance_diagram()
+
+    class_names = arguments
+
+    # Create a graph starting with the list of classes
+    graph = InheritanceGraph(class_names)
+
+    # Create xref nodes for each target of the graph's image map and
+    # add them to the doc tree so that Sphinx can resolve the
+    # references to real URLs later.  These nodes will eventually be
+    # removed from the doctree after we're done with them.
+    for name in graph.get_all_class_names():
+        refnodes, x = xfileref_role(
+            'class', ':class:`%s`' % name, name, 0, state)
+        node.extend(refnodes)
+    # Store the graph object so we can use it to generate the
+    # dot file later
+    node['graph'] = graph
+    # Store the original content for use as a hash
+    node['parts'] = options.get('parts', 0)
+    node['content'] = " ".join(class_names)
+    return [node]
+
+def get_graph_hash(node):
+    return md5(node['content'] + str(node['parts'])).hexdigest()[-10:]
+
+def html_output_graph(self, node):
+    """
+    Output the graph for HTML.  This will insert a PNG with clickable
+    image map.
+    """
+    graph = node['graph']
+    parts = node['parts']
+
+    graph_hash = get_graph_hash(node)
+    name = "inheritance%s" % graph_hash
+    path = '_images'
+    dest_path = os.path.join(setup.app.builder.outdir, path)
+    if not os.path.exists(dest_path):
+        os.makedirs(dest_path)
+    png_path = os.path.join(dest_path, name + ".png")
+    path = setup.app.builder.imgpath
+
+    # Create a mapping from fully-qualified class names to URLs.
+    urls = {}
+    for child in node:
+        if child.get('refuri') is not None:
+            urls[child['reftitle']] = child.get('refuri')
+        elif child.get('refid') is not None:
+            urls[child['reftitle']] = '#' + child.get('refid')
+
+    # These arguments to dot will save a PNG file to disk and write
+    # an HTML image map to stdout.
+    image_map = graph.run_dot(['-Tpng', '-o%s' % png_path, '-Tcmapx'],
+                              name, parts, urls)
+    return ('<img src="%s/%s.png" usemap="#%s" class="inheritance"/>%s' %
+            (path, name, name, image_map))
+
+def latex_output_graph(self, node):
+    """
+    Output the graph for LaTeX.  This will insert a PDF.
+    """
+    graph = node['graph']
+    parts = node['parts']
+
+    graph_hash = get_graph_hash(node)
+    name = "inheritance%s" % graph_hash
+    dest_path = os.path.abspath(os.path.join(setup.app.builder.outdir, '_images'))
+    if not os.path.exists(dest_path):
+        os.makedirs(dest_path)
+    pdf_path = os.path.abspath(os.path.join(dest_path, name + ".pdf"))
+
+    graph.run_dot(['-Tpdf', '-o%s' % pdf_path],
+                  name, parts, graph_options={'size': '"6.0,6.0"'})
+    return '\n\\includegraphics{%s}\n\n' % pdf_path
+
+def visit_inheritance_diagram(inner_func):
+    """
+    This is just a wrapper around html/latex_output_graph to make it
+    easier to handle errors and insert warnings.
+    """
+    def visitor(self, node):
+        try:
+            content = inner_func(self, node)
+        except DotException, e:
+            # Insert the exception as a warning in the document
+            warning = self.document.reporter.warning(str(e), line=node.line)
+            warning.parent = node
+            node.children = [warning]
+        else:
+            source = self.document.attributes['source']
+            self.body.append(content)
+            node.children = []
+    return visitor
+
+def do_nothing(self, node):
+    pass
+
+def setup(app):
+    setup.app = app
+    setup.confdir = app.confdir
+
+    app.add_node(
+        inheritance_diagram,
+        latex=(visit_inheritance_diagram(latex_output_graph), do_nothing),
+        html=(visit_inheritance_diagram(html_output_graph), do_nothing))
+    app.add_directive(
+        'inheritance-diagram', inheritance_diagram_directive,
+        False, (1, 100, 0), parts = directives.nonnegative_int)
diff --git a/docs/sphinxext/ipython_console_highlighting.py b/docs/sphinxext/ipython_console_highlighting.py
new file mode 100644
index 0000000..217b779
--- /dev/null
+++ b/docs/sphinxext/ipython_console_highlighting.py
@@ -0,0 +1,114 @@
+"""reST directive for syntax-highlighting ipython interactive sessions.
+
+XXX - See what improvements can be made based on the new (as of Sept 2009)
+'pycon' lexer for the python console.  At the very least it will give better
+highlighted tracebacks.
+"""
+
+#-----------------------------------------------------------------------------
+# Needed modules
+
+# Standard library
+import re
+
+# Third party
+from pygments.lexer import Lexer, do_insertions
+from pygments.lexers.agile import (PythonConsoleLexer, PythonLexer, 
+                                   PythonTracebackLexer)
+from pygments.token import Comment, Generic
+
+from sphinx import highlighting
+
+#-----------------------------------------------------------------------------
+# Global constants
+line_re = re.compile('.*?\n')
+
+#-----------------------------------------------------------------------------
+# Code begins - classes and functions
+
+class IPythonConsoleLexer(Lexer):
+    """
+    For IPython console output or doctests, such as:
+
+    .. sourcecode:: ipython
+
+      In [1]: a = 'foo'
+
+      In [2]: a
+      Out[2]: 'foo'
+
+      In [3]: print a
+      foo
+
+      In [4]: 1 / 0
+
+    Notes:
+
+      - Tracebacks are not currently supported.
+
+      - It assumes the default IPython prompts, not customized ones.
+    """
+    
+    name = 'IPython console session'
+    aliases = ['ipython']
+    mimetypes = ['text/x-ipython-console']
+    input_prompt = re.compile("(In \[[0-9]+\]: )|(   \.\.\.+:)")
+    output_prompt = re.compile("(Out\[[0-9]+\]: )|(   \.\.\.+:)")
+    continue_prompt = re.compile("   \.\.\.+:")
+    tb_start = re.compile("\-+")
+
+    def get_tokens_unprocessed(self, text):
+        pylexer = PythonLexer(**self.options)
+        tblexer = PythonTracebackLexer(**self.options)
+
+        curcode = ''
+        insertions = []
+        for match in line_re.finditer(text):
+            line = match.group()
+            input_prompt = self.input_prompt.match(line)
+            continue_prompt = self.continue_prompt.match(line.rstrip())
+            output_prompt = self.output_prompt.match(line)
+            if line.startswith("#"):
+                insertions.append((len(curcode),
+                                   [(0, Comment, line)]))
+            elif input_prompt is not None:
+                insertions.append((len(curcode),
+                                   [(0, Generic.Prompt, input_prompt.group())]))
+                curcode += line[input_prompt.end():]
+            elif continue_prompt is not None:
+                insertions.append((len(curcode),
+                                   [(0, Generic.Prompt, continue_prompt.group())]))
+                curcode += line[continue_prompt.end():]
+            elif output_prompt is not None:
+                # Use the 'error' token for output.  We should probably make
+                # our own token, but error is typicaly in a bright color like
+                # red, so it works fine for our output prompts.
+                insertions.append((len(curcode),
+                                   [(0, Generic.Error, output_prompt.group())]))
+                curcode += line[output_prompt.end():]
+            else:
+                if curcode:
+                    for item in do_insertions(insertions,
+                                              pylexer.get_tokens_unprocessed(curcode)):
+                        yield item
+                        curcode = ''
+                        insertions = []
+                yield match.start(), Generic.Output, line
+        if curcode:
+            for item in do_insertions(insertions,
+                                      pylexer.get_tokens_unprocessed(curcode)):
+                yield item
+
+
+def setup(app):
+    """Setup as a sphinx extension."""
+
+    # This is only a lexer, so adding it below to pygments appears sufficient.
+    # But if somebody knows that the right API usage should be to do that via
+    # sphinx, by all means fix it here.  At least having this setup.py
+    # suppresses the sphinx warning we'd get without it.
+    pass
+
+#-----------------------------------------------------------------------------
+# Register the extension as a valid pygments lexer
+highlighting.lexers['ipython'] = IPythonConsoleLexer()
diff --git a/docs/sphinxext/numpydoc.py b/docs/sphinxext/numpydoc.py
new file mode 100644
index 0000000..ff6c44c
--- /dev/null
+++ b/docs/sphinxext/numpydoc.py
@@ -0,0 +1,116 @@
+"""
+========
+numpydoc
+========
+
+Sphinx extension that handles docstrings in the Numpy standard format. [1]
+
+It will:
+
+- Convert Parameters etc. sections to field lists.
+- Convert See Also section to a See also entry.
+- Renumber references.
+- Extract the signature from the docstring, if it can't be determined otherwise.
+
+.. [1] http://projects.scipy.org/scipy/numpy/wiki/CodingStyleGuidelines#docstring-standard
+
+"""
+
+import os, re, pydoc
+from docscrape_sphinx import get_doc_object, SphinxDocString
+import inspect
+
+def mangle_docstrings(app, what, name, obj, options, lines,
+                      reference_offset=[0]):
+    if what == 'module':
+        # Strip top title
+        title_re = re.compile(r'^\s*[#*=]{4,}\n[a-z0-9 -]+\n[#*=]{4,}\s*',
+                              re.I|re.S)
+        lines[:] = title_re.sub('', "\n".join(lines)).split("\n")
+    else:
+        doc = get_doc_object(obj, what, "\n".join(lines))
+        lines[:] = str(doc).split("\n")
+
+    if app.config.numpydoc_edit_link and hasattr(obj, '__name__') and \
+           obj.__name__:
+        if hasattr(obj, '__module__'):
+            v = dict(full_name="%s.%s" % (obj.__module__, obj.__name__))
+        else:
+            v = dict(full_name=obj.__name__)
+        lines += ['', '.. htmlonly::', '']
+        lines += ['    %s' % x for x in
+                  (app.config.numpydoc_edit_link % v).split("\n")]
+
+    # replace reference numbers so that there are no duplicates
+    references = []
+    for l in lines:
+        l = l.strip()
+        if l.startswith('.. ['):
+            try:
+                references.append(int(l[len('.. ['):l.index(']')]))
+            except ValueError:
+                print "WARNING: invalid reference in %s docstring" % name
+
+    # Start renaming from the biggest number, otherwise we may
+    # overwrite references.
+    references.sort()
+    if references:
+        for i, line in enumerate(lines):
+            for r in references:
+                new_r = reference_offset[0] + r
+                lines[i] = lines[i].replace('[%d]_' % r,
+                                            '[%d]_' % new_r)
+                lines[i] = lines[i].replace('.. [%d]' % r,
+                                            '.. [%d]' % new_r)
+
+    reference_offset[0] += len(references)
+
+def mangle_signature(app, what, name, obj, options, sig, retann):
+    # Do not try to inspect classes that don't define `__init__`
+    if (inspect.isclass(obj) and
+        'initializes x; see ' in pydoc.getdoc(obj.__init__)):
+        return '', ''
+
+    if not (callable(obj) or hasattr(obj, '__argspec_is_invalid_')): return
+    if not hasattr(obj, '__doc__'): return
+
+    doc = SphinxDocString(pydoc.getdoc(obj))
+    if doc['Signature']:
+        sig = re.sub("^[^(]*", "", doc['Signature'])
+        return sig, ''
+
+def initialize(app):
+    try:
+        app.connect('autodoc-process-signature', mangle_signature)
+    except:
+        monkeypatch_sphinx_ext_autodoc()
+
+def setup(app, get_doc_object_=get_doc_object):
+    global get_doc_object
+    get_doc_object = get_doc_object_
+    
+    app.connect('autodoc-process-docstring', mangle_docstrings)
+    app.connect('builder-inited', initialize)
+    app.add_config_value('numpydoc_edit_link', None, True)
+
+#------------------------------------------------------------------------------
+# Monkeypatch sphinx.ext.autodoc to accept argspecless autodocs (Sphinx < 0.5)
+#------------------------------------------------------------------------------
+
+def monkeypatch_sphinx_ext_autodoc():
+    global _original_format_signature
+    import sphinx.ext.autodoc
+
+    if sphinx.ext.autodoc.format_signature is our_format_signature:
+        return
+
+    print "[numpydoc] Monkeypatching sphinx.ext.autodoc ..."
+    _original_format_signature = sphinx.ext.autodoc.format_signature
+    sphinx.ext.autodoc.format_signature = our_format_signature
+
+def our_format_signature(what, obj):
+    r = mangle_signature(None, what, None, obj, None, None, None)
+    if r is not None:
+        return r[0]
+    else:
+        return _original_format_signature(what, obj)
diff --git a/requirements-dev.txt b/requirements-dev.txt
new file mode 100644
index 0000000..6f1d903
--- /dev/null
+++ b/requirements-dev.txt
@@ -0,0 +1,4 @@
+setuptools >= 0.9.8
+Numpy==1.8.0
+Cython==0.19.2
+descartes==1.0.1
diff --git a/setup.cfg b/setup.cfg
deleted file mode 100644
index 861a9f5..0000000
--- a/setup.cfg
+++ /dev/null
@@ -1,5 +0,0 @@
-[egg_info]
-tag_build = 
-tag_date = 0
-tag_svn_revision = 0
-
diff --git a/setup.py b/setup.py
old mode 100644
new mode 100755
index 328c0b6..413a474
--- a/setup.py
+++ b/setup.py
@@ -1,35 +1,89 @@
-import glob
-import warnings
+#!/usr/bin/env python
 
-try:
-    from distribute_setup import use_setuptools
-    use_setuptools()
-except:
-    warnings.warn(
-    "Failed to import distribute_setup, continuing without distribute.", 
-    Warning)
+from __future__ import print_function
 
+try:
+    # If possible, use setuptools
+    from setuptools import setup
+    from setuptools.extension import Extension
+    from setuptools.command.build_ext import build_ext as distutils_build_ext
+except ImportError:
+    from distutils.core import setup
+    from distutils.extension import Extension
+    from distutils.command.build_ext import build_ext as distutils_build_ext
+from distutils.cmd import Command
 from distutils.errors import CCompilerError, DistutilsExecError, \
     DistutilsPlatformError
-from setuptools.extension import Extension
-from setuptools.command.build_ext import build_ext
-from setuptools import setup, find_packages
-import sys
+import errno
+import glob
+import os
 import platform
+import shutil
+import subprocess
+import sys
 
-readme_text = open('README.rst', 'rb').read()
+class test(Command):
+    """Run unit tests after in-place build"""
+    description = __doc__
+    user_options = []
 
-# Skip the first line of the changes file to get the right header level
-f = open('CHANGES.txt', 'rb')
-f.readline()
-changes_text = f.read()
+    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
+with open('shapely/__init__.py', 'r') as fp:
+    for line in fp:
+        if "__version__" in line:
+            exec(line.replace('_', ''))
+            break
+if version is None:
+    raise ValueError("Could not determine Shapely's version")
+
+with open('VERSION.txt', 'w') as fp:
+    fp.write(version)
+
+with open('README.rst', 'r') as fp:
+    readme_text = fp.read()
+readme_text = readme_text.replace(".. include:: CREDITS.txt", "")
+
+with open('CREDITS.txt', 'r') as fp:
+    credits = fp.read()
+
+with open('CHANGES.txt', 'r') as fp:
+    changes_text = fp.read()
 
 setup_args = dict(
-    metadata_version    = '1.2',
     name                = 'Shapely',
-    version             = '1.2.14',
-    requires_python     = '>=2.5,<3',
-    requires_external   = 'libgeos_c (>=3.1)', 
+    version             = version,
+    requires            = ['Python (>=2.6)', 'libgeos_c (>=3.1)'],
     description         = 'Geometric objects, predicates, and operations',
     license             = 'BSD',
     keywords            = 'geometry topology gis',
@@ -37,97 +91,136 @@ setup_args = dict(
     author_email        = 'sean.gillies at gmail.com',
     maintainer          = 'Sean Gillies',
     maintainer_email    = 'sean.gillies at gmail.com',
-    url                 = 'https://github.com/sgillies/shapely',
-    long_description    = readme_text + "\n" + changes_text,
-    packages            = find_packages(),
-    test_suite          = 'shapely.tests.test_suite',
+    url                 = 'https://github.com/Toblerity/Shapely',
+    long_description    = readme_text + "\n" + credits + "\n" + changes_text,
+    packages            = [
+        'shapely',
+        'shapely.geometry',
+        'shapely.algorithms',
+        'shapely.examples',
+        'shapely.speedups',
+        'shapely.tests',
+    ],
+    cmdclass            = {'test': test},
     classifiers         = [
         'Development Status :: 5 - Production/Stable',
         'Intended Audience :: Developers',
         'Intended Audience :: Science/Research',
         'License :: OSI Approved :: BSD License',
         'Operating System :: OS Independent',
-        'Programming Language :: Python',
+        'Programming Language :: Python :: 2.6',
+        'Programming Language :: Python :: 2.7',
+        'Programming Language :: Python :: 3',
         'Topic :: Scientific/Engineering :: GIS',
-        ],
-    )
+    ],
+)
 
 # Add DLLs for Windows
 if sys.platform == 'win32':
+    try:
+        os.mkdir('shapely/DLLs')
+    except OSError as ex:
+        if ex.errno != errno.EEXIST:
+            raise
     if '(AMD64)' in sys.version:
-        setup_args.update(
-            data_files=[('DLLs', glob.glob('DLLs_AMD64_VC9/*.dll'))]
-            )
-    elif platform.python_version().startswith('2.5.'):
-        setup_args.update(
-            data_files=[('DLLs', glob.glob('DLLs_x86_VC7/*.dll'))]
-            )       
+        for dll in glob.glob('DLLs_AMD64_VC9/*.dll'):
+            shutil.copy(dll, 'shapely/DLLs')
+    elif sys.version_info[0:2] == (2, 5):
+        for dll in glob.glob('DLLs_x86_VC7/*.dll'):
+            shutil.copy(dll, 'shapely/DLLs')
     else:
-        setup_args.update(
-            data_files=[('DLLs', glob.glob('DLLs_x86_VC9/*.dll'))]
-            )
+        for dll in glob.glob('DLLs_x86_VC9/*.dll'):
+            shutil.copy(dll, 'shapely/DLLs')
+    setup_args.update(
+        package_data={'shapely': ['shapely/DLLs/*.dll']},
+        include_package_data=True,
+    )
 
 
 # Optional compilation of speedups
 # setuptools stuff from Bob Ippolito's simplejson project
 if sys.platform == 'win32' and sys.version_info > (2, 6):
-   # 2.6's distutils.msvc9compiler can raise an IOError when failing to
-   # find the compiler
-   ext_errors = (CCompilerError, DistutilsExecError, DistutilsPlatformError,
-                 IOError)
+    # 2.6's distutils.msvc9compiler can raise an IOError when failing to
+    # find the compiler
+    ext_errors = (CCompilerError, DistutilsExecError, DistutilsPlatformError,
+                  IOError)
 else:
-   ext_errors = (CCompilerError, DistutilsExecError, DistutilsPlatformError)
+    ext_errors = (CCompilerError, DistutilsExecError, DistutilsPlatformError)
+
 
 class BuildFailed(Exception):
     pass
 
-class ve_build_ext(build_ext):
+
+class build_ext(distutils_build_ext):
     # This class allows C extension building to fail.
 
     def run(self):
         try:
-            build_ext.run(self)
-        except DistutilsPlatformError, x:
+            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, x:
+            distutils_build_ext.build_extension(self, ext)
+        except ext_errors as x:
             raise BuildFailed(x)
 
-if sys.platform == 'win32':
-    # geos DLL is geos.dll instead of geos_c.dll on Windows
-    ext_modules = [
-        Extension("shapely.speedups._speedups",
-              ["shapely/speedups/_speedups.c"], libraries=['geos']),
-    ]
-elif (hasattr(platform, 'python_implementation') 
-    and platform.python_implementation() == 'PyPy'):
-    # python_implementation >= 2.6
+if (hasattr(platform, 'python_implementation')
+        and platform.python_implementation() == 'PyPy'):
+    # python_implementation is only available since 2.6
     ext_modules = []
+    libraries = []
+elif sys.platform == 'win32':
+    libraries = ['geos']
 else:
-    ext_modules = [
-        Extension("shapely.speedups._speedups", 
-              ["shapely/speedups/_speedups.c"], libraries=['geos_c']),
-    ]
+    libraries = ['geos_c']
+
+if os.path.exists("MANIFEST.in"):
+    pyx_file = "shapely/speedups/_speedups.pyx"
+    c_file = "shapely/speedups/_speedups.c"
+
+    force_cython = False
+    if 'sdist' in sys.argv:
+        force_cython = True
+
+    try:
+        if (force_cython or not os.path.exists(c_file)
+                or os.path.getmtime(pyx_file) > os.path.getmtime(c_file)):
+            print("Updating C extension with Cython.", file=sys.stderr)
+            subprocess.check_call(["cython", "shapely/speedups/_speedups.pyx"])
+    except (subprocess.CalledProcessError, OSError):
+        print("Warning: Could not (re)create C extension with Cython.",
+              file=sys.stderr)
+        if force_cython:
+            raise
+    if not os.path.exists("shapely/speedups/_speedups.c"):
+        print("Warning: speedup extension not found", file=sys.stderr)
+
+ext_modules = [
+    Extension(
+        "shapely.speedups._speedups",
+        ["shapely/speedups/_speedups.c"],
+        libraries=libraries)
+]
 
 try:
     # try building with speedups
+    setup_args['cmdclass']['build_ext'] = build_ext
     setup(
-        cmdclass={'build_ext': ve_build_ext},
         ext_modules=ext_modules,
         **setup_args
     )
-except BuildFailed, ex:
-    BUILD_EXT_WARNING = "Warning: The C extension could not be compiled, speedups are not enabled."
-    print ex
-    print BUILD_EXT_WARNING
-    print "Failure information, if any, is above."
-    print "I'm retrying the build without the C extension now."
+except BuildFailed as ex:
+    BUILD_EXT_WARNING = "Warning: The C extension could not be compiled, " \
+                        "speedups are not enabled."
+    print(ex)
+    print(BUILD_EXT_WARNING)
+    print("Failure information, if any, is above.")
+    print("I'm retrying the build without the C extension now.")
 
     setup(**setup_args)
 
-    print BUILD_EXT_WARNING
-    print "Plain-Python installation succeeded."
-
+    print(BUILD_EXT_WARNING)
+    print("Plain-Python installation succeeded.")
diff --git a/shapely/__init__.py b/shapely/__init__.py
index a6df0ba..67bc602 100644
--- a/shapely/__init__.py
+++ b/shapely/__init__.py
@@ -1,2 +1 @@
-__version__ = "1.2.14"
-
+__version__ = "1.3.0"
diff --git a/shapely/affinity.py b/shapely/affinity.py
new file mode 100755
index 0000000..794bb30
--- /dev/null
+++ b/shapely/affinity.py
@@ -0,0 +1,255 @@
+"""Affine transforms, both in general and specific, named transforms."""
+
+from math import sin, cos, tan, pi
+
+__all__ = ['affine_transform', 'rotate', 'scale', 'skew', 'translate']
+
+
+def affine_transform(geom, matrix):
+    """Returns a transformed geometry using an affine transformation matrix.
+
+    The coefficient matrix is provided as a list or tuple with 6 or 12 items
+    for 2D or 3D transformations, respectively.
+
+    For 2D affine transformations, the 6 parameter matrix is:
+
+        [a, b, d, e, xoff, yoff]
+
+    which represents the augmented matrix:
+
+                            / a  b xoff \ 
+        [x' y' 1] = [x y 1] | d  e yoff |
+                            \ 0  0   1  /
+
+    or the equations for the transformed coordinates:
+
+        x' = a * x + b * y + xoff
+        y' = d * x + e * y + yoff
+
+    For 3D affine transformations, the 12 parameter matrix is:
+
+        [a, b, c, d, e, f, g, h, i, xoff, yoff, zoff]
+
+    which represents the augmented matrix:
+
+                                 / a  b  c xoff \ 
+        [x' y' z' 1] = [x y z 1] | d  e  f yoff |
+                                 | g  h  i zoff |
+                                 \ 0  0  0   1  /
+
+    or the equations for the transformed coordinates:
+
+        x' = a * x + b * y + c * z + xoff
+        y' = d * x + e * y + f * z + yoff
+        z' = g * x + h * y + i * z + zoff
+    """
+    if geom.is_empty:
+        return geom
+    if len(matrix) == 6:
+        ndim = 2
+        a, b, d, e, xoff, yoff = matrix
+        if geom.has_z:
+            ndim = 3
+            i = 1.0
+            c = f = g = h = zoff = 0.0
+            matrix = a, b, c, d, e, f, g, h, i, xoff, yoff, zoff
+    elif len(matrix) == 12:
+        ndim = 3
+        a, b, c, d, e, f, g, h, i, xoff, yoff, zoff = matrix
+        if not geom.has_z:
+            ndim = 2
+            matrix = a, b, d, e, xoff, yoff
+    else:
+        raise ValueError("'matrix' expects either 6 or 12 coefficients")
+
+    def affine_pts(pts):
+        """Internal function to yield affine transform of coordinate tuples"""
+        if ndim == 2:
+            for x, y in pts:
+                xp = a * x + b * y + xoff
+                yp = d * x + e * y + yoff
+                yield (xp, yp)
+        elif ndim == 3:
+            for x, y, z in pts:
+                xp = a * x + b * y + c * z + xoff
+                yp = d * x + e * y + f * z + yoff
+                zp = g * x + h * y + i * z + zoff
+                yield (xp, yp, zp)
+
+    # Process coordinates from each supported geometry type
+    if geom.type in ('Point', 'LineString'):
+        return type(geom)(list(affine_pts(geom.coords)))
+    elif geom.type == 'Polygon':
+        ring = geom.exterior
+        shell = type(ring)(list(affine_pts(ring.coords)))
+        holes = list(geom.interiors)
+        for pos, ring in enumerate(holes):
+            holes[pos] = type(ring)(list(affine_pts(ring.coords)))
+        return type(geom)(shell, holes)
+    elif geom.type.startswith('Multi') or geom.type == 'GeometryCollection':
+        # Recursive call
+        # TODO: fix GeometryCollection constructor
+        return type(geom)([affine_transform(part, matrix)
+                           for part in geom.geoms])
+    else:
+        raise ValueError('Type %r not recognized' % geom.type)
+
+
+def interpret_origin(geom, origin, ndim):
+    """Returns interpreted coordinate tuple for origin parameter.
+
+    This is a helper function for other transform functions.
+
+    The point of origin can be a keyword 'center' for the 2D bounding box
+    center, 'centroid' for the geometry's 2D centroid, a Point object or a
+    coordinate tuple (x0, y0, z0).
+    """
+    # get coordinate tuple from 'origin' from keyword or Point type
+    if origin == 'center':
+        # bounding box center
+        minx, miny, maxx, maxy = geom.bounds
+        origin = ((maxx + minx)/2.0, (maxy + miny)/2.0)
+    elif origin == 'centroid':
+        origin = geom.centroid.coords[0]
+    elif isinstance(origin, str):
+        raise ValueError("'origin' keyword %r is not recognized" % origin)
+    elif hasattr(origin, 'type') and origin.type == 'Point':
+        origin = origin.coords[0]
+
+    # origin should now be tuple-like
+    if len(origin) not in (2, 3):
+        raise ValueError("Expected number of items in 'origin' to be "
+                         "either 2 or 3")
+    if ndim == 2:
+        return origin[0:2]
+    else:  # 3D coordinate
+        if len(origin) == 2:
+            return origin + (0.0,)
+        else:
+            return origin
+
+
+def rotate(geom, angle, origin='center', use_radians=False):
+    """Returns a rotated geometry on a 2D plane.
+
+    The angle of rotation can be specified in either degrees (default) or
+    radians by setting ``use_radians=True``. Positive angles are
+    counter-clockwise and negative are clockwise rotations.
+
+    The point of origin can be a keyword 'center' for the bounding box
+    center (default), 'centroid' for the geometry's centroid, a Point object
+    or a coordinate tuple (x0, y0).
+
+    The affine transformation matrix for 2D rotation is:
+
+      / cos(r) -sin(r) xoff \ 
+      | sin(r)  cos(r) yoff |
+      \   0       0      1  /
+
+    where the offsets are calculated from the origin Point(x0, y0):
+
+        xoff = x0 - x0 * cos(r) + y0 * sin(r)
+        yoff = y0 - x0 * sin(r) - y0 * cos(r)
+    """
+    if not use_radians:  # convert from degrees
+        angle *= pi/180.0
+    cosp = cos(angle)
+    sinp = sin(angle)
+    if abs(cosp) < 2.5e-16:
+        cosp = 0.0
+    if abs(sinp) < 2.5e-16:
+        sinp = 0.0
+    x0, y0 = interpret_origin(geom, origin, 2)
+
+    matrix = (cosp, -sinp, 0.0,
+              sinp,  cosp, 0.0,
+              0.0,    0.0, 1.0,
+              x0 - x0 * cosp + y0 * sinp, y0 - x0 * sinp - y0 * cosp, 0.0)
+    return affine_transform(geom, matrix)
+
+
+def scale(geom, xfact=1.0, yfact=1.0, zfact=1.0, origin='center'):
+    """Returns a scaled geometry, scaled by factors along each dimension.
+
+    The point of origin can be a keyword 'center' for the 2D bounding box
+    center (default), 'centroid' for the geometry's 2D centroid, a Point
+    object or a coordinate tuple (x0, y0, z0).
+
+    Negative scale factors will mirror or reflect coordinates.
+
+    The general 3D affine transformation matrix for scaling is:
+
+        / xfact  0    0   xoff \ 
+        |   0  yfact  0   yoff |
+        |   0    0  zfact zoff |
+        \   0    0    0     1  /
+
+    where the offsets are calculated from the origin Point(x0, y0, z0):
+
+        xoff = x0 - x0 * xfact
+        yoff = y0 - y0 * yfact
+        zoff = z0 - z0 * zfact
+    """
+    x0, y0, z0 = interpret_origin(geom, origin, 3)
+
+    matrix = (xfact, 0.0, 0.0,
+              0.0, yfact, 0.0,
+              0.0, 0.0, zfact,
+              x0 - x0 * xfact, y0 - y0 * yfact, z0 - z0 * zfact)
+    return affine_transform(geom, matrix)
+
+
+def skew(geom, xs=0.0, ys=0.0, origin='center', use_radians=False):
+    """Returns a skewed geometry, sheared by angles along x and y dimensions.
+
+    The shear angle can be specified in either degrees (default) or radians
+    by setting ``use_radians=True``.
+
+    The point of origin can be a keyword 'center' for the bounding box
+    center (default), 'centroid' for the geometry's centroid, a Point object
+    or a coordinate tuple (x0, y0).
+
+    The general 2D affine transformation matrix for skewing is:
+
+        /   1    tan(xs) xoff \ 
+        | tan(ys)  1     yoff |
+        \   0      0       1  /
+
+    where the offsets are calculated from the origin Point(x0, y0):
+
+        xoff = -y0 * tan(xs)
+        yoff = -x0 * tan(ys)
+    """
+    if not use_radians:  # convert from degrees
+        xs *= pi/180.0
+        ys *= pi/180.0
+    tanx = tan(xs)
+    tany = tan(ys)
+    if abs(tanx) < 2.5e-16:
+        tanx = 0.0
+    if abs(tany) < 2.5e-16:
+        tany = 0.0
+    x0, y0 = interpret_origin(geom, origin, 2)
+
+    matrix = (1.0, tanx, 0.0,
+              tany, 1.0, 0.0,
+              0.0,  0.0, 1.0,
+              -y0 * tanx, -x0 * tany, 0.0)
+    return affine_transform(geom, matrix)
+
+
+def translate(geom, xoff=0.0, yoff=0.0, zoff=0.0):
+    """Returns a translated geometry shifted by offsets along each dimension.
+
+    The general 3D affine transformation matrix for translation is:
+
+        / 1  0  0 xoff \ 
+        | 0  1  0 yoff |
+        | 0  0  1 zoff |
+        \ 0  0  0   1  /
+    """
+    matrix = (1.0, 0.0, 0.0,
+              0.0, 1.0, 0.0,
+              0.0, 0.0, 1.0,
+              xoff, yoff, zoff)
+    return affine_transform(geom, matrix)
diff --git a/shapely/algorithms/cga.py b/shapely/algorithms/cga.py
index 040e376..3b8d925 100644
--- a/shapely/algorithms/cga.py
+++ b/shapely/algorithms/cga.py
@@ -1,4 +1,3 @@
-from itertools import islice, izip
 
 def signed_area(ring):
     """Return the signed area enclosed by a ring in linear time using the 
diff --git a/shapely/coords.py b/shapely/coords.py
index 505d67e..b2c5dda 100644
--- a/shapely/coords.py
+++ b/shapely/coords.py
@@ -1,13 +1,31 @@
 """Coordinate sequence utilities
 """
 
+import sys
 from array import array
 from ctypes import byref, c_double, c_uint
-import sys
 
 from shapely.geos import lgeos
 from shapely.topology import Validating
 
+if sys.version_info[0] < 3:
+    range = xrange
+
+try:
+    import numpy
+    has_numpy = True
+except ImportError:
+    has_numpy = False
+
+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"])
+    else:
+        return ob
+
 
 class CoordinateSequence(object):
     """
@@ -54,7 +72,7 @@ class CoordinateSequence(object):
         dy = c_double()
         dz = c_double()
         has_z = self._ndim == 3
-        for i in xrange(self.__len__()):
+        for i in range(self.__len__()):
             lgeos.GEOSCoordSeq_getX(self._cseq, i, byref(dx))
             lgeos.GEOSCoordSeq_getY(self._cseq, i, byref(dy))
             if has_z:
@@ -87,7 +105,7 @@ class CoordinateSequence(object):
         elif isinstance(key, slice):
             res = []
             start, stop, stride = key.indices(m)
-            for i in xrange(start, stop, stride):
+            for i in range(start, stop, stride):
                 lgeos.GEOSCoordSeq_getX(self._cseq, i, byref(dx))
                 lgeos.GEOSCoordSeq_getY(self._cseq, i, byref(dy))
                 if has_z:
@@ -108,7 +126,7 @@ class CoordinateSequence(object):
         array_type = c_double * (m * n)
         data = array_type()
         temp = c_double()
-        for i in xrange(m):
+        for i in range(m):
             lgeos.GEOSCoordSeq_getX(self._cseq, i, byref(temp))
             data[n*i] = temp.value
             lgeos.GEOSCoordSeq_getY(self._cseq, i, byref(temp))
@@ -145,7 +163,7 @@ class CoordinateSequence(object):
         x = array('d')
         y = array('d')
         temp = c_double()
-        for i in xrange(m):
+        for i in range(m):
             lgeos.GEOSCoordSeq_getX(self._cseq, i, byref(temp))
             x.append(temp.value)
             lgeos.GEOSCoordSeq_getY(self._cseq, i, byref(temp))
@@ -171,7 +189,7 @@ class BoundsOp(Validating):
         miny = 1.e+20
         maxy = -1e+20
         temp = c_double()
-        for i in xrange(cs_len.value):
+        for i in range(cs_len.value):
             lgeos.GEOSCoordSeq_getX(cs, i, byref(temp))
             x = temp.value
             if x < minx: minx = x
diff --git a/shapely/ctypes_declarations.py b/shapely/ctypes_declarations.py
index 3039945..c41e922 100644
--- a/shapely/ctypes_declarations.py
+++ b/shapely/ctypes_declarations.py
@@ -1,284 +1,455 @@
-# Prototyping of libgeos_c functions, now using a function written by
-# `tartley`: http://trac.gispython.org/lab/ticket/189
+'''Prototyping of the GEOS C API
 
-import ctypes
+See header file: geos-x.y.z/capi/geos_c.h
+'''
 
-class allocated_c_char_p(ctypes.c_char_p):
+from ctypes import CFUNCTYPE, POINTER, c_void_p, c_char_p, \
+    c_size_t, c_byte, c_char, c_uint, c_int, c_double
+
+# Derived pointer types
+c_size_t_p = POINTER(c_size_t)
+
+
+class allocated_c_char_p(c_char_p):
+    '''char pointer return type'''
     pass
 
-EXCEPTION_HANDLER_FUNCTYPE = ctypes.CFUNCTYPE(None, ctypes.c_char_p, ctypes.c_char_p)
+EXCEPTION_HANDLER_FUNCTYPE = CFUNCTYPE(None, c_char_p, c_char_p)
 
-def prototype(lgeos, geosVersion):
 
-    lgeos.initGEOS.argtypes = [EXCEPTION_HANDLER_FUNCTYPE, EXCEPTION_HANDLER_FUNCTYPE]
+def prototype(lgeos, geos_version):
+    '''Protype functions in geos_c.h for different version of GEOS
+
+    Use the GEOS version, not the C API version.
+    '''
+
+    '''
+    Initialization, cleanup, version
+    '''
+
     lgeos.initGEOS.restype = None
+    lgeos.initGEOS.argtypes = [EXCEPTION_HANDLER_FUNCTYPE, EXCEPTION_HANDLER_FUNCTYPE]
 
-    lgeos.finishGEOS.argtypes = []
     lgeos.finishGEOS.restype = None
+    lgeos.finishGEOS.argtypes = []
 
+    lgeos.GEOSversion.restype = c_char_p
     lgeos.GEOSversion.argtypes = []
-    lgeos.GEOSversion.restype = ctypes.c_char_p
 
-    lgeos.GEOSGeomFromWKT.restype = ctypes.c_void_p
-    lgeos.GEOSGeomFromWKT.argtypes = [ctypes.c_char_p]
+    '''
+    NOTE - These functions are DEPRECATED.  Please use the new Reader and
+    writer APIS!
+    '''
+
+    lgeos.GEOSGeomFromWKT.restype = c_void_p
+    lgeos.GEOSGeomFromWKT.argtypes = [c_char_p]
 
     lgeos.GEOSGeomToWKT.restype = allocated_c_char_p
-    lgeos.GEOSGeomToWKT.argtypes = [ctypes.c_void_p]
+    lgeos.GEOSGeomToWKT.argtypes = [c_void_p]
 
-    lgeos.GEOS_setWKBOutputDims.restype = ctypes.c_int
-    lgeos.GEOS_setWKBOutputDims.argtypes = [ctypes.c_int]
+    lgeos.GEOS_setWKBOutputDims.restype = c_int
+    lgeos.GEOS_setWKBOutputDims.argtypes = [c_int]
 
-    lgeos.GEOSGeomFromWKB_buf.restype = ctypes.c_void_p
-    lgeos.GEOSGeomFromWKB_buf.argtypes = [ctypes.c_void_p, ctypes.c_size_t]
+    lgeos.GEOSGeomFromWKB_buf.restype = c_void_p
+    lgeos.GEOSGeomFromWKB_buf.argtypes = [c_void_p, c_size_t]
 
     lgeos.GEOSGeomToWKB_buf.restype = allocated_c_char_p
-    lgeos.GEOSGeomToWKB_buf.argtypes = [ctypes.c_void_p , ctypes.POINTER(ctypes.c_size_t)]
+    lgeos.GEOSGeomToWKB_buf.argtypes = [c_void_p, c_size_t_p]
+
+    '''
+    Coordinate sequence
+    '''
 
-    lgeos.GEOSCoordSeq_create.restype = ctypes.c_void_p
-    lgeos.GEOSCoordSeq_create.argtypes = [ctypes.c_uint, ctypes.c_uint]
+    lgeos.GEOSCoordSeq_create.restype = c_void_p
+    lgeos.GEOSCoordSeq_create.argtypes = [c_uint, c_uint]
 
-    lgeos.GEOSCoordSeq_clone.restype = ctypes.c_void_p
-    lgeos.GEOSCoordSeq_clone.argtypes = [ctypes.c_void_p]
+    lgeos.GEOSCoordSeq_clone.restype = c_void_p
+    lgeos.GEOSCoordSeq_clone.argtypes = [c_void_p]
 
     lgeos.GEOSCoordSeq_destroy.restype = None
-    lgeos.GEOSCoordSeq_destroy.argtypes = [ctypes.c_void_p]
+    lgeos.GEOSCoordSeq_destroy.argtypes = [c_void_p]
+
+    lgeos.GEOSCoordSeq_setX.restype = c_int
+    lgeos.GEOSCoordSeq_setX.argtypes = [c_void_p, c_uint, c_double]
+
+    lgeos.GEOSCoordSeq_setY.restype = c_int
+    lgeos.GEOSCoordSeq_setY.argtypes = [c_void_p, c_uint, c_double]
+
+    lgeos.GEOSCoordSeq_setZ.restype = c_int
+    lgeos.GEOSCoordSeq_setZ.argtypes = [c_void_p, c_uint, c_double]
+
+    lgeos.GEOSCoordSeq_setOrdinate.restype = c_int
+    lgeos.GEOSCoordSeq_setOrdinate.argtypes = [c_void_p, c_uint, c_uint, c_double]
 
-    lgeos.GEOSCoordSeq_setX.restype = ctypes.c_int
-    lgeos.GEOSCoordSeq_setX.argtypes = [ctypes.c_void_p, ctypes.c_uint, ctypes.c_double]
+    lgeos.GEOSCoordSeq_getX.restype = c_int
+    lgeos.GEOSCoordSeq_getX.argtypes = [c_void_p, c_uint, c_void_p]
 
-    lgeos.GEOSCoordSeq_setY.restype = ctypes.c_int
-    lgeos.GEOSCoordSeq_setY.argtypes = [ctypes.c_void_p, ctypes.c_uint, ctypes.c_double]
+    lgeos.GEOSCoordSeq_getY.restype = c_int
+    lgeos.GEOSCoordSeq_getY.argtypes = [c_void_p, c_uint, c_void_p]
 
-    lgeos.GEOSCoordSeq_setZ.restype = ctypes.c_int
-    lgeos.GEOSCoordSeq_setZ.argtypes = [ctypes.c_void_p, ctypes.c_uint, ctypes.c_double]
+    lgeos.GEOSCoordSeq_getZ.restype = c_int
+    lgeos.GEOSCoordSeq_getZ.argtypes = [c_void_p, c_uint, c_void_p]
 
-    lgeos.GEOSCoordSeq_setOrdinate.restype = ctypes.c_int
-    lgeos.GEOSCoordSeq_setOrdinate.argtypes = [ctypes.c_void_p, ctypes.c_uint, ctypes.c_uint, ctypes.c_double]
+    lgeos.GEOSCoordSeq_getSize.restype = c_int
+    lgeos.GEOSCoordSeq_getSize.argtypes = [c_void_p, c_void_p]
 
-    lgeos.GEOSCoordSeq_getX.restype = ctypes.c_int
-    lgeos.GEOSCoordSeq_getX.argtypes = [ctypes.c_void_p, ctypes.c_uint, ctypes.c_void_p]
+    lgeos.GEOSCoordSeq_getDimensions.restype = c_int
+    lgeos.GEOSCoordSeq_getDimensions.argtypes = [c_void_p, c_void_p]
 
-    lgeos.GEOSCoordSeq_getY.restype = ctypes.c_int
-    lgeos.GEOSCoordSeq_getY.argtypes = [ctypes.c_void_p, ctypes.c_uint, ctypes.c_void_p]
+    '''
+    Linear refeferencing
+    '''
 
-    lgeos.GEOSCoordSeq_getZ.restype = ctypes.c_int
-    lgeos.GEOSCoordSeq_getZ.argtypes = [ctypes.c_void_p, ctypes.c_uint, ctypes.c_void_p]
+    if geos_version >= (3, 2, 0):
 
-    lgeos.GEOSCoordSeq_getSize.restype = ctypes.c_int
-    lgeos.GEOSCoordSeq_getSize.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
+        lgeos.GEOSProject.restype = c_double
+        lgeos.GEOSProject.argtypes = [c_void_p, c_void_p]
 
-    lgeos.GEOSCoordSeq_getDimensions.restype = ctypes.c_int
-    lgeos.GEOSCoordSeq_getDimensions.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
+        lgeos.GEOSInterpolate.restype = c_void_p
+        lgeos.GEOSInterpolate.argtypes = [c_void_p, c_double]
 
-    lgeos.GEOSGeom_createPoint.restype = ctypes.c_void_p
-    lgeos.GEOSGeom_createPoint.argtypes = [ctypes.c_void_p]
+        lgeos.GEOSProjectNormalized.restype = c_double
+        lgeos.GEOSProjectNormalized.argtypes = [c_void_p, c_void_p]
 
-    lgeos.GEOSGeom_createLinearRing.restype = ctypes.c_void_p
-    lgeos.GEOSGeom_createLinearRing.argtypes = [ctypes.c_void_p]
+        lgeos.GEOSInterpolateNormalized.restype = c_void_p
+        lgeos.GEOSInterpolateNormalized.argtypes = [c_void_p, c_double]
 
-    lgeos.GEOSGeom_createLineString.restype = ctypes.c_void_p
-    lgeos.GEOSGeom_createLineString.argtypes = [ctypes.c_void_p]
+    '''
+    Buffer related
+    '''
 
-    lgeos.GEOSGeom_createPolygon.restype = ctypes.c_void_p
-    lgeos.GEOSGeom_createPolygon.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_uint]
+    lgeos.GEOSBuffer.restype = c_void_p
+    lgeos.GEOSBuffer.argtypes = [c_void_p, c_double, c_int]
 
-    lgeos.GEOSGeom_createCollection.restype = ctypes.c_void_p
-    lgeos.GEOSGeom_createCollection.argtypes = [ctypes.c_int, ctypes.c_void_p, ctypes.c_uint]
+    if geos_version >= (3, 2, 0):
 
-    lgeos.GEOSGeom_clone.restype = ctypes.c_void_p
-    lgeos.GEOSGeom_clone.argtypes = [ctypes.c_void_p]
+        lgeos.GEOSBufferWithStyle.restype = c_void_p
+        lgeos.GEOSBufferWithStyle.argtypes = [c_void_p, c_double, c_int, c_int, c_int, c_double]
+
+        lgeos.GEOSSingleSidedBuffer.restype = c_void_p
+        lgeos.GEOSSingleSidedBuffer.argtypes = [c_void_p, c_double, c_int, c_int, c_double, c_int]
+
+    '''
+    Geometry constructors
+    '''
+
+    lgeos.GEOSGeom_createPoint.restype = c_void_p
+    lgeos.GEOSGeom_createPoint.argtypes = [c_void_p]
+
+    lgeos.GEOSGeom_createLinearRing.restype = c_void_p
+    lgeos.GEOSGeom_createLinearRing.argtypes = [c_void_p]
+
+    lgeos.GEOSGeom_createLineString.restype = c_void_p
+    lgeos.GEOSGeom_createLineString.argtypes = [c_void_p]
+
+    lgeos.GEOSGeom_createPolygon.restype = c_void_p
+    lgeos.GEOSGeom_createPolygon.argtypes = [c_void_p, c_void_p, c_uint]
+
+    lgeos.GEOSGeom_createCollection.restype = c_void_p
+    lgeos.GEOSGeom_createCollection.argtypes = [c_int, c_void_p, c_uint]
+
+    lgeos.GEOSGeom_clone.restype = c_void_p
+    lgeos.GEOSGeom_clone.argtypes = [c_void_p]
+
+    '''
+    Memory management
+    '''
 
     lgeos.GEOSGeom_destroy.restype = None
-    lgeos.GEOSGeom_destroy.argtypes = [ctypes.c_void_p]
+    lgeos.GEOSGeom_destroy.argtypes = [c_void_p]
 
-    lgeos.GEOSEnvelope.restype = ctypes.c_void_p
-    lgeos.GEOSEnvelope.argtypes = [ctypes.c_void_p]
+    '''
+    Topology operations
+    Return NULL on exception
+    '''
 
-    lgeos.GEOSIntersection.restype = ctypes.c_void_p
-    lgeos.GEOSIntersection.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
+    lgeos.GEOSEnvelope.restype = c_void_p
+    lgeos.GEOSEnvelope.argtypes = [c_void_p]
 
-    lgeos.GEOSBuffer.restype = ctypes.c_void_p
-    lgeos.GEOSBuffer.argtypes = [ctypes.c_void_p, ctypes.c_double, ctypes.c_int]
+    lgeos.GEOSIntersection.restype = c_void_p
+    lgeos.GEOSIntersection.argtypes = [c_void_p, c_void_p]
 
-    lgeos.GEOSSimplify.restype = ctypes.c_void_p
-    lgeos.GEOSSimplify.argtypes = [ctypes.c_void_p, ctypes.c_double]
+    lgeos.GEOSConvexHull.restype = c_void_p
+    lgeos.GEOSConvexHull.argtypes = [c_void_p]
 
-    lgeos.GEOSTopologyPreserveSimplify.restype = ctypes.c_void_p
-    lgeos.GEOSTopologyPreserveSimplify.argtypes = [ctypes.c_void_p, ctypes.c_double]
+    lgeos.GEOSDifference.restype = c_void_p
+    lgeos.GEOSDifference.argtypes = [c_void_p, c_void_p]
 
-    lgeos.GEOSConvexHull.restype = ctypes.c_void_p
-    lgeos.GEOSConvexHull.argtypes = [ctypes.c_void_p]
+    lgeos.GEOSSymDifference.restype = c_void_p
+    lgeos.GEOSSymDifference.argtypes = [c_void_p, c_void_p]
 
-    lgeos.GEOSDifference.restype = ctypes.c_void_p
-    lgeos.GEOSDifference.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
+    lgeos.GEOSBoundary.restype = c_void_p
+    lgeos.GEOSBoundary.argtypes = [c_void_p]
 
-    lgeos.GEOSSymDifference.restype = ctypes.c_void_p
-    lgeos.GEOSSymDifference.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
+    lgeos.GEOSUnion.restype = c_void_p
+    lgeos.GEOSUnion.argtypes = [c_void_p, c_void_p]
 
-    lgeos.GEOSBoundary.restype = ctypes.c_void_p
-    lgeos.GEOSBoundary.argtypes = [ctypes.c_void_p]
+    if geos_version >= (3, 3, 0):
+        lgeos.GEOSUnaryUnion.restype = c_void_p
+        lgeos.GEOSUnaryUnion.argtypes = [c_void_p]
 
-    lgeos.GEOSUnion.restype = ctypes.c_void_p
-    lgeos.GEOSUnion.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
+    if geos_version >= (3, 1, 0):
+        '''deprecated in 3.3.0: use GEOSUnaryUnion instead'''
+        lgeos.GEOSUnionCascaded.restype = c_void_p
+        lgeos.GEOSUnionCascaded.argtypes = [c_void_p]
 
-    lgeos.GEOSPointOnSurface.restype = ctypes.c_void_p
-    lgeos.GEOSPointOnSurface.argtypes = [ctypes.c_void_p]
+    lgeos.GEOSPointOnSurface.restype = c_void_p
+    lgeos.GEOSPointOnSurface.argtypes = [c_void_p]
 
-    lgeos.GEOSGetCentroid.restype = ctypes.c_void_p
-    lgeos.GEOSGetCentroid.argtypes = [ctypes.c_void_p]
+    lgeos.GEOSGetCentroid.restype = c_void_p
+    lgeos.GEOSGetCentroid.argtypes = [c_void_p]
 
-    lgeos.GEOSRelate.restype = allocated_c_char_p
-    lgeos.GEOSRelate.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
+    lgeos.GEOSPolygonize.restype = c_void_p
+    lgeos.GEOSPolygonize.argtypes = [c_void_p, c_uint]
+
+    if geos_version >= (3, 3, 0):
+        lgeos.GEOSPolygonize_full.restype = c_void_p
+        lgeos.GEOSPolygonize_full.argtypes = [c_void_p, c_void_p, c_void_p, c_void_p]
+
+    lgeos.GEOSLineMerge.restype = c_void_p
+    lgeos.GEOSLineMerge.argtypes = [c_void_p]
+
+    lgeos.GEOSSimplify.restype = c_void_p
+    lgeos.GEOSSimplify.argtypes = [c_void_p, c_double]
+
+    lgeos.GEOSTopologyPreserveSimplify.restype = c_void_p
+    lgeos.GEOSTopologyPreserveSimplify.argtypes = [c_void_p, c_double]
+
+    '''
+    Binary predicates
+    Return 2 on exception, 1 on true, 0 on false
+    '''
 
-    lgeos.GEOSPolygonize.restype = ctypes.c_void_p
-    lgeos.GEOSPolygonize.argtypes = [ctypes.c_void_p, ctypes.c_uint]
+    lgeos.GEOSDisjoint.restype = c_byte
+    lgeos.GEOSDisjoint.argtypes = [c_void_p, c_void_p]
 
-    lgeos.GEOSLineMerge.restype = ctypes.c_void_p
-    lgeos.GEOSLineMerge.argtypes = [ctypes.c_void_p]
+    lgeos.GEOSTouches.restype = c_byte
+    lgeos.GEOSTouches.argtypes = [c_void_p, c_void_p]
 
-    lgeos.GEOSRelatePattern.restype = ctypes.c_char
-    lgeos.GEOSRelatePattern.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_char_p]
+    lgeos.GEOSIntersects.restype = c_byte
+    lgeos.GEOSIntersects.argtypes = [c_void_p, c_void_p]
 
-    lgeos.GEOSDisjoint.restype = ctypes.c_byte
-    lgeos.GEOSDisjoint.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
+    lgeos.GEOSCrosses.restype = c_byte
+    lgeos.GEOSCrosses.argtypes = [c_void_p, c_void_p]
 
-    lgeos.GEOSTouches.restype = ctypes.c_byte
-    lgeos.GEOSTouches.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
+    lgeos.GEOSWithin.restype = c_byte
+    lgeos.GEOSWithin.argtypes = [c_void_p, c_void_p]
 
-    lgeos.GEOSIntersects.restype = ctypes.c_byte
-    lgeos.GEOSIntersects.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
+    lgeos.GEOSContains.restype = c_byte
+    lgeos.GEOSContains.argtypes = [c_void_p, c_void_p]
 
-    lgeos.GEOSCrosses.restype = ctypes.c_byte
-    lgeos.GEOSCrosses.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
+    lgeos.GEOSOverlaps.restype = c_byte
+    lgeos.GEOSOverlaps.argtypes = [c_void_p, c_void_p]
 
-    lgeos.GEOSWithin.restype = ctypes.c_byte
-    lgeos.GEOSWithin.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
+    lgeos.GEOSEquals.restype = c_byte
+    lgeos.GEOSEquals.argtypes = [c_void_p, c_void_p]
 
-    lgeos.GEOSContains.restype = ctypes.c_byte
-    lgeos.GEOSContains.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
+    lgeos.GEOSEqualsExact.restype = c_byte
+    lgeos.GEOSEqualsExact.argtypes = [c_void_p, c_void_p, c_double]
 
-    lgeos.GEOSOverlaps.restype = ctypes.c_byte
-    lgeos.GEOSOverlaps.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
+    '''
+    Unary predicate
+    Return 2 on exception, 1 on true, 0 on false
+    '''
 
-    lgeos.GEOSEquals.restype = ctypes.c_byte
-    lgeos.GEOSEquals.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
+    lgeos.GEOSisEmpty.restype = c_byte
+    lgeos.GEOSisEmpty.argtypes = [c_void_p]
 
-    lgeos.GEOSEqualsExact.restype = ctypes.c_byte
-    lgeos.GEOSEqualsExact.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_double]
+    lgeos.GEOSisValid.restype = c_byte
+    lgeos.GEOSisValid.argtypes = [c_void_p]
 
-    lgeos.GEOSisEmpty.restype = ctypes.c_byte
-    lgeos.GEOSisEmpty.argtypes = [ctypes.c_void_p]
+    if geos_version >= (3, 1, 0):
+        lgeos.GEOSisValidReason.restype = allocated_c_char_p
+        lgeos.GEOSisValidReason.argtypes = [c_void_p]
+
+    lgeos.GEOSisSimple.restype = c_byte
+    lgeos.GEOSisSimple.argtypes = [c_void_p]
+
+    lgeos.GEOSisRing.restype = c_byte
+    lgeos.GEOSisRing.argtypes = [c_void_p]
+
+    lgeos.GEOSHasZ.restype = c_byte
+    lgeos.GEOSHasZ.argtypes = [c_void_p]
+
+    '''
+    Dimensionally Extended 9 Intersection Model related
+    '''
+
+    lgeos.GEOSRelatePattern.restype = c_char
+    lgeos.GEOSRelatePattern.argtypes = [c_void_p, c_void_p, c_char_p]
+
+    lgeos.GEOSRelate.restype = allocated_c_char_p
+    lgeos.GEOSRelate.argtypes = [c_void_p, c_void_p]
 
-    lgeos.GEOSisValid.restype = ctypes.c_byte
-    lgeos.GEOSisValid.argtypes = [ctypes.c_void_p]
+    '''
+    Prepared Geometry Binary predicates
+    Return 2 on exception, 1 on true, 0 on false
+    '''
 
-    lgeos.GEOSisSimple.restype = ctypes.c_byte
-    lgeos.GEOSisSimple.argtypes = [ctypes.c_void_p]
+    if geos_version >= (3, 1, 0):
 
-    lgeos.GEOSisRing.restype = ctypes.c_byte
-    lgeos.GEOSisRing.argtypes = [ctypes.c_void_p]
+        lgeos.GEOSPrepare.restype = c_void_p
+        lgeos.GEOSPrepare.argtypes = [c_void_p]
 
-    lgeos.GEOSHasZ.restype = ctypes.c_byte
-    lgeos.GEOSHasZ.argtypes = [ctypes.c_void_p]
+        lgeos.GEOSPreparedGeom_destroy.restype = None
+        lgeos.GEOSPreparedGeom_destroy.argtypes = [c_void_p]
+
+        lgeos.GEOSPreparedContains.restype = c_int
+        lgeos.GEOSPreparedContains.argtypes = [c_void_p, c_void_p]
+
+        lgeos.GEOSPreparedContainsProperly.restype = c_int
+        lgeos.GEOSPreparedContainsProperly.argtypes = [c_void_p, c_void_p]
+
+        lgeos.GEOSPreparedCovers.restype = c_int
+        lgeos.GEOSPreparedCovers.argtypes = [c_void_p, c_void_p]
 
-    lgeos.GEOSGeomType.restype = ctypes.c_char_p
-    lgeos.GEOSGeomType.argtypes = [ctypes.c_void_p]
+        lgeos.GEOSPreparedIntersects.restype = c_int
+        lgeos.GEOSPreparedIntersects.argtypes = [c_void_p, c_void_p]
 
-    lgeos.GEOSGeomTypeId.restype = ctypes.c_int
-    lgeos.GEOSGeomTypeId.argtypes = [ctypes.c_void_p]
+    '''
+    Geometry info
+    '''
 
-    lgeos.GEOSGetSRID.restype = ctypes.c_int
-    lgeos.GEOSGetSRID.argtypes = [ctypes.c_void_p]
+    lgeos.GEOSGeomType.restype = c_char_p
+    lgeos.GEOSGeomType.argtypes = [c_void_p]
+
+    lgeos.GEOSGeomTypeId.restype = c_int
+    lgeos.GEOSGeomTypeId.argtypes = [c_void_p]
+
+    lgeos.GEOSGetSRID.restype = c_int
+    lgeos.GEOSGetSRID.argtypes = [c_void_p]
 
     lgeos.GEOSSetSRID.restype = None
-    lgeos.GEOSSetSRID.argtypes = [ctypes.c_void_p, ctypes.c_int]
+    lgeos.GEOSSetSRID.argtypes = [c_void_p, c_int]
 
-    lgeos.GEOSGetNumGeometries.restype = ctypes.c_int
-    lgeos.GEOSGetNumGeometries.argtypes = [ctypes.c_void_p]
+    lgeos.GEOSGetNumGeometries.restype = c_int
+    lgeos.GEOSGetNumGeometries.argtypes = [c_void_p]
 
-    lgeos.GEOSGetGeometryN.restype = ctypes.c_void_p
-    lgeos.GEOSGetGeometryN.argtypes = [ctypes.c_void_p, ctypes.c_int]
+    lgeos.GEOSGetGeometryN.restype = c_void_p
+    lgeos.GEOSGetGeometryN.argtypes = [c_void_p, c_int]
 
-    lgeos.GEOSGetNumInteriorRings.restype = ctypes.c_int
-    lgeos.GEOSGetNumInteriorRings.argtypes = [ctypes.c_void_p]
+    lgeos.GEOSGetNumInteriorRings.restype = c_int
+    lgeos.GEOSGetNumInteriorRings.argtypes = [c_void_p]
 
-    lgeos.GEOSGetInteriorRingN.restype = ctypes.c_void_p
-    lgeos.GEOSGetInteriorRingN.argtypes = [ctypes.c_void_p, ctypes.c_int]
+    lgeos.GEOSGetInteriorRingN.restype = c_void_p
+    lgeos.GEOSGetInteriorRingN.argtypes = [c_void_p, c_int]
 
-    lgeos.GEOSGetExteriorRing.restype = ctypes.c_void_p
-    lgeos.GEOSGetExteriorRing.argtypes = [ctypes.c_void_p]
+    lgeos.GEOSGetExteriorRing.restype = c_void_p
+    lgeos.GEOSGetExteriorRing.argtypes = [c_void_p]
 
-    lgeos.GEOSGetNumCoordinates.restype = ctypes.c_int
-    lgeos.GEOSGetNumCoordinates.argtypes = [ctypes.c_void_p]
+    lgeos.GEOSGetNumCoordinates.restype = c_int
+    lgeos.GEOSGetNumCoordinates.argtypes = [c_void_p]
 
-    lgeos.GEOSGeom_getCoordSeq.restype = ctypes.c_void_p
-    lgeos.GEOSGeom_getCoordSeq.argtypes = [ctypes.c_void_p]
+    lgeos.GEOSGeom_getCoordSeq.restype = c_void_p
+    lgeos.GEOSGeom_getCoordSeq.argtypes = [c_void_p]
 
-    lgeos.GEOSGeom_getDimensions.restype = ctypes.c_int
-    lgeos.GEOSGeom_getDimensions.argtypes = [ctypes.c_void_p]
+    lgeos.GEOSGeom_getDimensions.restype = c_int
+    lgeos.GEOSGeom_getDimensions.argtypes = [c_void_p]
 
-    lgeos.GEOSArea.restype = ctypes.c_double
-    lgeos.GEOSArea.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
+    '''
+    Misc functions
+    '''
 
-    lgeos.GEOSLength.restype = ctypes.c_int
-    lgeos.GEOSLength.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
+    lgeos.GEOSArea.restype = c_double
+    lgeos.GEOSArea.argtypes = [c_void_p, c_void_p]
 
-    lgeos.GEOSDistance.restype = ctypes.c_int
-    lgeos.GEOSDistance.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p]
+    lgeos.GEOSLength.restype = c_int
+    lgeos.GEOSLength.argtypes = [c_void_p, c_void_p]
 
-    if geosVersion >= (1, 5, 0):
+    lgeos.GEOSDistance.restype = c_int
+    lgeos.GEOSDistance.argtypes = [c_void_p, c_void_p, c_void_p]
 
-        if hasattr(lgeos, 'GEOSFree'):
-            lgeos.GEOSFree.restype = None
-            lgeos.GEOSFree.argtypes = [ctypes.c_void_p]
+    '''
+    Reader and Writer APIs
+    '''
 
-        # Prepared geometry, GEOS C API 1.5.0+
-        lgeos.GEOSPrepare.restype = ctypes.c_void_p
-        lgeos.GEOSPrepare.argtypes = [ctypes.c_void_p]
+    '''WKT Reader'''
+    lgeos.GEOSWKTReader_create.restype = c_void_p
+    lgeos.GEOSWKTReader_create.argtypes = []
 
-        lgeos.GEOSPreparedGeom_destroy.restype = None
-        lgeos.GEOSPreparedGeom_destroy.argtypes = [ctypes.c_void_p]
+    lgeos.GEOSWKTReader_destroy.restype = None
+    lgeos.GEOSWKTReader_destroy.argtypes = [c_void_p]
 
-        lgeos.GEOSPreparedIntersects.restype = ctypes.c_int
-        lgeos.GEOSPreparedIntersects.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
+    lgeos.GEOSWKTReader_read.restype = c_void_p
+    lgeos.GEOSWKTReader_read.argtypes = [c_void_p, c_char_p]
 
-        lgeos.GEOSPreparedContains.restype = ctypes.c_int
-        lgeos.GEOSPreparedContains.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
+    '''WKT Writer'''
+    lgeos.GEOSWKTWriter_create.restype = c_void_p
+    lgeos.GEOSWKTWriter_create.argtypes = []
 
-        lgeos.GEOSPreparedContainsProperly.restype = ctypes.c_int
-        lgeos.GEOSPreparedContainsProperly.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
+    lgeos.GEOSWKTWriter_destroy.restype = None
+    lgeos.GEOSWKTWriter_destroy.argtypes = [c_void_p]
 
-        lgeos.GEOSPreparedCovers.restype = ctypes.c_int
-        lgeos.GEOSPreparedCovers.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
+    lgeos.GEOSWKTWriter_write.restype = allocated_c_char_p
+    lgeos.GEOSWKTWriter_write.argtypes = [c_void_p, c_void_p]
 
-        lgeos.GEOSisValidReason.restype = allocated_c_char_p
-        lgeos.GEOSisValidReason.argtypes = [ctypes.c_void_p]
+    if geos_version >= (3, 3, 0):
+
+        lgeos.GEOSWKTWriter_setTrim.restype = None
+        lgeos.GEOSWKTWriter_setTrim.argtypes = [c_void_p, c_int]
+
+        lgeos.GEOSWKTWriter_setRoundingPrecision.restype = None
+        lgeos.GEOSWKTWriter_setRoundingPrecision.argtypes = [c_void_p, c_int]
+
+        lgeos.GEOSWKTWriter_setOutputDimension.restype = None
+        lgeos.GEOSWKTWriter_setOutputDimension.argtypes = [c_void_p, c_int]
+
+        lgeos.GEOSWKTWriter_getOutputDimension.restype = c_int
+        lgeos.GEOSWKTWriter_getOutputDimension.argtypes = [c_void_p]
+
+        lgeos.GEOSWKTWriter_setOld3D.restype = None
+        lgeos.GEOSWKTWriter_setOld3D.argtypes = [c_void_p, c_int]
+
+    '''WKB Reader'''
+    lgeos.GEOSWKBReader_create.restype = c_void_p
+    lgeos.GEOSWKBReader_create.argtypes = []
+
+    lgeos.GEOSWKBReader_destroy.restype = None
+    lgeos.GEOSWKBReader_destroy.argtypes = [c_void_p]
+
+    lgeos.GEOSWKBReader_read.restype = c_void_p
+    lgeos.GEOSWKBReader_read.argtypes = [c_void_p, c_char_p, c_size_t]
+
+    lgeos.GEOSWKBReader_readHEX.restype = c_void_p
+    lgeos.GEOSWKBReader_readHEX.argtypes = [c_void_p, c_char_p, c_size_t]
+
+    '''WKB Writer'''
+    lgeos.GEOSWKBWriter_create.restype = c_void_p
+    lgeos.GEOSWKBWriter_create.argtypes = []
+
+    lgeos.GEOSWKBWriter_destroy.restype = None
+    lgeos.GEOSWKBWriter_destroy.argtypes = [c_void_p]
+
+    lgeos.GEOSWKBWriter_write.restype = allocated_c_char_p
+    lgeos.GEOSWKBWriter_write.argtypes = [c_void_p, c_void_p, c_size_t_p]
+
+    lgeos.GEOSWKBWriter_writeHEX.restype = allocated_c_char_p
+    lgeos.GEOSWKBWriter_writeHEX.argtypes = [c_void_p, c_void_p, c_size_t_p]
+
+    lgeos.GEOSWKBWriter_getOutputDimension.restype = c_int
+    lgeos.GEOSWKBWriter_getOutputDimension.argtypes = [c_void_p]
+
+    lgeos.GEOSWKBWriter_setOutputDimension.restype = None
+    lgeos.GEOSWKBWriter_setOutputDimension.argtypes = [c_void_p, c_int]
 
-    # Other, GEOS C API 1.5.0+
-    if geosVersion >= (1, 5, 0):
-        lgeos.GEOSUnionCascaded.restype = ctypes.c_void_p
-        lgeos.GEOSUnionCascaded.argtypes = [ctypes.c_void_p]
+    lgeos.GEOSWKBWriter_getByteOrder.restype = c_int
+    lgeos.GEOSWKBWriter_getByteOrder.argtypes = [c_void_p]
 
-    # 1.6.0
-    if geosVersion >= (1, 6, 0):
-        # Linear referencing features aren't found in versions 1.5,
-        # but not in all libs versioned 1.6.0 either!
-        if hasattr(lgeos, 'GEOSProject'):
-            lgeos.GEOSSingleSidedBuffer.restype = ctypes.c_void_p
-            lgeos.GEOSSingleSidedBuffer.argtypes = [ctypes.c_void_p, ctypes.c_double, ctypes.c_int, ctypes.c_int, ctypes.c_double, ctypes.c_int]
+    lgeos.GEOSWKBWriter_setByteOrder.restype = None
+    lgeos.GEOSWKBWriter_setByteOrder.argtypes = [c_void_p, c_int]
 
-            lgeos.GEOSProject.restype = ctypes.c_double
-            lgeos.GEOSProject.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
+    lgeos.GEOSWKBWriter_getIncludeSRID.restype = c_int
+    lgeos.GEOSWKBWriter_getIncludeSRID.argtypes = [c_void_p]
 
-            lgeos.GEOSProjectNormalized.restype = ctypes.c_double
-            lgeos.GEOSProjectNormalized.argtypes = [ctypes.c_void_p, 
-                                                    ctypes.c_void_p]
+    lgeos.GEOSWKBWriter_setIncludeSRID.restype = None
+    lgeos.GEOSWKBWriter_setIncludeSRID.argtypes = [c_void_p, c_int]
 
-            lgeos.GEOSInterpolate.restype = ctypes.c_void_p
-            lgeos.GEOSInterpolate.argtypes = [ctypes.c_void_p, 
-                                              ctypes.c_double]
+    if geos_version >= (3, 1, 1):
 
-            lgeos.GEOSInterpolateNormalized.restype = ctypes.c_void_p
-            lgeos.GEOSInterpolateNormalized.argtypes = [ctypes.c_void_p, 
-                                                        ctypes.c_double]
+        '''
+        Free buffers returned by stuff like GEOSWKBWriter_write(),
+        GEOSWKBWriter_writeHEX() and GEOSWKTWriter_write()
+        '''
 
+        lgeos.GEOSFree.restype = None
+        lgeos.GEOSFree.argtypes = [c_void_p]
diff --git a/shapely/geometry/__init__.py b/shapely/geometry/__init__.py
index 3f29cbc..50ddbc2 100644
--- a/shapely/geometry/__init__.py
+++ b/shapely/geometry/__init__.py
@@ -1,20 +1,20 @@
 """Geometry classes and factories
 """
 
-from geo import box, shape, asShape, mapping
-from point import Point, asPoint
-from linestring import LineString, asLineString
-from polygon import Polygon, asPolygon
-from multipoint import MultiPoint, asMultiPoint
-from multilinestring import MultiLineString, asMultiLineString
-from multipolygon import MultiPolygon, asMultiPolygon
-from collection import GeometryCollection
+from .base import CAP_STYLE, JOIN_STYLE
+from .geo import box, shape, asShape, mapping
+from .point import Point, asPoint
+from .linestring import LineString, asLineString
+from .polygon import Polygon, asPolygon, LinearRing, asLinearRing
+from .multipoint import MultiPoint, asMultiPoint
+from .multilinestring import MultiLineString, asMultiLineString
+from .multipolygon import MultiPolygon, asMultiPolygon
+from .collection import GeometryCollection
 
 __all__ = [
-    'box', 'shape', 'asShape', 'Point', 'asPoint', 'LineString', 'asLineString',
-    'Polygon', 'asPolygon', 'MultiPoint', 'asMultiPoint',
+    'box', 'shape', 'asShape', 'Point', 'asPoint', 'LineString',
+    'asLineString', 'Polygon', 'asPolygon', 'MultiPoint', 'asMultiPoint',
     'MultiLineString', 'asMultiLineString', 'MultiPolygon', 'asMultiPolygon',
-    'GeometryCollection', 'mapping'
-    ]
-
-
+    'GeometryCollection', 'mapping', 'LinearRing', 'asLinearRing',
+    'CAP_STYLE', 'JOIN_STYLE',
+]
diff --git a/shapely/geometry/base.py b/shapely/geometry/base.py
index af1bb31..ed1f4cf 100644
--- a/shapely/geometry/base.py
+++ b/shapely/geometry/base.py
@@ -2,13 +2,18 @@
 """
 
 import sys
-import warnings
+from warnings import warn
+from binascii import a2b_hex
+from ctypes import pointer, c_size_t, c_char_p, c_void_p
 
 from shapely.coords import CoordinateSequence
 from shapely.ftools import wraps
-from shapely.geos import lgeos
+from shapely.geos import lgeos, ReadingError
+from shapely.geos import WKBWriter, WKTWriter
 from shapely.impl import DefaultImplementation, delegated
-from shapely import wkb, wkt
+
+if sys.version_info[0] < 3:
+    range = xrange
 
 GEOMETRY_TYPES = [
     'Point',
@@ -18,14 +23,32 @@ GEOMETRY_TYPES = [
     'MultiPoint',
     'MultiLineString',
     'MultiPolygon',
-    'GeometryCollection'
-    ]
+    'GeometryCollection',
+]
+
+
+def dump_coords(geom):
+    """Dump coordinates of a geometry in the same order as data packing"""
+    if not isinstance(geom, BaseGeometry):
+        raise ValueError('Must be instance of a geometry class; found ' +
+                         geom.__class__.__name__)
+    elif geom.type in ('Point', 'LineString', 'LinearRing'):
+        return geom.coords[:]
+    elif geom.type == 'Polygon':
+        return geom.exterior.coords[:] + [i.coords[:] for i in geom.interiors]
+    elif geom.type.startswith('Multi') or geom.type == 'GeometryCollection':
+        # Recursive call
+        return [dump_coords(part) for part in geom]
+    else:
+        raise ValueError('Unhandled geometry type: ' + repr(geom.type))
+
 
 def geometry_type_name(g):
     if g is None:
         raise ValueError("Null geometry has no type")
     return GEOMETRY_TYPES[lgeos.GEOSGeomTypeId(g)]
 
+
 def geom_factory(g, parent=None):
     # Abstract geometry factory for use with topological methods below
     if not g:
@@ -34,9 +57,9 @@ def geom_factory(g, parent=None):
     geom_type = geometry_type_name(g)
     # TODO: check cost of dynamic import by profiling
     mod = __import__(
-        'shapely.geometry', 
-        globals(), 
-        locals(), 
+        'shapely.geometry',
+        globals(),
+        locals(),
         [geom_type],
         )
     ob.__class__ = getattr(mod, geom_type)
@@ -48,6 +71,50 @@ def geom_factory(g, parent=None):
         ob._ndim = 2
     return ob
 
+
+def geom_from_wkt(data):
+    warn("`geom_from_wkt` is deprecated. Use `geos.wkt_reader.read(data)`.",
+         DeprecationWarning)
+    if sys.version_info[0] >= 3:
+        data = data.encode('ascii')
+    geom = lgeos.GEOSGeomFromWKT(c_char_p(data))
+    if not geom:
+        raise ReadingError(
+            "Could not create geometry because of errors while reading input.")
+    return geom_factory(geom)
+
+
+def geom_to_wkt(ob):
+    warn("`geom_to_wkt` is deprecated. Use `geos.wkt_writer.write(ob)`.",
+         DeprecationWarning)
+    if ob is None or ob._geom is None:
+        raise ValueError("Null geometry supports no operations")
+    return lgeos.GEOSGeomToWKT(ob._geom)
+
+
+def deserialize_wkb(data):
+    geom = lgeos.GEOSGeomFromWKB_buf(c_char_p(data), c_size_t(len(data)))
+    if not geom:
+        raise ReadingError(
+            "Could not create geometry because of errors while reading input.")
+    return geom
+
+
+def geom_from_wkb(data):
+    warn("`geom_from_wkb` is deprecated. Use `geos.wkb_reader.read(data)`.",
+         DeprecationWarning)
+    return geom_factory(deserialize_wkb(data))
+
+
+def geom_to_wkb(ob):
+    warn("`geom_to_wkb` is deprecated. Use `geos.wkb_writer.write(ob)`.",
+         DeprecationWarning)
+    if ob is None or ob._geom is None:
+        raise ValueError("Null geometry supports no operations")
+    size = c_size_t()
+    return lgeos.GEOSGeomToWKB_buf(c_void_p(ob._geom), pointer(size))
+
+
 def exceptNull(func):
     """Decorator which helps avoid GEOS operations on null pointers."""
     @wraps(func)
@@ -57,7 +124,20 @@ def exceptNull(func):
         return func(*args, **kwargs)
     return wrapper
 
-EMPTY = wkb.deserialize('010700000000000000'.decode('hex'))
+
+class CAP_STYLE(object):
+    round = 1
+    flat = 2
+    square = 3
+
+
+class JOIN_STYLE(object):
+    round = 1
+    mitre = 2
+    bevel = 3
+
+EMPTY = deserialize_wkb(a2b_hex(b'010700000000000000'))
+
 
 class BaseGeometry(object):
     """
@@ -81,15 +161,15 @@ class BaseGeometry(object):
     #     Coordinate reference system. Available for Shapely extensions, but
     #     not implemented here.
     # _owned : bool
-    #     True if this object's GEOS geometry is owned by another as in the case
-    #     of a multipart geometry member.
+    #     True if this object's GEOS geometry is owned by another as in the
+    #     case of a multipart geometry member.
     __geom__ = EMPTY
     __p__ = None
     _ctypes_data = None
     _ndim = None
     _crs = None
     _owned = False
-    
+
     # Backend config
     impl = DefaultImplementation
 
@@ -107,7 +187,7 @@ class BaseGeometry(object):
             try:
                 self._lgeos.GEOSGeom_destroy(self.__geom__)
             except AttributeError:
-                pass # _lgeos might be empty on shutdown
+                pass  # _lgeos might be empty on shutdown
         self.__geom__ = EMPTY
 
     def __del__(self):
@@ -116,23 +196,43 @@ class BaseGeometry(object):
         self.__p__ = None
 
     def __str__(self):
-        return self.to_wkt()
+        return self.wkt
 
     # To support pickling
     def __reduce__(self):
-        return (self.__class__, (), self.to_wkb())
+        return (self.__class__, (), self.wkb)
 
     def __setstate__(self, state):
         self.empty()
-        self.__geom__ = wkb.deserialize(state)
-    
-    # The _geom property
-    def _get_geom(self):
+        self.__geom__ = deserialize_wkb(state)
+        if lgeos.methods['has_z'](self.__geom__):
+            self._ndim = 3
+        else:
+            self._ndim = 2
+
+    @property
+    def _geom(self):
         return self.__geom__
-    def _set_geom(self, val):
+
+    @_geom.setter
+    def _geom(self, val):
         self.empty()
         self.__geom__ = val
-    _geom = property(_get_geom, _set_geom)
+
+    # Operators
+    # ---------
+
+    def __and__(self, other):
+        return self.intersection(other)
+
+    def __or__(self, other):
+        return self.union(other)
+
+    def __sub__(self, other):
+        return self.difference(other)
+
+    def __xor__(self, other):
+        return self.symmetric_difference(other)
 
     # Array and ctypes interfaces
     # ---------------------------
@@ -150,7 +250,7 @@ class BaseGeometry(object):
             typestr = '>f8'
         else:
             raise ValueError(
-                  "Unsupported byteorder: neither little nor big-endian")
+                "Unsupported byteorder: neither little nor big-endian")
         return {
             'version': 3,
             'typestr': typestr,
@@ -194,24 +294,40 @@ class BaseGeometry(object):
 
     def geometryType(self):
         return geometry_type_name(self._geom)
-    
+
     @property
     def type(self):
         return self.geometryType()
 
     def to_wkb(self):
-        return wkb.dumps(self)
+        warn("`to_wkb` is deprecated. Use the `wkb` property.",
+             DeprecationWarning)
+        return geom_to_wkb(self)
 
     def to_wkt(self):
-        return wkt.dumps(self)
+        warn("`to_wkt` is deprecated. Use the `wkt` property.",
+             DeprecationWarning)
+        return geom_to_wkt(self)
 
-    geom_type = property(geometryType, 
-        doc="""Name of the geometry's type, such as 'Point'"""
-        )
-    wkt = property(to_wkt,
-        doc="""WKT representation of the geometry""")
-    wkb = property(to_wkb,
-        doc="""WKB representation of the geometry""")
+    @property
+    def wkt(self, **kw):
+        """WKT representation of the geometry"""
+        return WKTWriter(lgeos, **kw).write(self)
+
+    @property
+    def wkb(self):
+        """WKB representation of the geometry"""
+        return WKBWriter(lgeos).write(self)
+
+    @property
+    def wkb_hex(self):
+        """WKB hex representation of the geometry"""
+        return WKBWriter(lgeos).write_hex(self)
+
+    @property
+    def geom_type(self):
+        """Name of the geometry's type, such as 'Point'"""
+        return self.geometryType()
 
     # Real-valued properties and methods
     # ----------------------------------
@@ -236,7 +352,7 @@ class BaseGeometry(object):
     @property
     def boundary(self):
         """Returns a lower dimension geometry that bounds the object
-        
+
         The boundary of a polygon is a line, the boundary of a line is a
         collection of points. The boundary of a point is an empty (null)
         collection.
@@ -250,7 +366,7 @@ class BaseGeometry(object):
             return ()
         else:
             return self.impl['bounds'](self)
-            
+
     @property
     def centroid(self):
         """Returns the geometric center of the object"""
@@ -263,12 +379,12 @@ class BaseGeometry(object):
 
     @property
     def convex_hull(self):
-        """Imagine an elastic band stretched around the geometry: that's a 
+        """Imagine an elastic band stretched around the geometry: that's a
         convex hull, more or less
 
         The convex hull of a three member multipoint, for example, is a
         triangular polygon.
-        """ 
+        """
         return geom_factory(self.impl['convex_hull'](self))
 
     @property
@@ -276,16 +392,32 @@ class BaseGeometry(object):
         """A figure that envelopes the geometry"""
         return geom_factory(self.impl['envelope'](self))
 
-    def buffer(self, distance, resolution=16, quadsegs=None):
-        """Returns a geometry with an envelope at a distance from the object's 
+    def buffer(self, distance, resolution=16, quadsegs=None,
+               cap_style=CAP_STYLE.round, join_style=JOIN_STYLE.round,
+               mitre_limit=0):
+        """Returns a geometry with an envelope at a distance from the object's
         envelope
-        
+
         A negative distance has a "shrink" effect. A zero distance may be used
         to "tidy" a polygon. The resolution of the buffer around each vertex of
         the object increases by increasing the resolution keyword parameter
         or second positional parameter. Note: the use of a `quadsegs` parameter
         is deprecated and will be gone from the next major release.
 
+        The styles of caps are: CAP_STYLE.round (1), CAP_STYLE.flat (2), and
+        CAP_STYLE.square (3).
+
+        The styles of joins between offset segments are: JOIN_STYLE.round (1),
+        JOIN_STYLE.mitre (2), and JOIN_STYLE.bevel (3).
+
+        The mitre limit ratio is used for very sharp corners. The mitre ratio
+        is the ratio of the distance from the corner to the end of the mitred
+        offset corner. When two line segments meet at a sharp angle, a miter
+        join will extend 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.
+
         Example:
 
           >>> from shapely.wkt import loads
@@ -296,19 +428,35 @@ class BaseGeometry(object):
           3.1415138011443009
           >>> g.buffer(1.0, 3).area     # triangle approximation
           3.0
+          >>> list(g.buffer(1.0, cap_style='square').exterior.coords)
+          [(1.0, 1.0), (1.0, -1.0), (-1.0, -1.0), (-1.0, 1.0), (1.0, 1.0)]
+          >>> g.buffer(1.0, cap_style='square').area
+          4.0
         """
+
         if quadsegs is not None:
-            warnings.warn(
-                "The `quadsegs` argument is deprecated. Use `resolution`.", 
+            warn(
+                "The `quadsegs` argument is deprecated. Use `resolution`.",
                 DeprecationWarning)
             res = quadsegs
         else:
             res = resolution
-        return geom_factory(self.impl['buffer'](self, distance, res))
+
+        if cap_style == CAP_STYLE.round and join_style == JOIN_STYLE.round:
+            return geom_factory(self.impl['buffer'](self, distance, res))
+
+        if 'buffer_with_style' not in self.impl:
+            raise NotImplementedError("Styled buffering not available for "
+                                      "GEOS versions < 3.2.")
+
+        return geom_factory(self.impl['buffer_with_style'](self, distance, res,
+                                                           cap_style,
+                                                           join_style,
+                                                           mitre_limit))
 
     @delegated
     def simplify(self, tolerance, preserve_topology=True):
-        """Returns a simplified geometry produced by the Douglas-Puecker 
+        """Returns a simplified geometry produced by the Douglas-Puecker
         algorithm
 
         Coordinates of the simplified geometry will be no more than the
@@ -328,13 +476,13 @@ class BaseGeometry(object):
     def difference(self, other):
         """Returns the difference of the geometries"""
         return geom_factory(self.impl['difference'](self, other))
-    
+
     def intersection(self, other):
         """Returns the intersection of the geometries"""
         return geom_factory(self.impl['intersection'](self, other))
 
     def symmetric_difference(self, other):
-        """Returns the symmetric difference of the geometries 
+        """Returns the symmetric difference of the geometries
         (Shapely geometry)"""
         return geom_factory(self.impl['symmetric_difference'](self, other))
 
@@ -354,7 +502,7 @@ class BaseGeometry(object):
     @property
     def is_empty(self):
         """True if the set of points in this geometry is empty, else False"""
-        return bool(self.impl['is_empty'](self)) or (self._geom is None)
+        return (self._geom is None) or bool(self.impl['is_empty'](self))
 
     @property
     def is_ring(self):
@@ -363,13 +511,13 @@ class BaseGeometry(object):
 
     @property
     def is_simple(self):
-        """True if the geometry is simple, meaning that any self-intersections 
+        """True if the geometry is simple, meaning that any self-intersections
         are only at boundary points, else False"""
         return bool(self.impl['is_simple'](self))
 
     @property
     def is_valid(self):
-        """True if the geometry is valid (definition depends on sub-class), 
+        """True if the geometry is valid (definition depends on sub-class),
         else False"""
         return bool(self.impl['is_valid'](self))
 
@@ -377,7 +525,7 @@ class BaseGeometry(object):
     # -----------------
 
     def relate(self, other):
-        """Returns the DE-9IM intersection matrix for the two geometries 
+        """Returns the DE-9IM intersection matrix for the two geometries
         (string)"""
         return self.impl['relate'](self, other)
 
@@ -414,13 +562,13 @@ class BaseGeometry(object):
         return bool(self.impl['within'](self, other))
 
     def equals_exact(self, other, tolerance):
-        """Returns True if geometries are equal to within a specified 
+        """Returns True if geometries are equal to within a specified
         tolerance"""
         # return BinaryPredicateOp('equals_exact', self)(other, tolerance)
         return bool(self.impl['equals_exact'](self, other, tolerance))
 
     def almost_equals(self, other, decimal=6):
-        """Returns True if geometries are equal at all coordinates to a 
+        """Returns True if geometries are equal at all coordinates to a
         specified decimal place"""
         return self.equals_exact(other, 0.5 * 10**(-decimal))
 
@@ -429,22 +577,22 @@ class BaseGeometry(object):
 
     @delegated
     def project(self, other, normalized=False):
-        """Returns the distance along this geometry to a point nearest the 
+        """Returns the distance along this geometry to a point nearest the
         specified point
-        
+
         If the normalized arg is True, return the distance normalized to the
         length of the linear geometry.
-        """ 
+        """
         if normalized:
             op = self.impl['project_normalized']
         else:
             op = self.impl['project']
         return op(self, other)
-           
+
     @delegated
     def interpolate(self, distance, normalized=False):
         """Return a point at the specified distance along a linear geometry
-        
+
         If the normalized arg is True, the distance will be interpreted as a
         fraction of the geometry's length.
         """
@@ -464,26 +612,26 @@ class BaseMultipartGeometry(BaseGeometry):
     @property
     def ctypes(self):
         raise NotImplementedError(
-        "Multi-part geometries have no ctypes representations")
+            "Multi-part geometries have no ctypes representations")
 
     @property
     def __array_interface__(self):
         """Provide the Numpy array protocol."""
-        raise NotImplementedError(
-        "Multi-part geometries do not themselves provide the array interface")
+        raise NotImplementedError("Multi-part geometries do not themselves "
+                                  "provide the array interface")
 
     def _get_coords(self):
-        raise NotImplementedError(
-        "Sub-geometries may have coordinate sequences, but collections do not")
+        raise NotImplementedError("Sub-geometries may have coordinate "
+                                  "sequences, but collections do not")
 
     def _set_coords(self, ob):
-        raise NotImplementedError(
-        "Sub-geometries may have coordinate sequences, but collections do not")
+        raise NotImplementedError("Sub-geometries may have coordinate "
+                                  "sequences, but collections do not")
 
     @property
     def coords(self):
         raise NotImplementedError(
-        "Multi-part geometries do not provide a coordinate sequence")
+            "Multi-part geometries do not provide a coordinate sequence")
 
     @property
     def geoms(self):
@@ -537,7 +685,7 @@ class GeometrySequence(object):
     def _update(self):
         self._geom = self.__p__._geom
         self._ndim = self.__p__._ndim
-        
+
     def _get_geom_item(self, i):
         g = self.shape_factory()
         g._owned = True
@@ -548,7 +696,7 @@ class GeometrySequence(object):
 
     def __iter__(self):
         self._update()
-        for i in xrange(self.__len__()):
+        for i in range(self.__len__()):
             yield self._get_geom_item(i)
 
     def __len__(self):
@@ -572,7 +720,7 @@ class GeometrySequence(object):
                     "Heterogenous geometry collections are not sliceable")
             res = []
             start, stop, stride = key.indices(m)
-            for i in xrange(start, stop, stride):
+            for i in range(start, stop, stride):
                 res.append(self._get_geom_item(i))
             return type(self.__p__)(res or None)
         else:
@@ -597,12 +745,13 @@ class HeterogeneousGeometrySequence(GeometrySequence):
 
     def _get_geom_item(self, i):
         sub = lgeos.GEOSGetGeometryN(self._geom, i)
-        g = geom_factory(sub)
+        g = geom_factory(sub, parent=self)
         g._owned = True
         return g
 
-# Test runner
+
 def _test():
+    """Test runner"""
     import doctest
     doctest.testmod()
 
diff --git a/shapely/geometry/geo.py b/shapely/geometry/geo.py
index 1f7447e..55d2d90 100644
--- a/shapely/geometry/geo.py
+++ b/shapely/geometry/geo.py
@@ -2,23 +2,23 @@
 Geometry factories based on the geo interface
 """
 
-from point import Point, asPoint
-from linestring import LineString, asLineString
-from polygon import Polygon, asPolygon
-from multipoint import MultiPoint, asMultiPoint
-from multilinestring import MultiLineString, asMultiLineString
-from multipolygon import MultiPolygon, MultiPolygonAdapter
+from .point import Point, asPoint
+from .linestring import LineString, asLineString
+from .polygon import Polygon, asPolygon
+from .multipoint import MultiPoint, asMultiPoint
+from .multilinestring import MultiLineString, asMultiLineString
+from .multipolygon import MultiPolygon, MultiPolygonAdapter
 
 
 def box(minx, miny, maxx, maxy, ccw=True):
-    """Return a rectangular polygon with configurable normal vector"""
+    """Returns a rectangular polygon with configurable normal vector"""
     coords = [(maxx, miny), (maxx, maxy), (minx, maxy), (minx, miny)]
     if not ccw:
         coords = coords[::-1]
     return Polygon(coords)
 
 def shape(context):
-    """Return a new, independent geometry with coordinates *copied* from the
+    """Returns a new, independent geometry with coordinates *copied* from the
     context.
     """
     if hasattr(context, "__geo_interface__"):
diff --git a/shapely/geometry/linestring.py b/shapely/geometry/linestring.py
index da172a0..3af8197 100644
--- a/shapely/geometry/linestring.py
+++ b/shapely/geometry/linestring.py
@@ -1,11 +1,16 @@
 """Line strings and related utilities
 """
 
+import sys
+
+if sys.version_info[0] < 3:
+    range = xrange
+
 from ctypes import c_double, cast, POINTER
-from ctypes import ArgumentError
 
+from shapely.coords import required
 from shapely.geos import lgeos, TopologicalError
-from shapely.geometry.base import BaseGeometry, geom_factory
+from shapely.geometry.base import BaseGeometry, geom_factory, JOIN_STYLE
 from shapely.geometry.proxy import CachingGeometryProxy
 
 __all__ = ['LineString', 'asLineString']
@@ -14,7 +19,7 @@ __all__ = ['LineString', 'asLineString']
 class LineString(BaseGeometry):
     """
     A one-dimensional figure comprising one or more line segments
-    
+
     A LineString has non-zero length and zero area. It may approximate a curve
     and need not be straight. Unlike a LinearRing, a LineString is not closed.
     """
@@ -25,8 +30,8 @@ class LineString(BaseGeometry):
         ----------
         coordinates : sequence
             A sequence of (x, y [,z]) numeric coordinate pairs or triples or
-            an object that provides the numpy array interface, including another
-            instance of LineString.
+            an object that provides the numpy array interface, including
+            another instance of LineString.
 
         Example
         -------
@@ -56,7 +61,7 @@ class LineString(BaseGeometry):
     def array_interface(self):
         """Provide the Numpy array protocol."""
         return self.coords.array_interface()
-    
+
     __array_interface__ = property(array_interface)
 
     # Coordinate access
@@ -69,9 +74,9 @@ class LineString(BaseGeometry):
     @property
     def xy(self):
         """Separate arrays of X and Y coordinate values
-        
+
         Example:
-        
+
           >>> x, y = LineString(((0, 0), (1, 1))).xy
           >>> list(x)
           [0.0, 1.0]
@@ -79,22 +84,23 @@ class LineString(BaseGeometry):
           [0.0, 1.0]
         """
         return self.coords.xy
-    
+
     def parallel_offset(
-        self, distance, side, 
-        resolution=16, join_style=1, mitre_limit=1.0):
-        
+            self, distance, side,
+            resolution=16, join_style=JOIN_STYLE.round, mitre_limit=1.0):
+
         """Returns a LineString or MultiLineString geometry at a distance from
         the object on its right or its left side.
-        
+
         Distance must be a positive float value. The side parameter may be
         'left' or 'right'. The resolution of the buffer around each vertex of
         the object increases by increasing the resolution keyword parameter or
         third positional parameter.
-        
+
         The join style is for outside corners between line segments. Accepted
-        values are 1 => ROUND, 2 => MITRE, 3 => BEVEL.
-        
+        values are JOIN_STYLE.round (1), JOIN_STYLE.mitre (2), and
+        JOIN_STYLE.bevel (3).
+
         The mitre ratio limit is used for very sharp corners. It is the ratio
         of the distance from the corner to the end of the mitred offset corner.
         When two line segments meet at a sharp angle, a miter join will extend
@@ -104,11 +110,11 @@ class LineString(BaseGeometry):
 
         try:
             return geom_factory(self.impl['parallel_offset'](
-                self, distance, resolution, join_style, mitre_limit, 
-                bool(side=='left')))
+                self, distance, resolution, join_style, mitre_limit,
+                bool(side == 'left')))
         except WindowsError:
             raise TopologicalError()
-        
+
 
 class LineStringAdapter(CachingGeometryProxy, LineString):
 
@@ -151,6 +157,9 @@ def asLineString(context):
 
 
 def geos_linestring_from_py(ob, update_geom=None, update_ndim=0):
+    # 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)
     try:
         # From array protocol
         array = ob.__array_interface__
@@ -178,13 +187,13 @@ def geos_linestring_from_py(ob, update_geom=None, update_ndim=0):
             cs = lgeos.GEOSGeom_getCoordSeq(update_geom)
             if n != update_ndim:
                 raise ValueError(
-                "Wrong coordinate dimensions; this geometry has dimensions: %d" \
-                % update_ndim)
+                    "Wrong coordinate dimensions; this geometry has "
+                    "dimensions: %d" % update_ndim)
         else:
             cs = lgeos.GEOSCoordSeq_create(m, n)
 
         # add to coordinate sequence
-        for i in xrange(m):
+        for i in range(m):
             dx = c_double(cp[n*i])
             dy = c_double(cp[n*i+1])
             dz = None
@@ -194,7 +203,7 @@ def geos_linestring_from_py(ob, update_geom=None, update_ndim=0):
                 except IndexError:
                     raise ValueError("Inconsistent coordinate dimensionality")
 
-            # Because of a bug in the GEOS C API, 
+            # Because of a bug in the GEOS C API,
             # always set X before Y
             lgeos.GEOSCoordSeq_setX(cs, i, dx)
             lgeos.GEOSCoordSeq_setY(cs, i, dy)
@@ -203,7 +212,12 @@ def geos_linestring_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)
+
         if m < 2:
             raise ValueError(
                 "LineStrings must have at least 2 coordinate tuples")
@@ -219,15 +233,15 @@ def geos_linestring_from_py(ob, update_geom=None, update_ndim=0):
             cs = lgeos.GEOSGeom_getCoordSeq(update_geom)
             if n != update_ndim:
                 raise ValueError(
-                "Wrong coordinate dimensions; this geometry has dimensions: %d" \
-                % update_ndim)
+                    "Wrong coordinate dimensions; this geometry has "
+                    "dimensions: %d" % update_ndim)
         else:
             cs = lgeos.GEOSCoordSeq_create(m, n)
-        
+
         # add to coordinate sequence
-        for i in xrange(m):
+        for i in range(m):
             coords = ob[i]
-            # Because of a bug in the GEOS C API, 
+            # Because of a bug in the GEOS C API,
             # always set X before Y
             lgeos.GEOSCoordSeq_setX(cs, i, coords[0])
             lgeos.GEOSCoordSeq_setY(cs, i, coords[1])
@@ -236,15 +250,17 @@ def geos_linestring_from_py(ob, update_geom=None, update_ndim=0):
                     lgeos.GEOSCoordSeq_setZ(cs, i, coords[2])
                 except IndexError:
                     raise ValueError("Inconsistent coordinate dimensionality")
-    
+
     if update_geom is not None:
         return None
     else:
         return lgeos.GEOSGeom_createLineString(cs), n
 
+
 def update_linestring_from_py(geom, ob):
     geos_linestring_from_py(ob, geom._geom, geom._ndim)
 
+
 # Test runner
 def _test():
     import doctest
diff --git a/shapely/geometry/multilinestring.py b/shapely/geometry/multilinestring.py
index 8f34c17..b78d174 100644
--- a/shapely/geometry/multilinestring.py
+++ b/shapely/geometry/multilinestring.py
@@ -1,6 +1,11 @@
 """Collections of linestrings and related utilities
 """
 
+import sys
+
+if sys.version_info[0] < 3:
+    range = xrange
+
 from ctypes import c_double, c_void_p, cast, POINTER
 
 from shapely.geos import lgeos
@@ -40,7 +45,7 @@ class MultiLineString(BaseMultipartGeometry):
         """
         super(MultiLineString, self).__init__()
 
-        if lines is None:
+        if not lines:
             # allow creation of empty multilinestrings, to support unpickling
             pass
         else:
@@ -96,7 +101,7 @@ def geos_multilinestring_from_py(ob):
         # Array of pointers to sub-geometries
         subs = (c_void_p * L)()
 
-        for l in xrange(L):
+        for l in range(L):
             geom, ndims = geos_linestring_from_py(array['data'][l])
             subs[i] = cast(geom, c_void_p)
         N = lgeos.GEOSGeom_getDimensions(subs[0])
@@ -115,7 +120,7 @@ def geos_multilinestring_from_py(ob):
         subs = (c_void_p * L)()
         
         # add to coordinate sequence
-        for l in xrange(L):
+        for l in range(L):
             geom, ndims = geos_linestring_from_py(obs[l])
             subs[l] = cast(geom, c_void_p)
             
diff --git a/shapely/geometry/multipoint.py b/shapely/geometry/multipoint.py
index a02e7a9..1e43117 100644
--- a/shapely/geometry/multipoint.py
+++ b/shapely/geometry/multipoint.py
@@ -1,9 +1,15 @@
 """Collections of points and related utilities
 """
 
+import sys
+
+if sys.version_info[0] < 3:
+    range = xrange
+
 from ctypes import byref, c_double, c_void_p, cast, POINTER
 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
@@ -70,7 +76,7 @@ class MultiPoint(BaseMultipartGeometry):
             m = len(self.geoms)
             array_type = c_double * (m * n)
             data = array_type()
-            for i in xrange(m):
+            for i in range(m):
                 g = self.geoms[i]._geom    
                 cs = lgeos.GEOSGeom_getCoordSeq(g)
                 lgeos.GEOSCoordSeq_getX(cs, 0, byref(temp))
@@ -128,6 +134,9 @@ def asMultiPoint(context):
 
 
 def geos_multipoint_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)
     try:
         # From array protocol
         array = ob.__array_interface__
@@ -147,7 +156,7 @@ def geos_multipoint_from_py(ob):
         # Array of pointers to sub-geometries
         subs = (c_void_p * m)()
 
-        for i in xrange(m):
+        for i in range(m):
             geom, ndims = geos_point_from_py(cp[n*i:n*i+2])
             subs[i] = cast(geom, c_void_p)
 
@@ -164,7 +173,7 @@ def geos_multipoint_from_py(ob):
         subs = (c_void_p * m)()
         
         # add to coordinate sequence
-        for i in xrange(m):
+        for i in range(m):
             coords = ob[i]
             geom, ndims = geos_point_from_py(coords)
             subs[i] = cast(geom, c_void_p)
diff --git a/shapely/geometry/multipolygon.py b/shapely/geometry/multipolygon.py
index 4597364..e2d0f58 100644
--- a/shapely/geometry/multipolygon.py
+++ b/shapely/geometry/multipolygon.py
@@ -1,6 +1,11 @@
 """Collections of polygons and related utilities
 """
 
+import sys
+
+if sys.version_info[0] < 3:
+    range = xrange
+
 from ctypes import c_void_p, cast
 
 from shapely.geos import lgeos
@@ -50,7 +55,7 @@ class MultiPolygon(BaseMultipartGeometry):
         """
         super(MultiPolygon, self).__init__()
 
-        if polygons is None:
+        if not polygons:
             # allow creation of empty multipolygons, to support unpickling
             pass
         elif context_type == 'polygons':
@@ -69,7 +74,7 @@ class MultiPolygon(BaseMultipartGeometry):
             coords.append(tuple(geom.exterior.coords))
             for hole in geom.interiors:
                 coords.append(tuple(hole.coords))
-            allcoords.append(coords)
+            allcoords.append(tuple(coords))
         return {
             'type': 'MultiPolygon',
             'coordinates': allcoords
@@ -109,12 +114,13 @@ def asMultiPolygon(context):
 def geos_multipolygon_from_py(ob):
     """ob must provide Python geo interface coordinates."""
     L = len(ob)
-    N = len(ob[0][0][0])
     assert L >= 1
+    
+    N = len(ob[0][0][0])
     assert N == 2 or N == 3
 
     subs = (c_void_p * L)()
-    for l in xrange(L):
+    for l in range(L):
         geom, ndims = geos_polygon_from_py(ob[l][0], ob[l][1:])
         subs[l] = cast(geom, c_void_p)
             
@@ -124,16 +130,18 @@ def geos_multipolygon_from_polygons(ob):
     """ob must be either a sequence or array of sequences or arrays."""
     obs = getattr(ob, 'geoms', None) or ob
     L = len(obs)
+    assert L >= 1
+    
     exemplar = obs[0]
     try:
         N = len(exemplar[0][0])
     except TypeError:
         N = exemplar._ndim
-    assert L >= 1
+    
     assert N == 2 or N == 3
 
     subs = (c_void_p * L)()
-    for l in xrange(L):
+    for l in range(L):
         shell = getattr(obs[l], 'exterior', None)
         if shell is None:
             shell = obs[l][0]
diff --git a/shapely/geometry/point.py b/shapely/geometry/point.py
index 9f5e8d4..e8f0605 100644
--- a/shapely/geometry/point.py
+++ b/shapely/geometry/point.py
@@ -4,6 +4,7 @@
 from ctypes import c_double
 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.proxy import CachingGeometryProxy
@@ -15,8 +16,8 @@ class Point(BaseGeometry):
     """
     A zero dimensional feature
 
-    A point has zero length and zero area. 
-    
+    A point has zero length and zero area.
+
     Attributes
     ----------
     x, y, z : float
@@ -24,9 +25,8 @@ class Point(BaseGeometry):
 
     Example
     -------
-    
       >>> p = Point(1.0, -1.0)
-      >>> print p
+      >>> print(p)
       POINT (1.0000000000000000 -1.0000000000000000)
       >>> p.y
       -1.0
@@ -54,19 +54,19 @@ class Point(BaseGeometry):
     def x(self):
         """Return x coordinate."""
         return self.coords[0][0]
-    
+
     @property
     def y(self):
         """Return y coordinate."""
         return self.coords[0][1]
-    
+
     @property
     def z(self):
         """Return z coordinate."""
         if self._ndim != 3:
             raise DimensionError("This point has no z coordinate.")
         return self.coords[0][2]
-    
+
     @property
     def __geo_interface__(self):
         return {
@@ -113,9 +113,8 @@ class Point(BaseGeometry):
     @property
     def xy(self):
         """Separate arrays of X and Y coordinate values
-        
+
         Example:
-        
           >>> x, y = Point(0, 0).xy
           >>> list(x)
           [0.0]
@@ -172,6 +171,9 @@ def geos_point_from_py(ob, update_geom=None, update_ndim=0):
 
     Returns the GEOS geometry and the number of its dimensions.
     """
+    # 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)
     try:
         # From array protocol
         array = ob.__array_interface__
@@ -181,24 +183,28 @@ def geos_point_from_py(ob, update_geom=None, update_ndim=0):
 
         dz = None
         da = array['data']
-        if type(da) == type((0,)):
+        if isinstance(da, tuple):
             cdata = da[0]
+            # If we had numpy, we would do
+            # from numpy.ctypeslib import as_ctypes
+            # cp = as_ctypes(ob) - check that code?
             cp = cast(cdata, POINTER(c_double))
             dx = c_double(cp[0])
             dy = c_double(cp[1])
             if n == 3:
                 dz = c_double(cp[2])
-                ndim = 3
         else:
             dx, dy = da[0:2]
             if n == 3:
                 dz = da[2]
-                ndim = 3
 
     except AttributeError:
         # Fall back on the case of Python sequence data
         # Accept either (x, y) or [(x, y)]
-        if type(ob[0]) == type(tuple()):
+        if not hasattr(ob, '__getitem__'):  # Iterators, e.g. Python 3 zip
+            ob = list(ob)
+
+        if isinstance(ob[0], tuple):
             coords = ob[0]
         else:
             coords = ob
@@ -213,25 +219,27 @@ def geos_point_from_py(ob, update_geom=None, update_ndim=0):
         cs = lgeos.GEOSGeom_getCoordSeq(update_geom)
         if n != update_ndim:
             raise ValueError(
-            "Wrong coordinate dimensions; this geometry has dimensions: %d" \
-            % update_ndim)
+                "Wrong coordinate dimensions; this geometry has dimensions: "
+                "%d" % update_ndim)
     else:
         cs = lgeos.GEOSCoordSeq_create(1, n)
-    
+
     # Because of a bug in the GEOS C API, always set X before Y
     lgeos.GEOSCoordSeq_setX(cs, 0, dx)
     lgeos.GEOSCoordSeq_setY(cs, 0, dy)
     if n == 3:
         lgeos.GEOSCoordSeq_setZ(cs, 0, dz)
-   
+
     if update_geom:
         return None
     else:
         return lgeos.GEOSGeom_createPoint(cs), n
 
+
 def update_point_from_py(geom, ob):
     geos_point_from_py(ob, geom._geom, geom._ndim)
 
+
 # Test runner
 def _test():
     import doctest
diff --git a/shapely/geometry/polygon.py b/shapely/geometry/polygon.py
index 8d9a435..d4e1f43 100644
--- a/shapely/geometry/polygon.py
+++ b/shapely/geometry/polygon.py
@@ -1,11 +1,17 @@
 """Polygons and their linear ring components
 """
 
+import sys
+
+if sys.version_info[0] < 3:
+    range = xrange
+
 from ctypes import c_double, c_void_p, cast, POINTER
 from ctypes import ArgumentError
 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.linestring import LineString, LineStringAdapter
@@ -65,9 +71,15 @@ class LinearRing(LineString):
 
     @property
     def is_ccw(self):
-        """Brand new"""
+        """True is the ring is oriented counter clock-wise"""
         return bool(self.impl['is_ccw'](self))
 
+    @property
+    def is_simple(self):
+        """True if the geometry is simple, meaning that any self-intersections
+        are only at boundary points, else False"""
+        return LineString(self).is_simple
+
 
 class LinearRingAdapter(LineStringAdapter):
 
@@ -113,7 +125,7 @@ class InteriorRingSequence(object):
         self._length = self.__len__()
         return self
 
-    def next(self):
+    def __next__(self):
         if self._index < self._length:
             ring = self._get_ring(self._index)
             self._index += 1
@@ -121,6 +133,9 @@ class InteriorRingSequence(object):
         else:
             raise StopIteration 
 
+    if sys.version_info[0] < 3:
+        next = __next__
+
     def __len__(self):
         return lgeos.GEOSGetNumInteriorRings(self._geom)
 
@@ -137,7 +152,7 @@ class InteriorRingSequence(object):
         elif isinstance(key, slice):
             res = []
             start, stop, stride = key.indices(m)
-            for i in xrange(start, stop, stride):
+            for i in range(start, stop, stride):
                 res.append(self._get_ring(i))
             return res
         else:
@@ -309,6 +324,9 @@ 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 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)
     try:
         # From array protocol
         array = ob.__array_interface__
@@ -344,7 +362,7 @@ def geos_linearring_from_py(ob, update_geom=None, update_ndim=0):
             cs = lgeos.GEOSCoordSeq_create(M, n)
 
         # add to coordinate sequence
-        for i in xrange(m):
+        for i in range(m):
             # Because of a bug in the GEOS C API, 
             # always set X before Y
             lgeos.GEOSCoordSeq_setX(cs, i, cp[n*i])
@@ -363,7 +381,12 @@ 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(
@@ -387,7 +410,7 @@ def geos_linearring_from_py(ob, update_geom=None, update_ndim=0):
             cs = lgeos.GEOSCoordSeq_create(M, n)
         
         # add to coordinate sequence
-        for i in xrange(m):
+        for i in range(m):
             coords = ob[i]
             # Because of a bug in the GEOS C API, 
             # always set X before Y
@@ -420,7 +443,7 @@ def update_linearring_from_py(geom, ob):
 def geos_polygon_from_py(shell, holes=None):
     if shell is not None:
         geos_shell, ndim = geos_linearring_from_py(shell)
-        if holes:
+        if holes is not None and len(holes) > 0:
             ob = holes
             L = len(ob)
             exemplar = ob[0]
@@ -428,14 +451,16 @@ def geos_polygon_from_py(shell, holes=None):
                 N = len(exemplar[0])
             except TypeError:
                 N = exemplar._ndim
-            assert L >= 1
-            assert N == 2 or N == 3
+            if not L >= 1:
+                raise ValueError("number of holes must be non zero")
+            if not N in (2, 3):
+                raise ValueError("insufficiant coordinate dimension")
 
             # Array of pointers to ring geometries
             geos_holes = (c_void_p * L)()
     
             # add to coordinate sequence
-            for l in xrange(L):
+            for l in range(L):
                 geom, ndim = geos_linearring_from_py(ob[l])
                 geos_holes[l] = cast(geom, c_void_p)
         else:
diff --git a/shapely/geometry/proxy.py b/shapely/geometry/proxy.py
index fe7ed12..a89c690 100644
--- a/shapely/geometry/proxy.py
+++ b/shapely/geometry/proxy.py
@@ -1,10 +1,8 @@
 """Proxy for coordinates stored outside Shapely geometries
 """
 
+from shapely.geometry.base import deserialize_wkb, EMPTY
 from shapely.geos import lgeos
-from shapely import wkb
-
-EMPTY = wkb.deserialize('010700000000000000'.decode('hex'))
 
 
 class CachingGeometryProxy(object):
diff --git a/shapely/geos.py b/shapely/geos.py
index eff83ff..5cb15a4 100644
--- a/shapely/geos.py
+++ b/shapely/geos.py
@@ -2,26 +2,33 @@
 Proxies for the libgeos_c shared lib, GEOS-specific exceptions, and utilities
 """
 
-import atexit
-import logging
 import os
+import re
 import sys
+import atexit
+import logging
 import threading
-import ctypes
-from ctypes import cdll, CDLL, CFUNCTYPE, c_char_p, c_void_p, string_at
+from ctypes import CDLL, cdll, pointer, c_void_p, c_size_t, c_char_p, string_at
 from ctypes.util import find_library
 
-import ftools
-from ctypes_declarations import prototype, EXCEPTION_HANDLER_FUNCTYPE
-
+from . import ftools
+from .ctypes_declarations import prototype, EXCEPTION_HANDLER_FUNCTYPE
 
 
-# Begin by creating a do-nothing handler and adding to this module's logger.
-class NullHandler(logging.Handler):
-    def emit(self, record):
-        pass
+# Add message handler to this module's logger
 LOG = logging.getLogger(__name__)
-LOG.addHandler(NullHandler())
+
+if 'all' in sys.warnoptions:
+    # show GEOS messages in console with: python -W all
+    logging.basicConfig()
+else:
+    # no handler messages shown
+    class NullHandler(logging.Handler):
+        def emit(self, record):
+            pass
+
+    LOG.addHandler(NullHandler())
+
 
 # Find and load the GEOS and C libraries
 # If this ever gets any longer, we'll break it into separate modules
@@ -42,7 +49,7 @@ def load_dll(libname, fallbacks=None):
         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'])
     free = load_dll('c').free
@@ -50,12 +57,17 @@ if sys.platform.startswith('linux'):
     free.restype = None
 
 elif sys.platform == 'darwin':
-    alt_paths = [
+    if hasattr(sys, 'frozen'):
+        # .app file from py2app
+        alt_paths = [os.path.join(os.environ['RESOURCEPATH'],
+                     '..', 'Frameworks', 'libgeos_c.dylib')]
+    else:
+        alt_paths = [
             # The Framework build from Kyng Chaos:
             "/Library/Frameworks/GEOS.framework/Versions/Current/GEOS",
             # macports
             '/opt/local/lib/libgeos_c.dylib',
-    ]
+        ]
     _lgeos = load_dll('geos_c', fallbacks=alt_paths)
     free = load_dll('c').free
     free.argtypes = [c_void_p]
@@ -64,13 +76,15 @@ elif sys.platform == 'darwin':
 elif sys.platform == 'win32':
     try:
         egg_dlls = os.path.abspath(os.path.join(os.path.dirname(__file__),
-                                     r"..\DLLs"))
-        wininst_dlls =  os.path.abspath(os.__file__ + "../../../DLLs")
+                                   "DLLs"))
+        wininst_dlls = os.path.abspath(os.__file__ + "../../../DLLs")
         original_path = os.environ['PATH']
-        os.environ['PATH'] = "%s;%s;%s" % (egg_dlls, wininst_dlls, original_path)
+        os.environ['PATH'] = "%s;%s;%s" % \
+            (egg_dlls, wininst_dlls, original_path)
         _lgeos = CDLL("geos.dll")
     except (ImportError, WindowsError, OSError):
         raise
+
     def free(m):
         try:
             cdll.msvcrt.free(m)
@@ -83,36 +97,44 @@ elif sys.platform == 'sunos5':
     free = CDLL('libc.so.1').free
     free.argtypes = [c_void_p]
     free.restype = None
-
-else: # other *nix systems
+else:  # other *nix systems
     _lgeos = load_dll('geos_c', fallbacks=['libgeos_c.so.1', 'libgeos_c.so'])
     free = load_dll('c', fallbacks=['libc.so.6']).free
     free.argtypes = [c_void_p]
     free.restype = None
 
-def _geos_c_version():
-    func = _lgeos.GEOSversion
-    func.argtypes = []
-    func.restype = c_char_p
-    v = func().split('-')[2]
-    return tuple(int(n) for n in v.split('.'))
 
-geos_capi_version = geos_c_version = _geos_c_version()
+def _geos_version():
+    # extern const char GEOS_DLL *GEOSversion();
+    GEOSversion = _lgeos.GEOSversion
+    GEOSversion.restype = c_char_p
+    GEOSversion.argtypes = []
+    #define GEOS_CAPI_VERSION "@VERSION at -CAPI-@CAPI_VERSION@"
+    geos_version_string = GEOSversion()
+    if sys.version_info[0] >= 3:
+        geos_version_string = geos_version_string.decode('ascii')
+    res = re.findall(r'(\d+)\.(\d+)\.(\d+)', geos_version_string)
+    assert len(res) == 2, res
+    geos_version = tuple(int(x) for x in res[0])
+    capi_version = tuple(int(x) for x in res[1])
+    return geos_version_string, geos_version, capi_version
+
+geos_version_string, geos_version, geos_capi_version = _geos_version()
 
 # If we have the new interface, then record a baseline so that we know what
 # additional functions are declared in ctypes_declarations.
-if geos_c_version >= (1,5,0):
+if geos_version >= (3, 1, 0):
     start_set = set(_lgeos.__dict__)
 
 # Apply prototypes for the libgeos_c functions
-prototype(_lgeos, geos_c_version)
+prototype(_lgeos, geos_version)
 
 # If we have the new interface, automatically detect all function
 # declarations, and declare their re-entrant counterpart.
-if geos_c_version >= (1,5,0):
+if geos_version >= (3, 1, 0):
     end_set = set(_lgeos.__dict__)
     new_func_names = end_set - start_set
-    
+
     for func_name in new_func_names:
         new_func_name = "%s_r" % func_name
         if hasattr(_lgeos, new_func_name):
@@ -127,102 +149,400 @@ if geos_c_version >= (1,5,0):
                 new_func.argtypes = [c_void_p] + old_func.argtypes
             if old_func.errcheck is not None:
                 new_func.errcheck = old_func.errcheck
-    
+
     # Handle special case.
     _lgeos.initGEOS_r.restype = c_void_p
-    _lgeos.initGEOS_r.argtypes = [EXCEPTION_HANDLER_FUNCTYPE, EXCEPTION_HANDLER_FUNCTYPE]
+    _lgeos.initGEOS_r.argtypes = \
+        [EXCEPTION_HANDLER_FUNCTYPE, EXCEPTION_HANDLER_FUNCTYPE]
     _lgeos.finishGEOS_r.argtypes = [c_void_p]
 
 # Exceptions
 
+
 class ReadingError(Exception):
     pass
 
+
 class DimensionError(Exception):
     pass
 
+
 class TopologicalError(Exception):
     pass
 
+
 class PredicateError(Exception):
     pass
 
-def error_handler(fmt, list):
-    LOG.error("%s", list)
-error_h = EXCEPTION_HANDLER_FUNCTYPE(error_handler)
 
-def notice_handler(fmt, list):
-    LOG.warning("%s", list)
-notice_h = EXCEPTION_HANDLER_FUNCTYPE(notice_handler)
+def error_handler(fmt, *args):
+    if sys.version_info[0] >= 3:
+        fmt = fmt.decode('ascii')
+        args = [arg.decode('ascii') for arg in args]
+    LOG.error(fmt, *args)
 
-def cleanup():
-    if _lgeos is not None :
-        _lgeos.finishGEOS()
 
-atexit.register(cleanup)
+def notice_handler(fmt, args):
+    if sys.version_info[0] >= 3:
+        fmt = fmt.decode('ascii')
+        args = args.decode('ascii')
+    LOG.warning(fmt, args)
+
+error_h = EXCEPTION_HANDLER_FUNCTYPE(error_handler)
+notice_h = EXCEPTION_HANDLER_FUNCTYPE(notice_handler)
 
-# Errcheck functions
+
+class WKTReader(object):
+
+    _lgeos = None
+    _reader = None
+
+    def __init__(self, lgeos):
+        """Create WKT Reader"""
+        self._lgeos = lgeos
+        self._reader = self._lgeos.GEOSWKTReader_create()
+
+    def __del__(self):
+        """Destroy WKT Reader"""
+        if self._lgeos is not None:
+            self._lgeos.GEOSWKTReader_destroy(self._reader)
+            self._reader = None
+            self._lgeos = None
+
+    def read(self, text):
+        """Returns geometry from WKT"""
+        if sys.version_info[0] >= 3:
+            text = text.encode('ascii')
+        geom = self._lgeos.GEOSWKTReader_read(self._reader, c_char_p(text))
+        if not geom:
+            raise ReadingError("Could not create geometry because of errors "
+                               "while reading input.")
+        # avoid circular import dependency
+        from shapely.geometry.base import geom_factory
+        return geom_factory(geom)
+
+
+class WKTWriter(object):
+
+    _lgeos = None
+    _writer = None
+
+    # Establish default output settings
+    defaults = {}
+
+    if geos_version >= (3, 3, 0):
+
+        defaults['trim'] = True
+        defaults['output_dimension'] = 3
+
+        # GEOS' defaults for methods without "get"
+        _trim = False
+        _rounding_precision = -1
+        _old_3d = False
+
+        @property
+        def trim(self):
+            """Trimming of unnecessary decimals (default: True)"""
+            return getattr(self, '_trim')
+
+        @trim.setter
+        def trim(self, value):
+            self._trim = bool(value)
+            self._lgeos.GEOSWKTWriter_setTrim(self._writer, self._trim)
+
+        @property
+        def rounding_precision(self):
+            """Rounding precision when writing the WKT.
+            A precision of -1 (default) disables it."""
+            return getattr(self, '_rounding_precision')
+
+        @rounding_precision.setter
+        def rounding_precision(self, value):
+            self._rounding_precision = int(value)
+            self._lgeos.GEOSWKTWriter_setRoundingPrecision(
+                self._writer, self._rounding_precision)
+
+        @property
+        def output_dimension(self):
+            """Output dimension, either 2 or 3 (default)"""
+            return self._lgeos.GEOSWKTWriter_getOutputDimension(
+                self._writer)
+
+        @output_dimension.setter
+        def output_dimension(self, value):
+            self._lgeos.GEOSWKTWriter_setOutputDimension(
+                self._writer, int(value))
+
+        @property
+        def old_3d(self):
+            """Show older style for 3D WKT, without 'Z' (default: False)"""
+            return getattr(self, '_old_3d')
+
+        @old_3d.setter
+        def old_3d(self, value):
+            self._old_3d = bool(value)
+            self._lgeos.GEOSWKTWriter_setOld3D(self._writer, self._old_3d)
+
+    def __init__(self, lgeos, **settings):
+        """Create WKT Writer
+
+        Note: writer defaults are set differently for GEOS 3.3.0 and up.
+        For example, with 'POINT Z (1 2 3)':
+
+            newer: POINT Z (1 2 3)
+            older: POINT (1.0000000000000000 2.0000000000000000)
+
+        The older formatting can be achieved for GEOS 3.3.0 and up by setting
+        the properties:
+            trim = False
+            output_dimension = 2
+        """
+        self._lgeos = lgeos
+        self._writer = self._lgeos.GEOSWKTWriter_create()
+
+        applied_settings = self.defaults.copy()
+        applied_settings.update(settings)
+        for name in applied_settings:
+            setattr(self, name, applied_settings[name])
+
+    def __setattr__(self, name, value):
+        """Limit setting attributes"""
+        if hasattr(self, name):
+            object.__setattr__(self, name, value)
+        else:
+            raise AttributeError('%r object has no attribute %r' %
+                                 (self.__class__.__name__, name))
+
+    def __del__(self):
+        """Destroy WKT Writer"""
+        if self._lgeos is not None:
+            self._lgeos.GEOSWKTWriter_destroy(self._writer)
+            self._writer = None
+            self._lgeos = None
+
+    def write(self, geom):
+        """Returns WKT string for geometry"""
+        if geom is None or geom._geom is None:
+            raise ValueError("Null geometry supports no operations")
+        result = self._lgeos.GEOSWKTWriter_write(self._writer, geom._geom)
+        text = string_at(result)
+        lgeos.GEOSFree(result)
+        if sys.version_info[0] >= 3:
+            return text.decode('ascii')
+        else:
+            return text
+
+
+class WKBReader(object):
+
+    _lgeos = None
+    _reader = None
+
+    def __init__(self, lgeos):
+        """Create WKB Reader"""
+        self._lgeos = lgeos
+        self._reader = self._lgeos.GEOSWKBReader_create()
+
+    def __del__(self):
+        """Destroy WKB Reader"""
+        if self._lgeos is not None:
+            self._lgeos.GEOSWKBReader_destroy(self._reader)
+            self._reader = None
+            self._lgeos = None
+
+    def read(self, data):
+        """Returns geometry from WKB"""
+        geom = self._lgeos.GEOSWKBReader_read(
+            self._reader, c_char_p(data), c_size_t(len(data)))
+        if not geom:
+            raise ReadingError("Could not create geometry because of errors "
+                               "while reading input.")
+        # avoid circular import dependency
+        from shapely import geometry
+        return geometry.base.geom_factory(geom)
+
+    def read_hex(self, data):
+        """Returns geometry from WKB hex"""
+        if sys.version_info[0] >= 3:
+            data = data.encode('ascii')
+        geom = self._lgeos.GEOSWKBReader_readHEX(
+            self._reader, c_char_p(data), c_size_t(len(data)))
+        if not geom:
+            raise ReadingError("Could not create geometry because of errors "
+                               "while reading input.")
+        # avoid circular import dependency
+        from shapely import geometry
+        return geometry.base.geom_factory(geom)
+
+
+class WKBWriter(object):
+
+    _lgeos = None
+    _writer = None
+
+    # Establish default output setting
+    defaults = {'output_dimension': 3}
+
+    @property
+    def output_dimension(self):
+        """Output dimension, either 2 or 3 (default)"""
+        return self._lgeos.GEOSWKBWriter_getOutputDimension(self._writer)
+
+    @output_dimension.setter
+    def output_dimension(self, value):
+        self._lgeos.GEOSWKBWriter_setOutputDimension(
+            self._writer, int(value))
+
+    @property
+    def big_endian(self):
+        """Byte order is big endian, True (default) or False"""
+        return bool(self._lgeos.GEOSWKBWriter_getByteOrder(self._writer))
+
+    @big_endian.setter
+    def big_endian(self, value):
+        self._lgeos.GEOSWKBWriter_setByteOrder(self._writer, bool(value))
+
+    @property
+    def include_srid(self):
+        """Include SRID, True or False (default)"""
+        return bool(self._lgeos.GEOSWKBWriter_getIncludeSRID(self._writer))
+
+    @include_srid.setter
+    def include_srid(self, value):
+        self._lgeos.GEOSWKBWriter_setIncludeSRID(self._writer, bool(value))
+
+    def __init__(self, lgeos, **settings):
+        """Create WKB Writer"""
+        self._lgeos = lgeos
+        self._writer = self._lgeos.GEOSWKBWriter_create()
+
+        applied_settings = self.defaults.copy()
+        applied_settings.update(settings)
+        for name in applied_settings:
+            setattr(self, name, applied_settings[name])
+
+    def __setattr__(self, name, value):
+        """Limit setting attributes"""
+        if hasattr(self, name):
+            object.__setattr__(self, name, value)
+        else:
+            raise AttributeError('%r object has no attribute %r' %
+                                 (self.__class__.__name__, name))
+
+    def __del__(self):
+        """Destroy WKB Writer"""
+        if self._lgeos is not None:
+            self._lgeos.GEOSWKBWriter_destroy(self._writer)
+            self._writer = None
+            self._lgeos = None
+
+    def write(self, geom):
+        """Returns WKB byte string for geometry"""
+        if geom is None or geom._geom is None:
+            raise ValueError("Null geometry supports no operations")
+        size = c_size_t()
+        result = self._lgeos.GEOSWKBWriter_write(
+            self._writer, geom._geom, pointer(size))
+        data = string_at(result, size.value)
+        lgeos.GEOSFree(result)
+        return data
+
+    def write_hex(self, geom):
+        """Returns WKB hex string for geometry"""
+        if geom is None or geom._geom is None:
+            raise ValueError("Null geometry supports no operations")
+        size = c_size_t()
+        result = self._lgeos.GEOSWKBWriter_writeHEX(
+            self._writer, geom._geom, pointer(size))
+        data = string_at(result, size.value)
+        lgeos.GEOSFree(result)
+        if sys.version_info[0] >= 3:
+            return data.decode('ascii')
+        else:
+            return data
+
+
+# Errcheck functions for ctypes
 
 def errcheck_wkb(result, func, argtuple):
+    '''Returns bytes from a C pointer'''
     if not result:
         return None
     size_ref = argtuple[-1]
     size = size_ref.contents
-    retval = ctypes.string_at(result, size.value)[:]
+    retval = string_at(result, size.value)[:]
     lgeos.GEOSFree(result)
     return retval
 
+
 def errcheck_just_free(result, func, argtuple):
+    '''Returns string from a C pointer'''
     retval = string_at(result)
     lgeos.GEOSFree(result)
-    return retval
+    if sys.version_info[0] >= 3:
+        return retval.decode('ascii')
+    else:
+        return retval
+
 
 def errcheck_predicate(result, func, argtuple):
+    '''Result is 2 on exception, 1 on True, 0 on False'''
     if result == 2:
         raise PredicateError("Failed to evaluate %s" % repr(func))
     return result
 
 
 class LGEOSBase(threading.local):
-    """Proxy for the GEOS_C DLL/SO
+    """Proxy for GEOS C API
 
     This is a base class. Do not instantiate.
     """
     methods = {}
+
     def __init__(self, dll):
         self._lgeos = dll
         self.geos_handle = None
-        
 
-class LGEOS14(LGEOSBase):    
-    """Proxy for the GEOS_C DLL/SO API version 1.4
+    def __del__(self):
+        """Cleanup GEOS related processes"""
+        if self._lgeos is not None:
+            self._lgeos.finishGEOS()
+            self._lgeos = None
+            self.geos_handle = None
+
+
+class LGEOS300(LGEOSBase):
+    """Proxy for GEOS 3.0.0-CAPI-1.4.1
     """
+    geos_version = (3, 0, 0)
     geos_capi_version = (1, 4, 0)
+
     def __init__(self, dll):
-        super(LGEOS14, self).__init__(dll)
+        super(LGEOS300, self).__init__(dll)
         self.geos_handle = self._lgeos.initGEOS(notice_h, error_h)
-        keys = self._lgeos.__dict__.keys()
+        keys = list(self._lgeos.__dict__.keys())
         for key in keys:
             setattr(self, key, getattr(self._lgeos, key))
         self.GEOSFree = self._lgeos.free
+        # Deprecated
         self.GEOSGeomToWKB_buf.errcheck = errcheck_wkb
         self.GEOSGeomToWKT.errcheck = errcheck_just_free
         self.GEOSRelate.errcheck = errcheck_just_free
-        for pred in ( self.GEOSDisjoint,
-              self.GEOSTouches,
-              self.GEOSIntersects,
-              self.GEOSCrosses,
-              self.GEOSWithin,
-              self.GEOSContains,
-              self.GEOSOverlaps,
-              self.GEOSEquals,
-              self.GEOSEqualsExact,
-              self.GEOSisEmpty,
-              self.GEOSisValid,
-              self.GEOSisSimple,
-              self.GEOSisRing,
-              self.GEOSHasZ
-              ):
+        for pred in (
+                self.GEOSDisjoint,
+                self.GEOSTouches,
+                self.GEOSIntersects,
+                self.GEOSCrosses,
+                self.GEOSWithin,
+                self.GEOSContains,
+                self.GEOSOverlaps,
+                self.GEOSEquals,
+                self.GEOSEqualsExact,
+                self.GEOSisEmpty,
+                self.GEOSisValid,
+                self.GEOSisSimple,
+                self.GEOSisRing,
+                self.GEOSHasZ):
             pred.errcheck = errcheck_predicate
 
         self.methods['area'] = self.GEOSArea
@@ -258,15 +578,17 @@ class LGEOS14(LGEOSBase):
             self.GEOSTopologyPreserveSimplify
 
 
-class LGEOS15(LGEOSBase):    
-    """Proxy for the reentrant GEOS_C DLL/SO API version 1.5
+class LGEOS310(LGEOSBase):
+    """Proxy for GEOS 3.1.0-CAPI-1.5.0
     """
-    geos_capi_version = (1, 5, 0)    
+    geos_version = (3, 1, 0)
+    geos_capi_version = (1, 5, 0)
+
     def __init__(self, dll):
-        super(LGEOS15, self).__init__(dll)
+        super(LGEOS310, self).__init__(dll)
         self.geos_handle = self._lgeos.initGEOS_r(notice_h, error_h)
-        keys = self._lgeos.__dict__.keys()
-        for key in filter(lambda x: not x.endswith('_r'), keys):
+        keys = list(self._lgeos.__dict__.keys())
+        for key in [x for x in keys if not x.endswith('_r')]:
             if key + '_r' in keys:
                 reentr_func = getattr(self._lgeos, key + '_r')
                 attr = ftools.partial(reentr_func, self.geos_handle)
@@ -275,29 +597,31 @@ class LGEOS15(LGEOSBase):
             else:
                 setattr(self, key, getattr(self._lgeos, key))
         if not hasattr(self, 'GEOSFree'):
+            # GEOS < 3.1.1
             self.GEOSFree = self._lgeos.free
+        # Deprecated
         self.GEOSGeomToWKB_buf.func.errcheck = errcheck_wkb
         self.GEOSGeomToWKT.func.errcheck = errcheck_just_free
         self.GEOSRelate.func.errcheck = errcheck_just_free
-        for pred in ( self.GEOSDisjoint,
-              self.GEOSTouches,
-              self.GEOSIntersects,
-              self.GEOSCrosses,
-              self.GEOSWithin,
-              self.GEOSContains,
-              self.GEOSOverlaps,
-              self.GEOSEquals,
-              self.GEOSEqualsExact,
-              self.GEOSisEmpty,
-              self.GEOSisValid,
-              self.GEOSisSimple,
-              self.GEOSisRing,
-              self.GEOSHasZ
-              ):
+        for pred in (
+                self.GEOSDisjoint,
+                self.GEOSTouches,
+                self.GEOSIntersects,
+                self.GEOSCrosses,
+                self.GEOSWithin,
+                self.GEOSContains,
+                self.GEOSOverlaps,
+                self.GEOSEquals,
+                self.GEOSEqualsExact,
+                self.GEOSisEmpty,
+                self.GEOSisValid,
+                self.GEOSisSimple,
+                self.GEOSisRing,
+                self.GEOSHasZ):
             pred.func.errcheck = errcheck_predicate
 
         self.GEOSisValidReason.func.errcheck = errcheck_just_free
-        
+
         self.methods['area'] = self.GEOSArea
         self.methods['boundary'] = self.GEOSBoundary
         self.methods['buffer'] = self.GEOSBuffer
@@ -334,22 +658,27 @@ class LGEOS15(LGEOSBase):
         self.methods['simplify'] = self.GEOSSimplify
         self.methods['topology_preserve_simplify'] = \
             self.GEOSTopologyPreserveSimplify
+        self.methods['cascaded_union'] = self.GEOSUnionCascaded
+
 
-class LGEOS16(LGEOS15):
-    """Proxy for the reentrant GEOS_C DLL/SO API version 1.6
+class LGEOS311(LGEOS310):
+    """Proxy for GEOS 3.1.1-CAPI-1.6.0
     """
+    geos_version = (3, 1, 1)
     geos_capi_version = (1, 6, 0)
+
     def __init__(self, dll):
-        super(LGEOS16, self).__init__(dll)
+        super(LGEOS311, self).__init__(dll)
 
 
-class LGEOS16LR(LGEOS16):    
-    """Proxy for the reentrant GEOS_C DLL/SO API version 1.6 with linear
-    referencing
+class LGEOS320(LGEOS311):
+    """Proxy for GEOS 3.2.0-CAPI-1.6.0
     """
-    geos_capi_version = geos_c_version
+    geos_version = (3, 2, 0)
+    geos_capi_version = (1, 6, 0)
+
     def __init__(self, dll):
-        super(LGEOS16LR, self).__init__(dll)
+        super(LGEOS320, self).__init__(dll)
 
         self.methods['parallel_offset'] = self.GEOSSingleSidedBuffer
         self.methods['project'] = self.GEOSProject
@@ -357,17 +686,44 @@ class LGEOS16LR(LGEOS16):
         self.methods['interpolate'] = self.GEOSInterpolate
         self.methods['interpolate_normalized'] = \
             self.GEOSInterpolateNormalized
+        self.methods['buffer_with_style'] = self.GEOSBufferWithStyle
 
 
-if geos_c_version >= (1, 6, 0):
-    if hasattr(_lgeos, 'GEOSProject'):
-        L = LGEOS16LR
-    else:
-        L = LGEOS16
-elif geos_c_version >= (1, 5, 0):
-        L = LGEOS15
+class LGEOS330(LGEOS320):
+    """Proxy for GEOS 3.3.0-CAPI-1.7.0
+    """
+    geos_version = (3, 3, 0)
+    geos_capi_version = (1, 7, 0)
+
+    def __init__(self, dll):
+        super(LGEOS330, self).__init__(dll)
+
+        # GEOS 3.3.8 from homebrew has, but doesn't advertise
+        # GEOSPolygonize_full. We patch it in explicitly here.
+        key = 'GEOSPolygonize_full'
+        func = getattr(self._lgeos, key + '_r')
+        attr = ftools.partial(func, self.geos_handle)
+        attr.__name__ = func.__name__
+        setattr(self, key, attr)
+
+        self.methods['unary_union'] = self.GEOSUnaryUnion
+        self.methods['cascaded_union'] = self.methods['unary_union']
+
+
+if geos_version >= (3, 3, 0):
+    L = LGEOS330
+elif geos_version >= (3, 2, 0):
+    L = LGEOS320
+elif geos_version >= (3, 1, 1):
+    L = LGEOS311
+elif geos_version >= (3, 1, 0):
+    L = LGEOS310
 else:
-        L = LGEOS14
+    L = LGEOS300
 
 lgeos = L(_lgeos)
 
+
+ at atexit.register
+def cleanup():
+    lgeos.__del__()
diff --git a/shapely/impl.py b/shapely/impl.py
index 214a333..c71518e 100644
--- a/shapely/impl.py
+++ b/shapely/impl.py
@@ -4,14 +4,14 @@ This is layer number 2 from the list below.
 
 1) geometric objects: the Python OO API.
 2) implementation map: an abstraction that permits different backends.
-3) backend: callable objects that take Shapely geometric objects as arguments 
+3) backend: callable objects that take Shapely geometric objects as arguments
    and, with GEOS as a backend, translate them to C data structures.
 4) GEOS library: algorithms implemented in C++.
 
 Shapely 1.2 includes a GEOS backend and it is the default.
 """
 
-from ftools import wraps
+from .ftools import wraps
 
 from shapely.algorithms import cga
 from shapely.coords import BoundsOp
@@ -22,15 +22,15 @@ from shapely.topology import BinaryRealProperty, BinaryTopologicalOp
 from shapely.topology import UnaryRealProperty, UnaryTopologicalOp
 
 def delegated(func):
-    """A delegated method raises AttributeError in the absence of backend 
+    """A delegated method raises AttributeError in the absence of backend
     support."""
     @wraps(func)
     def wrapper(*args, **kwargs):
         try:
             return func(*args, **kwargs)
         except KeyError:
-            raise AttributeError, "Method '%s' is not supported by %s" % (
-                func.__name__, repr(args[0].impl))
+            raise AttributeError("Method %r is not supported by %r" %
+                                 (func.__name__, args[0].impl))
     return wrapper
 
 # Map geometry methods to their GEOS delegates
@@ -42,13 +42,15 @@ class BaseImpl(object):
         self.map.update(values)
     def __getitem__(self, key):
         return self.map[key]
+    def __contains__(self, key):
+        return key in self.map
 
 class GEOSImpl(BaseImpl):
     def __repr__(self):
         return '<GEOSImpl object: GEOS C API version %s>' % (
             lgeos.geos_capi_version,)
 
-IMPL14 = {
+IMPL300 = {
     'area': (UnaryRealProperty, 'area'),
     'distance': (BinaryRealProperty, 'distance'),
     'length': (UnaryRealProperty, 'length'),
@@ -82,42 +84,43 @@ IMPL14 = {
     'touches': (BinaryPredicate, 'touches'),
     'within': (BinaryPredicate, 'within'),
     'equals_exact': (BinaryPredicate, 'equals_exact'),
-    
+
     # First pure Python implementation
     'is_ccw': (cga.is_ccw_impl, 'is_ccw'),
     }
 
-IMPL15 = {
+IMPL310 = {
     'simplify': (UnaryTopologicalOp, 'simplify'),
-    'topology_preserve_simplify': 
+    'topology_preserve_simplify':
         (UnaryTopologicalOp, 'topology_preserve_simplify'),
     'prepared_intersects': (BinaryPredicate, 'prepared_intersects'),
     'prepared_contains': (BinaryPredicate, 'prepared_contains'),
-    'prepared_contains_properly': 
+    'prepared_contains_properly':
         (BinaryPredicate, 'prepared_contains_properly'),
     'prepared_covers': (BinaryPredicate, 'prepared_covers'),
     }
 
-IMPL16 = {
+IMPL311 = {
     }
 
-IMPL16LR = {
-	'parallel_offset': (UnaryTopologicalOp, 'parallel_offset'),
-	'project_normalized': (ProjectOp, 'project_normalized'),
+IMPL320 = {
+    'parallel_offset': (UnaryTopologicalOp, 'parallel_offset'),
+    'project_normalized': (ProjectOp, 'project_normalized'),
     'project': (ProjectOp, 'project'),
     'interpolate_normalized': (InterpolateOp, 'interpolate_normalized'),
     'interpolate': (InterpolateOp, 'interpolate'),
+    'buffer_with_style': (UnaryTopologicalOp, 'buffer_with_style'),
     }
 
 def impl_items(defs):
-    return [(k, v[0](v[1])) for k, v in defs.items()]
-
-imp = GEOSImpl(dict(impl_items(IMPL14)))
-if lgeos.geos_capi_version >= (1, 5, 0):
-    imp.update(impl_items(IMPL15))
-if lgeos.geos_capi_version >= (1, 6, 0):
-    imp.update(impl_items(IMPL16))
-    if 'project' in lgeos.methods:
-        imp.update(impl_items(IMPL16LR))
+    return [(k, v[0](v[1])) for k, v in list(defs.items())]
+
+imp = GEOSImpl(dict(impl_items(IMPL300)))
+if lgeos.geos_version >= (3, 1, 0):
+    imp.update(impl_items(IMPL310))
+if lgeos.geos_version >= (3, 1, 1):
+    imp.update(impl_items(IMPL311))
+if lgeos.geos_version >= (3, 2, 0):
+    imp.update(impl_items(IMPL320))
 
 DefaultImplementation = imp
diff --git a/shapely/iterops.py b/shapely/iterops.py
index 894aa9a..70faef0 100644
--- a/shapely/iterops.py
+++ b/shapely/iterops.py
@@ -1,12 +1,14 @@
 """
 Iterative forms of operations
 """
-
+from warnings import warn
 from ctypes import c_char_p, c_size_t
 from shapely.geos import lgeos, PredicateError
 
 
 def geos_from_geometry(geom):
+    warn("`geos_from_geometry` is deprecated. Use geometry's `wkb` property "
+         "instead.", DeprecationWarning)
     data = geom.to_wkb()
     return lgeos.GEOSGeomFromWKB_buf(
                         c_char_p(data),
diff --git a/shapely/ops.py b/shapely/ops.py
index 71b77e8..1f2387a 100644
--- a/shapely/ops.py
+++ b/shapely/ops.py
@@ -1,13 +1,21 @@
 """Support for various GEOS geometry operations
 """
 
+import sys
+
+if sys.version_info[0] < 3:
+    from itertools import izip
+else:
+    izip = zip
+
 from ctypes import byref, c_void_p
 
 from shapely.geos import lgeos
 from shapely.geometry.base import geom_factory, BaseGeometry
 from shapely.geometry import asShape, asLineString, asMultiLineString
 
-__all__= ['operator', 'polygonize', 'linemerge', 'cascaded_union']
+__all__ = ['cascaded_union', 'linemerge', 'operator', 'polygonize',
+           'polygonize_full', 'transform', 'unary_union']
 
 
 class CollectionOperator(object):
@@ -23,7 +31,7 @@ class CollectionOperator(object):
 
     def polygonize(self, lines):
         """Creates polygons from a source of lines
-        
+
         The source may be a MultiLineString, a sequence of LineString objects,
         or a sequence of objects than can be adapted to LineStrings.
         """
@@ -41,29 +49,64 @@ class CollectionOperator(object):
             g._owned = False
             yield g
 
-    def linemerge(self, lines): 
+    def polygonize_full(self, lines):
+        """Creates polygons from a source of lines, returning the polygons
+        and leftover geometries.
+
+        The source may be a MultiLineString, a sequence of LineString objects,
+        or a sequence of objects than can be adapted to LineStrings.
+
+        Returns a tuple of objects: (polygons, dangles, cut edges, invalid ring
+        lines). Each are a geometry collection.
+
+        Dangles are edges which have one or both ends which are not incident on
+        another edge endpoint. Cut edges are connected at both ends but do not
+        form part of polygon. Invalid ring lines form rings which are invalid
+        (bowties, etc).
+        """
+        source = getattr(lines, 'geoms', None) or lines
+        obs = [self.shapeup(l) for l in source]
+
+        L = len(obs)
+        subs = (c_void_p * L)()
+        for i, g in enumerate(obs):
+            subs[i] = g._geom
+        collection = lgeos.GEOSGeom_createCollection(5, subs, L)
+        dangles = c_void_p()
+        cuts = c_void_p()
+        invalids = c_void_p()
+        product = lgeos.GEOSPolygonize_full(
+            collection, byref(dangles), byref(cuts), byref(invalids))
+        return (
+            geom_factory(product),
+            geom_factory(dangles),
+            geom_factory(cuts),
+            geom_factory(invalids)
+            )
+
+    def linemerge(self, lines):
         """Merges all connected lines from a source
-        
+
         The source may be a MultiLineString, a sequence of LineString objects,
         or a sequence of objects than can be adapted to LineStrings.  Returns a
-        LineString or MultiLineString when lines are not contiguous. 
-        """ 
-        source = None 
-        if hasattr(lines, 'type') and lines.type == 'MultiLineString': 
-            source = lines 
-        elif hasattr(lines, '__iter__'): 
-            try: 
-                source = asMultiLineString([ls.coords for ls in lines]) 
-            except AttributeError: 
-                source = asMultiLineString(lines) 
-        if source is None: 
+        LineString or MultiLineString when lines are not contiguous.
+        """
+        source = None
+        if hasattr(lines, 'type') and lines.type == 'MultiLineString':
+            source = lines
+        elif hasattr(lines, '__iter__'):
+            try:
+                source = asMultiLineString([ls.coords for ls in lines])
+            except AttributeError:
+                source = asMultiLineString(lines)
+        if source is None:
             raise ValueError("Cannot linemerge %s" % lines)
-        result = lgeos.GEOSLineMerge(source._geom) 
-        return geom_factory(result)   
+        result = lgeos.GEOSLineMerge(source._geom)
+        return geom_factory(result)
 
     def cascaded_union(self, geoms):
         """Returns the union of a sequence of geometries
-        
+
         This is the most efficient method of dissolving many polygons.
         """
         L = len(geoms)
@@ -71,13 +114,29 @@ class CollectionOperator(object):
         for i, g in enumerate(geoms):
             subs[i] = g._geom
         collection = lgeos.GEOSGeom_createCollection(6, subs, L)
-        return geom_factory(lgeos.GEOSUnionCascaded(collection))
+        return geom_factory(lgeos.methods['cascaded_union'](collection))
+
+    def unary_union(self, geoms):
+        """Returns the union of a sequence of geometries
+
+        This method replaces :meth:`cascaded_union` as the
+        prefered method for dissolving many polygons.
 
+        """
+        L = len(geoms)
+        subs = (c_void_p * L)()
+        for i, g in enumerate(geoms):
+            subs[i] = g._geom
+        collection = lgeos.GEOSGeom_createCollection(6, subs, L)
+        return geom_factory(lgeos.methods['unary_union'](collection))
 
 operator = CollectionOperator()
 polygonize = operator.polygonize
+polygonize_full = operator.polygonize_full
 linemerge = operator.linemerge
 cascaded_union = operator.cascaded_union
+unary_union = operator.unary_union
+
 
 class ValidateOp(object):
     def __call__(self, this):
@@ -85,3 +144,74 @@ class ValidateOp(object):
 
 validate = ValidateOp()
 
+
+def transform(func, geom):
+    """Applies `func` to all coordinates of `geom` and returns a new
+    geometry of the same type from the transformed coordinates.
+
+    `func` maps x, y, and optionally z to output xp, yp, zp. The input
+    parameters may iterable types like lists or arrays or single values.
+    The output shall be of the same type. Scalars in, scalars out.
+    Lists in, lists out.
+
+    For example, here is an identity function applicable to both types
+    of input.
+
+      def id_func(x, y, z=None):
+          return tuple(filter(None, [x, y, z]))
+
+      g2 = transform(id_func, g1)
+
+    A partially applied transform function from pyproj satisfies the
+    requirements for `func`.
+
+      from functools import partial
+      import pyproj
+
+      project = partial(
+          pyproj.transform,
+          pyproj.Proj(init='espg:4326'),
+          pyproj.Proj(init='epsg:26913'))
+
+      g2 = transform(project, g1)
+
+    Lambda expressions such as the one in
+
+      g2 = transform(lambda x, y, z=None: (x+1.0, y+1.0), g1)
+
+    also satisfy the requirements for `func`.
+    """
+    if geom.is_empty:
+        return geom
+    if geom.type in ('Point', 'LineString', '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'):
+                return type(geom)(zip(*func(*izip(*geom.coords))))
+            elif geom.type == 'Polygon':
+                shell = type(geom.exterior)(
+                    zip(*func(*izip(*geom.exterior.coords))))
+                holes = list(type(ring)(zip(*func(*izip(*ring.coords))))
+                             for ring in geom.interiors)
+                return type(geom)(shell, holes)
+
+        # 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'):
+                return type(geom)([func(*c) for c in geom.coords])
+            elif geom.type == 'Polygon':
+                shell = type(geom.exterior)(
+                    [func(*c) for c in geom.exterior.coords])
+                holes = list(type(ring)([func(*c) for c in ring.coords])
+                             for ring in geom.interiors)
+                return type(geom)(shell, holes)
+
+    elif geom.type.startswith('Multi') or geom.type == 'GeometryCollection':
+        return type(geom)([transform(func, part) for part in geom.geoms])
+    else:
+        raise ValueError('Type %r not recognized' % geom.type)
diff --git a/shapely/speedups/__init__.py b/shapely/speedups/__init__.py
index c3f932a..a3d3e5f 100644
--- a/shapely/speedups/__init__.py
+++ b/shapely/speedups/__init__.py
@@ -10,7 +10,8 @@ try:
 except ImportError:
     import sys
     available = False
-    import_error_msg = tuple(sys.exc_info()[1])
+    # TODO: This does not appear to do anything useful
+    import_error_msg = sys.exc_info()[1]
 
 __all__ = ['available', 'enable', 'disable']
 _orig = {}
@@ -39,4 +40,4 @@ def disable():
     coords.CoordinateSequence.ctypes = _orig['CoordinateSequence.ctypes']
     linestring.geos_linestring_from_py = _orig['geos_linestring_from_py']
     polygon.geos_linearring_from_py = _orig['geos_linearring_from_py']
-    _orig.clear()
\ No newline at end of file
+    _orig.clear()
diff --git a/shapely/speedups/_speedups.c b/shapely/speedups/_speedups.c
deleted file mode 100644
index d3e27bc..0000000
--- a/shapely/speedups/_speedups.c
+++ /dev/null
@@ -1,4560 +0,0 @@
-/* Generated by Cython 0.14 on Wed Sep  7 22:30:12 2011 */
-
-#define PY_SSIZE_T_CLEAN
-#include "Python.h"
-#ifndef Py_PYTHON_H
-    #error Python headers needed to compile C extensions, please install development version of Python.
-#else
-
-#include <stddef.h> /* For offsetof */
-#ifndef offsetof
-#define offsetof(type, member) ( (size_t) & ((type*)0) -> member )
-#endif
-
-#if !defined(WIN32) && !defined(MS_WINDOWS)
-  #ifndef __stdcall
-    #define __stdcall
-  #endif
-  #ifndef __cdecl
-    #define __cdecl
-  #endif
-  #ifndef __fastcall
-    #define __fastcall
-  #endif
-#endif
-
-#ifndef DL_IMPORT
-  #define DL_IMPORT(t) t
-#endif
-#ifndef DL_EXPORT
-  #define DL_EXPORT(t) t
-#endif
-
-#ifndef PY_LONG_LONG
-  #define PY_LONG_LONG LONG_LONG
-#endif
-
-#if PY_VERSION_HEX < 0x02040000
-  #define METH_COEXIST 0
-  #define PyDict_CheckExact(op) (Py_TYPE(op) == &PyDict_Type)
-  #define PyDict_Contains(d,o)   PySequence_Contains(d,o)
-#endif
-
-#if PY_VERSION_HEX < 0x02050000
-  typedef int Py_ssize_t;
-  #define PY_SSIZE_T_MAX INT_MAX
-  #define PY_SSIZE_T_MIN INT_MIN
-  #define PY_FORMAT_SIZE_T ""
-  #define PyInt_FromSsize_t(z) PyInt_FromLong(z)
-  #define PyInt_AsSsize_t(o)   PyInt_AsLong(o)
-  #define PyNumber_Index(o)    PyNumber_Int(o)
-  #define PyIndex_Check(o)     PyNumber_Check(o)
-  #define PyErr_WarnEx(category, message, stacklevel) PyErr_Warn(category, message)
-#endif
-
-#if PY_VERSION_HEX < 0x02060000
-  #define Py_REFCNT(ob) (((PyObject*)(ob))->ob_refcnt)
-  #define Py_TYPE(ob)   (((PyObject*)(ob))->ob_type)
-  #define Py_SIZE(ob)   (((PyVarObject*)(ob))->ob_size)
-  #define PyVarObject_HEAD_INIT(type, size) \
-          PyObject_HEAD_INIT(type) size,
-  #define PyType_Modified(t)
-
-  typedef struct {
-     void *buf;
-     PyObject *obj;
-     Py_ssize_t len;
-     Py_ssize_t itemsize;
-     int readonly;
-     int ndim;
-     char *format;
-     Py_ssize_t *shape;
-     Py_ssize_t *strides;
-     Py_ssize_t *suboffsets;
-     void *internal;
-  } Py_buffer;
-
-  #define PyBUF_SIMPLE 0
-  #define PyBUF_WRITABLE 0x0001
-  #define PyBUF_FORMAT 0x0004
-  #define PyBUF_ND 0x0008
-  #define PyBUF_STRIDES (0x0010 | PyBUF_ND)
-  #define PyBUF_C_CONTIGUOUS (0x0020 | PyBUF_STRIDES)
-  #define PyBUF_F_CONTIGUOUS (0x0040 | PyBUF_STRIDES)
-  #define PyBUF_ANY_CONTIGUOUS (0x0080 | PyBUF_STRIDES)
-  #define PyBUF_INDIRECT (0x0100 | PyBUF_STRIDES)
-
-#endif
-
-#if PY_MAJOR_VERSION < 3
-  #define __Pyx_BUILTIN_MODULE_NAME "__builtin__"
-#else
-  #define __Pyx_BUILTIN_MODULE_NAME "builtins"
-#endif
-
-#if PY_MAJOR_VERSION >= 3
-  #define Py_TPFLAGS_CHECKTYPES 0
-  #define Py_TPFLAGS_HAVE_INDEX 0
-#endif
-
-#if (PY_VERSION_HEX < 0x02060000) || (PY_MAJOR_VERSION >= 3)
-  #define Py_TPFLAGS_HAVE_NEWBUFFER 0
-#endif
-
-#if PY_MAJOR_VERSION >= 3
-  #define PyBaseString_Type            PyUnicode_Type
-  #define PyStringObject               PyUnicodeObject
-  #define PyString_Type                PyUnicode_Type
-  #define PyString_Check               PyUnicode_Check
-  #define PyString_CheckExact          PyUnicode_CheckExact
-#endif
-
-#if PY_VERSION_HEX < 0x02060000
-  #define PyBytesObject                PyStringObject
-  #define PyBytes_Type                 PyString_Type
-  #define PyBytes_Check                PyString_Check
-  #define PyBytes_CheckExact           PyString_CheckExact
-  #define PyBytes_FromString           PyString_FromString
-  #define PyBytes_FromStringAndSize    PyString_FromStringAndSize
-  #define PyBytes_FromFormat           PyString_FromFormat
-  #define PyBytes_DecodeEscape         PyString_DecodeEscape
-  #define PyBytes_AsString             PyString_AsString
-  #define PyBytes_AsStringAndSize      PyString_AsStringAndSize
-  #define PyBytes_Size                 PyString_Size
-  #define PyBytes_AS_STRING            PyString_AS_STRING
-  #define PyBytes_GET_SIZE             PyString_GET_SIZE
-  #define PyBytes_Repr                 PyString_Repr
-  #define PyBytes_Concat               PyString_Concat
-  #define PyBytes_ConcatAndDel         PyString_ConcatAndDel
-  #define PySet_Check(obj)             PyObject_TypeCheck(obj, &PySet_Type)
-  #define PyFrozenSet_Check(obj)       PyObject_TypeCheck(obj, &PyFrozenSet_Type)
-#endif
-
-#ifndef PySet_CheckExact
-#  define PySet_CheckExact(obj)          (Py_TYPE(obj) == &PySet_Type)
-#endif
-
-#if PY_MAJOR_VERSION >= 3
-  #define PyIntObject                  PyLongObject
-  #define PyInt_Type                   PyLong_Type
-  #define PyInt_Check(op)              PyLong_Check(op)
-  #define PyInt_CheckExact(op)         PyLong_CheckExact(op)
-  #define PyInt_FromString             PyLong_FromString
-  #define PyInt_FromUnicode            PyLong_FromUnicode
-  #define PyInt_FromLong               PyLong_FromLong
-  #define PyInt_FromSize_t             PyLong_FromSize_t
-  #define PyInt_FromSsize_t            PyLong_FromSsize_t
-  #define PyInt_AsLong                 PyLong_AsLong
-  #define PyInt_AS_LONG                PyLong_AS_LONG
-  #define PyInt_AsSsize_t              PyLong_AsSsize_t
-  #define PyInt_AsUnsignedLongMask     PyLong_AsUnsignedLongMask
-  #define PyInt_AsUnsignedLongLongMask PyLong_AsUnsignedLongLongMask
-#endif
-
-#if PY_MAJOR_VERSION >= 3
-  #define PyBoolObject PyLongObject
-#endif
-
-
-#if PY_MAJOR_VERSION >= 3
-  #define __Pyx_PyNumber_Divide(x,y)         PyNumber_TrueDivide(x,y)
-  #define __Pyx_PyNumber_InPlaceDivide(x,y)  PyNumber_InPlaceTrueDivide(x,y)
-#else
-  #define __Pyx_PyNumber_Divide(x,y)         PyNumber_Divide(x,y)
-  #define __Pyx_PyNumber_InPlaceDivide(x,y)  PyNumber_InPlaceDivide(x,y)
-#endif
-
-#if (PY_MAJOR_VERSION < 3) || (PY_VERSION_HEX >= 0x03010300)
-  #define __Pyx_PySequence_GetSlice(obj, a, b) PySequence_GetSlice(obj, a, b)
-  #define __Pyx_PySequence_SetSlice(obj, a, b, value) PySequence_SetSlice(obj, a, b, value)
-  #define __Pyx_PySequence_DelSlice(obj, a, b) PySequence_DelSlice(obj, a, b)
-#else
-  #define __Pyx_PySequence_GetSlice(obj, a, b) (unlikely(!(obj)) ? \
-        (PyErr_SetString(PyExc_SystemError, "null argument to internal routine"), (PyObject*)0) : \
-        (likely((obj)->ob_type->tp_as_mapping) ? (PySequence_GetSlice(obj, a, b)) : \
-            (PyErr_Format(PyExc_TypeError, "'%.200s' object is unsliceable", (obj)->ob_type->tp_name), (PyObject*)0)))
-  #define __Pyx_PySequence_SetSlice(obj, a, b, value) (unlikely(!(obj)) ? \
-        (PyErr_SetString(PyExc_SystemError, "null argument to internal routine"), -1) : \
-        (likely((obj)->ob_type->tp_as_mapping) ? (PySequence_SetSlice(obj, a, b, value)) : \
-            (PyErr_Format(PyExc_TypeError, "'%.200s' object doesn't support slice assignment", (obj)->ob_type->tp_name), -1)))
-  #define __Pyx_PySequence_DelSlice(obj, a, b) (unlikely(!(obj)) ? \
-        (PyErr_SetString(PyExc_SystemError, "null argument to internal routine"), -1) : \
-        (likely((obj)->ob_type->tp_as_mapping) ? (PySequence_DelSlice(obj, a, b)) : \
-            (PyErr_Format(PyExc_TypeError, "'%.200s' object doesn't support slice deletion", (obj)->ob_type->tp_name), -1)))
-#endif
-
-#if PY_MAJOR_VERSION >= 3
-  #define PyMethod_New(func, self, klass) ((self) ? PyMethod_New(func, self) : PyInstanceMethod_New(func))
-#endif
-
-#if PY_VERSION_HEX < 0x02050000
-  #define __Pyx_GetAttrString(o,n)   PyObject_GetAttrString((o),((char *)(n)))
-  #define __Pyx_SetAttrString(o,n,a) PyObject_SetAttrString((o),((char *)(n)),(a))
-  #define __Pyx_DelAttrString(o,n)   PyObject_DelAttrString((o),((char *)(n)))
-#else
-  #define __Pyx_GetAttrString(o,n)   PyObject_GetAttrString((o),(n))
-  #define __Pyx_SetAttrString(o,n,a) PyObject_SetAttrString((o),(n),(a))
-  #define __Pyx_DelAttrString(o,n)   PyObject_DelAttrString((o),(n))
-#endif
-
-#if PY_VERSION_HEX < 0x02050000
-  #define __Pyx_NAMESTR(n) ((char *)(n))
-  #define __Pyx_DOCSTR(n)  ((char *)(n))
-#else
-  #define __Pyx_NAMESTR(n) (n)
-  #define __Pyx_DOCSTR(n)  (n)
-#endif
-
-#ifdef __cplusplus
-#define __PYX_EXTERN_C extern "C"
-#else
-#define __PYX_EXTERN_C extern
-#endif
-
-#if defined(WIN32) || defined(MS_WINDOWS)
-#define _USE_MATH_DEFINES
-#endif
-#include <math.h>
-#define __PYX_HAVE_API__shapely__speedups___speedups
-#include "geos_c.h"
-
-#ifdef PYREX_WITHOUT_ASSERTIONS
-#define CYTHON_WITHOUT_ASSERTIONS
-#endif
-
-
-/* inline attribute */
-#ifndef CYTHON_INLINE
-  #if defined(__GNUC__)
-    #define CYTHON_INLINE __inline__
-  #elif defined(_MSC_VER)
-    #define CYTHON_INLINE __inline
-  #elif defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
-    #define CYTHON_INLINE inline
-  #else
-    #define CYTHON_INLINE
-  #endif
-#endif
-
-/* unused attribute */
-#ifndef CYTHON_UNUSED
-# if defined(__GNUC__)
-#   if !(defined(__cplusplus)) || (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4))
-#     define CYTHON_UNUSED __attribute__ ((__unused__))
-#   else
-#     define CYTHON_UNUSED
-#   endif
-# elif defined(__ICC) || defined(__INTEL_COMPILER)
-#   define CYTHON_UNUSED __attribute__ ((__unused__))
-# else
-#   define CYTHON_UNUSED
-# endif
-#endif
-
-typedef struct {PyObject **p; char *s; const long n; const char* encoding; const char is_unicode; const char is_str; const char intern; } __Pyx_StringTabEntry; /*proto*/
-
-
-/* Type Conversion Predeclarations */
-
-#define __Pyx_PyBytes_FromUString(s) PyBytes_FromString((char*)s)
-#define __Pyx_PyBytes_AsUString(s)   ((unsigned char*) PyBytes_AsString(s))
-
-#define __Pyx_PyBool_FromLong(b) ((b) ? (Py_INCREF(Py_True), Py_True) : (Py_INCREF(Py_False), Py_False))
-static CYTHON_INLINE int __Pyx_PyObject_IsTrue(PyObject*);
-static CYTHON_INLINE PyObject* __Pyx_PyNumber_Int(PyObject* x);
-
-static CYTHON_INLINE Py_ssize_t __Pyx_PyIndex_AsSsize_t(PyObject*);
-static CYTHON_INLINE PyObject * __Pyx_PyInt_FromSize_t(size_t);
-static CYTHON_INLINE size_t __Pyx_PyInt_AsSize_t(PyObject*);
-
-#define __pyx_PyFloat_AsDouble(x) (PyFloat_CheckExact(x) ? PyFloat_AS_DOUBLE(x) : PyFloat_AsDouble(x))
-
-
-#ifdef __GNUC__
-/* Test for GCC > 2.95 */
-#if __GNUC__ > 2 || (__GNUC__ == 2 && (__GNUC_MINOR__ > 95))
-#define likely(x)   __builtin_expect(!!(x), 1)
-#define unlikely(x) __builtin_expect(!!(x), 0)
-#else /* __GNUC__ > 2 ... */
-#define likely(x)   (x)
-#define unlikely(x) (x)
-#endif /* __GNUC__ > 2 ... */
-#else /* __GNUC__ */
-#define likely(x)   (x)
-#define unlikely(x) (x)
-#endif /* __GNUC__ */
-    
-static PyObject *__pyx_m;
-static PyObject *__pyx_b;
-static PyObject *__pyx_empty_tuple;
-static PyObject *__pyx_empty_bytes;
-static int __pyx_lineno;
-static int __pyx_clineno = 0;
-static const char * __pyx_cfilenm= __FILE__;
-static const char *__pyx_filename;
-
-
-static const char *__pyx_f[] = {
-  "_speedups.pyx",
-};
-
-/* Type declarations */
-
-#ifndef CYTHON_REFNANNY
-  #define CYTHON_REFNANNY 0
-#endif
-
-#if CYTHON_REFNANNY
-  typedef struct {
-    void (*INCREF)(void*, PyObject*, int);
-    void (*DECREF)(void*, PyObject*, int);
-    void (*GOTREF)(void*, PyObject*, int);
-    void (*GIVEREF)(void*, PyObject*, int);
-    void* (*SetupContext)(const char*, int, const char*);
-    void (*FinishContext)(void**);
-  } __Pyx_RefNannyAPIStruct;
-  static __Pyx_RefNannyAPIStruct *__Pyx_RefNanny = NULL;
-  static __Pyx_RefNannyAPIStruct * __Pyx_RefNannyImportAPI(const char *modname) {
-    PyObject *m = NULL, *p = NULL;
-    void *r = NULL;
-    m = PyImport_ImportModule((char *)modname);
-    if (!m) goto end;
-    p = PyObject_GetAttrString(m, (char *)"RefNannyAPI");
-    if (!p) goto end;
-    r = PyLong_AsVoidPtr(p);
-  end:
-    Py_XDECREF(p);
-    Py_XDECREF(m);
-    return (__Pyx_RefNannyAPIStruct *)r;
-  }
-  #define __Pyx_RefNannySetupContext(name)           void *__pyx_refnanny = __Pyx_RefNanny->SetupContext((name), __LINE__, __FILE__)
-  #define __Pyx_RefNannyFinishContext()           __Pyx_RefNanny->FinishContext(&__pyx_refnanny)
-  #define __Pyx_INCREF(r) __Pyx_RefNanny->INCREF(__pyx_refnanny, (PyObject *)(r), __LINE__)
-  #define __Pyx_DECREF(r) __Pyx_RefNanny->DECREF(__pyx_refnanny, (PyObject *)(r), __LINE__)
-  #define __Pyx_GOTREF(r) __Pyx_RefNanny->GOTREF(__pyx_refnanny, (PyObject *)(r), __LINE__)
-  #define __Pyx_GIVEREF(r) __Pyx_RefNanny->GIVEREF(__pyx_refnanny, (PyObject *)(r), __LINE__)
-  #define __Pyx_XDECREF(r) do { if((r) != NULL) {__Pyx_DECREF(r);} } while(0)
-#else
-  #define __Pyx_RefNannySetupContext(name)
-  #define __Pyx_RefNannyFinishContext()
-  #define __Pyx_INCREF(r) Py_INCREF(r)
-  #define __Pyx_DECREF(r) Py_DECREF(r)
-  #define __Pyx_GOTREF(r)
-  #define __Pyx_GIVEREF(r)
-  #define __Pyx_XDECREF(r) Py_XDECREF(r)
-#endif /* CYTHON_REFNANNY */
-#define __Pyx_XGIVEREF(r) do { if((r) != NULL) {__Pyx_GIVEREF(r);} } while(0)
-#define __Pyx_XGOTREF(r) do { if((r) != NULL) {__Pyx_GOTREF(r);} } while(0)
-
-static PyObject *__Pyx_GetName(PyObject *dict, PyObject *name); /*proto*/
-
-static void __Pyx_RaiseDoubleKeywordsError(
-    const char* func_name, PyObject* kw_name); /*proto*/
-
-static void __Pyx_RaiseArgtupleInvalid(const char* func_name, int exact,
-    Py_ssize_t num_min, Py_ssize_t num_max, Py_ssize_t num_found); /*proto*/
-
-static int __Pyx_ParseOptionalKeywords(PyObject *kwds, PyObject **argnames[],     PyObject *kwds2, PyObject *values[], Py_ssize_t num_pos_args,     const char* function_name); /*proto*/
-
-
-static CYTHON_INLINE PyObject *__Pyx_GetItemInt_Generic(PyObject *o, PyObject* j) {
-    PyObject *r;
-    if (!j) return NULL;
-    r = PyObject_GetItem(o, j);
-    Py_DECREF(j);
-    return r;
-}
-
-
-#define __Pyx_GetItemInt_List(o, i, size, to_py_func) (((size) <= sizeof(Py_ssize_t)) ? \
-                                                    __Pyx_GetItemInt_List_Fast(o, i) : \
-                                                    __Pyx_GetItemInt_Generic(o, to_py_func(i)))
-
-static CYTHON_INLINE PyObject *__Pyx_GetItemInt_List_Fast(PyObject *o, Py_ssize_t i) {
-    if (likely(o != Py_None)) {
-        if (likely((0 <= i) & (i < PyList_GET_SIZE(o)))) {
-            PyObject *r = PyList_GET_ITEM(o, i);
-            Py_INCREF(r);
-            return r;
-        }
-        else if ((-PyList_GET_SIZE(o) <= i) & (i < 0)) {
-            PyObject *r = PyList_GET_ITEM(o, PyList_GET_SIZE(o) + i);
-            Py_INCREF(r);
-            return r;
-        }
-    }
-    return __Pyx_GetItemInt_Generic(o, PyInt_FromSsize_t(i));
-}
-
-#define __Pyx_GetItemInt_Tuple(o, i, size, to_py_func) (((size) <= sizeof(Py_ssize_t)) ? \
-                                                    __Pyx_GetItemInt_Tuple_Fast(o, i) : \
-                                                    __Pyx_GetItemInt_Generic(o, to_py_func(i)))
-
-static CYTHON_INLINE PyObject *__Pyx_GetItemInt_Tuple_Fast(PyObject *o, Py_ssize_t i) {
-    if (likely(o != Py_None)) {
-        if (likely((0 <= i) & (i < PyTuple_GET_SIZE(o)))) {
-            PyObject *r = PyTuple_GET_ITEM(o, i);
-            Py_INCREF(r);
-            return r;
-        }
-        else if ((-PyTuple_GET_SIZE(o) <= i) & (i < 0)) {
-            PyObject *r = PyTuple_GET_ITEM(o, PyTuple_GET_SIZE(o) + i);
-            Py_INCREF(r);
-            return r;
-        }
-    }
-    return __Pyx_GetItemInt_Generic(o, PyInt_FromSsize_t(i));
-}
-
-
-#define __Pyx_GetItemInt(o, i, size, to_py_func) (((size) <= sizeof(Py_ssize_t)) ? \
-                                                    __Pyx_GetItemInt_Fast(o, i) : \
-                                                    __Pyx_GetItemInt_Generic(o, to_py_func(i)))
-
-static CYTHON_INLINE PyObject *__Pyx_GetItemInt_Fast(PyObject *o, Py_ssize_t i) {
-    PyObject *r;
-    if (PyList_CheckExact(o) && ((0 <= i) & (i < PyList_GET_SIZE(o)))) {
-        r = PyList_GET_ITEM(o, i);
-        Py_INCREF(r);
-    }
-    else if (PyTuple_CheckExact(o) && ((0 <= i) & (i < PyTuple_GET_SIZE(o)))) {
-        r = PyTuple_GET_ITEM(o, i);
-        Py_INCREF(r);
-    }
-    else if (Py_TYPE(o)->tp_as_sequence && Py_TYPE(o)->tp_as_sequence->sq_item && (likely(i >= 0))) {
-        r = PySequence_GetItem(o, i);
-    }
-    else {
-        r = __Pyx_GetItemInt_Generic(o, PyInt_FromSsize_t(i));
-    }
-    return r;
-}
-
-static int __Pyx_GetException(PyObject **type, PyObject **value, PyObject **tb); /*proto*/
-
-static CYTHON_INLINE void __Pyx_ExceptionSave(PyObject **type, PyObject **value, PyObject **tb); /*proto*/
-static void __Pyx_ExceptionReset(PyObject *type, PyObject *value, PyObject *tb); /*proto*/
-
-static PyObject *__Pyx_Import(PyObject *name, PyObject *from_list); /*proto*/
-
-static CYTHON_INLINE void __Pyx_ErrRestore(PyObject *type, PyObject *value, PyObject *tb); /*proto*/
-static CYTHON_INLINE void __Pyx_ErrFetch(PyObject **type, PyObject **value, PyObject **tb); /*proto*/
-
-static void __Pyx_Raise(PyObject *type, PyObject *value, PyObject *tb); /*proto*/
-
-static CYTHON_INLINE unsigned char __Pyx_PyInt_AsUnsignedChar(PyObject *);
-
-static CYTHON_INLINE unsigned short __Pyx_PyInt_AsUnsignedShort(PyObject *);
-
-static CYTHON_INLINE unsigned int __Pyx_PyInt_AsUnsignedInt(PyObject *);
-
-static CYTHON_INLINE char __Pyx_PyInt_AsChar(PyObject *);
-
-static CYTHON_INLINE short __Pyx_PyInt_AsShort(PyObject *);
-
-static CYTHON_INLINE int __Pyx_PyInt_AsInt(PyObject *);
-
-static CYTHON_INLINE signed char __Pyx_PyInt_AsSignedChar(PyObject *);
-
-static CYTHON_INLINE signed short __Pyx_PyInt_AsSignedShort(PyObject *);
-
-static CYTHON_INLINE signed int __Pyx_PyInt_AsSignedInt(PyObject *);
-
-static CYTHON_INLINE int __Pyx_PyInt_AsLongDouble(PyObject *);
-
-static CYTHON_INLINE unsigned long __Pyx_PyInt_AsUnsignedLong(PyObject *);
-
-static CYTHON_INLINE unsigned PY_LONG_LONG __Pyx_PyInt_AsUnsignedLongLong(PyObject *);
-
-static CYTHON_INLINE long __Pyx_PyInt_AsLong(PyObject *);
-
-static CYTHON_INLINE PY_LONG_LONG __Pyx_PyInt_AsLongLong(PyObject *);
-
-static CYTHON_INLINE signed long __Pyx_PyInt_AsSignedLong(PyObject *);
-
-static CYTHON_INLINE signed PY_LONG_LONG __Pyx_PyInt_AsSignedLongLong(PyObject *);
-
-static void __Pyx_AddTraceback(const char *funcname); /*proto*/
-
-static int __Pyx_InitStrings(__Pyx_StringTabEntry *t); /*proto*/
-/* Module declarations from shapely.speedups._speedups */
-
-static CYTHON_INLINE GEOSGeometry *__pyx_f_7shapely_8speedups_9_speedups_cast_geom(unsigned long); /*proto*/
-static CYTHON_INLINE struct GEOSContextHandle_HS *__pyx_f_7shapely_8speedups_9_speedups_cast_handle(unsigned long); /*proto*/
-static CYTHON_INLINE GEOSCoordSequence *__pyx_f_7shapely_8speedups_9_speedups_cast_seq(unsigned long); /*proto*/
-#define __Pyx_MODULE_NAME "shapely.speedups._speedups"
-int __pyx_module_is_main_shapely__speedups___speedups = 0;
-
-/* Implementation of shapely.speedups._speedups */
-static PyObject *__pyx_builtin_ValueError;
-static PyObject *__pyx_builtin_IndexError;
-static PyObject *__pyx_builtin_xrange;
-static PyObject *__pyx_builtin_AttributeError;
-static PyObject *__pyx_builtin_TypeError;
-static char __pyx_k_1[] = "LineStrings must have at least 2 coordinate tuples";
-static char __pyx_k_3[] = "Input %s is the wrong shape for a LineString";
-static char __pyx_k_4[] = "Wrong coordinate dimensions; this geometry has dimensions: %d";
-static char __pyx_k_6[] = "Inconsistent coordinate dimensionality";
-static char __pyx_k_8[] = "A LinearRing must have at least 3 coordinate tuples";
-static char __pyx_k_11[] = "shapely.geos";
-static char __pyx_k_12[] = "shapely.speedups._speedups";
-static char __pyx_k_13[] = "geos_linestring_from_py";
-static char __pyx_k_14[] = "geos_linearring_from_py";
-static char __pyx_k__ob[] = "ob";
-static char __pyx_k__data[] = "data";
-static char __pyx_k__Array[] = "Array";
-static char __pyx_k___cseq[] = "_cseq";
-static char __pyx_k___ndim[] = "_ndim";
-static char __pyx_k__lgeos[] = "lgeos";
-static char __pyx_k__range[] = "range";
-static char __pyx_k__shape[] = "shape";
-static char __pyx_k__ctypes[] = "ctypes";
-static char __pyx_k__xrange[] = "xrange";
-static char __pyx_k____len__[] = "__len__";
-static char __pyx_k___update[] = "_update";
-static char __pyx_k__destroy[] = "destroy";
-static char __pyx_k____main__[] = "__main__";
-static char __pyx_k____test__[] = "__test__";
-static char __pyx_k__c_double[] = "c_double";
-static char __pyx_k__TypeError[] = "TypeError";
-static char __pyx_k__addressof[] = "addressof";
-static char __pyx_k__IndexError[] = "IndexError";
-static char __pyx_k__ValueError[] = "ValueError";
-static char __pyx_k__geos_handle[] = "geos_handle";
-static char __pyx_k__update_geom[] = "update_geom";
-static char __pyx_k__update_ndim[] = "update_ndim";
-static char __pyx_k__AttributeError[] = "AttributeError";
-static char __pyx_k__coordseq_ctypes[] = "coordseq_ctypes";
-static char __pyx_k____array_interface__[] = "__array_interface__";
-static PyObject *__pyx_kp_s_1;
-static PyObject *__pyx_n_s_11;
-static PyObject *__pyx_n_s_12;
-static PyObject *__pyx_n_s_13;
-static PyObject *__pyx_n_s_14;
-static PyObject *__pyx_kp_s_3;
-static PyObject *__pyx_kp_s_4;
-static PyObject *__pyx_kp_s_6;
-static PyObject *__pyx_kp_s_8;
-static PyObject *__pyx_n_s__Array;
-static PyObject *__pyx_n_s__AttributeError;
-static PyObject *__pyx_n_s__IndexError;
-static PyObject *__pyx_n_s__TypeError;
-static PyObject *__pyx_n_s__ValueError;
-static PyObject *__pyx_n_s____array_interface__;
-static PyObject *__pyx_n_s____len__;
-static PyObject *__pyx_n_s____main__;
-static PyObject *__pyx_n_s____test__;
-static PyObject *__pyx_n_s___cseq;
-static PyObject *__pyx_n_s___ndim;
-static PyObject *__pyx_n_s___update;
-static PyObject *__pyx_n_s__addressof;
-static PyObject *__pyx_n_s__c_double;
-static PyObject *__pyx_n_s__coordseq_ctypes;
-static PyObject *__pyx_n_s__ctypes;
-static PyObject *__pyx_n_s__data;
-static PyObject *__pyx_n_s__destroy;
-static PyObject *__pyx_n_s__geos_handle;
-static PyObject *__pyx_n_s__lgeos;
-static PyObject *__pyx_n_s__ob;
-static PyObject *__pyx_n_s__range;
-static PyObject *__pyx_n_s__shape;
-static PyObject *__pyx_n_s__update_geom;
-static PyObject *__pyx_n_s__update_ndim;
-static PyObject *__pyx_n_s__xrange;
-static PyObject *__pyx_int_0;
-static PyObject *__pyx_k_tuple_2;
-static PyObject *__pyx_k_tuple_5;
-static PyObject *__pyx_k_tuple_7;
-static PyObject *__pyx_k_tuple_9;
-static PyObject *__pyx_k_tuple_10;
-
-/* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":28
- *     void GEOSGeom_destroy_r(GEOSContextHandle_HS *, GEOSGeometry *)
- * 
- * cdef inline GEOSGeometry *cast_geom(unsigned long geom_addr):             # <<<<<<<<<<<<<<
- *     return <GEOSGeometry *>geom_addr
- * 
- */
-
-static CYTHON_INLINE GEOSGeometry *__pyx_f_7shapely_8speedups_9_speedups_cast_geom(unsigned long __pyx_v_geom_addr) {
-  GEOSGeometry *__pyx_r;
-  __Pyx_RefNannySetupContext("cast_geom");
-
-  /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":29
- * 
- * cdef inline GEOSGeometry *cast_geom(unsigned long geom_addr):
- *     return <GEOSGeometry *>geom_addr             # <<<<<<<<<<<<<<
- * 
- * cdef inline GEOSContextHandle_HS *cast_handle(unsigned long handle_addr):
- */
-  __pyx_r = ((GEOSGeometry *)__pyx_v_geom_addr);
-  goto __pyx_L0;
-
-  __pyx_r = 0;
-  __pyx_L0:;
-  __Pyx_RefNannyFinishContext();
-  return __pyx_r;
-}
-
-/* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":31
- *     return <GEOSGeometry *>geom_addr
- * 
- * cdef inline GEOSContextHandle_HS *cast_handle(unsigned long handle_addr):             # <<<<<<<<<<<<<<
- *     return <GEOSContextHandle_HS *>handle_addr
- * 
- */
-
-static CYTHON_INLINE struct GEOSContextHandle_HS *__pyx_f_7shapely_8speedups_9_speedups_cast_handle(unsigned long __pyx_v_handle_addr) {
-  struct GEOSContextHandle_HS *__pyx_r;
-  __Pyx_RefNannySetupContext("cast_handle");
-
-  /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":32
- * 
- * cdef inline GEOSContextHandle_HS *cast_handle(unsigned long handle_addr):
- *     return <GEOSContextHandle_HS *>handle_addr             # <<<<<<<<<<<<<<
- * 
- * cdef inline GEOSCoordSequence *cast_seq(unsigned long handle_addr):
- */
-  __pyx_r = ((struct GEOSContextHandle_HS *)__pyx_v_handle_addr);
-  goto __pyx_L0;
-
-  __pyx_r = 0;
-  __pyx_L0:;
-  __Pyx_RefNannyFinishContext();
-  return __pyx_r;
-}
-
-/* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":34
- *     return <GEOSContextHandle_HS *>handle_addr
- * 
- * cdef inline GEOSCoordSequence *cast_seq(unsigned long handle_addr):             # <<<<<<<<<<<<<<
- *     return <GEOSCoordSequence *>handle_addr
- * 
- */
-
-static CYTHON_INLINE GEOSCoordSequence *__pyx_f_7shapely_8speedups_9_speedups_cast_seq(unsigned long __pyx_v_handle_addr) {
-  GEOSCoordSequence *__pyx_r;
-  __Pyx_RefNannySetupContext("cast_seq");
-
-  /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":35
- * 
- * cdef inline GEOSCoordSequence *cast_seq(unsigned long handle_addr):
- *     return <GEOSCoordSequence *>handle_addr             # <<<<<<<<<<<<<<
- * 
- * def destroy(geom):
- */
-  __pyx_r = ((GEOSCoordSequence *)__pyx_v_handle_addr);
-  goto __pyx_L0;
-
-  __pyx_r = 0;
-  __pyx_L0:;
-  __Pyx_RefNannyFinishContext();
-  return __pyx_r;
-}
-
-/* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":37
- *     return <GEOSCoordSequence *>handle_addr
- * 
- * def destroy(geom):             # <<<<<<<<<<<<<<
- *     GEOSGeom_destroy_r(cast_handle(lgeos.geos_handle), cast_geom(geom))
- * 
- */
-
-static PyObject *__pyx_pf_7shapely_8speedups_9_speedups_0destroy(PyObject *__pyx_self, PyObject *__pyx_v_geom); /*proto*/
-static PyMethodDef __pyx_mdef_7shapely_8speedups_9_speedups_0destroy = {__Pyx_NAMESTR("destroy"), (PyCFunction)__pyx_pf_7shapely_8speedups_9_speedups_0destroy, METH_O, __Pyx_DOCSTR(0)};
-static PyObject *__pyx_pf_7shapely_8speedups_9_speedups_0destroy(PyObject *__pyx_self, PyObject *__pyx_v_geom) {
-  PyObject *__pyx_r = NULL;
-  PyObject *__pyx_t_1 = NULL;
-  PyObject *__pyx_t_2 = NULL;
-  unsigned long __pyx_t_3;
-  unsigned long __pyx_t_4;
-  __Pyx_RefNannySetupContext("destroy");
-  __pyx_self = __pyx_self;
-
-  /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":38
- * 
- * def destroy(geom):
- *     GEOSGeom_destroy_r(cast_handle(lgeos.geos_handle), cast_geom(geom))             # <<<<<<<<<<<<<<
- * 
- * def geos_linestring_from_py(ob, update_geom=None, update_ndim=0):
- */
-  __pyx_t_1 = __Pyx_GetName(__pyx_m, __pyx_n_s__lgeos); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 38; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
-  __Pyx_GOTREF(__pyx_t_1);
-  __pyx_t_2 = PyObject_GetAttr(__pyx_t_1, __pyx_n_s__geos_handle); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 38; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
-  __Pyx_GOTREF(__pyx_t_2);
-  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
-  __pyx_t_3 = __Pyx_PyInt_AsUnsignedLong(__pyx_t_2); if (unlikely((__pyx_t_3 == (unsigned long)-1) && PyErr_Occurred())) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 38; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
-  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
-  __pyx_t_4 = __Pyx_PyInt_AsUnsignedLong(__pyx_v_geom); if (unlikely((__pyx_t_4 == (unsigned long)-1) && PyErr_Occurred())) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 38; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
-  GEOSGeom_destroy_r(__pyx_f_7shapely_8speedups_9_speedups_cast_handle(__pyx_t_3), __pyx_f_7shapely_8speedups_9_speedups_cast_geom(__pyx_t_4));
-
-  __pyx_r = Py_None; __Pyx_INCREF(Py_None);
-  goto __pyx_L0;
-  __pyx_L1_error:;
-  __Pyx_XDECREF(__pyx_t_1);
-  __Pyx_XDECREF(__pyx_t_2);
-  __Pyx_AddTraceback("shapely.speedups._speedups.destroy");
-  __pyx_r = NULL;
-  __pyx_L0:;
-  __Pyx_XGIVEREF(__pyx_r);
-  __Pyx_RefNannyFinishContext();
-  return __pyx_r;
-}
-
-/* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":40
- *     GEOSGeom_destroy_r(cast_handle(lgeos.geos_handle), cast_geom(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)
- */
-
-static PyObject *__pyx_pf_7shapely_8speedups_9_speedups_1geos_linestring_from_py(PyObject *__pyx_self, PyObject *__pyx_args, PyObject *__pyx_kwds); /*proto*/
-static PyMethodDef __pyx_mdef_7shapely_8speedups_9_speedups_1geos_linestring_from_py = {__Pyx_NAMESTR("geos_linestring_from_py"), (PyCFunction)__pyx_pf_7shapely_8speedups_9_speedups_1geos_linestring_from_py, METH_VARARGS|METH_KEYWORDS, __Pyx_DOCSTR(0)};
-static PyObject *__pyx_pf_7shapely_8speedups_9_speedups_1geos_linestring_from_py(PyObject *__pyx_self, PyObject *__pyx_args, PyObject *__pyx_kwds) {
-  PyObject *__pyx_v_ob = 0;
-  PyObject *__pyx_v_update_geom = 0;
-  PyObject *__pyx_v_update_ndim = 0;
-  double *__pyx_v_cp;
-  struct GEOSContextHandle_HS *__pyx_v_handle;
-  GEOSCoordSequence *__pyx_v_cs;
-  double __pyx_v_dx;
-  double __pyx_v_dy;
-  double __pyx_v_dz;
-  int __pyx_v_i;
-  int __pyx_v_n;
-  int __pyx_v_m;
-  PyObject *__pyx_v_array;
-  PyObject *__pyx_v_coords;
-  PyObject *__pyx_r = NULL;
-  PyObject *__pyx_t_1 = NULL;
-  PyObject *__pyx_t_2 = NULL;
-  unsigned long __pyx_t_3;
-  Py_ssize_t __pyx_t_4;
-  int __pyx_t_5;
-  int __pyx_t_6;
-  PyObject *__pyx_t_7 = NULL;
-  PyObject *__pyx_t_8 = NULL;
-  PyObject *__pyx_t_9 = NULL;
-  int __pyx_t_10;
-  PyObject *__pyx_t_11 = NULL;
-  PyObject *__pyx_t_12 = NULL;
-  PyObject *__pyx_t_13 = NULL;
-  double __pyx_t_14;
-  static PyObject **__pyx_pyargnames[] = {&__pyx_n_s__ob,&__pyx_n_s__update_geom,&__pyx_n_s__update_ndim,0};
-  __Pyx_RefNannySetupContext("geos_linestring_from_py");
-  __pyx_self = __pyx_self;
-  if (unlikely(__pyx_kwds)) {
-    Py_ssize_t kw_args = PyDict_Size(__pyx_kwds);
-    PyObject* values[3] = {0,0,0};
-    values[1] = ((PyObject *)Py_None);
-    values[2] = ((PyObject *)__pyx_int_0);
-    switch (PyTuple_GET_SIZE(__pyx_args)) {
-      case  3: values[2] = PyTuple_GET_ITEM(__pyx_args, 2);
-      case  2: values[1] = PyTuple_GET_ITEM(__pyx_args, 1);
-      case  1: values[0] = PyTuple_GET_ITEM(__pyx_args, 0);
-      case  0: break;
-      default: goto __pyx_L5_argtuple_error;
-    }
-    switch (PyTuple_GET_SIZE(__pyx_args)) {
-      case  0:
-      values[0] = PyDict_GetItem(__pyx_kwds, __pyx_n_s__ob);
-      if (likely(values[0])) kw_args--;
-      else goto __pyx_L5_argtuple_error;
-      case  1:
-      if (kw_args > 0) {
-        PyObject* value = PyDict_GetItem(__pyx_kwds, __pyx_n_s__update_geom);
-        if (value) { values[1] = value; kw_args--; }
-      }
-      case  2:
-      if (kw_args > 0) {
-        PyObject* value = PyDict_GetItem(__pyx_kwds, __pyx_n_s__update_ndim);
-        if (value) { values[2] = value; kw_args--; }
-      }
-    }
-    if (unlikely(kw_args > 0)) {
-      if (unlikely(__Pyx_ParseOptionalKeywords(__pyx_kwds, __pyx_pyargnames, 0, values, PyTuple_GET_SIZE(__pyx_args), "geos_linestring_from_py") < 0)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 40; __pyx_clineno = __LINE__; goto __pyx_L3_error;}
-    }
-    __pyx_v_ob = values[0];
-    __pyx_v_update_geom = values[1];
-    __pyx_v_update_ndim = values[2];
-  } else {
-    __pyx_v_update_geom = ((PyObject *)Py_None);
-    __pyx_v_update_ndim = ((PyObject *)__pyx_int_0);
-    switch (PyTuple_GET_SIZE(__pyx_args)) {
-      case  3: __pyx_v_update_ndim = PyTuple_GET_ITEM(__pyx_args, 2);
-      case  2: __pyx_v_update_geom = PyTuple_GET_ITEM(__pyx_args, 1);
-      case  1: __pyx_v_ob = PyTuple_GET_ITEM(__pyx_args, 0);
-      break;
-      default: goto __pyx_L5_argtuple_error;
-    }
-  }
-  goto __pyx_L4_argument_unpacking_done;
-  __pyx_L5_argtuple_error:;
-  __Pyx_RaiseArgtupleInvalid("geos_linestring_from_py", 0, 1, 3, PyTuple_GET_SIZE(__pyx_args)); {__pyx_filename = __pyx_f[0]; __pyx_lineno = 40; __pyx_clineno = __LINE__; goto __pyx_L3_error;}
-  __pyx_L3_error:;
-  __Pyx_AddTraceback("shapely.speedups._speedups.geos_linestring_from_py");
-  __Pyx_RefNannyFinishContext();
-  return NULL;
-  __pyx_L4_argument_unpacking_done:;
-  __pyx_v_array = Py_None; __Pyx_INCREF(Py_None);
-  __pyx_v_coords = Py_None; __Pyx_INCREF(Py_None);
-
-  /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":42
- * 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 GEOSCoordSequence *cs
- *     cdef double dx, dy, dz
- */
-  __pyx_t_1 = __Pyx_GetName(__pyx_m, __pyx_n_s__lgeos); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 42; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
-  __Pyx_GOTREF(__pyx_t_1);
-  __pyx_t_2 = PyObject_GetAttr(__pyx_t_1, __pyx_n_s__geos_handle); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 42; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
-  __Pyx_GOTREF(__pyx_t_2);
-  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
-  __pyx_t_3 = __Pyx_PyInt_AsUnsignedLong(__pyx_t_2); if (unlikely((__pyx_t_3 == (unsigned long)-1) && PyErr_Occurred())) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 42; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
-  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
-  __pyx_v_handle = __pyx_f_7shapely_8speedups_9_speedups_cast_handle(__pyx_t_3);
-
-  /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":46
- *     cdef double dx, dy, dz
- *     cdef int i, n, m
- *     try:             # <<<<<<<<<<<<<<
- *         # From array protocol
- *         array = ob.__array_interface__
- */
-  {
-    PyObject *__pyx_save_exc_type, *__pyx_save_exc_value, *__pyx_save_exc_tb;
-    __Pyx_ExceptionSave(&__pyx_save_exc_type, &__pyx_save_exc_value, &__pyx_save_exc_tb);
-    __Pyx_XGOTREF(__pyx_save_exc_type);
-    __Pyx_XGOTREF(__pyx_save_exc_value);
-    __Pyx_XGOTREF(__pyx_save_exc_tb);
-    /*try:*/ {
-
-      /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":48
- *     try:
- *         # From array protocol
- *         array = ob.__array_interface__             # <<<<<<<<<<<<<<
- *         assert len(array['shape']) == 2
- *         m = array['shape'][0]
- */
-      __pyx_t_2 = PyObject_GetAttr(__pyx_v_ob, __pyx_n_s____array_interface__); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 48; __pyx_clineno = __LINE__; goto __pyx_L6_error;}
-      __Pyx_GOTREF(__pyx_t_2);
-      __Pyx_DECREF(__pyx_v_array);
-      __pyx_v_array = __pyx_t_2;
-      __pyx_t_2 = 0;
-
-      /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":49
- *         # From array protocol
- *         array = ob.__array_interface__
- *         assert len(array['shape']) == 2             # <<<<<<<<<<<<<<
- *         m = array['shape'][0]
- *         if m < 2:
- */
-      #ifndef CYTHON_WITHOUT_ASSERTIONS
-      __pyx_t_2 = PyObject_GetItem(__pyx_v_array, ((PyObject *)__pyx_n_s__shape)); if (!__pyx_t_2) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 49; __pyx_clineno = __LINE__; goto __pyx_L6_error;}
-      __Pyx_GOTREF(__pyx_t_2);
-      __pyx_t_4 = PyObject_Length(__pyx_t_2); if (unlikely(__pyx_t_4 == -1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 49; __pyx_clineno = __LINE__; goto __pyx_L6_error;}
-      __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
-      if (unlikely(!(__pyx_t_4 == 2))) {
-        PyErr_SetNone(PyExc_AssertionError);
-        {__pyx_filename = __pyx_f[0]; __pyx_lineno = 49; __pyx_clineno = __LINE__; goto __pyx_L6_error;}
-      }
-      #endif
-
-      /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":50
- *         array = ob.__array_interface__
- *         assert len(array['shape']) == 2
- *         m = array['shape'][0]             # <<<<<<<<<<<<<<
- *         if m < 2:
- *             raise ValueError(
- */
-      __pyx_t_2 = PyObject_GetItem(__pyx_v_array, ((PyObject *)__pyx_n_s__shape)); if (!__pyx_t_2) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 50; __pyx_clineno = __LINE__; goto __pyx_L6_error;}
-      __Pyx_GOTREF(__pyx_t_2);
-      __pyx_t_1 = __Pyx_GetItemInt(__pyx_t_2, 0, sizeof(long), PyInt_FromLong); if (!__pyx_t_1) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 50; __pyx_clineno = __LINE__; goto __pyx_L6_error;}
-      __Pyx_GOTREF(__pyx_t_1);
-      __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
-      __pyx_t_5 = __Pyx_PyInt_AsInt(__pyx_t_1); if (unlikely((__pyx_t_5 == (int)-1) && PyErr_Occurred())) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 50; __pyx_clineno = __LINE__; goto __pyx_L6_error;}
-      __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
-      __pyx_v_m = __pyx_t_5;
-
-      /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":51
- *         assert len(array['shape']) == 2
- *         m = array['shape'][0]
- *         if m < 2:             # <<<<<<<<<<<<<<
- *             raise ValueError(
- *                 "LineStrings must have at least 2 coordinate tuples")
- */
-      __pyx_t_6 = (__pyx_v_m < 2);
-      if (__pyx_t_6) {
-
-        /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":52
- *         m = array['shape'][0]
- *         if m < 2:
- *             raise ValueError(             # <<<<<<<<<<<<<<
- *                 "LineStrings must have at least 2 coordinate tuples")
- *         try:
- */
-        __pyx_t_1 = PyObject_Call(__pyx_builtin_ValueError, ((PyObject *)__pyx_k_tuple_2), NULL); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 52; __pyx_clineno = __LINE__; goto __pyx_L6_error;}
-        __Pyx_GOTREF(__pyx_t_1);
-        __Pyx_Raise(__pyx_t_1, 0, 0);
-        __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
-        {__pyx_filename = __pyx_f[0]; __pyx_lineno = 52; __pyx_clineno = __LINE__; goto __pyx_L6_error;}
-        goto __pyx_L14;
-      }
-      __pyx_L14:;
-
-      /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":54
- *             raise ValueError(
- *                 "LineStrings must have at least 2 coordinate tuples")
- *         try:             # <<<<<<<<<<<<<<
- *             n = array['shape'][1]
- *         except IndexError:
- */
-      {
-        PyObject *__pyx_save_exc_type, *__pyx_save_exc_value, *__pyx_save_exc_tb;
-        __Pyx_ExceptionSave(&__pyx_save_exc_type, &__pyx_save_exc_value, &__pyx_save_exc_tb);
-        __Pyx_XGOTREF(__pyx_save_exc_type);
-        __Pyx_XGOTREF(__pyx_save_exc_value);
-        __Pyx_XGOTREF(__pyx_save_exc_tb);
-        /*try:*/ {
-
-          /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":55
- *                 "LineStrings must have at least 2 coordinate tuples")
- *         try:
- *             n = array['shape'][1]             # <<<<<<<<<<<<<<
- *         except IndexError:
- *             raise ValueError(
- */
-          __pyx_t_1 = PyObject_GetItem(__pyx_v_array, ((PyObject *)__pyx_n_s__shape)); if (!__pyx_t_1) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 55; __pyx_clineno = __LINE__; goto __pyx_L15_error;}
-          __Pyx_GOTREF(__pyx_t_1);
-          __pyx_t_2 = __Pyx_GetItemInt(__pyx_t_1, 1, sizeof(long), PyInt_FromLong); if (!__pyx_t_2) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 55; __pyx_clineno = __LINE__; goto __pyx_L15_error;}
-          __Pyx_GOTREF(__pyx_t_2);
-          __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
-          __pyx_t_5 = __Pyx_PyInt_AsInt(__pyx_t_2); if (unlikely((__pyx_t_5 == (int)-1) && PyErr_Occurred())) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 55; __pyx_clineno = __LINE__; goto __pyx_L15_error;}
-          __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
-          __pyx_v_n = __pyx_t_5;
-        }
-        __Pyx_XDECREF(__pyx_save_exc_type); __pyx_save_exc_type = 0;
-        __Pyx_XDECREF(__pyx_save_exc_value); __pyx_save_exc_value = 0;
-        __Pyx_XDECREF(__pyx_save_exc_tb); __pyx_save_exc_tb = 0;
-        goto __pyx_L22_try_end;
-        __pyx_L15_error:;
-        __Pyx_XDECREF(__pyx_t_1); __pyx_t_1 = 0;
-        __Pyx_XDECREF(__pyx_t_2); __pyx_t_2 = 0;
-
-        /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":56
- *         try:
- *             n = array['shape'][1]
- *         except IndexError:             # <<<<<<<<<<<<<<
- *             raise ValueError(
- *                 "Input %s is the wrong shape for a LineString" % str(ob))
- */
-        __pyx_t_5 = PyErr_ExceptionMatches(__pyx_builtin_IndexError);
-        if (__pyx_t_5) {
-          __Pyx_AddTraceback("shapely.speedups._speedups.geos_linestring_from_py");
-          if (__Pyx_GetException(&__pyx_t_2, &__pyx_t_1, &__pyx_t_7) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 56; __pyx_clineno = __LINE__; goto __pyx_L17_except_error;}
-          __Pyx_GOTREF(__pyx_t_2);
-          __Pyx_GOTREF(__pyx_t_1);
-          __Pyx_GOTREF(__pyx_t_7);
-
-          /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":58
- *         except IndexError:
- *             raise ValueError(
- *                 "Input %s is the wrong shape for a LineString" % str(ob))             # <<<<<<<<<<<<<<
- *         assert n == 2 or n == 3
- * 
- */
-          __pyx_t_8 = PyTuple_New(1); if (unlikely(!__pyx_t_8)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 58; __pyx_clineno = __LINE__; goto __pyx_L17_except_error;}
-          __Pyx_GOTREF(((PyObject *)__pyx_t_8));
-          __Pyx_INCREF(__pyx_v_ob);
-          PyTuple_SET_ITEM(__pyx_t_8, 0, __pyx_v_ob);
-          __Pyx_GIVEREF(__pyx_v_ob);
-          __pyx_t_9 = PyObject_Call(((PyObject *)((PyObject*)(&PyString_Type))), ((PyObject *)__pyx_t_8), NULL); if (unlikely(!__pyx_t_9)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 58; __pyx_clineno = __LINE__; goto __pyx_L17_except_error;}
-          __Pyx_GOTREF(__pyx_t_9);
-          __Pyx_DECREF(((PyObject *)__pyx_t_8)); __pyx_t_8 = 0;
-          __pyx_t_8 = PyNumber_Remainder(((PyObject *)__pyx_kp_s_3), __pyx_t_9); if (unlikely(!__pyx_t_8)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 58; __pyx_clineno = __LINE__; goto __pyx_L17_except_error;}
-          __Pyx_GOTREF(((PyObject *)__pyx_t_8));
-          __Pyx_DECREF(__pyx_t_9); __pyx_t_9 = 0;
-          __pyx_t_9 = PyTuple_New(1); if (unlikely(!__pyx_t_9)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 57; __pyx_clineno = __LINE__; goto __pyx_L17_except_error;}
-          __Pyx_GOTREF(((PyObject *)__pyx_t_9));
-          PyTuple_SET_ITEM(__pyx_t_9, 0, ((PyObject *)__pyx_t_8));
-          __Pyx_GIVEREF(((PyObject *)__pyx_t_8));
-          __pyx_t_8 = 0;
-          __pyx_t_8 = PyObject_Call(__pyx_builtin_ValueError, ((PyObject *)__pyx_t_9), NULL); if (unlikely(!__pyx_t_8)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 57; __pyx_clineno = __LINE__; goto __pyx_L17_except_error;}
-          __Pyx_GOTREF(__pyx_t_8);
-          __Pyx_DECREF(((PyObject *)__pyx_t_9)); __pyx_t_9 = 0;
-          __Pyx_Raise(__pyx_t_8, 0, 0);
-          __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0;
-          {__pyx_filename = __pyx_f[0]; __pyx_lineno = 57; __pyx_clineno = __LINE__; goto __pyx_L17_except_error;}
-          __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
-          __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
-          __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0;
-          goto __pyx_L16_exception_handled;
-        }
-        __pyx_L17_except_error:;
-        __Pyx_XGIVEREF(__pyx_save_exc_type);
-        __Pyx_XGIVEREF(__pyx_save_exc_value);
-        __Pyx_XGIVEREF(__pyx_save_exc_tb);
-        __Pyx_ExceptionReset(__pyx_save_exc_type, __pyx_save_exc_value, __pyx_save_exc_tb);
-        goto __pyx_L6_error;
-        __pyx_L16_exception_handled:;
-        __Pyx_XGIVEREF(__pyx_save_exc_type);
-        __Pyx_XGIVEREF(__pyx_save_exc_value);
-        __Pyx_XGIVEREF(__pyx_save_exc_tb);
-        __Pyx_ExceptionReset(__pyx_save_exc_type, __pyx_save_exc_value, __pyx_save_exc_tb);
-        __pyx_L22_try_end:;
-      }
-
-      /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":59
- *             raise ValueError(
- *                 "Input %s is the wrong shape for a LineString" % str(ob))
- *         assert n == 2 or n == 3             # <<<<<<<<<<<<<<
- * 
- *         # Make pointer to the coordinate array
- */
-      #ifndef CYTHON_WITHOUT_ASSERTIONS
-      switch (__pyx_v_n) {
-        case 2:
-        case 3:
-        __pyx_t_6 = 1;
-        break;
-        default:
-        __pyx_t_6 = 0;
-        break;
-      }
-      if (unlikely(!__pyx_t_6)) {
-        PyErr_SetNone(PyExc_AssertionError);
-        {__pyx_filename = __pyx_f[0]; __pyx_lineno = 59; __pyx_clineno = __LINE__; goto __pyx_L6_error;}
-      }
-      #endif
-
-      /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":62
- * 
- *         # Make pointer to the coordinate array
- *         if isinstance(array['data'], ctypes.Array):             # <<<<<<<<<<<<<<
- *             cp = <double *><unsigned long>ctypes.addressof(array['data'])
- *         else:
- */
-      __pyx_t_7 = PyObject_GetItem(__pyx_v_array, ((PyObject *)__pyx_n_s__data)); if (!__pyx_t_7) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 62; __pyx_clineno = __LINE__; goto __pyx_L6_error;}
-      __Pyx_GOTREF(__pyx_t_7);
-      __pyx_t_1 = __Pyx_GetName(__pyx_m, __pyx_n_s__ctypes); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 62; __pyx_clineno = __LINE__; goto __pyx_L6_error;}
-      __Pyx_GOTREF(__pyx_t_1);
-      __pyx_t_2 = PyObject_GetAttr(__pyx_t_1, __pyx_n_s__Array); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 62; __pyx_clineno = __LINE__; goto __pyx_L6_error;}
-      __Pyx_GOTREF(__pyx_t_2);
-      __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
-      __pyx_t_6 = PyObject_IsInstance(__pyx_t_7, __pyx_t_2); if (unlikely(__pyx_t_6 == -1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 62; __pyx_clineno = __LINE__; goto __pyx_L6_error;}
-      __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0;
-      __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
-      if (__pyx_t_6) {
-
-        /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":63
- *         # Make pointer to the coordinate array
- *         if isinstance(array['data'], ctypes.Array):
- *             cp = <double *><unsigned long>ctypes.addressof(array['data'])             # <<<<<<<<<<<<<<
- *         else:
- *             cp = <double *><unsigned long>array['data'][0]
- */
-        __pyx_t_2 = __Pyx_GetName(__pyx_m, __pyx_n_s__ctypes); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 63; __pyx_clineno = __LINE__; goto __pyx_L6_error;}
-        __Pyx_GOTREF(__pyx_t_2);
-        __pyx_t_7 = PyObject_GetAttr(__pyx_t_2, __pyx_n_s__addressof); if (unlikely(!__pyx_t_7)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 63; __pyx_clineno = __LINE__; goto __pyx_L6_error;}
-        __Pyx_GOTREF(__pyx_t_7);
-        __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
-        __pyx_t_2 = PyObject_GetItem(__pyx_v_array, ((PyObject *)__pyx_n_s__data)); if (!__pyx_t_2) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 63; __pyx_clineno = __LINE__; goto __pyx_L6_error;}
-        __Pyx_GOTREF(__pyx_t_2);
-        __pyx_t_1 = PyTuple_New(1); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 63; __pyx_clineno = __LINE__; goto __pyx_L6_error;}
-        __Pyx_GOTREF(((PyObject *)__pyx_t_1));
-        PyTuple_SET_ITEM(__pyx_t_1, 0, __pyx_t_2);
-        __Pyx_GIVEREF(__pyx_t_2);
-        __pyx_t_2 = 0;
-        __pyx_t_2 = PyObject_Call(__pyx_t_7, ((PyObject *)__pyx_t_1), NULL); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 63; __pyx_clineno = __LINE__; goto __pyx_L6_error;}
-        __Pyx_GOTREF(__pyx_t_2);
-        __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0;
-        __Pyx_DECREF(((PyObject *)__pyx_t_1)); __pyx_t_1 = 0;
-        __pyx_t_3 = __Pyx_PyInt_AsUnsignedLong(__pyx_t_2); if (unlikely((__pyx_t_3 == (unsigned long)-1) && PyErr_Occurred())) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 63; __pyx_clineno = __LINE__; goto __pyx_L6_error;}
-        __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
-        __pyx_v_cp = ((double *)__pyx_t_3);
-        goto __pyx_L25;
-      }
-      /*else*/ {
-
-        /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":65
- *             cp = <double *><unsigned long>ctypes.addressof(array['data'])
- *         else:
- *             cp = <double *><unsigned long>array['data'][0]             # <<<<<<<<<<<<<<
- * 
- *         # Create a coordinate sequence
- */
-        __pyx_t_2 = PyObject_GetItem(__pyx_v_array, ((PyObject *)__pyx_n_s__data)); if (!__pyx_t_2) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 65; __pyx_clineno = __LINE__; goto __pyx_L6_error;}
-        __Pyx_GOTREF(__pyx_t_2);
-        __pyx_t_1 = __Pyx_GetItemInt(__pyx_t_2, 0, sizeof(long), PyInt_FromLong); if (!__pyx_t_1) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 65; __pyx_clineno = __LINE__; goto __pyx_L6_error;}
-        __Pyx_GOTREF(__pyx_t_1);
-        __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
-        __pyx_t_3 = __Pyx_PyInt_AsUnsignedLong(__pyx_t_1); if (unlikely((__pyx_t_3 == (unsigned long)-1) && PyErr_Occurred())) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 65; __pyx_clineno = __LINE__; goto __pyx_L6_error;}
-        __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
-        __pyx_v_cp = ((double *)__pyx_t_3);
-      }
-      __pyx_L25:;
-
-      /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":68
- * 
- *         # Create a coordinate sequence
- *         if update_geom is not None:             # <<<<<<<<<<<<<<
- *             cs = GEOSGeom_getCoordSeq_r(handle, cast_geom(update_geom))
- *             if n != update_ndim:
- */
-      __pyx_t_6 = (__pyx_v_update_geom != Py_None);
-      if (__pyx_t_6) {
-
-        /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":69
- *         # Create a coordinate sequence
- *         if update_geom is not None:
- *             cs = GEOSGeom_getCoordSeq_r(handle, cast_geom(update_geom))             # <<<<<<<<<<<<<<
- *             if n != update_ndim:
- *                 raise ValueError(
- */
-        __pyx_t_3 = __Pyx_PyInt_AsUnsignedLong(__pyx_v_update_geom); if (unlikely((__pyx_t_3 == (unsigned long)-1) && PyErr_Occurred())) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 69; __pyx_clineno = __LINE__; goto __pyx_L6_error;}
-        __pyx_v_cs = GEOSGeom_getCoordSeq_r(__pyx_v_handle, __pyx_f_7shapely_8speedups_9_speedups_cast_geom(__pyx_t_3));
-
-        /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":70
- *         if update_geom is not None:
- *             cs = GEOSGeom_getCoordSeq_r(handle, cast_geom(update_geom))
- *             if n != update_ndim:             # <<<<<<<<<<<<<<
- *                 raise ValueError(
- *                 "Wrong coordinate dimensions; this geometry has dimensions: %d" \
- */
-        __pyx_t_1 = PyInt_FromLong(__pyx_v_n); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 70; __pyx_clineno = __LINE__; goto __pyx_L6_error;}
-        __Pyx_GOTREF(__pyx_t_1);
-        __pyx_t_2 = PyObject_RichCompare(__pyx_t_1, __pyx_v_update_ndim, Py_NE); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 70; __pyx_clineno = __LINE__; goto __pyx_L6_error;}
-        __Pyx_GOTREF(__pyx_t_2);
-        __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
-        __pyx_t_6 = __Pyx_PyObject_IsTrue(__pyx_t_2); if (unlikely(__pyx_t_6 < 0)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 70; __pyx_clineno = __LINE__; goto __pyx_L6_error;}
-        __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
-        if (__pyx_t_6) {
-
-          /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":73
- *                 raise ValueError(
- *                 "Wrong coordinate dimensions; this geometry has dimensions: %d" \
- *                 % update_ndim)             # <<<<<<<<<<<<<<
- *         else:
- *             cs = GEOSCoordSeq_create_r(handle, <int>m, <int>n)
- */
-          __pyx_t_2 = PyNumber_Remainder(((PyObject *)__pyx_kp_s_4), __pyx_v_update_ndim); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 73; __pyx_clineno = __LINE__; goto __pyx_L6_error;}
-          __Pyx_GOTREF(((PyObject *)__pyx_t_2));
-          __pyx_t_1 = PyTuple_New(1); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 71; __pyx_clineno = __LINE__; goto __pyx_L6_error;}
-          __Pyx_GOTREF(((PyObject *)__pyx_t_1));
-          PyTuple_SET_ITEM(__pyx_t_1, 0, ((PyObject *)__pyx_t_2));
-          __Pyx_GIVEREF(((PyObject *)__pyx_t_2));
-          __pyx_t_2 = 0;
-          __pyx_t_2 = PyObject_Call(__pyx_builtin_ValueError, ((PyObject *)__pyx_t_1), NULL); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 71; __pyx_clineno = __LINE__; goto __pyx_L6_error;}
-          __Pyx_GOTREF(__pyx_t_2);
-          __Pyx_DECREF(((PyObject *)__pyx_t_1)); __pyx_t_1 = 0;
-          __Pyx_Raise(__pyx_t_2, 0, 0);
-          __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
-          {__pyx_filename = __pyx_f[0]; __pyx_lineno = 71; __pyx_clineno = __LINE__; goto __pyx_L6_error;}
-          goto __pyx_L27;
-        }
-        __pyx_L27:;
-        goto __pyx_L26;
-      }
-      /*else*/ {
-
-        /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":75
- *                 % update_ndim)
- *         else:
- *             cs = GEOSCoordSeq_create_r(handle, <int>m, <int>n)             # <<<<<<<<<<<<<<
- * 
- *         # add to coordinate sequence
- */
-        __pyx_v_cs = GEOSCoordSeq_create_r(__pyx_v_handle, __pyx_v_m, __pyx_v_n);
-      }
-      __pyx_L26:;
-
-      /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":78
- * 
- *         # add to coordinate sequence
- *         for i in xrange(m):             # <<<<<<<<<<<<<<
- *             dx = cp[n*i]
- *             dy = cp[n*i+1]
- */
-      __pyx_t_5 = __pyx_v_m;
-      for (__pyx_t_10 = 0; __pyx_t_10 < __pyx_t_5; __pyx_t_10+=1) {
-        __pyx_v_i = __pyx_t_10;
-
-        /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":79
- *         # add to coordinate sequence
- *         for i in xrange(m):
- *             dx = cp[n*i]             # <<<<<<<<<<<<<<
- *             dy = cp[n*i+1]
- *             dz = 0
- */
-        __pyx_v_dx = (__pyx_v_cp[(__pyx_v_n * __pyx_v_i)]);
-
-        /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":80
- *         for i in xrange(m):
- *             dx = cp[n*i]
- *             dy = cp[n*i+1]             # <<<<<<<<<<<<<<
- *             dz = 0
- *             if n == 3:
- */
-        __pyx_v_dy = (__pyx_v_cp[((__pyx_v_n * __pyx_v_i) + 1)]);
-
-        /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":81
- *             dx = cp[n*i]
- *             dy = cp[n*i+1]
- *             dz = 0             # <<<<<<<<<<<<<<
- *             if n == 3:
- *                 dz = cp[n*i+2]
- */
-        __pyx_v_dz = 0.0;
-
-        /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":82
- *             dy = cp[n*i+1]
- *             dz = 0
- *             if n == 3:             # <<<<<<<<<<<<<<
- *                 dz = cp[n*i+2]
- * 
- */
-        __pyx_t_6 = (__pyx_v_n == 3);
-        if (__pyx_t_6) {
-
-          /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":83
- *             dz = 0
- *             if n == 3:
- *                 dz = cp[n*i+2]             # <<<<<<<<<<<<<<
- * 
- *             # Because of a bug in the GEOS C API,
- */
-          __pyx_v_dz = (__pyx_v_cp[((__pyx_v_n * __pyx_v_i) + 2)]);
-          goto __pyx_L30;
-        }
-        __pyx_L30:;
-
-        /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":87
- *             # Because of a bug in the GEOS C API,
- *             # always set X before Y
- *             GEOSCoordSeq_setX_r(handle, cs, i, dx)             # <<<<<<<<<<<<<<
- *             GEOSCoordSeq_setY_r(handle, cs, i, dy)
- *             if n == 3:
- */
-        GEOSCoordSeq_setX_r(__pyx_v_handle, __pyx_v_cs, __pyx_v_i, __pyx_v_dx);
-
-        /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":88
- *             # always set X before Y
- *             GEOSCoordSeq_setX_r(handle, cs, i, dx)
- *             GEOSCoordSeq_setY_r(handle, cs, i, dy)             # <<<<<<<<<<<<<<
- *             if n == 3:
- *                 GEOSCoordSeq_setZ_r(handle, cs, i, dz)
- */
-        GEOSCoordSeq_setY_r(__pyx_v_handle, __pyx_v_cs, __pyx_v_i, __pyx_v_dy);
-
-        /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":89
- *             GEOSCoordSeq_setX_r(handle, cs, i, dx)
- *             GEOSCoordSeq_setY_r(handle, cs, i, dy)
- *             if n == 3:             # <<<<<<<<<<<<<<
- *                 GEOSCoordSeq_setZ_r(handle, cs, i, dz)
- * 
- */
-        __pyx_t_6 = (__pyx_v_n == 3);
-        if (__pyx_t_6) {
-
-          /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":90
- *             GEOSCoordSeq_setY_r(handle, cs, i, dy)
- *             if n == 3:
- *                 GEOSCoordSeq_setZ_r(handle, cs, i, dz)             # <<<<<<<<<<<<<<
- * 
- *     except AttributeError:
- */
-          GEOSCoordSeq_setZ_r(__pyx_v_handle, __pyx_v_cs, __pyx_v_i, __pyx_v_dz);
-          goto __pyx_L31;
-        }
-        __pyx_L31:;
-      }
-    }
-    __Pyx_XDECREF(__pyx_save_exc_type); __pyx_save_exc_type = 0;
-    __Pyx_XDECREF(__pyx_save_exc_value); __pyx_save_exc_value = 0;
-    __Pyx_XDECREF(__pyx_save_exc_tb); __pyx_save_exc_tb = 0;
-    goto __pyx_L13_try_end;
-    __pyx_L6_error:;
-    __Pyx_XDECREF(__pyx_t_9); __pyx_t_9 = 0;
-    __Pyx_XDECREF(__pyx_t_8); __pyx_t_8 = 0;
-    __Pyx_XDECREF(__pyx_t_7); __pyx_t_7 = 0;
-    __Pyx_XDECREF(__pyx_t_1); __pyx_t_1 = 0;
-    __Pyx_XDECREF(__pyx_t_2); __pyx_t_2 = 0;
-
-    /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":92
- *                 GEOSCoordSeq_setZ_r(handle, cs, i, dz)
- * 
- *     except AttributeError:             # <<<<<<<<<<<<<<
- *         # Fall back on list
- *         m = len(ob)
- */
-    __pyx_t_5 = PyErr_ExceptionMatches(__pyx_builtin_AttributeError);
-    if (__pyx_t_5) {
-      __Pyx_AddTraceback("shapely.speedups._speedups.geos_linestring_from_py");
-      if (__Pyx_GetException(&__pyx_t_2, &__pyx_t_1, &__pyx_t_7) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 92; __pyx_clineno = __LINE__; goto __pyx_L8_except_error;}
-      __Pyx_GOTREF(__pyx_t_2);
-      __Pyx_GOTREF(__pyx_t_1);
-      __Pyx_GOTREF(__pyx_t_7);
-
-      /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":94
- *     except AttributeError:
- *         # Fall back on list
- *         m = len(ob)             # <<<<<<<<<<<<<<
- *         if m < 2:
- *             raise ValueError(
- */
-      __pyx_t_4 = PyObject_Length(__pyx_v_ob); if (unlikely(__pyx_t_4 == -1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 94; __pyx_clineno = __LINE__; goto __pyx_L8_except_error;}
-      __pyx_v_m = __pyx_t_4;
-
-      /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":95
- *         # Fall back on list
- *         m = len(ob)
- *         if m < 2:             # <<<<<<<<<<<<<<
- *             raise ValueError(
- *                 "LineStrings must have at least 2 coordinate tuples")
- */
-      __pyx_t_6 = (__pyx_v_m < 2);
-      if (__pyx_t_6) {
-
-        /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":96
- *         m = len(ob)
- *         if m < 2:
- *             raise ValueError(             # <<<<<<<<<<<<<<
- *                 "LineStrings must have at least 2 coordinate tuples")
- *         try:
- */
-        __pyx_t_8 = PyObject_Call(__pyx_builtin_ValueError, ((PyObject *)__pyx_k_tuple_5), NULL); if (unlikely(!__pyx_t_8)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 96; __pyx_clineno = __LINE__; goto __pyx_L8_except_error;}
-        __Pyx_GOTREF(__pyx_t_8);
-        __Pyx_Raise(__pyx_t_8, 0, 0);
-        __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0;
-        {__pyx_filename = __pyx_f[0]; __pyx_lineno = 96; __pyx_clineno = __LINE__; goto __pyx_L8_except_error;}
-        goto __pyx_L34;
-      }
-      __pyx_L34:;
-
-      /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":98
- *             raise ValueError(
- *                 "LineStrings must have at least 2 coordinate tuples")
- *         try:             # <<<<<<<<<<<<<<
- *             n = len(ob[0])
- *         except TypeError:
- */
-      {
-        PyObject *__pyx_save_exc_type, *__pyx_save_exc_value, *__pyx_save_exc_tb;
-        __Pyx_ExceptionSave(&__pyx_save_exc_type, &__pyx_save_exc_value, &__pyx_save_exc_tb);
-        __Pyx_XGOTREF(__pyx_save_exc_type);
-        __Pyx_XGOTREF(__pyx_save_exc_value);
-        __Pyx_XGOTREF(__pyx_save_exc_tb);
-        /*try:*/ {
-
-          /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":99
- *                 "LineStrings must have at least 2 coordinate tuples")
- *         try:
- *             n = len(ob[0])             # <<<<<<<<<<<<<<
- *         except TypeError:
- *             raise ValueError(
- */
-          __pyx_t_8 = __Pyx_GetItemInt(__pyx_v_ob, 0, sizeof(long), PyInt_FromLong); if (!__pyx_t_8) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 99; __pyx_clineno = __LINE__; goto __pyx_L35_error;}
-          __Pyx_GOTREF(__pyx_t_8);
-          __pyx_t_4 = PyObject_Length(__pyx_t_8); if (unlikely(__pyx_t_4 == -1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 99; __pyx_clineno = __LINE__; goto __pyx_L35_error;}
-          __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0;
-          __pyx_v_n = __pyx_t_4;
-        }
-        __Pyx_XDECREF(__pyx_save_exc_type); __pyx_save_exc_type = 0;
-        __Pyx_XDECREF(__pyx_save_exc_value); __pyx_save_exc_value = 0;
-        __Pyx_XDECREF(__pyx_save_exc_tb); __pyx_save_exc_tb = 0;
-        goto __pyx_L42_try_end;
-        __pyx_L35_error:;
-        __Pyx_XDECREF(__pyx_t_9); __pyx_t_9 = 0;
-        __Pyx_XDECREF(__pyx_t_8); __pyx_t_8 = 0;
-
-        /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":100
- *         try:
- *             n = len(ob[0])
- *         except TypeError:             # <<<<<<<<<<<<<<
- *             raise ValueError(
- *                 "Input %s is the wrong shape for a LineString" % str(ob))
- */
-        __pyx_t_5 = PyErr_ExceptionMatches(__pyx_builtin_TypeError);
-        if (__pyx_t_5) {
-          __Pyx_AddTraceback("shapely.speedups._speedups.geos_linestring_from_py");
-          if (__Pyx_GetException(&__pyx_t_8, &__pyx_t_9, &__pyx_t_11) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 100; __pyx_clineno = __LINE__; goto __pyx_L37_except_error;}
-          __Pyx_GOTREF(__pyx_t_8);
-          __Pyx_GOTREF(__pyx_t_9);
-          __Pyx_GOTREF(__pyx_t_11);
-
-          /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":102
- *         except TypeError:
- *             raise ValueError(
- *                 "Input %s is the wrong shape for a LineString" % str(ob))             # <<<<<<<<<<<<<<
- *         assert n == 2 or n == 3
- * 
- */
-          __pyx_t_12 = PyTuple_New(1); if (unlikely(!__pyx_t_12)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 102; __pyx_clineno = __LINE__; goto __pyx_L37_except_error;}
-          __Pyx_GOTREF(((PyObject *)__pyx_t_12));
-          __Pyx_INCREF(__pyx_v_ob);
-          PyTuple_SET_ITEM(__pyx_t_12, 0, __pyx_v_ob);
-          __Pyx_GIVEREF(__pyx_v_ob);
-          __pyx_t_13 = PyObject_Call(((PyObject *)((PyObject*)(&PyString_Type))), ((PyObject *)__pyx_t_12), NULL); if (unlikely(!__pyx_t_13)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 102; __pyx_clineno = __LINE__; goto __pyx_L37_except_error;}
-          __Pyx_GOTREF(__pyx_t_13);
-          __Pyx_DECREF(((PyObject *)__pyx_t_12)); __pyx_t_12 = 0;
-          __pyx_t_12 = PyNumber_Remainder(((PyObject *)__pyx_kp_s_3), __pyx_t_13); if (unlikely(!__pyx_t_12)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 102; __pyx_clineno = __LINE__; goto __pyx_L37_except_error;}
-          __Pyx_GOTREF(((PyObject *)__pyx_t_12));
-          __Pyx_DECREF(__pyx_t_13); __pyx_t_13 = 0;
-          __pyx_t_13 = PyTuple_New(1); if (unlikely(!__pyx_t_13)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 101; __pyx_clineno = __LINE__; goto __pyx_L37_except_error;}
-          __Pyx_GOTREF(((PyObject *)__pyx_t_13));
-          PyTuple_SET_ITEM(__pyx_t_13, 0, ((PyObject *)__pyx_t_12));
-          __Pyx_GIVEREF(((PyObject *)__pyx_t_12));
-          __pyx_t_12 = 0;
-          __pyx_t_12 = PyObject_Call(__pyx_builtin_ValueError, ((PyObject *)__pyx_t_13), NULL); if (unlikely(!__pyx_t_12)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 101; __pyx_clineno = __LINE__; goto __pyx_L37_except_error;}
-          __Pyx_GOTREF(__pyx_t_12);
-          __Pyx_DECREF(((PyObject *)__pyx_t_13)); __pyx_t_13 = 0;
-          __Pyx_Raise(__pyx_t_12, 0, 0);
-          __Pyx_DECREF(__pyx_t_12); __pyx_t_12 = 0;
-          {__pyx_filename = __pyx_f[0]; __pyx_lineno = 101; __pyx_clineno = __LINE__; goto __pyx_L37_except_error;}
-          __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0;
-          __Pyx_DECREF(__pyx_t_9); __pyx_t_9 = 0;
-          __Pyx_DECREF(__pyx_t_11); __pyx_t_11 = 0;
-          goto __pyx_L36_exception_handled;
-        }
-        __pyx_L37_except_error:;
-        __Pyx_XGIVEREF(__pyx_save_exc_type);
-        __Pyx_XGIVEREF(__pyx_save_exc_value);
-        __Pyx_XGIVEREF(__pyx_save_exc_tb);
-        __Pyx_ExceptionReset(__pyx_save_exc_type, __pyx_save_exc_value, __pyx_save_exc_tb);
-        goto __pyx_L8_except_error;
-        __pyx_L36_exception_handled:;
-        __Pyx_XGIVEREF(__pyx_save_exc_type);
-        __Pyx_XGIVEREF(__pyx_save_exc_value);
-        __Pyx_XGIVEREF(__pyx_save_exc_tb);
-        __Pyx_ExceptionReset(__pyx_save_exc_type, __pyx_save_exc_value, __pyx_save_exc_tb);
-        __pyx_L42_try_end:;
-      }
-
-      /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":103
- *             raise ValueError(
- *                 "Input %s is the wrong shape for a LineString" % str(ob))
- *         assert n == 2 or n == 3             # <<<<<<<<<<<<<<
- * 
- *         # Create a coordinate sequence
- */
-      #ifndef CYTHON_WITHOUT_ASSERTIONS
-      switch (__pyx_v_n) {
-        case 2:
-        case 3:
-        __pyx_t_6 = 1;
-        break;
-        default:
-        __pyx_t_6 = 0;
-        break;
-      }
-      if (unlikely(!__pyx_t_6)) {
-        PyErr_SetNone(PyExc_AssertionError);
-        {__pyx_filename = __pyx_f[0]; __pyx_lineno = 103; __pyx_clineno = __LINE__; goto __pyx_L8_except_error;}
-      }
-      #endif
-
-      /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":106
- * 
- *         # Create a coordinate sequence
- *         if update_geom is not None:             # <<<<<<<<<<<<<<
- *             cs = GEOSGeom_getCoordSeq_r(handle, cast_geom(update_geom))
- *             if n != update_ndim:
- */
-      __pyx_t_6 = (__pyx_v_update_geom != Py_None);
-      if (__pyx_t_6) {
-
-        /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":107
- *         # Create a coordinate sequence
- *         if update_geom is not None:
- *             cs = GEOSGeom_getCoordSeq_r(handle, cast_geom(update_geom))             # <<<<<<<<<<<<<<
- *             if n != update_ndim:
- *                 raise ValueError(
- */
-        __pyx_t_3 = __Pyx_PyInt_AsUnsignedLong(__pyx_v_update_geom); if (unlikely((__pyx_t_3 == (unsigned long)-1) && PyErr_Occurred())) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 107; __pyx_clineno = __LINE__; goto __pyx_L8_except_error;}
-        __pyx_v_cs = GEOSGeom_getCoordSeq_r(__pyx_v_handle, __pyx_f_7shapely_8speedups_9_speedups_cast_geom(__pyx_t_3));
-
-        /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":108
- *         if update_geom is not None:
- *             cs = GEOSGeom_getCoordSeq_r(handle, cast_geom(update_geom))
- *             if n != update_ndim:             # <<<<<<<<<<<<<<
- *                 raise ValueError(
- *                 "Wrong coordinate dimensions; this geometry has dimensions: %d" \
- */
-        __pyx_t_11 = PyInt_FromLong(__pyx_v_n); if (unlikely(!__pyx_t_11)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 108; __pyx_clineno = __LINE__; goto __pyx_L8_except_error;}
-        __Pyx_GOTREF(__pyx_t_11);
-        __pyx_t_9 = PyObject_RichCompare(__pyx_t_11, __pyx_v_update_ndim, Py_NE); if (unlikely(!__pyx_t_9)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 108; __pyx_clineno = __LINE__; goto __pyx_L8_except_error;}
-        __Pyx_GOTREF(__pyx_t_9);
-        __Pyx_DECREF(__pyx_t_11); __pyx_t_11 = 0;
-        __pyx_t_6 = __Pyx_PyObject_IsTrue(__pyx_t_9); if (unlikely(__pyx_t_6 < 0)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 108; __pyx_clineno = __LINE__; goto __pyx_L8_except_error;}
-        __Pyx_DECREF(__pyx_t_9); __pyx_t_9 = 0;
-        if (__pyx_t_6) {
-
-          /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":111
- *                 raise ValueError(
- *                 "Wrong coordinate dimensions; this geometry has dimensions: %d" \
- *                 % update_ndim)             # <<<<<<<<<<<<<<
- *         else:
- *             cs = GEOSCoordSeq_create_r(handle, <int>m, <int>n)
- */
-          __pyx_t_9 = PyNumber_Remainder(((PyObject *)__pyx_kp_s_4), __pyx_v_update_ndim); if (unlikely(!__pyx_t_9)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 111; __pyx_clineno = __LINE__; goto __pyx_L8_except_error;}
-          __Pyx_GOTREF(((PyObject *)__pyx_t_9));
-          __pyx_t_11 = PyTuple_New(1); if (unlikely(!__pyx_t_11)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 109; __pyx_clineno = __LINE__; goto __pyx_L8_except_error;}
-          __Pyx_GOTREF(((PyObject *)__pyx_t_11));
-          PyTuple_SET_ITEM(__pyx_t_11, 0, ((PyObject *)__pyx_t_9));
-          __Pyx_GIVEREF(((PyObject *)__pyx_t_9));
-          __pyx_t_9 = 0;
-          __pyx_t_9 = PyObject_Call(__pyx_builtin_ValueError, ((PyObject *)__pyx_t_11), NULL); if (unlikely(!__pyx_t_9)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 109; __pyx_clineno = __LINE__; goto __pyx_L8_except_error;}
-          __Pyx_GOTREF(__pyx_t_9);
-          __Pyx_DECREF(((PyObject *)__pyx_t_11)); __pyx_t_11 = 0;
-          __Pyx_Raise(__pyx_t_9, 0, 0);
-          __Pyx_DECREF(__pyx_t_9); __pyx_t_9 = 0;
-          {__pyx_filename = __pyx_f[0]; __pyx_lineno = 109; __pyx_clineno = __LINE__; goto __pyx_L8_except_error;}
-          goto __pyx_L46;
-        }
-        __pyx_L46:;
-        goto __pyx_L45;
-      }
-      /*else*/ {
-
-        /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":113
- *                 % update_ndim)
- *         else:
- *             cs = GEOSCoordSeq_create_r(handle, <int>m, <int>n)             # <<<<<<<<<<<<<<
- * 
- *         # add to coordinate sequence
- */
-        __pyx_v_cs = GEOSCoordSeq_create_r(__pyx_v_handle, __pyx_v_m, __pyx_v_n);
-      }
-      __pyx_L45:;
-
-      /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":116
- * 
- *         # add to coordinate sequence
- *         for i in xrange(m):             # <<<<<<<<<<<<<<
- *             coords = ob[i]
- *             dx = coords[0]
- */
-      __pyx_t_5 = __pyx_v_m;
-      for (__pyx_t_10 = 0; __pyx_t_10 < __pyx_t_5; __pyx_t_10+=1) {
-        __pyx_v_i = __pyx_t_10;
-
-        /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":117
- *         # add to coordinate sequence
- *         for i in xrange(m):
- *             coords = ob[i]             # <<<<<<<<<<<<<<
- *             dx = coords[0]
- *             dy = coords[1]
- */
-        __pyx_t_9 = __Pyx_GetItemInt(__pyx_v_ob, __pyx_v_i, sizeof(int), PyInt_FromLong); if (!__pyx_t_9) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 117; __pyx_clineno = __LINE__; goto __pyx_L8_except_error;}
-        __Pyx_GOTREF(__pyx_t_9);
-        __Pyx_DECREF(__pyx_v_coords);
-        __pyx_v_coords = __pyx_t_9;
-        __pyx_t_9 = 0;
-
-        /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":118
- *         for i in xrange(m):
- *             coords = ob[i]
- *             dx = coords[0]             # <<<<<<<<<<<<<<
- *             dy = coords[1]
- *             dz = 0
- */
-        __pyx_t_9 = __Pyx_GetItemInt(__pyx_v_coords, 0, sizeof(long), PyInt_FromLong); if (!__pyx_t_9) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 118; __pyx_clineno = __LINE__; goto __pyx_L8_except_error;}
-        __Pyx_GOTREF(__pyx_t_9);
-        __pyx_t_14 = __pyx_PyFloat_AsDouble(__pyx_t_9); if (unlikely((__pyx_t_14 == (double)-1) && PyErr_Occurred())) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 118; __pyx_clineno = __LINE__; goto __pyx_L8_except_error;}
-        __Pyx_DECREF(__pyx_t_9); __pyx_t_9 = 0;
-        __pyx_v_dx = __pyx_t_14;
-
-        /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":119
- *             coords = ob[i]
- *             dx = coords[0]
- *             dy = coords[1]             # <<<<<<<<<<<<<<
- *             dz = 0
- *             if n == 3:
- */
-        __pyx_t_9 = __Pyx_GetItemInt(__pyx_v_coords, 1, sizeof(long), PyInt_FromLong); if (!__pyx_t_9) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 119; __pyx_clineno = __LINE__; goto __pyx_L8_except_error;}
-        __Pyx_GOTREF(__pyx_t_9);
-        __pyx_t_14 = __pyx_PyFloat_AsDouble(__pyx_t_9); if (unlikely((__pyx_t_14 == (double)-1) && PyErr_Occurred())) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 119; __pyx_clineno = __LINE__; goto __pyx_L8_except_error;}
-        __Pyx_DECREF(__pyx_t_9); __pyx_t_9 = 0;
-        __pyx_v_dy = __pyx_t_14;
-
-        /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":120
- *             dx = coords[0]
- *             dy = coords[1]
- *             dz = 0             # <<<<<<<<<<<<<<
- *             if n == 3:
- *                 if len(coords) != 3:
- */
-        __pyx_v_dz = 0.0;
-
-        /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":121
- *             dy = coords[1]
- *             dz = 0
- *             if n == 3:             # <<<<<<<<<<<<<<
- *                 if len(coords) != 3:
- *                     raise ValueError("Inconsistent coordinate dimensionality")
- */
-        __pyx_t_6 = (__pyx_v_n == 3);
-        if (__pyx_t_6) {
-
-          /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":122
- *             dz = 0
- *             if n == 3:
- *                 if len(coords) != 3:             # <<<<<<<<<<<<<<
- *                     raise ValueError("Inconsistent coordinate dimensionality")
- *                 dz = coords[2]
- */
-          __pyx_t_4 = PyObject_Length(__pyx_v_coords); if (unlikely(__pyx_t_4 == -1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 122; __pyx_clineno = __LINE__; goto __pyx_L8_except_error;}
-          __pyx_t_6 = (__pyx_t_4 != 3);
-          if (__pyx_t_6) {
-
-            /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":123
- *             if n == 3:
- *                 if len(coords) != 3:
- *                     raise ValueError("Inconsistent coordinate dimensionality")             # <<<<<<<<<<<<<<
- *                 dz = coords[2]
- * 
- */
-            __pyx_t_9 = PyObject_Call(__pyx_builtin_ValueError, ((PyObject *)__pyx_k_tuple_7), NULL); if (unlikely(!__pyx_t_9)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 123; __pyx_clineno = __LINE__; goto __pyx_L8_except_error;}
-            __Pyx_GOTREF(__pyx_t_9);
-            __Pyx_Raise(__pyx_t_9, 0, 0);
-            __Pyx_DECREF(__pyx_t_9); __pyx_t_9 = 0;
-            {__pyx_filename = __pyx_f[0]; __pyx_lineno = 123; __pyx_clineno = __LINE__; goto __pyx_L8_except_error;}
-            goto __pyx_L50;
-          }
-          __pyx_L50:;
-
-          /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":124
- *                 if len(coords) != 3:
- *                     raise ValueError("Inconsistent coordinate dimensionality")
- *                 dz = coords[2]             # <<<<<<<<<<<<<<
- * 
- *             # Because of a bug in the GEOS C API,
- */
-          __pyx_t_9 = __Pyx_GetItemInt(__pyx_v_coords, 2, sizeof(long), PyInt_FromLong); if (!__pyx_t_9) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 124; __pyx_clineno = __LINE__; goto __pyx_L8_except_error;}
-          __Pyx_GOTREF(__pyx_t_9);
-          __pyx_t_14 = __pyx_PyFloat_AsDouble(__pyx_t_9); if (unlikely((__pyx_t_14 == (double)-1) && PyErr_Occurred())) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 124; __pyx_clineno = __LINE__; goto __pyx_L8_except_error;}
-          __Pyx_DECREF(__pyx_t_9); __pyx_t_9 = 0;
-          __pyx_v_dz = __pyx_t_14;
-          goto __pyx_L49;
-        }
-        __pyx_L49:;
-
-        /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":128
- *             # Because of a bug in the GEOS C API,
- *             # always set X before Y
- *             GEOSCoordSeq_setX_r(handle, cs, i, dx)             # <<<<<<<<<<<<<<
- *             GEOSCoordSeq_setY_r(handle, cs, i, dy)
- *             if n == 3:
- */
-        GEOSCoordSeq_setX_r(__pyx_v_handle, __pyx_v_cs, __pyx_v_i, __pyx_v_dx);
-
-        /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":129
- *             # always set X before Y
- *             GEOSCoordSeq_setX_r(handle, cs, i, dx)
- *             GEOSCoordSeq_setY_r(handle, cs, i, dy)             # <<<<<<<<<<<<<<
- *             if n == 3:
- *                 GEOSCoordSeq_setZ_r(handle, cs, i, dz)
- */
-        GEOSCoordSeq_setY_r(__pyx_v_handle, __pyx_v_cs, __pyx_v_i, __pyx_v_dy);
-
-        /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":130
- *             GEOSCoordSeq_setX_r(handle, cs, i, dx)
- *             GEOSCoordSeq_setY_r(handle, cs, i, dy)
- *             if n == 3:             # <<<<<<<<<<<<<<
- *                 GEOSCoordSeq_setZ_r(handle, cs, i, dz)
- * 
- */
-        __pyx_t_6 = (__pyx_v_n == 3);
-        if (__pyx_t_6) {
-
-          /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":131
- *             GEOSCoordSeq_setY_r(handle, cs, i, dy)
- *             if n == 3:
- *                 GEOSCoordSeq_setZ_r(handle, cs, i, dz)             # <<<<<<<<<<<<<<
- * 
- *     if update_geom is not None:
- */
-          GEOSCoordSeq_setZ_r(__pyx_v_handle, __pyx_v_cs, __pyx_v_i, __pyx_v_dz);
-          goto __pyx_L51;
-        }
-        __pyx_L51:;
-      }
-      __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
-      __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
-      __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0;
-      goto __pyx_L7_exception_handled;
-    }
-    __pyx_L8_except_error:;
-    __Pyx_XGIVEREF(__pyx_save_exc_type);
-    __Pyx_XGIVEREF(__pyx_save_exc_value);
-    __Pyx_XGIVEREF(__pyx_save_exc_tb);
-    __Pyx_ExceptionReset(__pyx_save_exc_type, __pyx_save_exc_value, __pyx_save_exc_tb);
-    goto __pyx_L1_error;
-    __pyx_L7_exception_handled:;
-    __Pyx_XGIVEREF(__pyx_save_exc_type);
-    __Pyx_XGIVEREF(__pyx_save_exc_value);
-    __Pyx_XGIVEREF(__pyx_save_exc_tb);
-    __Pyx_ExceptionReset(__pyx_save_exc_type, __pyx_save_exc_value, __pyx_save_exc_tb);
-    __pyx_L13_try_end:;
-  }
-
-  /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":133
- *                 GEOSCoordSeq_setZ_r(handle, cs, i, dz)
- * 
- *     if update_geom is not None:             # <<<<<<<<<<<<<<
- *         return None
- *     else:
- */
-  __pyx_t_6 = (__pyx_v_update_geom != Py_None);
-  if (__pyx_t_6) {
-
-    /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":134
- * 
- *     if update_geom is not None:
- *         return None             # <<<<<<<<<<<<<<
- *     else:
- *         return <unsigned long>GEOSGeom_createLineString_r(handle, cs), n
- */
-    __Pyx_XDECREF(__pyx_r);
-    __Pyx_INCREF(Py_None);
-    __pyx_r = Py_None;
-    goto __pyx_L0;
-    goto __pyx_L52;
-  }
-  /*else*/ {
-
-    /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":136
- *         return None
- *     else:
- *         return <unsigned long>GEOSGeom_createLineString_r(handle, cs), n             # <<<<<<<<<<<<<<
- * 
- * 
- */
-    __Pyx_XDECREF(__pyx_r);
-    __pyx_t_7 = PyLong_FromUnsignedLong(((unsigned long)GEOSGeom_createLineString_r(__pyx_v_handle, __pyx_v_cs))); if (unlikely(!__pyx_t_7)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 136; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
-    __Pyx_GOTREF(__pyx_t_7);
-    __pyx_t_1 = PyInt_FromLong(__pyx_v_n); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 136; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
-    __Pyx_GOTREF(__pyx_t_1);
-    __pyx_t_2 = PyTuple_New(2); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 136; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
-    __Pyx_GOTREF(((PyObject *)__pyx_t_2));
-    PyTuple_SET_ITEM(__pyx_t_2, 0, __pyx_t_7);
-    __Pyx_GIVEREF(__pyx_t_7);
-    PyTuple_SET_ITEM(__pyx_t_2, 1, __pyx_t_1);
-    __Pyx_GIVEREF(__pyx_t_1);
-    __pyx_t_7 = 0;
-    __pyx_t_1 = 0;
-    __pyx_r = ((PyObject *)__pyx_t_2);
-    __pyx_t_2 = 0;
-    goto __pyx_L0;
-  }
-  __pyx_L52:;
-
-  __pyx_r = Py_None; __Pyx_INCREF(Py_None);
-  goto __pyx_L0;
-  __pyx_L1_error:;
-  __Pyx_XDECREF(__pyx_t_1);
-  __Pyx_XDECREF(__pyx_t_2);
-  __Pyx_XDECREF(__pyx_t_7);
-  __Pyx_XDECREF(__pyx_t_8);
-  __Pyx_XDECREF(__pyx_t_9);
-  __Pyx_XDECREF(__pyx_t_11);
-  __Pyx_XDECREF(__pyx_t_12);
-  __Pyx_XDECREF(__pyx_t_13);
-  __Pyx_AddTraceback("shapely.speedups._speedups.geos_linestring_from_py");
-  __pyx_r = NULL;
-  __pyx_L0:;
-  __Pyx_DECREF(__pyx_v_array);
-  __Pyx_DECREF(__pyx_v_coords);
-  __Pyx_XGIVEREF(__pyx_r);
-  __Pyx_RefNannyFinishContext();
-  return __pyx_r;
-}
-
-/* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":139
- * 
- * 
- * def geos_linearring_from_py(ob, update_geom=None, update_ndim=0):             # <<<<<<<<<<<<<<
- *     cdef double *cp
- *     cdef GEOSContextHandle_HS *handle = cast_handle(lgeos.geos_handle)
- */
-
-static PyObject *__pyx_pf_7shapely_8speedups_9_speedups_2geos_linearring_from_py(PyObject *__pyx_self, PyObject *__pyx_args, PyObject *__pyx_kwds); /*proto*/
-static PyMethodDef __pyx_mdef_7shapely_8speedups_9_speedups_2geos_linearring_from_py = {__Pyx_NAMESTR("geos_linearring_from_py"), (PyCFunction)__pyx_pf_7shapely_8speedups_9_speedups_2geos_linearring_from_py, METH_VARARGS|METH_KEYWORDS, __Pyx_DOCSTR(0)};
-static PyObject *__pyx_pf_7shapely_8speedups_9_speedups_2geos_linearring_from_py(PyObject *__pyx_self, PyObject *__pyx_args, PyObject *__pyx_kwds) {
-  PyObject *__pyx_v_ob = 0;
-  PyObject *__pyx_v_update_geom = 0;
-  PyObject *__pyx_v_update_ndim = 0;
-  double *__pyx_v_cp;
-  struct GEOSContextHandle_HS *__pyx_v_handle;
-  GEOSCoordSequence *__pyx_v_cs;
-  double __pyx_v_dx;
-  double __pyx_v_dy;
-  double __pyx_v_dz;
-  int __pyx_v_i;
-  int __pyx_v_n;
-  int __pyx_v_m;
-  int __pyx_v_M;
-  PyObject *__pyx_v_array;
-  PyObject *__pyx_v_coords;
-  PyObject *__pyx_r = NULL;
-  PyObject *__pyx_t_1 = NULL;
-  PyObject *__pyx_t_2 = NULL;
-  unsigned long __pyx_t_3;
-  Py_ssize_t __pyx_t_4;
-  int __pyx_t_5;
-  int __pyx_t_6;
-  PyObject *__pyx_t_7 = NULL;
-  int __pyx_t_8;
-  int __pyx_t_9;
-  int __pyx_t_10;
-  PyObject *__pyx_t_11 = NULL;
-  PyObject *__pyx_t_12 = NULL;
-  PyObject *__pyx_t_13 = NULL;
-  int __pyx_t_14;
-  double __pyx_t_15;
-  static PyObject **__pyx_pyargnames[] = {&__pyx_n_s__ob,&__pyx_n_s__update_geom,&__pyx_n_s__update_ndim,0};
-  __Pyx_RefNannySetupContext("geos_linearring_from_py");
-  __pyx_self = __pyx_self;
-  if (unlikely(__pyx_kwds)) {
-    Py_ssize_t kw_args = PyDict_Size(__pyx_kwds);
-    PyObject* values[3] = {0,0,0};
-    values[1] = ((PyObject *)Py_None);
-    values[2] = ((PyObject *)__pyx_int_0);
-    switch (PyTuple_GET_SIZE(__pyx_args)) {
-      case  3: values[2] = PyTuple_GET_ITEM(__pyx_args, 2);
-      case  2: values[1] = PyTuple_GET_ITEM(__pyx_args, 1);
-      case  1: values[0] = PyTuple_GET_ITEM(__pyx_args, 0);
-      case  0: break;
-      default: goto __pyx_L5_argtuple_error;
-    }
-    switch (PyTuple_GET_SIZE(__pyx_args)) {
-      case  0:
-      values[0] = PyDict_GetItem(__pyx_kwds, __pyx_n_s__ob);
-      if (likely(values[0])) kw_args--;
-      else goto __pyx_L5_argtuple_error;
-      case  1:
-      if (kw_args > 0) {
-        PyObject* value = PyDict_GetItem(__pyx_kwds, __pyx_n_s__update_geom);
-        if (value) { values[1] = value; kw_args--; }
-      }
-      case  2:
-      if (kw_args > 0) {
-        PyObject* value = PyDict_GetItem(__pyx_kwds, __pyx_n_s__update_ndim);
-        if (value) { values[2] = value; kw_args--; }
-      }
-    }
-    if (unlikely(kw_args > 0)) {
-      if (unlikely(__Pyx_ParseOptionalKeywords(__pyx_kwds, __pyx_pyargnames, 0, values, PyTuple_GET_SIZE(__pyx_args), "geos_linearring_from_py") < 0)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 139; __pyx_clineno = __LINE__; goto __pyx_L3_error;}
-    }
-    __pyx_v_ob = values[0];
-    __pyx_v_update_geom = values[1];
-    __pyx_v_update_ndim = values[2];
-  } else {
-    __pyx_v_update_geom = ((PyObject *)Py_None);
-    __pyx_v_update_ndim = ((PyObject *)__pyx_int_0);
-    switch (PyTuple_GET_SIZE(__pyx_args)) {
-      case  3: __pyx_v_update_ndim = PyTuple_GET_ITEM(__pyx_args, 2);
-      case  2: __pyx_v_update_geom = PyTuple_GET_ITEM(__pyx_args, 1);
-      case  1: __pyx_v_ob = PyTuple_GET_ITEM(__pyx_args, 0);
-      break;
-      default: goto __pyx_L5_argtuple_error;
-    }
-  }
-  goto __pyx_L4_argument_unpacking_done;
-  __pyx_L5_argtuple_error:;
-  __Pyx_RaiseArgtupleInvalid("geos_linearring_from_py", 0, 1, 3, PyTuple_GET_SIZE(__pyx_args)); {__pyx_filename = __pyx_f[0]; __pyx_lineno = 139; __pyx_clineno = __LINE__; goto __pyx_L3_error;}
-  __pyx_L3_error:;
-  __Pyx_AddTraceback("shapely.speedups._speedups.geos_linearring_from_py");
-  __Pyx_RefNannyFinishContext();
-  return NULL;
-  __pyx_L4_argument_unpacking_done:;
-  __pyx_v_array = Py_None; __Pyx_INCREF(Py_None);
-  __pyx_v_coords = Py_None; __Pyx_INCREF(Py_None);
-
-  /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":141
- * 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 GEOSCoordSequence *cs
- *     cdef double dx, dy, dz
- */
-  __pyx_t_1 = __Pyx_GetName(__pyx_m, __pyx_n_s__lgeos); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 141; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
-  __Pyx_GOTREF(__pyx_t_1);
-  __pyx_t_2 = PyObject_GetAttr(__pyx_t_1, __pyx_n_s__geos_handle); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 141; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
-  __Pyx_GOTREF(__pyx_t_2);
-  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
-  __pyx_t_3 = __Pyx_PyInt_AsUnsignedLong(__pyx_t_2); if (unlikely((__pyx_t_3 == (unsigned long)-1) && PyErr_Occurred())) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 141; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
-  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
-  __pyx_v_handle = __pyx_f_7shapely_8speedups_9_speedups_cast_handle(__pyx_t_3);
-
-  /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":145
- *     cdef double dx, dy, dz
- *     cdef int i, n, m, M
- *     try:             # <<<<<<<<<<<<<<
- *         # From array protocol
- *         array = ob.__array_interface__
- */
-  {
-    PyObject *__pyx_save_exc_type, *__pyx_save_exc_value, *__pyx_save_exc_tb;
-    __Pyx_ExceptionSave(&__pyx_save_exc_type, &__pyx_save_exc_value, &__pyx_save_exc_tb);
-    __Pyx_XGOTREF(__pyx_save_exc_type);
-    __Pyx_XGOTREF(__pyx_save_exc_value);
-    __Pyx_XGOTREF(__pyx_save_exc_tb);
-    /*try:*/ {
-
-      /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":147
- *     try:
- *         # From array protocol
- *         array = ob.__array_interface__             # <<<<<<<<<<<<<<
- *         assert len(array['shape']) == 2
- *         m = array['shape'][0]
- */
-      __pyx_t_2 = PyObject_GetAttr(__pyx_v_ob, __pyx_n_s____array_interface__); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 147; __pyx_clineno = __LINE__; goto __pyx_L6_error;}
-      __Pyx_GOTREF(__pyx_t_2);
-      __Pyx_DECREF(__pyx_v_array);
-      __pyx_v_array = __pyx_t_2;
-      __pyx_t_2 = 0;
-
-      /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":148
- *         # From array protocol
- *         array = ob.__array_interface__
- *         assert len(array['shape']) == 2             # <<<<<<<<<<<<<<
- *         m = array['shape'][0]
- *         n = array['shape'][1]
- */
-      #ifndef CYTHON_WITHOUT_ASSERTIONS
-      __pyx_t_2 = PyObject_GetItem(__pyx_v_array, ((PyObject *)__pyx_n_s__shape)); if (!__pyx_t_2) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 148; __pyx_clineno = __LINE__; goto __pyx_L6_error;}
-      __Pyx_GOTREF(__pyx_t_2);
-      __pyx_t_4 = PyObject_Length(__pyx_t_2); if (unlikely(__pyx_t_4 == -1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 148; __pyx_clineno = __LINE__; goto __pyx_L6_error;}
-      __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
-      if (unlikely(!(__pyx_t_4 == 2))) {
-        PyErr_SetNone(PyExc_AssertionError);
-        {__pyx_filename = __pyx_f[0]; __pyx_lineno = 148; __pyx_clineno = __LINE__; goto __pyx_L6_error;}
-      }
-      #endif
-
-      /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":149
- *         array = ob.__array_interface__
- *         assert len(array['shape']) == 2
- *         m = array['shape'][0]             # <<<<<<<<<<<<<<
- *         n = array['shape'][1]
- *         if m < 3:
- */
-      __pyx_t_2 = PyObject_GetItem(__pyx_v_array, ((PyObject *)__pyx_n_s__shape)); if (!__pyx_t_2) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 149; __pyx_clineno = __LINE__; goto __pyx_L6_error;}
-      __Pyx_GOTREF(__pyx_t_2);
-      __pyx_t_1 = __Pyx_GetItemInt(__pyx_t_2, 0, sizeof(long), PyInt_FromLong); if (!__pyx_t_1) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 149; __pyx_clineno = __LINE__; goto __pyx_L6_error;}
-      __Pyx_GOTREF(__pyx_t_1);
-      __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
-      __pyx_t_5 = __Pyx_PyInt_AsInt(__pyx_t_1); if (unlikely((__pyx_t_5 == (int)-1) && PyErr_Occurred())) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 149; __pyx_clineno = __LINE__; goto __pyx_L6_error;}
-      __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
-      __pyx_v_m = __pyx_t_5;
-
-      /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":150
- *         assert len(array['shape']) == 2
- *         m = array['shape'][0]
- *         n = array['shape'][1]             # <<<<<<<<<<<<<<
- *         if m < 3:
- *             raise ValueError(
- */
-      __pyx_t_1 = PyObject_GetItem(__pyx_v_array, ((PyObject *)__pyx_n_s__shape)); if (!__pyx_t_1) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 150; __pyx_clineno = __LINE__; goto __pyx_L6_error;}
-      __Pyx_GOTREF(__pyx_t_1);
-      __pyx_t_2 = __Pyx_GetItemInt(__pyx_t_1, 1, sizeof(long), PyInt_FromLong); if (!__pyx_t_2) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 150; __pyx_clineno = __LINE__; goto __pyx_L6_error;}
-      __Pyx_GOTREF(__pyx_t_2);
-      __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
-      __pyx_t_5 = __Pyx_PyInt_AsInt(__pyx_t_2); if (unlikely((__pyx_t_5 == (int)-1) && PyErr_Occurred())) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 150; __pyx_clineno = __LINE__; goto __pyx_L6_error;}
-      __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
-      __pyx_v_n = __pyx_t_5;
-
-      /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":151
- *         m = array['shape'][0]
- *         n = array['shape'][1]
- *         if m < 3:             # <<<<<<<<<<<<<<
- *             raise ValueError(
- *                 "A LinearRing must have at least 3 coordinate tuples")
- */
-      __pyx_t_6 = (__pyx_v_m < 3);
-      if (__pyx_t_6) {
-
-        /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":152
- *         n = array['shape'][1]
- *         if m < 3:
- *             raise ValueError(             # <<<<<<<<<<<<<<
- *                 "A LinearRing must have at least 3 coordinate tuples")
- *         assert n == 2 or n == 3
- */
-        __pyx_t_2 = PyObject_Call(__pyx_builtin_ValueError, ((PyObject *)__pyx_k_tuple_9), NULL); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 152; __pyx_clineno = __LINE__; goto __pyx_L6_error;}
-        __Pyx_GOTREF(__pyx_t_2);
-        __Pyx_Raise(__pyx_t_2, 0, 0);
-        __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
-        {__pyx_filename = __pyx_f[0]; __pyx_lineno = 152; __pyx_clineno = __LINE__; goto __pyx_L6_error;}
-        goto __pyx_L14;
-      }
-      __pyx_L14:;
-
-      /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":154
- *             raise ValueError(
- *                 "A LinearRing must have at least 3 coordinate tuples")
- *         assert n == 2 or n == 3             # <<<<<<<<<<<<<<
- * 
- *         # Make pointer to the coordinate array
- */
-      #ifndef CYTHON_WITHOUT_ASSERTIONS
-      switch (__pyx_v_n) {
-        case 2:
-        case 3:
-        __pyx_t_6 = 1;
-        break;
-        default:
-        __pyx_t_6 = 0;
-        break;
-      }
-      if (unlikely(!__pyx_t_6)) {
-        PyErr_SetNone(PyExc_AssertionError);
-        {__pyx_filename = __pyx_f[0]; __pyx_lineno = 154; __pyx_clineno = __LINE__; goto __pyx_L6_error;}
-      }
-      #endif
-
-      /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":157
- * 
- *         # Make pointer to the coordinate array
- *         if isinstance(array['data'], ctypes.Array):             # <<<<<<<<<<<<<<
- *             cp = <double *><unsigned long>ctypes.addressof(array['data'])
- *         else:
- */
-      __pyx_t_2 = PyObject_GetItem(__pyx_v_array, ((PyObject *)__pyx_n_s__data)); if (!__pyx_t_2) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 157; __pyx_clineno = __LINE__; goto __pyx_L6_error;}
-      __Pyx_GOTREF(__pyx_t_2);
-      __pyx_t_1 = __Pyx_GetName(__pyx_m, __pyx_n_s__ctypes); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 157; __pyx_clineno = __LINE__; goto __pyx_L6_error;}
-      __Pyx_GOTREF(__pyx_t_1);
-      __pyx_t_7 = PyObject_GetAttr(__pyx_t_1, __pyx_n_s__Array); if (unlikely(!__pyx_t_7)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 157; __pyx_clineno = __LINE__; goto __pyx_L6_error;}
-      __Pyx_GOTREF(__pyx_t_7);
-      __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
-      __pyx_t_6 = PyObject_IsInstance(__pyx_t_2, __pyx_t_7); if (unlikely(__pyx_t_6 == -1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 157; __pyx_clineno = __LINE__; goto __pyx_L6_error;}
-      __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
-      __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0;
-      if (__pyx_t_6) {
-
-        /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":158
- *         # Make pointer to the coordinate array
- *         if isinstance(array['data'], ctypes.Array):
- *             cp = <double *><unsigned long>ctypes.addressof(array['data'])             # <<<<<<<<<<<<<<
- *         else:
- *             cp = <double *><unsigned long>array['data'][0]
- */
-        __pyx_t_7 = __Pyx_GetName(__pyx_m, __pyx_n_s__ctypes); if (unlikely(!__pyx_t_7)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 158; __pyx_clineno = __LINE__; goto __pyx_L6_error;}
-        __Pyx_GOTREF(__pyx_t_7);
-        __pyx_t_2 = PyObject_GetAttr(__pyx_t_7, __pyx_n_s__addressof); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 158; __pyx_clineno = __LINE__; goto __pyx_L6_error;}
-        __Pyx_GOTREF(__pyx_t_2);
-        __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0;
-        __pyx_t_7 = PyObject_GetItem(__pyx_v_array, ((PyObject *)__pyx_n_s__data)); if (!__pyx_t_7) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 158; __pyx_clineno = __LINE__; goto __pyx_L6_error;}
-        __Pyx_GOTREF(__pyx_t_7);
-        __pyx_t_1 = PyTuple_New(1); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 158; __pyx_clineno = __LINE__; goto __pyx_L6_error;}
-        __Pyx_GOTREF(((PyObject *)__pyx_t_1));
-        PyTuple_SET_ITEM(__pyx_t_1, 0, __pyx_t_7);
-        __Pyx_GIVEREF(__pyx_t_7);
-        __pyx_t_7 = 0;
-        __pyx_t_7 = PyObject_Call(__pyx_t_2, ((PyObject *)__pyx_t_1), NULL); if (unlikely(!__pyx_t_7)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 158; __pyx_clineno = __LINE__; goto __pyx_L6_error;}
-        __Pyx_GOTREF(__pyx_t_7);
-        __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
-        __Pyx_DECREF(((PyObject *)__pyx_t_1)); __pyx_t_1 = 0;
-        __pyx_t_3 = __Pyx_PyInt_AsUnsignedLong(__pyx_t_7); if (unlikely((__pyx_t_3 == (unsigned long)-1) && PyErr_Occurred())) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 158; __pyx_clineno = __LINE__; goto __pyx_L6_error;}
-        __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0;
-        __pyx_v_cp = ((double *)__pyx_t_3);
-        goto __pyx_L15;
-      }
-      /*else*/ {
-
-        /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":160
- *             cp = <double *><unsigned long>ctypes.addressof(array['data'])
- *         else:
- *             cp = <double *><unsigned long>array['data'][0]             # <<<<<<<<<<<<<<
- * 
- *         # Add closing coordinates to sequence?
- */
-        __pyx_t_7 = PyObject_GetItem(__pyx_v_array, ((PyObject *)__pyx_n_s__data)); if (!__pyx_t_7) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 160; __pyx_clineno = __LINE__; goto __pyx_L6_error;}
-        __Pyx_GOTREF(__pyx_t_7);
-        __pyx_t_1 = __Pyx_GetItemInt(__pyx_t_7, 0, sizeof(long), PyInt_FromLong); if (!__pyx_t_1) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 160; __pyx_clineno = __LINE__; goto __pyx_L6_error;}
-        __Pyx_GOTREF(__pyx_t_1);
-        __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0;
-        __pyx_t_3 = __Pyx_PyInt_AsUnsignedLong(__pyx_t_1); if (unlikely((__pyx_t_3 == (unsigned long)-1) && PyErr_Occurred())) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 160; __pyx_clineno = __LINE__; goto __pyx_L6_error;}
-        __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
-        __pyx_v_cp = ((double *)__pyx_t_3);
-      }
-      __pyx_L15:;
-
-      /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":163
- * 
- *         # Add closing coordinates to sequence?
- *         if cp[0] != cp[m*n-n] or cp[1] != cp[m*n-n+1]:             # <<<<<<<<<<<<<<
- *             M = m + 1
- *         else:
- */
-      __pyx_t_6 = ((__pyx_v_cp[0]) != (__pyx_v_cp[((__pyx_v_m * __pyx_v_n) - __pyx_v_n)]));
-      if (!__pyx_t_6) {
-        __pyx_t_8 = ((__pyx_v_cp[1]) != (__pyx_v_cp[(((__pyx_v_m * __pyx_v_n) - __pyx_v_n) + 1)]));
-        __pyx_t_9 = __pyx_t_8;
-      } else {
-        __pyx_t_9 = __pyx_t_6;
-      }
-      if (__pyx_t_9) {
-
-        /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":164
- *         # Add closing coordinates to sequence?
- *         if cp[0] != cp[m*n-n] or cp[1] != cp[m*n-n+1]:
- *             M = m + 1             # <<<<<<<<<<<<<<
- *         else:
- *             M = m
- */
-        __pyx_v_M = (__pyx_v_m + 1);
-        goto __pyx_L16;
-      }
-      /*else*/ {
-
-        /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":166
- *             M = m + 1
- *         else:
- *             M = m             # <<<<<<<<<<<<<<
- * 
- *         # Create a coordinate sequence
- */
-        __pyx_v_M = __pyx_v_m;
-      }
-      __pyx_L16:;
-
-      /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":169
- * 
- *         # Create a coordinate sequence
- *         if update_geom is not None:             # <<<<<<<<<<<<<<
- *             cs = GEOSGeom_getCoordSeq_r(handle, cast_geom(update_geom))
- *             if n != update_ndim:
- */
-      __pyx_t_9 = (__pyx_v_update_geom != Py_None);
-      if (__pyx_t_9) {
-
-        /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":170
- *         # Create a coordinate sequence
- *         if update_geom is not None:
- *             cs = GEOSGeom_getCoordSeq_r(handle, cast_geom(update_geom))             # <<<<<<<<<<<<<<
- *             if n != update_ndim:
- *                 raise ValueError(
- */
-        __pyx_t_3 = __Pyx_PyInt_AsUnsignedLong(__pyx_v_update_geom); if (unlikely((__pyx_t_3 == (unsigned long)-1) && PyErr_Occurred())) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 170; __pyx_clineno = __LINE__; goto __pyx_L6_error;}
-        __pyx_v_cs = GEOSGeom_getCoordSeq_r(__pyx_v_handle, __pyx_f_7shapely_8speedups_9_speedups_cast_geom(__pyx_t_3));
-
-        /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":171
- *         if update_geom is not None:
- *             cs = GEOSGeom_getCoordSeq_r(handle, cast_geom(update_geom))
- *             if n != update_ndim:             # <<<<<<<<<<<<<<
- *                 raise ValueError(
- *                 "Wrong coordinate dimensions; this geometry has dimensions: %d" \
- */
-        __pyx_t_1 = PyInt_FromLong(__pyx_v_n); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 171; __pyx_clineno = __LINE__; goto __pyx_L6_error;}
-        __Pyx_GOTREF(__pyx_t_1);
-        __pyx_t_7 = PyObject_RichCompare(__pyx_t_1, __pyx_v_update_ndim, Py_NE); if (unlikely(!__pyx_t_7)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 171; __pyx_clineno = __LINE__; goto __pyx_L6_error;}
-        __Pyx_GOTREF(__pyx_t_7);
-        __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
-        __pyx_t_9 = __Pyx_PyObject_IsTrue(__pyx_t_7); if (unlikely(__pyx_t_9 < 0)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 171; __pyx_clineno = __LINE__; goto __pyx_L6_error;}
-        __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0;
-        if (__pyx_t_9) {
-
-          /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":174
- *                 raise ValueError(
- *                 "Wrong coordinate dimensions; this geometry has dimensions: %d" \
- *                 % update_ndim)             # <<<<<<<<<<<<<<
- *         else:
- *             cs = GEOSCoordSeq_create_r(handle, M, n)
- */
-          __pyx_t_7 = PyNumber_Remainder(((PyObject *)__pyx_kp_s_4), __pyx_v_update_ndim); if (unlikely(!__pyx_t_7)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 174; __pyx_clineno = __LINE__; goto __pyx_L6_error;}
-          __Pyx_GOTREF(((PyObject *)__pyx_t_7));
-          __pyx_t_1 = PyTuple_New(1); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 172; __pyx_clineno = __LINE__; goto __pyx_L6_error;}
-          __Pyx_GOTREF(((PyObject *)__pyx_t_1));
-          PyTuple_SET_ITEM(__pyx_t_1, 0, ((PyObject *)__pyx_t_7));
-          __Pyx_GIVEREF(((PyObject *)__pyx_t_7));
-          __pyx_t_7 = 0;
-          __pyx_t_7 = PyObject_Call(__pyx_builtin_ValueError, ((PyObject *)__pyx_t_1), NULL); if (unlikely(!__pyx_t_7)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 172; __pyx_clineno = __LINE__; goto __pyx_L6_error;}
-          __Pyx_GOTREF(__pyx_t_7);
-          __Pyx_DECREF(((PyObject *)__pyx_t_1)); __pyx_t_1 = 0;
-          __Pyx_Raise(__pyx_t_7, 0, 0);
-          __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0;
-          {__pyx_filename = __pyx_f[0]; __pyx_lineno = 172; __pyx_clineno = __LINE__; goto __pyx_L6_error;}
-          goto __pyx_L18;
-        }
-        __pyx_L18:;
-        goto __pyx_L17;
-      }
-      /*else*/ {
-
-        /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":176
- *                 % update_ndim)
- *         else:
- *             cs = GEOSCoordSeq_create_r(handle, M, n)             # <<<<<<<<<<<<<<
- * 
- *         # add to coordinate sequence
- */
-        __pyx_v_cs = GEOSCoordSeq_create_r(__pyx_v_handle, __pyx_v_M, __pyx_v_n);
-      }
-      __pyx_L17:;
-
-      /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":179
- * 
- *         # add to coordinate sequence
- *         for i in xrange(m):             # <<<<<<<<<<<<<<
- *             dx = cp[n*i]
- *             dy = cp[n*i+1]
- */
-      __pyx_t_5 = __pyx_v_m;
-      for (__pyx_t_10 = 0; __pyx_t_10 < __pyx_t_5; __pyx_t_10+=1) {
-        __pyx_v_i = __pyx_t_10;
-
-        /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":180
- *         # add to coordinate sequence
- *         for i in xrange(m):
- *             dx = cp[n*i]             # <<<<<<<<<<<<<<
- *             dy = cp[n*i+1]
- *             dz = 0
- */
-        __pyx_v_dx = (__pyx_v_cp[(__pyx_v_n * __pyx_v_i)]);
-
-        /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":181
- *         for i in xrange(m):
- *             dx = cp[n*i]
- *             dy = cp[n*i+1]             # <<<<<<<<<<<<<<
- *             dz = 0
- *             if n == 3:
- */
-        __pyx_v_dy = (__pyx_v_cp[((__pyx_v_n * __pyx_v_i) + 1)]);
-
-        /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":182
- *             dx = cp[n*i]
- *             dy = cp[n*i+1]
- *             dz = 0             # <<<<<<<<<<<<<<
- *             if n == 3:
- *                 dz = cp[n*i+2]
- */
-        __pyx_v_dz = 0.0;
-
-        /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":183
- *             dy = cp[n*i+1]
- *             dz = 0
- *             if n == 3:             # <<<<<<<<<<<<<<
- *                 dz = cp[n*i+2]
- * 
- */
-        __pyx_t_9 = (__pyx_v_n == 3);
-        if (__pyx_t_9) {
-
-          /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":184
- *             dz = 0
- *             if n == 3:
- *                 dz = cp[n*i+2]             # <<<<<<<<<<<<<<
- * 
- *             # Because of a bug in the GEOS C API,
- */
-          __pyx_v_dz = (__pyx_v_cp[((__pyx_v_n * __pyx_v_i) + 2)]);
-          goto __pyx_L21;
-        }
-        __pyx_L21:;
-
-        /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":188
- *             # Because of a bug in the GEOS C API,
- *             # always set X before Y
- *             GEOSCoordSeq_setX_r(handle, cs, i, dx)             # <<<<<<<<<<<<<<
- *             GEOSCoordSeq_setY_r(handle, cs, i, dy)
- *             if n == 3:
- */
-        GEOSCoordSeq_setX_r(__pyx_v_handle, __pyx_v_cs, __pyx_v_i, __pyx_v_dx);
-
-        /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":189
- *             # always set X before Y
- *             GEOSCoordSeq_setX_r(handle, cs, i, dx)
- *             GEOSCoordSeq_setY_r(handle, cs, i, dy)             # <<<<<<<<<<<<<<
- *             if n == 3:
- *                 GEOSCoordSeq_setZ_r(handle, cs, i, dz)
- */
-        GEOSCoordSeq_setY_r(__pyx_v_handle, __pyx_v_cs, __pyx_v_i, __pyx_v_dy);
-
-        /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":190
- *             GEOSCoordSeq_setX_r(handle, cs, i, dx)
- *             GEOSCoordSeq_setY_r(handle, cs, i, dy)
- *             if n == 3:             # <<<<<<<<<<<<<<
- *                 GEOSCoordSeq_setZ_r(handle, cs, i, dz)
- * 
- */
-        __pyx_t_9 = (__pyx_v_n == 3);
-        if (__pyx_t_9) {
-
-          /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":191
- *             GEOSCoordSeq_setY_r(handle, cs, i, dy)
- *             if n == 3:
- *                 GEOSCoordSeq_setZ_r(handle, cs, i, dz)             # <<<<<<<<<<<<<<
- * 
- *         # Add closing coordinates to sequence?
- */
-          GEOSCoordSeq_setZ_r(__pyx_v_handle, __pyx_v_cs, __pyx_v_i, __pyx_v_dz);
-          goto __pyx_L22;
-        }
-        __pyx_L22:;
-      }
-
-      /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":194
- * 
- *         # Add closing coordinates to sequence?
- *         if M > m:             # <<<<<<<<<<<<<<
- *             dx = cp[0]
- *             dy = cp[1]
- */
-      __pyx_t_9 = (__pyx_v_M > __pyx_v_m);
-      if (__pyx_t_9) {
-
-        /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":195
- *         # Add closing coordinates to sequence?
- *         if M > m:
- *             dx = cp[0]             # <<<<<<<<<<<<<<
- *             dy = cp[1]
- *             dz = 0
- */
-        __pyx_v_dx = (__pyx_v_cp[0]);
-
-        /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":196
- *         if M > m:
- *             dx = cp[0]
- *             dy = cp[1]             # <<<<<<<<<<<<<<
- *             dz = 0
- *             if n == 3:
- */
-        __pyx_v_dy = (__pyx_v_cp[1]);
-
-        /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":197
- *             dx = cp[0]
- *             dy = cp[1]
- *             dz = 0             # <<<<<<<<<<<<<<
- *             if n == 3:
- *                 dz = cp[2]
- */
-        __pyx_v_dz = 0.0;
-
-        /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":198
- *             dy = cp[1]
- *             dz = 0
- *             if n == 3:             # <<<<<<<<<<<<<<
- *                 dz = cp[2]
- * 
- */
-        __pyx_t_9 = (__pyx_v_n == 3);
-        if (__pyx_t_9) {
-
-          /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":199
- *             dz = 0
- *             if n == 3:
- *                 dz = cp[2]             # <<<<<<<<<<<<<<
- * 
- *             # Because of a bug in the GEOS C API,
- */
-          __pyx_v_dz = (__pyx_v_cp[2]);
-          goto __pyx_L24;
-        }
-        __pyx_L24:;
-
-        /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":203
- *             # Because of a bug in the GEOS C API,
- *             # always set X before Y
- *             GEOSCoordSeq_setX_r(handle, cs, M-1, dx)             # <<<<<<<<<<<<<<
- *             GEOSCoordSeq_setY_r(handle, cs, M-1, dy)
- *             if n == 3:
- */
-        GEOSCoordSeq_setX_r(__pyx_v_handle, __pyx_v_cs, (__pyx_v_M - 1), __pyx_v_dx);
-
-        /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":204
- *             # always set X before Y
- *             GEOSCoordSeq_setX_r(handle, cs, M-1, dx)
- *             GEOSCoordSeq_setY_r(handle, cs, M-1, dy)             # <<<<<<<<<<<<<<
- *             if n == 3:
- *                 GEOSCoordSeq_setZ_r(handle, cs, M-1, dz)
- */
-        GEOSCoordSeq_setY_r(__pyx_v_handle, __pyx_v_cs, (__pyx_v_M - 1), __pyx_v_dy);
-
-        /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":205
- *             GEOSCoordSeq_setX_r(handle, cs, M-1, dx)
- *             GEOSCoordSeq_setY_r(handle, cs, M-1, dy)
- *             if n == 3:             # <<<<<<<<<<<<<<
- *                 GEOSCoordSeq_setZ_r(handle, cs, M-1, dz)
- * 
- */
-        __pyx_t_9 = (__pyx_v_n == 3);
-        if (__pyx_t_9) {
-
-          /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":206
- *             GEOSCoordSeq_setY_r(handle, cs, M-1, dy)
- *             if n == 3:
- *                 GEOSCoordSeq_setZ_r(handle, cs, M-1, dz)             # <<<<<<<<<<<<<<
- * 
- *     except AttributeError:
- */
-          GEOSCoordSeq_setZ_r(__pyx_v_handle, __pyx_v_cs, (__pyx_v_M - 1), __pyx_v_dz);
-          goto __pyx_L25;
-        }
-        __pyx_L25:;
-        goto __pyx_L23;
-      }
-      __pyx_L23:;
-    }
-    __Pyx_XDECREF(__pyx_save_exc_type); __pyx_save_exc_type = 0;
-    __Pyx_XDECREF(__pyx_save_exc_value); __pyx_save_exc_value = 0;
-    __Pyx_XDECREF(__pyx_save_exc_tb); __pyx_save_exc_tb = 0;
-    goto __pyx_L13_try_end;
-    __pyx_L6_error:;
-    __Pyx_XDECREF(__pyx_t_2); __pyx_t_2 = 0;
-    __Pyx_XDECREF(__pyx_t_1); __pyx_t_1 = 0;
-    __Pyx_XDECREF(__pyx_t_7); __pyx_t_7 = 0;
-
-    /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":208
- *                 GEOSCoordSeq_setZ_r(handle, cs, M-1, dz)
- * 
- *     except AttributeError:             # <<<<<<<<<<<<<<
- *         # Fall back on list
- *         m = len(ob)
- */
-    __pyx_t_5 = PyErr_ExceptionMatches(__pyx_builtin_AttributeError);
-    if (__pyx_t_5) {
-      __Pyx_AddTraceback("shapely.speedups._speedups.geos_linearring_from_py");
-      if (__Pyx_GetException(&__pyx_t_7, &__pyx_t_1, &__pyx_t_2) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 208; __pyx_clineno = __LINE__; goto __pyx_L8_except_error;}
-      __Pyx_GOTREF(__pyx_t_7);
-      __Pyx_GOTREF(__pyx_t_1);
-      __Pyx_GOTREF(__pyx_t_2);
-
-      /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":210
- *     except AttributeError:
- *         # Fall back on list
- *         m = len(ob)             # <<<<<<<<<<<<<<
- *         n = len(ob[0])
- *         if m < 3:
- */
-      __pyx_t_4 = PyObject_Length(__pyx_v_ob); if (unlikely(__pyx_t_4 == -1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 210; __pyx_clineno = __LINE__; goto __pyx_L8_except_error;}
-      __pyx_v_m = __pyx_t_4;
-
-      /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":211
- *         # Fall back on list
- *         m = len(ob)
- *         n = len(ob[0])             # <<<<<<<<<<<<<<
- *         if m < 3:
- *             raise ValueError(
- */
-      __pyx_t_11 = __Pyx_GetItemInt(__pyx_v_ob, 0, sizeof(long), PyInt_FromLong); if (!__pyx_t_11) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 211; __pyx_clineno = __LINE__; goto __pyx_L8_except_error;}
-      __Pyx_GOTREF(__pyx_t_11);
-      __pyx_t_4 = PyObject_Length(__pyx_t_11); if (unlikely(__pyx_t_4 == -1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 211; __pyx_clineno = __LINE__; goto __pyx_L8_except_error;}
-      __Pyx_DECREF(__pyx_t_11); __pyx_t_11 = 0;
-      __pyx_v_n = __pyx_t_4;
-
-      /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":212
- *         m = len(ob)
- *         n = len(ob[0])
- *         if m < 3:             # <<<<<<<<<<<<<<
- *             raise ValueError(
- *                 "A LinearRing must have at least 3 coordinate tuples")
- */
-      __pyx_t_9 = (__pyx_v_m < 3);
-      if (__pyx_t_9) {
-
-        /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":213
- *         n = len(ob[0])
- *         if m < 3:
- *             raise ValueError(             # <<<<<<<<<<<<<<
- *                 "A LinearRing must have at least 3 coordinate tuples")
- *         assert (n == 2 or n == 3)
- */
-        __pyx_t_11 = PyObject_Call(__pyx_builtin_ValueError, ((PyObject *)__pyx_k_tuple_10), NULL); if (unlikely(!__pyx_t_11)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 213; __pyx_clineno = __LINE__; goto __pyx_L8_except_error;}
-        __Pyx_GOTREF(__pyx_t_11);
-        __Pyx_Raise(__pyx_t_11, 0, 0);
-        __Pyx_DECREF(__pyx_t_11); __pyx_t_11 = 0;
-        {__pyx_filename = __pyx_f[0]; __pyx_lineno = 213; __pyx_clineno = __LINE__; goto __pyx_L8_except_error;}
-        goto __pyx_L28;
-      }
-      __pyx_L28:;
-
-      /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":215
- *             raise ValueError(
- *                 "A LinearRing must have at least 3 coordinate tuples")
- *         assert (n == 2 or n == 3)             # <<<<<<<<<<<<<<
- * 
- *         # Add closing coordinates if not provided
- */
-      #ifndef CYTHON_WITHOUT_ASSERTIONS
-      switch (__pyx_v_n) {
-        case 2:
-        case 3:
-        __pyx_t_9 = 1;
-        break;
-        default:
-        __pyx_t_9 = 0;
-        break;
-      }
-      if (unlikely(!__pyx_t_9)) {
-        PyErr_SetNone(PyExc_AssertionError);
-        {__pyx_filename = __pyx_f[0]; __pyx_lineno = 215; __pyx_clineno = __LINE__; goto __pyx_L8_except_error;}
-      }
-      #endif
-
-      /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":218
- * 
- *         # Add closing coordinates if not provided
- *         if m == 3 or ob[0][0] != ob[-1][0] or ob[0][1] != ob[-1][1]:             # <<<<<<<<<<<<<<
- *             M = m + 1
- *         else:
- */
-      __pyx_t_9 = (__pyx_v_m == 3);
-      if (!__pyx_t_9) {
-        __pyx_t_11 = __Pyx_GetItemInt(__pyx_v_ob, 0, sizeof(long), PyInt_FromLong); if (!__pyx_t_11) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 218; __pyx_clineno = __LINE__; goto __pyx_L8_except_error;}
-        __Pyx_GOTREF(__pyx_t_11);
-        __pyx_t_12 = __Pyx_GetItemInt(__pyx_t_11, 0, sizeof(long), PyInt_FromLong); if (!__pyx_t_12) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 218; __pyx_clineno = __LINE__; goto __pyx_L8_except_error;}
-        __Pyx_GOTREF(__pyx_t_12);
-        __Pyx_DECREF(__pyx_t_11); __pyx_t_11 = 0;
-        __pyx_t_11 = __Pyx_GetItemInt(__pyx_v_ob, -1, sizeof(long), PyInt_FromLong); if (!__pyx_t_11) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 218; __pyx_clineno = __LINE__; goto __pyx_L8_except_error;}
-        __Pyx_GOTREF(__pyx_t_11);
-        __pyx_t_13 = __Pyx_GetItemInt(__pyx_t_11, 0, sizeof(long), PyInt_FromLong); if (!__pyx_t_13) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 218; __pyx_clineno = __LINE__; goto __pyx_L8_except_error;}
-        __Pyx_GOTREF(__pyx_t_13);
-        __Pyx_DECREF(__pyx_t_11); __pyx_t_11 = 0;
-        __pyx_t_11 = PyObject_RichCompare(__pyx_t_12, __pyx_t_13, Py_NE); if (unlikely(!__pyx_t_11)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 218; __pyx_clineno = __LINE__; goto __pyx_L8_except_error;}
-        __Pyx_GOTREF(__pyx_t_11);
-        __Pyx_DECREF(__pyx_t_12); __pyx_t_12 = 0;
-        __Pyx_DECREF(__pyx_t_13); __pyx_t_13 = 0;
-        __pyx_t_6 = __Pyx_PyObject_IsTrue(__pyx_t_11); if (unlikely(__pyx_t_6 < 0)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 218; __pyx_clineno = __LINE__; goto __pyx_L8_except_error;}
-        __Pyx_DECREF(__pyx_t_11); __pyx_t_11 = 0;
-        if (!__pyx_t_6) {
-          __pyx_t_11 = __Pyx_GetItemInt(__pyx_v_ob, 0, sizeof(long), PyInt_FromLong); if (!__pyx_t_11) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 218; __pyx_clineno = __LINE__; goto __pyx_L8_except_error;}
-          __Pyx_GOTREF(__pyx_t_11);
-          __pyx_t_13 = __Pyx_GetItemInt(__pyx_t_11, 1, sizeof(long), PyInt_FromLong); if (!__pyx_t_13) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 218; __pyx_clineno = __LINE__; goto __pyx_L8_except_error;}
-          __Pyx_GOTREF(__pyx_t_13);
-          __Pyx_DECREF(__pyx_t_11); __pyx_t_11 = 0;
-          __pyx_t_11 = __Pyx_GetItemInt(__pyx_v_ob, -1, sizeof(long), PyInt_FromLong); if (!__pyx_t_11) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 218; __pyx_clineno = __LINE__; goto __pyx_L8_except_error;}
-          __Pyx_GOTREF(__pyx_t_11);
-          __pyx_t_12 = __Pyx_GetItemInt(__pyx_t_11, 1, sizeof(long), PyInt_FromLong); if (!__pyx_t_12) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 218; __pyx_clineno = __LINE__; goto __pyx_L8_except_error;}
-          __Pyx_GOTREF(__pyx_t_12);
-          __Pyx_DECREF(__pyx_t_11); __pyx_t_11 = 0;
-          __pyx_t_11 = PyObject_RichCompare(__pyx_t_13, __pyx_t_12, Py_NE); if (unlikely(!__pyx_t_11)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 218; __pyx_clineno = __LINE__; goto __pyx_L8_except_error;}
-          __Pyx_GOTREF(__pyx_t_11);
-          __Pyx_DECREF(__pyx_t_13); __pyx_t_13 = 0;
-          __Pyx_DECREF(__pyx_t_12); __pyx_t_12 = 0;
-          __pyx_t_8 = __Pyx_PyObject_IsTrue(__pyx_t_11); if (unlikely(__pyx_t_8 < 0)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 218; __pyx_clineno = __LINE__; goto __pyx_L8_except_error;}
-          __Pyx_DECREF(__pyx_t_11); __pyx_t_11 = 0;
-          __pyx_t_14 = __pyx_t_8;
-        } else {
-          __pyx_t_14 = __pyx_t_6;
-        }
-        __pyx_t_6 = __pyx_t_14;
-      } else {
-        __pyx_t_6 = __pyx_t_9;
-      }
-      if (__pyx_t_6) {
-
-        /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":219
- *         # Add closing coordinates if not provided
- *         if m == 3 or ob[0][0] != ob[-1][0] or ob[0][1] != ob[-1][1]:
- *             M = m + 1             # <<<<<<<<<<<<<<
- *         else:
- *             M = m
- */
-        __pyx_v_M = (__pyx_v_m + 1);
-        goto __pyx_L29;
-      }
-      /*else*/ {
-
-        /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":221
- *             M = m + 1
- *         else:
- *             M = m             # <<<<<<<<<<<<<<
- * 
- *         # Create a coordinate sequence
- */
-        __pyx_v_M = __pyx_v_m;
-      }
-      __pyx_L29:;
-
-      /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":224
- * 
- *         # Create a coordinate sequence
- *         if update_geom is not None:             # <<<<<<<<<<<<<<
- *             cs = GEOSGeom_getCoordSeq_r(handle, cast_geom(update_geom))
- *             if n != update_ndim:
- */
-      __pyx_t_6 = (__pyx_v_update_geom != Py_None);
-      if (__pyx_t_6) {
-
-        /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":225
- *         # Create a coordinate sequence
- *         if update_geom is not None:
- *             cs = GEOSGeom_getCoordSeq_r(handle, cast_geom(update_geom))             # <<<<<<<<<<<<<<
- *             if n != update_ndim:
- *                 raise ValueError(
- */
-        __pyx_t_3 = __Pyx_PyInt_AsUnsignedLong(__pyx_v_update_geom); if (unlikely((__pyx_t_3 == (unsigned long)-1) && PyErr_Occurred())) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 225; __pyx_clineno = __LINE__; goto __pyx_L8_except_error;}
-        __pyx_v_cs = GEOSGeom_getCoordSeq_r(__pyx_v_handle, __pyx_f_7shapely_8speedups_9_speedups_cast_geom(__pyx_t_3));
-
-        /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":226
- *         if update_geom is not None:
- *             cs = GEOSGeom_getCoordSeq_r(handle, cast_geom(update_geom))
- *             if n != update_ndim:             # <<<<<<<<<<<<<<
- *                 raise ValueError(
- *                 "Wrong coordinate dimensions; this geometry has dimensions: %d" \
- */
-        __pyx_t_11 = PyInt_FromLong(__pyx_v_n); if (unlikely(!__pyx_t_11)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 226; __pyx_clineno = __LINE__; goto __pyx_L8_except_error;}
-        __Pyx_GOTREF(__pyx_t_11);
-        __pyx_t_12 = PyObject_RichCompare(__pyx_t_11, __pyx_v_update_ndim, Py_NE); if (unlikely(!__pyx_t_12)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 226; __pyx_clineno = __LINE__; goto __pyx_L8_except_error;}
-        __Pyx_GOTREF(__pyx_t_12);
-        __Pyx_DECREF(__pyx_t_11); __pyx_t_11 = 0;
-        __pyx_t_6 = __Pyx_PyObject_IsTrue(__pyx_t_12); if (unlikely(__pyx_t_6 < 0)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 226; __pyx_clineno = __LINE__; goto __pyx_L8_except_error;}
-        __Pyx_DECREF(__pyx_t_12); __pyx_t_12 = 0;
-        if (__pyx_t_6) {
-
-          /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":229
- *                 raise ValueError(
- *                 "Wrong coordinate dimensions; this geometry has dimensions: %d" \
- *                 % update_ndim)             # <<<<<<<<<<<<<<
- *         else:
- *             cs = GEOSCoordSeq_create_r(handle, M, n)
- */
-          __pyx_t_12 = PyNumber_Remainder(((PyObject *)__pyx_kp_s_4), __pyx_v_update_ndim); if (unlikely(!__pyx_t_12)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 229; __pyx_clineno = __LINE__; goto __pyx_L8_except_error;}
-          __Pyx_GOTREF(((PyObject *)__pyx_t_12));
-          __pyx_t_11 = PyTuple_New(1); if (unlikely(!__pyx_t_11)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 227; __pyx_clineno = __LINE__; goto __pyx_L8_except_error;}
-          __Pyx_GOTREF(((PyObject *)__pyx_t_11));
-          PyTuple_SET_ITEM(__pyx_t_11, 0, ((PyObject *)__pyx_t_12));
-          __Pyx_GIVEREF(((PyObject *)__pyx_t_12));
-          __pyx_t_12 = 0;
-          __pyx_t_12 = PyObject_Call(__pyx_builtin_ValueError, ((PyObject *)__pyx_t_11), NULL); if (unlikely(!__pyx_t_12)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 227; __pyx_clineno = __LINE__; goto __pyx_L8_except_error;}
-          __Pyx_GOTREF(__pyx_t_12);
-          __Pyx_DECREF(((PyObject *)__pyx_t_11)); __pyx_t_11 = 0;
-          __Pyx_Raise(__pyx_t_12, 0, 0);
-          __Pyx_DECREF(__pyx_t_12); __pyx_t_12 = 0;
-          {__pyx_filename = __pyx_f[0]; __pyx_lineno = 227; __pyx_clineno = __LINE__; goto __pyx_L8_except_error;}
-          goto __pyx_L31;
-        }
-        __pyx_L31:;
-        goto __pyx_L30;
-      }
-      /*else*/ {
-
-        /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":231
- *                 % update_ndim)
- *         else:
- *             cs = GEOSCoordSeq_create_r(handle, M, n)             # <<<<<<<<<<<<<<
- * 
- *         # add to coordinate sequence
- */
-        __pyx_v_cs = GEOSCoordSeq_create_r(__pyx_v_handle, __pyx_v_M, __pyx_v_n);
-      }
-      __pyx_L30:;
-
-      /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":234
- * 
- *         # add to coordinate sequence
- *         for i in xrange(m):             # <<<<<<<<<<<<<<
- *             coords = ob[i]
- *             dx = coords[0]
- */
-      __pyx_t_5 = __pyx_v_m;
-      for (__pyx_t_10 = 0; __pyx_t_10 < __pyx_t_5; __pyx_t_10+=1) {
-        __pyx_v_i = __pyx_t_10;
-
-        /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":235
- *         # add to coordinate sequence
- *         for i in xrange(m):
- *             coords = ob[i]             # <<<<<<<<<<<<<<
- *             dx = coords[0]
- *             dy = coords[1]
- */
-        __pyx_t_12 = __Pyx_GetItemInt(__pyx_v_ob, __pyx_v_i, sizeof(int), PyInt_FromLong); if (!__pyx_t_12) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 235; __pyx_clineno = __LINE__; goto __pyx_L8_except_error;}
-        __Pyx_GOTREF(__pyx_t_12);
-        __Pyx_DECREF(__pyx_v_coords);
-        __pyx_v_coords = __pyx_t_12;
-        __pyx_t_12 = 0;
-
-        /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":236
- *         for i in xrange(m):
- *             coords = ob[i]
- *             dx = coords[0]             # <<<<<<<<<<<<<<
- *             dy = coords[1]
- *             dz = 0
- */
-        __pyx_t_12 = __Pyx_GetItemInt(__pyx_v_coords, 0, sizeof(long), PyInt_FromLong); if (!__pyx_t_12) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 236; __pyx_clineno = __LINE__; goto __pyx_L8_except_error;}
-        __Pyx_GOTREF(__pyx_t_12);
-        __pyx_t_15 = __pyx_PyFloat_AsDouble(__pyx_t_12); if (unlikely((__pyx_t_15 == (double)-1) && PyErr_Occurred())) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 236; __pyx_clineno = __LINE__; goto __pyx_L8_except_error;}
-        __Pyx_DECREF(__pyx_t_12); __pyx_t_12 = 0;
-        __pyx_v_dx = __pyx_t_15;
-
-        /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":237
- *             coords = ob[i]
- *             dx = coords[0]
- *             dy = coords[1]             # <<<<<<<<<<<<<<
- *             dz = 0
- *             if n == 3:
- */
-        __pyx_t_12 = __Pyx_GetItemInt(__pyx_v_coords, 1, sizeof(long), PyInt_FromLong); if (!__pyx_t_12) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 237; __pyx_clineno = __LINE__; goto __pyx_L8_except_error;}
-        __Pyx_GOTREF(__pyx_t_12);
-        __pyx_t_15 = __pyx_PyFloat_AsDouble(__pyx_t_12); if (unlikely((__pyx_t_15 == (double)-1) && PyErr_Occurred())) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 237; __pyx_clineno = __LINE__; goto __pyx_L8_except_error;}
-        __Pyx_DECREF(__pyx_t_12); __pyx_t_12 = 0;
-        __pyx_v_dy = __pyx_t_15;
-
-        /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":238
- *             dx = coords[0]
- *             dy = coords[1]
- *             dz = 0             # <<<<<<<<<<<<<<
- *             if n == 3:
- *                 dz = coords[2]
- */
-        __pyx_v_dz = 0.0;
-
-        /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":239
- *             dy = coords[1]
- *             dz = 0
- *             if n == 3:             # <<<<<<<<<<<<<<
- *                 dz = coords[2]
- * 
- */
-        __pyx_t_6 = (__pyx_v_n == 3);
-        if (__pyx_t_6) {
-
-          /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":240
- *             dz = 0
- *             if n == 3:
- *                 dz = coords[2]             # <<<<<<<<<<<<<<
- * 
- *             # Because of a bug in the GEOS C API,
- */
-          __pyx_t_12 = __Pyx_GetItemInt(__pyx_v_coords, 2, sizeof(long), PyInt_FromLong); if (!__pyx_t_12) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 240; __pyx_clineno = __LINE__; goto __pyx_L8_except_error;}
-          __Pyx_GOTREF(__pyx_t_12);
-          __pyx_t_15 = __pyx_PyFloat_AsDouble(__pyx_t_12); if (unlikely((__pyx_t_15 == (double)-1) && PyErr_Occurred())) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 240; __pyx_clineno = __LINE__; goto __pyx_L8_except_error;}
-          __Pyx_DECREF(__pyx_t_12); __pyx_t_12 = 0;
-          __pyx_v_dz = __pyx_t_15;
-          goto __pyx_L34;
-        }
-        __pyx_L34:;
-
-        /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":244
- *             # Because of a bug in the GEOS C API,
- *             # always set X before Y
- *             GEOSCoordSeq_setX_r(handle, cs, i, dx)             # <<<<<<<<<<<<<<
- *             GEOSCoordSeq_setY_r(handle, cs, i, dy)
- *             if n == 3:
- */
-        GEOSCoordSeq_setX_r(__pyx_v_handle, __pyx_v_cs, __pyx_v_i, __pyx_v_dx);
-
-        /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":245
- *             # always set X before Y
- *             GEOSCoordSeq_setX_r(handle, cs, i, dx)
- *             GEOSCoordSeq_setY_r(handle, cs, i, dy)             # <<<<<<<<<<<<<<
- *             if n == 3:
- *                 GEOSCoordSeq_setZ_r(handle, cs, i, dz)
- */
-        GEOSCoordSeq_setY_r(__pyx_v_handle, __pyx_v_cs, __pyx_v_i, __pyx_v_dy);
-
-        /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":246
- *             GEOSCoordSeq_setX_r(handle, cs, i, dx)
- *             GEOSCoordSeq_setY_r(handle, cs, i, dy)
- *             if n == 3:             # <<<<<<<<<<<<<<
- *                 GEOSCoordSeq_setZ_r(handle, cs, i, dz)
- * 
- */
-        __pyx_t_6 = (__pyx_v_n == 3);
-        if (__pyx_t_6) {
-
-          /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":247
- *             GEOSCoordSeq_setY_r(handle, cs, i, dy)
- *             if n == 3:
- *                 GEOSCoordSeq_setZ_r(handle, cs, i, dz)             # <<<<<<<<<<<<<<
- * 
- *         # Add closing coordinates to sequence?
- */
-          GEOSCoordSeq_setZ_r(__pyx_v_handle, __pyx_v_cs, __pyx_v_i, __pyx_v_dz);
-          goto __pyx_L35;
-        }
-        __pyx_L35:;
-      }
-
-      /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":250
- * 
- *         # Add closing coordinates to sequence?
- *         if M > m:             # <<<<<<<<<<<<<<
- *             coords = ob[0]
- *             dx = coords[0]
- */
-      __pyx_t_6 = (__pyx_v_M > __pyx_v_m);
-      if (__pyx_t_6) {
-
-        /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":251
- *         # Add closing coordinates to sequence?
- *         if M > m:
- *             coords = ob[0]             # <<<<<<<<<<<<<<
- *             dx = coords[0]
- *             dy = coords[1]
- */
-        __pyx_t_12 = __Pyx_GetItemInt(__pyx_v_ob, 0, sizeof(long), PyInt_FromLong); if (!__pyx_t_12) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 251; __pyx_clineno = __LINE__; goto __pyx_L8_except_error;}
-        __Pyx_GOTREF(__pyx_t_12);
-        __Pyx_DECREF(__pyx_v_coords);
-        __pyx_v_coords = __pyx_t_12;
-        __pyx_t_12 = 0;
-
-        /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":252
- *         if M > m:
- *             coords = ob[0]
- *             dx = coords[0]             # <<<<<<<<<<<<<<
- *             dy = coords[1]
- *             dz = 0
- */
-        __pyx_t_12 = __Pyx_GetItemInt(__pyx_v_coords, 0, sizeof(long), PyInt_FromLong); if (!__pyx_t_12) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 252; __pyx_clineno = __LINE__; goto __pyx_L8_except_error;}
-        __Pyx_GOTREF(__pyx_t_12);
-        __pyx_t_15 = __pyx_PyFloat_AsDouble(__pyx_t_12); if (unlikely((__pyx_t_15 == (double)-1) && PyErr_Occurred())) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 252; __pyx_clineno = __LINE__; goto __pyx_L8_except_error;}
-        __Pyx_DECREF(__pyx_t_12); __pyx_t_12 = 0;
-        __pyx_v_dx = __pyx_t_15;
-
-        /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":253
- *             coords = ob[0]
- *             dx = coords[0]
- *             dy = coords[1]             # <<<<<<<<<<<<<<
- *             dz = 0
- *             if n == 3:
- */
-        __pyx_t_12 = __Pyx_GetItemInt(__pyx_v_coords, 1, sizeof(long), PyInt_FromLong); if (!__pyx_t_12) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 253; __pyx_clineno = __LINE__; goto __pyx_L8_except_error;}
-        __Pyx_GOTREF(__pyx_t_12);
-        __pyx_t_15 = __pyx_PyFloat_AsDouble(__pyx_t_12); if (unlikely((__pyx_t_15 == (double)-1) && PyErr_Occurred())) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 253; __pyx_clineno = __LINE__; goto __pyx_L8_except_error;}
-        __Pyx_DECREF(__pyx_t_12); __pyx_t_12 = 0;
-        __pyx_v_dy = __pyx_t_15;
-
-        /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":254
- *             dx = coords[0]
- *             dy = coords[1]
- *             dz = 0             # <<<<<<<<<<<<<<
- *             if n == 3:
- *                 dz = coords[2]
- */
-        __pyx_v_dz = 0.0;
-
-        /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":255
- *             dy = coords[1]
- *             dz = 0
- *             if n == 3:             # <<<<<<<<<<<<<<
- *                 dz = coords[2]
- * 
- */
-        __pyx_t_6 = (__pyx_v_n == 3);
-        if (__pyx_t_6) {
-
-          /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":256
- *             dz = 0
- *             if n == 3:
- *                 dz = coords[2]             # <<<<<<<<<<<<<<
- * 
- *             # Because of a bug in the GEOS C API,
- */
-          __pyx_t_12 = __Pyx_GetItemInt(__pyx_v_coords, 2, sizeof(long), PyInt_FromLong); if (!__pyx_t_12) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 256; __pyx_clineno = __LINE__; goto __pyx_L8_except_error;}
-          __Pyx_GOTREF(__pyx_t_12);
-          __pyx_t_15 = __pyx_PyFloat_AsDouble(__pyx_t_12); if (unlikely((__pyx_t_15 == (double)-1) && PyErr_Occurred())) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 256; __pyx_clineno = __LINE__; goto __pyx_L8_except_error;}
-          __Pyx_DECREF(__pyx_t_12); __pyx_t_12 = 0;
-          __pyx_v_dz = __pyx_t_15;
-          goto __pyx_L37;
-        }
-        __pyx_L37:;
-
-        /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":260
- *             # Because of a bug in the GEOS C API,
- *             # always set X before Y
- *             GEOSCoordSeq_setX_r(handle, cs, M-1, dx)             # <<<<<<<<<<<<<<
- *             GEOSCoordSeq_setY_r(handle, cs, M-1, dy)
- *             if n == 3:
- */
-        GEOSCoordSeq_setX_r(__pyx_v_handle, __pyx_v_cs, (__pyx_v_M - 1), __pyx_v_dx);
-
-        /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":261
- *             # always set X before Y
- *             GEOSCoordSeq_setX_r(handle, cs, M-1, dx)
- *             GEOSCoordSeq_setY_r(handle, cs, M-1, dy)             # <<<<<<<<<<<<<<
- *             if n == 3:
- *                 GEOSCoordSeq_setZ_r(handle, cs, M-1, dz)
- */
-        GEOSCoordSeq_setY_r(__pyx_v_handle, __pyx_v_cs, (__pyx_v_M - 1), __pyx_v_dy);
-
-        /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":262
- *             GEOSCoordSeq_setX_r(handle, cs, M-1, dx)
- *             GEOSCoordSeq_setY_r(handle, cs, M-1, dy)
- *             if n == 3:             # <<<<<<<<<<<<<<
- *                 GEOSCoordSeq_setZ_r(handle, cs, M-1, dz)
- * 
- */
-        __pyx_t_6 = (__pyx_v_n == 3);
-        if (__pyx_t_6) {
-
-          /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":263
- *             GEOSCoordSeq_setY_r(handle, cs, M-1, dy)
- *             if n == 3:
- *                 GEOSCoordSeq_setZ_r(handle, cs, M-1, dz)             # <<<<<<<<<<<<<<
- * 
- *     if update_geom is not None:
- */
-          GEOSCoordSeq_setZ_r(__pyx_v_handle, __pyx_v_cs, (__pyx_v_M - 1), __pyx_v_dz);
-          goto __pyx_L38;
-        }
-        __pyx_L38:;
-        goto __pyx_L36;
-      }
-      __pyx_L36:;
-      __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0;
-      __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
-      __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
-      goto __pyx_L7_exception_handled;
-    }
-    __pyx_L8_except_error:;
-    __Pyx_XGIVEREF(__pyx_save_exc_type);
-    __Pyx_XGIVEREF(__pyx_save_exc_value);
-    __Pyx_XGIVEREF(__pyx_save_exc_tb);
-    __Pyx_ExceptionReset(__pyx_save_exc_type, __pyx_save_exc_value, __pyx_save_exc_tb);
-    goto __pyx_L1_error;
-    __pyx_L7_exception_handled:;
-    __Pyx_XGIVEREF(__pyx_save_exc_type);
-    __Pyx_XGIVEREF(__pyx_save_exc_value);
-    __Pyx_XGIVEREF(__pyx_save_exc_tb);
-    __Pyx_ExceptionReset(__pyx_save_exc_type, __pyx_save_exc_value, __pyx_save_exc_tb);
-    __pyx_L13_try_end:;
-  }
-
-  /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":265
- *                 GEOSCoordSeq_setZ_r(handle, cs, M-1, dz)
- * 
- *     if update_geom is not None:             # <<<<<<<<<<<<<<
- *         return None
- *     else:
- */
-  __pyx_t_6 = (__pyx_v_update_geom != Py_None);
-  if (__pyx_t_6) {
-
-    /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":266
- * 
- *     if update_geom is not None:
- *         return None             # <<<<<<<<<<<<<<
- *     else:
- *         return <unsigned long>GEOSGeom_createLinearRing_r(handle, cs), n
- */
-    __Pyx_XDECREF(__pyx_r);
-    __Pyx_INCREF(Py_None);
-    __pyx_r = Py_None;
-    goto __pyx_L0;
-    goto __pyx_L39;
-  }
-  /*else*/ {
-
-    /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":268
- *         return None
- *     else:
- *         return <unsigned long>GEOSGeom_createLinearRing_r(handle, cs), n             # <<<<<<<<<<<<<<
- * 
- * 
- */
-    __Pyx_XDECREF(__pyx_r);
-    __pyx_t_2 = PyLong_FromUnsignedLong(((unsigned long)GEOSGeom_createLinearRing_r(__pyx_v_handle, __pyx_v_cs))); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 268; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
-    __Pyx_GOTREF(__pyx_t_2);
-    __pyx_t_1 = PyInt_FromLong(__pyx_v_n); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 268; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
-    __Pyx_GOTREF(__pyx_t_1);
-    __pyx_t_7 = PyTuple_New(2); if (unlikely(!__pyx_t_7)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 268; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
-    __Pyx_GOTREF(((PyObject *)__pyx_t_7));
-    PyTuple_SET_ITEM(__pyx_t_7, 0, __pyx_t_2);
-    __Pyx_GIVEREF(__pyx_t_2);
-    PyTuple_SET_ITEM(__pyx_t_7, 1, __pyx_t_1);
-    __Pyx_GIVEREF(__pyx_t_1);
-    __pyx_t_2 = 0;
-    __pyx_t_1 = 0;
-    __pyx_r = ((PyObject *)__pyx_t_7);
-    __pyx_t_7 = 0;
-    goto __pyx_L0;
-  }
-  __pyx_L39:;
-
-  __pyx_r = Py_None; __Pyx_INCREF(Py_None);
-  goto __pyx_L0;
-  __pyx_L1_error:;
-  __Pyx_XDECREF(__pyx_t_1);
-  __Pyx_XDECREF(__pyx_t_2);
-  __Pyx_XDECREF(__pyx_t_7);
-  __Pyx_XDECREF(__pyx_t_11);
-  __Pyx_XDECREF(__pyx_t_12);
-  __Pyx_XDECREF(__pyx_t_13);
-  __Pyx_AddTraceback("shapely.speedups._speedups.geos_linearring_from_py");
-  __pyx_r = NULL;
-  __pyx_L0:;
-  __Pyx_DECREF(__pyx_v_array);
-  __Pyx_DECREF(__pyx_v_coords);
-  __Pyx_XGIVEREF(__pyx_r);
-  __Pyx_RefNannyFinishContext();
-  return __pyx_r;
-}
-
-/* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":271
- * 
- * 
- * def coordseq_ctypes(self):             # <<<<<<<<<<<<<<
- *     cdef int i, n, m
- *     cdef double temp = 0
- */
-
-static PyObject *__pyx_pf_7shapely_8speedups_9_speedups_3coordseq_ctypes(PyObject *__pyx_self, PyObject *__pyx_v_self); /*proto*/
-static PyMethodDef __pyx_mdef_7shapely_8speedups_9_speedups_3coordseq_ctypes = {__Pyx_NAMESTR("coordseq_ctypes"), (PyCFunction)__pyx_pf_7shapely_8speedups_9_speedups_3coordseq_ctypes, METH_O, __Pyx_DOCSTR(0)};
-static PyObject *__pyx_pf_7shapely_8speedups_9_speedups_3coordseq_ctypes(PyObject *__pyx_self, PyObject *__pyx_v_self) {
-  int __pyx_v_i;
-  int __pyx_v_n;
-  int __pyx_v_m;
-  double __pyx_v_temp;
-  struct GEOSContextHandle_HS *__pyx_v_handle;
-  GEOSCoordSequence *__pyx_v_cs;
-  double *__pyx_v_data_p;
-  PyObject *__pyx_v_array_type;
-  PyObject *__pyx_v_data;
-  PyObject *__pyx_r = NULL;
-  PyObject *__pyx_t_1 = NULL;
-  PyObject *__pyx_t_2 = NULL;
-  unsigned long __pyx_t_3;
-  int __pyx_t_4;
-  PyObject *__pyx_t_5 = NULL;
-  int __pyx_t_6;
-  int __pyx_t_7;
-  __Pyx_RefNannySetupContext("coordseq_ctypes");
-  __pyx_self = __pyx_self;
-  __pyx_v_array_type = Py_None; __Pyx_INCREF(Py_None);
-  __pyx_v_data = Py_None; __Pyx_INCREF(Py_None);
-
-  /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":273
- * def coordseq_ctypes(self):
- *     cdef int i, n, m
- *     cdef double temp = 0             # <<<<<<<<<<<<<<
- *     cdef GEOSContextHandle_HS *handle = cast_handle(lgeos.geos_handle)
- *     cdef GEOSCoordSequence *cs
- */
-  __pyx_v_temp = 0.0;
-
-  /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":274
- *     cdef int i, n, m
- *     cdef double temp = 0
- *     cdef GEOSContextHandle_HS *handle = cast_handle(lgeos.geos_handle)             # <<<<<<<<<<<<<<
- *     cdef GEOSCoordSequence *cs
- *     cdef double *data_p
- */
-  __pyx_t_1 = __Pyx_GetName(__pyx_m, __pyx_n_s__lgeos); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 274; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
-  __Pyx_GOTREF(__pyx_t_1);
-  __pyx_t_2 = PyObject_GetAttr(__pyx_t_1, __pyx_n_s__geos_handle); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 274; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
-  __Pyx_GOTREF(__pyx_t_2);
-  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
-  __pyx_t_3 = __Pyx_PyInt_AsUnsignedLong(__pyx_t_2); if (unlikely((__pyx_t_3 == (unsigned long)-1) && PyErr_Occurred())) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 274; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
-  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
-  __pyx_v_handle = __pyx_f_7shapely_8speedups_9_speedups_cast_handle(__pyx_t_3);
-
-  /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":277
- *     cdef GEOSCoordSequence *cs
- *     cdef double *data_p
- *     self._update()             # <<<<<<<<<<<<<<
- *     n = self._ndim
- *     m = self.__len__()
- */
-  __pyx_t_2 = PyObject_GetAttr(__pyx_v_self, __pyx_n_s___update); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 277; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
-  __Pyx_GOTREF(__pyx_t_2);
-  __pyx_t_1 = PyObject_Call(__pyx_t_2, ((PyObject *)__pyx_empty_tuple), NULL); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 277; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
-  __Pyx_GOTREF(__pyx_t_1);
-  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
-  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
-
-  /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":278
- *     cdef double *data_p
- *     self._update()
- *     n = self._ndim             # <<<<<<<<<<<<<<
- *     m = self.__len__()
- *     array_type = ctypes.c_double * (m * n)
- */
-  __pyx_t_1 = PyObject_GetAttr(__pyx_v_self, __pyx_n_s___ndim); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 278; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
-  __Pyx_GOTREF(__pyx_t_1);
-  __pyx_t_4 = __Pyx_PyInt_AsInt(__pyx_t_1); if (unlikely((__pyx_t_4 == (int)-1) && PyErr_Occurred())) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 278; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
-  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
-  __pyx_v_n = __pyx_t_4;
-
-  /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":279
- *     self._update()
- *     n = self._ndim
- *     m = self.__len__()             # <<<<<<<<<<<<<<
- *     array_type = ctypes.c_double * (m * n)
- *     data = array_type()
- */
-  __pyx_t_1 = PyObject_GetAttr(__pyx_v_self, __pyx_n_s____len__); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 279; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
-  __Pyx_GOTREF(__pyx_t_1);
-  __pyx_t_2 = PyObject_Call(__pyx_t_1, ((PyObject *)__pyx_empty_tuple), NULL); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 279; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
-  __Pyx_GOTREF(__pyx_t_2);
-  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
-  __pyx_t_4 = __Pyx_PyInt_AsInt(__pyx_t_2); if (unlikely((__pyx_t_4 == (int)-1) && PyErr_Occurred())) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 279; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
-  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
-  __pyx_v_m = __pyx_t_4;
-
-  /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":280
- *     n = self._ndim
- *     m = self.__len__()
- *     array_type = ctypes.c_double * (m * n)             # <<<<<<<<<<<<<<
- *     data = array_type()
- * 
- */
-  __pyx_t_2 = __Pyx_GetName(__pyx_m, __pyx_n_s__ctypes); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 280; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
-  __Pyx_GOTREF(__pyx_t_2);
-  __pyx_t_1 = PyObject_GetAttr(__pyx_t_2, __pyx_n_s__c_double); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 280; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
-  __Pyx_GOTREF(__pyx_t_1);
-  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
-  __pyx_t_2 = PyInt_FromLong((__pyx_v_m * __pyx_v_n)); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 280; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
-  __Pyx_GOTREF(__pyx_t_2);
-  __pyx_t_5 = PyNumber_Multiply(__pyx_t_1, __pyx_t_2); if (unlikely(!__pyx_t_5)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 280; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
-  __Pyx_GOTREF(__pyx_t_5);
-  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
-  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
-  __Pyx_DECREF(__pyx_v_array_type);
-  __pyx_v_array_type = __pyx_t_5;
-  __pyx_t_5 = 0;
-
-  /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":281
- *     m = self.__len__()
- *     array_type = ctypes.c_double * (m * n)
- *     data = array_type()             # <<<<<<<<<<<<<<
- * 
- *     cs = cast_seq(self._cseq)
- */
-  __pyx_t_5 = PyObject_Call(__pyx_v_array_type, ((PyObject *)__pyx_empty_tuple), NULL); if (unlikely(!__pyx_t_5)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 281; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
-  __Pyx_GOTREF(__pyx_t_5);
-  __Pyx_DECREF(__pyx_v_data);
-  __pyx_v_data = __pyx_t_5;
-  __pyx_t_5 = 0;
-
-  /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":283
- *     data = array_type()
- * 
- *     cs = cast_seq(self._cseq)             # <<<<<<<<<<<<<<
- *     data_p = <double *><unsigned long>ctypes.addressof(data)
- * 
- */
-  __pyx_t_5 = PyObject_GetAttr(__pyx_v_self, __pyx_n_s___cseq); if (unlikely(!__pyx_t_5)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 283; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
-  __Pyx_GOTREF(__pyx_t_5);
-  __pyx_t_3 = __Pyx_PyInt_AsUnsignedLong(__pyx_t_5); if (unlikely((__pyx_t_3 == (unsigned long)-1) && PyErr_Occurred())) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 283; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
-  __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
-  __pyx_v_cs = __pyx_f_7shapely_8speedups_9_speedups_cast_seq(__pyx_t_3);
-
-  /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":284
- * 
- *     cs = cast_seq(self._cseq)
- *     data_p = <double *><unsigned long>ctypes.addressof(data)             # <<<<<<<<<<<<<<
- * 
- *     for i in xrange(m):
- */
-  __pyx_t_5 = __Pyx_GetName(__pyx_m, __pyx_n_s__ctypes); if (unlikely(!__pyx_t_5)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 284; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
-  __Pyx_GOTREF(__pyx_t_5);
-  __pyx_t_2 = PyObject_GetAttr(__pyx_t_5, __pyx_n_s__addressof); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 284; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
-  __Pyx_GOTREF(__pyx_t_2);
-  __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
-  __pyx_t_5 = PyTuple_New(1); if (unlikely(!__pyx_t_5)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 284; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
-  __Pyx_GOTREF(((PyObject *)__pyx_t_5));
-  __Pyx_INCREF(__pyx_v_data);
-  PyTuple_SET_ITEM(__pyx_t_5, 0, __pyx_v_data);
-  __Pyx_GIVEREF(__pyx_v_data);
-  __pyx_t_1 = PyObject_Call(__pyx_t_2, ((PyObject *)__pyx_t_5), NULL); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 284; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
-  __Pyx_GOTREF(__pyx_t_1);
-  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
-  __Pyx_DECREF(((PyObject *)__pyx_t_5)); __pyx_t_5 = 0;
-  __pyx_t_3 = __Pyx_PyInt_AsUnsignedLong(__pyx_t_1); if (unlikely((__pyx_t_3 == (unsigned long)-1) && PyErr_Occurred())) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 284; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
-  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
-  __pyx_v_data_p = ((double *)__pyx_t_3);
-
-  /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":286
- *     data_p = <double *><unsigned long>ctypes.addressof(data)
- * 
- *     for i in xrange(m):             # <<<<<<<<<<<<<<
- *         GEOSCoordSeq_getX_r(handle, cs, i, &temp)
- *         data_p[n*i] = temp
- */
-  __pyx_t_4 = __pyx_v_m;
-  for (__pyx_t_6 = 0; __pyx_t_6 < __pyx_t_4; __pyx_t_6+=1) {
-    __pyx_v_i = __pyx_t_6;
-
-    /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":287
- * 
- *     for i in xrange(m):
- *         GEOSCoordSeq_getX_r(handle, cs, i, &temp)             # <<<<<<<<<<<<<<
- *         data_p[n*i] = temp
- *         GEOSCoordSeq_getY_r(handle, cs, i, &temp)
- */
-    GEOSCoordSeq_getX_r(__pyx_v_handle, __pyx_v_cs, __pyx_v_i, (&__pyx_v_temp));
-
-    /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":288
- *     for i in xrange(m):
- *         GEOSCoordSeq_getX_r(handle, cs, i, &temp)
- *         data_p[n*i] = temp             # <<<<<<<<<<<<<<
- *         GEOSCoordSeq_getY_r(handle, cs, i, &temp)
- *         data_p[n*i+1] = temp
- */
-    (__pyx_v_data_p[(__pyx_v_n * __pyx_v_i)]) = __pyx_v_temp;
-
-    /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":289
- *         GEOSCoordSeq_getX_r(handle, cs, i, &temp)
- *         data_p[n*i] = temp
- *         GEOSCoordSeq_getY_r(handle, cs, i, &temp)             # <<<<<<<<<<<<<<
- *         data_p[n*i+1] = temp
- *         if n == 3: # TODO: use hasz
- */
-    GEOSCoordSeq_getY_r(__pyx_v_handle, __pyx_v_cs, __pyx_v_i, (&__pyx_v_temp));
-
-    /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":290
- *         data_p[n*i] = temp
- *         GEOSCoordSeq_getY_r(handle, cs, i, &temp)
- *         data_p[n*i+1] = temp             # <<<<<<<<<<<<<<
- *         if n == 3: # TODO: use hasz
- *             GEOSCoordSeq_getZ_r(handle, cs, i, &temp)
- */
-    (__pyx_v_data_p[((__pyx_v_n * __pyx_v_i) + 1)]) = __pyx_v_temp;
-
-    /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":291
- *         GEOSCoordSeq_getY_r(handle, cs, i, &temp)
- *         data_p[n*i+1] = temp
- *         if n == 3: # TODO: use hasz             # <<<<<<<<<<<<<<
- *             GEOSCoordSeq_getZ_r(handle, cs, i, &temp)
- *             data_p[n*i+2] = temp
- */
-    __pyx_t_7 = (__pyx_v_n == 3);
-    if (__pyx_t_7) {
-
-      /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":292
- *         data_p[n*i+1] = temp
- *         if n == 3: # TODO: use hasz
- *             GEOSCoordSeq_getZ_r(handle, cs, i, &temp)             # <<<<<<<<<<<<<<
- *             data_p[n*i+2] = temp
- *     return data
- */
-      GEOSCoordSeq_getZ_r(__pyx_v_handle, __pyx_v_cs, __pyx_v_i, (&__pyx_v_temp));
-
-      /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":293
- *         if n == 3: # TODO: use hasz
- *             GEOSCoordSeq_getZ_r(handle, cs, i, &temp)
- *             data_p[n*i+2] = temp             # <<<<<<<<<<<<<<
- *     return data
- * 
- */
-      (__pyx_v_data_p[((__pyx_v_n * __pyx_v_i) + 2)]) = __pyx_v_temp;
-      goto __pyx_L7;
-    }
-    __pyx_L7:;
-  }
-
-  /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":294
- *             GEOSCoordSeq_getZ_r(handle, cs, i, &temp)
- *             data_p[n*i+2] = temp
- *     return data             # <<<<<<<<<<<<<<
- * 
- */
-  __Pyx_XDECREF(__pyx_r);
-  __Pyx_INCREF(__pyx_v_data);
-  __pyx_r = __pyx_v_data;
-  goto __pyx_L0;
-
-  __pyx_r = Py_None; __Pyx_INCREF(Py_None);
-  goto __pyx_L0;
-  __pyx_L1_error:;
-  __Pyx_XDECREF(__pyx_t_1);
-  __Pyx_XDECREF(__pyx_t_2);
-  __Pyx_XDECREF(__pyx_t_5);
-  __Pyx_AddTraceback("shapely.speedups._speedups.coordseq_ctypes");
-  __pyx_r = NULL;
-  __pyx_L0:;
-  __Pyx_DECREF(__pyx_v_array_type);
-  __Pyx_DECREF(__pyx_v_data);
-  __Pyx_XGIVEREF(__pyx_r);
-  __Pyx_RefNannyFinishContext();
-  return __pyx_r;
-}
-
-static PyMethodDef __pyx_methods[] = {
-  {0, 0, 0, 0}
-};
-
-#if PY_MAJOR_VERSION >= 3
-static struct PyModuleDef __pyx_moduledef = {
-    PyModuleDef_HEAD_INIT,
-    __Pyx_NAMESTR("_speedups"),
-    0, /* m_doc */
-    -1, /* m_size */
-    __pyx_methods /* m_methods */,
-    NULL, /* m_reload */
-    NULL, /* m_traverse */
-    NULL, /* m_clear */
-    NULL /* m_free */
-};
-#endif
-
-static __Pyx_StringTabEntry __pyx_string_tab[] = {
-  {&__pyx_kp_s_1, __pyx_k_1, sizeof(__pyx_k_1), 0, 0, 1, 0},
-  {&__pyx_n_s_11, __pyx_k_11, sizeof(__pyx_k_11), 0, 0, 1, 1},
-  {&__pyx_n_s_12, __pyx_k_12, sizeof(__pyx_k_12), 0, 0, 1, 1},
-  {&__pyx_n_s_13, __pyx_k_13, sizeof(__pyx_k_13), 0, 0, 1, 1},
-  {&__pyx_n_s_14, __pyx_k_14, sizeof(__pyx_k_14), 0, 0, 1, 1},
-  {&__pyx_kp_s_3, __pyx_k_3, sizeof(__pyx_k_3), 0, 0, 1, 0},
-  {&__pyx_kp_s_4, __pyx_k_4, sizeof(__pyx_k_4), 0, 0, 1, 0},
-  {&__pyx_kp_s_6, __pyx_k_6, sizeof(__pyx_k_6), 0, 0, 1, 0},
-  {&__pyx_kp_s_8, __pyx_k_8, sizeof(__pyx_k_8), 0, 0, 1, 0},
-  {&__pyx_n_s__Array, __pyx_k__Array, sizeof(__pyx_k__Array), 0, 0, 1, 1},
-  {&__pyx_n_s__AttributeError, __pyx_k__AttributeError, sizeof(__pyx_k__AttributeError), 0, 0, 1, 1},
-  {&__pyx_n_s__IndexError, __pyx_k__IndexError, sizeof(__pyx_k__IndexError), 0, 0, 1, 1},
-  {&__pyx_n_s__TypeError, __pyx_k__TypeError, sizeof(__pyx_k__TypeError), 0, 0, 1, 1},
-  {&__pyx_n_s__ValueError, __pyx_k__ValueError, sizeof(__pyx_k__ValueError), 0, 0, 1, 1},
-  {&__pyx_n_s____array_interface__, __pyx_k____array_interface__, sizeof(__pyx_k____array_interface__), 0, 0, 1, 1},
-  {&__pyx_n_s____len__, __pyx_k____len__, sizeof(__pyx_k____len__), 0, 0, 1, 1},
-  {&__pyx_n_s____main__, __pyx_k____main__, sizeof(__pyx_k____main__), 0, 0, 1, 1},
-  {&__pyx_n_s____test__, __pyx_k____test__, sizeof(__pyx_k____test__), 0, 0, 1, 1},
-  {&__pyx_n_s___cseq, __pyx_k___cseq, sizeof(__pyx_k___cseq), 0, 0, 1, 1},
-  {&__pyx_n_s___ndim, __pyx_k___ndim, sizeof(__pyx_k___ndim), 0, 0, 1, 1},
-  {&__pyx_n_s___update, __pyx_k___update, sizeof(__pyx_k___update), 0, 0, 1, 1},
-  {&__pyx_n_s__addressof, __pyx_k__addressof, sizeof(__pyx_k__addressof), 0, 0, 1, 1},
-  {&__pyx_n_s__c_double, __pyx_k__c_double, sizeof(__pyx_k__c_double), 0, 0, 1, 1},
-  {&__pyx_n_s__coordseq_ctypes, __pyx_k__coordseq_ctypes, sizeof(__pyx_k__coordseq_ctypes), 0, 0, 1, 1},
-  {&__pyx_n_s__ctypes, __pyx_k__ctypes, sizeof(__pyx_k__ctypes), 0, 0, 1, 1},
-  {&__pyx_n_s__data, __pyx_k__data, sizeof(__pyx_k__data), 0, 0, 1, 1},
-  {&__pyx_n_s__destroy, __pyx_k__destroy, sizeof(__pyx_k__destroy), 0, 0, 1, 1},
-  {&__pyx_n_s__geos_handle, __pyx_k__geos_handle, sizeof(__pyx_k__geos_handle), 0, 0, 1, 1},
-  {&__pyx_n_s__lgeos, __pyx_k__lgeos, sizeof(__pyx_k__lgeos), 0, 0, 1, 1},
-  {&__pyx_n_s__ob, __pyx_k__ob, sizeof(__pyx_k__ob), 0, 0, 1, 1},
-  {&__pyx_n_s__range, __pyx_k__range, sizeof(__pyx_k__range), 0, 0, 1, 1},
-  {&__pyx_n_s__shape, __pyx_k__shape, sizeof(__pyx_k__shape), 0, 0, 1, 1},
-  {&__pyx_n_s__update_geom, __pyx_k__update_geom, sizeof(__pyx_k__update_geom), 0, 0, 1, 1},
-  {&__pyx_n_s__update_ndim, __pyx_k__update_ndim, sizeof(__pyx_k__update_ndim), 0, 0, 1, 1},
-  {&__pyx_n_s__xrange, __pyx_k__xrange, sizeof(__pyx_k__xrange), 0, 0, 1, 1},
-  {0, 0, 0, 0, 0, 0, 0}
-};
-static int __Pyx_InitCachedBuiltins(void) {
-  __pyx_builtin_ValueError = __Pyx_GetName(__pyx_b, __pyx_n_s__ValueError); if (!__pyx_builtin_ValueError) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 52; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
-  __pyx_builtin_IndexError = __Pyx_GetName(__pyx_b, __pyx_n_s__IndexError); if (!__pyx_builtin_IndexError) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 56; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
-  #if PY_MAJOR_VERSION >= 3
-  __pyx_builtin_xrange = __Pyx_GetName(__pyx_b, __pyx_n_s__range); if (!__pyx_builtin_xrange) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 78; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
-  #else
-  __pyx_builtin_xrange = __Pyx_GetName(__pyx_b, __pyx_n_s__xrange); if (!__pyx_builtin_xrange) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 78; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
-  #endif
-  __pyx_builtin_AttributeError = __Pyx_GetName(__pyx_b, __pyx_n_s__AttributeError); if (!__pyx_builtin_AttributeError) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 92; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
-  __pyx_builtin_TypeError = __Pyx_GetName(__pyx_b, __pyx_n_s__TypeError); if (!__pyx_builtin_TypeError) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 100; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
-  return 0;
-  __pyx_L1_error:;
-  return -1;
-}
-
-static int __Pyx_InitCachedConstants(void) {
-  __Pyx_RefNannySetupContext("__Pyx_InitCachedConstants");
-
-  /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":52
- *         m = array['shape'][0]
- *         if m < 2:
- *             raise ValueError(             # <<<<<<<<<<<<<<
- *                 "LineStrings must have at least 2 coordinate tuples")
- *         try:
- */
-  __pyx_k_tuple_2 = PyTuple_New(1); if (unlikely(!__pyx_k_tuple_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 52; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
-  __Pyx_GOTREF(((PyObject *)__pyx_k_tuple_2));
-  __Pyx_INCREF(((PyObject *)__pyx_kp_s_1));
-  PyTuple_SET_ITEM(__pyx_k_tuple_2, 0, ((PyObject *)__pyx_kp_s_1));
-  __Pyx_GIVEREF(((PyObject *)__pyx_kp_s_1));
-  __Pyx_GIVEREF(((PyObject *)__pyx_k_tuple_2));
-
-  /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":96
- *         m = len(ob)
- *         if m < 2:
- *             raise ValueError(             # <<<<<<<<<<<<<<
- *                 "LineStrings must have at least 2 coordinate tuples")
- *         try:
- */
-  __pyx_k_tuple_5 = PyTuple_New(1); if (unlikely(!__pyx_k_tuple_5)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 96; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
-  __Pyx_GOTREF(((PyObject *)__pyx_k_tuple_5));
-  __Pyx_INCREF(((PyObject *)__pyx_kp_s_1));
-  PyTuple_SET_ITEM(__pyx_k_tuple_5, 0, ((PyObject *)__pyx_kp_s_1));
-  __Pyx_GIVEREF(((PyObject *)__pyx_kp_s_1));
-  __Pyx_GIVEREF(((PyObject *)__pyx_k_tuple_5));
-
-  /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":123
- *             if n == 3:
- *                 if len(coords) != 3:
- *                     raise ValueError("Inconsistent coordinate dimensionality")             # <<<<<<<<<<<<<<
- *                 dz = coords[2]
- * 
- */
-  __pyx_k_tuple_7 = PyTuple_New(1); if (unlikely(!__pyx_k_tuple_7)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 123; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
-  __Pyx_GOTREF(((PyObject *)__pyx_k_tuple_7));
-  __Pyx_INCREF(((PyObject *)__pyx_kp_s_6));
-  PyTuple_SET_ITEM(__pyx_k_tuple_7, 0, ((PyObject *)__pyx_kp_s_6));
-  __Pyx_GIVEREF(((PyObject *)__pyx_kp_s_6));
-  __Pyx_GIVEREF(((PyObject *)__pyx_k_tuple_7));
-
-  /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":152
- *         n = array['shape'][1]
- *         if m < 3:
- *             raise ValueError(             # <<<<<<<<<<<<<<
- *                 "A LinearRing must have at least 3 coordinate tuples")
- *         assert n == 2 or n == 3
- */
-  __pyx_k_tuple_9 = PyTuple_New(1); if (unlikely(!__pyx_k_tuple_9)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 152; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
-  __Pyx_GOTREF(((PyObject *)__pyx_k_tuple_9));
-  __Pyx_INCREF(((PyObject *)__pyx_kp_s_8));
-  PyTuple_SET_ITEM(__pyx_k_tuple_9, 0, ((PyObject *)__pyx_kp_s_8));
-  __Pyx_GIVEREF(((PyObject *)__pyx_kp_s_8));
-  __Pyx_GIVEREF(((PyObject *)__pyx_k_tuple_9));
-
-  /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":213
- *         n = len(ob[0])
- *         if m < 3:
- *             raise ValueError(             # <<<<<<<<<<<<<<
- *                 "A LinearRing must have at least 3 coordinate tuples")
- *         assert (n == 2 or n == 3)
- */
-  __pyx_k_tuple_10 = PyTuple_New(1); if (unlikely(!__pyx_k_tuple_10)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 213; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
-  __Pyx_GOTREF(((PyObject *)__pyx_k_tuple_10));
-  __Pyx_INCREF(((PyObject *)__pyx_kp_s_8));
-  PyTuple_SET_ITEM(__pyx_k_tuple_10, 0, ((PyObject *)__pyx_kp_s_8));
-  __Pyx_GIVEREF(((PyObject *)__pyx_kp_s_8));
-  __Pyx_GIVEREF(((PyObject *)__pyx_k_tuple_10));
-  __Pyx_RefNannyFinishContext();
-  return 0;
-  __pyx_L1_error:;
-  __Pyx_RefNannyFinishContext();
-  return -1;
-}
-
-static int __Pyx_InitGlobals(void) {
-  if (__Pyx_InitStrings(__pyx_string_tab) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;};
-  __pyx_int_0 = PyInt_FromLong(0); if (unlikely(!__pyx_int_0)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;};
-  return 0;
-  __pyx_L1_error:;
-  return -1;
-}
-
-#if PY_MAJOR_VERSION < 3
-PyMODINIT_FUNC init_speedups(void); /*proto*/
-PyMODINIT_FUNC init_speedups(void)
-#else
-PyMODINIT_FUNC PyInit__speedups(void); /*proto*/
-PyMODINIT_FUNC PyInit__speedups(void)
-#endif
-{
-  PyObject *__pyx_t_1 = NULL;
-  PyObject *__pyx_t_2 = NULL;
-  #if CYTHON_REFNANNY
-  void* __pyx_refnanny = NULL;
-  __Pyx_RefNanny = __Pyx_RefNannyImportAPI("refnanny");
-  if (!__Pyx_RefNanny) {
-      PyErr_Clear();
-      __Pyx_RefNanny = __Pyx_RefNannyImportAPI("Cython.Runtime.refnanny");
-      if (!__Pyx_RefNanny)
-          Py_FatalError("failed to import 'refnanny' module");
-  }
-  __pyx_refnanny = __Pyx_RefNanny->SetupContext("PyMODINIT_FUNC PyInit__speedups(void)", __LINE__, __FILE__);
-  #endif
-  __pyx_empty_tuple = PyTuple_New(0); if (unlikely(!__pyx_empty_tuple)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
-  __pyx_empty_bytes = PyBytes_FromStringAndSize("", 0); if (unlikely(!__pyx_empty_bytes)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
-  #ifdef __pyx_binding_PyCFunctionType_USED
-  if (__pyx_binding_PyCFunctionType_init() < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
-  #endif
-  /*--- Library function declarations ---*/
-  /*--- Threads initialization code ---*/
-  #if defined(__PYX_FORCE_INIT_THREADS) && __PYX_FORCE_INIT_THREADS
-  #ifdef WITH_THREAD /* Python build with threading support? */
-  PyEval_InitThreads();
-  #endif
-  #endif
-  /*--- Module creation code ---*/
-  #if PY_MAJOR_VERSION < 3
-  __pyx_m = Py_InitModule4(__Pyx_NAMESTR("_speedups"), __pyx_methods, 0, 0, PYTHON_API_VERSION);
-  #else
-  __pyx_m = PyModule_Create(&__pyx_moduledef);
-  #endif
-  if (!__pyx_m) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;};
-  #if PY_MAJOR_VERSION < 3
-  Py_INCREF(__pyx_m);
-  #endif
-  __pyx_b = PyImport_AddModule(__Pyx_NAMESTR(__Pyx_BUILTIN_MODULE_NAME));
-  if (!__pyx_b) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;};
-  if (__Pyx_SetAttrString(__pyx_m, "__builtins__", __pyx_b) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;};
-  /*--- Initialize various global constants etc. ---*/
-  if (unlikely(__Pyx_InitGlobals() < 0)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
-  if (__pyx_module_is_main_shapely__speedups___speedups) {
-    if (__Pyx_SetAttrString(__pyx_m, "__name__", __pyx_n_s____main__) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;};
-  }
-  /*--- Builtin init code ---*/
-  if (unlikely(__Pyx_InitCachedBuiltins() < 0)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
-  /*--- Constants init code ---*/
-  if (unlikely(__Pyx_InitCachedConstants() < 0)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
-  /*--- Global init code ---*/
-  /*--- Function export code ---*/
-  /*--- Type init code ---*/
-  /*--- Type import code ---*/
-  /*--- Function import code ---*/
-  /*--- Execution code ---*/
-
-  /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":8
- * # Transcription to cython: Copyright (c) 2011, Oliver Tonnhofer
- * 
- * import ctypes             # <<<<<<<<<<<<<<
- * from shapely.geos import lgeos
- * 
- */
-  __pyx_t_1 = __Pyx_Import(((PyObject *)__pyx_n_s__ctypes), 0); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 8; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
-  __Pyx_GOTREF(__pyx_t_1);
-  if (PyObject_SetAttr(__pyx_m, __pyx_n_s__ctypes, __pyx_t_1) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 8; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
-  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
-
-  /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":9
- * 
- * import ctypes
- * from shapely.geos import lgeos             # <<<<<<<<<<<<<<
- * 
- * cdef extern from "geos_c.h":
- */
-  __pyx_t_1 = PyList_New(1); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 9; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
-  __Pyx_GOTREF(((PyObject *)__pyx_t_1));
-  __Pyx_INCREF(((PyObject *)__pyx_n_s__lgeos));
-  PyList_SET_ITEM(__pyx_t_1, 0, ((PyObject *)__pyx_n_s__lgeos));
-  __Pyx_GIVEREF(((PyObject *)__pyx_n_s__lgeos));
-  __pyx_t_2 = __Pyx_Import(((PyObject *)__pyx_n_s_11), ((PyObject *)__pyx_t_1)); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 9; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
-  __Pyx_GOTREF(__pyx_t_2);
-  __Pyx_DECREF(((PyObject *)__pyx_t_1)); __pyx_t_1 = 0;
-  __pyx_t_1 = PyObject_GetAttr(__pyx_t_2, __pyx_n_s__lgeos); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 9; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
-  __Pyx_GOTREF(__pyx_t_1);
-  if (PyObject_SetAttr(__pyx_m, __pyx_n_s__lgeos, __pyx_t_1) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 9; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
-  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
-  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
-
-  /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":37
- *     return <GEOSCoordSequence *>handle_addr
- * 
- * def destroy(geom):             # <<<<<<<<<<<<<<
- *     GEOSGeom_destroy_r(cast_handle(lgeos.geos_handle), cast_geom(geom))
- * 
- */
-  __pyx_t_2 = PyCFunction_NewEx(&__pyx_mdef_7shapely_8speedups_9_speedups_0destroy, NULL, __pyx_n_s_12); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 37; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
-  __Pyx_GOTREF(__pyx_t_2);
-  if (PyObject_SetAttr(__pyx_m, __pyx_n_s__destroy, __pyx_t_2) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 37; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
-  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
-
-  /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":40
- *     GEOSGeom_destroy_r(cast_handle(lgeos.geos_handle), cast_geom(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)
- */
-  __pyx_t_2 = PyCFunction_NewEx(&__pyx_mdef_7shapely_8speedups_9_speedups_1geos_linestring_from_py, NULL, __pyx_n_s_12); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 40; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
-  __Pyx_GOTREF(__pyx_t_2);
-  if (PyObject_SetAttr(__pyx_m, __pyx_n_s_13, __pyx_t_2) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 40; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
-  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
-
-  /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":139
- * 
- * 
- * def geos_linearring_from_py(ob, update_geom=None, update_ndim=0):             # <<<<<<<<<<<<<<
- *     cdef double *cp
- *     cdef GEOSContextHandle_HS *handle = cast_handle(lgeos.geos_handle)
- */
-  __pyx_t_2 = PyCFunction_NewEx(&__pyx_mdef_7shapely_8speedups_9_speedups_2geos_linearring_from_py, NULL, __pyx_n_s_12); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 139; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
-  __Pyx_GOTREF(__pyx_t_2);
-  if (PyObject_SetAttr(__pyx_m, __pyx_n_s_14, __pyx_t_2) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 139; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
-  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
-
-  /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":271
- * 
- * 
- * def coordseq_ctypes(self):             # <<<<<<<<<<<<<<
- *     cdef int i, n, m
- *     cdef double temp = 0
- */
-  __pyx_t_2 = PyCFunction_NewEx(&__pyx_mdef_7shapely_8speedups_9_speedups_3coordseq_ctypes, NULL, __pyx_n_s_12); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 271; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
-  __Pyx_GOTREF(__pyx_t_2);
-  if (PyObject_SetAttr(__pyx_m, __pyx_n_s__coordseq_ctypes, __pyx_t_2) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 271; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
-  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
-
-  /* "/Users/olt/dev/shapely.git/shapely/speedups/_speedups.pyx":1
- * # geos_linestring_from_py was transcribed from shapely.geometry.linestring             # <<<<<<<<<<<<<<
- * # geos_linearring_from_py was transcribed from shapely.geometry.polygon
- * # coordseq_ctypes was transcribed from shapely.coords.CoordinateSequence.ctypes
- */
-  __pyx_t_2 = PyDict_New(); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
-  __Pyx_GOTREF(((PyObject *)__pyx_t_2));
-  if (PyObject_SetAttr(__pyx_m, __pyx_n_s____test__, ((PyObject *)__pyx_t_2)) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
-  __Pyx_DECREF(((PyObject *)__pyx_t_2)); __pyx_t_2 = 0;
-  goto __pyx_L0;
-  __pyx_L1_error:;
-  __Pyx_XDECREF(__pyx_t_1);
-  __Pyx_XDECREF(__pyx_t_2);
-  if (__pyx_m) {
-    __Pyx_AddTraceback("init shapely.speedups._speedups");
-    Py_DECREF(__pyx_m); __pyx_m = 0;
-  } else if (!PyErr_Occurred()) {
-    PyErr_SetString(PyExc_ImportError, "init shapely.speedups._speedups");
-  }
-  __pyx_L0:;
-  __Pyx_RefNannyFinishContext();
-  #if PY_MAJOR_VERSION < 3
-  return;
-  #else
-  return __pyx_m;
-  #endif
-}
-
-/* Runtime support code */
-
-static PyObject *__Pyx_GetName(PyObject *dict, PyObject *name) {
-    PyObject *result;
-    result = PyObject_GetAttr(dict, name);
-    if (!result)
-        PyErr_SetObject(PyExc_NameError, name);
-    return result;
-}
-
-static void __Pyx_RaiseDoubleKeywordsError(
-    const char* func_name,
-    PyObject* kw_name)
-{
-    PyErr_Format(PyExc_TypeError,
-        #if PY_MAJOR_VERSION >= 3
-        "%s() got multiple values for keyword argument '%U'", func_name, kw_name);
-        #else
-        "%s() got multiple values for keyword argument '%s'", func_name,
-        PyString_AS_STRING(kw_name));
-        #endif
-}
-
-static void __Pyx_RaiseArgtupleInvalid(
-    const char* func_name,
-    int exact,
-    Py_ssize_t num_min,
-    Py_ssize_t num_max,
-    Py_ssize_t num_found)
-{
-    Py_ssize_t num_expected;
-    const char *number, *more_or_less;
-
-    if (num_found < num_min) {
-        num_expected = num_min;
-        more_or_less = "at least";
-    } else {
-        num_expected = num_max;
-        more_or_less = "at most";
-    }
-    if (exact) {
-        more_or_less = "exactly";
-    }
-    number = (num_expected == 1) ? "" : "s";
-    PyErr_Format(PyExc_TypeError,
-        #if PY_VERSION_HEX < 0x02050000
-            "%s() takes %s %d positional argument%s (%d given)",
-        #else
-            "%s() takes %s %zd positional argument%s (%zd given)",
-        #endif
-        func_name, more_or_less, num_expected, number, num_found);
-}
-
-static int __Pyx_ParseOptionalKeywords(
-    PyObject *kwds,
-    PyObject **argnames[],
-    PyObject *kwds2,
-    PyObject *values[],
-    Py_ssize_t num_pos_args,
-    const char* function_name)
-{
-    PyObject *key = 0, *value = 0;
-    Py_ssize_t pos = 0;
-    PyObject*** name;
-    PyObject*** first_kw_arg = argnames + num_pos_args;
-
-    while (PyDict_Next(kwds, &pos, &key, &value)) {
-        name = first_kw_arg;
-        while (*name && (**name != key)) name++;
-        if (*name) {
-            values[name-argnames] = value;
-        } else {
-            #if PY_MAJOR_VERSION < 3
-            if (unlikely(!PyString_CheckExact(key)) && unlikely(!PyString_Check(key))) {
-            #else
-            if (unlikely(!PyUnicode_CheckExact(key)) && unlikely(!PyUnicode_Check(key))) {
-            #endif
-                goto invalid_keyword_type;
-            } else {
-                for (name = first_kw_arg; *name; name++) {
-                    #if PY_MAJOR_VERSION >= 3
-                    if (PyUnicode_GET_SIZE(**name) == PyUnicode_GET_SIZE(key) &&
-                        PyUnicode_Compare(**name, key) == 0) break;
-                    #else
-                    if (PyString_GET_SIZE(**name) == PyString_GET_SIZE(key) &&
-                        _PyString_Eq(**name, key)) break;
-                    #endif
-                }
-                if (*name) {
-                    values[name-argnames] = value;
-                } else {
-                    /* unexpected keyword found */
-                    for (name=argnames; name != first_kw_arg; name++) {
-                        if (**name == key) goto arg_passed_twice;
-                        #if PY_MAJOR_VERSION >= 3
-                        if (PyUnicode_GET_SIZE(**name) == PyUnicode_GET_SIZE(key) &&
-                            PyUnicode_Compare(**name, key) == 0) goto arg_passed_twice;
-                        #else
-                        if (PyString_GET_SIZE(**name) == PyString_GET_SIZE(key) &&
-                            _PyString_Eq(**name, key)) goto arg_passed_twice;
-                        #endif
-                    }
-                    if (kwds2) {
-                        if (unlikely(PyDict_SetItem(kwds2, key, value))) goto bad;
-                    } else {
-                        goto invalid_keyword;
-                    }
-                }
-            }
-        }
-    }
-    return 0;
-arg_passed_twice:
-    __Pyx_RaiseDoubleKeywordsError(function_name, **name);
-    goto bad;
-invalid_keyword_type:
-    PyErr_Format(PyExc_TypeError,
-        "%s() keywords must be strings", function_name);
-    goto bad;
-invalid_keyword:
-    PyErr_Format(PyExc_TypeError,
-    #if PY_MAJOR_VERSION < 3
-        "%s() got an unexpected keyword argument '%s'",
-        function_name, PyString_AsString(key));
-    #else
-        "%s() got an unexpected keyword argument '%U'",
-        function_name, key);
-    #endif
-bad:
-    return -1;
-}
-
-
-static int __Pyx_GetException(PyObject **type, PyObject **value, PyObject **tb) {
-    PyObject *local_type, *local_value, *local_tb;
-    PyObject *tmp_type, *tmp_value, *tmp_tb;
-    PyThreadState *tstate = PyThreadState_GET();
-    local_type = tstate->curexc_type;
-    local_value = tstate->curexc_value;
-    local_tb = tstate->curexc_traceback;
-    tstate->curexc_type = 0;
-    tstate->curexc_value = 0;
-    tstate->curexc_traceback = 0;
-    PyErr_NormalizeException(&local_type, &local_value, &local_tb);
-    if (unlikely(tstate->curexc_type))
-        goto bad;
-    #if PY_MAJOR_VERSION >= 3
-    if (unlikely(PyException_SetTraceback(local_value, local_tb) < 0))
-        goto bad;
-    #endif
-    *type = local_type;
-    *value = local_value;
-    *tb = local_tb;
-    Py_INCREF(local_type);
-    Py_INCREF(local_value);
-    Py_INCREF(local_tb);
-    tmp_type = tstate->exc_type;
-    tmp_value = tstate->exc_value;
-    tmp_tb = tstate->exc_traceback;
-    tstate->exc_type = local_type;
-    tstate->exc_value = local_value;
-    tstate->exc_traceback = local_tb;
-    /* Make sure tstate is in a consistent state when we XDECREF
-       these objects (XDECREF may run arbitrary code). */
-    Py_XDECREF(tmp_type);
-    Py_XDECREF(tmp_value);
-    Py_XDECREF(tmp_tb);
-    return 0;
-bad:
-    *type = 0;
-    *value = 0;
-    *tb = 0;
-    Py_XDECREF(local_type);
-    Py_XDECREF(local_value);
-    Py_XDECREF(local_tb);
-    return -1;
-}
-
-
-static CYTHON_INLINE void __Pyx_ExceptionSave(PyObject **type, PyObject **value, PyObject **tb) {
-    PyThreadState *tstate = PyThreadState_GET();
-    *type = tstate->exc_type;
-    *value = tstate->exc_value;
-    *tb = tstate->exc_traceback;
-    Py_XINCREF(*type);
-    Py_XINCREF(*value);
-    Py_XINCREF(*tb);
-}
-
-static void __Pyx_ExceptionReset(PyObject *type, PyObject *value, PyObject *tb) {
-    PyObject *tmp_type, *tmp_value, *tmp_tb;
-    PyThreadState *tstate = PyThreadState_GET();
-    tmp_type = tstate->exc_type;
-    tmp_value = tstate->exc_value;
-    tmp_tb = tstate->exc_traceback;
-    tstate->exc_type = type;
-    tstate->exc_value = value;
-    tstate->exc_traceback = tb;
-    Py_XDECREF(tmp_type);
-    Py_XDECREF(tmp_value);
-    Py_XDECREF(tmp_tb);
-}
-
-static PyObject *__Pyx_Import(PyObject *name, PyObject *from_list) {
-    PyObject *py_import = 0;
-    PyObject *empty_list = 0;
-    PyObject *module = 0;
-    PyObject *global_dict = 0;
-    PyObject *empty_dict = 0;
-    PyObject *list;
-    py_import = __Pyx_GetAttrString(__pyx_b, "__import__");
-    if (!py_import)
-        goto bad;
-    if (from_list)
-        list = from_list;
-    else {
-        empty_list = PyList_New(0);
-        if (!empty_list)
-            goto bad;
-        list = empty_list;
-    }
-    global_dict = PyModule_GetDict(__pyx_m);
-    if (!global_dict)
-        goto bad;
-    empty_dict = PyDict_New();
-    if (!empty_dict)
-        goto bad;
-    module = PyObject_CallFunctionObjArgs(py_import,
-        name, global_dict, empty_dict, list, NULL);
-bad:
-    Py_XDECREF(empty_list);
-    Py_XDECREF(py_import);
-    Py_XDECREF(empty_dict);
-    return module;
-}
-
-static CYTHON_INLINE void __Pyx_ErrRestore(PyObject *type, PyObject *value, PyObject *tb) {
-    PyObject *tmp_type, *tmp_value, *tmp_tb;
-    PyThreadState *tstate = PyThreadState_GET();
-
-    tmp_type = tstate->curexc_type;
-    tmp_value = tstate->curexc_value;
-    tmp_tb = tstate->curexc_traceback;
-    tstate->curexc_type = type;
-    tstate->curexc_value = value;
-    tstate->curexc_traceback = tb;
-    Py_XDECREF(tmp_type);
-    Py_XDECREF(tmp_value);
-    Py_XDECREF(tmp_tb);
-}
-
-static CYTHON_INLINE void __Pyx_ErrFetch(PyObject **type, PyObject **value, PyObject **tb) {
-    PyThreadState *tstate = PyThreadState_GET();
-    *type = tstate->curexc_type;
-    *value = tstate->curexc_value;
-    *tb = tstate->curexc_traceback;
-
-    tstate->curexc_type = 0;
-    tstate->curexc_value = 0;
-    tstate->curexc_traceback = 0;
-}
-
-
-#if PY_MAJOR_VERSION < 3
-static void __Pyx_Raise(PyObject *type, PyObject *value, PyObject *tb) {
-    Py_XINCREF(type);
-    Py_XINCREF(value);
-    Py_XINCREF(tb);
-    /* First, check the traceback argument, replacing None with NULL. */
-    if (tb == Py_None) {
-        Py_DECREF(tb);
-        tb = 0;
-    }
-    else if (tb != NULL && !PyTraceBack_Check(tb)) {
-        PyErr_SetString(PyExc_TypeError,
-            "raise: arg 3 must be a traceback or None");
-        goto raise_error;
-    }
-    /* Next, replace a missing value with None */
-    if (value == NULL) {
-        value = Py_None;
-        Py_INCREF(value);
-    }
-    #if PY_VERSION_HEX < 0x02050000
-    if (!PyClass_Check(type))
-    #else
-    if (!PyType_Check(type))
-    #endif
-    {
-        /* Raising an instance.  The value should be a dummy. */
-        if (value != Py_None) {
-            PyErr_SetString(PyExc_TypeError,
-                "instance exception may not have a separate value");
-            goto raise_error;
-        }
-        /* Normalize to raise <class>, <instance> */
-        Py_DECREF(value);
-        value = type;
-        #if PY_VERSION_HEX < 0x02050000
-            if (PyInstance_Check(type)) {
-                type = (PyObject*) ((PyInstanceObject*)type)->in_class;
-                Py_INCREF(type);
-            }
-            else {
-                type = 0;
-                PyErr_SetString(PyExc_TypeError,
-                    "raise: exception must be an old-style class or instance");
-                goto raise_error;
-            }
-        #else
-            type = (PyObject*) Py_TYPE(type);
-            Py_INCREF(type);
-            if (!PyType_IsSubtype((PyTypeObject *)type, (PyTypeObject *)PyExc_BaseException)) {
-                PyErr_SetString(PyExc_TypeError,
-                    "raise: exception class must be a subclass of BaseException");
-                goto raise_error;
-            }
-        #endif
-    }
-
-    __Pyx_ErrRestore(type, value, tb);
-    return;
-raise_error:
-    Py_XDECREF(value);
-    Py_XDECREF(type);
-    Py_XDECREF(tb);
-    return;
-}
-
-#else /* Python 3+ */
-
-static void __Pyx_Raise(PyObject *type, PyObject *value, PyObject *tb) {
-    if (tb == Py_None) {
-        tb = 0;
-    } else if (tb && !PyTraceBack_Check(tb)) {
-        PyErr_SetString(PyExc_TypeError,
-            "raise: arg 3 must be a traceback or None");
-        goto bad;
-    }
-    if (value == Py_None)
-        value = 0;
-
-    if (PyExceptionInstance_Check(type)) {
-        if (value) {
-            PyErr_SetString(PyExc_TypeError,
-                "instance exception may not have a separate value");
-            goto bad;
-        }
-        value = type;
-        type = (PyObject*) Py_TYPE(value);
-    } else if (!PyExceptionClass_Check(type)) {
-        PyErr_SetString(PyExc_TypeError,
-            "raise: exception class must be a subclass of BaseException");
-        goto bad;
-    }
-
-    PyErr_SetObject(type, value);
-
-    if (tb) {
-        PyThreadState *tstate = PyThreadState_GET();
-        PyObject* tmp_tb = tstate->curexc_traceback;
-        if (tb != tmp_tb) {
-            Py_INCREF(tb);
-            tstate->curexc_traceback = tb;
-            Py_XDECREF(tmp_tb);
-        }
-    }
-
-bad:
-    return;
-}
-#endif
-
-static CYTHON_INLINE unsigned char __Pyx_PyInt_AsUnsignedChar(PyObject* x) {
-    const unsigned char neg_one = (unsigned char)-1, const_zero = 0;
-    const int is_unsigned = neg_one > const_zero;
-    if (sizeof(unsigned char) < sizeof(long)) {
-        long val = __Pyx_PyInt_AsLong(x);
-        if (unlikely(val != (long)(unsigned char)val)) {
-            if (!unlikely(val == -1 && PyErr_Occurred())) {
-                PyErr_SetString(PyExc_OverflowError,
-                    (is_unsigned && unlikely(val < 0)) ?
-                    "can't convert negative value to unsigned char" :
-                    "value too large to convert to unsigned char");
-            }
-            return (unsigned char)-1;
-        }
-        return (unsigned char)val;
-    }
-    return (unsigned char)__Pyx_PyInt_AsUnsignedLong(x);
-}
-
-static CYTHON_INLINE unsigned short __Pyx_PyInt_AsUnsignedShort(PyObject* x) {
-    const unsigned short neg_one = (unsigned short)-1, const_zero = 0;
-    const int is_unsigned = neg_one > const_zero;
-    if (sizeof(unsigned short) < sizeof(long)) {
-        long val = __Pyx_PyInt_AsLong(x);
-        if (unlikely(val != (long)(unsigned short)val)) {
-            if (!unlikely(val == -1 && PyErr_Occurred())) {
-                PyErr_SetString(PyExc_OverflowError,
-                    (is_unsigned && unlikely(val < 0)) ?
-                    "can't convert negative value to unsigned short" :
-                    "value too large to convert to unsigned short");
-            }
-            return (unsigned short)-1;
-        }
-        return (unsigned short)val;
-    }
-    return (unsigned short)__Pyx_PyInt_AsUnsignedLong(x);
-}
-
-static CYTHON_INLINE unsigned int __Pyx_PyInt_AsUnsignedInt(PyObject* x) {
-    const unsigned int neg_one = (unsigned int)-1, const_zero = 0;
-    const int is_unsigned = neg_one > const_zero;
-    if (sizeof(unsigned int) < sizeof(long)) {
-        long val = __Pyx_PyInt_AsLong(x);
-        if (unlikely(val != (long)(unsigned int)val)) {
-            if (!unlikely(val == -1 && PyErr_Occurred())) {
-                PyErr_SetString(PyExc_OverflowError,
-                    (is_unsigned && unlikely(val < 0)) ?
-                    "can't convert negative value to unsigned int" :
-                    "value too large to convert to unsigned int");
-            }
-            return (unsigned int)-1;
-        }
-        return (unsigned int)val;
-    }
-    return (unsigned int)__Pyx_PyInt_AsUnsignedLong(x);
-}
-
-static CYTHON_INLINE char __Pyx_PyInt_AsChar(PyObject* x) {
-    const char neg_one = (char)-1, const_zero = 0;
-    const int is_unsigned = neg_one > const_zero;
-    if (sizeof(char) < sizeof(long)) {
-        long val = __Pyx_PyInt_AsLong(x);
-        if (unlikely(val != (long)(char)val)) {
-            if (!unlikely(val == -1 && PyErr_Occurred())) {
-                PyErr_SetString(PyExc_OverflowError,
-                    (is_unsigned && unlikely(val < 0)) ?
-                    "can't convert negative value to char" :
-                    "value too large to convert to char");
-            }
-            return (char)-1;
-        }
-        return (char)val;
-    }
-    return (char)__Pyx_PyInt_AsLong(x);
-}
-
-static CYTHON_INLINE short __Pyx_PyInt_AsShort(PyObject* x) {
-    const short neg_one = (short)-1, const_zero = 0;
-    const int is_unsigned = neg_one > const_zero;
-    if (sizeof(short) < sizeof(long)) {
-        long val = __Pyx_PyInt_AsLong(x);
-        if (unlikely(val != (long)(short)val)) {
-            if (!unlikely(val == -1 && PyErr_Occurred())) {
-                PyErr_SetString(PyExc_OverflowError,
-                    (is_unsigned && unlikely(val < 0)) ?
-                    "can't convert negative value to short" :
-                    "value too large to convert to short");
-            }
-            return (short)-1;
-        }
-        return (short)val;
-    }
-    return (short)__Pyx_PyInt_AsLong(x);
-}
-
-static CYTHON_INLINE int __Pyx_PyInt_AsInt(PyObject* x) {
-    const int neg_one = (int)-1, const_zero = 0;
-    const int is_unsigned = neg_one > const_zero;
-    if (sizeof(int) < sizeof(long)) {
-        long val = __Pyx_PyInt_AsLong(x);
-        if (unlikely(val != (long)(int)val)) {
-            if (!unlikely(val == -1 && PyErr_Occurred())) {
-                PyErr_SetString(PyExc_OverflowError,
-                    (is_unsigned && unlikely(val < 0)) ?
-                    "can't convert negative value to int" :
-                    "value too large to convert to int");
-            }
-            return (int)-1;
-        }
-        return (int)val;
-    }
-    return (int)__Pyx_PyInt_AsLong(x);
-}
-
-static CYTHON_INLINE signed char __Pyx_PyInt_AsSignedChar(PyObject* x) {
-    const signed char neg_one = (signed char)-1, const_zero = 0;
-    const int is_unsigned = neg_one > const_zero;
-    if (sizeof(signed char) < sizeof(long)) {
-        long val = __Pyx_PyInt_AsLong(x);
-        if (unlikely(val != (long)(signed char)val)) {
-            if (!unlikely(val == -1 && PyErr_Occurred())) {
-                PyErr_SetString(PyExc_OverflowError,
-                    (is_unsigned && unlikely(val < 0)) ?
-                    "can't convert negative value to signed char" :
-                    "value too large to convert to signed char");
-            }
-            return (signed char)-1;
-        }
-        return (signed char)val;
-    }
-    return (signed char)__Pyx_PyInt_AsSignedLong(x);
-}
-
-static CYTHON_INLINE signed short __Pyx_PyInt_AsSignedShort(PyObject* x) {
-    const signed short neg_one = (signed short)-1, const_zero = 0;
-    const int is_unsigned = neg_one > const_zero;
-    if (sizeof(signed short) < sizeof(long)) {
-        long val = __Pyx_PyInt_AsLong(x);
-        if (unlikely(val != (long)(signed short)val)) {
-            if (!unlikely(val == -1 && PyErr_Occurred())) {
-                PyErr_SetString(PyExc_OverflowError,
-                    (is_unsigned && unlikely(val < 0)) ?
-                    "can't convert negative value to signed short" :
-                    "value too large to convert to signed short");
-            }
-            return (signed short)-1;
-        }
-        return (signed short)val;
-    }
-    return (signed short)__Pyx_PyInt_AsSignedLong(x);
-}
-
-static CYTHON_INLINE signed int __Pyx_PyInt_AsSignedInt(PyObject* x) {
-    const signed int neg_one = (signed int)-1, const_zero = 0;
-    const int is_unsigned = neg_one > const_zero;
-    if (sizeof(signed int) < sizeof(long)) {
-        long val = __Pyx_PyInt_AsLong(x);
-        if (unlikely(val != (long)(signed int)val)) {
-            if (!unlikely(val == -1 && PyErr_Occurred())) {
-                PyErr_SetString(PyExc_OverflowError,
-                    (is_unsigned && unlikely(val < 0)) ?
-                    "can't convert negative value to signed int" :
-                    "value too large to convert to signed int");
-            }
-            return (signed int)-1;
-        }
-        return (signed int)val;
-    }
-    return (signed int)__Pyx_PyInt_AsSignedLong(x);
-}
-
-static CYTHON_INLINE int __Pyx_PyInt_AsLongDouble(PyObject* x) {
-    const int neg_one = (int)-1, const_zero = 0;
-    const int is_unsigned = neg_one > const_zero;
-    if (sizeof(int) < sizeof(long)) {
-        long val = __Pyx_PyInt_AsLong(x);
-        if (unlikely(val != (long)(int)val)) {
-            if (!unlikely(val == -1 && PyErr_Occurred())) {
-                PyErr_SetString(PyExc_OverflowError,
-                    (is_unsigned && unlikely(val < 0)) ?
-                    "can't convert negative value to int" :
-                    "value too large to convert to int");
-            }
-            return (int)-1;
-        }
-        return (int)val;
-    }
-    return (int)__Pyx_PyInt_AsLong(x);
-}
-
-static CYTHON_INLINE unsigned long __Pyx_PyInt_AsUnsignedLong(PyObject* x) {
-    const unsigned long neg_one = (unsigned long)-1, const_zero = 0;
-    const int is_unsigned = neg_one > const_zero;
-#if PY_VERSION_HEX < 0x03000000
-    if (likely(PyInt_Check(x))) {
-        long val = PyInt_AS_LONG(x);
-        if (is_unsigned && unlikely(val < 0)) {
-            PyErr_SetString(PyExc_OverflowError,
-                            "can't convert negative value to unsigned long");
-            return (unsigned long)-1;
-        }
-        return (unsigned long)val;
-    } else
-#endif
-    if (likely(PyLong_Check(x))) {
-        if (is_unsigned) {
-            if (unlikely(Py_SIZE(x) < 0)) {
-                PyErr_SetString(PyExc_OverflowError,
-                                "can't convert negative value to unsigned long");
-                return (unsigned long)-1;
-            }
-            return PyLong_AsUnsignedLong(x);
-        } else {
-            return PyLong_AsLong(x);
-        }
-    } else {
-        unsigned long val;
-        PyObject *tmp = __Pyx_PyNumber_Int(x);
-        if (!tmp) return (unsigned long)-1;
-        val = __Pyx_PyInt_AsUnsignedLong(tmp);
-        Py_DECREF(tmp);
-        return val;
-    }
-}
-
-static CYTHON_INLINE unsigned PY_LONG_LONG __Pyx_PyInt_AsUnsignedLongLong(PyObject* x) {
-    const unsigned PY_LONG_LONG neg_one = (unsigned PY_LONG_LONG)-1, const_zero = 0;
-    const int is_unsigned = neg_one > const_zero;
-#if PY_VERSION_HEX < 0x03000000
-    if (likely(PyInt_Check(x))) {
-        long val = PyInt_AS_LONG(x);
-        if (is_unsigned && unlikely(val < 0)) {
-            PyErr_SetString(PyExc_OverflowError,
-                            "can't convert negative value to unsigned PY_LONG_LONG");
-            return (unsigned PY_LONG_LONG)-1;
-        }
-        return (unsigned PY_LONG_LONG)val;
-    } else
-#endif
-    if (likely(PyLong_Check(x))) {
-        if (is_unsigned) {
-            if (unlikely(Py_SIZE(x) < 0)) {
-                PyErr_SetString(PyExc_OverflowError,
-                                "can't convert negative value to unsigned PY_LONG_LONG");
-                return (unsigned PY_LONG_LONG)-1;
-            }
-            return PyLong_AsUnsignedLongLong(x);
-        } else {
-            return PyLong_AsLongLong(x);
-        }
-    } else {
-        unsigned PY_LONG_LONG val;
-        PyObject *tmp = __Pyx_PyNumber_Int(x);
-        if (!tmp) return (unsigned PY_LONG_LONG)-1;
-        val = __Pyx_PyInt_AsUnsignedLongLong(tmp);
-        Py_DECREF(tmp);
-        return val;
-    }
-}
-
-static CYTHON_INLINE long __Pyx_PyInt_AsLong(PyObject* x) {
-    const long neg_one = (long)-1, const_zero = 0;
-    const int is_unsigned = neg_one > const_zero;
-#if PY_VERSION_HEX < 0x03000000
-    if (likely(PyInt_Check(x))) {
-        long val = PyInt_AS_LONG(x);
-        if (is_unsigned && unlikely(val < 0)) {
-            PyErr_SetString(PyExc_OverflowError,
-                            "can't convert negative value to long");
-            return (long)-1;
-        }
-        return (long)val;
-    } else
-#endif
-    if (likely(PyLong_Check(x))) {
-        if (is_unsigned) {
-            if (unlikely(Py_SIZE(x) < 0)) {
-                PyErr_SetString(PyExc_OverflowError,
-                                "can't convert negative value to long");
-                return (long)-1;
-            }
-            return PyLong_AsUnsignedLong(x);
-        } else {
-            return PyLong_AsLong(x);
-        }
-    } else {
-        long val;
-        PyObject *tmp = __Pyx_PyNumber_Int(x);
-        if (!tmp) return (long)-1;
-        val = __Pyx_PyInt_AsLong(tmp);
-        Py_DECREF(tmp);
-        return val;
-    }
-}
-
-static CYTHON_INLINE PY_LONG_LONG __Pyx_PyInt_AsLongLong(PyObject* x) {
-    const PY_LONG_LONG neg_one = (PY_LONG_LONG)-1, const_zero = 0;
-    const int is_unsigned = neg_one > const_zero;
-#if PY_VERSION_HEX < 0x03000000
-    if (likely(PyInt_Check(x))) {
-        long val = PyInt_AS_LONG(x);
-        if (is_unsigned && unlikely(val < 0)) {
-            PyErr_SetString(PyExc_OverflowError,
-                            "can't convert negative value to PY_LONG_LONG");
-            return (PY_LONG_LONG)-1;
-        }
-        return (PY_LONG_LONG)val;
-    } else
-#endif
-    if (likely(PyLong_Check(x))) {
-        if (is_unsigned) {
-            if (unlikely(Py_SIZE(x) < 0)) {
-                PyErr_SetString(PyExc_OverflowError,
-                                "can't convert negative value to PY_LONG_LONG");
-                return (PY_LONG_LONG)-1;
-            }
-            return PyLong_AsUnsignedLongLong(x);
-        } else {
-            return PyLong_AsLongLong(x);
-        }
-    } else {
-        PY_LONG_LONG val;
-        PyObject *tmp = __Pyx_PyNumber_Int(x);
-        if (!tmp) return (PY_LONG_LONG)-1;
-        val = __Pyx_PyInt_AsLongLong(tmp);
-        Py_DECREF(tmp);
-        return val;
-    }
-}
-
-static CYTHON_INLINE signed long __Pyx_PyInt_AsSignedLong(PyObject* x) {
-    const signed long neg_one = (signed long)-1, const_zero = 0;
-    const int is_unsigned = neg_one > const_zero;
-#if PY_VERSION_HEX < 0x03000000
-    if (likely(PyInt_Check(x))) {
-        long val = PyInt_AS_LONG(x);
-        if (is_unsigned && unlikely(val < 0)) {
-            PyErr_SetString(PyExc_OverflowError,
-                            "can't convert negative value to signed long");
-            return (signed long)-1;
-        }
-        return (signed long)val;
-    } else
-#endif
-    if (likely(PyLong_Check(x))) {
-        if (is_unsigned) {
-            if (unlikely(Py_SIZE(x) < 0)) {
-                PyErr_SetString(PyExc_OverflowError,
-                                "can't convert negative value to signed long");
-                return (signed long)-1;
-            }
-            return PyLong_AsUnsignedLong(x);
-        } else {
-            return PyLong_AsLong(x);
-        }
-    } else {
-        signed long val;
-        PyObject *tmp = __Pyx_PyNumber_Int(x);
-        if (!tmp) return (signed long)-1;
-        val = __Pyx_PyInt_AsSignedLong(tmp);
-        Py_DECREF(tmp);
-        return val;
-    }
-}
-
-static CYTHON_INLINE signed PY_LONG_LONG __Pyx_PyInt_AsSignedLongLong(PyObject* x) {
-    const signed PY_LONG_LONG neg_one = (signed PY_LONG_LONG)-1, const_zero = 0;
-    const int is_unsigned = neg_one > const_zero;
-#if PY_VERSION_HEX < 0x03000000
-    if (likely(PyInt_Check(x))) {
-        long val = PyInt_AS_LONG(x);
-        if (is_unsigned && unlikely(val < 0)) {
-            PyErr_SetString(PyExc_OverflowError,
-                            "can't convert negative value to signed PY_LONG_LONG");
-            return (signed PY_LONG_LONG)-1;
-        }
-        return (signed PY_LONG_LONG)val;
-    } else
-#endif
-    if (likely(PyLong_Check(x))) {
-        if (is_unsigned) {
-            if (unlikely(Py_SIZE(x) < 0)) {
-                PyErr_SetString(PyExc_OverflowError,
-                                "can't convert negative value to signed PY_LONG_LONG");
-                return (signed PY_LONG_LONG)-1;
-            }
-            return PyLong_AsUnsignedLongLong(x);
-        } else {
-            return PyLong_AsLongLong(x);
-        }
-    } else {
-        signed PY_LONG_LONG val;
-        PyObject *tmp = __Pyx_PyNumber_Int(x);
-        if (!tmp) return (signed PY_LONG_LONG)-1;
-        val = __Pyx_PyInt_AsSignedLongLong(tmp);
-        Py_DECREF(tmp);
-        return val;
-    }
-}
-
-#include "compile.h"
-#include "frameobject.h"
-#include "traceback.h"
-
-static void __Pyx_AddTraceback(const char *funcname) {
-    PyObject *py_srcfile = 0;
-    PyObject *py_funcname = 0;
-    PyObject *py_globals = 0;
-    PyCodeObject *py_code = 0;
-    PyFrameObject *py_frame = 0;
-
-    #if PY_MAJOR_VERSION < 3
-    py_srcfile = PyString_FromString(__pyx_filename);
-    #else
-    py_srcfile = PyUnicode_FromString(__pyx_filename);
-    #endif
-    if (!py_srcfile) goto bad;
-    if (__pyx_clineno) {
-        #if PY_MAJOR_VERSION < 3
-        py_funcname = PyString_FromFormat( "%s (%s:%d)", funcname, __pyx_cfilenm, __pyx_clineno);
-        #else
-        py_funcname = PyUnicode_FromFormat( "%s (%s:%d)", funcname, __pyx_cfilenm, __pyx_clineno);
-        #endif
-    }
-    else {
-        #if PY_MAJOR_VERSION < 3
-        py_funcname = PyString_FromString(funcname);
-        #else
-        py_funcname = PyUnicode_FromString(funcname);
-        #endif
-    }
-    if (!py_funcname) goto bad;
-    py_globals = PyModule_GetDict(__pyx_m);
-    if (!py_globals) goto bad;
-    py_code = PyCode_New(
-        0,            /*int argcount,*/
-        #if PY_MAJOR_VERSION >= 3
-        0,            /*int kwonlyargcount,*/
-        #endif
-        0,            /*int nlocals,*/
-        0,            /*int stacksize,*/
-        0,            /*int flags,*/
-        __pyx_empty_bytes, /*PyObject *code,*/
-        __pyx_empty_tuple,  /*PyObject *consts,*/
-        __pyx_empty_tuple,  /*PyObject *names,*/
-        __pyx_empty_tuple,  /*PyObject *varnames,*/
-        __pyx_empty_tuple,  /*PyObject *freevars,*/
-        __pyx_empty_tuple,  /*PyObject *cellvars,*/
-        py_srcfile,   /*PyObject *filename,*/
-        py_funcname,  /*PyObject *name,*/
-        __pyx_lineno,   /*int firstlineno,*/
-        __pyx_empty_bytes  /*PyObject *lnotab*/
-    );
-    if (!py_code) goto bad;
-    py_frame = PyFrame_New(
-        PyThreadState_GET(), /*PyThreadState *tstate,*/
-        py_code,             /*PyCodeObject *code,*/
-        py_globals,          /*PyObject *globals,*/
-        0                    /*PyObject *locals*/
-    );
-    if (!py_frame) goto bad;
-    py_frame->f_lineno = __pyx_lineno;
-    PyTraceBack_Here(py_frame);
-bad:
-    Py_XDECREF(py_srcfile);
-    Py_XDECREF(py_funcname);
-    Py_XDECREF(py_code);
-    Py_XDECREF(py_frame);
-}
-
-static int __Pyx_InitStrings(__Pyx_StringTabEntry *t) {
-    while (t->p) {
-        #if PY_MAJOR_VERSION < 3
-        if (t->is_unicode) {
-            *t->p = PyUnicode_DecodeUTF8(t->s, t->n - 1, NULL);
-        } else if (t->intern) {
-            *t->p = PyString_InternFromString(t->s);
-        } else {
-            *t->p = PyString_FromStringAndSize(t->s, t->n - 1);
-        }
-        #else  /* Python 3+ has unicode identifiers */
-        if (t->is_unicode | t->is_str) {
-            if (t->intern) {
-                *t->p = PyUnicode_InternFromString(t->s);
-            } else if (t->encoding) {
-                *t->p = PyUnicode_Decode(t->s, t->n - 1, t->encoding, NULL);
-            } else {
-                *t->p = PyUnicode_FromStringAndSize(t->s, t->n - 1);
-            }
-        } else {
-            *t->p = PyBytes_FromStringAndSize(t->s, t->n - 1);
-        }
-        #endif
-        if (!*t->p)
-            return -1;
-        ++t;
-    }
-    return 0;
-}
-
-/* Type Conversion Functions */
-
-static CYTHON_INLINE int __Pyx_PyObject_IsTrue(PyObject* x) {
-   int is_true = x == Py_True;
-   if (is_true | (x == Py_False) | (x == Py_None)) return is_true;
-   else return PyObject_IsTrue(x);
-}
-
-static CYTHON_INLINE PyObject* __Pyx_PyNumber_Int(PyObject* x) {
-  PyNumberMethods *m;
-  const char *name = NULL;
-  PyObject *res = NULL;
-#if PY_VERSION_HEX < 0x03000000
-  if (PyInt_Check(x) || PyLong_Check(x))
-#else
-  if (PyLong_Check(x))
-#endif
-    return Py_INCREF(x), x;
-  m = Py_TYPE(x)->tp_as_number;
-#if PY_VERSION_HEX < 0x03000000
-  if (m && m->nb_int) {
-    name = "int";
-    res = PyNumber_Int(x);
-  }
-  else if (m && m->nb_long) {
-    name = "long";
-    res = PyNumber_Long(x);
-  }
-#else
-  if (m && m->nb_int) {
-    name = "int";
-    res = PyNumber_Long(x);
-  }
-#endif
-  if (res) {
-#if PY_VERSION_HEX < 0x03000000
-    if (!PyInt_Check(res) && !PyLong_Check(res)) {
-#else
-    if (!PyLong_Check(res)) {
-#endif
-      PyErr_Format(PyExc_TypeError,
-                   "__%s__ returned non-%s (type %.200s)",
-                   name, name, Py_TYPE(res)->tp_name);
-      Py_DECREF(res);
-      return NULL;
-    }
-  }
-  else if (!PyErr_Occurred()) {
-    PyErr_SetString(PyExc_TypeError,
-                    "an integer is required");
-  }
-  return res;
-}
-
-static CYTHON_INLINE Py_ssize_t __Pyx_PyIndex_AsSsize_t(PyObject* b) {
-  Py_ssize_t ival;
-  PyObject* x = PyNumber_Index(b);
-  if (!x) return -1;
-  ival = PyInt_AsSsize_t(x);
-  Py_DECREF(x);
-  return ival;
-}
-
-static CYTHON_INLINE PyObject * __Pyx_PyInt_FromSize_t(size_t ival) {
-#if PY_VERSION_HEX < 0x02050000
-   if (ival <= LONG_MAX)
-       return PyInt_FromLong((long)ival);
-   else {
-       unsigned char *bytes = (unsigned char *) &ival;
-       int one = 1; int little = (int)*(unsigned char*)&one;
-       return _PyLong_FromByteArray(bytes, sizeof(size_t), little, 0);
-   }
-#else
-   return PyInt_FromSize_t(ival);
-#endif
-}
-
-static CYTHON_INLINE size_t __Pyx_PyInt_AsSize_t(PyObject* x) {
-   unsigned PY_LONG_LONG val = __Pyx_PyInt_AsUnsignedLongLong(x);
-   if (unlikely(val == (unsigned PY_LONG_LONG)-1 && PyErr_Occurred())) {
-       return (size_t)-1;
-   } else if (unlikely(val != (unsigned PY_LONG_LONG)(size_t)val)) {
-       PyErr_SetString(PyExc_OverflowError,
-                       "value too large to convert to size_t");
-       return (size_t)-1;
-   }
-   return (size_t)val;
-}
-
-
-#endif /* Py_PYTHON_H */
diff --git a/shapely/tests/Array.txt b/shapely/tests/Array.txt
deleted file mode 100644
index c30b687..0000000
--- a/shapely/tests/Array.txt
+++ /dev/null
@@ -1,86 +0,0 @@
-Test implementation of array interface
-======================================
-
-  >>> from shapely.geometry import Point
-
-  >>> p = Point(0.0, 0.0, 1.0)
-  >>> coords = p.coords[0]
-  >>> coords
-  (0.0, 0.0, 1.0)
-  >>> p.ctypes #doctest: +ELLIPSIS
-  <shapely.geometry.point.c_double_Array_3 object at ...>
-
-  Convert to Numpy array, passing through Python sequence
-
-  >>> from numpy import asarray
-  >>> a = asarray(coords)
-  >>> a.ndim
-  1
-  >>> a.size
-  3
-  >>> a.shape
-  (3,)
-
-  Convert to Numpy array, passing through a ctypes array
-
-  >>> b = asarray(p)
-  >>> b.size
-  3
-  >>> b.shape
-  (3,)
-  >>> b[0]
-  0.0
-  >>> b[1]
-  0.0
-  >>> b[2]
-  1.0
-
-  Make a point from a Numpy array
-
-  >>> a = asarray([1.0, 1.0, 0.0])
-  >>> p = Point(*list(a))
-  >>> p.wkt
-  'POINT (1.0000000000000000 1.0000000000000000)'
-
-LineString
-----------
-
-  >>> a = asarray([[0.0, 0.0], [2.0, 2.0], [1.0, 1.0]])
-  >>> from shapely.geometry import LineString
-  >>> line = LineString(a)
-  >>> line.wkt
-  'LINESTRING (0.0000000000000000 0.0000000000000000, 2.0000000000000000 2.0000000000000000, 1.0000000000000000 1.0000000000000000)'
-
-  >>> data = line.ctypes
-  >>> data[0]
-  0.0
-  >>> data[5]
-  1.0
-
-  >>> b = asarray(line)
-  >>> b.shape
-  (3, 2)
-  >>> b
-  array([[ 0.,  0.],
-         [ 2.,  2.],
-         [ 1.,  1.]])
-
-Polygon
--------
-
-  >>> a = asarray(((0., 0.), (0., 1.), (1., 1.), (1., 0.), (0., 0.)))
-  >>> from shapely.geometry import Polygon
-  >>> polygon = Polygon(a)
-  >>> polygon.wkt
-  'POLYGON ((0.0000000000000000 0.0000000000000000, 0.0000000000000000 1.0000000000000000, 1.0000000000000000 1.0000000000000000, 1.0000000000000000 0.0000000000000000, 0.0000000000000000 0.0000000000000000))'
-
-  >>> b = asarray(polygon.exterior)
-  >>> b.shape
-  (5, 2)
-  >>> b
-  array([[ 0.,  0.],
-         [ 0.,  1.],
-         [ 1.,  1.],
-         [ 1.,  0.],
-         [ 0.,  0.]])
-
diff --git a/shapely/tests/GeoInterface.txt b/shapely/tests/GeoInterface.txt
deleted file mode 100644
index 5c94030..0000000
--- a/shapely/tests/GeoInterface.txt
+++ /dev/null
@@ -1,69 +0,0 @@
-Test implementation of geo interface
-====================================
-
-  >>> from shapely.geometry import asShape
-
-  Adapt a dictionary
-
-  >>> d = {"type": "Point", "coordinates": (0.0, 0.0)}
-  >>> shape = asShape(d)
-  >>> shape.geom_type
-  'Point'
-  >>> tuple(shape.coords)
-  ((0.0, 0.0),)
-
-  Adapt an object that implements the geo protocol
-
-  >>> class GeoThing(object):
-  ...     def __init__(self, d):
-  ...         self.__geo_interface__ = d
-
-  >>> shape = None
-  >>> thing = GeoThing({"type": "Point", "coordinates": (0.0, 0.0)})
-  >>> shape = asShape(thing)
-  >>> shape.geom_type
-  'Point'
-  >>> tuple(shape.coords)
-  ((0.0, 0.0),)
-
-  Check line string
-
-  >>> shape = asShape({'type': 'LineString', 'coordinates': ((-1.0, -1.0), (1.0, 1.0))})
-  >>> shape # doctest: +ELLIPSIS
-  <shapely.geometry.linestring.LineStringAdapter object at ...>
-  >>> tuple(shape.coords)
-  ((-1.0, -1.0), (1.0, 1.0))
-
-  polygon
-
-  >>> shape = asShape({'type': 'Polygon', 'coordinates': (((0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (2.0, -1.0), (0.0, 0.0)), ((0.10000000000000001, 0.10000000000000001), (0.10000000000000001, 0.20000000000000001), (0.20000000000000001, 0.20000000000000001), (0.20000000000000001, 0.10000000000000001), (0.10000000000000001, 0.10000000000000001)))})
-  >>> shape # doctest: +ELLIPSIS
-  <shapely.geometry.polygon.PolygonAdapter object at ...>
-  >>> tuple(shape.exterior.coords)
-  ((0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (2.0, -1.0), (0.0, 0.0))
-  >>> len(shape.interiors)
-  1
-
-  multi point
-  >>> shape = asShape({'type': 'MultiPoint', 'coordinates': ((1.0, 2.0), (3.0, 4.0))})
-  >>> shape # doctest: +ELLIPSIS
-  <shapely.geometry.multipoint.MultiPointAdapter object at ...>
-  >>> len(shape.geoms)
-  2
-
-  multi line string
-
-  >>> shape = asShape({'type': 'MultiLineString', 'coordinates': (((0.0, 0.0), (1.0, 2.0)),)})
-  >>> shape # doctest: +ELLIPSIS
-  <shapely.geometry.multilinestring.MultiLineStringAdapter object at ...>
-  >>> len(shape.geoms)
-  1
-
-  multi polygon
-
-  >>> shape = asShape({'type': 'MultiPolygon', 'coordinates': [(((0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (1.0, 0.0), (0.0, 0.0)), ((0.10000000000000001, 0.10000000000000001), (0.10000000000000001, 0.20000000000000001), (0.20000000000000001, 0.20000000000000001), (0.20000000000000001, 0.10000000000000001), (0.10000000000000001, 0.10000000000000001)))]})
-  >>> shape # doctest: +ELLIPSIS
-  <shapely.geometry.multipolygon.MultiPolygonAdapter object at ...>
-  >>> len(shape.geoms)
-  1
-
diff --git a/shapely/tests/IterOps.txt b/shapely/tests/IterOps.txt
deleted file mode 100644
index c71f9a3..0000000
--- a/shapely/tests/IterOps.txt
+++ /dev/null
@@ -1,31 +0,0 @@
-Test operator iterations
-========================
-
-  >>> from shapely.geometry import Polygon
-  >>> from shapely.geometry import Point
-  >>> coords = ((0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (1.0, 0.0), (0.0, 0.0))
-  >>> polygon = Polygon(coords)
-  >>> points = [Point(0.5, 0.5), Point(2.0, 2.0)]
-  
-  >>> from shapely import iterops
-  
-  List of the points contained by the polygon
-  >>> list(iterops.contains(polygon, points, True)) # doctest: +ELLIPSIS
-  [<shapely.geometry.point.Point object at ...>]
-
-  'True' is the default value
-  >>> list(iterops.contains(polygon, points)) # doctest: +ELLIPSIS
-  [<shapely.geometry.point.Point object at ...>]
-
-  Test a false value
-  >>> list(iterops.contains(polygon, points, False)) # doctest: +ELLIPSIS
-  [<shapely.geometry.point.Point object at ...>]
-
-  If the provided iterator yields tuples, the second value will be yielded
-  >>> list(iterops.contains(polygon, [(p, p.wkt) for p in points], False))
-  ['POINT (2.0000000000000000 2.0000000000000000)']
-
-  Just to demonstrate that the important thing is that the second parameter
-  is an iterator:
-  >>> list(iterops.contains(polygon, iter((p, p.wkt) for p in points)))
-  ['POINT (0.5000000000000000 0.5000000000000000)']
diff --git a/shapely/tests/LineString.txt b/shapely/tests/LineString.txt
deleted file mode 100644
index b6aeaf2..0000000
--- a/shapely/tests/LineString.txt
+++ /dev/null
@@ -1,134 +0,0 @@
-LineStrings
-===========
-
-Initialization
---------------
-
-    >>> from shapely.geometry import LineString
-
-Construct from a numpy array
-
-    >>> from numpy import array
-    >>> line = LineString(array([[0.0, 0.0], [1.0, 2.0]]))
-    >>> len(line.coords)
-    2
-    >>> line.wkt
-    'LINESTRING (0.0000000000000000 0.0000000000000000, 1.0000000000000000 2.0000000000000000)'
-
-From coordinate tuples
-
-    >>> line = LineString(((1.0, 2.0), (3.0, 4.0)))
-    >>> len(line.coords)
-    2
-    >>> line.wkt
-    'LINESTRING (1.0000000000000000 2.0000000000000000, 3.0000000000000000 4.0000000000000000)'
-
-From lines
-
-    >>> copy = LineString(line)
-    >>> len(copy.coords)
-    2
-    >>> print copy.wkt
-    LINESTRING (1.0000000000000000 2.0000000000000000, 3.0000000000000000 4.0000000000000000)
-
-
-Numpy Support
--------------
-
-    >>> from numpy import asarray
-    >>> la = asarray(line)
-    >>> la
-    array([[ 1.,  2.],
-           [ 3.,  4.]])
-    >>> la[0]
-    array([ 1.,  2.])
-
-Coordinate sequences can be adapted as well
-
-    >>> asarray(line.coords)
-    array([[ 1.,  2.],
-           [ 3.,  4.]])
-
-Bounds
-------
-
-    >>> line.bounds
-    (1.0, 2.0, 3.0, 4.0)
-
-Coordinate Access
------------------
-
-    >>> tuple(line.coords)
-    ((1.0, 2.0), (3.0, 4.0))
-
-    >>> line.coords[0]
-    (1.0, 2.0)
-    >>> line.coords[1]
-    (3.0, 4.0)
-    >>> line.coords[2]
-    Traceback (most recent call last):
-    ...
-    IndexError: index out of range
-
-Geo interface
--------------
-
-    >>> line.__geo_interface__
-    {'type': 'LineString', 'coordinates': ((1.0, 2.0), (3.0, 4.0))}
-
-
-Coordinate modification
------------------------
-
-    >>> line.coords = ((-1.0, -1.0), (1.0, 1.0))
-    >>> line.__geo_interface__
-    {'type': 'LineString', 'coordinates': ((-1.0, -1.0), (1.0, 1.0))}
-
-
-Adapter
--------
-
-    >>> from shapely.geometry import asLineString
-
-    Adapt a Numpy array to a line string
-
-    >>> a = array([[1.0, 2.0], [3.0, 4.0]])
-    >>> la = asLineString(a)
-    >>> la.context
-    array([[ 1.,  2.],
-           [ 3.,  4.]])
-
-    >>> la.wkt
-    'LINESTRING (1.0000000000000000 2.0000000000000000, 3.0000000000000000 4.0000000000000000)'
-
-Now, the inverse
-
-    >>> la.__array_interface__ == la.context.__array_interface__
-    True
-    >>> pas = asarray(la)
-    >>> pas
-    array([[ 1.,  2.],
-           [ 3.,  4.]])
-
-Adapt a coordinate list to a line string
-
-    >>> coords = [[5.0, 6.0], [7.0, 8.0]]
-    >>> la = asLineString(coords)
-    >>> la.wkt
-    'LINESTRING (5.0000000000000000 6.0000000000000000, 7.0000000000000000 8.0000000000000000)'
-
-Test Non-operability of Null geometry
-
-    >>> l_null = LineString()
-    >>> l_null.wkt # doctest: +ELLIPSIS
-    'GEOMETRYCOLLECTION EMPTY'
-
-    >>> l_null.length # doctest: +ELLIPSIS
-    0.0
-
-Check that we can set coordinates of a null geometry
-
-    >>> l_null.coords = [(0, 0), (1, 1)]
-    >>> print l_null.length # doctest: +ELLIPSIS
-    1.414...
-
diff --git a/shapely/tests/MultiLineString.txt b/shapely/tests/MultiLineString.txt
deleted file mode 100644
index 71c1951..0000000
--- a/shapely/tests/MultiLineString.txt
+++ /dev/null
@@ -1,81 +0,0 @@
-MultiLineStrings
-================
-
-Initialization
---------------
-
-  >>> from shapely.geometry import LineString, MultiLineString
-
-From coordinate tuples
-
-  >>> geom = MultiLineString( (((1.0, 2.0), (3.0, 4.0)),) )
-  >>> geom # doctest: +ELLIPSIS
-  <shapely.geometry.multilinestring.MultiLineString object at ...>
-  >>> len(geom.geoms)
-  1
-  >>> geom.wkt
-  'MULTILINESTRING ((1.0000000000000000 2.0000000000000000, 3.0000000000000000 4.0000000000000000))'
-
-Construct from a numpy array
-
-  >>> from numpy import array
-  >>> geom = MultiLineString( [array(((0.0, 0.0), (1.0, 2.0)))] )
-  >>> geom # doctest: +ELLIPSIS
-  <shapely.geometry.multilinestring.MultiLineString object at ...>
-  >>> len(geom.geoms)
-  1
-  >>> geom.wkt
-  'MULTILINESTRING ((0.0000000000000000 0.0000000000000000, 1.0000000000000000 2.0000000000000000))'
-
-From lines
-
-  >>> a = LineString(((1.0, 2.0), (3.0, 4.0)))
-  >>> ml = MultiLineString([a])
-  >>> len(ml.geoms)
-  1
-  >>> print ml.wkt
-  MULTILINESTRING ((1.0000000000000000 2.0000000000000000, 3.0000000000000000 4.0000000000000000))
-
-From another multi-line
-
-  >>> ml2 = MultiLineString(ml)
-  >>> len(ml2.geoms)
-  1
-  >>> print ml2.wkt
-  MULTILINESTRING ((1.0000000000000000 2.0000000000000000, 3.0000000000000000 4.0000000000000000))
-
-
-Sub-geometry Access
--------------------
-
-  >>> geom.geoms[0] # doctest: +ELLIPSIS
-  <shapely.geometry.linestring.LineString object at ...>
-  >>> geom.geoms[0].wkt
-  'LINESTRING (0.0000000000000000 0.0000000000000000, 1.0000000000000000 2.0000000000000000)'
-  >>> geom.geoms[1]
-  Traceback (most recent call last):
-  ...
-  IndexError: index out of range
-
-Geo interface
--------------
-
-  >>> geom.__geo_interface__
-  {'type': 'MultiLineString', 'coordinates': (((0.0, 0.0), (1.0, 2.0)),)}
-
-Adapter
--------
-
-  >>> from shapely.geometry import asMultiLineString
-
-  Adapt a sequence of Numpy arrays to a multilinestring
-
-  >>> a = [ array(((1.0, 2.0), (3.0, 4.0))) ]
-  >>> geoma = asMultiLineString(a)
-  >>> geoma.context
-  [array([[ 1.,  2.],
-         [ 3.,  4.]])]
-
-  >>> geoma.wkt
-  'MULTILINESTRING ((1.0000000000000000 2.0000000000000000, 3.0000000000000000 4.0000000000000000))'
-
diff --git a/shapely/tests/MultiPoint.txt b/shapely/tests/MultiPoint.txt
deleted file mode 100644
index 4e5df0b..0000000
--- a/shapely/tests/MultiPoint.txt
+++ /dev/null
@@ -1,101 +0,0 @@
-MultiPoints
-===========
-
-Initialization
---------------
-
-  >>> from shapely.geometry import Point, MultiPoint
-
-Construct from a numpy array
-
-  >>> from numpy import array
-  >>> geom = MultiPoint(array([[0.0, 0.0], [1.0, 2.0]]))
-  >>> geom # doctest: +ELLIPSIS
-  <shapely.geometry.multipoint.MultiPoint object at ...>
-  >>> len(geom.geoms)
-  2
-  >>> geom.wkt
-  'MULTIPOINT (0.0000000000000000 0.0000000000000000, 1.0000000000000000 2.0000000000000000)'
-
-From coordinate tuples
-
-  >>> geom = MultiPoint(((1.0, 2.0), (3.0, 4.0)))
-  >>> len(geom.geoms)
-  2
-  >>> geom.wkt
-  'MULTIPOINT (1.0000000000000000 2.0000000000000000, 3.0000000000000000 4.0000000000000000)'
-
-From points
-
-  >>> geom = MultiPoint((Point(1.0, 2.0), Point(3.0, 4.0)))
-  >>> len(geom.geoms)
-  2
-  >>> print geom.wkt
-  MULTIPOINT (1.0000000000000000 2.0000000000000000, 3.0000000000000000 4.0000000000000000)
-
-From another multi-point
-
-  >>> geom2 = MultiPoint(geom)
-  >>> len(geom2.geoms)
-  2
-  >>> print geom2.wkt
-  MULTIPOINT (1.0000000000000000 2.0000000000000000, 3.0000000000000000 4.0000000000000000)
-
-
-Sub-geometry Access
--------------------
-
-  >>> geom.geoms[0] # doctest: +ELLIPSIS
-  <shapely.geometry.point.Point object at ...>
-  >>> geom.geoms[0].x
-  1.0
-  >>> geom.geoms[0].y
-  2.0
-  >>> geom.geoms[2]
-  Traceback (most recent call last):
-  ...
-  IndexError: index out of range
-
-Geo interface
--------------
-
-  >>> geom.__geo_interface__
-  {'type': 'MultiPoint', 'coordinates': ((1.0, 2.0), (3.0, 4.0))}
-
-  >>> array(geom)
-  array([[ 1.,  2.],
-         [ 3.,  4.]])
-
-Adapter
--------
-
-  >>> from shapely.geometry import asMultiPoint
-
-  Adapt a Numpy array to a multipoint
-
-  >>> a = array([[1.0, 2.0], [3.0, 4.0]])
-  >>> geoma = asMultiPoint(a)
-  >>> geoma.context
-  array([[ 1.,  2.],
-         [ 3.,  4.]])
-
-  >>> geoma.wkt
-  'MULTIPOINT (1.0000000000000000 2.0000000000000000, 3.0000000000000000 4.0000000000000000)'
-
-  Now, the inverse
-
-  >>> geoma.__array_interface__ == geoma.context.__array_interface__
-  True
-  >>> from numpy import asarray
-  >>> pas = asarray(geoma)
-  >>> pas
-  array([[ 1.,  2.],
-         [ 3.,  4.]])
-
-  Adapt a coordinate list to a line string
-
-  >>> coords = [[5.0, 6.0], [7.0, 8.0]]
-  >>> geoma = asMultiPoint(coords)
-  >>> geoma.wkt
-  'MULTIPOINT (5.0000000000000000 6.0000000000000000, 7.0000000000000000 8.0000000000000000)'
-  
diff --git a/shapely/tests/MultiPolygon.txt b/shapely/tests/MultiPolygon.txt
deleted file mode 100644
index 376ddb6..0000000
--- a/shapely/tests/MultiPolygon.txt
+++ /dev/null
@@ -1,71 +0,0 @@
-MultiPolygons
-=============
-
-Initialization
---------------
-
-  >>> from shapely.geometry import Polygon, MultiPolygon
-
-From coordinate tuples
-
-  >>> geom = MultiPolygon( [
-  ... (((0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (1.0, 0.0)), [((0.25,0.25), (0.25,0.5), (0.5,0.5), (0.5,0.25))])
-  ... ] )
-  >>> geom # doctest: +ELLIPSIS
-  <shapely.geometry.multipolygon.MultiPolygon object at ...>
-  >>> len(geom.geoms)
-  1
-  >>> geom.wkt
-  'MULTIPOLYGON (((0.0000000000000000 0.0000000000000000, 0.0000000000000000 1.0000000000000000, 1.0000000000000000 1.0000000000000000, 1.0000000000000000 0.0000000000000000, 0.0000000000000000 0.0000000000000000), (0.2500000000000000 0.2500000000000000, 0.2500000000000000 0.5000000000000000, 0.5000000000000000 0.5000000000000000, 0.5000000000000000 0.2500000000000000, 0.2500000000000000 0.2500000000000000)))'
-
-Or from polygons
-
-  >>> p = Polygon(((0, 0), (0, 1), (1, 1), (1, 0)), [((0.25,0.25), (0.25,0.5), (0.5,0.5), (0.5,0.25))])
-  >>> geom = MultiPolygon([p])
-  >>> len(geom.geoms)
-  1
-  >>> print geom.wkt
-  MULTIPOLYGON (((0.0000000000000000 0.0000000000000000, 0.0000000000000000 1.0000000000000000, 1.0000000000000000 1.0000000000000000, 1.0000000000000000 0.0000000000000000, 0.0000000000000000 0.0000000000000000), (0.2500000000000000 0.2500000000000000, 0.2500000000000000 0.5000000000000000, 0.5000000000000000 0.5000000000000000, 0.5000000000000000 0.2500000000000000, 0.2500000000000000 0.2500000000000000)))
-
-Or from another multi-polygon
-
-  >>> geom2 = MultiPolygon(geom)
-  >>> len(geom2.geoms)
-  1
-  >>> print geom2.wkt
-  MULTIPOLYGON (((0.0000000000000000 0.0000000000000000, 0.0000000000000000 1.0000000000000000, 1.0000000000000000 1.0000000000000000, 1.0000000000000000 0.0000000000000000, 0.0000000000000000 0.0000000000000000), (0.2500000000000000 0.2500000000000000, 0.2500000000000000 0.5000000000000000, 0.5000000000000000 0.5000000000000000, 0.5000000000000000 0.2500000000000000, 0.2500000000000000 0.2500000000000000)))
-
-Sub-geometry Access
--------------------
-
-  >>> geom.geoms[0] # doctest: +ELLIPSIS
-  <shapely.geometry.polygon.Polygon object at ...>
-  >>> geom.geoms[0].wkt
-  'POLYGON ((0.0000000000000000 0.0000000000000000, 0.0000000000000000 1.0000000000000000, 1.0000000000000000 1.0000000000000000, 1.0000000000000000 0.0000000000000000, 0.0000000000000000 0.0000000000000000), (0.2500000000000000 0.2500000000000000, 0.2500000000000000 0.5000000000000000, 0.5000000000000000 0.5000000000000000, 0.5000000000000000 0.2500000000000000, 0.2500000000000000 0.2500000000000000))'
-  >>> geom.geoms[1]
-  Traceback (most recent call last):
-  ...
-  IndexError: index out of range
-
-Geo interface
--------------
-
-  >>> geom.__geo_interface__
-  {'type': 'MultiPolygon', 'coordinates': [[((0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (1.0, 0.0), (0.0, 0.0)), ((0.25, 0.25), (0.25, 0.5), (0.5, 0.5), (0.5, 0.25), (0.25, 0.25))]]}
-
-Adapter
--------
-
-  >>> from shapely.geometry import asMultiPolygon
-  >>> coords = ((0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (1.0, 0.0))
-  >>> holes_coords = [((0.25,0.25), (0.25,0.5), (0.5,0.5), (0.5,0.25))]
-  >>> mpa = asMultiPolygon([(coords, holes_coords)])
-  >>> len(mpa.geoms)
-  1
-  >>> len(mpa.geoms[0].exterior.coords)
-  5
-  >>> len(mpa.geoms[0].interiors)
-  1
-  >>> len(mpa.geoms[0].interiors[0].coords)
-  5
-  
diff --git a/shapely/tests/Operations.txt b/shapely/tests/Operations.txt
deleted file mode 100644
index c41b8b7..0000000
--- a/shapely/tests/Operations.txt
+++ /dev/null
@@ -1,89 +0,0 @@
-Test GEOS operations
-====================
-
-  >>> from shapely.geometry import Point
-  >>> point = Point(0.0, 0.0)
-
-General geometry
-----------------
-
-  >>> point.area
-  0.0
-
-  >>> point.length
-  0.0
-
-  >>> point.distance(Point(-1.0, -1.0))
-  1.4142135623730951
-
-Topology operations
--------------------
-
-  Envelope
-
-  >>> point.envelope #doctest: +ELLIPSIS
-  <shapely.geometry.point.Point object at ...>
-
-  Intersection
-
-  >>> point.intersection(Point(-1,-1)) #doctest: +ELLIPSIS
-  <shapely.geometry.collection.GeometryCollection object at ...>
-  
-  Buffer
-
-  >>> point.buffer(10.0) #doctest: +ELLIPSIS
-  <shapely.geometry.polygon.Polygon object at ...>
-  
-  >>> point.buffer(10.0, 32) #doctest: +ELLIPSIS
-  <shapely.geometry.polygon.Polygon object at ...>
-
-  Simplify
-
-  >>> from shapely.wkt import loads
-  >>> p = loads('POLYGON ((120 120, 121 121, 122 122, 220 120, 180 199, 160 200, 140 199, 120 120))')
-  >>> expected = loads('POLYGON ((120 120, 140 199, 160 200, 180 199, 220 120, 120 120))')
-  >>> s = p.simplify(10.0, preserve_topology = False)
-  >>> s.equals_exact(expected, 0.001)
-  True
-
-  >>> from shapely.wkt import loads
-  >>> p = loads('POLYGON ((80 200, 240 200, 240 60, 80 60, 80 200),(120 120, 220 120, 180 199, 160 200, 140 199, 120 120))')
-  >>> expected = loads('POLYGON ((80 200, 240 200, 240 60, 80 60, 80 200),(120 120, 220 120, 180 199, 160 200, 140 199, 120 120))')
-  >>> s = p.simplify(10.0, preserve_topology = True)
-  >>> s.equals_exact(expected, 0.001)
-  True
-
-  Convex Hull
-
-  >>> point.convex_hull #doctest: +ELLIPSIS
-  <shapely.geometry.point.Point object at ...>
-  
-  Differences
-
-  >>> point.difference(Point(-1, 1)) #doctest: +ELLIPSIS
-  <shapely.geometry.point.Point object at ...>
-  
-  >>> point.symmetric_difference(Point(-1, 1)) #doctest: +ELLIPSIS
-  <shapely.geometry.multipoint.MultiPoint object at ...>
-  
-  Boundary
-
-  >>> point.boundary #doctest: +ELLIPSIS
-  <shapely.geometry.collection.GeometryCollection object at ...>
-  
-  Union
-
-  >>> point.union(Point(-1, 1)) #doctest: +ELLIPSIS
-  <shapely.geometry.multipoint.MultiPoint object at ...>
-
-  >>> point.representative_point() #doctest: +ELLIPSIS
-  <shapely.geometry.point.Point object at ...>
-
-  >>> point.centroid #doctest: +ELLIPSIS
-  <shapely.geometry.point.Point object at ...>
-
-  Relate
-
-  >>> point.relate(Point(-1, -1)) #doctest: +ELLIPSIS
-  'FF0FFF0F2'
-
diff --git a/shapely/tests/Persist.txt b/shapely/tests/Persist.txt
deleted file mode 100644
index be0b56f..0000000
--- a/shapely/tests/Persist.txt
+++ /dev/null
@@ -1,35 +0,0 @@
-Persistence tests
-=================
-
-Pickle
-------
-
-  >>> from shapely.geometry import Point
-  >>> import pickle
-  >>> p = Point(0.0, 0.0)
-  >>> data = pickle.dumps(p)
-  >>> q = pickle.loads(data)
-  >>> q.equals(p)
-  True
-
-WKB
----
-
-  >>> from shapely import wkb
-  >>> bytes = wkb.dumps(p)
-  >>> pb = wkb.loads(bytes)
-  >>> pb.equals(p)
-  True
-
-WKT
----
-
-  >>> from shapely import wkt
-  >>> text = wkt.dumps(p)
-  >>> text
-  'POINT (0.0000000000000000 0.0000000000000000)'
-  >>> pt = wkt.loads(text)
-  >>> pt.equals(p)
-  True
-
-
diff --git a/shapely/tests/Point.txt b/shapely/tests/Point.txt
deleted file mode 100644
index 7ef16cb..0000000
--- a/shapely/tests/Point.txt
+++ /dev/null
@@ -1,148 +0,0 @@
-Testing Point
-=============
-
-  >>> from shapely.geometry import Point
-
-Test 2D points
-
-  >>> p = Point(0.0, 0.0)
-  >>> str(p)
-  'POINT (0.0000000000000000 0.0000000000000000)'
-  >>> p.wkt
-  'POINT (0.0000000000000000 0.0000000000000000)'
-    
-Check 3D
-
-  >>> p = Point(0.0, 0.0, 1.0)
-  >>> p.z
-  1.0
-  >>> str(p)
-  'POINT (0.0000000000000000 0.0000000000000000)'
-  >>> p.wkt
-  'POINT (0.0000000000000000 0.0000000000000000)'
-
-Construct from a numpy array
-
-  >>> from numpy import array
-  >>> p = Point(array([1.0, 2.0]))
-  >>> p.wkt
-  'POINT (1.0000000000000000 2.0000000000000000)'
-
-From coordinate sequence
-
-  >>> p = Point((3.0, 4.0))
-  >>> p.wkt
-  'POINT (3.0000000000000000 4.0000000000000000)'
-
-From another point
-
-  >>> q = Point(p)
-  >>> print p.wkt
-  POINT (3.0000000000000000 4.0000000000000000)
-
-Coordinate access
------------------
-
-  >>> p.x
-  3.0
-  >>> p.y
-  4.0
-
-  >>> tuple(p.coords)
-  ((3.0, 4.0),)
-
-  >>> p.coords[0]
-  (3.0, 4.0)
-  >>> p.coords[1] # doctest: +ELLIPSIS
-  Traceback (most recent call last):
-  ...
-  IndexError: index out of range
-
-Bounds
-------
-
-  >>> p.bounds
-  (3.0, 4.0, 3.0, 4.0)
-
-Geo interface
--------------
-
-  >>> p.__geo_interface__
-  {'type': 'Point', 'coordinates': (3.0, 4.0)}
-
-
-Modify coordinates
-------------------
-
-  >>> p.coords = (0.0, 0.0)
-  >>> p.__geo_interface__
-  {'type': 'Point', 'coordinates': (0.0, 0.0)}
-
-Alternate method
-
-  >>> p.coords = ((0.0, 0.0),)
-  >>> p.__geo_interface__
-  {'type': 'Point', 'coordinates': (0.0, 0.0)}
-
-Adapter
--------
-
-  >>> from shapely.geometry import asPoint
-
-  Adapt a Numpy array to a point
-
-  >>> a = array([1.0, 2.0])
-  >>> pa = asPoint(a)
-  >>> pa.context
-  array([ 1.,  2.])
-  >>> pa.wkt
-  'POINT (1.0000000000000000 2.0000000000000000)'
-
-  Now, the inverse
-
-  >>> pa.__array_interface__ == pa.context.__array_interface__
-  True
-  >>> from numpy import asarray
-  >>> pas = asarray(pa)
-  >>> pas
-  array([ 1.,  2.])
-
-  Adapt a coordinate list to a point
-
-  >>> coords = [3.0, 4.0]
-  >>> pa = asPoint(coords)
-  >>> pa.wkt
-  'POINT (3.0000000000000000 4.0000000000000000)'
-  >>> pa.distance(p)
-  5.0
-  
-  Move the coordinates and watch the distance change
-
-  >>> coords[0] = 1.0
-  >>> pa.wkt
-  'POINT (1.0000000000000000 4.0000000000000000)'
-  >>> '%.10f' % pa.distance(p)
-  '4.1231056256'
-
-  Now, the inverse
-  
-  >>> ai = pa.__array_interface__
-  >>> pas = asarray(pa)
-  >>> pas
-  array([ 1.,  4.])
-
-Test Non-operability of Null geometry
-
-  >>> p_null = Point()
-  >>> p_null.wkt # doctest: +ELLIPSIS
-  'GEOMETRYCOLLECTION EMPTY'
-
-  >>> p_null.area # doctest: +ELLIPSIS
-  0.0
-
-Check that we can set coordinates of a null geometry
-
-  >>> p_null.coords = (0, 0)
-  >>> p_null.wkt
-  'POINT (0.0000000000000000 0.0000000000000000)'
-
diff --git a/shapely/tests/Polygon.txt b/shapely/tests/Polygon.txt
deleted file mode 100644
index 93721b5..0000000
--- a/shapely/tests/Polygon.txt
+++ /dev/null
@@ -1,143 +0,0 @@
-Polygons and Linear Rings
-=========================
-
-Initialization
---------------
-
-  Linear rings won't usually be created by users, but by polygons
-
-  >>> coords = ((0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (1.0, 0.0))
-  >>> from shapely.geometry.polygon import LinearRing
-  >>> ring = LinearRing(coords)
-  >>> len(ring.coords)
-  5
-  >>> ring.coords[0] == ring.coords[4]
-  True
-  >>> ring.coords[0] == ring.coords[-1]
-  True
-  >>> ring.is_ring
-  True
-
-  Coordinate modification
-  -----------------------
-
-  >>> ring.coords = ((0.0, 0.0), (0.0, 2.0), (2.0, 2.0), (2.0, 0.0))
-  >>> ring.__geo_interface__
-  {'type': 'LinearRing', 'coordinates': ((0.0, 0.0), (0.0, 2.0), (2.0, 2.0), (2.0, 0.0), (0.0, 0.0))}
-
-  Test ring adapter
-
-  >>> coords = [[0.0, 0.0], [0.0, 1.0], [1.0, 1.0], [1.0, 0.0]]
-  >>> from shapely.geometry.polygon import asLinearRing
-  >>> ra = asLinearRing(coords)
-  >>> ra.wkt
-  'LINEARRING (0.0000000000000000 0.0000000000000000, 0.0000000000000000 1.0000000000000000, 1.0000000000000000 1.0000000000000000, 1.0000000000000000 0.0000000000000000, 0.0000000000000000 0.0000000000000000)'
-  >>> coords[3] = [2.0, -1.0]
-  >>> ra.wkt
-  'LINEARRING (0.0000000000000000 0.0000000000000000, 0.0000000000000000 1.0000000000000000, 1.0000000000000000 1.0000000000000000, 2.0000000000000000 -1.0000000000000000, 0.0000000000000000 0.0000000000000000)'
-
-  Construct a polygon, exterior ring only
-
-  >>> from shapely.geometry import Polygon
-  >>> polygon = Polygon(coords)
-  >>> len(polygon.exterior.coords)
-  5
-  
-Ring Access
------------
-
-  >>> polygon.exterior # doctest: +ELLIPSIS
-  <shapely.geometry.polygon.LinearRing object at ...>
-  >>> ring = polygon.exterior
-  >>> len(ring.coords)
-  5
-  >>> ring.coords[0] == ring.coords[4] == (0., 0.)
-  True
-  >>> ring.is_ring
-  True
-  >>> len(polygon.interiors)
-  0
-
-  Create a new polygon from WKB
-
-  >>> data = polygon.wkb
-  >>> polygon = None
-  >>> ring = None
-  >>> from shapely.wkb import loads
-  >>> polygon = loads(data)
-  >>> ring = polygon.exterior
-  >>> len(ring.coords)
-  5
-  >>> ring.coords[0] == ring.coords[4] == (0., 0.)
-  True
-  >>> ring.is_ring
-  True
-  >>> polygon = None
-
-Interior rings (holes)
-----------------------
-
-  >>> polygon = Polygon(coords, [((0.25,0.25), (0.25,0.5), (0.5,0.5), (0.5,0.25))])
-  >>> len(polygon.exterior.coords)
-  5
-  >>> len(polygon.interiors[0].coords)
-  5
-  >>> polygon.interiors[1] # doctest: +ELLIPSIS
-  Traceback (most recent call last):
-  ...
-  IndexError: index out of range
-
-Coordinate getters and setters raise exceptions
-
-  >>> polygon._get_coords()
-  Traceback (most recent call last):
-  ...
-  NotImplementedError: Component rings have coordinate sequences, but the polygon does not
-  >>> polygon.coords
-  Traceback (most recent call last):
-  ...
-  NotImplementedError: Component rings have coordinate sequences, but the polygon does not
-
-
-Geo interface
--------------
-
-  >>> polygon.__geo_interface__
-  {'type': 'Polygon', 'coordinates': (((0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (2.0, -1.0), (0.0, 0.0)), ((0.25, 0.25), (0.25, 0.5), (0.5, 0.5), (0.5, 0.25), (0.25, 0.25)))}
-
-
-Adapter
--------
-
-  >>> hole_coords = [((0.25,0.25), (0.25,0.5), (0.5,0.5), (0.5,0.25))]
-  >>> from shapely.geometry import asPolygon
-  >>> pa = asPolygon(coords, hole_coords)
-  >>> len(pa.exterior.coords)
-  5
-  >>> len(pa.interiors)
-  1
-  >>> len(pa.interiors[0].coords)
-  5
-
-Test Non-operability of Null rings
-
-  >>> r_null = LinearRing()
-  >>> r_null.wkt # doctest: +ELLIPSIS
-  'GEOMETRYCOLLECTION EMPTY'
-
-  >>> r_null.length
-  0.0
-
-Check that we can set coordinates of a null geometry
-
-  >>> r_null.coords = [(0, 0), (1, 1), (1, 0)]
-  >>> print r_null.length # doctest: +ELLIPSIS
-  3.414...
-
-Error handling
---------------
-
-  >>> p = Polygon([[1,2], [2, 3]])
-  Traceback (most recent call last):
-  ...
-  ValueError: A LinearRing must have at least 3 coordinate tuples
diff --git a/shapely/tests/Predicates.txt b/shapely/tests/Predicates.txt
deleted file mode 100644
index beef7b1..0000000
--- a/shapely/tests/Predicates.txt
+++ /dev/null
@@ -1,51 +0,0 @@
-Test GEOS predicates
-====================
-
-  >>> from shapely.geometry import Point
-  >>> point = Point(0.0, 0.0)
-
-Binary Predicates
------------------
-
-  >>> point.disjoint(Point(-1.0, -1.0))
-  True
-
-  >>> point.touches(Point(-1.0, -1.0))
-  False
-
-  >>> point.crosses(Point(-1.0, -1.0))
-  False
-
-  >>> point.within(Point(-1.0, -1.0))
-  False
-
-  >>> point.contains(Point(-1.0, -1.0))
-  False
-
-  >>> point.overlaps(Point(-1.0, -1.0))
-  False
-
-  >>> point.equals(Point(-1.0, -1.0))
-  False
-
-  >>> point.equals(Point(0.0, 0.0))
-  True
-
-Unary Predicates
-----------------
-
-  >>> point.is_empty
-  False
-
-  >>> point.is_valid
-  True
-  
-  >>> point.is_simple
-  True
-
-  >>> point.is_ring
-  False
-
-  >>> point.has_z
-  False
-
diff --git a/shapely/tests/__init__.py b/shapely/tests/__init__.py
old mode 100644
new mode 100755
index 627df68..a714388
--- a/shapely/tests/__init__.py
+++ b/shapely/tests/__init__.py
@@ -1,18 +1,58 @@
-from unittest import TestSuite
+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
 
-import test_doctests, test_prepared, test_equality, test_geomseq, test_xy
-import test_collection, test_emptiness, test_singularity, test_validation
-import test_mapping, test_delegated, test_dlls, test_linear_referencing
-import test_products_z, test_box, test_speedups, test_cga, test_getitem
 
 def test_suite():
-    suite = TestSuite()
+    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())
@@ -25,5 +65,17 @@ def 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/attribute-chains.txt b/shapely/tests/attribute-chains.txt
deleted file mode 100644
index 9b7933d..0000000
--- a/shapely/tests/attribute-chains.txt
+++ /dev/null
@@ -1,40 +0,0 @@
-Attribute Chaining
-==================
-
-See also ticket #151.
-
-    >>> from shapely.geometry import Polygon
-    >>> p = Polygon(((0.0, 0.0), (0.0, 1.0), (-1.0, 1.0), (-1.0, 0.0)))
-    >>> list(p.boundary.coords)
-    [(0.0, 0.0), (0.0, 1.0), (-1.0, 1.0), (-1.0, 0.0), (0.0, 0.0)]
-
-    >>> from shapely.geometry import Point
-    >>> print list(Point(0.0, 0.0).buffer(1.0, 1).exterior.coords)
-    [(1.0, 0.0), ..., (1.0, 0.0)]
-
-Test chained access to interiors
-
-    >>> p = Polygon(
-    ...         ((0.0, 0.0), (0.0, 1.0), (-1.0, 1.0), (-1.0, 0.0)),
-    ...         [((-0.25, 0.25), (-0.25, 0.75), (-0.75, 0.75), (-0.75, 0.25))]
-    ...     )
-    >>> p.area
-    0.75
-
-Not so much testing the exact values here, which are the responsibility of the
-geometry engine (GEOS), but that we can get chain functions and properties using
-anonymous references.
-
-    >>> print list(p.interiors[0].coords)
-    [(-0.25, 0.25), (-0.25, 0.75), (-0.75, 0.75), (-0.75, 0.25), (-0.25, 0.25)]
-    >>> xy = list(p.interiors[0].buffer(1).exterior.coords)[0]
-    >>> xy[0] + 0.25 <= 1.0e-7
-    True
-    >>> xy[1] + 0.75 <= 1.0e-7
-    True
-
-Test multiple operators, boundary of a buffer
-
-    >>> print list(p.buffer(1).boundary.coords)
-    [(0.0, -1.0), (-1.0, -1.0), ...
-
diff --git a/shapely/tests/binascii_hex.txt b/shapely/tests/binascii_hex.txt
index c502a4f..19757b4 100644
--- a/shapely/tests/binascii_hex.txt
+++ b/shapely/tests/binascii_hex.txt
@@ -9,10 +9,10 @@ Point
 
   >>> from shapely.geometry import Point
   >>> point = Point(0.0, 0.0)
-  >>> import binascii
-  >>> x = binascii.b2a_hex(point.wkb)
+  >>> from binascii import a2b_hex, b2a_hex
+  >>> x = b2a_hex(point.wkb)
   >>> from shapely import wkb
-  >>> shape = wkb.loads(binascii.a2b_hex(x))
+  >>> shape = wkb.loads(a2b_hex(x))
   >>> shape # doctest: +ELLIPSIS
   <shapely.geometry.point.Point object at ...>
 
@@ -21,8 +21,8 @@ LineString
 
   >>> from shapely.geometry import LineString
   >>> line = LineString(((0.0, 0.0), (1.0, 1.0)))
-  >>> x = binascii.b2a_hex(line.wkb)
-  >>> shape = wkb.loads(binascii.a2b_hex(x))
+  >>> x = b2a_hex(line.wkb)
+  >>> shape = wkb.loads(a2b_hex(x))
   >>> shape # doctest: +ELLIPSIS
   <shapely.geometry.linestring.LineString object at ...>
 
@@ -31,8 +31,8 @@ Polygon
 
   >>> from shapely.geometry import Polygon
   >>> polygon = Polygon(((0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (1.0, 0.0)))
-  >>> x = binascii.b2a_hex(polygon.wkb)
-  >>> shape = wkb.loads(binascii.a2b_hex(x))
+  >>> x = b2a_hex(polygon.wkb)
+  >>> shape = wkb.loads(a2b_hex(x))
   >>> shape # doctest: +ELLIPSIS
   <shapely.geometry.polygon.Polygon object at ...>
 
@@ -40,8 +40,8 @@ Polygon with hole
 -----------------
 
   >>> polygon = Polygon(((0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (1.0, 0.0)), [((0.1,0.1), (0.1,0.2), (0.2,0.2), (0.2,0.1))])
-  >>> x = binascii.b2a_hex(polygon.wkb)
-  >>> shape = wkb.loads(binascii.a2b_hex(x))
+  >>> x = b2a_hex(polygon.wkb)
+  >>> shape = wkb.loads(a2b_hex(x))
   >>> shape # doctest: +ELLIPSIS
   <shapely.geometry.polygon.Polygon object at ...>
 
diff --git a/shapely/tests/cascaded_union.txt b/shapely/tests/cascaded_union.txt
deleted file mode 100644
index 7d54f31..0000000
--- a/shapely/tests/cascaded_union.txt
+++ /dev/null
@@ -1,24 +0,0 @@
-Cascaded Union
-==============
-
-  >>> from shapely.ftools import partial
-  >>> import random
-  >>> from shapely.geometry import Point
-  >>> from shapely.ops import cascaded_union
-
-Use a partial function to make 100 points uniformly distributed in a 40x40 
-box centered on 0,0.
-
-  >>> r = partial(random.uniform, -20.0, 20.0)
-  >>> points = [Point(r(), r()) for i in range(100)]
-
-Buffer the points, producing 100 polygon spots
-
-  >>> spots = [p.buffer(2.5) for p in points]
-
-Perform a cascaded union of the polygon spots, dissolving them into a 
-collection of polygon patches
-
-  >>> cascaded_union(spots) # doctest: +ELLIPSIS
-  <shapely.geometry.multipolygon.MultiPolygon object at 0x...>
-
diff --git a/shapely/tests/dimensions.txt b/shapely/tests/dimensions.txt
deleted file mode 100644
index 6b41c96..0000000
--- a/shapely/tests/dimensions.txt
+++ /dev/null
@@ -1,17 +0,0 @@
-Background: see http://trac.gispython.org/lab/ticket/168
-
-  >>> from shapely.geometry import Polygon
-  >>> coords = ((0.0, 0.0, 0.0), (0.0, 1.0, 0.0), (1.0, 1.0, 0.0), (1.0, 0.0, 0.0))
-  >>> polygon = Polygon(coords)
-  >>> polygon._ndim
-  3
-  >>> gi = polygon.__geo_interface__
-  >>> gi['coordinates']
-  (((0.0, 0.0, 0.0), (0.0, 1.0, 0.0), (1.0, 1.0, 0.0), (1.0, 0.0, 0.0), (0.0, 0.0, 0.0)),)
-
-  >>> e = polygon.exterior
-  >>> e._ndim
-  3
-  >>> gi = e.__geo_interface__
-  >>> gi['coordinates']
-  ((0.0, 0.0, 0.0), (0.0, 1.0, 0.0), (1.0, 1.0, 0.0), (1.0, 0.0, 0.0), (0.0, 0.0, 0.0))
diff --git a/shapely/tests/invalid_intersection.txt b/shapely/tests/invalid_intersection.txt
deleted file mode 100644
index 9c65d08..0000000
--- a/shapely/tests/invalid_intersection.txt
+++ /dev/null
@@ -1,29 +0,0 @@
-Test recovery from operation on invalid geometries
-==================================================
-
-  Make a self-intersecting polygon
-
-  >>> from shapely.geometry import Polygon
-  >>> polygon_invalid = Polygon(((0, 0), (1, 1), (1, -1), (0, 1), (0, 0)))
-  >>> print polygon_invalid.is_valid # doctest: +ELLIPSIS
-  False
-
-  Intersect with a valid polygon
-
-  >>> polygon = Polygon(((-.5, -.5), (-.5, .5), (.5, .5), (.5, -5)))
-  >>> polygon.is_valid
-  True
-  >>> polygon_invalid.intersects(polygon)
-  True
-  >>> result = polygon_invalid.intersection(polygon) # doctest: +ELLIPSIS
-  Traceback (most recent call last):
-  ...
-  TopologicalError: The operation 'GEOSIntersection_r' produced a null geometry. Likely cause is invalidity of the geometry <shapely.geometry.polygon.Polygon object at ...>
-
-  Check exception symmetry
-
-  >>> result = polygon.intersection(polygon_invalid) # doctest: +ELLIPSIS
-  Traceback (most recent call last):
-  ...
-  TopologicalError: The operation 'GEOSIntersection_r' produced a null geometry. Likely cause is invalidity of the 'other' geometry <shapely.geometry.polygon.Polygon object at ...>
-
diff --git a/shapely/tests/linemerge.txt b/shapely/tests/linemerge.txt
deleted file mode 100644
index 50c94af..0000000
--- a/shapely/tests/linemerge.txt
+++ /dev/null
@@ -1,61 +0,0 @@
-Linemerge 
-========= 
-
-  >>> from shapely.geometry import LineString, MultiLineString, Polygon 
-  >>> from shapely.ops import linemerge 
-    
-  >>> lines = MultiLineString([ 
-  ...     ((0, 0), (1, 1)), 
-  ...     ((2, 0), (2, 1), (1, 1)) 
-  ...     ]) 
-  >>> result = linemerge(lines)
-  >>> result # docstring: +ELLIPSIS
-  <shapely.geometry.linestring.LineString object at 0x...>
-  >>> result.is_ring 
-  False 
-  >>> len(result.coords) 
-  4 
-  >>> result.coords[0] 
-  (0.0, 0.0) 
-  >>> result.coords[3] 
-  (2.0, 0.0) 
-    
-  >>> lines2 = MultiLineString([ 
-  ...     ((0, 0), (1, 1)), 
-  ...     ((0, 0), (2, 0), (2, 1), (1, 1)) 
-  ...     ]) 
-  >>> result = linemerge(lines2) 
-  >>> result.is_ring 
-  True 
-  >>> len(result.coords) 
-  5 
-
-  >>> lines3 = [ 
-  ...     LineString(((0, 0), (1, 1))), 
-  ...     LineString(((0, 0), (0, 1))), 
-  ...     ] 
-  >>> result = linemerge(lines3) 
-  >>> result.is_ring 
-  False 
-  >>> len(result.coords) 
-  3 
-  >>> result.coords[0] 
-  (0.0, 1.0) 
-  >>> result.coords[2] 
-  (1.0, 1.0) 
-   
-  >>> lines4 = [ 
-  ...     ((0, 0), (1, 1)), 
-  ...     ((0, 0), (0, 1)), 
-  ...     ] 
-  >>> result.equals(linemerge(lines4)) 
-  True 
-   
-  >>> lines5 = [ 
-  ...     ((0, 0), (1, 1)), 
-  ...     ((1, 0), (0, 1)), 
-  ...     ] 
-  >>> result = linemerge(lines5) 
-  >>> result.type == 'MultiLineString' 
-  True
-
diff --git a/shapely/tests/polygonize.txt b/shapely/tests/polygonize.txt
deleted file mode 100644
index 50d1767..0000000
--- a/shapely/tests/polygonize.txt
+++ /dev/null
@@ -1,29 +0,0 @@
-Polygonizing
-============
-
-    >>> from shapely.geometry import LineString, Point
-    >>> from shapely.ops import polygonize
-    >>> lines = [
-    ...     LineString(((0, 0), (1, 1))),
-    ...     LineString(((0, 0), (0, 1))),
-    ...     LineString(((0, 1), (1, 1))),
-    ...     LineString(((1, 1), (1, 0))),
-    ...     LineString(((1, 0), (0, 0))),
-    ...     LineString(((5, 5), (6, 6))),
-    ...     Point(0, 0),
-    ...     ]
-    >>> result = polygonize(lines)
-    >>> list(result)
-    [<shapely.geometry.polygon.Polygon object at ...>, <shapely.geometry.polygon.Polygon object at ...>]
-
-    >>> lines2 = [
-    ...     ((0, 0), (1, 1)),
-    ...     ((0, 0), (0, 1)),
-    ...     ((0, 1), (1, 1)),
-    ...     ((1, 1), (1, 0)),
-    ...     ((1, 0), (0, 0)),
-    ...     ((5, 5), (6, 6)),
-    ...     ]
-    >>> result2 = polygonize(lines2)
-    >>> list(result2)
-    [<shapely.geometry.polygon.Polygon object at ...>, <shapely.geometry.polygon.Polygon object at ...>]
diff --git a/shapely/tests/rungrind.dist b/shapely/tests/rungrind.dist
new file mode 100644
index 0000000..80f8005
--- /dev/null
+++ b/shapely/tests/rungrind.dist
@@ -0,0 +1,4 @@
+#!/bin/sh
+#export PYTHONPATH=YOUR_CUSTOM_PATH
+valgrind --tool=memcheck --leak-check=yes --suppressions=valgrind-python.supp python test_doctests.py
+
diff --git a/shapely/tests/test_affinity.py b/shapely/tests/test_affinity.py
new file mode 100755
index 0000000..1f3b9ba
--- /dev/null
+++ b/shapely/tests/test_affinity.py
@@ -0,0 +1,251 @@
+from . import unittest
+from math import pi
+from shapely import affinity
+from shapely.wkt import loads as load_wkt
+from shapely.geometry import Point
+
+
+class AffineTestCase(unittest.TestCase):
+
+    def test_affine_params(self):
+        g = load_wkt('LINESTRING(2.4 4.1, 2.4 3, 3 3)')
+        self.assertRaises(
+            TypeError, affinity.affine_transform, g, None)
+        self.assertRaises(
+            TypeError, affinity.affine_transform, g, '123456')
+        self.assertRaises(ValueError, affinity.affine_transform, g,
+                          [1, 2, 3, 4, 5, 6, 7, 8, 9])
+        self.assertRaises(AttributeError, affinity.affine_transform, None,
+                          [1, 2, 3, 4, 5, 6])
+
+    def test_affine_geom_types(self):
+
+        # identity matrices, which should result with no transformation
+        matrix2d = (1, 0,
+                    0, 1,
+                    0, 0)
+        matrix3d = (1, 0, 0,
+                    0, 1, 0,
+                    0, 0, 1,
+                    0, 0, 0)
+
+        # empty in, empty out
+        empty2d = load_wkt('MULTIPOLYGON EMPTY')
+        self.assertTrue(affinity.affine_transform(empty2d, matrix2d).is_empty)
+
+        def test_geom(g2, g3=None):
+            self.assertFalse(g2.has_z)
+            a2 = affinity.affine_transform(g2, matrix2d)
+            self.assertFalse(a2.has_z)
+            self.assertTrue(g2.equals(a2))
+            if g3 is not None:
+                self.assertTrue(g3.has_z)
+                a3 = affinity.affine_transform(g3, matrix3d)
+                self.assertTrue(a3.has_z)
+                self.assertTrue(g3.equals(a3))
+            return
+
+        pt2d = load_wkt('POINT(12.3 45.6)')
+        pt3d = load_wkt('POINT(12.3 45.6 7.89)')
+        test_geom(pt2d, pt3d)
+        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)
+        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))'))
+        test_geom(load_wkt(
+            'MULTIPOINT ((-300 300), (700 300), (-800 -1100), (200 -300))'))
+        test_geom(load_wkt(
+            'MULTILINESTRING((0 0, -0.7 -0.7, 0.6 -1), '
+            '(-0.5 0.5, 0.7 0.6, 0 -0.6))'))
+        test_geom(load_wkt(
+            'MULTIPOLYGON(((900 4300, -1100 -400, 900 -800, 900 4300)), '
+            '((1200 4300, 2300 4400, 1900 1000, 1200 4300)))'))
+        # GeometryCollection fails, since it does not have a good constructor
+        gc = load_wkt('GEOMETRYCOLLECTION(POINT(20 70),'
+                      ' POLYGON((60 70, 13 35, 60 -30, 60 70)),'
+                      ' LINESTRING(60 70, 50 100, 80 100))')
+        self.assertRaises(TypeError, test_geom, gc)  # TODO: fix this
+
+    def test_affine_2d(self):
+        g = load_wkt('LINESTRING(2.4 4.1, 2.4 3, 3 3)')
+        # custom scale and translate
+        expected2d = load_wkt('LINESTRING(-0.2 14.35, -0.2 11.6, 1 11.6)')
+        matrix2d = (2, 0,
+                    0, 2.5,
+                    -5, 4.1)
+        a2 = affinity.affine_transform(g, matrix2d)
+        self.assertTrue(a2.almost_equals(expected2d))
+        self.assertFalse(a2.has_z)
+        # Make sure a 3D matrix does not make a 3D shape from a 2D input
+        matrix3d = (2, 0, 0,
+                    0, 2.5, 0,
+                    0, 0, 10,
+                    -5, 4.1, 100)
+        a3 = affinity.affine_transform(g, matrix3d)
+        self.assertTrue(a3.almost_equals(expected2d))
+        self.assertFalse(a3.has_z)
+
+    def test_affine_3d(self):
+        g2 = load_wkt('LINESTRING(2.4 4.1, 2.4 3, 3 3)')
+        g3 = load_wkt('LINESTRING(2.4 4.1 100.2, 2.4 3 132.8, 3 3 128.6)')
+        # custom scale and translate
+        matrix2d = (2, 0,
+                    0, 2.5,
+                    -5, 4.1)
+        matrix3d = (2, 0, 0,
+                    0, 2.5, 0,
+                    0, 0, 0.3048,
+                    -5, 4.1, 100)
+        # Combinations of 2D and 3D geometries and matrices
+        a22 = affinity.affine_transform(g2, matrix2d)
+        a23 = affinity.affine_transform(g2, matrix3d)
+        a32 = affinity.affine_transform(g3, matrix2d)
+        a33 = affinity.affine_transform(g3, matrix3d)
+        # Check dimensions
+        self.assertFalse(a22.has_z)
+        self.assertFalse(a23.has_z)
+        self.assertTrue(a32.has_z)
+        self.assertTrue(a33.has_z)
+        # 2D equality checks
+        expected2d = load_wkt('LINESTRING(-0.2 14.35, -0.2 11.6, 1 11.6)')
+        expected3d = load_wkt('LINESTRING(-0.2 14.35 130.54096, '
+                              '-0.2 11.6 140.47744, 1 11.6 139.19728)')
+        expected32 = load_wkt('LINESTRING(-0.2 14.35 100.2, '
+                              '-0.2 11.6 132.8, 1 11.6 128.6)')
+        self.assertTrue(a22.almost_equals(expected2d))
+        self.assertTrue(a23.almost_equals(expected2d))
+        # Do explicit 3D check of coordinate values
+        for a, e in zip(a32.coords, expected32.coords):
+            for ap, ep in zip(a, e):
+                self.assertAlmostEqual(ap, ep)
+        for a, e in zip(a33.coords, expected3d.coords):
+            for ap, ep in zip(a, e):
+                self.assertAlmostEqual(ap, ep)
+
+
+class TransformOpsTestCase(unittest.TestCase):
+
+    def test_rotate(self):
+        ls = load_wkt('LINESTRING(240 400, 240 300, 300 300)')
+        # counter-clockwise degrees
+        rls = affinity.rotate(ls, 90)
+        els = load_wkt('LINESTRING(220 320, 320 320, 320 380)')
+        self.assertTrue(rls.equals(els))
+        # retest with named parameters for the same result
+        rls = affinity.rotate(geom=ls, angle=90, origin='center')
+        self.assertTrue(rls.equals(els))
+        # clockwise radians
+        rls = affinity.rotate(ls, -pi/2, use_radians=True)
+        els = load_wkt('LINESTRING(320 380, 220 380, 220 320)')
+        self.assertTrue(rls.equals(els))
+        ## other `origin` parameters
+        # around the centroid
+        rls = affinity.rotate(ls, 90, origin='centroid')
+        els = load_wkt('LINESTRING(182.5 320, 282.5 320, 282.5 380)')
+        self.assertTrue(rls.equals(els))
+        # around the second coordinate tuple
+        rls = affinity.rotate(ls, 90, origin=ls.coords[1])
+        els = load_wkt('LINESTRING(140 300, 240 300, 240 360)')
+        self.assertTrue(rls.equals(els))
+        # around the absolute Point of origin
+        rls = affinity.rotate(ls, 90, origin=Point(0, 0))
+        els = load_wkt('LINESTRING(-400 240, -300 240, -300 300)')
+        self.assertTrue(rls.equals(els))
+
+    def test_scale(self):
+        ls = load_wkt('LINESTRING(240 400 10, 240 300 30, 300 300 20)')
+        # test defaults of 1.0
+        sls = affinity.scale(ls)
+        self.assertTrue(sls.equals(ls))
+        # different scaling in different dimensions
+        sls = affinity.scale(ls, 2, 3, 0.5)
+        els = load_wkt('LINESTRING(210 500 5, 210 200 15, 330 200 10)')
+        self.assertTrue(sls.equals(els))
+        # Do explicit 3D check of coordinate values
+        for a, b in zip(sls.coords, els.coords):
+            for ap, bp in zip(a, b):
+                self.assertEqual(ap, bp)
+        # retest with named parameters for the same result
+        sls = affinity.scale(geom=ls, xfact=2, yfact=3, zfact=0.5,
+                             origin='center')
+        self.assertTrue(sls.equals(els))
+        ## other `origin` parameters
+        # around the centroid
+        sls = affinity.scale(ls, 2, 3, 0.5, origin='centroid')
+        els = load_wkt('LINESTRING(228.75 537.5, 228.75 237.5, 348.75 237.5)')
+        self.assertTrue(sls.equals(els))
+        # around the second coordinate tuple
+        sls = affinity.scale(ls, 2, 3, 0.5, origin=ls.coords[1])
+        els = load_wkt('LINESTRING(240 600, 240 300, 360 300)')
+        self.assertTrue(sls.equals(els))
+        # around some other 3D Point of origin
+        sls = affinity.scale(ls, 2, 3, 0.5, origin=Point(100, 200, 1000))
+        els = load_wkt('LINESTRING(380 800 505, 380 500 515, 500 500 510)')
+        self.assertTrue(sls.equals(els))
+        # Do explicit 3D check of coordinate values
+        for a, b in zip(sls.coords, els.coords):
+            for ap, bp in zip(a, b):
+                self.assertEqual(ap, bp)
+
+    def test_skew(self):
+        ls = load_wkt('LINESTRING(240 400 10, 240 300 30, 300 300 20)')
+        # test default shear angles of 0.0
+        sls = affinity.skew(ls)
+        self.assertTrue(sls.equals(ls))
+        # different shearing in x- and y-directions
+        sls = affinity.skew(ls, 15, -30)
+        els = load_wkt('LINESTRING (253.39745962155615 417.3205080756888, '
+                       '226.60254037844385 317.3205080756888, '
+                       '286.60254037844385 282.67949192431126)')
+        self.assertTrue(sls.almost_equals(els))
+        # retest with radians for the same result
+        sls = affinity.skew(ls, pi/12, -pi/6, use_radians=True)
+        self.assertTrue(sls.almost_equals(els))
+        # retest with named parameters for the same result
+        sls = affinity.skew(geom=ls, xs=15, ys=-30,
+                            origin='center', use_radians=False)
+        self.assertTrue(sls.almost_equals(els))
+        ## other `origin` parameters
+        # around the centroid
+        sls = affinity.skew(ls, 15, -30, origin='centroid')
+        els = load_wkt('LINESTRING(258.42150697963973 406.49519052838332, '
+                       '231.6265877365273980 306.4951905283833185, '
+                       '291.6265877365274264 271.8541743770057337)')
+        self.assertTrue(sls.almost_equals(els))
+        # around the second coordinate tuple
+        sls = affinity.skew(ls, 15, -30, origin=ls.coords[1])
+        els = load_wkt('LINESTRING(266.7949192431123038 400, 240 300, '
+                       '300 265.3589838486224153)')
+        self.assertTrue(sls.almost_equals(els))
+        # around the absolute Point of origin
+        sls = affinity.skew(ls, 15, -30, origin=Point(0, 0))
+        els = load_wkt('LINESTRING(347.179676972449101 261.435935394489832, '
+                       '320.3847577293367976 161.4359353944898317, '
+                       '380.3847577293367976 126.7949192431122754)')
+        self.assertTrue(sls.almost_equals(els))
+
+    def test_translate(self):
+        ls = load_wkt('LINESTRING(240 400 10, 240 300 30, 300 300 20)')
+        # test default offset of 0.0
+        tls = affinity.translate(ls)
+        self.assertTrue(tls.equals(ls))
+        # test all offsets
+        tls = affinity.translate(ls, 100, 400, -10)
+        els = load_wkt('LINESTRING(340 800 0, 340 700 20, 400 700 10)')
+        self.assertTrue(tls.equals(els))
+        # Do explicit 3D check of coordinate values
+        for a, b in zip(tls.coords, els.coords):
+            for ap, bp in zip(a, b):
+                self.assertEqual(ap, bp)
+        # retest with named parameters for the same result
+        tls = affinity.translate(geom=ls, xoff=100, yoff=400, zoff=-10)
+        self.assertTrue(tls.equals(els))
+
+
+def test_suite():
+    loader = unittest.TestLoader()
+    return unittest.TestSuite([
+        loader.loadTestsFromTestCase(AffineTestCase),
+        loader.loadTestsFromTestCase(TransformOpsTestCase)])
diff --git a/shapely/tests/test_box.py b/shapely/tests/test_box.py
index 761c288..438d381 100644
--- a/shapely/tests/test_box.py
+++ b/shapely/tests/test_box.py
@@ -1,19 +1,24 @@
-import unittest
+from . import unittest
 from shapely import geometry
 
+
 class BoxTestCase(unittest.TestCase):
+
     def test_ccw(self):
         b = geometry.box(0, 0, 1, 1, ccw=True)
         self.assertEqual(b.exterior.coords[0], (1.0, 0.0))
         self.assertEqual(b.exterior.coords[1], (1.0, 1.0))
+
     def test_ccw_default(self):
         b = geometry.box(0, 0, 1, 1)
         self.assertEqual(b.exterior.coords[0], (1.0, 0.0))
         self.assertEqual(b.exterior.coords[1], (1.0, 1.0))
+
     def test_cw(self):
         b = geometry.box(0, 0, 1, 1, ccw=False)
         self.assertEqual(b.exterior.coords[0], (0.0, 0.0))
         self.assertEqual(b.exterior.coords[1], (0.0, 1.0))
 
+
 def test_suite():
     return unittest.TestLoader().loadTestsFromTestCase(BoxTestCase)
diff --git a/shapely/tests/test_cga.py b/shapely/tests/test_cga.py
index 87ef1c7..3ed4c71 100644
--- a/shapely/tests/test_cga.py
+++ b/shapely/tests/test_cga.py
@@ -1,33 +1,37 @@
-import unittest
+from . import unittest
 from shapely.geometry.polygon import LinearRing, orient, Polygon
 
+
 class RingOrientationTestCase(unittest.TestCase):
     def test_ccw(self):
-        ring = LinearRing([(1,0),(0,1),(0,0)])
-        self.failUnless(ring.is_ccw)
+        ring = LinearRing([(1, 0), (0, 1), (0, 0)])
+        self.assertTrue(ring.is_ccw)
+
     def test_cw(self):
-        ring = LinearRing([(0,0),(0,1),(1,0)])
-        self.failIf(ring.is_ccw)
+        ring = LinearRing([(0, 0), (0, 1), (1, 0)])
+        self.assertFalse(ring.is_ccw)
+
 
 class PolygonOrienterTestCase(unittest.TestCase):
     def test_no_holes(self):
-        ring = LinearRing([(0,0),(0,1),(1,0)])
+        ring = LinearRing([(0, 0), (0, 1), (1, 0)])
         polygon = Polygon(ring)
-        self.failIf(polygon.exterior.is_ccw)
+        self.assertFalse(polygon.exterior.is_ccw)
         polygon = orient(polygon, 1)
-        self.failUnless(polygon.exterior.is_ccw)
+        self.assertTrue(polygon.exterior.is_ccw)
+
     def test_holes(self):
-        polygon = Polygon([(0,0),(0,1),(1,0)], 
-                        [[(0.5,0.25),(0.25,0.5),(0.25,0.25)]])
-        self.failIf(polygon.exterior.is_ccw)
-        self.failUnless(polygon.interiors[0].is_ccw)
+        polygon = Polygon([(0, 0), (0, 1), (1, 0)],
+                          [[(0.5, 0.25), (0.25, 0.5), (0.25, 0.25)]])
+        self.assertFalse(polygon.exterior.is_ccw)
+        self.assertTrue(polygon.interiors[0].is_ccw)
         polygon = orient(polygon, 1)
-        self.failUnless(polygon.exterior.is_ccw)
-        self.failIf(polygon.interiors[0].is_ccw)
+        self.assertTrue(polygon.exterior.is_ccw)
+        self.assertFalse(polygon.interiors[0].is_ccw)
+
 
 def test_suite():
     loader = unittest.TestLoader()
     return unittest.TestSuite([
         loader.loadTestsFromTestCase(RingOrientationTestCase),
         loader.loadTestsFromTestCase(PolygonOrienterTestCase)])
-
diff --git a/shapely/tests/test_collection.py b/shapely/tests/test_collection.py
index 4310cc3..a892c52 100644
--- a/shapely/tests/test_collection.py
+++ b/shapely/tests/test_collection.py
@@ -1,11 +1,29 @@
-import unittest
+from . import unittest
+from shapely.geometry import LineString
 from shapely.geometry.collection import GeometryCollection
 
+
 class CollectionTestCase(unittest.TestCase):
+
     def test_array_interface(self):
         m = GeometryCollection()
-        self.failUnlessEqual(len(m), 0)
-        self.failUnlessEqual(m.geoms, [])
+        self.assertEqual(len(m), 0)
+        self.assertEqual(m.geoms, [])
+
+    def test_child_with_deleted_parent(self):
+        # test that we can remove a collection while having
+        # childs around
+        a = LineString([(0, 0), (1, 1), (1, 2), (2, 2)])
+        b = LineString([(0, 0), (1, 1), (2, 1), (2, 2)])
+        collection = a.intersection(b)
+
+        child = collection.geoms[0]
+        # delete parent of child
+        del collection
+
+        # access geometry, this should not seg fault as 1.2.15 did
+        self.assertIsNotNone(child.wkt)
+
 
 def test_suite():
     return unittest.TestLoader().loadTestsFromTestCase(CollectionTestCase)
diff --git a/shapely/tests/test_delegated.py b/shapely/tests/test_delegated.py
index a0d81f6..9d1bc5d 100644
--- a/shapely/tests/test_delegated.py
+++ b/shapely/tests/test_delegated.py
@@ -1,19 +1,26 @@
-import unittest
+from . import unittest
 from shapely.geometry import Point
 from shapely.impl import BaseImpl
 from shapely.geometry.base import delegated
 
+
 class Geometry(object):
+
     impl = BaseImpl({})
+
     @property
     @delegated
     def foo(self):
         return self.impl['foo']()
 
+
 class WrapperTestCase(unittest.TestCase):
-    """When the backend has no support for a method, we get an AttributeError"""
+    """When the backend has no support for a method, we get an AttributeError
+    """
+
     def test_delegated(self):
         self.assertRaises(AttributeError, getattr, Geometry(), 'foo')
+
     def test_defaultimpl(self):
         project_impl = Point.impl.map.pop('project', None)
         try:
@@ -22,5 +29,6 @@ class WrapperTestCase(unittest.TestCase):
             if project_impl is not None:
                 Point.impl.map['project'] = project_impl
 
+
 def test_suite():
     return unittest.TestLoader().loadTestsFromTestCase(WrapperTestCase)
diff --git a/shapely/tests/test_dlls.py b/shapely/tests/test_dlls.py
index 7a2ca16..8c82bcb 100644
--- a/shapely/tests/test_dlls.py
+++ b/shapely/tests/test_dlls.py
@@ -1,16 +1,20 @@
-import unittest
+from . import unittest
 
 from shapely.geos import load_dll
 
+
 class LoadingTestCase(unittest.TestCase):
+
     def test_load(self):
         self.assertRaises(OSError, load_dll, 'geosh_c')
+
     def test_fallbacks(self):
-        a = load_dll('geosh_c', fallbacks=[
-            '/opt/local/lib/libgeos_c.dylib', # MacPorts
-            '/usr/local/lib/libgeos_c.dylib', # homebrew (Mac OS X)
+        load_dll('geos_c', fallbacks=[
+            '/opt/local/lib/libgeos_c.dylib',  # MacPorts
+            '/usr/local/lib/libgeos_c.dylib',  # homebrew (Mac OS X)
             'libgeos_c.so.1',
             'libgeos_c.so'])
 
+
 def test_suite():
     return unittest.TestLoader().loadTestsFromTestCase(LoadingTestCase)
diff --git a/shapely/tests/test_doctests.py b/shapely/tests/test_doctests.py
index 25d5f7a..7d7124c 100644
--- a/shapely/tests/test_doctests.py
+++ b/shapely/tests/test_doctests.py
@@ -1,26 +1,27 @@
-import doctest
-import unittest
-import glob
 import os
+import doctest
+from . import unittest
+from glob import glob
 
 optionflags = (doctest.REPORT_ONLY_FIRST_FAILURE |
                doctest.NORMALIZE_WHITESPACE |
                doctest.ELLIPSIS)
 
+
 def list_doctests():
-    print __file__
-    return [filename
-            for filename
-            in glob.glob(os.path.join(os.path.dirname(__file__), '*.txt'))]
+    print(__file__)
+    source_files = glob(os.path.join(os.path.dirname(__file__), '*.txt'))
+    return [filename for filename in source_files]
+
 
 def open_file(filename, mode='r'):
     """Helper function to open files from within the tests package."""
     return open(os.path.join(os.path.dirname(__file__), filename), mode)
 
+
 def setUp(test):
-    test.globs.update(dict(
-            open_file = open_file,
-            ))
+    test.globs.update(dict(open_file=open_file,))
+
 
 def test_suite():
     return unittest.TestSuite(
diff --git a/shapely/tests/test_emptiness.py b/shapely/tests/test_emptiness.py
index 137f292..301bad1 100644
--- a/shapely/tests/test_emptiness.py
+++ b/shapely/tests/test_emptiness.py
@@ -1,19 +1,53 @@
-import unittest
+from . import unittest
 from shapely.geometry.base import BaseGeometry
-from shapely.geometry import Point
+import shapely.geometry as sgeom
+from shapely.geometry.polygon import LinearRing
+
 
 class EmptinessTestCase(unittest.TestCase):
+
     def test_empty_base(self):
         g = BaseGeometry()
-        self.failUnless(g._is_empty, True)
-    def test_empty_point(self):
-        p = Point()
-        self.failUnless(p._is_empty, True)
+        self.assertTrue(g._is_empty)
+
     def test_emptying_point(self):
-        p = Point(0, 0)
-        self.failIf(p._is_empty, False)
+        p = sgeom.Point(0, 0)
+        self.assertFalse(p._is_empty)
         p.empty()
-        self.failUnless(p._is_empty, True)
+        self.assertTrue(p._is_empty)
+
+    def test_none_geom(self):
+        p = BaseGeometry()
+        p._geom = None
+        self.assertTrue(p.is_empty)
+
+    def test_empty_point(self):
+        self.assertTrue(sgeom.Point().is_empty)
+
+    def test_empty_multipoint(self):
+        self.assertTrue(sgeom.MultiPoint().is_empty)
+
+    def test_empty_geometry_collection(self):
+        self.assertTrue(sgeom.GeometryCollection().is_empty)
+
+    def test_empty_linestring(self):
+        self.assertTrue(sgeom.LineString().is_empty)
+
+    def test_empty_multilinestring(self):
+        self.assertTrue(sgeom.MultiLineString([]).is_empty)
+
+    def test_empty_polygon(self):
+        self.assertTrue(sgeom.Polygon().is_empty)
+
+    def test_empty_multipolygon(self):
+        self.assertTrue(sgeom.MultiPolygon([]).is_empty)
+
+    def test_empty_linear_ring(self):
+        self.assertTrue(LinearRing().is_empty)
+
 
 def test_suite():
     return unittest.TestLoader().loadTestsFromTestCase(EmptinessTestCase)
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/shapely/tests/test_equality.py b/shapely/tests/test_equality.py
index 5ade663..c52d980 100644
--- a/shapely/tests/test_equality.py
+++ b/shapely/tests/test_equality.py
@@ -1,30 +1,29 @@
-import unittest
+from . import unittest
 from shapely import geometry
 
 
 class PointEqualityTestCase(unittest.TestCase):
-    
+
     def test_equals_exact(self):
         p1 = geometry.Point(1.0, 1.0)
         p2 = geometry.Point(2.0, 2.0)
-        self.failIf(p1.equals(p2))
-        self.failIf(p1.equals_exact(p2, 0.001))
+        self.assertFalse(p1.equals(p2))
+        self.assertFalse(p1.equals_exact(p2, 0.001))
 
     def test_almost_equals_default(self):
         p1 = geometry.Point(1.0, 1.0)
-        p2 = geometry.Point(1.0+1e-7, 1.0+1e-7) # almost equal to 6 places
-        p3 = geometry.Point(1.0+1e-6, 1.0+1e-6) # not almost equal
-        self.failUnless(p1.almost_equals(p2))
-        self.failIf(p1.almost_equals(p3))
+        p2 = geometry.Point(1.0+1e-7, 1.0+1e-7)  # almost equal to 6 places
+        p3 = geometry.Point(1.0+1e-6, 1.0+1e-6)  # not almost equal
+        self.assertTrue(p1.almost_equals(p2))
+        self.assertFalse(p1.almost_equals(p3))
 
     def test_almost_equals(self):
         p1 = geometry.Point(1.0, 1.0)
         p2 = geometry.Point(1.1, 1.1)
-        self.failIf(p1.equals(p2))
-        self.failUnless(p1.almost_equals(p2, 0))
-        self.failIf(p1.almost_equals(p2, 1))
+        self.assertFalse(p1.equals(p2))
+        self.assertTrue(p1.almost_equals(p2, 0))
+        self.assertFalse(p1.almost_equals(p2, 1))
+
 
 def test_suite():
-    return unittest.TestLoader().loadTestsFromTestCase(
-                                    PointEqualityTestCase
-                                    )
+    return unittest.TestLoader().loadTestsFromTestCase(PointEqualityTestCase)
diff --git a/shapely/tests/test_geointerface.py b/shapely/tests/test_geointerface.py
new file mode 100644
index 0000000..34130a3
--- /dev/null
+++ b/shapely/tests/test_geointerface.py
@@ -0,0 +1,74 @@
+from . import unittest
+from shapely.geometry import asShape
+from shapely.geometry.multipoint import MultiPointAdapter
+from shapely.geometry.linestring import LineStringAdapter
+from shapely.geometry.multilinestring import MultiLineStringAdapter
+from shapely.geometry.polygon import PolygonAdapter
+from shapely.geometry.multipolygon import MultiPolygonAdapter
+
+
+class GeoThing(object):
+    def __init__(self, d):
+        self.__geo_interface__ = d
+
+
+class GeoInterfaceTestCase(unittest.TestCase):
+
+    def test_geointerface(self):
+        # Adapt a dictionary
+        d = {"type": "Point", "coordinates": (0.0, 0.0)}
+        shape = asShape(d)
+        self.assertEqual(shape.geom_type, 'Point')
+        self.assertEqual(tuple(shape.coords), ((0.0, 0.0),))
+
+        # Adapt an object that implements the geo protocol
+        shape = None
+        thing = GeoThing({"type": "Point", "coordinates": (0.0, 0.0)})
+        shape = asShape(thing)
+        self.assertEqual(shape.geom_type, 'Point')
+        self.assertEqual(tuple(shape.coords), ((0.0, 0.0),))
+
+        # Check line string
+        shape = asShape(
+            {'type': 'LineString', 'coordinates': ((-1.0, -1.0), (1.0, 1.0))})
+        self.assertIsInstance(shape, LineStringAdapter)
+        self.assertEqual(tuple(shape.coords), ((-1.0, -1.0), (1.0, 1.0)))
+
+        # polygon
+        shape = asShape(
+            {'type': 'Polygon',
+             'coordinates':
+                (((0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (2.0, -1.0), (0.0, 0.0)),
+                 ((0.1, 0.1), (0.1, 0.2), (0.2, 0.2), (0.2, 0.1), (0.1, 0.1)))}
+        )
+        self.assertIsInstance(shape, PolygonAdapter)
+        self.assertEqual(
+            tuple(shape.exterior.coords),
+            ((0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (2.0, -1.0), (0.0, 0.0)))
+        self.assertEqual(len(shape.interiors), 1)
+
+        # multi point
+        shape = asShape({'type': 'MultiPoint',
+                         'coordinates': ((1.0, 2.0), (3.0, 4.0))})
+        self.assertIsInstance(shape, MultiPointAdapter)
+        self.assertEqual(len(shape.geoms), 2)
+
+        # multi line string
+        shape = asShape({'type': 'MultiLineString',
+                         'coordinates': (((0.0, 0.0), (1.0, 2.0)),)})
+        self.assertIsInstance(shape, MultiLineStringAdapter)
+        self.assertEqual(len(shape.geoms), 1)
+
+        # multi polygon
+        shape = asShape(
+            {'type': 'MultiPolygon',
+             'coordinates':
+                [(((0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (1.0, 0.0), (0.0, 0.0)),
+                  ((0.1, 0.1), (0.1, 0.2), (0.2, 0.2), (0.2, 0.1), (0.1, 0.1))
+                  )]})
+        self.assertIsInstance(shape, MultiPolygonAdapter)
+        self.assertEqual(len(shape.geoms), 1)
+
+
+def test_suite():
+    return unittest.TestLoader().loadTestsFromTestCase(GeoInterfaceTestCase)
diff --git a/shapely/tests/test_geomseq.py b/shapely/tests/test_geomseq.py
index f384e62..95132fb 100644
--- a/shapely/tests/test_geomseq.py
+++ b/shapely/tests/test_geomseq.py
@@ -1,11 +1,13 @@
-import unittest
+from . import unittest
 from shapely import geometry
 
+
 class MultiLineTestCase(unittest.TestCase):
     def test_array_interface(self):
         m = geometry.MultiLineString([((0, 0), (1, 1)), ((2, 2), (3, 3))])
         ai = m.geoms[0].__array_interface__
-        self.failUnlessEqual(ai['shape'], (2, 2))
+        self.assertEqual(ai['shape'], (2, 2))
+
 
 def test_suite():
     return unittest.TestLoader().loadTestsFromTestCase(MultiLineTestCase)
diff --git a/shapely/tests/test_getitem.py b/shapely/tests/test_getitem.py
index 382156c..cd31770 100644
--- a/shapely/tests/test_getitem.py
+++ b/shapely/tests/test_getitem.py
@@ -1,11 +1,13 @@
-import unittest
+from . import unittest
 from shapely import geometry
 
+
 class CoordsGetItemTestCase(unittest.TestCase):
+
     def test_index_2d_coords(self):
         c = [(float(x), float(-x)) for x in range(4)]
         g = geometry.LineString(c)
-        for i in range(-4,4):
+        for i in range(-4, 4):
             self.assertTrue(g.coords[i] == c[i])
         self.assertRaises(IndexError, lambda: g.coords[4])
         self.assertRaises(IndexError, lambda: g.coords[-5])
@@ -13,13 +15,13 @@ class CoordsGetItemTestCase(unittest.TestCase):
     def test_index_3d_coords(self):
         c = [(float(x), float(-x), float(x*2)) for x in range(4)]
         g = geometry.LineString(c)
-        for i in range(-4,4):
+        for i in range(-4, 4):
             self.assertTrue(g.coords[i] == c[i])
         self.assertRaises(IndexError, lambda: g.coords[4])
         self.assertRaises(IndexError, lambda: g.coords[-5])
 
     def test_index_coords_misc(self):
-        g = geometry.LineString() # empty
+        g = geometry.LineString()  # empty
         self.assertRaises(IndexError, lambda: g.coords[0])
         self.assertRaises(TypeError, lambda: g.coords[0.0])
 
@@ -43,31 +45,35 @@ class CoordsGetItemTestCase(unittest.TestCase):
         self.assertTrue(g.coords[:4] == c[:4])
         self.assertTrue(g.coords[4:] == c[4:] == [])
 
+
 class MultiGeomGetItemTestCase(unittest.TestCase):
+
     def test_index_multigeom(self):
         c = [(float(x), float(-x)) for x in range(4)]
         g = geometry.MultiPoint(c)
-        for i in range(-4,4):
+        for i in range(-4, 4):
             self.assertTrue(g[i].equals(geometry.Point(c[i])))
         self.assertRaises(IndexError, lambda: g[4])
         self.assertRaises(IndexError, lambda: g[-5])
 
     def test_index_multigeom_misc(self):
-        g = geometry.MultiLineString() # empty
+        g = geometry.MultiLineString()  # empty
         self.assertRaises(IndexError, lambda: g[0])
         self.assertRaises(TypeError, lambda: g[0.0])
 
     def test_slice_multigeom(self):
         c = [(float(x), float(-x)) for x in range(4)]
         g = geometry.MultiPoint(c)
-        self.failUnlessEqual(type(g[:]), type(g))
-        self.failUnlessEqual(len(g[:]), len(g))
-        self.failUnless(g[1:].equals(geometry.MultiPoint(c[1:])))
-        self.failUnless(g[:-1].equals(geometry.MultiPoint(c[:-1])))
-        self.failUnless(g[::-1].equals(geometry.MultiPoint(c[::-1])))
-        self.failUnless(g[4:].is_empty)
+        self.assertEqual(type(g[:]), type(g))
+        self.assertEqual(len(g[:]), len(g))
+        self.assertTrue(g[1:].equals(geometry.MultiPoint(c[1:])))
+        self.assertTrue(g[:-1].equals(geometry.MultiPoint(c[:-1])))
+        self.assertTrue(g[::-1].equals(geometry.MultiPoint(c[::-1])))
+        self.assertTrue(g[4:].is_empty)
+
 
 class LinearRingGetItemTestCase(unittest.TestCase):
+
     def test_index_linearring(self):
         shell = geometry.polygon.LinearRing([(0.0, 0.0), (70.0, 120.0),
                                              (140.0, 0.0), (0.0, 0.0)])
@@ -78,13 +84,13 @@ class LinearRingGetItemTestCase(unittest.TestCase):
                  geometry.polygon.LinearRing([(90.0, 10), (110.0, 10.0),
                                               (100.0, 30.0), (90.0, 10.0)])]
         g = geometry.Polygon(shell, holes)
-        for i in range(-3,3):
+        for i in range(-3, 3):
             self.assertTrue(g.interiors[i].equals(holes[i]))
         self.assertRaises(IndexError, lambda: g.interiors[3])
         self.assertRaises(IndexError, lambda: g.interiors[-4])
 
     def test_index_linearring_misc(self):
-        g = geometry.Polygon() # empty
+        g = geometry.Polygon()  # empty
         self.assertRaises(IndexError, lambda: g.interiors[0])
         self.assertRaises(TypeError, lambda: g.interiors[0.0])
 
@@ -98,16 +104,22 @@ class LinearRingGetItemTestCase(unittest.TestCase):
                  geometry.polygon.LinearRing([(90.0, 10), (110.0, 10.0),
                                               (100.0, 30.0), (90.0, 10.0)])]
         g = geometry.Polygon(shell, holes)
-        self.assertTrue(all([a.equals(b) for (a, b) in zip(g.interiors[1:], holes[1:])]))
-        self.assertTrue(all([a.equals(b) for (a, b) in zip(g.interiors[:-1], holes[:-1])]))
-        self.assertTrue(all([a.equals(b) for (a, b) in zip(g.interiors[::-1], holes[::-1])]))
-        self.assertTrue(all([a.equals(b) for (a, b) in zip(g.interiors[::2], holes[::2])]))
-        self.assertTrue(all([a.equals(b) for (a, b) in zip(g.interiors[:3], holes[:3])]))
+        t = [a.equals(b) for (a, b) in zip(g.interiors[1:], holes[1:])]
+        self.assertTrue(all(t))
+        t = [a.equals(b) for (a, b) in zip(g.interiors[:-1], holes[:-1])]
+        self.assertTrue(all(t))
+        t = [a.equals(b) for (a, b) in zip(g.interiors[::-1], holes[::-1])]
+        self.assertTrue(all(t))
+        t = [a.equals(b) for (a, b) in zip(g.interiors[::2], holes[::2])]
+        self.assertTrue(all(t))
+        t = [a.equals(b) for (a, b) in zip(g.interiors[:3], holes[:3])]
+        self.assertTrue(all(t))
         self.assertTrue(g.interiors[3:] == holes[3:] == [])
 
+
 def test_suite():
     loader = unittest.TestLoader()
     return unittest.TestSuite([
-        unittest.TestLoader().loadTestsFromTestCase(CoordsGetItemTestCase),
-        unittest.TestLoader().loadTestsFromTestCase(MultiGeomGetItemTestCase),
-        unittest.TestLoader().loadTestsFromTestCase(LinearRingGetItemTestCase)])
+        loader.loadTestsFromTestCase(CoordsGetItemTestCase),
+        loader.loadTestsFromTestCase(MultiGeomGetItemTestCase),
+        loader.loadTestsFromTestCase(LinearRingGetItemTestCase)])
diff --git a/shapely/tests/test_invalid_geometries.py b/shapely/tests/test_invalid_geometries.py
new file mode 100644
index 0000000..3dfa3c0
--- /dev/null
+++ b/shapely/tests/test_invalid_geometries.py
@@ -0,0 +1,30 @@
+'''Test recovery from operation on invalid geometries
+'''
+
+from . import unittest
+from shapely.geometry import Polygon
+from shapely.topology import TopologicalError
+
+
+class InvalidGeometriesTestCase(unittest.TestCase):
+
+    def test_invalid_intersection(self):
+        # Make a self-intersecting polygon
+        polygon_invalid = Polygon(((0, 0), (1, 1), (1, -1), (0, 1), (0, 0)))
+        self.assertFalse(polygon_invalid.is_valid)
+
+        # Intersect with a valid polygon
+        polygon = Polygon(((-.5, -.5), (-.5, .5), (.5, .5), (.5, -5)))
+        self.assertTrue(polygon.is_valid)
+        self.assertTrue(polygon_invalid.intersects(polygon))
+        self.assertRaises(TopologicalError,
+                          polygon_invalid.intersection, polygon)
+        self.assertRaises(TopologicalError,
+                          polygon.intersection, polygon_invalid)
+        return
+
+
+def test_suite():
+    loader = unittest.TestLoader()
+    return unittest.TestSuite([
+        loader.loadTestsFromTestCase(InvalidGeometriesTestCase)])
diff --git a/shapely/tests/test_iterops.py b/shapely/tests/test_iterops.py
new file mode 100644
index 0000000..fd73ccd
--- /dev/null
+++ b/shapely/tests/test_iterops.py
@@ -0,0 +1,47 @@
+"""Test operator iterations
+"""
+from . import unittest
+from shapely import iterops
+from shapely.geometry import Point, Polygon
+
+
+class IterOpsTestCase(unittest.TestCase):
+
+    def test_iterops(self):
+
+        coords = ((0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (1.0, 0.0), (0.0, 0.0))
+        polygon = Polygon(coords)
+        points = [Point(0.5, 0.5), Point(2.0, 2.0)]
+
+        # List of the points contained by the polygon
+        self.assertTrue(
+            all([isinstance(x, Point)
+                 for x in iterops.contains(polygon, points, True)]))
+
+        # 'True' is the default value
+        self.assertTrue(
+            all([isinstance(x, Point)
+                 for x in iterops.contains(polygon, points)]))
+
+        # Test a false value
+        self.assertTrue(
+            all([isinstance(x, Point)
+                 for x in iterops.contains(polygon, points, False)]))
+
+        # If the provided iterator yields tuples, the second value will be
+        # yielded
+        self.assertEqual(
+            list(iterops.contains(polygon, [(p, p.coords[:])
+                 for p in points], False)),
+            [[(2.0, 2.0)]])
+
+        # Just to demonstrate that the important thing is that the second
+        # parameter is an iterator:
+        self.assertEqual(
+            list(iterops.contains(polygon, iter((p, p.coords[:])
+                 for p in points))),
+            [[(0.5, 0.5)]])
+
+
+def test_suite():
+    return unittest.TestLoader().loadTestsFromTestCase(IterOpsTestCase)
diff --git a/shapely/tests/test_linear_referencing.py b/shapely/tests/test_linear_referencing.py
index af787a3..e1da7c5 100644
--- a/shapely/tests/test_linear_referencing.py
+++ b/shapely/tests/test_linear_referencing.py
@@ -1,67 +1,72 @@
-import unittest
+from . import unittest
+from shapely.geos import geos_version
 from shapely.geometry import Point, LineString, MultiLineString
 
+
 class LinearReferencingTestCase(unittest.TestCase):
+
     def setUp(self):
         self.point = Point(1, 1)
         self.line1 = LineString(([0, 0], [2, 0]))
         self.line2 = LineString(([3, 0], [3, 6]))
         self.multiline = MultiLineString([
             list(self.line1.coords), list(self.line2.coords)
-        ]) 
+        ])
 
+    @unittest.skipIf(geos_version < (3, 2, 0), 'GEOS 3.2.0 required')
     def test_line1_project(self):
         self.assertEqual(self.line1.project(self.point), 1.0)
         self.assertEqual(self.line1.project(self.point, normalized=True), 0.5)
 
+    @unittest.skipIf(geos_version < (3, 2, 0), 'GEOS 3.2.0 required')
     def test_line2_project(self):
         self.assertEqual(self.line2.project(self.point), 1.0)
-        self.assertAlmostEqual(self.line2.project(self.point, normalized=True),
-            0.16666666666, 8)
+        self.assertAlmostEqual(
+            self.line2.project(self.point, normalized=True), 0.16666666666, 8)
 
+    @unittest.skipIf(geos_version < (3, 2, 0), 'GEOS 3.2.0 required')
     def test_multiline_project(self):
         self.assertEqual(self.multiline.project(self.point), 1.0)
-        self.assertEqual(self.multiline.project(self.point, normalized=True),
-            0.125)
+        self.assertEqual(
+            self.multiline.project(self.point, normalized=True), 0.125)
 
+    @unittest.skipIf(geos_version < (3, 2, 0), 'GEOS 3.2.0 required')
     def test_not_supported_project(self):
-        self.assertRaises(TypeError, self.point.buffer(1.0).project,
-            self.point)
+        with self.assertRaises(TypeError):
+            self.point.buffer(1.0).project(self.point)
 
+    @unittest.skipIf(geos_version < (3, 2, 0), 'GEOS 3.2.0 required')
     def test_not_on_line_project(self):
         # Points that aren't on the line project to 0.
-        self.assertEqual(self.line1.project(Point(-10,-10)), 0.0)
+        self.assertEqual(self.line1.project(Point(-10, -10)), 0.0)
 
+    @unittest.skipIf(geos_version < (3, 2, 0), 'GEOS 3.2.0 required')
     def test_line1_interpolate(self):
-        self.failUnless(self.line1.interpolate(0.5).equals(Point(0.5, 0.0)))
-        self.failUnless(
-            self.line1.interpolate(0.5, normalized=True).equals(
-                Point(1.0, 0.0)))
+        self.assertTrue(self.line1.interpolate(0.5).equals(Point(0.5, 0.0)))
+        self.assertTrue(
+            self.line1.interpolate(0.5, normalized=True).equals(Point(1, 0)))
 
+    @unittest.skipIf(geos_version < (3, 2, 0), 'GEOS 3.2.0 required')
     def test_line2_interpolate(self):
-        self.failUnless(self.line2.interpolate(0.5).equals(Point(3.0, 0.5)))
-        self.failUnless(
-            self.line2.interpolate(0.5, normalized=True).equals(
-                Point(3.0, 3.0)))
+        self.assertTrue(self.line2.interpolate(0.5).equals(Point(3.0, 0.5)))
+        self.assertTrue(
+            self.line2.interpolate(0.5, normalized=True).equals(Point(3, 3)))
 
+    @unittest.skipIf(geos_version < (3, 2, 0), 'GEOS 3.2.0 required')
     def test_multiline_interpolate(self):
-        self.failUnless(self.multiline.interpolate(0.5).equals(
-            Point(0.5, 0.0)))
-        self.failUnless(
+        self.assertTrue(self.multiline.interpolate(0.5).equals(Point(0.5, 0)))
+        self.assertTrue(
             self.multiline.interpolate(0.5, normalized=True).equals(
                 Point(3.0, 2.0)))
 
+    @unittest.skipIf(geos_version < (3, 2, 0), 'GEOS 3.2.0 required')
     def test_line_ends_interpolate(self):
         # Distances greater than length of the line or less than
         # zero yield the line's ends.
-        self.failUnless(self.line1.interpolate(-1000).equals(Point(0.0, 0.0)))
-        self.failUnless(self.line1.interpolate(1000).equals(Point(2.0, 0.0)))
+        self.assertTrue(self.line1.interpolate(-1000).equals(Point(0.0, 0.0)))
+        self.assertTrue(self.line1.interpolate(1000).equals(Point(2.0, 0.0)))
+
 
 def test_suite():
-    try:
-        LineString(([0, 0], [2, 0])).project(Point(0, 0))
-    except AttributeError:
-        return lambda x: None
-    return unittest.TestLoader().loadTestsFromTestCase(
-                                  LinearReferencingTestCase
-                                  )
+    loader = unittest.TestLoader()
+    return loader.loadTestsFromTestCase(LinearReferencingTestCase)
diff --git a/shapely/tests/test_linemerge.py b/shapely/tests/test_linemerge.py
new file mode 100644
index 0000000..bd0f37d
--- /dev/null
+++ b/shapely/tests/test_linemerge.py
@@ -0,0 +1,52 @@
+from . import unittest
+from shapely.geometry import LineString, MultiLineString
+from shapely.ops import linemerge
+
+
+class LineMergeTestCase(unittest.TestCase):
+
+    def test_linemerge(self):
+
+        lines = MultiLineString(
+            [((0, 0), (1, 1)),
+             ((2, 0), (2, 1), (1, 1))])
+        result = linemerge(lines)
+        self.assertIsInstance(result, LineString)
+        self.assertFalse(result.is_ring)
+        self.assertEqual(len(result.coords), 4)
+        self.assertEqual(result.coords[0], (0.0, 0.0))
+        self.assertEqual(result.coords[3], (2.0, 0.0))
+
+        lines2 = MultiLineString(
+            [((0, 0), (1, 1)),
+             ((0, 0), (2, 0), (2, 1), (1, 1))])
+        result = linemerge(lines2)
+        self.assertTrue(result.is_ring)
+        self.assertEqual(len(result.coords), 5)
+
+        lines3 = [
+            LineString(((0, 0), (1, 1))),
+            LineString(((0, 0), (0, 1))),
+        ]
+        result = linemerge(lines3)
+        self.assertFalse(result.is_ring)
+        self.assertEqual(len(result.coords), 3)
+        self.assertEqual(result.coords[0], (0.0, 1.0))
+        self.assertEqual(result.coords[2], (1.0, 1.0))
+
+        lines4 = [
+            ((0, 0), (1, 1)),
+            ((0, 0), (0, 1)),
+        ]
+        self.assertTrue(result.equals(linemerge(lines4)))
+
+        lines5 = [
+            ((0, 0), (1, 1)),
+            ((1, 0), (0, 1)),
+        ]
+        result = linemerge(lines5)
+        self.assertEqual(result.type, 'MultiLineString')
+
+
+def test_suite():
+    return unittest.TestLoader().loadTestsFromTestCase(LineMergeTestCase)
diff --git a/shapely/tests/test_linestring.py b/shapely/tests/test_linestring.py
new file mode 100644
index 0000000..ab9fc47
--- /dev/null
+++ b/shapely/tests/test_linestring.py
@@ -0,0 +1,101 @@
+from . import unittest, numpy
+from shapely.geometry import LineString, asLineString
+
+
+class LineStringTestCase(unittest.TestCase):
+
+    def test_linestring(self):
+
+        # From coordinate tuples
+        line = LineString(((1.0, 2.0), (3.0, 4.0)))
+        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)])
+
+        # Bounds
+        self.assertEqual(line.bounds, (1.0, 2.0, 3.0, 4.0))
+
+        # Coordinate access
+        self.assertEqual(tuple(line.coords), ((1.0, 2.0), (3.0, 4.0)))
+        self.assertEqual(line.coords[0], (1.0, 2.0))
+        self.assertEqual(line.coords[1], (3.0, 4.0))
+        with self.assertRaises(IndexError):
+            line.coords[2]  # index out of range
+
+        # Geo interface
+        self.assertEqual(line.__geo_interface__,
+                         {'type': 'LineString',
+                          'coordinates': ((1.0, 2.0), (3.0, 4.0))})
+
+        # Coordinate modification
+        line.coords = ((-1.0, -1.0), (1.0, 1.0))
+        self.assertEqual(line.__geo_interface__,
+                         {'type': 'LineString',
+                          'coordinates': ((-1.0, -1.0), (1.0, 1.0))})
+
+        # Adapt a coordinate list to a line string
+        coords = [[5.0, 6.0], [7.0, 8.0]]
+        la = asLineString(coords)
+        self.assertEqual(la.coords[:], [(5.0, 6.0), (7.0, 8.0)])
+
+        # Test Non-operability of Null geometry
+        l_null = LineString()
+        self.assertEqual(l_null.wkt, 'GEOMETRYCOLLECTION EMPTY')
+        self.assertEqual(l_null.length, 0.0)
+
+        # Check that we can set coordinates of a null geometry
+        l_null.coords = [(0, 0), (1, 1)]
+        self.assertAlmostEqual(l_null.length, 1.4142135623730951)
+
+    @unittest.skipIf(not numpy, 'Numpy required')
+    def test_numpy(self):
+
+        from numpy import array, asarray
+        from numpy.testing import assert_array_equal
+
+        # Construct from a numpy array
+        line = LineString(array([[0.0, 0.0], [1.0, 2.0]]))
+        self.assertEqual(len(line.coords), 2)
+        self.assertEqual(line.coords[:], [(0.0, 0.0), (1.0, 2.0)])
+
+        line = LineString(((1.0, 2.0), (3.0, 4.0)))
+        la = asarray(line)
+        expected = array([[1.0, 2.0], [3.0, 4.0]])
+        assert_array_equal(la, expected)
+
+        # Coordinate sequences can be adapted as well
+        la = asarray(line.coords)
+        assert_array_equal(la, expected)
+
+        # Adapt a Numpy array to a line string
+        a = array([[1.0, 2.0], [3.0, 4.0]])
+        la = asLineString(a)
+        assert_array_equal(la.context, a)
+        self.assertEqual(la.coords[:], [(1.0, 2.0), (3.0, 4.0)])
+
+        # Now, the inverse
+        self.assertEqual(la.__array_interface__,
+                         la.context.__array_interface__)
+
+        pas = asarray(la)
+        assert_array_equal(pas, array([[1.0, 2.0], [3.0, 4.0]]))
+
+        # From Array.txt
+        a = asarray([[0.0, 0.0], [2.0, 2.0], [1.0, 1.0]])
+        line = LineString(a)
+        self.assertEqual(line.coords[:], [(0.0, 0.0), (2.0, 2.0), (1.0, 1.0)])
+
+        data = line.ctypes
+        self.assertEqual(data[0], 0.0)
+        self.assertEqual(data[5], 1.0)
+
+        b = asarray(line)
+        assert_array_equal(b, array([[0., 0.], [2., 2.], [1., 1.]]))
+
+
+def test_suite():
+    return unittest.TestLoader().loadTestsFromTestCase(LineStringTestCase)
diff --git a/shapely/tests/test_locale.py b/shapely/tests/test_locale.py
new file mode 100644
index 0000000..2041c2c
--- /dev/null
+++ b/shapely/tests/test_locale.py
@@ -0,0 +1,56 @@
+'''Test locale independence of WKT
+'''
+from . import unittest
+import sys
+import locale
+from shapely.wkt import loads, dumps
+
+# Set locale to one that uses a comma as decimal seperator
+# TODO: try a few other common locales
+if sys.platform == 'win32':
+    test_locales = {
+        'Portuguese': 'portuguese_brazil',
+    }
+else:
+    test_locales = {
+        'Portuguese': 'pt_BR.UTF-8',
+    }
+
+do_test_locale = False
+
+
+def setUpModule():
+    global do_test_locale
+    for name in test_locales:
+        try:
+            test_locale = test_locales[name]
+            locale.setlocale(locale.LC_ALL, test_locale)
+            do_test_locale = True
+            break
+        except:
+            pass
+    if not do_test_locale:
+        raise unittest.SkipTest('test locale not found')
+
+
+def tearDownModule():
+    locale.resetlocale()
+
+
+class LocaleTestCase(unittest.TestCase):
+
+    #@unittest.skipIf(not do_test_locale, 'test locale not found')
+
+    def test_wkt_locale(self):
+
+        # Test reading and writing
+        p = loads('POINT (0.0 0.0)')
+        self.assertEqual(p.x, 0.0)
+        self.assertEqual(p.y, 0.0)
+        wkt = dumps(p)
+        self.assertTrue(wkt.startswith('POINT'))
+        self.assertFalse(',' in wkt)
+
+
+def test_suite():
+    return unittest.TestLoader().loadTestsFromTestCase(LocaleTestCase)
diff --git a/shapely/tests/test_mapping.py b/shapely/tests/test_mapping.py
index e21b407..18cb287 100644
--- a/shapely/tests/test_mapping.py
+++ b/shapely/tests/test_mapping.py
@@ -1,11 +1,13 @@
-import unittest
+from . import unittest
 from shapely.geometry import Point, mapping
 
+
 class MappingTestCase(unittest.TestCase):
     def test_point(self):
         m = mapping(Point(0, 0))
-        self.failUnlessEqual(m['type'], 'Point')
-        self.failUnlessEqual(m['coordinates'], (0.0, 0.0))
+        self.assertEqual(m['type'], 'Point')
+        self.assertEqual(m['coordinates'], (0.0, 0.0))
+
 
 def test_suite():
     return unittest.TestLoader().loadTestsFromTestCase(MappingTestCase)
diff --git a/shapely/tests/test_multilinestring.py b/shapely/tests/test_multilinestring.py
new file mode 100644
index 0000000..f4930b9
--- /dev/null
+++ b/shapely/tests/test_multilinestring.py
@@ -0,0 +1,61 @@
+from . import unittest, numpy
+from shapely.geometry import LineString, MultiLineString, asMultiLineString
+from shapely.geometry.base import dump_coords
+
+
+class MultiLineStringTestCase(unittest.TestCase):
+
+    def test_multipoint(self):
+
+        # From coordinate tuples
+        geom = MultiLineString((((1.0, 2.0), (3.0, 4.0)),))
+        self.assertIsInstance(geom, MultiLineString)
+        self.assertEqual(len(geom.geoms), 1)
+        self.assertEqual(dump_coords(geom), [[(1.0, 2.0), (3.0, 4.0)]])
+
+        # From lines
+        a = LineString(((1.0, 2.0), (3.0, 4.0)))
+        ml = MultiLineString([a])
+        self.assertEqual(len(ml.geoms), 1)
+        self.assertEqual(dump_coords(ml), [[(1.0, 2.0), (3.0, 4.0)]])
+
+        # From another multi-line
+        ml2 = MultiLineString(ml)
+        self.assertEqual(len(ml2.geoms), 1)
+        self.assertEqual(dump_coords(ml2), [[(1.0, 2.0), (3.0, 4.0)]])
+
+        # Sub-geometry Access
+        geom = MultiLineString([(((0.0, 0.0), (1.0, 2.0)))])
+        self.assertIsInstance(geom[0], LineString)
+        self.assertEqual(dump_coords(geom[0]), [(0.0, 0.0), (1.0, 2.0)])
+        with self.assertRaises(IndexError):  # index out of range
+            geom.geoms[1]
+
+        # Geo interface
+        self.assertEqual(geom.__geo_interface__,
+                         {'type': 'MultiLineString',
+                          'coordinates': (((0.0, 0.0), (1.0, 2.0)),)})
+
+    @unittest.skipIf(not numpy, 'Numpy required')
+    def test_numpy(self):
+
+        from numpy import array
+        from numpy.testing import assert_array_equal
+
+        # Construct from a numpy array
+        geom = MultiLineString([array(((0.0, 0.0), (1.0, 2.0)))])
+        self.assertIsInstance(geom, MultiLineString)
+        self.assertEqual(len(geom.geoms), 1)
+        self.assertEqual(dump_coords(geom), [[(0.0, 0.0), (1.0, 2.0)]])
+
+        # Adapt a sequence of Numpy arrays to a multilinestring
+        a = [array(((1.0, 2.0), (3.0, 4.0)))]
+        geoma = asMultiLineString(a)
+        assert_array_equal(geoma.context, [array([[1., 2.], [3., 4.]])])
+        self.assertEqual(dump_coords(geoma), [[(1.0, 2.0), (3.0, 4.0)]])
+
+        # TODO: is there an inverse?
+
+
+def test_suite():
+    return unittest.TestLoader().loadTestsFromTestCase(MultiLineStringTestCase)
diff --git a/shapely/tests/test_multipoint.py b/shapely/tests/test_multipoint.py
new file mode 100644
index 0000000..013ba0d
--- /dev/null
+++ b/shapely/tests/test_multipoint.py
@@ -0,0 +1,73 @@
+from . import unittest, numpy
+from shapely.geometry import Point, MultiPoint, asMultiPoint
+from shapely.geometry.base import dump_coords
+
+
+class MultiPointTestCase(unittest.TestCase):
+
+    def test_multipoint(self):
+
+        # From coordinate tuples
+        geom = MultiPoint(((1.0, 2.0), (3.0, 4.0)))
+        self.assertEqual(len(geom.geoms), 2)
+        self.assertEqual(dump_coords(geom), [[(1.0, 2.0)], [(3.0, 4.0)]])
+
+        # From points
+        geom = MultiPoint((Point(1.0, 2.0), Point(3.0, 4.0)))
+        self.assertEqual(len(geom.geoms), 2)
+        self.assertEqual(dump_coords(geom), [[(1.0, 2.0)], [(3.0, 4.0)]])
+
+        # From another multi-point
+        geom2 = MultiPoint(geom)
+        self.assertEqual(len(geom2.geoms), 2)
+        self.assertEqual(dump_coords(geom2), [[(1.0, 2.0)], [(3.0, 4.0)]])
+
+        # Sub-geometry Access
+        self.assertIsInstance(geom.geoms[0], Point)
+        self.assertEqual(geom.geoms[0].x, 1.0)
+        self.assertEqual(geom.geoms[0].y, 2.0)
+        with self.assertRaises(IndexError):  # index out of range
+            geom.geoms[2]
+
+        # Geo interface
+        self.assertEqual(geom.__geo_interface__,
+                         {'type': 'MultiPoint',
+                          'coordinates': ((1.0, 2.0), (3.0, 4.0))})
+
+        # Adapt a coordinate list to a line string
+        coords = [[5.0, 6.0], [7.0, 8.0]]
+        geoma = asMultiPoint(coords)
+        self.assertEqual(dump_coords(geoma), [[(5.0, 6.0)], [(7.0, 8.0)]])
+
+    @unittest.skipIf(not numpy, 'Numpy required')
+    def test_numpy(self):
+
+        from numpy import array, asarray
+        from numpy.testing import assert_array_equal
+
+        # Construct from a numpy array
+        geom = MultiPoint(array([[0.0, 0.0], [1.0, 2.0]]))
+        self.assertIsInstance(geom, MultiPoint)
+        self.assertEqual(len(geom.geoms), 2)
+        self.assertEqual(dump_coords(geom), [[(0.0, 0.0)], [(1.0, 2.0)]])
+
+        # Geo interface (cont.)
+        geom = MultiPoint((Point(1.0, 2.0), Point(3.0, 4.0)))
+        assert_array_equal(array(geom), array([[1., 2.], [3., 4.]]))
+
+        # Adapt a Numpy array to a multipoint
+        a = array([[1.0, 2.0], [3.0, 4.0]])
+        geoma = asMultiPoint(a)
+        assert_array_equal(geoma.context, array([[1., 2.], [3., 4.]]))
+        self.assertEqual(dump_coords(geoma), [[(1.0, 2.0)], [(3.0, 4.0)]])
+
+        # Now, the inverse
+        self.assertEqual(geoma.__array_interface__,
+                         geoma.context.__array_interface__)
+
+        pas = asarray(geoma)
+        assert_array_equal(pas, array([[1., 2.], [3., 4.]]))
+
+
+def test_suite():
+    return unittest.TestLoader().loadTestsFromTestCase(MultiPointTestCase)
diff --git a/shapely/tests/test_multipolygon.py b/shapely/tests/test_multipolygon.py
new file mode 100644
index 0000000..9e7d257
--- /dev/null
+++ b/shapely/tests/test_multipolygon.py
@@ -0,0 +1,72 @@
+from . import unittest
+from shapely.geometry import Polygon, MultiPolygon, asMultiPolygon
+from shapely.geometry.base import dump_coords
+
+
+class MultiPolygonTestCase(unittest.TestCase):
+
+    def test_multipolygon(self):
+
+        # From coordinate tuples
+        geom = MultiPolygon(
+            [(((0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (1.0, 0.0)),
+              [((0.25, 0.25), (0.25, 0.5), (0.5, 0.5), (0.5, 0.25))])])
+        self.assertIsInstance(geom, MultiPolygon)
+        self.assertEqual(len(geom.geoms), 1)
+        self.assertEqual(
+            dump_coords(geom),
+            [[(0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (1.0, 0.0), (0.0, 0.0),
+              [(0.25, 0.25), (0.25, 0.5), (0.5, 0.5), (0.5, 0.25),
+               (0.25, 0.25)]]])
+
+        # Or from polygons
+        p = Polygon(((0, 0), (0, 1), (1, 1), (1, 0)),
+                    [((0.25, 0.25), (0.25, 0.5), (0.5, 0.5), (0.5, 0.25))])
+        geom = MultiPolygon([p])
+        self.assertEqual(len(geom.geoms), 1)
+        self.assertEqual(
+            dump_coords(geom),
+            [[(0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (1.0, 0.0), (0.0, 0.0),
+              [(0.25, 0.25), (0.25, 0.5), (0.5, 0.5), (0.5, 0.25),
+               (0.25, 0.25)]]])
+
+        # Or from another multi-polygon
+        geom2 = MultiPolygon(geom)
+        self.assertEqual(len(geom2.geoms), 1)
+        self.assertEqual(
+            dump_coords(geom2),
+            [[(0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (1.0, 0.0), (0.0, 0.0),
+              [(0.25, 0.25), (0.25, 0.5), (0.5, 0.5), (0.5, 0.25),
+               (0.25, 0.25)]]])
+
+        # Sub-geometry Access
+        self.assertIsInstance(geom.geoms[0], Polygon)
+        self.assertEqual(
+            dump_coords(geom.geoms[0]),
+            [(0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (1.0, 0.0), (0.0, 0.0),
+             [(0.25, 0.25), (0.25, 0.5), (0.5, 0.5), (0.5, 0.25),
+              (0.25, 0.25)]])
+        with self.assertRaises(IndexError):  # index out of range
+            geom.geoms[1]
+
+        # Geo interface
+        self.assertEqual(
+            geom.__geo_interface__,
+            {'type': 'MultiPolygon',
+             'coordinates': [(((0.0, 0.0), (0.0, 1.0), (1.0, 1.0),
+                               (1.0, 0.0), (0.0, 0.0)),
+                              ((0.25, 0.25), (0.25, 0.5), (0.5, 0.5),
+                               (0.5, 0.25), (0.25, 0.25)))]})
+
+        # Adapter
+        coords = ((0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (1.0, 0.0))
+        holes_coords = [((0.25, 0.25), (0.25, 0.5), (0.5, 0.5), (0.5, 0.25))]
+        mpa = asMultiPolygon([(coords, holes_coords)])
+        self.assertEqual(len(mpa.geoms), 1)
+        self.assertEqual(len(mpa.geoms[0].exterior.coords), 5)
+        self.assertEqual(len(mpa.geoms[0].interiors), 1)
+        self.assertEqual(len(mpa.geoms[0].interiors[0].coords), 5)
+
+
+def test_suite():
+    return unittest.TestLoader().loadTestsFromTestCase(MultiPolygonTestCase)
diff --git a/shapely/tests/test_ndarrays.py b/shapely/tests/test_ndarrays.py
new file mode 100644
index 0000000..e9b9f6d
--- /dev/null
+++ b/shapely/tests/test_ndarrays.py
@@ -0,0 +1,54 @@
+# Tests of support for Numpy ndarrays. See
+# https://github.com/sgillies/shapely/issues/26 for discussion.
+# Requires numpy.
+
+import sys
+
+if sys.version_info[0] >= 3:
+    from functools import reduce
+
+from . import unittest
+from shapely import geometry
+
+try:
+    import numpy
+except ImportError:
+    numpy = False
+
+
+class TransposeTestCase(unittest.TestCase):
+
+    @unittest.skipIf(not numpy, 'numpy not installed')
+    def test_multipoint(self):
+        a = numpy.array([[1.0, 1.0, 2.0, 2.0, 1.0], [3.0, 4.0, 4.0, 3.0, 3.0]])
+        t = a.T
+        s = geometry.asMultiPoint(t)
+        coords = reduce(lambda x, y: x + y, [list(g.coords) for g in s])
+        self.assertEqual(
+            coords,
+            [(1.0, 3.0), (1.0, 4.0), (2.0, 4.0), (2.0, 3.0), (1.0, 3.0)]
+        )
+
+    @unittest.skipIf(not numpy, 'numpy not installed')
+    def test_linestring(self):
+        a = numpy.array([[1.0, 1.0, 2.0, 2.0, 1.0], [3.0, 4.0, 4.0, 3.0, 3.0]])
+        t = a.T
+        s = geometry.asLineString(t)
+        self.assertEqual(
+            list(s.coords),
+            [(1.0, 3.0), (1.0, 4.0), (2.0, 4.0), (2.0, 3.0), (1.0, 3.0)]
+        )
+
+    @unittest.skipIf(not numpy, 'numpy not installed')
+    def test_polygon(self):
+        a = numpy.array([[1.0, 1.0, 2.0, 2.0, 1.0], [3.0, 4.0, 4.0, 3.0, 3.0]])
+        t = a.T
+        s = geometry.asPolygon(t)
+        self.assertEqual(
+            list(s.exterior.coords),
+            [(1.0, 3.0), (1.0, 4.0), (2.0, 4.0), (2.0, 3.0), (1.0, 3.0)]
+        )
+
+
+def test_suite():
+    return unittest.TestLoader().loadTestsFromTestCase(TransposeTestCase)
diff --git a/shapely/tests/test_operations.py b/shapely/tests/test_operations.py
new file mode 100644
index 0000000..bdea03a
--- /dev/null
+++ b/shapely/tests/test_operations.py
@@ -0,0 +1,70 @@
+from . import unittest
+from shapely.geometry import Point, Polygon, MultiPoint, GeometryCollection
+from shapely.wkt import loads
+
+
+class OperationsTestCase(unittest.TestCase):
+
+    def test_operations(self):
+        point = Point(0.0, 0.0)
+
+        # General geometry
+        self.assertEqual(point.area, 0.0)
+        self.assertEqual(point.length, 0.0)
+        self.assertAlmostEqual(point.distance(Point(-1.0, -1.0)),
+                               1.4142135623730951)
+
+        # Topology operations
+
+        # Envelope
+        self.assertIsInstance(point.envelope, Point)
+
+        # Intersection
+        self.assertIsInstance(point.intersection(Point(-1, -1)),
+                              GeometryCollection)
+
+        # Buffer
+        self.assertIsInstance(point.buffer(10.0), Polygon)
+        self.assertIsInstance(point.buffer(10.0, 32), Polygon)
+
+        # Simplify
+        p = loads('POLYGON ((120 120, 121 121, 122 122, 220 120, 180 199, '
+                  '160 200, 140 199, 120 120))')
+        expected = loads('POLYGON ((120 120, 140 199, 160 200, 180 199, '
+                         '220 120, 120 120))')
+        s = p.simplify(10.0, preserve_topology=False)
+        self.assertTrue(s.equals_exact(expected, 0.001))
+
+        p = loads('POLYGON ((80 200, 240 200, 240 60, 80 60, 80 200),'
+                  '(120 120, 220 120, 180 199, 160 200, 140 199, 120 120))')
+        expected = loads(
+            'POLYGON ((80 200, 240 200, 240 60, 80 60, 80 200),'
+            '(120 120, 220 120, 180 199, 160 200, 140 199, 120 120))')
+        s = p.simplify(10.0, preserve_topology=True)
+        self.assertTrue(s.equals_exact(expected, 0.001))
+
+        # Convex Hull
+        self.assertIsInstance(point.convex_hull, Point)
+
+        # Differences
+        self.assertIsInstance(point.difference(Point(-1, 1)), Point)
+
+        self.assertIsInstance(point.symmetric_difference(Point(-1, 1)),
+                              MultiPoint)
+
+        # Boundary
+        self.assertIsInstance(point.boundary, GeometryCollection)
+
+        # Union
+        self.assertIsInstance(point.union(Point(-1, 1)), MultiPoint)
+
+        self.assertIsInstance(point.representative_point(), Point)
+
+        self.assertIsInstance(point.centroid, Point)
+
+        # Relate
+        self.assertEqual(point.relate(Point(-1, -1)), 'FF0FFF0F2')
+
+
+def test_suite():
+    return unittest.TestLoader().loadTestsFromTestCase(OperationsTestCase)
diff --git a/shapely/tests/test_operators.py b/shapely/tests/test_operators.py
new file mode 100644
index 0000000..407a7c0
--- /dev/null
+++ b/shapely/tests/test_operators.py
@@ -0,0 +1,18 @@
+from . import unittest
+from shapely.geometry import Point
+
+
+class OperatorsTestCase(unittest.TestCase):
+
+    def test_point(self):
+        point = Point(0, 0)
+        point2 = Point(-1, 1)
+        self.assertTrue(point.union(point2).equals(point | point2))
+        self.assertTrue((point & point2).is_empty)
+        self.assertTrue(point.equals(point - point2))
+        self.assertTrue(
+            point.symmetric_difference(point2).equals(point ^ point2))
+
+
+def test_suite():
+    return unittest.TestLoader().loadTestsFromTestCase(OperatorsTestCase)
diff --git a/shapely/tests/test_persist.py b/shapely/tests/test_persist.py
new file mode 100644
index 0000000..251459f
--- /dev/null
+++ b/shapely/tests/test_persist.py
@@ -0,0 +1,34 @@
+"""Persistence tests
+"""
+from . import unittest
+import pickle
+from shapely import wkb, wkt
+from shapely.geometry import Point
+
+
+class PersistTestCase(unittest.TestCase):
+
+    def test_pickle(self):
+
+        p = Point(0.0, 0.0)
+        data = pickle.dumps(p)
+        q = pickle.loads(data)
+        self.assertTrue(q.equals(p))
+
+    def test_wkb(self):
+
+        p = Point(0.0, 0.0)
+        bytes = wkb.dumps(p)
+        pb = wkb.loads(bytes)
+        self.assertTrue(pb.equals(p))
+
+    def test_wkt(self):
+        p = Point(0.0, 0.0)
+        text = wkt.dumps(p)
+        self.assertTrue(text.startswith('POINT'))
+        pt = wkt.loads(text)
+        self.assertTrue(pt.equals(p))
+
+
+def test_suite():
+    return unittest.TestLoader().loadTestsFromTestCase(PersistTestCase)
diff --git a/shapely/tests/test_pickle.py b/shapely/tests/test_pickle.py
new file mode 100644
index 0000000..0c0692a
--- /dev/null
+++ b/shapely/tests/test_pickle.py
@@ -0,0 +1,22 @@
+from . import unittest
+from shapely import geometry
+
+import sys
+if sys.version_info[0] >= 3:
+    from pickle import dumps, loads, HIGHEST_PROTOCOL
+else:
+    from cPickle import dumps, loads, HIGHEST_PROTOCOL
+
+
+class TwoDeeTestCase(unittest.TestCase):
+
+    def test_linestring(self):
+        l = geometry.LineString(((0.0, 0.0), (0.0, 1.0), (1.0, 1.0)))
+        self.assertEqual(l._ndim, 2)
+        s = dumps(l, HIGHEST_PROTOCOL)
+        t = loads(s)
+        self.assertEqual(t._ndim, 2)
+
+
+def test_suite():
+    return unittest.TestLoader().loadTestsFromTestCase(TwoDeeTestCase)
diff --git a/shapely/tests/test_point.py b/shapely/tests/test_point.py
new file mode 100644
index 0000000..01a091b
--- /dev/null
+++ b/shapely/tests/test_point.py
@@ -0,0 +1,139 @@
+from . import unittest, numpy
+from shapely.geometry import Point, asPoint
+from shapely.geos import DimensionError
+
+
+class LineStringTestCase(unittest.TestCase):
+
+    def test_point(self):
+
+        # Test 2D points
+        p = Point(1.0, 2.0)
+        self.assertEqual(p.x, 1.0)
+        self.assertEqual(p.y, 2.0)
+        self.assertEqual(p.coords[:], [(1.0, 2.0)])
+        self.assertEqual(str(p), p.wkt)
+        self.assertFalse(p.has_z)
+        with self.assertRaises(DimensionError):
+            p.z
+
+        # Check 3D
+        p = Point(1.0, 2.0, 3.0)
+        self.assertEqual(p.coords[:], [(1.0, 2.0, 3.0)])
+        self.assertEqual(str(p), p.wkt)
+        self.assertTrue(p.has_z)
+        self.assertEqual(p.z, 3.0)
+
+        # From coordinate sequence
+        p = Point((3.0, 4.0))
+        self.assertEqual(p.coords[:], [(3.0, 4.0)])
+
+        # From another point
+        q = Point(p)
+        self.assertEqual(q.coords[:], [(3.0, 4.0)])
+
+        # Coordinate access
+        self.assertEqual(p.x, 3.0)
+        self.assertEqual(p.y, 4.0)
+        self.assertEqual(tuple(p.coords), ((3.0, 4.0),))
+        self.assertEqual(p.coords[0], (3.0, 4.0))
+        with self.assertRaises(IndexError):  # index out of range
+            p.coords[1]
+
+        # Bounds
+        self.assertEqual(p.bounds, (3.0, 4.0, 3.0, 4.0))
+
+        # Geo interface
+        self.assertEqual(p.__geo_interface__,
+                         {'type': 'Point', 'coordinates': (3.0, 4.0)})
+
+        # Modify coordinates
+        p.coords = (2.0, 1.0)
+        self.assertEqual(p.__geo_interface__,
+                         {'type': 'Point', 'coordinates': (2.0, 1.0)})
+
+        # Alternate method
+        p.coords = ((0.0, 0.0),)
+        self.assertEqual(p.__geo_interface__,
+                         {'type': 'Point', 'coordinates': (0.0, 0.0)})
+
+        # Adapt a coordinate list to a point
+        coords = [3.0, 4.0]
+        pa = asPoint(coords)
+        self.assertEqual(pa.coords[0], (3.0, 4.0))
+        self.assertEqual(pa.distance(p), 5.0)
+
+        # Move the coordinates and watch the distance change
+        coords[0] = 1.0
+        self.assertEqual(pa.coords[0], (1.0, 4.0))
+        self.assertAlmostEqual(pa.distance(p), 4.123105625617661)
+
+        # Test Non-operability of Null geometry
+        p_null = Point()
+        self.assertEqual(p_null.wkt, 'GEOMETRYCOLLECTION EMPTY')
+        self.assertEqual(p_null.coords[:], [])
+        self.assertEqual(p_null.area, 0.0)
+
+        # Check that we can set coordinates of a null geometry
+        p_null.coords = (1, 2)
+        self.assertEqual(p_null.coords[:], [(1.0, 2.0)])
+
+    @unittest.skipIf(not numpy, 'Numpy required')
+    def test_numpy(self):
+
+        from numpy import array, asarray
+        from numpy.testing import assert_array_equal
+
+        # Construct from a numpy array
+        p = Point(array([1.0, 2.0]))
+        self.assertEqual(p.coords[:], [(1.0, 2.0)])
+
+        # Adapt a Numpy array to a point
+        a = array([1.0, 2.0])
+        pa = asPoint(a)
+        assert_array_equal(pa.context, array([1.0, 2.0]))
+        self.assertEqual(pa.coords[:], [(1.0, 2.0)])
+
+        # Now, the inverse
+        self.assertEqual(pa.__array_interface__,
+                         pa.context.__array_interface__)
+
+        pas = asarray(pa)
+        assert_array_equal(pas, array([1.0, 2.0]))
+
+        # Adapt a coordinate list to a point
+        coords = [3.0, 4.0]
+        pa = asPoint(coords)
+        coords[0] = 1.0
+
+        # Now, the inverse (again?)
+        self.assertIsNotNone(pa.__array_interface__)
+        pas = asarray(pa)
+        assert_array_equal(pas, array([1.0, 4.0]))
+
+        # From Array.txt
+        p = Point(0.0, 0.0, 1.0)
+        coords = p.coords[0]
+        self.assertEqual(coords, (0.0, 0.0, 1.0))
+        self.assertIsNotNone(p.ctypes)
+
+        # Convert to Numpy array, passing through Python sequence
+        a = asarray(coords)
+        self.assertEqual(a.ndim, 1)
+        self.assertEqual(a.size, 3)
+        self.assertEqual(a.shape, (3,))
+
+        # Convert to Numpy array, passing through a ctypes array
+        b = asarray(p)
+        self.assertEqual(b.size, 3)
+        self.assertEqual(b.shape, (3,))
+        assert_array_equal(b, array([0.0, 0.0, 1.0]))
+
+        # Make a point from a Numpy array
+        a = asarray([1.0, 1.0, 0.0])
+        p = Point(*list(a))
+        self.assertEqual(p.coords[:], [(1.0, 1.0, 0.0)])
+
+
+def test_suite():
+    return unittest.TestLoader().loadTestsFromTestCase(LineStringTestCase)
diff --git a/shapely/tests/test_polygon.py b/shapely/tests/test_polygon.py
new file mode 100644
index 0000000..d973786
--- /dev/null
+++ b/shapely/tests/test_polygon.py
@@ -0,0 +1,186 @@
+"""Polygons and Linear Rings
+"""
+from . import unittest, numpy
+from shapely.wkb import loads as load_wkb
+from shapely.geometry import Point, Polygon, asPolygon
+from shapely.geometry.polygon import LinearRing, asLinearRing
+from shapely.geometry.base import dump_coords
+
+
+class PolygonTestCase(unittest.TestCase):
+
+    def test_polygon(self):
+
+        # Initialization
+        # Linear rings won't usually be created by users, but by polygons
+        coords = ((0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (1.0, 0.0))
+        ring = LinearRing(coords)
+        self.assertEqual(len(ring.coords), 5)
+        self.assertEqual(ring.coords[0], ring.coords[4])
+        self.assertEqual(ring.coords[0], ring.coords[-1])
+        self.assertTrue(ring.is_ring)
+
+        # Coordinate modification
+        ring.coords = ((0.0, 0.0), (0.0, 2.0), (2.0, 2.0), (2.0, 0.0))
+        self.assertEqual(
+            ring.__geo_interface__,
+            {'type': 'LinearRing',
+             'coordinates': ((0.0, 0.0), (0.0, 2.0), (2.0, 2.0), (2.0, 0.0),
+                             (0.0, 0.0))})
+
+        # Test ring adapter
+        coords = [[0.0, 0.0], [0.0, 1.0], [1.0, 1.0], [1.0, 0.0]]
+        ra = asLinearRing(coords)
+        self.assertTrue(ra.wkt.upper().startswith('LINEARRING'))
+        self.assertEqual(dump_coords(ra),
+                         [(0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (1.0, 0.0),
+                          (0.0, 0.0)])
+        coords[3] = [2.0, -1.0]
+        self.assertEqual(dump_coords(ra),
+                         [(0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (2.0, -1.0),
+                          (0.0, 0.0)])
+
+        # Construct a polygon, exterior ring only
+        polygon = Polygon(coords)
+        self.assertEqual(len(polygon.exterior.coords), 5)
+
+        # Ring Access
+        self.assertIsInstance(polygon.exterior, LinearRing)
+        ring = polygon.exterior
+        self.assertEqual(len(ring.coords), 5)
+        self.assertEqual(ring.coords[0], ring.coords[4])
+        self.assertEqual(ring.coords[0], (0., 0.))
+        self.assertTrue(ring.is_ring)
+        self.assertEqual(len(polygon.interiors), 0)
+
+        # Create a new polygon from WKB
+        data = polygon.wkb
+        polygon = None
+        ring = None
+        polygon = load_wkb(data)
+        ring = polygon.exterior
+        self.assertEqual(len(ring.coords), 5)
+        self.assertEqual(ring.coords[0], ring.coords[4])
+        self.assertEqual(ring.coords[0], (0., 0.))
+        self.assertTrue(ring.is_ring)
+        polygon = None
+
+        # Interior rings (holes)
+        polygon = Polygon(coords, [((0.25, 0.25), (0.25, 0.5),
+                                    (0.5, 0.5), (0.5, 0.25))])
+        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):
+            polygon.coords
+
+        # Geo interface
+        self.assertEqual(
+            polygon.__geo_interface__,
+            {'type': 'Polygon',
+             'coordinates': (((0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (2.0, -1.0),
+                             (0.0, 0.0)), ((0.25, 0.25), (0.25, 0.5),
+                             (0.5, 0.5), (0.5, 0.25), (0.25, 0.25)))})
+
+        # Adapter
+        hole_coords = [((0.25, 0.25), (0.25, 0.5), (0.5, 0.5), (0.5, 0.25))]
+        pa = asPolygon(coords, hole_coords)
+        self.assertEqual(len(pa.exterior.coords), 5)
+        self.assertEqual(len(pa.interiors), 1)
+        self.assertEqual(len(pa.interiors[0].coords), 5)
+
+        # Test Non-operability of Null rings
+        r_null = LinearRing()
+        self.assertEqual(r_null.wkt, 'GEOMETRYCOLLECTION EMPTY')
+        self.assertEqual(r_null.length, 0.0)
+
+        # Check that we can set coordinates of a null geometry
+        r_null.coords = [(0, 0), (1, 1), (1, 0)]
+        self.assertAlmostEqual(r_null.length, 3.414213562373095)
+
+        # Error handling
+        with self.assertRaises(ValueError):
+            # A LinearRing must have at least 3 coordinate tuples
+            Polygon([[1, 2], [2, 3]])
+
+    @unittest.skipIf(not numpy, 'Numpy required')
+    def test_numpy(self):
+
+        from numpy import array, asarray
+        from numpy.testing import assert_array_equal
+
+        a = asarray(((0., 0.), (0., 1.), (1., 1.), (1., 0.), (0., 0.)))
+        polygon = Polygon(a)
+        self.assertEqual(len(polygon.exterior.coords), 5)
+        self.assertEqual(dump_coords(polygon.exterior),
+                         [(0., 0.), (0., 1.), (1., 1.), (1., 0.), (0., 0.)])
+        self.assertEqual(len(polygon.interiors), 0)
+        b = asarray(polygon.exterior)
+        self.assertEqual(b.shape, (5, 2))
+        assert_array_equal(
+            b, array([(0., 0.), (0., 1.), (1., 1.), (1., 0.), (0., 0.)]))
+
+    def test_dimensions(self):
+
+        # Background: see http://trac.gispython.org/lab/ticket/168
+    # http://lists.gispython.org/pipermail/community/2008-August/001859.html
+
+        coords = ((0.0, 0.0, 0.0), (0.0, 1.0, 0.0), (1.0, 1.0, 0.0),
+                  (1.0, 0.0, 0.0))
+        polygon = Polygon(coords)
+        self.assertEqual(polygon._ndim, 3)
+        gi = polygon.__geo_interface__
+        self.assertEqual(
+            gi['coordinates'],
+            (((0.0, 0.0, 0.0), (0.0, 1.0, 0.0), (1.0, 1.0, 0.0),
+              (1.0, 0.0, 0.0), (0.0, 0.0, 0.0)),))
+
+        e = polygon.exterior
+        self.assertEqual(e._ndim, 3)
+        gi = e.__geo_interface__
+        self.assertEqual(
+            gi['coordinates'],
+            ((0.0, 0.0, 0.0), (0.0, 1.0, 0.0), (1.0, 1.0, 0.0),
+             (1.0, 0.0, 0.0), (0.0, 0.0, 0.0)))
+
+    def test_attribute_chains(self):
+
+        # Attribute Chaining
+        # See also ticket #151.
+        p = Polygon(((0.0, 0.0), (0.0, 1.0), (-1.0, 1.0), (-1.0, 0.0)))
+        self.assertEqual(
+            list(p.boundary.coords),
+            [(0.0, 0.0), (0.0, 1.0), (-1.0, 1.0), (-1.0, 0.0), (0.0, 0.0)])
+
+        ec = list(Point(0.0, 0.0).buffer(1.0, 1).exterior.coords)
+        self.assertIsInstance(ec, list)  # TODO: this is a poor test
+
+        # Test chained access to interiors
+        p = Polygon(
+            ((0.0, 0.0), (0.0, 1.0), (-1.0, 1.0), (-1.0, 0.0)),
+            [((-0.25, 0.25), (-0.25, 0.75), (-0.75, 0.75), (-0.75, 0.25))]
+        )
+        self.assertEqual(p.area, 0.75)
+
+        """Not so much testing the exact values here, which are the
+        responsibility of the geometry engine (GEOS), but that we can get
+        chain functions and properties using anonymous references.
+        """
+        self.assertEqual(
+            list(p.interiors[0].coords),
+            [(-0.25, 0.25), (-0.25, 0.75), (-0.75, 0.75), (-0.75, 0.25),
+             (-0.25, 0.25)])
+        xy = list(p.interiors[0].buffer(1).exterior.coords)[0]
+        self.assertEqual(len(xy), 2)
+
+        # Test multiple operators, boundary of a buffer
+        ec = list(p.buffer(1).boundary.coords)
+        self.assertIsInstance(ec, list)  # TODO: this is a poor test
+
+
+def test_suite():
+    return unittest.TestLoader().loadTestsFromTestCase(PolygonTestCase)
diff --git a/shapely/tests/test_polygonize.py b/shapely/tests/test_polygonize.py
new file mode 100755
index 0000000..cb6feda
--- /dev/null
+++ b/shapely/tests/test_polygonize.py
@@ -0,0 +1,49 @@
+from . import unittest
+from shapely.geos import geos_version
+from shapely.geometry import Point, LineString, Polygon
+from shapely.geometry.base import dump_coords
+from shapely.ops import polygonize, polygonize_full
+
+
+class PolygonizeTestCase(unittest.TestCase):
+
+    def test_polygonize(self):
+        lines = [
+            LineString(((0, 0), (1, 1))),
+            LineString(((0, 0), (0, 1))),
+            LineString(((0, 1), (1, 1))),
+            LineString(((1, 1), (1, 0))),
+            LineString(((1, 0), (0, 0))),
+            LineString(((5, 5), (6, 6))),
+            Point(0, 0),
+            ]
+        result = list(polygonize(lines))
+        self.assertTrue(all([isinstance(x, Polygon) for x in result]))
+
+    @unittest.skipIf(geos_version < (3, 3, 0), 'GEOS 3.3.0 required')
+    def test_polygonize_full(self):
+
+        lines2 = [
+            ((0, 0), (1, 1)),
+            ((0, 0), (0, 1)),
+            ((0, 1), (1, 1)),
+            ((1, 1), (1, 0)),
+            ((1, 0), (0, 0)),
+            ((5, 5), (6, 6)),
+            ((1, 1), (100, 100)),
+            ]
+
+        result2, dangles, cuts, invalids = polygonize_full(lines2)
+        self.assertEqual(len(result2), 2)
+        self.assertTrue(all([isinstance(x, Polygon) for x in result2]))
+        self.assertEqual(list(dangles.geoms), [])
+        self.assertTrue(all([isinstance(x, LineString) for x in cuts.geoms]))
+
+        self.assertEqual(
+            dump_coords(cuts),
+            [[(1.0, 1.0), (100.0, 100.0)], [(5.0, 5.0), (6.0, 6.0)]])
+        self.assertEqual(list(invalids.geoms), [])
+
+
+def test_suite():
+    return unittest.TestLoader().loadTestsFromTestCase(PolygonizeTestCase)
diff --git a/shapely/tests/test_predicates.py b/shapely/tests/test_predicates.py
new file mode 100644
index 0000000..e90a941
--- /dev/null
+++ b/shapely/tests/test_predicates.py
@@ -0,0 +1,34 @@
+"""Test GEOS predicates
+"""
+from . import unittest
+from shapely.geometry import Point
+
+
+class PredicatesTestCase(unittest.TestCase):
+
+    def test_binary_predicates(self):
+
+        point = Point(0.0, 0.0)
+
+        self.assertTrue(point.disjoint(Point(-1.0, -1.0)))
+        self.assertFalse(point.touches(Point(-1.0, -1.0)))
+        self.assertFalse(point.crosses(Point(-1.0, -1.0)))
+        self.assertFalse(point.within(Point(-1.0, -1.0)))
+        self.assertFalse(point.contains(Point(-1.0, -1.0)))
+        self.assertFalse(point.equals(Point(-1.0, -1.0)))
+        self.assertFalse(point.touches(Point(-1.0, -1.0)))
+        self.assertTrue(point.equals(Point(0.0, 0.0)))
+
+    def test_unary_predicates(self):
+
+        point = Point(0.0, 0.0)
+
+        self.assertFalse(point.is_empty)
+        self.assertTrue(point.is_valid)
+        self.assertTrue(point.is_simple)
+        self.assertFalse(point.is_ring)
+        self.assertFalse(point.has_z)
+
+
+def test_suite():
+    return unittest.TestLoader().loadTestsFromTestCase(PredicatesTestCase)
diff --git a/shapely/tests/test_prepared.py b/shapely/tests/test_prepared.py
index 0ec947d..fb3d4ed 100644
--- a/shapely/tests/test_prepared.py
+++ b/shapely/tests/test_prepared.py
@@ -1,10 +1,12 @@
-import unittest
+from . import unittest
+from shapely.geos import geos_version
 from shapely import prepared
 from shapely import geometry
 
 
 class PreparedGeometryTestCase(unittest.TestCase):
-    
+
+    @unittest.skipIf(geos_version < (3, 1, 0), 'GEOS 3.1.0 required')
     def test_prepared(self):
         polygon = geometry.Polygon([
             (0, 0), (1, 0), (1, 1), (0, 1)
@@ -13,16 +15,17 @@ class PreparedGeometryTestCase(unittest.TestCase):
         self.assertTrue(p.contains(geometry.Point(0.5, 0.5)))
         self.assertFalse(p.contains(geometry.Point(0.5, 1.5)))
 
+    @unittest.skipIf(geos_version < (3, 1, 0), 'GEOS 3.1.0 required')
     def test_op_not_allowed(self):
         p = prepared.PreparedGeometry(geometry.Point(0.0, 0.0).buffer(1.0))
         self.assertRaises(ValueError, geometry.Point(0.0, 0.0).union, p)
 
+    @unittest.skipIf(geos_version < (3, 1, 0), 'GEOS 3.1.0 required')
     def test_predicate_not_allowed(self):
         p = prepared.PreparedGeometry(geometry.Point(0.0, 0.0).buffer(1.0))
         self.assertRaises(ValueError, geometry.Point(0.0, 0.0).contains, p)
 
 
 def test_suite():
-    return unittest.TestLoader().loadTestsFromTestCase(
-                                    PreparedGeometryTestCase
-                                    )
+    loader = unittest.TestLoader()
+    return loader.loadTestsFromTestCase(PreparedGeometryTestCase)
diff --git a/shapely/tests/test_products_z.py b/shapely/tests/test_products_z.py
index c6fe0aa..b710770 100644
--- a/shapely/tests/test_products_z.py
+++ b/shapely/tests/test_products_z.py
@@ -1,14 +1,17 @@
-import unittest
+from . import unittest
 from shapely.geometry import LineString
 
+
 class ProductZTestCase(unittest.TestCase):
+
     def test_line_intersection(self):
-        line1 = LineString([(0,0,0), (1,1,1)])
-        line2 = LineString([(0,1,1), (1,0,0)])
+        line1 = LineString([(0, 0, 0), (1, 1, 1)])
+        line2 = LineString([(0, 1, 1), (1, 0, 0)])
         interxn = line1.intersection(line2)
-        self.failUnless(interxn.has_z)
-        self.failUnless(interxn._ndim == 3)
-        self.failUnless(0.0 <= interxn.z <= 1.0)
+        self.assertTrue(interxn.has_z)
+        self.assertEqual(interxn._ndim, 3)
+        self.assertTrue(0.0 <= interxn.z <= 1.0)
+
 
 def test_suite():
     return unittest.TestLoader().loadTestsFromTestCase(ProductZTestCase)
diff --git a/shapely/tests/test_singularity.py b/shapely/tests/test_singularity.py
index a2d2493..d25436a 100644
--- a/shapely/tests/test_singularity.py
+++ b/shapely/tests/test_singularity.py
@@ -1,15 +1,19 @@
-import unittest
+from . import unittest
 from shapely.geometry import Polygon
 
+
 class PolygonTestCase(unittest.TestCase):
+
     def test_polygon_3(self):
         p = (1.0, 1.0)
         poly = Polygon([p, p, p])
-        self.failUnlessEqual(poly.bounds, (1.0, 1.0, 1.0, 1.0))
+        self.assertEqual(poly.bounds, (1.0, 1.0, 1.0, 1.0))
+
     def test_polygon_5(self):
         p = (1.0, 1.0)
         poly = Polygon([p, p, p, p, p])
-        self.failUnlessEqual(poly.bounds, (1.0, 1.0, 1.0, 1.0))
+        self.assertEqual(poly.bounds, (1.0, 1.0, 1.0, 1.0))
+
 
 def test_suite():
     return unittest.TestLoader().loadTestsFromTestCase(PolygonTestCase)
diff --git a/shapely/tests/test_speedups.py b/shapely/tests/test_speedups.py
index ada3e3e..705d0ab 100644
--- a/shapely/tests/test_speedups.py
+++ b/shapely/tests/test_speedups.py
@@ -1,28 +1,34 @@
-import unittest
+from . import unittest
 
 from shapely import speedups
 from shapely.geometry import LineString, Polygon
 
+
 class SpeedupsTestCase(unittest.TestCase):
-    
+
     def setUp(self):
         self.assertFalse(speedups._orig)
-        speedups.enable()
-        self.assertTrue(speedups._orig)
-        
+        if speedups.available:
+            speedups.enable()
+            self.assertTrue(speedups._orig)
+
     def tearDown(self):
-        self.assertTrue(speedups._orig)
+        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)
@@ -30,9 +36,4 @@ class SpeedupsTestCase(unittest.TestCase):
 
 
 def test_suite():
-    if speedups.available:
-        return unittest.TestLoader().loadTestsFromTestCase(
-            SpeedupsTestCase
-        )
-    else:
-        return lambda x: None
\ No newline at end of file
+    return unittest.TestLoader().loadTestsFromTestCase(SpeedupsTestCase)
diff --git a/shapely/tests/test_styles.py b/shapely/tests/test_styles.py
new file mode 100644
index 0000000..69bc407
--- /dev/null
+++ b/shapely/tests/test_styles.py
@@ -0,0 +1,20 @@
+from . import unittest
+from shapely.geometry import CAP_STYLE, JOIN_STYLE
+
+
+class StylesTest(unittest.TestCase):
+
+    def test_cap(self):
+        self.assertEqual(CAP_STYLE.round, 1)
+        self.assertEqual(CAP_STYLE.flat, 2)
+        self.assertEqual(CAP_STYLE.square, 3)
+
+    def test_join(self):
+        self.assertEqual(JOIN_STYLE.round, 1)
+        self.assertEqual(JOIN_STYLE.mitre, 2)
+        self.assertEqual(JOIN_STYLE.bevel, 3)
+
+
+def test_suite():
+    return unittest.TestSuite([
+        unittest.TestLoader().loadTestsFromTestCase(StylesTest)])
diff --git a/shapely/tests/test_transform.py b/shapely/tests/test_transform.py
new file mode 100644
index 0000000..c1b8d94
--- /dev/null
+++ b/shapely/tests/test_transform.py
@@ -0,0 +1,78 @@
+from . import unittest
+from shapely import geometry
+from shapely.ops import transform
+
+
+class IdentityTestCase(unittest.TestCase):
+    """New geometry/coordseq method 'xy' makes numpy interop easier"""
+
+    def func(self, x, y, z=None):
+        return tuple([c for c in [x, y, z] if c])
+
+    def test_empty(self):
+        g = geometry.Point()
+        h = transform(self.func, g)
+        self.assertTrue(h.is_empty)
+
+    def test_point(self):
+        g = geometry.Point(0, 1)
+        h = transform(self.func, g)
+        self.assertEqual(h.geom_type, 'Point')
+        self.assertEqual(list(h.coords), [(0, 1)])
+
+    def test_line(self):
+        g = geometry.LineString(((0, 1), (2, 3)))
+        h = transform(self.func, g)
+        self.assertEqual(h.geom_type, 'LineString')
+        self.assertEqual(list(h.coords), [(0, 1), (2, 3)])
+
+    def test_polygon(self):
+        g = geometry.Point(0, 1).buffer(1.0)
+        h = transform(self.func, g)
+        self.assertEqual(h.geom_type, 'Polygon')
+        self.assertAlmostEqual(g.area, h.area)
+
+    def test_multipolygon(self):
+        g = geometry.MultiPoint([(0, 1), (0, 4)]).buffer(1.0)
+        h = transform(self.func, g)
+        self.assertEqual(h.geom_type, 'MultiPolygon')
+        self.assertAlmostEqual(g.area, h.area)
+
+
+class LambdaTestCase(unittest.TestCase):
+    """New geometry/coordseq method 'xy' makes numpy interop easier"""
+
+    def test_point(self):
+        g = geometry.Point(0, 1)
+        h = transform(lambda x, y, z=None: (x+1.0, y+1.0), g)
+        self.assertEqual(h.geom_type, 'Point')
+        self.assertEqual(list(h.coords), [(1.0, 2.0)])
+
+    def test_line(self):
+        g = geometry.LineString(((0, 1), (2, 3)))
+        h = transform(lambda x, y, z=None: (x+1.0, y+1.0), g)
+        self.assertEqual(h.geom_type, 'LineString')
+        self.assertEqual(list(h.coords), [(1.0, 2.0), (3.0, 4.0)])
+
+    def test_polygon(self):
+        g = geometry.Point(0, 1).buffer(1.0)
+        h = transform(lambda x, y, z=None: (x+1.0, y+1.0), g)
+        self.assertEqual(h.geom_type, 'Polygon')
+        self.assertAlmostEqual(g.area, h.area)
+        self.assertAlmostEqual(h.centroid.x, 1.0)
+        self.assertAlmostEqual(h.centroid.y, 2.0)
+
+    def test_multipolygon(self):
+        g = geometry.MultiPoint([(0, 1), (0, 4)]).buffer(1.0)
+        h = transform(lambda x, y, z=None: (x+1.0, y+1.0), g)
+        self.assertEqual(h.geom_type, 'MultiPolygon')
+        self.assertAlmostEqual(g.area, h.area)
+        self.assertAlmostEqual(h.centroid.x, 1.0)
+        self.assertAlmostEqual(h.centroid.y, 3.5)
+
+
+def test_suite():
+    loader = unittest.TestLoader()
+    return unittest.TestSuite([
+        loader.loadTestsFromTestCase(IdentityTestCase),
+        loader.loadTestsFromTestCase(LambdaTestCase)])
diff --git a/shapely/tests/test_union.py b/shapely/tests/test_union.py
new file mode 100644
index 0000000..7a657db
--- /dev/null
+++ b/shapely/tests/test_union.py
@@ -0,0 +1,72 @@
+from . import unittest
+import random
+from itertools import islice
+from shapely.geos import geos_version
+from shapely.ftools import partial
+from shapely.geometry import Point, MultiPolygon
+from shapely.ops import cascaded_union, unary_union
+
+
+def halton(base):
+    """Returns an iterator over an infinite Halton sequence"""
+    def value(index):
+        result = 0.0
+        f = 1.0 / base
+        i = index
+        while i > 0:
+            result += f * (i % base)
+            i = i // base
+            f = f / base
+        return result
+    i = 1
+    while i > 0:
+        yield value(i)
+        i += 1
+
+
+class UnionTestCase(unittest.TestCase):
+
+    def test_cascaded_union(self):
+
+        # Use a partial function to make 100 points uniformly distributed
+        # in a 40x40 box centered on 0,0.
+
+        r = partial(random.uniform, -20.0, 20.0)
+        points = [Point(r(), r()) for i in range(100)]
+
+        # Buffer the points, producing 100 polygon spots
+        spots = [p.buffer(2.5) for p in points]
+
+        # Perform a cascaded union of the polygon spots, dissolving them
+        # into a collection of polygon patches
+        u = cascaded_union(spots)
+        self.assertTrue(u.geom_type in ('Polygon', 'MultiPolygon'))
+
+    def setUp(self):
+        # Instead of random points, use deterministic, pseudo-random Halton
+        # sequences for repeatability sake.
+        self.coords = zip(
+            list(islice(halton(5), 20, 120)),
+            list(islice(halton(7), 20, 120)),
+        )
+
+    @unittest.skipIf(geos_version < (3, 3, 0), 'GEOS 3.3.0 required')
+    def test_unary_union(self):
+        patches = [Point(xy).buffer(0.05) for xy in self.coords]
+        u = unary_union(patches)
+        self.assertEqual(u.geom_type, 'MultiPolygon')
+        self.assertAlmostEqual(u.area, 0.71857254056)
+
+    @unittest.skipIf(geos_version < (3, 3, 0), 'GEOS 3.3.0 required')
+    def test_unary_union_multi(self):
+        # Test of multipart input based on comment by @schwehr at
+        # https://github.com/Toblerity/Shapely/issues/47#issuecomment-21809308
+        patches = MultiPolygon([Point(xy).buffer(0.05) for xy in self.coords])
+        self.assertAlmostEqual(unary_union(patches).area,
+                               0.71857254056)
+        self.assertAlmostEqual(unary_union([patches, patches]).area,
+                               0.71857254056)
+
+
+def test_suite():
+    return unittest.TestLoader().loadTestsFromTestCase(UnionTestCase)
diff --git a/shapely/tests/test_validation.py b/shapely/tests/test_validation.py
index 0a008de..882bee7 100644
--- a/shapely/tests/test_validation.py
+++ b/shapely/tests/test_validation.py
@@ -1,10 +1,12 @@
-import unittest
-from shapely.geometry import *
+from . import unittest
+from shapely.geometry import Point
 from shapely.validation import explain_validity
 
+
 class ValidationTestCase(unittest.TestCase):
     def test_valid(self):
-        self.failUnlessEqual(explain_validity(Point(0, 0)), 'Valid Geometry')
+        self.assertEqual(explain_validity(Point(0, 0)), 'Valid Geometry')
+
 
 def test_suite():
     return unittest.TestLoader().loadTestsFromTestCase(ValidationTestCase)
diff --git a/shapely/tests/test_xy.py b/shapely/tests/test_xy.py
index 0bed93f..8a6e69e 100644
--- a/shapely/tests/test_xy.py
+++ b/shapely/tests/test_xy.py
@@ -1,14 +1,17 @@
-import unittest
+from . import unittest
 from shapely import geometry
 
+
 class XYTestCase(unittest.TestCase):
     """New geometry/coordseq method 'xy' makes numpy interop easier"""
+
     def test_arrays(self):
         x, y = geometry.LineString(((0, 0), (1, 1))).xy
-        self.failUnless(len(x) == 2)
-        self.failUnless(list(x) == [0.0, 1.0])
-        self.failUnless(len(y) == 2)
-        self.failUnless(list(y) == [0.0, 1.0])
+        self.assertEqual(len(x), 2)
+        self.assertEqual(list(x), [0.0, 1.0])
+        self.assertEqual(len(y), 2)
+        self.assertEqual(list(y), [0.0, 1.0])
+
 
 def test_suite():
     return unittest.TestLoader().loadTestsFromTestCase(XYTestCase)
diff --git a/shapely/tests/threading_test.py b/shapely/tests/threading_test.py
index fd3b4bf..5c57a0e 100644
--- a/shapely/tests/threading_test.py
+++ b/shapely/tests/threading_test.py
@@ -1,37 +1,40 @@
 import threading
+from binascii import b2a_hex
+
 
 def main():
     num_threads = 10
     use_threads = True
-    
+
     if not use_threads:
         # Run core code
         runShapelyBuilding()
     else:
-        threads = [threading.Thread(target=runShapelyBuilding, name=str(i), args=(i,)) for i in range(num_threads)]
+        threads = [threading.Thread(target=runShapelyBuilding, name=str(i),
+                                    args=(i,)) for i in range(num_threads)]
         for t in threads:
             t.start()
         for t in threads:
             t.join()
-        
+
+
 def runShapelyBuilding(num):
-    print "%s: Running shapely tests on wkb" % num
+    print("%s: Running shapely tests on wkb" % num)
     import shapely.geos
-    print "%s GEOS Handle: %s" % (num, shapely.geos.lgeos.geos_handle)
+    print("%s GEOS Handle: %s" % (num, shapely.geos.lgeos.geos_handle))
     import shapely.wkt
     import shapely.wkb
     p = shapely.wkt.loads("POINT (0 0)")
-    print "%s WKT: %s" % (num, shapely.wkt.dumps(p))
+    print("%s WKT: %s" % (num, shapely.wkt.dumps(p)))
     wkb = shapely.wkb.dumps(p)
-    print "%s WKB: %s" % (num, wkb.encode('hex'))
-    
-    for i in xrange(10):
-        obj = shapely.wkb.loads(wkb)
-    
-    print "%s GEOS Handle: %s" % (num, shapely.geos.lgeos.geos_handle)
-    print "Done %s" % num
-    
-# Run main
-if __name__ == '__main__':
-   main()   
+    print("%s WKB: %s" % (num, b2a_hex(wkb)))
 
+    for i in range(10):
+        shapely.wkb.loads(wkb)
+
+    print("%s GEOS Handle: %s" % (num, shapely.geos.lgeos.geos_handle))
+    print("Done %s" % num)
+
+
+if __name__ == '__main__':
+    main()
diff --git a/shapely/tests/valgrind-python.supp b/shapely/tests/valgrind-python.supp
new file mode 100644
index 0000000..440fb4a
--- /dev/null
+++ b/shapely/tests/valgrind-python.supp
@@ -0,0 +1,240 @@
+#
+# This is a valgrind suppression file that should be used when using valgrind.
+#
+#  Here's an example of running valgrind:
+#
+#	cd python/dist/src
+#	valgrind --tool=memcheck --suppressions=Misc/valgrind-python.supp \
+#		./python -E -tt ./Lib/test/regrtest.py -u bsddb,network
+#
+# You must edit Objects/obmalloc.c and uncomment Py_USING_MEMORY_DEBUGGER
+# to use the preferred suppressions with Py_ADDRESS_IN_RANGE.
+#
+# If you do not want to recompile Python, you can uncomment
+# suppressions for PyObject_Free and PyObject_Realloc.
+#
+# See Misc/README.valgrind for more information.
+
+# all tool names: Addrcheck,Memcheck,cachegrind,helgrind,massif
+{
+   ADDRESS_IN_RANGE/Invalid read of size 4
+   Memcheck:Addr4
+   fun:Py_ADDRESS_IN_RANGE
+}
+
+{
+   ADDRESS_IN_RANGE/Invalid read of size 4
+   Memcheck:Value4
+   fun:Py_ADDRESS_IN_RANGE
+}
+
+{
+   ADDRESS_IN_RANGE/Conditional jump or move depends on uninitialised value
+   Memcheck:Cond
+   fun:Py_ADDRESS_IN_RANGE
+}
+
+{
+   ADDRESS_IN_RANGE/Invalid read of size 4
+   Memcheck:Addr4
+   fun:PyObject_Free
+}
+
+{
+   ADDRESS_IN_RANGE/Invalid read of size 4
+   Memcheck:Value4
+   fun:PyObject_Free
+}
+
+{
+   ADDRESS_IN_RANGE/Conditional jump or move depends on uninitialised value
+   Memcheck:Cond
+   fun:PyObject_Free
+}
+
+{
+   ADDRESS_IN_RANGE/Invalid read of size 4
+   Memcheck:Addr4
+   fun:PyObject_Realloc
+}
+
+{
+   ADDRESS_IN_RANGE/Invalid read of size 4
+   Memcheck:Value4
+   fun:PyObject_Realloc
+}
+
+{
+  ADDRESS_IN_RANGE/Conditional jump or move depends on uninitialised value
+  Memcheck:Cond
+  fun:PyObject_Realloc
+}
+
+###
+### All the suppressions below are for errors that occur within libraries
+### that Python uses.  The problems to not appear to be related to Python's
+### use of the libraries.
+###
+{
+   GDBM problems, see test_gdbm
+   Memcheck:Param
+   write(buf)
+   fun:write
+   fun:gdbm_open
+
+}
+
+###
+### These occur from somewhere within the SSL, when running
+###  test_socket_sll.  They are too general to leave on by default.
+###
+###{
+###   somewhere in SSL stuff
+###   Memcheck:Cond
+###   fun:memset
+###}
+###{
+###   somewhere in SSL stuff
+###   Memcheck:Value4
+###   fun:memset
+###}
+###
+###{
+###   somewhere in SSL stuff
+###   Memcheck:Cond
+###   fun:MD5_Update
+###}
+###
+###{
+###   somewhere in SSL stuff
+###   Memcheck:Value4
+###   fun:MD5_Update
+###}
+
+#
+# All of these problems come from using test_socket_ssl
+#
+{
+   from test_socket_ssl
+   Memcheck:Cond
+   fun:BN_bin2bn
+}
+
+{
+   from test_socket_ssl
+   Memcheck:Cond
+   fun:BN_num_bits_word
+}
+
+{
+   from test_socket_ssl
+   Memcheck:Value4
+   fun:BN_num_bits_word
+}
+
+{
+   from test_socket_ssl
+   Memcheck:Cond
+   fun:BN_mod_exp_mont_word
+}
+
+{
+   from test_socket_ssl
+   Memcheck:Cond
+   fun:BN_mod_exp_mont
+}
+
+{
+   from test_socket_ssl
+   Memcheck:Param
+   write(buf)
+   fun:write
+   obj:/usr/lib/libcrypto.so.0.9.7
+}
+
+{
+   from test_socket_ssl
+   Memcheck:Cond
+   fun:RSA_verify
+}
+
+{
+   from test_socket_ssl
+   Memcheck:Value4
+   fun:RSA_verify
+}
+
+{
+   from test_socket_ssl
+   Memcheck:Value4
+   fun:DES_set_key_unchecked
+}
+
+{
+   from test_socket_ssl
+   Memcheck:Value4
+   fun:DES_encrypt2
+}
+
+{
+   from test_socket_ssl
+   Memcheck:Cond
+   obj:/usr/lib/libssl.so.0.9.7
+}
+
+{
+   from test_socket_ssl
+   Memcheck:Value4
+   obj:/usr/lib/libssl.so.0.9.7
+}
+
+{
+   from test_socket_ssl
+   Memcheck:Cond
+   fun:BUF_MEM_grow_clean
+}
+
+{
+   from test_socket_ssl
+   Memcheck:Cond
+   fun:memcpy
+   fun:ssl3_read_bytes
+}
+
+{
+   from test_socket_ssl
+   Memcheck:Cond
+   fun:SHA1_Update
+}
+
+{
+   from test_socket_ssl
+   Memcheck:Value4
+   fun:SHA1_Update
+}
+
+
+# some extra lxml specific (?) suppressions..
+
+{
+   Test
+   Memcheck:Param
+   sigaction(act)
+   fun:__libc_sigaction
+}
+
+{
+   ld
+   Memcheck:Cond
+   obj:/lib/ld-2.6.so
+   obj:/lib/ld-2.6.so
+   obj:*
+}
+
+{
+   ld
+   Memcheck:Addr4
+   obj:/lib/ld-2.6.so
+   obj:/lib/ld-2.6.so
+   obj:*
+}
diff --git a/shapely/tests/wkt_locale.txt b/shapely/tests/wkt_locale.txt
deleted file mode 100644
index ebbc024..0000000
--- a/shapely/tests/wkt_locale.txt
+++ /dev/null
@@ -1,28 +0,0 @@
-Test locale independence of WKT
-===============================
-
-Set locale to one that uses a comma as decimal seperator`
-
-  >>> import locale, sys
-  >>> if sys.platform == 'win32':
-  ...     _ = locale.setlocale(locale.LC_ALL, 'portuguese_brazil')
-  ... else:
-  ...     _ = locale.setlocale(locale.LC_ALL, 'pt_BR.UTF-8')
-
-Test reading and writing`
-
-  >>> from shapely.wkt import loads
-  >>> p = loads('POINT (0.0 0.0)')
-  >>> p.x == 0.0
-  True
-  >>> p.y == 0.0
-  True
-  >>> p.wkt
-  'POINT (0.0...)'
-  >>> from shapely.wkt import dumps
-  >>> dumps(p)
-  'POINT (0.0...)'
-
-Reset locale
-
-  >>> _ = locale.setlocale(locale.LC_ALL, '')
diff --git a/shapely/validation.py b/shapely/validation.py
index cd390d2..cb87074 100644
--- a/shapely/validation.py
+++ b/shapely/validation.py
@@ -1,7 +1,8 @@
 # TODO: allow for implementations using other than GEOS
 
+import sys
+
 from shapely.geos import lgeos
 
 def explain_validity(ob):
     return lgeos.GEOSisValidReason(ob._geom)
-
diff --git a/shapely/wkb.py b/shapely/wkb.py
index fbb675e..a962412 100644
--- a/shapely/wkb.py
+++ b/shapely/wkb.py
@@ -1,38 +1,37 @@
 """Load/dump geometries using the well-known binary (WKB) format
 """
 
-from ctypes import pointer, c_size_t, c_char_p, c_void_p
-
-from shapely.geos import lgeos, ReadingError
-
+from shapely import geos
 
 # Pickle-like convenience functions
 
-def deserialize(data):
-    geom = lgeos.GEOSGeomFromWKB_buf(c_char_p(data), c_size_t(len(data)));
-    if not geom:
-        raise ReadingError(
-            "Could not create geometry because of errors while reading input.")
-    return geom
-
-def loads(data):
-    """Load a geometry from a WKB string."""
-    from shapely.geometry.base import geom_factory
-    return geom_factory(deserialize(data))
-
-def load(fp):
+def loads(data, hex=False):
+    """Load a geometry from a WKB byte string, or hex-encoded string if
+    ``hex=True``.
+    """
+    reader = geos.WKBReader(geos.lgeos)
+    if hex:
+        return reader.read_hex(data)
+    else:
+        return reader.read(data)
+
+def load(fp, hex=False):
     """Load a geometry from an open file."""
     data = fp.read()
-    return loads(data)
+    return loads(data, hex=hex)
 
-def dumps(ob):
-    """Dump a WKB representation of a geometry to a byte string."""
-    if ob is None or ob._geom is None:
-        raise ValueError("Null geometry supports no operations")
-    size = c_size_t()
-    return lgeos.GEOSGeomToWKB_buf(c_void_p(ob._geom), pointer(size))
+def dumps(ob, hex=False, **kw):
+    """Dump a WKB representation of a geometry to a byte string, or a
+    hex-encoded string if ``hex=True``.
+
+    See available keyword output settings in ``shapely.geos.WKBWriter``."""
+    writer = geos.WKBWriter(geos.lgeos, **kw)
+    if hex:
+        return writer.write_hex(ob)
+    else:
+        return writer.write(ob)
 
-def dump(ob, fp):
-    """Dump a geometry to an open file."""
-    fp.write(dumps(ob))
 
+def dump(ob, fp, hex=False, **kw):
+    """Dump a geometry to an open file."""
+    fp.write(dumps(ob, hex=hex, **kw))
diff --git a/shapely/wkt.py b/shapely/wkt.py
index c5650ed..51ffbfa 100644
--- a/shapely/wkt.py
+++ b/shapely/wkt.py
@@ -1,34 +1,26 @@
 """Load/dump geometries using the well-known text (WKT) format
 """
 
-from ctypes import c_char_p
-
-from shapely.geos import lgeos, ReadingError
-
+from shapely import geos
 
 # Pickle-like convenience functions
 
 def loads(data):
     """Load a geometry from a WKT string."""
-    from shapely.geometry.base import geom_factory
-    geom = lgeos.GEOSGeomFromWKT(c_char_p(data))
-    if not geom:
-        raise ReadingError, \
-        "Could not create geometry because of errors while reading input."
-    return geom_factory(geom)
+    return geos.WKTReader(geos.lgeos).read(data)
 
 def load(fp):
     """Load a geometry from an open file."""
     data = fp.read()
     return loads(data)
 
-def dumps(ob):
-    """Dump a WKB representation of a geometry to a byte string."""
-    if ob is None or ob._geom is None:
-        raise ValueError("Null geometry supports no operations")
-    return lgeos.GEOSGeomToWKT(ob._geom)
+def dumps(ob, **kw):
+    """Dump a WKT representation of a geometry to a string.
 
-def dump(ob, fp):
-    """Dump a geometry to an open file."""
-    fp.write(dumps(ob))
+    See available keyword output settings in ``shapely.geos.WKTWriter``.
+    """
+    return geos.WKTWriter(geos.lgeos, **kw).write(ob)
 
+def dump(ob, fp, **settings):
+    """Dump a geometry to an open file."""
+    fp.write(dumps(ob, **settings))
diff --git a/tools/README.txt b/tools/README.txt
new file mode 100644
index 0000000..e69de29

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