Repository: Toblerity/Shapely
Pull request #459 (2017-01-22 15:17:27)

Interpolate section
Added a section operation (https://github.com/Toblerity/Shapely/issues/456) for review.



Patch:
diff --git a/.travis.yml b/.travis.yml
index 13b029d..9a03247 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -35,15 +35,15 @@ matrix:
         - SPEEDUPS=0
         - NUMPY=1
 
-    - python: "3.6"
+    - python: "3.5"
       env:
         - SPEEDUPS=1
         - NUMPY=1
-    - python: "3.6"
+    - python: "3.5"
       env:
         - SPEEDUPS=0
         - NUMPY=1
-    - python: "3.6"
+    - python: "3.5"
       env:
         - SPEEDUPS=0
         - NUMPY=0
@@ -64,7 +64,7 @@ before_install:
   - if [ "$SPEEDUPS" == "1" ]; then SPEEDUPS_FLAG=--with-speedups; else SPEEDUPS_FLAG=--without-speedups; fi
 
 install:
-  - pip install -e .[all]
+  - pip install -e .[test]
   - pip install coveralls
 
 script:
@@ -72,6 +72,6 @@ script:
 
 after_success:
   - coveralls || echo "!! intermittent coveralls failure"
-
+  
 notifications:
     email: false
diff --git a/CHANGES.txt b/CHANGES.txt
index 8dd19ad..7ddc32d 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,69 +1,8 @@
 Changes
 =======
 
-1.6.2 (2017-10-26)
-------------------
-
-- Handle a ``TypeError`` that can occur when geometries are torn down (#473,
-  #528).
-- Splitting a linestring by one of its end points will now succeed instead of
-  failing with a ``ValueError`` (#524, #533).
-- Missing documentation of a geometry's ``overlaps`` predicate has been added
-  (#522).
-
-1.6.1 (2017-09-01)
-------------------
-
-- Avoid ``STRTree`` crashes due to dangling references (#505) by maintaining
-  references to added geometries.
-- Reduce log level to debug when reporting on calls to ctypes ``CDLL()`` that
-  don't succeed and are retried (#515).
-- Clarification: applications like GeoPandas that need an empty geometry object
-  should use ``BaseGeometry()`` instead of ``Point()`` or ``Polygon()``. An
-  ``EmptyGeometry`` class has been added in the master development branch and
-  will be available in the next non-bugfix release.
-
-1.6.0 (2017-08-21)
-------------------
-
-Shapely 1.6.0 adds new attributes to existing geometry classes and new
-functions (``split()`` and ``polylabel()``) to the shapely.ops module.
-Exceptions are consolidated in a shapely.errors module and logging practices
-have been improved. Shapely's optional features depending on Numpy are now
-gathered into a requirements set named "vectorized" and these may be installed
-like ``pip install shapely[vectorized]``.
-
-Much of the work on 1.6.0 was aimed to improve the project's build and
-packaging scripts and to minimize run-time dependencies. Shapely now vendorizes
-packaging to use during builds only and never again invokes the geos-config
-utility at run-time.
-
-In addition to the changes listed under the alpha and beta pre-releases below,
-the following change has been made to the project:
-
-- Project documentation is now hosted at 
-  https://shapely.readthedocs.io/en/latest/.
-
-Thank you all for using, promoting, and contributing to the Shapely project.
-
-1.6b5 (2017-08-18)
-------------------
-
-Bug fixes:
-
-- Passing a single coordinate to ``LineString()`` with speedups disabled now
-  raises a ValueError as happens with speedups enabled. This resolves #509.
-
-1.6b4 (2017-02-15)
-------------------
-
-Bug fixes:
-
-- Isolate vendorized packaging in a _vendor directory, remove obsolete
-  dist-info, and remove packaging from project requirements (resolves #468).
-
-1.6b3 (2016-12-31)
-------------------
+1.6b3 (soon)
+------------
 
 Bug fixes:
 
diff --git a/CREDITS.txt b/CREDITS.txt
index d1c2c84..abca549 100644
--- a/CREDITS.txt
+++ b/CREDITS.txt
@@ -8,17 +8,16 @@ Shapely is written by:
 * Joshua Arnott <josh@snorfalorpagus.net>
 * Mike Toews <mwtoews@gmail.com>
 * Jacob Wasserman <jwasserman@gmail.com>
-* Aron Bierbaum <aronbierbaum@gmail.com>
+* aronbierbaum <aronbierbaum@b426a367-1105-0410-b9ff-cdf4ab011145>
 * Allan Adair <allan@rfspot.com>
 * Johannes Schönberger <jschoenberger@demuc.de>
 * georgeouzou <geothrock@gmail.com>
 * Phil Elson <pelson.pub@gmail.com>
 * Howard Butler <hobu.inc@gmail.com>
-* Kelsey Jordahl <kjordahl@enthought.com>
 * dokai <dokai@b426a367-1105-0410-b9ff-cdf4ab011145>
-* Kevin Wurster <kevin@skytruth.org>
 * Gabi Davar <grizzly.nyo@gmail.com>
-* Thibault Deutsch <thibault.deutsch@gmail.com>
+* Kevin Wurster <kevin@skytruth.org>
+* Kelsey Jordahl <kjordahl@enthought.com>
 * Dave Collins <dave@hopest.net>
 * fredj <frederic.junod@camptocamp.com>
 * Brad Hards <bradh@frogmouth.net>
@@ -34,18 +33,16 @@ Shapely is written by:
 * Jeethu Rao <jeethu@jeethurao.com>
 * Peter Sagerson <psagers.github@ignorare.net>
 * Jason Sanford <jason.sanford@mapmyfitness.com>
-* mindw <grizzly.nyo@gmail.com>
 * Jamie Hall <jamie1212@gmail.com>
 * James Spencer <james.s.spencer@gmail.com>
 * Stephan Hügel <urschrei@gmail.com>
-* Bas Couwenberg <sebastic@xs4all.nl>
-* James Douglass <jamesdouglassusa@gmail.com>
+* Benjamin Root <ben.v.root@gmail.com>
 * Tobias Sauerwein <tobias.sauerwein@camptocamp.com>
 * WANG Aiyong <gepcelway@gmail.com>
+* James Douglass <jamesdouglassusa@gmail.com>
 * Brandon Wood <btwood@geometeor.com>
-* BertrandGervais <bertrand.gervais.pro@gmail.com>
 * Andy Freeland <andy@andyfreeland.net>
-* Benjamin Root <ben.v.root@gmail.com>
+* BertrandGervais <bertrand.gervais.pro@gmail.com>
 * giumas <gmasetti@ccom.unh.edu>
 * Leandro Lima <leandro@limaesilva.com.br>
 * Maarten Vermeyen <maarten.vermeyen@rwo.vlaanderen.be>
@@ -59,6 +56,7 @@ 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
diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md
deleted file mode 100644
index 764f8ef..0000000
--- a/ISSUE_TEMPLATE.md
+++ /dev/null
@@ -1,25 +0,0 @@
-## Please note
-
-If you are reporting an installation or module import issue, please note that
-this project only accepts reports about problems with packages downloaded from
-the Python Package Index. Conda users should take issues to one of the
-following trackers:
-
-- https://github.com/ContinuumIO/anaconda-issues/issues
-- https://github.com/conda-forge/shapely-feedstock
-
-## Expected behavior and actual behavior.
-
-(For example: the area of my geometry is `1.417` when it should be `1.414`.)
-
-## Steps to reproduce the problem.
-
-(For example, a script with required data)
-
-## Operating system
-
-(For example, Mac OS X 10.12.3)
-
-## Shapely version and provenance
-
-(For example, 1.6b4 installed from PyPI using pip)
diff --git a/MANIFEST.in b/MANIFEST.in
index d968426..2903f9e 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -6,7 +6,8 @@ prune DLLs_x86
 exclude *.txt
 exclude MANIFEST.in
 include CHANGES.txt CREDITS.txt LICENSE.txt README.rst VERSION.txt
-recursive-include _vendor *.py
+recursive-include packaging *.py
+recursive-include packaging-15.3.dist-info *
 recursive-include tests *.py *.txt
 recursive-include shapely/examples *.py
 recursive-include shapely/speedups *.pyx
diff --git a/README.rst b/README.rst
index 078e43c..45d10ad 100644
--- a/README.rst
+++ b/README.rst
@@ -2,7 +2,7 @@
 Shapely
 =======
 
-.. image:: https://travis-ci.org/Toblerity/Shapely.svg?branch=master
+.. image:: https://travis-ci.org/Toblerity/Shapely.png?branch=master
    :target: https://travis-ci.org/Toblerity/Shapely
 
 .. image:: https://coveralls.io/repos/github/Toblerity/Shapely/badge.svg?branch=master
@@ -18,66 +18,63 @@ Shapely is a BSD-licensed Python package for manipulation and analysis of
 planar geometric objects. It is based on the widely deployed `GEOS
 <http://trac.osgeo.org/geos/>`__ (the engine of `PostGIS
 <http://postgis.org>`__) and `JTS
-<https://locationtech.github.io/jts/>`__ (from which GEOS is ported)
+<http://www.vividsolutions.com/jts/jtshome.htm>`__ (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 GitHub repository <https://github.com/Toblerity/Shapely>`__
-* `Shapely documentation and manual <https://shapely.readthedocs.io/en/latest/>`__
+* Shapely on `GitHub <https://github.com/Toblerity/Shapely>`__
+* The Shapely `manual <http://toblerity.github.com/shapely/manual.html>`__
 
 Requirements
 ============
 
-Shapely 1.6 requires
+Shapely 1.6.x requires
 
 * Python >=2.6 (including Python 3.x)
 * GEOS >=3.3 
 
-Installing Shapely 1.6
-======================
-
-Shapely may be installed from a source distribution or one of several kinds
-of built distribution.
-
-Built distributions
--------------------
+Installing Shapely (1.6b2)
+==========================
 
 Windows users have two good installation options: the wheels at
 http://www.lfd.uci.edu/~gohlke/pythonlibs/#shapely and the 
-Anaconda platform's `conda-forge <https://conda-forge.github.io/>`__
+Anaconda platform's [conda-forge](https://conda-forge.github.io/)
 channel.
 
-OS X and Linux users can get Shapely wheels with GEOS included from the 
+OS X users can get Shapely wheels with GEOS included from the 
 Python Package Index with a recent version of pip (8+):
 
 .. code-block:: console
 
-    $ pip install shapely
+    $ pip install shapely==1.6b2
 
 A few extra speedups that require Numpy can be had by running
 
 .. code-block:: console
 
-    $ pip install shapely[vectorized]
-
-Shapely is available via system package management tools like apt, yum, and
-Homebrew, and is also provided by popular Python distributions like Canopy and
-Anaconda.
-
-Source distributions
---------------------
+    $ pip install shapely[vectorized]==1.6b2
 
 If you want to build Shapely from source for compatibility with
-other modules that depend on GEOS (such as cartopy or osgeo.ogr) or want to
-use a different version of GEOS than the one included in the project wheels
-you should first install the GEOS library, Cython, and Numpy on your system
-(using apt, yum, brew, or other means) and then direct pip to ignore the binary
-wheels.
+other modules that depend on GEOS (such as cartopy or osgeo.ogr)
+you may ignore the binary wheels.
 
 .. code-block:: console
 
     $ pip install shapely --no-binary shapely
 
+Binary wheels are also available for Linux. To get them, use `pip shapely
+shapely==1.6b2`. To avoid them, use `pip install shapely --no-binary
+shapely==1.6b2`.
+
+In other situations, install `geos_c` libs and headers by any means 
+(for example, `brew install geos` on OS X or
+`apt-get install libgeos-dev` on Debian/Ubuntu) and install Shapely
+from the Python package index.
+
+.. code-block:: console
+
+    $ pip install shapely
+
 If you've installed GEOS to a standard location, the geos-config program
 will be used to get compiler and linker options. If geos-config is not on
 your executable, it can be specified with a GEOS_CONFIG environment
@@ -87,6 +84,9 @@ variable, e.g.:
 
     $ GEOS_CONFIG=/path/to/geos-config pip install shapely
 
+Shapely is also provided by popular Python distributions like Canopy
+(Enthought) and Anaconda (Continuum Analytics).
+
 Usage
 =====
 
diff --git a/_vendor/__init__.py b/_vendor/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/docs/conf.py b/docs/conf.py
index 0d575a5..fe3b911 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -121,7 +121,7 @@ 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.6 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
diff --git a/docs/manual.rst b/docs/manual.rst
index 234a28a..25ced98 100644
--- a/docs/manual.rst
+++ b/docs/manual.rst
@@ -5,7 +5,7 @@ The Shapely User Manual
 =======================
 
 :Author: Sean Gillies, <sean.gillies@gmail.com>
-:Version: 1.6
+:Version: 1.2 and 1.3
 :Date: |today|
 :Copyright:
   This work is licensed under a `Creative Commons Attribution 3.0
@@ -442,7 +442,7 @@ constructor parameter.
 Polygons
 --------
 
-.. class:: Polygon(shell [,holes=None])
+.. class:: Polygon(exterior [,interiors=None])
 
   The `Polygon` constructor takes two positional parameters. The first is an
   ordered sequence of ``(x, y[, z])`` point tuples and is treated exactly as in
@@ -790,54 +790,6 @@ longer empty.
   >>> line.bounds
   (0.0, 0.0, 1.0, 1.0)
 
-Coordinate sequences
---------------------
-
-The list of coordinates that describe a geometry are represented as the
-``CoordinateSequence`` object. These sequences should not be initialised
-directly, but can be accessed from an existing geometry as the
-``Geometry.coords`` property.
-
-.. code-block:: pycon
-
-  >>> line = LineString([(0, 1), (2, 3), (4, 5)])
-  >>> line.coords
-  <shapely.coords.CoordinateSequence object at 0x00000276EED1C7F0>
-
-Coordinate sequences can be indexed, sliced and iterated over as if they were a
-list of coordinate tuples.
-
-.. code-block:: pycon
-
-  >>> line.coords[0]
-  (0.0, 1.0)
-  >>> line.coords[1:]
-  [(2.0, 3.0), (4.0, 5.0)]
-  >>> for x, y in line.coords:
-  ...     print("x={}, y={}".format(x, y))
-  ...
-  x=0.0, y=1.0
-  x=2.0, y=3.0
-  x=4.0, y=5.0
-
-Polygons have a coordinate sequence for their exterior and each of their
-interior rings.
-
-.. code-block:: pycon
-
-  >>> poly = Polygon([(0, 0), (0, 1), (1, 1), (0, 0)])
-  >>> poly.exterior.coords
-  <shapely.coords.CoordinateSequence object at 0x00000276EED1C048>
-
-Multipart geometries do not have a coordinate sequence. Instead the coordinate
-sequences are stored on their component geometries.
-
-.. code-block:: pycon
-
-  >>> p = MultiPoint([(0, 0), (1, 1), (2, 2)])
-  >>> p[2].coords
-  <shapely.coords.CoordinateSequence object at 0x00000276EFB9B320>
-
 Linear Referencing Methods
 --------------------------
 
@@ -1190,14 +1142,9 @@ This predicate applies to all types and is the inverse of :meth:`intersects`.
   Returns ``True`` if the `boundary` or `interior` of the object intersect in
   any way with those of the other.
 
-In other words, geometric objects intersect if they have any boundary or
+In other words, geometric objects intersect if they have any boundary or 
 interior point in common.
 
-.. method:: object.overlaps(other)
-
-  Returns ``True`` if the objects intersect (see above) but neither contains
-  the other.
-
 .. method:: object.touches(other)
 
   Returns ``True`` if the objects have at least one point in common and their
@@ -1356,13 +1303,6 @@ Almost every binary predicate method has a counterpart that returns a new
 geometric object. In addition, the set-theoretic `boundary` of an object is
 available as a read-only attribute.
 
-.. note::
-
-  These methods will `always` return a geometric object. An intersection of
-  disjoint geometries for example will return an empty `GeometryCollection`,
-  not `None` or `False`. To test for a non-empty result, use the geometry's
-  :ref:`is_empty` property.
-
 .. attribute:: object.boundary
 
   Returns a lower dimensional object representing the object's set-theoretic
@@ -1581,10 +1521,8 @@ With a `resolution` of 1, the buffer is a square patch.
   >>> q.area
   200.0
 
-Passed a `distance` of 0, :meth:`buffer` can sometimes be used to "clean" self-touching
-or self-crossing polygons such as the classic "bowtie". Users have reported
-that very small distance values sometimes produce cleaner results than 0. Your
-mileage may vary when cleaning surfaces.
+Passed a `distance` of 0, :meth:`buffer` can be used to "clean" self-touching
+or self-crossing polygons such as the classic "bowtie".
 
 .. code-block:: pycon
 
@@ -1639,16 +1577,16 @@ Figure 10. Convex hull (blue) of 2 points (left) and of 6 points (right).
   <shapely.geometry.polygon.Polygon object at 0x...>
 
 .. attribute:: object.minimum_rotated_rectangle
-
-  Returns the general minimum bounding rectangle that contains the object.
-  Unlike envelope this rectangle is not constrained to be parallel to the
-  coordinate axes. If the convex hull of the object is a degenerate (line or point)
+  
+  Returns the general minimum bounding rectangle that contains the object. 
+  Unlike envelope this rectangle is not constrained to be parallel to the 
+  coordinate axes. If the convex hull of the object is a degenerate (line or point) 
   this degenerate is returned.
 
   `New in Shapely 1.6.0`
 
 .. code-block:: pycon
-
+  
   >>> Point(0, 0).minimum_rotated_rectangle
   <shapely.geometry.point.Point object at 0x...>
   >>> MultiPoint([(0,0),(1,1),(2,0.5)]).minimum_rotated_rectangle
@@ -1656,7 +1594,7 @@ Figure 10. Convex hull (blue) of 2 points (left) and of 6 points (right).
 
 .. plot:: code/minimum_rotated_rectangle.py
 
-Figure 11. Minimum rotated rectangle for a multipoint feature (left) and a
+Figure 11. Minimum rotated rectangle for a multipoint feature (left) and a 
 linestring feature (right).
 
 .. method:: object.parallel_offset(distance, side, resolution=16, join_style=1, mitre_limit=5.0)
@@ -1724,9 +1662,7 @@ Figure 14. Simplification of a nearly circular polygon using a tolerance of 0.2
 .. note::
 
   `Invalid` geometric objects may result from simplification that does not
-  preserve topology and simplification may be sensitive to the order of
-  coordinates: two geometries differing only in order of coordinates may be
-  simplified differently.
+  preserve topology.
 
 
 Affine Transformations
@@ -2192,12 +2128,12 @@ paths between two lineal geometries.
 
    Finds the shared paths between `geom1` and `geom2`, where both geometries
    are `LineStrings`.
-
+   
    A `GeometryCollection` is returned with two elements. The first element is a
    `MultiLineString` containing shared paths with the same direction for both
    inputs. The second element is a MultiLineString containing shared paths with
    the opposite direction for the two inputs.
-
+   
    `New in version 1.6.0`
 
 .. code-block:: pycon
@@ -2220,11 +2156,11 @@ The :func:`~shapely.ops.split` function in `shapely.ops` splits a geometry by an
 
    Splits a geometry by another geometry and returns a collection of geometries. This function is the theoretical
    opposite of the union of the split geometry parts. If the splitter does not split the geometry, a  collection with a single geometry equal to the input geometry is returned.
-
+  
    The function supports:
 
    * Splitting a (Multi)LineString by a (Multi)Point or (Multi)LineString or (Multi)Polygon boundary
-
+   
    * Splitting a (Multi)Polygon by a LineString
 
    It may be convenient to snap the splitter with low tolerance to the geometry. For example in the case of splitting a line by a point, the point must be exactly on the line, for the line to be correctly split.
@@ -2241,6 +2177,31 @@ The :func:`~shapely.ops.split` function in `shapely.ops` splits a geometry by an
   >>> result.wkt
   'GEOMETRYCOLLECTION (LINESTRING (0 0, 1 1), LINESTRING (1 1, 2 2))'
 
+Substring
+---------
+
+The :func:`~shapely.ops.substring` function in `shapely.ops` returns a line segment between specified distances along a linear geometry.
+
+.. function:: shapely.ops.substring(geom, start_dist, end_dist[, normalized=False])
+
+    Negative distance values are taken as measured in the reverse
+    direction from the end of the geometry. Out-of-range index
+    values are handled by clamping them to the valid range of values.
+    
+    If the start distance equals the end distance, a point is being returned.
+    
+    If the normalized arg is True, the distance will be interpreted as a
+    fraction of the geometry's length.
+
+   `New in version 1.6.0`
+
+.. code-block:: pycon
+
+  >>> line = LineString(([0, 0], [2, 0]))
+  >>> result = substring(line, 0.5, 0.6)
+  >>> result.wkt
+  'LINESTRING (0.5 0, 0.6 0)'
+
 Prepared Geometry Operations
 ----------------------------
 
@@ -2453,19 +2414,7 @@ and a N x 2 array can be adapted to a line string
   >>> la.wkt
   'LINESTRING (1.0000000000000000 2.0000000000000000, 3.0000000000000000 4.0000000000000000)'
 
-Polygon and MultiPoint can also be created from N x 2 arrays:
-
-.. code-block:: pycon
-
-  >>> from shapely.geometry import asMultiPoint
-  >>> ma = asMultiPoint(np.array([[1.1, 2.2], [3.3, 4.4], [5.5, 6.6]]))
-  >>> ma.wkt
-  'MULTIPOINT (1.1 2.2, 3.3 4.4, 5.5 6.6)'
-
-  >>> from shapely.geometry import asPolygon
-  >>> pa = asPolygon(np.array([[1.1, 2.2], [3.3, 4.4], [5.5, 6.6]]))
-  >>> pa.wkt
-  'POLYGON ((1.1 2.2, 3.3 4.4, 5.5 6.6, 1.1 2.2))'
+There is no Numpy array representation of a polygon.
 
 Python Geo Interface
 --------------------
@@ -2545,7 +2494,7 @@ involves some overhead that might slow down your code.
 
 The :mod:`shapely.speedups` module contains performance enhancements written in
 C. They are automatically installed when Python has access to a compiler and
-GEOS development headers during installation.
+GEOS development headers during installation. 
 
 You can check if the speedups are installed with the :attr:`available`
 attribute. To enable the speedups call :func:`enable`. You can revert to the
diff --git a/environment.yml b/environment.yml
deleted file mode 100644
index 1715107..0000000
--- a/environment.yml
+++ /dev/null
@@ -1,11 +0,0 @@
-name: _shapely
-channels:
-- defaults
-- conda-forge
-dependencies:
-- python>=3.5
-- cython
-- descartes
-- geos>=3.3
-- matplotlib
-- numpy>=1.9
diff --git a/packaging-15.3.dist-info/DESCRIPTION.rst b/packaging-15.3.dist-info/DESCRIPTION.rst
new file mode 100644
index 0000000..e373b33
--- /dev/null
+++ b/packaging-15.3.dist-info/DESCRIPTION.rst
@@ -0,0 +1,121 @@
+packaging
+=========
+
+Core utilities for Python packages
+
+
+Documentation
+-------------
+
+`documentation`_
+
+
+Discussion
+----------
+
+If you run into bugs, you can file them in our `issue tracker`_.
+
+You can also join ``#pypa`` on Freenode to ask questions or get involved.
+
+
+.. _`documentation`: https://packaging.pypa.io/
+.. _`issue tracker`: https://github.com/pypa/packaging/issues
+
+Changelog
+---------
+
+15.3 - 2015-08-01
+~~~~~~~~~~~~~~~~~
+
+* Normalize post-release spellings for rev/r prefixes. :issue:`35`
+
+
+15.2 - 2015-05-13
+~~~~~~~~~~~~~~~~~
+
+* Fix an error where the arbitary specifier (``===``) was not correctly
+  allowing pre-releases when it was being used.
+
+* Expose the specifier and version parts through properties on the
+  ``Specifier`` classes.
+
+* Allow iterating over the ``SpecifierSet`` to get access to all of the
+  ``Specifier`` instances.
+
+* Allow testing if a version is contained within a specifier via the ``in``
+  operator.
+
+
+15.1 - 2015-04-13
+~~~~~~~~~~~~~~~~~
+
+* Fix a logic error that was causing inconsistent answers about whether or not
+  a pre-release was contained within a ``SpecifierSet`` or not.
+
+
+15.0 - 2015-01-02
+~~~~~~~~~~~~~~~~~
+
+* Add ``Version().is_postrelease`` and ``LegacyVersion().is_postrelease`` to
+  make it easy to determine if a release is a post release.
+
+* Add ``Version().base_version`` and ``LegacyVersion().base_version`` to make
+  it easy to get the public version without any pre or post release markers.
+
+* Support the update to PEP 440 which removed the implied ``!=V.*`` when using
+  either ``>V`` or ``<V`` and which instead special cased the handling of
+  pre-releases, post-releases, and local versions when using ``>V`` or ``<V``.
+
+
+14.5 - 2014-12-17
+~~~~~~~~~~~~~~~~~
+
+* Normalize release candidates as ``rc`` instead of ``c``.
+
+* Expose the ``VERSION_PATTERN`` constant, a regular expression matching
+  a valid version.
+
+
+14.4 - 2014-12-15
+~~~~~~~~~~~~~~~~~
+
+* Ensure that versions are normalized before comparison when used in a
+  specifier with a less than (``<``) or greater than (``>``) operator.
+
+
+14.3 - 2014-11-19
+~~~~~~~~~~~~~~~~~
+
+* **BACKWARDS INCOMPATIBLE** Refactor specifier support so that it can sanely
+  handle legacy specifiers as well as PEP 440 specifiers.
+
+* **BACKWARDS INCOMPATIBLE** Move the specifier support out of
+  ``packaging.version`` into ``packaging.specifiers``.
+
+
+14.2 - 2014-09-10
+~~~~~~~~~~~~~~~~~
+
+* Add prerelease support to ``Specifier``.
+* Remove the ability to do ``item in Specifier()`` and replace it with
+  ``Specifier().contains(item)`` in order to allow flags that signal if a
+  prerelease should be accepted or not.
+* Add a method ``Specifier().filter()`` which will take an iterable and returns
+  an iterable with items that do not match the specifier filtered out.
+
+
+14.1 - 2014-09-08
+~~~~~~~~~~~~~~~~~
+
+* Allow ``LegacyVersion`` and ``Version`` to be sorted together.
+* Add ``packaging.version.parse()`` to enable easily parsing a version string
+  as either a ``Version`` or a ``LegacyVersion`` depending on it's PEP 440
+  validity.
+
+
+14.0 - 2014-09-05
+~~~~~~~~~~~~~~~~~
+
+* Initial release.
+
+
diff --git a/packaging-15.3.dist-info/METADATA b/packaging-15.3.dist-info/METADATA
new file mode 100644
index 0000000..a353e5a
--- /dev/null
+++ b/packaging-15.3.dist-info/METADATA
@@ -0,0 +1,141 @@
+Metadata-Version: 2.0
+Name: packaging
+Version: 15.3
+Summary: Core utilities for Python packages
+Home-page: https://github.com/pypa/packaging
+Author: Donald Stufft
+Author-email: donald@stufft.io
+License: Apache License, Version 2.0
+Platform: UNKNOWN
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: Apache Software License
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 2
+Classifier: Programming Language :: Python :: 2.6
+Classifier: Programming Language :: Python :: 2.7
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3.2
+Classifier: Programming Language :: Python :: 3.3
+Classifier: Programming Language :: Python :: 3.4
+
+packaging
+=========
+
+Core utilities for Python packages
+
+
+Documentation
+-------------
+
+`documentation`_
+
+
+Discussion
+----------
+
+If you run into bugs, you can file them in our `issue tracker`_.
+
+You can also join ``#pypa`` on Freenode to ask questions or get involved.
+
+
+.. _`documentation`: https://packaging.pypa.io/
+.. _`issue tracker`: https://github.com/pypa/packaging/issues
+
+Changelog
+---------
+
+15.3 - 2015-08-01
+~~~~~~~~~~~~~~~~~
+
+* Normalize post-release spellings for rev/r prefixes. :issue:`35`
+
+
+15.2 - 2015-05-13
+~~~~~~~~~~~~~~~~~
+
+* Fix an error where the arbitary specifier (``===``) was not correctly
+  allowing pre-releases when it was being used.
+
+* Expose the specifier and version parts through properties on the
+  ``Specifier`` classes.
+
+* Allow iterating over the ``SpecifierSet`` to get access to all of the
+  ``Specifier`` instances.
+
+* Allow testing if a version is contained within a specifier via the ``in``
+  operator.
+
+
+15.1 - 2015-04-13
+~~~~~~~~~~~~~~~~~
+
+* Fix a logic error that was causing inconsistent answers about whether or not
+  a pre-release was contained within a ``SpecifierSet`` or not.
+
+
+15.0 - 2015-01-02
+~~~~~~~~~~~~~~~~~
+
+* Add ``Version().is_postrelease`` and ``LegacyVersion().is_postrelease`` to
+  make it easy to determine if a release is a post release.
+
+* Add ``Version().base_version`` and ``LegacyVersion().base_version`` to make
+  it easy to get the public version without any pre or post release markers.
+
+* Support the update to PEP 440 which removed the implied ``!=V.*`` when using
+  either ``>V`` or ``<V`` and which instead special cased the handling of
+  pre-releases, post-releases, and local versions when using ``>V`` or ``<V``.
+
+
+14.5 - 2014-12-17
+~~~~~~~~~~~~~~~~~
+
+* Normalize release candidates as ``rc`` instead of ``c``.
+
+* Expose the ``VERSION_PATTERN`` constant, a regular expression matching
+  a valid version.
+
+
+14.4 - 2014-12-15
+~~~~~~~~~~~~~~~~~
+
+* Ensure that versions are normalized before comparison when used in a
+  specifier with a less than (``<``) or greater than (``>``) operator.
+
+
+14.3 - 2014-11-19
+~~~~~~~~~~~~~~~~~
+
+* **BACKWARDS INCOMPATIBLE** Refactor specifier support so that it can sanely
+  handle legacy specifiers as well as PEP 440 specifiers.
+
+* **BACKWARDS INCOMPATIBLE** Move the specifier support out of
+  ``packaging.version`` into ``packaging.specifiers``.
+
+
+14.2 - 2014-09-10
+~~~~~~~~~~~~~~~~~
+
+* Add prerelease support to ``Specifier``.
+* Remove the ability to do ``item in Specifier()`` and replace it with
+  ``Specifier().contains(item)`` in order to allow flags that signal if a
+  prerelease should be accepted or not.
+* Add a method ``Specifier().filter()`` which will take an iterable and returns
+  an iterable with items that do not match the specifier filtered out.
+
+
+14.1 - 2014-09-08
+~~~~~~~~~~~~~~~~~
+
+* Allow ``LegacyVersion`` and ``Version`` to be sorted together.
+* Add ``packaging.version.parse()`` to enable easily parsing a version string
+  as either a ``Version`` or a ``LegacyVersion`` depending on it's PEP 440
+  validity.
+
+
+14.0 - 2014-09-05
+~~~~~~~~~~~~~~~~~
+
+* Initial release.
+
+
diff --git a/packaging-15.3.dist-info/RECORD b/packaging-15.3.dist-info/RECORD
new file mode 100644
index 0000000..eed1cde
--- /dev/null
+++ b/packaging-15.3.dist-info/RECORD
@@ -0,0 +1,13 @@
+packaging/__about__.py,sha256=YzdrW-1lWmyCBDyrcNkZbJo4tiDWXpoiqPjfyCYMzIE,1073
+packaging/__init__.py,sha256=2V8n-eEpSgBuXlV8hlMmhU7ZklpsrrusWMZNp2gC4Hs,906
+packaging/_compat.py,sha256=wofog8iYo_zudt_10i6JiXKHDs5GhCuXC09hCuSJiv4,1253
+packaging/_structures.py,sha256=93YvgrEE2HgFp8AdXy0pwCRVnZeutRHO_-puJ7T0cPw,1809
+packaging/specifiers.py,sha256=UV9T01_kKloA8PSeMI3HTYBSJ_4KLs00yLvrlciZ3yU,28079
+packaging/version.py,sha256=dEGrWZJZ6sef1xMxSfDCego2hS3Q86by0hUIFVk-AGc,11949
+packaging-15.3.dist-info/DESCRIPTION.rst,sha256=Gd817S15Dv8sdxUJ9pM4t5Gq_ZYflsJYB64gItd9vM8,3108
+packaging-15.3.dist-info/METADATA,sha256=VV19NM69tso91JP9TM8Pdq3CDomcP6bdn-0q3YUeNvE,3854
+packaging-15.3.dist-info/metadata.json,sha256=RpvEfDwiZ5KQreMlvUvsqF-u5MCqXRNIVml3neejEj0,859
+packaging-15.3.dist-info/pbr.json,sha256=8NuqguCaMTQfH_1HqSV4DBKcPE0Uy_lxkONIfDvWT7Y,46
+packaging-15.3.dist-info/RECORD,,
+packaging-15.3.dist-info/top_level.txt,sha256=zFdHrhWnPslzsiP455HutQsqPB6v0KCtNUMtUtrefDw,10
+packaging-15.3.dist-info/WHEEL,sha256=AvR0WeTpDaxT645bl5FQxUK6NPsTls2ttpcGJg3j1Xg,110
diff --git a/packaging-15.3.dist-info/WHEEL b/packaging-15.3.dist-info/WHEEL
new file mode 100644
index 0000000..9dff69d
--- /dev/null
+++ b/packaging-15.3.dist-info/WHEEL
@@ -0,0 +1,6 @@
+Wheel-Version: 1.0
+Generator: bdist_wheel (0.24.0)
+Root-Is-Purelib: true
+Tag: py2-none-any
+Tag: py3-none-any
+
diff --git a/packaging-15.3.dist-info/metadata.json b/packaging-15.3.dist-info/metadata.json
new file mode 100644
index 0000000..e4b5041
--- /dev/null
+++ b/packaging-15.3.dist-info/metadata.json
@@ -0,0 +1,1 @@
+{"license": "Apache License, Version 2.0", "summary": "Core utilities for Python packages", "extensions": {"python.details": {"project_urls": {"Home": "https://github.com/pypa/packaging"}, "contacts": [{"role": "author", "name": "Donald Stufft", "email": "donald@stufft.io"}], "document_names": {"description": "DESCRIPTION.rst"}}}, "generator": "bdist_wheel (0.24.0)", "version": "15.3", "metadata_version": "2.0", "name": "packaging", "classifiers": ["Intended Audience :: Developers", "License :: OSI Approved :: Apache Software License", "Programming Language :: Python", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.6", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.2", "Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.4"]}
\ No newline at end of file
diff --git a/packaging-15.3.dist-info/pbr.json b/packaging-15.3.dist-info/pbr.json
new file mode 100644
index 0000000..693661c
--- /dev/null
+++ b/packaging-15.3.dist-info/pbr.json
@@ -0,0 +1,1 @@
+{"git_version": "099fe4e", "is_release": true}
\ No newline at end of file
diff --git a/packaging-15.3.dist-info/top_level.txt b/packaging-15.3.dist-info/top_level.txt
new file mode 100644
index 0000000..748809f
--- /dev/null
+++ b/packaging-15.3.dist-info/top_level.txt
@@ -0,0 +1,1 @@
+packaging
diff --git a/_vendor/packaging/__about__.py b/packaging/__about__.py
similarity index 100%
rename from _vendor/packaging/__about__.py
rename to packaging/__about__.py
diff --git a/_vendor/packaging/__init__.py b/packaging/__init__.py
similarity index 100%
rename from _vendor/packaging/__init__.py
rename to packaging/__init__.py
diff --git a/_vendor/packaging/_compat.py b/packaging/_compat.py
similarity index 100%
rename from _vendor/packaging/_compat.py
rename to packaging/_compat.py
diff --git a/_vendor/packaging/_structures.py b/packaging/_structures.py
similarity index 100%
rename from _vendor/packaging/_structures.py
rename to packaging/_structures.py
diff --git a/_vendor/packaging/markers.py b/packaging/markers.py
similarity index 100%
rename from _vendor/packaging/markers.py
rename to packaging/markers.py
diff --git a/_vendor/packaging/requirements.py b/packaging/requirements.py
similarity index 100%
rename from _vendor/packaging/requirements.py
rename to packaging/requirements.py
diff --git a/_vendor/packaging/specifiers.py b/packaging/specifiers.py
similarity index 100%
rename from _vendor/packaging/specifiers.py
rename to packaging/specifiers.py
diff --git a/_vendor/packaging/utils.py b/packaging/utils.py
similarity index 100%
rename from _vendor/packaging/utils.py
rename to packaging/utils.py
diff --git a/_vendor/packaging/version.py b/packaging/version.py
similarity index 100%
rename from _vendor/packaging/version.py
rename to packaging/version.py
diff --git a/readthedocs.yml b/readthedocs.yml
deleted file mode 100644
index df1e840..0000000
--- a/readthedocs.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-python:
-  version: 3
-  pip_install: true
-conda:
-  file: environment.yml
diff --git a/requirements-dev.txt b/requirements-dev.txt
index eaa7f4c..8180010 100644
--- a/requirements-dev.txt
+++ b/requirements-dev.txt
@@ -2,4 +2,5 @@ setuptools
 Numpy>=1.4.1
 Cython>=0.19
 descartes==1.0.1
+packaging
 pytest
diff --git a/setup.cfg b/setup.cfg
index 9eba825..25c6497 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,6 +1,2 @@
 [tool:pytest]
 testpaths = tests
-addopts = --ignore _vendor
-
-[bdist_wheel]
-universal=1
diff --git a/setup.py b/setup.py
index 96d6d38..7418d5a 100755
--- a/setup.py
+++ b/setup.py
@@ -73,7 +73,7 @@ except ImportError:
 from distutils.errors import CCompilerError, DistutilsExecError, \
     DistutilsPlatformError
 
-from _vendor.packaging.version import Version
+from packaging.version import Version
 
 # Get geos_version from GEOS dynamic library, which depends on
 # GEOS_LIBRARY_PATH and/or GEOS_CONFIG environment variables
@@ -178,7 +178,7 @@ with open('CHANGES.txt', 'r', **open_kwds) as fp:
 long_description = readme + '\n\n' + credits + '\n\n' + changes
 
 extra_reqs = {
-    'test': ['pytest', 'pytest-cov'],
+    'test': ['pytest', 'pytest-cov', 'packaging'],
     'vectorized': ['numpy']}
 
 extra_reqs['all'] = list(it.chain.from_iterable(extra_reqs.values()))
@@ -358,8 +358,8 @@ try:
         include_dirs=include_dirs,
         library_dirs=library_dirs,
         libraries=libraries,
-        extra_link_args=extra_link_args,))
-
+        extra_link_args=extra_link_args,
+    ))
 except ImportError:
     log.info("Numpy or Cython not available, shapely.vectorized submodule "
              "not being built.")
diff --git a/shapely/__init__.py b/shapely/__init__.py
index 51bbb3f..2060dbb 100644
--- a/shapely/__init__.py
+++ b/shapely/__init__.py
@@ -1,1 +1,1 @@
-__version__ = "1.6.2"
+__version__ = "1.6b3"
diff --git a/shapely/geometry/base.py b/shapely/geometry/base.py
index e953501..79455b6 100644
--- a/shapely/geometry/base.py
+++ b/shapely/geometry/base.py
@@ -225,7 +225,7 @@ class BaseGeometry(object):
         if not self._is_empty and not self._other_owned and self.__geom__:
             try:
                 self._lgeos.GEOSGeom_destroy(self.__geom__)
-            except (AttributeError, TypeError):
+            except AttributeError:
                 pass  # _lgeos might be empty on shutdown
         self._is_empty = True
         self.__geom__ = val
@@ -699,11 +699,7 @@ class BaseGeometry(object):
         return bool(self.impl['disjoint'](self, other))
 
     def equals(self, other):
-        """Returns True if geometries are equal, else False
-        
-        Refers to point-set equality (or topological equality), and is equivalent to
-        (self.within(other) & self.contains(other))
-        """
+        """Returns True if geometries are equal, else False"""
         return bool(self.impl['equals'](self, other))
 
     def intersects(self, other):
@@ -724,20 +720,12 @@ class BaseGeometry(object):
 
     def equals_exact(self, other, tolerance):
         """Returns True if geometries are equal to within a specified
-        tolerance
-        
-        Refers to coordinate equality, which requires coordinates to be equal 
-        and in the same order for all components of a geometry
-        """
+        tolerance"""
         return bool(self.impl['equals_exact'](self, other, tolerance))
 
     def almost_equals(self, other, decimal=6):
         """Returns True if geometries are equal at all coordinates to a
-        specified decimal place
-
-        Refers to approximate coordinate equality, which requires coordinates be
-        approximately equal and in the same order for all components of a geometry.
-        """
+        specified decimal place"""
         return self.equals_exact(other, 0.5 * 10**(-decimal))
 
     def relate_pattern(self, other, pattern):
@@ -767,6 +755,9 @@ class BaseGeometry(object):
     def interpolate(self, distance, normalized=False):
         """Return a point at the specified distance along a linear geometry
 
+        Negative length values are taken as measured in the reverse
+        direction from the end of the geometry. Out-of-range index
+        values are handled by clamping them to the valid range of values.
         If the normalized arg is True, the distance will be interpreted as a
         fraction of the geometry's length.
         """
@@ -955,12 +946,6 @@ class HeterogeneousGeometrySequence(GeometrySequence):
         return g
 
 
-class EmptyGeometry(BaseGeometry):
-    def __init__(self):
-        """Create an empty geometry."""
-        BaseGeometry.__init__(self)
-
-
 def _test():
     """Test runner"""
     import doctest
diff --git a/shapely/geometry/linestring.py b/shapely/geometry/linestring.py
index 578656d..9bba6cf 100644
--- a/shapely/geometry/linestring.py
+++ b/shapely/geometry/linestring.py
@@ -6,7 +6,7 @@ import sys
 if sys.version_info[0] < 3:
     range = xrange
 
-from ctypes import c_double
+from ctypes import c_double, cast, POINTER
 
 from shapely.geos import lgeos, TopologicalError
 from shapely.geometry.base import (
@@ -210,12 +210,6 @@ def geos_linestring_from_py(ob, update_geom=None, update_ndim=0):
 
     if m == 0:
         return None
-    elif m == 1:
-        raise ValueError("LineStrings must have at least 2 coordinate tuples")
-
-    if m < 2:
-        raise ValueError(
-            "LineStrings must have at least 2 coordinate tuples")
 
     def _coords(o):
         if isinstance(o, Point):
@@ -256,10 +250,7 @@ def geos_linestring_from_py(ob, update_geom=None, update_ndim=0):
     if update_geom is not None:
         return None
     else:
-        ptr = lgeos.GEOSGeom_createLineString(cs)
-        if not ptr:
-            raise ValueError("GEOSGeom_createLineString returned a NULL pointer")
-        return ptr, n
+        return lgeos.GEOSGeom_createLineString(cs), n
 
 
 def update_linestring_from_py(geom, ob):
diff --git a/shapely/geometry/multilinestring.py b/shapely/geometry/multilinestring.py
index ca432df..39b94fb 100644
--- a/shapely/geometry/multilinestring.py
+++ b/shapely/geometry/multilinestring.py
@@ -6,7 +6,7 @@ import sys
 if sys.version_info[0] < 3:
     range = xrange
 
-from ctypes import c_void_p, cast
+from ctypes import c_double, c_void_p, cast, POINTER
 
 from shapely.geos import lgeos
 from shapely.geometry.base import BaseMultipartGeometry, geos_geom_from_py
diff --git a/shapely/geometry/multipoint.py b/shapely/geometry/multipoint.py
index ab55cb7..55c5d65 100644
--- a/shapely/geometry/multipoint.py
+++ b/shapely/geometry/multipoint.py
@@ -6,7 +6,8 @@ import sys
 if sys.version_info[0] < 3:
     range = xrange
 
-from ctypes import byref, c_double, c_void_p, cast
+from ctypes import byref, c_double, c_void_p, cast, POINTER
+from ctypes import ArgumentError
 
 from shapely.geos import lgeos
 from shapely.geometry.base import (
diff --git a/shapely/geometry/point.py b/shapely/geometry/point.py
index fed086f..70d0eeb 100644
--- a/shapely/geometry/point.py
+++ b/shapely/geometry/point.py
@@ -2,6 +2,7 @@
 """
 
 from ctypes import c_double
+from ctypes import cast, POINTER
 
 from shapely.errors import DimensionError
 from shapely.geos import lgeos
@@ -127,8 +128,6 @@ class Point(BaseGeometry):
         self.empty()
         if len(args) == 1:
             self._geom, self._ndim = geos_point_from_py(args[0])
-        elif len(args) > 3:
-            raise TypeError("Point() takes at most 3 arguments ({} given)".format(len(args)))
         else:
             self._geom, self._ndim = geos_point_from_py(tuple(args))
 
diff --git a/shapely/geometry/polygon.py b/shapely/geometry/polygon.py
index da5f5df..9602df5 100644
--- a/shapely/geometry/polygon.py
+++ b/shapely/geometry/polygon.py
@@ -6,7 +6,8 @@ import sys
 if sys.version_info[0] < 3:
     range = xrange
 
-from ctypes import c_void_p, cast, POINTER
+from ctypes import c_double, c_void_p, cast, POINTER
+from ctypes import ArgumentError
 import weakref
 
 from shapely.algorithms.cga import signed_area
diff --git a/shapely/geometry/proxy.py b/shapely/geometry/proxy.py
index 71b6110..c9cad3a 100644
--- a/shapely/geometry/proxy.py
+++ b/shapely/geometry/proxy.py
@@ -1,7 +1,7 @@
 """Proxy for coordinates stored outside Shapely geometries
 """
 
-from shapely.geometry.base import EMPTY
+from shapely.geometry.base import deserialize_wkb, EMPTY
 from shapely.geos import lgeos
 
 
diff --git a/shapely/geos.py b/shapely/geos.py
index 28ea95d..b9d66f6 100644
--- a/shapely/geos.py
+++ b/shapely/geos.py
@@ -32,7 +32,7 @@ def load_dll(libname, fallbacks=None, mode=DEFAULT_MODE):
             LOG.debug("Trying `CDLL(%s)`", lib)
             dll = CDLL(lib, mode=mode)
         except OSError:
-            LOG.debug("Failed `CDLL(%s)`", lib)
+            LOG.warn("Failed `CDLL(%s)`", lib)
             pass
 
     if not dll and fallbacks is not None:
@@ -42,7 +42,7 @@ def load_dll(libname, fallbacks=None, mode=DEFAULT_MODE):
                 dll = CDLL(name, mode=mode)
             except OSError:
                 # move on to the next fallback
-                LOG.debug("Failed `CDLL(%s)`", name)
+                LOG.warn("Failed `CDLL(%s)`", name)
                 pass
 
     if dll:
@@ -65,19 +65,12 @@ if sys.platform.startswith('linux'):
     if len(geos_whl_so) == 1:
         _lgeos = CDLL(geos_whl_so[0])
         LOG.debug("Found GEOS DLL: %r, using it.", _lgeos)
-    elif hasattr(sys, 'frozen'):
-        geos_pyinstaller_so = glob.glob(os.path.join(sys.prefix,
-                                                    'libgeos_c-*.so.*'))
-        if len(geos_pyinstaller_so) == 1:
-            _lgeos = CDLL(geos_pyinstaller_so[0])
-            LOG.debug("Found GEOS DLL: %r, using it.", _lgeos)
-    elif os.getenv('CONDA_PREFIX', ''):
-        # conda package.
-        _lgeos = CDLL(os.path.join(sys.prefix, 'lib', 'libgeos_c.so'))
     else:
         alt_paths = [
             'libgeos_c.so.1',
             'libgeos_c.so',
+            # anaconda
+            os.path.join(sys.prefix, "lib", "libgeos_c.so"),
         ]
         _lgeos = load_dll('geos_c', fallbacks=alt_paths)
     free = load_dll('c').free
@@ -91,9 +84,7 @@ elif sys.platform == 'darwin':
     if os.path.exists(geos_whl_dylib):
         _lgeos = CDLL(geos_whl_dylib)
         LOG.debug("Found GEOS DLL: %r, using it.", _lgeos)
-    elif os.getenv('CONDA_PREFIX', ''):
-        # conda package.
-        _lgeos = CDLL(os.path.join(sys.prefix, 'lib', 'libgeos_c.dylib'))
+
     else:
         if hasattr(sys, 'frozen'):
             try:
@@ -110,6 +101,8 @@ elif sys.platform == 'darwin':
                         os.path.join(sys._MEIPASS, 'libgeos_c.1.dylib'))
         else:
             alt_paths = [
+                # anaconda
+                os.path.join(sys.prefix, "lib", "libgeos_c.dylib"),
                 # The Framework build from Kyng Chaos
                 "/Library/Frameworks/GEOS.framework/Versions/Current/GEOS",
                 # macports
@@ -122,31 +115,29 @@ elif sys.platform == 'darwin':
     free.restype = None
 
 elif sys.platform == 'win32':
-    if os.getenv('CONDA_PREFIX', ''):
-        # conda package.
-        _lgeos = CDLL(os.path.join(sys.prefix, 'Library', 'bin', 'geos_c.dll'))
-    else:
+    try:
+        egg_dlls = os.path.abspath(
+            os.path.join(os.path.dirname(__file__), 'DLLs'))
+        if hasattr(sys, "frozen"):
+            wininst_dlls = os.path.normpath(
+                os.path.abspath(sys.executable + '../../DLLS'))
+        else:
+            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)
+        _lgeos = load_dll("geos_c.dll", fallbacks=[
+            os.path.join(sys.prefix, "Library", "lib", "geos_c.dll"),
+        ])
+    except (ImportError, WindowsError, OSError):
+        raise
+
+    def free(m):
         try:
-            egg_dlls = os.path.abspath(
-                os.path.join(os.path.dirname(__file__), 'DLLs'))
-            if hasattr(sys, "frozen"):
-                wininst_dlls = os.path.normpath(
-                    os.path.abspath(sys.executable + '../../DLLS'))
-            else:
-                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)
-            _lgeos = load_dll("geos_c.dll")
-        except (ImportError, WindowsError, OSError):
-            raise
-
-        def free(m):
-            try:
-                cdll.msvcrt.free(m)
-            except WindowsError:
-                # XXX: See http://trac.gispython.org/projects/PCL/ticket/149
-                pass
+            cdll.msvcrt.free(m)
+        except WindowsError:
+            # XXX: See http://trac.gispython.org/projects/PCL/ticket/149
+            pass
 
 elif sys.platform == 'sunos5':
     _lgeos = load_dll('geos_c', fallbacks=['libgeos_c.so.1', 'libgeos_c.so'])
diff --git a/shapely/iterops.py b/shapely/iterops.py
index a0c7deb..4b15b84 100644
--- a/shapely/iterops.py
+++ b/shapely/iterops.py
@@ -2,6 +2,7 @@
 Iterative forms of operations
 """
 
+from shapely.geos import PredicateError
 from shapely.topology import Delegating
 
 
diff --git a/shapely/ops.py b/shapely/ops.py
index f1d091b..2bfe3a6 100644
--- a/shapely/ops.py
+++ b/shapely/ops.py
@@ -13,11 +13,12 @@ from ctypes import byref, c_void_p, c_double
 from shapely.geos import lgeos
 from shapely.geometry.base import geom_factory, BaseGeometry
 from shapely.geometry import asShape, asLineString, asMultiLineString, Point, MultiPoint,\
-                             LineString, MultiLineString, Polygon, GeometryCollection
+                             LineString, MultiLineString, Polygon, MultiPolygon, GeometryCollection
 from shapely.algorithms.polylabel import polylabel
 
 __all__ = ['cascaded_union', 'linemerge', 'operator', 'polygonize',
-           'polygonize_full', 'transform', 'unary_union', 'triangulate', 'split']
+           'polygonize_full', 'transform', 'unary_union', 'triangulate',
+           'split', 'section']
 
 
 class CollectionOperator(object):
@@ -257,7 +258,7 @@ def transform(func, geom):
 
 def nearest_points(g1, g2):
     """Returns the calculated nearest points in the input geometries
-
+    
     The points are returned in the same order as the input geometries.
     """
     seq = lgeos.methods['nearest_points'](g1._geom, g2._geom)
@@ -339,21 +340,21 @@ class SplitOp(object):
         union = poly.boundary.union(splitter)
 
         # some polygonized geometries may be holes, we do not want them
-        # that's why we test if the original polygon (poly) contains
+        # that's why we test if the original polygon (poly) contains 
         # an inner point of polygonized geometry (pg)
         return [pg for pg in polygonize(union) if poly.contains(pg.representative_point())]
 
     @staticmethod
     def _split_line_with_line(line, splitter):
         """Split a LineString with another (Multi)LineString or (Multi)Polygon"""
-
+        
         # if splitter is a polygon, pick it's boundary
         if splitter.type in ('Polygon', 'MultiPolygon'):
             splitter = splitter.boundary
 
         assert(isinstance(line, LineString))
         assert(isinstance(splitter, LineString) or isinstance(splitter, MultiLineString))
-
+        
         if splitter.crosses(line):
             # The lines cross --> return multilinestring from the split
             return line.difference(splitter)
@@ -364,7 +365,7 @@ class SplitOp(object):
             # The lines do not cross --> return collection with identity line
             return [line]
 
-    @staticmethod
+    @staticmethod  
     def _split_line_with_point(line, splitter):
         """Split a LineString with a Point"""
 
@@ -374,13 +375,10 @@ class SplitOp(object):
         # check if point is in the interior of the line
         if not line.relate_pattern(splitter, '0********'):
             # point not on line interior --> return collection with single identity line
-            # (REASONING: Returning a list with the input line reference and creating a
-            # GeometryCollection at the general split function prevents unnecessary copying
+            # (REASONING: Returning a list with the input line reference and creating a 
+            # GeometryCollection at the general split function prevents unnecessary copying 
             # of linestrings in multipoint splitting function)
             return [line]
-        elif line.coords[0] == splitter.coords[0]:
-            # if line is a closed ring the previous test doesn't behave as desired
-            return [line]
 
         # point is on line, get the distance from the first point on line
         distance_on_line = line.project(splitter)
@@ -391,7 +389,7 @@ class SplitOp(object):
             pd = line.project(Point(p))
             if pd == distance_on_line:
                 return [
-                    LineString(coords[:i+1]),
+                    LineString(coords[:i+1]), 
                     LineString(coords[i:])
                 ]
             elif distance_on_line < pd:
@@ -409,17 +407,17 @@ class SplitOp(object):
 
         assert(isinstance(line, LineString))
         assert(isinstance(splitter, MultiPoint))
-
+        
         chunks = [line]
         for pt in splitter.geoms:
             new_chunks = []
-            for chunk in filter(lambda x: not x.is_empty, chunks):
+            for chunk in chunks:
                 # add the newly split 2 lines or the same line if not split
                 new_chunks.extend(SplitOp._split_line_with_point(chunk, pt))
             chunks = new_chunks
-
+        
         return chunks
-
+    
     @staticmethod
     def split(geom, splitter):
         """
@@ -476,3 +474,56 @@ class SplitOp(object):
         return GeometryCollection(split_func(geom, splitter))
 
 split = SplitOp.split
+
+
+def substring(geom, start_dist, end_dist, normalized=False):
+    """Return a line segment between specified distances along a linear geometry.
+
+    Negative distance values are taken as measured in the reverse
+    direction from the end of the geometry. Out-of-range index
+    values are handled by clamping them to the valid range of values.
+    If the start distances equals the end distance, a point is being returned.
+    If the normalized arg is True, the distance will be interpreted as a
+    fraction of the geometry's length.
+    """
+
+    assert(isinstance(geom, LineString))
+    
+    # Filter out cases in which to return a point
+    if start_dist == end_dist:
+        return geom.interpolate(start_dist, normalized)
+    elif not normalized and start_dist >= geom.length and end_dist >= geom.length:
+        return geom.interpolate(geom.length, normalized)
+    elif not normalized and -start_dist >= geom.length and -end_dist >= geom.length:
+        return geom.interpolate(0, normalized)                    
+    elif normalized and start_dist >= 1 and end_dist >= 1:
+        return geom.interpolate(1, normalized)  
+    elif normalized and -start_dist >= 1 and -end_dist >= 1:
+        return geom.interpolate(0, normalized)
+
+    start_point = geom.interpolate(start_dist, normalized)
+    end_point = geom.interpolate(end_dist, normalized)
+    
+    min_dist = min(start_dist, end_dist)
+    max_dist = max(start_dist, end_dist)
+    if normalized:
+        min_dist *= geom.length
+        max_dist *= geom.length
+    
+    vertex_list = [(start_point.x, start_point.y)]
+    coords = list(geom.coords)
+    for i, p in enumerate(coords):
+        pd = geom.project(Point(p))
+        if pd < min_dist:
+            pass
+        elif min_dist < pd < max_dist:
+            vertex_list.append(p)
+        else:
+            break
+    vertex_list.append((end_point.x, end_point.y))
+
+    # reverse direction of section
+    if start_dist > end_dist:
+        vertex_list = reversed(vertex_list)
+
+    return LineString(vertex_list)
diff --git a/shapely/predicates.py b/shapely/predicates.py
index 5170077..eff42c9 100644
--- a/shapely/predicates.py
+++ b/shapely/predicates.py
@@ -2,7 +2,7 @@
 Support for GEOS spatial predicates
 """
 
-from shapely.geos import PredicateError
+from shapely.geos import PredicateError, TopologicalError
 from shapely.topology import Delegating
 
 
diff --git a/shapely/speedups/_speedups.pyx b/shapely/speedups/_speedups.pyx
index d336d2f..8754c1b 100644
--- a/shapely/speedups/_speedups.pyx
+++ b/shapely/speedups/_speedups.pyx
@@ -149,10 +149,6 @@ def geos_linestring_from_py(ob, update_geom=None, update_ndim=0):
         if m == 0:
             return None
 
-        if m < 2:
-            raise ValueError(
-                "LineStrings must have at least 2 coordinate tuples")
-
         def _coords(o):
             if isinstance(o, Point):
                 return o.coords[0]
@@ -197,10 +193,7 @@ def geos_linestring_from_py(ob, update_geom=None, update_ndim=0):
     if update_geom is not None:
         return None
     else:
-        g = GEOSGeom_createLineString_r(handle, cs);
-        if not g:
-            raise ValueError("GEOSGeom_createLineString_r returned a NULL pointer")
-        return <uintptr_t>g, n
+        return <uintptr_t>GEOSGeom_createLineString_r(handle, cs), n
 
 
 def geos_linearring_from_py(ob, update_geom=None, update_ndim=0):
@@ -377,10 +370,7 @@ def geos_linearring_from_py(ob, update_geom=None, update_ndim=0):
     if update_geom is not None:
         return None
     else:
-        g = GEOSGeom_createLinearRing_r(handle, cs)
-        if not g:
-            raise ValueError("GEOSGeom_createLinearRing_r returned a NULL pointer")
-        return <uintptr_t>g, n
+        return <uintptr_t>GEOSGeom_createLinearRing_r(handle, cs), n
 
 
 def coordseq_ctypes(self):
@@ -460,24 +450,17 @@ cdef GEOSCoordSequence* transform(GEOSCoordSequence* cs,
     cs_t = GEOSCoordSeq_create_r(handle, m, ndim)
     
     # perform the transform
-    if ndim == 2:
-        for n in range(0, m):
-            GEOSCoordSeq_getX_r(handle, cs, n, &x)
-            GEOSCoordSeq_getY_r(handle, cs, n, &y)
-            x_t = a * x + b * y + xoff
-            y_t = d * x + e * y + yoff
-            GEOSCoordSeq_setX_r(handle, cs_t, n, x_t)
-            GEOSCoordSeq_setY_r(handle, cs_t, n, y_t)
+    for n in range(0, m):
+        GEOSCoordSeq_getX_r(handle, cs, n, &x)
+        GEOSCoordSeq_getY_r(handle, cs, n, &y)
+        x_t = a * x + b * y + xoff
+        y_t = d * x + e * y + yoff
+        GEOSCoordSeq_setX_r(handle, cs_t, n, x_t)
+        GEOSCoordSeq_setY_r(handle, cs_t, n, y_t)
     if ndim == 3:
         for n in range(0, m):
-            GEOSCoordSeq_getX_r(handle, cs, n, &x)
-            GEOSCoordSeq_getY_r(handle, cs, n, &y)
             GEOSCoordSeq_getZ_r(handle, cs, n, &z)
-            x_t = a * x + b * y + c * z + xoff
-            y_t = d * x + e * y + f * z + yoff
             z_t = g * x + h * y + i * z + zoff
-            GEOSCoordSeq_setX_r(handle, cs_t, n, x_t)
-            GEOSCoordSeq_setY_r(handle, cs_t, n, y_t)
             GEOSCoordSeq_setZ_r(handle, cs_t, n, z_t)
     
     return cs_t
diff --git a/shapely/strtree.py b/shapely/strtree.py
index 161554d..7abe685 100644
--- a/shapely/strtree.py
+++ b/shapely/strtree.py
@@ -39,9 +39,6 @@ class STRtree:
         for geom in geoms:
             lgeos.GEOSSTRtree_insert(self._tree_handle, geom._geom, ctypes.py_object(geom))
 
-        # Keep references to geoms.
-        self._geoms = list(geoms)
-
     def __del__(self):
         lgeos.GEOSSTRtree_destroy(self._tree_handle)
 
diff --git a/shapely/validation.py b/shapely/validation.py
index 541a779..cb87074 100644
--- a/shapely/validation.py
+++ b/shapely/validation.py
@@ -1,5 +1,7 @@
 # TODO: allow for implementations using other than GEOS
 
+import sys
+
 from shapely.geos import lgeos
 
 def explain_validity(ob):


Test patch:
diff --git a/tests/conftest.py b/tests/conftest.py
index 9cf4ac0..b4295b3 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -4,9 +4,9 @@ import pytest
 
 def pytest_addoption(parser):
     parser.addoption("--with-speedups", action="store_true", default=False,
-                     help="Run tests with speedups.")
+        help="Run tests with speedups.")
     parser.addoption("--without-speedups", action="store_true", default=False,
-                     help="Run tests without speedups.")
+        help="Run tests without speedups.")
 
 def pytest_runtest_setup(item):
     if item.config.getoption("--with-speedups"):
diff --git a/tests/test_emptiness.py b/tests/test_emptiness.py
index 91d5ec2..541224e 100644
--- a/tests/test_emptiness.py
+++ b/tests/test_emptiness.py
@@ -1,5 +1,5 @@
 from . import unittest
-from shapely.geometry.base import BaseGeometry, EmptyGeometry
+from shapely.geometry.base import BaseGeometry
 import shapely.geometry as sgeom
 from shapely.geometry.polygon import LinearRing
 
@@ -7,10 +7,6 @@ empty_generator = lambda: iter([])
 
 class EmptinessTestCase(unittest.TestCase):
 
-    def test_empty_class(self):
-        g = EmptyGeometry()
-        self.assertTrue(g._is_empty)
-
     def test_empty_base(self):
         g = BaseGeometry()
         self.assertTrue(g._is_empty)
diff --git a/tests/test_linear_referencing.py b/tests/test_linear_referencing.py
index e1da7c5..3b8e370 100644
--- a/tests/test_linear_referencing.py
+++ b/tests/test_linear_referencing.py
@@ -43,8 +43,11 @@ class LinearReferencingTestCase(unittest.TestCase):
     @unittest.skipIf(geos_version < (3, 2, 0), 'GEOS 3.2.0 required')
     def test_line1_interpolate(self):
         self.assertTrue(self.line1.interpolate(0.5).equals(Point(0.5, 0.0)))
+        self.assertTrue(self.line1.interpolate(-0.5).equals(Point(1.5, 0.0)))
         self.assertTrue(
             self.line1.interpolate(0.5, normalized=True).equals(Point(1, 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):
diff --git a/tests/test_linestring.py b/tests/test_linestring.py
index 6fd9093..90c5fa8 100644
--- a/tests/test_linestring.py
+++ b/tests/test_linestring.py
@@ -1,5 +1,4 @@
 from . import unittest, numpy
-import pytest
 from shapely.geos import lgeos
 from shapely.geometry import LineString, asLineString, Point, LinearRing
 
@@ -61,17 +60,17 @@ class LineStringTestCase(unittest.TestCase):
     def test_equals_argument_order(self):
         """
         Test equals predicate functions correctly regardless of the order
-        of the inputs. See issue #317.
+        of the inputs. See issue #317. 
         """
         coords = ((0, 0), (1, 0), (1, 1), (0, 0))
         ls = LineString(coords)
         lr = LinearRing(coords)
-
+        
         self.assertFalse(ls.__eq__(lr))  # previously incorrectly returned True
         self.assertFalse(lr.__eq__(ls))
         self.assertFalse(ls == lr)
         self.assertFalse(lr == ls)
-
+        
         ls_clone = LineString(coords)
         lr_clone = LinearRing(coords)
 
@@ -109,13 +108,6 @@ class LineStringTestCase(unittest.TestCase):
         self.assertEqual('LineString',
                          lgeos.GEOSGeomType(copy._geom).decode('ascii'))
 
-    def test_from_single_coordinate(self):
-        """Test for issue #486"""
-        coords = [[-122.185933073564, 37.3629353839073]]
-        with pytest.raises(ValueError):
-            ls = LineString(coords)
-            ls.geom_type # caused segfault before fix
-
 
     @unittest.skipIf(not numpy, 'Numpy required')
     def test_numpy(self):
@@ -165,7 +157,7 @@ class LineStringTestCase(unittest.TestCase):
         # Test array interface of empty linestring
         le = LineString()
         a = asarray(le)
-        self.assertEqual(a.shape[0], 0)
+        assert(a.shape[0], 0)
 
 
 def test_suite():
diff --git a/tests/test_point.py b/tests/test_point.py
index 8135aa5..452d034 100644
--- a/tests/test_point.py
+++ b/tests/test_point.py
@@ -78,10 +78,6 @@ class LineStringTestCase(unittest.TestCase):
         p_null.coords = (1, 2)
         self.assertEqual(p_null.coords[:], [(1.0, 2.0)])
 
-        # Passing > 3 arguments to Point is erroneous
-        with self.assertRaises(TypeError):
-            Point(1.0, 2.0, 3.0, 4.0)
-
     @unittest.skipIf(not numpy, 'Numpy required')
     def test_numpy(self):
 
diff --git a/tests/test_split.py b/tests/test_split.py
index c18f088..dcaae0b 100644
--- a/tests/test_split.py
+++ b/tests/test_split.py
@@ -7,7 +7,7 @@ from shapely.ops import cascaded_union, linemerge
 
 class TestSplitGeometry(unittest.TestCase):
 	# helper class for testing below
-	def helper(self, geom, splitter, expected_chunks):
+	def helper(self, geom, splitter, expected_chunks):	
 		s = split(geom, splitter)
 		self.assertEqual(s.type, "GeometryCollection")
 		self.assertEqual(len(s), expected_chunks)
@@ -25,14 +25,6 @@ class TestSplitGeometry(unittest.TestCase):
 			# not split --> expected equal to line
 			self.assertTrue(s[0].equals(geom))
 
-	def test_split_closed_line_with_point(self):
-		# point at start/end of closed ring -> return equal
-		# see GH #524
-		ls = LineString([(0,0), (0, 1), (1, 1), (1, 0), (0, 0)])
-		splitter = Point(0, 0)
-		self.helper(ls, splitter, 1)
-
-
 class TestSplitPolygon(TestSplitGeometry):
 	poly_simple = Polygon([(0, 0), (2, 0), (2, 2), (0, 2), (0, 0)])
 	poly_hole = Polygon([(0, 0), (2, 0), (2, 2), (0, 2), (0, 0)], [[(0.5, 0.5), (0.5, 1.5), (1.5, 1.5), (1.5, 0.5), (0.5, 0.5)]])
@@ -102,7 +94,7 @@ class TestSplitLine(TestSplitGeometry):
 	def test_split_line_with_line(self):
 		# crosses at one point --> return 2 segments
 		splitter = LineString([(0, 1), (1, 0)])
-		self.helper(self.ls, splitter, 2)
+		self.helper(self.ls, splitter, 2)	
 
 		# crosses at two points --> return 3 segments
 		splitter = LineString([(0, 1), (1, 0), (1, 2)])
@@ -125,7 +117,7 @@ class TestSplitLine(TestSplitGeometry):
 	def test_split_line_with_multiline(self):
 		# crosses at one point --> return 2 segments
 		splitter = MultiLineString([[(0, 1), (1, 0)], [(0, 0), (2, -2)]])
-		self.helper(self.ls, splitter, 2)
+		self.helper(self.ls, splitter, 2)	
 
 		# crosses at two points --> return 3 segments
 		splitter = MultiLineString([[(0, 1), (1, 0)], [(0, 2), (2, 0)]])
@@ -166,7 +158,7 @@ class TestSplitLine(TestSplitGeometry):
 		self.helper(self.ls, splitter, 4)
 
 class TestSplitMulti(TestSplitGeometry):
-
+	
 	def test_split_multiline_with_point(self):
 		# a cross-like multilinestring with a point in the middle --> return 4 line segments
 		l1 = LineString([(0, 1), (2, 1)])
@@ -174,27 +166,27 @@ class TestSplitMulti(TestSplitGeometry):
 		ml = MultiLineString([l1, l2])
 		splitter = Point((1, 1))
 		self.helper(ml, splitter, 4)
-
+		
 	def test_split_multiline_with_multipoint(self):
-		# a cross-like multilinestring with a point in middle, a point on one of the lines and a point in the exterior
+		# a cross-like multilinestring with a point in middle, a point on one of the lines and a point in the exterior 
 		# --> return 4+1 line segments
 		l1 = LineString([(0, 1), (3, 1)])
 		l2 = LineString([(1, 0), (1, 2)])
 		ml = MultiLineString([l1, l2])
 		splitter = MultiPoint([(1, 1), (2, 1), (4, 2)])
 		self.helper(ml, splitter, 5)
-
+				
 	def test_split_multipolygon_with_line(self):
 		# two polygons with a crossing line --> return 4 triangles
-		poly1 = Polygon([(0, 0), (1, 0), (1, 1), (0, 1), (0, 0)])
+		poly1 = Polygon([(0, 0), (1, 0), (1, 1), (0, 1), (0, 0)]) 
 		poly2 = Polygon([(1, 1), (1, 2), (2, 2), (2, 1), (1, 1)])
 		mpoly = MultiPolygon([poly1, poly2])
 		ls = LineString([(-1, -1), (3, 3)])
 		self.helper(mpoly, ls, 4)
 
 		# two polygons away from the crossing line --> return identity
-		poly1 = Polygon([(10, 10), (10, 11), (11, 11), (11, 10), (10, 10)])
+		poly1 = Polygon([(10, 10), (10, 11), (11, 11), (11, 10), (10, 10)]) 
 		poly2 = Polygon([(-10, -10), (-10, -11), (-11, -11), (-11, -10), (-10, -10)])
 		mpoly = MultiPolygon([poly1, poly2])
 		ls = LineString([(-1, -1), (3, 3)])
-		self.helper(mpoly, ls, 2)
+		self.helper(mpoly, ls, 2)
\ No newline at end of file
diff --git a/tests/test_strtree.py b/tests/test_strtree.py
index 8eb5dcf..372a987 100644
--- a/tests/test_strtree.py
+++ b/tests/test_strtree.py
@@ -1,5 +1,3 @@
-import gc
-
 from . import unittest
 
 from shapely.strtree import STRtree
@@ -44,23 +42,6 @@ class STRTestCase(unittest.TestCase):
         self.assertEqual(len(results), 1)
         self.assertEqual(results[0], point)
 
-    def test_references(self):
-        """Don't crash due to dangling references"""
-        empty = Polygon()
-        point = Point(1, 0.5)
-        geoms = [empty, point]
-        tree = STRtree(geoms)
-        assert(tree._n_geoms == 1)
-
-        empty = None
-        point = None
-        gc.collect()
-
-        query = Polygon([(0,0),(1,1),(2,0),(0,0)])
-        results = tree.query(query)
-        self.assertEqual(len(results), 1)
-        self.assertEqual(results[0], Point(1, 0.5))
-
 
 def test_suite():
     return unittest.TestLoader().loadTestsFromTestCase(STRTestCase)
diff --git a/tests/test_substring.py b/tests/test_substring.py
new file mode 100644
index 0000000..b2015a2
--- /dev/null
+++ b/tests/test_substring.py
@@ -0,0 +1,75 @@
+from . import unittest
+from shapely.ops import substring
+from shapely.geos import geos_version
+from shapely.geometry import Point, LineString
+
+
+class SubstringTestCase(unittest.TestCase):
+
+    def setUp(self):
+        self.point = Point(1, 1)
+        self.line1 = LineString(([0, 0], [2, 0]))
+        self.line2 = LineString(([3, 0], [3, 6], [4.5, 6]))
+  
+    @unittest.skipIf(geos_version < (3, 2, 0), 'GEOS 3.2.0 required')
+    def test_return_startpoint(self):
+        self.assertTrue(substring(self.line1, -500, -600).equals(Point(0, 0)))
+        self.assertTrue(substring(self.line1, -500, -500).equals(Point(0, 0)))
+        self.assertTrue(substring(self.line1, -1, -1.1, True).equals(Point(0, 0)))
+        self.assertTrue(substring(self.line1, -1.1, -1.1, True).equals(Point(0, 0)))               
+
+    @unittest.skipIf(geos_version < (3, 2, 0), 'GEOS 3.2.0 required')
+    def test_return_endpoint(self):
+        self.assertTrue(substring(self.line1, 500, 600).equals(Point(2, 0)))
+        self.assertTrue(substring(self.line1, 500, 500).equals(Point(2, 0)))
+        self.assertTrue(substring(self.line1, 1, 1.1, True).equals(Point(2, 0)))   
+        self.assertTrue(substring(self.line1, 1.1, 1.1, True).equals(Point(2, 0)))
+
+    @unittest.skipIf(geos_version < (3, 2, 0), 'GEOS 3.2.0 required')
+    def test_return_midpoint(self):
+        self.assertTrue(substring(self.line1, 0.5, 0.5).equals(Point(0.5, 0)))        
+        self.assertTrue(substring(self.line1, -0.5, -0.5).equals(Point(1.5, 0)))
+        self.assertTrue(substring(self.line1, 0.5, 0.5, True).equals(Point(1, 0)))        
+        self.assertTrue(substring(self.line1, -0.5, -0.5, True).equals(Point(1, 0)))       
+
+    @unittest.skipIf(geos_version < (3, 2, 0), 'GEOS 3.2.0 required')
+    def test_return_startsubstring(self):
+        self.assertTrue(substring(self.line1, -500, 0.6).equals(LineString(([0, 0], [0.6, 0]))))
+        self.assertTrue(substring(self.line1, -1.1, 0.6, True).equals(LineString(([0, 0], [1.2, 0]))))
+
+    @unittest.skipIf(geos_version < (3, 2, 0), 'GEOS 3.2.0 required')
+    def test_return_endsubstring(self):
+        self.assertTrue(substring(self.line1, 0.6, 500).equals(LineString(([0.6, 0], [2, 0]))))
+        self.assertTrue(substring(self.line1, 0.6, 1.1, True).equals(LineString(([1.2, 0], [2, 0]))))
+
+    @unittest.skipIf(geos_version < (3, 2, 0), 'GEOS 3.2.0 required')
+    def test_return_midsubstring(self):
+        self.assertTrue(substring(self.line1, 0.5, 0.6).equals(LineString(([0.5, 0], [0.6, 0]))))
+        self.assertTrue(substring(self.line1, 0.6, 0.5).equals(LineString(([0.6, 0], [0.5, 0]))))
+        self.assertTrue(substring(self.line1, -0.5, -0.6).equals(LineString(([1.5, 0], [1.4, 0]))))
+        self.assertTrue(substring(self.line1, -0.6, -0.5).equals(LineString(([1.4, 0], [1.5, 0]))))
+        self.assertTrue(substring(self.line1, 0.5, 0.6, True).equals(LineString(([1, 0], [1.2, 0]))))
+        self.assertTrue(substring(self.line1, 0.6, 0.5, True).equals(LineString(([1.2, 0], [1, 0]))))
+        self.assertTrue(substring(self.line1, -0.5, -0.6, True).equals(LineString(([1, 0], [0.8, 0]))))
+        self.assertTrue(substring(self.line1, -0.6, -0.5, True).equals(LineString(([0.8, 0], [1, 0]))))
+
+    @unittest.skipIf(geos_version < (3, 2, 0), 'GEOS 3.2.0 required')
+    def test_return_midsubstring(self):
+        self.assertTrue(substring(self.line1, 0.5, 0.6).equals(LineString(([0.5, 0], [0.6, 0]))))
+        self.assertTrue(substring(self.line1, 0.6, 0.5).equals(LineString(([0.6, 0], [0.5, 0]))))
+        self.assertTrue(substring(self.line1, -0.5, -0.6).equals(LineString(([1.5, 0], [1.4, 0]))))
+        self.assertTrue(substring(self.line1, -0.6, -0.5).equals(LineString(([1.4, 0], [1.5, 0]))))
+        self.assertTrue(substring(self.line1, 0.5, 0.6, True).equals(LineString(([1, 0], [1.2, 0]))))
+        self.assertTrue(substring(self.line1, 0.6, 0.5, True).equals(LineString(([1.2, 0], [1, 0]))))
+        self.assertTrue(substring(self.line1, -0.5, -0.6, True).equals(LineString(([1, 0], [0.8, 0]))))
+        self.assertTrue(substring(self.line1, -0.6, -0.5, True).equals(LineString(([0.8, 0], [1, 0]))))
+
+    @unittest.skipIf(geos_version < (3, 2, 0), 'GEOS 3.2.0 required')
+    def test_return_substring_with_vertices(self):
+        self.assertTrue(substring(self.line2, 1, 7).equals(LineString(([3, 1], [3, 6], [4, 6]))))
+        self.assertTrue(substring(self.line2, 0.2, 0.9, True).equals(LineString(([3, 1.5], [3, 6], [3.75, 6]))))
+
+
+def test_suite():
+    loader = unittest.TestLoader()
+    return loader.loadTestsFromTestCase(SubstringTestCase)
diff --git a/tests/test_vectorized.py b/tests/test_vectorized.py
index 17081d4..7929693 100644
--- a/tests/test_vectorized.py
+++ b/tests/test_vectorized.py
@@ -2,14 +2,13 @@ from . import unittest, numpy
 from shapely.geometry import Point, box, MultiPolygon
 
 try:
-    import shapely.vectorized
     import numpy as np
-    has_vectorized = True
+    has_numpy = True
 except ImportError:
-    has_vectorized = False
+    has_numpy = False
 
 
-@unittest.skipIf(not has_vectorized, 'shapely.vectorized required')
+@unittest.skipIf(not has_numpy, 'numpy required')
 class VectorizedContainsTestCase(unittest.TestCase):
     def assertContainsResults(self, geom, x, y):
         from shapely.vectorized import contains
@@ -87,7 +86,7 @@ class VectorizedContainsTestCase(unittest.TestCase):
         self.assertContainsResults(self.construct_torus(), *g.exterior.xy)
 
 
-@unittest.skipIf(not has_vectorized, 'shapely.vectorized required')
+@unittest.skipIf(not has_numpy, 'numpy required')
 class VectorizedTouchesTestCase(unittest.TestCase):
     def test_touches(self):
         from shapely.vectorized import touches