[jts] 06/10: Imported Upstream version 1.7

Sebastiaan Couwenberg sebastic at moszumanska.debian.org
Tue Aug 4 17:08:45 UTC 2015


This is an automated email from the git hooks/post-receive script.

sebastic pushed a commit to branch master
in repository jts.

commit a1dbb2743d8b3f41b95626796dd2dd8f09f32bd2
Author: Bas Couwenberg <sebastic at xs4all.nl>
Date:   Tue Aug 4 19:06:19 2015 +0200

    Imported Upstream version 1.7
---
 doc/JTS Developer Guide.pdf                        | Bin 565884 -> 0 bytes
 doc/JTS Technical Specs.pdf                        | Bin 804075 -> 0 bytes
 doc/JTS TestBuilder & TestRunner User Guide.pdf    | Bin 598039 -> 0 bytes
 doc/JTS Version History.html                       | 301 ------------
 src/MANIFEST.MF                                    |   2 +-
 src/com/vividsolutions/jts/JTSVersion.java         |  86 ++++
 .../vividsolutions/jts/algorithm/CGAlgorithms.java |   2 +-
 .../vividsolutions/jts/algorithm/CentroidArea.java |   2 +-
 .../vividsolutions/jts/algorithm/CentroidLine.java |   2 +-
 .../jts/algorithm/CentroidPoint.java               |   2 +-
 .../vividsolutions/jts/algorithm/ConvexHull.java   | 433 +++++++++++------
 .../vividsolutions/jts/algorithm/HCoordinate.java  | 181 +++----
 .../jts/algorithm/InteriorPointArea.java           |   2 +-
 .../jts/algorithm/InteriorPointLine.java           |   2 +-
 .../jts/algorithm/InteriorPointPoint.java          |   2 +-
 .../jts/algorithm/LineIntersector.java             |  34 +-
 .../jts/algorithm/MCPointInRing.java               |   5 +-
 .../jts/algorithm/MinimumDiameter.java             |   2 +-
 .../jts/algorithm/NonRobustCGAlgorithms.java       |   2 +-
 .../jts/algorithm/NonRobustLineIntersector.java    |   4 +-
 .../jts/algorithm/NotRepresentableException.java   |   2 +-
 .../vividsolutions/jts/algorithm/PointInRing.java  |   2 +-
 .../vividsolutions/jts/algorithm/PointLocator.java |  31 +-
 .../jts/algorithm/RobustCGAlgorithms.java          |   2 +-
 .../jts/algorithm/RobustDeterminant.java           |   4 +-
 .../jts/algorithm/RobustLineIntersector.java       | 109 ++++-
 .../jts/algorithm/SIRtreePointInRing.java          |   2 +-
 .../jts/algorithm/SimplePointInAreaLocator.java    |   2 +-
 .../jts/algorithm/SimplePointInRing.java           |   2 +-
 src/com/vividsolutions/jts/algorithm/package.html  |  38 +-
 src/com/vividsolutions/jts/geom/Coordinate.java    | 476 ++++++++++--------
 .../vividsolutions/jts/geom/CoordinateArrays.java  | 210 +++++++-
 .../vividsolutions/jts/geom/CoordinateFilter.java  |   4 +-
 .../vividsolutions/jts/geom/CoordinateList.java    |  21 +-
 .../jts/geom/CoordinateSequence.java               |  42 +-
 .../jts/geom/CoordinateSequenceComparator.java     | 130 +++++
 .../jts/geom/CoordinateSequenceFactory.java        |  12 +-
 ...eometryFilter.java => CoordinateSequences.java} |  47 +-
 .../jts/geom/DefaultCoordinateSequence.java        |   9 +-
 .../jts/geom/DefaultCoordinateSequenceFactory.java |   8 +-
 src/com/vividsolutions/jts/geom/Dimension.java     |   2 +-
 src/com/vividsolutions/jts/geom/Envelope.java      |  93 +++-
 src/com/vividsolutions/jts/geom/Geometry.java      | 487 ++++++++++++++-----
 .../jts/geom/GeometryCollection.java               |  21 +-
 .../jts/geom/GeometryCollectionIterator.java       |   2 +-
 .../jts/geom/GeometryComponentFilter.java          |   4 +-
 .../vividsolutions/jts/geom/GeometryFactory.java   |  28 +-
 .../vividsolutions/jts/geom/GeometryFilter.java    |   4 +-
 .../jts/geom/IntersectionMatrix.java               |  78 ++-
 src/com/vividsolutions/jts/geom/LineSegment.java   |  45 +-
 src/com/vividsolutions/jts/geom/LineString.java    |  86 ++--
 src/com/vividsolutions/jts/geom/LinearRing.java    |  38 +-
 src/com/vividsolutions/jts/geom/Location.java      |   8 +-
 .../vividsolutions/jts/geom/MultiLineString.java   |  21 +-
 src/com/vividsolutions/jts/geom/MultiPoint.java    |   2 +-
 src/com/vividsolutions/jts/geom/MultiPolygon.java  |   2 +-
 src/com/vividsolutions/jts/geom/Point.java         |  13 +-
 src/com/vividsolutions/jts/geom/Polygon.java       |  27 +-
 .../vividsolutions/jts/geom/PrecisionModel.java    |   2 +-
 .../vividsolutions/jts/geom/TopologyException.java |   2 +-
 src/com/vividsolutions/jts/geom/Triangle.java      |   2 +-
 .../jts/geom/impl/CoordinateArraySequence.java     |  23 +-
 .../geom/impl/CoordinateArraySequenceFactory.java  |  14 +-
 .../jts/geom/impl/PackedCoordinateSequence.java    |   4 +-
 .../jts/geom/util/GeometryEditor.java              |  15 +-
 .../jts/geom/util/GeometryTransformer.java         |  11 +-
 .../jts/geom/util/LinearComponentExtracter.java    |   2 +-
 .../jts/geom/util/PointExtracter.java              |   2 +-
 .../jts/geom/util/PolygonExtracter.java            |   2 +-
 .../geom/util/ShortCircuitedGeometryVisitor.java   |   4 +-
 src/com/vividsolutions/jts/geomgraph/Depth.java    |  14 +-
 .../vividsolutions/jts/geomgraph/DirectedEdge.java |   8 +-
 .../jts/geomgraph/DirectedEdgeStar.java            |   8 +-
 src/com/vividsolutions/jts/geomgraph/Edge.java     |   2 +-
 src/com/vividsolutions/jts/geomgraph/EdgeEnd.java  |   5 +-
 .../vividsolutions/jts/geomgraph/EdgeEndStar.java  |  24 +-
 .../jts/geomgraph/EdgeIntersection.java            |  13 +-
 .../jts/geomgraph/EdgeIntersectionList.java        |  83 +---
 src/com/vividsolutions/jts/geomgraph/EdgeList.java |   2 +-
 .../jts/geomgraph/EdgeNodingValidator.java         |   2 +-
 src/com/vividsolutions/jts/geomgraph/EdgeRing.java |   8 +-
 .../jts/geomgraph/GeometryGraph.java               |   4 +-
 .../jts/geomgraph/GraphComponent.java              |   2 +-
 src/com/vividsolutions/jts/geomgraph/Label.java    |  28 +-
 src/com/vividsolutions/jts/geomgraph/Node.java     |  28 +-
 .../vividsolutions/jts/geomgraph/NodeFactory.java  |   2 +-
 src/com/vividsolutions/jts/geomgraph/NodeMap.java  |   2 +-
 .../vividsolutions/jts/geomgraph/PlanarGraph.java  |   4 +-
 src/com/vividsolutions/jts/geomgraph/Position.java |   2 +-
 src/com/vividsolutions/jts/geomgraph/Quadrant.java |   4 +-
 .../jts/geomgraph/TopologyLocation.java            |  27 +-
 .../jts/geomgraph/index/EdgeSetIntersector.java    |   4 +-
 .../jts/geomgraph/index/MonotoneChain.java         |   2 +-
 .../jts/geomgraph/index/MonotoneChainEdge.java     |   2 +-
 .../jts/geomgraph/index/MonotoneChainIndexer.java  |   2 +-
 .../jts/geomgraph/index/SegmentIntersector.java    |   2 +-
 .../geomgraph/index/SimpleEdgeSetIntersector.java  |   2 +-
 .../index/SimpleMCSweepLineIntersector.java        |   4 +-
 .../index/SimpleSweepLineIntersector.java          |   4 +-
 .../jts/geomgraph/index/SweepLineEvent.java        |   2 +-
 .../jts/geomgraph/index/SweepLineSegment.java      |   2 +-
 .../vividsolutions/jts/index/ArrayListVisitor.java |   4 +-
 src/com/vividsolutions/jts/index/IndexVisitor.java |  11 -
 src/com/vividsolutions/jts/index/ItemVisitor.java  |   4 +-
 src/com/vividsolutions/jts/index/SpatialIndex.java |  23 +-
 .../vividsolutions/jts/index/bintree/Bintree.java  |   2 +-
 .../vividsolutions/jts/index/bintree/Interval.java |   2 +-
 src/com/vividsolutions/jts/index/bintree/Key.java  |   2 +-
 src/com/vividsolutions/jts/index/bintree/Node.java |   2 +-
 .../vividsolutions/jts/index/bintree/NodeBase.java |   2 +-
 src/com/vividsolutions/jts/index/bintree/Root.java |   2 +-
 .../jts/index/chain/MonotoneChain.java             |   2 +-
 .../jts/index/chain/MonotoneChainBuilder.java      |   2 +-
 .../index/chain/MonotoneChainOverlapAction.java    |   2 +-
 .../jts/index/chain/MonotoneChainSelectAction.java |   2 +-
 .../jts/index/quadtree/DoubleBits.java             |   2 +-
 .../jts/index/quadtree/IntervalSize.java           |   2 +-
 src/com/vividsolutions/jts/index/quadtree/Key.java |   2 +-
 .../vividsolutions/jts/index/quadtree/Node.java    |   2 +-
 .../jts/index/quadtree/NodeBase.java               |   2 +-
 .../jts/index/quadtree/Quadtree.java               |   2 +-
 .../vividsolutions/jts/index/quadtree/Root.java    |   2 +-
 .../jts/index/strtree/AbstractNode.java            |   2 +-
 .../jts/index/strtree/AbstractSTRtree.java         |  37 +-
 .../jts/index/strtree/Boundable.java               |   4 +-
 .../vividsolutions/jts/index/strtree/Interval.java |   2 +-
 .../jts/index/strtree/ItemBoundable.java           |   2 +-
 .../vividsolutions/jts/index/strtree/SIRtree.java  |   2 +-
 .../vividsolutions/jts/index/strtree/STRtree.java  |  24 +-
 .../jts/index/sweepline/SweepLineEvent.java        |   2 +-
 .../jts/index/sweepline/SweepLineIndex.java        |   2 +-
 .../jts/index/sweepline/SweepLineInterval.java     |   2 +-
 .../index/sweepline/SweepLineOverlapAction.java    |   9 +-
 ...{ParseException.java => ByteArrayInStream.java} |  47 +-
 .../jts/io/ByteOrderDataInStream.java              | 108 +++++
 src/com/vividsolutions/jts/io/ByteOrderValues.java | 139 ++++++
 .../OverlayNodeFactory.java => io/InStream.java}   |  32 +-
 .../InputStreamInStream.java}                      |  26 +-
 .../PointInRing.java => io/OutStream.java}         |  18 +-
 .../OutputStreamOutStream.java}                    |  29 +-
 src/com/vividsolutions/jts/io/ParseException.java  |   2 +-
 .../PointInRing.java => io/WKBConstants.java}      |  22 +-
 src/com/vividsolutions/jts/io/WKBReader.java       | 255 ++++++++++
 src/com/vividsolutions/jts/io/WKBWriter.java       | 242 ++++++++++
 src/com/vividsolutions/jts/io/WKTReader.java       | 473 ++++++++++--------
 src/com/vividsolutions/jts/io/WKTWriter.java       | 130 ++++-
 .../jts/linearref/ExtractLineByLocation.java       | 175 +++++++
 .../jts/linearref/LengthIndexOfPoint.java          | 116 +++++
 .../jts/linearref/LengthIndexedLine.java           | 196 ++++++++
 .../jts/linearref/LengthLocationMap.java           | 119 +++++
 .../jts/linearref/LinearGeometryBuilder.java       | 119 +++++
 .../jts/linearref/LinearIterator.java              | 166 +++++++
 .../jts/linearref/LinearLocation.java              | 338 +++++++++++++
 .../jts/linearref/LocationIndexOfLine.java         |  52 ++
 .../jts/linearref/LocationIndexOfPoint.java        | 121 +++++
 .../jts/linearref/LocationIndexedLine.java         | 145 ++++++
 src/com/vividsolutions/jts/linearref/package.html  |  37 ++
 ...mentIntersector.java => IntersectionAdder.java} |  14 +-
 .../jts/noding/IntersectionFinderAdder.java        |  68 +++
 .../vividsolutions/jts/noding/IteratedNoder.java   |  55 ++-
 .../{MCQuadtreeNoder.java => MCIndexNoder.java}    |  40 +-
 src/com/vividsolutions/jts/noding/Noder.java       |  52 +-
 .../vividsolutions/jts/noding/NodingValidator.java |  72 ++-
 src/com/vividsolutions/jts/noding/Octant.java      |  77 +++
 .../jts/noding/OrientedCoordinateArray.java        | 131 +++++
 src/com/vividsolutions/jts/noding/ScaledNoder.java | 102 ++++
 .../jts/noding/SegmentIntersector.java             | 137 +-----
 src/com/vividsolutions/jts/noding/SegmentNode.java |  66 +--
 .../vividsolutions/jts/noding/SegmentNodeList.java | 279 ++++++-----
 .../jts/noding/SegmentPointComparator.java         |  65 +++
 .../vividsolutions/jts/noding/SegmentString.java   | 124 ++---
 .../jts/noding/SegmentStringDissolver.java         | 164 +++++++
 src/com/vividsolutions/jts/noding/SimpleNoder.java |  16 +-
 .../noding/{Noder.java => SinglePassNoder.java}    |  60 ++-
 .../jts/noding/snapround/HotPixel.java             | 227 +++++++++
 .../jts/noding/snapround/MCIndexPointSnapper.java  |  91 ++++
 .../jts/noding/snapround/MCIndexSnapRounder.java   | 169 +++++++
 .../jts/noding/snapround/SegmentSnapper.java       | 118 -----
 .../snapround/SimpleSegmentStringsSnapper.java     |  83 ----
 .../jts/noding/snapround/SimpleSnapRounder.java    | 215 +++++++++
 .../jts/noding/snapround/SnapRounder.java          | 124 -----
 .../jts/operation/GeometryGraphOperation.java      |   2 +-
 .../vividsolutions/jts/operation/IsSimpleOp.java   |  24 +-
 .../jts/operation/buffer/BufferBuilder.java        |  39 +-
 .../jts/operation/buffer/BufferOp.java             |  91 +++-
 .../jts/operation/buffer/BufferSubgraph.java       |  27 +-
 .../jts/operation/buffer/OffsetCurveBuilder.java   |   4 +-
 .../operation/buffer/OffsetCurveSetBuilder.java    |  23 +-
 .../jts/operation/buffer/RightmostEdgeFinder.java  |   4 +-
 .../jts/operation/buffer/SubgraphDepthLocater.java |  12 +-
 .../distance/ConnectedElementLocationFilter.java   |   2 +-
 .../distance/ConnectedElementPointFilter.java      |   2 +-
 .../jts/operation/distance/DistanceOp.java         |   2 +-
 .../jts/operation/distance/GeometryLocation.java   |   2 +-
 .../jts/operation/linemerge/EdgeString.java        |   2 +-
 .../operation/linemerge/LineMergeDirectedEdge.java |   2 +-
 .../jts/operation/linemerge/LineMergeEdge.java     |   2 +-
 .../jts/operation/linemerge/LineMergeGraph.java    |   2 +-
 .../jts/operation/linemerge/LineMerger.java        |   2 +-
 .../jts/operation/linemerge/LineSequencer.java     | 464 ++++++++++++++++++
 .../jts/operation/overlay/EdgeSetNoder.java        |   2 +-
 .../jts/operation/overlay/LineBuilder.java         |   2 +-
 .../jts/operation/overlay/MaximalEdgeRing.java     |   2 +-
 .../jts/operation/overlay/MinimalEdgeRing.java     |   2 +-
 .../jts/operation/overlay/OverlayNodeFactory.java  |   4 +-
 .../jts/operation/overlay/OverlayOp.java           |  13 +-
 .../jts/operation/overlay/PointBuilder.java        |  89 ++--
 .../jts/operation/overlay/PolygonBuilder.java      |   8 +-
 .../jts/operation/polygonize/EdgeRing.java         |   2 +-
 .../polygonize/PolygonizeDirectedEdge.java         |   2 +-
 .../jts/operation/polygonize/PolygonizeEdge.java   |   2 +-
 .../jts/operation/polygonize/PolygonizeGraph.java  |   6 +-
 .../jts/operation/polygonize/Polygonizer.java      |   2 +-
 .../jts/operation/predicate/RectangleContains.java |   4 +-
 .../operation/predicate/RectangleIntersects.java   |  61 ++-
 .../predicate/SegmentIntersectionTester.java       |   4 +-
 .../jts/operation/relate/EdgeEndBuilder.java       |   4 +-
 .../jts/operation/relate/EdgeEndBundle.java        |  11 +-
 .../jts/operation/relate/EdgeEndBundleStar.java    |   2 +-
 .../jts/operation/relate/RelateComputer.java       |   4 +-
 .../jts/operation/relate/RelateNode.java           |   4 +-
 .../jts/operation/relate/RelateNodeFactory.java    |   2 +-
 .../jts/operation/relate/RelateNodeGraph.java      |   4 +-
 .../jts/operation/relate/RelateOp.java             |   4 +-
 .../operation/valid/ConnectedInteriorTester.java   |  14 +-
 .../jts/operation/valid/ConsistentAreaTester.java  |  33 +-
 .../jts/operation/valid/IsValidOp.java             | 133 +++--
 .../operation/valid/QuadtreeNestedRingTester.java  |   2 +-
 .../jts/operation/valid/RepeatedPointTester.java   |   2 +-
 .../operation/valid/SimpleNestedRingTester.java    |   5 +-
 .../operation/valid/SweeplineNestedRingTester.java |   8 +-
 .../operation/valid/TopologyValidationError.java   | 100 +++-
 .../jts/planargraph/DirectedEdge.java              |  33 +-
 .../jts/planargraph/DirectedEdgeStar.java          |   2 +-
 src/com/vividsolutions/jts/planargraph/Edge.java   |  27 +-
 .../jts/planargraph/GraphComponent.java            |  91 +++-
 src/com/vividsolutions/jts/planargraph/Node.java   |  20 +-
 .../vividsolutions/jts/planargraph/NodeMap.java    |   2 +-
 .../jts/planargraph/PlanarGraph.java               |  62 ++-
 .../vividsolutions/jts/planargraph/Subgraph.java   |  91 ++++
 .../algorithm/ConnectedSubgraphFinder.java         |  74 +++
 .../vividsolutions/jts/precision/CommonBits.java   |   2 +-
 .../vividsolutions/jts/precision/CommonBitsOp.java |   2 +-
 .../jts/precision/CommonBitsRemover.java           |   2 +-
 .../jts/precision/EnhancedPrecisionOp.java         |   2 +-
 .../precision/SimpleGeometryPrecisionReducer.java  |   3 +-
 .../jts/simplify/DouglasPeuckerLineSimplifier.java |   2 +-
 .../jts/simplify/DouglasPeuckerSimplifier.java     |   6 +-
 .../jts/simplify/TaggedLineString.java             |   4 +-
 .../jts/simplify/TaggedLineStringSimplifier.java   |   4 +-
 .../jts/simplify/TopologyPreservingSimplifier.java |   4 +
 src/com/vividsolutions/jts/util/Assert.java        |   2 +-
 .../jts/util/AssertionFailedException.java         |   2 +-
 .../vividsolutions/jts/util/CollectionUtil.java    |  65 +++
 .../jts/util/CoordinateArrayFilter.java            |   2 +-
 .../jts/util/CoordinateCountFilter.java            |   2 +-
 src/com/vividsolutions/jts/util/Debug.java         |  42 +-
 .../jts/util/GeometricShapeFactory.java            |  15 +-
 src/com/vividsolutions/jts/util/Stopwatch.java     |  45 +-
 .../jts/util/UniqueCoordinateArrayFilter.java      |   2 +-
 .../jtsexample/geom/BasicExample.java              |   2 +-
 .../jtsexample/geom/ConstructionExample.java       |   4 +-
 .../jtsexample/geom/ExtendedCoordinate.java        |   2 +-
 .../jtsexample/geom/ExtendedCoordinateExample.java |   2 +-
 .../geom/ExtendedCoordinateSequence.java           |   6 +-
 .../geom/ExtendedCoordinateSequenceFactory.java    |   2 +-
 .../jtsexample/geom/PrecisionModelExample.java     |   2 +-
 .../jtsexample/geom/SimpleMethodsExample.java      |   2 +-
 .../jtsexample/linearref/LinearRefExample.java     |  87 ++++
 .../operation/distance/ClosestPointExample.java    |   2 +-
 .../operation/linemerge/LineMergeExample.java      |   2 +-
 .../operation/polygonize/PolygonizeExample.java    |   2 +-
 .../precision/EnhancedPrecisionOpExample.java      |   2 +-
 .../technique/LineStringSelfIntersections.java     |   4 +-
 ...ingBuffer.java => PolygonUnionUsingBuffer.java} |  11 +-
 .../vividsolutions/jts/io/gml2/GMLConstants.java   |  76 +++
 .../com/vividsolutions/jts/io/gml2/GMLHandler.java | 249 ++++++++++
 .../com/vividsolutions/jts/io/gml2/GMLReader.java  | 121 +++++
 .../com/vividsolutions/jts/io/gml2/GMLWriter.java  | 350 ++++++++++++++
 .../jts/io/gml2/GeometryStrategies.java            | 533 +++++++++++++++++++++
 test/vivid/TestFunctionAA.xml                      |   6 +-
 test/vivid/TestFunctionAAPrec.xml                  |   6 +-
 test/vivid/TestRectanglePredicate.xml              |  20 +
 test/vivid/TestValid.xml                           |  19 +-
 284 files changed, 9957 insertions(+), 2789 deletions(-)

diff --git a/doc/JTS Developer Guide.pdf b/doc/JTS Developer Guide.pdf
deleted file mode 100644
index 314ea4f..0000000
Binary files a/doc/JTS Developer Guide.pdf and /dev/null differ
diff --git a/doc/JTS Technical Specs.pdf b/doc/JTS Technical Specs.pdf
deleted file mode 100644
index 57d17c2..0000000
Binary files a/doc/JTS Technical Specs.pdf and /dev/null differ
diff --git a/doc/JTS TestBuilder & TestRunner User Guide.pdf b/doc/JTS TestBuilder & TestRunner User Guide.pdf
deleted file mode 100644
index 719bbf6..0000000
Binary files a/doc/JTS TestBuilder & TestRunner User Guide.pdf and /dev/null differ
diff --git a/doc/JTS Version History.html b/doc/JTS Version History.html
deleted file mode 100644
index 92eb99e..0000000
--- a/doc/JTS Version History.html	
+++ /dev/null
@@ -1,301 +0,0 @@
-<html>
-
-<head>
-<title>JTS Version History</title>
-</head>
-
-<body bgcolor='lightblue'>
-<h1 style='text-align:center;'>
-JTS TOPOLOGY SUITE
-<br>
-Version History
-</h1>
-
-
-This document lists the change history of release versions of the JTS Topology Suite
-
-<hr>
-<h2>Version 1.6</h2>
-
-Release Date: February 3, 2005
-
-<h3>API Changes</h3>
-<ul>
-<li>Changed to using <code>CoordinateArraySequence</code> instead of <code>DefaultCoordinateSequence</code>
-(to provide a more descriptive name).
-</ul>
-
-<h3>Semantics Changes</h3>
-<ul>
-<li>PrecisionModel#makePrecise changed to use Symmetric Arithmetic Rounding rather than Banker's Rounding
-</ul>
-
-<h3>Functionality Improvements</h3>
-<ul>
-<li>Added ability to enable <code>Debug</code> methods by setting a system property
-<li>Added <code>getNumGeometries</code> and <code>getGeometryN</code> methods to Geometry class, to make API more uniform
-<li>Improved API for <code>CoordinateSequence</code> allows more options for improving memory usage and handling custom coordinate storage methods
-<li>Added <code>PackedCoordinateSequence</code> to provide reduced memory footprint for geometry objects if desired
-<li>Added optimized spatial predicates for rectangles
-<li>Added Debug#isDebugging method
-</ul>
-
-<h3>Bug Fixes</h3>
-<ul>
-<li>Fixed bug in <code>Geometry#within()</code> short circuiting
-<li>Fixed bug causing <code>Geometry#isValid</code> to throw IllegalArgumentException for certain kinds of holes with invalid rings
-<li>Fixed bug causing redundant linestrings to be returned in the result of overlaying polygons containing holes touching their shell.
-<li><code>Polygon#getBoundary</code> now returns a <code>LinearRing</code> if the polygon does not have holes
-</ul>
-
-<h3>Architecture Changes</h3>
-<ul>
-<li>Removed a proliferation of references to the default <code>CoordinateSequenceFactory</code>
-</ul>
-
-<h2>Contributors</h2>
-<ul>
-<li>Andrea Aime
-</ul>
-
-<h2>Version 1.5</h2>
-Release Date: September 22, 2004
-<p>
-This version is upwards compatible with Version 1.4
-
-<h3>API Changes</h3>
-<ul>
-<li>None
-</ul>
-
-<h3>Semantics Changes</h3>
-<ul>
-<li>None
-</ul>
-
-<h3>Functionality Improvements</h3>
-<ul>
-<li>CGAlgorithms#isCCW now handles coordinate lists with repeated points.  Also throws an IllegalArgumentException if the input ring does not have 3 distinct points
-<li>isValid now checks for invalid coordinates (e.g. ones with Nan or infinite numbers)
-<li>added copyDeep() method to CoordinateArrays
-<li>added geometry simplification operations DouglasPeuckerSimplifier and TopologyPreservingSimplifier
-<li>added methods to Quadtree and STRtree to remove items and query using the Visitor pattern
-</ul>
-
-<h3>Performance Improvements</h3>
-<ul>
-<li>Added short-circuit tests in geometry named predicates based on envelope tests
-</ul>
-<h3>Bug Fixes</h3>
-<ul>
-<li>Fixed bugs in Geometry serialization
-<li>Fixed bug in ValidOp which reported some MultiPolygons with shells nested inside a hole as invalid
-<li>Fixed bug in buffer which caused buffers of some polygons with small & large holes to not contain any holes
-<li>Fixed bug in Polygonizer which caused exception if no lines were supplied
-</ul>
-<h3>Architecture Changes</h3>
-<ul>
-<li>Basic CG algorithm methods made static in the in CGAlgorithms class
-<li>Various utility methods made public in CoordinateArrays class
-</ul>
-<h3>Documentation</h3>
-<ul>
-<li>More examples provided in com.vividsolutions.jtsexamples package
-</ul>
-
-<h2>Version 1.4</h2>
-
-Release Date: November 4, 2003
-<h3>Semantics Changes</h3>
-<ul>
-<li>none
-</ul>
-<h3>Functionality Improvements</h3>
-<ul>
-<li>Added "LINEARRING" tag to WKT syntax
-<li>Added GeometryEditor class to allow easy copy/modify of Geometrys
-<li>Added GeometricShapeFactory class to easily create standard geometric shapes
-<li>Geometries can now carry arbitrary user-defined data objects (via Geometry#get/setUserData(Object) method)
-<li>Added CoordinateSequence and CoordinateSequenceFactory interfaces, and default implementations (BasicCoordinateSequence, BasicCoordinateSequenceFactory)
-<li>Added Geometry#getFactory
-<li>Added new PrecisionModel type of FLOATING_SINGLE, for rounding to single precision floating point
-<li>Added DistanceOp#getClosestPoints method, which returns the closest points between two Geometries
-<li>Added com.vividsolutions.jts.noding package containing classes to perform fast indexed noding of linestrings
-<li>Added com.vividsolutions.jts.operation.polygonize package containing classes to perform polygonization
-<li>Added com.vividsolutions.jts.operation.linemerge package containing classes to perform line merging
-<li>Added SimpleGeometryPrecisionReducer to allow reducing precision of coordinates of a Geometry
-<li>Added LineSegment#closestPoints method to compute the closest points between two line segments
-<li>Added MinimumDiameter class to compute minimum diameters of Geometries
-<li>Added geom.Triangle class to contain algorithms for Triangles
-<li>BufferOp now allows end cap styles to be specified.  Three types are supported: round, butt and square.
-</ul>
-<h3>Performance Improvements</h3>
-<ul>
-<li>EdgeList now provides a findEqualEdge method which is substantially faster than findEdgeIndex, for large lists
-<li>Buffering is now faster and much more robust
-<li>Overlap operations are now more robust
-</ul>
-<h3>Bug Fixes</h3>
-<ul>
-<li>Envelope#init(Envelope) now handles null Envelopes correctly
-<li>CoordinateList#add() now correctly ignores the z-value of Coordinates in determining equality
-<li>Geometry#isValid now correctly handles checking validity of LinearRings
-<li>Fixed infinite loop bug causing Out Of Memory errors during polygon intersection
-<li>Geometry#clone now correctly clones the Geometry's Envelope
-<li>LineIntersector#computeEdgeDistance now correctly computes a non-zero edge distance in certain situations when a fixed precision model was being used and the line segment was a single unit in length
-<li>Fixed incorrect calculation of depths in DirectedEdgeStar#computeDepths
-<li>Fixed BufferSubgraph#addReachable to use explicit stack to avoid stack overflow problems
-<li>Fixed various bugs causing some kinds of buffers to be computed incorrectly
-</ul>
-<h3>API Changes</h3>
-<ul>
-<li>WKTReader/Writer: changed protected members to private
-<li>PrecisionModel type is now an object rather than an int
-<li>ConvexHull API changed to remove requirement to pass in CGAlgorithms object
-</ul>
-<h3>Code Architecture Changes</h3>
-<ul>
-<li>geom.util package added for utility classes which parse and modify geometries
-</ul>
-<h3>Documentation</h3>
-<ul>
-<li>More examples provided in com.vividsolutions.jtsexamples package
-<li>Added JTS Developers Guide
-</ul>
-
-<h2>Version 1.3</h2>
-Release Date: April 4, 2003
-<h3>Semantics Changes</h3>
-<ul>
-<li>all Geometry methods are now reentrant (thread-safe)
-<li>Fixed-precision coordinates are now stored in a rounded but non-scaled form.  This makes them compatible with non-precise (Floating) coordinates, and simplifies working with precise coordinates directly.  Mixed precision models are now supported in Geometry methods; method results are in the more precise of the input precision models.
-<li>Offsets are no longer supported in the Fixed precision model.  This is necessary to allow storing fixed precision coordinates in a non-scaled form.  This does not reduce the total precision available, since coordinates are stored in a floating-point format.
-<li>SRID and Precision Model are no longer checked for equality during Geometry operations.  This removes a limitation which provided little semantic benefit.
-</ul>
-
-<h3>Functionality Improvements</h3>
-<ul>
-<li>added Geometry.isWithinDistance(Geometry g, double distance) method, to provide optimized proximity queries
-<li>added Geometry.buffer(double distance, int quadrantSegments) method, allowing control over accuracy of buffer approximation
-<li>added Geometry.getCentroid() method
-<li>added Geometry.getInteriorPoint() method, which uses heuristic methods to return a point in the interior of a Geometry
-<li>GeometryFactory.toGeometryArray now returns null if the argument is null
-</ul>
-<h3>Performance Improvements</h3>
-<ul>
-<li>Removed unnecessary string construction in EdgeEndStar.propagateSideLabels()
-<li>Eliminated unnecessary computation of self-intersections in rings during relate and spatial functions.  This provides a large increase in speed when working with large rings and polygons.  (Note that IsValid still checks for these self-intersections, which are illegal in LinearRings)
-<li>Add short-circuit code to RobustLineIntersector to detect non-intersections more efficiently
-</ul>
-<h3>Bug Fixes</h3>
-<ul>
-<li>Fixed ClassCastException occurring in GeometryCollection.getLength()
-<li>Fixed bug in Edge Intersection insertion (replaced Coordinate#equals with equals2D to ensure that intersection creation is not sensitive to Z-value).
-<li>Fixed handling LineStrings with too few points in GeometryGraph.addLineString
-<li>Fixed: was not checking that MultiPolygons don't contain components with too few points.
-<li>Fixed Envelope.distance() to return correct distance for all envelopes.
-<li>Fixed a few Geometry methods to make them re-entrant.
-<li>Fixed CoordinateList.closeRing() to ensure endpoints are not duplicated
-<li>Fixed CGAlgorithms.signedArea() to use a simpler algorithm which is more robust and faster.
-<li>Fixed bug preventing validating Rings containing an initial repeated point.
-</ul>
-<h3>API Changes</h3>
-<ul>
-<li>Added default constructor to WKTReader.  It uses the default GeometryFactory
-<li>Add two static intersects() methods to Envelope, to allow computing intersections with envelopes defined by points only.
-<li>Dropped BinaryPower; its functionality is provided by DoubleBits in a more robust fashion.
-<li>Removed a couple of redundant private static methods from Geometry; they have been replaced by methods in CoordinateArrays
-<li>The Geometry class is now marked as Serializable
-</ul>
-
-<h2>Version 1.2</h2>
-Release Date: 7 October 2002
-<h3>Semantics Changes</h3>
-<ul>
-<li>JTS now allows Geometrys to have repeated points.  All operations will continue to perform as before.  This removes a significant incompatibility with the OGC spatial data model.
-<li>TopologyExceptions may now be thrown by spatial overlay methods.  This helps to distinguish between code bugs and known robustness problems.  It also provides a machine-readable coordinate for the error location.
-</ul>
-<h3>Functionality Improvements</h3>
-<ul>
-<li>RobustLineIntersector now uses "normalized" coordinates to maximize the accuracy of intersection computation.
-<li>Upgraded Quadtree with more robust implementation
-<li>Replaced IntervalTree with a more robust implementation of BinTree
-<li>Added STRTree 2-D spatial index, which exhibits better performance than QuadTrees in many situations.
-<li>Added EnhancePrecisionOp, which uses precisioning enhancing techniques to reduce the number of failure cases due to robustness problems.
-</ul>
-<h3>Bug Fixes</h3>
-<ul>
-<li>fixed ConvexHull to use TreeSet instead of HashSet for coordinates
-<li>Fixed isValid for GeometryCollections containing Polygons, which were sometimes erroneously returning a validity failure for correct Geometrys.
-<li>Fixed bug in LineSegment.distancePointLine() which would return the incorrect distance for a LineSegment with two identical points
-<li>Improved error handling in CGAlgorithms.isCCW()
-<li>IsValid now checks for too few points in a geometry component (e.g. due to repeated points in a ring)
-</ul>
-<h3>API Changes</h3>
-<ul>
-<li>added Stopwatch class
-<li>added Geometry.getArea() and Geometry.getLength() methods
-<li>added CGAlgorithms.signedArea() method
-<li>added methods to LineSegment - closestPoint(), getLength()
-<li>added CoordinateArrrays and CoordinateLists utility classes
-<li>Added TopologyValidationError.getErrorType() method
-<li>Added Envelope#intersects; deprecated Envelope#overlaps.
-<li>Added Geometry#geometryChanged() method to allow signaling when Geometry coordinates have been mutated by a client class
-<li>Added STRTree class implementing a Sort-Tile-Recursive spatial index (a variant of a packed R-tree)
-<li>Deleted IntervalTree 1-D spatial index (replaced by BinTree)
-<li>Add BinTree 1-D spatial index
-</ul>
-
-<h2>Version 1.1.1</h2>
-Release Date: 9 April 2002
-<h3>Bug Fixes</h3>
-<ul>
-<li>fixed decimal-point symbol localization bug in WKTWriter
-<li>fixed bug in Envelope.int(Envelope env)
-<li>fixed filename case of SFSMultiLineString.java and IntervalTree.java
-</ul>
-<h3>API Changes</h3>
-<ul>
-<li>deleted TopologyException class
-<li>renamed CGAlgorithms.isPointInPolygon to isPointInRing (a more accurate description of what the method computes)
-</ul>
-<h3>API Additions</h3>
-<ul>
-<li>added Geometry.getCoordinate() method
-<li>added Geometry.distance() method
-<li>added GeometryComponentFilter interface and Geometry.apply(GeometryComponentFilter) method
-</ul>
-
-<h2>Version 1.1</h2>
-Release Date: 28 March 2002
-<h3>New Features</h3>
-<ul>
-<li>added Geometry.isSimple() and Geometry.isValid() methods
-<li>improved design of topological data structures
-<li>added Geometry.setSRID() method
-<li>improved functionality of the Envelope class
-<li>added ability to write to an arbitrary java.io.Writer object to WKTWriter
-<li>added Validate and Mark Location functionality to TestBuilder
-</ul>
-
-<h2>Version 1.0</h2>
-Release Date: 1 February 2002
-<ul>
-<li>Removed some non-compatibilities with Java 1.1
-<li>Fixed bug in constructing buffer outline around inside of angles
-<li>In TestBuilder vertices are now displayed with fixed size in view units
-<li>Improved code for WKTWriter.writeFormatted()
-<li>Fixed bug in constructor for LinearRing
-<li>Improved implementation of sweepline intersection algorithm to avoid use of dynamic set.
-<li>Fixed bug in ConvexHull.cleanRing()
-<li>Refactored RobustLineIntersector and NonRobustLineIntersector
-</ul>
-
-<h2>Version 0.0</h2>
-Release Date: 30 May 2001
-
-<p>
-<i>Baseline version</i>
-
-</body>
-</html>
diff --git a/src/MANIFEST.MF b/src/MANIFEST.MF
index 78e8373..2222b25 100644
--- a/src/MANIFEST.MF
+++ b/src/MANIFEST.MF
@@ -1,4 +1,4 @@
 Manifest-version: 1.0
 Implementation-Title: Java Topology Suite
-Implementation-Version: 1.6
+Implementation-Version: 1.7
 Implementation-Vendor: Vivid Solutions
diff --git a/src/com/vividsolutions/jts/JTSVersion.java b/src/com/vividsolutions/jts/JTSVersion.java
new file mode 100644
index 0000000..62e0235
--- /dev/null
+++ b/src/com/vividsolutions/jts/JTSVersion.java
@@ -0,0 +1,86 @@
+package com.vividsolutions.jts;
+
+/**
+ * JTS API version information.
+ * <p>
+ * Versions consist of a 3-part version number: <code>major.minor.patch</code>
+ * An optional release status string may be present in the string version of
+ * the version.
+ *
+ * @version 1.7
+ */
+public class JTSVersion {
+
+  /**
+   * The current version number of the JTS API.
+   */
+  public static final JTSVersion CURRENT_VERSION = new JTSVersion();
+
+  /**
+   * The major version number.
+   */
+  public static final int MAJOR = 1;
+
+  /**
+   * The minor version number.
+   */
+  public static final int MINOR = 7;
+
+  /**
+   * The patch version number.
+   */
+  public static final int PATCH = 0;
+
+  /**
+   * An optional string providing further release info (such as "alpha 1");
+   */
+  private static final String releaseInfo = "";
+
+  /**
+   * Prints the current JTS version to stdout.
+   *
+   * @param args the command-line arguments (none are required).
+   */
+  public static void main(String[] args)
+  {
+    System.out.println(CURRENT_VERSION);
+  }
+
+  private JTSVersion() {
+  }
+
+  /**
+   * Gets the major number of the release version.
+   *
+   * @return the major number of the release version.
+   */
+  public int getMajor() { return MAJOR; }
+
+  /**
+   * Gets the minor number of the release version.
+   *
+   * @return the minor number of the release version.
+   */
+  public int getMinor() { return MINOR; }
+
+  /**
+   * Gets the patch number of the release version.
+   *
+   * @return the patch number of the release version.
+   */
+  public int getPatch() { return PATCH; }
+
+  /**
+   * Gets the full version number, suitable for display.
+   *
+   * @return the full version number, suitable for display.
+   */
+  public String toString()
+  {
+    String ver = MAJOR + "." + MINOR + "." + PATCH;
+    if (releaseInfo != null && releaseInfo.length() > 0)
+      return ver + " " + releaseInfo;
+    return ver;
+  }
+
+}
\ No newline at end of file
diff --git a/src/com/vividsolutions/jts/algorithm/CGAlgorithms.java b/src/com/vividsolutions/jts/algorithm/CGAlgorithms.java
index ebb6479..064de74 100644
--- a/src/com/vividsolutions/jts/algorithm/CGAlgorithms.java
+++ b/src/com/vividsolutions/jts/algorithm/CGAlgorithms.java
@@ -39,7 +39,7 @@ import com.vividsolutions.jts.geom.CoordinateSequence;
  * Specifies and implements various fundamental Computational Geometric algorithms.
  * The algorithms supplied in this class are robust for double-precision floating point.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class CGAlgorithms
 {
diff --git a/src/com/vividsolutions/jts/algorithm/CentroidArea.java b/src/com/vividsolutions/jts/algorithm/CentroidArea.java
index 061e87c..bccf7b2 100644
--- a/src/com/vividsolutions/jts/algorithm/CentroidArea.java
+++ b/src/com/vividsolutions/jts/algorithm/CentroidArea.java
@@ -45,7 +45,7 @@ import com.vividsolutions.jts.geom.*;
  * See <code>http://www.faqs.org/faqs/graphics/algorithms-faq/</code>
  * for further details of the basic approach.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class CentroidArea
 {
diff --git a/src/com/vividsolutions/jts/algorithm/CentroidLine.java b/src/com/vividsolutions/jts/algorithm/CentroidLine.java
index 0cc2c56..dc7587f 100644
--- a/src/com/vividsolutions/jts/algorithm/CentroidLine.java
+++ b/src/com/vividsolutions/jts/algorithm/CentroidLine.java
@@ -41,7 +41,7 @@ import com.vividsolutions.jts.geom.*;
  * Compute the average of the midpoints
  * of all line segments weighted by the segment length.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class CentroidLine
 {
diff --git a/src/com/vividsolutions/jts/algorithm/CentroidPoint.java b/src/com/vividsolutions/jts/algorithm/CentroidPoint.java
index 1b9a172..88b7f04 100644
--- a/src/com/vividsolutions/jts/algorithm/CentroidPoint.java
+++ b/src/com/vividsolutions/jts/algorithm/CentroidPoint.java
@@ -40,7 +40,7 @@ import com.vividsolutions.jts.geom.*;
  * <h2>Algorithm</h2>
  * Compute the average of all points.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class CentroidPoint
 {
diff --git a/src/com/vividsolutions/jts/algorithm/ConvexHull.java b/src/com/vividsolutions/jts/algorithm/ConvexHull.java
index 00cf3de..9c10a11 100644
--- a/src/com/vividsolutions/jts/algorithm/ConvexHull.java
+++ b/src/com/vividsolutions/jts/algorithm/ConvexHull.java
@@ -1,36 +1,36 @@
 
 
 /*
- * The JTS Topology Suite is a collection of Java classes that
- * implement the fundamental operations required to validate a given
- * geo-spatial data set to a known topological specification.
- *
- * Copyright (C) 2001 Vivid Solutions
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- *
- * For more information, contact:
- *
- *     Vivid Solutions
- *     Suite #1A
- *     2328 Government Street
- *     Victoria BC  V8T 5G5
- *     Canada
- *
- *     (250)385-6040
- *     www.vividsolutions.com
+* The JTS Topology Suite is a collection of Java classes that
+* implement the fundamental operations required to validate a given
+* geo-spatial data set to a known topological specification.
+*
+* Copyright (C) 2001 Vivid Solutions
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation; either
+* version 2.1 of the License, or (at your option) any later version.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*
+* For more information, contact:
+*
+*     Vivid Solutions
+*     Suite #1A
+*     2328 Government Street
+*     Victoria BC  V8T 5G5
+*     Canada
+*
+*     (250)385-6040
+*     www.vividsolutions.com
  */
 package com.vividsolutions.jts.algorithm;
 import com.vividsolutions.jts.geom.*;
@@ -43,29 +43,43 @@ import com.vividsolutions.jts.util.UniqueCoordinateArrayFilter;
  * Computes the convex hull of a {@link Geometry}.
  * The convex hull is the smallest convex Geometry that contains all the
  * points in the input Geometry.
+ * <p>
  * Uses the Graham Scan algorithm.
  *
- *@version 1.6
+ *@version 1.7
  */
 public class ConvexHull
 {
-  private PointLocator pointLocator = new PointLocator();
-  //private CGAlgorithms cgAlgorithms = new RobustCGAlgorithms();
-  private Geometry geometry;
-  private GeometryFactory factory;
+  private GeometryFactory geomFactory;
+  private Coordinate[] inputPts;
 
   /**
    * Create a new convex hull construction for the input {@link Geometry}.
    */
   public ConvexHull(Geometry geometry)
   {
-    this.geometry = geometry;
+    this(extractCoordinates(geometry), geometry.getFactory());
+  }
+  /**
+   * Create a new convex hull construction for the input {@link Coordinate} array.
+   */
+  public ConvexHull(Coordinate[] pts, GeometryFactory geomFactory)
+  {
+    inputPts = pts;
+    this.geomFactory = geomFactory;
+  }
+
+  private static Coordinate[] extractCoordinates(Geometry geom)
+  {
+    UniqueCoordinateArrayFilter filter = new UniqueCoordinateArrayFilter();
+    geom.apply(filter);
+    return filter.getCoordinates();
   }
 
   /**
    * Returns a {@link Geometry} that represents the convex hull of the input
    * geometry.
-   * The geometry will contain the minimal number of points needed to
+   * The returned geometry contains the minimal number of points needed to
    * represent the convex hull.  In particular, no more than two consecutive
    * points will be collinear.
    *
@@ -75,40 +89,32 @@ public class ConvexHull
    * 0 points, an empty {@link GeometryCollection}.
    */
   public Geometry getConvexHull() {
-    factory = geometry.getFactory();
 
-    UniqueCoordinateArrayFilter filter = new UniqueCoordinateArrayFilter();
-    geometry.apply(filter);
-    Coordinate[] pts = filter.getCoordinates();
-
-    if (pts.length == 0) {
-      return factory.createGeometryCollection(null);
+    if (inputPts.length == 0) {
+      return geomFactory.createGeometryCollection(null);
     }
-    if (pts.length == 1) {
-      return factory.createPoint(pts[0]);
+    if (inputPts.length == 1) {
+      return geomFactory.createPoint(inputPts[0]);
     }
-    if (pts.length == 2) {
-      return factory.createLineString(pts);
+    if (inputPts.length == 2) {
+      return geomFactory.createLineString(inputPts);
     }
 
-    // sort points for Graham scan.
-    Coordinate[] pspts;
-    if (pts.length > 10) {
-      //Probably should be somewhere between 50 and 100?
-      Coordinate[] rpts = reduce(pts);
-      pspts = preSort(rpts);
-    }
-    else {
-      pspts = preSort(pts);
+    Coordinate[] reducedPts = inputPts;
+    // use heuristic to reduce points, if large
+    if (inputPts.length > 50) {
+      reducedPts = reduce(inputPts);
     }
+    // sort points for Graham scan.
+    Coordinate[] sortedPts = preSort(reducedPts);
 
     // Use Graham scan to find convex hull.
-    Stack cHS = grahamScan(pspts);
+    Stack cHS = grahamScan(sortedPts);
 
     // Convert stack to an array.
     Coordinate[] cH = toCoordinateArray(cHS);
 
-    // Convert array to linear ring.
+    // Convert array to appropriate output geometry.
     return lineOrPolygon(cH);
   }
 
@@ -125,42 +131,54 @@ public class ConvexHull
     return coordinates;
   }
 
-  private Coordinate[] reduce(Coordinate[] pts) {
-    BigQuad bigQuad = bigQuad(pts);
 
-    // Build a linear ring defining a big poly.
-    ArrayList bigPoly = new ArrayList();
-    bigPoly.add(bigQuad.westmost);
-    if (!bigPoly.contains(bigQuad.northmost)) {
-      bigPoly.add(bigQuad.northmost);
-    }
-    if (!bigPoly.contains(bigQuad.eastmost)) {
-      bigPoly.add(bigQuad.eastmost);
-    }
-    if (!bigPoly.contains(bigQuad.southmost)) {
-      bigPoly.add(bigQuad.southmost);
-    }
-    if (bigPoly.size() < 3) {
-      return pts;
+  /**
+   * Uses a heuristic to reduce the number of points scanned
+   * to compute the hull.
+   * The heuristic is to find a polygon guaranteed to
+   * be in (or on) the hull, and eliminate all points inside it.
+   * A quadrilateral defined by the extremal points
+   * in the four orthogonal directions
+   * can be used, but even more inclusive is
+   * to use an octilateral defined by the points in the 8 cardinal directions.
+   * <p>
+   * Note that even if the method used to determine the polygon vertices
+   * is not 100% robust, this does not affect the robustness of the convex hull.
+   *
+   * @param pts
+   * @return
+   */
+  private Coordinate[] reduce(Coordinate[] inputPts)
+  {
+    //Coordinate[] polyPts = computeQuad(inputPts);
+    Coordinate[] polyPts = computeOctRing(inputPts);
+    //Coordinate[] polyPts = null;
+
+    // unable to compute interior polygon for some reason
+    if (polyPts == null)
+      return inputPts;
+
+//    LinearRing ring = geomFactory.createLinearRing(polyPts);
+//    System.out.println(ring);
+
+    // add points defining polygon
+    TreeSet reducedSet = new TreeSet();
+    for (int i = 0; i < polyPts.length; i++) {
+      reducedSet.add(polyPts[i]);
     }
-    bigPoly.add(bigQuad.westmost);
-    Coordinate[] bigPolyArray = new Coordinate[bigPoly.size()];
-    LinearRing bQ = factory.createLinearRing((Coordinate[]) bigPoly.toArray(bigPolyArray));
-//    LinearRing bQ = new LinearRing((Coordinate[]) bigPoly.toArray(bigPolyArray),
-//        geometry.getPrecisionModel(), geometry.getSRID());
-
-    // load an array with all points not in the big poly
-    // and the defining points.
-    TreeSet reducedSet = new TreeSet(bigPoly);
-    for (int i = 0; i < pts.length; i++) {
-      if (pointLocator.locate(pts[i], bQ) == Location.EXTERIOR) {
-        reducedSet.add(pts[i]);
+    /**
+     * Add all unique points not in the interior poly.
+     * CGAlgorithms.isPointInRing is not defined for points actually on the ring,
+     * but this doesn't matter since the points of the interior polygon
+     * are forced to be in the reduced set.
+     */
+    for (int i = 0; i < inputPts.length; i++) {
+      if (! CGAlgorithms.isPointInRing(inputPts[i], polyPts)) {
+        reducedSet.add(inputPts[i]);
       }
     }
-    Coordinate[] rP = (Coordinate[]) reducedSet.toArray(new Coordinate[0]);
-
-    // Return this array as the reduced problem.
-    return rP;
+    Coordinate[] reducedPts = CoordinateArrays.toCoordinateArray(reducedSet);
+    return reducedPts;
   }
 
   private Coordinate[] preSort(Coordinate[] pts) {
@@ -178,14 +196,14 @@ public class ConvexHull
     }
 
     // sort the points radially around the focal point.
-    radialSort(pts);
+    Arrays.sort(pts, 1, pts.length, new RadialComparator(pts[0]));
+
+    //radialSort(pts);
     return pts;
   }
 
   private Stack grahamScan(Coordinate[] c) {
     Coordinate p;
-    Coordinate p1;
-    Coordinate p2;
     Stack ps = new Stack();
     p = (Coordinate) ps.push(c[0]);
     p = (Coordinate) ps.push(c[1]);
@@ -202,53 +220,6 @@ public class ConvexHull
     return ps;
   }
 
-  private void radialSort(Coordinate[] p) {
-
-    // A selection sort routine, assumes the pivot point is
-    // the first point (i.e., p[0]).
-    Coordinate t;
-    for (int i = 1; i < (p.length - 1); i++) {
-      int min = i;
-      for (int j = i + 1; j < p.length; j++) {
-        if (polarCompare(p[0], p[j], p[min]) < 0) {
-          min = j;
-        }
-      }
-      t = p[i];
-      p[i] = p[min];
-      p[min] = t;
-    }
-  }
-
-  private int polarCompare(Coordinate o, Coordinate p, Coordinate q) {
-
-    // Given two points p and q compare them with respect to their radial
-    // ordering about point o. -1, 0 or 1 depending on whether p is less than,
-    // equal to or greater than q. First checks radial ordering then if both
-    // points lie on the same line, check distance to o.
-    double dxp = p.x - o.x;
-    double dyp = p.y - o.y;
-    double dxq = q.x - o.x;
-    double dyq = q.y - o.y;
-    double alph = Math.atan2(dxp, dyp);
-    double beta = Math.atan2(dxq, dyq);
-    if (alph < beta) {
-      return -1;
-    }
-    if (alph > beta) {
-      return 1;
-    }
-    double op = dxp * dxp + dyp * dyp;
-    double oq = dxq * dxq + dyq * dyq;
-    if (op < oq) {
-      return -1;
-    }
-    if (op > oq) {
-      return 1;
-    }
-    return 0;
-  }
-
   /**
    *@return    whether the three coordinates are collinear and c2 lies between
    *      c1 and c3 inclusive
@@ -276,6 +247,84 @@ public class ConvexHull
     return false;
   }
 
+  private Coordinate[] computeOctRing(Coordinate[] inputPts) {
+    Coordinate[] octPts = computeOctPts(inputPts);
+    CoordinateList coordList = new CoordinateList();
+    coordList.add(octPts, false);
+
+    // points must all lie in a line
+    if (coordList.size() < 3) {
+      return null;
+    }
+    coordList.closeRing();
+    return coordList.toCoordinateArray();
+  }
+
+  private Coordinate[] computeOctPts(Coordinate[] inputPts)
+  {
+    Coordinate[] pts = new Coordinate[8];
+    for (int j = 0; j < pts.length; j++) {
+      pts[j] = inputPts[0];
+    }
+    for (int i = 1; i < inputPts.length; i++) {
+      if (inputPts[i].x < pts[0].x) {
+        pts[0] = inputPts[i];
+      }
+      if (inputPts[i].x - inputPts[i].y < pts[1].x - pts[1].y) {
+        pts[1] = inputPts[i];
+      }
+      if (inputPts[i].y > pts[2].y) {
+        pts[2] = inputPts[i];
+      }
+      if (inputPts[i].x + inputPts[i].y > pts[3].x + pts[3].y) {
+        pts[3] = inputPts[i];
+      }
+      if (inputPts[i].x > pts[4].x) {
+        pts[4] = inputPts[i];
+      }
+      if (inputPts[i].x - inputPts[i].y > pts[5].x - pts[5].y) {
+        pts[5] = inputPts[i];
+      }
+      if (inputPts[i].y < pts[6].y) {
+        pts[6] = inputPts[i];
+      }
+      if (inputPts[i].x + inputPts[i].y < pts[7].x + pts[7].y) {
+        pts[7] = inputPts[i];
+      }
+    }
+    return pts;
+
+  }
+
+/*
+  // MD - no longer used, but keep for reference purposes
+  private Coordinate[] computeQuad(Coordinate[] inputPts) {
+    BigQuad bigQuad = bigQuad(inputPts);
+
+    // Build a linear ring defining a big poly.
+    ArrayList bigPoly = new ArrayList();
+    bigPoly.add(bigQuad.westmost);
+    if (! bigPoly.contains(bigQuad.northmost)) {
+      bigPoly.add(bigQuad.northmost);
+    }
+    if (! bigPoly.contains(bigQuad.eastmost)) {
+      bigPoly.add(bigQuad.eastmost);
+    }
+    if (! bigPoly.contains(bigQuad.southmost)) {
+      bigPoly.add(bigQuad.southmost);
+    }
+    // points must all lie in a line
+    if (bigPoly.size() < 3) {
+      return null;
+    }
+    // closing point
+    bigPoly.add(bigQuad.westmost);
+
+    Coordinate[] bigPolyArray = CoordinateArrays.toCoordinateArray(bigPoly);
+
+    return bigPolyArray;
+  }
+
   private BigQuad bigQuad(Coordinate[] pts) {
     BigQuad bigQuad = new BigQuad();
     bigQuad.northmost = pts[0];
@@ -299,6 +348,14 @@ public class ConvexHull
     return bigQuad;
   }
 
+  private static class BigQuad {
+    public Coordinate northmost;
+    public Coordinate southmost;
+    public Coordinate westmost;
+    public Coordinate eastmost;
+  }
+  */
+
   /**
    *@param  vertices  the vertices of a linear ring, which may or may not be
    *      flattened (i.e. vertices collinear)
@@ -310,12 +367,12 @@ public class ConvexHull
 
     coordinates = cleanRing(coordinates);
     if (coordinates.length == 3) {
-     return factory.createLineString(new Coordinate[]{coordinates[0], coordinates[1]});
+      return geomFactory.createLineString(new Coordinate[]{coordinates[0], coordinates[1]});
 //      return new LineString(new Coordinate[]{coordinates[0], coordinates[1]},
 //          geometry.getPrecisionModel(), geometry.getSRID());
     }
-    LinearRing linearRing = factory.createLinearRing(coordinates);
-    return factory.createPolygon(linearRing, null);
+    LinearRing linearRing = geomFactory.createLinearRing(coordinates);
+    return geomFactory.createPolygon(linearRing, null);
   }
 
   /**
@@ -346,11 +403,85 @@ public class ConvexHull
     return (Coordinate[]) cleanedRing.toArray(cleanedRingCoordinates);
   }
 
-  private static class BigQuad {
-    public Coordinate northmost;
-    public Coordinate southmost;
-    public Coordinate westmost;
-    public Coordinate eastmost;
-  }
 
-}
+  /**
+   * Compares {@link Coordinate}s for their angle and distance
+   * relative to an origin.
+   *
+   * @author Martin Davis
+   * @version 1.7
+   */
+  private static class RadialComparator
+      implements Comparator
+  {
+    private Coordinate origin;
+
+    public RadialComparator(Coordinate origin)
+    {
+      this.origin = origin;
+    }
+    public int compare(Object o1, Object o2)
+    {
+      Coordinate p1 = (Coordinate) o1;
+      Coordinate p2 = (Coordinate) o2;
+      return polarCompare(origin, p1, p2);
+    }
+
+    /**
+     * Given two points p and q compare them with respect to their radial
+     * ordering about point o.  First checks radial ordering.
+     * If points are collinear, the comparison is based
+     * on their distance to the origin.
+     * <p>
+     * p < q iff
+     * <ul>
+     * <li>ang(o-p) < ang(o-q) (e.g. o-p-q is CCW)
+     * <li>or ang(o-p) == ang(o-q) && dist(o,p) < dist(o,q)
+     * </ul>
+     *
+     * @param o the origin
+     * @param p a point
+     * @param q another point
+     * @return -1, 0 or 1 depending on whether p is less than,
+     * equal to or greater than q
+     */
+    private static int polarCompare(Coordinate o, Coordinate p, Coordinate q)
+    {
+      double dxp = p.x - o.x;
+      double dyp = p.y - o.y;
+      double dxq = q.x - o.x;
+      double dyq = q.y - o.y;
+
+/*
+      // MD - non-robust
+      int result = 0;
+      double alph = Math.atan2(dxp, dyp);
+      double beta = Math.atan2(dxq, dyq);
+      if (alph < beta) {
+        result = -1;
+      }
+      if (alph > beta) {
+        result = 1;
+      }
+      if (result !=  0) return result;
+      //*/
+
+      int orient = CGAlgorithms.computeOrientation(o, p, q);
+
+      if (orient == CGAlgorithms.COUNTERCLOCKWISE) return 1;
+      if (orient == CGAlgorithms.CLOCKWISE) return -1;
+
+      // points are collinear - check distance
+      double op = dxp * dxp + dyp * dyp;
+      double oq = dxq * dxq + dyq * dyq;
+      if (op < oq) {
+        return -1;
+      }
+      if (op > oq) {
+        return 1;
+      }
+      return 0;
+    }
+
+  }
+}
\ No newline at end of file
diff --git a/src/com/vividsolutions/jts/algorithm/HCoordinate.java b/src/com/vividsolutions/jts/algorithm/HCoordinate.java
index 5a515b4..0a3cfbd 100644
--- a/src/com/vividsolutions/jts/algorithm/HCoordinate.java
+++ b/src/com/vividsolutions/jts/algorithm/HCoordinate.java
@@ -1,67 +1,64 @@
-
-
-
 /*
- * The JTS Topology Suite is a collection of Java classes that
- * implement the fundamental operations required to validate a given
- * geo-spatial data set to a known topological specification.
- *
- * Copyright (C) 2001 Vivid Solutions
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- *
- * For more information, contact:
- *
- *     Vivid Solutions
- *     Suite #1A
- *     2328 Government Street
- *     Victoria BC  V8T 5G5
- *     Canada
- *
- *     (250)385-6040
- *     www.vividsolutions.com
+* The JTS Topology Suite is a collection of Java classes that
+* implement the fundamental operations required to validate a given
+* geo-spatial data set to a known topological specification.
+*
+* Copyright (C) 2001 Vivid Solutions
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation; either
+* version 2.1 of the License, or (at your option) any later version.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*
+* For more information, contact:
+*
+*     Vivid Solutions
+*     Suite #1A
+*     2328 Government Street
+*     Victoria BC  V8T 5G5
+*     Canada
+*
+*     (250)385-6040
+*     www.vividsolutions.com
  */
 package com.vividsolutions.jts.algorithm;
 
-/**
- * @version 1.6
- */
 import com.vividsolutions.jts.geom.*;
 
 /**
- * Represents a homogeneous coordinate for 2-D coordinates.
+ * Represents a homogeneous coordinate in a 2-D coordinate space.
+ * In JTS {@link HCoordinate}s are used as a clean way
+ * of computing intersections between line segments.
  *
- * @version 1.6
+ * @author David Skea
+ * @version 1.7
  */
 public class HCoordinate
 {
 
-/**
- * Computes the (approximate) intersection point between two line segments
- * using homogeneous coordinates.
- * <p>
- * Note that this algorithm is
- * not numerically stable; i.e. it can produce intersection points which
- * lie outside the envelope of the line segments themselves.  In order
- * to increase the precision of the calculation input points should be normalized
- * before passing them to this routine.
- */
+  /**
+   * Computes the (approximate) intersection point between two line segments
+   * using homogeneous coordinates.
+   * <p>
+   * Note that this algorithm is
+   * not numerically stable; i.e. it can produce intersection points which
+   * lie outside the envelope of the line segments themselves.  In order
+   * to increase the precision of the calculation input points should be normalized
+   * before passing them to this routine.
+   */
   public static Coordinate intersection(
       Coordinate p1, Coordinate p2,
       Coordinate q1, Coordinate q2)
-    throws NotRepresentableException
+      throws NotRepresentableException
   {
     HCoordinate l1 = new HCoordinate(new HCoordinate(p1), new HCoordinate(p2));
     HCoordinate l2 = new HCoordinate(new HCoordinate(q1), new HCoordinate(q2));
@@ -71,52 +68,58 @@ public class HCoordinate
   }
 
 
-    public double x,y,w;
+  public double x,y,w;
 
-    public HCoordinate() {
-        x = 0.0;
-        y = 0.0;
-        w = 1.0;
-    }
+  public HCoordinate() {
+    x = 0.0;
+    y = 0.0;
+    w = 1.0;
+  }
 
-    public HCoordinate(double _x, double _y, double _w) {
-        x = _x;
-        y = _y;
-        w = _w;
-    }
+  public HCoordinate(double _x, double _y, double _w) {
+    x = _x;
+    y = _y;
+    w = _w;
+  }
 
-    public HCoordinate(Coordinate p) {
-        x = p.x;
-        y = p.y;
-        w = 1.0;
-    }
+  public HCoordinate(double _x, double _y) {
+    x = _x;
+    y = _y;
+    w = 1.0;
+  }
 
-    public HCoordinate(HCoordinate p1, HCoordinate p2) {
-        x = p1.y*p2.w - p2.y*p1.w;
-        y = p2.x*p1.w - p1.x*p2.w;
-        w = p1.x*p2.y - p2.x*p1.y;
-    }
+  public HCoordinate(Coordinate p) {
+    x = p.x;
+    y = p.y;
+    w = 1.0;
+  }
 
-    public double getX() throws NotRepresentableException {
-        double a = x/w;
-        if ((Double.isNaN(a)) || (Double.isInfinite(a))) {
-          throw new NotRepresentableException();
-        }
-        return a;
-    }
+  public HCoordinate(HCoordinate p1, HCoordinate p2) {
+    x = p1.y * p2.w - p2.y * p1.w;
+    y = p2.x * p1.w - p1.x * p2.w;
+    w = p1.x * p2.y - p2.x * p1.y;
+  }
 
-    public double getY() throws NotRepresentableException {
-        double a = y/w;
-        if  ((Double.isNaN(a)) || (Double.isInfinite(a))) {
-          throw new NotRepresentableException();
-        }
-        return a;
+  public double getX() throws NotRepresentableException {
+    double a = x/w;
+    if ((Double.isNaN(a)) || (Double.isInfinite(a))) {
+      throw new NotRepresentableException();
     }
+    return a;
+  }
 
-    public Coordinate getCoordinate() throws NotRepresentableException {
-      Coordinate p = new Coordinate();
-      p.x = getX();
-      p.y = getY();
-      return p;
+  public double getY() throws NotRepresentableException {
+    double a = y/w;
+    if  ((Double.isNaN(a)) || (Double.isInfinite(a))) {
+      throw new NotRepresentableException();
     }
-}
+    return a;
+  }
+
+  public Coordinate getCoordinate() throws NotRepresentableException {
+    Coordinate p = new Coordinate();
+    p.x = getX();
+    p.y = getY();
+    return p;
+  }
+}
\ No newline at end of file
diff --git a/src/com/vividsolutions/jts/algorithm/InteriorPointArea.java b/src/com/vividsolutions/jts/algorithm/InteriorPointArea.java
index 5021d93..e531ef2 100644
--- a/src/com/vividsolutions/jts/algorithm/InteriorPointArea.java
+++ b/src/com/vividsolutions/jts/algorithm/InteriorPointArea.java
@@ -52,7 +52,7 @@ import com.vividsolutions.jts.geom.*;
  * which does not lie in the interior.
  * </b>
  *
- * @version 1.6
+ * @version 1.7
  */
 public class InteriorPointArea {
 
diff --git a/src/com/vividsolutions/jts/algorithm/InteriorPointLine.java b/src/com/vividsolutions/jts/algorithm/InteriorPointLine.java
index c6aec93..3b7d936 100644
--- a/src/com/vividsolutions/jts/algorithm/InteriorPointLine.java
+++ b/src/com/vividsolutions/jts/algorithm/InteriorPointLine.java
@@ -45,7 +45,7 @@ import com.vividsolutions.jts.geom.*;
  * closest to the centroid.
  * </ul>
  *
- * @version 1.6
+ * @version 1.7
  */
 public class InteriorPointLine {
 
diff --git a/src/com/vividsolutions/jts/algorithm/InteriorPointPoint.java b/src/com/vividsolutions/jts/algorithm/InteriorPointPoint.java
index 49abc6a..ecb4358 100644
--- a/src/com/vividsolutions/jts/algorithm/InteriorPointPoint.java
+++ b/src/com/vividsolutions/jts/algorithm/InteriorPointPoint.java
@@ -40,7 +40,7 @@ import com.vividsolutions.jts.geom.*;
  * <h2>Algorithm</h2>
  * Find a point which is closest to the centroid of the geometry.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class InteriorPointPoint {
 
diff --git a/src/com/vividsolutions/jts/algorithm/LineIntersector.java b/src/com/vividsolutions/jts/algorithm/LineIntersector.java
index 15c0394..89dbeee 100644
--- a/src/com/vividsolutions/jts/algorithm/LineIntersector.java
+++ b/src/com/vividsolutions/jts/algorithm/LineIntersector.java
@@ -36,10 +36,11 @@
 package com.vividsolutions.jts.algorithm;
 
 /**
- * @version 1.6
+ * @version 1.7
  */
 import com.vividsolutions.jts.geom.*;
 import com.vividsolutions.jts.util.*;
+import com.vividsolutions.jts.io.WKTWriter;
 
 /**
  * A LineIntersector is an algorithm that can both test whether
@@ -50,7 +51,7 @@ import com.vividsolutions.jts.util.*;
  * that the input coordinates have been made precise by scaling them to
  * an integer grid.)
  *
- * @version 1.6
+ * @version 1.7
  */
 public abstract class LineIntersector {
 
@@ -208,22 +209,31 @@ public abstract class LineIntersector {
                 Coordinate p1, Coordinate p2,
                 Coordinate q1, Coordinate q2);
 
+/*
   public String toString() {
     String str = inputLines[0][0] + "-"
          + inputLines[0][1] + " "
          + inputLines[1][0] + "-"
-         + inputLines[1][1] + " : ";
-    if (isEndPoint()) {
-      str += " endpoint";
-    }
-    if (isProper) {
-      str += " proper";
-    }
-    if (isCollinear()) {
-      str += " collinear";
-    }
+         + inputLines[1][1] + " : "
+               + getTopologySummary();
     return str;
   }
+*/
+
+  public String toString() {
+    return WKTWriter.toLineString(inputLines[0][0], inputLines[0][1]) + " - "
+    + WKTWriter.toLineString(inputLines[1][0], inputLines[1][1])
+                 + getTopologySummary();
+  }
+
+  private String getTopologySummary()
+  {
+    StringBuffer catBuf = new StringBuffer();
+    if (isEndPoint()) catBuf.append(" endpoint");
+    if (isProper) catBuf.append(" proper");
+    if (isCollinear()) catBuf.append(" collinear");
+    return catBuf.toString();
+  }
 
   protected boolean isEndPoint() {
     return hasIntersection() && !isProper;
diff --git a/src/com/vividsolutions/jts/algorithm/MCPointInRing.java b/src/com/vividsolutions/jts/algorithm/MCPointInRing.java
index 00fa608..06a9a10 100644
--- a/src/com/vividsolutions/jts/algorithm/MCPointInRing.java
+++ b/src/com/vividsolutions/jts/algorithm/MCPointInRing.java
@@ -36,7 +36,6 @@ package com.vividsolutions.jts.algorithm;
 import java.util.*;
 import com.vividsolutions.jts.geom.*;
 import com.vividsolutions.jts.index.chain.*;
-import com.vividsolutions.jts.index.strtree.*;
 import com.vividsolutions.jts.index.bintree.*;
 import com.vividsolutions.jts.index.bintree.Interval;
 
@@ -45,7 +44,7 @@ import com.vividsolutions.jts.index.bintree.Interval;
  * using {@link MonotoneChain}s and a {@link BinTree} index to
  * increase performance.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class MCPointInRing   implements PointInRing {
 
@@ -76,7 +75,7 @@ public class MCPointInRing   implements PointInRing {
 
   private void buildIndex()
   {
-    Envelope env = ring.getEnvelopeInternal();
+    //Envelope env = ring.getEnvelopeInternal();
     tree = new Bintree();
 
     Coordinate[] pts = CoordinateArrays.removeRepeatedPoints(ring.getCoordinates());
diff --git a/src/com/vividsolutions/jts/algorithm/MinimumDiameter.java b/src/com/vividsolutions/jts/algorithm/MinimumDiameter.java
index c3e8a60..fa2a281 100644
--- a/src/com/vividsolutions/jts/algorithm/MinimumDiameter.java
+++ b/src/com/vividsolutions/jts/algorithm/MinimumDiameter.java
@@ -51,7 +51,7 @@ import com.vividsolutions.jts.geom.*;
  *
  * @see ConvexHull
  *
- * @version 1.6
+ * @version 1.7
  */
 public class MinimumDiameter
 {
diff --git a/src/com/vividsolutions/jts/algorithm/NonRobustCGAlgorithms.java b/src/com/vividsolutions/jts/algorithm/NonRobustCGAlgorithms.java
index 196d1bc..2ed54ba 100644
--- a/src/com/vividsolutions/jts/algorithm/NonRobustCGAlgorithms.java
+++ b/src/com/vividsolutions/jts/algorithm/NonRobustCGAlgorithms.java
@@ -40,7 +40,7 @@ import com.vividsolutions.jts.geom.CoordinateSequence;
  * <b>FOR TESTING PURPOSES ONLY!</b>.
  * The non-robustness is due to rounding error in floating point computation.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class NonRobustCGAlgorithms
   extends CGAlgorithms
diff --git a/src/com/vividsolutions/jts/algorithm/NonRobustLineIntersector.java b/src/com/vividsolutions/jts/algorithm/NonRobustLineIntersector.java
index 7b00766..406952e 100644
--- a/src/com/vividsolutions/jts/algorithm/NonRobustLineIntersector.java
+++ b/src/com/vividsolutions/jts/algorithm/NonRobustLineIntersector.java
@@ -36,7 +36,7 @@ package com.vividsolutions.jts.algorithm;
 import com.vividsolutions.jts.algorithm.LineIntersector;
 
 /**
- *@version 1.6
+ *@version 1.7
  */
 
 import com.vividsolutions.jts.geom.*;
@@ -45,7 +45,7 @@ import com.vividsolutions.jts.util.Debug;
 /**
  * A non-robust version of {@LineIntersector}.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class NonRobustLineIntersector
     extends LineIntersector
diff --git a/src/com/vividsolutions/jts/algorithm/NotRepresentableException.java b/src/com/vividsolutions/jts/algorithm/NotRepresentableException.java
index af6efa5..522f349 100644
--- a/src/com/vividsolutions/jts/algorithm/NotRepresentableException.java
+++ b/src/com/vividsolutions/jts/algorithm/NotRepresentableException.java
@@ -39,7 +39,7 @@ package com.vividsolutions.jts.algorithm;
  * Indicates that a {@link HCoordinate} has been computed which is
  * not representable on the Cartesian plane.
  *
- * @version 1.6
+ * @version 1.7
  * @see HCoordinate
  */
 public class NotRepresentableException extends Exception {
diff --git a/src/com/vividsolutions/jts/algorithm/PointInRing.java b/src/com/vividsolutions/jts/algorithm/PointInRing.java
index b9fdfff..7950ecd 100644
--- a/src/com/vividsolutions/jts/algorithm/PointInRing.java
+++ b/src/com/vividsolutions/jts/algorithm/PointInRing.java
@@ -39,7 +39,7 @@ import com.vividsolutions.jts.geom.Coordinate;
  * An interface for classes which test whether a {@link Coordinate} lies inside
  * a ring.
  *
- * @version 1.6
+ * @version 1.7
  */
 public interface PointInRing {
 
diff --git a/src/com/vividsolutions/jts/algorithm/PointLocator.java b/src/com/vividsolutions/jts/algorithm/PointLocator.java
index ca86dff..812f6fd 100644
--- a/src/com/vividsolutions/jts/algorithm/PointLocator.java
+++ b/src/com/vividsolutions/jts/algorithm/PointLocator.java
@@ -41,15 +41,16 @@ import com.vividsolutions.jts.geomgraph.GeometryGraph;
 
 /**
  * Computes the topological relationship ({@link Location})
- * of a single point to a Geometry.
- * The algorithm obeys the SFS boundaryDetermination rule to correctly determine
+ * of a single point to a {@link Geometry}.
+ * The algorithm obeys the SFS Boundary Determination Rule to determine
  * whether the point lies on the boundary or not.
- * Note that instances of this class are not reentrant.
- * @version 1.6
+ * <p>
+ * Instances of this class are not reentrant.
+ *
+ * @version 1.7
  */
-public class PointLocator {
-
-
+public class PointLocator
+{
   private boolean isIn;         // true if the point lies in or on any Geometry element
   private int numBoundaries;    // the number of sub-elements whose boundaries the point lies in
 
@@ -74,7 +75,7 @@ public class PointLocator {
    * It handles both single-element
    * and multi-element Geometries.
    * The algorithm for multi-part Geometries
-   * takes into account the boundaryDetermination rule.
+   * takes into account the SFS Boundary Determination Rule.
    *
    * @return the {@link Location} of the point relative to the input Geometry
    */
@@ -82,12 +83,12 @@ public class PointLocator {
   {
     if (geom.isEmpty()) return Location.EXTERIOR;
 
-    if (geom instanceof LineString) {
-      return locate(p, (LineString) geom);
-    }
     if (geom instanceof LinearRing) {
       return locate(p, (LinearRing) geom);
     }
+    if (geom instanceof LineString) {
+      return locate(p, (LineString) geom);
+    }
     else if (geom instanceof Polygon) {
       return locate(p, (Polygon) geom);
     }
@@ -102,12 +103,12 @@ public class PointLocator {
 
   private void computeLocation(Coordinate p, Geometry geom)
   {
-    if (geom instanceof LineString) {
-      updateLocationInfo(locate(p, (LineString) geom));
-    }
     if (geom instanceof LinearRing) {
       updateLocationInfo(locate(p, (LinearRing) geom));
     }
+    if (geom instanceof LineString) {
+      updateLocationInfo(locate(p, (LineString) geom));
+    }
     else if (geom instanceof Polygon) {
       updateLocationInfo(locate(p, (Polygon) geom));
     }
@@ -183,6 +184,4 @@ public class PointLocator {
     }
     return Location.INTERIOR;
   }
-
-
 }
diff --git a/src/com/vividsolutions/jts/algorithm/RobustCGAlgorithms.java b/src/com/vividsolutions/jts/algorithm/RobustCGAlgorithms.java
index c7aba52..04db936 100644
--- a/src/com/vividsolutions/jts/algorithm/RobustCGAlgorithms.java
+++ b/src/com/vividsolutions/jts/algorithm/RobustCGAlgorithms.java
@@ -40,7 +40,7 @@ import com.vividsolutions.jts.geom.*;
  * Stub version of RobustCGAlgorithms for backwards compatibility.
  * Will be deprecated in next release - use CGAlgorithms instead.
  *
- * @version 1.6
+ * @version 1.7
  *
  */
 public class RobustCGAlgorithms extends CGAlgorithms {
diff --git a/src/com/vividsolutions/jts/algorithm/RobustDeterminant.java b/src/com/vividsolutions/jts/algorithm/RobustDeterminant.java
index 634b216..15376ce 100644
--- a/src/com/vividsolutions/jts/algorithm/RobustDeterminant.java
+++ b/src/com/vividsolutions/jts/algorithm/RobustDeterminant.java
@@ -35,7 +35,7 @@
 package com.vividsolutions.jts.algorithm;
 
 /**
- * @version 1.6
+ * @version 1.7
  */
 
 /**
@@ -59,7 +59,7 @@ package com.vividsolutions.jts.algorithm;
  **************************************************************************
  * </pre>
  *
- * @version 1.6
+ * @version 1.7
  */
 public class RobustDeterminant {
 
diff --git a/src/com/vividsolutions/jts/algorithm/RobustLineIntersector.java b/src/com/vividsolutions/jts/algorithm/RobustLineIntersector.java
index 9bdb4f2..f275835 100644
--- a/src/com/vividsolutions/jts/algorithm/RobustLineIntersector.java
+++ b/src/com/vividsolutions/jts/algorithm/RobustLineIntersector.java
@@ -35,7 +35,7 @@
 package com.vividsolutions.jts.algorithm;
 
 /**
- *@version 1.6
+ *@version 1.7
  */
 
 import com.vividsolutions.jts.geom.*;
@@ -44,7 +44,7 @@ import com.vividsolutions.jts.util.Assert;
 /**
  * A robust version of {@LineIntersector}.
  *
- * @version 1.6
+ * @version 1.7
  * @see RobustDeterminant
  */
 public class RobustLineIntersector
@@ -200,7 +200,7 @@ public class RobustLineIntersector
     Coordinate n3 = new Coordinate(q1);
     Coordinate n4 = new Coordinate(q2);
     Coordinate normPt = new Coordinate();
-    normalize(n1, n2, n3, n4, normPt);
+    normalizeToEnvCentre(n1, n2, n3, n4, normPt);
 
     Coordinate intPt = null;
     try {
@@ -213,21 +213,53 @@ public class RobustLineIntersector
     intPt.x += normPt.x;
     intPt.y += normPt.y;
 
+    /**
+     *
+     * MD - May 4 2005 - This is still a problem.  Here is a failure case:
+     *
+     * LINESTRING (2089426.5233462777 1180182.3877339689, 2085646.6891757075 1195618.7333999649)
+     * LINESTRING (1889281.8148903656 1997547.0560044837, 2259977.3672235999 483675.17050843034)
+     * int point = (2097408.2633752143,1144595.8008114607)
+     */
+    if (! isInSegmentEnvelopes(intPt)) {
+      System.out.println("Intersection outside segment envelopes: " + intPt);
+    }
+    /*
+     // disabled until a better solution is found
+    if (! isInSegmentEnvelopes(intPt)) {
+      System.out.println("first value outside segment envelopes: " + intPt);
+
+      IteratedBisectionIntersector ibi = new IteratedBisectionIntersector(p1, p2, q1, q2);
+      intPt = ibi.getIntersection();
+    }
+    if (! isInSegmentEnvelopes(intPt)) {
+      System.out.println("ERROR - outside segment envelopes: " + intPt);
+
+      IteratedBisectionIntersector ibi = new IteratedBisectionIntersector(p1, p2, q1, q2);
+      Coordinate testPt = ibi.getIntersection();
+    }
+    */
+
     if (precisionModel != null) {
       precisionModel.makePrecise(intPt);
     }
 
-    /**
-     * MD - after fairly extensive testing
-     * it appears that the computed intPt always lies in the segment envelopes
-     */
-    //if (! isInSegmentEnvelopes(intPt))
-    //    System.out.println("outside segment envelopes: " + intPt);
-
     return intPt;
   }
 
-  private void normalize(
+  /**
+   * Normalize the supplied coordinates so that
+   * their minimum ordinate values lie at the origin.
+   * NOTE: this normalization technique appears to cause
+   * large errors in the position of the intersection point for some cases.
+   *
+   * @param n1
+   * @param n2
+   * @param n3
+   * @param n4
+   * @param normPt
+   */
+  private void normalizeToMinimum(
     Coordinate n1,
     Coordinate n2,
     Coordinate n3,
@@ -242,6 +274,61 @@ public class RobustLineIntersector
     n4.x -= normPt.x;    n4.y -= normPt.y;
   }
 
+  /**
+   * Normalize the supplied coordinates to
+   * so that the midpoint of their intersection envelope
+   * lies at the origin.
+   *
+   * @param n00
+   * @param n01
+   * @param n10
+   * @param n11
+   * @param normPt
+   */
+  private void normalizeToEnvCentre(
+    Coordinate n00,
+    Coordinate n01,
+    Coordinate n10,
+    Coordinate n11,
+    Coordinate normPt)
+  {
+    double minX0 = n00.x < n01.x ? n00.x : n01.x;
+    double minY0 = n00.y < n01.y ? n00.y : n01.y;
+    double maxX0 = n00.x > n01.x ? n00.x : n01.x;
+    double maxY0 = n00.y > n01.y ? n00.y : n01.y;
+
+    double minX1 = n10.x < n11.x ? n10.x : n11.x;
+    double minY1 = n10.y < n11.y ? n10.y : n11.y;
+    double maxX1 = n10.x > n11.x ? n10.x : n11.x;
+    double maxY1 = n10.y > n11.y ? n10.y : n11.y;
+
+    double intMinX = minX0 > minX1 ? minX0 : minX1;
+    double intMaxX = maxX0 < maxX1 ? maxX0 : maxX1;
+    double intMinY = minY0 > minY1 ? minY0 : minY1;
+    double intMaxY = maxY0 < maxY1 ? maxY0 : maxY1;
+
+    double intMidX = (intMinX + intMaxX) / 2.0;
+    double intMidY = (intMinY + intMaxY) / 2.0;
+    normPt.x = intMidX;
+    normPt.y = intMidY;
+
+    /*
+    // equilavalent code using more modular but slower method
+    Envelope env0 = new Envelope(n00, n01);
+    Envelope env1 = new Envelope(n10, n11);
+    Envelope intEnv = env0.intersection(env1);
+    Coordinate intMidPt = intEnv.centre();
+
+    normPt.x = intMidPt.x;
+    normPt.y = intMidPt.y;
+    */
+
+    n00.x -= normPt.x;    n00.y -= normPt.y;
+    n01.x -= normPt.x;    n01.y -= normPt.y;
+    n10.x -= normPt.x;    n10.y -= normPt.y;
+    n11.x -= normPt.x;    n11.y -= normPt.y;
+  }
+
   private double smallestInAbsValue(double x1, double x2, double x3, double x4)
   {
     double x = x1;
diff --git a/src/com/vividsolutions/jts/algorithm/SIRtreePointInRing.java b/src/com/vividsolutions/jts/algorithm/SIRtreePointInRing.java
index 9ca4b79..2796936 100644
--- a/src/com/vividsolutions/jts/algorithm/SIRtreePointInRing.java
+++ b/src/com/vividsolutions/jts/algorithm/SIRtreePointInRing.java
@@ -42,7 +42,7 @@ import com.vividsolutions.jts.index.strtree.*;
  * using a {@link SIRtree} index to
  * increase performance.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class SIRtreePointInRing implements PointInRing {
 
diff --git a/src/com/vividsolutions/jts/algorithm/SimplePointInAreaLocator.java b/src/com/vividsolutions/jts/algorithm/SimplePointInAreaLocator.java
index 8322de4..baf67fe 100644
--- a/src/com/vividsolutions/jts/algorithm/SimplePointInAreaLocator.java
+++ b/src/com/vividsolutions/jts/algorithm/SimplePointInAreaLocator.java
@@ -44,7 +44,7 @@ import com.vividsolutions.jts.geom.*;
  * The algorithm used is only guaranteed to return correct results
  * for points which are <b>not</b> on the boundary of the Geometry.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class SimplePointInAreaLocator
 {
diff --git a/src/com/vividsolutions/jts/algorithm/SimplePointInRing.java b/src/com/vividsolutions/jts/algorithm/SimplePointInRing.java
index b9a8d1c..5370109 100644
--- a/src/com/vividsolutions/jts/algorithm/SimplePointInRing.java
+++ b/src/com/vividsolutions/jts/algorithm/SimplePointInRing.java
@@ -39,7 +39,7 @@ import com.vividsolutions.jts.geom.*;
  * Tests whether a {@link Coordinate} lies inside
  * a ring, using a linear-time algorithm.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class SimplePointInRing
   implements PointInRing
diff --git a/src/com/vividsolutions/jts/algorithm/package.html b/src/com/vividsolutions/jts/algorithm/package.html
index 60b855d..70b5c0e 100644
--- a/src/com/vividsolutions/jts/algorithm/package.html
+++ b/src/com/vividsolutions/jts/algorithm/package.html
@@ -8,37 +8,33 @@
 <body bgcolor="white">
 
 Contains classes and interfaces implementing fundamental computational geometry algorithms.
-<P>
-The Java Topology Suite (JTS) is a Java API that implements a core set of spatial data operations using an explicit precision model and robust geometric algorithms. JTS is intended to be used in the development of applications that support the validation, cleaning, integration and querying of spatial datasets.
-<P>
-JTS attempts to implement the OpenGIS Simple Features Specification (SFS) as accurately as possible.  In some cases the SFS is unclear or omits a specification; in this case JTS attempts to choose a reasonable and consistent alternative.  Differences from and elaborations of the SFS are documented in this specification.
 
 <H3>Robustness</H3>
 
-Geometrical algorithms involve a combination of combinatorial and numerical computation.  As with 
-all numerical computation using finite-precision numbers, the algorithms chosen are susceptible to 
-problems of robustness.  A robustness problem occurs when a numerical calculation produces an 
-incorrect answer for some inputs due to round-off errors.  Robustness problems are especially 
+Geometrical algorithms involve a combination of combinatorial and numerical computation.  As with
+all numerical computation using finite-precision numbers, the algorithms chosen are susceptible to
+problems of robustness.  A robustness problem occurs when a numerical calculation produces an
+incorrect answer for some inputs due to round-off errors.  Robustness problems are especially
 serious in geometric computation, since they can result in errors during topology building.
 <P>
-There are many approaches to dealing with the problem of robustness in geometrical computation.  
-Not surprisingly, most robust algorithms are substantially more complex and less performant than 
-the non-robust versions.  Fortunately, JTS is sensitive to robustness problems in only a few key 
-functions (such as line intersection and the point-in-polygon test).  There are efficient robust 
+There are many approaches to dealing with the problem of robustness in geometrical computation.
+Not surprisingly, most robust algorithms are substantially more complex and less performant than
+the non-robust versions.  Fortunately, JTS is sensitive to robustness problems in only a few key
+functions (such as line intersection and the point-in-polygon test).  There are efficient robust
 algorithms available for these functions, and these algorithms are implemented in JTS.
 
 <H3>Computational Performance</H3>
 
-Runtime performance is an important consideration for a production-quality implementation of 
-geometric algorithms.  The most computationally intensive algorithm used in JTS is intersection 
-detection.  JTS methods need to determine both all intersection between the line segments in a 
-single Geometry (self-intersection) and all intersections between the line segments of two different 
-Geometries.  
+Runtime performance is an important consideration for a production-quality implementation of
+geometric algorithms.  The most computationally intensive algorithm used in JTS is intersection
+detection.  JTS methods need to determine both all intersection between the line segments in a
+single Geometry (self-intersection) and all intersections between the line segments of two different
+Geometries.
 <P>
-The obvious naive algorithm for intersection detection (comparing every segment with every other) 
-has unacceptably slow performance.  There is a large literature of faster algorithms for intersection 
-detection.  Unfortunately, many of them involve substantial code complexity.  JTS tries to balance code 
-simplicity with performance gains.  It uses some simple techniques to produce substantial performance 
+The obvious naive algorithm for intersection detection (comparing every segment with every other)
+has unacceptably slow performance.  There is a large literature of faster algorithms for intersection
+detection.  Unfortunately, many of them involve substantial code complexity.  JTS tries to balance code
+simplicity with performance gains.  It uses some simple techniques to produce substantial performance
 gains for common types of input data.
 
 
diff --git a/src/com/vividsolutions/jts/geom/Coordinate.java b/src/com/vividsolutions/jts/geom/Coordinate.java
index 1f56b46..89016fa 100644
--- a/src/com/vividsolutions/jts/geom/Coordinate.java
+++ b/src/com/vividsolutions/jts/geom/Coordinate.java
@@ -1,39 +1,39 @@
 /*
- * The JTS Topology Suite is a collection of Java classes that
- * implement the fundamental operations required to validate a given
- * geo-spatial data set to a known topological specification.
- *
- * Copyright (C) 2001 Vivid Solutions
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- *
- * For more information, contact:
- *
- *     Vivid Solutions
- *     Suite #1A
- *     2328 Government Street
- *     Victoria BC  V8T 5G5
- *     Canada
- *
- *     (250)385-6040
- *     www.vividsolutions.com
+* The JTS Topology Suite is a collection of Java classes that
+* implement the fundamental operations required to validate a given
+* geo-spatial data set to a known topological specification.
+*
+* Copyright (C) 2001 Vivid Solutions
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation; either
+* version 2.1 of the License, or (at your option) any later version.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*
+* For more information, contact:
+*
+*     Vivid Solutions
+*     Suite #1A
+*     2328 Government Street
+*     Victoria BC  V8T 5G5
+*     Canada
+*
+*     (250)385-6040
+*     www.vividsolutions.com
  */
 package com.vividsolutions.jts.geom;
 
 import java.io.Serializable;
-
+import java.util.Comparator;
 import com.vividsolutions.jts.util.Assert;
 
 
@@ -52,221 +52,281 @@ import com.vividsolutions.jts.util.Assert;
  *  z-ordinate of <code>NaN</code>.  The standard comparison functions will ignore
  *  the z-ordinate.
  *
- *@version 1.6
+ *@version 1.7
  */
 public class Coordinate implements Comparable, Cloneable, Serializable {
-    private static final long serialVersionUID = 6683108902428366910L;
-    /**
-     *  The x-coordinate.
-     */
-    public double x;
-    /**
-     *  The y-coordinate.
-     */
-    public double y;
-    /**
-     *  The z-coordinate.
-     */
-    public double z;
+  private static final long serialVersionUID = 6683108902428366910L;
+  /**
+   *  The x-coordinate.
+   */
+  public double x;
+  /**
+   *  The y-coordinate.
+   */
+  public double y;
+  /**
+   *  The z-coordinate.
+   */
+  public double z;
 
-    /**
-     *  Constructs a <code>Coordinate</code> at (x,y,z).
-     *
-     *@param  x  the x-value
-     *@param  y  the y-value
-     *@param  z  the z-value
-     */
-    public Coordinate(double x, double y, double z) {
-        this.x = x;
-        this.y = y;
-        this.z = z;
-    }
+  /**
+   *  Constructs a <code>Coordinate</code> at (x,y,z).
+   *
+   *@param  x  the x-value
+   *@param  y  the y-value
+   *@param  z  the z-value
+   */
+  public Coordinate(double x, double y, double z) {
+    this.x = x;
+    this.y = y;
+    this.z = z;
+  }
 
-    /**
-     *  Constructs a <code>Coordinate</code> at (0,0,NaN).
-     */
-    public Coordinate() {
-        this(0.0, 0.0);
-    }
+  /**
+   *  Constructs a <code>Coordinate</code> at (0,0,NaN).
+   */
+  public Coordinate() {
+    this(0.0, 0.0);
+  }
 
-    /**
-     *  Constructs a <code>Coordinate</code> having the same (x,y,z) values as
-     *  <code>other</code>.
-     *
-     *@param  c  the <code>Coordinate</code> to copy.
-     */
-    public Coordinate(Coordinate c) {
-        this(c.x, c.y, c.z);
-    }
+  /**
+   *  Constructs a <code>Coordinate</code> having the same (x,y,z) values as
+   *  <code>other</code>.
+   *
+   *@param  c  the <code>Coordinate</code> to copy.
+   */
+  public Coordinate(Coordinate c) {
+    this(c.x, c.y, c.z);
+  }
 
-    /**
-     *  Constructs a <code>Coordinate</code> at (x,y,NaN).
-     *
-     *@param  x  the x-value
-     *@param  y  the y-value
-     */
-    public Coordinate(double x, double y) {
-        this(x, y, Double.NaN);
-    }
+  /**
+   *  Constructs a <code>Coordinate</code> at (x,y,NaN).
+   *
+   *@param  x  the x-value
+   *@param  y  the y-value
+   */
+  public Coordinate(double x, double y) {
+    this(x, y, Double.NaN);
+  }
 
 
 
-    /**
-     *  Sets this <code>Coordinate</code>s (x,y,z) values to that of <code>other</code>
-     *  .
-     *
-     *@param  other  the <code>Coordinate</code> to copy
-     */
-    public void setCoordinate(Coordinate other) {
-        x = other.x;
-        y = other.y;
-        z = other.z;
+  /**
+   *  Sets this <code>Coordinate</code>s (x,y,z) values to that of <code>other</code>
+   *  .
+   *
+   *@param  other  the <code>Coordinate</code> to copy
+   */
+  public void setCoordinate(Coordinate other) {
+    x = other.x;
+    y = other.y;
+    z = other.z;
+  }
+
+  /**
+   *  Returns whether the planar projections of the two <code>Coordinate</code>s
+   *  are equal.
+   *
+   *@param  other  a <code>Coordinate</code> with which to do the 2D comparison.
+   *@return        <code>true</code> if the x- and y-coordinates are equal; the
+   *      z-coordinates do not have to be equal.
+   */
+  public boolean equals2D(Coordinate other) {
+    if (x != other.x) {
+      return false;
     }
 
-    /**
-     *  Returns whether the planar projections of the two <code>Coordinate</code>s
-     *  are equal.
-     *
-     *@param  other  a <code>Coordinate</code> with which to do the 2D comparison.
-     *@return        <code>true</code> if the x- and y-coordinates are equal; the
-     *      z-coordinates do not have to be equal.
-     */
-    public boolean equals2D(Coordinate other) {
-        if (x != other.x) {
-            return false;
-        }
+    if (y != other.y) {
+      return false;
+    }
 
-        if (y != other.y) {
-            return false;
-        }
+    return true;
+  }
 
-        return true;
+  /**
+   *  Returns <code>true</code> if <code>other</code> has the same values for
+   *  the x and y ordinates.
+   *  Since Coordinates are 2.5D, this routine ignores the z value when making the comparison.
+   *
+   *@param  other  a <code>Coordinate</code> with which to do the comparison.
+   *@return        <code>true</code> if <code>other</code> is a <code>Coordinate</code>
+   *      with the same values for the x and y ordinates.
+   */
+  public boolean equals(Object other) {
+    if (!(other instanceof Coordinate)) {
+      return false;
     }
+    return equals2D((Coordinate) other);
+  }
 
-    /**
-     *  Returns <code>true</code> if <code>other</code> has the same values for
-     *  the x and y ordinates.
-     *  Since Coordinates are 2.5D, this routine ignores the z value when making the comparison.
-     *
-     *@param  other  a <code>Coordinate</code> with which to do the comparison.
-     *@return        <code>true</code> if <code>other</code> is a <code>Coordinate</code>
-     *      with the same values for the x and y ordinates.
-     */
-    public boolean equals(Object other) {
-        if (!(other instanceof Coordinate)) {
-            return false;
-        }
-        return equals2D((Coordinate) other);
-    }
+  /**
+   *  Compares this {@link Coordinate} with the specified {@link Coordinate} for order.
+   *  This method ignores the z value when making the comparison.
+   *  Returns:
+   *  <UL>
+   *    <LI> -1 : this.x < other.x || ((this.x == other.x) && (this.y <
+   *    other.y))
+   *    <LI> 0 : this.x == other.x && this.y = other.y
+   *    <LI> 1 : this.x > other.x || ((this.x == other.x) && (this.y > other.y))
+   *
+   *  </UL>
+   *  Note: This method assumes that ordinate values
+   * are valid numbers.  NaN values are not handled correctly.
+   *
+   *@param  o  the <code>Coordinate</code> with which this <code>Coordinate</code>
+   *      is being compared
+   *@return    -1, zero, or 1 as this <code>Coordinate</code>
+   *      is less than, equal to, or greater than the specified <code>Coordinate</code>
+   */
+  public int compareTo(Object o) {
+    Coordinate other = (Coordinate) o;
 
-    /**
-     *  Compares this object with the specified object for order.
-     *  Since Coordinates are 2.5D, this routine ignores the z value when making the comparison.
-     *  Returns
-     *  <UL>
-     *    <LI> -1 : this.x < other.x || ((this.x == other.x) && (this.y <
-     *    other.y))
-     *    <LI> 0 : this.x == other.x && this.y = other.y
-     *    <LI> 1 : this.x > other.x || ((this.x == other.x) && (this.y > other.y))
-     *
-     *  </UL>
-     *
-     *
-     *@param  o  the <code>Coordinate</code> with which this <code>Coordinate</code>
-     *      is being compared
-     *@return    a negative integer, zero, or a positive integer as this <code>Coordinate</code>
-     *      is less than, equal to, or greater than the specified <code>Coordinate</code>
-     */
-    public int compareTo(Object o) {
-        Coordinate other = (Coordinate) o;
+    if (x < other.x) return -1;
+    if (x > other.x) return 1;
+    if (y < other.y) return -1;
+    if (y > other.y) return 1;
+    return 0;
+  }
 
-        if (x < other.x) {
-            return -1;
-        }
+  /**
+   *  Returns <code>true</code> if <code>other</code> has the same values for x,
+   *  y and z.
+   *
+   *@param  other  a <code>Coordinate</code> with which to do the 3D comparison.
+   *@return        <code>true</code> if <code>other</code> is a <code>Coordinate</code>
+   *      with the same values for x, y and z.
+   */
+  public boolean equals3D(Coordinate other) {
+    return (x == other.x) && (y == other.y) &&
+               ((z == other.z) ||
+               (Double.isNaN(z) && Double.isNaN(other.z)));
+  }
 
-        if (x > other.x) {
-            return 1;
-        }
+  /**
+   *  Returns a <code>String</code> of the form <I>(x,y,z)</I> .
+   *
+   *@return    a <code>String</code> of the form <I>(x,y,z)</I>
+   */
+  public String toString() {
+    return "(" + x + ", " + y + ", " + z + ")";
+  }
 
-        if (y < other.y) {
-            return -1;
-        }
+  public Object clone() {
+    try {
+      Coordinate coord = (Coordinate) super.clone();
 
-        if (y > other.y) {
-            return 1;
-        }
+      return coord; // return the clone
+    } catch (CloneNotSupportedException e) {
+      Assert.shouldNeverReachHere(
+          "this shouldn't happen because this class is Cloneable");
 
-        return 0;
+      return null;
     }
+  }
+
+  public double distance(Coordinate p) {
+    double dx = x - p.x;
+    double dy = y - p.y;
+
+    return Math.sqrt(dx * dx + dy * dy);
+  }
+
+  public int hashCode() {
+    //Algorithm from Effective Java by Joshua Bloch [Jon Aquino]
+    int result = 17;
+    result = 37 * result + hashCode(x);
+    result = 37 * result + hashCode(y);
+    return result;
+  }
+
+  /**
+   * Returns a hash code for a double value, using the algorithm from
+   * Joshua Bloch's book <i>Effective Java"</i>
+   */
+  public static int hashCode(double x) {
+    long f = Double.doubleToLongBits(x);
+    return (int)(f^(f>>>32));
+  }
+
 
+  /**
+   * Compares two {@link Coordinate}s, allowing for either a 2-dimensional
+   * or 3-dimensional comparison, and handling NaN values correctly.
+   */
+  public static class DimensionalComparator
+      implements Comparator
+  {
     /**
-     *  Returns <code>true</code> if <code>other</code> has the same values for x,
-     *  y and z.
+     * Compare two <code>double</code>s, allowing for NaN values.
+     * NaN is treated as being less than any valid number.
      *
-     *@param  other  a <code>Coordinate</code> with which to do the 3D comparison.
-     *@return        <code>true</code> if <code>other</code> is a <code>Coordinate</code>
-     *      with the same values for x, y and z.
+     * @param a a <code>double</code>
+     * @param b a <code>double</code>
+     * @return -1, 0, or 1 depending on whether a is less than, equal to or greater than b
      */
-    public boolean equals3D(Coordinate other) {
-        return (x == other.x) && (y == other.y) &&
-        ((z == other.z) ||
-        (Double.isNaN(z) && Double.isNaN(other.z)));
+    public static int compare(double a, double b)
+    {
+      if (a < b) return -1;
+      if (a > b) return 1;
+
+      if (Double.isNaN(a)) {
+        if (Double.isNaN(b)) return 0;
+        return -1;
+      }
+
+      if (Double.isNaN(b)) return 1;
+      return 0;
     }
 
+    private int dimensionsToTest = 2;
+
     /**
-     *  Returns a <code>String</code> of the form <I>(x,y,z)</I> .
-     *
-     *@return    a <code>String</code> of the form <I>(x,y,z)</I>
+     * Creates a comparator for 2 dimensional coordinates.
      */
-    public String toString() {
-        return "(" + x + ", " + y + ", " + z + ")";
+    public DimensionalComparator()
+    {
+      this(2);
     }
 
-    public Object clone() {
-        try {
-            Coordinate coord = (Coordinate) super.clone();
-
-            return coord; // return the clone
-        } catch (CloneNotSupportedException e) {
-            Assert.shouldNeverReachHere(
-                "this shouldn't happen because this class is Cloneable");
-
-            return null;
-        }
+    /**
+     * Creates a comparator for 2 or 3 dimensional coordinates, depending
+     * on the value provided.
+     *
+     * @param dimensionLimit the number of dimensions to test
+     */
+    public DimensionalComparator(int dimensionsToTest)
+    {
+      if (dimensionsToTest != 2 && dimensionsToTest != 3)
+        throw new IllegalArgumentException("only 2 or 3 dimensions may be specified");
+      this.dimensionsToTest = dimensionsToTest;
     }
 
     /**
-     * "Fixes" this Coordinate to the PrecisionModel grid.
+     * Compares two {@link Coordinate}s along to the number of
+     * dimensions specified.
+     *
+     * @param o1 a {@link Coordinate}
+     * @param o2 a {link Coordinate}
+     * @return -1, 0, or 1 depending on whether o1 is less than,
+     * equal to, or greater than 02
+     *
      */
+    public int compare(Object o1, Object o2)
+    {
+      Coordinate c1 = (Coordinate) o1;
+      Coordinate c2 = (Coordinate) o2;
 
-    /*
-       public void makePrecise(PrecisionModel precisionModel)
-       {
-         x = precisionModel.makePrecise(x);
-         y = precisionModel.makePrecise(y);
-       }
-     */
-    public double distance(Coordinate p) {
-        double dx = x - p.x;
-        double dy = y - p.y;
+      int compX = compare(c1.x, c2.x);
+      if (compX != 0) return compX;
 
-        return Math.sqrt(dx * dx + dy * dy);
-    }
+      int compY = compare(c1.y, c2.y);
+      if (compY != 0) return compY;
 
-    public int hashCode() {
-        //Algorithm from Effective Java by Joshua Bloch [Jon Aquino]
-        int result = 17;
-        result = 37 * result + hashCode(x);
-        result = 37 * result + hashCode(y);
-        return result;
-    }
+      if (dimensionsToTest <= 2) return 0;
 
-    /**
-     * Returns a hash code for a double value, using the algorithm from
-     * Joshua Bloch's book <i>Effective Java"</i>
-     */
-    public static int hashCode(double x) {
-        long f = Double.doubleToLongBits(x);
-        return (int)(f^(f>>>32));
+      int compZ = compare(c1.z, c2.z);
+      return compZ;
     }
-}
+  }
+
+}
\ No newline at end of file
diff --git a/src/com/vividsolutions/jts/geom/CoordinateArrays.java b/src/com/vividsolutions/jts/geom/CoordinateArrays.java
index 28fb2ba..8315c11 100644
--- a/src/com/vividsolutions/jts/geom/CoordinateArrays.java
+++ b/src/com/vividsolutions/jts/geom/CoordinateArrays.java
@@ -34,18 +34,176 @@
  */
 package com.vividsolutions.jts.geom;
 
-import java.util.List;
+import java.util.*;
 
 /**
  * Useful utility functions for handling Coordinate arrays
  *
- * @version 1.6
+ * @version 1.7
  */
 public class CoordinateArrays {
 
   private final static Coordinate[] coordArrayType = new Coordinate[0];
 
   /**
+   * Finds a point in a list of points which is not contained in another list of points
+   * @param testPts the {@link Coordinate}s to test
+   * @param pts an array of {@link Coordinate}s to test the input points against
+   * @return a {@link Coordinate} from <code>testPts</code> which is not in <code>pts</code>, '
+   * or <code>null</code>
+   */
+  public static Coordinate ptNotInList(Coordinate[] testPts, Coordinate[] pts)
+  {
+    for (int i = 0; i < testPts.length; i++) {
+      Coordinate testPt = testPts[i];
+      if (CoordinateArrays.indexOf(testPt, pts) >= 0)
+          return testPt;
+    }
+    return null;
+  }
+
+  /**
+   * Compares two {@link Coordinate} arrays
+   * in the forward direction of their coordinates,
+   * using lexicographic ordering.
+   *
+   * @param pts1
+   * @param pts2
+   * @return
+   */
+  public static int compare(Coordinate[] pts1, Coordinate[] pts2) {
+    int i = 0;
+    while (i < pts1.length && i < pts2.length) {
+      int compare = pts1[i].compareTo(pts2[i]);
+      if (compare != 0)
+        return compare;
+      i++;
+    }
+    // handle situation when arrays are of different length
+    if (i < pts2.length) return -1;
+    if (i < pts1.length) return 1;
+
+    return 0;
+  }
+
+  /**
+   * A {@link Comparator} for {@link Coordinate} arrays
+   * in the forward direction of their coordinates,
+   * using lexicographic ordering.
+   */
+  public static class ForwardComparator
+      implements Comparator
+  {
+    public int compare(Object o1, Object o2) {
+      Coordinate[] pts1 = (Coordinate[]) o1;
+      Coordinate[] pts2 = (Coordinate[]) o2;
+
+      return CoordinateArrays.compare(pts1, pts2);
+    }
+  }
+
+
+  /**
+   * Determines which orientation of the {@link Coordinate} array
+   * is (overall) increasing.
+   * In other words, determines which end of the array is "smaller"
+   * (using the standard ordering on {@link Coordinate}).
+   * Returns an integer indicating the increasing direction.
+   * If the sequence is a palindrome, it is defined to be
+   * oriented in a positive direction.
+   *
+   * @param pts the array of Coordinates to test
+   * @return <code>1</code> if the array is smaller at the start
+   * or is a palindrome,
+   * <code>-1</code> if smaller at the end
+   */
+  public static int increasingDirection(Coordinate[] pts) {
+    for (int i = 0; i < pts.length / 2; i++) {
+      int j = pts.length - 1 - i;
+      // skip equal points on both ends
+      int comp = pts[i].compareTo(pts[j]);
+      if (comp != 0)
+        return comp;
+    }
+    // array must be a palindrome - defined to be in positive direction
+    return 1;
+  }
+
+  /**
+   * Determines whether two {@link Coordinate} arrays of equal length
+   * are equal in opposite directions.
+   *
+   * @param pts1
+   * @param pts2
+   * @return <code>true</code> if the two arrays are equal in opposite directions.
+   */
+  private static boolean isEqualReversed(Coordinate[] pts1, Coordinate[] pts2)
+  {
+    for (int i = 0; i < pts1.length; i++) {
+      Coordinate p1 = pts1[i];
+      Coordinate p2 = pts2[pts1.length - i - 1];
+      if (p1.compareTo(p2) != 0)
+        return false;
+    }
+    return true;
+  }
+
+  /**
+   * A {@link Comparator} for {@link Coordinate} arrays
+   * modulo their directionality.
+   * E.g. if two coordinate arrays are identical but reversed
+   * they will compare as equal under this ordering.
+   * If the arrays are not equal, the ordering returned
+   * is the ordering in the forward direction.
+   *
+   */
+  public static class BidirectionalComparator
+      implements Comparator
+  {
+    public int compare(Object o1, Object o2) {
+      Coordinate[] pts1 = (Coordinate[]) o1;
+      Coordinate[] pts2 = (Coordinate[]) o2;
+
+      if (pts1.length < pts2.length) return -1;
+      if (pts1.length > pts2.length) return 1;
+
+      if (pts1.length == 0) return 0;
+
+      int forwardComp = CoordinateArrays.compare(pts1, pts2);
+      boolean isEqualRev = isEqualReversed(pts1, pts2);
+      if (isEqualRev)
+        return 0;
+      return forwardComp;
+    }
+
+    public int OLDcompare(Object o1, Object o2) {
+      Coordinate[] pts1 = (Coordinate[]) o1;
+      Coordinate[] pts2 = (Coordinate[]) o2;
+
+      if (pts1.length < pts2.length) return -1;
+      if (pts1.length > pts2.length) return 1;
+
+      if (pts1.length == 0) return 0;
+
+      int dir1 = increasingDirection(pts1);
+      int dir2 = increasingDirection(pts2);
+
+      int i1 = dir1 > 0 ? 0 : pts1.length - 1;
+      int i2 = dir2 > 0 ? 0 : pts1.length - 1;
+
+      for (int i = 0; i < pts1.length; i++) {
+        int comparePt = pts1[i1].compareTo(pts2[i2]);
+        if (comparePt != 0)
+          return comparePt;
+        i1 += dir1;
+        i2 += dir2;
+      }
+      return 0;
+    }
+
+  }
+
+  /**
    * Creates a deep copy of the argument {@link Coordinate) array.
    *
    * @param coordinates an array of Coordinates
@@ -60,9 +218,9 @@ public class CoordinateArrays {
   }
 
   /**
-   * Converts the given List of Coordinates into a Coordinate array.
+   * Converts the given Collection of Coordinates into a Coordinate array.
    */
-  public static Coordinate[] toCoordinateArray(List coordList)
+  public static Coordinate[] toCoordinateArray(Collection coordList)
   {
     return (Coordinate[]) coordList.toArray(coordArrayType);
   }
@@ -135,6 +293,29 @@ public class CoordinateArrays {
   }
 
   /**
+   * Returns true if the two arrays are identical, both null, or pointwise
+   * equal, using a user-defined {@link Comparator} for {@link Coordinate} s
+   *
+   * @param coord1 an array of Coordinates
+   * @param coord2 an array of Coordinates
+   * @param coordinateComparator a Comparator for Coordinates
+   */
+  public static boolean equals(
+    Coordinate[] coord1,
+    Coordinate[] coord2,
+    Comparator coordinateComparator)
+  {
+    if (coord1 == coord2) return true;
+    if (coord1 == null || coord2 == null) return false;
+    if (coord1.length != coord2.length) return false;
+    for (int i = 0; i < coord1.length; i++) {
+      if (coordinateComparator.compare(coord1[i], coord2[i]) != 0)
+          return false;
+    }
+    return true;
+  }
+
+  /**
    *  Returns the minimum coordinate, using the usual lexicographic comparison.
    *
    *@param  coordinates  the array to search
@@ -185,4 +366,25 @@ public class CoordinateArrays {
     return -1;
   }
 
+  /**
+   * Extracts a subsequence of the input {@link Coordinate} array
+   * from indices <code>start</code> to
+   * <code>end</code> (inclusive).
+   *
+   * @param pts the input array
+   * @param start the index of the start of the subsequence to extract
+   * @param end the index of the end of the subsequence to extract
+   * @return a subsequence of the input array
+   */
+  public static Coordinate[] extract(Coordinate[] pts, int start, int end)
+  {
+    int len = end - start + 1;
+    Coordinate[] extractPts = new Coordinate[len];
+    int iPts = 0;
+    for (int i = start; i <= end; i++) {
+      extractPts[iPts++] = pts[i];
+    }
+    return extractPts;
+  }
+
 }
diff --git a/src/com/vividsolutions/jts/geom/CoordinateFilter.java b/src/com/vividsolutions/jts/geom/CoordinateFilter.java
index 9a9a8c1..d45e752 100644
--- a/src/com/vividsolutions/jts/geom/CoordinateFilter.java
+++ b/src/com/vividsolutions/jts/geom/CoordinateFilter.java
@@ -45,7 +45,7 @@ package com.vividsolutions.jts.geom;
  *  used to implement such things as coordinate transformations, centroid and
  *  envelope computation, and many other functions.
  *
- *@version 1.6
+ *@version 1.7
  */
 public interface CoordinateFilter {
 
@@ -54,6 +54,6 @@ public interface CoordinateFilter {
    *
    *@param  coord  a <code>Coordinate</code> to which the filter is applied.
    */
-  public void filter(Coordinate coord);
+  void filter(Coordinate coord);
 }
 
diff --git a/src/com/vividsolutions/jts/geom/CoordinateList.java b/src/com/vividsolutions/jts/geom/CoordinateList.java
index 24c4776..fa32d90 100644
--- a/src/com/vividsolutions/jts/geom/CoordinateList.java
+++ b/src/com/vividsolutions/jts/geom/CoordinateList.java
@@ -43,7 +43,7 @@ import java.util.Iterator;
  * be set to prevent repeated coordinates from occuring in the list.
  *
  *
- * @version 1.6
+ * @version 1.7
  */
 public class CoordinateList
   extends ArrayList
@@ -83,7 +83,7 @@ public class CoordinateList
 
   public Coordinate getCoordinate(int i) { return (Coordinate) get(i); }
 
-  
+
   /** Add an array of coordinates
    * @param coord The coordinates
    * @param allowRepeated if set to false, repeated coordinates are collapsed
@@ -169,23 +169,24 @@ public class CoordinateList
   }
 
   /** Returns the Coordinates in this collection.
-   * 
+   *
    * @return the coordinates
    */
   public Coordinate[] toCoordinateArray()
   {
     return (Coordinate[]) toArray(coordArrayType);
   }
- 
+
   /**
-   * Returns a deep copy of this collection. 
-   * @return The copied object
+   * Returns a deep copy of this <tt>CoordinateList</tt> instance.
+   *
+   * @return a clone of this <tt>CoordinateList</tt> instance
    */
   public Object clone() {
-      CoordinateList result = (CoordinateList) super.clone();
-      for (int i=0; i<result.size(); i++) {
-          this.add(i, ((Coordinate)this.get(i)).clone());
+      CoordinateList clone = (CoordinateList) super.clone();
+      for (int i = 0; i < this.size(); i++) {
+          clone.add(i, ((Coordinate) this.get(i)).clone());
       }
-      return result;
+      return clone;
   }
 }
diff --git a/src/com/vividsolutions/jts/geom/CoordinateSequence.java b/src/com/vividsolutions/jts/geom/CoordinateSequence.java
index 167288c..6e8215e 100644
--- a/src/com/vividsolutions/jts/geom/CoordinateSequence.java
+++ b/src/com/vividsolutions/jts/geom/CoordinateSequence.java
@@ -53,7 +53,7 @@ package com.vividsolutions.jts.geom;
  * @see DefaultCoordinateSequenceFactory
  * @see TwoArrayCoordinateSequenceFactory
  *
- * @version 1.6
+ * @version 1.7
  */
 public interface CoordinateSequence
     extends Cloneable
@@ -61,10 +61,18 @@ public interface CoordinateSequence
   /**
    * Standard ordinate index values
    */
-  public static final int X = 0;
-  public static final int Y = 1;
-  public static final int Z = 2;
-  public static final int M = 3;
+  int X = 0;
+  int Y = 1;
+  int Z = 2;
+  int M = 3;
+
+  /**
+   * Returns the dimension (number of ordinates in each coordinate)
+   * for this sequence.
+   *
+   * @return the dimension of the sequence.
+   */
+  int getDimension();
 
   /**
    * Returns (possibly a copy of) the i'th coordinate in this sequence.
@@ -79,7 +87,7 @@ public interface CoordinateSequence
    * @param i the index of the coordinate to retrieve
    * @return the i'th coordinate in the sequence
    */
-  public Coordinate getCoordinate(int i);
+  Coordinate getCoordinate(int i);
 
   /**
    * Returns a copy of the i'th coordinate in this sequence.
@@ -90,7 +98,7 @@ public interface CoordinateSequence
    * @param i the index of the coordinate to retrieve
    * @return a copy of the i'th coordinate in the sequence
    */
-  public Coordinate getCoordinateCopy(int i);
+  Coordinate getCoordinateCopy(int i);
 
   /**
    * Copies the i'th coordinate in the sequence to the supplied
@@ -99,7 +107,7 @@ public interface CoordinateSequence
    * @param index the index of the coordinate to copy
    * @param coord a {@link Coordinate} to receive the value
    */
-  public void getCoordinate(int index, Coordinate coord);
+  void getCoordinate(int index, Coordinate coord);
 
   /**
    * Returns ordinate X (0) of the specified coordinate.
@@ -107,7 +115,7 @@ public interface CoordinateSequence
    * @param index
    * @return the value of the X ordinate in the index'th coordinate
    */
-  public double getX(int index);
+  double getX(int index);
 
   /**
    * Returns ordinate Y (1) of the specified coordinate.
@@ -115,7 +123,7 @@ public interface CoordinateSequence
    * @param index
    * @return the value of the Y ordinate in the index'th coordinate
    */
-  public double getY(int index);
+  double getY(int index);
 
   /**
    * Returns the ordinate of a coordinate in this sequence.
@@ -126,13 +134,13 @@ public interface CoordinateSequence
    * @param index  the coordinate index in the sequence
    * @param ordinateIndex the ordinate index in the coordinate (in range [0, dimension-1])
    */
-  public double getOrdinate(int index, int ordinateIndex);
+  double getOrdinate(int index, int ordinateIndex);
 
   /**
    * Returns the number of coordinates in this sequence.
    * @return the size of the sequence
    */
-  public int size();
+  int size();
 
   /**
    * Sets the value for a given ordinate of a coordinate in this sequence.
@@ -141,7 +149,7 @@ public interface CoordinateSequence
    * @param ordinateIndex the ordinate index in the coordinate (in range [0, dimension-1])
    * @param value  the new ordinate value
    */
-  public void setOrdinate(int index, int ordinateIndex, double value);
+  void setOrdinate(int index, int ordinateIndex, double value);
 
   /**
    * Returns (possibly copies of) the Coordinates in this collection.
@@ -153,7 +161,7 @@ public interface CoordinateSequence
    *
    * @return a array of coordinates containing the point values in this sequence
    */
-  public Coordinate[] toCoordinateArray();
+  Coordinate[] toCoordinateArray();
 
   /**
    * Expands the given {@link Envelope} to include the coordinates in the sequence.
@@ -162,7 +170,7 @@ public interface CoordinateSequence
    * @param env the envelope to expand
    * @return a ref to the expanded envelope
    */
-  public Envelope expandEnvelope(Envelope env);
+  Envelope expandEnvelope(Envelope env);
 
   /**
    * Returns a deep copy of this collection.
@@ -170,5 +178,5 @@ public interface CoordinateSequence
    *
    * @return a copy of the coordinate sequence containing copies of all points
    */
-  public Object clone();
-}
+  Object clone();
+}
\ No newline at end of file
diff --git a/src/com/vividsolutions/jts/geom/CoordinateSequenceComparator.java b/src/com/vividsolutions/jts/geom/CoordinateSequenceComparator.java
new file mode 100644
index 0000000..10b7627
--- /dev/null
+++ b/src/com/vividsolutions/jts/geom/CoordinateSequenceComparator.java
@@ -0,0 +1,130 @@
+package com.vividsolutions.jts.geom;
+
+import java.util.Comparator;
+
+/**
+ * Compares two {@link CoordinateSequence}s.
+ * For sequences of the same dimension, the ordering is lexicographic.
+ * Otherwise, lower dimensions are sorted before higher.
+ * The dimensions compared can be limited; if this is done
+ * ordinate dimensions above the limit will not be compared.
+ * <p>
+ * If different behaviour is required for comparing size, dimension, or
+ * coordinate values, any or all methods can be overridden.
+ *
+ */
+public class CoordinateSequenceComparator
+	implements Comparator
+{
+  /**
+   * Compare two <code>double</code>s, allowing for NaN values.
+   * NaN is treated as being less than any valid number.
+   *
+   * @param a a <code>double</code>
+   * @param b a <code>double</code>
+   * @return -1, 0, or 1 depending on whether a is less than, equal to or greater than b
+   */
+  public static int compare(double a, double b)
+  {
+    if (a < b) return -1;
+    if (a > b) return 1;
+
+    if (Double.isNaN(a)) {
+      if (Double.isNaN(b)) return 0;
+      return -1;
+    }
+
+    if (Double.isNaN(b)) return 1;
+    return 0;
+  }
+
+  /**
+   * The number of dimensions to test
+   */
+  protected int dimensionLimit;
+
+  /**
+   * Creates a comparator which will test all dimensions.
+   */
+  public CoordinateSequenceComparator()
+  {
+    dimensionLimit = Integer.MAX_VALUE;
+  }
+
+  /**
+   * Creates a comparator which will test only the specified number of dimensions.
+   *
+   * @param dimensionLimit the number of dimensions to test
+   */
+  public CoordinateSequenceComparator(int dimensionLimit)
+  {
+    this.dimensionLimit = dimensionLimit;
+  }
+
+  /**
+   * Compares two {@link CoordinateSequence}s for relative order.
+   *
+   * @param o1 a {@link CoordinateSequence}
+   * @param o2 a {@link CoordinateSequence}
+   * @return -1, 0, or 1 depending on whether o1 is less than, equal to, or greater than o2
+   */
+  public int compare(Object o1, Object o2)
+  {
+    CoordinateSequence s1 = (CoordinateSequence) o1;
+    CoordinateSequence s2 = (CoordinateSequence) o2;
+
+    int size1 = s1.size();
+    int size2 = s2.size();
+
+    int dim1 = s1.getDimension();
+    int dim2 = s2.getDimension();
+
+    int minDim = dim1;
+    if (dim2 < minDim)
+      minDim = dim2;
+    boolean dimLimited = false;
+    if (dimensionLimit < minDim) {
+      minDim = dimensionLimit;
+      dimLimited = true;
+    }
+
+    // lower dimension is less than higher
+    if (! dimLimited) {
+      if (dim1 < dim2) return -1;
+      if (dim1 > dim2) return 1;
+    }
+
+    // lexicographic ordering of point sequences
+    int i = 0;
+    while (i < size1 && i < size2) {
+      int ptComp = compareCoordinate(s1, s2, i, minDim);
+      if (ptComp != 0) return ptComp;
+      i++;
+    }
+    if (i < size1) return 1;
+    if (i < size2) return -1;
+
+    return 0;
+  }
+
+  /**
+   * Compares the same coordinate of two {@link CoordinateSequence}s
+   * along the given number of dimensions.
+   *
+   * @param s1 a {@link CoordinateSequence}
+   * @param s2 a {@link CoordinateSequence}
+   * @param i the index of the coordinate to test
+   * @param dimension the number of dimensiosn to test
+   * @return -1, 0, or 1 depending on whether s1[i] is less than, equal to, or greater than s2[i]
+   */
+  protected int compareCoordinate(CoordinateSequence s1, CoordinateSequence s2, int i, int dimension)
+  {
+    for (int d = 0; d < dimension; d++) {
+      double ord1 = s1.getOrdinate(i, d);
+      double ord2 = s2.getOrdinate(i, d);
+      int comp = compare(ord1, ord2);
+      if (comp != 0) return comp;
+    }
+    return 0;
+  }
+}
diff --git a/src/com/vividsolutions/jts/geom/CoordinateSequenceFactory.java b/src/com/vividsolutions/jts/geom/CoordinateSequenceFactory.java
index 3bda335..3409982 100644
--- a/src/com/vividsolutions/jts/geom/CoordinateSequenceFactory.java
+++ b/src/com/vividsolutions/jts/geom/CoordinateSequenceFactory.java
@@ -34,8 +34,10 @@ package com.vividsolutions.jts.geom;
 
 /**
  * A factory to create concrete instances of {@link CoordinateSequence}s.
+ * Used to configure {@link GeometryFactory}s
+ * to provide specific kinds of CoordinateSequences.
  *
- * @version 1.6
+ * @version 1.7
  */
 public interface CoordinateSequenceFactory
 {
@@ -48,7 +50,7 @@ public interface CoordinateSequenceFactory
    *
    * @param coordinates the coordinates
    */
-  public CoordinateSequence create(Coordinate[] coordinates);
+  CoordinateSequence create(Coordinate[] coordinates);
 
   /**
    * Creates a {@link CoordinateSequence} which is a copy
@@ -57,7 +59,7 @@ public interface CoordinateSequenceFactory
    *
    * @param coordSeq the coordinate sequence to copy
    */
-  public CoordinateSequence create(CoordinateSequence coordSeq);
+  CoordinateSequence create(CoordinateSequence coordSeq);
 
   /**
    * Creates a {@link CoordinateSequence} of the specified size and dimension.
@@ -68,6 +70,6 @@ public interface CoordinateSequenceFactory
    * @param dimension the dimension of the coordinates in the sequence (if user-specifiable,
    * otherwise ignored)
    */
-  public CoordinateSequence create(int size, int dimension);
+  CoordinateSequence create(int size, int dimension);
 
-}
+}
\ No newline at end of file
diff --git a/src/com/vividsolutions/jts/geom/GeometryFilter.java b/src/com/vividsolutions/jts/geom/CoordinateSequences.java
similarity index 59%
copy from src/com/vividsolutions/jts/geom/GeometryFilter.java
copy to src/com/vividsolutions/jts/geom/CoordinateSequences.java
index 8eaec5e..92d6e99 100644
--- a/src/com/vividsolutions/jts/geom/GeometryFilter.java
+++ b/src/com/vividsolutions/jts/geom/CoordinateSequences.java
@@ -1,5 +1,3 @@
-
-
 /*
  * The JTS Topology Suite is a collection of Java classes that
  * implement the fundamental operations required to validate a given
@@ -34,25 +32,42 @@
  */
 package com.vividsolutions.jts.geom;
 
+import java.util.*;
 
 /**
- *  <code>GeometryCollection</code> classes support the concept of
- *  applying a <code>GeometryFilter</code> to the <code>Geometry</code>.
- *  The filter is applied to every element <code>Geometry</code>.
- *  A <code>GeometryFilter</code> can either record information about the <code>Geometry</code>
- *  or change the <code>Geometry</code> in some way.
- *  <code>GeometryFilter</code>
- *  is an example of the Gang-of-Four Visitor pattern.
- *
- *@version 1.6
+ * Utility functions for manipulating {@link CoordinateSequence}s
+ *
+ * @version 1.7
  */
-public interface GeometryFilter {
+public class CoordinateSequences {
+
+  /**
+   * Reverses the coordinates in a sequence in-place.
+   */
+  public static void reverse(CoordinateSequence seq)
+  {
+    int last = seq.size() - 1;
+    int mid = last / 2;
+    for (int i = 0; i <= mid; i++) {
+      swap(seq, i, last - i);
+    }
+  }
 
   /**
-   *  Performs an operation with or on <code>geom</code>.
+   * Swaps two coordinates in a sequence.
    *
-   *@param  geom  a <code>Geometry</code> to which the filter is applied.
+   * @param seq
+   * @param i
+   * @param j
    */
-  public void filter(Geometry geom);
-}
+  public static void swap(CoordinateSequence seq, int i, int j)
+  {
+    if (i == j) return;
+    for (int dim = 0; dim < seq.getDimension(); dim++) {
+      double tmp = seq.getOrdinate(i, dim);
+      seq.setOrdinate(i, dim, seq.getOrdinate(j, dim));
+      seq.setOrdinate(j, dim, tmp);
+    }
+  }
 
+}
\ No newline at end of file
diff --git a/src/com/vividsolutions/jts/geom/DefaultCoordinateSequence.java b/src/com/vividsolutions/jts/geom/DefaultCoordinateSequence.java
index 901cc38..ff026a3 100644
--- a/src/com/vividsolutions/jts/geom/DefaultCoordinateSequence.java
+++ b/src/com/vividsolutions/jts/geom/DefaultCoordinateSequence.java
@@ -39,7 +39,7 @@ import java.io.Serializable;
  * parties that change them are actually changing the
  * DefaultCoordinateSequence's underlying data.
  *
- * @version 1.6
+ * @version 1.7
  *
  * @deprecated no longer used
  */
@@ -90,6 +90,11 @@ class DefaultCoordinateSequence
   }
 
   /**
+   * @see com.vividsolutions.jts.geom.CoordinateSequence#getDimension()
+   */
+  public int getDimension() { return 3; }
+
+  /**
    * Get the Coordinate with index i.
    *
    * @param i
@@ -211,4 +216,4 @@ class DefaultCoordinateSequence
       return "()";
     }
   }
-}
+}
\ No newline at end of file
diff --git a/src/com/vividsolutions/jts/geom/DefaultCoordinateSequenceFactory.java b/src/com/vividsolutions/jts/geom/DefaultCoordinateSequenceFactory.java
index 80f2c2c..b8c718a 100644
--- a/src/com/vividsolutions/jts/geom/DefaultCoordinateSequenceFactory.java
+++ b/src/com/vividsolutions/jts/geom/DefaultCoordinateSequenceFactory.java
@@ -38,7 +38,7 @@ import java.io.Serializable;
 /**
  * Creates CoordinateSequences represented as an array of {@link Coordinate}s.
  *
- * @version 1.6
+ * @version 1.7
  *
  * @deprecated no longer used
  */
@@ -46,7 +46,7 @@ public class DefaultCoordinateSequenceFactory
     implements CoordinateSequenceFactory, Serializable
 {
   private static final long serialVersionUID = -4099577099607551657L;
-  private static final DefaultCoordinateSequenceFactory instance = new DefaultCoordinateSequenceFactory();
+  private static final DefaultCoordinateSequenceFactory instanceObject = new DefaultCoordinateSequenceFactory();
 
   public DefaultCoordinateSequenceFactory() {
   }
@@ -62,7 +62,7 @@ public class DefaultCoordinateSequenceFactory
    * Returns the singleton instance of DefaultCoordinateSequenceFactory
    */
   public static DefaultCoordinateSequenceFactory instance() {
-    return instance;
+    return instanceObject;
   }
 
 
@@ -92,4 +92,4 @@ public class DefaultCoordinateSequenceFactory
   public CoordinateSequence create(int size, int dimension) {
     return new DefaultCoordinateSequence(size);
   }
-}
+}
\ No newline at end of file
diff --git a/src/com/vividsolutions/jts/geom/Dimension.java b/src/com/vividsolutions/jts/geom/Dimension.java
index 0feda20..8d3a0eb 100644
--- a/src/com/vividsolutions/jts/geom/Dimension.java
+++ b/src/com/vividsolutions/jts/geom/Dimension.java
@@ -39,7 +39,7 @@ package com.vividsolutions.jts.geom;
  * Also, constants representing the dimensions of the empty geometry and
  * non-empty geometries, and a wildcard dimension meaning "any dimension".
  * 
- * @version 1.6
+ * @version 1.7
  */
 public class Dimension {
 
diff --git a/src/com/vividsolutions/jts/geom/Envelope.java b/src/com/vividsolutions/jts/geom/Envelope.java
index 47bcc11..aa08135 100644
--- a/src/com/vividsolutions/jts/geom/Envelope.java
+++ b/src/com/vividsolutions/jts/geom/Envelope.java
@@ -47,12 +47,12 @@ import java.io.Serializable;
  *  When Envelope objects are created or initialized,
  *  the supplies extent values are automatically sorted into the correct order.
  *
- *@version 1.6
+ *@version 1.7
  */
 public class Envelope
     implements Serializable
 {
-    private static final long serialVersionUID = 5873921885273102420L;    
+    private static final long serialVersionUID = 5873921885273102420L;
 
     public int hashCode() {
         //Algorithm from Effective Java by Joshua Bloch [Jon Aquino]
@@ -356,7 +356,38 @@ public class Envelope
     expandToInclude(p.x, p.y);
   }
 
-  //<<TODO:FEATURE>> #expandBy(double distance) [Jon Aquino]
+  /**
+   * Expands this envelope by a given distance in all directions.
+   * Both positive and negative distances are supported.
+   *
+   * @param distance the distance to expand the envelope
+   * @return this envelope
+   */
+  public void expandBy(double distance)
+  {
+    expandBy(distance, distance);
+  }
+
+  /**
+   * Expands this envelope by a given distance in all directions.
+   * Both positive and negative distances are supported.
+   *
+   * @param deltaX the distance to expand the envelope along the the X axis
+   * @param deltaY the distance to expand the envelope along the the Y axis
+   */
+  public void expandBy(double deltaX, double deltaY)
+  {
+    if (isNull()) return;
+
+    minx -= deltaX;
+    maxx += deltaX;
+    miny -= deltaY;
+    maxy += deltaY;
+
+    // check for envelope disappearing
+    if (minx > maxx || miny > maxy)
+      setToNull();
+  }
 
   /**
    *  Enlarges the boundary of the <code>Envelope</code> so that it contains
@@ -422,6 +453,52 @@ public class Envelope
   }
 
   /**
+   * Translates this envelope by given amounts in the X and Y direction.
+   *
+   * @param transX the amount to translate along the X axis
+   * @param transY the amount to translate along the Y axis
+   */
+  public void translate(double transX, double transY) {
+    if (isNull()) {
+      return;
+    }
+    init(getMinX() + transX, getMaxX() + transX,
+         getMinY() + transY, getMaxY() + transY);
+  }
+
+  /**
+   * Computes the coordinate of the centre of this envelope (as long as it is non-null
+   *
+   * @return the centre coordinate of this envelope
+   * <code>null</code> if the envelope is null
+   */
+  public Coordinate centre() {
+    if (isNull()) return null;
+    return new Coordinate(
+        (getMinX() + getMaxX()) / 2.0,
+        (getMinY() + getMaxY()) / 2.0);
+  }
+
+  /**
+   * Computes the intersection of two {@link Envelopes}
+   *
+   * @param env the envelope to intersect with
+   * @return a new Envelope representing the intersection of the envelopes (this will be
+   * the null envelope if either argument is null, or they do not intersect
+   */
+  public Envelope intersection(Envelope env)
+  {
+    if (isNull() || env.isNull() || ! intersects(env)) return new Envelope();
+
+    double intMinX = minx > env.minx ? minx : env.minx;
+    double intMinY = miny > env.miny ? miny : env.miny;
+    double intMaxX = maxx < env.maxx ? maxx : env.maxx;
+    double intMaxY = maxy < env.maxy ? maxy : env.maxy;
+    return new Envelope(intMinX, intMaxX, intMinY, intMaxY);
+  }
+
+
+  /**
    *  Returns <code>true</code> if the given point lies in or on the envelope.
    *
    *@param  p  the point which this <code>Envelope</code> is
@@ -460,10 +537,10 @@ public class Envelope
    */
   public boolean intersects(Envelope other) {
       if (isNull() || other.isNull()) { return false; }
-    return !(other.getMinX() > maxx ||
-        other.getMaxX() < minx ||
-        other.getMinY() > maxy ||
-        other.getMaxY() < miny);
+    return !(other.minx > maxx ||
+        other.maxx < minx ||
+        other.miny > maxy ||
+        other.maxy < miny);
   }
   /**
    * @deprecated Use #intersects instead. In the future, #overlaps may be
@@ -561,7 +638,7 @@ public class Envelope
     return maxx == otherEnvelope.getMaxX() &&
         maxy == otherEnvelope.getMaxY() &&
         minx == otherEnvelope.getMinX() &&
-        maxx == otherEnvelope.getMaxX();
+        miny == otherEnvelope.getMinY();
   }
 
   public String toString()
diff --git a/src/com/vividsolutions/jts/geom/Geometry.java b/src/com/vividsolutions/jts/geom/Geometry.java
index f938524..42266ed 100644
--- a/src/com/vividsolutions/jts/geom/Geometry.java
+++ b/src/com/vividsolutions/jts/geom/Geometry.java
@@ -1,5 +1,3 @@
-
-
 /*
  * The JTS Topology Suite is a collection of Java classes that
  * implement the fundamental operations required to validate a given
@@ -49,9 +47,8 @@ import com.vividsolutions.jts.operation.relate.RelateOp;
 import com.vividsolutions.jts.operation.valid.IsValidOp;
 import com.vividsolutions.jts.util.Assert;
 /**
- *  Basic implementation of <code>Geometry</code>. <P>
- *
- *  <code>clone</code> returns a deep copy of the object.
+ * The base class for all geometric objects.
+ * <P>
  *
  *  <H3>Binary Predicates</H3>
  * Because it is not clear at this time
@@ -126,7 +123,7 @@ import com.vividsolutions.jts.util.Assert;
  *  topologically equal Geometries are added to HashMaps and HashSets, they
  *  remain distinct. This behaviour is desired in many cases.
  *
- *@version 1.6
+ *@version 1.7
  */
 public abstract class Geometry
     implements Cloneable, Comparable, Serializable
@@ -230,14 +227,12 @@ public abstract class Geometry
    *@return    the ID of the coordinate space in which the <code>Geometry</code>
    *      is defined.
    *
-   *  @deprecated use {@link getUserData} instead
    */
   public int getSRID() {
     return SRID;
   }
     /**
    *  Sets the ID of the Spatial Reference System used by the <code>Geometry</code>.
-   *  @deprecated use {@link setUserData} instead
    */
   public void setSRID(int SRID) {
     this.SRID = SRID;
@@ -337,20 +332,27 @@ public abstract class Geometry
   public abstract int getNumPoints();
 
   /**
-   *  Returns false if the <code>Geometry</code> not simple.
-   *  Subclasses provide their own definition of "simple". If
-   *  this <code>Geometry</code> is empty, returns <code>true</code>. <P>
-   *
-   *  In general, the SFS specifications of simplicity seem to follow the
-   *  following rule:
+   * Tests whether this {@link Geometry} is simple.
+   * In general, the SFS specification of simplicity
+   * follows the rule:
    *  <UL>
    *    <LI> A Geometry is simple iff the only self-intersections are at
    *    boundary points.
    *  </UL>
-   *  For all empty <code>Geometry</code>s, <code>isSimple</code> = <code>true</code>.
+   * Simplicity is defined for each {@link Geometry} subclass as follows:
+   * <ul>
+   * <li>Valid polygonal geometries are simple by definition, so
+   * <code>isSimple</code> trivially returns true.
+   * <li>Linear geometries are simple iff they do not self-intersect at points
+   * other than boundary points.
+   * <li>Zero-dimensional geometries (points) are simple iff they have no
+   * repeated points.
+   * <li>Empty <code>Geometry</code>s are always simple
+   * <ul>
    *
-   *@return    <code>true</code> if this <code>Geometry</code> has any points of
+   * @return    <code>true</code> if this <code>Geometry</code> has any points of
    *      self-tangency, self-intersection or other anomalous points
+   * @see #isValid
    */
   public abstract boolean isSimple();
 
@@ -576,24 +578,41 @@ public abstract class Geometry
   }
 
   /**
-   *  Returns <code>true</code> if the DE-9IM intersection matrix for the two
-   *  <code>Geometry</code>s is FF*FF****.
+   * Returns <code>true</code> if this geometry is disjoint to the specified geometry.
+   * <p>
+   * The <code>disjoint</code> predicate has the following equivalent definitions:
+   * <ul>
+   * <li>The two geometries have no point in common
+   * <li>The DE-9IM Intersection Matrix for the two geometries is FF*FF****
+   * <li>! <code>g.intersects(this)</code>
+   * (<code>disjoint</code> is the inverse of <code>intersects</code>)
+   * </ul>
    *
-   *@param  other  the <code>Geometry</code> with which to compare this <code>Geometry</code>
+   *@param  g  the <code>Geometry</code> with which to compare this <code>Geometry</code>
    *@return        <code>true</code> if the two <code>Geometry</code>s are
    *      disjoint
+   *
+   * @see Geometry#intersects
    */
   public boolean disjoint(Geometry g) {
     return ! intersects(g);
   }
 
   /**
-   *  Returns <code>true</code> if the DE-9IM intersection matrix for the two
-   *  <code>Geometry</code>s is FT*******, F**T***** or F***T****.
+   * Returns <code>true</code> if this geometry touches the
+   * specified geometry.
+   * <p>
+   * The <code>touches</code> predicate has the following equivalent definitions:
+   * <ul>
+   * <li>The geometries have at least one point in common, but their interiors do not intersect.
+   * <li>The DE-9IM Intersection Matrix for the two geometries is
+   *   FT*******, F**T***** or F***T****
+   * </ul>
+   * If both geometries have dimension 0, this predicate returns <code>false</code>
    *
-   *@param  other  the <code>Geometry</code> with which to compare this <code>Geometry</code>
+   *@param  g  the <code>Geometry</code> with which to compare this <code>Geometry</code>
    *@return        <code>true</code> if the two <code>Geometry</code>s touch;
-   *      Returns false if both <code>Geometry</code>s are points
+   *      Returns <code>false</code> if both <code>Geometry</code>s are points
    */
   public boolean touches(Geometry g) {
     // short-circuit test
@@ -603,16 +622,43 @@ public abstract class Geometry
   }
 
   /**
-   *  Returns <code>true</code> if <code>disjoint</code> returns false.
+   * Returns <code>true</code> if this geometry intersects the specified geometry.
+   * <p>
+   * The <code>intersects</code> predicate has the following equivalent definitions:
+   * <ul>
+   * <li>The two geometries have at least one point in common
+   * <li>! <code>g.disjoint(this)</code>
+   * (<code>intersects</code> is the inverse of <code>disjoint</code>)
+   * </ul>
    *
-   *@param  other  the <code>Geometry</code> with which to compare this <code>Geometry</code>
+   *@param  g  the <code>Geometry</code> with which to compare this <code>Geometry</code>
    *@return        <code>true</code> if the two <code>Geometry</code>s intersect
+   *
+   * @see Geometry#disjoint
    */
   public boolean intersects(Geometry g) {
+
     // short-circuit envelope test
     if (! getEnvelopeInternal().intersects(g.getEnvelopeInternal()))
       return false;
-    // optimizations for rectangle arguments
+
+    /**
+     * TODO: (MD) Add optimizations:
+     *
+     * - for P-A case:
+     * If P is in env(A), test for point-in-poly
+     *
+     * - for A-A case:
+     * If env(A1).overlaps(env(A2))
+     * test for overlaps via point-in-poly first (both ways)
+     * Possibly optimize selection of point to test by finding point of A1
+     * closest to centre of env(A2).
+     * (Is there a test where we shouldn't bother - e.g. if env A
+     * is much smaller than env B, maybe there's no point in testing
+     * pt(B) in env(A)?
+     */
+
+    // optimization for rectangle arguments
     if (isRectangle()) {
       return RectangleIntersects.intersects((Polygon) this, g);
     }
@@ -624,20 +670,27 @@ public abstract class Geometry
   }
 
   /**
-   *  Returns <code>true</code> if the DE-9IM intersection matrix for the two
-   *  <code>Geometry</code>s is
-   *  <UL>
-   *    <LI> T*T****** (for a point and a curve, a point and an area or a line
-   *    and an area)
-   *    <LI> 0******** (for two curves)
-   *  </UL>
-   *  .
+   * Returns <code>true</code> if this geometry crosses the
+   * specified geometry.
+   * <p>
+   * The <code>crosses</code> predicate has the following equivalent definitions:
+   * <ul>
+   * <li>The geometries have some but not all interior points in common.
+   * <li>The DE-9IM Intersection Matrix for the two geometries is
+   *   <ul>
+   *    <li>T*T****** (for P/L, P/A, and L/A situations)
+   *    <li>T*****T** (for L/P, L/A, and A/L situations)
+   *    <li>0******** (for L/L situations)
+   *   </ul>
+   * </ul>
+   * For any other combination of dimensions this predicate returns <code>false</code>.
+   * <p>
+   * The SFS defined this predicate only for P/L, P/A, L/L, and L/A situations.
+   * JTS extends the definition to apply to L/P, A/P and A/L situations as well.
+   * This makes the relation symmetric.
    *
-   *@param  other  the <code>Geometry</code> with which to compare this <code>Geometry</code>
+   *@param  g  the <code>Geometry</code> with which to compare this <code>Geometry</code>
    *@return        <code>true</code> if the two <code>Geometry</code>s cross.
-   *      For this function to return <code>true</code>, the <code>Geometry</code>
-   *      s must be a point and a curve; a point and a surface; two curves; or a
-   *      curve and a surface.
    */
   public boolean crosses(Geometry g) {
     // short-circuit test
@@ -647,30 +700,58 @@ public abstract class Geometry
   }
 
   /**
-   *  Returns <code>true</code> if the DE-9IM intersection matrix for the two
-   *  <code>Geometry</code>s is T*F**F***.
+   * Returns <code>true</code> if this geometry is within the
+   * specified geometry.
+   * <p>
+   * The <code>within</code> predicate has the following equivalent definitions:
+   * <ul>
+   * <li>Every point of this geometry is a point of the other geometry,
+   * and the interiors of the two geometries have at least one point in common.
+   * <li>The DE-9IM Intersection Matrix for the two geometries is T*F**F***
+   * <li><code>g.contains(this)</code>
+   * (<code>within</code> is the inverse of <code>contains</code>)
+   * </ul>
+   * An implication of the definition is that
+   * "The boundary of a Polygon is not within the Polygon".
+   * In other words, if a geometry G is a subset of
+   * the points in the boundary of a polygon P, <code>G.within(P) = false</code>
    *
-   *@param  other  the <code>Geometry</code> with which to compare this <code>Geometry</code>
+   *@param  g  the <code>Geometry</code> with which to compare this <code>Geometry</code>
    *@return        <code>true</code> if this <code>Geometry</code> is within
    *      <code>other</code>
+   *
+   * @see Geometry#contains
    */
   public boolean within(Geometry g) {
     return g.contains(this);
   }
 
   /**
-   *  Returns <code>true</code> if <code>other.within(this)</code> returns
-   *  <code>true</code>.
+   * Returns <code>true</code> if this geometry contains the
+   * specified geometry.
+   * <p>
+   * The <code>contains</code> predicate has the following equivalent definitions:
+   * <ul>
+   * <li>Every point of the other geometry is a point of this geometry,
+   * and the interiors of the two geometries have at least one point in common.
+   * <li>The DE-9IM Intersection Matrix for the two geometries is <code>T*****FF*</code>
+   * <li><code>g.within(this)</code>
+   * (<code>contains</code> is the inverse of <code>within</code>)
+   * </ul>
+   * An implication of the definition is that "Polygons do not
+   * contain their boundary".  In other words, if a geometry G is a subset of
+   * the points in the boundary of a polygon P, <code>P.contains(G) = false</code>
    *
-   *@param  other  the <code>Geometry</code> with which to compare this <code>Geometry</code>
-   *@return        <code>true</code> if this <code>Geometry</code> contains
-   *      <code>other</code>
+   *@param  g  the <code>Geometry</code> with which to compare this <code>Geometry</code>
+   *@return        <code>true</code> if this <code>Geometry</code> contains <code>g</code>
+   *
+   * @see Geometry#within
    */
   public boolean contains(Geometry g) {
     // short-circuit test
     if (! getEnvelopeInternal().contains(g.getEnvelopeInternal()))
       return false;
-    // optimizations for rectangle arguments
+    // optimization for rectangle arguments
     if (isRectangle()) {
       return RectangleContains.contains((Polygon) this, g);
     }
@@ -679,18 +760,23 @@ public abstract class Geometry
   }
 
   /**
-   *  Returns <code>true</code> if the DE-9IM intersection matrix for the two
-   *  <code>Geometry</code>s is
-   *  <UL>
-   *    <LI> T*T***T** (for two points or two surfaces)
-   *    <LI> 1*T***T** (for two curves)
-   *  </UL>
-   *  .
+   * Returns <code>true</code> if this geometry overlaps the
+   * specified geometry.
+   * <p>
+   * The <code>overlaps</code> predicate has the following equivalent definitions:
+   * <ul>
+   * <li>The geometries have some but not all points in common,
+   * they have the same dimension,
+   * and the intersection of the interiors of the two geometries has
+   * the same dimension as the geometries themselves.
+   * <li>The DE-9IM Intersection Matrix for the two geometries is
+   *   <code>T*T***T**</code> (for two points or two surfaces)
+   *   or <code>1*T***T**</code> (for two curves)
+   * </ul>
+   * If the geometries are of different dimension this predicate returns <code>false</code>.
    *
-   *@param  other  the <code>Geometry</code> with which to compare this <code>Geometry</code>
+   *@param  g  the <code>Geometry</code> with which to compare this <code>Geometry</code>
    *@return        <code>true</code> if the two <code>Geometry</code>s overlap.
-   *      For this function to return <code>true</code>, the <code>Geometry</code>
-   *      s must be two points, two curves or two surfaces.
    */
   public boolean overlaps(Geometry g) {
     // short-circuit test
@@ -700,19 +786,87 @@ public abstract class Geometry
   }
 
   /**
-   *  Returns <code>true</code> if the elements in the DE-9IM intersection
-   *  matrix for the two <code>Geometry</code>s match the elements in <code>intersectionPattern</code>
-   *  , which may be:
+   * Returns <code>true</code> if this geometry covers the
+   * specified geometry.
+   * <p>
+   * The <code>covers</code> predicate has the following equivalent definitions:
+   * <ul>
+   * <li>Every point of the other geometry is a point of this geometry.
+   * <li>The DE-9IM Intersection Matrix for the two geometries is
+   *    <code>T*****FF*</code>
+   * or <code>*T****FF*</code>
+   * or <code>***T**FF*</code>
+   * or <code>****T*FF*</code>
+   * <li><code>g.coveredBy(this)</code>
+   * (<code>covers</code> is the inverse of <code>coverdBy</code>)
+   * </ul>
+   * Note the difference between <code>covers</code> and <code>contains</code>
+   * - <code>covers</code> is a more inclusive relation.
+   * In particular, unlike <code>contains</code> it does not distinguish between
+   * points in the boundary and in the interior of geometries.
+   * For most situations, <code>covers</code> should be used in preference to <code>contains</code>.
+   * As an added benefit, <code>covers</code> is more amenable to optimization,
+   * and hence should be more performant.
+   *
+   *@param  g  the <code>Geometry</code> with which to compare this <code>Geometry</code>
+   *@return        <code>true</code> if this <code>Geometry</code> covers <code>g</code>
+   *
+   * @see Geometry#contains
+   * @see Geometry#coveredBy
+   */
+  public boolean covers(Geometry g) {
+    // short-circuit test
+    if (! getEnvelopeInternal().contains(g.getEnvelopeInternal()))
+      return false;
+    // optimization for rectangle arguments
+    if (isRectangle()) {
+      return getEnvelopeInternal().contains(g.getEnvelopeInternal());
+    }
+    return relate(g).isCovers();
+  }
+
+  /**
+   * Returns <code>true</code> if this geometry is covered by the
+   * specified geometry.
+   * <p>
+   * The <code>coveredBy</code> predicate has the following equivalent definitions:
+   * <ul>
+   * <li>Every point of this geometry is a point of the other geometry.
+   * <li>The DE-9IM Intersection Matrix for the two geometries is
+   *    <code>T*F**F***</code>
+   * or <code>*TF**F***</code>
+   * or <code>**FT*F***</code>
+   * or <code>**F*TF***</code>
+   * <li><code>g.covers(this)</code>
+   * (<code>coveredBy</code> is the inverse of <code>covers</code>)
+   * </ul>
+   * Note the difference between <code>coveredBy</code> and <code>within</code>
+   * - <code>coveredBy</code> is a more inclusive relation
+   *
+   *@param  g  the <code>Geometry</code> with which to compare this <code>Geometry</code>
+   *@return        <code>true</code> if this <code>Geometry</code> is covered by <code>g</code>
+   *
+   * @see Geometry#within
+   * @see Geometry#covers
+   */
+  public boolean coveredBy(Geometry g) {
+    return g.covers(this);
+  }
+
+  /**
+   *  Returns <code>true</code> if the elements in the DE-9IM
+   * {@link IntersectionMatrix} for the two <code>Geometry</code>s match the elements in <code>intersectionPattern</code>.
+   * The pattern is a 9-character string, with symbols drawn from the following set:
    *  <UL>
-   *    <LI> 0
-   *    <LI> 1
-   *    <LI> 2
-   *    <LI> T ( = 0, 1 or 2)
-   *    <LI> F ( = -1)
-   *    <LI> * ( = -1, 0, 1 or 2)
+   *    <LI> 0 (dimension 0)
+   *    <LI> 1 (dimension 1)
+   *    <LI> 2 (dimension 2)
+   *    <LI> T ( matches 0, 1 or 2)
+   *    <LI> F ( matches FALSE)
+   *    <LI> * ( matches any value)
    *  </UL>
-   *  For more information on the DE-9IM, see the OpenGIS Simple Features
-   *  Specification.
+   *  For more information on the DE-9IM, see the <i>OpenGIS Simple Features
+   *  Specification</i>.
    *
    *@param  other                the <code>Geometry</code> with which to compare
    *      this <code>Geometry</code>
@@ -720,16 +874,17 @@ public abstract class Geometry
    *      intersection matrix for the two <code>Geometry</code>s
    *@return                      <code>true</code> if the DE-9IM intersection
    *      matrix for the two <code>Geometry</code>s match <code>intersectionPattern</code>
+   * @see IntersectionMatrix
    */
   public boolean relate(Geometry g, String intersectionPattern) {
     return relate(g).matches(intersectionPattern);
   }
 
   /**
-   *  Returns the DE-9IM intersection matrix for the two <code>Geometry</code>s.
+   *  Returns the DE-9IM {@link IntersectionMatrix} for the two <code>Geometry</code>s.
    *
    *@param  other  the <code>Geometry</code> with which to compare this <code>Geometry</code>
-   *@return        a matrix describing the intersections of the interiors,
+   *@return        an {@link IntersectionMatrix} describing the intersections of the interiors,
    *      boundaries and exteriors of the two <code>Geometry</code>s
    */
   public IntersectionMatrix relate(Geometry g) {
@@ -739,8 +894,15 @@ public abstract class Geometry
   }
 
   /**
-   *  Returns <code>true</code> if the DE-9IM intersection matrix for the two
-   *  <code>Geometry</code>s is T*F**FFF*.
+   * Returns <code>true</code> if this geometry is equal to the
+   * specified geometry.
+   * <p>
+   * The <code>equals</code> predicate has the following equivalent definitions:
+   * <ul>
+   * <li>The two geometries have at least one point in common,
+   * and no point of either geometry lies in the exterior of the other geometry.
+   * <li>The DE-9IM Intersection Matrix for the two geometries is T*F**FFF*
+   * </ul>
    *
    *@param  other  the <code>Geometry</code> with which to compare this <code>Geometry</code>
    *@return        <code>true</code> if the two <code>Geometry</code>s are equal
@@ -771,42 +933,78 @@ public abstract class Geometry
   }
 
   /**
-   *  Returns a buffer region around this <code>Geometry</code> having the given
-   *  width.
-   * The buffer of a Geometry is
-   * the Minkowski sum or difference
-   * of the Geometry with
-   * a disc of radius <code>distance</code>.
+   * Computes a buffer area around this geometry having the given
+   * width.
+   * The buffer of a Geometry is the Minkowski sum or difference
+   * of the geometry with a disc of radius <code>abs(distance)</code>.
+   * The buffer is constructed using 8 segments per quadrant to represent curves.
+   * The end cap style is <tt>CAP_ROUND</tt>.
+   *
+   *@param  distance  the width of the buffer (may be positive, negative or 0)
+   *@return an area geometry representing the buffer region
    *
-   *@param  distance  the width of the buffer, interpreted according to the
-   *      <code>PrecisionModel</code> of the <code>Geometry</code>
-   *@return           all points whose distance from this <code>Geometry</code>
-   *      are less than or equal to <code>distance</code>
+   * @see #buffer(double, int)
+   * @see #buffer(double, int, int)
    */
   public Geometry buffer(double distance) {
     return BufferOp.bufferOp(this, distance);
   }
 
   /**
-   *  Returns a buffer region around this {@link Geometry} having the given
-   *  width and with a specified number of segments used to approximate curves.
-   * The buffer of a Geometry is the Minkowski sum of the Geometry with
-   * a disc of radius <code>distance</code>.  Curves in the buffer polygon are
-   * approximated with line segments.  This method allows specifying the
-   * accuracy of that approximation.
+   * Computes a buffer area around this geometry having the given
+   * width and with a specified accuracy of approximation for circular arcs.
+   * <p>
+   * Buffer area boundaries can contain circular arcs.
+   * To represent these arcs using linear geometry they must be approximated with line segments.
+   * The <code>quadrantSegments</code> argument allows controlling the
+   * accuracy of the approximation
+   * by specifying the number of line segments used to represent a quadrant of a circle
    *
-   *@param  distance  the width of the buffer, interpreted according to the
-   *      <code>PrecisionModel</code> of the <code>Geometry</code>
-   *@param quadrantSegments the number of segments to use to approximate a quadrant of a circle
-   *@return           all points whose distance from this <code>Geometry</code>
-   *      are less than or equal to <code>distance</code>
+   *@param  distance  the width of the buffer (may be positive, negative or 0)
+   *@param quadrantSegments the number of line segments used to represent a quadrant of a circle
+   *@return an area geometry representing the buffer region
+   *
+   * @see #buffer(double)
+   * @see #buffer(double, int, int)
    */
   public Geometry buffer(double distance, int quadrantSegments) {
     return BufferOp.bufferOp(this, distance, quadrantSegments);
   }
 
   /**
-   *  Returns the smallest convex <code>Polygon</code> that contains all the
+   * Computes a buffer area around this geometry having the given
+   * width and with a specified accuracy of approximation for circular arcs,
+   * and using a specified end cap style.
+   * <p>
+   * Buffer area boundaries can contain circular arcs.
+   * To represent these arcs using linear geometry they must be approximated with line segments.
+   * The <code>quadrantSegments</code> argument allows controlling the
+   * accuracy of the approximation
+   * by specifying the number of line segments used to represent a quadrant of a circle
+   * <p>
+   * The end cap style specifies the buffer geometry that will be
+   * created at the ends of linestrings.  The styles provided are:
+   * <ul>
+   * <li><tt>BufferOp.CAP_ROUND</tt> - (default) a semi-circle
+   * <li><tt>BufferOp.CAP_BUTT</tt> - a straight line perpendicular to the end segment
+   * <li><tt>BufferOp.CAP_SQUARE</tt> - a half-square
+   * </ul>
+   *
+   *@param  distance  the width of the buffer (may be positive, negative or 0)
+   *@param quadrantSegments the number of line segments used to represent a quadrant of a circle
+   *@param endCapStyle the end cap style to use
+   *@return an area geometry representing the buffer region
+   *
+   * @see #buffer(double)
+   * @see #buffer(double, int)
+   * @see BufferOp
+   */
+  public Geometry buffer(double distance, int quadrantSegments, int endCapStyle) {
+    return BufferOp.bufferOp(this, distance, quadrantSegments, endCapStyle);
+  }
+
+  /**
+   *  Computes the smallest convex <code>Polygon</code> that contains all the
    *  points in the <code>Geometry</code>. This obviously applies only to <code>Geometry</code>
    *  s which contain 3 or more points; the results for degenerate cases are
    *  specified as follows:
@@ -840,12 +1038,13 @@ public abstract class Geometry
   }
 
   /**
-   *  Returns a <code>Geometry</code> representing the points shared by this
+   *  Computes a <code>Geometry</code> representing the points shared by this
    *  <code>Geometry</code> and <code>other</code>.
    *
-   *@param  other  the <code>Geometry</code> with which to compute the
+   * @param  other  the <code>Geometry</code> with which to compute the
    *      intersection
-   *@return        the points common to the two <code>Geometry</code>s
+   * @return        the points common to the two <code>Geometry</code>s
+   * @throws TopologyException if a robustness error occurs
    */
   public Geometry intersection(Geometry other) {
     checkNotGeometryCollection(this);
@@ -854,12 +1053,13 @@ public abstract class Geometry
   }
 
   /**
-   *  Returns a <code>Geometry</code> representing all the points in this <code>Geometry</code>
+   *  Computes a <code>Geometry</code> representing all the points in this <code>Geometry</code>
    *  and <code>other</code>.
    *
    *@param  other  the <code>Geometry</code> with which to compute the union
    *@return        a set combining the points of this <code>Geometry</code> and
    *      the points of <code>other</code>
+   * @throws TopologyException if a robustness error occurs
    */
   public Geometry union(Geometry other) {
     checkNotGeometryCollection(this);
@@ -868,7 +1068,7 @@ public abstract class Geometry
   }
 
   /**
-   *  Returns a <code>Geometry</code> representing the points making up this
+   *  Computes a <code>Geometry</code> representing the points making up this
    *  <code>Geometry</code> that do not make up <code>other</code>. This method
    *  returns the closure of the resultant <code>Geometry</code>.
    *
@@ -876,6 +1076,7 @@ public abstract class Geometry
    *      difference
    *@return        the point set difference of this <code>Geometry</code> with
    *      <code>other</code>
+   * @throws TopologyException if a robustness error occurs
    */
   public Geometry difference(Geometry other) {
     checkNotGeometryCollection(this);
@@ -893,6 +1094,7 @@ public abstract class Geometry
    *      difference
    *@return        the point set symmetric difference of this <code>Geometry</code>
    *      with <code>other</code>
+   * @throws TopologyException if a robustness error occurs
    */
   public Geometry symDifference(Geometry other) {
     checkNotGeometryCollection(this);
@@ -901,24 +1103,24 @@ public abstract class Geometry
   }
 
   /**
-   *  Returns true if the two <code>Geometry</code>s are exactly equal,
-   * up to a specified tolerance.
-   * Two Geometries are exactly within a tolerance equal iff:
+   * Returns true if the two <code>Geometry</code>s are exactly equal,
+   * up to a specified distance tolerance.
+   * Two Geometries are exactly equal within a distance tolerance
+   * if and only if:
    * <ul>
    * <li>they have the same class
-   * <li>they have the same values of Coordinates,
-   * within the given tolerance distance, in their internal
-   * Coordinate lists, in exactly the same order.
+   * <li>they have the same values for their vertices,
+   * within the given tolerance distance, in exactly the same order.
    * </ul>
    * If this and the other <code>Geometry</code>s are
-   *  composites and any children are not <code>Geometry</code>s, returns
-   *  false.
+   * composites and any children are not <code>Geometry</code>s, returns
+   * <code>false</code>.
    *
-   *@param  other  the <code>Geometry</code> with which to compare this <code>Geometry</code>
-   *@parm tolerance distance at or below which two Coordinates will be considered
-   * equal
-   *@return        <code>true</code> if this and the other <code>Geometry</code>
-   *      are of the same class and have equal internal data.
+   * @param  other  the <code>Geometry</code> with which to compare this <code>Geometry</code>
+   * @parm tolerance distance at or below which two <code>Coordinate</code>s
+   *   are considered equal
+   * @return <code>true</code> if this and the other <code>Geometry</code>
+   *   are of the same class and have equal internal data.
    */
   public abstract boolean equalsExact(Geometry other, double tolerance);
 
@@ -1047,6 +1249,53 @@ public abstract class Geometry
   }
 
   /**
+   *  Returns whether this <code>Geometry</code> is greater than, equal to,
+   *  or less than another <code>Geometry</code>,
+   * using the given {@link CoordinateSequenceComparator}.
+   * <P>
+   *
+   *  If their classes are different, they are compared using the following
+   *  ordering:
+   *  <UL>
+   *    <LI> Point (lowest)
+   *    <LI> MultiPoint
+   *    <LI> LineString
+   *    <LI> LinearRing
+   *    <LI> MultiLineString
+   *    <LI> Polygon
+   *    <LI> MultiPolygon
+   *    <LI> GeometryCollection (highest)
+   *  </UL>
+   *  If the two <code>Geometry</code>s have the same class, their first
+   *  elements are compared. If those are the same, the second elements are
+   *  compared, etc.
+   *
+   *@param  o  a <code>Geometry</code> with which to compare this <code>Geometry</code>
+   *@param comp a <code>CoordinateSequenceComparator</code>
+   *
+   *@return    a positive number, 0, or a negative number, depending on whether
+   *      this object is greater than, equal to, or less than <code>o</code>, as
+   *      defined in "Normal Form For Geometry" in the JTS Technical
+   *      Specifications
+   */
+  public int compareTo(Object o, CoordinateSequenceComparator comp) {
+    Geometry other = (Geometry) o;
+    if (getClassSortIndex() != other.getClassSortIndex()) {
+      return getClassSortIndex() - other.getClassSortIndex();
+    }
+    if (isEmpty() && other.isEmpty()) {
+      return 0;
+    }
+    if (isEmpty()) {
+      return -1;
+    }
+    if (other.isEmpty()) {
+      return 1;
+    }
+    return compareToSameClass(o, comp);
+  }
+
+  /**
    *  Returns whether the two <code>Geometry</code>s are equal, from the point
    *  of view of the <code>equalsExact</code> method. Called by <code>equalsExact</code>
    *  . In general, two <code>Geometry</code> classes are considered to be
@@ -1103,6 +1352,20 @@ public abstract class Geometry
   protected abstract int compareToSameClass(Object o);
 
   /**
+   *  Returns whether this <code>Geometry</code> is greater than, equal to,
+   *  or less than another <code>Geometry</code> of the same class.
+   * using the given {@link CoordinateSequenceComparator}.
+   *
+   *@param  o  a <code>Geometry</code> having the same class as this <code>Geometry</code>
+   *@param comp a <code>CoordinateSequenceComparator</code>
+   *@return    a positive number, 0, or a negative number, depending on whether
+   *      this object is greater than, equal to, or less than <code>o</code>, as
+   *      defined in "Normal Form For Geometry" in the JTS Technical
+   *      Specifications
+   */
+  protected abstract int compareToSameClass(Object o, CoordinateSequenceComparator comp);
+
+  /**
    *  Returns the first non-zero result of <code>compareTo</code> encountered as
    *  the two <code>Collection</code>s are iterated over. If, by the time one of
    *  the iterations is complete, no non-zero result has been encountered,
diff --git a/src/com/vividsolutions/jts/geom/GeometryCollection.java b/src/com/vividsolutions/jts/geom/GeometryCollection.java
index b730461..22eba42 100644
--- a/src/com/vividsolutions/jts/geom/GeometryCollection.java
+++ b/src/com/vividsolutions/jts/geom/GeometryCollection.java
@@ -42,7 +42,7 @@ import com.vividsolutions.jts.util.Assert;
 /**
  *  Basic implementation of <code>GeometryCollection</code>.
  *
- *@version 1.6
+ *@version 1.7
  */
 public class GeometryCollection extends Geometry {
 //  With contributions from Markus Schaber [schabios at logi-track.com] 2004-03-26
@@ -248,5 +248,24 @@ public class GeometryCollection extends Geometry {
     TreeSet otherElements = new TreeSet(Arrays.asList(((GeometryCollection) o).geometries));
     return compare(theseElements, otherElements);
   }
+
+  protected int compareToSameClass(Object o, CoordinateSequenceComparator comp) {
+    GeometryCollection gc = (GeometryCollection) o;
+
+    int n1 = getNumGeometries();
+    int n2 = gc.getNumGeometries();
+    int i = 0;
+    while (i < n1 && i < n2) {
+      Geometry thisGeom = getGeometryN(i);
+      Geometry otherGeom = gc.getGeometryN(i);
+      int holeComp = thisGeom.compareToSameClass(otherGeom, comp);
+      if (holeComp != 0) return holeComp;
+      i++;
+    }
+    if (i < n1) return 1;
+    if (i < n2) return -1;
+    return 0;
+
+  }
 }
 
diff --git a/src/com/vividsolutions/jts/geom/GeometryCollectionIterator.java b/src/com/vividsolutions/jts/geom/GeometryCollectionIterator.java
index 8fd21e3..8690198 100644
--- a/src/com/vividsolutions/jts/geom/GeometryCollectionIterator.java
+++ b/src/com/vividsolutions/jts/geom/GeometryCollectionIterator.java
@@ -45,7 +45,7 @@ import java.util.NoSuchElementException;
  *  simple to ignore the <code>GeometryCollection</code> objects if they are not
  *  needed.
  *
- *@version 1.6
+ *@version 1.7
  */
 public class GeometryCollectionIterator implements Iterator {
 
diff --git a/src/com/vividsolutions/jts/geom/GeometryComponentFilter.java b/src/com/vividsolutions/jts/geom/GeometryComponentFilter.java
index 6eb8058..9f9814a 100644
--- a/src/com/vividsolutions/jts/geom/GeometryComponentFilter.java
+++ b/src/com/vividsolutions/jts/geom/GeometryComponentFilter.java
@@ -49,7 +49,7 @@ package com.vividsolutions.jts.geom;
  *  <code>GeometryComponentFilter</code>
  *  is an example of the Gang-of-Four Visitor pattern.
  *
- *@version 1.6
+ *@version 1.7
  */
 public interface GeometryComponentFilter {
 
@@ -58,6 +58,6 @@ public interface GeometryComponentFilter {
    *
    *@param  geom  a <code>Geometry</code> to which the filter is applied.
    */
-  public void filter(Geometry geom);
+  void filter(Geometry geom);
 }
 
diff --git a/src/com/vividsolutions/jts/geom/GeometryFactory.java b/src/com/vividsolutions/jts/geom/GeometryFactory.java
index 967d56b..403962e 100644
--- a/src/com/vividsolutions/jts/geom/GeometryFactory.java
+++ b/src/com/vividsolutions/jts/geom/GeometryFactory.java
@@ -44,7 +44,7 @@ import com.vividsolutions.jts.util.Assert;
  * Supplies a set of utility methods for building Geometry objects from lists
  * of Coordinates.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class GeometryFactory
     implements Serializable
@@ -337,7 +337,9 @@ public class GeometryFactory
    */
 
   public MultiPoint createMultiPoint(Coordinate[] coordinates) {
-      return createMultiPoint(coordinates != null ? getCoordinateSequenceFactory().create(coordinates) : null);
+      return createMultiPoint(coordinates != null
+                              ? getCoordinateSequenceFactory().create(coordinates)
+                              : null);
   }
 
   /**
@@ -347,13 +349,13 @@ public class GeometryFactory
    */
   public MultiPoint createMultiPoint(CoordinateSequence coordinates) {
     if (coordinates == null) {
-      coordinates = getCoordinateSequenceFactory().create(new Coordinate[]{});
+      return createMultiPoint(new Point[0]);
     }
-    ArrayList points = new ArrayList();
+    Point[] points = new Point[coordinates.size()];
     for (int i = 0; i < coordinates.size(); i++) {
-      points.add(createPoint(coordinates.getCoordinate(i)));
+      points[i] = createPoint(coordinates.getCoordinate(i));
     }
-    return createMultiPoint((Point[]) points.toArray(new Point[]{}));
+    return createMultiPoint(points);
   }
 
 
@@ -475,15 +477,15 @@ public class GeometryFactory
   }
 
 
-    public int getSRID() {
-		return SRID;
-	}
+  public int getSRID() {
+    return SRID;
+  }
 
-    private int SRID;
+  private int SRID;
 
-    public CoordinateSequenceFactory getCoordinateSequenceFactory() {
-        return coordinateSequenceFactory;
-    }
+  public CoordinateSequenceFactory getCoordinateSequenceFactory() {
+    return coordinateSequenceFactory;
+  }
 
 }
 
diff --git a/src/com/vividsolutions/jts/geom/GeometryFilter.java b/src/com/vividsolutions/jts/geom/GeometryFilter.java
index 8eaec5e..8cc5a4b 100644
--- a/src/com/vividsolutions/jts/geom/GeometryFilter.java
+++ b/src/com/vividsolutions/jts/geom/GeometryFilter.java
@@ -44,7 +44,7 @@ package com.vividsolutions.jts.geom;
  *  <code>GeometryFilter</code>
  *  is an example of the Gang-of-Four Visitor pattern.
  *
- *@version 1.6
+ *@version 1.7
  */
 public interface GeometryFilter {
 
@@ -53,6 +53,6 @@ public interface GeometryFilter {
    *
    *@param  geom  a <code>Geometry</code> to which the filter is applied.
    */
-  public void filter(Geometry geom);
+  void filter(Geometry geom);
 }
 
diff --git a/src/com/vividsolutions/jts/geom/IntersectionMatrix.java b/src/com/vividsolutions/jts/geom/IntersectionMatrix.java
index 7033a73..fab2213 100644
--- a/src/com/vividsolutions/jts/geom/IntersectionMatrix.java
+++ b/src/com/vividsolutions/jts/geom/IntersectionMatrix.java
@@ -52,7 +52,7 @@ package com.vividsolutions.jts.geom;
  *  HREF="http://www.opengis.org/techno/specs.htm">OpenGIS Simple Features
  *  Specification for SQL</A> .
  *
- *@version 1.6
+ *@version 1.7
  */
 public class IntersectionMatrix implements Cloneable {
   /**
@@ -332,21 +332,29 @@ public class IntersectionMatrix implements Cloneable {
   }
 
   /**
-   *  Returns <code>true</code> if this <code>IntersectionMatrix</code> is
-   *  <UL>
-   *    <LI> T*T****** (for a point and a curve, a point and an area or a line
-   *    and an area)
-   *    <LI> 0******** (for two curves)
-   *  </UL>
-   *  .
+   * Returns <code>true</code> if this geometry crosses the
+   * specified geometry.
+   * <p>
+   * The <code>crosses</code> predicate has the following equivalent definitions:
+   * <ul>
+   * <li>The geometries have some but not all interior points in common.
+   * <li>The DE-9IM Intersection Matrix for the two geometries is
+   *   <ul>
+   *    <li>T*T****** (for P/L, P/A, and L/A situations)
+   *    <li>T*****T** (for L/P, L/A, and A/L situations)
+   *    <li>0******** (for L/L situations)
+   *   </ul>
+   * </ul>
+   * For any other combination of dimensions this predicate returns <code>false</code>.
+   * <p>
+   * The SFS defined this predicate only for P/L, P/A, L/L, and L/A situations.
+   * JTS extends the definition to apply to L/P, A/P and A/L situations as well.
+   * This makes the relation symmetric.
    *
    *@param  dimensionOfGeometryA  the dimension of the first <code>Geometry</code>
    *@param  dimensionOfGeometryB  the dimension of the second <code>Geometry</code>
-   *@return                       <code>true</code> if the two <code>Geometry</code>
-   *      s related by this <code>IntersectionMatrix</code> cross. For this
-   *      function to return <code>true</code>, the <code>Geometry</code>s must
-   *      be a point and a curve; a point and a surface; two curves; or a curve
-   *      and a surface.
+   *@return                       <code>true</code> if the two <code>Geometry</code>s
+   *      related by this <code>IntersectionMatrix</code> cross.
    */
   public boolean isCrosses(int dimensionOfGeometryA, int dimensionOfGeometryB) {
     if ((dimensionOfGeometryA == Dimension.P && dimensionOfGeometryB == Dimension.L) ||
@@ -395,6 +403,50 @@ public class IntersectionMatrix implements Cloneable {
 
   /**
    *  Returns <code>true</code> if this <code>IntersectionMatrix</code> is
+   *    <code>T*****FF*</code>
+   * or <code>*T****FF*</code>
+   * or <code>***T**FF*</code>
+   * or <code>****T*FF*</code>
+   *
+   *@return    <code>true</code> if the first <code>Geometry</code> covers the
+   *      second
+   */
+  public boolean isCovers() {
+    boolean hasPointInCommon =
+        matches(matrix[Location.INTERIOR][Location.INTERIOR], 'T')
+        || matches(matrix[Location.INTERIOR][Location.BOUNDARY], 'T')
+        || matches(matrix[Location.BOUNDARY][Location.INTERIOR], 'T')
+        || matches(matrix[Location.BOUNDARY][Location.BOUNDARY], 'T');
+
+    return hasPointInCommon &&
+        matrix[Location.EXTERIOR][Location.INTERIOR] == Dimension.FALSE &&
+        matrix[Location.EXTERIOR][Location.BOUNDARY] == Dimension.FALSE;
+  }
+
+  /**
+   *  Returns <code>true</code> if this <code>IntersectionMatrix</code> is
+   *    <code>T*F**F***</code>
+   * or <code>*TF**F***</code>
+   * or <code>**FT*F***</code>
+   * or <code>**F*TF***</code>
+   *
+   *@return    <code>true</code> if the first <code>Geometry</code> covers the
+   *      second
+   */
+  public boolean isCoveredBy() {
+    boolean hasPointInCommon =
+        matches(matrix[Location.INTERIOR][Location.INTERIOR], 'T')
+        || matches(matrix[Location.INTERIOR][Location.BOUNDARY], 'T')
+        || matches(matrix[Location.BOUNDARY][Location.INTERIOR], 'T')
+        || matches(matrix[Location.BOUNDARY][Location.BOUNDARY], 'T');
+
+    return hasPointInCommon &&
+        matrix[Location.INTERIOR][Location.EXTERIOR] == Dimension.FALSE &&
+        matrix[Location.BOUNDARY][Location.EXTERIOR] == Dimension.FALSE;
+  }
+
+  /**
+   *  Returns <code>true</code> if this <code>IntersectionMatrix</code> is
    *  T*F**FFF*.
    *
    *@param  dimensionOfGeometryA  the dimension of the first <code>Geometry</code>
diff --git a/src/com/vividsolutions/jts/geom/LineSegment.java b/src/com/vividsolutions/jts/geom/LineSegment.java
index 49c40bf..8f9c0bf 100644
--- a/src/com/vividsolutions/jts/geom/LineSegment.java
+++ b/src/com/vividsolutions/jts/geom/LineSegment.java
@@ -49,7 +49,7 @@ import com.vividsolutions.jts.algorithm.*;
  * object as a way of computing segment properties on the
  * segments defined by arrays or lists of {@link Coordinate}s.
  *
- *@version 1.6
+ *@version 1.7
  */
 public class LineSegment
   implements Comparable, Serializable
@@ -154,10 +154,13 @@ public class LineSegment
     p0 = p1;
     p1 = temp;
   }
+
   /**
    * Puts the line segment into a normalized form.
    * This is useful for using line segments in maps and indexes when
    * topological equality rather than exact equality is desired.
+   * A segment in normalized form has the first point smaller
+   * than the second (according to the standard ordering on {@link Coordinate}).
    */
   public void normalize()
   {
@@ -165,7 +168,11 @@ public class LineSegment
   }
 
   /**
-   * @return the angle this segment makes with the x-axis (in radians)
+   * Computes the angle that the vector defined by this segment
+   * makes with the X-axis.
+   * The angle will be in the range [ -PI, PI ] radians.
+   *
+   * @return the angle this segment makes with the X-axis (in radians)
    */
   public double angle()
   {
@@ -173,7 +180,9 @@ public class LineSegment
   }
 
   /**
-   * Computes the distance between this line segment and another one.
+   * Computes the distance between this line segment and another segment.
+   *
+   * @return the distance to the other segment
    */
   public double distance(LineSegment ls)
   {
@@ -181,7 +190,9 @@ public class LineSegment
   }
 
   /**
-   * Computes the distance between this line segment and a point.
+   * Computes the distance between this line segment and a given point.
+   *
+   * @return the distance from this segment to the given point
    */
   public double distance(Coordinate p)
   {
@@ -191,6 +202,8 @@ public class LineSegment
   /**
    * Computes the perpendicular distance between the (infinite) line defined
    * by this line segment and a point.
+   *
+   * @return the perpendicular distance between the defined line and the given point
    */
   public double distancePerpendicular(Coordinate p)
   {
@@ -198,10 +211,28 @@ public class LineSegment
   }
 
   /**
-   * Compute the projection factor for the projection of the point p
-   * onto this LineSegment.  The projection factor is the constant k
+   * Computes the {@link Coordinate} that lies a given
+   * fraction along the line defined by this segment.
+   * A fraction of <code>0.0</code> returns the start point of the segment;
+   * a fraction of <code>1.0</code> returns the end point of the segment.
+   *
+   * @param segmentLengthFraction the fraction of the segment length along the line
+   * @return the point at that distance
+   */
+  public Coordinate pointAlong(double segmentLengthFraction)
+  {
+    Coordinate coord = new Coordinate();
+    coord.x = p0.x + segmentLengthFraction * (p1.x - p0.x);
+    coord.y = p0.y + segmentLengthFraction * (p1.y - p0.y);
+    return coord;
+  }
+
+  /**
+   * Computes the Projection Factor for the projection of the point p
+   * onto this LineSegment.  The Projection Factor is the constant r
    * by which the vector for this segment must be multiplied to
-   * equal the vector for the projection of p.
+   * equal the vector for the projection of p on the line
+   * defined by this segment.
    */
   public double projectionFactor(Coordinate p)
   {
diff --git a/src/com/vividsolutions/jts/geom/LineString.java b/src/com/vividsolutions/jts/geom/LineString.java
index 3e998ab..9bc13db 100644
--- a/src/com/vividsolutions/jts/geom/LineString.java
+++ b/src/com/vividsolutions/jts/geom/LineString.java
@@ -1,5 +1,3 @@
-
-
 /*
  * The JTS Topology Suite is a collection of Java classes that
  * implement the fundamental operations required to validate a given
@@ -40,7 +38,7 @@ import com.vividsolutions.jts.operation.IsSimpleOp;
 /**
  *  Basic implementation of <code>LineString</code>.
  *
- *@version 1.6
+ *@version 1.7
  */
 public class LineString extends Geometry {
   private static final long serialVersionUID = 3110669828065365560L;
@@ -183,10 +181,22 @@ public class LineString extends Geometry {
         });
   }
 
+  /**
+   * Creates a {@link LineString} whose coordinates are in the reverse
+   * order of this objects
+   *
+   * @return a {@link LineString} with coordinates in the reverse order
+   */
+  public LineString reverse()
+  {
+    CoordinateSequence seq = (CoordinateSequence) points.clone();
+    CoordinateSequences.reverse(seq);
+    LineString revLine = getFactory().createLineString(seq);
+    return revLine;
+  }
 
   /**
-   *  Returns true if the given point is a vertex of this <code>LineString</code>
-   *  .
+   *  Returns true if the given point is a vertex of this <code>LineString</code>.
    *
    *@param  pt  the <code>Coordinate</code> to check
    *@return     <code>true</code> if <code>pt</code> is one of this <code>LineString</code>
@@ -206,25 +216,6 @@ public class LineString extends Geometry {
       return new Envelope();
     }
     return points.expandEnvelope(new Envelope());
-    /*
-    //Convert to array, then access array directly, to avoid the function-call overhead
-    //of calling #get millions of times. #toArray may be inefficient for
-    //non-BasicCoordinateSequence CoordinateSequences. [Jon Aquino]
-    Coordinate[] coordinates = points.toCoordinateArray();
-    double minx = coordinates[0].x;
-    double miny = coordinates[0].y;
-    double maxx = coordinates[0].x;
-    double maxy = coordinates[0].y;
-    //OptimizeIt shows that Math#min and Math#max here are a bottleneck.
-    //Replace with direct comparisons. [Jon Aquino]
-    for (int i = 1; i < coordinates.length; i++) {
-      minx = minx < coordinates[i].x ? minx : coordinates[i].x;
-      maxx = maxx > coordinates[i].x ? maxx : coordinates[i].x;
-      miny = miny < coordinates[i].y ? miny : coordinates[i].y;
-      maxy = maxy > coordinates[i].y ? maxy : coordinates[i].y;
-    }
-    return new Envelope(minx, maxx, miny, maxy);
-    */
   }
 
   public boolean equalsExact(Geometry other, double tolerance) {
@@ -288,32 +279,31 @@ public class LineString extends Geometry {
 
   protected int compareToSameClass(Object o)
   {
-      LineString line = (LineString) o;
-      // MD - optimized implementation
-      int i = 0;
-      int j = 0;
-      while (i < points.size() && j < line.points.size()) {
-        int comparison = points.getCoordinate(i).compareTo(line.points.getCoordinate(j));
-        if (comparison != 0) {
-          return comparison;
-        }
-        i++;
-        j++;
+    LineString line = (LineString) o;
+    // MD - optimized implementation
+    int i = 0;
+    int j = 0;
+    while (i < points.size() && j < line.points.size()) {
+      int comparison = points.getCoordinate(i).compareTo(line.points.getCoordinate(j));
+      if (comparison != 0) {
+        return comparison;
       }
-      if (i < points.size()) {
-        return 1;
-      }
-      if (j < line.points.size()) {
-        return -1;
-      }
-      return 0;
+      i++;
+      j++;
+    }
+    if (i < points.size()) {
+      return 1;
+    }
+    if (j < line.points.size()) {
+      return -1;
+    }
+    return 0;
+  }
 
-    /*
-    ArrayList theseElements = new ArrayList(Arrays.asList(points));
-    ArrayList otherElements = new ArrayList(Arrays.asList(((LineString) o).points));
-    return compare(theseElements, otherElements);
-    */
+  protected int compareToSameClass(Object o, CoordinateSequenceComparator comp)
+  {
+    LineString line = (LineString) o;
+    return comp.compare(this.points, line.points);
   }
 
 }
-
diff --git a/src/com/vividsolutions/jts/geom/LinearRing.java b/src/com/vividsolutions/jts/geom/LinearRing.java
index 990df10..37e77fd 100644
--- a/src/com/vividsolutions/jts/geom/LinearRing.java
+++ b/src/com/vividsolutions/jts/geom/LinearRing.java
@@ -35,18 +35,21 @@
 package com.vividsolutions.jts.geom;
 
 /**
- *  Basic implementation of <code>LinearRing</code>.
- * The first and last point in the coordinate sequence must be equal.
+ * Models an OGC SFS <code>LinearRing</code>.
+ * A LinearRing is a LineString which is both closed and simple.
+ * In other words,
+ * the first and last coordinate in the ring must be equal,
+ * and the interior of the ring must not self-intersect.
  * Either orientation of the ring is allowed.
- * A valid ring must not self-intersect.
  *
- *@version 1.6
+ * @version 1.7
  */
 public class LinearRing extends LineString
 {
   private static final long serialVersionUID = -4261142084085851829L;
+
   /**
-   *  Constructs a <code>LinearRing</code> with the given points.
+   * Constructs a <code>LinearRing</code> with the given points.
    *
    *@param  points          points forming a closed and simple linestring, or
    *      <code>null</code> or an empty array to create the empty geometry.
@@ -75,11 +78,11 @@ public class LinearRing extends LineString
 
 
   /**
-   *  Constructs a <code>LinearRing</code> with the given points.
+   * Constructs a <code>LinearRing</code> with the vertices
+   * specifed by the given {@link CoordinateSequence}.
    *
-   *@param  points          points forming a closed and simple linestring, or
-   *      <code>null</code> or an empty array to create the empty geometry.
-   *      This array must not contain <code>null</code> elements.
+   *@param  points  a sequence points forming a closed and simple linestring, or
+   *      <code>null</code> to create the empty geometry.
    *
    */
   public LinearRing(CoordinateSequence points, GeometryFactory factory) {
@@ -88,15 +91,21 @@ public class LinearRing extends LineString
   }
 
   private void validateConstruction() {
-	if (!isEmpty() && ! super.isClosed()) {
+    if (!isEmpty() && ! super.isClosed()) {
       throw new IllegalArgumentException("points must form a closed linestring");
     }
     if (getCoordinateSequence().size() >= 1 && getCoordinateSequence().size() <= 3) {
       throw new IllegalArgumentException("Number of points must be 0 or >3");
     }
-}
+  }
 
-public boolean isSimple() {
+  /**
+   * Returns <code>true</code>, since by definition LinearRings are always simple.
+   * @return <code>true</code>
+   *
+   * @see Geometry#isSimple
+   */
+  public boolean isSimple() {
     return true;
   }
 
@@ -104,9 +113,4 @@ public boolean isSimple() {
     return "LinearRing";
   }
 
-  public boolean isClosed() {
-    return true;
-  }
-
 }
-
diff --git a/src/com/vividsolutions/jts/geom/Location.java b/src/com/vividsolutions/jts/geom/Location.java
index 59ad532..b4b941d 100644
--- a/src/com/vividsolutions/jts/geom/Location.java
+++ b/src/com/vividsolutions/jts/geom/Location.java
@@ -41,7 +41,7 @@ package com.vividsolutions.jts.geom;
  *  HREF="http://www.opengis.org/techno/specs.htm">OpenGIS Simple Features
  *  Specification for SQL</A> .
  *
- *@version 1.6
+ *@version 1.7
  */
 public class Location {
   /**
@@ -66,13 +66,13 @@ public class Location {
   /**
    *  Used for uninitialized location values.
    */
-  public final static int NULL = -1;
+  public final static int NONE = -1;
 
   /**
    *  Converts the location value to a location symbol, for example, <code>EXTERIOR => 'e'</code>
    *  .
    *
-   *@param  locationValue  either EXTERIOR, BOUNDARY, INTERIOR or NULL
+   *@param  locationValue  either EXTERIOR, BOUNDARY, INTERIOR or NONE
    *@return                either 'e', 'b', 'i' or '-'
    */
   public static char toLocationSymbol(int locationValue) {
@@ -83,7 +83,7 @@ public class Location {
         return 'b';
       case INTERIOR:
         return 'i';
-      case NULL:
+      case NONE:
         return '-';
     }
     throw new IllegalArgumentException("Unknown location value: " + locationValue);
diff --git a/src/com/vividsolutions/jts/geom/MultiLineString.java b/src/com/vividsolutions/jts/geom/MultiLineString.java
index 7ee1daa..6bdf433 100644
--- a/src/com/vividsolutions/jts/geom/MultiLineString.java
+++ b/src/com/vividsolutions/jts/geom/MultiLineString.java
@@ -41,7 +41,7 @@ import com.vividsolutions.jts.operation.IsSimpleOp;
 /**
  *  Basic implementation of <code>MultiLineString</code>.
  *
- *@version 1.6
+ *@version 1.7
  */
 public class MultiLineString extends GeometryCollection {
   private static final long serialVersionUID = 8166665132445433741L;
@@ -116,6 +116,25 @@ public class MultiLineString extends GeometryCollection {
     return getFactory().createMultiPoint(pts);
   }
 
+  /**
+   * Creates a {@link MultiLineString} in the reverse
+   * order to this object.
+   * Both the order of the component LineStrings
+   * and the order of their coordinate sequences
+   * are reversed.
+   *
+   * @return a {@link MultiLineString} in the reverse order
+   */
+  public MultiLineString reverse()
+  {
+    int nLines = geometries.length;
+    LineString[] revLines = new LineString[nLines];
+    for (int i = 0; i < geometries.length; i++) {
+      revLines[nLines - 1 - i] = ((LineString) geometries[i]).reverse();
+    }
+    return getFactory().createMultiLineString(revLines);
+  }
+
   public boolean equalsExact(Geometry other, double tolerance) {
     if (!isEquivalentClass(other)) {
       return false;
diff --git a/src/com/vividsolutions/jts/geom/MultiPoint.java b/src/com/vividsolutions/jts/geom/MultiPoint.java
index 2fe0e4d..f7d85be 100644
--- a/src/com/vividsolutions/jts/geom/MultiPoint.java
+++ b/src/com/vividsolutions/jts/geom/MultiPoint.java
@@ -39,7 +39,7 @@ import com.vividsolutions.jts.operation.IsSimpleOp;
 /**
  *  Models a collection of <code>Point</code>s.
  *
- *@version 1.6
+ *@version 1.7
  */
 public class MultiPoint
   extends GeometryCollection
diff --git a/src/com/vividsolutions/jts/geom/MultiPolygon.java b/src/com/vividsolutions/jts/geom/MultiPolygon.java
index bbc1c71..ba8a3e0 100644
--- a/src/com/vividsolutions/jts/geom/MultiPolygon.java
+++ b/src/com/vividsolutions/jts/geom/MultiPolygon.java
@@ -39,7 +39,7 @@ import java.util.ArrayList;
 /**
  *  Basic implementation of <code>MultiPolygon</code>.
  *
- *@version 1.6
+ *@version 1.7
  */
 public class MultiPolygon extends GeometryCollection {
   private static final long serialVersionUID = -551033529766975875L;
diff --git a/src/com/vividsolutions/jts/geom/Point.java b/src/com/vividsolutions/jts/geom/Point.java
index f2a64e3..4ab9185 100644
--- a/src/com/vividsolutions/jts/geom/Point.java
+++ b/src/com/vividsolutions/jts/geom/Point.java
@@ -39,7 +39,7 @@ import com.vividsolutions.jts.util.Assert;
 /**
  *  Basic implementation of <code>Point</code>.
  *
- *@version 1.6
+ *@version 1.7
  */
 public class Point extends Geometry {
   private static final long serialVersionUID = 4902022702746614570L;
@@ -184,9 +184,14 @@ public class Point extends Geometry {
     return getCoordinate().compareTo(point.getCoordinate());
   }
 
-    public CoordinateSequence getCoordinateSequence() {
-        return coordinates;
-    }
+  protected int compareToSameClass(Object other, CoordinateSequenceComparator comp)
+  {
+    Point point = (Point) other;
+    return comp.compare(this.coordinates, point.coordinates);
+  }
 
+  public CoordinateSequence getCoordinateSequence() {
+    return coordinates;
+  }
 }
 
diff --git a/src/com/vividsolutions/jts/geom/Polygon.java b/src/com/vividsolutions/jts/geom/Polygon.java
index 7742106..f41e563 100644
--- a/src/com/vividsolutions/jts/geom/Polygon.java
+++ b/src/com/vividsolutions/jts/geom/Polygon.java
@@ -49,7 +49,7 @@ import com.vividsolutions.jts.algorithm.*;
  *  HREF="http://www.opengis.org/techno/specs.htm">OpenGIS Simple Features
  *  Specification for SQL</A> .
  *
- *@version 1.6
+ *@version 1.7
  */
 public class Polygon extends Geometry {
   private static final long serialVersionUID = -3494792200821764533L;
@@ -201,7 +201,7 @@ public class Polygon extends Geometry {
 
     // check vertices are in right order
     double prevX = seq.getX(0);
-    double prevY = seq.getX(0);
+    double prevY = seq.getY(0);
     for (int i = 1; i <= 4; i++) {
       double x = seq.getX(i);
       double y = seq.getY(i);
@@ -350,6 +350,29 @@ public class Polygon extends Geometry {
     return thisShell.compareToSameClass(otherShell);
   }
 
+  protected int compareToSameClass(Object o, CoordinateSequenceComparator comp) {
+    Polygon poly = (Polygon) o;
+
+    LinearRing thisShell = shell;
+    LinearRing otherShell = poly.shell;
+    int shellComp = thisShell.compareToSameClass(otherShell, comp);
+    if (shellComp != 0) return shellComp;
+
+    int nHole1 = getNumInteriorRing();
+    int nHole2 = poly.getNumInteriorRing();
+    int i = 0;
+    while (i < nHole1 && i < nHole2) {
+      LinearRing thisHole = (LinearRing) getInteriorRingN(i);
+      LinearRing otherHole = (LinearRing) poly.getInteriorRingN(i);
+      int holeComp = thisHole.compareToSameClass(otherHole, comp);
+      if (holeComp != 0) return holeComp;
+      i++;
+    }
+    if (i < nHole1) return 1;
+    if (i < nHole2) return -1;
+    return 0;
+  }
+
   private void normalize(LinearRing ring, boolean clockwise) {
     if (ring.isEmpty()) {
       return;
diff --git a/src/com/vividsolutions/jts/geom/PrecisionModel.java b/src/com/vividsolutions/jts/geom/PrecisionModel.java
index 8a4aa32..d8a7bd6 100644
--- a/src/com/vividsolutions/jts/geom/PrecisionModel.java
+++ b/src/com/vividsolutions/jts/geom/PrecisionModel.java
@@ -79,7 +79,7 @@ import java.util.Map;
  *<p>
  *  JTS methods currently do not handle inputs with different precision models.
  *
- *@version 1.6
+ *@version 1.7
  */
 public class PrecisionModel implements Serializable, Comparable
 {
diff --git a/src/com/vividsolutions/jts/geom/TopologyException.java b/src/com/vividsolutions/jts/geom/TopologyException.java
index a6d282d..d8bb851 100644
--- a/src/com/vividsolutions/jts/geom/TopologyException.java
+++ b/src/com/vividsolutions/jts/geom/TopologyException.java
@@ -38,7 +38,7 @@ package com.vividsolutions.jts.geom;
 /**
  * Indicates an invalid or inconsistent topological situation encountered during processing
  *
- * @version 1.6
+ * @version 1.7
  */
 public class TopologyException
   extends RuntimeException
diff --git a/src/com/vividsolutions/jts/geom/Triangle.java b/src/com/vividsolutions/jts/geom/Triangle.java
index 489068c..1a8d332 100644
--- a/src/com/vividsolutions/jts/geom/Triangle.java
+++ b/src/com/vividsolutions/jts/geom/Triangle.java
@@ -37,7 +37,7 @@ package com.vividsolutions.jts.geom;
  * Represents a planar triangle, and provides methods for calculating various
  * properties of triangles.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class Triangle
 {
diff --git a/src/com/vividsolutions/jts/geom/impl/CoordinateArraySequence.java b/src/com/vividsolutions/jts/geom/impl/CoordinateArraySequence.java
index e8880da..bbe77bf 100644
--- a/src/com/vividsolutions/jts/geom/impl/CoordinateArraySequence.java
+++ b/src/com/vividsolutions/jts/geom/impl/CoordinateArraySequence.java
@@ -41,7 +41,7 @@ import com.vividsolutions.jts.geom.*;
  * modifications to them are actually changing the
  * CoordinateSequence's underlying data.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class CoordinateArraySequence
     implements CoordinateSequence, Serializable
@@ -95,6 +95,11 @@ public class CoordinateArraySequence
   }
 
   /**
+   * @see com.vividsolutions.jts.geom.CoordinateSequence#getDimension()
+   */
+  public int getDimension() { return 3; }
+
+  /**
    * Get the Coordinate with index i.
    *
    * @param i
@@ -177,9 +182,17 @@ public class CoordinateArraySequence
   public void setOrdinate(int index, int ordinateIndex, double value)
   {
     switch (ordinateIndex) {
-      case CoordinateSequence.X:  coordinates[index].x = value;
-      case CoordinateSequence.Y:  coordinates[index].y = value;
-      case CoordinateSequence.Z:  coordinates[index].z = value;
+      case CoordinateSequence.X:
+        coordinates[index].x = value;
+        break;
+      case CoordinateSequence.Y:
+        coordinates[index].y = value;
+        break;
+      case CoordinateSequence.Z:
+        coordinates[index].z = value;
+        break;
+      default:
+          throw new IllegalArgumentException("invalid ordinateIndex");
     }
   }
 
@@ -220,4 +233,4 @@ public class CoordinateArraySequence
       return "()";
     }
   }
-}
+}
\ No newline at end of file
diff --git a/src/com/vividsolutions/jts/geom/impl/CoordinateArraySequenceFactory.java b/src/com/vividsolutions/jts/geom/impl/CoordinateArraySequenceFactory.java
index 86813f8..95a0a94 100644
--- a/src/com/vividsolutions/jts/geom/impl/CoordinateArraySequenceFactory.java
+++ b/src/com/vividsolutions/jts/geom/impl/CoordinateArraySequenceFactory.java
@@ -37,15 +37,15 @@ import java.io.Serializable;
 import com.vividsolutions.jts.geom.*;
 
 /**
- * Creates CoordinateSequences represented as an array of {@link Coordinate}s.
+ * Creates {@link CoordinateSequence}s represented as an array of {@link Coordinate}s.
  *
- * @version 1.6
+ * @version 1.7
  */
 public final class CoordinateArraySequenceFactory
     implements CoordinateSequenceFactory, Serializable
 {
   private static final long serialVersionUID = -4099577099607551657L;
-  private static CoordinateArraySequenceFactory instance = new CoordinateArraySequenceFactory();
+  private static CoordinateArraySequenceFactory instanceObject = new CoordinateArraySequenceFactory();
 
   private CoordinateArraySequenceFactory() {
   }
@@ -61,7 +61,7 @@ public final class CoordinateArraySequenceFactory
    * Returns the singleton instance of {@link CoordinateArraySequenceFactory}
    */
   public static CoordinateArraySequenceFactory instance() {
-    return instance;
+    return instanceObject;
   }
 
   /**
@@ -85,8 +85,12 @@ public final class CoordinateArraySequenceFactory
 
   /**
    * @see com.vividsolutions.jts.geom.CoordinateSequenceFactory#create(int, int)
+   *
+   * @throws IllegalArgumentException if the dimension is > 3
    */
   public CoordinateSequence create(int size, int dimension) {
+    if (dimension > 3)
+      throw new IllegalArgumentException("dimension must be <= 3");
     return new CoordinateArraySequence(size);
   }
-}
+}
\ No newline at end of file
diff --git a/src/com/vividsolutions/jts/geom/impl/PackedCoordinateSequence.java b/src/com/vividsolutions/jts/geom/impl/PackedCoordinateSequence.java
index ecab30e..0a10abe 100644
--- a/src/com/vividsolutions/jts/geom/impl/PackedCoordinateSequence.java
+++ b/src/com/vividsolutions/jts/geom/impl/PackedCoordinateSequence.java
@@ -15,7 +15,7 @@ import java.lang.ref.SoftReference;
  * The cache is cleared each time the coordinate sequence contents are
  * modified through a setter method.
  *
- * @version 1.6
+ * @version 1.7
  */
 public abstract class PackedCoordinateSequence
     implements CoordinateSequence
@@ -432,4 +432,4 @@ public abstract class PackedCoordinateSequence
 
   }
 
-}
+}
\ No newline at end of file
diff --git a/src/com/vividsolutions/jts/geom/util/GeometryEditor.java b/src/com/vividsolutions/jts/geom/util/GeometryEditor.java
index 312b1ca..9bff093 100644
--- a/src/com/vividsolutions/jts/geom/util/GeometryEditor.java
+++ b/src/com/vividsolutions/jts/geom/util/GeometryEditor.java
@@ -67,7 +67,7 @@ import java.util.ArrayList;
  *
  * @see Geometry#isValid
  *
- * @version 1.6
+ * @version 1.7
  */
 public class GeometryEditor
 {
@@ -106,6 +106,9 @@ public class GeometryEditor
    */
   public Geometry edit(Geometry geometry, GeometryEditorOperation operation)
   {
+    // nothing to do
+    if (geometry == null) return null;
+
     // if client did not supply a GeometryFactory, use the one from the input Geometry
     if (factory == null)
       factory = geometry.getFactory();
@@ -127,9 +130,7 @@ public class GeometryEditor
       return operation.edit(geometry, factory);
     }
 
-    Assert.shouldNeverReachHere(
-        "Unsupported Geometry classes should be caught in the GeometryEditorOperation.");
-
+    Assert.shouldNeverReachHere("Unsupported Geometry class: " + geometry.getClass().getName());
     return null;
   }
 
@@ -205,7 +206,7 @@ public class GeometryEditor
   /**
    * A interface which specifies an edit operation for Geometries.
    *
-   * @version 1.6
+   * @version 1.7
    */
   public interface GeometryEditorOperation
   {
@@ -218,7 +219,7 @@ public class GeometryEditor
      * (may be different to the factory of the input geometry)
      * @return a new Geometry which is a modification of the input Geometry
      */
-    public Geometry edit(Geometry geometry, GeometryFactory factory);
+    Geometry edit(Geometry geometry, GeometryFactory factory);
   }
 
   /**
@@ -228,7 +229,7 @@ public class GeometryEditor
   public abstract static class CoordinateOperation
       implements GeometryEditorOperation
   {
-    public Geometry edit(Geometry geometry, GeometryFactory factory) {
+    public final Geometry edit(Geometry geometry, GeometryFactory factory) {
       if (geometry instanceof LinearRing) {
         return factory.createLinearRing(edit(geometry.getCoordinates(),
             geometry));
diff --git a/src/com/vividsolutions/jts/geom/util/GeometryTransformer.java b/src/com/vividsolutions/jts/geom/util/GeometryTransformer.java
index fe73375..f055b3d 100644
--- a/src/com/vividsolutions/jts/geom/util/GeometryTransformer.java
+++ b/src/com/vividsolutions/jts/geom/util/GeometryTransformer.java
@@ -11,7 +11,7 @@ import com.vividsolutions.jts.geom.*;
  * various different Geometry subclasses.
  * It provides an easy way of applying specific transformations
  * to given geometry types, while allowing unhandled types to be simply copied.
- * Also, the framework handles ensuring that if subcomponents change type
+ * Also, the framework ensures that if subcomponents change type
  * the parent geometries types change appropriately to maintain valid structure.
  * Subclasses will override whichever <code>transformX</code> methods
  * they need to to handle particular Geometry types.
@@ -31,11 +31,12 @@ import com.vividsolutions.jts.geom.*;
  * The @link transform} method itself will always
  * return a geometry object.
  *
- * @version 1.6
+ * @version 1.7
  *
  * @see GeometryEditor
  */
-public class GeometryTransformer {
+public class GeometryTransformer
+{
 
   /**
    * Possible extensions:
@@ -100,7 +101,7 @@ public class GeometryTransformer {
   }
 
   /**
-   * Convenience method which provides statndard way of
+   * Convenience method which provides standard way of
    * creating a {@link CoordinateSequence}
    *
    * @param coords the coordinate array to copy
@@ -226,4 +227,4 @@ public class GeometryTransformer {
     return factory.buildGeometry(transGeomList);
   }
 
-}
+}
\ No newline at end of file
diff --git a/src/com/vividsolutions/jts/geom/util/LinearComponentExtracter.java b/src/com/vividsolutions/jts/geom/util/LinearComponentExtracter.java
index 5acd25c..9329ae0 100644
--- a/src/com/vividsolutions/jts/geom/util/LinearComponentExtracter.java
+++ b/src/com/vividsolutions/jts/geom/util/LinearComponentExtracter.java
@@ -39,7 +39,7 @@ import com.vividsolutions.jts.geom.*;
 /**
  * Extracts all the 1-dimensional ({@link LineString}) components from a {@link Geometry}.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class LinearComponentExtracter
   implements GeometryComponentFilter
diff --git a/src/com/vividsolutions/jts/geom/util/PointExtracter.java b/src/com/vividsolutions/jts/geom/util/PointExtracter.java
index fd734a0..c303693 100644
--- a/src/com/vividsolutions/jts/geom/util/PointExtracter.java
+++ b/src/com/vividsolutions/jts/geom/util/PointExtracter.java
@@ -39,7 +39,7 @@ import com.vividsolutions.jts.geom.*;
 /**
  * Extracts all the 0-dimensional ({@link Point}) components from a {@link Geometry}.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class PointExtracter
   implements GeometryFilter
diff --git a/src/com/vividsolutions/jts/geom/util/PolygonExtracter.java b/src/com/vividsolutions/jts/geom/util/PolygonExtracter.java
index e01bff9..94604ca 100644
--- a/src/com/vividsolutions/jts/geom/util/PolygonExtracter.java
+++ b/src/com/vividsolutions/jts/geom/util/PolygonExtracter.java
@@ -39,7 +39,7 @@ import com.vividsolutions.jts.geom.*;
 /**
  * Extracts all the 2-dimensional ({@link Polygon}) components from a {@link Geometry}.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class PolygonExtracter
   implements GeometryFilter
diff --git a/src/com/vividsolutions/jts/geom/util/ShortCircuitedGeometryVisitor.java b/src/com/vividsolutions/jts/geom/util/ShortCircuitedGeometryVisitor.java
index 2b5f845..14f7ee5 100644
--- a/src/com/vividsolutions/jts/geom/util/ShortCircuitedGeometryVisitor.java
+++ b/src/com/vividsolutions/jts/geom/util/ShortCircuitedGeometryVisitor.java
@@ -6,7 +6,7 @@ import com.vividsolutions.jts.geom.*;
  * A visitor to {@link Geometry} elements which can
  * be short-circuited by a given condition
  *
- * @version 1.6
+ * @version 1.7
  */
 public abstract class ShortCircuitedGeometryVisitor
 {
@@ -33,4 +33,4 @@ public abstract class ShortCircuitedGeometryVisitor
   protected abstract void visit(Geometry element);
 
   protected abstract boolean isDone();
-}
+}
\ No newline at end of file
diff --git a/src/com/vividsolutions/jts/geomgraph/Depth.java b/src/com/vividsolutions/jts/geomgraph/Depth.java
index 9db637a..3293f37 100644
--- a/src/com/vividsolutions/jts/geomgraph/Depth.java
+++ b/src/com/vividsolutions/jts/geomgraph/Depth.java
@@ -40,17 +40,17 @@ import com.vividsolutions.jts.geom.Location;
 /**
  * A Depth object records the topological depth of the sides
  * of an Edge for up to two Geometries.
- * @version 1.6
+ * @version 1.7
  */
 public class Depth {
 
-  private final static int NULL = -1;
+  private final static int NULL_VALUE = -1;
 
   public static int depthAtLocation(int location)
   {
     if (location == Location.EXTERIOR) return 0;
     if (location == Location.INTERIOR) return 1;
-    return NULL;
+    return NULL_VALUE;
   }
 
   private int[][] depth = new int[2][3];
@@ -59,7 +59,7 @@ public class Depth {
     // initialize depth array to a sentinel value
     for (int i = 0; i < 2; i++) {
       for (int j = 0; j < 3; j++) {
-        depth[i][j] = NULL;
+        depth[i][j] = NULL_VALUE;
       }
     }
   }
@@ -89,7 +89,7 @@ public class Depth {
   {
     for (int i = 0; i < 2; i++) {
       for (int j = 0; j < 3; j++) {
-        if (depth[i][j] != NULL)
+        if (depth[i][j] != NULL_VALUE)
           return false;
       }
     }
@@ -97,11 +97,11 @@ public class Depth {
   }
   public boolean isNull(int geomIndex)
   {
-    return depth[geomIndex][1] == NULL;
+    return depth[geomIndex][1] == NULL_VALUE;
   }
   public boolean isNull(int geomIndex, int posIndex)
   {
-    return depth[geomIndex][posIndex] == NULL;
+    return depth[geomIndex][posIndex] == NULL_VALUE;
   }
   public void add(Label lbl)
   {
diff --git a/src/com/vividsolutions/jts/geomgraph/DirectedEdge.java b/src/com/vividsolutions/jts/geomgraph/DirectedEdge.java
index 69ad216..0fdc778 100644
--- a/src/com/vividsolutions/jts/geomgraph/DirectedEdge.java
+++ b/src/com/vividsolutions/jts/geomgraph/DirectedEdge.java
@@ -41,7 +41,7 @@ import com.vividsolutions.jts.geom.*;
 
 
 /**
- * @version 1.6
+ * @version 1.7
  */
 public class DirectedEdge
   extends EdgeEnd
@@ -102,9 +102,9 @@ public class DirectedEdge
   public void setDepth(int position, int depthVal)
   {
     if (depth[position] != -999) {
-      if (depth[position] != depthVal) {
-        Debug.print(this);
-      }
+//      if (depth[position] != depthVal) {
+//        Debug.print(this);
+//      }
       if (depth[position] != depthVal)
         throw new TopologyException("assigned depths do not match", getCoordinate());
       //Assert.isTrue(depth[position] == depthVal, "assigned depths do not match at " + getCoordinate());
diff --git a/src/com/vividsolutions/jts/geomgraph/DirectedEdgeStar.java b/src/com/vividsolutions/jts/geomgraph/DirectedEdgeStar.java
index c4e9b65..dc0cde4 100644
--- a/src/com/vividsolutions/jts/geomgraph/DirectedEdgeStar.java
+++ b/src/com/vividsolutions/jts/geomgraph/DirectedEdgeStar.java
@@ -45,7 +45,7 @@ import com.vividsolutions.jts.util.*;
  * It supports labelling the edges as well as linking the edges to form both
  * MaximalEdgeRings and MinimalEdgeRings.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class DirectedEdgeStar
   extends EdgeEndStar
@@ -128,7 +128,7 @@ public class DirectedEdgeStar
 
     // determine the overall labelling for this DirectedEdgeStar
     // (i.e. for the node it is based at)
-    label = new Label(Location.NULL);
+    label = new Label(Location.NONE);
     for (Iterator it = iterator(); it.hasNext(); ) {
       EdgeEnd ee = (EdgeEnd) it.next();
       Edge e = ee.getEdge();
@@ -316,7 +316,7 @@ public class DirectedEdgeStar
      * - INTERIOR if the edge is outgoing
      * - EXTERIOR if the edge is incoming
      */
-    int startLoc = Location.NULL ;
+    int startLoc = Location.NONE ;
     for (Iterator it = iterator(); it.hasNext(); ) {
       DirectedEdge nextOut  = (DirectedEdge) it.next();
       DirectedEdge nextIn   = nextOut.getSym();
@@ -332,7 +332,7 @@ public class DirectedEdgeStar
       }
     }
     // no A edges found, so can't determine if L edges are covered or not
-    if (startLoc == Location.NULL) return;
+    if (startLoc == Location.NONE) return;
 
     /**
      * move around ring, keeping track of the current location
diff --git a/src/com/vividsolutions/jts/geomgraph/Edge.java b/src/com/vividsolutions/jts/geomgraph/Edge.java
index 1283137..86f9d00 100644
--- a/src/com/vividsolutions/jts/geomgraph/Edge.java
+++ b/src/com/vividsolutions/jts/geomgraph/Edge.java
@@ -45,7 +45,7 @@ import com.vividsolutions.jts.geomgraph.index.*;
 
 
 /**
- * @version 1.6
+ * @version 1.7
  */
 public class Edge
   extends GraphComponent
diff --git a/src/com/vividsolutions/jts/geomgraph/EdgeEnd.java b/src/com/vividsolutions/jts/geomgraph/EdgeEnd.java
index 37c444e..fd386cb 100644
--- a/src/com/vividsolutions/jts/geomgraph/EdgeEnd.java
+++ b/src/com/vividsolutions/jts/geomgraph/EdgeEnd.java
@@ -50,14 +50,11 @@ import com.vividsolutions.jts.geomgraph.Edge;
  * EdgeEnds are comparable under the ordering
  * "a has a greater angle with the x-axis than b".
  * This ordering is used to sort EdgeEnds around a node.
- * @version 1.6
+ * @version 1.7
  */
 public class EdgeEnd
   implements Comparable
 {
-
-//  protected static final CGAlgorithms cga = new RobustCGAlgorithms();
-
   protected Edge edge;  // the parent edge of this edge end
   protected Label label;
 
diff --git a/src/com/vividsolutions/jts/geomgraph/EdgeEndStar.java b/src/com/vividsolutions/jts/geomgraph/EdgeEndStar.java
index 0be6378..e639ceb 100644
--- a/src/com/vividsolutions/jts/geomgraph/EdgeEndStar.java
+++ b/src/com/vividsolutions/jts/geomgraph/EdgeEndStar.java
@@ -46,7 +46,7 @@ import com.vividsolutions.jts.util.*;
  * They are maintained in CCW order (starting with the positive x-axis) around the node
  * for efficient lookup and topology building.
  *
- * @version 1.6
+ * @version 1.7
  */
 abstract public class EdgeEndStar
 {
@@ -62,7 +62,7 @@ abstract public class EdgeEndStar
   /**
    * The location of the point for this star in Geometry i Areas
    */
-  private int[] ptInAreaLocation = { Location.NULL, Location.NULL };
+  private int[] ptInAreaLocation = { Location.NONE, Location.NONE };
 
   public EdgeEndStar()
   {
@@ -184,7 +184,7 @@ abstract public class EdgeEndStar
 //Debug.println(e);
       for (int geomi = 0; geomi < 2; geomi++) {
         if (label.isAnyNull(geomi)) {
-          int loc = Location.NULL;
+          int loc = Location.NONE;
           if (hasDimensionalCollapseEdge[geomi]) {
             loc = Location.EXTERIOR;
           }
@@ -212,7 +212,7 @@ abstract public class EdgeEndStar
   int getLocation(int geomIndex, Coordinate p, GeometryGraph[] geom)
   {
     // compute location only on demand
-    if (ptInAreaLocation[geomIndex] == Location.NULL) {
+    if (ptInAreaLocation[geomIndex] == Location.NONE) {
       ptInAreaLocation[geomIndex] = SimplePointInAreaLocator.locate(p, geom[geomIndex].getGeometry());
     }
     return ptInAreaLocation[geomIndex];
@@ -236,7 +236,7 @@ abstract public class EdgeEndStar
     int lastEdgeIndex = edges.size() - 1;
     Label startLabel = ((EdgeEnd) edges.get(lastEdgeIndex)).getLabel();
     int startLoc = startLabel.getLocation(geomIndex, Position.LEFT);
-    Assert.isTrue(startLoc != Location.NULL, "Found unlabelled area edge");
+    Assert.isTrue(startLoc != Location.NONE, "Found unlabelled area edge");
 
     int currLoc = startLoc;
     for (Iterator it = iterator(); it.hasNext(); ) {
@@ -266,24 +266,24 @@ abstract public class EdgeEndStar
   {
     // Since edges are stored in CCW order around the node,
     // As we move around the ring we move from the right to the left side of the edge
-    int startLoc = Location.NULL ;
+    int startLoc = Location.NONE ;
     // initialize loc to location of last L side (if any)
 //System.out.println("finding start location");
     for (Iterator it = iterator(); it.hasNext(); ) {
       EdgeEnd e = (EdgeEnd) it.next();
       Label label = e.getLabel();
-      if (label.isArea(geomIndex) && label.getLocation(geomIndex, Position.LEFT) != Location.NULL)
+      if (label.isArea(geomIndex) && label.getLocation(geomIndex, Position.LEFT) != Location.NONE)
         startLoc = label.getLocation(geomIndex, Position.LEFT);
     }
     // no labelled sides found, so no labels to propagate
-    if (startLoc == Location.NULL) return;
+    if (startLoc == Location.NONE) return;
 
     int currLoc = startLoc;
     for (Iterator it = iterator(); it.hasNext(); ) {
       EdgeEnd e = (EdgeEnd) it.next();
       Label label = e.getLabel();
       // set null ON values to be in current location
-      if (label.getLocation(geomIndex, Position.ON) == Location.NULL)
+      if (label.getLocation(geomIndex, Position.ON) == Location.NONE)
           label.setLocation(geomIndex, Position.ON, currLoc);
       // set side labels (if any)
      // if (label.isArea()) {   //ORIGINAL
@@ -291,11 +291,11 @@ abstract public class EdgeEndStar
         int leftLoc   = label.getLocation(geomIndex, Position.LEFT);
         int rightLoc  = label.getLocation(geomIndex, Position.RIGHT);
         // if there is a right location, that is the next location to propagate
-        if (rightLoc != Location.NULL) {
+        if (rightLoc != Location.NONE) {
 //Debug.print(rightLoc != currLoc, this);
           if (rightLoc != currLoc)
             throw new TopologyException("side location conflict", e.getCoordinate());
-          if (leftLoc == Location.NULL) {
+          if (leftLoc == Location.NONE) {
             Assert.shouldNeverReachHere("found single null side (at " + e.getCoordinate() + ")");
           }
           currLoc = leftLoc;
@@ -307,7 +307,7 @@ abstract public class EdgeEndStar
            *  the other geometry (which is determined by the current location).
            *  Assign both sides to be the current location.
            */
-          Assert.isTrue(label.getLocation(geomIndex, Position.LEFT) == Location.NULL, "found single null side");
+          Assert.isTrue(label.getLocation(geomIndex, Position.LEFT) == Location.NONE, "found single null side");
           label.setLocation(geomIndex, Position.RIGHT, currLoc);
           label.setLocation(geomIndex, Position.LEFT, currLoc);
         }
diff --git a/src/com/vividsolutions/jts/geomgraph/EdgeIntersection.java b/src/com/vividsolutions/jts/geomgraph/EdgeIntersection.java
index 0d50883..4dca1e0 100644
--- a/src/com/vividsolutions/jts/geomgraph/EdgeIntersection.java
+++ b/src/com/vividsolutions/jts/geomgraph/EdgeIntersection.java
@@ -39,16 +39,14 @@ import java.io.PrintStream;
 import com.vividsolutions.jts.geom.Coordinate;
 
 /**
- * An EdgeIntersection represents a point on an
+ * Represents a point on an
  * edge which intersects with another edge.
- * <br>
+ * <p>
  * The intersection may either be a single point, or a line segment
  * (in which case this point is the start of the line segment)
- * The label attached to this intersection point applies to
- * the edge from this point forwards, until the next
- * intersection or the end of the edge.
  * The intersection point must be precise.
- * @version 1.6
+ *
+ * @version 1.7
  */
 public class EdgeIntersection
     implements Comparable
@@ -57,14 +55,11 @@ public class EdgeIntersection
   public Coordinate coord;   // the point of intersection
   public int segmentIndex;   // the index of the containing line segment in the parent edge
   public double dist;        // the edge distance of this point along the containing line segment
-  //Label label;
 
   public EdgeIntersection(Coordinate coord, int segmentIndex, double dist) {
-    //this.edge = edge;
     this.coord = new Coordinate(coord);
     this.segmentIndex = segmentIndex;
     this.dist = dist;
-    //label = new Label();
   }
 
   public int compareTo(Object obj)
diff --git a/src/com/vividsolutions/jts/geomgraph/EdgeIntersectionList.java b/src/com/vividsolutions/jts/geomgraph/EdgeIntersectionList.java
index 1791abc..c4820d5 100644
--- a/src/com/vividsolutions/jts/geomgraph/EdgeIntersectionList.java
+++ b/src/com/vividsolutions/jts/geomgraph/EdgeIntersectionList.java
@@ -1,6 +1,3 @@
-
-
-
 /*
  * The JTS Topology Suite is a collection of Java classes that
  * implement the fundamental operations required to validate a given
@@ -35,19 +32,20 @@
  */
 package com.vividsolutions.jts.geomgraph;
 
-import java.io.PrintStream;
+import com.vividsolutions.jts.geom.*;
+import java.io.*;
 import java.util.*;
-import com.vividsolutions.jts.geom.Coordinate;
-import com.vividsolutions.jts.util.Debug;
 
 /**
- * A list of edge intersections along an Edge
- * @version 1.6
+ * A list of edge intersections along an {@link Edge}.
+ * Implements splitting an edge with intersections
+ * into multiple resultant edges.
+ *
+ * @version 1.7
  */
 public class EdgeIntersectionList
 {
-  // a List of EdgeIntersections
-  //List list = new ArrayList();    // more efficient to use a LinkedList, but ArrayList is easier for debugging
+  // a Map <EdgeIntersection, EdgeIntersection>
   private Map nodeMap = new TreeMap();
   Edge edge;  // the parent edge
 
@@ -64,7 +62,6 @@ public class EdgeIntersectionList
   public EdgeIntersection add(Coordinate intPt, int segmentIndex, double dist)
   {
     EdgeIntersection eiNew = new EdgeIntersection(intPt, segmentIndex, dist);
-    Object obj = nodeMap.get(eiNew);
     EdgeIntersection ei = (EdgeIntersection) nodeMap.get(eiNew);
     if (ei != null) {
       return ei;
@@ -72,66 +69,20 @@ public class EdgeIntersectionList
     nodeMap.put(eiNew, eiNew);
     return eiNew;
   }
-  /*
-  public EdgeIntersection add(Coordinate intPt, int segmentIndex, double dist)
-  {
-//Debug.println("adding edgeInt " + intPt + " " + segmentIndex + " " + dist);
-    ListIterator insertIt = list.listIterator();
-    boolean isInList = findInsertionPoint(segmentIndex, dist, insertIt);
-    EdgeIntersection ei;
-    if (! isInList) {
-      ei = new EdgeIntersection(intPt, segmentIndex, dist);
-      insertIt.add(ei);
-    }
-    else
-      ei = (EdgeIntersection) insertIt.next();
-    return ei;
-  }
-  */
+
   /**
-   * returns an iterator of EdgeIntersections
+   * Returns an iterator of {@link EdgeIntersection}s
+   *
+   * @return an Iterator of EdgeIntersections
    */
   public Iterator iterator() { return nodeMap.values().iterator(); }
-/*
-  public boolean isEmpty()
-  {
-    Iterator it = list.iterator();
-    return ! it.hasNext();
-  }
-  */
+
   /**
-   * This routine searches the list for the insertion point for the given intersection
-   * (which must be in normalized form).
-   * The intersection point may already be in the list - in this case, the intersection
-   * is not inserted.
-   * If the intersection is new, it is inserted into the list.
-   * The insertIt iterator is left pointing at the correct place
-   * to insert the intersection, if the intersection was not found.
+   * Tests if the given point is an edge intersection
    *
-   * @return true if this intersection is already in the list
+   * @param pt the point to test
+   * @return true if the point is an intersection
    */
-  /*
-  boolean findInsertionPoint(int segmentIndex, double dist, ListIterator insertIt)
-  {
-    // The insertIt position trails the findIt position by one
-    ListIterator findIt = list.listIterator();
-    boolean found = false;
-    while (findIt.hasNext()) {
-      EdgeIntersection ei = (EdgeIntersection) findIt.next();
-      int compare = ei.compare(segmentIndex, dist);
-
-      // intersection found - insertIt.next() will retrieve it
-      if (compare == 0) return true;
-
-      // this ei is past the intersection location, so intersection was not found
-      if (compare > 0) return false;
-
-      // this ei was before the intersection point, so move to next
-      insertIt.next();
-    }
-    return false;
-  }
-  */
   public boolean isIntersection(Coordinate pt)
   {
     for (Iterator it = iterator(); it.hasNext(); ) {
@@ -157,6 +108,8 @@ public class EdgeIntersectionList
    * list split the parent edge into.
    * Adds the edges to the input list (this is so a single list
    * can be used to accumulate all split edges for a Geometry).
+   *
+   * @param edgeList a list of EdgeIntersections
    */
   public void addSplitEdges(List edgeList)
   {
diff --git a/src/com/vividsolutions/jts/geomgraph/EdgeList.java b/src/com/vividsolutions/jts/geomgraph/EdgeList.java
index dbdb3b9..121b688 100644
--- a/src/com/vividsolutions/jts/geomgraph/EdgeList.java
+++ b/src/com/vividsolutions/jts/geomgraph/EdgeList.java
@@ -44,7 +44,7 @@ import com.vividsolutions.jts.index.quadtree.Quadtree;
 /**
  * A EdgeList is a list of Edges.  It supports locating edges
  * that are pointwise equals to a target edge.
- * @version 1.6
+ * @version 1.7
  */
 public class EdgeList
 {
diff --git a/src/com/vividsolutions/jts/geomgraph/EdgeNodingValidator.java b/src/com/vividsolutions/jts/geomgraph/EdgeNodingValidator.java
index a7ae4f5..683e9a2 100644
--- a/src/com/vividsolutions/jts/geomgraph/EdgeNodingValidator.java
+++ b/src/com/vividsolutions/jts/geomgraph/EdgeNodingValidator.java
@@ -42,7 +42,7 @@ import com.vividsolutions.jts.noding.*;
  * Validates that a collection of SegmentStrings is correctly noded.
  * Throws an appropriate exception if an noding error is found.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class EdgeNodingValidator {
 
diff --git a/src/com/vividsolutions/jts/geomgraph/EdgeRing.java b/src/com/vividsolutions/jts/geomgraph/EdgeRing.java
index f20d6e9..dddecdf 100644
--- a/src/com/vividsolutions/jts/geomgraph/EdgeRing.java
+++ b/src/com/vividsolutions/jts/geomgraph/EdgeRing.java
@@ -45,7 +45,7 @@ import com.vividsolutions.jts.util.Assert;
 
 
 /**
- * @version 1.6
+ * @version 1.7
  */
 public abstract class EdgeRing {
 
@@ -53,7 +53,7 @@ public abstract class EdgeRing {
   private int maxNodeDegree = -1;
   private List edges = new ArrayList(); // the DirectedEdges making up this EdgeRing
   private List pts = new ArrayList();
-  private Label label = new Label(Location.NULL); // label stores the locations of each geometry on the face surrounded by this ring
+  private Label label = new Label(Location.NONE); // label stores the locations of each geometry on the face surrounded by this ring
   private LinearRing ring;  // the ring created for this EdgeRing
   private boolean isHole;
   private EdgeRing shell;   // if non-null, the ring is a hole and this EdgeRing is its containing shell
@@ -195,9 +195,9 @@ public abstract class EdgeRing {
   {
     int loc = deLabel.getLocation(geomIndex, Position.RIGHT);
     // no information to be had from this label
-    if (loc == Location.NULL) return;
+    if (loc == Location.NONE) return;
     // if there is no current RHS value, set it
-    if (label.getLocation(geomIndex) == Location.NULL) {
+    if (label.getLocation(geomIndex) == Location.NONE) {
       label.setLocation(geomIndex, loc);
       return;
     }
diff --git a/src/com/vividsolutions/jts/geomgraph/GeometryGraph.java b/src/com/vividsolutions/jts/geomgraph/GeometryGraph.java
index 285e8d5..65e1e0d 100644
--- a/src/com/vividsolutions/jts/geomgraph/GeometryGraph.java
+++ b/src/com/vividsolutions/jts/geomgraph/GeometryGraph.java
@@ -43,7 +43,7 @@ import com.vividsolutions.jts.util.*;
 
 /**
  * A GeometryGraph is a graph that models a given Geometry
- * @version 1.6
+ * @version 1.7
  */
 public class GeometryGraph
   extends PlanarGraph
@@ -376,7 +376,7 @@ Debug.print(e.getEdgeIntersectionList());
     // the new point to insert is on a boundary
     int boundaryCount = 1;
     // determine the current location for the point (if any)
-    int loc = Location.NULL;
+    int loc = Location.NONE;
     if (lbl != null) loc = lbl.getLocation(argIndex, Position.ON);
     if (loc == Location.BOUNDARY) boundaryCount++;
 
diff --git a/src/com/vividsolutions/jts/geomgraph/GraphComponent.java b/src/com/vividsolutions/jts/geomgraph/GraphComponent.java
index bd0a026..4ebcf29 100644
--- a/src/com/vividsolutions/jts/geomgraph/GraphComponent.java
+++ b/src/com/vividsolutions/jts/geomgraph/GraphComponent.java
@@ -45,7 +45,7 @@ import com.vividsolutions.jts.util.Assert;
  * A GraphComponent is the parent class for the objects'
  * that form a graph.  Each GraphComponent can carry a
  * Label.
- * @version 1.6
+ * @version 1.7
  */
 abstract public class GraphComponent {
 
diff --git a/src/com/vividsolutions/jts/geomgraph/Label.java b/src/com/vividsolutions/jts/geomgraph/Label.java
index 7b59f23..bf3e321 100644
--- a/src/com/vividsolutions/jts/geomgraph/Label.java
+++ b/src/com/vividsolutions/jts/geomgraph/Label.java
@@ -58,7 +58,7 @@ import com.vividsolutions.jts.geom.Location;
  * <P>
  * It is up to the client code to associate the 0 and 1 <code>TopologyLocation</code>s
  * with specific geometries.
- * @version 1.6
+ * @version 1.7
  *
  */
 public class Label {
@@ -66,7 +66,7 @@ public class Label {
   // converts a Label to a Line label (that is, one with no side Locations)
   public static Label toLineLabel(Label label)
   {
-    Label lineLabel = new Label(Location.NULL);
+    Label lineLabel = new Label(Location.NONE);
     for (int i = 0; i < 2; i++) {
       lineLabel.setLocation(i, label.getLocation(i));
     }
@@ -90,8 +90,8 @@ public class Label {
    */
   public Label(int geomIndex, int onLoc)
   {
-    elt[0] = new TopologyLocation(Location.NULL);
-    elt[1] = new TopologyLocation(Location.NULL);
+    elt[0] = new TopologyLocation(Location.NONE);
+    elt[1] = new TopologyLocation(Location.NONE);
     elt[geomIndex].setLocation(onLoc);
   }
   /**
@@ -109,22 +109,11 @@ public class Label {
    */
   public Label(int geomIndex, int onLoc, int leftLoc, int rightLoc)
   {
-    elt[0] = new TopologyLocation(Location.NULL, Location.NULL, Location.NULL);
-    elt[1] = new TopologyLocation(Location.NULL, Location.NULL, Location.NULL);
+    elt[0] = new TopologyLocation(Location.NONE, Location.NONE, Location.NONE);
+    elt[1] = new TopologyLocation(Location.NONE, Location.NONE, Location.NONE);
     elt[geomIndex].setLocations(onLoc, leftLoc, rightLoc);
   }
   /**
-   * Construct a Label with the same values as the argument for the
-   * given Geometry index.
-   */
-  public Label(int geomIndex, TopologyLocation gl)
-  {
-
-    elt[0] = new TopologyLocation(gl.getLocations());
-    elt[1] = new TopologyLocation(gl.getLocations());
-    elt[geomIndex].setLocations(gl);
-  }
-  /**
    * Construct a Label with the same values as the argument Label.
    */
   public Label(Label lbl)
@@ -177,11 +166,6 @@ public class Label {
       }
     }
   }
-  private void setGeometryLocation(int geomIndex, TopologyLocation tl)
-  {
-    if (tl == null) return;
-    elt[geomIndex].setLocations(tl);
-  }
   public int getGeometryCount()
   {
     int count = 0;
diff --git a/src/com/vividsolutions/jts/geomgraph/Node.java b/src/com/vividsolutions/jts/geomgraph/Node.java
index 2e7f296..b754b33 100644
--- a/src/com/vividsolutions/jts/geomgraph/Node.java
+++ b/src/com/vividsolutions/jts/geomgraph/Node.java
@@ -46,7 +46,7 @@ import com.vividsolutions.jts.util.*;
 
 
 /**
- * @version 1.6
+ * @version 1.7
  */
 public class Node
   extends GraphComponent
@@ -58,12 +58,30 @@ public class Node
   {
     this.coord = coord;
     this.edges = edges;
-    label = new Label(0, Location.NULL);
+    label = new Label(0, Location.NONE);
   }
 
   public Coordinate getCoordinate() { return coord; }
   public EdgeEndStar getEdges() { return edges; }
 
+  /**
+   * Tests whether any incident edge is flagged as
+   * being in the result.
+   * This test can be used to determine if the node is in the result,
+   * since if any incident edge is in the result, the node must be in the result as well.
+   *
+   * @return <code>true</code> if any indicident edge in the in the result
+   */
+  public boolean isIncidentEdgeInResult()
+  {
+    for (Iterator it = getEdges().getEdges().iterator(); it.hasNext(); ) {
+      DirectedEdge de = (DirectedEdge) it.next();
+      if (de.getEdge().isInResult())
+        return true;
+    }
+    return false;
+  }
+
   public boolean isIsolated()
   {
     return (label.getGeometryCount() == 1);
@@ -99,7 +117,7 @@ public class Node
     for (int i = 0; i < 2; i++) {
       int loc = computeMergedLocation(label2, i);
       int thisLoc = label.getLocation(i);
-      if (thisLoc == Location.NULL) label.setLocation(i, loc);
+      if (thisLoc == Location.NONE) label.setLocation(i, loc);
     }
   }
 
@@ -118,7 +136,7 @@ public class Node
   public void setLabelBoundary(int argIndex)
   {
     // determine the current location for the point (if any)
-    int loc = Location.NULL;
+    int loc = Location.NONE;
     if (label != null)
       loc = label.getLocation(argIndex);
     // flip the loc
@@ -140,7 +158,7 @@ public class Node
    */
   int computeMergedLocation(Label label2, int eltIndex)
   {
-    int loc = Location.NULL;
+    int loc = Location.NONE;
     loc = label.getLocation(eltIndex);
     if (! label2.isNull(eltIndex)) {
         int nLoc = label2.getLocation(eltIndex);
diff --git a/src/com/vividsolutions/jts/geomgraph/NodeFactory.java b/src/com/vividsolutions/jts/geomgraph/NodeFactory.java
index b717b2a..d06f315 100644
--- a/src/com/vividsolutions/jts/geomgraph/NodeFactory.java
+++ b/src/com/vividsolutions/jts/geomgraph/NodeFactory.java
@@ -37,7 +37,7 @@ import com.vividsolutions.jts.geom.Coordinate;
 
 
 /**
- * @version 1.6
+ * @version 1.7
  */
 public class NodeFactory {
 /**
diff --git a/src/com/vividsolutions/jts/geomgraph/NodeMap.java b/src/com/vividsolutions/jts/geomgraph/NodeMap.java
index c1f87a4..60859d9 100644
--- a/src/com/vividsolutions/jts/geomgraph/NodeMap.java
+++ b/src/com/vividsolutions/jts/geomgraph/NodeMap.java
@@ -44,7 +44,7 @@ import com.vividsolutions.jts.geomgraph.Node;
 
 /**
  * A map of nodes, indexed by the coordinate of the node
- * @version 1.6
+ * @version 1.7
  */
 public class NodeMap
 
diff --git a/src/com/vividsolutions/jts/geomgraph/PlanarGraph.java b/src/com/vividsolutions/jts/geomgraph/PlanarGraph.java
index b65d5c3..06d82ec 100644
--- a/src/com/vividsolutions/jts/geomgraph/PlanarGraph.java
+++ b/src/com/vividsolutions/jts/geomgraph/PlanarGraph.java
@@ -36,7 +36,7 @@
 package com.vividsolutions.jts.geomgraph;
 
 /**
- * @version 1.6
+ * @version 1.7
  */
 import java.io.PrintStream;
 import java.util.*;
@@ -61,7 +61,7 @@ import com.vividsolutions.jts.geom.*;
  *   <LI>Computing the intersections between the edges and nodes of two different graphs
  * </UL>
  *
- * @version 1.6
+ * @version 1.7
  */
 public class PlanarGraph {
 
diff --git a/src/com/vividsolutions/jts/geomgraph/Position.java b/src/com/vividsolutions/jts/geomgraph/Position.java
index 655566b..0b2fde0 100644
--- a/src/com/vividsolutions/jts/geomgraph/Position.java
+++ b/src/com/vividsolutions/jts/geomgraph/Position.java
@@ -38,7 +38,7 @@ package com.vividsolutions.jts.geomgraph;
 /**
  * A Position indicates the position of a Location relative to a graph component
  * (Node, Edge, or Area).
- * @version 1.6
+ * @version 1.7
  */
 public class Position {
 
diff --git a/src/com/vividsolutions/jts/geomgraph/Quadrant.java b/src/com/vividsolutions/jts/geomgraph/Quadrant.java
index 8593c4c..dc95b2c 100644
--- a/src/com/vividsolutions/jts/geomgraph/Quadrant.java
+++ b/src/com/vividsolutions/jts/geomgraph/Quadrant.java
@@ -36,7 +36,7 @@
 package com.vividsolutions.jts.geomgraph;
 
 /**
- * @version 1.6
+ * @version 1.7
  */
 import com.vividsolutions.jts.geom.Coordinate;
 
@@ -48,7 +48,7 @@ import com.vividsolutions.jts.geom.Coordinate;
  * 2 | 3
  * <pre>
  *
- * @version 1.6
+ * @version 1.7
  */
 public class Quadrant {
   /**
diff --git a/src/com/vividsolutions/jts/geomgraph/TopologyLocation.java b/src/com/vividsolutions/jts/geomgraph/TopologyLocation.java
index 89ff3d3..9360aec 100644
--- a/src/com/vividsolutions/jts/geomgraph/TopologyLocation.java
+++ b/src/com/vividsolutions/jts/geomgraph/TopologyLocation.java
@@ -55,11 +55,11 @@ import com.vividsolutions.jts.geom.Location;
   * topological relationship attribute, ON.
   * <p>
   * The possible values of a topological location are
-  * {Location.NULL, Location.EXTERIOR, Location.BOUNDARY, Location.INTERIOR}
+  * {Location.NONE, Location.EXTERIOR, Location.BOUNDARY, Location.INTERIOR}
   * <p>
   * The labelling is stored in an array location[j] where
   * where j has the values ON, LEFT, RIGHT
-  * @version 1.6
+  * @version 1.7
  */
 public class TopologyLocation {
 
@@ -72,7 +72,7 @@ public class TopologyLocation {
   /**
    * Constructs a TopologyLocation specifying how points on, to the left of, and to the
    * right of some GraphComponent relate to some Geometry. Possible values for the
-   * parameters are Location.NULL, Location.EXTERIOR, Location.BOUNDARY, 
+   * parameters are Location.NULL, Location.EXTERIOR, Location.BOUNDARY,
    * and Location.INTERIOR.
    * @see Location
    */
@@ -98,12 +98,12 @@ public class TopologyLocation {
   private void init(int size)
   {
     location = new int[size];
-    setAllLocations(Location.NULL);
+    setAllLocations(Location.NONE);
   }
   public int get(int posIndex)
   {
     if (posIndex < location.length) return location[posIndex];
-    return Location.NULL;
+    return Location.NONE;
   }
   /**
    * @return true if all locations are NULL
@@ -111,7 +111,7 @@ public class TopologyLocation {
   public boolean isNull()
   {
     for (int i = 0; i < location.length; i++) {
-      if (location[i] != Location.NULL) return false;
+      if (location[i] != Location.NONE) return false;
     }
     return true;
   }
@@ -121,7 +121,7 @@ public class TopologyLocation {
   public boolean isAnyNull()
   {
     for (int i = 0; i < location.length; i++) {
-      if (location[i] == Location.NULL) return true;
+      if (location[i] == Location.NONE) return true;
     }
     return false;
   }
@@ -150,7 +150,7 @@ public class TopologyLocation {
   public void setAllLocationsIfNull(int locValue)
   {
     for (int i = 0; i < location.length; i++) {
-      if (location[i] == Location.NULL) location[i]     = locValue;
+      if (location[i] == Location.NONE) location[i]     = locValue;
     }
   }
 
@@ -168,11 +168,6 @@ public class TopologyLocation {
       location[Position.LEFT] = left;
       location[Position.RIGHT] = right;
   }
-  public void setLocations(TopologyLocation gl) {
-    for (int i = 0; i < gl.location.length; i++) {
-      location[i] = gl.location[i];
-    }
-  }
   public boolean allPositionsEqual(int loc)
   {
     for (int i = 0; i < location.length; i++) {
@@ -191,12 +186,12 @@ public class TopologyLocation {
     if (gl.location.length > location.length) {
       int [] newLoc = new int[3];
       newLoc[Position.ON] = location[Position.ON];
-      newLoc[Position.LEFT] = Location.NULL;
-      newLoc[Position.RIGHT] = Location.NULL;
+      newLoc[Position.LEFT] = Location.NONE;
+      newLoc[Position.RIGHT] = Location.NONE;
       location = newLoc;
     }
     for (int i = 0; i < location.length; i++) {
-      if (location[i] == Location.NULL && i < gl.location.length)
+      if (location[i] == Location.NONE && i < gl.location.length)
         location[i] = gl.location[i];
     }
   }
diff --git a/src/com/vividsolutions/jts/geomgraph/index/EdgeSetIntersector.java b/src/com/vividsolutions/jts/geomgraph/index/EdgeSetIntersector.java
index ffcbe32..ca7f4e5 100644
--- a/src/com/vividsolutions/jts/geomgraph/index/EdgeSetIntersector.java
+++ b/src/com/vividsolutions/jts/geomgraph/index/EdgeSetIntersector.java
@@ -36,7 +36,7 @@
 package com.vividsolutions.jts.geomgraph.index;
 
 /**
- * @version 1.6
+ * @version 1.7
  */
 import java.util.*;
 import com.vividsolutions.jts.geomgraph.*;
@@ -52,7 +52,7 @@ import com.vividsolutions.jts.geomgraph.*;
  * It uses a {@link SegmentIntersector} to compute the intersections between
  * segments and to record statistics about what kinds of intersections were found.
  *
- * @version 1.6
+ * @version 1.7
  */
 public abstract class EdgeSetIntersector {
 
diff --git a/src/com/vividsolutions/jts/geomgraph/index/MonotoneChain.java b/src/com/vividsolutions/jts/geomgraph/index/MonotoneChain.java
index b7ca2e5..07aef8f 100644
--- a/src/com/vividsolutions/jts/geomgraph/index/MonotoneChain.java
+++ b/src/com/vividsolutions/jts/geomgraph/index/MonotoneChain.java
@@ -36,7 +36,7 @@
 package com.vividsolutions.jts.geomgraph.index;
 
 /**
- * @version 1.6
+ * @version 1.7
  */
 public class MonotoneChain {
 
diff --git a/src/com/vividsolutions/jts/geomgraph/index/MonotoneChainEdge.java b/src/com/vividsolutions/jts/geomgraph/index/MonotoneChainEdge.java
index 121a20c..522dbd2 100644
--- a/src/com/vividsolutions/jts/geomgraph/index/MonotoneChainEdge.java
+++ b/src/com/vividsolutions/jts/geomgraph/index/MonotoneChainEdge.java
@@ -57,7 +57,7 @@ import com.vividsolutions.jts.util.Debug;
  * binary search to be used to find the intersection points of two monotone chains.
  * For many types of real-world data, these properties eliminate a large number of
  * segment comparisons, producing substantial speed gains.
- * @version 1.6
+ * @version 1.7
  */
 public class MonotoneChainEdge {
 
diff --git a/src/com/vividsolutions/jts/geomgraph/index/MonotoneChainIndexer.java b/src/com/vividsolutions/jts/geomgraph/index/MonotoneChainIndexer.java
index 0153549..08b25e8 100644
--- a/src/com/vividsolutions/jts/geomgraph/index/MonotoneChainIndexer.java
+++ b/src/com/vividsolutions/jts/geomgraph/index/MonotoneChainIndexer.java
@@ -55,7 +55,7 @@ import com.vividsolutions.jts.geomgraph.Quadrant;
  * For many types of real-world data, these properties eliminate a large number of
  * segment comparisons, producing substantial speed gains.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class MonotoneChainIndexer {
 
diff --git a/src/com/vividsolutions/jts/geomgraph/index/SegmentIntersector.java b/src/com/vividsolutions/jts/geomgraph/index/SegmentIntersector.java
index 3247202..98a64a2 100644
--- a/src/com/vividsolutions/jts/geomgraph/index/SegmentIntersector.java
+++ b/src/com/vividsolutions/jts/geomgraph/index/SegmentIntersector.java
@@ -43,7 +43,7 @@ import com.vividsolutions.jts.util.Debug;
 
 
 /**
- * @version 1.6
+ * @version 1.7
  */
 public class SegmentIntersector {
 
diff --git a/src/com/vividsolutions/jts/geomgraph/index/SimpleEdgeSetIntersector.java b/src/com/vividsolutions/jts/geomgraph/index/SimpleEdgeSetIntersector.java
index ff452dd..f452151 100644
--- a/src/com/vividsolutions/jts/geomgraph/index/SimpleEdgeSetIntersector.java
+++ b/src/com/vividsolutions/jts/geomgraph/index/SimpleEdgeSetIntersector.java
@@ -44,7 +44,7 @@ import com.vividsolutions.jts.geomgraph.*;
  * using the straightforward method of
  * comparing all segments.
  * This algorithm is too slow for production use, but is useful for testing purposes.
- * @version 1.6
+ * @version 1.7
  */
 public class SimpleEdgeSetIntersector
   extends EdgeSetIntersector
diff --git a/src/com/vividsolutions/jts/geomgraph/index/SimpleMCSweepLineIntersector.java b/src/com/vividsolutions/jts/geomgraph/index/SimpleMCSweepLineIntersector.java
index bd110ec..f78315e 100644
--- a/src/com/vividsolutions/jts/geomgraph/index/SimpleMCSweepLineIntersector.java
+++ b/src/com/vividsolutions/jts/geomgraph/index/SimpleMCSweepLineIntersector.java
@@ -36,7 +36,7 @@
 package com.vividsolutions.jts.geomgraph.index;
 
 /**
- * @version 1.6
+ * @version 1.7
  */
 import java.util.*;
 import com.vividsolutions.jts.geomgraph.*;
@@ -49,7 +49,7 @@ import com.vividsolutions.jts.geomgraph.*;
  * The use of MonotoneChains as the items in the index
  * seems to offer an improvement in performance over a sweep-line alone.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class SimpleMCSweepLineIntersector
   extends EdgeSetIntersector
diff --git a/src/com/vividsolutions/jts/geomgraph/index/SimpleSweepLineIntersector.java b/src/com/vividsolutions/jts/geomgraph/index/SimpleSweepLineIntersector.java
index b41f5c8..a71c602 100644
--- a/src/com/vividsolutions/jts/geomgraph/index/SimpleSweepLineIntersector.java
+++ b/src/com/vividsolutions/jts/geomgraph/index/SimpleSweepLineIntersector.java
@@ -36,7 +36,7 @@
 package com.vividsolutions.jts.geomgraph.index;
 
 /**
- * @version 1.6
+ * @version 1.7
  */
 import java.util.*;
 import com.vividsolutions.jts.geom.Coordinate;
@@ -48,7 +48,7 @@ import com.vividsolutions.jts.geomgraph.*;
  * While still O(n^2) in the worst case, this algorithm
  * drastically improves the average-case time.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class SimpleSweepLineIntersector
   extends EdgeSetIntersector
diff --git a/src/com/vividsolutions/jts/geomgraph/index/SweepLineEvent.java b/src/com/vividsolutions/jts/geomgraph/index/SweepLineEvent.java
index 1072a3e..70e68c8 100644
--- a/src/com/vividsolutions/jts/geomgraph/index/SweepLineEvent.java
+++ b/src/com/vividsolutions/jts/geomgraph/index/SweepLineEvent.java
@@ -36,7 +36,7 @@
 package com.vividsolutions.jts.geomgraph.index;
 
 /**
- * @version 1.6
+ * @version 1.7
  */
 public class SweepLineEvent
   implements Comparable
diff --git a/src/com/vividsolutions/jts/geomgraph/index/SweepLineSegment.java b/src/com/vividsolutions/jts/geomgraph/index/SweepLineSegment.java
index 9bd2a88..7c32897 100644
--- a/src/com/vividsolutions/jts/geomgraph/index/SweepLineSegment.java
+++ b/src/com/vividsolutions/jts/geomgraph/index/SweepLineSegment.java
@@ -40,7 +40,7 @@ import com.vividsolutions.jts.geomgraph.*;
 
 
 /**
- * @version 1.6
+ * @version 1.7
  */
 public class SweepLineSegment {
 
diff --git a/src/com/vividsolutions/jts/index/ArrayListVisitor.java b/src/com/vividsolutions/jts/index/ArrayListVisitor.java
index 39065bc..0124baf 100644
--- a/src/com/vividsolutions/jts/index/ArrayListVisitor.java
+++ b/src/com/vividsolutions/jts/index/ArrayListVisitor.java
@@ -4,7 +4,7 @@ import java.util.*;
 import com.vividsolutions.jts.index.ItemVisitor;
 
 /**
- * @version 1.6
+ * @version 1.7
  */
 public class ArrayListVisitor
     implements ItemVisitor
@@ -21,4 +21,4 @@ public class ArrayListVisitor
 
   public ArrayList getItems() { return items; }
 
-}
+}
\ No newline at end of file
diff --git a/src/com/vividsolutions/jts/index/IndexVisitor.java b/src/com/vividsolutions/jts/index/IndexVisitor.java
deleted file mode 100644
index d7f7042..0000000
--- a/src/com/vividsolutions/jts/index/IndexVisitor.java
+++ /dev/null
@@ -1,11 +0,0 @@
-package com.vividsolutions.jts.index;
-
-/**
- * A visitor for nodes and items in an index.
- *
- * @version 1.6
- */
-
-public interface IndexVisitor {
-  void visitItem(Object item);
-}
diff --git a/src/com/vividsolutions/jts/index/ItemVisitor.java b/src/com/vividsolutions/jts/index/ItemVisitor.java
index 60a7b04..af81b0b 100644
--- a/src/com/vividsolutions/jts/index/ItemVisitor.java
+++ b/src/com/vividsolutions/jts/index/ItemVisitor.java
@@ -3,10 +3,10 @@ package com.vividsolutions.jts.index;
 /**
  * A visitor for items in an index.
  *
- * @version 1.6
+ * @version 1.7
  */
 
 public interface ItemVisitor
 {
   void visitItem(Object item);
-}
+}
\ No newline at end of file
diff --git a/src/com/vividsolutions/jts/index/SpatialIndex.java b/src/com/vividsolutions/jts/index/SpatialIndex.java
index a464a44..79d2a3f 100644
--- a/src/com/vividsolutions/jts/index/SpatialIndex.java
+++ b/src/com/vividsolutions/jts/index/SpatialIndex.java
@@ -37,15 +37,15 @@ import java.util.*;
 import com.vividsolutions.jts.geom.Envelope;
 
 /**
- * The basic insertion and query operations supported by classes
+ * The basic operations supported by classes
  * implementing spatial index algorithms.
  * <p>
- * A spatial index typically provides a primary filter for range rectangle queries. A
- * secondary filter is required to test for exact intersection. Of course, this
- * secondary filter may consist of other tests besides intersection, such as
- * testing other kinds of spatial relationships.
+ * A spatial index typically provides a primary filter for range rectangle queries.
+ * A secondary filter is required to test for exact intersection.
+ * The secondary filter may consist of other kinds of tests,
+ * such as testing other spatial relationships.
  *
- * @version 1.6
+ * @version 1.7
  */
 public interface SpatialIndex
 {
@@ -65,6 +65,17 @@ public interface SpatialIndex
   List query(Envelope searchEnv);
 
   /**
+   * Queries the index for all items whose extents intersect the given search {@link Envelope},
+   * and applies an {@link ItemVisitor} to them.
+   * Note that some kinds of indexes may also return objects which do not in fact
+   * intersect the query envelope.
+   *
+   * @param searchEnv the envelope to query for
+   * @param visitor a visitor object to apply to the items found
+   */
+  void query(Envelope searchEnv, ItemVisitor visitor);
+
+  /**
    * Removes a single item from the tree.
    *
    * @param itemEnv the Envelope of the item to remove
diff --git a/src/com/vividsolutions/jts/index/bintree/Bintree.java b/src/com/vividsolutions/jts/index/bintree/Bintree.java
index 10e007e..77f160d 100644
--- a/src/com/vividsolutions/jts/index/bintree/Bintree.java
+++ b/src/com/vividsolutions/jts/index/bintree/Bintree.java
@@ -52,7 +52,7 @@ import java.util.List;
  * This index is different to the Interval Tree of Edelsbrunner
  * or the Segment Tree of Bentley.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class Bintree
 {
diff --git a/src/com/vividsolutions/jts/index/bintree/Interval.java b/src/com/vividsolutions/jts/index/bintree/Interval.java
index 41fe202..a977ebc 100644
--- a/src/com/vividsolutions/jts/index/bintree/Interval.java
+++ b/src/com/vividsolutions/jts/index/bintree/Interval.java
@@ -36,7 +36,7 @@ package com.vividsolutions.jts.index.bintree;
 /**
  * Represents an (1-dimensional) closed interval on the Real number line.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class Interval {
 
diff --git a/src/com/vividsolutions/jts/index/bintree/Key.java b/src/com/vividsolutions/jts/index/bintree/Key.java
index 84299ad..f8e528f 100644
--- a/src/com/vividsolutions/jts/index/bintree/Key.java
+++ b/src/com/vividsolutions/jts/index/bintree/Key.java
@@ -42,7 +42,7 @@ import com.vividsolutions.jts.index.quadtree.DoubleBits;
  * It contains a lower-left point and a level number. The level number
  * is the power of two for the size of the node envelope
  *
- * @version 1.6
+ * @version 1.7
  */
 public class Key {
 
diff --git a/src/com/vividsolutions/jts/index/bintree/Node.java b/src/com/vividsolutions/jts/index/bintree/Node.java
index 1489b29..606166e 100644
--- a/src/com/vividsolutions/jts/index/bintree/Node.java
+++ b/src/com/vividsolutions/jts/index/bintree/Node.java
@@ -38,7 +38,7 @@ import com.vividsolutions.jts.util.Assert;
 /**
  * A node of a {@link Bintree}.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class Node
   extends NodeBase
diff --git a/src/com/vividsolutions/jts/index/bintree/NodeBase.java b/src/com/vividsolutions/jts/index/bintree/NodeBase.java
index d70f26a..ea0b3dc 100644
--- a/src/com/vividsolutions/jts/index/bintree/NodeBase.java
+++ b/src/com/vividsolutions/jts/index/bintree/NodeBase.java
@@ -41,7 +41,7 @@ import java.util.List;
 /**
  * The base class for nodes in a {@link Bintree}.
  *
- * @version 1.6
+ * @version 1.7
  */
 public abstract class NodeBase {
 
diff --git a/src/com/vividsolutions/jts/index/bintree/Root.java b/src/com/vividsolutions/jts/index/bintree/Root.java
index d9942be..88cbbbf 100644
--- a/src/com/vividsolutions/jts/index/bintree/Root.java
+++ b/src/com/vividsolutions/jts/index/bintree/Root.java
@@ -41,7 +41,7 @@ import com.vividsolutions.jts.util.Assert;
  * It is centred at the origin,
  * and does not have a defined extent.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class Root
   extends NodeBase
diff --git a/src/com/vividsolutions/jts/index/chain/MonotoneChain.java b/src/com/vividsolutions/jts/index/chain/MonotoneChain.java
index 5fe6566..5eccb51 100644
--- a/src/com/vividsolutions/jts/index/chain/MonotoneChain.java
+++ b/src/com/vividsolutions/jts/index/chain/MonotoneChain.java
@@ -75,7 +75,7 @@ import com.vividsolutions.jts.geom.*;
  * returned by the query.
  * However, it does mean that the queries are not thread-safe.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class MonotoneChain {
 
diff --git a/src/com/vividsolutions/jts/index/chain/MonotoneChainBuilder.java b/src/com/vividsolutions/jts/index/chain/MonotoneChainBuilder.java
index c003b4a..48bf5a5 100644
--- a/src/com/vividsolutions/jts/index/chain/MonotoneChainBuilder.java
+++ b/src/com/vividsolutions/jts/index/chain/MonotoneChainBuilder.java
@@ -42,7 +42,7 @@ import com.vividsolutions.jts.geomgraph.Quadrant;
  * A MonotoneChainBuilder implements functions to determine the monotone chains
  * in a sequence of points.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class MonotoneChainBuilder {
 
diff --git a/src/com/vividsolutions/jts/index/chain/MonotoneChainOverlapAction.java b/src/com/vividsolutions/jts/index/chain/MonotoneChainOverlapAction.java
index 9650317..15ab893 100644
--- a/src/com/vividsolutions/jts/index/chain/MonotoneChainOverlapAction.java
+++ b/src/com/vividsolutions/jts/index/chain/MonotoneChainOverlapAction.java
@@ -40,7 +40,7 @@ import com.vividsolutions.jts.geom.*;
  * The action for the internal iterator for performing
  * overlap queries on a MonotoneChain
  *
- * @version 1.6
+ * @version 1.7
  */
 public class MonotoneChainOverlapAction
 {
diff --git a/src/com/vividsolutions/jts/index/chain/MonotoneChainSelectAction.java b/src/com/vividsolutions/jts/index/chain/MonotoneChainSelectAction.java
index 2e8a80d..4c1b3e3 100644
--- a/src/com/vividsolutions/jts/index/chain/MonotoneChainSelectAction.java
+++ b/src/com/vividsolutions/jts/index/chain/MonotoneChainSelectAction.java
@@ -39,7 +39,7 @@ import com.vividsolutions.jts.geom.*;
  * The action for the internal iterator for performing
  * envelope select queries on a MonotoneChain
  *
- * @version 1.6
+ * @version 1.7
  */
 public class MonotoneChainSelectAction
 {
diff --git a/src/com/vividsolutions/jts/index/quadtree/DoubleBits.java b/src/com/vividsolutions/jts/index/quadtree/DoubleBits.java
index 6319141..4e2e9c8 100644
--- a/src/com/vividsolutions/jts/index/quadtree/DoubleBits.java
+++ b/src/com/vividsolutions/jts/index/quadtree/DoubleBits.java
@@ -43,7 +43,7 @@ package com.vividsolutions.jts.index.quadtree;
  * The algorithms and constants in this class
  * apply only to IEEE-754 double-precision floating point format.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class DoubleBits {
 
diff --git a/src/com/vividsolutions/jts/index/quadtree/IntervalSize.java b/src/com/vividsolutions/jts/index/quadtree/IntervalSize.java
index 6299a21..7dcf625 100644
--- a/src/com/vividsolutions/jts/index/quadtree/IntervalSize.java
+++ b/src/com/vividsolutions/jts/index/quadtree/IntervalSize.java
@@ -42,7 +42,7 @@ package com.vividsolutions.jts.index.quadtree;
  * computing a midpoint value which does not lie strictly between the
  * endpoints.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class IntervalSize {
 
diff --git a/src/com/vividsolutions/jts/index/quadtree/Key.java b/src/com/vividsolutions/jts/index/quadtree/Key.java
index a2ab714..2928f09 100644
--- a/src/com/vividsolutions/jts/index/quadtree/Key.java
+++ b/src/com/vividsolutions/jts/index/quadtree/Key.java
@@ -41,7 +41,7 @@ import com.vividsolutions.jts.geom.Envelope;
  * It contains a lower-left point and a level number. The level number
  * is the power of two for the size of the node envelope
  *
- * @version 1.6
+ * @version 1.7
  */
 public class Key {
 
diff --git a/src/com/vividsolutions/jts/index/quadtree/Node.java b/src/com/vividsolutions/jts/index/quadtree/Node.java
index ea9ac8c..742e24a 100644
--- a/src/com/vividsolutions/jts/index/quadtree/Node.java
+++ b/src/com/vividsolutions/jts/index/quadtree/Node.java
@@ -42,7 +42,7 @@ import com.vividsolutions.jts.util.Assert;
  * items which have a spatial extent corresponding to the node's position
  * in the quadtree.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class Node
   extends NodeBase
diff --git a/src/com/vividsolutions/jts/index/quadtree/NodeBase.java b/src/com/vividsolutions/jts/index/quadtree/NodeBase.java
index e2501e7..d65b9cd 100644
--- a/src/com/vividsolutions/jts/index/quadtree/NodeBase.java
+++ b/src/com/vividsolutions/jts/index/quadtree/NodeBase.java
@@ -42,7 +42,7 @@ import com.vividsolutions.jts.index.*;
 /**
  * The base class for nodes in a {@link Quadtree}.
  *
- * @version 1.6
+ * @version 1.7
  */
 public abstract class NodeBase {
 
diff --git a/src/com/vividsolutions/jts/index/quadtree/Quadtree.java b/src/com/vividsolutions/jts/index/quadtree/Quadtree.java
index 0e5c7a2..60958f0 100644
--- a/src/com/vividsolutions/jts/index/quadtree/Quadtree.java
+++ b/src/com/vividsolutions/jts/index/quadtree/Quadtree.java
@@ -59,7 +59,7 @@ import com.vividsolutions.jts.index.*;
  * This data structure is also known as an <i>MX-CIF quadtree</i>
  * following the usage of Samet and others.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class Quadtree
     implements SpatialIndex
diff --git a/src/com/vividsolutions/jts/index/quadtree/Root.java b/src/com/vividsolutions/jts/index/quadtree/Root.java
index 1e37a3a..c7d713b 100644
--- a/src/com/vividsolutions/jts/index/quadtree/Root.java
+++ b/src/com/vividsolutions/jts/index/quadtree/Root.java
@@ -41,7 +41,7 @@ import com.vividsolutions.jts.util.Assert;
  * QuadRoot is the root of a single Quadtree.  It is centred at the origin,
  * and does not have a defined extent.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class Root
   extends NodeBase
diff --git a/src/com/vividsolutions/jts/index/strtree/AbstractNode.java b/src/com/vividsolutions/jts/index/strtree/AbstractNode.java
index 6b71e49..87db062 100644
--- a/src/com/vividsolutions/jts/index/strtree/AbstractNode.java
+++ b/src/com/vividsolutions/jts/index/strtree/AbstractNode.java
@@ -41,7 +41,7 @@ import java.util.*;
  * (AbstractNodes) or real data (ItemBoundables). If this node contains real data
  * (rather than nodes), then we say that this node is a "leaf node".
  *
- * @version 1.6
+ * @version 1.7
  */
 public abstract class AbstractNode implements Boundable {
   private ArrayList childBoundables = new ArrayList();
diff --git a/src/com/vividsolutions/jts/index/strtree/AbstractSTRtree.java b/src/com/vividsolutions/jts/index/strtree/AbstractSTRtree.java
index c0adc99..aee5674 100644
--- a/src/com/vividsolutions/jts/index/strtree/AbstractSTRtree.java
+++ b/src/com/vividsolutions/jts/index/strtree/AbstractSTRtree.java
@@ -32,6 +32,8 @@
  *     www.vividsolutions.com
  */
 package com.vividsolutions.jts.index.strtree;
+
+import com.vividsolutions.jts.index.ItemVisitor;
 import com.vividsolutions.jts.util.*;
 import java.util.*;
 import java.util.List;
@@ -48,7 +50,7 @@ import java.util.List;
  * @see STRtree
  * @see SIRtree
  *
- * @version 1.6
+ * @version 1.7
  */
 public abstract class AbstractSTRtree {
 
@@ -64,7 +66,7 @@ public abstract class AbstractSTRtree {
      * @param bBounds the bounds of another spatial object
      * @return whether the two bounds intersect
      */
-    public boolean intersects(Object aBounds, Object bBounds);
+    boolean intersects(Object aBounds, Object bBounds);
   }
 
   protected AbstractNode root;
@@ -222,6 +224,19 @@ public abstract class AbstractSTRtree {
   }
 
   /**
+   *  Also builds the tree, if necessary.
+   */
+  protected void query(Object searchBounds, ItemVisitor visitor) {
+    if (!built) { build(); }
+    if (itemBoundables.isEmpty()) {
+      Assert.isTrue(root.getBounds() == null);
+    }
+    if (getIntersectsOp().intersects(root.getBounds(), searchBounds)) {
+      query(searchBounds, root, visitor);
+    }
+  }
+
+  /**
    * @return a test for intersection between two bounds, necessary because subclasses
    * of AbstractSTRtree have different implementations of bounds.
    * @see IntersectsOp
@@ -246,6 +261,24 @@ public abstract class AbstractSTRtree {
     }
   }
 
+  private void query(Object searchBounds, AbstractNode node, ItemVisitor visitor) {
+    for (Iterator i = node.getChildBoundables().iterator(); i.hasNext(); ) {
+      Boundable childBoundable = (Boundable) i.next();
+      if (!getIntersectsOp().intersects(childBoundable.getBounds(), searchBounds)) {
+        continue;
+      }
+      if (childBoundable instanceof AbstractNode) {
+        query(searchBounds, (AbstractNode) childBoundable, visitor);
+      }
+      else if (childBoundable instanceof ItemBoundable) {
+        visitor.visitItem(((ItemBoundable)childBoundable).getItem());
+      }
+      else {
+        Assert.shouldNeverReachHere();
+      }
+    }
+  }
+
   /**
    *  Also builds the tree, if necessary.
    */
diff --git a/src/com/vividsolutions/jts/index/strtree/Boundable.java b/src/com/vividsolutions/jts/index/strtree/Boundable.java
index 200cded..b5578e3 100644
--- a/src/com/vividsolutions/jts/index/strtree/Boundable.java
+++ b/src/com/vividsolutions/jts/index/strtree/Boundable.java
@@ -36,7 +36,7 @@ package com.vividsolutions.jts.index.strtree;
 /**
  * A spatial object in an AbstractSTRtree.
  *
- * @version 1.6
+ * @version 1.7
  */
 public interface Boundable {
   /**
@@ -48,5 +48,5 @@ public interface Boundable {
    * (for other subclasses of AbstractSTRtree)
    * @see AbstractSTRtree.IntersectsOp
    */
-  public Object getBounds();
+  Object getBounds();
 }
diff --git a/src/com/vividsolutions/jts/index/strtree/Interval.java b/src/com/vividsolutions/jts/index/strtree/Interval.java
index 165cf26..bc1ef2b 100644
--- a/src/com/vividsolutions/jts/index/strtree/Interval.java
+++ b/src/com/vividsolutions/jts/index/strtree/Interval.java
@@ -39,7 +39,7 @@ import com.vividsolutions.jts.util.*;
  * A contiguous portion of 1D-space. Used internally by SIRtree.
  * @see SIRtree
  *
- * @version 1.6
+ * @version 1.7
  */
 public class Interval {
 
diff --git a/src/com/vividsolutions/jts/index/strtree/ItemBoundable.java b/src/com/vividsolutions/jts/index/strtree/ItemBoundable.java
index fd9a75a..76fa1f6 100644
--- a/src/com/vividsolutions/jts/index/strtree/ItemBoundable.java
+++ b/src/com/vividsolutions/jts/index/strtree/ItemBoundable.java
@@ -37,7 +37,7 @@ package com.vividsolutions.jts.index.strtree;
  * Boundable wrapper for a non-Boundable spatial object. Used internally by
  * AbstractSTRtree.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class ItemBoundable implements Boundable {
   private Object bounds;
diff --git a/src/com/vividsolutions/jts/index/strtree/SIRtree.java b/src/com/vividsolutions/jts/index/strtree/SIRtree.java
index adb6a66..95f6d7d 100644
--- a/src/com/vividsolutions/jts/index/strtree/SIRtree.java
+++ b/src/com/vividsolutions/jts/index/strtree/SIRtree.java
@@ -44,7 +44,7 @@ import java.util.List;
  * Application To GIS. Morgan Kaufmann, San Francisco, 2002.
  * @see STRtree
  *
- * @version 1.6
+ * @version 1.7
  */
 public class SIRtree extends AbstractSTRtree {
 
diff --git a/src/com/vividsolutions/jts/index/strtree/STRtree.java b/src/com/vividsolutions/jts/index/strtree/STRtree.java
index 58c0be4..4180ca2 100644
--- a/src/com/vividsolutions/jts/index/strtree/STRtree.java
+++ b/src/com/vividsolutions/jts/index/strtree/STRtree.java
@@ -41,18 +41,19 @@ import com.vividsolutions.jts.index.*;
 
 /**
  *  A query-only R-tree created using the Sort-Tile-Recursive (STR) algorithm.
- *  For two-dimensional spatial data. <P>
- *
+ *  For two-dimensional spatial data.
+ * <P>
  *  The STR packed R-tree is simple to implement and maximizes space
  *  utilization; that is, as many leaves as possible are filled to capacity.
  *  Overlap between nodes is far less than in a basic R-tree. However, once the
  *  tree has been built (explicitly or on the first call to #query), items may
- *  not be added or removed. <P>
- *
- * Described in: P. Rigaux, Michel Scholl and Agnes Voisard. Spatial Databases With
- *  Application To GIS. Morgan Kaufmann, San Francisco, 2002.
+ *  not be added or removed.
+ * <P>
+ * Described in: P. Rigaux, Michel Scholl and Agnes Voisard.
+ * <i>Spatial Databases With Application To GIS</i>.
+ * Morgan Kaufmann, San Francisco, 2002.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class STRtree extends AbstractSTRtree implements SpatialIndex {
 
@@ -192,6 +193,15 @@ public class STRtree extends AbstractSTRtree implements SpatialIndex {
   }
 
   /**
+   * Returns items whose bounds intersect the given envelope.
+   */
+  public void query(Envelope searchEnv, ItemVisitor visitor) {
+    //Yes this method does something. It specifies that the bounds is an
+    //Envelope. super.query takes an Object, not an Envelope. [Jon Aquino 10/24/2003]
+    super.query(searchEnv, visitor);
+  }
+
+  /**
    * Removes a single item from the tree.
    *
    * @param itemEnv the Envelope of the item to remove
diff --git a/src/com/vividsolutions/jts/index/sweepline/SweepLineEvent.java b/src/com/vividsolutions/jts/index/sweepline/SweepLineEvent.java
index 2c9e6b9..d93b8fb 100644
--- a/src/com/vividsolutions/jts/index/sweepline/SweepLineEvent.java
+++ b/src/com/vividsolutions/jts/index/sweepline/SweepLineEvent.java
@@ -36,7 +36,7 @@
 package com.vividsolutions.jts.index.sweepline;
 
 /**
- * @version 1.6
+ * @version 1.7
  */
 public class SweepLineEvent
   implements Comparable
diff --git a/src/com/vividsolutions/jts/index/sweepline/SweepLineIndex.java b/src/com/vividsolutions/jts/index/sweepline/SweepLineIndex.java
index 06ff4f1..762fcd0 100644
--- a/src/com/vividsolutions/jts/index/sweepline/SweepLineIndex.java
+++ b/src/com/vividsolutions/jts/index/sweepline/SweepLineIndex.java
@@ -40,7 +40,7 @@ import java.util.*;
  * A sweepline implements a sorted index on a set of intervals.
  * It is used to compute all overlaps between the interval in the index.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class SweepLineIndex {
 
diff --git a/src/com/vividsolutions/jts/index/sweepline/SweepLineInterval.java b/src/com/vividsolutions/jts/index/sweepline/SweepLineInterval.java
index 65014ae..2fd9249 100644
--- a/src/com/vividsolutions/jts/index/sweepline/SweepLineInterval.java
+++ b/src/com/vividsolutions/jts/index/sweepline/SweepLineInterval.java
@@ -35,7 +35,7 @@
 package com.vividsolutions.jts.index.sweepline;
 
 /**
- * @version 1.6
+ * @version 1.7
  */
 public class SweepLineInterval {
 
diff --git a/src/com/vividsolutions/jts/index/sweepline/SweepLineOverlapAction.java b/src/com/vividsolutions/jts/index/sweepline/SweepLineOverlapAction.java
index dfd938b..5591eef 100644
--- a/src/com/vividsolutions/jts/index/sweepline/SweepLineOverlapAction.java
+++ b/src/com/vividsolutions/jts/index/sweepline/SweepLineOverlapAction.java
@@ -34,12 +34,13 @@
  */
 package com.vividsolutions.jts.index.sweepline;
 
-
-
 /**
- * @version 1.6
+ * An action taken when a {@link SweepLineIndex} detects that two
+ * {@link SweepLineInterval}s overlap
+ *
+ * @version 1.7
  */
 public interface SweepLineOverlapAction {
 
-  public void overlap(SweepLineInterval s0, SweepLineInterval s1);
+  void overlap(SweepLineInterval s0, SweepLineInterval s1);
 }
diff --git a/src/com/vividsolutions/jts/io/ParseException.java b/src/com/vividsolutions/jts/io/ByteArrayInStream.java
similarity index 63%
copy from src/com/vividsolutions/jts/io/ParseException.java
copy to src/com/vividsolutions/jts/io/ByteArrayInStream.java
index 609f8da..f34ba75 100644
--- a/src/com/vividsolutions/jts/io/ParseException.java
+++ b/src/com/vividsolutions/jts/io/ByteArrayInStream.java
@@ -1,5 +1,3 @@
-
-
 /*
  * The JTS Topology Suite is a collection of Java classes that
  * implement the fundamental operations required to validate a given
@@ -34,31 +32,34 @@
  */
 package com.vividsolutions.jts.io;
 
+import java.io.*;
+
 /**
- *  Thrown by a <code>WKTReader</code> when a parsing problem occurs.
- *
- *@version 1.6
+ * Allows an array of bytes to be used as an {@link InStream}.
+ * To optimize memory usage, instances can be reused
+ * with different byte arrays.
  */
-public class ParseException extends Exception {
+public class ByteArrayInStream
+	implements InStream
+{
+  private byte[] byteBuffer;
+  private ByteArrayInputStream bis;
 
-  /**
-   *  Creates a <code>ParseException</code> with the given detail message.
-   *
-   *@param  message  a description of this <code>ParseException</code>
-   */
-  public ParseException(String message) {
-    super(message);
+  public ByteArrayInStream(byte[] byteBuffer)
+  {
+    setBuffer(byteBuffer);
   }
 
-  /**
-   *  Creates a <code>ParseException</code> with <code>e</code>s detail message.
-   *
-   *@param  e  an exception that occurred while a <code>WKTReader</code> was
-   *      parsing a Well-known Text string
-   */
-  public ParseException(Exception e) {
-    this(e.toString());
+  public void setBuffer(byte[] byteBuffer)
+  {
+    this.byteBuffer = byteBuffer;
+    // for now - could be replaced with optimized custom code
+    bis = new ByteArrayInputStream(byteBuffer);
   }
-}
-
 
+  public void read(byte[] buf)
+  throws IOException
+  {
+    bis.read(buf);
+  }
+}
diff --git a/src/com/vividsolutions/jts/io/ByteOrderDataInStream.java b/src/com/vividsolutions/jts/io/ByteOrderDataInStream.java
new file mode 100644
index 0000000..9b53da4
--- /dev/null
+++ b/src/com/vividsolutions/jts/io/ByteOrderDataInStream.java
@@ -0,0 +1,108 @@
+/*
+ * The JTS Topology Suite is a collection of Java classes that
+ * implement the fundamental operations required to validate a given
+ * geo-spatial data set to a known topological specification.
+ *
+ * Copyright (C) 2001 Vivid Solutions
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * For more information, contact:
+ *
+ *     Vivid Solutions
+ *     Suite #1A
+ *     2328 Government Street
+ *     Victoria BC  V8T 5G5
+ *     Canada
+ *
+ *     (250)385-6040
+ *     www.vividsolutions.com
+ */
+package com.vividsolutions.jts.io;
+
+import java.io.IOException;
+
+/**
+ * Allows reading a stream of Java primitive datatypes from an underlying
+ * {@link InStream},
+ * with the representation being in either common byte ordering.
+ */
+public class ByteOrderDataInStream
+{
+  private int byteOrder = ByteOrderValues.BIG_ENDIAN;
+  private InStream stream;
+  // buffers to hold primitive datatypes
+  private byte[] buf1 = new byte[1];
+  private byte[] buf4 = new byte[4];
+  private byte[] buf8 = new byte[8];
+
+  public ByteOrderDataInStream()
+  {
+    this.stream = null;
+  }
+
+  public ByteOrderDataInStream(InStream stream)
+  {
+    this.stream = stream;
+  }
+
+  /**
+   * Allows a single ByteOrderDataInStream to be reused
+   * on multiple InStreams.
+   *
+   * @param stream
+   */
+  public void setInStream(InStream stream)
+  {
+    this.stream = stream;
+  }
+  public void setOrder(int byteOrder)
+  {
+    this.byteOrder = byteOrder;
+  }
+
+  /**
+   *
+   *
+   * @return
+   */
+  public byte readByte()
+  	throws IOException
+  {
+    stream.read(buf1);
+    return buf1[0];
+  }
+
+  public int readInt()
+	throws IOException
+  {
+    stream.read(buf4);
+    return ByteOrderValues.getInt(buf4, byteOrder);
+  }
+  public long readLong()
+	throws IOException
+  {
+    stream.read(buf8);
+    return ByteOrderValues.getLong(buf8, byteOrder);
+  }
+
+  public double readDouble()
+	throws IOException
+  {
+    stream.read(buf8);
+    return ByteOrderValues.getDouble(buf8, byteOrder);
+  }
+
+}
diff --git a/src/com/vividsolutions/jts/io/ByteOrderValues.java b/src/com/vividsolutions/jts/io/ByteOrderValues.java
new file mode 100644
index 0000000..df3cd4d
--- /dev/null
+++ b/src/com/vividsolutions/jts/io/ByteOrderValues.java
@@ -0,0 +1,139 @@
+/*
+* The JTS Topology Suite is a collection of Java classes that
+* implement the fundamental operations required to validate a given
+* geo-spatial data set to a known topological specification.
+*
+* Copyright (C) 2001 Vivid Solutions
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation; either
+* version 2.1 of the License, or (at your option) any later version.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*
+* For more information, contact:
+*
+*     Vivid Solutions
+*     Suite #1A
+*     2328 Government Street
+*     Victoria BC  V8T 5G5
+*     Canada
+*
+*     (250)385-6040
+*     www.vividsolutions.com
+ */
+package com.vividsolutions.jts.io;
+
+/**
+ * Methods to read and write primitive Java datatypes from/to byte
+ * sequences, allowing the byte order to be specified
+ * <p>
+ * Similar to the standard Java <code>ByteBuffer</code> class.
+ */
+public class ByteOrderValues
+{
+  public static final int BIG_ENDIAN = 1;
+  public static final int LITTLE_ENDIAN = 2;
+
+  public static int getInt(byte[] buf, int byteOrder)
+  {
+    if (byteOrder == BIG_ENDIAN) {
+      return  ( (int) (buf[0] & 0xff) << 24)
+            | ( (int) (buf[1] & 0xff) << 16)
+            | ( (int) (buf[2] & 0xff) << 8)
+            | (( int) (buf[3] & 0xff) );
+    }
+    else {// LITTLE_ENDIAN
+      return  ( (int) (buf[3] & 0xff) << 24)
+            | ( (int) (buf[2] & 0xff) << 16)
+            | ( (int) (buf[1] & 0xff) << 8)
+            | ( (int) (buf[0] & 0xff) );
+    }
+  }
+
+  public static void putInt(int intValue, byte[] buf, int byteOrder)
+  {
+    if (byteOrder == BIG_ENDIAN) {
+      buf[0] = (byte)(intValue >> 24);
+      buf[1] = (byte)(intValue >> 16);
+      buf[2] = (byte)(intValue >> 8);
+      buf[3] = (byte) intValue;
+    }
+    else {// LITTLE_ENDIAN
+      buf[0] = (byte) intValue;
+      buf[1] = (byte)(intValue >> 8);
+      buf[2] = (byte)(intValue >> 16);
+      buf[3] = (byte)(intValue >> 24);
+    }
+  }
+  public static long getLong(byte[] buf, int byteOrder)
+  {
+    if (byteOrder == BIG_ENDIAN) {
+      return
+            (long) (buf[0] & 0xff) << 56
+          | (long) (buf[1] & 0xff) << 48
+          | (long) (buf[2] & 0xff) << 40
+          | (long) (buf[3] & 0xff) << 32
+          | (long) (buf[4] & 0xff) << 24
+          | (long) (buf[5] & 0xff) << 16
+          | (long) (buf[6] & 0xff) <<  8
+          | (long) (buf[7] & 0xff);
+    }
+    else {// LITTLE_ENDIAN
+      return
+            (long) (buf[7] & 0xff) << 56
+          | (long) (buf[6] & 0xff) << 48
+          | (long) (buf[5] & 0xff) << 40
+          | (long) (buf[4] & 0xff) << 32
+          | (long) (buf[3] & 0xff) << 24
+          | (long) (buf[2] & 0xff) << 16
+          | (long) (buf[1] & 0xff) <<  8
+          | (long) (buf[0] & 0xff);
+    }
+  }
+
+  public static void putLong(long longValue, byte[] buf, int byteOrder)
+  {
+    if (byteOrder == BIG_ENDIAN) {
+      buf[0] = (byte)(longValue >> 56);
+      buf[1] = (byte)(longValue >> 48);
+      buf[2] = (byte)(longValue >> 40);
+      buf[3] = (byte)(longValue >> 32);
+      buf[4] = (byte)(longValue >> 24);
+      buf[5] = (byte)(longValue >> 16);
+      buf[6] = (byte)(longValue >> 8);
+      buf[7] = (byte) longValue;
+    }
+    else {  // LITTLE_ENDIAN
+      buf[0] = (byte) longValue;
+      buf[1] = (byte)(longValue >> 8);
+      buf[2] = (byte)(longValue >> 16);
+      buf[3] = (byte)(longValue >> 24);
+      buf[4] = (byte)(longValue >> 32);
+      buf[5] = (byte)(longValue >> 40);
+      buf[6] = (byte)(longValue >> 48);
+      buf[7] = (byte)(longValue >> 56);
+    }
+  }
+
+  public static double getDouble(byte[] buf, int byteOrder)
+  {
+    long longVal = getLong(buf, byteOrder);
+    return Double.longBitsToDouble(longVal);
+  }
+
+  public static void putDouble(double doubleValue, byte[] buf, int byteOrder)
+  {
+    long longVal = Double.doubleToLongBits(doubleValue);
+    putLong(longVal, buf, byteOrder);
+  }
+
+}
\ No newline at end of file
diff --git a/src/com/vividsolutions/jts/operation/overlay/OverlayNodeFactory.java b/src/com/vividsolutions/jts/io/InStream.java
similarity index 66%
copy from src/com/vividsolutions/jts/operation/overlay/OverlayNodeFactory.java
copy to src/com/vividsolutions/jts/io/InStream.java
index b7e0971..03a6c6e 100644
--- a/src/com/vividsolutions/jts/operation/overlay/OverlayNodeFactory.java
+++ b/src/com/vividsolutions/jts/io/InStream.java
@@ -1,5 +1,3 @@
-
-
 /*
  * The JTS Topology Suite is a collection of Java classes that
  * implement the fundamental operations required to validate a given
@@ -32,25 +30,25 @@
  *     (250)385-6040
  *     www.vividsolutions.com
  */
-package com.vividsolutions.jts.operation.overlay;
+package com.vividsolutions.jts.io;
 
-/**
- * @version 1.6
- */
-import com.vividsolutions.jts.geom.Coordinate;
-import com.vividsolutions.jts.geomgraph.*;
+import java.io.IOException;
 
 /**
- * Creates nodes for use in the {@link PlanarGraph}s constructed during
- * overlay operations.
+ * A interface for classes providing an input stream of bytes.
+ * This interface is similar to the Java {@link InputStream},
+ * but with a narrower interface to make it easier to implement.
  *
- * @version 1.6
  */
-public class OverlayNodeFactory
-  extends NodeFactory
+public interface InStream
 {
-  public Node createNode(Coordinate coord)
-  {
-    return new Node(coord, new DirectedEdgeStar());
-  }
+  /**
+   * Reads <code>buf.length</code> bytes from the input stream
+   * and stores them in the supplied buffer.
+   *
+   * @param buf the buffer to receive the bytes
+   *
+   * @throws IOException if an I/O error occurs
+   */
+  void read(byte[] buf) throws IOException;
 }
diff --git a/src/com/vividsolutions/jts/geomgraph/NodeFactory.java b/src/com/vividsolutions/jts/io/InputStreamInStream.java
similarity index 74%
copy from src/com/vividsolutions/jts/geomgraph/NodeFactory.java
copy to src/com/vividsolutions/jts/io/InputStreamInStream.java
index b717b2a..dc5745d 100644
--- a/src/com/vividsolutions/jts/geomgraph/NodeFactory.java
+++ b/src/com/vividsolutions/jts/io/InputStreamInStream.java
@@ -1,4 +1,3 @@
-
 /*
  * The JTS Topology Suite is a collection of Java classes that
  * implement the fundamental operations required to validate a given
@@ -31,20 +30,25 @@
  *     (250)385-6040
  *     www.vividsolutions.com
  */
-package com.vividsolutions.jts.geomgraph;
-
-import com.vividsolutions.jts.geom.Coordinate;
+package com.vividsolutions.jts.io;
 
+import java.io.*;
 
 /**
- * @version 1.6
- */
-public class NodeFactory {
-/**
- * The basic node constructor does not allow for incident edges
+ * An adapter to allow an {@link InputStream} to be used as an {@link InStream}
  */
-  public Node createNode(Coordinate coord)
+public class InputStreamInStream
+	implements InStream
+{
+  private InputStream is;
+
+  public InputStreamInStream(InputStream is)
+  {
+    this.is = is;
+  }
+
+  public void read(byte[] buf) throws IOException
   {
-    return new Node(coord, null);
+    is.read(buf);
   }
 }
diff --git a/src/com/vividsolutions/jts/algorithm/PointInRing.java b/src/com/vividsolutions/jts/io/OutStream.java
similarity index 75%
copy from src/com/vividsolutions/jts/algorithm/PointInRing.java
copy to src/com/vividsolutions/jts/io/OutStream.java
index b9fdfff..773574f 100644
--- a/src/com/vividsolutions/jts/algorithm/PointInRing.java
+++ b/src/com/vividsolutions/jts/io/OutStream.java
@@ -1,4 +1,3 @@
-
 /*
  * The JTS Topology Suite is a collection of Java classes that
  * implement the fundamental operations required to validate a given
@@ -31,17 +30,16 @@
  *     (250)385-6040
  *     www.vividsolutions.com
  */
-package com.vividsolutions.jts.algorithm;
+package com.vividsolutions.jts.io;
 
-import com.vividsolutions.jts.geom.Coordinate;
+import java.io.IOException;
 
 /**
- * An interface for classes which test whether a {@link Coordinate} lies inside
- * a ring.
- *
- * @version 1.6
+ * A interface for classes providing an output stream of bytes.
+ * This interface is similar to the Java {@link OutputStream},
+ * but with a narrower interface to make it easier to implement.
  */
-public interface PointInRing {
-
-  boolean isInside(Coordinate pt);
+public interface OutStream
+{
+  void write(byte[] buf, int len) throws IOException;
 }
diff --git a/src/com/vividsolutions/jts/geomgraph/index/MonotoneChain.java b/src/com/vividsolutions/jts/io/OutputStreamOutStream.java
similarity index 72%
copy from src/com/vividsolutions/jts/geomgraph/index/MonotoneChain.java
copy to src/com/vividsolutions/jts/io/OutputStreamOutStream.java
index b7ca2e5..63becb6 100644
--- a/src/com/vividsolutions/jts/geomgraph/index/MonotoneChain.java
+++ b/src/com/vividsolutions/jts/io/OutputStreamOutStream.java
@@ -1,6 +1,3 @@
-
-
-
 /*
  * The JTS Topology Suite is a collection of Java classes that
  * implement the fundamental operations required to validate a given
@@ -33,23 +30,25 @@
  *     (250)385-6040
  *     www.vividsolutions.com
  */
-package com.vividsolutions.jts.geomgraph.index;
+package com.vividsolutions.jts.io;
+
+import java.io.IOException;
+import java.io.OutputStream;
 
 /**
- * @version 1.6
+ * An adapter to allow an {@link OutputStream} to be used as an {@link OutStream}
  */
-public class MonotoneChain {
+public class OutputStreamOutStream
+	implements OutStream
+{
+  private OutputStream os;
 
-  MonotoneChainEdge mce;
-  int chainIndex;
-
-  public MonotoneChain(MonotoneChainEdge mce, int chainIndex) {
-    this.mce = mce;
-    this.chainIndex = chainIndex;
+  public OutputStreamOutStream(OutputStream os)
+  {
+    this.os = os;
   }
-
-  public void computeIntersections(MonotoneChain mc, SegmentIntersector si)
+  public void write(byte[] buf, int len) throws IOException
   {
-    this.mce.computeIntersectsForChain(chainIndex, mc.mce, mc.chainIndex, si);
+    os.write(buf, 0, len);
   }
 }
diff --git a/src/com/vividsolutions/jts/io/ParseException.java b/src/com/vividsolutions/jts/io/ParseException.java
index 609f8da..44b6291 100644
--- a/src/com/vividsolutions/jts/io/ParseException.java
+++ b/src/com/vividsolutions/jts/io/ParseException.java
@@ -37,7 +37,7 @@ package com.vividsolutions.jts.io;
 /**
  *  Thrown by a <code>WKTReader</code> when a parsing problem occurs.
  *
- *@version 1.6
+ *@version 1.7
  */
 public class ParseException extends Exception {
 
diff --git a/src/com/vividsolutions/jts/algorithm/PointInRing.java b/src/com/vividsolutions/jts/io/WKBConstants.java
similarity index 76%
copy from src/com/vividsolutions/jts/algorithm/PointInRing.java
copy to src/com/vividsolutions/jts/io/WKBConstants.java
index b9fdfff..ecfe87a 100644
--- a/src/com/vividsolutions/jts/algorithm/PointInRing.java
+++ b/src/com/vividsolutions/jts/io/WKBConstants.java
@@ -1,4 +1,3 @@
-
 /*
  * The JTS Topology Suite is a collection of Java classes that
  * implement the fundamental operations required to validate a given
@@ -31,17 +30,20 @@
  *     (250)385-6040
  *     www.vividsolutions.com
  */
-package com.vividsolutions.jts.algorithm;
-
-import com.vividsolutions.jts.geom.Coordinate;
+package com.vividsolutions.jts.io;
 
 /**
- * An interface for classes which test whether a {@link Coordinate} lies inside
- * a ring.
- *
- * @version 1.6
+ * Constant values used by the WKB format
  */
-public interface PointInRing {
+public interface WKBConstants {
+  int wkbXDR = 0;
+  int wkbNDR = 1;
 
-  boolean isInside(Coordinate pt);
+  int wkbPoint = 1;
+  int wkbLineString = 2;
+  int wkbPolygon = 3;
+  int wkbMultiPoint = 4;
+  int wkbMultiLineString = 5;
+  int wkbMultiPolygon = 6;
+  int wkbGeometryCollection = 7;
 }
diff --git a/src/com/vividsolutions/jts/io/WKBReader.java b/src/com/vividsolutions/jts/io/WKBReader.java
new file mode 100644
index 0000000..795802a
--- /dev/null
+++ b/src/com/vividsolutions/jts/io/WKBReader.java
@@ -0,0 +1,255 @@
+/*
+ * The JTS Topology Suite is a collection of Java classes that
+ * implement the fundamental operations required to validate a given
+ * geo-spatial data set to a known topological specification.
+ *
+ * Copyright (C) 2001 Vivid Solutions
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * For more information, contact:
+ *
+ *     Vivid Solutions
+ *     Suite #1A
+ *     2328 Government Street
+ *     Victoria BC  V8T 5G5
+ *     Canada
+ *
+ *     (250)385-6040
+ *     www.vividsolutions.com
+ */
+package com.vividsolutions.jts.io;
+
+import java.io.IOException;
+import com.vividsolutions.jts.geom.*;
+
+/**
+ * Reads a {@link Geometry}from a byte stream in Well-Known Binary format.
+ * Supports use of an {@link InStream}, which allows easy use
+ * with arbitary byte stream sources.
+ * <p>
+ * This class is designed to support reuse of a single instance to read multiple
+ * geometries. This class is not thread-safe; each thread should create its own
+ * instance.
+ *
+ * @see WKBWriter
+ */
+public class WKBReader
+{
+  private static final String INVALID_GEOM_TYPE_MSG
+  = "Invalid geometry type encountered in ";
+
+  private GeometryFactory factory;
+  private PrecisionModel precisionModel;
+  // default dimension - will be set on read
+  private int inputDimension = 2;
+  private ByteOrderDataInStream dis = new ByteOrderDataInStream();
+  private double[] ordValues;
+
+  public WKBReader() {
+    this(new GeometryFactory());
+  }
+
+  public WKBReader(GeometryFactory geometryFactory) {
+    this.factory = geometryFactory;
+    precisionModel = factory.getPrecisionModel();
+  }
+
+  /**
+   * Reads a single {@link Geometry} from a byte array.
+   *
+   * @param bytes the byte array to read from
+   * @return the geometry read
+   * @throws IOException if an input exception occurs
+   * @throws ParseException if a parse exception occurs
+   */
+  public Geometry read(byte[] bytes) throws IOException, ParseException
+  {
+    // possibly reuse the ByteArrayInStream?
+    return read(new ByteArrayInStream(bytes));
+  }
+
+  /**
+   * Reads a {@link Geometry} from an {@link InStream).
+   *
+   * @param is the stream to read from
+   * @return the Geometry read
+   * @throws IOException
+   * @throws ParseException
+   */
+  public Geometry read(InStream is)
+  throws IOException, ParseException
+  {
+    dis.setInStream(is);
+
+
+    return readGeometry();
+  }
+
+  private Geometry readGeometry()
+  throws IOException, ParseException
+  {
+    // determine byte order
+    byte byteOrder = dis.readByte();
+    // default is big endian
+    if (byteOrder == WKBConstants.wkbNDR)
+      dis.setOrder(ByteOrderValues.LITTLE_ENDIAN);
+
+    int typeInt = dis.readInt();
+    int geometryType = typeInt & 0xff;
+    boolean hasZ = (typeInt & 0x80000000) != 0;
+    inputDimension =  hasZ ? 3 : 2;
+
+    // only allocate ordValues buffer if necessary
+    if (ordValues == null || ordValues.length < inputDimension)
+      ordValues = new double[inputDimension];
+
+    switch (geometryType) {
+      case WKBConstants.wkbPoint :
+        return readPoint();
+      case WKBConstants.wkbLineString :
+        return readLineString();
+      case WKBConstants.wkbPolygon :
+        return readPolygon();
+      case WKBConstants.wkbMultiPoint :
+        return readMultiPoint();
+      case WKBConstants.wkbMultiLineString :
+        return readMultiLineString();
+      case WKBConstants.wkbMultiPolygon :
+        return readMultiPolygon();
+      case WKBConstants.wkbGeometryCollection :
+        return readGeometryCollection();
+    }
+    throw new ParseException("Unknown WKB type " + geometryType);
+    //return null;
+  }
+
+  private Point readPoint() throws IOException
+  {
+    CoordinateSequence pts = readCoordinateSequence(1);
+    return factory.createPoint(pts);
+  }
+
+  private LineString readLineString() throws IOException
+  {
+    int size = dis.readInt();
+    CoordinateSequence pts = readCoordinateSequence(size);
+    return factory.createLineString(pts);
+  }
+
+  private LinearRing readLinearRing() throws IOException
+  {
+    int size = dis.readInt();
+    CoordinateSequence pts = readCoordinateSequence(size);
+    return factory.createLinearRing(pts);
+  }
+
+  private Polygon readPolygon() throws IOException
+  {
+    int numRings = dis.readInt();
+    LinearRing[] holes = null;
+    if (numRings > 1)
+      holes = new LinearRing[numRings - 1];
+
+    LinearRing shell = readLinearRing();
+    for (int i = 0; i < numRings - 1; i++) {
+      holes[i] = readLinearRing();
+    }
+    return factory.createPolygon(shell, holes);
+  }
+
+  private MultiPoint readMultiPoint() throws IOException, ParseException
+  {
+    int numGeom = dis.readInt();
+    Point[] geoms = new Point[numGeom];
+    for (int i = 0; i < numGeom; i++) {
+      Geometry g = readGeometry();
+      if (! (g instanceof Point))
+        throw new ParseException(INVALID_GEOM_TYPE_MSG + "MultiPoint");
+      geoms[i] = (Point) g;
+    }
+    return factory.createMultiPoint(geoms);
+  }
+
+  private MultiLineString readMultiLineString() throws IOException, ParseException
+  {
+    int numGeom = dis.readInt();
+    LineString[] geoms = new LineString[numGeom];
+    for (int i = 0; i < numGeom; i++) {
+      Geometry g = readGeometry();
+      if (! (g instanceof LineString))
+        throw new ParseException(INVALID_GEOM_TYPE_MSG + "MultiLineString");
+      geoms[i] = (LineString) g;
+    }
+    return factory.createMultiLineString(geoms);
+  }
+
+  private MultiPolygon readMultiPolygon() throws IOException, ParseException
+  {
+    int numGeom = dis.readInt();
+    Polygon[] geoms = new Polygon[numGeom];
+    for (int i = 0; i < numGeom; i++) {
+      Geometry g = readGeometry();
+      if (! (g instanceof Polygon))
+        throw new ParseException(INVALID_GEOM_TYPE_MSG + "MultiPolygon");
+      geoms[i] = (Polygon) g;
+    }
+    return factory.createMultiPolygon(geoms);
+  }
+
+  private GeometryCollection readGeometryCollection() throws IOException, ParseException
+  {
+    int numGeom = dis.readInt();
+    Geometry[] geoms = new Geometry[numGeom];
+    for (int i = 0; i < numGeom; i++) {
+      geoms[i] = readGeometry();
+    }
+    return factory.createGeometryCollection(geoms);
+  }
+
+  private CoordinateSequence readCoordinateSequence(int size) throws IOException
+  {
+    CoordinateSequence seq = factory.getCoordinateSequenceFactory().create(size, inputDimension);
+    int targetDim = seq.getDimension();
+    if (targetDim > inputDimension)
+      targetDim = inputDimension;
+    for (int i = 0; i < size; i++) {
+      readCoordinate();
+      for (int j = 0; j < targetDim; j++) {
+        seq.setOrdinate(i, j, ordValues[j]);
+      }
+    }
+    return seq;
+  }
+
+  /**
+   * Reads a coordinate value with the specified dimensionality.
+   * Makes the X and Y ordinates precise according to the precision model
+   * in use.
+   */
+  private void readCoordinate() throws IOException
+  {
+    for (int i = 0; i < inputDimension; i++) {
+      if (i <= 1) {
+        ordValues[i] = precisionModel.makePrecise(dis.readDouble());
+      }
+      else {
+        ordValues[i] = dis.readDouble();
+      }
+
+    }
+  }
+
+}
\ No newline at end of file
diff --git a/src/com/vividsolutions/jts/io/WKBWriter.java b/src/com/vividsolutions/jts/io/WKBWriter.java
new file mode 100644
index 0000000..abaa73b
--- /dev/null
+++ b/src/com/vividsolutions/jts/io/WKBWriter.java
@@ -0,0 +1,242 @@
+/*
+ * The JTS Topology Suite is a collection of Java classes that
+ * implement the fundamental operations required to validate a given
+ * geo-spatial data set to a known topological specification.
+ *
+ * Copyright (C) 2001 Vivid Solutions
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * For more information, contact:
+ *
+ *     Vivid Solutions
+ *     Suite #1A
+ *     2328 Government Street
+ *     Victoria BC  V8T 5G5
+ *     Canada
+ *
+ *     (250)385-6040
+ *     www.vividsolutions.com
+ */
+package com.vividsolutions.jts.io;
+
+import java.io.*;
+import com.vividsolutions.jts.geom.*;
+import com.vividsolutions.jts.util.Assert;
+
+/**
+ * Writes a {@link Geometry} into Well-Known Binary format.
+ * Supports use of an {@link OutStream}, which allows easy use
+ * with arbitary byte stream sinks.
+ * <p>
+ * The WKB format is specified in the OGC Simple Features for SQL specification.
+ * This implementation supports the extended WKB standard for representing
+ * 3-dimensional coordinates.  The presence of 3D coordinates is signified
+ * by setting the high bit of the wkbType word.
+ * <p>
+ * Empty Points cannot be represented in WKB; an
+ * {@link IllegalArgumentException} will be thrown if one is
+ * written. The WKB specification does not support representing {@link LinearRing}s;
+ * they will be written as {@link LineString}s.
+ * <p>
+ * This class is designed to support reuse of a single instance to read multiple
+ * geometries. This class is not thread-safe; each thread should create its own
+ * instance.
+ *
+ * @see WKBReader
+ */
+public class WKBWriter
+{
+  private int outputDimension;
+  private int byteOrder;
+  private ByteArrayOutputStream byteArrayOS = new ByteArrayOutputStream();
+  private OutStream byteArrayOutStream = new OutputStreamOutStream(byteArrayOS);
+  // holds output data values
+  private byte[] buf = new byte[8];
+
+  /**
+   * Creates a writer that writes {@link Geometry}s with
+   * output dimension = 2 and BIG_ENDIAN byte order
+   */
+  public WKBWriter() {
+    this(2, ByteOrderValues.BIG_ENDIAN);
+  }
+
+  /**
+   * Creates a writer that writes {@link Geometry}s with
+   * the given output dimension (2 or 3) and BIG_ENDIAN byte order
+   *
+   * @param outputDimension the dimension to output (2 or 3)
+   */
+  public WKBWriter(int outputDimension) {
+    this(outputDimension, ByteOrderValues.BIG_ENDIAN);
+  }
+
+  /**
+   * Creates a writer that writes {@link Geometry}s with
+   * the given output dimension (2 or 3) and byte order
+   *
+   * @param outputDimension the dimension to output (2 or 3)
+   * @param byteOrder the byte ordering to use
+   */
+  public WKBWriter(int outputDimension, int byteOrder) {
+    this.outputDimension = outputDimension;
+    this.byteOrder = byteOrder;
+
+    if (outputDimension < 2 || outputDimension > 3)
+      throw new IllegalArgumentException("Output dimension must be 2 or 3");
+  }
+
+  /**
+   * Writes a {@link Geometry} into a byte array.
+   *
+   * @param geom the geometry to write
+   * @return the byte array containing the WKB
+   */
+  public byte[] write(Geometry geom)
+  {
+    try {
+      byteArrayOS.reset();
+      write(geom, byteArrayOutStream);
+    }
+    catch (IOException ex) {
+      throw new RuntimeException("Unexpected IO exception: " + ex.getMessage());
+    }
+    return byteArrayOS.toByteArray();
+  }
+
+  /**
+   * Writes a {@link Geometry} to an {@link OutStream}.
+   *
+   * @param geom the geometry to write
+   * @param os the out stream to write to
+   * @throws IOException if an I/O error occurs
+   */
+  public void write(Geometry geom, OutStream os) throws IOException
+  {
+    if (geom instanceof Point)
+      writePoint((Point) geom, os);
+    // LinearRings will be written as LineStrings
+    else if (geom instanceof LineString)
+      writeLineString((LineString) geom, os);
+    else if (geom instanceof Polygon)
+      writePolygon((Polygon) geom, os);
+    else if (geom instanceof MultiPoint)
+      writeGeometryCollection(WKBConstants.wkbMultiPoint, (MultiPoint) geom, os);
+    else if (geom instanceof MultiLineString)
+      writeGeometryCollection(WKBConstants.wkbMultiLineString,
+          (MultiLineString) geom, os);
+    else if (geom instanceof MultiPolygon)
+      writeGeometryCollection(WKBConstants.wkbMultiPolygon,
+          (MultiPolygon) geom, os);
+    else if (geom instanceof GeometryCollection)
+      writeGeometryCollection(WKBConstants.wkbGeometryCollection,
+          (GeometryCollection) geom, os);
+    else {
+      Assert.shouldNeverReachHere("Unknown Geometry type");
+    }
+  }
+
+  private void writePoint(Point pt, OutStream os) throws IOException
+  {
+    if (pt.getCoordinateSequence().size() == 0)
+      throw new IllegalArgumentException("Empty Points cannot be represented in WKB");
+    writeByteOrder(os);
+    writeGeometryType(WKBConstants.wkbPoint, os);
+    writeCoordinateSequence(pt.getCoordinateSequence(), false, os);
+  }
+
+  private void writeLineString(LineString line, OutStream os)
+      throws IOException
+  {
+    writeByteOrder(os);
+    writeGeometryType(WKBConstants.wkbLineString, os);
+    writeCoordinateSequence(line.getCoordinateSequence(), true, os);
+  }
+
+  private void writePolygon(Polygon poly, OutStream os) throws IOException
+  {
+    writeByteOrder(os);
+    writeGeometryType(WKBConstants.wkbPolygon, os);
+    writeInt(poly.getNumInteriorRing() + 1, os);
+    writeCoordinateSequence(poly.getExteriorRing().getCoordinateSequence(), true, os);
+    for (int i = 0; i < poly.getNumInteriorRing(); i++) {
+      writeCoordinateSequence(poly.getInteriorRingN(i).getCoordinateSequence(), true,
+          os);
+    }
+  }
+
+  private void writeGeometryCollection(int geometryType, GeometryCollection gc,
+      OutStream os) throws IOException
+  {
+    writeByteOrder(os);
+    writeGeometryType(geometryType, os);
+    writeInt(gc.getNumGeometries(), os);
+    for (int i = 0; i < gc.getNumGeometries(); i++) {
+      write(gc.getGeometryN(i), os);
+    }
+  }
+
+  private void writeByteOrder(OutStream os) throws IOException
+  {
+    if (byteOrder == ByteOrderValues.LITTLE_ENDIAN)
+      buf[0] = WKBConstants.wkbNDR;
+    else
+      buf[0] = WKBConstants.wkbXDR;
+    os.write(buf, 1);
+  }
+
+  private void writeGeometryType(int geometryType, OutStream os)
+      throws IOException
+  {
+    int flag3D = (outputDimension == 3) ? 0x80000000 : 0;
+    int typeInt = geometryType | flag3D;
+    writeInt(typeInt, os);
+  }
+
+  private void writeInt(int intValue, OutStream os) throws IOException
+  {
+    ByteOrderValues.putInt(intValue, buf, byteOrder);
+    os.write(buf, 4);
+  }
+
+  private void writeCoordinateSequence(CoordinateSequence seq, boolean writeSize, OutStream os)
+      throws IOException
+  {
+    if (writeSize)
+      writeInt(seq.size(), os);
+
+    boolean output3D = false;
+    if (seq.getDimension() >= 3 && outputDimension >= 3)
+      output3D = true;
+
+    for (int i = 0; i < seq.size(); i++) {
+      writeCoordinate(seq, i, output3D, os);
+    }
+  }
+
+  private void writeCoordinate(CoordinateSequence seq, int index, boolean output3D, OutStream os)
+  throws IOException
+  {
+    ByteOrderValues.putDouble(seq.getX(index), buf, byteOrder);
+    os.write(buf, 8);
+    ByteOrderValues.putDouble(seq.getY(index), buf, byteOrder);
+    os.write(buf, 8);
+    if (output3D) {
+      ByteOrderValues.putDouble(seq.getOrdinate(index, 2), buf, byteOrder);
+      os.write(buf, 8);
+    }
+  }
+}
\ No newline at end of file
diff --git a/src/com/vividsolutions/jts/io/WKTReader.java b/src/com/vividsolutions/jts/io/WKTReader.java
index 2a7f153..8381fe4 100644
--- a/src/com/vividsolutions/jts/io/WKTReader.java
+++ b/src/com/vividsolutions/jts/io/WKTReader.java
@@ -1,5 +1,3 @@
-
-
 /*
  * The JTS Topology Suite is a collection of Java classes that
  * implement the fundamental operations required to validate a given
@@ -35,9 +33,9 @@
 package com.vividsolutions.jts.io;
 
 import com.vividsolutions.jts.geom.*;
+import com.vividsolutions.jts.util.*;
 import com.vividsolutions.jts.io.ParseException;
 
-import com.vividsolutions.jts.util.Assert;
 import java.io.IOException;
 import java.io.Reader;
 import java.io.StreamTokenizer;
@@ -45,50 +43,100 @@ import java.io.StringReader;
 import java.util.ArrayList;
 
 /**
- *  Converts a Well-Known Text string to a <code>Geometry</code>.
+ * Converts a geometry in Well-Known Text format to a {@link Geometry}.
  * <p>
- *  The <code>WKTReader</code> allows
- *  extracting <code>Geometry</code> objects from either input streams or
- *  internal strings. This allows it to function as a parser to read <code>Geometry</code>
+ * <code>WKTReader</code> supports
+ * extracting <code>Geometry</code> objects from either {@link Reader}s or
+ *  {@link String}s. This allows it to function as a parser to read <code>Geometry</code>
  *  objects from text blocks embedded in other data formats (e.g. XML). <P>
  * <p>
- * The Well-known
- *  Text format is defined in the <A HREF="http://www.opengis.org/techno/specs.htm">
- *  OpenGIS Simple Features Specification for SQL</A> . <P>
- * <p>
- *  <B>Note: </B> There is an inconsistency in the SFS. The WKT grammar states
- *  that <code>MultiPoints</code> are represented by <code>MULTIPOINT ( ( x y), (x y) )</code>
- *  , but the examples show <code>MultiPoint</code>s as <code>MULTIPOINT ( x y, x y )</code>
- *  . Other implementations follow the latter syntax, so JTS will adopt it as
- *  well.
- *
- *  A <code>WKTReader</code> is parameterized by a <code>GeometryFactory</code>
- *  , to allow it to create <code>Geometry</code> objects of the appropriate
- *  implementation. In particular, the <code>GeometryFactory</code> will
- *  determine the <code>PrecisionModel</code> and <code>SRID</code> that is
+ *  A <code>WKTReader</code> is parameterized by a <code>GeometryFactory</code>,
+ *  to allow it to create <code>Geometry</code> objects of the appropriate
+ *  implementation. In particular, the <code>GeometryFactory</code>
+ *  determines the <code>PrecisionModel</code> and <code>SRID</code> that is
  *  used. <P>
  *
- *  The <code>WKTReader</code> will convert the input numbers to the precise
+ *  The <code>WKTReader</code> converts all input numbers to the precise
  *  internal representation.
  *
- *  Reads non-standard "LINEARRING" tags.
+ * <h3>Notes:</h3>
+ * <ul>
+ * <li>The reader supports non-standard "LINEARRING" tags.
+ * <li>The reader uses Double.parseDouble to perform the conversion of ASCII
+ * numbers to floating point.  This means it supports the Java
+ * syntax for floating point literals (including scientific notation).
+ * </ul>
+ *
+ * <h3>Syntax</h3>
+ * The following syntax specification describes the version of Well-Known Text
+ * supported by JTS.
+ * (The specification uses a syntax language similar to that used in
+ * the C and Java language specifications.)
+ * <p>
+ *
+ * <blockquote><pre>
+ * <i>WKTGeometry:</i> one of<i>
+ *
+ *       WKTPoint  WKTLineString  WKTLinearRing  WKTPolygon
+ *       WKTMultiPoint  WKTMultiLineString  WKTMultiPolygon
+ *       WKTGeometryCollection</i>
+ *
+ * <i>WKTPoint:</i> <b>POINT ( </b><i>Coordinate</i> <b>)</b>
+ *
+ * <i>WKTLineString:</i> <b>LINESTRING</b> <i>CoordinateSequence</i>
+ *
+ * <i>WKTLinearRing:</i> <b>LINEARRING</b> <i>CoordinateSequence</i>
+ *
+ * <i>WKTPolygon:</i> <b>POLYGON</b> <i>CoordinateSequenceList</i>
+ *
+ * <i>WKTMultiPoint:</i> <b>MULTIPOINT</b> <i>CoordinateSequence</i>
+ *
+ * <i>WKTMultiLineString:</i> <b>MULTILINESTRING</b> <i>CoordinateSequenceList</i>
+ *
+ * <i>WKTMultiPolygon:</i>
+ *         <b>MULTIPOLYGON (</b> <i>CoordinateSequenceList {</i> , <i>CoordinateSequenceList }</i> <b>)</b>
+ *
+ * <i>WKTGeometryCollection: </i>
+ *         <b>GEOMETRYCOLLECTION (</b> <i>WKTGeometry {</i> , <i>WKTGeometry }</i> <b>)</b>
  *
- *@version 1.6
+ * <i>CoordinateSequenceList:</i>
+ *         <b>(</b> <i>CoordinateSequence {</i> <b>,</b> <i>CoordinateSequence }</i> <b>)</b>
+ *
+ * <i>CoordinateSequence:</i>
+ *         <b>(</b> <i>Coordinate {</i> , <i>Coordinate }</i> <b>)</b>
+ *
+ * <i>Coordinate:
+ *         Number Number Number<sub>opt</sub></i>
+ *
+ * <i>Number:</i> A Java-style floating-point number
+ *
+ * </pre></blockquote>
+ *
+ *
+ *@version 1.7
+ * @see WKTWriter
  */
-public class WKTReader {
+public class WKTReader
+{
+  private static final String EMPTY = "EMPTY";
+  private static final String COMMA = ",";
+  private static final String L_PAREN = "(";
+  private static final String R_PAREN = ")";
+
   private GeometryFactory geometryFactory;
   private PrecisionModel precisionModel;
+  private StreamTokenizer tokenizer;
 
   /**
-   * Creates a WKTReader that creates objects using a basic GeometryFactory.
+   * Creates a reader that creates objects using the default {@link GeometryFactory}.
    */
   public WKTReader() {
     this(new GeometryFactory());
   }
 
   /**
-   *  Creates a <code>WKTReader</code> that creates objects using the given
-   *  <code>GeometryFactory</code>.
+   *  Creates a reader that creates objects using the given
+   *  {@link GeometryFactory}.
    *
    *@param  geometryFactory  the factory used to create <code>Geometry</code>s.
    */
@@ -97,18 +145,17 @@ public class WKTReader {
     precisionModel = geometryFactory.getPrecisionModel();
   }
 
-  
-
-	/**
-     * Converts a Well-known Text representation to a <code>Geometry</code>.
-     * 
-     * @param wellKnownText
-     *            one or more <Geometry Tagged Text>strings (see the OpenGIS
-     *            Simple Features Specification) separated by whitespace
-     * @return a <code>Geometry</code> specified by <code>wellKnownText</code>
-     * @throws ParseException
-     *             if a parsing problem occurs
-	 */
+  /**
+   * Reads a Well-Known Text representation of a {@link Geometry}
+   * from a {@link String}.
+   *
+   * @param wellKnownText
+   *            one or more <Geometry Tagged Text>strings (see the OpenGIS
+   *            Simple Features Specification) separated by whitespace
+   * @return a <code>Geometry</code> specified by <code>wellKnownText</code>
+   * @throws ParseException
+   *             if a parsing problem occurs
+   */
   public Geometry read(String wellKnownText) throws ParseException {
     StringReader reader = new StringReader(wellKnownText);
     try {
@@ -120,7 +167,8 @@ public class WKTReader {
   }
 
   /**
-   *  Converts a Well-known Text representation to a <code>Geometry</code>.
+   * Reads a Well-Known Text representation of a {@link Geometry}
+   * from a {@link Reader}.
    *
    *@param  reader           a Reader which will return a <Geometry Tagged Text>
    *      string (see the OpenGIS Simple Features Specification)
@@ -128,9 +176,21 @@ public class WKTReader {
    *@throws  ParseException  if a parsing problem occurs
    */
   public Geometry read(Reader reader) throws ParseException {
-    StreamTokenizer tokenizer = new StreamTokenizer(reader);
+    tokenizer = new StreamTokenizer(reader);
+    // set tokenizer to NOT parse numbers
+    tokenizer.resetSyntax();
+    tokenizer.wordChars('a', 'z');
+    tokenizer.wordChars('A', 'Z');
+    tokenizer.wordChars(128 + 32, 255);
+    tokenizer.wordChars('0', '9');
+    tokenizer.wordChars('-', '-');
+    tokenizer.wordChars('+', '+');
+    tokenizer.wordChars('.', '.');
+    tokenizer.whitespaceChars(0, ' ');
+    tokenizer.commentChar('#');
+
     try {
-      return readGeometryTaggedText(tokenizer);
+      return readGeometryTaggedText();
     }
     catch (IOException e) {
       throw new ParseException(e.toString());
@@ -138,145 +198,138 @@ public class WKTReader {
   }
 
   /**
-   *  Returns the next array of <code>Coordinate</code>s in the stream.
+   * Returns the next array of <code>Coordinate</code>s in the stream.
    *
    *@param  tokenizer        tokenizer over a stream of text in Well-known Text
-   *      format. The next element returned by the stream should be "(" (the
-   *      beginning of "(x1 y1, x2 y2, ..., xn yn)") or "EMPTY".
+   *      format. The next element returned by the stream should be L_PAREN (the
+   *      beginning of "(x1 y1, x2 y2, ..., xn yn)") or EMPTY.
    *@return                  the next array of <code>Coordinate</code>s in the
-   *      stream, or an empty array if "EMPTY" is the next element returned by
+   *      stream, or an empty array if EMPTY is the next element returned by
    *      the stream.
    *@throws  IOException     if an I/O error occurs
    *@throws  ParseException  if an unexpected token was encountered
    */
-  private Coordinate[] getCoordinates(StreamTokenizer tokenizer)
+  private Coordinate[] getCoordinates()
       throws IOException, ParseException
   {
-    String nextToken = getNextEmptyOrOpener(tokenizer);
-    if (nextToken.equals("EMPTY")) {
+    String nextToken = getNextEmptyOrOpener();
+    if (nextToken.equals(EMPTY)) {
       return new Coordinate[]{};
     }
     ArrayList coordinates = new ArrayList();
-    coordinates.add(getPreciseCoordinate(tokenizer));
-    nextToken = getNextCloserOrComma(tokenizer);
-    while (nextToken.equals(",")) {
-      coordinates.add(getPreciseCoordinate(tokenizer));
-      nextToken = getNextCloserOrComma(tokenizer);
+    coordinates.add(getPreciseCoordinate());
+    nextToken = getNextCloserOrComma();
+    while (nextToken.equals(COMMA)) {
+      coordinates.add(getPreciseCoordinate());
+      nextToken = getNextCloserOrComma();
     }
     Coordinate[] array = new Coordinate[coordinates.size()];
     return (Coordinate[]) coordinates.toArray(array);
   }
 
-  private Coordinate getPreciseCoordinate(StreamTokenizer tokenizer)
+  private Coordinate getPreciseCoordinate()
       throws IOException, ParseException
   {
     Coordinate coord = new Coordinate();
-    coord.x = getNextNumber(tokenizer);
-    coord.y = getNextNumber(tokenizer);
-    if (isNumberNext(tokenizer)) {
-        coord.z = getNextNumber(tokenizer);
+    coord.x = getNextNumber();
+    coord.y = getNextNumber();
+    if (isNumberNext()) {
+        coord.z = getNextNumber();
     }
     precisionModel.makePrecise(coord);
     return coord;
   }
-  private boolean isNumberNext(StreamTokenizer tokenizer) throws IOException {
-      try {
-          return tokenizer.nextToken() == StreamTokenizer.TT_NUMBER;
-      }
-      finally {
-          tokenizer.pushBack();
-      }
+
+  private boolean isNumberNext() throws IOException {
+    int type = tokenizer.nextToken();
+    tokenizer.pushBack();
+    return type == StreamTokenizer.TT_WORD;
   }
+
   /**
-   *  Returns the next number in the stream.
+   * Parses the next number in the stream.
+   * Numbers with exponents are handled.
    *
    *@param  tokenizer        tokenizer over a stream of text in Well-known Text
    *      format. The next token must be a number.
    *@return                  the next number in the stream
-   *@throws  ParseException  if the next token is not a number
+   *@throws  ParseException  if the next token is not a valid number
    *@throws  IOException     if an I/O error occurs
    */
-  private double getNextNumber(StreamTokenizer tokenizer) throws IOException,
+  private double getNextNumber() throws IOException,
       ParseException {
     int type = tokenizer.nextToken();
     switch (type) {
-      case StreamTokenizer.TT_EOF:
-        throw new ParseException("Expected number but encountered end of stream");
-      case StreamTokenizer.TT_EOL:
-        throw new ParseException("Expected number but encountered end of line");
-      case StreamTokenizer.TT_NUMBER:
-        return tokenizer.nval;
       case StreamTokenizer.TT_WORD:
-        throw new ParseException("Expected number but encountered word: " +
-            tokenizer.sval);
-      case '(':
-        throw new ParseException("Expected number but encountered '('");
-      case ')':
-        throw new ParseException("Expected number but encountered ')'");
-      case ',':
-        throw new ParseException("Expected number but encountered ','");
+      {
+        try {
+          return Double.parseDouble(tokenizer.sval);
+        }
+        catch (NumberFormatException ex) {
+          throw new ParseException("Invalid number: " + tokenizer.sval);
+        }
+      }
     }
-    Assert.shouldNeverReachHere("Encountered unexpected StreamTokenizer type: "
-         + type);
-    return 0;
+    parseError("number");
+    return 0.0;
   }
-
   /**
-   *  Returns the next "EMPTY" or "(" in the stream as uppercase text.
+   *  Returns the next EMPTY or L_PAREN in the stream as uppercase text.
    *
    *@param  tokenizer        tokenizer over a stream of text in Well-known Text
-   *      format. The next token must be "EMPTY" or "(".
-   *@return                  the next "EMPTY" or "(" in the stream as uppercase
+   *      format. The next token must be EMPTY or L_PAREN.
+   *@return                  the next EMPTY or L_PAREN in the stream as uppercase
    *      text.
-   *@throws  ParseException  if the next token is not "EMPTY" or "("
+   *@throws  ParseException  if the next token is not EMPTY or L_PAREN
    *@throws  IOException     if an I/O error occurs
    */
-  private String getNextEmptyOrOpener(StreamTokenizer tokenizer) throws IOException, ParseException {
-    String nextWord = getNextWord(tokenizer);
-    if (nextWord.equals("EMPTY") || nextWord.equals("(")) {
+  private String getNextEmptyOrOpener() throws IOException, ParseException {
+    String nextWord = getNextWord();
+    if (nextWord.equals(EMPTY) || nextWord.equals(L_PAREN)) {
       return nextWord;
     }
-    throw new ParseException("Expected 'EMPTY' or '(' but encountered '" +
-        nextWord + "'");
+    parseError(EMPTY + " or " + L_PAREN);
+    return null;
   }
 
   /**
-   *  Returns the next ")" or "," in the stream.
+   *  Returns the next R_PAREN or COMMA in the stream.
    *
    *@param  tokenizer        tokenizer over a stream of text in Well-known Text
-   *      format. The next token must be ")" or ",".
-   *@return                  the next ")" or "," in the stream
-   *@throws  ParseException  if the next token is not ")" or ","
+   *      format. The next token must be R_PAREN or COMMA.
+   *@return                  the next R_PAREN or COMMA in the stream
+   *@throws  ParseException  if the next token is not R_PAREN or COMMA
    *@throws  IOException     if an I/O error occurs
    */
-  private String getNextCloserOrComma(StreamTokenizer tokenizer) throws IOException, ParseException {
-    String nextWord = getNextWord(tokenizer);
-    if (nextWord.equals(",") || nextWord.equals(")")) {
+  private String getNextCloserOrComma() throws IOException, ParseException {
+    String nextWord = getNextWord();
+    if (nextWord.equals(COMMA) || nextWord.equals(R_PAREN)) {
       return nextWord;
     }
-    throw new ParseException("Expected ')' or ',' but encountered '" + nextWord
-         + "'");
+    parseError(COMMA + " or " + R_PAREN);
+    return null;
   }
 
   /**
-   *  Returns the next ")" in the stream.
+   *  Returns the next R_PAREN in the stream.
    *
    *@param  tokenizer        tokenizer over a stream of text in Well-known Text
-   *      format. The next token must be ")".
-   *@return                  the next ")" in the stream
-   *@throws  ParseException  if the next token is not ")"
+   *      format. The next token must be R_PAREN.
+   *@return                  the next R_PAREN in the stream
+   *@throws  ParseException  if the next token is not R_PAREN
    *@throws  IOException     if an I/O error occurs
    */
-  private String getNextCloser(StreamTokenizer tokenizer) throws IOException, ParseException {
-    String nextWord = getNextWord(tokenizer);
-    if (nextWord.equals(")")) {
+  private String getNextCloser() throws IOException, ParseException {
+    String nextWord = getNextWord();
+    if (nextWord.equals(R_PAREN)) {
       return nextWord;
     }
-    throw new ParseException("Expected ')' but encountered '" + nextWord + "'");
+    parseError(R_PAREN);
+    return null;
   }
 
   /**
-   *  Returns the next word in the stream as uppercase text.
+   *  Returns the next word in the stream.
    *
    *@param  tokenizer        tokenizer over a stream of text in Well-known Text
    *      format. The next token must be a word.
@@ -284,30 +337,63 @@ public class WKTReader {
    *@throws  ParseException  if the next token is not a word
    *@throws  IOException     if an I/O error occurs
    */
-  private String getNextWord(StreamTokenizer tokenizer) throws IOException, ParseException {
+  private String getNextWord() throws IOException, ParseException {
     int type = tokenizer.nextToken();
     switch (type) {
-      case StreamTokenizer.TT_EOF:
-        throw new ParseException("Expected word but encountered end of stream");
-      case StreamTokenizer.TT_EOL:
-        throw new ParseException("Expected word but encountered end of line");
-      case StreamTokenizer.TT_NUMBER:
-        throw new ParseException("Expected word but encountered number: " +
-            tokenizer.nval);
-      case StreamTokenizer.TT_WORD:
-        return tokenizer.sval.toUpperCase();
-      case '(':
-        return "(";
-      case ')':
-        return ")";
-      case ',':
-        return ",";
+    case StreamTokenizer.TT_WORD:
+
+      String word = tokenizer.sval;
+      if (word.equalsIgnoreCase(EMPTY))
+          return EMPTY;
+      return word;
+
+    case '(': return L_PAREN;
+    case ')': return R_PAREN;
+    case ',': return COMMA;
     }
-    Assert.shouldNeverReachHere("Encountered unexpected StreamTokenizer type: " + type);
+    parseError("word");
     return null;
   }
 
   /**
+   * Throws a formatted ParseException for the current token.
+   *
+   * @param expected a description of what was expected
+   * @throws ParseException
+   * @throws AssertionFailedException if an invalid token is encountered
+   */
+  private void parseError(String expected)
+      throws ParseException
+  {
+    // throws Asserts for tokens that should never be seen
+    if (tokenizer.ttype == StreamTokenizer.TT_NUMBER)
+      Assert.shouldNeverReachHere("Unexpected NUMBER token");
+    if (tokenizer.ttype == StreamTokenizer.TT_EOL)
+      Assert.shouldNeverReachHere("Unexpected EOL token");
+
+    String tokenStr = tokenString();
+    throw new ParseException("Expected " + expected + " but found " + tokenStr);
+  }
+
+  /**
+   * Gets a description of the current token
+   *
+   * @return a description of the current token
+   */
+  private String tokenString()
+  {
+    switch (tokenizer.ttype) {
+      case StreamTokenizer.TT_NUMBER:
+        return "<NUMBER>";
+      case StreamTokenizer.TT_EOL:
+        return "End-of-Line";
+      case StreamTokenizer.TT_EOF: return "End-of-Stream";
+      case StreamTokenizer.TT_WORD: return "'" + tokenizer.sval + "'";
+    }
+    return "'" + (char) tokenizer.ttype + "'";
+  }
+
+  /**
    *  Creates a <code>Geometry</code> using the next token in the stream.
    *
    *@param  tokenizer        tokenizer over a stream of text in Well-known Text
@@ -319,33 +405,33 @@ public class WKTReader {
    *      token was encountered
    *@throws  IOException     if an I/O error occurs
    */
-  private Geometry readGeometryTaggedText(StreamTokenizer tokenizer) throws IOException, ParseException {
-    String type = getNextWord(tokenizer);
+  private Geometry readGeometryTaggedText() throws IOException, ParseException {
+    String type = getNextWord();
     if (type.equals("POINT")) {
-      return readPointText(tokenizer);
+      return readPointText();
     }
-    else if (type.equals("LINESTRING")) {
-      return readLineStringText(tokenizer);
+    else if (type.equalsIgnoreCase("LINESTRING")) {
+      return readLineStringText();
     }
-    else if (type.equals("LINEARRING")) {
-      return readLinearRingText(tokenizer);
+    else if (type.equalsIgnoreCase("LINEARRING")) {
+      return readLinearRingText();
     }
-    else if (type.equals("POLYGON")) {
-      return readPolygonText(tokenizer);
+    else if (type.equalsIgnoreCase("POLYGON")) {
+      return readPolygonText();
     }
-    else if (type.equals("MULTIPOINT")) {
-      return readMultiPointText(tokenizer);
+    else if (type.equalsIgnoreCase("MULTIPOINT")) {
+      return readMultiPointText();
     }
-    else if (type.equals("MULTILINESTRING")) {
-      return readMultiLineStringText(tokenizer);
+    else if (type.equalsIgnoreCase("MULTILINESTRING")) {
+      return readMultiLineStringText();
     }
-    else if (type.equals("MULTIPOLYGON")) {
-      return readMultiPolygonText(tokenizer);
+    else if (type.equalsIgnoreCase("MULTIPOLYGON")) {
+      return readMultiPolygonText();
     }
-    else if (type.equals("GEOMETRYCOLLECTION")) {
-      return readGeometryCollectionText(tokenizer);
+    else if (type.equalsIgnoreCase("GEOMETRYCOLLECTION")) {
+      return readGeometryCollectionText();
     }
-    throw new ParseException("Unknown type: " + type);
+    throw new ParseException("Unknown geometry type: " + type);
   }
 
   /**
@@ -358,13 +444,13 @@ public class WKTReader {
    *@throws  IOException     if an I/O error occurs
    *@throws  ParseException  if an unexpected token was encountered
    */
-  private Point readPointText(StreamTokenizer tokenizer) throws IOException, ParseException {
-    String nextToken = getNextEmptyOrOpener(tokenizer);
-    if (nextToken.equals("EMPTY")) {
+  private Point readPointText() throws IOException, ParseException {
+    String nextToken = getNextEmptyOrOpener();
+    if (nextToken.equals(EMPTY)) {
       return geometryFactory.createPoint((Coordinate)null);
     }
-    Point point = geometryFactory.createPoint(getPreciseCoordinate(tokenizer));
-    getNextCloser(tokenizer);
+    Point point = geometryFactory.createPoint(getPreciseCoordinate());
+    getNextCloser();
     return point;
   }
 
@@ -378,8 +464,8 @@ public class WKTReader {
    *@throws  IOException     if an I/O error occurs
    *@throws  ParseException  if an unexpected token was encountered
    */
-  private LineString readLineStringText(StreamTokenizer tokenizer) throws IOException, ParseException {
-    return geometryFactory.createLineString(getCoordinates(tokenizer));
+  private LineString readLineStringText() throws IOException, ParseException {
+    return geometryFactory.createLineString(getCoordinates());
   }
 
   /**
@@ -394,10 +480,10 @@ public class WKTReader {
    *      do not form a closed linestring, or if an unexpected token was
    *      encountered
    */
-  private LinearRing readLinearRingText(StreamTokenizer tokenizer)
+  private LinearRing readLinearRingText()
     throws IOException, ParseException
   {
-    return geometryFactory.createLinearRing(getCoordinates(tokenizer));
+    return geometryFactory.createLinearRing(getCoordinates());
   }
 
   /**
@@ -410,8 +496,8 @@ public class WKTReader {
    *@throws  IOException     if an I/O error occurs
    *@throws  ParseException  if an unexpected token was encountered
    */
-  private MultiPoint readMultiPointText(StreamTokenizer tokenizer) throws IOException, ParseException {
-    return geometryFactory.createMultiPoint(toPoints(getCoordinates(tokenizer)));
+  private MultiPoint readMultiPointText() throws IOException, ParseException {
+    return geometryFactory.createMultiPoint(toPoints(getCoordinates()));
   }
 
   /**
@@ -443,19 +529,19 @@ public class WKTReader {
    *      token was encountered.
    *@throws  IOException     if an I/O error occurs
    */
-  private Polygon readPolygonText(StreamTokenizer tokenizer) throws IOException, ParseException {
-    String nextToken = getNextEmptyOrOpener(tokenizer);
-    if (nextToken.equals("EMPTY")) {
+  private Polygon readPolygonText() throws IOException, ParseException {
+    String nextToken = getNextEmptyOrOpener();
+    if (nextToken.equals(EMPTY)) {
         return geometryFactory.createPolygon(geometryFactory.createLinearRing(
             new Coordinate[]{}), new LinearRing[]{});
     }
     ArrayList holes = new ArrayList();
-    LinearRing shell = readLinearRingText(tokenizer);
-    nextToken = getNextCloserOrComma(tokenizer);
-    while (nextToken.equals(",")) {
-      LinearRing hole = readLinearRingText(tokenizer);
+    LinearRing shell = readLinearRingText();
+    nextToken = getNextCloserOrComma();
+    while (nextToken.equals(COMMA)) {
+      LinearRing hole = readLinearRingText();
       holes.add(hole);
-      nextToken = getNextCloserOrComma(tokenizer);
+      nextToken = getNextCloserOrComma();
     }
     LinearRing[] array = new LinearRing[holes.size()];
     return geometryFactory.createPolygon(shell, (LinearRing[]) holes.toArray(array));
@@ -471,19 +557,19 @@ public class WKTReader {
    *@throws  IOException     if an I/O error occurs
    *@throws  ParseException  if an unexpected token was encountered
    */
-  private com.vividsolutions.jts.geom.MultiLineString readMultiLineStringText(StreamTokenizer tokenizer) throws IOException, ParseException {
-    String nextToken = getNextEmptyOrOpener(tokenizer);
-    if (nextToken.equals("EMPTY")) {
+  private com.vividsolutions.jts.geom.MultiLineString readMultiLineStringText() throws IOException, ParseException {
+    String nextToken = getNextEmptyOrOpener();
+    if (nextToken.equals(EMPTY)) {
       return geometryFactory.createMultiLineString(new LineString[]{});
     }
     ArrayList lineStrings = new ArrayList();
-    LineString lineString = readLineStringText(tokenizer);
+    LineString lineString = readLineStringText();
     lineStrings.add(lineString);
-    nextToken = getNextCloserOrComma(tokenizer);
-    while (nextToken.equals(",")) {
-      lineString = readLineStringText(tokenizer);
+    nextToken = getNextCloserOrComma();
+    while (nextToken.equals(COMMA)) {
+      lineString = readLineStringText();
       lineStrings.add(lineString);
-      nextToken = getNextCloserOrComma(tokenizer);
+      nextToken = getNextCloserOrComma();
     }
     LineString[] array = new LineString[lineStrings.size()];
     return geometryFactory.createMultiLineString((LineString[]) lineStrings.toArray(array));
@@ -500,19 +586,19 @@ public class WKTReader {
    *@throws  IOException     if an I/O error occurs
    *@throws  ParseException  if an unexpected token was encountered
    */
-  private MultiPolygon readMultiPolygonText(StreamTokenizer tokenizer) throws IOException, ParseException {
-    String nextToken = getNextEmptyOrOpener(tokenizer);
-    if (nextToken.equals("EMPTY")) {
+  private MultiPolygon readMultiPolygonText() throws IOException, ParseException {
+    String nextToken = getNextEmptyOrOpener();
+    if (nextToken.equals(EMPTY)) {
       return geometryFactory.createMultiPolygon(new Polygon[]{});
     }
     ArrayList polygons = new ArrayList();
-    Polygon polygon = readPolygonText(tokenizer);
+    Polygon polygon = readPolygonText();
     polygons.add(polygon);
-    nextToken = getNextCloserOrComma(tokenizer);
-    while (nextToken.equals(",")) {
-      polygon = readPolygonText(tokenizer);
+    nextToken = getNextCloserOrComma();
+    while (nextToken.equals(COMMA)) {
+      polygon = readPolygonText();
       polygons.add(polygon);
-      nextToken = getNextCloserOrComma(tokenizer);
+      nextToken = getNextCloserOrComma();
     }
     Polygon[] array = new Polygon[polygons.size()];
     return geometryFactory.createMultiPolygon((Polygon[]) polygons.toArray(array));
@@ -531,22 +617,23 @@ public class WKTReader {
    *      token was encountered
    *@throws  IOException     if an I/O error occurs
    */
-  private GeometryCollection readGeometryCollectionText(StreamTokenizer tokenizer) throws IOException, ParseException {
-    String nextToken = getNextEmptyOrOpener(tokenizer);
-    if (nextToken.equals("EMPTY")) {
+  private GeometryCollection readGeometryCollectionText() throws IOException, ParseException {
+    String nextToken = getNextEmptyOrOpener();
+    if (nextToken.equals(EMPTY)) {
       return geometryFactory.createGeometryCollection(new Geometry[]{});
     }
     ArrayList geometries = new ArrayList();
-    Geometry geometry = readGeometryTaggedText(tokenizer);
+    Geometry geometry = readGeometryTaggedText();
     geometries.add(geometry);
-    nextToken = getNextCloserOrComma(tokenizer);
-    while (nextToken.equals(",")) {
-      geometry = readGeometryTaggedText(tokenizer);
+    nextToken = getNextCloserOrComma();
+    while (nextToken.equals(COMMA)) {
+      geometry = readGeometryTaggedText();
       geometries.add(geometry);
-      nextToken = getNextCloserOrComma(tokenizer);
+      nextToken = getNextCloserOrComma();
     }
     Geometry[] array = new Geometry[geometries.size()];
     return geometryFactory.createGeometryCollection((Geometry[]) geometries.toArray(array));
   }
+
 }
 
diff --git a/src/com/vividsolutions/jts/io/WKTWriter.java b/src/com/vividsolutions/jts/io/WKTWriter.java
index 192b93d..91a13b3 100644
--- a/src/com/vividsolutions/jts/io/WKTWriter.java
+++ b/src/com/vividsolutions/jts/io/WKTWriter.java
@@ -1,5 +1,3 @@
-
-
 /*
  * The JTS Topology Suite is a collection of Java classes that
  * implement the fundamental operations required to validate a given
@@ -42,25 +40,76 @@ import java.text.DecimalFormat;
 import java.text.DecimalFormatSymbols;
 
 /**
- * Outputs the textual representation of a {@link Geometry}.
+ * Outputs the Well-Known Text representation of a {@link Geometry}.
+ * The Well-known Text format is defined in the
+ * <A HREF="http://www.opengis.org/techno/specs.htm">
+ * OGC Simple Features Specification for SQL</A>.
  * <p>
  * The <code>WKTWriter</code> outputs coordinates rounded to the precision
  * model. No more than the maximum number of necessary decimal places will be
  * output.
  * <p>
- * The Well-known Text format is defined in the <A
- * HREF="http://www.opengis.org/techno/specs.htm">OpenGIS Simple Features
- * Specification for SQL</A>.
- * <p>
- * A non-standard "LINEARRING" tag is used for LinearRings. The WKT spec does
- * not define a special tag for LinearRings. The standard tag to use is
- * "LINESTRING".
+ * A non-standard <code>LINEARRING</code> tag is used for LinearRings.
+ * The SFS WKT spec does not define a special tag for <code>LinearRing</code>s.
+ * Under it, rings are output using <code>LINESTRING</code>.
  *
- * @version 1.6
+ * @version 1.7
+ * @see WKTReader
  */
-public class WKTWriter {
+public class WKTWriter
+{
+  /**
+   * Generates the WKT for a <code>Point</code>.
+   *
+   * @param p0 the point coordinate
+   *
+   * @return the WKT
+   */
+  public static String toPoint(Coordinate p0)
+  {
+    return "POINT ( " + p0.x + " " + p0.y  + " )";
+  }
+
+  /**
+   * Generates the WKT for a N-point <code>LineString</code>.
+   *
+   * @param seq the sequence to outpout
+   *
+   * @return the WKT
+   */
+  public static String toLineString(CoordinateSequence seq)
+  {
+    StringBuffer buf = new StringBuffer();
+    buf.append("LINESTRING ");
+    if (seq.size() == 0)
+      buf.append(" EMPTY");
+    else {
+      buf.append("(");
+      for (int i = 0; i < seq.size(); i++) {
+        if (i > 0)
+          buf.append(", ");
+        buf.append(seq.getX(i) + " " + seq.getY(i));
+      }
+      buf.append(")");
+    }
+    return buf.toString();
+  }
+
+  /**
+   * Generates the WKT for a 2-point <code>LineString</code>.
+   *
+   * @param p0 the first coordinate
+   * @param p1 the second coordinate
+   *
+   * @return the WKT
+   */
+  public static String toLineString(Coordinate p0, Coordinate p1)
+  {
+    return "LINESTRING ( " + p0.x + " " + p0.y + ", " + p1.x + " " + p1.y + " )";
+  }
 
   private static int INDENT = 2;
+
   /**
    *  Creates the <code>DecimalFormat</code> used to write <code>double</code>s
    *  with a sufficient number of decimal places.
@@ -77,8 +126,8 @@ public class WKTWriter {
     // specify decimal separator explicitly to avoid problems in other locales
     DecimalFormatSymbols symbols = new DecimalFormatSymbols();
     symbols.setDecimalSeparator('.');
-    return new DecimalFormat("#" + (decimalPlaces > 0 ? "." : "")
-         + stringOfChar('#', decimalPlaces), symbols);
+    return new DecimalFormat("0" + (decimalPlaces > 0 ? "." : "")
+                 +  stringOfChar('#', decimalPlaces), symbols);
   }
 
   /**
@@ -362,25 +411,34 @@ public class WKTWriter {
     }
     else {
       writer.write("(");
-      appendCoordinate(coordinate, writer, precisionModel);
+      appendCoordinate(coordinate, writer);
       writer.write(")");
     }
   }
 
   /**
+   *  Appends the i'th coordinate from the sequence to the writer
+   *
+   *@param  seq      the <code>CoordinateSequence</code> to process
+   * @param i the index of the coordinate to write
+   *@param  writer          the output writer to append to
+   */
+  private void appendCoordinate(CoordinateSequence seq, int i, Writer writer)
+    throws IOException
+  {
+    writer.write(writeNumber(seq.getX(i)) + " " + writeNumber(seq.getY(i)));
+  }
+
+  /**
    *  Converts a <code>Coordinate</code> to <Point> format, then appends
    *  it to the writer.
    *
    *@param  coordinate      the <code>Coordinate</code> to process
    *@param  writer          the output writer to append to
-   *@param  precisionModel  the <code>PrecisionModel</code> to use to convert
-   *      from a precise coordinate to an external coordinate
    */
-  private void appendCoordinate(Coordinate coordinate, Writer writer, PrecisionModel precisionModel)
+  private void appendCoordinate(Coordinate coordinate, Writer writer)
     throws IOException
   {
-    //Coordinate externalCoordinate = new Coordinate();
-    //precisionModel.toExternal(coordinate, externalCoordinate);
     writer.write(writeNumber(coordinate.x) + " " + writeNumber(coordinate.y));
   }
 
@@ -403,6 +461,33 @@ public class WKTWriter {
    *@param  lineString  the <code>LineString</code> to process
    *@param  writer      the output writer to append to
    */
+  private void appendSequenceText(CoordinateSequence seq, int level, boolean doIndent, Writer writer)
+    throws IOException
+  {
+    if (seq.size() == 0) {
+      writer.write("EMPTY");
+    }
+    else {
+      if (doIndent) indent(level, writer);
+      writer.write("(");
+      for (int i = 0; i < seq.size(); i++) {
+        if (i > 0) {
+          writer.write(", ");
+          if (i % 10 == 0) indent(level + 2, writer);
+        }
+        appendCoordinate(seq, i, writer);
+      }
+      writer.write(")");
+    }
+  }
+
+  /**
+   *  Converts a <code>LineString</code> to <LineString Text> format, then
+   *  appends it to the writer.
+   *
+   *@param  lineString  the <code>LineString</code> to process
+   *@param  writer      the output writer to append to
+   */
   private void appendLineStringText(LineString lineString, int level, boolean doIndent, Writer writer)
     throws IOException
   {
@@ -417,7 +502,7 @@ public class WKTWriter {
           writer.write(", ");
           if (i % 10 == 0) indent(level + 2, writer);
         }
-        appendCoordinate(lineString.getCoordinateN(i), writer, lineString.getPrecisionModel());
+        appendCoordinate(lineString.getCoordinateN(i), writer);
       }
       writer.write(")");
     }
@@ -467,8 +552,7 @@ public class WKTWriter {
         if (i > 0) {
           writer.write(", ");
         }
-        appendCoordinate(((Point) multiPoint.getGeometryN(i)).getCoordinate(), writer,
-            multiPoint.getPrecisionModel());
+        appendCoordinate(((Point) multiPoint.getGeometryN(i)).getCoordinate(), writer);
       }
       writer.write(")");
     }
diff --git a/src/com/vividsolutions/jts/linearref/ExtractLineByLocation.java b/src/com/vividsolutions/jts/linearref/ExtractLineByLocation.java
new file mode 100644
index 0000000..6d4a0e5
--- /dev/null
+++ b/src/com/vividsolutions/jts/linearref/ExtractLineByLocation.java
@@ -0,0 +1,175 @@
+package com.vividsolutions.jts.linearref;
+
+import com.vividsolutions.jts.geom.*;
+import com.vividsolutions.jts.util.Assert;
+
+/**
+ * Extracts the subline of a linear {@link Geometry} between
+ * two {@link LinearLocation}s on the line.
+ */
+class ExtractLineByLocation
+{
+  /**
+   * Computes the subline of a {@link LineString} between
+   * two {@link LineStringLocation}s on the line.
+   * If the start location is after the end location,
+   * the computed geometry is reversed.
+   *
+   * @param line the line to use as the baseline
+   * @param start the start location
+   * @param end the end location
+   * @return the extracted subline
+   */
+  public static Geometry extract(Geometry line, LinearLocation start, LinearLocation end)
+  {
+    ExtractLineByLocation ls = new ExtractLineByLocation(line);
+    return ls.extract(start, end);
+  }
+
+  private Geometry line;
+
+  public ExtractLineByLocation(Geometry line) {
+    this.line = line;
+  }
+
+  /**
+   * Extracts a subline of the input.
+   * If <code>end < start</code> the linear geometry computed will be reversed.
+   *
+   * @param start the start location
+   * @param end the end location
+   * @return a linear geometry
+   */
+  public Geometry extract(LinearLocation start, LinearLocation end)
+  {
+    if (end.compareTo(start) < 0) {
+      return reverse(computeLinear(end, start));
+    }
+    return computeLinear(start, end);
+  }
+
+  private Geometry reverse(Geometry linear)
+  {
+    if (linear instanceof LineString)
+      return ((LineString) linear).reverse();
+    if (linear instanceof MultiLineString)
+      return ((MultiLineString) linear).reverse();
+    Assert.shouldNeverReachHere("non-linear geometry encountered");
+    return null;
+  }
+  /**
+   * Assumes input is valid (e.g. start <= end)
+   *
+   * @param start
+   * @param end
+   * @return a linear geometry
+   */
+  private LineString computeLine(LinearLocation start, LinearLocation end)
+  {
+    Coordinate[] coordinates = line.getCoordinates();
+    CoordinateList newCoordinates = new CoordinateList();
+
+    int startSegmentIndex = start.getSegmentIndex();
+    if (start.getSegmentFraction() > 0.0)
+      startSegmentIndex += 1;
+    int lastSegmentIndex = end.getSegmentIndex();
+    if (end.getSegmentFraction() == 1.0)
+      lastSegmentIndex += 1;
+    if (lastSegmentIndex >= coordinates.length)
+      lastSegmentIndex = coordinates.length - 1;
+    // not needed - LinearLocation values should always be correct
+    //Assert.isTrue(end.getSegmentFraction() <= 1.0, "invalid segment fraction value");
+
+    if (! start.isVertex())
+      newCoordinates.add(start.getCoordinate(line));
+    for (int i = startSegmentIndex; i <= lastSegmentIndex; i++) {
+      newCoordinates.add(coordinates[i]);
+    }
+    if (! end.isVertex())
+      newCoordinates.add(end.getCoordinate(line));
+
+    // ensure there is at least one coordinate in the result
+    if (newCoordinates.size() <= 0)
+      newCoordinates.add(start.getCoordinate(line));
+
+    Coordinate[] newCoordinateArray = newCoordinates.toCoordinateArray();
+    /**
+     * Ensure there is enough coordinates to build a valid line.
+     * Make a 2-point line with duplicate coordinates, if necessary.
+     * There will always be at least one coordinate in the coordList.
+     */
+    if (newCoordinateArray.length <= 1) {
+      newCoordinateArray = new Coordinate[] { newCoordinateArray[0], newCoordinateArray[0]};
+    }
+    return line.getFactory().createLineString(newCoordinateArray);
+  }
+
+  /**
+   * Assumes input is valid (e.g. start <= end)
+   *
+   * @param start
+   * @param end
+   * @return a linear geometry
+   */
+  private Geometry computeLinear(LinearLocation start, LinearLocation end)
+  {
+    LinearGeometryBuilder builder = new LinearGeometryBuilder(line.getFactory());
+    builder.setFixInvalidLines(true);
+
+    if (! start.isVertex())
+      builder.add(start.getCoordinate(line));
+
+    for (LinearIterator it = new LinearIterator(line, start); it.hasNext(); it.next()) {
+      if (end.compareLocationValues(it.getComponentIndex(), it.getVertexIndex(), 0.0)
+        < 0)
+        break;
+
+      Coordinate pt = it.getSegmentStart();
+      builder.add(pt);
+      if (it.isEndOfLine())
+        builder.endLine();
+    }
+    if (! end.isVertex())
+      builder.add(end.getCoordinate(line));
+
+    return builder.getGeometry();
+  }
+
+  /**
+   * Computes a valid and normalized location
+   * compatible with the values in a LinearIterator.
+   * (I.e. segmentFractions of 1.0 are converted to the next highest coordinate index)
+   */
+  /*
+  private LinearLocation normalize(LinearLocation loc)
+  {
+    int componentIndex = loc.getComponentIndex();
+    int segmentIndex = loc.getSegmentIndex();
+    double segmentFraction = loc.getSegmentFraction();
+
+    if (segmentFraction < 0.0) {
+      segmentFraction = 0.0;
+    }
+    if (segmentFraction > 1.0) {
+      segmentFraction = 1.0;
+    }
+
+    if (componentIndex < 0) {
+      componentIndex = 0;
+      segmentIndex = 0;
+      segmentFraction = 0.0;
+    }
+    if (segmentIndex < 0) {
+      segmentIndex = 0;
+      segmentFraction = 0.0;
+    }
+
+    if (segmentFraction == 1.0) {
+      segmentFraction = 0.0;
+      segmentIndex += 1;
+    }
+
+    return new LinearLocation(componentIndex, segmentIndex, segmentFraction);
+  }
+  */
+}
\ No newline at end of file
diff --git a/src/com/vividsolutions/jts/linearref/LengthIndexOfPoint.java b/src/com/vividsolutions/jts/linearref/LengthIndexOfPoint.java
new file mode 100644
index 0000000..a00e6f4
--- /dev/null
+++ b/src/com/vividsolutions/jts/linearref/LengthIndexOfPoint.java
@@ -0,0 +1,116 @@
+package com.vividsolutions.jts.linearref;
+
+import com.vividsolutions.jts.geom.*;
+import com.vividsolutions.jts.util.Assert;
+
+/**
+ * Computes the length index of the point
+ * on a linear {@link Geometry} nearest a given {@link Coordinate}.
+ * The nearest point is not necessarily unique; this class
+ * always computes the nearest point closest to
+ * the start of the geometry.
+ */
+class LengthIndexOfPoint
+{
+  public static double indexOf(Geometry linearGeom, Coordinate inputPt)
+  {
+    LengthIndexOfPoint locater = new LengthIndexOfPoint(linearGeom);
+    return locater.indexOf(inputPt);
+  }
+
+  public static double indexOfAfter(Geometry linearGeom, Coordinate inputPt, double minIndex)
+  {
+    LengthIndexOfPoint locater = new LengthIndexOfPoint(linearGeom);
+    return locater.indexOfAfter(inputPt, minIndex);
+  }
+
+  private Geometry linearGeom;
+
+  public LengthIndexOfPoint(Geometry linearGeom) {
+    this.linearGeom = linearGeom;
+  }
+
+  /**
+   * Find the nearest location along a linear {@link Geometry} to a given point.
+   *
+   * @param inputPt the coordinate to locate
+   * @return the location of the nearest point
+   */
+  public double indexOf(Coordinate inputPt)
+  {
+    return indexOfFromStart(inputPt, -1.0);
+  }
+
+  /**
+   * Finds the nearest index along the linear {@link Geometry}
+   * to a given {@link Coordinate}
+   * after the specified minimum index.
+   * If possible the location returned will be strictly greater than the
+   * <code>minLocation</code>.
+   * If this is not possible, the
+   * value returned will equal <code>minLocation</code>.
+   * (An example where this is not possible is when
+   * minLocation = [end of line] ).
+   *
+   * @param inputPt the coordinate to locate
+   * @param minLocation the minimum location for the point location
+   * @return the location of the nearest point
+   */
+  public double indexOfAfter(Coordinate inputPt, double minIndex)
+  {
+    if (minIndex < 0.0) return indexOf(inputPt);
+
+    // sanity check for minIndex at or past end of line
+    double endIndex = linearGeom.getLength();
+    if (endIndex < minIndex)
+      return endIndex;
+
+    double closestAfter = indexOfFromStart(inputPt, minIndex);
+    /**
+     * Return the minDistanceLocation found.
+     * This will not be null, since it was initialized to minLocation
+     */
+    Assert.isTrue(closestAfter > minIndex,
+                  "computed index is before specified minimum index");
+    return closestAfter;
+  }
+
+  private double indexOfFromStart(Coordinate inputPt, double minIndex)
+  {
+    double minDistance = Double.MAX_VALUE;
+
+    double ptMeasure = minIndex;
+    double segmentStartMeasure = 0.0;
+    LineSegment seg = new LineSegment();
+    LinearIterator it = new LinearIterator(linearGeom);
+    while (it.hasNext()) {
+      if (! it.isEndOfLine()) {
+        seg.p0 = it.getSegmentStart();
+        seg.p1 = it.getSegmentEnd();
+        double segDistance = seg.distance(inputPt);
+        double segMeasureToPt = segmentNearestMeasure(seg, inputPt, segmentStartMeasure);
+        if (segDistance < minDistance
+            && segMeasureToPt > minIndex) {
+          ptMeasure = segMeasureToPt;
+          minDistance = segDistance;
+        }
+        segmentStartMeasure += seg.getLength();
+      }
+      it.next();
+    }
+    return ptMeasure;
+  }
+
+  private double segmentNearestMeasure(LineSegment seg, Coordinate inputPt,
+                            double segmentStartMeasure)
+  {
+    // found new minimum, so compute location distance of point
+    double projFactor = seg.projectionFactor(inputPt);
+    if (projFactor <= 0.0)
+      return segmentStartMeasure;
+    if (projFactor <= 1.0)
+      return segmentStartMeasure + projFactor * seg.getLength();
+    // projFactor > 1.0
+    return segmentStartMeasure + seg.getLength();
+  }
+}
diff --git a/src/com/vividsolutions/jts/linearref/LengthIndexedLine.java b/src/com/vividsolutions/jts/linearref/LengthIndexedLine.java
new file mode 100644
index 0000000..b131954
--- /dev/null
+++ b/src/com/vividsolutions/jts/linearref/LengthIndexedLine.java
@@ -0,0 +1,196 @@
+package com.vividsolutions.jts.linearref;
+
+import com.vividsolutions.jts.geom.*;
+
+/**
+ * Supports linear referencing along a linear {@link Geometry}
+ * using the length along the line as the index.
+ * 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.
+ * Non-simple lines (i.e. which loop back to cross or touch
+ * themselves) are supported.
+ */
+public class LengthIndexedLine
+{
+  private Geometry linearGeom;
+
+  /**
+   * Constructs an object which allows a linear {@link Geometry}
+   * to be linearly referenced using length as an index.
+   *
+   * @param linearGeom the linear geometry to reference along
+   */
+  public LengthIndexedLine(Geometry linearGeom) {
+    this.linearGeom = linearGeom;
+  }
+
+  /**
+   * Computes the {@link Coordinate} for the point
+   * on the line at the given index.
+   * If the index is out of range the first or last point on the
+   * line will be returned.
+   *
+   * @param index the index of the desired point
+   * @return the Coordinate at the given index
+   */
+  public Coordinate extractPoint(double index)
+  {
+    LinearLocation loc = LengthLocationMap.getLocation(linearGeom, index);
+    return loc.getCoordinate(linearGeom);
+  }
+
+  /**
+   * Computes the {@link LineString} for the interval
+   * on the line between the given indices.
+   * If the endIndex lies before the startIndex,
+   * the computed geometry is reversed.
+   *
+   * @param startIndex the index of the start of the interval
+   * @param endIndex the index of the end of the interval
+   * @return the linear interval between the indices
+   */
+  public Geometry extractLine(double startIndex, double endIndex)
+  {
+    LocationIndexedLine lil = new LocationIndexedLine(linearGeom);
+    LinearLocation startLoc = locationOf(startIndex);
+    LinearLocation endLoc = locationOf(endIndex);
+    return ExtractLineByLocation.extract(linearGeom, startLoc, endLoc);
+  }
+
+  private LinearLocation locationOf(double index)
+  {
+    return LengthLocationMap.getLocation(linearGeom, index);
+  }
+
+  /**
+   * Computes the minimum index for a point on the line.
+   * If the line is not simple (i.e. loops back on itself)
+   * a single point may have more than one possible index.
+   * In this case, the smallest index is returned.
+   *
+   * The supplied point does not <i>necessarily</i> have to lie precisely
+   * on the line, but if it is far from the line the accuracy and
+   * performance of this function is not guaranteed.
+   * Use {@link #project} to compute a guaranteed result for points
+   * which may be far from the line.
+   *
+   * @param pt a point on the line
+   * @return the minimum index of the point
+   *
+   * @see project
+   */
+  public double indexOf(Coordinate pt)
+  {
+    return LengthIndexOfPoint.indexOf(linearGeom, pt);
+  }
+
+  /**
+   * Finds the index for a point on the line
+   * which is greater than the given index.
+   * If no such index exists, returns <tt>minIndex</tt>.
+   * This method can be used to determine all indexes for
+   * a point which occurs more than once on a non-simple line.
+   * It can also be used to disambiguate cases where the given point lies
+   * slightly off the line and is equidistant from two different
+   * points on the line.
+   *
+   * The supplied point does not <i>necessarily</i> have to lie precisely
+   * on the line, but if it is far from the line the accuracy and
+   * performance of this function is not guaranteed.
+   * Use {@link #project} to compute a guaranteed result for points
+   * which may be far from the line.
+   *
+   * @param pt a point on the line
+   * @param minIndex the value the returned index must be greater than
+   * @return the index of the point greater than the given minimum index
+   *
+   * @see project
+   */
+  public double indexOfAfter(Coordinate pt, double minIndex)
+  {
+    return LengthIndexOfPoint.indexOfAfter(linearGeom, pt, minIndex);
+  }
+
+  /**
+   * Computes the indices for a subline of the line.
+   * (The subline must <b>conform</b> to the line; that is,
+   * all vertices in the subline (except possibly the first and last)
+   * must be vertices of the line and occcur in the same order).
+   *
+   * @param subLine a subLine of the line
+   * @return a pair of indices for the start and end of the subline.
+   */
+  public double[] indicesOf(Geometry subLine)
+  {
+    LinearLocation[] locIndex = LocationIndexOfLine.indicesOf(linearGeom, subLine);
+    double[] index = new double[] {
+      LengthLocationMap.getLength(linearGeom, locIndex[0]),
+      LengthLocationMap.getLength(linearGeom, locIndex[1])
+      };
+    return index;
+  }
+
+
+  /**
+   * Computes the index for the closest point on the line to the given point.
+   * If more than one point has the closest distance the first one along the line
+   * is returned.
+   * (The point does not necessarily have to lie precisely on the line.)
+   *
+   * @param pt a point on the line
+   * @return the index of the point
+   */
+  public double project(Coordinate pt)
+  {
+    return LengthIndexOfPoint.indexOf(linearGeom, pt);
+  }
+
+  /**
+   * Returns the index of the start of the line
+   * @return the start index
+   */
+  public double getStartIndex()
+  {
+    return 0.0;
+  }
+
+  /**
+   * Returns the index of the end of the line
+   * @return the end index
+   */
+  public double getEndIndex()
+  {
+    return linearGeom.getLength();
+  }
+
+  /**
+   * Tests whether an index is in the valid index range for the line.
+   *
+   * @param length the index to test
+   * @return <code>true</code> if the index is in the valid range
+   */
+  public boolean isValidIndex(double index)
+  {
+    return (index >= getStartIndex()
+            && index <= getEndIndex());
+  }
+
+  /**
+   * Computes a valid index for this line
+   * by clamping the given index to the valid range of index values
+   *
+   * @return a valid index value
+   */
+  public double clampIndex(double index)
+  {
+    double startIndex = getStartIndex();
+    if (index < startIndex) return startIndex;
+
+    double endIndex = getEndIndex();
+    if (index > endIndex) return endIndex;
+
+    return index;
+  }
+}
\ No newline at end of file
diff --git a/src/com/vividsolutions/jts/linearref/LengthLocationMap.java b/src/com/vividsolutions/jts/linearref/LengthLocationMap.java
new file mode 100644
index 0000000..7461fbd
--- /dev/null
+++ b/src/com/vividsolutions/jts/linearref/LengthLocationMap.java
@@ -0,0 +1,119 @@
+package com.vividsolutions.jts.linearref;
+
+import com.vividsolutions.jts.geom.*;
+
+/**
+ * Computes the {@link LinearLocation} for a given length
+ * along a linear {@link Geometry}.
+ * Negative lengths are measured in reverse from end of the linear geometry.
+ * Out-of-range values are clamped.
+ */
+public class LengthLocationMap
+{
+  // TODO: cache computed cumulative length for each vertex
+  // TODO: support user-defined measures
+  // TODO: support measure index for fast mapping to a location
+
+  /**
+   * Computes the {@link LinearLocation} for a
+   * given length along a linear {@link Geometry}.
+   *
+   * @param line the linear geometry to use
+   * @param length the length index of the location
+   * @return the {@link LinearLocation} for the length
+   */
+  public static LinearLocation getLocation(Geometry linearGeom, double length)
+  {
+    LengthLocationMap locater = new LengthLocationMap(linearGeom);
+    return locater.getLocation(length);
+  }
+
+  /**
+   * Computes the length for a given {@link LinearLocation}
+   * on a linear {@link Geometry}.
+   *
+   * @param line the linear geometry to use
+   * @param loc the {@link LinearLocation} index of the location
+   * @return the length for the {@link LinearLocation}
+   */
+  public static double getLength(Geometry linearGeom, LinearLocation loc)
+  {
+    LengthLocationMap locater = new LengthLocationMap(linearGeom);
+    return locater.getLength(loc);
+  }
+
+  private Geometry linearGeom;
+
+  public LengthLocationMap(Geometry linearGeom)
+  {
+    this.linearGeom = linearGeom;
+  }
+
+  /**
+   * Compute the {@link LinearLocation} corresponding to a length.
+   * Negative lengths are measured in reverse from end of the linear geometry.
+   * Out-of-range values are clamped.
+   *
+   * @param length the length index
+   * @return the corresponding LinearLocation
+   */
+  public LinearLocation getLocation(double length)
+  {
+    double forwardLength = length;
+    if (length < 0.0) {
+      double lineLen = linearGeom.getLength();
+      forwardLength = lineLen + length;
+    }
+    return getLocationForward(forwardLength);
+  }
+
+  private LinearLocation getLocationForward(double length)
+  {
+    if (length <= 0.0)
+      return new LinearLocation();
+
+    double totalLength = 0.0;
+
+    LinearIterator it = new LinearIterator(linearGeom);
+    while (it.hasNext()) {
+      if (! it.isEndOfLine()) {
+        Coordinate p0 = it.getSegmentStart();
+        Coordinate p1 = it.getSegmentEnd();
+        double segLen = p1.distance(p0);
+        // length falls in this segment
+        if (totalLength + segLen > length) {
+          double frac = (length - totalLength) / segLen;
+          int compIndex = it.getComponentIndex();
+          int segIndex = it.getVertexIndex();
+          return new LinearLocation(compIndex, segIndex, frac);
+        }
+        totalLength += segLen;
+      }
+      it.next();
+    }
+    // length is longer than line - return end location
+    return LinearLocation.getEndLocation(linearGeom);
+  }
+
+  public double getLength(LinearLocation loc)
+  {
+    double totalLength = 0.0;
+
+    LinearIterator it = new LinearIterator(linearGeom);
+    while (it.hasNext()) {
+      if (! it.isEndOfLine()) {
+        Coordinate p0 = it.getSegmentStart();
+        Coordinate p1 = it.getSegmentEnd();
+        double segLen = p1.distance(p0);
+        // length falls in this segment
+        if (loc.getComponentIndex() == it.getComponentIndex()
+            && loc.getSegmentIndex() == it.getVertexIndex()) {
+          return totalLength + segLen * loc.getSegmentFraction();
+        }
+        totalLength += segLen;
+      }
+      it.next();
+    }
+    return totalLength;
+  }
+}
diff --git a/src/com/vividsolutions/jts/linearref/LinearGeometryBuilder.java b/src/com/vividsolutions/jts/linearref/LinearGeometryBuilder.java
new file mode 100644
index 0000000..89dcccb
--- /dev/null
+++ b/src/com/vividsolutions/jts/linearref/LinearGeometryBuilder.java
@@ -0,0 +1,119 @@
+package com.vividsolutions.jts.linearref;
+
+import java.util.*;
+import com.vividsolutions.jts.geom.*;
+
+/**
+ * Builds a linear geometry ({@link LineString} or {@link MultiLineString})
+ * incrementally (point-by-point).
+ *
+ * @version 1.7
+ */
+public class LinearGeometryBuilder
+{
+  private GeometryFactory geomFact;
+  private List lines = new ArrayList();
+  private CoordinateList coordList = null;
+
+  private boolean ignoreInvalidLines = false;
+  private boolean fixInvalidLines = false;
+
+  private Coordinate lastPt = null;
+
+  public LinearGeometryBuilder(GeometryFactory geomFact) {
+    this.geomFact = geomFact;
+  }
+
+  /**
+   * Allows invalid lines to be ignored rather than causing Exceptions.
+   * An invalid line is one which has only one unique point.
+   *
+   * @param ignoreShortLines <code>true</code> if short lines are to be ignored
+   */
+  public void setIgnoreInvalidLines(boolean ignoreInvalidLines)
+  {
+    this.ignoreInvalidLines = ignoreInvalidLines;
+  }
+
+  /**
+   * Allows invalid lines to be ignored rather than causing Exceptions.
+   * An invalid line is one which has only one unique point.
+   *
+   * @param ignoreShortLines <code>true</code> if short lines are to be ignored
+   */
+  public void setFixInvalidLines(boolean fixInvalidLines)
+  {
+    this.fixInvalidLines = fixInvalidLines;
+  }
+
+  /**
+   * Adds a point to the current line.
+   *
+   * @param pt the Coordinate to add
+   */
+  public void add(Coordinate pt)
+  {
+    add(pt, true);
+  }
+
+  /**
+   * Adds a point to the current line.
+   *
+   * @param pt the Coordinate to add
+   */
+  public void add(Coordinate pt, boolean allowRepeatedPoints)
+  {
+    if (coordList == null)
+      coordList = new CoordinateList();
+    coordList.add(pt, allowRepeatedPoints);
+    lastPt = pt;
+  }
+
+  public Coordinate getLastCoordinate() { return lastPt; }
+
+  /**
+   * Terminate the current LineString.
+   */
+  public void endLine()
+  {
+    if (coordList == null) {
+      return;
+    }
+    if (ignoreInvalidLines && coordList.size() < 2) {
+      coordList = null;
+      return;
+    }
+    Coordinate[] rawPts = coordList.toCoordinateArray();
+    Coordinate[] pts = rawPts;
+    if (fixInvalidLines)
+      pts = validCoordinateSequence(rawPts);
+
+    coordList = null;
+    LineString line = null;
+    try {
+      line = geomFact.createLineString(pts);
+    }
+    catch (IllegalArgumentException ex) {
+      // exception is due to too few points in line.
+      // only propagate if not ignoring short lines
+      if (! ignoreInvalidLines)
+        throw ex;
+    }
+
+    if (line != null) lines.add(line);
+  }
+
+  private Coordinate[] validCoordinateSequence(Coordinate[] pts)
+  {
+    if (pts.length >= 2) return pts;
+    Coordinate[] validPts = new Coordinate[] { pts[0], pts[0]};
+    return validPts;
+  }
+
+  public Geometry getGeometry()
+  {
+    // end last line in case it was not done by user
+    endLine();
+    return geomFact.buildGeometry(lines);
+  }
+}
\ No newline at end of file
diff --git a/src/com/vividsolutions/jts/linearref/LinearIterator.java b/src/com/vividsolutions/jts/linearref/LinearIterator.java
new file mode 100644
index 0000000..a2aa2ea
--- /dev/null
+++ b/src/com/vividsolutions/jts/linearref/LinearIterator.java
@@ -0,0 +1,166 @@
+package com.vividsolutions.jts.linearref;
+
+import com.vividsolutions.jts.geom.*;
+
+/**
+ * An iterator over the components and coordinates of a linear geometry
+ * ({@link LineString}s and {@link MultiLineString}s.
+ *
+ * The standard usage pattern for a {@link LinearIterator} is:
+ *
+ * <pre>
+ * for (LinearIterator it = new LinearIterator(...); it.hasNext(); it.next()) {
+ *   ...
+ *   int ci = it.getComponentIndex();   // for example
+ *   int vi = it.getVertexIndex();      // for example
+ *   ...
+ * }
+ * </pre>
+ *
+ * @version 1.7
+ */
+public class LinearIterator
+{
+  private static int segmentEndVertexIndex(LinearLocation loc)
+  {
+    if (loc.getSegmentFraction() > 0.0)
+      return loc.getSegmentIndex() + 1;
+    return loc.getSegmentIndex();
+  }
+
+  private Geometry linear;
+  private final int numLines;
+
+  /**
+   * Invariant: currentLine <> null if the iterator is pointing at a valid coordinate
+   */
+  private LineString currentLine;
+  private int componentIndex = 0;
+  private int vertexIndex = 0;
+
+  /**
+   * Creates an iterator initialized to the start of a linear {@link Geometry}
+   *
+   * @param linear the linear geometry to iterate over
+   */
+  public LinearIterator(Geometry linear) {
+    this(linear, 0, 0);
+  }
+
+  /**
+   * Creates an iterator starting at
+   * a {@link LinearLocation} on a linear {@link Geometry}
+   *
+   * @param linear the linear geometry to iterate over
+   * @param start the location to start at
+   */
+  public LinearIterator(Geometry linear, LinearLocation start) {
+    this(linear, start.getComponentIndex(), segmentEndVertexIndex(start));
+  }
+
+  /**
+   * Creates an iterator starting at
+   * a component and vertex in a linear {@link Geometry}
+   *
+   * @param linear the linear geometry to iterate over
+   * @param componentIndex the component to start at
+   * @param vertexIndex the vertex to start at
+   */
+  public LinearIterator(Geometry linear, int componentIndex, int vertexIndex) {
+    this.linear = linear;
+    numLines = linear.getNumGeometries();
+    this.componentIndex = componentIndex;
+    this.vertexIndex = vertexIndex;
+    loadCurrentLine();
+  }
+
+  private void loadCurrentLine()
+  {
+    if (componentIndex >= numLines) {
+      currentLine = null;
+      return;
+    }
+    currentLine = (LineString) linear.getGeometryN(componentIndex);
+  }
+
+  /**
+   * Tests whether there are any vertices left to iterator over.
+   * @return <code>true</code> if there are more vertices to scan
+   */
+  public boolean hasNext()
+  {
+    if (componentIndex >= numLines) return false;
+    if (componentIndex == numLines - 1
+        && vertexIndex >= currentLine.getNumPoints())
+      return false;
+    return true;
+  }
+
+  /**
+   * Moves the iterator ahead to the next vertex and (possibly) linear component.
+   */
+  public void next()
+  {
+    if (! hasNext()) return;
+
+    vertexIndex++;
+    if (vertexIndex >= currentLine.getNumPoints()) {
+      componentIndex++;
+      loadCurrentLine();
+      vertexIndex = 0;
+    }
+  }
+
+  /**
+   * Checks whether the iterator cursor is pointing to the
+   * endpoint of a linestring.
+   *
+   * @return <code>true</true> if the iterator is at an endpoint
+   */
+  public boolean isEndOfLine() {
+    if (componentIndex >= numLines) return false;
+    //LineString currentLine = (LineString) linear.getGeometryN(componentIndex);
+    if (vertexIndex < currentLine.getNumPoints() - 1)
+      return false;
+    return true;
+  }
+
+  /**
+   * The component index of the vertex the iterator is currently at.
+   * @return the current component index
+   */
+  public int getComponentIndex() { return componentIndex; }
+
+  /**
+   * The vertex index of the vertex the iterator is currently at.
+   * @return the current vertex index
+   */
+  public int getVertexIndex() { return vertexIndex; }
+
+  /**
+   * Gets the {@link LineString} component the iterator is current at.
+   * @return a linestring
+   */
+  public LineString getLine()  {    return currentLine;  }
+
+  /**
+   * Gets the first {@link Coordinate} of the current segment.
+   * (the coordinate of the current vertex).
+   * @return a {@link Coordinate}
+   */
+  public Coordinate getSegmentStart() { return currentLine.getCoordinateN(vertexIndex); }
+
+  /**
+   * Gets the second {@link Coordinate} of the current segment.
+   * (the coordinate of the next vertex).
+   * If the iterator is at the end of a line, <code>null</code> is returned.
+   *
+   * @return a {@link Coordinate} or <code>null</code>
+   */
+  public Coordinate getSegmentEnd()
+  {
+    if (vertexIndex < getLine().getNumPoints() - 1)
+      return currentLine.getCoordinateN(vertexIndex + 1);
+    return null;
+  }
+}
\ No newline at end of file
diff --git a/src/com/vividsolutions/jts/linearref/LinearLocation.java b/src/com/vividsolutions/jts/linearref/LinearLocation.java
new file mode 100644
index 0000000..8df1540
--- /dev/null
+++ b/src/com/vividsolutions/jts/linearref/LinearLocation.java
@@ -0,0 +1,338 @@
+package com.vividsolutions.jts.linearref;
+
+import com.vividsolutions.jts.geom.*;
+
+/**
+ * Represents a location along a {@link LineString} or {@link MultiLineString}.
+ * The referenced geometry is not maintained within
+ * this location, but must be provided for operations which require it.
+ * Various methods are provided to manipulate the location value
+ * and query the geometry it references.
+ */
+public class LinearLocation
+    implements Comparable
+{
+   /**
+    * Gets a location which refers to the end of a linear {@link Geometry}.
+    * @param linear the linear geometry
+    * @return a new <tt>LinearLocation</tt>
+    */
+  public static LinearLocation getEndLocation(Geometry linear)
+  {
+    // assert: linear is LineString or MultiLineString
+    LinearLocation loc = new LinearLocation();
+    loc.setToEnd(linear);
+    return loc;
+  }
+
+  /**
+   * Computes the {@link Coordinate} of a point a given fraction
+   * along the line segment <tt>(p0, p1)</tt>.
+   * If the fraction is greater than 1.0 the last
+   * point of the segment is returned.
+   * If the fraction is less than or equal to 0.0 the first point
+   * of the segment is returned.
+   *
+   * @param p0 the first point of the line segment
+   * @param p1 the last point of the line segment
+   * @param frac the length to the desired point
+   * @return the <tt>Coordinate</tt> of the desired point
+   */
+  public static Coordinate pointAlongSegmentByFraction(Coordinate p0, Coordinate p1, double frac)
+  {
+    if (frac <= 0.0) return p0;
+    if (frac >= 1.0) return p1;
+
+    double x = (p1.x - p0.x) * frac + p0.x;
+    double y = (p1.y - p0.y) * frac + p0.y;
+    return new Coordinate(x, y);
+  }
+
+  private int componentIndex = 0;
+  private int segmentIndex = 0;
+  private double segmentFraction = 0.0;
+
+  /**
+   * Creates a location referring to the start of a linear geometry
+   */
+  public LinearLocation()
+  {
+  }
+
+  public LinearLocation(int segmentIndex, double segmentFraction) {
+    this(0, segmentIndex, segmentFraction);
+  }
+
+  public LinearLocation(int componentIndex, int segmentIndex, double segmentFraction)
+  {
+    this.componentIndex = componentIndex;
+    this.segmentIndex = segmentIndex;
+    this.segmentFraction = segmentFraction;
+    normalize();
+  }
+
+  /**
+   * Ensures the individual values are locally valid.
+   * Does <b>not</b> ensure that the indexes are valid for
+   * a particular linear geometry.
+   *
+   * @see clamp
+   */
+  private void normalize()
+  {
+    if (segmentFraction < 0.0) {
+      segmentFraction = 0.0;
+    }
+    if (segmentFraction > 1.0) {
+      segmentFraction = 1.0;
+    }
+
+    if (componentIndex < 0) {
+      componentIndex = 0;
+      segmentIndex = 0;
+      segmentFraction = 0.0;
+    }
+    if (segmentIndex < 0) {
+      segmentIndex = 0;
+      segmentFraction = 0.0;
+    }
+    if (segmentFraction == 1.0) {
+      segmentFraction = 0.0;
+      segmentIndex += 1;
+    }
+  }
+
+
+  /**
+   * Ensures the indexes are valid for a given linear {@link Geometry}.
+   *
+   * @param linear a linear geometry
+   */
+  public void clamp(Geometry linear)
+  {
+    if (componentIndex >= linear.getNumGeometries()) {
+      setToEnd(linear);
+      return;
+    }
+    if (segmentIndex >= linear.getNumPoints()) {
+      LineString line = (LineString) linear.getGeometryN(componentIndex);
+      segmentIndex = line.getNumPoints() - 1;
+      segmentFraction = 1.0;
+    }
+  }
+  /**
+   * Snaps the value of this location to
+   * the nearest vertex on the given linear {@link Geometry},
+   * if the vertex is closer than <tt>maxDistance</tt>.
+   *
+   * @param linearGeom a linear geometry
+   * @param minDistance the minimum allowable distance to a vertex
+   */
+  public void snapToVertex(Geometry linearGeom, double minDistance)
+  {
+    if (segmentFraction <= 0.0 || segmentFraction >= 1.0)
+      return;
+    double segLen = getSegmentLength(linearGeom);
+    double lenToStart = segmentFraction * segLen;
+    double lenToEnd = segLen - lenToStart;
+    if (lenToStart <= lenToEnd && lenToStart < minDistance) {
+      segmentFraction = 0.0;
+    }
+    else if (lenToEnd <= lenToStart && lenToEnd < minDistance) {
+      segmentFraction = 1.0;
+    }
+  }
+
+  /**
+   * Gets the length of the segment in the given
+   * Geometry containing this location.
+   *
+   * @param linearGeom a linear geometry
+   * @return the length of the segment
+   */
+  public double getSegmentLength(Geometry linearGeom)
+  {
+    LineString lineComp = (LineString) linearGeom.getGeometryN(componentIndex);
+
+    // ensure segment index is valid
+    int segIndex = segmentIndex;
+    if (segmentIndex >= lineComp.getNumPoints() - 1)
+      segIndex = lineComp.getNumPoints() - 2;
+
+    Coordinate p0 = lineComp.getCoordinateN(segIndex);
+    Coordinate p1 = lineComp.getCoordinateN(segIndex + 1);
+    return p0.distance(p1);
+  }
+
+  /**
+   * Sets the value of this location to
+   * refer the end of a linear geometry
+   *
+   * @param linear the linear geometry to set
+   */
+  public void setToEnd(Geometry linear)
+  {
+    componentIndex = linear.getNumGeometries() - 1;
+    LineString lastLine = (LineString) linear.getGeometryN(componentIndex);
+    segmentIndex = lastLine.getNumPoints() - 1;
+    segmentFraction = 1.0;
+  }
+
+  /**
+   * Gets the component index for this location.
+   *
+   * @return the component index
+   */
+  public int getComponentIndex() { return componentIndex; }
+
+  /**
+   * Gets the segment index for this location
+   *
+   * @return the segment index
+   */
+  public int getSegmentIndex() { return segmentIndex; }
+
+  /**
+   * Gets the segment fraction for this location
+   *
+   * @return the segment fraction
+   */
+  public double getSegmentFraction() { return segmentFraction; }
+
+  /**
+   * Tests whether this location refers to a vertex
+   *
+   * @return true if the location is a vertex
+   */
+  public boolean isVertex()
+  {
+    return segmentFraction <= 0.0 || segmentFraction >= 1.0;
+  }
+
+  /**
+   * Gets the {@link Coordinate} along the
+   * given linear {@link Geometry} which is
+   * referenced by this location.
+   *
+   * @param linearGeom a linear geometry
+   * @return the <tt>Coordinate</tt> at the location
+   */
+  public Coordinate getCoordinate(Geometry linearGeom)
+  {
+    LineString lineComp = (LineString) linearGeom.getGeometryN(componentIndex);
+    Coordinate p0 = lineComp.getCoordinateN(segmentIndex);
+    if (segmentIndex >= lineComp.getNumPoints() - 1)
+      return p0;
+    Coordinate p1 = lineComp.getCoordinateN(segmentIndex + 1);
+    return pointAlongSegmentByFraction(p0, p1, segmentFraction);
+  }
+
+  /**
+   * Tests whether this location refers to a valid
+   * location on the given linear {@link Geometry}.
+   *
+   * @param linearGeom a linear geometry
+   * @return true if this location is valid
+   */
+  public boolean isValid(Geometry linearGeom)
+  {
+    if (componentIndex < 0 || componentIndex >= linearGeom.getNumGeometries())
+      return false;
+
+    LineString lineComp = (LineString) linearGeom.getGeometryN(componentIndex);
+    if (segmentIndex < 0 || segmentIndex > lineComp.getNumGeometries())
+      return false;
+    if (segmentIndex == lineComp.getNumGeometries() && segmentFraction != 0.0)
+      return false;
+
+    if (segmentFraction < 0.0 || segmentFraction > 1.0)
+      return false;
+    return true;
+  }
+
+  /**
+   *  Compares this object with the specified object for order.
+   *
+   *@param  o  the <code>LineStringLocation</code> with which this <code>Coordinate</code>
+   *      is being compared
+   *@return    a negative integer, zero, or a positive integer as this <code>LineStringLocation</code>
+   *      is less than, equal to, or greater than the specified <code>LineStringLocation</code>
+   */
+  public int compareTo(Object o) {
+    LinearLocation other = (LinearLocation) o;
+    // compare component indices
+    if (componentIndex < other.componentIndex) return -1;
+    if (componentIndex > other.componentIndex) return 1;
+    // compare segments
+    if (segmentIndex < other.segmentIndex) return -1;
+    if (segmentIndex > other.segmentIndex) return 1;
+    // same segment, so compare segment fraction
+    if (segmentFraction < other.segmentFraction) return -1;
+    if (segmentFraction > other.segmentFraction) return 1;
+    // same location
+    return 0;
+  }
+
+  /**
+   *  Compares this object with the specified index values for order.
+   *
+   * @param componentIndex1 a component index
+   * @param segmentIndex1 a segment index
+   * @param segmentFraction1 a segment fraction
+   * @return    a negative integer, zero, or a positive integer as this <code>LineStringLocation</code>
+   *      is less than, equal to, or greater than the specified locationValues
+   */
+  public int compareLocationValues(int componentIndex1, int segmentIndex1, double segmentFraction1) {
+    // compare component indices
+    if (componentIndex < componentIndex1) return -1;
+    if (componentIndex > componentIndex1) return 1;
+    // compare segments
+    if (segmentIndex < segmentIndex1) return -1;
+    if (segmentIndex > segmentIndex1) return 1;
+    // same segment, so compare segment fraction
+    if (segmentFraction < segmentFraction1) return -1;
+    if (segmentFraction > segmentFraction1) return 1;
+    // same location
+    return 0;
+  }
+
+  /**
+   *  Compares two sets of location values for order.
+   *
+   * @param componentIndex0 a component index
+   * @param segmentIndex0 a segment index
+   * @param segmentFraction0 a segment fraction
+   * @param componentIndex1 another component index
+   * @param segmentIndex1 another segment index
+   * @param segmentFraction1 another segment fraction
+   *@return    a negative integer, zero, or a positive integer
+   *      as the first set of location values
+   *      is less than, equal to, or greater than the second set of locationValues
+   */
+  public static int compareLocationValues(
+      int componentIndex0, int segmentIndex0, double segmentFraction0,
+      int componentIndex1, int segmentIndex1, double segmentFraction1)
+  {
+    // compare component indices
+    if (componentIndex0 < componentIndex1) return -1;
+    if (componentIndex0 > componentIndex1) return 1;
+    // compare segments
+    if (segmentIndex0 < segmentIndex1) return -1;
+    if (segmentIndex0 > segmentIndex1) return 1;
+    // same segment, so compare segment fraction
+    if (segmentFraction0 < segmentFraction1) return -1;
+    if (segmentFraction0 > segmentFraction1) return 1;
+    // same location
+    return 0;
+  }
+
+  /**
+   * Copies this location
+   *
+   * @return a copy of this location
+   */
+  public Object clone()
+  {
+    return new LinearLocation(segmentIndex, segmentFraction);
+  }
+}
\ No newline at end of file
diff --git a/src/com/vividsolutions/jts/linearref/LocationIndexOfLine.java b/src/com/vividsolutions/jts/linearref/LocationIndexOfLine.java
new file mode 100644
index 0000000..104037b
--- /dev/null
+++ b/src/com/vividsolutions/jts/linearref/LocationIndexOfLine.java
@@ -0,0 +1,52 @@
+package com.vividsolutions.jts.linearref;
+
+import com.vividsolutions.jts.geom.*;
+
+/**
+ * Determines the location of a subline along a linear {@link Geometry}.
+ * The location is reported as a pair of {@link LinearLocation}s.
+ * <p>
+ * <b>Note:</b> Currently this algorithm is not guaranteed to
+ * return the correct substring in some situations where
+ * an endpoint of the test line occurs more than once in the input line.
+ * (However, the common case of a ring is always handled correctly).
+ */
+class LocationIndexOfLine
+{
+  /**
+  * MD - this algorithm has been extracted into a class
+  * because it is intended to validate that the subline truly is a subline,
+  * and also to use the internal vertex information to unambiguously locate the subline.
+  */
+ public static LinearLocation[] indicesOf(Geometry linearGeom, Geometry subLine)
+  {
+    LocationIndexOfLine locater = new LocationIndexOfLine(linearGeom);
+    return locater.indicesOf(subLine);
+  }
+
+  private Geometry linearGeom;
+
+  public LocationIndexOfLine(Geometry linearGeom) {
+    this.linearGeom = linearGeom;
+  }
+
+  public LinearLocation[] indicesOf(Geometry subLine)
+  {
+    Coordinate startPt = ((LineString) subLine.getGeometryN(0)).getCoordinateN(0);
+    LineString lastLine = (LineString) subLine.getGeometryN(subLine.getNumGeometries() - 1);
+    Coordinate endPt = lastLine.getCoordinateN(lastLine.getNumPoints() - 1);
+
+    LocationIndexOfPoint locPt = new LocationIndexOfPoint(linearGeom);
+    LinearLocation[] subLineLoc = new LinearLocation[2];
+    subLineLoc[0] = locPt.indexOf(startPt);
+
+    // check for case where subline is zero length
+    if (subLine.getLength() == 0.0) {
+      subLineLoc[1] = (LinearLocation) subLineLoc[0].clone();
+    }
+    else  {
+      subLineLoc[1] = locPt.indexOfAfter(endPt, subLineLoc[0]);
+    }
+    return subLineLoc;
+  }
+}
\ No newline at end of file
diff --git a/src/com/vividsolutions/jts/linearref/LocationIndexOfPoint.java b/src/com/vividsolutions/jts/linearref/LocationIndexOfPoint.java
new file mode 100644
index 0000000..58076e6
--- /dev/null
+++ b/src/com/vividsolutions/jts/linearref/LocationIndexOfPoint.java
@@ -0,0 +1,121 @@
+package com.vividsolutions.jts.linearref;
+
+import com.vividsolutions.jts.geom.*;
+import com.vividsolutions.jts.util.Assert;
+
+/**
+ * Computes the {@link LinearLocation} of the point
+ * on a linear {@link Geometry} nearest a given {@link Coordinate}.
+ * The nearest point is not necessarily unique; this class
+ * always computes the nearest point closest to
+ * the start of the geometry.
+ */
+class LocationIndexOfPoint
+{
+  public static LinearLocation indexOf(Geometry linearGeom, Coordinate inputPt)
+  {
+    LocationIndexOfPoint locater = new LocationIndexOfPoint(linearGeom);
+    return locater.indexOf(inputPt);
+  }
+
+  private Geometry linearGeom;
+
+  public LocationIndexOfPoint(Geometry linearGeom) {
+    this.linearGeom = linearGeom;
+  }
+
+  /**
+   * Find the nearest location along a linear {@link Geometry} to a given point.
+   *
+   * @param inputPt the coordinate to locate
+   * @return the location of the nearest point
+   */
+  public LinearLocation indexOf(Coordinate inputPt)
+  {
+    return indexOfFromStart(inputPt, null);
+  }
+
+  /**
+   * Find the nearest {@link LinearLocation} along the linear {@link Geometry}
+   * to a given {@link Coordinate}
+   * after the specified minimum {@link LinearLocation}.
+   * If possible the location returned will be strictly greater than the
+   * <code>minLocation</code>.
+   * If this is not possible, the
+   * value returned will equal <code>minLocation</code>.
+   * (An example where this is not possible is when
+   * minLocation = [end of line] ).
+   *
+   * @param inputPt the coordinate to locate
+   * @param minLocation the minimum location for the point location
+   * @return the location of the nearest point
+   */
+  public LinearLocation indexOfAfter(Coordinate inputPt, LinearLocation minIndex)
+  {
+    if (minIndex == null) return indexOf(inputPt);
+
+    // sanity check for minLocation at or past end of line
+    LinearLocation endLoc = LinearLocation.getEndLocation(linearGeom);
+    if (endLoc.compareTo(minIndex) <= 0)
+      return endLoc;
+
+    LinearLocation closestAfter = indexOfFromStart(inputPt, minIndex);
+    /**
+     * Return the minDistanceLocation found.
+     * This will not be null, since it was initialized to minLocation
+     */
+    Assert.isTrue(closestAfter.compareTo(minIndex) >= 0,
+                  "computed location is before specified minimum location");
+    return closestAfter;
+  }
+
+  private LinearLocation indexOfFromStart(Coordinate inputPt, LinearLocation minIndex)
+  {
+    double minDistance = Double.MAX_VALUE;
+    int minComponentIndex = 0;
+    int minSegmentIndex = 0;
+    double minFrac = -1.0;
+
+    LineSegment seg = new LineSegment();
+    for (LinearIterator it = new LinearIterator(linearGeom);
+         it.hasNext(); it.next()) {
+      if (! it.isEndOfLine()) {
+        seg.p0 = it.getSegmentStart();
+        seg.p1 = it.getSegmentEnd();
+        double segDistance = seg.distance(inputPt);
+        double segFrac = segmentFraction(seg, inputPt);
+
+        int candidateComponentIndex = it.getComponentIndex();
+        int candidateSegmentIndex = it.getVertexIndex();
+        if (segDistance < minDistance) {
+          // ensure after minLocation, if any
+          if (minIndex == null ||
+              minIndex.compareLocationValues(
+              candidateComponentIndex, candidateSegmentIndex, segFrac)
+              < 0
+              ) {
+            // otherwise, save this as new minimum
+            minComponentIndex = candidateComponentIndex;
+            minSegmentIndex = candidateSegmentIndex;
+            minFrac = segFrac;
+            minDistance = segDistance;
+          }
+        }
+      }
+    }
+    LinearLocation loc = new LinearLocation(minComponentIndex, minSegmentIndex, minFrac);
+    return loc;
+  }
+
+  public static double segmentFraction(
+      LineSegment seg,
+      Coordinate inputPt)
+  {
+    double segFrac = seg.projectionFactor(inputPt);
+    if (segFrac < 0.0)
+      segFrac = 0.0;
+    else if (segFrac > 1.0)
+      segFrac = 1.0;
+    return segFrac;
+  }
+}
diff --git a/src/com/vividsolutions/jts/linearref/LocationIndexedLine.java b/src/com/vividsolutions/jts/linearref/LocationIndexedLine.java
new file mode 100644
index 0000000..b1f668a
--- /dev/null
+++ b/src/com/vividsolutions/jts/linearref/LocationIndexedLine.java
@@ -0,0 +1,145 @@
+package com.vividsolutions.jts.linearref;
+
+import com.vividsolutions.jts.geom.*;
+
+/**
+ * Supports linear referencing
+ * along a linear {@link Geometry}
+ * using {@link LinearLocation}s as the index.
+ */
+public class LocationIndexedLine
+{
+  private Geometry linearGeom;
+
+  /**
+   * Constructs an object which allows linear referencing along
+   * a given linear {@link Geometry}.
+   *
+   * @param linearGeom the linear geometry to reference along
+   */
+  public LocationIndexedLine(Geometry linearGeom)
+  {
+    this.linearGeom = linearGeom;
+    checkGeometryType();
+  }
+
+  private void checkGeometryType()
+  {
+    if (! (linearGeom instanceof LineString || linearGeom instanceof MultiLineString))
+      throw new IllegalArgumentException("Input geometry must be linear");
+  }
+  /**
+   * Computes the {@link Coordinate} for the point
+   * on the line at the given index.
+   * If the index is out of range the first or last point on the
+   * line will be returned.
+   *
+   * @param length the index of the desired point
+   * @return the Coordinate at the given index
+   */
+  public Coordinate extractPoint(LinearLocation index)
+  {
+    return index.getCoordinate(linearGeom);
+  }
+
+  /**
+   * Computes the {@link LineString} for the interval
+   * on the line between the given indices.
+   *
+   * @param startIndex the index of the start of the interval
+   * @param endIndex the index of the end of the interval
+   * @return the linear interval between the indices
+   */
+  public Geometry extractLine(LinearLocation startIndex, LinearLocation endIndex)
+  {
+    return ExtractLineByLocation.extract(linearGeom, startIndex, endIndex);
+  }
+
+  /**
+   * Computes the index for a given point on the line.
+   * <p>
+   * The supplied point does not <i>necessarily</i> have to lie precisely
+   * on the line, but if it is far from the line the accuracy and
+   * performance of this function is not guaranteed.
+   * Use {@link #project} to compute a guaranteed result for points
+   * which may be far from the line.
+   *
+   * @param pt a point on the line
+   * @return the index of the point
+   * @see project
+   */
+  public LinearLocation indexOf(Coordinate pt)
+  {
+    return LocationIndexOfPoint.indexOf(linearGeom, pt);
+  }
+
+  /**
+   * Computes the indices for a subline of the line.
+   * (The subline must <i>conform</i> to the line; that is,
+   * all vertices in the subline (except possibly the first and last)
+   * must be vertices of the line and occcur in the same order).
+   *
+   * @param subLine a subLine of the line
+   * @return a pair of indices for the start and end of the subline.
+   */
+  public LinearLocation[] indicesOf(Geometry subLine)
+  {
+    return LocationIndexOfLine.indicesOf(linearGeom, subLine);
+  }
+
+  /**
+   * Computes the index for the closest point on the line to the given point.
+   * If more than one point has the closest distance the first one along the line
+   * is returned.
+   * (The point does not necessarily have to lie precisely on the line.)
+   *
+   * @param pt a point on the line
+   * @return the index of the point
+   */
+  public LinearLocation project(Coordinate pt)
+  {
+    return LocationIndexOfPoint.indexOf(linearGeom, pt);
+  }
+
+  /**
+   * Returns the index of the start of the line
+   * @return
+   */
+  public LinearLocation getStartIndex()
+  {
+    return new LinearLocation();
+  }
+
+  /**
+   * Returns the index of the end of the line
+   * @return
+   */
+  public LinearLocation getEndIndex()
+  {
+    return LinearLocation.getEndLocation(linearGeom);
+  }
+
+  /**
+   * Tests whether an index is in the valid index range for the line.
+   *
+   * @param length the index to test
+   * @return <code>true</code> if the index is in the valid range
+   */
+  public boolean isValidIndex(LinearLocation index)
+  {
+    return index.isValid(linearGeom);
+  }
+
+  /**
+   * Computes a valid index for this line
+   * by clamping the given index to the valid range of index values
+   *
+   * @return a valid index value
+   */
+  public LinearLocation clampIndex(LinearLocation index)
+  {
+    LinearLocation loc = (LinearLocation) index.clone();
+    loc.clamp(linearGeom);
+    return loc;
+  }
+}
\ No newline at end of file
diff --git a/src/com/vividsolutions/jts/linearref/package.html b/src/com/vividsolutions/jts/linearref/package.html
new file mode 100644
index 0000000..e7e79d7
--- /dev/null
+++ b/src/com/vividsolutions/jts/linearref/package.html
@@ -0,0 +1,37 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+<!--
+
+-->
+</head>
+<body bgcolor="white">
+
+Contains classes and interfaces implementing linear referencing on linear geometries
+
+<H3>Linear Referencing</H3>
+
+Linear Referencing is a way of defining positions along linear geometries
+(<code>LineStrings</code> and <code>MultiLineStrings</code>).
+It is used extensively in linear network systems.
+There are numerous possible <b>Linear Referencing Methods</b> which
+can be used to define positions along linear geometry.
+This package supports two:
+<ul>
+<li><b>Linear Location</b> - a linear location is a triple
+<code>(component index, segment index, segment fraction)</code>
+which precisely specifies a point on a linear geometry.
+It allows for efficient mapping of the index value to actual coordinate values.
+<li><b>Length</b> - the natural concept of using the length along
+the geometry to specify a position.
+
+<h2>Package Specification</h2>
+
+<ul>
+  <li>Java Topology Suite Technical Specifications
+  <li><A HREF="http://www.opengis.org/techno/specs.htm">
+      OpenGIS Simple Features Specification for SQL</A>
+</ul>
+
+</body>
+</html>
diff --git a/src/com/vividsolutions/jts/noding/SegmentIntersector.java b/src/com/vividsolutions/jts/noding/IntersectionAdder.java
similarity index 93%
copy from src/com/vividsolutions/jts/noding/SegmentIntersector.java
copy to src/com/vividsolutions/jts/noding/IntersectionAdder.java
index f762b15..859e0b8 100644
--- a/src/com/vividsolutions/jts/noding/SegmentIntersector.java
+++ b/src/com/vividsolutions/jts/noding/IntersectionAdder.java
@@ -45,9 +45,10 @@ import com.vividsolutions.jts.util.Debug;
  * detects that two SegmentStrings <i>might</i> intersect.
  * This class is an example of the <i>Strategy</i> pattern.
  *
- * @version 1.6
+ * @version 1.7
  */
-public class SegmentIntersector
+public class IntersectionAdder
+    implements SegmentIntersector
 {
   public static boolean isAdjacentSegments(int i1, int i2)
   {
@@ -67,7 +68,6 @@ public class SegmentIntersector
   private Coordinate properIntersectionPoint = null;
 
   private LineIntersector li;
-  private boolean recordIsolated;
   private boolean isSelfIntersection;
   //private boolean intersectionFound;
   public int numIntersections = 0;
@@ -77,7 +77,7 @@ public class SegmentIntersector
   // testing only
   public int numTests = 0;
 
-  public SegmentIntersector(LineIntersector li)
+  public IntersectionAdder(LineIntersector li)
   {
     this.li = li;
   }
@@ -156,10 +156,6 @@ numTests++;
     li.computeIntersection(p00, p01, p10, p11);
 //if (li.hasIntersection() && li.isProper()) Debug.println(li);
     if (li.hasIntersection()) {
-      if (recordIsolated) {
-        e0.setIsolated(false);
-        e1.setIsolated(false);
-      }
       //intersectionFound = true;
       numIntersections++;
       if (li.isInteriorIntersection()) {
@@ -172,11 +168,11 @@ numTests++;
       // only intersection.
       if (! isTrivialIntersection(e0, segIndex0, e1, segIndex1)) {
         hasIntersection = true;
-//Debug.println(li);
         e0.addIntersections(li, segIndex0, 0);
         e1.addIntersections(li, segIndex1, 1);
         if (li.isProper()) {
           numProperIntersections++;
+//Debug.println(li.toString());  Debug.println(li.getIntersection(0));
           //properIntersectionPoint = (Coordinate) li.getIntersection(0).clone();
           hasProper = true;
           hasProperInterior = true;
diff --git a/src/com/vividsolutions/jts/noding/IntersectionFinderAdder.java b/src/com/vividsolutions/jts/noding/IntersectionFinderAdder.java
new file mode 100644
index 0000000..f1aad72
--- /dev/null
+++ b/src/com/vividsolutions/jts/noding/IntersectionFinderAdder.java
@@ -0,0 +1,68 @@
+package com.vividsolutions.jts.noding;
+
+import java.util.*;
+import com.vividsolutions.jts.geom.*;
+import com.vividsolutions.jts.algorithm.LineIntersector;
+import com.vividsolutions.jts.util.Debug;
+
+/**
+ * Finds proper and interior intersections in a set of SegmentStrings,
+ * and adds them as nodes.
+ *
+ * @version 1.7
+ */
+public class IntersectionFinderAdder
+    implements SegmentIntersector
+{
+  private LineIntersector li;
+  private final List interiorIntersections;
+
+
+  /**
+   * Creates an intersection finder which finds all proper intersections
+   *
+   * @param li the LineIntersector to use
+   */
+  public IntersectionFinderAdder(LineIntersector li)
+  {
+    this.li = li;
+    interiorIntersections = new ArrayList();
+  }
+
+  public List getInteriorIntersections()  {    return interiorIntersections;  }
+
+  /**
+   * This method is called by clients
+   * of the {@link SegmentIntersector} class to process
+   * intersections for two segments of the {@link SegmentStrings} being intersected.
+   * Note that some clients (such as {@link MonotoneChain}s) may optimize away
+   * this call for segment pairs which they have determined do not intersect
+   * (e.g. by an disjoint envelope test).
+   */
+  public void processIntersections(
+      SegmentString e0,  int segIndex0,
+      SegmentString e1,  int segIndex1
+      )
+  {
+    // don't bother intersecting a segment with itself
+    if (e0 == e1 && segIndex0 == segIndex1) return;
+
+    Coordinate p00 = e0.getCoordinates()[segIndex0];
+    Coordinate p01 = e0.getCoordinates()[segIndex0 + 1];
+    Coordinate p10 = e1.getCoordinates()[segIndex1];
+    Coordinate p11 = e1.getCoordinates()[segIndex1 + 1];
+
+    li.computeIntersection(p00, p01, p10, p11);
+//if (li.hasIntersection() && li.isProper()) Debug.println(li);
+
+    if (li.hasIntersection()) {
+      if (li.isInteriorIntersection()) {
+        for (int intIndex = 0; intIndex < li.getIntersectionNum(); intIndex++) {
+          interiorIntersections.add(li.getIntersection(intIndex));
+        }
+        e0.addIntersections(li, segIndex0, 0);
+        e1.addIntersections(li, segIndex1, 1);
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/src/com/vividsolutions/jts/noding/IteratedNoder.java b/src/com/vividsolutions/jts/noding/IteratedNoder.java
index b19e8b6..726e1e3 100644
--- a/src/com/vividsolutions/jts/noding/IteratedNoder.java
+++ b/src/com/vividsolutions/jts/noding/IteratedNoder.java
@@ -33,9 +33,9 @@
  */
 package com.vividsolutions.jts.noding;
 
-import java.util.*;
 import com.vividsolutions.jts.algorithm.*;
 import com.vividsolutions.jts.geom.*;
+import java.util.*;
 
 /**
  * Nodes a set of SegmentStrings completely.
@@ -47,12 +47,17 @@ import com.vividsolutions.jts.geom.*;
  * due to roundoff error.   This problem is detected and an exception is thrown.
  * Clients can choose to rerun the noding using a lower precision model.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class IteratedNoder
+    implements Noder
 {
+  public static final int MAX_ITER = 5;
+
   private PrecisionModel pm;
   private LineIntersector li;
+  private Collection nodedSegStrings;
+  private int maxIter = MAX_ITER;
 
   public IteratedNoder(PrecisionModel pm)
   {
@@ -62,6 +67,22 @@ public class IteratedNoder
   }
 
   /**
+   * Sets the maximum number of noding iterations performed before
+   * the noding is aborted.
+   * Experience suggests that this should rarely need to be changed
+   * from the default.
+   * The default is MAX_ITER.
+   *
+   * @param maxIter the maximum number of iterations to perform
+   */
+  public void setMaximumIterations(int maxIter)
+  {
+    this.maxIter = maxIter;
+  }
+
+  public Collection getNodedSubstrings()  {    return nodedSegStrings;  }
+
+  /**
    * Fully nodes a list of {@link SegmentStrings}, i.e. peforms noding iteratively
    * until no intersections are found between segments.
    * Maintains labelling of edges correctly through
@@ -71,29 +92,33 @@ public class IteratedNoder
    * @return a collection of the noded SegmentStrings
    * @throws TopologyException if the iterated noding fails to converge.
    */
-  public Collection node(Collection segStrings)
+  public void computeNodes(Collection segStrings)
     throws TopologyException
   {
     int[] numInteriorIntersections = new int[1];
-    Collection nodedEdges = segStrings;
+    nodedSegStrings = segStrings;
     int nodingIterationCount = 0;
     int lastNodesCreated = -1;
     do {
-      nodedEdges = node(nodedEdges, numInteriorIntersections);
+      node(nodedSegStrings, numInteriorIntersections);
       nodingIterationCount++;
       int nodesCreated = numInteriorIntersections[0];
+
+      /**
+       * Fail if the number of nodes created is not declining.
+       * However, allow a few iterations at least before doing this
+       */
 //System.out.println("# nodes created: " + nodesCreated);
-      if (lastNodesCreated > 0 && nodesCreated > lastNodesCreated) {
+      if (lastNodesCreated > 0
+          && nodesCreated >= lastNodesCreated
+          && nodingIterationCount > maxIter) {
         throw new TopologyException("Iterated noding failed to converge after "
                                     + nodingIterationCount + " iterations");
       }
       lastNodesCreated = nodesCreated;
 
-//saveEdges(nodedEdges, "run" + runCount + "_nodedEdges");
-
     } while (lastNodesCreated > 0);
 //System.out.println("# nodings = " + nodingIterationCount);
-    return nodedEdges;
   }
 
 
@@ -101,17 +126,15 @@ public class IteratedNoder
  * Node the input segment strings once
  * and create the split edges between the nodes
  */
-  private Collection node(Collection segStrings, int[] numInteriorIntersections)
+  private void node(Collection segStrings, int[] numInteriorIntersections)
   {
-    SegmentIntersector si = new SegmentIntersector(li);
-    MCQuadtreeNoder noder = new MCQuadtreeNoder();
+    IntersectionAdder si = new IntersectionAdder(li);
+    MCIndexNoder noder = new MCIndexNoder();
     noder.setSegmentIntersector(si);
-
-    // perform the noding
-    Collection nodedSegStrings = noder.node(segStrings);
+    noder.computeNodes(segStrings);
+    nodedSegStrings = noder.getNodedSubstrings();
     numInteriorIntersections[0] = si.numInteriorIntersections;
 //System.out.println("# intersection tests: " + si.numTests);
-    return nodedSegStrings;
   }
 
 }
diff --git a/src/com/vividsolutions/jts/noding/MCQuadtreeNoder.java b/src/com/vividsolutions/jts/noding/MCIndexNoder.java
similarity index 79%
rename from src/com/vividsolutions/jts/noding/MCQuadtreeNoder.java
rename to src/com/vividsolutions/jts/noding/MCIndexNoder.java
index cbd2218..b7553e2 100644
--- a/src/com/vividsolutions/jts/noding/MCQuadtreeNoder.java
+++ b/src/com/vividsolutions/jts/noding/MCIndexNoder.java
@@ -33,12 +33,10 @@
  */
 package com.vividsolutions.jts.noding;
 
-import java.util.*;
-import com.vividsolutions.jts.algorithm.LineIntersector;
+import com.vividsolutions.jts.index.*;
 import com.vividsolutions.jts.index.chain.*;
-import com.vividsolutions.jts.index.SpatialIndex;
-import com.vividsolutions.jts.index.strtree.STRtree;
-import com.vividsolutions.jts.index.quadtree.Quadtree;
+import com.vividsolutions.jts.index.strtree.*;
+import java.util.*;
 
 /**
  * Nodes a set of {@link SegmentStrings} using a index based
@@ -47,38 +45,46 @@ import com.vividsolutions.jts.index.quadtree.Quadtree;
  * envelope (range) queries efficiently (such as a {@link Quadtree}
  * or {@link STRtree}.
  *
- * @version 1.6
+ * @version 1.7
  */
-public class MCQuadtreeNoder
-    extends Noder
+public class MCIndexNoder
+    extends SinglePassNoder
 {
-  private Collection chains = new ArrayList();
+  private List monoChains = new ArrayList();
   private SpatialIndex index= new STRtree();
   private int idCounter = 0;
-
+  private Collection nodedSegStrings;
   // statistics
   private int nOverlaps = 0;
 
-  public MCQuadtreeNoder()
+  public MCIndexNoder()
+  {
+  }
+
+  public List getMonotoneChains() { return monoChains; }
+
+  public SpatialIndex getIndex() { return index; }
+
+  public Collection getNodedSubstrings()
   {
+    return  SegmentString.getNodedSubstrings(nodedSegStrings);
   }
 
-  public Collection node(Collection inputSegStrings)
+  public void computeNodes(Collection inputSegStrings)
   {
+    this.nodedSegStrings = inputSegStrings;
     for (Iterator i = inputSegStrings.iterator(); i.hasNext(); ) {
       add((SegmentString) i.next());
     }
     intersectChains();
-//System.out.println("MCQuadtreeNoder: # chain overlaps = " + nOverlaps);
-    List nodedSegStrings = getNodedEdges(inputSegStrings);
-    return nodedSegStrings;
+//System.out.println("MCIndexNoder: # chain overlaps = " + nOverlaps);
   }
 
   private void intersectChains()
   {
     MonotoneChainOverlapAction overlapAction = new SegmentOverlapAction(segInt);
 
-    for (Iterator i = chains.iterator(); i.hasNext(); ) {
+    for (Iterator i = monoChains.iterator(); i.hasNext(); ) {
       MonotoneChain queryChain = (MonotoneChain) i.next();
       List overlapChains = index.query(queryChain.getEnvelope());
       for (Iterator j = overlapChains.iterator(); j.hasNext(); ) {
@@ -102,7 +108,7 @@ public class MCQuadtreeNoder
       MonotoneChain mc = (MonotoneChain) i.next();
       mc.setId(idCounter++);
       index.insert(mc.getEnvelope(), mc);
-      chains.add(mc);
+      monoChains.add(mc);
     }
   }
 
diff --git a/src/com/vividsolutions/jts/noding/Noder.java b/src/com/vividsolutions/jts/noding/Noder.java
index 27d8439..7245fb3 100644
--- a/src/com/vividsolutions/jts/noding/Noder.java
+++ b/src/com/vividsolutions/jts/noding/Noder.java
@@ -34,46 +34,34 @@
 package com.vividsolutions.jts.noding;
 
 import java.util.*;
-import com.vividsolutions.jts.algorithm.LineIntersector;
-import com.vividsolutions.jts.geom.*;
-import com.vividsolutions.jts.geomgraph.index.*;
 
 /**
  * Computes all intersections between segments in a set of {@link SegmentString}s.
- * Intersections found are represented as {@link SegmentNode}s and add to the
+ * Intersections found are represented as {@link SegmentNode}s and added to the
  * {@link SegmentString}s in which they occur.
+ * As a final step in the noding a new set of segment strings split
+ * at the nodes may be returned.
  *
- * @version 1.6
+ * @version 1.7
  */
-public abstract class Noder
+public interface Noder
 {
 
-  public static List getNodedEdges(Collection segStrings)
-  {
-    List resultEdgelist = new ArrayList();
-    getNodedEdges(segStrings, resultEdgelist);
-    return resultEdgelist;
-  }
+  /**
+   * Computes the noding for a collection of {@link SegmentString}s.
+   * Some Noders may add all these nodes to the input SegmentStrings;
+   * others may only add some or none at all.
+   *
+   * @param segStrings a collection of {@link SegmentString}s to node
+   */
+  void computeNodes(Collection segStrings);
 
-  public static void getNodedEdges(Collection segStrings, Collection resultEdgelist)
-  {
-    for (Iterator i = segStrings.iterator(); i.hasNext(); ) {
-      SegmentString ss = (SegmentString) i.next();
-      ss.getIntersectionList().addSplitEdges(resultEdgelist);
-    }
-  }
-
-  protected SegmentIntersector segInt;
-  //protected LineIntersector li;
-
-  public Noder() {
-  }
-
-  public void setSegmentIntersector(SegmentIntersector segInt)
-  {
-    this.segInt = segInt;
-  }
-
-  public abstract Collection node(Collection segStrings);
+  /**
+   * Returns a {@link Collection} of fully noded {@link SegmentStrings}.
+   * The SegmentStrings have the same context as their parent.
+   *
+   * @return a Collection of SegmentStrings
+   */
+  Collection getNodedSubstrings();
 
 }
diff --git a/src/com/vividsolutions/jts/noding/NodingValidator.java b/src/com/vividsolutions/jts/noding/NodingValidator.java
index 5fa4ddc..595bf77 100644
--- a/src/com/vividsolutions/jts/noding/NodingValidator.java
+++ b/src/com/vividsolutions/jts/noding/NodingValidator.java
@@ -36,12 +36,13 @@ package com.vividsolutions.jts.noding;
 import java.util.*;
 import com.vividsolutions.jts.algorithm.*;
 import com.vividsolutions.jts.geom.*;
+import com.vividsolutions.jts.util.*;
 
 /**
  * Validates that a collection of {@link SegmentString}s is correctly noded.
  * Throws an appropriate exception if an noding error is found.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class NodingValidator {
 
@@ -56,35 +57,64 @@ public class NodingValidator {
 
   public void checkValid()
   {
-    checkNoInteriorPointsSame();
-    checkProperIntersections();
+    checkEndPtVertexIntersections();
+    checkInteriorIntersections();
+    checkCollapses();
   }
 
+  /**
+   * Checks if a segment string contains a segment pattern a-b-a (which implies a self-intersection)
+   */
+  private void checkCollapses()
+  {
+    for (Iterator i = segStrings.iterator(); i.hasNext(); ) {
+      SegmentString ss = (SegmentString) i.next();
+      checkCollapses(ss);
+    }
+  }
+
+  private void checkCollapses(SegmentString ss)
+  {
+    Coordinate[] pts = ss.getCoordinates();
+    for (int i = 0; i < pts.length - 2; i++) {
+      checkCollapse(pts[i], pts[i + 1], pts[i + 2]);
+    }
+  }
 
-  private void checkProperIntersections()
+  private void checkCollapse(Coordinate p0, Coordinate p1, Coordinate p2)
+  {
+    if (p0.equals(p2))
+      throw new RuntimeException("found non-noded collapse at "
+                                 + Debug.toLine(p0, p1, p2));
+  }
+
+  /**
+   * Checks all pairs of segments for intersections at an interior point of a segment
+   */
+  private void checkInteriorIntersections()
   {
     for (Iterator i = segStrings.iterator(); i.hasNext(); ) {
       SegmentString ss0 = (SegmentString) i.next();
       for (Iterator j = segStrings.iterator(); j.hasNext(); ) {
         SegmentString ss1 = (SegmentString) j.next();
 
-          checkProperIntersections(ss0, ss1);
+          checkInteriorIntersections(ss0, ss1);
       }
     }
   }
 
-  private void checkProperIntersections(SegmentString ss0, SegmentString ss1)
+  private void checkInteriorIntersections(SegmentString ss0, SegmentString ss1)
   {
     Coordinate[] pts0 = ss0.getCoordinates();
-     Coordinate[] pts1 = ss1.getCoordinates();
-     for (int i0 = 0; i0 < pts0.length - 1; i0++) {
-       for (int i1 = 0; i1 < pts1.length - 1; i1++) {
-         checkProperIntersections(ss0, i0, ss1, i1);
-       }
-     }
+    Coordinate[] pts1 = ss1.getCoordinates();
+    for (int i0 = 0; i0 < pts0.length - 1; i0++) {
+      for (int i1 = 0; i1 < pts1.length - 1; i1++) {
+        checkInteriorIntersections(ss0, i0, ss1, i1);
+      }
+    }
   }
 
-  private void checkProperIntersections(SegmentString e0, int segIndex0, SegmentString e1, int segIndex1)
+  private void checkInteriorIntersections(SegmentString e0, int segIndex0, SegmentString e1, int segIndex1)
   {
     if (e0 == e1 && segIndex0 == segIndex1) return;
 //numTests++;
@@ -98,7 +128,7 @@ public class NodingValidator {
 
       if (li.isProper()
           || hasInteriorIntersection(li, p00, p01)
-          || hasInteriorIntersection(li, p00, p01)) {
+          || hasInteriorIntersection(li, p10, p11)) {
         throw new RuntimeException("found non-noded intersection at "
                                    + p00 + "-" + p01
                                    + " and "
@@ -119,24 +149,28 @@ public class NodingValidator {
     return false;
   }
 
-  private void checkNoInteriorPointsSame()
+  /**
+   * Checks for intersections between an endpoint of a segment string
+   * and an interior vertex of another segment string
+   */
+  private void checkEndPtVertexIntersections()
   {
     for (Iterator i = segStrings.iterator(); i.hasNext(); ) {
       SegmentString ss = (SegmentString) i.next();
       Coordinate[] pts = ss.getCoordinates();
-      checkNoInteriorPointsSame(pts[0], segStrings);
-      checkNoInteriorPointsSame(pts[pts.length - 1], segStrings);
+      checkEndPtVertexIntersections(pts[0], segStrings);
+      checkEndPtVertexIntersections(pts[pts.length - 1], segStrings);
     }
   }
 
-  private void checkNoInteriorPointsSame(Coordinate testPt, Collection segStrings)
+  private void checkEndPtVertexIntersections(Coordinate testPt, Collection segStrings)
   {
     for (Iterator i = segStrings.iterator(); i.hasNext(); ) {
       SegmentString ss = (SegmentString) i.next();
       Coordinate[] pts = ss.getCoordinates();
       for (int j = 1; j < pts.length - 1; j++) {
         if (pts[j].equals(testPt))
-          throw new RuntimeException("found bad noding at index " + j + " pt " + testPt);
+          throw new RuntimeException("found endpt/interior pt intersection at index " + j + " :pt " + testPt);
       }
     }
   }
diff --git a/src/com/vividsolutions/jts/noding/Octant.java b/src/com/vividsolutions/jts/noding/Octant.java
new file mode 100644
index 0000000..d79c83e
--- /dev/null
+++ b/src/com/vividsolutions/jts/noding/Octant.java
@@ -0,0 +1,77 @@
+package com.vividsolutions.jts.noding;
+
+import com.vividsolutions.jts.geom.Coordinate;
+/**
+ * Methods for computing and working with octants of the Cartesian plane
+ * Octants are numbered as follows:
+ * <pre>
+ *  \2|1/
+ * 3 \|/ 0
+ * ---+--
+ * 4 /|\ 7
+ *  /5|6\
+ * <pre>
+ * If line segments lie along a coordinate axis, the octant is the lower of the two
+ * possible values.
+ *
+ * @version 1.7
+ */
+public class Octant {
+
+  /**
+   * Returns the octant of a directed line segment (specified as x and y
+   * displacements, which cannot both be 0).
+   */
+  public static int octant(double dx, double dy)
+  {
+    if (dx == 0.0 && dy == 0.0)
+      throw new IllegalArgumentException("Cannot compute the octant for point ( "+ dx + ", " + dy + " )" );
+
+    double adx = Math.abs(dx);
+    double ady = Math.abs(dy);
+
+    if (dx >= 0) {
+      if (dy >= 0) {
+        if (adx >= ady)
+          return 0;
+        else
+          return 1;
+      }
+      else { // dy < 0
+        if (adx >= ady)
+          return 7;
+        else
+          return 6;
+      }
+    }
+    else { // dx < 0
+      if (dy >= 0) {
+        if (adx >= ady)
+          return 3;
+        else
+          return 2;
+      }
+      else { // dy < 0
+        if (adx >= ady)
+          return 4;
+        else
+          return 5;
+      }
+    }
+  }
+
+  /**
+   * Returns the octant of a directed line segment from p0 to p1.
+   */
+  public static int octant(Coordinate p0, Coordinate p1)
+  {
+    double dx = p1.x - p0.x;
+    double dy = p1.y - p0.y;
+    if (dx == 0.0 && dy == 0.0)
+      throw new IllegalArgumentException("Cannot compute the octant for two identical points " + p0);
+    return octant(dx, dy);
+  }
+
+  private Octant() {
+  }
+}
\ No newline at end of file
diff --git a/src/com/vividsolutions/jts/noding/OrientedCoordinateArray.java b/src/com/vividsolutions/jts/noding/OrientedCoordinateArray.java
new file mode 100644
index 0000000..72080f4
--- /dev/null
+++ b/src/com/vividsolutions/jts/noding/OrientedCoordinateArray.java
@@ -0,0 +1,131 @@
+/*
+ * The JTS Topology Suite is a collection of Java classes that
+ * implement the fundamental operations required to validate a given
+ * geo-spatial data set to a known topological specification.
+ *
+ * Copyright (C) 2001 Vivid Solutions
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * For more information, contact:
+ *
+ *     Vivid Solutions
+ *     Suite #1A
+ *     2328 Government Street
+ *     Victoria BC  V8T 5G5
+ *     Canada
+ *
+ *     (250)385-6040
+ *     www.vividsolutions.com
+ */
+package com.vividsolutions.jts.noding;
+
+import java.util.*;
+import com.vividsolutions.jts.geom.*;
+
+/**
+ * Allows comparing {@link Coordinate} arrays
+ * in an orientation-independent way.
+ *
+ * @author Martin Davis
+ * @version 1.7
+ */
+public class OrientedCoordinateArray
+    implements Comparable
+{
+  private Coordinate[] pts;
+  private boolean orientation;
+
+  /**
+   * Creates a new {@link OrientedCoordinateArray}
+   * for the given {@link Coordinate} array.
+   *
+   * @param pts the coordinates to orient
+   */
+  public OrientedCoordinateArray(Coordinate[] pts)
+  {
+    this.pts = pts;
+    orientation = orientation(pts);
+  }
+
+  /**
+   * Computes the canonical orientation for a coordinate array.
+   *
+   * @param pts the array to test
+   * @return <code>true</code> if the points are oriented forwards
+   * @return <code>false</code if the points are oriented in reverse
+   */
+  private static boolean orientation(Coordinate[] pts)
+  {
+    return CoordinateArrays.increasingDirection(pts) == 1;
+  }
+
+  /**
+   * Compares two {@link OrientedCoordinateArray}s for their relative order
+   *
+   * @return -1 this one is smaller
+   * @return 0 the two objects are equal
+   * @return 1 this one is greater
+   */
+
+  public int compareTo(Object o1) {
+    OrientedCoordinateArray oca = (OrientedCoordinateArray) o1;
+    int comp = compareOriented(pts, orientation,
+                               oca.pts, oca.orientation);
+/*
+    // MD - testing only
+    int oldComp = SegmentStringDissolver.ptsComp.compare(pts, oca.pts);
+    if ((oldComp == 0 || comp == 0) && oldComp != comp) {
+      System.out.println("bidir mismatch");
+
+      boolean orient1 = orientation(pts);
+      boolean orient2 = orientation(oca.pts);
+      int comp2 = compareOriented(pts, orientation,
+                               oca.pts, oca.orientation);
+      int oldComp2 = SegmentStringDissolver.ptsComp.compare(pts, oca.pts);
+    }
+    */
+    return comp;
+  }
+
+  private static int compareOriented(Coordinate[] pts1,
+                                     boolean orientation1,
+                                     Coordinate[] pts2,
+                                     boolean orientation2)
+  {
+    int dir1 = orientation1 ? 1 : -1;
+    int dir2 = orientation2 ? 1 : -1;
+    int limit1 = orientation1 ? pts1.length : -1;
+    int limit2 = orientation2 ? pts2.length : -1;
+
+    int i1 = orientation1 ? 0 : pts1.length - 1;
+    int i2 = orientation2 ? 0 : pts2.length - 1;
+    int comp = 0;
+    while (true) {
+      int compPt = pts1[i1].compareTo(pts2[i2]);
+      if (compPt != 0)
+        return compPt;
+      i1 += dir1;
+      i2 += dir2;
+      boolean done1 = i1 == limit1;
+      boolean done2 = i2 == limit2;
+      if (done1 && ! done2) return -1;
+      if (! done1 && done2) return 1;
+      if (done1 && done2) return 0;
+    }
+  }
+
+
+}
diff --git a/src/com/vividsolutions/jts/noding/ScaledNoder.java b/src/com/vividsolutions/jts/noding/ScaledNoder.java
new file mode 100644
index 0000000..d7aa927
--- /dev/null
+++ b/src/com/vividsolutions/jts/noding/ScaledNoder.java
@@ -0,0 +1,102 @@
+package com.vividsolutions.jts.noding;
+
+import java.util.*;
+import com.vividsolutions.jts.geom.*;
+import com.vividsolutions.jts.algorithm.*;
+import com.vividsolutions.jts.util.*;
+
+/**
+ * Wraps a {@link Noder} and transforms its input
+ * into the integer domain.
+ * This is intended for use with Snap-Rounding noders,
+ * which typically are only intended to work in the integer domain.
+ * Offsets can be provided to increase the number of digits of available precision.
+ *
+ * @version 1.7
+ */
+public class ScaledNoder
+    implements Noder
+{
+  private Noder noder;
+  private double scaleFactor;
+  private double offsetX;
+  private double offsetY;
+  private boolean isScaled = false;
+
+  public ScaledNoder(Noder noder, double scaleFactor) {
+    this(noder, scaleFactor, 0, 0);
+  }
+
+  public ScaledNoder(Noder noder, double scaleFactor, double offsetX, double offsetY) {
+    this.noder = noder;
+    this.scaleFactor = scaleFactor;
+    // no need to scale if input precision is already integral
+    isScaled = ! isIntegerPrecision();
+  }
+
+  public boolean isIntegerPrecision() { return scaleFactor == 1.0; }
+
+  public Collection getNodedSubstrings()
+  {
+    Collection splitSS = noder.getNodedSubstrings();
+    if (isScaled) rescale(splitSS);
+    return splitSS;
+  }
+
+  public void computeNodes(Collection inputSegStrings)
+  {
+    Collection intSegStrings = inputSegStrings;
+    if (isScaled)
+      intSegStrings = scale(inputSegStrings);
+    noder.computeNodes(intSegStrings);
+  }
+
+  private Collection scale(Collection segStrings)
+  {
+    return CollectionUtil.transform(segStrings,
+                                    new CollectionUtil.Function() {
+      public Object execute(Object obj) {
+        SegmentString ss = (SegmentString) obj;
+        return new SegmentString(scale(ss.getCoordinates()), ss.getData());
+      }
+                                    }
+      );
+  }
+
+  private Coordinate[] scale(Coordinate[] pts)
+  {
+    Coordinate[] roundPts = new Coordinate[pts.length];
+    for (int i = 0; i < pts.length; i++) {
+      roundPts[i] = new Coordinate(
+      Math.round((pts[i].x - offsetX) * scaleFactor),
+      Math.round((pts[i].y - offsetY) * scaleFactor)
+        );
+    }
+    return roundPts;
+  }
+
+  //private double scale(double val) { return (double) Math.round(val * scaleFactor); }
+
+  private void rescale(Collection segStrings)
+  {
+    CollectionUtil.apply(segStrings,
+                                    new CollectionUtil.Function() {
+      public Object execute(Object obj) {
+        SegmentString ss = (SegmentString) obj;
+        rescale(ss.getCoordinates());
+        return null;
+      }
+                                    }
+      );
+  }
+
+  private void rescale(Coordinate[] pts)
+  {
+    for (int i = 0; i < pts.length; i++) {
+      pts[i].x = pts[i].x / scaleFactor + offsetX;
+      pts[i].y = pts[i].y / scaleFactor + offsetY;
+    }
+  }
+
+  //private double rescale(double val) { return val / scaleFactor; }
+}
\ No newline at end of file
diff --git a/src/com/vividsolutions/jts/noding/SegmentIntersector.java b/src/com/vividsolutions/jts/noding/SegmentIntersector.java
index f762b15..4f959aa 100644
--- a/src/com/vividsolutions/jts/noding/SegmentIntersector.java
+++ b/src/com/vividsolutions/jts/noding/SegmentIntersector.java
@@ -45,143 +45,18 @@ import com.vividsolutions.jts.util.Debug;
  * detects that two SegmentStrings <i>might</i> intersect.
  * This class is an example of the <i>Strategy</i> pattern.
  *
- * @version 1.6
+ * @version 1.7
  */
-public class SegmentIntersector
-{
-  public static boolean isAdjacentSegments(int i1, int i2)
-  {
-    return Math.abs(i1 - i2) == 1;
-  }
-
-  /**
-   * These variables keep track of what types of intersections were
-   * found during ALL edges that have been intersected.
-   */
-  private boolean hasIntersection = false;
-  private boolean hasProper = false;
-  private boolean hasProperInterior = false;
-  private boolean hasInterior = false;
-
-  // the proper intersection point found
-  private Coordinate properIntersectionPoint = null;
-
-  private LineIntersector li;
-  private boolean recordIsolated;
-  private boolean isSelfIntersection;
-  //private boolean intersectionFound;
-  public int numIntersections = 0;
-  public int numInteriorIntersections = 0;
-  public int numProperIntersections = 0;
-
-  // testing only
-  public int numTests = 0;
-
-  public SegmentIntersector(LineIntersector li)
-  {
-    this.li = li;
-  }
-
-  public LineIntersector getLineIntersector() { return li; }
-
-  /**
-   * @return the proper intersection point, or <code>null</code> if none was found
-   */
-  public Coordinate getProperIntersectionPoint()  {    return properIntersectionPoint;  }
-
-  public boolean hasIntersection() { return hasIntersection; }
-  /**
-   * A proper intersection is an intersection which is interior to at least two
-   * line segments.  Note that a proper intersection is not necessarily
-   * in the interior of the entire Geometry, since another edge may have
-   * an endpoint equal to the intersection, which according to SFS semantics
-   * can result in the point being on the Boundary of the Geometry.
-   */
-  public boolean hasProperIntersection() { return hasProper; }
-  /**
-   * A proper interior intersection is a proper intersection which is <b>not</b>
-   * contained in the set of boundary nodes set for this SegmentIntersector.
-   */
-  public boolean hasProperInteriorIntersection() { return hasProperInterior; }
-  /**
-   * An interior intersection is an intersection which is
-   * in the interior of some segment.
-   */
-  public boolean hasInteriorIntersection() { return hasInterior; }
-
-  /**
-   * A trivial intersection is an apparent self-intersection which in fact
-   * is simply the point shared by adjacent line segments.
-   * Note that closed edges require a special check for the point shared by the beginning
-   * and end segments.
-   */
-  private boolean isTrivialIntersection(SegmentString e0, int segIndex0, SegmentString e1, int segIndex1)
-  {
-    if (e0 == e1) {
-      if (li.getIntersectionNum() == 1) {
-        if (isAdjacentSegments(segIndex0, segIndex1))
-          return true;
-        if (e0.isClosed()) {
-          int maxSegIndex = e0.size() - 1;
-          if (    (segIndex0 == 0 && segIndex1 == maxSegIndex)
-              ||  (segIndex1 == 0 && segIndex0 == maxSegIndex) ) {
-            return true;
-          }
-        }
-      }
-    }
-    return false;
-  }
 
+public interface SegmentIntersector
+{
   /**
    * This method is called by clients
-   * of the {@link SegmentIntersector} class to process
+   * of the {@link SegmentIntersector} interface to process
    * intersections for two segments of the {@link SegmentStrings} being intersected.
-   * Note that some clients (such as {@link MonotoneChain}s) may optimize away
-   * this call for segment pairs which they have determined do not intersect
-   * (e.g. by an disjoint envelope test).
    */
-  public void processIntersections(
+  void processIntersections(
     SegmentString e0,  int segIndex0,
     SegmentString e1,  int segIndex1
-     )
-  {
-    if (e0 == e1 && segIndex0 == segIndex1) return;
-numTests++;
-    Coordinate p00 = e0.getCoordinates()[segIndex0];
-    Coordinate p01 = e0.getCoordinates()[segIndex0 + 1];
-    Coordinate p10 = e1.getCoordinates()[segIndex1];
-    Coordinate p11 = e1.getCoordinates()[segIndex1 + 1];
-
-    li.computeIntersection(p00, p01, p10, p11);
-//if (li.hasIntersection() && li.isProper()) Debug.println(li);
-    if (li.hasIntersection()) {
-      if (recordIsolated) {
-        e0.setIsolated(false);
-        e1.setIsolated(false);
-      }
-      //intersectionFound = true;
-      numIntersections++;
-      if (li.isInteriorIntersection()) {
-        numInteriorIntersections++;
-        hasInterior = true;
-//System.out.println(li);
-      }
-      // if the segments are adjacent they have at least one trivial intersection,
-      // the shared endpoint.  Don't bother adding it if it is the
-      // only intersection.
-      if (! isTrivialIntersection(e0, segIndex0, e1, segIndex1)) {
-        hasIntersection = true;
-//Debug.println(li);
-        e0.addIntersections(li, segIndex0, 0);
-        e1.addIntersections(li, segIndex1, 1);
-        if (li.isProper()) {
-          numProperIntersections++;
-          //properIntersectionPoint = (Coordinate) li.getIntersection(0).clone();
-          hasProper = true;
-          hasProperInterior = true;
-        }
-      }
-    }
-  }
+     );
 }
diff --git a/src/com/vividsolutions/jts/noding/SegmentNode.java b/src/com/vividsolutions/jts/noding/SegmentNode.java
index c8ca36e..ace5690 100644
--- a/src/com/vividsolutions/jts/noding/SegmentNode.java
+++ b/src/com/vividsolutions/jts/noding/SegmentNode.java
@@ -1,6 +1,3 @@
-
-
-
 /*
  * The JTS Topology Suite is a collection of Java classes that
  * implement the fundamental operations required to validate a given
@@ -35,74 +32,61 @@
  */
 package com.vividsolutions.jts.noding;
 
-/**
- * Represents a point on an
- * edge which intersects with another edge.
- * <br>
- * The intersection may either be a single point, or a line segment
- * (in which case this point is the start of the line segment)
- * The label attached to this intersection point applies to
- * the edge from this point forwards, until the next
- * intersection or the end of the edge.
- * The intersection point must be precise.
- * @version 1.6
- */
 import java.io.PrintStream;
 import com.vividsolutions.jts.geom.Coordinate;
 
 /**
  * Represents an intersection point between two {@link SegmentString}s.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class SegmentNode
     implements Comparable
 {
+  private final SegmentString segString;
+  public final Coordinate coord;   // the point of intersection
+  public final int segmentIndex;   // the index of the containing line segment in the parent edge
+  private final int segmentOctant;
+  private final boolean isInterior;
 
-  public Coordinate coord;   // the point of intersection
-  public int segmentIndex;   // the index of the containing line segment in the parent edge
-  public double dist;        // the edge distance of this point along the containing line segment
-  //Label label;
-
-  public SegmentNode(Coordinate coord, int segmentIndex, double dist) {
-    //this.edge = edge;
+  public SegmentNode(SegmentString segString, Coordinate coord, int segmentIndex, int segmentOctant) {
+    this.segString = segString;
     this.coord = new Coordinate(coord);
     this.segmentIndex = segmentIndex;
-    this.dist = dist;
-    //label = new Label();
+    this.segmentOctant = segmentOctant;
+    isInterior = ! coord.equals2D(segString.getCoordinate(segmentIndex));
   }
 
-  /**
-   * @return -1 this EdgeIntersection is located before the argument location
-   * @return 0 this EdgeIntersection is at the argument location
-   * @return 1 this EdgeIntersection is located after the argument location
-   */
-  public int compare(int segmentIndex, double dist)
-  {
-    if (this.segmentIndex < segmentIndex) return -1;
-    if (this.segmentIndex > segmentIndex) return 1;
-    if (this.dist < dist) return -1;
-    if (this.dist > dist) return 1;
-    return 0;
-  }
+  public boolean isInterior() { return isInterior; }
 
   public boolean isEndPoint(int maxSegmentIndex)
   {
-    if (segmentIndex == 0 && dist == 0.0) return true;
+    if (segmentIndex == 0 && ! isInterior) return true;
     if (segmentIndex == maxSegmentIndex) return true;
     return false;
   }
 
+  /**
+   * @return -1 this SegmentNode is located before the argument location
+   * @return 0 this SegmentNode is at the argument location
+   * @return 1 this SegmentNode is located after the argument location
+   */
   public int compareTo(Object obj)
   {
     SegmentNode other = (SegmentNode) obj;
-    return compare(other.segmentIndex, other.dist);
+
+    if (segmentIndex < other.segmentIndex) return -1;
+    if (segmentIndex > other.segmentIndex) return 1;
+
+    if (coord.equals2D(other.coord)) return 0;
+
+    return SegmentPointComparator.compare(segmentOctant, coord, other.coord);
+    //return segment.compareNodePosition(this, other);
   }
 
   public void print(PrintStream out)
   {
     out.print(coord);
     out.print(" seg # = " + segmentIndex);
-    out.println(" dist = " + dist);
   }
 }
diff --git a/src/com/vividsolutions/jts/noding/SegmentNodeList.java b/src/com/vividsolutions/jts/noding/SegmentNodeList.java
index af46a14..b58754e 100644
--- a/src/com/vividsolutions/jts/noding/SegmentNodeList.java
+++ b/src/com/vividsolutions/jts/noding/SegmentNodeList.java
@@ -1,6 +1,3 @@
-
-
-
 /*
  * The JTS Topology Suite is a collection of Java classes that
  * implement the fundamental operations required to validate a given
@@ -38,154 +35,173 @@ package com.vividsolutions.jts.noding;
 import java.io.PrintStream;
 import java.util.*;
 import com.vividsolutions.jts.geom.*;
-import com.vividsolutions.jts.util.Debug;
+import com.vividsolutions.jts.util.*;
 
 /**
  * A list of the {@link SegmentNode}s present along a noded {@link SegmentString}.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class SegmentNodeList
 {
-  // a List of SegmentNodes
-  //List list = new ArrayList();    // more efficient to use a LinkedList, but ArrayList is easier for debugging
   private Map nodeMap = new TreeMap();
   private SegmentString edge;  // the parent edge
-  private List sortedNodes;
 
   public SegmentNodeList(SegmentString edge)
   {
     this.edge = edge;
   }
 
+  public SegmentString getEdge() { return edge; }
+
   /**
    * Adds an intersection into the list, if it isn't already there.
    * The input segmentIndex and dist are expected to be normalized.
+   *
    * @return the SegmentIntersection found or added
    */
-  public SegmentNode add(Coordinate intPt, int segmentIndex, double dist)
+  public SegmentNode add(Coordinate intPt, int segmentIndex)
   {
-    SegmentNode eiNew = new SegmentNode(intPt, segmentIndex, dist);
-    Object obj = nodeMap.get(eiNew);
+    SegmentNode eiNew = new SegmentNode(edge, intPt, segmentIndex, edge.getSegmentOctant(segmentIndex));
     SegmentNode ei = (SegmentNode) nodeMap.get(eiNew);
     if (ei != null) {
+      // debugging sanity check
+      Assert.isTrue(ei.coord.equals2D(intPt), "Found equal nodes with different coordinates");
+//      if (! ei.coord.equals2D(intPt))
+//        Debug.println("Found equal nodes with different coordinates");
+
       return ei;
     }
+    // node does not exist, so create it
     nodeMap.put(eiNew, eiNew);
     return eiNew;
   }
-  /**
-   * Adds an intersection into the list, if it isn't already there.
-   * The input segmentIndex and dist are expected to be normalized.
-   * @return the SegmentIntersection found or added
-   */
-  /*
-  public SegmentNode OLDadd(Coordinate intPt, int segmentIndex, double dist)
-  {
-//Debug.println("adding edgeInt " + intPt + " " + segmentIndex + " " + dist);
-    ListIterator insertIt = list.listIterator();
-    boolean isInList = findInsertionPoint(segmentIndex, dist, insertIt);
-    SegmentNode ei;
-    if (! isInList) {
-      ei = new SegmentNode(intPt, segmentIndex, dist);
-      insertIt.add(ei);
-    }
-    else
-      ei = (SegmentNode) insertIt.next();
-    return ei;
-  }
+
   /**
    * returns an iterator of SegmentNodes
    */
   public Iterator iterator() { return nodeMap.values().iterator(); }
-/*
-  public boolean isEmpty()
+
+  /**
+   * Adds nodes for the first and last points of the edge
+   */
+  private void addEndpoints()
   {
-    Iterator it = list.iterator();
-    return ! it.hasNext();
+    int maxSegIndex = edge.size() - 1;
+    add(edge.getCoordinate(0), 0);
+    add(edge.getCoordinate(maxSegIndex), maxSegIndex);
   }
-  */
+
   /**
-   * This routine searches the list for the insertion point for the given intersection
-   * (which must be in normalized form).
-   * The intersection point may already be in the list - in this case, the intersection
-   * is not inserted.
-   * If the intersection is new, it is inserted into the list.
-   * The insertIt iterator is left pointing at the correct place
-   * to insert the intersection, if the intersection was not found.
-   *
-   * @return true if this intersection is already in the list
+   * Adds nodes for any collapsed edge pairs.
+   * Collapsed edge pairs can be caused by inserted nodes, or they can be
+   * pre-existing in the edge vertex list.
+   * In order to provide the correct fully noded semantics,
+   * the vertex at the base of a collapsed pair must also be added as a node.
    */
-  /*
-  boolean findInsertionPoint(int segmentIndex, double dist, ListIterator insertIt)
+  private void addCollapsedNodes()
   {
-    // The insertIt position trails the findIt position by one
-    ListIterator findIt = list.listIterator();
-    boolean found = false;
-    while (findIt.hasNext()) {
-      SegmentNode ei = (SegmentNode) findIt.next();
-      int compare = ei.compare(segmentIndex, dist);
+    List collapsedVertexIndexes = new ArrayList();
 
-      // intersection found - insertIt.next() will retrieve it
-      if (compare == 0) return true;
+    findCollapsesFromInsertedNodes(collapsedVertexIndexes);
+    findCollapsesFromExistingVertices(collapsedVertexIndexes);
 
-      // this ei is past the intersection location, so intersection was not found
-      if (compare > 0) return false;
-
-      // this ei was before the intersection point, so move to next
-      insertIt.next();
+    // node the collapses
+    for (Iterator it = collapsedVertexIndexes.iterator(); it.hasNext(); ) {
+      int vertexIndex = ((Integer) it.next()).intValue();
+      add(edge.getCoordinate(vertexIndex), vertexIndex);
     }
-    return false;
   }
 
-  public boolean isIntersection(Coordinate pt)
+  /**
+   * Adds nodes for any collapsed edge pairs
+   * which are pre-existing in the vertex list.
+   */
+  private void findCollapsesFromExistingVertices(List collapsedVertexIndexes)
   {
-    for (Iterator it = list.iterator(); it.hasNext(); ) {
-      SegmentNode ei = (SegmentNode) it.next();
-      if (ei.coord.equals(pt))
-       return true;
+    for (int i = 0; i < edge.size() - 2; i++) {
+      Coordinate p0 = edge.getCoordinate(i);
+      Coordinate p1 = edge.getCoordinate(i + 1);
+      Coordinate p2 = edge.getCoordinate(i + 2);
+      if (p0.equals2D(p2)) {
+        // add base of collapse as node
+        collapsedVertexIndexes.add(new Integer(i + 1));
+      }
     }
-    return false;
   }
-  */
+
   /**
-   * Adds entries for the first and last points of the edge to the list
+   * Adds nodes for any collapsed edge pairs caused by inserted nodes
+   * Collapsed edge pairs occur when the same coordinate is inserted as a node
+   * both before and after an existing edge vertex.
+   * To provide the correct fully noded semantics,
+   * the vertex must be added as a node as well.
    */
-  public void addEndpoints()
+  private void findCollapsesFromInsertedNodes(List collapsedVertexIndexes)
   {
-    int maxSegIndex = edge.size() - 1;
-    add(edge.getCoordinate(0), 0, 0.0);
-    add(edge.getCoordinate(maxSegIndex), maxSegIndex, 0.0);
+    int[] collapsedVertexIndex = new int[1];
+    Iterator it = iterator();
+    // there should always be at least two entries in the list, since the endpoints are nodes
+    SegmentNode eiPrev = (SegmentNode) it.next();
+    while (it.hasNext()) {
+      SegmentNode ei = (SegmentNode) it.next();
+      boolean isCollapsed = findCollapseIndex(eiPrev, ei, collapsedVertexIndex);
+      if (isCollapsed)
+        collapsedVertexIndexes.add(new Integer(collapsedVertexIndex[0]));
+
+      eiPrev = ei;
+    }
+  }
+
+  private boolean findCollapseIndex(SegmentNode ei0, SegmentNode ei1, int[] collapsedVertexIndex)
+  {
+    // only looking for equal nodes
+    if (! ei0.coord.equals2D(ei1.coord)) return false;
+
+    int numVerticesBetween = ei1.segmentIndex - ei0.segmentIndex;
+    if (! ei1.isInterior()) {
+      numVerticesBetween--;
+    }
+
+    // if there is a single vertex between the two equal nodes, this is a collapse
+    if (numVerticesBetween == 1) {
+      collapsedVertexIndex[0] = ei0.segmentIndex + 1;
+      return true;
+    }
+    return false;
   }
 
+
   /**
    * Creates new edges for all the edges that the intersections in this
    * list split the parent edge into.
-   * Adds the edges to the input list (this is so a single list
-   * can be used to accumulate all split edges for a Geometry).
+   * Adds the edges to the provided argument list
+   * (this is so a single list can be used to accumulate all split edges
+   * for a set of {@link SegmentString}s).
    */
   public void addSplitEdges(Collection edgeList)
   {
-    // testingOnly
-    List testingSplitEdges = new ArrayList();
     // ensure that the list has entries for the first and last point of the edge
     addEndpoints();
+    addCollapsedNodes();
 
     Iterator it = iterator();
-    // there should always be at least two entries in the list
+    // there should always be at least two entries in the list, since the endpoints are nodes
     SegmentNode eiPrev = (SegmentNode) it.next();
     while (it.hasNext()) {
       SegmentNode ei = (SegmentNode) it.next();
       SegmentString newEdge = createSplitEdge(eiPrev, ei);
       edgeList.add(newEdge);
-
-      testingSplitEdges.add(newEdge);
-
       eiPrev = ei;
     }
     //checkSplitEdgesCorrectness(testingSplitEdges);
   }
 
+  /**
+   * Checks the correctness of the set of split edges corresponding to this edge
+   *
+   * @param splitEdges the split edges for this edge (in order)
+   */
   private void checkSplitEdgesCorrectness(List splitEdges)
   {
     Coordinate[] edgePts = edge.getCoordinates();
@@ -193,16 +209,17 @@ public class SegmentNodeList
     // check that first and last points of split edges are same as endpoints of edge
     SegmentString split0 = (SegmentString) splitEdges.get(0);
     Coordinate pt0 = split0.getCoordinate(0);
-    if (! pt0.equals(edgePts[0]))
+    if (! pt0.equals2D(edgePts[0]))
       throw new RuntimeException("bad split edge start point at " + pt0);
 
     SegmentString splitn = (SegmentString) splitEdges.get(splitEdges.size() - 1);
     Coordinate[] splitnPts = splitn.getCoordinates();
     Coordinate ptn = splitnPts[splitnPts.length - 1];
-    if (! ptn.equals(edgePts[edgePts.length - 1]))
+    if (! ptn.equals2D(edgePts[edgePts.length - 1]))
       throw new RuntimeException("bad split edge end point at " + ptn);
 
   }
+
   /**
    * Create a new "split edge" with the section of points between
    * (and including) the two intersections.
@@ -210,7 +227,7 @@ public class SegmentNodeList
    */
   SegmentString createSplitEdge(SegmentNode ei0, SegmentNode ei1)
   {
-//Debug.print("\ncreateSplitEdge"); Debug.print(ei0); Debug.print(ei1);
+//Debug.println("\ncreateSplitEdge"); Debug.print(ei0); Debug.print(ei1);
     int npts = ei1.segmentIndex - ei0.segmentIndex + 2;
 
     Coordinate lastSegStartPt = edge.getCoordinate(ei1.segmentIndex);
@@ -218,7 +235,7 @@ public class SegmentNodeList
     // add it to the points list as well.
     // (This check is needed because the distance metric is not totally reliable!)
     // The check for point equality is 2D only - Z values are ignored
-    boolean useIntPt1 = ei1.dist > 0.0 || ! ei1.coord.equals2D(lastSegStartPt);
+    boolean useIntPt1 = ei1.isInterior() || ! ei1.coord.equals2D(lastSegStartPt);
     if (! useIntPt1) {
       npts--;
     }
@@ -230,32 +247,10 @@ public class SegmentNodeList
       pts[ipt++] = edge.getCoordinate(i);
     }
     if (useIntPt1) pts[ipt] = ei1.coord;
-    return new SegmentString(pts, edge.getContext());
-  }
-/*
-  public Coordinate[] getCoordinates()
-  {
-    List pts = new ArrayList();
-    Iterator it = list.iterator();
-    int nextIndex = 0;
-    while (it.hasNext()) {
-      SegmentNode ei = (SegmentNode) it.next();
-
-      // add points up to this intersection
 
-      for (int i = nextIndex; i <= ei.segmentIndex; i++) {
-        pts.add(edge.getCoordinate(i));
-      }
-      nextIndex = ei.segmentIndex + 1;
-      if (ei.dist > 0.0) pts.add(ei.coord);
-    }
-    // add remaining edge coordinates, if any
-    for (int i = nextIndex; i < edge.getCoordinates().length; i++) {
-      pts.add(edge.getCoordinate(i));
-    }
-    return CoordinateArrays.toCoordinateArray(pts);
+    return new SegmentString(pts, edge.getData());
   }
-*/
+
   public void print(PrintStream out)
   {
     out.println("Intersections:");
@@ -265,3 +260,69 @@ public class SegmentNodeList
     }
   }
 }
+
+// INCOMPLETE!
+class NodeVertexIterator
+    implements Iterator
+{
+  private SegmentNodeList nodeList;
+  private SegmentString edge;
+  private Iterator nodeIt;
+  private SegmentNode currNode = null;
+  private SegmentNode nextNode = null;
+  private int currSegIndex = 0;
+
+  NodeVertexIterator(SegmentNodeList nodeList)
+  {
+    this.nodeList = nodeList;
+    edge = nodeList.getEdge();
+    nodeIt = nodeList.iterator();
+    readNextNode();
+  }
+
+  public boolean hasNext() {
+    if (nextNode == null) return false;
+    return true;
+  }
+
+  public Object next()
+  {
+    if (currNode == null) {
+      currNode = nextNode;
+      currSegIndex = currNode.segmentIndex;
+      readNextNode();
+      return currNode;
+    }
+    // check for trying to read too far
+    if (nextNode == null) return null;
+
+    if (nextNode.segmentIndex == currNode.segmentIndex) {
+      currNode = nextNode;
+      currSegIndex = currNode.segmentIndex;
+      readNextNode();
+      return currNode;
+    }
+
+    if (nextNode.segmentIndex > currNode.segmentIndex) {
+
+    }
+    return null;
+  }
+
+  private void readNextNode()
+  {
+    if (nodeIt.hasNext())
+      nextNode = (SegmentNode) nodeIt.next();
+    else
+      nextNode = null;
+  }
+  /**
+   *  Not implemented.
+   *
+   *@throws  UnsupportedOperationException  This method is not implemented.
+   */
+  public void remove() {
+    throw new UnsupportedOperationException(getClass().getName());
+  }
+
+}
diff --git a/src/com/vividsolutions/jts/noding/SegmentPointComparator.java b/src/com/vividsolutions/jts/noding/SegmentPointComparator.java
new file mode 100644
index 0000000..4ea11df
--- /dev/null
+++ b/src/com/vividsolutions/jts/noding/SegmentPointComparator.java
@@ -0,0 +1,65 @@
+package com.vividsolutions.jts.noding;
+
+import com.vividsolutions.jts.geom.Coordinate;
+import com.vividsolutions.jts.util.Assert;
+
+/**
+ * Implements a robust method of comparing the relative position of two
+ * points along the same segment.
+ * The coordinates are assumed to lie "near" the segment.
+ * This means that this algorithm will only return correct results
+ * if the input coordinates
+ * have the same precision and correspond to rounded values
+ * of exact coordinates lying on the segment.
+ *
+ * @version 1.7
+ */
+public class SegmentPointComparator {
+
+  /**
+   * Compares two {@link Coordinate}s for their relative position along a segment
+   * lying in the specified {@link Octant}.
+   *
+   * @return -1 node0 occurs first
+   * @return 0 the two nodes are equal
+   * @return 1 node1 occurs first
+   */
+  public static int compare(int octant, Coordinate p0, Coordinate p1)
+  {
+    // nodes can only be equal if their coordinates are equal
+    if (p0.equals2D(p1)) return 0;
+
+    int xSign = relativeSign(p0.x, p1.x);
+    int ySign = relativeSign(p0.y, p1.y);
+
+    switch (octant) {
+      case 0: return compareValue(xSign, ySign);
+      case 1: return compareValue(ySign, xSign);
+      case 2: return compareValue(ySign, -xSign);
+      case 3: return compareValue(-xSign, ySign);
+      case 4: return compareValue(-xSign, -ySign);
+      case 5: return compareValue(-ySign, -xSign);
+      case 6: return compareValue(-ySign, xSign);
+      case 7: return compareValue(xSign, -ySign);
+    }
+    Assert.shouldNeverReachHere("invalid octant value");
+    return 0;
+  }
+
+  public static int relativeSign(double x0, double x1)
+  {
+    if (x0 < x1) return -1;
+    if (x0 > x1) return 1;
+    return 0;
+  }
+
+  private static int compareValue(int compareSign0, int compareSign1)
+  {
+    if (compareSign0 < 0) return -1;
+    if (compareSign0 > 0) return 1;
+    if (compareSign1 < 0) return -1;
+    if (compareSign1 > 0) return 1;
+    return 0;
+
+  }
+}
\ No newline at end of file
diff --git a/src/com/vividsolutions/jts/noding/SegmentString.java b/src/com/vividsolutions/jts/noding/SegmentString.java
index 1291b3a..2c7a9a6 100644
--- a/src/com/vividsolutions/jts/noding/SegmentString.java
+++ b/src/com/vividsolutions/jts/noding/SegmentString.java
@@ -33,45 +33,93 @@
  */
 package com.vividsolutions.jts.noding;
 
+import java.util.*;
 import com.vividsolutions.jts.algorithm.LineIntersector;
 import com.vividsolutions.jts.geom.Coordinate;
 
 /**
- * Contains a list of consecutive line segments which can be used to node the segments.
+ * Represents a list of contiguous line segments,
+ * and supports noding the segments.
  * The line segments are represented by an array of {@link Coordinate}s.
+ * Intended to optimize the noding of contiguous segments by
+ * reducing the number of allocated objects.
+ * SegmentStrings can carry a context object, which is useful
+ * for preserving topological or parentage information.
+ * All noded substrings are initialized with the same context object.
  *
- *
- * @version 1.6
+ * @version 1.7
  */
-public class SegmentString {
+public class SegmentString
+{
+  public static List getNodedSubstrings(Collection segStrings)
+  {
+    List resultEdgelist = new ArrayList();
+    getNodedSubstrings(segStrings, resultEdgelist);
+    return resultEdgelist;
+  }
+
+  public static void getNodedSubstrings(Collection segStrings, Collection resultEdgelist)
+  {
+    for (Iterator i = segStrings.iterator(); i.hasNext(); ) {
+      SegmentString ss = (SegmentString) i.next();
+      ss.getNodeList().addSplitEdges(resultEdgelist);
+    }
+  }
 
-  private SegmentNodeList eiList = new SegmentNodeList(this);
+  private SegmentNodeList nodeList = new SegmentNodeList(this);
   private Coordinate[] pts;
-  private Object context;
-  private boolean isIsolated;
+  private Object data;
 
-  public SegmentString(Coordinate[] pts, Object context)
+  /**
+   * Creates a new segment string from a list of vertices.
+   *
+   * @param pts the vertices of the segment string
+   * @param data the user-defined data of this segment string (may be null)
+   */
+  public SegmentString(Coordinate[] pts, Object data)
   {
     this.pts = pts;
-    this.context = context;
+    this.data = data;
   }
 
-  public Object getContext() { return context; }
-  public SegmentNodeList getIntersectionList() { return eiList; }
+  /**
+   * Gets the user-defined data for this segment string.
+   *
+   * @return the user-defined data
+   */
+  public Object getData() { return data; }
+
+  /**
+   * Sets the user-defined data for this segment string.
+   *
+   * @param data an Object containing user-defined data
+   */
+  public void setData(Object data) { this.data = data; }
+
+  public SegmentNodeList getNodeList() { return nodeList; }
   public int size() { return pts.length; }
   public Coordinate getCoordinate(int i) { return pts[i]; }
   public Coordinate[] getCoordinates() { return pts; }
 
-  public void setIsolated(boolean isIsolated)  {    this.isIsolated = isIsolated;  }
-  public boolean isIsolated()  {    return isIsolated;  }
-
-
   public boolean isClosed()
   {
     return pts[0].equals(pts[pts.length - 1]);
   }
 
   /**
+   * Gets the octant of the segment starting at vertex <code>index</code>.
+   *
+   * @param index the index of the vertex starting the segment.  Must not be
+   * the last index in the vertex list
+   * @return the octant of the segment at the vertex
+   */
+  public int getSegmentOctant(int index)
+  {
+    if (index == pts.length - 1) return -1;
+    return Octant.octant(getCoordinate(index), getCoordinate(index + 1));
+  }
+
+  /**
    * Adds EdgeIntersections for one or both
    * intersections found for a segment of an edge to the edge intersection list.
    */
@@ -90,49 +138,11 @@ public class SegmentString {
   public void addIntersection(LineIntersector li, int segmentIndex, int geomIndex, int intIndex)
   {
     Coordinate intPt = new Coordinate(li.getIntersection(intIndex));
-    double dist = li.getEdgeDistance(geomIndex, intIndex);
-    addIntersection(intPt, segmentIndex, dist);
+    addIntersection(intPt, segmentIndex);
   }
 
-  public void OLDaddIntersection(LineIntersector li, int segmentIndex, int geomIndex, int intIndex)
-  {
-    Coordinate intPt = new Coordinate(li.getIntersection(intIndex));
-    int normalizedSegmentIndex = segmentIndex;
-    double dist = li.getEdgeDistance(geomIndex, intIndex);
-//Debug.println("edge intpt: " + intPt + " dist: " + dist);
-    // normalize the intersection point location
-    int nextSegIndex = normalizedSegmentIndex + 1;
-    if (nextSegIndex < pts.length) {
-      Coordinate nextPt = pts[nextSegIndex];
-//Debug.println("next pt: " + nextPt);
-
-      // Normalize segment index if intPt falls on vertex
-      // The check for point equality is 2D only - Z values are ignored
-      if (intPt.equals2D(nextPt)) {
-//Debug.println("normalized distance");
-          normalizedSegmentIndex = nextSegIndex;
-          dist = 0.0;
-      }
-    }
-    /**
-    * Add the intersection point to edge intersection list.
-    */
-    SegmentNode ei = eiList.add(intPt, normalizedSegmentIndex, dist);
-//ei.print(System.out);
-  }
-  /**
-   * Add an EdgeIntersection for intersection intIndex.
-   * An intersection that falls exactly on a vertex of the edge is normalized
-   * to use the higher of the two possible segmentIndexes
-   */
   public void addIntersection(Coordinate intPt, int segmentIndex)
   {
-    double dist = LineIntersector.computeEdgeDistance(intPt, pts[segmentIndex], pts[segmentIndex + 1]);
-    addIntersection(intPt, segmentIndex, dist);
-  }
-
-  public void addIntersection(Coordinate intPt, int segmentIndex, double dist)
-  {
     int normalizedSegmentIndex = segmentIndex;
 //Debug.println("edge intpt: " + intPt + " dist: " + dist);
       // normalize the intersection point location
@@ -146,15 +156,11 @@ public class SegmentString {
         if (intPt.equals2D(nextPt)) {
 //Debug.println("normalized distance");
             normalizedSegmentIndex = nextSegIndex;
-            dist = 0.0;
         }
       }
       /**
       * Add the intersection point to edge intersection list.
       */
-      SegmentNode ei = eiList.add(intPt, normalizedSegmentIndex, dist);
-//ei.print(System.out);
-
+      SegmentNode ei = nodeList.add(intPt, normalizedSegmentIndex);
   }
-
 }
diff --git a/src/com/vividsolutions/jts/noding/SegmentStringDissolver.java b/src/com/vividsolutions/jts/noding/SegmentStringDissolver.java
new file mode 100644
index 0000000..90cba46
--- /dev/null
+++ b/src/com/vividsolutions/jts/noding/SegmentStringDissolver.java
@@ -0,0 +1,164 @@
+/*
+ * The JTS Topology Suite is a collection of Java classes that
+ * implement the fundamental operations required to validate a given
+ * geo-spatial data set to a known topological specification.
+ *
+ * Copyright (C) 2001 Vivid Solutions
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * For more information, contact:
+ *
+ *     Vivid Solutions
+ *     Suite #1A
+ *     2328 Government Street
+ *     Victoria BC  V8T 5G5
+ *     Canada
+ *
+ *     (250)385-6040
+ *     www.vividsolutions.com
+ */
+package com.vividsolutions.jts.noding;
+
+import java.util.*;
+import com.vividsolutions.jts.geom.*;
+
+/**
+ * Dissolves a noded collection of {@link SegmentString}s to produce
+ * a set of merged linework with unique segments.
+ * A custom merging strategy can be applied when two identical (up to orientation)
+ * strings are dissolved together.
+ * The default merging strategy is simply to discard the merged string.
+ * <p>
+ * A common use for this class is to merge noded edges
+ * while preserving topological labelling.
+ *
+ * @version 1.7
+ * @see SegmentStringMerger
+ */
+public class SegmentStringDissolver
+{
+  public interface SegmentStringMerger {
+    /**
+     * Updates the context data of a SegmentString
+     * when an identical (up to orientation) one is found during dissolving.
+     *
+     * @param mergeTarget the segment string to update
+     * @param ssToMerge the segment string being dissolved
+     * @param isSameOrientation <code>true</code> if the strings are in the same direction,
+     * <code>false</code> if they are opposite
+     */
+    void merge(SegmentString mergeTarget, SegmentString ssToMerge, boolean isSameOrientation);
+  }
+
+  private SegmentStringMerger merger;
+  private Map ocaMap = new TreeMap();
+
+  // testing only
+  //private List testAddedSS = new ArrayList();
+
+  /**
+   * Creates a dissolver with a user-defined merge strategy.
+   *
+   * @param merger the merging strategy to use
+   */
+  public SegmentStringDissolver(SegmentStringMerger merger) {
+    this.merger = merger;
+  }
+
+  /**
+   * Creates a dissolver with the default merging strategy.
+   */
+  public SegmentStringDissolver() {
+    this(null);
+  }
+
+  /**
+   * Dissolve all {@link SegmentString}s in the input {@link Collection}
+   * @param segStrings
+   */
+  public void dissolve(Collection segStrings)
+  {
+    for (Iterator i = segStrings.iterator(); i.hasNext(); ) {
+      dissolve((SegmentString) i.next());
+    }
+  }
+
+  private void add(OrientedCoordinateArray oca, SegmentString segString)
+  {
+    ocaMap.put(oca, segString);
+    //testAddedSS.add(oca);
+  }
+
+  /**
+   * Dissolve the given {@link SegmentString}.
+   *
+   * @param segString the string to dissolve
+   */
+  public void dissolve(SegmentString segString)
+  {
+    OrientedCoordinateArray oca = new OrientedCoordinateArray(segString.getCoordinates());
+    SegmentString existing = findMatching(oca, segString);
+    if (existing == null) {
+      add(oca, segString);
+    }
+    else {
+      if (merger != null) {
+        boolean isSameOrientation
+            = CoordinateArrays.equals(existing.getCoordinates(), segString.getCoordinates());
+        merger.merge(existing, segString, isSameOrientation);
+      }
+    }
+  }
+
+  private SegmentString findMatching(OrientedCoordinateArray oca,
+                                    SegmentString segString)
+  {
+    SegmentString matchSS = (SegmentString) ocaMap.get(oca);
+    /*
+    boolean hasBeenAdded = checkAdded(oca);
+    if (matchSS == null && hasBeenAdded) {
+      System.out.println("added!");
+    }
+    */
+    return matchSS;
+  }
+
+/*
+  public static CoordinateArrays.BidirectionalComparator ptsComp
+      = new CoordinateArrays.BidirectionalComparator();
+
+
+  private boolean checkAdded(OrientedCoordinateArray oca)
+  {
+    for (Iterator i = testAddedSS.iterator(); i.hasNext(); ) {
+      OrientedCoordinateArray addedOCA = (OrientedCoordinateArray) i.next();
+      if (oca.compareTo(addedOCA) == 0)
+        return true;
+    }
+    return false;
+  }
+*/
+
+  /**
+   * Gets the collection of dissolved (i.e. unique) {@link SegmentString}s
+   *
+   * @return the unique {@link SegmentString}s
+   */
+  public Collection getDissolved() { return ocaMap.values(); }
+}
+
+
+
diff --git a/src/com/vividsolutions/jts/noding/SimpleNoder.java b/src/com/vividsolutions/jts/noding/SimpleNoder.java
index dc4a50b..b238852 100644
--- a/src/com/vividsolutions/jts/noding/SimpleNoder.java
+++ b/src/com/vividsolutions/jts/noding/SimpleNoder.java
@@ -42,17 +42,25 @@ import com.vividsolutions.jts.geom.*;
  * This has n^2 performance, so is too slow for use on large numbers
  * of segments.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class SimpleNoder
-    extends Noder
+    extends SinglePassNoder
 {
 
+  private Collection nodedSegStrings;
+
   public SimpleNoder() {
   }
 
-  public Collection node(Collection inputSegStrings)
+  public Collection getNodedSubstrings()
+  {
+    return  SegmentString.getNodedSubstrings(nodedSegStrings);
+  }
+
+  public void computeNodes(Collection inputSegStrings)
   {
+    this.nodedSegStrings = inputSegStrings;
     for (Iterator i0 = inputSegStrings.iterator(); i0.hasNext(); ) {
       SegmentString edge0 = (SegmentString) i0.next();
       for (Iterator i1 = inputSegStrings.iterator(); i1.hasNext(); ) {
@@ -60,8 +68,6 @@ public class SimpleNoder
         computeIntersects(edge0, edge1);
       }
     }
-    List nodedSegStrings = getNodedEdges(inputSegStrings);
-    return nodedSegStrings;
   }
 
   private void computeIntersects(SegmentString e0, SegmentString e1)
diff --git a/src/com/vividsolutions/jts/noding/Noder.java b/src/com/vividsolutions/jts/noding/SinglePassNoder.java
similarity index 51%
copy from src/com/vividsolutions/jts/noding/Noder.java
copy to src/com/vividsolutions/jts/noding/SinglePassNoder.java
index 27d8439..90707fa 100644
--- a/src/com/vividsolutions/jts/noding/Noder.java
+++ b/src/com/vividsolutions/jts/noding/SinglePassNoder.java
@@ -34,46 +34,54 @@
 package com.vividsolutions.jts.noding;
 
 import java.util.*;
-import com.vividsolutions.jts.algorithm.LineIntersector;
-import com.vividsolutions.jts.geom.*;
-import com.vividsolutions.jts.geomgraph.index.*;
 
 /**
- * Computes all intersections between segments in a set of {@link SegmentString}s.
- * Intersections found are represented as {@link SegmentNode}s and add to the
- * {@link SegmentString}s in which they occur.
+ * Base class for {@link Noder}s which make a single
+ * pass to find intersections.
+ * This allows using a custom {@link SegmentIntersector}
+ * (which for instance may simply identify intersections, rather than
+ * insert them).
  *
- * @version 1.6
+ * @version 1.7
  */
-public abstract class Noder
+public abstract class SinglePassNoder
+    implements Noder
 {
 
-  public static List getNodedEdges(Collection segStrings)
-  {
-    List resultEdgelist = new ArrayList();
-    getNodedEdges(segStrings, resultEdgelist);
-    return resultEdgelist;
-  }
-
-  public static void getNodedEdges(Collection segStrings, Collection resultEdgelist)
-  {
-    for (Iterator i = segStrings.iterator(); i.hasNext(); ) {
-      SegmentString ss = (SegmentString) i.next();
-      ss.getIntersectionList().addSplitEdges(resultEdgelist);
-    }
-  }
-
   protected SegmentIntersector segInt;
-  //protected LineIntersector li;
 
-  public Noder() {
+  public SinglePassNoder() {
   }
 
+  /**
+   * Sets the SegmentIntersector to use with this noder.
+   * A SegmentIntersector will normally add intersection nodes
+   * to the input segment strings, but it may not - it may
+   * simply record the presence of intersections.
+   * However, some Noders may require that intersections be added.
+   *
+   * @param segInt
+   */
   public void setSegmentIntersector(SegmentIntersector segInt)
   {
     this.segInt = segInt;
   }
 
-  public abstract Collection node(Collection segStrings);
+  /**
+   * Computes the noding for a collection of {@link SegmentString}s.
+   * Some Noders may add all these nodes to the input SegmentStrings;
+   * others may only add some or none at all.
+   *
+   * @param segStrings a collection of {@link SegmentString}s to node
+   */
+  public abstract void computeNodes(Collection segStrings);
+
+  /**
+   * Returns a {@link Collection} of fully noded {@link SegmentStrings}.
+   * The SegmentStrings have the same context as their parent.
+   *
+   * @return a Collection of SegmentStrings
+   */
+  public abstract Collection getNodedSubstrings();
 
 }
diff --git a/src/com/vividsolutions/jts/noding/snapround/HotPixel.java b/src/com/vividsolutions/jts/noding/snapround/HotPixel.java
new file mode 100644
index 0000000..76f929b
--- /dev/null
+++ b/src/com/vividsolutions/jts/noding/snapround/HotPixel.java
@@ -0,0 +1,227 @@
+package com.vividsolutions.jts.noding.snapround;
+
+import com.vividsolutions.jts.algorithm.*;
+import com.vividsolutions.jts.geom.*;
+import com.vividsolutions.jts.noding.*;
+import com.vividsolutions.jts.util.*;
+
+/**
+ * Implements a "hot pixel" as used in the Snap Rounding algorithm.
+ * A hot pixel contains the interior of the tolerance square and
+ * the boundary
+ * <b>minus</b> the top and right segments.
+ * <p>
+ * The hot pixel operations are all computed in the integer domain
+ * to avoid rounding problems.
+ *
+ * @version 1.7
+ */
+public class HotPixel
+{
+  // testing only
+//  public static int nTests = 0;
+
+  private LineIntersector li;
+
+  private Coordinate pt;
+  private Coordinate originalPt;
+  private Coordinate ptScaled;
+
+  private Coordinate p0Scaled;
+  private Coordinate p1Scaled;
+
+  private double scaleFactor;
+
+  private double minx;
+  private double maxx;
+  private double miny;
+  private double maxy;
+  /**
+   * The corners of the hot pixel, in the order:
+   *  10
+   *  23
+   */
+  private Coordinate[] corner = new Coordinate[4];
+
+  private Envelope safeEnv = null;
+
+  public HotPixel(Coordinate pt, double scaleFactor, LineIntersector li) {
+    originalPt = pt;
+    this.pt = pt;
+    this.scaleFactor = scaleFactor;
+    this.li = li;
+    //tolerance = 0.5;
+    if (scaleFactor != 1.0) {
+      this.pt = new Coordinate(scale(pt.x), scale(pt.y));
+      p0Scaled = new Coordinate();
+      p1Scaled = new Coordinate();
+    }
+    initCorners(this.pt);
+  }
+
+  public Coordinate getCoordinate() { return originalPt; }
+
+  /**
+   * Returns a "safe" envelope that is guaranteed to contain the hot pixel
+   * @return
+   */
+  public Envelope getSafeEnvelope()
+  {
+    if (safeEnv == null) {
+      double safeTolerance = .75 / scaleFactor;
+      safeEnv = new Envelope(originalPt.x - safeTolerance,
+                             originalPt.x + safeTolerance,
+                             originalPt.y - safeTolerance,
+                             originalPt.y + safeTolerance
+                             );
+    }
+    return safeEnv;
+  }
+
+  private void initCorners(Coordinate pt)
+  {
+    double tolerance = 0.5;
+    minx = pt.x - tolerance;
+    maxx = pt.x + tolerance;
+    miny = pt.y - tolerance;
+    maxy = pt.y + tolerance;
+
+    corner[0] = new Coordinate(maxx, maxy);
+    corner[1] = new Coordinate(minx, maxy);
+    corner[2] = new Coordinate(minx, miny);
+    corner[3] = new Coordinate(maxx, miny);
+  }
+
+  private double scale(double val)
+  {
+    return (double) Math.round(val * scaleFactor);
+  }
+
+  public boolean intersects(Coordinate p0, Coordinate p1)
+  {
+    if (scaleFactor == 1.0)
+      return intersectsScaled(p0, p1);
+
+    copyScaled(p0, p0Scaled);
+    copyScaled(p1, p1Scaled);
+    return intersectsScaled(p0Scaled, p1Scaled);
+  }
+
+  private void copyScaled(Coordinate p, Coordinate pScaled)
+  {
+    pScaled.x = scale(p.x);
+    pScaled.y = scale(p.y);
+  }
+
+  public boolean intersectsScaled(Coordinate p0, Coordinate p1)
+  {
+    double segMinx = Math.min(p0.x, p1.x);
+    double segMaxx = Math.max(p0.x, p1.x);
+    double segMiny = Math.min(p0.y, p1.y);
+    double segMaxy = Math.max(p0.y, p1.y);
+
+    boolean isOutsidePixelEnv =  maxx < segMinx
+                         || minx > segMaxx
+                         || maxy < segMiny
+                         || miny > segMaxy;
+    if (isOutsidePixelEnv)
+      return false;
+    boolean intersects = intersectsToleranceSquare(p0, p1);
+//    boolean intersectsPixelClosure = intersectsPixelClosure(p0, p1);
+
+//    if (intersectsPixel != intersects) {
+//      Debug.println("Found hot pixel intersection mismatch at " + pt);
+//      Debug.println("Test segment: " + p0 + " " + p1);
+//    }
+
+/*
+    if (scaleFactor != 1.0) {
+      boolean intersectsScaled = intersectsScaledTest(p0, p1);
+      if (intersectsScaled != intersects) {
+        intersectsScaledTest(p0, p1);
+//        Debug.println("Found hot pixel scaled intersection mismatch at " + pt);
+//        Debug.println("Test segment: " + p0 + " " + p1);
+      }
+      return intersectsScaled;
+    }
+*/
+
+    Assert.isTrue(! (isOutsidePixelEnv && intersects), "Found bad envelope test");
+//    if (isOutsideEnv && intersects) {
+//      Debug.println("Found bad envelope test");
+//    }
+
+    return intersects;
+    //return intersectsPixelClosure;
+  }
+
+  /**
+   * Tests whether the segment p0-p1 intersects the hot pixel tolerance square.
+   * Because the tolerance square point set is partially open (along the
+   * top and right) the test needs to be more sophisticated than
+   * simply checking for any intersection.  However, it
+   * can take advantage of the fact that because the hot pixel edges
+   * do not lie on the coordinate grid.  It is sufficient to check
+   * if there is at least one of:
+   * <ul>
+   * <li>a proper intersection with the segment and any hot pixel edge
+   * <li>an intersection between the segment and both the left and bottom edges
+   * <li>an intersection between a segment endpoint and the hot pixel coordinate
+   * </ul>
+   *
+   * @param p0
+   * @param p1
+   * @return
+   */
+  private boolean intersectsToleranceSquare(Coordinate p0, Coordinate p1)
+  {
+    boolean intersectsLeft = false;
+    boolean intersectsBottom = false;
+
+    li.computeIntersection(p0, p1, corner[0], corner[1]);
+    if (li.isProper()) return true;
+
+    li.computeIntersection(p0, p1, corner[1], corner[2]);
+    if (li.isProper()) return true;
+    if (li.hasIntersection()) intersectsLeft = true;
+
+    li.computeIntersection(p0, p1, corner[2], corner[3]);
+    if (li.isProper()) return true;
+    if (li.hasIntersection()) intersectsBottom = true;
+
+    li.computeIntersection(p0, p1, corner[3], corner[0]);
+    if (li.isProper()) return true;
+
+    if (intersectsLeft && intersectsBottom) return true;
+
+    if (p0.equals(pt)) return true;
+    if (p1.equals(pt)) return true;
+
+    return false;
+  }
+  /**
+   * Test whether the given segment intersects
+   * the closure of this hot pixel.
+   * This is NOT the test used in the standard snap-rounding
+   * algorithm, which uses the partially closed tolerance square
+   * instead.
+   * This routine is provided for testing purposes only.
+   *
+   * @param p0 the start point of a line segment
+   * @param p1 the end point of a line segment
+   * @return <code>true</code> if the segment intersects the closure of the pixel's tolerance square
+   */
+  private boolean intersectsPixelClosure(Coordinate p0, Coordinate p1)
+  {
+    li.computeIntersection(p0, p1, corner[0], corner[1]);
+    if (li.hasIntersection()) return true;
+    li.computeIntersection(p0, p1, corner[1], corner[2]);
+    if (li.hasIntersection()) return true;
+    li.computeIntersection(p0, p1, corner[2], corner[3]);
+    if (li.hasIntersection()) return true;
+    li.computeIntersection(p0, p1, corner[3], corner[0]);
+    if (li.hasIntersection()) return true;
+
+    return false;
+  }
+}
\ No newline at end of file
diff --git a/src/com/vividsolutions/jts/noding/snapround/MCIndexPointSnapper.java b/src/com/vividsolutions/jts/noding/snapround/MCIndexPointSnapper.java
new file mode 100644
index 0000000..e4ec76d
--- /dev/null
+++ b/src/com/vividsolutions/jts/noding/snapround/MCIndexPointSnapper.java
@@ -0,0 +1,91 @@
+package com.vividsolutions.jts.noding.snapround;
+
+import java.util.*;
+import com.vividsolutions.jts.geom.*;
+import com.vividsolutions.jts.noding.*;
+import com.vividsolutions.jts.algorithm.LineIntersector;
+import com.vividsolutions.jts.index.chain.*;
+import com.vividsolutions.jts.index.*;
+import com.vividsolutions.jts.index.strtree.STRtree;
+import com.vividsolutions.jts.index.quadtree.Quadtree;
+
+/**
+ * "Snaps" all {@link SegmentString}s in a {@link SpatialIndex} containing
+ * {@link MonotoneChain}s to a given {@link HotPixel}.
+ *
+ * @version 1.7
+ */
+public class MCIndexPointSnapper
+{
+  public static int nSnaps = 0;
+
+  private Collection monoChains;
+  private STRtree index;
+
+  public MCIndexPointSnapper(Collection monoChains, SpatialIndex index) {
+    this.monoChains = monoChains;
+    this.index = (STRtree) index;
+  }
+
+  /**
+   * Snaps (nodes) all interacting segments to this hot pixel.
+   * The hot pixel may represent a vertex of an edge,
+   * in which case this routine uses the optimization
+   * of not noding the vertex itself
+   *
+   * @param hotPixel the hot pixel to snap to
+   * @param parentEdge the edge containing the vertex, if applicable, or <code>null</code>
+   * @param vertexIndex the index of the vertex, if applicable, or -1
+   * @return <code>true</code> if a node was added for this pixel
+   */
+  public boolean snap(HotPixel hotPixel, SegmentString parentEdge, int vertexIndex)
+  {
+    final Envelope pixelEnv = hotPixel.getSafeEnvelope();
+    final HotPixelSnapAction hotPixelSnapAction = new HotPixelSnapAction(hotPixel, parentEdge, vertexIndex);
+
+    index.query(pixelEnv, new ItemVisitor() {
+      public void visitItem(Object item) {
+        MonotoneChain testChain = (MonotoneChain) item;
+        testChain.select(pixelEnv, hotPixelSnapAction);
+      }
+    }
+    );
+    return hotPixelSnapAction.isNodeAdded();
+  }
+
+  public boolean snap(HotPixel hotPixel)
+  {
+    return snap(hotPixel, null, -1);
+  }
+
+  public class HotPixelSnapAction
+      extends MonotoneChainSelectAction
+  {
+    private HotPixel hotPixel;
+    private SegmentString parentEdge;
+    private int vertexIndex;
+    private boolean isNodeAdded = false;
+
+    public HotPixelSnapAction(HotPixel hotPixel, SegmentString parentEdge, int vertexIndex)
+    {
+      this.hotPixel = hotPixel;
+      this.parentEdge = parentEdge;
+      this.vertexIndex = vertexIndex;
+    }
+
+    public boolean isNodeAdded() { return isNodeAdded; }
+
+    public void select(MonotoneChain mc, int startIndex)
+    {
+      SegmentString ss = (SegmentString) mc.getContext();
+      // don't snap a vertex to itself
+      if (parentEdge != null) {
+        if (ss == parentEdge && startIndex == vertexIndex)
+          return;
+      }
+      isNodeAdded = SimpleSnapRounder.addSnappedNode(hotPixel, ss, startIndex);
+    }
+
+  }
+
+}
\ No newline at end of file
diff --git a/src/com/vividsolutions/jts/noding/snapround/MCIndexSnapRounder.java b/src/com/vividsolutions/jts/noding/snapround/MCIndexSnapRounder.java
new file mode 100644
index 0000000..294bf01
--- /dev/null
+++ b/src/com/vividsolutions/jts/noding/snapround/MCIndexSnapRounder.java
@@ -0,0 +1,169 @@
+
+/*
+ * The JTS Topology Suite is a collection of Java classes that
+ * implement the fundamental operations required to validate a given
+ * geo-spatial data set to a known topological specification.
+ *
+ * Copyright (C) 2001 Vivid Solutions
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * For more information, contact:
+ *
+ *     Vivid Solutions
+ *     Suite #1A
+ *     2328 Government Street
+ *     Victoria BC  V8T 5G5
+ *     Canada
+ *
+ *     (250)385-6040
+ *     www.vividsolutions.com
+ */
+package com.vividsolutions.jts.noding.snapround;
+
+import java.util.*;
+import com.vividsolutions.jts.geom.*;
+import com.vividsolutions.jts.algorithm.*;
+import com.vividsolutions.jts.noding.*;
+
+/**
+ * Uses Snap Rounding to compute a rounded,
+ * fully noded arrangement from a set of {@link SegmentString}s.
+ * Implements the Snap Rounding technique described in Hobby, Guibas & Marimont,
+ * and Goodrich et al.
+ * Snap Rounding assumes that all vertices lie on a uniform grid
+ * (hence the precision model of the input must be fixed precision,
+ * and all the input vertices must be rounded to that precision).
+ * <p>
+ * This implementation uses a monotone chains and a spatial index to
+ * speed up the intersection tests.
+ * <p>
+ * This implementation appears to be fully robust using an integer precision model.
+ * It will function with non-integer precision models, but the
+ * results are not 100% guaranteed to be correctly noded.
+ *
+ * @version 1.7
+ */
+public class MCIndexSnapRounder
+    implements Noder
+{
+  private final PrecisionModel pm;
+  private LineIntersector li;
+  private final double scaleFactor;
+  private MCIndexNoder noder;
+  private MCIndexPointSnapper pointSnapper;
+  private Collection nodedSegStrings;
+
+  public MCIndexSnapRounder(PrecisionModel pm) {
+    this.pm = pm;
+    li = new RobustLineIntersector();
+    li.setPrecisionModel(pm);
+    scaleFactor = pm.getScale();
+  }
+
+  public Collection getNodedSubstrings()
+  {
+    return  SegmentString.getNodedSubstrings(nodedSegStrings);
+  }
+
+  public void computeNodes(Collection inputSegmentStrings)
+  {
+    this.nodedSegStrings = inputSegmentStrings;
+    noder = new MCIndexNoder();
+    pointSnapper = new MCIndexPointSnapper(noder.getMonotoneChains(), noder.getIndex());
+    snapRound(inputSegmentStrings, li);
+
+    // testing purposes only - remove in final version
+    //checkCorrectness(inputSegmentStrings);
+  }
+
+  private void checkCorrectness(Collection inputSegmentStrings)
+  {
+    Collection resultSegStrings = SegmentString.getNodedSubstrings(inputSegmentStrings);
+    NodingValidator nv = new NodingValidator(resultSegStrings);
+    try {
+      nv.checkValid();
+    } catch (Exception ex) {
+      ex.printStackTrace();
+    }
+  }
+
+  private void snapRound(Collection segStrings, LineIntersector li)
+  {
+    List intersections = findInteriorIntersections(segStrings, li);
+    computeIntersectionSnaps(intersections);
+    computeVertexSnaps(segStrings);
+  }
+
+  /**
+   * Computes all interior intersections in the collection of {@link SegmentString}s,
+   * and returns their @link Coordinate}s.
+   *
+   * Does NOT node the segStrings.
+   *
+   * @return a list of Coordinates for the intersections
+   */
+  private List findInteriorIntersections(Collection segStrings, LineIntersector li)
+  {
+    IntersectionFinderAdder intFinderAdder = new IntersectionFinderAdder(li);
+    noder.setSegmentIntersector(intFinderAdder);
+    noder.computeNodes(segStrings);
+    return intFinderAdder.getInteriorIntersections();
+  }
+
+  /**
+   * Computes nodes introduced as a result of snapping segments to snap points (hot pixels)
+   */
+  private void computeIntersectionSnaps(Collection snapPts)
+  {
+    for (Iterator it = snapPts.iterator(); it.hasNext(); ) {
+      Coordinate snapPt = (Coordinate) it.next();
+      HotPixel hotPixel = new HotPixel(snapPt, scaleFactor, li);
+      pointSnapper.snap(hotPixel);
+    }
+  }
+
+  /**
+   * Computes nodes introduced as a result of
+   * snapping segments to vertices of other segments
+   *
+   * @param segStrings the list of segment strings to snap together
+   */
+  public void computeVertexSnaps(Collection edges)
+  {
+    for (Iterator i0 = edges.iterator(); i0.hasNext(); ) {
+      SegmentString edge0 = (SegmentString) i0.next();
+      computeVertexSnaps(edge0);
+    }
+  }
+
+  /**
+   * Performs a brute-force comparison of every segment in each {@link SegmentString}.
+   * This has n^2 performance.
+   */
+  private void computeVertexSnaps(SegmentString e)
+  {
+    Coordinate[] pts0 = e.getCoordinates();
+    for (int i = 0; i < pts0.length - 1; i++) {
+      HotPixel hotPixel = new HotPixel(pts0[i], scaleFactor, li);
+      boolean isNodeAdded = pointSnapper.snap(hotPixel, e, i);
+      // if a node is created for a vertex, that vertex must be noded too
+      if (isNodeAdded) {
+        e.addIntersection(pts0[i], i);
+      }
+  }
+}
+
+}
diff --git a/src/com/vividsolutions/jts/noding/snapround/SegmentSnapper.java b/src/com/vividsolutions/jts/noding/snapround/SegmentSnapper.java
deleted file mode 100644
index cf3e330..0000000
--- a/src/com/vividsolutions/jts/noding/snapround/SegmentSnapper.java
+++ /dev/null
@@ -1,118 +0,0 @@
-
-/*
- * The JTS Topology Suite is a collection of Java classes that
- * implement the fundamental operations required to validate a given
- * geo-spatial data set to a known topological specification.
- *
- * Copyright (C) 2001 Vivid Solutions
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- *
- * For more information, contact:
- *
- *     Vivid Solutions
- *     Suite #1A
- *     2328 Government Street
- *     Victoria BC  V8T 5G5
- *     Canada
- *
- *     (250)385-6040
- *     www.vividsolutions.com
- */
-package com.vividsolutions.jts.noding.snapround;
-
-import com.vividsolutions.jts.geom.Coordinate;
-import com.vividsolutions.jts.noding.*;
-
-
-/**
- * @version 1.6
- */
-public class SegmentSnapper {
-
-  private static double TOLERANCE = 0.5;
-  /**
-   * @return true if the point p is within the snap tolerance of the line p0-p1
-   */
-  public static boolean isWithinTolerance(Coordinate p, Coordinate p0, Coordinate p1)
-  {
-    double minx = p.x - TOLERANCE;
-    double maxx = p.x + TOLERANCE;
-    double miny = p.y - TOLERANCE;
-    double maxy = p.y + TOLERANCE;
-    double segMinx = Math.min(p0.x, p1.x);
-    double segMaxx = Math.max(p0.x, p1.x);
-    double segMiny = Math.min(p0.y, p1.y);
-    double segMaxy = Math.max(p0.y, p1.y);
-    if ( maxx < segMinx
-      || minx > segMaxx
-      || maxy < segMiny
-      || miny > segMaxy) return false;
-
-    double dx = p1.x - p0.x;
-    double dy = p1.y - p0.y;
-
-    double px = p.x - p0.x;
-    double py = p.y - p0.y;
-
-    double dely = px * dy / dx - py;
-    double delx = py * dx / dy - px;
-    double discy = px * dy - py * dx;
-
-    if (Math.abs(discy) < Math.abs(0.5 * dx) ) return true;
-    double discx = py * dx - px * dy;
-    if (Math.abs(discx) < Math.abs(0.5 * dy) ) return true;
-/*
-    double dely = px * dy / dx - py;
-    if (dely > 0.5 || dely < -0.5) return false;
-
-    double delx = py * dx / dy - px;
-    if (delx > 0.5 || delx < -0.5) return false;
-*/
-    return false;
-  }
-
-  public SegmentSnapper() {
-  }
-
-  /**
-   * Adds a new node (equal to the snap pt) to the segment
-   * if the snapPt is
-   * within tolerance of the segment
-   *
-   * @param snapPt
-   * @param segStr
-   * @param segIndex
-   * @return <code>true</code> if a node was added
-   */
-  public boolean addSnappedNode(
-      Coordinate snapPt,
-      SegmentString segStr,  int segIndex
-      )
-  {
-    Coordinate p0 = segStr.getCoordinate(segIndex);
-    Coordinate p1 = segStr.getCoordinate(segIndex + 1);
-
-    // no need to snap if the snapPt equals an endpoint of the segment
-    if (snapPt.equals(p0)) return false;
-    if (snapPt.equals(p1)) return false;
-
-    if (isWithinTolerance(snapPt, p0, p1)) {
-      segStr.addIntersection(snapPt, segIndex);
-      return true;
-    }
-    return false;
-  }
-}
diff --git a/src/com/vividsolutions/jts/noding/snapround/SimpleSegmentStringsSnapper.java b/src/com/vividsolutions/jts/noding/snapround/SimpleSegmentStringsSnapper.java
deleted file mode 100644
index 8c8f1e0..0000000
--- a/src/com/vividsolutions/jts/noding/snapround/SimpleSegmentStringsSnapper.java
+++ /dev/null
@@ -1,83 +0,0 @@
-
-/*
- * The JTS Topology Suite is a collection of Java classes that
- * implement the fundamental operations required to validate a given
- * geo-spatial data set to a known topological specification.
- *
- * Copyright (C) 2001 Vivid Solutions
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- *
- * For more information, contact:
- *
- *     Vivid Solutions
- *     Suite #1A
- *     2328 Government Street
- *     Victoria BC  V8T 5G5
- *     Canada
- *
- *     (250)385-6040
- *     www.vividsolutions.com
- */
-package com.vividsolutions.jts.noding.snapround;
-
-import java.util.*;
-import com.vividsolutions.jts.geom.*;
-import com.vividsolutions.jts.noding.*;
-
-
-/**
- * @version 1.6
- */
-public class SimpleSegmentStringsSnapper {
-
-  private int nSnaps = 0;
-
-  public SimpleSegmentStringsSnapper() {
-  }
-
-  public int getNumSnaps() { return nSnaps; }
-
-  public void computeNodes(Collection edges, SegmentSnapper ss, boolean testAllSegments)
-  {
-    nSnaps = 0;
-
-    for (Iterator i0 = edges.iterator(); i0.hasNext(); ) {
-      SegmentString edge0 = (SegmentString) i0.next();
-      for (Iterator i1 = edges.iterator(); i1.hasNext(); ) {
-        SegmentString edge1 = (SegmentString) i1.next();
-        if (testAllSegments || edge0 != edge1)
-          computeSnaps(edge0, edge1, ss);
-      }
-    }
-    System.out.println("nSnaps = " + nSnaps);
-  }
-
-  /**
- * Performs a brute-force comparison of every segment in each SegmentString.
- * This has n^2 performance.
- */
-  private void computeSnaps(SegmentString e0, SegmentString e1, SegmentSnapper ss)
-  {
-    Coordinate[] pts0 = e0.getCoordinates();
-    Coordinate[] pts1 = e1.getCoordinates();
-    for (int i0 = 0; i0 < pts0.length - 1; i0++) {
-      for (int i1 = 0; i1 < pts1.length - 1; i1++) {
-        boolean isNodeAdded = ss.addSnappedNode(pts0[i0], e1, i1);
-        if (isNodeAdded) nSnaps++;
-      }
-    }
-  }
-}
diff --git a/src/com/vividsolutions/jts/noding/snapround/SimpleSnapRounder.java b/src/com/vividsolutions/jts/noding/snapround/SimpleSnapRounder.java
new file mode 100644
index 0000000..2f8b664
--- /dev/null
+++ b/src/com/vividsolutions/jts/noding/snapround/SimpleSnapRounder.java
@@ -0,0 +1,215 @@
+
+/*
+ * The JTS Topology Suite is a collection of Java classes that
+ * implement the fundamental operations required to validate a given
+ * geo-spatial data set to a known topological specification.
+ *
+ * Copyright (C) 2001 Vivid Solutions
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * For more information, contact:
+ *
+ *     Vivid Solutions
+ *     Suite #1A
+ *     2328 Government Street
+ *     Victoria BC  V8T 5G5
+ *     Canada
+ *
+ *     (250)385-6040
+ *     www.vividsolutions.com
+ */
+package com.vividsolutions.jts.noding.snapround;
+
+import java.util.*;
+import com.vividsolutions.jts.geom.*;
+import com.vividsolutions.jts.algorithm.*;
+import com.vividsolutions.jts.noding.*;
+
+/**
+ * Uses Snap Rounding to compute a rounded,
+ * fully noded arrangement from a set of {@link SegmentString}s.
+ * Implements the Snap Rounding technique described in Hobby, Guibas & Marimont,
+ * and Goodrich et al.
+ * Snap Rounding assumes that all vertices lie on a uniform grid
+ * (hence the precision model of the input must be fixed precision,
+ * and all the input vertices must be rounded to that precision).
+ * <p>
+ * This implementation uses simple iteration over the line segments.
+ * <p>
+ * This implementation appears to be fully robust using an integer precision model.
+ * It will function with non-integer precision models, but the
+ * results are not 100% guaranteed to be correctly noded.
+ *
+ * @version 1.7
+ */
+public class SimpleSnapRounder
+    implements Noder
+{
+  private final PrecisionModel pm;
+  private LineIntersector li;
+  private final double scaleFactor;
+  private Collection nodedSegStrings;
+
+  public SimpleSnapRounder(PrecisionModel pm) {
+    this.pm = pm;
+    li = new RobustLineIntersector();
+    li.setPrecisionModel(pm);
+    scaleFactor = pm.getScale();
+  }
+
+  public Collection getNodedSubstrings()
+  {
+    return  SegmentString.getNodedSubstrings(nodedSegStrings);
+  }
+
+  public void computeNodes(Collection inputSegmentStrings)
+  {
+    this.nodedSegStrings = inputSegmentStrings;
+    snapRound(inputSegmentStrings, li);
+
+    // testing purposes only - remove in final version
+    //checkCorrectness(inputSegmentStrings);
+  }
+
+  private void checkCorrectness(Collection inputSegmentStrings)
+  {
+    Collection resultSegStrings = SegmentString.getNodedSubstrings(inputSegmentStrings);
+    NodingValidator nv = new NodingValidator(resultSegStrings);
+    try {
+      nv.checkValid();
+    } catch (Exception ex) {
+      ex.printStackTrace();
+    }
+  }
+  private void snapRound(Collection segStrings, LineIntersector li)
+  {
+    List intersections = findInteriorIntersections(segStrings, li);
+    computeSnaps(segStrings, intersections);
+    computeVertexSnaps(segStrings);
+  }
+
+  /**
+   * Computes all interior intersections in the collection of {@link SegmentString}s,
+   * and returns their @link Coordinate}s.
+   *
+   * Does NOT node the segStrings.
+   *
+   * @return a list of Coordinates for the intersections
+   */
+  private List findInteriorIntersections(Collection segStrings, LineIntersector li)
+  {
+    IntersectionFinderAdder intFinderAdder = new IntersectionFinderAdder(li);
+    SinglePassNoder noder = new MCIndexNoder();
+    noder.setSegmentIntersector(intFinderAdder);
+    noder.computeNodes(segStrings);
+    return intFinderAdder.getInteriorIntersections();
+  }
+
+
+  /**
+   * Computes nodes introduced as a result of snapping segments to snap points (hot pixels)
+   * @param li
+   */
+  private void computeSnaps(Collection segStrings, Collection snapPts)
+  {
+    for (Iterator i0 = segStrings.iterator(); i0.hasNext(); ) {
+      SegmentString ss = (SegmentString) i0.next();
+      computeSnaps(ss, snapPts);
+    }
+  }
+
+  private void computeSnaps(SegmentString ss, Collection snapPts)
+  {
+    for (Iterator it = snapPts.iterator(); it.hasNext(); ) {
+      Coordinate snapPt = (Coordinate) it.next();
+      HotPixel hotPixel = new HotPixel(snapPt, scaleFactor, li);
+      for (int i = 0; i < ss.size() - 1; i++) {
+        addSnappedNode(hotPixel, ss, i);
+      }
+    }
+  }
+
+  /**
+   * Computes nodes introduced as a result of
+   * snapping segments to vertices of other segments
+   *
+   * @param segStrings the list of segment strings to snap together
+   */
+  public void computeVertexSnaps(Collection edges)
+  {
+    for (Iterator i0 = edges.iterator(); i0.hasNext(); ) {
+      SegmentString edge0 = (SegmentString) i0.next();
+      for (Iterator i1 = edges.iterator(); i1.hasNext(); ) {
+        SegmentString edge1 = (SegmentString) i1.next();
+        computeVertexSnaps(edge0, edge1);
+      }
+    }
+  }
+
+  /**
+   * Performs a brute-force comparison of every segment in each {@link SegmentString}.
+   * This has n^2 performance.
+   */
+  private void computeVertexSnaps(SegmentString e0, SegmentString e1)
+  {
+    Coordinate[] pts0 = e0.getCoordinates();
+    Coordinate[] pts1 = e1.getCoordinates();
+    for (int i0 = 0; i0 < pts0.length - 1; i0++) {
+      HotPixel hotPixel = new HotPixel(pts0[i0], scaleFactor, li);
+      for (int i1 = 0; i1 < pts1.length - 1; i1++) {
+        // don't snap a vertex to itself
+        if (e0 == e1) {
+          if (i0 == i1) continue;
+        }
+        //System.out.println("trying " + pts0[i0] + " against " + pts1[i1] + pts1[i1 + 1]);
+        boolean isNodeAdded = addSnappedNode(hotPixel, e1, i1);
+        // if a node is created for a vertex, that vertex must be noded too
+        if (isNodeAdded) {
+          e0.addIntersection(pts0[i0], i0);
+        }
+      }
+    }
+  }
+
+  /**
+   * Adds a new node (equal to the snap pt) to the segment
+   * if the segment passes through the hot pixel
+   *
+   * @param hotPix
+   * @param segStr
+   * @param segIndex
+   * @return <code>true</code> if a node was added
+   */
+  public static boolean addSnappedNode(
+      HotPixel hotPix,
+      SegmentString segStr,
+      int segIndex
+      )
+  {
+    Coordinate p0 = segStr.getCoordinate(segIndex);
+    Coordinate p1 = segStr.getCoordinate(segIndex + 1);
+
+    if (hotPix.intersects(p0, p1)) {
+      //System.out.println("snapped: " + snapPt);
+      //System.out.println("POINT (" + snapPt.x + " " + snapPt.y + ")");
+      segStr.addIntersection(hotPix.getCoordinate(), segIndex);
+
+      return true;
+    }
+    return false;
+  }
+
+}
diff --git a/src/com/vividsolutions/jts/noding/snapround/SnapRounder.java b/src/com/vividsolutions/jts/noding/snapround/SnapRounder.java
deleted file mode 100644
index 6c1c8a7..0000000
--- a/src/com/vividsolutions/jts/noding/snapround/SnapRounder.java
+++ /dev/null
@@ -1,124 +0,0 @@
-
-/*
- * The JTS Topology Suite is a collection of Java classes that
- * implement the fundamental operations required to validate a given
- * geo-spatial data set to a known topological specification.
- *
- * Copyright (C) 2001 Vivid Solutions
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- *
- * For more information, contact:
- *
- *     Vivid Solutions
- *     Suite #1A
- *     2328 Government Street
- *     Victoria BC  V8T 5G5
- *     Canada
- *
- *     (250)385-6040
- *     www.vividsolutions.com
- */
-package com.vividsolutions.jts.noding.snapround;
-
-import java.util.*;
-import com.vividsolutions.jts.algorithm.LineIntersector;
-import com.vividsolutions.jts.noding.*;
-
-/**
- * Uses snap rounding to compute a rounded, noded arrangement from a
- * set of linestrings.
- *
- * @version 1.6
- */
-public class SnapRounder
-    //extends Noder  // cut this free for now
-{
-  protected LineIntersector li;
-
-/*
-  private SegmentStringsIntersector createEdgeSetIntersector()
-  {
-  // various options for computing intersections, from slowest to fastest
-
-  //private EdgeSetIntersector esi = new SimpleEdgeSetIntersector();
-  //private EdgeSetIntersector esi = new MonotoneChainIntersector();
-  //private EdgeSetIntersector esi = new NonReversingChainIntersector();
-  //private EdgeSetIntersector esi = new SimpleSweepLineIntersector();
-  //private EdgeSetIntersector esi = new MCSweepLineIntersector();
-
-    //return new SimpleEdgeSetIntersector();
-    return new SimpleSegmentStringsNoder();
-  }
-*/
-
-  public SnapRounder() {
-  }
-
-  public void setLineIntersector(LineIntersector li) { this.li = li; }
-
-
-  public Collection node(Collection inputSegmentStrings)
-  {
-    Collection resultSegStrings = fullyIntersectSegments(inputSegmentStrings, li);
-    NodingValidator nv = new NodingValidator(resultSegStrings);
-    nv.checkValid();
-    return resultSegStrings;
-  }
-
-  private Collection fullyIntersectSegments(Collection segStrings, LineIntersector li)
-  {
-    SegmentIntersector si = null;
-    Collection inputSegStrings = segStrings;
-    Collection nodedSegStrings = null;
-    do {
-      si = new SegmentIntersector(li);
-      Noder noder = new SimpleNoder();
-      noder.setSegmentIntersector(si);
-      nodedSegStrings = noder.node(inputSegStrings);
-      List snappedSegStrings = computeSnaps(nodedSegStrings);
-System.out.println("interior ints = " + si.numInteriorIntersections);
-//System.out.println("snapRounder result");
-//BufferOp.printSegStringList(nodedSegStrings);
-      inputSegStrings = snappedSegStrings;
-    } while (si.numInteriorIntersections > 0);
-    return nodedSegStrings;
-  }
-
-  /**
-   * Computes new nodes introduced as a result of snapping segments to near vertices
-   * @param li
-   */
-  private List computeSnaps(Collection segStrings)
-  {
-    List splitSegStringList = null;
-    int numSnaps;
-    /**
-     * Have to snap repeatedly, because snapping a line may move it enough
-     * that it crosses another hot pixel.
-     */
-    do {
-      SimpleSegmentStringsSnapper snapper = new SimpleSegmentStringsSnapper();
-      SegmentSnapper ss = new SegmentSnapper();
-      snapper.computeNodes(segStrings, ss, true);
-      numSnaps = snapper.getNumSnaps();
-      // save the list of split seg strings in case we are going to return it
-      splitSegStringList = Noder.getNodedEdges(segStrings);
-      segStrings = splitSegStringList;
-    } while (numSnaps > 0);
-    return splitSegStringList;
-  }
-
-}
diff --git a/src/com/vividsolutions/jts/operation/GeometryGraphOperation.java b/src/com/vividsolutions/jts/operation/GeometryGraphOperation.java
index b4fb950..f911f05 100644
--- a/src/com/vividsolutions/jts/operation/GeometryGraphOperation.java
+++ b/src/com/vividsolutions/jts/operation/GeometryGraphOperation.java
@@ -44,7 +44,7 @@ import com.vividsolutions.jts.geomgraph.GeometryGraph;
 /**
  * The base class for operations that require {@link GeometryGraph)s.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class GeometryGraphOperation
 {
diff --git a/src/com/vividsolutions/jts/operation/IsSimpleOp.java b/src/com/vividsolutions/jts/operation/IsSimpleOp.java
index a54b5ca..45f8630 100644
--- a/src/com/vividsolutions/jts/operation/IsSimpleOp.java
+++ b/src/com/vividsolutions/jts/operation/IsSimpleOp.java
@@ -41,13 +41,25 @@ import com.vividsolutions.jts.algorithm.*;
 import com.vividsolutions.jts.geomgraph.index.SegmentIntersector;
 
 /**
- * Tests whether a {@link Geometry} is simple.
- * Only {@link Geometry}s whose definition allows them
- * to be simple or non-simple are tested.  (E.g. Polygons must be simple
- * by definition, so no test is provided.  To test whether a given Polygon is valid,
- * use <code>Geometry#isValid</code>)
+ * Tests whether a <code>Geometry</code> is simple.
+ * In general, the SFS specification of simplicity
+ * follows the rule:
+ *  <UL>
+ *    <LI> A Geometry is simple iff the only self-intersections are at
+ *    boundary points.
+ *  </UL>
+ * Simplicity is defined for each {@link Geometry} subclass as follows:
+ * <ul>
+ * <li>Valid polygonal geometries are simple by definition, so
+ * <code>isSimple</code> trivially returns true.
+ * <li>Linear geometries are simple iff they do not self-intersect at points
+ * other than boundary points.
+ * <li>Zero-dimensional geometries (points) are simple iff they have no
+ * repeated points.
+ * <li>Empty <code>Geometry</code>s are always simple
+ * <ul>
  *
- * @version 1.6
+ * @version 1.7
  */
 public class IsSimpleOp {
 
diff --git a/src/com/vividsolutions/jts/operation/buffer/BufferBuilder.java b/src/com/vividsolutions/jts/operation/buffer/BufferBuilder.java
index 0b07be5..69c08cc 100644
--- a/src/com/vividsolutions/jts/operation/buffer/BufferBuilder.java
+++ b/src/com/vividsolutions/jts/operation/buffer/BufferBuilder.java
@@ -35,7 +35,7 @@
 package com.vividsolutions.jts.operation.buffer;
 
 /**
- * @version 1.6
+ * @version 1.7
  */
 
 import java.util.*;
@@ -58,7 +58,7 @@ import com.vividsolutions.jts.noding.*;
  * Retrying the computation in a fixed precision
  * can produce more robust results.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class BufferBuilder
 {
@@ -82,6 +82,7 @@ public class BufferBuilder
   private int endCapStyle = BufferOp.CAP_ROUND;
 
   private PrecisionModel workingPrecisionModel;
+  private Noder workingNoder;
   private GeometryFactory geomFact;
   private PlanarGraph graph;
   private EdgeList edgeList     = new EdgeList();
@@ -116,6 +117,15 @@ public class BufferBuilder
     workingPrecisionModel = pm;
   }
 
+  /**
+   * Sets the {@link Noder} to use during noding.
+   * This allows choosing fast but non-robust noding, or slower
+   * but robust noding.
+   *
+   * @param noder the noder to use
+   */
+  public void setNoder(Noder noder) { workingNoder = noder; }
+
   public void setEndCapStyle(int endCapStyle)
   {
     this.endCapStyle = endCapStyle;
@@ -162,17 +172,34 @@ public class BufferBuilder
     return resultGeom;
   }
 
+  private Noder getNoder(PrecisionModel precisionModel)
+  {
+    if (workingNoder != null) return workingNoder;
+
+    // otherwise use a fast (but non-robust) noder
+    MCIndexNoder noder = new MCIndexNoder();
+    LineIntersector li = new RobustLineIntersector();
+    li.setPrecisionModel(precisionModel);
+    noder.setSegmentIntersector(new IntersectionAdder(li));
+//    Noder noder = new IteratedNoder(precisionModel);
+    return noder;
+//    Noder noder = new SimpleSnapRounder(precisionModel);
+//    Noder noder = new MCIndexSnapRounder(precisionModel);
+//    Noder noder = new ScaledNoder(new MCIndexSnapRounder(new PrecisionModel(1.0)),
+//                                  precisionModel.getScale());
+  }
+
   private void computeNodedEdges(List bufferSegStrList, PrecisionModel precisionModel)
   {
-    //BufferCurveGraphNoder noder = new BufferCurveGraphNoder(geomFact.getPrecisionModel());
-    IteratedNoder noder = new IteratedNoder(precisionModel);
-    Collection nodedSegStrings = noder.node(bufferSegStrList);
+    Noder noder = getNoder(precisionModel);
+    noder.computeNodes(bufferSegStrList);
+    Collection nodedSegStrings = noder.getNodedSubstrings();
 // DEBUGGING ONLY
 //BufferDebug.saveEdges(nodedEdges, "run" + BufferDebug.runCount + "_nodedEdges");
 
     for (Iterator i = nodedSegStrings.iterator(); i.hasNext(); ) {
       SegmentString segStr = (SegmentString) i.next();
-      Label oldLabel = (Label) segStr.getContext();
+      Label oldLabel = (Label) segStr.getData();
       Edge edge = new Edge(segStr.getCoordinates(), new Label(oldLabel));
       insertEdge(edge);
     }
diff --git a/src/com/vividsolutions/jts/operation/buffer/BufferOp.java b/src/com/vividsolutions/jts/operation/buffer/BufferOp.java
index 53763f8..358a31d 100644
--- a/src/com/vividsolutions/jts/operation/buffer/BufferOp.java
+++ b/src/com/vividsolutions/jts/operation/buffer/BufferOp.java
@@ -33,25 +33,30 @@
 package com.vividsolutions.jts.operation.buffer;
 
 /**
- * @version 1.6
+ * @version 1.7
  */
 import com.vividsolutions.jts.geom.*;
 import com.vividsolutions.jts.precision.SimpleGeometryPrecisionReducer;
+import com.vividsolutions.jts.noding.*;
+import com.vividsolutions.jts.noding.snapround.*;
 
 //import debug.*;
 
 /**
  * Computes the buffer of a geometry, for both positive and negative buffer distances.
  * <p>
- * In GIS, the buffer of a geometry is defined as
+ * In GIS, the positive buffer of a geometry is defined as
  * the Minkowski sum or difference of the geometry
- * with a circle with radius equal to the absolute value of the buffer distance.
- * In the CAD/CAM world buffers are known as </b>offset curves</b>.
+ * with a circle of radius equal to the absolute value of the buffer distance.
+ * In the CAD/CAM world buffers are known as </i>offset curves</i>.
+ * In morphological analysis they are known as <i>erosion</i> and <i>dilation</i>
+ * <p>
+ * The negative buffer of lines and points is always empty geometry.
  * <p>
  * Since true buffer curves may contain circular arcs,
  * computed buffer polygons can only be approximations to the true geometry.
  * The user can control the accuracy of the curve approximation by specifying
- * the number of linear segments with which to approximate a curve.
+ * the number of linear segments used to approximate curves.
  * <p>
  * The <b>end cap style</b> of a linear buffer may be specified. The
  * following end cap styles are supported:
@@ -61,11 +66,8 @@ import com.vividsolutions.jts.precision.SimpleGeometryPrecisionReducer;
  * <li>{@link CAP_SQUARE} - end caps are squared off at the buffer distance beyond the line ends
  * </ul>
  * <p>
- * The computation uses an algorithm involving iterated noding and precision reduction
- * to provide a high degree of robustness.
-
  *
- * @version 1.6
+ * @version 1.7
  */
 public class BufferOp
 {
@@ -85,16 +87,18 @@ public class BufferOp
   private static int MAX_PRECISION_DIGITS = 12;
 
   /**
-   * Compute a reasonable scale factor to limit the precision of
+   * Compute a scale factor to limit the precision of
    * a given combination of Geometry and buffer distance.
-   * The scale factor is based on a heuristic.
+   * The scale factor is determined by a combination of
+   * the number of digits of precision in the (geometry + buffer distance),
+   * limited by the supplied <code>maxPrecisionDigits</code> value.
    *
    * @param g the Geometry being buffered
    * @param distance the buffer distance
-   * @param maxPrecisionDigits the mzx # of digits that should be allowed by
+   * @param maxPrecisionDigits the max # of digits that should be allowed by
    *          the precision determined by the computed scale factor
    *
-   * @return a scale factor that allows a reasonable amount of precision for the buffer computation
+   * @return a scale factor for the buffer computation
    */
   private static double precisionScaleFactor(Geometry g,
       double distance,
@@ -147,13 +151,36 @@ public class BufferOp
     return geomBuf;
   }
 
+  /**
+   * Comutes the buffer for a geometry for a given buffer distance
+   * and accuracy of approximation.
+   *
+   * @param g the geometry to buffer
+   * @param distance the buffer distance
+   * @param quadrantSegments the number of segments used to approximate a quarter circle
+   * @param endCapStyle the end cap style to use
+   * @return the buffer of the input geometry
+   *
+   */
+  public static Geometry bufferOp(Geometry g,
+                                  double distance,
+    int quadrantSegments,
+    int endCapStyle)
+  {
+    BufferOp bufOp = new BufferOp(g);
+    bufOp.setQuadrantSegments(quadrantSegments);
+    bufOp.setEndCapStyle(endCapStyle);
+    Geometry geomBuf = bufOp.getResultGeometry(distance);
+    return geomBuf;
+  }
+
   private Geometry argGeom;
   private double distance;
   private int quadrantSegments = OffsetCurveBuilder.DEFAULT_QUADRANT_SEGMENTS;
   private int endCapStyle = BufferOp.CAP_ROUND;
 
   private Geometry resultGeometry = null;
-  private TopologyException saveException;   // debugging only
+  private RuntimeException saveException;   // debugging only
 
   /**
    * Initializes a buffer computation for the given geometry
@@ -226,10 +253,19 @@ public class BufferOp
     bufferOriginalPrecision();
     if (resultGeometry != null) return;
 
+    PrecisionModel argPM = argGeom.getFactory().getPrecisionModel();
+    if (argPM.getType() == PrecisionModel.FIXED)
+      bufferFixedPrecision(argPM);
+    else
+      bufferReducedPrecision();
+  }
+
+  private void bufferReducedPrecision()
+  {
     // try and compute with decreasing precision
     for (int precDigits = MAX_PRECISION_DIGITS; precDigits >= 0; precDigits--) {
       try {
-        bufferFixedPrecision(precDigits);
+        bufferReducedPrecision(precDigits);
       }
       catch (TopologyException ex) {
         saveException = ex;
@@ -240,39 +276,46 @@ public class BufferOp
 
     // tried everything - have to bail
     throw saveException;
-    //return resultGeometry;
   }
 
   private void bufferOriginalPrecision()
   {
     try {
+      // use fast noding by default
       BufferBuilder bufBuilder = new BufferBuilder();
       bufBuilder.setQuadrantSegments(quadrantSegments);
       bufBuilder.setEndCapStyle(endCapStyle);
       resultGeometry = bufBuilder.buffer(argGeom, distance);
     }
-    catch (TopologyException ex) {
+    catch (RuntimeException ex) {
       saveException = ex;
       // don't propagate the exception - it will be detected by fact that resultGeometry is null
+
+      // testing - propagate exception
+      //throw ex;
     }
   }
 
-  private void bufferFixedPrecision(int precisionDigits)
+  private void bufferReducedPrecision(int precisionDigits)
   {
     double sizeBasedScaleFactor = precisionScaleFactor(argGeom, distance, precisionDigits);
+//    System.out.println("recomputing with precision scale factor = " + sizeBasedScaleFactor);
 
     PrecisionModel fixedPM = new PrecisionModel(sizeBasedScaleFactor);
-    // don't change the precision model of the Geometry, just reduce the precision
-    SimpleGeometryPrecisionReducer reducer = new SimpleGeometryPrecisionReducer(fixedPM);
-    Geometry reducedGeom = reducer.reduce(argGeom);
+    bufferFixedPrecision(fixedPM);
+  }
 
-//System.out.println("recomputing with precision scale factor = " + sizeBasedScaleFactor);
+  private void bufferFixedPrecision(PrecisionModel fixedPM)
+  {
+    Noder noder = new ScaledNoder(new MCIndexSnapRounder(new PrecisionModel(1.0)),
+                                  fixedPM.getScale());
 
     BufferBuilder bufBuilder = new BufferBuilder();
     bufBuilder.setWorkingPrecisionModel(fixedPM);
+    bufBuilder.setNoder(noder);
     bufBuilder.setQuadrantSegments(quadrantSegments);
+    bufBuilder.setEndCapStyle(endCapStyle);
     // this may throw an exception, if robustness errors are encountered
-    resultGeometry = bufBuilder.buffer(reducedGeom, distance);
+    resultGeometry = bufBuilder.buffer(argGeom, distance);
   }
-
 }
diff --git a/src/com/vividsolutions/jts/operation/buffer/BufferSubgraph.java b/src/com/vividsolutions/jts/operation/buffer/BufferSubgraph.java
index 908f649..e716796 100644
--- a/src/com/vividsolutions/jts/operation/buffer/BufferSubgraph.java
+++ b/src/com/vividsolutions/jts/operation/buffer/BufferSubgraph.java
@@ -35,7 +35,7 @@
 package com.vividsolutions.jts.operation.buffer;
 
 /**
- * @version 1.6
+ * @version 1.7
  */
 
 import java.util.*;
@@ -55,7 +55,7 @@ import com.vividsolutions.jts.util.*;
  * </ul>
  *
  *
- * @version 1.6
+ * @version 1.7
  */
 public class BufferSubgraph
   implements Comparable
@@ -64,6 +64,7 @@ public class BufferSubgraph
   private List dirEdgeList  = new ArrayList();
   private List nodes        = new ArrayList();
   private Coordinate rightMostCoord = null;
+  private Envelope env = null;
 
   public BufferSubgraph(CGAlgorithms cga)
   {
@@ -74,6 +75,28 @@ public class BufferSubgraph
   public List getNodes() { return nodes; }
 
   /**
+   * Computes the envelope of the edges in the subgraph.
+   * The envelope is cached after being computed.
+   *
+   * @return the envelope of the graph.
+   */
+  public Envelope getEnvelope()
+  {
+    if (env == null) {
+      Envelope edgeEnv = new Envelope();
+      for (Iterator it = dirEdgeList.iterator(); it.hasNext(); ) {
+        DirectedEdge dirEdge = (DirectedEdge) it.next();
+        Coordinate[] pts = dirEdge.getEdge().getCoordinates();
+        for (int i = 0; i < pts.length - 1; i++) {
+          edgeEnv.expandToInclude(pts[i]);
+        }
+      }
+      env = edgeEnv;
+    }
+    return env;
+  }
+
+  /**
    * Gets the rightmost coordinate in the edges of the subgraph
    */
   public Coordinate getRightmostCoordinate()
diff --git a/src/com/vividsolutions/jts/operation/buffer/OffsetCurveBuilder.java b/src/com/vividsolutions/jts/operation/buffer/OffsetCurveBuilder.java
index 9ad0642..05ed33c 100644
--- a/src/com/vividsolutions/jts/operation/buffer/OffsetCurveBuilder.java
+++ b/src/com/vividsolutions/jts/operation/buffer/OffsetCurveBuilder.java
@@ -46,7 +46,7 @@ import com.vividsolutions.jts.geomgraph.*;
  * of all the noded raw curves and tracing outside contours.
  * The points in the raw curve are rounded to the required precision model.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class OffsetCurveBuilder {
 
@@ -358,7 +358,7 @@ public class OffsetCurveBuilder {
    * The offset points are computed in full double precision, for accuracy.
    *
    * @param seg the segment to offset
-   * @param side the side of the segment the offset lies on
+   * @param side the side of the segment ({@link Position}) the offset lies on
    * @param distance the offset distance
    * @param offset the points computed for the offset segment
    */
diff --git a/src/com/vividsolutions/jts/operation/buffer/OffsetCurveSetBuilder.java b/src/com/vividsolutions/jts/operation/buffer/OffsetCurveSetBuilder.java
index 0e33da4..2ca64b7 100644
--- a/src/com/vividsolutions/jts/operation/buffer/OffsetCurveSetBuilder.java
+++ b/src/com/vividsolutions/jts/operation/buffer/OffsetCurveSetBuilder.java
@@ -36,7 +36,7 @@
 package com.vividsolutions.jts.operation.buffer;
 
 /**
- * @version 1.6
+ * @version 1.7
  */
 import java.util.*;
 import com.vividsolutions.jts.geom.*;
@@ -48,7 +48,7 @@ import com.vividsolutions.jts.noding.SegmentString;
  * Creates all the raw offset curves for a buffer of a {@link Geometry}.
  * Raw curves need to be noded together and polygonized to form the final buffer area.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class OffsetCurveSetBuilder {
 
@@ -163,7 +163,7 @@ public class OffsetCurveSetBuilder {
     Coordinate[] shellCoord = CoordinateArrays.removeRepeatedPoints(shell.getCoordinates());
     // optimization - don't bother computing buffer
     // if the polygon would be completely eroded
-    if (distance < 0.0 && isErodedCompletely(shellCoord, distance))
+    if (distance < 0.0 && isErodedCompletely(shell, distance))
         return;
 
     addPolygonRing(
@@ -180,7 +180,7 @@ public class OffsetCurveSetBuilder {
 
       // optimization - don't bother computing buffer for this hole
       // if the hole would be completely covered
-      if (distance > 0.0 && isErodedCompletely(holeCoord, -distance))
+      if (distance > 0.0 && isErodedCompletely(hole, -distance))
           continue;
 
       // Holes are topologically labelled opposite to the shell, since
@@ -230,8 +230,9 @@ public class OffsetCurveSetBuilder {
    * @param offsetDistance
    * @return
    */
-  private boolean isErodedCompletely(Coordinate[] ringCoord, double bufferDistance)
+  private boolean isErodedCompletely(LinearRing ring, double bufferDistance)
   {
+    Coordinate[] ringCoord = ring.getCoordinates();
     double minDiam = 0.0;
     // degenerate ring has no area
     if (ringCoord.length < 4)
@@ -242,6 +243,14 @@ public class OffsetCurveSetBuilder {
     if (ringCoord.length == 4)
       return isTriangleErodedCompletely(ringCoord, bufferDistance);
 
+    // if envelope is narrower than twice the buffer distance, ring is eroded
+    Envelope env = ring.getEnvelopeInternal();
+    double envMinDimension = Math.min(env.getHeight(), env.getWidth());
+    if (bufferDistance < 0.0
+        && 2 * Math.abs(bufferDistance) > envMinDimension)
+      return true;
+
+    return false;
     /**
      * The following is a heuristic test to determine whether an
      * inside buffer will be eroded completely.
@@ -253,11 +262,13 @@ public class OffsetCurveSetBuilder {
      * a full topological computation.
      *
      */
-    LinearRing ring = inputGeom.getFactory().createLinearRing(ringCoord);
+//System.out.println(ring);
+/* MD  7 Feb 2005 - there's an unknown bug in the MD code, so disable this for now
     MinimumDiameter md = new MinimumDiameter(ring);
     minDiam = md.getLength();
     //System.out.println(md.getDiameter());
     return minDiam < 2 * Math.abs(bufferDistance);
+    */
   }
 
   /**
diff --git a/src/com/vividsolutions/jts/operation/buffer/RightmostEdgeFinder.java b/src/com/vividsolutions/jts/operation/buffer/RightmostEdgeFinder.java
index af90e49..5fcd6e1 100644
--- a/src/com/vividsolutions/jts/operation/buffer/RightmostEdgeFinder.java
+++ b/src/com/vividsolutions/jts/operation/buffer/RightmostEdgeFinder.java
@@ -35,7 +35,7 @@
 package com.vividsolutions.jts.operation.buffer;
 
 /**
- * @version 1.6
+ * @version 1.7
  */
 import java.util.*;
 import com.vividsolutions.jts.geom.*;
@@ -48,7 +48,7 @@ import com.vividsolutions.jts.util.*;
  * A RightmostEdgeFinder find the DirectedEdge in a list which has the highest coordinate,
  * and which is oriented L to R at that point. (I.e. the right side is on the RHS of the edge.)
  *
- * @version 1.6
+ * @version 1.7
  */
 public class RightmostEdgeFinder {
 
diff --git a/src/com/vividsolutions/jts/operation/buffer/SubgraphDepthLocater.java b/src/com/vividsolutions/jts/operation/buffer/SubgraphDepthLocater.java
index 1f227a5..24468db 100644
--- a/src/com/vividsolutions/jts/operation/buffer/SubgraphDepthLocater.java
+++ b/src/com/vividsolutions/jts/operation/buffer/SubgraphDepthLocater.java
@@ -44,7 +44,7 @@ import com.vividsolutions.jts.algorithm.*;
  * The input subgraphs are assumed to have had depths
  * already calculated for their edges.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class SubgraphDepthLocater
 {
@@ -80,6 +80,13 @@ public class SubgraphDepthLocater
     List stabbedSegments = new ArrayList();
     for (Iterator i = subgraphs.iterator(); i.hasNext(); ) {
       BufferSubgraph bsg = (BufferSubgraph) i.next();
+
+      // optimization - don't bother checking subgraphs which the ray does not intersect
+      Envelope env = bsg.getEnvelope();
+      if (stabbingRayLeftPt.y < env.getMinY()
+          || stabbingRayLeftPt.y > env.getMaxY())
+        continue;
+
       findStabbedSegments(stabbingRayLeftPt, bsg.getDirectedEdges(), stabbedSegments);
     }
     return stabbedSegments;
@@ -154,8 +161,6 @@ public class SubgraphDepthLocater
         depth = dirEdge.getDepth(Position.RIGHT);
       DepthSegment ds = new DepthSegment(seg, depth);
       stabbedSegments.add(ds);
-
-
     }
   }
 
@@ -167,7 +172,6 @@ public class SubgraphDepthLocater
   private class DepthSegment
       implements Comparable
   {
-
     private LineSegment upwardSeg;
     private int leftDepth;
 
diff --git a/src/com/vividsolutions/jts/operation/distance/ConnectedElementLocationFilter.java b/src/com/vividsolutions/jts/operation/distance/ConnectedElementLocationFilter.java
index 1dcc7b2..073e771 100644
--- a/src/com/vividsolutions/jts/operation/distance/ConnectedElementLocationFilter.java
+++ b/src/com/vividsolutions/jts/operation/distance/ConnectedElementLocationFilter.java
@@ -43,7 +43,7 @@ import com.vividsolutions.jts.geom.*;
  * and returns them in a list. The elements of the list are 
  * {@link com.vividsolutions.jts.operation.distance.GeometryLocation}s.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class ConnectedElementLocationFilter
   implements GeometryFilter
diff --git a/src/com/vividsolutions/jts/operation/distance/ConnectedElementPointFilter.java b/src/com/vividsolutions/jts/operation/distance/ConnectedElementPointFilter.java
index 8477c28..70c127e 100644
--- a/src/com/vividsolutions/jts/operation/distance/ConnectedElementPointFilter.java
+++ b/src/com/vividsolutions/jts/operation/distance/ConnectedElementPointFilter.java
@@ -42,7 +42,7 @@ import com.vividsolutions.jts.geom.*;
  * (e.g. a polygon, linestring or point)
  * and returns them in a list
  *
- * @version 1.6
+ * @version 1.7
  */
 public class ConnectedElementPointFilter
   implements GeometryFilter
diff --git a/src/com/vividsolutions/jts/operation/distance/DistanceOp.java b/src/com/vividsolutions/jts/operation/distance/DistanceOp.java
index c47e673..3570ff4 100644
--- a/src/com/vividsolutions/jts/operation/distance/DistanceOp.java
+++ b/src/com/vividsolutions/jts/operation/distance/DistanceOp.java
@@ -51,7 +51,7 @@ import com.vividsolutions.jts.algorithm.*;
  * comparisons.  This worst-case performance could be improved on
  * by using Voronoi techniques.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class DistanceOp {
 
diff --git a/src/com/vividsolutions/jts/operation/distance/GeometryLocation.java b/src/com/vividsolutions/jts/operation/distance/GeometryLocation.java
index 1397461..dbe0f56 100644
--- a/src/com/vividsolutions/jts/operation/distance/GeometryLocation.java
+++ b/src/com/vividsolutions/jts/operation/distance/GeometryLocation.java
@@ -43,7 +43,7 @@ import com.vividsolutions.jts.geom.*;
  * Locations inside area Geometrys will not have an associated segment index,
  * so in this case the segment index will have the sentinel value of INSIDE_AREA.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class GeometryLocation
 {
diff --git a/src/com/vividsolutions/jts/operation/linemerge/EdgeString.java b/src/com/vividsolutions/jts/operation/linemerge/EdgeString.java
index 129fba3..4b47322 100644
--- a/src/com/vividsolutions/jts/operation/linemerge/EdgeString.java
+++ b/src/com/vividsolutions/jts/operation/linemerge/EdgeString.java
@@ -47,7 +47,7 @@ import java.util.List;
  * A sequence of {@link LineMergeDirectedEdge}s forming one of the lines that will
  * be output by the line-merging process.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class EdgeString {
   private GeometryFactory factory;
diff --git a/src/com/vividsolutions/jts/operation/linemerge/LineMergeDirectedEdge.java b/src/com/vividsolutions/jts/operation/linemerge/LineMergeDirectedEdge.java
index 67a2f44..c056f35 100644
--- a/src/com/vividsolutions/jts/operation/linemerge/LineMergeDirectedEdge.java
+++ b/src/com/vividsolutions/jts/operation/linemerge/LineMergeDirectedEdge.java
@@ -42,7 +42,7 @@ import com.vividsolutions.jts.util.Assert;
  * A {@link com.vividsolutions.jts.planargraph.DirectedEdge} of a 
  * {@link LineMergeGraph}. 
  *
- * @version 1.6
+ * @version 1.7
  */
 public class LineMergeDirectedEdge extends DirectedEdge {
   /**
diff --git a/src/com/vividsolutions/jts/operation/linemerge/LineMergeEdge.java b/src/com/vividsolutions/jts/operation/linemerge/LineMergeEdge.java
index 7db2192..619f353 100644
--- a/src/com/vividsolutions/jts/operation/linemerge/LineMergeEdge.java
+++ b/src/com/vividsolutions/jts/operation/linemerge/LineMergeEdge.java
@@ -40,7 +40,7 @@ import com.vividsolutions.jts.planargraph.Edge;
  * An edge of a {@link LineMergeGraph}. The <code>marked</code> field indicates
  * whether this Edge has been logically deleted from the graph.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class LineMergeEdge extends Edge {
   private LineString line;
diff --git a/src/com/vividsolutions/jts/operation/linemerge/LineMergeGraph.java b/src/com/vividsolutions/jts/operation/linemerge/LineMergeGraph.java
index 6d4beee..1a58f45 100644
--- a/src/com/vividsolutions/jts/operation/linemerge/LineMergeGraph.java
+++ b/src/com/vividsolutions/jts/operation/linemerge/LineMergeGraph.java
@@ -47,7 +47,7 @@ import com.vividsolutions.jts.planargraph.PlanarGraph;
  * and @{link com.vividsolutions.planargraph.Node}s indicates whether they have been
  * logically deleted from the graph.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class LineMergeGraph extends PlanarGraph {
   /**
diff --git a/src/com/vividsolutions/jts/operation/linemerge/LineMerger.java b/src/com/vividsolutions/jts/operation/linemerge/LineMerger.java
index d4b3f9b..076c281 100644
--- a/src/com/vividsolutions/jts/operation/linemerge/LineMerger.java
+++ b/src/com/vividsolutions/jts/operation/linemerge/LineMerger.java
@@ -56,7 +56,7 @@ import com.vividsolutions.jts.util.Assert;
  * at their endpoints.  The LineMerger will still run on incorrectly noded input
  * but will not form polygons from incorrected noded edges.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class LineMerger {
   /**
diff --git a/src/com/vividsolutions/jts/operation/linemerge/LineSequencer.java b/src/com/vividsolutions/jts/operation/linemerge/LineSequencer.java
new file mode 100644
index 0000000..c974dcd
--- /dev/null
+++ b/src/com/vividsolutions/jts/operation/linemerge/LineSequencer.java
@@ -0,0 +1,464 @@
+/*
+ * The JTS Topology Suite is a collection of Java classes that
+ * implement the fundamental operations required to validate a given
+ * geo-spatial data set to a known topological specification.
+ *
+ * Copyright (C) 2001 Vivid Solutions
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * For more information, contact:
+ *
+ *     Vivid Solutions
+ *     Suite #1A
+ *     2328 Government Street
+ *     Victoria BC  V8T 5G5
+ *     Canada
+ *
+ *     (250)385-6040
+ *     www.vividsolutions.com
+ */
+package com.vividsolutions.jts.operation.linemerge;
+
+import java.util.*;
+
+import com.vividsolutions.jts.geom.*;
+import com.vividsolutions.jts.planargraph.*;
+import com.vividsolutions.jts.planargraph.algorithm.ConnectedSubgraphFinder;
+import com.vividsolutions.jts.util.Assert;
+
+/**
+ * Builds a sequence from a set of LineStrings so that
+ * they are ordered end to end.
+ * A sequence is a complete non-repeating list of the linear
+ * components of the input.  Each linestring is oriented
+ * so that identical endpoints are adjacent in the list.
+ *
+ * The input linestrings may form one or more connected sets.
+ * The input linestrings should be correctly noded, or the results may
+ * not be what is expected.
+ * The output of this method is a single MultiLineString containing the ordered
+ * linestrings in the sequence.
+ * <p>
+ * The sequencing employs the classic <b>Eulerian path</b> graph algorithm.
+ * Since Eulerian paths are not uniquely determined,
+ * further rules are used to
+ * make the computed sequence preserve as much as possible of the input
+ * ordering.
+ * Within a connected subset of lines, the ordering rules are:
+ * <ul>
+ * <li>If there is degree-1 node which is the start
+ * node of an linestring, use that node as the start of the sequence
+ * <li>If there is a degree-1 node which is the end
+ * node of an linestring, use that node as the end of the sequence
+ * <li>If the sequence has no degree-1 nodes, use any node as the start
+ * </ul>
+ *
+ * <p>
+ * Not all arrangements of lines can be sequenced.
+ * For a connected set of edges in a graph,
+ * Euler's Theorem states that there is a sequence containing each edge once
+ * if and only if there are no more than 2 nodes of odd degree.
+ * If it is not possible to find a sequence, the {@link #isSequenceable} method
+ * will return <code>false</code>.
+ *
+ * @version 1.7
+ */
+public class LineSequencer
+{
+  /**
+   * Tests whether a {@link Geometry} is sequenced correctly.
+   * {@llink LineString}s are trivially sequenced.
+   * {@link MultiLineString}s are checked for correct sequencing.
+   * Otherwise, <code>isSequenced</code> is defined
+   * to be <code>true</code> for geometries that are not lineal.
+   *
+   * @param geom the geometry to test
+   * @return <code>true</code> if the geometry is sequenced or is not lineal
+   */
+  public static boolean isSequenced(Geometry geom)
+  {
+    if (! (geom instanceof MultiLineString)) {
+      return true;
+    }
+
+    MultiLineString mls = (MultiLineString) geom;
+    // the nodes in all subgraphs which have been completely scanned
+    Set prevSubgraphNodes = new TreeSet();
+
+    Coordinate lastNode = null;
+    List currNodes = new ArrayList();
+    for (int i = 0; i < mls.getNumGeometries(); i++) {
+      LineString line = (LineString) mls.getGeometryN(i);
+      Coordinate startNode = line.getCoordinateN(0);
+      Coordinate endNode = line.getCoordinateN(line.getNumPoints() - 1);
+
+      /**
+       * If this linestring is connected to a previous subgraph, geom is not sequenced
+       */
+      if (prevSubgraphNodes.contains(startNode)) return false;
+      if (prevSubgraphNodes.contains(endNode)) return false;
+
+      if (lastNode != null) {
+        if (! startNode.equals(lastNode)) {
+          // start new connected sequence
+          prevSubgraphNodes.addAll(currNodes);
+          currNodes.clear();
+        }
+      }
+      currNodes.add(startNode);
+      currNodes.add(endNode);
+      lastNode = endNode;
+    }
+    return true;
+  }
+
+  private LineMergeGraph graph = new LineMergeGraph();
+  // initialize with default, in case no lines are input
+  private GeometryFactory factory = new GeometryFactory();
+  private int lineCount = 0;
+
+  private boolean isRun = false;
+  private Geometry sequencedGeometry = null;
+  private boolean isSequenceable = false;
+
+  /**
+   * Adds a {@link Collection} of {@link Geometry}s to be sequenced.
+   * May be called multiple times.
+   * Any dimension of Geometry may be added; the constituent linework will be
+   * extracted.
+   *
+   * @param geometries a Collection of geometries to add
+   */
+  public void add(Collection geometries) {
+    for (Iterator i = geometries.iterator(); i.hasNext(); ) {
+      Geometry geometry = (Geometry) i.next();
+      add(geometry);
+    }
+  }
+  /**
+   * Adds a {@link Geometry} to be sequenced.
+   * May be called multiple times.
+   * Any dimension of Geometry may be added; the constituent linework will be
+   * extracted.
+   *
+   * @param geometry the geometry to add
+   */
+  public void add(Geometry geometry) {
+    geometry.apply(new GeometryComponentFilter() {
+      public void filter(Geometry component) {
+        if (component instanceof LineString) {
+          addLine((LineString)component);
+        }
+      }
+    });
+  }
+
+  private void addLine(LineString lineString) {
+    if (factory == null) {
+      this.factory = lineString.getFactory();
+    }
+    graph.addEdge(lineString);
+    lineCount++;
+  }
+
+  /**
+   * Tests whether the arrangement of linestrings has a valid
+   * sequence.
+   *
+   * @return <code>true</code> if a valid sequence exists.
+   */
+  public boolean isSequenceable()
+  {
+    computeSequence();
+    return isSequenceable;
+  }
+  /**
+   * Returns the {@link LineString} or {@link MultiLineString}
+   * built by the sequencing process, if one exists.
+   *
+   * @return the sequenced linestrings,
+   * or <code>null</code> if a valid sequence does not exist
+   */
+  public Geometry getSequencedLineStrings() {
+    computeSequence();
+    return sequencedGeometry;
+  }
+
+  private void computeSequence() {
+    if (isRun) { return; }
+    isRun = true;
+
+    List sequences = findSequences();
+    if (sequences == null)
+      return;
+
+    sequencedGeometry = buildSequencedGeometry(sequences);
+    isSequenceable = true;
+
+    int finalLineCount = sequencedGeometry.getNumGeometries();
+    Assert.isTrue(lineCount == finalLineCount, "Lines were missing from result");
+    Assert.isTrue(sequencedGeometry instanceof LineString
+                  || sequencedGeometry instanceof MultiLineString,
+                  "Result is not lineal");
+  }
+
+  private List findSequences()
+  {
+    List sequences = new ArrayList();
+    ConnectedSubgraphFinder csFinder = new ConnectedSubgraphFinder(graph);
+    List subgraphs = csFinder.getConnectedSubgraphs();
+    for (Iterator i = subgraphs.iterator(); i.hasNext(); ) {
+      Subgraph subgraph = (Subgraph) i.next();
+      if (hasSequence(subgraph)) {
+        List seq = findSequence(subgraph);
+        sequences.add(seq);
+      }
+      else {
+        // if any subgraph cannot be sequenced, abort
+        return null;
+      }
+    }
+    return sequences;
+  }
+
+  /**
+   * Tests whether a complete unique path exists in a graph
+   * using Euler's Theorem.
+   *
+   * @param graph the subgraph containing the edges
+   * @return <code>true</code> if a sequence exists
+   */
+  private boolean hasSequence(Subgraph graph)
+  {
+    int oddDegreeCount = 0;
+    for (Iterator i = graph.nodeIterator(); i.hasNext(); ) {
+      Node node = (Node) i.next();
+      if (node.getDegree() % 2 == 1)
+        oddDegreeCount++;
+    }
+    return oddDegreeCount <= 2;
+  }
+
+  private List findSequence(Subgraph graph)
+  {
+    GraphComponent.setVisited(graph.edgeIterator(), false);
+
+    Node startNode = findLowestDegreeNode(graph);
+    DirectedEdge startDE = (DirectedEdge) startNode.getOutEdges().iterator().next();
+    DirectedEdge startDESym = startDE.getSym();
+
+    List seq = new LinkedList();
+    ListIterator lit = seq.listIterator();
+    addReverseSubpath(startDESym, lit, false);
+    while (lit.hasPrevious()) {
+      DirectedEdge prev = (DirectedEdge) lit.previous();
+      DirectedEdge unvisitedOutDE = findUnvisitedBestOrientedDE(prev.getFromNode());
+      if (unvisitedOutDE != null)
+        addReverseSubpath(unvisitedOutDE.getSym(), lit, true);
+    }
+
+    /**
+     * At this point, we have a valid sequence of graph DirectedEdges, but it
+     * is not necessarily appropriately oriented relative to the underlying
+     * geometry.
+     */
+    List orientedSeq = orient(seq);
+    return orientedSeq;
+  }
+
+  /**
+   * Finds an {@link DirectedEdge} for an unvisited edge (if any),
+   * choosing the dirEdge which preserves orientation, if possible.
+   *
+   * @param node the node to examine
+   * @return the dirEdge found, or <code>null</code> if none were unvisited
+   */
+  private static DirectedEdge findUnvisitedBestOrientedDE(Node node)
+  {
+    DirectedEdge wellOrientedDE = null;
+    DirectedEdge unvisitedDE = null;
+    for (Iterator i = node.getOutEdges().iterator(); i.hasNext(); ) {
+       DirectedEdge de = (DirectedEdge) i.next();
+       if (! de.getEdge().isVisited()) {
+         unvisitedDE = de;
+         if (de.getEdgeDirection())
+           wellOrientedDE = de;
+       }
+    }
+    if (wellOrientedDE != null)
+      return wellOrientedDE;
+    return unvisitedDE;
+  }
+
+  private void addReverseSubpath(DirectedEdge de, ListIterator lit, boolean expectedClosed)
+  {
+    // trace an unvisited path *backwards* from this de
+    Node endNode = de.getToNode();
+
+    Node fromNode = null;
+    while (true) {
+      lit.add(de.getSym());
+      de.getEdge().setVisited(true);
+      fromNode = de.getFromNode();
+      DirectedEdge unvisitedOutDE = findUnvisitedBestOrientedDE(fromNode);
+      // this must terminate, since we are continually marking edges as visited
+      if (unvisitedOutDE == null)
+        break;
+      de = unvisitedOutDE.getSym();
+    }
+    if (expectedClosed) {
+      // the path should end at the toNode of this de, otherwise we have an error
+      Assert.isTrue(fromNode == endNode, "path not contiguous");
+    }
+  }
+
+  private static Node findLowestDegreeNode(Subgraph graph)
+  {
+    int minDegree = Integer.MAX_VALUE;
+    Node minDegreeNode = null;
+    for (Iterator i = graph.nodeIterator(); i.hasNext(); ) {
+      Node node = (Node) i.next();
+      if (minDegreeNode == null || node.getDegree() < minDegree) {
+        minDegree = node.getDegree();
+        minDegreeNode = node;
+      }
+    }
+    return minDegreeNode;
+  }
+
+  /**
+   * Computes a version of the sequence which is optimally
+   * oriented relative to the underlying geometry.
+   * <p>
+   * Heuristics used are:
+   * <ul>
+   * <li>If the path has a degree-1 node which is the start
+   * node of an linestring, use that node as the start of the sequence
+   * <li>If the path has a degree-1 node which is the end
+   * node of an linestring, use that node as the end of the sequence
+   * <li>If the sequence has no degree-1 nodes, use any node as the start
+   * (NOTE: in this case could orient the sequence according to the majority of the
+   * linestring orientations)
+   * </ul>
+   *
+   * @param seq a List of DirectedEdges
+   * @return a List of DirectedEdges oriented appropriately
+   */
+  private List orient(List seq)
+  {
+    DirectedEdge startEdge = (DirectedEdge) seq.get(0);
+    DirectedEdge endEdge = (DirectedEdge) seq.get(seq.size() - 1);
+    Node startNode = startEdge.getFromNode();
+    Node endNode = endEdge.getToNode();
+
+    boolean flipSeq = false;
+    boolean hasDegree1Node = startNode.getDegree() == 1
+                           || endNode.getDegree() == 1;
+
+    if (hasDegree1Node) {
+      boolean hasObviousStartNode = false;
+
+      // test end edge before start edge, to make result stable
+      // (ie. if both are good starts, pick the actual start
+      if (endEdge.getToNode().getDegree() == 1 && endEdge.getEdgeDirection() == false) {
+        hasObviousStartNode = true;
+        flipSeq = true;
+      }
+      if (startEdge.getFromNode().getDegree() == 1 && startEdge.getEdgeDirection() == true) {
+        hasObviousStartNode = true;
+        flipSeq = false;
+      }
+
+      // since there is no obvious start node, use any node of degree 1
+      if (! hasObviousStartNode) {
+        // check if the start node should actually be the end node
+        if (startEdge.getFromNode().getDegree() == 1)
+          flipSeq = true;
+        // if the end node is of degree 1, it is properly the end node
+      }
+
+    }
+
+
+    // if there is no degree 1 node, just use the sequence as is
+    // (Could insert heuristic of taking direction of majority of lines as overall direction)
+
+    if (flipSeq)
+      return reverse(seq);
+    return seq;
+  }
+
+  /**
+   * Reverse the sequence.
+   * This requires reversing the order of the dirEdges, and flipping
+   * each dirEdge as well
+   *
+   * @param seq a List of DirectedEdges, in sequential order
+   * @return the reversed sequence
+   */
+  private List reverse(List seq)
+  {
+    LinkedList newSeq = new LinkedList();
+    for (Iterator i = seq.iterator(); i.hasNext(); ) {
+      DirectedEdge de = (DirectedEdge) i.next();
+      newSeq.addFirst(de.getSym());
+    }
+    return newSeq;
+  }
+
+  /**
+   * Builds a geometry ({@link LineString} or {@link MultiLineString} )
+   * representing the sequence.
+   *
+   * @param sequences a List of Lists of DirectedEdges with
+   *   LineMergeEdges as their parent edges.
+   * @return the sequenced geometry, or <code>null</code> if no sequence exists
+   */
+  private Geometry buildSequencedGeometry(List sequences)
+  {
+    List lines = new ArrayList();
+
+    for (Iterator i1 = sequences.iterator(); i1.hasNext(); ) {
+      List seq = (List) i1.next();
+      for (Iterator i2 = seq.iterator(); i2.hasNext(); ) {
+        DirectedEdge de = (DirectedEdge) i2.next();
+        LineMergeEdge e = (LineMergeEdge) de.getEdge();
+        LineString line = e.getLine();
+
+        LineString lineToAdd = line;
+        if (! de.getEdgeDirection() && ! line.isClosed())
+          lineToAdd = reverse(line);
+
+        lines.add(lineToAdd);
+      }
+    }
+    if (lines.size() == 0)
+      return factory.createMultiLineString(new LineString[0]);
+    return factory.buildGeometry(lines);
+  }
+
+  private static LineString reverse(LineString line)
+  {
+    Coordinate[] pts = line.getCoordinates();
+    Coordinate[] revPts = new Coordinate[pts.length];
+    int len = pts.length;
+    for (int i = 0; i < len; i++) {
+      revPts[len - 1 - i] = new Coordinate(pts[i]);
+    }
+    return line.getFactory().createLineString(revPts);
+  }
+
+}
diff --git a/src/com/vividsolutions/jts/operation/overlay/EdgeSetNoder.java b/src/com/vividsolutions/jts/operation/overlay/EdgeSetNoder.java
index 672ec26..d95b1a1 100644
--- a/src/com/vividsolutions/jts/operation/overlay/EdgeSetNoder.java
+++ b/src/com/vividsolutions/jts/operation/overlay/EdgeSetNoder.java
@@ -45,7 +45,7 @@ import com.vividsolutions.jts.util.*;
  * Takes one or more sets of edges and constructs a
  * new set of edges consisting of all the split edges created by
  * noding the input edges together
- * @version 1.6
+ * @version 1.7
  */
 public class EdgeSetNoder {
 
diff --git a/src/com/vividsolutions/jts/operation/overlay/LineBuilder.java b/src/com/vividsolutions/jts/operation/overlay/LineBuilder.java
index 765e420..dcb6412 100644
--- a/src/com/vividsolutions/jts/operation/overlay/LineBuilder.java
+++ b/src/com/vividsolutions/jts/operation/overlay/LineBuilder.java
@@ -42,7 +42,7 @@ import com.vividsolutions.jts.util.*;
  * Forms JTS LineStrings out of a the graph of {@link DirectedEdge}s
  * created by an {@link OverlayOp}.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class LineBuilder {
   private OverlayOp op;
diff --git a/src/com/vividsolutions/jts/operation/overlay/MaximalEdgeRing.java b/src/com/vividsolutions/jts/operation/overlay/MaximalEdgeRing.java
index 1fb5d39..9c96c9e 100644
--- a/src/com/vividsolutions/jts/operation/overlay/MaximalEdgeRing.java
+++ b/src/com/vividsolutions/jts/operation/overlay/MaximalEdgeRing.java
@@ -54,7 +54,7 @@ import com.vividsolutions.jts.geomgraph.*;
  * A MaximalEdgeRing can be converted to a list of MinimalEdgeRings using the
  * {@link #buildMinimalRings() } method.
  *
- * @version 1.6
+ * @version 1.7
  * @see com.vividsolutions.jts.operation.overlay.MinimalEdgeRing
  */
 public class MaximalEdgeRing
diff --git a/src/com/vividsolutions/jts/operation/overlay/MinimalEdgeRing.java b/src/com/vividsolutions/jts/operation/overlay/MinimalEdgeRing.java
index 21166f4..339342a 100644
--- a/src/com/vividsolutions/jts/operation/overlay/MinimalEdgeRing.java
+++ b/src/com/vividsolutions/jts/operation/overlay/MinimalEdgeRing.java
@@ -43,7 +43,7 @@ import com.vividsolutions.jts.geomgraph.*;
  * has degree greater than 2.  These are the form of rings required
  * to represent polygons under the OGC SFS spatial data model.
  *
- * @version 1.6
+ * @version 1.7
  * @see com.vividsolutions.jts.operation.overlay.MaximalEdgeRing
  */
 public class MinimalEdgeRing
diff --git a/src/com/vividsolutions/jts/operation/overlay/OverlayNodeFactory.java b/src/com/vividsolutions/jts/operation/overlay/OverlayNodeFactory.java
index b7e0971..5abefa1 100644
--- a/src/com/vividsolutions/jts/operation/overlay/OverlayNodeFactory.java
+++ b/src/com/vividsolutions/jts/operation/overlay/OverlayNodeFactory.java
@@ -35,7 +35,7 @@
 package com.vividsolutions.jts.operation.overlay;
 
 /**
- * @version 1.6
+ * @version 1.7
  */
 import com.vividsolutions.jts.geom.Coordinate;
 import com.vividsolutions.jts.geomgraph.*;
@@ -44,7 +44,7 @@ import com.vividsolutions.jts.geomgraph.*;
  * Creates nodes for use in the {@link PlanarGraph}s constructed during
  * overlay operations.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class OverlayNodeFactory
   extends NodeFactory
diff --git a/src/com/vividsolutions/jts/operation/overlay/OverlayOp.java b/src/com/vividsolutions/jts/operation/overlay/OverlayOp.java
index 1665ca6..0b4278f 100644
--- a/src/com/vividsolutions/jts/operation/overlay/OverlayOp.java
+++ b/src/com/vividsolutions/jts/operation/overlay/OverlayOp.java
@@ -46,7 +46,7 @@ import com.vividsolutions.jts.operation.GeometryGraphOperation;
  * Computes the overlay of two {@link Geometry}s.  The overlay
  * can be used to determine any boolean combination of the geometries.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class OverlayOp
   extends GeometryGraphOperation
@@ -75,7 +75,7 @@ public class OverlayOp
   }
 
   /**
-   * This method will handle arguments of Location.NULL correctly
+   * This method will handle arguments of Location.NONE correctly
    *
    * @return true if the locations correspond to the opCode
    */
@@ -206,11 +206,12 @@ public class OverlayOp
    */
   protected void insertUniqueEdge(Edge e)
   {
-//Debug.println(e);
-    int foundIndex = edgeList.findEdgeIndex(e);
+//<FIX> MD 8 Oct 03  speed up identical edge lookup
+    // fast lookup
+    Edge existingEdge = edgeList.findEqualEdge(e);
+
     // If an identical edge already exists, simply update its label
-    if (foundIndex >= 0) {
-      Edge existingEdge = (Edge) edgeList.get(foundIndex);
+    if (existingEdge != null) {
       Label existingLabel = existingEdge.getLabel();
 
       Label labelToMerge = e.getLabel();
diff --git a/src/com/vividsolutions/jts/operation/overlay/PointBuilder.java b/src/com/vividsolutions/jts/operation/overlay/PointBuilder.java
index 883bada..0820112 100644
--- a/src/com/vividsolutions/jts/operation/overlay/PointBuilder.java
+++ b/src/com/vividsolutions/jts/operation/overlay/PointBuilder.java
@@ -1,5 +1,3 @@
-
-
 /*
  * The JTS Topology Suite is a collection of Java classes that
  * implement the fundamental operations required to validate a given
@@ -41,61 +39,88 @@ import com.vividsolutions.jts.geomgraph.*;
 
 /**
  * Constructs {@link Point}s from the nodes of an overlay graph.
- * @version 1.6
+ * @version 1.7
  */
 public class PointBuilder {
   private OverlayOp op;
   private GeometryFactory geometryFactory;
-  private PointLocator ptLocator;
+  private List resultPointList = new ArrayList();
 
   public PointBuilder(OverlayOp op, GeometryFactory geometryFactory, PointLocator ptLocator) {
     this.op = op;
     this.geometryFactory = geometryFactory;
-    this.ptLocator = ptLocator;
+    // ptLocator is never used in this class
   }
+
   /**
-   * @return a list of the Points in the result of the specified overlay operation
+   * Computes the Point geometries which will appear in the result,
+   * given the specified overlay operation.
+   *
+   * @return a list of the Points objects in the result
    */
   public List build(int opCode)
   {
-    List nodeList = collectNodes(opCode);
-    List resultPointList = simplifyPoints(nodeList);
+    extractNonCoveredResultNodes(opCode);
+    /**
+     * It can happen that connected result nodes are still covered by
+     * result geometries, so must perform this filter.
+     * (For instance, this can happen during topology collapse).
+     */
     return resultPointList;
   }
 
-  private List collectNodes(int opCode)
+  /**
+   * Determines nodes which are in the result, and creates {@link Point}s for them.
+   *
+   * This method determines nodes which are candidates for the result via their
+   * labelling and their graph topology.
+   *
+   * @param opCode the overlay operation
+   */
+  private void extractNonCoveredResultNodes(int opCode)
   {
-    List resultNodeList = new ArrayList();
-    // add nodes from edge intersections which have not already been included in the result
+    // testing only
+    //if (true) return resultNodeList;
+
     for (Iterator nodeit = op.getGraph().getNodes().iterator(); nodeit.hasNext(); ) {
       Node n = (Node) nodeit.next();
-      if (! n.isInResult()) {
-        Label label = n.getLabel();
-        if (OverlayOp.isResultOfOp(label, opCode)) {
-          resultNodeList.add(n);
-        }
+
+      // filter out nodes which are known to be in the result
+      if (n.isInResult())
+        continue;
+      // if an incident edge is in the result, then the node coordinate is included already
+      if (n.isIncidentEdgeInResult())
+        continue;
+      if (n.getEdges().getDegree() == 0 || opCode == OverlayOp.INTERSECTION) {
+
+        /**
+         * For nodes on edges, only INTERSECTION can result in edge nodes being included even
+         * if none of their incident edges are included
+         */
+          Label label = n.getLabel();
+          if (OverlayOp.isResultOfOp(label, opCode)) {
+            filterCoveredNodeToPoint(n);
+          }
       }
     }
-    return resultNodeList;
+    //System.out.println("connectedResultNodes collected = " + connectedResultNodes.size());
   }
+
   /**
-   * This method simplifies the resultant Geometry by finding and eliminating
-   * "covered" points.
-   * A point is covered if it is contained in another element Geometry
-   * with higher dimension (e.g. a point might be contained in a polygon,
-   * in which case the point can be eliminated from the resultant).
+   * Converts non-covered nodes to Point objects and adds them to the result.
+   *
+   * A node is covered if it is contained in another element Geometry
+   * with higher dimension (e.g. a node point might be contained in a polygon,
+   * in which case the point can be eliminated from the result).
+   *
+   * @param n the node to test
    */
-  private List simplifyPoints(List resultNodeList)
+  private void filterCoveredNodeToPoint(Node n)
   {
-    List nonCoveredPointList = new ArrayList();
-    for (Iterator it = resultNodeList.iterator(); it.hasNext(); ) {
-      Node n = (Node) it.next();
-      Coordinate coord = n.getCoordinate();
-      if (! op.isCoveredByLA(coord)) {
-        Point pt = geometryFactory.createPoint(coord);
-        nonCoveredPointList.add(pt);
-      }
+    Coordinate coord = n.getCoordinate();
+    if (! op.isCoveredByLA(coord)) {
+      Point pt = geometryFactory.createPoint(coord);
+      resultPointList.add(pt);
     }
-    return nonCoveredPointList;
   }
 }
diff --git a/src/com/vividsolutions/jts/operation/overlay/PolygonBuilder.java b/src/com/vividsolutions/jts/operation/overlay/PolygonBuilder.java
index 5d180f1..7f177e6 100644
--- a/src/com/vividsolutions/jts/operation/overlay/PolygonBuilder.java
+++ b/src/com/vividsolutions/jts/operation/overlay/PolygonBuilder.java
@@ -43,14 +43,14 @@ import com.vividsolutions.jts.util.*;
  * The edges to use are marked as being in the result Area.
  * <p>
  *
- * @version 1.6
+ * @version 1.7
  */
 public class PolygonBuilder {
 
   private GeometryFactory geometryFactory;
   private CGAlgorithms cga;
-  private List dirEdgeList;
-  private NodeMap nodes;
+  //private List dirEdgeList;
+  //private NodeMap nodes;
   private List shellList        = new ArrayList();
 
   public PolygonBuilder(GeometryFactory geometryFactory, CGAlgorithms cga)
@@ -254,7 +254,7 @@ public class PolygonBuilder {
       if (minShell != null) minEnv = minShell.getLinearRing().getEnvelopeInternal();
       boolean isContained = false;
       if (tryEnv.contains(testEnv)
-          && cga.isPointInRing(testPt, tryRing.getCoordinates()) )
+          && CGAlgorithms.isPointInRing(testPt, tryRing.getCoordinates()) )
         isContained = true;
       // check if this new containing ring is smaller than the current minimum ring
       if (isContained) {
diff --git a/src/com/vividsolutions/jts/operation/polygonize/EdgeRing.java b/src/com/vividsolutions/jts/operation/polygonize/EdgeRing.java
index 72d8eb9..1d4ab06 100644
--- a/src/com/vividsolutions/jts/operation/polygonize/EdgeRing.java
+++ b/src/com/vividsolutions/jts/operation/polygonize/EdgeRing.java
@@ -44,7 +44,7 @@ import com.vividsolutions.jts.planargraph.*;
  * Represents a ring of {@link PolygonizeDirectedEdge}s which form
  * a ring of a polygon.  The ring may be either an outer shell or a hole.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class EdgeRing {
 
diff --git a/src/com/vividsolutions/jts/operation/polygonize/PolygonizeDirectedEdge.java b/src/com/vividsolutions/jts/operation/polygonize/PolygonizeDirectedEdge.java
index fb027da..9a47463 100644
--- a/src/com/vividsolutions/jts/operation/polygonize/PolygonizeDirectedEdge.java
+++ b/src/com/vividsolutions/jts/operation/polygonize/PolygonizeDirectedEdge.java
@@ -42,7 +42,7 @@ import com.vividsolutions.jts.planargraph.Node;
  * an edge of a polygon formed by the graph.
  * May be logically deleted from the graph by setting the <code>marked</code> flag.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class PolygonizeDirectedEdge
     extends DirectedEdge
diff --git a/src/com/vividsolutions/jts/operation/polygonize/PolygonizeEdge.java b/src/com/vividsolutions/jts/operation/polygonize/PolygonizeEdge.java
index 630952e..6b8d58f 100644
--- a/src/com/vividsolutions/jts/operation/polygonize/PolygonizeEdge.java
+++ b/src/com/vividsolutions/jts/operation/polygonize/PolygonizeEdge.java
@@ -41,7 +41,7 @@ import com.vividsolutions.jts.planargraph.*;
 /**
  * An edge of a polygonization graph.
  *
- * @version 1.6
+ * @version 1.7
  */
 class PolygonizeEdge
     extends Edge
diff --git a/src/com/vividsolutions/jts/operation/polygonize/PolygonizeGraph.java b/src/com/vividsolutions/jts/operation/polygonize/PolygonizeGraph.java
index a0f6a8d..2e4097a 100644
--- a/src/com/vividsolutions/jts/operation/polygonize/PolygonizeGraph.java
+++ b/src/com/vividsolutions/jts/operation/polygonize/PolygonizeGraph.java
@@ -48,7 +48,7 @@ import com.vividsolutions.jts.planargraph.*;
  * The marked flag on {@link DirectedEdge}s is used to indicate that a directed edge
  * has be logically deleted from the graph.
  *
- * @version 1.6
+ * @version 1.7
  */
 class PolygonizeGraph
     extends PlanarGraph
@@ -225,7 +225,7 @@ class PolygonizeGraph
    * @param dirEdges a List of the DirectedEdges in the graph
    * @return a List of DirectedEdges, one for each edge ring found
    */
-  private static List findLabeledEdgeRings(List dirEdges)
+  private static List findLabeledEdgeRings(Collection dirEdges)
   {
     List edgeRingStarts = new ArrayList();
     // label the edge rings formed
@@ -277,7 +277,7 @@ class PolygonizeGraph
     return cutLines;
   }
 
-  private static void label(List dirEdges, long label)
+  private static void label(Collection dirEdges, long label)
   {
     for (Iterator i = dirEdges.iterator(); i.hasNext(); ) {
       PolygonizeDirectedEdge de = (PolygonizeDirectedEdge) i.next();
diff --git a/src/com/vividsolutions/jts/operation/polygonize/Polygonizer.java b/src/com/vividsolutions/jts/operation/polygonize/Polygonizer.java
index 37e6047..cf476bc 100644
--- a/src/com/vividsolutions/jts/operation/polygonize/Polygonizer.java
+++ b/src/com/vividsolutions/jts/operation/polygonize/Polygonizer.java
@@ -53,7 +53,7 @@ import com.vividsolutions.jts.geom.*;
  * (e.g. the component lines contain a self-intersection)
  * </ul>
  *
- * @version 1.6
+ * @version 1.7
  */
 public class Polygonizer
 {
diff --git a/src/com/vividsolutions/jts/operation/predicate/RectangleContains.java b/src/com/vividsolutions/jts/operation/predicate/RectangleContains.java
index 2f5838a..d28181a 100644
--- a/src/com/vividsolutions/jts/operation/predicate/RectangleContains.java
+++ b/src/com/vividsolutions/jts/operation/predicate/RectangleContains.java
@@ -10,7 +10,7 @@ import com.vividsolutions.jts.geom.*;
  * this class can be used directly to test many geometries against a single
  * rectangle.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class RectangleContains {
 
@@ -119,4 +119,4 @@ public class RectangleContains {
     return false;
   }
 
-}
+}
\ No newline at end of file
diff --git a/src/com/vividsolutions/jts/operation/predicate/RectangleIntersects.java b/src/com/vividsolutions/jts/operation/predicate/RectangleIntersects.java
index 278a770..71e2637 100644
--- a/src/com/vividsolutions/jts/operation/predicate/RectangleIntersects.java
+++ b/src/com/vividsolutions/jts/operation/predicate/RectangleIntersects.java
@@ -13,7 +13,7 @@ import com.vividsolutions.jts.geom.util.*;
  * this class can be used directly to test many geometries against a single
  * rectangle.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class RectangleIntersects {
 
@@ -70,6 +70,14 @@ public class RectangleIntersects {
   }
 }
 
+/**
+ * Tests whether it can be concluded
+ * that a rectangle intersects a geometry,
+ * based on the locations of the envelope(s) of the geometry.
+ *
+ * @author Martin Davis
+ * @version 1.7
+ */
 class EnvelopeIntersectsVisitor
     extends ShortCircuitedGeometryVisitor
 {
@@ -81,6 +89,13 @@ class EnvelopeIntersectsVisitor
     this.rectEnv = rectEnv;
   }
 
+  /**
+   * Reports whether it can be concluded that an intersection occurs,
+   * or whether further testing is required.
+   *
+   * @return <code>true</code> if an intersection must occur
+   * <code>false</code> if no conclusion can be made
+   */
   public boolean intersects() { return intersects; }
 
   protected void visit(Geometry element)
@@ -97,11 +112,14 @@ class EnvelopeIntersectsVisitor
     }
     /**
      * Since the envelopes intersect and the test element is connected,
-     * if its envelope is completely bisected by an edge of the rectangle
-     * the element and the rectangle must touch.
-     * (Note it is NOT possible to make this conclusion
-     * if the test envelope is "on a corner" of the rectangle
-     * envelope)
+     * if the test envelope is completely bisected by an edge of the rectangle
+     * the element and the rectangle must touch
+     * (This is basically an application of the Jordan Curve Theorem).
+     * The alternative situation is that
+     * the test envelope is "on a corner" of the rectangle envelope,
+     * i.e. is not completely bisected.
+     * In this case it is not possible to make a conclusion
+     * about the presence of an intersection.
      */
     if (elementEnv.getMinX() >= rectEnv.getMinX()
         && elementEnv.getMaxX() <= rectEnv.getMaxX()) {
@@ -120,6 +138,13 @@ class EnvelopeIntersectsVisitor
   }
 }
 
+/**
+ * Tests whether it can be concluded
+ * that a geometry contains a corner point of a rectangle.
+ *
+ * @author Martin Davis
+ * @version 1.7
+ */
 class ContainsPointVisitor
     extends ShortCircuitedGeometryVisitor
 {
@@ -133,6 +158,14 @@ class ContainsPointVisitor
     rectEnv = rectangle.getEnvelopeInternal();
   }
 
+  /**
+   * Reports whether it can be concluded that a corner
+   * point of the rectangle is contained in the geometry,
+   * or whether further testing is required.
+   *
+   * @return <code>true</code> if a corner point is contained
+   * <code>false</code> if no conclusion can be made
+   */
   public boolean containsPoint() { return containsPoint; }
 
   protected void visit(Geometry geom)
@@ -161,6 +194,14 @@ class ContainsPointVisitor
   }
 }
 
+/**
+ * Tests whether any line segment of a geometry intersects a given rectangle.
+ * Optimizes the algorithm used based on the number of line segments in the
+ * test geometry.
+ *
+ * @author Martin Davis
+ * @version 1.7
+ */
 class LineIntersectsVisitor
     extends ShortCircuitedGeometryVisitor
 {
@@ -176,6 +217,13 @@ class LineIntersectsVisitor
     rectEnv = rectangle.getEnvelopeInternal();
   }
 
+
+  /**
+   * Reports whether any segment intersection exists.
+   *
+   * @return <code>true</code> if a segment intersection exists
+   * <code>false</code> if no segment intersection exists
+   */
   public boolean intersects() { return intersects; }
 
   protected void visit(Geometry geom)
@@ -188,6 +236,7 @@ class LineIntersectsVisitor
       intersects = rectangle.relate(geom).isIntersects();
       return;
     }
+    // if small enough, test for segment intersection directly
     computeSegmentIntersection(geom);
   }
 
diff --git a/src/com/vividsolutions/jts/operation/predicate/SegmentIntersectionTester.java b/src/com/vividsolutions/jts/operation/predicate/SegmentIntersectionTester.java
index c799243..7920c1f 100644
--- a/src/com/vividsolutions/jts/operation/predicate/SegmentIntersectionTester.java
+++ b/src/com/vividsolutions/jts/operation/predicate/SegmentIntersectionTester.java
@@ -9,7 +9,7 @@ import com.vividsolutions.jts.geom.*;
  * Optimized for small geometry size.
  * Short-circuited to return as soon an intersection is found.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class SegmentIntersectionTester {
 
@@ -51,4 +51,4 @@ public class SegmentIntersectionTester {
     }
     return hasIntersection;
   }
-}
+}
\ No newline at end of file
diff --git a/src/com/vividsolutions/jts/operation/relate/EdgeEndBuilder.java b/src/com/vividsolutions/jts/operation/relate/EdgeEndBuilder.java
index d32a4a7..62cb8e3 100644
--- a/src/com/vividsolutions/jts/operation/relate/EdgeEndBuilder.java
+++ b/src/com/vividsolutions/jts/operation/relate/EdgeEndBuilder.java
@@ -40,7 +40,7 @@ package com.vividsolutions.jts.operation.relate;
  * created by the
  * intersections determined for an Edge.
  *
- * @version 1.6
+ * @version 1.7
  */
 import java.util.*;
 import com.vividsolutions.jts.geom.*;
@@ -50,7 +50,7 @@ import com.vividsolutions.jts.util.*;
 /**
  * Computes the {@link EdgeEnd}s which arise from a noded {@link Edge}.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class EdgeEndBuilder {
 
diff --git a/src/com/vividsolutions/jts/operation/relate/EdgeEndBundle.java b/src/com/vividsolutions/jts/operation/relate/EdgeEndBundle.java
index e1f63a0..52982c4 100644
--- a/src/com/vividsolutions/jts/operation/relate/EdgeEndBundle.java
+++ b/src/com/vividsolutions/jts/operation/relate/EdgeEndBundle.java
@@ -38,7 +38,7 @@ package com.vividsolutions.jts.operation.relate;
 /**
  * A collection of EdgeStubs which obey the following invariant:
  * They originate at the same node and have the same direction.
- * @version 1.6
+ * @version 1.7
  */
 import java.io.PrintStream;
 import java.util.*;
@@ -50,12 +50,11 @@ import com.vividsolutions.jts.util.Assert;
 /**
  * Contains all {@link EdgeEnd}s which start at the same point and are parallel.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class EdgeEndBundle
   extends EdgeEnd
 {
-  //private Label label;
   private List edgeEnds = new ArrayList();
 
   public EdgeEndBundle(EdgeEnd e)
@@ -89,9 +88,9 @@ public class EdgeEndBundle
       if (e.getLabel().isArea()) isArea = true;
     }
     if (isArea)
-      label = new Label(Location.NULL, Location.NULL, Location.NULL);
+      label = new Label(Location.NONE, Location.NONE, Location.NONE);
     else
-      label = new Label(Location.NULL);
+      label = new Label(Location.NONE);
 
     // compute the On label, and the side labels if present
     for (int i = 0; i < 2; i++) {
@@ -133,7 +132,7 @@ public class EdgeEndBundle
       if (loc == Location.BOUNDARY) boundaryCount++;
       if (loc == Location.INTERIOR) foundInterior = true;
     }
-    int loc = Location.NULL;
+    int loc = Location.NONE;
     if (foundInterior)  loc = Location.INTERIOR;
     if (boundaryCount > 0) {
         loc = GeometryGraph.determineBoundary(boundaryCount);
diff --git a/src/com/vividsolutions/jts/operation/relate/EdgeEndBundleStar.java b/src/com/vividsolutions/jts/operation/relate/EdgeEndBundleStar.java
index f6e2a44..c8f8d9b 100644
--- a/src/com/vividsolutions/jts/operation/relate/EdgeEndBundleStar.java
+++ b/src/com/vividsolutions/jts/operation/relate/EdgeEndBundleStar.java
@@ -46,7 +46,7 @@ import com.vividsolutions.jts.util.Assert;
  * An ordered list of {@link EdgeEndBundle}s around a {@link RelateNode}.
  * They are maintained in CCW order (starting with the positive x-axis) around the node
  * for efficient lookup and topology building.
- * @version 1.6
+ * @version 1.7
  */
 public class EdgeEndBundleStar
   extends EdgeEndStar
diff --git a/src/com/vividsolutions/jts/operation/relate/RelateComputer.java b/src/com/vividsolutions/jts/operation/relate/RelateComputer.java
index 6beb57a..c6988f3 100644
--- a/src/com/vividsolutions/jts/operation/relate/RelateComputer.java
+++ b/src/com/vividsolutions/jts/operation/relate/RelateComputer.java
@@ -36,7 +36,7 @@
 package com.vividsolutions.jts.operation.relate;
 
 /**
- * @version 1.6
+ * @version 1.7
  */
 import java.util.*;
 import com.vividsolutions.jts.geom.*;
@@ -58,7 +58,7 @@ import com.vividsolutions.jts.geomgraph.index.SegmentIntersector;
  * would first need to be noded and merged (if not explicitly, at least
  * implicitly).
  *
- * @version 1.6
+ * @version 1.7
  */
 public class RelateComputer
 {
diff --git a/src/com/vividsolutions/jts/operation/relate/RelateNode.java b/src/com/vividsolutions/jts/operation/relate/RelateNode.java
index ac1109d..1509aa1 100644
--- a/src/com/vividsolutions/jts/operation/relate/RelateNode.java
+++ b/src/com/vividsolutions/jts/operation/relate/RelateNode.java
@@ -39,7 +39,7 @@ package com.vividsolutions.jts.operation.relate;
  * A RelateNode is a Node that maintains a list of EdgeStubs
  * for the edges that are incident on it.
  *
- * @version 1.6
+ * @version 1.7
  */
 
 import java.io.PrintStream;
@@ -51,7 +51,7 @@ import com.vividsolutions.jts.geomgraph.*;
 /**
  * Represents a node in the topological graph used to compute spatial relationships.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class RelateNode
   extends Node
diff --git a/src/com/vividsolutions/jts/operation/relate/RelateNodeFactory.java b/src/com/vividsolutions/jts/operation/relate/RelateNodeFactory.java
index 520a28d..8372769 100644
--- a/src/com/vividsolutions/jts/operation/relate/RelateNodeFactory.java
+++ b/src/com/vividsolutions/jts/operation/relate/RelateNodeFactory.java
@@ -40,7 +40,7 @@ import com.vividsolutions.jts.geomgraph.*;
 /**
  * Used by the {@link NodeMap} in a {@link RelateNodeGraph} to create {@link RelateNode}s.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class RelateNodeFactory
   extends NodeFactory
diff --git a/src/com/vividsolutions/jts/operation/relate/RelateNodeGraph.java b/src/com/vividsolutions/jts/operation/relate/RelateNodeGraph.java
index 30f9cb3..d7e704e 100644
--- a/src/com/vividsolutions/jts/operation/relate/RelateNodeGraph.java
+++ b/src/com/vividsolutions/jts/operation/relate/RelateNodeGraph.java
@@ -34,7 +34,7 @@
 package com.vividsolutions.jts.operation.relate;
 
 /**
- * @version 1.6
+ * @version 1.7
  */
 import java.util.*;
 import com.vividsolutions.jts.geom.*;
@@ -59,7 +59,7 @@ import com.vividsolutions.jts.geomgraph.*;
  * have their topology determined implicitly, without creating a Node object
  * to represent them.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class RelateNodeGraph {
 
diff --git a/src/com/vividsolutions/jts/operation/relate/RelateOp.java b/src/com/vividsolutions/jts/operation/relate/RelateOp.java
index 6ede8b0..d2db31c 100644
--- a/src/com/vividsolutions/jts/operation/relate/RelateOp.java
+++ b/src/com/vividsolutions/jts/operation/relate/RelateOp.java
@@ -36,7 +36,7 @@
 package com.vividsolutions.jts.operation.relate;
 
 /**
- * @version 1.6
+ * @version 1.7
  */
 
 import com.vividsolutions.jts.geomgraph.*;
@@ -47,7 +47,7 @@ import java.util.*;
 /**
  * Implements the relate() operation on {@link Geometry}s.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class RelateOp
   extends GeometryGraphOperation
diff --git a/src/com/vividsolutions/jts/operation/valid/ConnectedInteriorTester.java b/src/com/vividsolutions/jts/operation/valid/ConnectedInteriorTester.java
index d3fe9b1..ff9f95c 100644
--- a/src/com/vividsolutions/jts/operation/valid/ConnectedInteriorTester.java
+++ b/src/com/vividsolutions/jts/operation/valid/ConnectedInteriorTester.java
@@ -43,17 +43,17 @@ import com.vividsolutions.jts.util.*;
 
 /**
  * This class tests that the interior of an area {@link Geometry}
- *  ({@link Polygon}  or {@link MultiPolygon} )
- * is connected.  An area Geometry is invalid if the interior is disconnected.
+ * ( {@link Polygon}  or {@link MultiPolygon} )
+ * is connected.
  * This can happen if:
  * <ul>
- * <li>one or more holes either form a chain touching the shell at two places
- * <li>one or more holes form a ring around a portion of the interior
+ * <li>a shell self-intersects
+ * <li>one or more holes form a connected chain touching a shell at two different points
+ * <li>one or more holes form a ring around a subset of the interior
  * </ul>
- * If an inconsistency if found the location of the problem
- * is recorded.
+ * If a disconnected situation is found the location of the problem is recorded.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class ConnectedInteriorTester {
 
diff --git a/src/com/vividsolutions/jts/operation/valid/ConsistentAreaTester.java b/src/com/vividsolutions/jts/operation/valid/ConsistentAreaTester.java
index a2fda05..fe358bb 100644
--- a/src/com/vividsolutions/jts/operation/valid/ConsistentAreaTester.java
+++ b/src/com/vividsolutions/jts/operation/valid/ConsistentAreaTester.java
@@ -45,17 +45,23 @@ import com.vividsolutions.jts.util.*;
 /**
  * Checks that a {@link GeometryGraph} representing an area
  * (a {@link Polygon} or {@link MultiPolygon} )
- * is consistent with the SFS semantics for area geometries.
+ * has consistent semantics for area geometries.
+ * This check is required for any reasonable polygonal model
+ * (including the OGC-SFS model, as well as models which allow ring self-intersection at single points)
+ * <p>
  * Checks include:
  * <ul>
- * <li>testing for rings which self-intersect (both properly
- * and at nodes)
- * <li>testing for duplicate rings
+ * <li>test for rings which properly intersect
+ * (but not for ring self-intersection, or intersections at vertices)
+ * <li>test for consistent labelling at all node points
+ * (this detects vertex intersections with invalid topology,
+ * i.e. where the exterior side of an edge lies in the interior of the area)
+ * <li>test for duplicate rings
  * </ul>
- * If an inconsistency if found the location of the problem
- * is recorded.
+ * If an inconsistency is found the location of the problem
+ * is recorded and is available to the caller.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class ConsistentAreaTester {
 
@@ -66,6 +72,11 @@ public class ConsistentAreaTester {
   // the intersection point found (if any)
   private Coordinate invalidPoint;
 
+  /**
+   * Creates a new tester for consistent areas.
+   *
+   * @param geomGraph the topology graph of the area geometry
+   */
   public ConsistentAreaTester(GeometryGraph geomGraph)
   {
     this.geomGraph = geomGraph;
@@ -76,6 +87,11 @@ public class ConsistentAreaTester {
    */
   public Coordinate getInvalidPoint() { return invalidPoint; }
 
+  /**
+   * Check all nodes to see if their labels are consistent with area topology.
+   *
+   * @return <code>true</code> if this area has a consistent node labelling
+   */
   public boolean isNodeConsistentArea()
   {
     /**
@@ -96,6 +112,8 @@ public class ConsistentAreaTester {
   /**
    * Check all nodes to see if their labels are consistent.
    * If any are not, return false
+   *
+   * @return <code>true</code> if the edge area labels are consistent at this node
    */
   private boolean isNodeEdgeAreaLabelsConsistent()
   {
@@ -108,6 +126,7 @@ public class ConsistentAreaTester {
     }
     return true;
   }
+
   /**
    * Checks for two duplicate rings in an area.
    * Duplicate rings are rings that are topologically equal
diff --git a/src/com/vividsolutions/jts/operation/valid/IsValidOp.java b/src/com/vividsolutions/jts/operation/valid/IsValidOp.java
index d17e4bc..bbbdb61 100644
--- a/src/com/vividsolutions/jts/operation/valid/IsValidOp.java
+++ b/src/com/vividsolutions/jts/operation/valid/IsValidOp.java
@@ -38,14 +38,14 @@ import java.util.*;
 import com.vividsolutions.jts.algorithm.*;
 import com.vividsolutions.jts.geomgraph.*;
 import com.vividsolutions.jts.geom.*;
-import com.vividsolutions.jts.operation.GeometryGraphOperation;
 import com.vividsolutions.jts.util.*;
 
 /**
  * Implements the algorithsm required to compute the <code>isValid()</code> method
  * for {@link Geometry}s.
+ * See the documentation for the various geometry types for a specification of validity.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class IsValidOp
 {
@@ -91,6 +91,11 @@ public class IsValidOp
   }
 
   private Geometry parentGeometry;  // the base Geometry to be validated
+  /**
+   * If the following condition is TRUE JTS will validate inverted shells and exverted holes
+   * (the ESRI SDE model)
+   */
+  private boolean isSelfTouchingRingFormingHoleValid = false;
   private boolean isChecked = false;
   private TopologyValidationError validErr;
 
@@ -99,6 +104,35 @@ public class IsValidOp
     this.parentGeometry = parentGeometry;
   }
 
+  /**
+   * Sets whether polygons using <b>Self-Touching Rings</b> to form
+   * holes are reported as valid.
+   * If this flag is set, the following Self-Touching conditions
+   * are treated as being valid:
+   * <ul>
+   * <li>the shell ring self-touches to create a hole touching the shell
+   * <li>a hole ring self-touches to create two holes touching at a point
+   * </ul>
+   * <p>
+   * The default (following the OGC SFS standard)
+   * is that this condition is <b>not</b> valid (<code>false</code>).
+   * <p>
+   * This does not affect whether Self-Touching Rings
+   * disconnecting the polygon interior are considered valid
+   * (these are considered to be <b>invalid</b> under the SFS, and many other
+   * spatial models as well).
+   * This includes "bow-tie" shells,
+   * which self-touch at a single point causing the interior to
+   * be disconnected,
+   * and "C-shaped" holes which self-touch at a single point causing an island to be formed.
+   *
+   * @param isValid states whether geometry with this condition is valid
+   */
+  public void setSelfTouchingRingFormingHoleValid(boolean isValid)
+  {
+    isSelfTouchingRingFormingHoleValid = isValid;
+  }
+
   public boolean isValid()
   {
     checkValid(parentGeometry);
@@ -115,7 +149,10 @@ public class IsValidOp
   {
     if (isChecked) return;
     validErr = null;
+
+    // empty geometries are always valid!
     if (g.isEmpty()) return;
+
     if (g instanceof Point)                   checkValid((Point) g);
     else if (g instanceof MultiPoint)         checkValid((MultiPoint) g);
                         // LineString also handles LinearRings
@@ -159,6 +196,9 @@ public class IsValidOp
   {
     checkInvalidCoordinates(g.getCoordinates());
     if (validErr != null) return;
+    checkClosedRing(g);
+    if (validErr != null) return;
+
     GeometryGraph graph = new GeometryGraph(0, g);
     checkTooFewPoints(graph);
     if (validErr != null) return;
@@ -175,6 +215,8 @@ public class IsValidOp
   {
     checkInvalidCoordinates(g);
     if (validErr != null) return;
+    checkClosedRings(g);
+    if (validErr != null) return;
 
     GeometryGraph graph = new GeometryGraph(0, g);
 
@@ -182,8 +224,11 @@ public class IsValidOp
     if (validErr != null) return;
     checkConsistentArea(graph);
     if (validErr != null) return;
-    checkNoSelfIntersectingRings(graph);
-    if (validErr != null) return;
+
+    if (! isSelfTouchingRingFormingHoleValid) {
+      checkNoSelfIntersectingRings(graph);
+      if (validErr != null) return;
+    }
     checkHolesInShell(g, graph);
     if (validErr != null) return;
     //SLOWcheckHolesNotNested(g);
@@ -191,12 +236,15 @@ public class IsValidOp
     if (validErr != null) return;
     checkConnectedInteriors(graph);
   }
+
   private void checkValid(MultiPolygon g)
   {
     for (int i = 0; i < g.getNumGeometries(); i++) {
       Polygon p = (Polygon) g.getGeometryN(i);
       checkInvalidCoordinates(p);
       if (validErr != null) return;
+      checkClosedRings(p);
+      if (validErr != null) return;
     }
 
     GeometryGraph graph = new GeometryGraph(0, g);
@@ -205,9 +253,10 @@ public class IsValidOp
     if (validErr != null) return;
     checkConsistentArea(graph);
     if (validErr != null) return;
-    checkNoSelfIntersectingRings(graph);
-    if (validErr != null) return;
-
+    if (! isSelfTouchingRingFormingHoleValid) {
+      checkNoSelfIntersectingRings(graph);
+      if (validErr != null) return;
+    }
     for (int i = 0; i < g.getNumGeometries(); i++) {
       Polygon p = (Polygon) g.getGeometryN(i);
       checkHolesInShell(p, graph);
@@ -240,10 +289,10 @@ public class IsValidOp
                           TopologyValidationError.INVALID_COORDINATE,
                           coords[i]);
         return;
-
       }
     }
   }
+
   private void checkInvalidCoordinates(Polygon poly)
   {
     checkInvalidCoordinates(poly.getExteriorRing().getCoordinates());
@@ -254,6 +303,24 @@ public class IsValidOp
     }
   }
 
+  private void checkClosedRings(Polygon poly)
+  {
+    checkClosedRing((LinearRing) poly.getExteriorRing());
+    if (validErr != null) return;
+    for (int i = 0; i < poly.getNumInteriorRing(); i++) {
+      checkClosedRing((LinearRing) poly.getInteriorRingN(i));
+      if (validErr != null) return;
+    }
+  }
+
+  private void checkClosedRing(LinearRing ring)
+  {
+    if (! ring.isClosed() )
+      validErr = new TopologyValidationError(
+                        TopologyValidationError.RING_NOT_CLOSED,
+                        ring.getCoordinateN(0));
+  }
+
   private void checkTooFewPoints(GeometryGraph graph)
   {
     if (graph.hasTooFewPoints()) {
@@ -264,6 +331,14 @@ public class IsValidOp
     }
   }
 
+  /**
+   * Checks that the arrangement of edges in a polygonal geometry graph
+   * forms a consistent area.
+   *
+   * @param graph
+   *
+   * @see ConsistentAreaTester
+   */
   private void checkConsistentArea(GeometryGraph graph)
   {
     ConsistentAreaTester cat = new ConsistentAreaTester(graph);
@@ -281,22 +356,29 @@ public class IsValidOp
     }
   }
 
+  /**
+   * Check that there is no ring which self-intersects (except of course at its endpoints).
+   * This is required by OGC topology rules (but not by other models
+   * such as ESRI SDE, which allow inverted shells and exverted holes).
+   *
+   * @param graph the topology graph of the geometry
+   */
   private void checkNoSelfIntersectingRings(GeometryGraph graph)
   {
     for (Iterator i = graph.getEdgeIterator(); i.hasNext(); ) {
       Edge e = (Edge) i.next();
-      checkSelfIntersectingRing(e.getEdgeIntersectionList());
+      checkNoSelfIntersectingRing(e.getEdgeIntersectionList());
       if (validErr != null)
         return;
     }
   }
 
   /**
-   * check that a ring does not self-intersect, except at its endpoints.
+   * Check that a ring does not self-intersect, except at its endpoints.
    * Algorithm is to count the number of times each node along edge occurs.
    * If any occur more than once, that must be a self-intersection.
    */
-  private void checkSelfIntersectingRing(EdgeIntersectionList eiList)
+  private void checkNoSelfIntersectingRing(EdgeIntersectionList eiList)
   {
     Set nodeSet = new TreeSet();
     boolean isFirst = true;
@@ -318,25 +400,14 @@ public class IsValidOp
     }
   }
 
-  /* NO LONGER NEEDED AS OF JTS Ver 1.2
-  private void checkNoRepeatedPoint(Geometry g)
-  {
-    RepeatedPointTester rpt = new RepeatedPointTester();
-    if (rpt.hasRepeatedPoint(g)) {
-      validErr = new TopologyValidationError(
-                        TopologyValidationError.REPEATED_POINT,
-                        rpt.getCoordinate());
-    }
-  }
-  */
-
   /**
    * Tests that each hole is inside the polygon shell.
    * This routine assumes that the holes have previously been tested
-   * to ensure that all vertices lie on the shell or inside it.
-   * A simple test of a single point in the hole can be used,
-   * provide the point is chosen such that it does not lie on the
-   * boundary of the shell.
+   * to ensure that all vertices lie on the shell oon the same side of it
+   * (i.e that the hole rings do not cross the shell ring).
+   * In other words, this test is only correct if the ConsistentArea test is passed first.
+   * Given this, a simple point-in-polygon test of a single point in the hole can be used,
+   * provided the point is chosen such that it does not lie on the shell.
    *
    * @param p the polygon to be tested for hole inclusion
    * @param graph a GeometryGraph incorporating the polygon
@@ -344,7 +415,6 @@ public class IsValidOp
   private void checkHolesInShell(Polygon p, GeometryGraph graph)
   {
     LinearRing shell = (LinearRing) p.getExteriorRing();
-    Coordinate[] shellPts = shell.getCoordinates();
 
     //PointInRing pir = new SimplePointInRing(shell);
     //PointInRing pir = new SIRtreePointInRing(shell);
@@ -354,7 +424,12 @@ public class IsValidOp
 
       LinearRing hole = (LinearRing) p.getInteriorRingN(i);
       Coordinate holePt = findPtNotNode(hole.getCoordinates(), shell, graph);
-      Assert.isTrue(holePt != null, "Unable to find a hole point not a vertex of the shell");
+      /**
+       * If no non-node hole vertex can be found, the hole must
+       * split the polygon into disconnected interiors.
+       * This will be caught by a subsequent check.
+       */
+      if (holePt == null) return;
 
       boolean outside = ! pir.isInside(holePt);
       if ( outside ) {
diff --git a/src/com/vividsolutions/jts/operation/valid/QuadtreeNestedRingTester.java b/src/com/vividsolutions/jts/operation/valid/QuadtreeNestedRingTester.java
index 0e2ff23..98cea53 100644
--- a/src/com/vividsolutions/jts/operation/valid/QuadtreeNestedRingTester.java
+++ b/src/com/vividsolutions/jts/operation/valid/QuadtreeNestedRingTester.java
@@ -45,7 +45,7 @@ import com.vividsolutions.jts.util.*;
  * nested inside another ring in the set, using a {@link Quadtree}
  * index to speed up the comparisons.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class QuadtreeNestedRingTester
 {
diff --git a/src/com/vividsolutions/jts/operation/valid/RepeatedPointTester.java b/src/com/vividsolutions/jts/operation/valid/RepeatedPointTester.java
index f3eec34..8750649 100644
--- a/src/com/vividsolutions/jts/operation/valid/RepeatedPointTester.java
+++ b/src/com/vividsolutions/jts/operation/valid/RepeatedPointTester.java
@@ -41,7 +41,7 @@ import com.vividsolutions.jts.geom.*;
  * (consecutive identical coordinates) as defined in the
  * JTS spec.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class RepeatedPointTester {
 
diff --git a/src/com/vividsolutions/jts/operation/valid/SimpleNestedRingTester.java b/src/com/vividsolutions/jts/operation/valid/SimpleNestedRingTester.java
index 821fc95..023fc33 100644
--- a/src/com/vividsolutions/jts/operation/valid/SimpleNestedRingTester.java
+++ b/src/com/vividsolutions/jts/operation/valid/SimpleNestedRingTester.java
@@ -44,11 +44,10 @@ import com.vividsolutions.jts.util.*;
  * nested inside another ring in the set, using a simple O(n^2)
  * comparison.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class SimpleNestedRingTester
 {
-  private final CGAlgorithms cga = new CGAlgorithms();
 
   private GeometryGraph graph;  // used to find non-node vertices
   private List rings = new ArrayList();
@@ -86,7 +85,7 @@ public class SimpleNestedRingTester
         Assert.isTrue(innerRingPt != null, "Unable to find a ring point not a node of the search ring");
         //Coordinate innerRingPt = innerRingPts[0];
 
-        boolean isInside = cga.isPointInRing(innerRingPt, searchRingPts);
+        boolean isInside = CGAlgorithms.isPointInRing(innerRingPt, searchRingPts);
         if (isInside) {
           nestedPt = innerRingPt;
           return false;
diff --git a/src/com/vividsolutions/jts/operation/valid/SweeplineNestedRingTester.java b/src/com/vividsolutions/jts/operation/valid/SweeplineNestedRingTester.java
index c924e89..d888cb0 100644
--- a/src/com/vividsolutions/jts/operation/valid/SweeplineNestedRingTester.java
+++ b/src/com/vividsolutions/jts/operation/valid/SweeplineNestedRingTester.java
@@ -45,16 +45,14 @@ import com.vividsolutions.jts.util.*;
  * nested inside another ring in the set, using a {@link SweepLineIndex}
  * index to speed up the comparisons.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class SweeplineNestedRingTester
 {
 
-  private final CGAlgorithms cga = new CGAlgorithms();
-
   private GeometryGraph graph;  // used to find non-node vertices
   private List rings = new ArrayList();
-  private Envelope totalEnv = new Envelope();
+  //private Envelope totalEnv = new Envelope();
   private SweepLineIndex sweepLine;
   private Coordinate nestedPt = null;
 
@@ -103,7 +101,7 @@ public class SweeplineNestedRingTester
     Coordinate innerRingPt = IsValidOp.findPtNotNode(innerRingPts, searchRing, graph);
     Assert.isTrue(innerRingPt != null, "Unable to find a ring point not a node of the search ring");
 
-    boolean isInside = cga.isPointInRing(innerRingPt, searchRingPts);
+    boolean isInside = CGAlgorithms.isPointInRing(innerRingPt, searchRingPts);
     if (isInside) {
       nestedPt = innerRingPt;
       return true;
diff --git a/src/com/vividsolutions/jts/operation/valid/TopologyValidationError.java b/src/com/vividsolutions/jts/operation/valid/TopologyValidationError.java
index a25565c..47f0f2c 100644
--- a/src/com/vividsolutions/jts/operation/valid/TopologyValidationError.java
+++ b/src/com/vividsolutions/jts/operation/valid/TopologyValidationError.java
@@ -40,22 +40,78 @@ import com.vividsolutions.jts.geom.Coordinate;
  * Contains information about the nature and location of a {@link Geometry}
  * validation error
  *
- * @version 1.6
+ * @version 1.7
  */
 public class TopologyValidationError {
 
+  /**
+   * Not used
+   * @deprecated
+   */
   public static final int ERROR                   = 0;
+  /**
+   * No longer used - repeated points are considered valid as per the SFS
+   * @deprecated
+   */
   public static final int REPEATED_POINT          = 1;
+
+  /**
+   * Indicates that a hole of a polygon lies partially or completely in the exterior of the shell
+   */
   public static final int HOLE_OUTSIDE_SHELL      = 2;
+
+  /**
+   * Indicates that a hole lies in the interior of another hole in the same polygon
+   */
   public static final int NESTED_HOLES            = 3;
+
+  /**
+   * Indicates that the interior of a polygon is disjoint
+   * (often caused by set of contiguous holes splitting the polygon into two parts)
+   */
   public static final int DISCONNECTED_INTERIOR   = 4;
+
+  /**
+   * Indicates that two rings of a polygonal geometry intersect
+   */
   public static final int SELF_INTERSECTION       = 5;
+
+  /**
+   * Indicates that a ring self-intersects
+   */
   public static final int RING_SELF_INTERSECTION  = 6;
+
+  /**
+   * Indicates that a polygon component of a MultiPolygon lies inside another polygonal component
+   */
   public static final int NESTED_SHELLS           = 7;
+
+  /**
+   * Indicates that a polygonal geometry contains two rings which are identical
+   */
   public static final int DUPLICATE_RINGS         = 8;
+
+  /**
+   * Indicates that either
+   * <ul>
+   * <li>a LineString contains a single point
+   * <li>a LinearRing contains 2 or 3 points
+   * </ul>
+   */
   public static final int TOO_FEW_POINTS          = 9;
+
+  /**
+   * Indicates that the <code>X</code> or <code>Y</code> ordinate of
+   * a Coordinate is not a valid numeric value (e.g. {@link Double.Nan} )
+   */
   public static final int INVALID_COORDINATE      = 10;
 
+  /**
+   * Indicates that a ring is not correctly closed
+   * (the first and the last coordinate are different)
+   */
+  public static final int RING_NOT_CLOSED      = 11;
+
   // these messages must synch up with the indexes above
   private static String[] errMsg = {
     "Topology Validation Error",
@@ -68,31 +124,67 @@ public class TopologyValidationError {
     "Nested shells",
     "Duplicate Rings",
     "Too few points in geometry component",
-    "Invalid Coordinate"
+    "Invalid Coordinate",
+    "Ring is not closed"
   };
 
-
   private int errorType;
   private Coordinate pt;
 
+  /**
+   * Creates a validation error with the given type and location
+   *
+   * @param errorType the type of the error
+   * @param pt the location of the error
+   */
   public TopologyValidationError(int errorType, Coordinate pt)
   {
     this.errorType = errorType;
     this.pt = (Coordinate) pt.clone();
   }
+
+  /**
+   * Creates a validation error of the given type with a null location
+   *
+   * @param errorType the type of the error
+   *
+   */
   public TopologyValidationError(int errorType)
   {
     this(errorType, null);
   }
 
+  /**
+   * Returns the location of this error (on the {@link Geometry} containing the error).
+   *
+   * @return a {@link Coordinate} on the input geometry
+   */
   public Coordinate getCoordinate() { return pt; }
 
+  /**
+   * Gets the type of this error.
+   *
+   * @return the error type
+   */
   public int getErrorType() { return errorType; }
 
+  /**
+   * Gets an error message describing this error.
+   * The error message does not describe the location of the error.
+   *
+   * @return
+   */
   public String getMessage() { return errMsg[errorType]; }
 
+  /**
+   * Gets a message describing the type and location of this error.
+   * @return
+   */
   public String toString()
   {
-    return getMessage() + " at or near point " + pt;
+    String locStr = "";
+    if (pt != null)
+      locStr = " at or near point " + pt;
+    return getMessage() + locStr;
   }
 }
diff --git a/src/com/vividsolutions/jts/planargraph/DirectedEdge.java b/src/com/vividsolutions/jts/planargraph/DirectedEdge.java
index f36a5c2..aaf7c37 100644
--- a/src/com/vividsolutions/jts/planargraph/DirectedEdge.java
+++ b/src/com/vividsolutions/jts/planargraph/DirectedEdge.java
@@ -31,8 +31,6 @@
  *     (250)385-6040
  *     www.vividsolutions.com
  */
-
-
 package com.vividsolutions.jts.planargraph;
 
 import java.util.*;
@@ -48,7 +46,7 @@ import com.vividsolutions.jts.geomgraph.Quadrant;
  * a client using a <code>PlanarGraph</code> will subclass <code>DirectedEdge</code>
  * to add its own application-specific data and methods.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class DirectedEdge
     extends GraphComponent
@@ -83,11 +81,12 @@ public class DirectedEdge
    * <code>to</code> node.
    *
    * @param directionPt
-   *                  specifies this DirectedEdge's direction (given by an imaginary
-   *                  line from the <code>from</code> node to <code>directionPt</code>)
+   *   specifies this DirectedEdge's direction vector
+   *   (determined by the vector from the <code>from</code> node
+   *   to <code>directionPt</code>)
    * @param edgeDirection
-   *                  whether this DirectedEdge's direction is the same as or
-   *                  opposite to that of the parent Edge (if any)
+   *   whether this DirectedEdge's direction is the same as or
+   *   opposite to that of the parent Edge (if any)
    */
   public DirectedEdge(Node from, Node to, Coordinate directionPt, boolean edgeDirection)
   {
@@ -156,6 +155,24 @@ public class DirectedEdge
   public void setSym(DirectedEdge sym) { this.sym = sym; }
 
   /**
+   * Removes this directed edge from its containing graph.
+   */
+  void remove() {
+    this.sym = null;
+    this.parentEdge = null;
+  }
+
+  /**
+   * Tests whether this directed edge has been removed from its containing graph
+   *
+   * @return <code>true</code> if this directed edge is removed
+   */
+  public boolean isRemoved()
+  {
+    return parentEdge == null;
+  }
+
+  /**
    * Returns 1 if this DirectedEdge has a greater angle with the
    * positive x-axis than b", 0 if the DirectedEdges are collinear, and -1 otherwise.
    * <p>
@@ -198,7 +215,7 @@ public class DirectedEdge
     if (quadrant < e.quadrant) return -1;
     // vectors are in the same quadrant - check relative orientation of direction vectors
     // this is > e if it is CCW of e
-    return cga.computeOrientation(e.p0, e.p1, p1);
+    return CGAlgorithms.computeOrientation(e.p0, e.p1, p1);
   }
 
   /**
diff --git a/src/com/vividsolutions/jts/planargraph/DirectedEdgeStar.java b/src/com/vividsolutions/jts/planargraph/DirectedEdgeStar.java
index 924a0e1..5e717ed 100644
--- a/src/com/vividsolutions/jts/planargraph/DirectedEdgeStar.java
+++ b/src/com/vividsolutions/jts/planargraph/DirectedEdgeStar.java
@@ -42,7 +42,7 @@ import com.vividsolutions.jts.geom.Coordinate;
  * A sorted collection of {@link DirectedEdge}s which leave a {@link Node}
  * in a {@link PlanarGraph}.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class DirectedEdgeStar
 {
diff --git a/src/com/vividsolutions/jts/planargraph/Edge.java b/src/com/vividsolutions/jts/planargraph/Edge.java
index eac8adc..e1d5481 100644
--- a/src/com/vividsolutions/jts/planargraph/Edge.java
+++ b/src/com/vividsolutions/jts/planargraph/Edge.java
@@ -41,13 +41,16 @@ package com.vividsolutions.jts.planargraph;
  * Usually a client using a <code>PlanarGraph</code> will subclass <code>Edge</code>
  * to add its own application-specific data and methods.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class Edge
     extends GraphComponent
 {
 
-  /** The two DirectedEdges associated with this Edge */
+  /**
+   * The two DirectedEdges associated with this Edge.
+   * 0 is forward, 1 is reverse
+   */
   protected DirectedEdge[] dirEdge;
 
   /**
@@ -85,7 +88,7 @@ public class Edge
 
   /**
    * Returns one of the DirectedEdges associated with this Edge.
-   * @param i 0 or 1
+   * @param i 0 or 1.  0 returns the forward directed edge, 1 returns the reverse
    */
   public DirectedEdge getDirEdge(int i)
   {
@@ -117,4 +120,22 @@ public class Edge
     // possibly should throw an exception here?
     return null;
   }
+
+  /**
+   * Removes this edge from its containing graph.
+   */
+  void remove() {
+    this.dirEdge = null;
+  }
+
+  /**
+   * Tests whether this edge has been removed from its containing graph
+   *
+   * @return <code>true</code> if this edge is removed
+   */
+  public boolean isRemoved()
+  {
+    return dirEdge == null;
+  }
+
 }
diff --git a/src/com/vividsolutions/jts/planargraph/GraphComponent.java b/src/com/vividsolutions/jts/planargraph/GraphComponent.java
index 28a9209..977c664 100644
--- a/src/com/vividsolutions/jts/planargraph/GraphComponent.java
+++ b/src/com/vividsolutions/jts/planargraph/GraphComponent.java
@@ -33,6 +33,8 @@
  */
 package com.vividsolutions.jts.planargraph;
 
+import java.util.Iterator;
+
 /**
  * The base class for all graph component classes.
  * Maintains flags of use in generic graph algorithms.
@@ -46,12 +48,63 @@ package com.vividsolutions.jts.planargraph;
  * graph might use this to indicate that a node has already been traversed.
  * The visited flag may be set and cleared many times during the lifetime of a graph.
  *
- * @version 1.6
+ * <p>
+ * Graph components support storing user context data.  This will typically be
+ * used by client algorithms which use planar graphs.
+ *
+ * @version 1.7
  */
-public class GraphComponent {
+public abstract class GraphComponent
+{
+  /**
+   * Sets the Visited state for all {@link GraphComponent}s in an {@link Iterator}
+   *
+   * @param i the Iterator to scan
+   * @param visited the state to set the visited flag to
+   */
+  public static void setVisited(Iterator i, boolean visited)
+  {
+    while (i.hasNext()) {
+      GraphComponent comp = (GraphComponent) i.next();
+      comp.setVisited(visited);
+    }
+  }
+
+  /**
+   * Sets the Marked state for all {@link GraphComponent}s in an {@link Iterator}
+   *
+   * @param i the Iterator to scan
+   * @param marked the state to set the Marked flag to
+   */
+  public static void setMarked(Iterator i, boolean marked)
+  {
+    while (i.hasNext()) {
+      GraphComponent comp = (GraphComponent) i.next();
+      comp.setMarked(marked);
+    }
+  }
+
+  /**
+   * Finds the first {@link GraphComponent} in a {@link Iterator} set
+   * which has the specified visited state.
+   *
+   * @param i an Iterator of GraphComponents
+   * @param visitedState the visited state to test
+   * @return the first component found, or <code>null</code> if none found
+   */
+  public static GraphComponent getComponentWithVisitedState(Iterator i, boolean visitedState)
+  {
+    while (i.hasNext()) {
+      GraphComponent comp = (GraphComponent) i.next();
+      if (comp.isVisited() == visitedState)
+        return comp;
+    }
+    return null;
+  }
 
   protected boolean isMarked = false;
   protected boolean isVisited = false;
+  private Object data;
 
   public GraphComponent() {
   }
@@ -81,4 +134,38 @@ public class GraphComponent {
    */
   public void setMarked(boolean isMarked) { this.isMarked = isMarked; }
 
+  /**
+   * Sets the user-defined data for this component.
+   *
+   * @param data an Object containing user-defined data
+   */
+  public void setContext(Object data) { this.data = data; }
+
+  /**
+   * Gets the user-defined data for this component.
+   *
+   * @return the user-defined data
+   */
+  public Object getContext() { return data; }
+
+  /**
+   * Sets the user-defined data for this component.
+   *
+   * @param data an Object containing user-defined data
+   */
+  public void setData(Object data) { this.data = data; }
+
+  /**
+   * Gets the user-defined data for this component.
+   *
+   * @return the user-defined data
+   */
+  public Object getData() { return data; }
+
+  /**
+   * Tests whether this component has been removed from its containing graph
+   *
+   * @return <code>true</code> if this component is removed
+   */
+  public abstract boolean isRemoved();
 }
diff --git a/src/com/vividsolutions/jts/planargraph/Node.java b/src/com/vividsolutions/jts/planargraph/Node.java
index 2f5a5f0..38ba3f4 100644
--- a/src/com/vividsolutions/jts/planargraph/Node.java
+++ b/src/com/vividsolutions/jts/planargraph/Node.java
@@ -45,7 +45,7 @@ import com.vividsolutions.jts.geom.Coordinate;
  * subclass <code>Node</code> to add their own application-specific
  * data and methods.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class Node
     extends GraphComponent
@@ -115,4 +115,22 @@ public class Node
     return deStar.getIndex(edge);
   }
 
+  /**
+   * Removes this node from its containing graph.
+   */
+  void remove() {
+    pt = null;
+  }
+
+
+  /**
+   * Tests whether this node has been removed from its containing graph
+   *
+   * @return <code>true</code> if this node is removed
+   */
+  public boolean isRemoved()
+  {
+    return pt == null;
+  }
+
 }
diff --git a/src/com/vividsolutions/jts/planargraph/NodeMap.java b/src/com/vividsolutions/jts/planargraph/NodeMap.java
index ed904e4..e465699 100644
--- a/src/com/vividsolutions/jts/planargraph/NodeMap.java
+++ b/src/com/vividsolutions/jts/planargraph/NodeMap.java
@@ -44,7 +44,7 @@ import com.vividsolutions.jts.geom.Coordinate;
 /**
  * A map of {@link Node}s, indexed by the coordinate of the node.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class NodeMap
 
diff --git a/src/com/vividsolutions/jts/planargraph/PlanarGraph.java b/src/com/vividsolutions/jts/planargraph/PlanarGraph.java
index cb7a8f8..6afa24e 100644
--- a/src/com/vividsolutions/jts/planargraph/PlanarGraph.java
+++ b/src/com/vividsolutions/jts/planargraph/PlanarGraph.java
@@ -48,23 +48,28 @@ import com.vividsolutions.jts.geom.Coordinate;
  * subclasses for one or more graph components, which hold application-specific
  * data and graph algorithms.
  *
- * @version 1.6
+ * @version 1.7
  */
 public abstract class PlanarGraph
 {
-
-
-  protected List edges = new ArrayList();
-  protected List dirEdges = new ArrayList();
+  protected Set edges = new HashSet();
+  protected Set dirEdges = new HashSet();
   protected NodeMap nodeMap = new NodeMap();
+
   /**
-   * Constructs a PlanarGraph without any Edges, DirectedEdges, or Nodes.
+   * Constructs a empty graph.
    */
   public PlanarGraph()
   {
   }
+
   /**
-   * Returns the Node at the given location, or null if no Node was there.
+   * Returns the {@link Node} at the given location,
+   * or null if no {@link Node} was there.
+   *
+   * @param pt the location to query
+   * @return the node found
+   * @return <code>null</code> if this graph contains no node at the location
    */
   public Node findNode(Coordinate pt)
   {
@@ -107,7 +112,29 @@ public abstract class PlanarGraph
   public Iterator nodeIterator()  {    return nodeMap.iterator();  }
   /**
    * Returns the Nodes in this PlanarGraph.
-   */  
+   */
+
+  /**
+   * Tests whether this graph contains the given {@link Edge}
+   *
+   * @param de the edge to query
+   * @return <code>true</code> if the graph contains the edge
+   */
+  public boolean contains(Edge e)
+  {
+    return edges.contains(e);
+  }
+
+  /**
+   * Tests whether this graph contains the given {@link DirectedEdge}
+   *
+   * @param de the directed edge to query
+   * @return <code>true</code> if the graph contains the directed edge
+   */
+  public boolean contains(DirectedEdge de)
+  {
+    return dirEdges.contains(de);
+  }
 
   public Collection getNodes()  {    return nodeMap.values();  }
 
@@ -126,27 +153,30 @@ public abstract class PlanarGraph
    * @see #add(Edge)
    */
   public Iterator edgeIterator()  {    return edges.iterator();  }
+
   /**
    * Returns the Edges that have been added to this PlanarGraph
    * @see #add(Edge)
    */
-  public List getEdges()  {    return edges;  }
+  public Collection getEdges()  {    return edges;  }
 
   /**
-   * Removes an Edge and its associated DirectedEdges from their from-Nodes and
-   * from this PlanarGraph. Note: This method does not remove the Nodes associated
-   * with the Edge, even if the removal of the Edge reduces the degree of a
-   * Node to zero.
+   * Removes an {@link Edge} and its associated {@link DirectedEdge}s
+   * from their from-Nodes and from the graph.
+   * Note: This method does not remove the {@link Node}s associated
+   * with the {@link Edge}, even if the removal of the {@link Edge}
+   * reduces the degree of a {@link Node} to zero.
    */
   public void remove(Edge edge)
   {
     remove(edge.getDirEdge(0));
     remove(edge.getDirEdge(1));
     edges.remove(edge);
+    edge.remove();
   }
 
   /**
-   * Removes DirectedEdge from its from-Node and from this PlanarGraph. Note:
+   * Removes DirectedEdge from its from-Node and from this PlanarGraph.
    * This method does not remove the Nodes associated with the DirectedEdge,
    * even if the removal of the DirectedEdge reduces the degree of a Node to
    * zero.
@@ -156,8 +186,10 @@ public abstract class PlanarGraph
     DirectedEdge sym = de.getSym();
     if (sym != null) sym.setSym(null);
     de.getFromNode().getOutEdges().remove(de);
+    de.remove();
     dirEdges.remove(de);
   }
+
   /**
    * Removes a node from the graph, along with any associated DirectedEdges and
    * Edges.
@@ -182,7 +214,7 @@ public abstract class PlanarGraph
     }
     // remove the node from the graph
     nodeMap.remove(node.getCoordinate());
-    //nodes.remove(node);
+    node.remove();
   }
 
   /**
diff --git a/src/com/vividsolutions/jts/planargraph/Subgraph.java b/src/com/vividsolutions/jts/planargraph/Subgraph.java
new file mode 100644
index 0000000..51ca21d
--- /dev/null
+++ b/src/com/vividsolutions/jts/planargraph/Subgraph.java
@@ -0,0 +1,91 @@
+package com.vividsolutions.jts.planargraph;
+
+import java.util.*;
+
+/**
+ * A subgraph of a {@link PlanarGraph}.
+ * A subgraph may contain any subset of {@link Edges}
+ * from the parent graph.
+ * It will also automatically contain all {@link DirectedEdge}s
+ * and {@link Node}s associated with those edges.
+ * No new objects are created when edges are added -
+ * all associated components must already exist in the parent graph.
+ */
+public class Subgraph
+{
+  protected PlanarGraph parentGraph;
+  protected Set edges = new HashSet();
+  protected List dirEdges = new ArrayList();
+  protected NodeMap nodeMap = new NodeMap();
+
+  /**
+   * Creates a new subgraph of the given {@link PlanarGraph}
+   *
+   * @param parentGraph the parent graph
+   */
+  public Subgraph(PlanarGraph parentGraph) {
+    this.parentGraph = parentGraph;
+  }
+
+  /**
+   * Gets the {@link PlanarGraph} which this subgraph
+   * is part of.
+   *
+   * @return the parent PlanarGraph
+   */
+  public PlanarGraph getParent()
+  {
+    return parentGraph;
+  }
+  /**
+   * Adds an {@link Edge} to the subgraph.
+   * The associated {@link DirectedEdge}s and {@link Node}s
+   * are also added.
+   *
+   * @param e the edge to add
+   */
+  public void add(Edge e)
+  {
+    if (edges.contains(e)) return;
+
+    edges.add(e);
+    dirEdges.add(e.getDirEdge(0));
+    dirEdges.add(e.getDirEdge(1));
+    nodeMap.add(e.getDirEdge(0).getFromNode());
+    nodeMap.add(e.getDirEdge(1).getFromNode());
+  }
+
+  /**
+   * Returns an {@link Iterator} over the {@link DirectedEdge}s in this graph,
+   * in the order in which they were added.
+   *
+   * @return an iterator over the directed edges
+   *
+   * @see #add(Edge)
+   */
+  public Iterator dirEdgeIterator()  {    return dirEdges.iterator();  }
+
+  /**
+   * Returns an {@link Iterator} over the {@link Edge}s in this graph,
+   * in the order in which they were added.
+   *
+   * @return an iterator over the edges
+   *
+   * @see #add(Edge)
+   */
+  public Iterator edgeIterator()  {    return edges.iterator();  }
+
+  /**
+   * Returns an {@link Iterator} over the {@link Nodes} in this graph.
+   * @return an iterator over the nodes
+   */
+  public Iterator nodeIterator()  {    return nodeMap.iterator();  }
+
+  /**
+   * Tests whether an {@link Edge} is contained in this subgraph
+   * @param e the edge to test
+   * @return <code>true</code> if the edge is contained in this subgraph
+   */
+  public boolean contains(Edge e) { return edges.contains(e); }
+
+}
\ No newline at end of file
diff --git a/src/com/vividsolutions/jts/planargraph/algorithm/ConnectedSubgraphFinder.java b/src/com/vividsolutions/jts/planargraph/algorithm/ConnectedSubgraphFinder.java
new file mode 100644
index 0000000..90630cd
--- /dev/null
+++ b/src/com/vividsolutions/jts/planargraph/algorithm/ConnectedSubgraphFinder.java
@@ -0,0 +1,74 @@
+package com.vividsolutions.jts.planargraph.algorithm;
+
+import java.util.*;
+import com.vividsolutions.jts.planargraph.*;
+
+/**
+ * Finds all connected {@link Subgraph}s of a {@link PlanarGraph}.
+ * <p>
+ * <b>Note:</b> uses the <code>isVisited</code> flag on the nodes.
+ */
+public class ConnectedSubgraphFinder
+{
+
+  private PlanarGraph graph;
+
+  public ConnectedSubgraphFinder(PlanarGraph graph) {
+    this.graph = graph;
+  }
+
+  public List getConnectedSubgraphs()
+  {
+    List subgraphs = new ArrayList();
+
+    GraphComponent.setVisited(graph.nodeIterator(), false);
+    for (Iterator i = graph.edgeIterator(); i.hasNext(); ) {
+      Edge e = (Edge) i.next();
+      Node node = e.getDirEdge(0).getFromNode();
+      if (! node.isVisited()) {
+        subgraphs.add(findSubgraph(node));
+      }
+    }
+    return subgraphs;
+  }
+
+  private Subgraph findSubgraph(Node node)
+  {
+    Subgraph subgraph = new Subgraph(graph);
+    addReachable(node, subgraph);
+    return subgraph;
+  }
+
+  /**
+   * Adds all nodes and edges reachable from this node to the subgraph.
+   * Uses an explicit stack to avoid a large depth of recursion.
+   *
+   * @param node a node known to be in the subgraph
+   */
+  private void addReachable(Node startNode, Subgraph subgraph)
+  {
+    Stack nodeStack = new Stack();
+    nodeStack.add(startNode);
+    while (! nodeStack.empty()) {
+      Node node = (Node) nodeStack.pop();
+      addEdges(node, nodeStack, subgraph);
+    }
+  }
+
+  /**
+   * Adds the argument node and all its out edges to the subgraph.
+   * @param node the node to add
+   * @param nodeStack the current set of nodes being traversed
+   */
+  private void addEdges(Node node, Stack nodeStack, Subgraph subgraph)
+  {
+    node.setVisited(true);
+    for (Iterator i = ((DirectedEdgeStar) node.getOutEdges()).iterator(); i.hasNext(); ) {
+      DirectedEdge de = (DirectedEdge) i.next();
+      subgraph.add(de.getEdge());
+      Node toNode = de.getToNode();
+      if (! toNode.isVisited()) nodeStack.push(toNode);
+    }
+  }
+
+}
\ No newline at end of file
diff --git a/src/com/vividsolutions/jts/precision/CommonBits.java b/src/com/vividsolutions/jts/precision/CommonBits.java
index d429799..b4a6815 100644
--- a/src/com/vividsolutions/jts/precision/CommonBits.java
+++ b/src/com/vividsolutions/jts/precision/CommonBits.java
@@ -40,7 +40,7 @@ package com.vividsolutions.jts.precision;
  * is represented by the common bits.
  * If there are no common bits, the number computed is 0.0.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class CommonBits {
 
diff --git a/src/com/vividsolutions/jts/precision/CommonBitsOp.java b/src/com/vividsolutions/jts/precision/CommonBitsOp.java
index e5562ad..22036d4 100644
--- a/src/com/vividsolutions/jts/precision/CommonBitsOp.java
+++ b/src/com/vividsolutions/jts/precision/CommonBitsOp.java
@@ -44,7 +44,7 @@ import com.vividsolutions.jts.geom.*;
  * that the returned Geometry is invalid.
  * Client classes should check the validity of the returned result themselves.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class CommonBitsOp {
 
diff --git a/src/com/vividsolutions/jts/precision/CommonBitsRemover.java b/src/com/vividsolutions/jts/precision/CommonBitsRemover.java
index 3268c69..bbdf766 100644
--- a/src/com/vividsolutions/jts/precision/CommonBitsRemover.java
+++ b/src/com/vividsolutions/jts/precision/CommonBitsRemover.java
@@ -38,7 +38,7 @@ import com.vividsolutions.jts.geom.*;
 /**
  * Allow computing and removing common mantissa bits from one or more Geometries.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class CommonBitsRemover
 {
diff --git a/src/com/vividsolutions/jts/precision/EnhancedPrecisionOp.java b/src/com/vividsolutions/jts/precision/EnhancedPrecisionOp.java
index d7abe5e..8589c0b 100644
--- a/src/com/vividsolutions/jts/precision/EnhancedPrecisionOp.java
+++ b/src/com/vividsolutions/jts/precision/EnhancedPrecisionOp.java
@@ -40,7 +40,7 @@ import com.vividsolutions.jts.operation.overlay.OverlayOp;
   * Provides versions of Geometry spatial functions which use
   * enhanced precision techniques to reduce the likelihood of robustness problems.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class EnhancedPrecisionOp
 {
diff --git a/src/com/vividsolutions/jts/precision/SimpleGeometryPrecisionReducer.java b/src/com/vividsolutions/jts/precision/SimpleGeometryPrecisionReducer.java
index aa6f416..e02cf8e 100644
--- a/src/com/vividsolutions/jts/precision/SimpleGeometryPrecisionReducer.java
+++ b/src/com/vividsolutions/jts/precision/SimpleGeometryPrecisionReducer.java
@@ -48,7 +48,7 @@ import com.vividsolutions.jts.geom.util.*;
  * is simplifying the input to the buffer algorithm.
  * The buffer algorithm does not depend on the validity of the input geometry.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class SimpleGeometryPrecisionReducer
 {
@@ -65,6 +65,7 @@ public class SimpleGeometryPrecisionReducer
    * Sets whether the reduction will result in collapsed components
    * being removed completely, or simply being collapsed to an (invalid)
    * Geometry of the same type.
+   * The default is to remove collapsed components.
    *
    * @param removeCollapsed if <code>true</code> collapsed components will be removed
    */
diff --git a/src/com/vividsolutions/jts/simplify/DouglasPeuckerLineSimplifier.java b/src/com/vividsolutions/jts/simplify/DouglasPeuckerLineSimplifier.java
index d845fcd..7c1cc30 100644
--- a/src/com/vividsolutions/jts/simplify/DouglasPeuckerLineSimplifier.java
+++ b/src/com/vividsolutions/jts/simplify/DouglasPeuckerLineSimplifier.java
@@ -7,7 +7,7 @@ import com.vividsolutions.jts.geom.*;
  * Simplifies a linestring (sequence of points) using
  * the standard Douglas-Peucker algorithm.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class DouglasPeuckerLineSimplifier
 {
diff --git a/src/com/vividsolutions/jts/simplify/DouglasPeuckerSimplifier.java b/src/com/vividsolutions/jts/simplify/DouglasPeuckerSimplifier.java
index 5672247..01feb54 100644
--- a/src/com/vividsolutions/jts/simplify/DouglasPeuckerSimplifier.java
+++ b/src/com/vividsolutions/jts/simplify/DouglasPeuckerSimplifier.java
@@ -16,7 +16,7 @@ import com.vividsolutions.jts.geom.util.*;
  * To simplify geometry while preserving topology use {@link TopologyPreservingSimplifier}.
  * (However, using D-P is significantly faster).
  *
- * @version 1.6
+ * @version 1.7
  */
 public class DouglasPeuckerSimplifier
 {
@@ -40,10 +40,14 @@ public class DouglasPeuckerSimplifier
    * Sets the distance tolerance for the simplification.
    * All vertices in the simplified geometry will be within this
    * distance of the original geometry.
+   * The tolerance value must be non-negative.  A tolerance value
+   * of zero is effectively a no-op.
    *
    * @param distanceTolerance the approximation tolerance to use
    */
   public void setDistanceTolerance(double distanceTolerance) {
+    if (distanceTolerance < 0.0)
+      throw new IllegalArgumentException("Tolerance must be non-negative");
     this.distanceTolerance = distanceTolerance;
   }
 
diff --git a/src/com/vividsolutions/jts/simplify/TaggedLineString.java b/src/com/vividsolutions/jts/simplify/TaggedLineString.java
index e4caf59..d45a9f2 100644
--- a/src/com/vividsolutions/jts/simplify/TaggedLineString.java
+++ b/src/com/vividsolutions/jts/simplify/TaggedLineString.java
@@ -4,7 +4,7 @@ import java.util.*;
 import com.vividsolutions.jts.geom.*;
 
 /**
- * @version 1.6
+ * @version 1.7
  */
 public class TaggedLineString {
 
@@ -77,4 +77,4 @@ public class TaggedLineString {
   }
 
 
-}
+}
\ No newline at end of file
diff --git a/src/com/vividsolutions/jts/simplify/TaggedLineStringSimplifier.java b/src/com/vividsolutions/jts/simplify/TaggedLineStringSimplifier.java
index b1dbde5..6228485 100644
--- a/src/com/vividsolutions/jts/simplify/TaggedLineStringSimplifier.java
+++ b/src/com/vividsolutions/jts/simplify/TaggedLineStringSimplifier.java
@@ -10,7 +10,7 @@ import com.vividsolutions.jts.util.Debug;
  * (in the sense that no new intersections are introduced).
  * Uses the recursive Douglas-Peucker algorithm.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class TaggedLineStringSimplifier
 {
@@ -209,4 +209,4 @@ public class TaggedLineStringSimplifier
       inputIndex.remove(seg);
     }
   }
-}
+}
\ No newline at end of file
diff --git a/src/com/vividsolutions/jts/simplify/TopologyPreservingSimplifier.java b/src/com/vividsolutions/jts/simplify/TopologyPreservingSimplifier.java
index 3a01f35..6edbec1 100644
--- a/src/com/vividsolutions/jts/simplify/TopologyPreservingSimplifier.java
+++ b/src/com/vividsolutions/jts/simplify/TopologyPreservingSimplifier.java
@@ -46,10 +46,14 @@ public class TopologyPreservingSimplifier
    * Sets the distance tolerance for the simplification.
    * All vertices in the simplified geometry will be within this
    * distance of the original geometry.
+   * The tolerance value must be non-negative.  A tolerance value
+   * of zero is effectively a no-op.
    *
    * @param distanceTolerance the approximation tolerance to use
    */
   public void setDistanceTolerance(double distanceTolerance) {
+    if (distanceTolerance < 0.0)
+      throw new IllegalArgumentException("Tolerance must be non-negative");
     lineSimplifier.setDistanceTolerance(distanceTolerance);
   }
 
diff --git a/src/com/vividsolutions/jts/util/Assert.java b/src/com/vividsolutions/jts/util/Assert.java
index 88034ac..ee1a97e 100644
--- a/src/com/vividsolutions/jts/util/Assert.java
+++ b/src/com/vividsolutions/jts/util/Assert.java
@@ -39,7 +39,7 @@ import com.vividsolutions.jts.util.AssertionFailedException;
 /**
  *  A utility for making programming assertions.
  *
- *@version 1.6
+ *@version 1.7
  */
 public class Assert {
 
diff --git a/src/com/vividsolutions/jts/util/AssertionFailedException.java b/src/com/vividsolutions/jts/util/AssertionFailedException.java
index a4f5e96..ddca70c 100644
--- a/src/com/vividsolutions/jts/util/AssertionFailedException.java
+++ b/src/com/vividsolutions/jts/util/AssertionFailedException.java
@@ -38,7 +38,7 @@ package com.vividsolutions.jts.util;
  *  Thrown when the application is in an inconsistent state. Indicates a problem
  *  with the code.
  *
- *@version 1.6
+ *@version 1.7
  */
 public class AssertionFailedException extends RuntimeException {
 
diff --git a/src/com/vividsolutions/jts/util/CollectionUtil.java b/src/com/vividsolutions/jts/util/CollectionUtil.java
new file mode 100644
index 0000000..96273cf
--- /dev/null
+++ b/src/com/vividsolutions/jts/util/CollectionUtil.java
@@ -0,0 +1,65 @@
+package com.vividsolutions.jts.util;
+
+import java.util.*;
+
+/**
+ * Executes a transformation function on each element of a collection
+ * and returns the results in a new List.
+ *
+ * @version 1.7
+ */
+public class CollectionUtil {
+
+  public interface Function {
+    Object execute(Object obj);
+  }
+
+  /**
+   * Executes a function on each item in a {@link Collection}
+   * and returns the results in a new {@link List}
+   *
+   * @param coll
+   * @param func the Function to execute
+   */
+  public static List transform(Collection coll, Function func)
+  {
+    List result = new ArrayList();
+    for (Iterator i = coll.iterator(); i.hasNext(); ) {
+      result.add(func.execute(i.next()));
+    }
+    return result;
+  }
+
+  /**
+   * Executes a function on each item in a Collection but does
+   * not accumulate the result
+   *
+   * @param coll
+   * @param func the Function to execute
+   */
+  public static void apply(Collection coll, Function func)
+  {
+    for (Iterator i = coll.iterator(); i.hasNext(); ) {
+      func.execute(i.next());
+    }
+  }
+
+  /**
+   * Executes a function on each item in a Collection
+   * and collects all the entries for which the result
+   * of the function is equal to {@link Boolean}.TRUE.
+   *
+   * @param coll
+   * @param func the Function to execute
+   */
+  public static List select(Collection collection, Function func) {
+    List result = new ArrayList();
+    for (Iterator i = collection.iterator(); i.hasNext();) {
+      Object item = i.next();
+      if (Boolean.TRUE.equals(func.execute(item))) {
+        result.add(item);
+      }
+    }
+    return result;
+  }
+}
\ No newline at end of file
diff --git a/src/com/vividsolutions/jts/util/CoordinateArrayFilter.java b/src/com/vividsolutions/jts/util/CoordinateArrayFilter.java
index c0e4a50..0f319f0 100644
--- a/src/com/vividsolutions/jts/util/CoordinateArrayFilter.java
+++ b/src/com/vividsolutions/jts/util/CoordinateArrayFilter.java
@@ -40,7 +40,7 @@ import com.vividsolutions.jts.geom.*;
  *  A {@link CoordinateFilter} that creates an array containing every
  *  coordinate in a {@link Geometry}.
  *
- *@version 1.6
+ *@version 1.7
  */
 public class CoordinateArrayFilter implements CoordinateFilter {
   Coordinate[] pts = null;
diff --git a/src/com/vividsolutions/jts/util/CoordinateCountFilter.java b/src/com/vividsolutions/jts/util/CoordinateCountFilter.java
index 30e9c59..8859581 100644
--- a/src/com/vividsolutions/jts/util/CoordinateCountFilter.java
+++ b/src/com/vividsolutions/jts/util/CoordinateCountFilter.java
@@ -40,7 +40,7 @@ import com.vividsolutions.jts.geom.*;
  *  A {@link CoordinateFilter} that counts the total number of coordinates
  *  in a <code>Geometry</code>.
  *
- *@version 1.6
+ *@version 1.7
  */
 public class CoordinateCountFilter implements CoordinateFilter {
   private int n = 0;
diff --git a/src/com/vividsolutions/jts/util/Debug.java b/src/com/vividsolutions/jts/util/Debug.java
index 657cdc9..aad3fc0 100644
--- a/src/com/vividsolutions/jts/util/Debug.java
+++ b/src/com/vividsolutions/jts/util/Debug.java
@@ -35,22 +35,30 @@
 package com.vividsolutions.jts.util;
 
 /**
- *@version 1.6
+ *@version 1.7
  */
 import java.io.*;
 import java.util.*;
 import java.lang.reflect.*;
+import com.vividsolutions.jts.geom.*;
 
 /**
  * Provides routines to simplify and localize debugging output.
+ * Debugging is controlled via a Java system property value.
+ * If the system property with the name given in
+ * DEBUG_PROPERTY_NAME (currently "jts.debug") has the value
+ * "on" or "true" debugging is enabled.
+ * Otherwise, debugging is disabled.
+ * The system property can be set by adding an option '-Djts_debug=on'
+ * to the Java VM commandline.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class Debug {
 
-  private static String DEBUG_PROPERTY_NAME = "debug";
-  private static String DEBUG_PROPERTY_VALUE_ON = "on";
-  private static String DEBUG_PROPERTY_VALUE_TRUE = "true";
+  public static String DEBUG_PROPERTY_NAME = "jts.debug";
+  public static String DEBUG_PROPERTY_VALUE_ON = "on";
+  public static String DEBUG_PROPERTY_VALUE_TRUE = "true";
 
   private static boolean debugOn = false;
 
@@ -64,13 +72,19 @@ public class Debug {
   }
 
 
+  /**
+   * Prints the status of debugging to <tt>System.out</tt>
+   *
+   * @param args the cmd-line arguments (no arguments are required)
+   */
   public static void main(String[] args)
   {
-    Debug.println("Debugging is ON");
+    System.out.println("JTS Debugging is " +
+                       (debugOn ? "ON" : "OFF") );
   }
 
-  private static Debug debug = new Debug();
-
+  private static final Debug debug = new Debug();
+  private static final GeometryFactory fact = new GeometryFactory();
   private static final String DEBUG_LINE_TAG = "D! ";
 
   private PrintStream out;
@@ -80,6 +94,18 @@ public class Debug {
 
   public static boolean isDebugging() { return debugOn; }
 
+  public static LineString toLine(Coordinate p0, Coordinate p1) {
+    return fact.createLineString(new Coordinate[] { p0, p1 });
+  }
+
+  public static LineString toLine(Coordinate p0, Coordinate p1, Coordinate p2) {
+    return fact.createLineString(new Coordinate[] { p0, p1, p2});
+  }
+
+  public static LineString toLine(Coordinate p0, Coordinate p1, Coordinate p2, Coordinate p3) {
+    return fact.createLineString(new Coordinate[] { p0, p1, p2, p3});
+  }
+
   public static void print(String str) {
     if (!debugOn) {
       return;
diff --git a/src/com/vividsolutions/jts/util/GeometricShapeFactory.java b/src/com/vividsolutions/jts/util/GeometricShapeFactory.java
index 6c98a59..940b03c 100644
--- a/src/com/vividsolutions/jts/util/GeometricShapeFactory.java
+++ b/src/com/vividsolutions/jts/util/GeometricShapeFactory.java
@@ -42,8 +42,17 @@ import com.vividsolutions.jts.geom.*;
  * Computes various kinds of common geometric shapes.
  * Allows various ways of specifying the location and extent of the shapes,
  * as well as number of line segments used to form them.
+ * <p>
+ * Example:
+ * <pre>
+ *  GeometricShapeFactory gsf = new GeometricShapeFactory();
+ *  gsf.setSize(100);
+ *  gsf.setNumPoints(100);
+ *  gsf.setBase(new Coordinate(0, 0));
+ *  Polygon rect = gsf.createRectangle();
+ * </pre>
  *
- * @version 1.6
+ * @version 1.7
  */
 public class GeometricShapeFactory
 {
@@ -88,7 +97,9 @@ public class GeometricShapeFactory
   public void setCentre(Coordinate centre)  {  dim.setCentre(centre);    }
 
   /**
-   * Sets the total number of points in the created Geometry
+   * Sets the total number of points in the created {@link Geometry}.
+   * The created geometry will have no more than this number of points,
+   * unless more are needed to create a valid geometry.
    */
   public void setNumPoints(int nPts) { this.nPts = nPts; }
 
diff --git a/src/com/vividsolutions/jts/util/Stopwatch.java b/src/com/vividsolutions/jts/util/Stopwatch.java
index 43edefc..a4de7f2 100644
--- a/src/com/vividsolutions/jts/util/Stopwatch.java
+++ b/src/com/vividsolutions/jts/util/Stopwatch.java
@@ -37,26 +37,59 @@ package com.vividsolutions.jts.util;
  * Implements a timer function which can compute
  * elapsed time as well as split times.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class Stopwatch {
 
-  private long startTime;
+  private long startTimestamp;
+  private long totalTime = 0;
+  private boolean isRunning = false;
 
   public Stopwatch()
   {
-    startTime = System.currentTimeMillis();
+    start();
   }
 
   public void start()
   {
-    startTime = System.currentTimeMillis();
+    if (isRunning) return;
+    startTimestamp = System.currentTimeMillis();
+    isRunning = true;
+  }
+
+  public long stop()
+  {
+    if (isRunning) {
+      updateTotalTime();
+      isRunning = false;
+    }
+    return totalTime;
+  }
+
+  public void reset()
+  {
+    totalTime = 0;
+    startTimestamp = System.currentTimeMillis();
+  }
+
+  public long split()
+  {
+    if (isRunning)
+      updateTotalTime();
+    return totalTime;
+  }
+
+  private void updateTotalTime()
+  {
+    long endTimestamp = System.currentTimeMillis();
+    long elapsedTime = endTimestamp - startTimestamp;
+    startTimestamp = endTimestamp;
+    totalTime += elapsedTime;
   }
 
   public long getTime()
   {
-    long endTime = System.currentTimeMillis();
-    long totalTime = endTime - startTime;
+    updateTotalTime();
     return totalTime;
   }
 
diff --git a/src/com/vividsolutions/jts/util/UniqueCoordinateArrayFilter.java b/src/com/vividsolutions/jts/util/UniqueCoordinateArrayFilter.java
index 526c8a2..993229e 100644
--- a/src/com/vividsolutions/jts/util/UniqueCoordinateArrayFilter.java
+++ b/src/com/vividsolutions/jts/util/UniqueCoordinateArrayFilter.java
@@ -44,7 +44,7 @@ import com.vividsolutions.jts.geom.CoordinateFilter;
  *  A {@link CoordinateFilter} that builds a set of <code>Coordinate</code>s.
  *  The set of coordinates contains no duplicate points.
  *
- *@version 1.6
+ *@version 1.7
  */
 public class UniqueCoordinateArrayFilter implements CoordinateFilter {
   TreeSet treeSet = new TreeSet();
diff --git a/src/com/vividsolutions/jtsexample/geom/BasicExample.java b/src/com/vividsolutions/jtsexample/geom/BasicExample.java
index 403511e..16e2c61 100644
--- a/src/com/vividsolutions/jtsexample/geom/BasicExample.java
+++ b/src/com/vividsolutions/jtsexample/geom/BasicExample.java
@@ -41,7 +41,7 @@ import com.vividsolutions.jts.io.WKTReader;
 /**
  * Shows basic ways of creating and operating on geometries
  *
- * @version 1.6
+ * @version 1.7
  */
 public class BasicExample
 {
diff --git a/src/com/vividsolutions/jtsexample/geom/ConstructionExample.java b/src/com/vividsolutions/jtsexample/geom/ConstructionExample.java
index 931a396..be52c8b 100644
--- a/src/com/vividsolutions/jtsexample/geom/ConstructionExample.java
+++ b/src/com/vividsolutions/jtsexample/geom/ConstructionExample.java
@@ -16,7 +16,7 @@ import com.vividsolutions.jts.geom.*;
  * <li>Insulates your code from changes in the signature of JTS constructors
  * </ol>
  *
- * @version 1.6
+ * @version 1.7
  */
 public class ConstructionExample
 {
@@ -37,4 +37,4 @@ public class ConstructionExample
     System.out.println(mpt);
 
   }
-}
+}
\ No newline at end of file
diff --git a/src/com/vividsolutions/jtsexample/geom/ExtendedCoordinate.java b/src/com/vividsolutions/jtsexample/geom/ExtendedCoordinate.java
index bf7d747..5226efc 100644
--- a/src/com/vividsolutions/jtsexample/geom/ExtendedCoordinate.java
+++ b/src/com/vividsolutions/jtsexample/geom/ExtendedCoordinate.java
@@ -37,7 +37,7 @@ import com.vividsolutions.jts.geom.*;
 
 
 /**
- * @version 1.6
+ * @version 1.7
  */
 public class ExtendedCoordinate
     extends Coordinate
diff --git a/src/com/vividsolutions/jtsexample/geom/ExtendedCoordinateExample.java b/src/com/vividsolutions/jtsexample/geom/ExtendedCoordinateExample.java
index 5772b55..c1323f6 100644
--- a/src/com/vividsolutions/jtsexample/geom/ExtendedCoordinateExample.java
+++ b/src/com/vividsolutions/jtsexample/geom/ExtendedCoordinateExample.java
@@ -37,7 +37,7 @@ import com.vividsolutions.jts.geom.*;
 
 
 /**
- * @version 1.6
+ * @version 1.7
  */
 public class ExtendedCoordinateExample
 {
diff --git a/src/com/vividsolutions/jtsexample/geom/ExtendedCoordinateSequence.java b/src/com/vividsolutions/jtsexample/geom/ExtendedCoordinateSequence.java
index ca2ae11..eb70d6d 100644
--- a/src/com/vividsolutions/jtsexample/geom/ExtendedCoordinateSequence.java
+++ b/src/com/vividsolutions/jtsexample/geom/ExtendedCoordinateSequence.java
@@ -42,7 +42,7 @@ import com.vividsolutions.jts.geom.*;
  * that change them are actually changing the ExtendedCoordinateSequence's
  * underlying data.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class ExtendedCoordinateSequence
     implements CoordinateSequence
@@ -103,6 +103,10 @@ public class ExtendedCoordinateSequence
     }
   }
 
+  /**
+   * @see com.vividsolutions.jts.geom.CoordinateSequence#getDimension()
+   */
+  public int getDimension() { return 4; }
 
   public Coordinate getCoordinate(int i) {
     return coordinates[i];
diff --git a/src/com/vividsolutions/jtsexample/geom/ExtendedCoordinateSequenceFactory.java b/src/com/vividsolutions/jtsexample/geom/ExtendedCoordinateSequenceFactory.java
index e7e9012..9bef816 100644
--- a/src/com/vividsolutions/jtsexample/geom/ExtendedCoordinateSequenceFactory.java
+++ b/src/com/vividsolutions/jtsexample/geom/ExtendedCoordinateSequenceFactory.java
@@ -39,7 +39,7 @@ import com.vividsolutions.jts.geom.*;
  * Creates ExtendedCoordinateSequenceFactory internally represented
  * as an array of {@link ExtendedCoordinate}s.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class ExtendedCoordinateSequenceFactory
     implements CoordinateSequenceFactory
diff --git a/src/com/vividsolutions/jtsexample/geom/PrecisionModelExample.java b/src/com/vividsolutions/jtsexample/geom/PrecisionModelExample.java
index 6418ec8..ddc9dc9 100644
--- a/src/com/vividsolutions/jtsexample/geom/PrecisionModelExample.java
+++ b/src/com/vividsolutions/jtsexample/geom/PrecisionModelExample.java
@@ -45,7 +45,7 @@ import com.vividsolutions.jts.io.*;
  * The output shows the effects of rounding in the single-precision and fixed-precision
  * models.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class PrecisionModelExample
 {
diff --git a/src/com/vividsolutions/jtsexample/geom/SimpleMethodsExample.java b/src/com/vividsolutions/jtsexample/geom/SimpleMethodsExample.java
index bd5c71c..3d5f1b7 100644
--- a/src/com/vividsolutions/jtsexample/geom/SimpleMethodsExample.java
+++ b/src/com/vividsolutions/jtsexample/geom/SimpleMethodsExample.java
@@ -54,7 +54,7 @@ import com.vividsolutions.jts.io.*;
  * ----------------------------------------------------------
  * </pre>
  *
- * @version 1.6
+ * @version 1.7
  */
 public class SimpleMethodsExample
 {
diff --git a/src/com/vividsolutions/jtsexample/linearref/LinearRefExample.java b/src/com/vividsolutions/jtsexample/linearref/LinearRefExample.java
new file mode 100644
index 0000000..8446c46
--- /dev/null
+++ b/src/com/vividsolutions/jtsexample/linearref/LinearRefExample.java
@@ -0,0 +1,87 @@
+/*
+ * The JTS Topology Suite is a collection of Java classes that
+ * implement the fundamental operations required to validate a given
+ * geo-spatial data set to a known topological specification.
+ *
+ * Copyright (C) 2001 Vivid Solutions
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * For more information, contact:
+ *
+ *     Vivid Solutions
+ *     Suite #1A
+ *     2328 Government Street
+ *     Victoria BC  V8T 5G5
+ *     Canada
+ *
+ *     (250)385-6040
+ *     www.vividsolutions.com
+ */
+package com.vividsolutions.jtsexample.linearref;
+
+import com.vividsolutions.jts.geom.*;
+import com.vividsolutions.jts.io.*;
+import com.vividsolutions.jts.linearref.*;
+
+/**
+ * Examples of Linear Referencing
+ *
+ * @version 1.7
+ */
+
+public class LinearRefExample {
+
+  static GeometryFactory fact = new GeometryFactory();
+  static WKTReader rdr = new WKTReader(fact);
+
+  public static void main(String[] args)
+      throws Exception
+  {
+    LinearRefExample example = new LinearRefExample();
+    example.run();
+  }
+
+
+  public LinearRefExample() {
+  }
+
+  public void run()
+      throws Exception
+  {
+    runExtractedLine("LINESTRING (0 0, 10 10, 20 20)", 1, 10);
+    runExtractedLine("MULTILINESTRING ((0 0, 10 10), (20 20, 25 25, 30 40))", 1, 20);
+  }
+
+  public void runExtractedLine(String wkt, double start, double end)
+    throws ParseException
+  {
+    System.out.println("=========================");
+    Geometry g1 = rdr.read(wkt);
+    System.out.println("Input Geometry: " + g1);
+    System.out.println("Indices to extract: " + start + " " + end);
+
+    LengthIndexedLine indexedLine = new LengthIndexedLine(g1);
+
+    Geometry subLine = indexedLine.extractLine(start, end);
+    System.out.println("Extracted Line: " + subLine);
+
+    double[] index = indexedLine.indicesOf(subLine);
+    System.out.println("Indices of extracted line: " + index[0] + " " + index[1]);
+
+    Coordinate midpt = indexedLine.extractPoint((index[0] + index[1]) / 2);
+    System.out.println("Midpoint of extracted line: " + midpt);
+  }
+}
\ No newline at end of file
diff --git a/src/com/vividsolutions/jtsexample/operation/distance/ClosestPointExample.java b/src/com/vividsolutions/jtsexample/operation/distance/ClosestPointExample.java
index bd6047a..0482eb2 100644
--- a/src/com/vividsolutions/jtsexample/operation/distance/ClosestPointExample.java
+++ b/src/com/vividsolutions/jtsexample/operation/distance/ClosestPointExample.java
@@ -41,7 +41,7 @@ import com.vividsolutions.jts.operation.distance.DistanceOp;
  * Example of computing distance and closest points between geometries
  * using the DistanceOp class.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class ClosestPointExample
 {
diff --git a/src/com/vividsolutions/jtsexample/operation/linemerge/LineMergeExample.java b/src/com/vividsolutions/jtsexample/operation/linemerge/LineMergeExample.java
index 0758a9b..9ad4b44 100644
--- a/src/com/vividsolutions/jtsexample/operation/linemerge/LineMergeExample.java
+++ b/src/com/vividsolutions/jtsexample/operation/linemerge/LineMergeExample.java
@@ -45,7 +45,7 @@ import java.util.Collection;
  * Example of using the LineMerger class to sew together a set of fully noded 
  * linestrings.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class LineMergeExample {
   private WKTReader reader = new WKTReader();
diff --git a/src/com/vividsolutions/jtsexample/operation/polygonize/PolygonizeExample.java b/src/com/vividsolutions/jtsexample/operation/polygonize/PolygonizeExample.java
index 0ab431a..b33398c 100644
--- a/src/com/vividsolutions/jtsexample/operation/polygonize/PolygonizeExample.java
+++ b/src/com/vividsolutions/jtsexample/operation/polygonize/PolygonizeExample.java
@@ -43,7 +43,7 @@ import com.vividsolutions.jts.operation.polygonize.Polygonizer;
 /**
  *  Example of using Polygonizer class to polygonize a set of fully noded linestrings
  *
- * @version 1.6
+ * @version 1.7
  */
 public class PolygonizeExample
 {
diff --git a/src/com/vividsolutions/jtsexample/precision/EnhancedPrecisionOpExample.java b/src/com/vividsolutions/jtsexample/precision/EnhancedPrecisionOpExample.java
index 37fd3e1..ab875b7 100644
--- a/src/com/vividsolutions/jtsexample/precision/EnhancedPrecisionOpExample.java
+++ b/src/com/vividsolutions/jtsexample/precision/EnhancedPrecisionOpExample.java
@@ -42,7 +42,7 @@ import com.vividsolutions.jts.precision.EnhancedPrecisionOp;
 /**
  * Example of using {@link EnhancedPrecisionOp} to avoid robustness problems
  *
- * @version 1.6
+ * @version 1.7
  */
 public class EnhancedPrecisionOpExample
 {
diff --git a/src/com/vividsolutions/jtsexample/technique/LineStringSelfIntersections.java b/src/com/vividsolutions/jtsexample/technique/LineStringSelfIntersections.java
index f24fa27..5483bbe 100644
--- a/src/com/vividsolutions/jtsexample/technique/LineStringSelfIntersections.java
+++ b/src/com/vividsolutions/jtsexample/technique/LineStringSelfIntersections.java
@@ -8,7 +8,7 @@ import java.util.*;
  * Shows a technique for identifying the location of self-intersections
  * in a non-simple LineString.
  *
- * @version 1.6
+ * @version 1.7
  */
 
 public class LineStringSelfIntersections {
@@ -62,4 +62,4 @@ public class LineStringSelfIntersections {
   }
 
 
-}
+}
\ No newline at end of file
diff --git a/src/com/vividsolutions/jtsexample/technique/UnionUsingBuffer.java b/src/com/vividsolutions/jtsexample/technique/PolygonUnionUsingBuffer.java
similarity index 80%
rename from src/com/vividsolutions/jtsexample/technique/UnionUsingBuffer.java
rename to src/com/vividsolutions/jtsexample/technique/PolygonUnionUsingBuffer.java
index 7e26ca0..12407b5 100644
--- a/src/com/vividsolutions/jtsexample/technique/UnionUsingBuffer.java
+++ b/src/com/vividsolutions/jtsexample/technique/PolygonUnionUsingBuffer.java
@@ -6,22 +6,23 @@ import java.util.*;
 
 /**
  * Shows a technique for using a zero-width buffer to compute
- * unions of geometrys.
+ * the union of a collection of <b>polygonal</b> geometrys.
  * The advantages of this technique are:
  * <ul>
  * <li>can avoid robustness issues
  * <li>faster for large numbers of input geometries
- * <li>handles GeometryCollections as input
+ * <li>handles GeometryCollections as input (although only the polygons will be buffered)
  * </ul>
  * Disadvantages are:
  * <ul>
  * <li>may not preserve input coordinate precision in some cases
+ * <li>only works for polygons
  * </ul>
  *
- * @version 1.6
+ * @version 1.7
  */
 
-public class UnionUsingBuffer {
+public class PolygonUnionUsingBuffer {
 
   public static void main(String[] args)
       throws Exception
@@ -46,4 +47,4 @@ public class UnionUsingBuffer {
 
 
 
-}
+}
\ No newline at end of file
diff --git a/src/jtsio/src/com/vividsolutions/jts/io/gml2/GMLConstants.java b/src/jtsio/src/com/vividsolutions/jts/io/gml2/GMLConstants.java
new file mode 100644
index 0000000..84bf078
--- /dev/null
+++ b/src/jtsio/src/com/vividsolutions/jts/io/gml2/GMLConstants.java
@@ -0,0 +1,76 @@
+/*
+ * The JTS Topology Suite is a collection of Java classes that
+ * implement the fundamental operations required to validate a given
+ * geo-spatial data set to a known topological specification.
+ *
+ * Copyright (C) 2001 Vivid Solutions
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * For more information, contact:
+ *
+ *     Vivid Solutions
+ *     Suite #1A
+ *     2328 Government Street
+ *     Victoria BC  V8T 5G5
+ *     Canada
+ *
+ *     (250)385-6040
+ *     www.vividsolutions.com
+ */
+package com.vividsolutions.jts.io.gml2;
+
+/**
+ * This class encapsualtes a collection of parsable GML tag names. For the purpose
+ * of performing a Geometry Spatial Validation we'll restrict ourselves to parsing
+ * only XML/GML tags defined in the geometry.xsd schema file
+ */
+final class GMLConstants{
+	
+	  // Namespace constants
+	  public static final String GML_NAMESPACE = "http://www.opengis.net/gml";
+	  public static final String GML_PREFIX = "gml";
+
+	  // Source Coordinate System
+	  public static final String GML_ATTR_SRSNAME = "srsName";
+
+	  // GML associative types
+	  public static final String GML_GEOMETRY_MEMBER = "geometryMember";
+	  public static final String GML_POINT_MEMBER = "pointMember";
+	  public static final String GML_POLYGON_MEMBER = "polygonMember";
+	  public static final String GML_LINESTRING_MEMBER = "lineStringMember";
+	  public static final String GML_OUTER_BOUNDARY_IS = "outerBoundaryIs";
+	  public static final String GML_INNER_BOUNDARY_IS = "innerBoundaryIs";
+
+	  // Primitive Geometries
+	  public static final String GML_POINT = "Point";
+	  public static final String GML_LINESTRING = "LineString";
+	  public static final String GML_LINEARRING = "LinearRing";
+	  public static final String GML_POLYGON = "Polygon";
+	  public static final String GML_BOX = "Box";
+
+	  // Aggregate Ggeometries
+	  public static final String GML_MULTI_GEOMETRY = "MultiGeometry";
+	  public static final String GML_MULTI_POINT = "MultiPoint";
+	  public static final String GML_MULTI_LINESTRING = "MultiLineString";
+	  public static final String GML_MULTI_POLYGON = "MultiPolygon";
+
+	  // Coordinates
+	  public static final String GML_COORDINATES = "coordinates";
+	  public static final String GML_COORD = "coord";
+	  public static final String GML_COORD_X = "X";
+	  public static final String GML_COORD_Y = "Y";
+	  public static final String GML_COORD_Z = "Z";
+}
\ No newline at end of file
diff --git a/src/jtsio/src/com/vividsolutions/jts/io/gml2/GMLHandler.java b/src/jtsio/src/com/vividsolutions/jts/io/gml2/GMLHandler.java
new file mode 100644
index 0000000..56c85d3
--- /dev/null
+++ b/src/jtsio/src/com/vividsolutions/jts/io/gml2/GMLHandler.java
@@ -0,0 +1,249 @@
+/*
+ * The JTS Topology Suite is a collection of Java classes that
+ * implement the fundamental operations required to validate a given
+ * geo-spatial data set to a known topological specification.
+ *
+ * Copyright (C) 2001 Vivid Solutions
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * For more information, contact:
+ *
+ *     Vivid Solutions
+ *     Suite #1A
+ *     2328 Government Street
+ *     Victoria BC  V8T 5G5
+ *     Canada
+ *
+ *     (250)385-6040
+ *     www.vividsolutions.com
+ */
+package com.vividsolutions.jts.io.gml2;
+
+import java.util.*;
+
+import org.xml.sax.*;
+import org.xml.sax.helpers.AttributesImpl;
+import org.xml.sax.helpers.DefaultHandler;
+
+import com.vividsolutions.jts.geom.Geometry;
+import com.vividsolutions.jts.geom.GeometryFactory;
+import com.vividsolutions.jts.io.gml2.GeometryStrategies.ParseStrategy;
+
+/**
+ * When you encounter some GML Geometry elements, you may either delegate the events to this handler.
+ * 
+ * This handler ignores both namespaces and prefixes. These mappings may be included at a later date, but for the moment are left as an exercise for the reader. 
+ * 
+ * Hints: 
+ * 		If your parent handler is a DefaultHandler register the parent handler to receive the errors and locator calls.
+ * 		Use the @see com.vividsolutions.jts.io.gml2.GeometryStrategies#findStrategy(String, String) to help check for applicability
+ * 
+ * @see DefaultHandler
+ *
+ * @author David Zwiers, Vivid Solutions. 
+ */
+public class GMLHandler extends DefaultHandler {
+	
+	/**
+	 * This class is intended to log the SAX acitivity within a given element until it's termination. 
+	 * 
+	 * At this time, a new object of value is created and passed to the parent. An object of value is typically either java.lang.* or a JTS Geometry
+	 * 
+	 * This class is not intended for use outside this distribution, and may change in subsequent versions.
+	 *
+	 * @author David Zwiers, Vivid Solutions.
+	 */
+	static class Handler{
+		protected Attributes attrs = null;
+		protected ParseStrategy strategy;
+		
+		/**
+		 * @param strategy 
+		 * @param attributes Nullable
+		 */
+		public Handler(ParseStrategy strategy, Attributes attributes){
+			if(attributes!=null)
+				this.attrs = new AttributesImpl(attributes);
+			this.strategy = strategy;
+		}
+		
+		protected StringBuffer text = null;
+		/**
+		 * Caches text for the future
+		 * @param str
+		 */
+		public void addText(String str){
+			if(text == null)
+				text = new StringBuffer();
+			text.append(str);
+		}
+		
+		protected List children = null; 
+		/**
+		 * Store param for the future
+		 * 
+		 * @param obj
+		 */
+		public void keep(Object obj){
+			if(children == null)
+				children = new LinkedList();
+			children.add(obj);
+			
+		}
+		
+		/**
+		 * @param gf GeometryFactory
+		 * @return Parsed Object
+		 * @throws SAXException 
+		 */
+		public Object create(GeometryFactory gf) throws SAXException{
+			return strategy.parse(this,gf);
+		}
+	}
+	
+	private Stack stack = new Stack();
+    private ErrorHandler delegate = null;
+    private GeometryFactory gf = null;
+    
+    /**
+     * Allows the user to specify a delegate object for error / warning messages. 
+     * 
+     * If the delegate also implements ContentHandler then the document Locator will be passed on.
+     * @param gf Geometry Factory
+     * 
+     * @see ErrorHandler
+     * @see ContentHandler
+     * @see ContentHandler#setDocumentLocator(org.xml.sax.Locator)
+     * @see org.xml.sax.Locator
+     * 
+     * @param delegate Nullable
+     */
+    public GMLHandler(GeometryFactory gf, ErrorHandler delegate){
+    	this.delegate = delegate;
+    	this.gf = gf;
+    	stack.push(new Handler(null,null));
+    }
+    
+    /**
+     * This method should only be called AFTER the parser has completed execution
+     * 
+     * @return Last Geometry Parsed, or a collection when there is morethan one geometry
+     */
+    public Geometry getGeometry(){
+    	if(stack.size() == 1){
+    		Handler h = (Handler)stack.peek();
+    		if(h.children.size() == 1)
+    			return (Geometry) h.children.get(0);
+        	return gf.createGeometryCollection((Geometry[]) h.children.toArray(new Geometry[stack.size()]));
+    	}
+    	throw new IllegalStateException("Parse did not complete as expected, there are "+stack.size()+" elements on the Stack");
+    }
+	
+	//////////////////////////////////////////////
+	// Parsing Methods
+	
+	/**
+	 * @see org.xml.sax.helpers.DefaultHandler#characters(char[], int, int)
+	 */
+	public void characters(char[] ch, int start, int length) throws SAXException {
+		if(!stack.isEmpty())
+			((Handler)stack.peek()).addText(new String(ch,start,length));
+	}
+
+	/**
+	 * @see org.xml.sax.helpers.DefaultHandler#ignorableWhitespace(char[], int, int)
+	 */
+	public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException {
+		if(!stack.isEmpty())
+			((Handler)stack.peek()).addText(" ");
+	}
+
+	/**
+	 * @see org.xml.sax.helpers.DefaultHandler#endElement(java.lang.String, java.lang.String, java.lang.String)
+	 */
+	public void endElement(String uri, String localName, String qName) throws SAXException {
+		Handler thisAction = (Handler) stack.pop();
+		((Handler) stack.peek()).keep(thisAction.create(gf));
+	}
+
+	/**
+	 * @see org.xml.sax.helpers.DefaultHandler#startElement(java.lang.String, java.lang.String, java.lang.String, org.xml.sax.Attributes)
+	 */
+	public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
+		// create a handler
+		ParseStrategy ps = GeometryStrategies.findStrategy(uri,localName);
+		if(ps == null){
+			String qn = qName.substring(qName.indexOf(':')+1,qName.length());
+			ps = GeometryStrategies.findStrategy(null,qn);
+		}
+		Handler h = new Handler(ps,attributes);
+		// and add it to the stack
+		stack.push(h);
+	}
+	
+	//////////////////////////////////////////////
+	// Logging Methods
+
+	/**
+	 * @see org.xml.sax.helpers.DefaultHandler#setDocumentLocator(org.xml.sax.Locator)
+	 */
+	public void setDocumentLocator(Locator locator) {
+		this.locator = locator;
+		if(delegate!=null && delegate instanceof ContentHandler)
+			((ContentHandler) delegate).setDocumentLocator(locator);
+		
+	}
+	
+	private Locator locator = null;
+	
+	protected Locator getDocumentLocator(){
+		return locator;
+	}
+	
+	//////////////////////////////////////////////
+	// ERROR Methods
+
+	/**
+	 * @see org.xml.sax.helpers.DefaultHandler#fatalError(org.xml.sax.SAXParseException)
+	 */
+	public void fatalError(SAXParseException e) throws SAXException {
+		if(delegate!=null)
+			delegate.fatalError(e);
+		else
+			super.fatalError(e);
+	}
+
+	/**
+	 * @see org.xml.sax.helpers.DefaultHandler#error(org.xml.sax.SAXParseException)
+	 */
+	public void error(SAXParseException e) throws SAXException {
+		if(delegate!=null)
+			delegate.error(e);
+		else
+			super.error(e);
+	}
+
+	/**
+	 * @see org.xml.sax.helpers.DefaultHandler#warning(org.xml.sax.SAXParseException)
+	 */
+	public void warning(SAXParseException e) throws SAXException {
+		if(delegate!=null)
+			delegate.warning(e);
+		else
+			super.warning(e);
+	}
+
+}
diff --git a/src/jtsio/src/com/vividsolutions/jts/io/gml2/GMLReader.java b/src/jtsio/src/com/vividsolutions/jts/io/gml2/GMLReader.java
new file mode 100644
index 0000000..0b2dae2
--- /dev/null
+++ b/src/jtsio/src/com/vividsolutions/jts/io/gml2/GMLReader.java
@@ -0,0 +1,121 @@
+/*
+ * The JTS Topology Suite is a collection of Java classes that
+ * implement the fundamental operations required to validate a given
+ * geo-spatial data set to a known topological specification.
+ *
+ * Copyright (C) 2001 Vivid Solutions
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * For more information, contact:
+ *
+ *     Vivid Solutions
+ *     Suite #1A
+ *     2328 Government Street
+ *     Victoria BC  V8T 5G5
+ *     Canada
+ *
+ *     (250)385-6040
+ *     www.vividsolutions.com
+ */
+package com.vividsolutions.jts.io.gml2;
+
+import java.io.*;
+
+import javax.xml.parsers.*;
+
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+
+import com.vividsolutions.jts.geom.Geometry;
+import com.vividsolutions.jts.geom.GeometryFactory;
+
+/**
+ * Reads the gml geometry(s) from the user specified xml fragment into a JTS geometry.
+ * 
+ * <code>
+ *   <LineString>
+ *  	<coordinates>24824.045318333192,38536.15071012041
+ *  		26157.378651666528,37567.42733944659 26666.666,36000.0
+ *  		26157.378651666528,34432.57266055341
+ *  		24824.045318333192,33463.84928987959
+ *  		23175.954681666804,33463.84928987959
+ *  		21842.621348333472,34432.57266055341 21333.333,36000.0
+ *  		21842.621348333472,37567.42733944659
+ *  		23175.954681666808,38536.15071012041 </coordinates>
+ *  </LineString>
+ * </code>
+ * 
+ * This reader ignores namespace prefixes, and disables both the validation and namespace options on the SAXParser. 
+ * 
+ * For a full description of GML geometries, visit the OGC web site <a href='http://www.opengeospatial.org/'>http://www.opengeospatial.org/</a>.
+ * 
+ * In most use cases, portions of a document will be delegated to this package. 
+ * 
+ * @author David Zwiers, Vivid Solutions. 
+ */
+public class GMLReader {
+	
+	/**
+	 * Reads the GML2 String into a single JTS Geometry
+	 * 
+	 * Where a collection of Geometries are found, a JTS GeometryCollection is returned.
+	 * 
+	 * @param gml The GML String to parse
+	 * @param geometryFactory When null, a default will be used.
+	 * @return Geometry The resulting JTS Geometry
+	 * @throws ParserConfigurationException 
+	 * @throws IOException 
+	 * @throws SAXException 
+	 * @throws ParserConfigurationException
+	 * 
+	 * @see #read(Reader, GeometryFactory)
+	 */
+	public Geometry read(String gml, GeometryFactory geometryFactory) throws SAXException, IOException, ParserConfigurationException{
+		return read(new StringReader(gml),geometryFactory);
+	}
+	
+	/**
+	 * Reads the Character Stream into a single JTS Geometry
+	 * 
+	 * Where a collection of Geometries are found, a JTS GeometryCollection is returned.
+	 * 
+	 * @param reader The input source
+	 * @param geometryFactory When null, a default will be used.
+	 * @return Geometry The resulting JTS Geometry
+	 * @throws SAXException
+	 * @throws IOException
+	 * @throws ParserConfigurationException
+	 * 
+	 * Note: The parser will be both namespace aware and validating.
+	 */
+	public Geometry read(Reader reader, GeometryFactory geometryFactory) throws SAXException, IOException, ParserConfigurationException{
+		SAXParserFactory fact = SAXParserFactory.newInstance();
+		
+		fact.setNamespaceAware(false);
+		fact.setValidating(false);
+		
+		SAXParser parser = fact.newSAXParser();
+		
+		if(geometryFactory == null)
+			geometryFactory = new GeometryFactory();
+		
+		GMLHandler gh = new GMLHandler(geometryFactory,null);
+		parser.parse(new InputSource(reader),gh);
+		
+		return gh.getGeometry();
+	}
+	
+}
diff --git a/src/jtsio/src/com/vividsolutions/jts/io/gml2/GMLWriter.java b/src/jtsio/src/com/vividsolutions/jts/io/gml2/GMLWriter.java
new file mode 100644
index 0000000..e9759d2
--- /dev/null
+++ b/src/jtsio/src/com/vividsolutions/jts/io/gml2/GMLWriter.java
@@ -0,0 +1,350 @@
+/*
+ * The JTS Topology Suite is a collection of Java classes that
+ * implement the fundamental operations required to validate a given
+ * geo-spatial data set to a known topological specification.
+ *
+ * Copyright (C) 2001 Vivid Solutions
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * For more information, contact:
+ *
+ *     Vivid Solutions
+ *     Suite #1A
+ *     2328 Government Street
+ *     Victoria BC  V8T 5G5
+ *     Canada
+ *
+ *     (250)385-6040
+ *     www.vividsolutions.com
+ */
+package com.vividsolutions.jts.io.gml2;
+
+import java.io.*;
+
+import com.vividsolutions.jts.geom.*;
+
+/**
+ * Writes JTS Geometries as GML2 into the writer provided, or as a string.
+ *
+ * @author David Zwiers, Vivid Solutions. 
+ */
+public class GMLWriter {
+	private final String INDENT = "  ";
+	
+	private int startingIndentIndex = 0;
+	private int maxCoordinatesPerLine = 2;
+	
+	private String prefix = GMLConstants.GML_PREFIX;
+	
+	/**
+	 * Allows the user to force a prefix for the GML namespace. 
+	 * 
+	 * In XML blobs, the user may wish to leave the polygons un-qualified, thus setting the prefix to the empty string
+	 * 
+	 * @param prefix
+	 */
+	public void setPrefix(String prefix){
+		this.prefix =prefix;
+	}
+	
+	/**
+	 * Sets the starting index for preaty printing
+	 * 
+	 * @param arg
+	 */
+	public void setStartingIndentIndex(int arg){
+		if(arg<0)
+			throw new IndexOutOfBoundsException("In-valid index, must be > or = 0");
+		startingIndentIndex = arg;
+	}
+	
+	/**
+	 * Sets the number of coordinates printed per line. 
+	 * 
+	 * Use full when configuring preaty printing.
+	 * 
+	 * @param arg
+	 */
+	public void setMaxCoordinatesPerLine(int arg){
+		if(arg<1)
+			throw new IndexOutOfBoundsException("In-valid coordinate count per line, must be > 0");
+		maxCoordinatesPerLine = arg;
+	}
+	
+	/**
+	 * @param geom
+	 * @return String GML2 Encoded Geometry
+	 * @throws IOException 
+	 */
+	public String write(Geometry geom) throws IOException{
+		StringWriter writer = new StringWriter();
+		write(geom,writer);
+		return writer.getBuffer().toString();
+	}
+	
+	/**
+	 * Writes the JTS Geometry provided as GML2 into the writer provided.
+	 * 
+	 * @param geom Geometry to encode
+	 * @param writer Stream to encode to.
+	 * @throws IOException 
+	 */
+	public void write(Geometry geom, Writer writer) throws IOException{
+		write(geom,writer,startingIndentIndex);
+	}
+
+	private void write(Geometry geom, Writer writer, int level) throws IOException{
+		if(writer == null)
+	           throw new NullPointerException("Writer is null");
+		if (geom == null) {
+           throw new NullPointerException("Geometry is null");
+        } else if (geom instanceof Point) {
+        	writePoint((Point)geom,writer,level);
+        } else if (geom instanceof LineString) {
+        	writeLineString((LineString)geom,writer,level);
+        } else if (geom instanceof Polygon) {
+        	writePolygon((Polygon)geom,writer,level);
+        } else if (geom instanceof MultiPoint) {
+        	writeMultiPoint((MultiPoint)geom,writer,level);
+        } else if (geom instanceof MultiLineString) {
+        	writeMultiLineString((MultiLineString)geom,writer,level);
+        } else if (geom instanceof MultiPolygon) {
+        	writeMultiPolygon((MultiPolygon)geom,writer,level);
+        } else if (geom instanceof GeometryCollection) {
+        	writeGeometryCollection((GeometryCollection)geom,writer,startingIndentIndex);
+        }else{
+	        throw new IllegalArgumentException("Cannot encode JTS "
+	            + geom.getGeometryType() + " as SDO_GTEMPLATE "
+	            + "(Limitied to Point, Line, Polygon, GeometryCollection, MultiPoint,"
+	            + " MultiLineString and MultiPolygon)");
+        }
+		writer.flush();
+	}
+
+	  //<gml:Point><gml:coordinates>1195156.78946687,382069.533723461</gml:coordinates></gml:Point>
+	  private void writePoint(Point p, Writer writer, int level) throws IOException {
+	      startLine(level,writer);
+	      startGeomTag(GMLConstants.GML_POINT,p,writer);
+	      
+	      write(new Coordinate[] { p.getCoordinate() },writer, level + 1);
+
+	      startLine(level,writer);
+	      endGeomTag(GMLConstants.GML_POINT,writer);
+	  }
+
+	  //<gml:LineString><gml:coordinates>1195123.37289257,381985.763974674 1195120.22369473,381964.660533343 1195118.14929823,381942.597718511</gml:coordinates></gml:LineString>
+	  private void writeLineString(LineString ls, Writer writer, int level) throws IOException {
+	      startLine(level,writer);
+	      startGeomTag(GMLConstants.GML_LINESTRING,ls,writer);
+	      
+	      write(ls.getCoordinates(),writer, level + 1);
+
+	      startLine(level,writer);
+	      endGeomTag(GMLConstants.GML_LINESTRING,writer);
+	  }
+
+	  //<gml:LinearRing><gml:coordinates>1226890.26761027,1466433.47430292 1226880.59239079,1466427.03208053...></coordinates></gml:LinearRing>
+	  private void writeLinearRing(LinearRing lr, Writer writer, int level) throws IOException {
+	      startLine(level,writer);
+	      startGeomTag(GMLConstants.GML_LINEARRING,lr,writer);
+	      
+	      write(lr.getCoordinates(),writer, level + 1);
+
+	      startLine(level,writer);
+	      endGeomTag(GMLConstants.GML_LINEARRING,writer);
+	  }
+
+	  private void writePolygon(Polygon p, Writer writer, int level) throws IOException {
+	      startLine(level,writer);
+	      startGeomTag(GMLConstants.GML_POLYGON,p,writer);
+	      
+
+	      startLine(level+1,writer);
+	      startGeomTag(GMLConstants.GML_OUTER_BOUNDARY_IS,null,writer);
+
+		    writeLinearRing((LinearRing) p.getExteriorRing(), writer, level + 2);
+
+	      startLine(level+1,writer);
+	      endGeomTag(GMLConstants.GML_OUTER_BOUNDARY_IS,writer);
+	      
+
+	    for (int t = 0; t < p.getNumInteriorRing(); t++) {
+		      startLine(level+1,writer);
+		      startGeomTag(GMLConstants.GML_INNER_BOUNDARY_IS,null,writer);
+
+			    writeLinearRing((LinearRing) p.getInteriorRingN(t), writer, level + 2);
+
+		      startLine(level+1,writer);
+		      endGeomTag(GMLConstants.GML_INNER_BOUNDARY_IS,writer);
+	    }
+
+
+	      startLine(level,writer);
+	      endGeomTag(GMLConstants.GML_POLYGON,writer);
+	  }
+
+	  private void writeMultiPoint(MultiPoint mp, Writer writer, int level) throws IOException {
+	      startLine(level,writer);
+	      startGeomTag(GMLConstants.GML_MULTI_POINT,mp,writer);
+	      
+		    for (int t = 0; t < mp.getNumGeometries(); t++) {
+			  startLine(level+1,writer);
+			  startGeomTag(GMLConstants.GML_POINT_MEMBER,null,writer);
+			      
+		      writePoint((Point) mp.getGeometryN(t), writer, level + 2);
+		      
+		      startLine(level+1,writer);
+		      endGeomTag(GMLConstants.GML_POINT_MEMBER,writer);
+		    }
+	      startLine(level,writer);
+	      endGeomTag(GMLConstants.GML_MULTI_POINT,writer);
+	  }
+
+	  private void writeMultiLineString(MultiLineString mls, Writer writer, int level) throws IOException {
+	      startLine(level,writer);
+	      startGeomTag(GMLConstants.GML_MULTI_LINESTRING,mls,writer);
+	      
+		    for (int t = 0; t < mls.getNumGeometries(); t++) {
+			  startLine(level+1,writer);
+			  startGeomTag(GMLConstants.GML_LINESTRING_MEMBER,null,writer);
+			      
+		      writeLineString((LineString) mls.getGeometryN(t), writer, level + 2);
+		      
+		      startLine(level+1,writer);
+		      endGeomTag(GMLConstants.GML_LINESTRING_MEMBER,writer);
+		    }
+	      startLine(level,writer);
+	      endGeomTag(GMLConstants.GML_MULTI_LINESTRING,writer);
+	  }
+
+	  private void writeMultiPolygon(MultiPolygon mp, Writer writer, int level) throws IOException {
+	      startLine(level,writer);
+	      startGeomTag(GMLConstants.GML_MULTI_POLYGON,mp,writer);
+	      
+		    for (int t = 0; t < mp.getNumGeometries(); t++) {
+			  startLine(level+1,writer);
+			  startGeomTag(GMLConstants.GML_POLYGON_MEMBER,null,writer);
+			      
+		      writePolygon((Polygon) mp.getGeometryN(t), writer, level + 2);
+		      
+		      startLine(level+1,writer);
+		      endGeomTag(GMLConstants.GML_POLYGON_MEMBER,writer);
+		    }
+	      startLine(level,writer);
+	      endGeomTag(GMLConstants.GML_MULTI_POLYGON,writer);
+	  }
+
+	  private void writeGeometryCollection(GeometryCollection gc, Writer writer, int level) throws IOException {
+	      startLine(level,writer);
+	      startGeomTag(GMLConstants.GML_MULTI_GEOMETRY,gc,writer);
+	      
+		    for (int t = 0; t < gc.getNumGeometries(); t++) {
+			  startLine(level+1,writer);
+			  startGeomTag(GMLConstants.GML_GEOMETRY_MEMBER,null,writer);
+			      
+		      write(gc.getGeometryN(t), writer, level + 2);
+		      
+		      startLine(level+1,writer);
+		      endGeomTag(GMLConstants.GML_GEOMETRY_MEMBER,writer);
+		    }
+	      startLine(level,writer);
+	      endGeomTag(GMLConstants.GML_MULTI_GEOMETRY,writer);
+	  }
+
+	  private static final String coordinateSeparator = ",";
+	  private static final String tupleSeparator = " ";
+	  
+	  /**
+	   * Takes a list of coordinates and converts it to GML.<br>
+	   * 2d and 3d aware.
+	   * 
+	   * @param coords array of coordinates
+	 * @throws IOException 
+	   */
+	  private void write(Coordinate[] coords,Writer writer, int level) throws IOException {
+	      startLine(level,writer);
+	      startGeomTag(GMLConstants.GML_COORDINATES,null,writer);
+	      
+	      int dim = 2;
+
+	      if (coords.length > 0) {
+	        if (!(Double.isNaN(coords[0].z)))
+	          dim = 3;
+	      }
+
+	      boolean isNewLine = false;
+	      for (int i = 0; i < coords.length; i++) {
+	        if (isNewLine) {
+	  	      startLine(level+1,writer);
+	          isNewLine = false;
+	        }
+	        if (dim == 2) {
+	        	writer.write(""+coords[i].x);
+	        	writer.write(coordinateSeparator);
+	        	writer.write(""+coords[i].y);
+	        } else if (dim == 3) {
+	        	writer.write(""+coords[i].x);
+	        	writer.write(coordinateSeparator);
+	        	writer.write(""+coords[i].y);
+	        	writer.write(coordinateSeparator);
+	        	writer.write(""+coords[i].z);
+	        }
+	        writer.write(tupleSeparator);
+
+	        // break output lines to prevent them from getting too long
+	        if ((i + 1) % maxCoordinatesPerLine == 0 && i < coords.length - 1) {
+		      writer.write("\n");
+	          isNewLine = true;
+	        }
+	      }
+	      if(!isNewLine)
+	    	  writer.write("\n");
+
+	      startLine(level,writer);
+	      endGeomTag(GMLConstants.GML_COORDINATES,writer);
+	  }
+
+
+	  private void startLine(int level, Writer writer) throws IOException
+	  {
+		  for(int i=0;i<level;i++)
+			  writer.write(INDENT);
+	  }
+
+	  private void startGeomTag(String geometryName, Geometry g, Writer writer) throws IOException
+	  {
+		writer.write("<"+((prefix == null || "".equals(prefix))?"":prefix+":"));
+		writer.write(geometryName);
+	    printAttr(g,writer);
+	    writer.write(">\n");
+	  }
+
+	  private void printAttr(Geometry geom, Writer writer) throws IOException
+	  {
+		  if(geom == null)
+			  return;
+		  writer.write(" "+GMLConstants.GML_ATTR_SRSNAME+"='");
+		  writer.write(geom.getSRID()+"");
+		  writer.write("'");
+	  }
+
+	  private void endGeomTag(String geometryName, Writer writer) throws IOException
+	  {
+		  writer.write("</"+((prefix == null || "".equals(prefix))?"":prefix+":"));
+		  writer.write(geometryName);
+		  writer.write(">\n");
+	  }
+}
diff --git a/src/jtsio/src/com/vividsolutions/jts/io/gml2/GeometryStrategies.java b/src/jtsio/src/com/vividsolutions/jts/io/gml2/GeometryStrategies.java
new file mode 100644
index 0000000..a1f3911
--- /dev/null
+++ b/src/jtsio/src/com/vividsolutions/jts/io/gml2/GeometryStrategies.java
@@ -0,0 +1,533 @@
+/*
+ * The JTS Topology Suite is a collection of Java classes that
+ * implement the fundamental operations required to validate a given
+ * geo-spatial data set to a known topological specification.
+ *
+ * Copyright (C) 2001 Vivid Solutions
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * For more information, contact:
+ *
+ *     Vivid Solutions
+ *     Suite #1A
+ *     2328 Government Street
+ *     Victoria BC  V8T 5G5
+ *     Canada
+ *
+ *     (250)385-6040
+ *     www.vividsolutions.com
+ */
+package com.vividsolutions.jts.io.gml2;
+
+import java.util.*;
+import java.util.regex.Pattern;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+
+import com.vividsolutions.jts.geom.*;
+import com.vividsolutions.jts.io.gml2.GMLHandler.Handler;
+
+/**
+ * Container for GML2 Geometry parsing strategies which can be represented in JTS.
+ *
+ * @author David Zwiers, Vivid Solutions.
+ */
+public class GeometryStrategies{
+
+	/**
+	 * This set of strategies is not expected to be used directly outside of this distribution.
+	 * 
+	 * The implementation of this class are intended to be used as static function points in C. These strategies should be associated with an element when the element begins. The strategy is utilized at the end of the element to create an object of value to the user. 
+	 * 
+	 * In this case all the objects are either java.lang.* or JTS Geometry objects
+	 *
+	 * @author David Zwiers, Vivid Solutions.
+	 */
+	static interface ParseStrategy{
+		/**
+		 * @param arg Value to interpret
+		 * @param gf GeometryFactory
+		 * @return The interpreted value
+		 * @throws SAXException 
+		 */
+		Object parse(Handler arg, GeometryFactory gf) throws SAXException;
+	}
+	
+	private static HashMap strategies = loadStrategies();
+	private static HashMap loadStrategies(){
+		HashMap strats = new HashMap();
+		
+		// point
+		strats.put(GMLConstants.GML_POINT.toLowerCase(),new ParseStrategy(){
+
+			public Object parse(Handler arg, GeometryFactory gf) throws SAXException {
+				// one child, either a coord
+				// or a coordinate sequence
+				
+				if(arg.children.size()!=1)
+					throw new SAXException("Cannot create a point without exactly one coordinate");
+
+				int srid = getSrid(arg.attrs,gf.getSRID());
+
+				Object c = arg.children.get(0);
+				Point p = null;
+				if(c instanceof Coordinate){
+					p = gf.createPoint((Coordinate)c);
+				}else{
+					p = gf.createPoint((CoordinateSequence)c);
+				}
+				if(p.getSRID()!=srid)
+					p.setSRID(srid);
+				
+				return p;
+			}
+		});
+		
+		// linestring
+		strats.put(GMLConstants.GML_LINESTRING.toLowerCase(),new ParseStrategy(){
+
+			public Object parse(Handler arg, GeometryFactory gf) throws SAXException {
+				// one child, either a coord
+				// or a coordinate sequence
+				
+				if(arg.children.size()<1)
+					throw new SAXException("Cannot create a linestring without atleast two coordinates or one coordinate sequence");
+
+				int srid = getSrid(arg.attrs,gf.getSRID());
+				
+				LineString ls = null;
+				if(arg.children.size() == 1){
+					// coord set
+					try{
+						CoordinateSequence cs = (CoordinateSequence) arg.children.get(0);
+						ls = gf.createLineString(cs);
+					}catch(ClassCastException e){
+						throw new SAXException("Cannot create a linestring without atleast two coordinates or one coordinate sequence",e);
+					}
+				}else{
+					try{
+						Coordinate[] coords = (Coordinate[]) arg.children.toArray(new Coordinate[arg.children.size()]);
+						ls = gf.createLineString(coords);
+					}catch(ClassCastException e){
+						throw new SAXException("Cannot create a linestring without atleast two coordinates or one coordinate sequence",e);
+					}
+				}
+				
+				if(ls.getSRID()!=srid)
+					ls.setSRID(srid);
+				
+				return ls;
+			}
+		});
+		
+		// linearring
+		strats.put(GMLConstants.GML_LINEARRING.toLowerCase(),new ParseStrategy(){
+
+			public Object parse(Handler arg, GeometryFactory gf) throws SAXException {
+				// one child, either a coord
+				// or a coordinate sequence
+				
+				if(arg.children.size()!=1 && arg.children.size()<4)
+					throw new SAXException("Cannot create a linear ring without atleast four coordinates or one coordinate sequence");
+
+				int srid = getSrid(arg.attrs,gf.getSRID());
+				
+				LinearRing ls = null;
+				if(arg.children.size() == 1){
+					// coord set
+					try{
+						CoordinateSequence cs = (CoordinateSequence) arg.children.get(0);
+						ls = gf.createLinearRing(cs);
+					}catch(ClassCastException e){
+						throw new SAXException("Cannot create a linear ring without atleast four coordinates or one coordinate sequence",e);
+					}
+				}else{
+					try{
+						Coordinate[] coords = (Coordinate[]) arg.children.toArray(new Coordinate[arg.children.size()]);
+						ls = gf.createLinearRing(coords);
+					}catch(ClassCastException e){
+						throw new SAXException("Cannot create a linear ring without atleast four coordinates or one coordinate sequence",e);
+					}
+				}
+				
+				if(ls.getSRID()!=srid)
+					ls.setSRID(srid);
+				
+				return ls;
+			}
+		});
+		
+		// polygon
+		strats.put(GMLConstants.GML_POLYGON.toLowerCase(),new ParseStrategy(){
+
+			public Object parse(Handler arg, GeometryFactory gf) throws SAXException {
+				// one child, either a coord
+				// or a coordinate sequence
+				
+				if(arg.children.size()<1)
+					throw new SAXException("Cannot create a polygon without atleast one linear ring");
+
+				int srid = getSrid(arg.attrs,gf.getSRID());
+				
+				LinearRing outer = (LinearRing) arg.children.get(0); // will be the first
+				List t = arg.children.size()>1?arg.children.subList(1,arg.children.size()):null;
+				LinearRing[] inner = t==null?null:(LinearRing[]) t.toArray(new LinearRing[t.size()]);
+				
+				Polygon p = gf.createPolygon(outer,inner);
+				
+				if(p.getSRID()!=srid)
+					p.setSRID(srid);
+				
+				return p;
+			}
+		});
+		
+		// box
+		strats.put(GMLConstants.GML_BOX.toLowerCase(),new ParseStrategy(){
+
+			public Object parse(Handler arg, GeometryFactory gf) throws SAXException {
+				// one child, either a coord
+				// or a coordinate sequence
+				
+				if(arg.children.size()<1 || arg.children.size()>2)
+					throw new SAXException("Cannot create a box without either two coords or one coordinate sequence");
+
+//				int srid = getSrid(arg.attrs,gf.getSRID());
+				
+				Envelope box = null;
+				if(arg.children.size() == 1){
+					CoordinateSequence cs = (CoordinateSequence) arg.children.get(0);
+					box = cs.expandEnvelope(new Envelope());
+				}else{
+					box = new Envelope((Coordinate)arg.children.get(0),(Coordinate)arg.children.get(1));
+				}
+				
+				return box;
+			}
+		});
+		
+		// multi-point
+		strats.put(GMLConstants.GML_MULTI_POINT.toLowerCase(),new ParseStrategy(){
+
+			public Object parse(Handler arg, GeometryFactory gf) throws SAXException {
+				// one child, either a coord
+				// or a coordinate sequence
+				
+				if(arg.children.size()<1)
+					throw new SAXException("Cannot create a multi-point without atleast one point");
+
+				int srid = getSrid(arg.attrs,gf.getSRID());
+				
+				Point[] pts = (Point[]) arg.children.toArray(new Point[arg.children.size()]);
+				
+				MultiPoint mp = gf.createMultiPoint(pts);
+				
+				if(mp.getSRID()!=srid)
+					mp.setSRID(srid);
+				
+				return mp;
+			}
+		});
+		
+		// multi-linestring
+		strats.put(GMLConstants.GML_MULTI_LINESTRING.toLowerCase(),new ParseStrategy(){
+
+			public Object parse(Handler arg, GeometryFactory gf) throws SAXException {
+				// one child, either a coord
+				// or a coordinate sequence
+				
+				if(arg.children.size()<1)
+					throw new SAXException("Cannot create a multi-linestring without atleast one linestring");
+
+				int srid = getSrid(arg.attrs,gf.getSRID());
+				
+				LineString[] lns = (LineString[]) arg.children.toArray(new LineString[arg.children.size()]);
+				
+				MultiLineString mp = gf.createMultiLineString(lns);
+				
+				if(mp.getSRID()!=srid)
+					mp.setSRID(srid);
+				
+				return mp;
+			}
+		});
+		
+		// multi-poly
+		strats.put(GMLConstants.GML_MULTI_POLYGON.toLowerCase(),new ParseStrategy(){
+
+			public Object parse(Handler arg, GeometryFactory gf) throws SAXException {
+				// one child, either a coord
+				// or a coordinate sequence
+				
+				if(arg.children.size()<1)
+					throw new SAXException("Cannot create a multi-polygon without atleast one polygon");
+
+				int srid = getSrid(arg.attrs,gf.getSRID());
+				
+				Polygon[] plys = (Polygon[]) arg.children.toArray(new Polygon[arg.children.size()]);
+				
+				MultiPolygon mp = gf.createMultiPolygon(plys);
+				
+				if(mp.getSRID()!=srid)
+					mp.setSRID(srid);
+				
+				return mp;
+			}
+		});
+		
+		// multi-geom
+		strats.put(GMLConstants.GML_MULTI_GEOMETRY.toLowerCase(),new ParseStrategy(){
+
+			public Object parse(Handler arg, GeometryFactory gf) throws SAXException {
+				// one child, either a coord
+				// or a coordinate sequence
+				
+				if(arg.children.size()<1)
+					throw new SAXException("Cannot create a multi-polygon without atleast one geometry");
+				
+				Geometry[] geoms = (Geometry[]) arg.children.toArray(new Geometry[arg.children.size()]);
+				
+				GeometryCollection gc = gf.createGeometryCollection(geoms);
+								
+				return gc;
+			}
+		});
+		
+		// coordinates
+		strats.put(GMLConstants.GML_COORDINATES.toLowerCase(),new ParseStrategy(){
+
+			private WeakHashMap patterns = new WeakHashMap();
+			
+			public Object parse(Handler arg, GeometryFactory gf) throws SAXException {
+				// one child, either a coord
+				// or a coordinate sequence
+
+				if(arg.text == null || "".equals(arg.text))
+					throw new SAXException("Cannot create a coordinate sequence without text to parse");
+				
+				String decimal = ".";
+				String coordSeperator = ",";
+				String toupleSeperator = " ";
+				
+				// get overides from coordinates
+				if(arg.attrs.getIndex("decimal")>=0)
+					decimal = arg.attrs.getValue("decimal");
+				else if(arg.attrs.getIndex(GMLConstants.GML_NAMESPACE,"decimal")>=0)
+					decimal = arg.attrs.getValue(GMLConstants.GML_NAMESPACE,"decimal");
+
+				if(arg.attrs.getIndex("cs")>=0)
+					coordSeperator = arg.attrs.getValue("cs");
+				else if(arg.attrs.getIndex(GMLConstants.GML_NAMESPACE,"cs")>=0)
+					coordSeperator = arg.attrs.getValue(GMLConstants.GML_NAMESPACE,"cs");
+
+				if(arg.attrs.getIndex("ts")>=0)
+					toupleSeperator = arg.attrs.getValue("ts");
+				else if(arg.attrs.getIndex(GMLConstants.GML_NAMESPACE,"ts")>=0)
+					toupleSeperator = arg.attrs.getValue(GMLConstants.GML_NAMESPACE,"ts");
+				
+				// now to start parse
+				String t = arg.text.toString();
+				t = t.replaceAll("\\s"," ");
+				
+				Pattern ptn = (Pattern) patterns.get(toupleSeperator);
+				if(ptn == null){
+					String ts = new String(toupleSeperator);
+					if(ts.indexOf('\\')>-1){
+							// need to escape it
+							ts = ts.replaceAll("\\","\\\\");
+					}
+					if(ts.indexOf('.')>-1){
+						// need to escape it
+						ts = ts.replaceAll("\\.","\\\\.");
+					}
+					ptn = Pattern.compile(ts);
+					patterns.put(toupleSeperator,ptn);
+				}
+				String[] touples = ptn.split(t.trim());//  t.trim().split(toupleSeperator);
+				
+				if(touples.length == 0)
+					throw new SAXException("Cannot create a coordinate sequence without a touple to parse");
+				
+				// we may have null touples, so calculate the num first
+				int numNonNullTouples = 0;
+				for(int i=0;i<touples.length;i++){
+					if(touples[i] !=null && !"".equals(touples[i].trim())){
+						if(i!=numNonNullTouples){
+							touples[numNonNullTouples] = touples[i]; // always shift left
+						}
+						numNonNullTouples++;
+					}
+				}
+				for(int i=numNonNullTouples;i<touples.length;i++)
+					touples[i] = null;
+				
+				// null touples now at end of array
+				if(numNonNullTouples == 0)
+					throw new SAXException("Cannot create a coordinate sequence without a non-null touple to parse");
+				
+				int dim = touples[0].split(coordSeperator).length;
+				CoordinateSequence cs = gf.getCoordinateSequenceFactory().create(numNonNullTouples,dim);
+				dim = cs.getDimension(); // max dim
+				
+				boolean replaceDec = !".".equals(decimal);
+				
+				for(int i=0;i<numNonNullTouples;i++){
+					// for each touple, split, parse, add
+
+					ptn = (Pattern) patterns.get(coordSeperator);
+					if(ptn == null){
+						String ts = new String(coordSeperator);
+						if(ts.indexOf('\\')>-1){
+								// need to escape it
+							ts = ts.replaceAll("\\","\\\\");
+						}
+						if(ts.indexOf('.')>-1){
+							// need to escape it
+							ts = ts.replaceAll("\\.","\\\\.");
+						}
+						ptn = Pattern.compile(ts);
+						patterns.put(coordSeperator,ptn);
+					}
+					String[] coords = ptn.split(touples[i]);//  touples[i].split(coordSeperator);
+					
+					int dimIndex = 0;
+					for(int j=0;j<coords.length && j<dim;j++){
+						if(coords[j] != null && !"".equals(coords[j].trim())){
+							double ordinate = Double.parseDouble(replaceDec?coords[j].replaceAll(decimal,"."):coords[j]);
+							cs.setOrdinate(i,dimIndex++,ordinate);
+						}
+					}
+						// fill remaining dim
+					for(;dimIndex<dim;)cs.setOrdinate(i,dimIndex++,Double.NaN);
+				}
+				
+				return cs;
+			}
+		});
+		
+		// coord
+		strats.put(GMLConstants.GML_COORD.toLowerCase(),new ParseStrategy(){
+
+			public Object parse(Handler arg, GeometryFactory gf) throws SAXException {
+				// one child, either a coord
+				// or a coordinate sequence
+
+				if(arg.children.size()<1)
+					throw new SAXException("Cannot create a coordinate without atleast one axis");
+				if(arg.children.size()>3)
+					throw new SAXException("Cannot create a coordinate with more than 3 axis");
+				
+				Double[] axis = (Double[]) arg.children.toArray(new Double[arg.children.size()]);
+				Coordinate c = new Coordinate();
+				c.x = axis[0].doubleValue();
+				if(axis.length>1)
+					c.y = axis[1].doubleValue();
+				if(axis.length>2)
+					c.z = axis[2].doubleValue();
+				
+				return c;
+			}
+		});
+		
+		ParseStrategy coord_child = new ParseStrategy(){
+
+			public Object parse(Handler arg, GeometryFactory gf) throws SAXException {
+				if(arg.text == null)
+					return null;
+				return new Double((arg.text.toString()));
+			}
+		};
+		
+		// coord-x
+		strats.put(GMLConstants.GML_COORD_X.toLowerCase(),coord_child);
+		
+		// coord-y
+		strats.put(GMLConstants.GML_COORD_Y.toLowerCase(),coord_child);
+		
+		// coord-z
+		strats.put(GMLConstants.GML_COORD_Z.toLowerCase(),coord_child);
+		
+		ParseStrategy member = new ParseStrategy(){
+
+			public Object parse(Handler arg, GeometryFactory gf) throws SAXException {
+				if(arg.children.size()!=1)
+					throw new SAXException("Geometry Members may only contain one geometry.");
+				
+				// type checking will occur in the parent geom collection.
+				// may wish to add this in the future
+				
+				return arg.children.get(0);
+			}
+		};
+		// outerBoundary - linear ring member
+		strats.put(GMLConstants.GML_OUTER_BOUNDARY_IS.toLowerCase(),member);
+		
+		// innerBoundary - linear ring member
+		strats.put(GMLConstants.GML_INNER_BOUNDARY_IS.toLowerCase(),member);
+		
+		// point member
+		strats.put(GMLConstants.GML_POINT_MEMBER.toLowerCase(),member);
+		
+		// line string member
+		strats.put(GMLConstants.GML_LINESTRING_MEMBER.toLowerCase(),member);
+		
+		// polygon member
+		strats.put(GMLConstants.GML_POLYGON_MEMBER.toLowerCase(),member);
+		
+		return strats;
+	}
+	
+	static int getSrid(Attributes attrs, int defaultValue){
+		String srs = null;
+		if(attrs.getIndex(GMLConstants.GML_ATTR_SRSNAME)>=0)
+			srs = attrs.getValue(GMLConstants.GML_ATTR_SRSNAME);
+		else if(attrs.getIndex(GMLConstants.GML_NAMESPACE,GMLConstants.GML_ATTR_SRSNAME)>=0)
+			srs = attrs.getValue(GMLConstants.GML_NAMESPACE,GMLConstants.GML_ATTR_SRSNAME);
+		
+		if(srs != null){
+			srs = srs.trim();
+			if(srs != null && !"".equals(srs)){
+				try{
+					return Integer.parseInt(srs);
+				}catch(NumberFormatException e){
+					// rip out the end, uri's are used here sometimes
+					int index = srs.lastIndexOf('#');
+					if(index > -1)
+						srs = srs.substring(index);
+					try{
+						return Integer.parseInt(srs);
+					}catch(NumberFormatException e2){
+						// ignore
+					}
+				}
+			}
+		}
+		
+		return defaultValue;
+	}
+	
+	/**
+	 * @param uri Not currently used, included for future work
+	 * @param localName Used to look up an appropriate parse strategy
+	 * @return The ParseStrategy which should be employed
+	 * 
+	 * @see ParseStrategy
+	 */
+	public static ParseStrategy findStrategy(String uri,String localName){
+		return localName == null?null:(ParseStrategy) strategies.get(localName.toLowerCase());
+	}
+}
diff --git a/test/vivid/TestFunctionAA.xml b/test/vivid/TestFunctionAA.xml
index 4ea6cb2..6e109c0 100644
--- a/test/vivid/TestFunctionAA.xml
+++ b/test/vivid/TestFunctionAA.xml
@@ -127,12 +127,10 @@
 <case>
   <desc>AA - simple polygons intersecting in P, L and A</desc>
   <a>
-    POLYGON(
-      (0 0, 110 0, 110 60, 40 60, 180 140, 40 220, 110 260, 0 260, 0 0))
+    POLYGON((0 0, 110 0, 110 60, 40 60, 180 140, 40 220, 110 260, 0 260, 0 0))
   </a>
   <b>
-    POLYGON(
-      (220 0, 110 0, 110 60, 180 60, 40 140, 180 220, 110 260, 220 260, 220 0))
+    POLYGON((220 0, 110 0, 110 60, 180 60, 40 140, 180 220, 110 260, 220 260, 220 0))
   </b>
 <test>
   <op name="intersection" arg1="A" arg2="B">
diff --git a/test/vivid/TestFunctionAAPrec.xml b/test/vivid/TestFunctionAAPrec.xml
index 841a7a9..759cc08 100644
--- a/test/vivid/TestFunctionAAPrec.xml
+++ b/test/vivid/TestFunctionAAPrec.xml
@@ -72,12 +72,10 @@
 <case>
   <desc>AA - narrow wedge in polygon</desc>
   <a>
-    POLYGON(
-      (10 10, 50 10, 50 50, 10 50, 10 31, 49 30, 10 30, 10 10))
+    POLYGON((10 10, 50 10, 50 50, 10 50, 10 31, 49 30, 10 30, 10 10))
   </a>
   <b>
-    POLYGON(
-      (60 40, 40 40, 40 20, 60 20, 60 40))
+    POLYGON((60 40, 40 40, 40 20, 60 20, 60 40))
   </b>
 <test>
   <op name="relate" arg3="212101212" arg1="A" arg2="B">
diff --git a/test/vivid/TestRectanglePredicate.xml b/test/vivid/TestRectanglePredicate.xml
index 64282a0..651dff3 100644
--- a/test/vivid/TestRectanglePredicate.xml
+++ b/test/vivid/TestRectanglePredicate.xml
@@ -50,6 +50,9 @@
   </b>
 <test>  <op name="intersects" arg1="A" arg2="B">   true   </op> </test>
 <test>  <op name="contains" arg1="A" arg2="B">   false   </op> </test>
+<test>  <op name="within" arg1="A" arg2="B">   true   </op> </test>
+<test>  <op name="covers" arg1="A" arg2="B">   false   </op> </test>
+<test>  <op name="coveredBy" arg1="A" arg2="B">   true   </op> </test>
 </case>
 
 <case>
@@ -72,6 +75,8 @@
     LINESTRING(10 10, 10 2000)
   </b>
 <test>  <op name="intersects" arg1="A" arg2="B">   true   </op> </test>
+<test>  <op name="contains" arg1="A" arg2="B">   false   </op> </test>
+<test>  <op name="covers" arg1="A" arg2="B">   false   </op> </test>
 </case>
 
 <case>
@@ -83,6 +88,21 @@
     LINESTRING( 10 10, -10 -20 )
   </b>
 <test>  <op name="intersects" arg1="A" arg2="B">   true   </op> </test>
+<test>  <op name="covers" arg1="A" arg2="B">   false   </op> </test>
+</case>
+
+<case>
+  <desc>L in polygon boundary</desc>
+  <a>
+    POLYGON((0 0, 100 0, 100 100, 0 100, 0 0))
+  </a>
+  <b>
+    LINESTRING( 10 0, 90 0 )
+  </b>
+<test>  <op name="intersects" arg1="A" arg2="B">   true   </op> </test>
+<test>  <op name="contains" arg1="A" arg2="B">   false   </op> </test>
+<test>  <op name="covers" arg1="A" arg2="B">   true   </op> </test>
+<test>  <op name="coveredBy" arg1="B" arg2="A">   true   </op> </test>
 </case>
 
 <case>
diff --git a/test/vivid/TestValid.xml b/test/vivid/TestValid.xml
index 87447bf..33094df 100644
--- a/test/vivid/TestValid.xml
+++ b/test/vivid/TestValid.xml
@@ -474,11 +474,22 @@ POLYGON ((190 190, 360 20, 20 20, 190 190),
    </case>
 
    <case>
-      <desc>A - duplicate holes </desc>
+      <desc>A - holes touch in one point </desc>
+      <a>
+POLYGON ((190 190, 360 20, 20 20, 190 190), 
+  (90 50, 150 110, 190 50, 90 50), 
+  (190 50, 230 110, 290 50, 190 50))
+	</a>
+      <test>
+         <op name="isValid" arg1="A">      true      </op>
+      </test>
+   </case>
+
+   <case>
+      <desc>A - hole disconnects interiors </desc>
       <a>
-POLYGON ((40 340, 300 340, 300 40, 40 40, 40 340), 
-  (180 260, 100 120, 240 100, 180 260), 
-  (100 120, 240 100, 180 260, 100 120))
+POLYGON ((0 0, 10 10, 10 0, 0 0), 
+  (5 5, 5 0, 10 5, 5 5))
 	</a>
       <test>
          <op name="isValid" arg1="A">      false      </op>

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-grass/jts.git



More information about the Pkg-grass-devel mailing list