[DebianGIS-dev] r887 - in packages/jts/branches/upstream/current: doc src src/com/vividsolutions/jts src/com/vividsolutions/jts/algorithm src/com/vividsolutions/jts/geom src/com/vividsolutions/jts/geom/impl src/com/vividsolutions/jts/geom/util src/com/vividsolutions/jts/geomgraph src/com/vividsolutions/jts/geomgraph/index src/com/vividsolutions/jts/index src/com/vividsolutions/jts/index/bintree src/com/vividsolutions/jts/index/chain src/com/vividsolutions/jts/index/quadtree src/com/vividsolutions/jts/index/strtree src/com/vividsolutions/jts/index/sweepline src/com/vividsolutions/jts/io src/com/vividsolutions/jts/linearref src/com/vividsolutions/jts/noding src/com/vividsolutions/jts/noding/snapround src/com/vividsolutions/jts/operation src/com/vividsolutions/jts/operation/buffer src/com/vividsolutions/jts/operation/distance src/com/vividsolutions/jts/operation/linemerge src/com/vividsolutions/jts/operation/overlay src/com/vividsolutions/jts/operation/polygonize src/com/vividsolutions/jts/operation/predicate src/com/vividsolutions/jts/operation/relate src/com/vividsolutions/jts/operation/valid src/com/vividsolutions/jts/planargraph src/com/vividsolutions/jts/planargraph/algorithm src/com/vividsolutions/jts/precision src/com/vividsolutions/jts/simplify src/com/vividsolutions/jts/util src/com/vividsolutions/jtsexample src/com/vividsolutions/jtsexample/geom src/com/vividsolutions/jtsexample/linearref src/com/vividsolutions/jtsexample/operation/distance src/com/vividsolutions/jtsexample/operation/linemerge src/com/vividsolutions/jtsexample/operation/polygonize src/com/vividsolutions/jtsexample/precision src/com/vividsolutions/jtsexample/technique src/jtsio src/jtsio/src src/jtsio/src/com src/jtsio/src/com/vividsolutions src/jtsio/src/com/vividsolutions/jts src/jtsio/src/com/vividsolutions/jts/io src/jtsio/src/com/vividsolutions/jts/io/gml2 src/jtsio/src/com/vividsolutions/jts/io/oracle test/vivid

frankie at alioth.debian.org frankie at alioth.debian.org
Fri Jun 15 20:03:43 UTC 2007


Author: frankie
Date: 2007-06-15 20:03:42 +0000 (Fri, 15 Jun 2007)
New Revision: 887

Added:
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/JTSVersion.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/CoordinateSequenceComparator.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/CoordinateSequences.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/io/ByteArrayInStream.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/io/ByteOrderDataInStream.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/io/ByteOrderValues.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/io/InStream.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/io/InputStreamInStream.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/io/OutStream.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/io/OutputStreamOutStream.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/io/WKBConstants.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/io/WKBReader.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/io/WKBWriter.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/linearref/
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/linearref/ExtractLineByLocation.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/linearref/LengthIndexOfPoint.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/linearref/LengthIndexedLine.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/linearref/LengthLocationMap.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/linearref/LinearGeometryBuilder.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/linearref/LinearIterator.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/linearref/LinearLocation.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/linearref/LocationIndexOfLine.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/linearref/LocationIndexOfPoint.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/linearref/LocationIndexedLine.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/linearref/package.html
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/IntersectionAdder.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/IntersectionFinderAdder.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/MCIndexNoder.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/Octant.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/OrientedCoordinateArray.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/ScaledNoder.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/SegmentPointComparator.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/SegmentStringDissolver.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/SinglePassNoder.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/snapround/HotPixel.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/snapround/MCIndexPointSnapper.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/snapround/MCIndexSnapRounder.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/snapround/SimpleSnapRounder.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/linemerge/LineSequencer.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/planargraph/Subgraph.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/planargraph/algorithm/
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/planargraph/algorithm/ConnectedSubgraphFinder.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/util/CollectionUtil.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/linearref/
   packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/linearref/LinearRefExample.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/technique/PolygonUnionUsingBuffer.java
   packages/jts/branches/upstream/current/src/jtsio/
   packages/jts/branches/upstream/current/src/jtsio/src/
   packages/jts/branches/upstream/current/src/jtsio/src/com/
   packages/jts/branches/upstream/current/src/jtsio/src/com/vividsolutions/
   packages/jts/branches/upstream/current/src/jtsio/src/com/vividsolutions/jts/
   packages/jts/branches/upstream/current/src/jtsio/src/com/vividsolutions/jts/io/
   packages/jts/branches/upstream/current/src/jtsio/src/com/vividsolutions/jts/io/gml2/
   packages/jts/branches/upstream/current/src/jtsio/src/com/vividsolutions/jts/io/gml2/GMLConstants.java
   packages/jts/branches/upstream/current/src/jtsio/src/com/vividsolutions/jts/io/gml2/GMLHandler.java
   packages/jts/branches/upstream/current/src/jtsio/src/com/vividsolutions/jts/io/gml2/GMLReader.java
   packages/jts/branches/upstream/current/src/jtsio/src/com/vividsolutions/jts/io/gml2/GMLWriter.java
   packages/jts/branches/upstream/current/src/jtsio/src/com/vividsolutions/jts/io/gml2/GeometryStrategies.java
   packages/jts/branches/upstream/current/src/jtsio/src/com/vividsolutions/jts/io/oracle/
   packages/jts/branches/upstream/current/src/jtsio/src/com/vividsolutions/jts/io/oracle/Constants.java
   packages/jts/branches/upstream/current/src/jtsio/src/com/vividsolutions/jts/io/oracle/OraReader.java
   packages/jts/branches/upstream/current/src/jtsio/src/com/vividsolutions/jts/io/oracle/OraWriter.java
   packages/jts/branches/upstream/current/src/jtsio/test/
Removed:
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/IndexVisitor.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/MCQuadtreeNoder.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/snapround/SegmentSnapper.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/snapround/SimpleSegmentStringsSnapper.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/snapround/SnapRounder.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/technique/UnionUsingBuffer.java
Modified:
   packages/jts/branches/upstream/current/doc/JTS Version History.html
   packages/jts/branches/upstream/current/src/MANIFEST.MF
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/CGAlgorithms.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/CentroidArea.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/CentroidLine.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/CentroidPoint.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/ConvexHull.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/HCoordinate.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/InteriorPointArea.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/InteriorPointLine.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/InteriorPointPoint.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/LineIntersector.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/MCPointInRing.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/MinimumDiameter.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/NonRobustCGAlgorithms.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/NonRobustLineIntersector.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/NotRepresentableException.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/PointInRing.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/PointLocator.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/RobustCGAlgorithms.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/RobustDeterminant.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/RobustLineIntersector.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/SIRtreePointInRing.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/SimplePointInAreaLocator.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/SimplePointInRing.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/package.html
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/Coordinate.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/CoordinateArrays.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/CoordinateFilter.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/CoordinateList.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/CoordinateSequence.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/CoordinateSequenceFactory.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/DefaultCoordinateSequence.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/DefaultCoordinateSequenceFactory.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/Dimension.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/Envelope.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/Geometry.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/GeometryCollection.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/GeometryCollectionIterator.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/GeometryComponentFilter.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/GeometryFactory.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/GeometryFilter.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/IntersectionMatrix.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/LineSegment.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/LineString.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/LinearRing.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/Location.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/MultiLineString.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/MultiPoint.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/MultiPolygon.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/Point.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/Polygon.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/PrecisionModel.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/TopologyException.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/Triangle.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/impl/CoordinateArraySequence.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/impl/CoordinateArraySequenceFactory.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/impl/PackedCoordinateSequence.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/util/GeometryEditor.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/util/GeometryTransformer.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/util/LinearComponentExtracter.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/util/PointExtracter.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/util/PolygonExtracter.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/util/ShortCircuitedGeometryVisitor.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/Depth.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/DirectedEdge.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/DirectedEdgeStar.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/Edge.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/EdgeEnd.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/EdgeEndStar.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/EdgeIntersection.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/EdgeIntersectionList.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/EdgeList.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/EdgeNodingValidator.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/EdgeRing.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/GeometryGraph.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/GraphComponent.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/Label.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/Node.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/NodeFactory.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/NodeMap.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/PlanarGraph.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/Position.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/Quadrant.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/TopologyLocation.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/index/EdgeSetIntersector.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/index/MonotoneChain.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/index/MonotoneChainEdge.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/index/MonotoneChainIndexer.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/index/SegmentIntersector.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/index/SimpleEdgeSetIntersector.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/index/SimpleMCSweepLineIntersector.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/index/SimpleSweepLineIntersector.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/index/SweepLineEvent.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/index/SweepLineSegment.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/ArrayListVisitor.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/ItemVisitor.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/SpatialIndex.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/bintree/Bintree.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/bintree/Interval.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/bintree/Key.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/bintree/Node.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/bintree/NodeBase.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/bintree/Root.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/chain/MonotoneChain.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/chain/MonotoneChainBuilder.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/chain/MonotoneChainOverlapAction.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/chain/MonotoneChainSelectAction.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/quadtree/DoubleBits.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/quadtree/IntervalSize.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/quadtree/Key.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/quadtree/Node.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/quadtree/NodeBase.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/quadtree/Quadtree.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/quadtree/Root.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/strtree/AbstractNode.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/strtree/AbstractSTRtree.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/strtree/Boundable.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/strtree/Interval.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/strtree/ItemBoundable.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/strtree/SIRtree.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/strtree/STRtree.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/sweepline/SweepLineEvent.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/sweepline/SweepLineIndex.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/sweepline/SweepLineInterval.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/sweepline/SweepLineOverlapAction.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/io/ParseException.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/io/WKTReader.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/io/WKTWriter.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/IteratedNoder.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/Noder.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/NodingValidator.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/SegmentIntersector.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/SegmentNode.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/SegmentNodeList.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/SegmentString.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/SimpleNoder.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/GeometryGraphOperation.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/IsSimpleOp.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/buffer/BufferBuilder.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/buffer/BufferOp.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/buffer/BufferSubgraph.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/buffer/OffsetCurveBuilder.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/buffer/OffsetCurveSetBuilder.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/buffer/RightmostEdgeFinder.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/buffer/SubgraphDepthLocater.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/distance/ConnectedElementLocationFilter.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/distance/ConnectedElementPointFilter.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/distance/DistanceOp.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/distance/GeometryLocation.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/linemerge/EdgeString.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/linemerge/LineMergeDirectedEdge.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/linemerge/LineMergeEdge.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/linemerge/LineMergeGraph.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/linemerge/LineMerger.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/overlay/EdgeSetNoder.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/overlay/LineBuilder.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/overlay/MaximalEdgeRing.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/overlay/MinimalEdgeRing.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/overlay/OverlayNodeFactory.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/overlay/OverlayOp.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/overlay/PointBuilder.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/overlay/PolygonBuilder.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/polygonize/EdgeRing.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/polygonize/PolygonizeDirectedEdge.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/polygonize/PolygonizeEdge.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/polygonize/PolygonizeGraph.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/polygonize/Polygonizer.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/predicate/RectangleContains.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/predicate/RectangleIntersects.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/predicate/SegmentIntersectionTester.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/relate/EdgeEndBuilder.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/relate/EdgeEndBundle.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/relate/EdgeEndBundleStar.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/relate/RelateComputer.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/relate/RelateNode.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/relate/RelateNodeFactory.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/relate/RelateNodeGraph.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/relate/RelateOp.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/valid/ConnectedInteriorTester.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/valid/ConsistentAreaTester.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/valid/IsValidOp.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/valid/QuadtreeNestedRingTester.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/valid/RepeatedPointTester.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/valid/SimpleNestedRingTester.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/valid/SweeplineNestedRingTester.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/valid/TopologyValidationError.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/planargraph/DirectedEdge.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/planargraph/DirectedEdgeStar.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/planargraph/Edge.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/planargraph/GraphComponent.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/planargraph/Node.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/planargraph/NodeMap.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/planargraph/PlanarGraph.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/precision/CommonBits.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/precision/CommonBitsOp.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/precision/CommonBitsRemover.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/precision/EnhancedPrecisionOp.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/precision/SimpleGeometryPrecisionReducer.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/simplify/DouglasPeuckerLineSimplifier.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/simplify/DouglasPeuckerSimplifier.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/simplify/TaggedLineString.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/simplify/TaggedLineStringSimplifier.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/simplify/TopologyPreservingSimplifier.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/util/Assert.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/util/AssertionFailedException.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/util/CoordinateArrayFilter.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/util/CoordinateCountFilter.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/util/Debug.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/util/GeometricShapeFactory.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/util/Stopwatch.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/util/UniqueCoordinateArrayFilter.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/geom/BasicExample.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/geom/ConstructionExample.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/geom/ExtendedCoordinate.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/geom/ExtendedCoordinateExample.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/geom/ExtendedCoordinateSequence.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/geom/ExtendedCoordinateSequenceFactory.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/geom/PrecisionModelExample.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/geom/SimpleMethodsExample.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/operation/distance/ClosestPointExample.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/operation/linemerge/LineMergeExample.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/operation/polygonize/PolygonizeExample.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/precision/EnhancedPrecisionOpExample.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/technique/LineStringSelfIntersections.java
   packages/jts/branches/upstream/current/test/vivid/TestFunctionAA.xml
   packages/jts/branches/upstream/current/test/vivid/TestFunctionAAPrec.xml
   packages/jts/branches/upstream/current/test/vivid/TestRectanglePredicate.xml
   packages/jts/branches/upstream/current/test/vivid/TestValid.xml
Log:
[svn-upgrade] Integrating new upstream version, jts (1.7)

Modified: packages/jts/branches/upstream/current/doc/JTS Version History.html
===================================================================
--- packages/jts/branches/upstream/current/doc/JTS Version History.html	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/doc/JTS Version History.html	2007-06-15 20:03:42 UTC (rev 887)
@@ -15,6 +15,78 @@
 This document lists the change history of release versions of the JTS Topology Suite
 
 <hr>
+
+<!----------------------------------------------------------------------->
+<h2>Version 1.7</h2>
+
+Release Date: December 7, 2005
+
+<h3>Functionality Improvements</h3>
+<ul>
+<li>Added <code>JTSVersion</code> class to provide access to the API version information
+<li>Added <code>covers</code> and <code>coveredBy</code> predicates to <code>Geometry</code>
+<li>Added <code>Geometry#buffer(distance, quadSegs, endCapStyle)</code> method to expose buffer end cap styles
+<li>Added <code>LineString#reverse</code> and <code>MultiLineString#reverse</code> methods
+<li>Added <code>centre</code>, <code>intersection</code>, <code>translate</code>,
+<code>expandBy(distance)</code>, <code>expandBy(dx, dy)</code>
+methods to <code>Envelope</code>
+<li>Added <code>CollectionUtil</code> class for performing operations over collections
+<li>Added <code>CoordinateArrays</code> comparators
+<li>Added <code>CoordinateSequence#getDimension</code>
+<li>Added convenience methods <code>toPoint</code> and <code>toLineString</code> to <code>WKTWriter</code>
+<li>Added WKB API (<code>WKBReader</code> and <code>WKBWriter</code> classes in <code>com.vividsolutions.jts.io</code>
+<li><code>WKBReader</code> has better handling of numeric parsing, including support for scientific notation
+<li>Added <code>IsValidOp#setSelfTouchingRingFormingHoleValid</code> to allow validating SDE-style polygons
+<li>Added check for non-closed rings in <code>IsValidOp</code>
+<li>Added Linear Referencing API (<code>com.vividsolutions.jts.linearref</code>
+<li>Added <code>LineSequencer</code> class to <code>com.vividsolutions.jts.operation.linemerge</code>
+<li>Added <code>Subgraph</code> class to <code>com.vividsolutions.jts.planargraph</code>
+<li>Added <code>isRemoved</code> method to <code>GraphComponent</code>
+<li>Added <code>ConnectedSubgraphFinder</code> class to <code>com.vividsolutions.jts.planargraph.algorithm</code>
+<li>Added <code>setVisited</code>, <code>getComponentWithVisitedState</code>,
+<code>setVisited</code>, <code>getComponentWithVisitedState</code>
+methods to <code>com.vividsolutions.jts.planargraph.GraphComponent</code>
+<li>Added classes to perform Snap Rounding (in <code>com.vividsolutions.jts.noding.snapround</code>>
+<li>Improved buffering speed and robustness by using Snap Rounding
+<li>Added buffer optimization for results with large numbers of polygons and/or holes
+<li>Added <code>STRtree#query(Envelope, ItemVisitor)</code> method
+<li>Added <code>Debug#toLine</code> methods
+<li>Added <code>ConvexHull(Coordinate[])</code> constructor
+</ul>
+
+<h3>Bug Fixes</h3>
+<ul>
+<li>Fixed decimal formatting in <code>WKTWriter</code> to force a leading 0 in decimal numbers
+<li>Fixed bug in <code>CoordinateArraySequence#setOrdinate</code>
+<li>Fixed bug when checking validity of polygon with hole (<code>IsValidOp#checkHolesInShell</code>)
+<li>Improved correctness of computated intersections in <code>RobustLineIntersector</code>
+<li>Fixed bugs in <code>CoordinateList.clone</code> (thanks to Matthias Bobzien)
+<li>Fixed bug in <code>Envelope.equals</code> (thanks to John Cartwright)
+<li>Fixed <code>PointLocator<code> for LinearRings
+</ul>
+
+<h3>Performance Improvements</h3>
+<ul>
+<li>Improved performance for overlay operations (point inclusion, identical edge detection)
+<li>Improved Convex Hull performance
+</ul>
+
+<h3>API Changes</h3>
+<ul>
+<li>Added <code>SpatiaIndex#query(Envelope, ItemVisitor)</code> method signature
+<li>Added <code>CoordinateSequence#getDimension()</code> method signature
+<li>Marked <code>GeometryEditor.CoordinateOperation#edit(Geometry, GeometryFactory)</code> method
+as <code>final</code>, to prevent incorrect use
+</ul>
+
+<h3>Semantics Changes</h3>
+<ul>
+<li>CoordinateArraySequence#setOrdinate now checks that the ordinate index is in range
+</ul>
+
+
+
+<!----------------------------------------------------------------------->
 <h2>Version 1.6</h2>
 
 Release Date: February 3, 2005

Modified: packages/jts/branches/upstream/current/src/MANIFEST.MF
===================================================================
--- packages/jts/branches/upstream/current/src/MANIFEST.MF	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/MANIFEST.MF	2007-06-15 20:03:42 UTC (rev 887)
@@ -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

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/JTSVersion.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/JTSVersion.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/JTSVersion.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -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

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/CGAlgorithms.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/CGAlgorithms.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/CGAlgorithms.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -39,7 +39,7 @@
  * 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
 {

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/CentroidArea.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/CentroidArea.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/CentroidArea.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -45,7 +45,7 @@
  * 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
 {

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/CentroidLine.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/CentroidLine.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/CentroidLine.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -41,7 +41,7 @@
  * Compute the average of the midpoints
  * of all line segments weighted by the segment length.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class CentroidLine
 {

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/CentroidPoint.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/CentroidPoint.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/CentroidPoint.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -40,7 +40,7 @@
  * <h2>Algorithm</h2>
  * Compute the average of all points.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class CentroidPoint
 {

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/ConvexHull.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/ConvexHull.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/ConvexHull.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -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 @@
  * 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 @@
    * 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);
     }
 
+    Coordinate[] reducedPts = inputPts;
+    // use heuristic to reduce points, if large
+    if (inputPts.length > 50) {
+      reducedPts = reduce(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[] 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 @@
     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);
+  /**
+   * 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]);
     }
-    if (!bigPoly.contains(bigQuad.eastmost)) {
-      bigPoly.add(bigQuad.eastmost);
-    }
-    if (!bigPoly.contains(bigQuad.southmost)) {
-      bigPoly.add(bigQuad.southmost);
-    }
-    if (bigPoly.size() < 3) {
-      return pts;
-    }
-    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 @@
     }
 
     // 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 @@
     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 @@
     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 @@
     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 @@
 
     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 @@
     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

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/HCoordinate.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/HCoordinate.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/HCoordinate.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -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 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 getX() throws NotRepresentableException {
+    double a = x/w;
+    if ((Double.isNaN(a)) || (Double.isInfinite(a))) {
+      throw new NotRepresentableException();
     }
+    return a;
+  }
 
-    public double getY() throws NotRepresentableException {
-        double a = y/w;
-        if  ((Double.isNaN(a)) || (Double.isInfinite(a))) {
-          throw new NotRepresentableException();
-        }
-        return a;
+  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;
-    }
-}
+  public Coordinate getCoordinate() throws NotRepresentableException {
+    Coordinate p = new Coordinate();
+    p.x = getX();
+    p.y = getY();
+    return p;
+  }
+}
\ No newline at end of file

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/InteriorPointArea.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/InteriorPointArea.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/InteriorPointArea.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -52,7 +52,7 @@
  * which does not lie in the interior.
  * </b>
  *
- * @version 1.6
+ * @version 1.7
  */
 public class InteriorPointArea {
 

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/InteriorPointLine.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/InteriorPointLine.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/InteriorPointLine.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -45,7 +45,7 @@
  * closest to the centroid.
  * </ul>
  *
- * @version 1.6
+ * @version 1.7
  */
 public class InteriorPointLine {
 

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/InteriorPointPoint.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/InteriorPointPoint.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/InteriorPointPoint.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -40,7 +40,7 @@
  * <h2>Algorithm</h2>
  * Find a point which is closest to the centroid of the geometry.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class InteriorPointPoint {
 

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/LineIntersector.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/LineIntersector.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/LineIntersector.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -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 @@
  * 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,23 +209,32 @@
                 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;
   }

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/MCPointInRing.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/MCPointInRing.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/MCPointInRing.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -36,7 +36,6 @@
 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 @@
  * 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 @@
 
   private void buildIndex()
   {
-    Envelope env = ring.getEnvelopeInternal();
+    //Envelope env = ring.getEnvelopeInternal();
     tree = new Bintree();
 
     Coordinate[] pts = CoordinateArrays.removeRepeatedPoints(ring.getCoordinates());

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/MinimumDiameter.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/MinimumDiameter.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/MinimumDiameter.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -51,7 +51,7 @@
  *
  * @see ConvexHull
  *
- * @version 1.6
+ * @version 1.7
  */
 public class MinimumDiameter
 {

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/NonRobustCGAlgorithms.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/NonRobustCGAlgorithms.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/NonRobustCGAlgorithms.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -40,7 +40,7 @@
  * <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

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/NonRobustLineIntersector.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/NonRobustLineIntersector.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/NonRobustLineIntersector.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -36,7 +36,7 @@
 import com.vividsolutions.jts.algorithm.LineIntersector;
 
 /**
- *@version 1.6
+ *@version 1.7
  */
 
 import com.vividsolutions.jts.geom.*;
@@ -45,7 +45,7 @@
 /**
  * A non-robust version of {@LineIntersector}.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class NonRobustLineIntersector
     extends LineIntersector

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/NotRepresentableException.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/NotRepresentableException.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/NotRepresentableException.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -39,7 +39,7 @@
  * 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 {

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/PointInRing.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/PointInRing.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/PointInRing.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -39,7 +39,7 @@
  * An interface for classes which test whether a {@link Coordinate} lies inside
  * a ring.
  *
- * @version 1.6
+ * @version 1.7
  */
 public interface PointInRing {
 

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/PointLocator.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/PointLocator.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/PointLocator.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -41,15 +41,16 @@
 
 /**
  * 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 @@
    * 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 @@
   {
     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 @@
 
   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 @@
     }
     return Location.INTERIOR;
   }
-
-
 }

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/RobustCGAlgorithms.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/RobustCGAlgorithms.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/RobustCGAlgorithms.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -40,7 +40,7 @@
  * 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 {

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/RobustDeterminant.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/RobustDeterminant.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/RobustDeterminant.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -35,7 +35,7 @@
 package com.vividsolutions.jts.algorithm;
 
 /**
- * @version 1.6
+ * @version 1.7
  */
 
 /**
@@ -59,7 +59,7 @@
  **************************************************************************
  * </pre>
  *
- * @version 1.6
+ * @version 1.7
  */
 public class RobustDeterminant {
 

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/RobustLineIntersector.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/RobustLineIntersector.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/RobustLineIntersector.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -35,7 +35,7 @@
 package com.vividsolutions.jts.algorithm;
 
 /**
- *@version 1.6
+ *@version 1.7
  */
 
 import com.vividsolutions.jts.geom.*;
@@ -44,7 +44,7 @@
 /**
  * A robust version of {@LineIntersector}.
  *
- * @version 1.6
+ * @version 1.7
  * @see RobustDeterminant
  */
 public class RobustLineIntersector
@@ -200,7 +200,7 @@
     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 @@
     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 @@
     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;

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/SIRtreePointInRing.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/SIRtreePointInRing.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/SIRtreePointInRing.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -42,7 +42,7 @@
  * using a {@link SIRtree} index to
  * increase performance.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class SIRtreePointInRing implements PointInRing {
 

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/SimplePointInAreaLocator.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/SimplePointInAreaLocator.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/SimplePointInAreaLocator.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -44,7 +44,7 @@
  * 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
 {

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/SimplePointInRing.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/SimplePointInRing.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/SimplePointInRing.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -39,7 +39,7 @@
  * 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

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/package.html
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/package.html	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/package.html	2007-06-15 20:03:42 UTC (rev 887)
@@ -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.
 
 

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/Coordinate.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/Coordinate.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/Coordinate.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -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 @@
  *  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> 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;
+  }
+
+  /**
+   *  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;
     }
 
-    /**
-     *  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);
+    if (y != other.y) {
+      return false;
     }
 
-    /**
-     *  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);
+    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);
+  }
 
+  /**
+   *  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;
 
+    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;
+  }
 
-    /**
-     *  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 <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)));
+  }
 
-    /**
-     *  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 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 false;
-        }
+  public Object clone() {
+    try {
+      Coordinate coord = (Coordinate) super.clone();
 
-        return true;
-    }
+      return coord; // return the clone
+    } catch (CloneNotSupportedException e) {
+      Assert.shouldNeverReachHere(
+          "this shouldn't happen because this class is Cloneable");
 
-    /**
-     *  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);
+      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
+  {
     /**
-     *  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))
+     * Compare two <code>double</code>s, allowing for NaN values.
+     * NaN is treated as being less than any valid number.
      *
-     *  </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>
+     * @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 int compareTo(Object o) {
-        Coordinate other = (Coordinate) o;
+    public static int compare(double a, double b)
+    {
+      if (a < b) return -1;
+      if (a > b) return 1;
 
-        if (x < other.x) {
-            return -1;
-        }
+      if (Double.isNaN(a)) {
+        if (Double.isNaN(b)) return 0;
+        return -1;
+      }
 
-        if (x > other.x) {
-            return 1;
-        }
+      if (Double.isNaN(b)) return 1;
+      return 0;
+    }
 
-        if (y < other.y) {
-            return -1;
-        }
+    private int dimensionsToTest = 2;
 
-        if (y > other.y) {
-            return 1;
-        }
-
-        return 0;
+    /**
+     * Creates a comparator for 2 dimensional coordinates.
+     */
+    public DimensionalComparator()
+    {
+      this(2);
     }
 
     /**
-     *  Returns <code>true</code> if <code>other</code> has the same values for x,
-     *  y and z.
+     * Creates a comparator for 2 or 3 dimensional coordinates, depending
+     * on the value provided.
      *
-     *@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 dimensionLimit the number of dimensions to test
      */
-    public boolean equals3D(Coordinate other) {
-        return (x == other.x) && (y == other.y) &&
-        ((z == other.z) ||
-        (Double.isNaN(z) && Double.isNaN(other.z)));
+    public DimensionalComparator(int dimensionsToTest)
+    {
+      if (dimensionsToTest != 2 && dimensionsToTest != 3)
+        throw new IllegalArgumentException("only 2 or 3 dimensions may be specified");
+      this.dimensionsToTest = dimensionsToTest;
     }
 
     /**
-     *  Returns a <code>String</code> of the form <I>(x,y,z)</I> .
+     * Compares two {@link Coordinate}s along to the number of
+     * dimensions specified.
      *
-     *@return    a <code>String</code> of the form <I>(x,y,z)</I>
+     * @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 String toString() {
-        return "(" + x + ", " + y + ", " + z + ")";
-    }
+    public int compare(Object o1, Object o2)
+    {
+      Coordinate c1 = (Coordinate) o1;
+      Coordinate c2 = (Coordinate) o2;
 
-    public Object clone() {
-        try {
-            Coordinate coord = (Coordinate) super.clone();
+      int compX = compare(c1.x, c2.x);
+      if (compX != 0) return compX;
 
-            return coord; // return the clone
-        } catch (CloneNotSupportedException e) {
-            Assert.shouldNeverReachHere(
-                "this shouldn't happen because this class is Cloneable");
+      int compY = compare(c1.y, c2.y);
+      if (compY != 0) return compY;
 
-            return null;
-        }
-    }
+      if (dimensionsToTest <= 2) return 0;
 
-    /**
-     * "Fixes" this Coordinate to the PrecisionModel grid.
-     */
-
-    /*
-       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;
-
-        return Math.sqrt(dx * dx + dy * dy);
+      int compZ = compare(c1.z, c2.z);
+      return compZ;
     }
+  }
 
-    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));
-    }
-}
+}
\ No newline at end of file

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/CoordinateArrays.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/CoordinateArrays.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/CoordinateArrays.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -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 @@
   }
 
   /**
-   * 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 @@
   }
 
   /**
+   * 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 @@
     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;
+  }
+
 }

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/CoordinateFilter.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/CoordinateFilter.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/CoordinateFilter.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -45,7 +45,7 @@
  *  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 @@
    *
    *@param  coord  a <code>Coordinate</code> to which the filter is applied.
    */
-  public void filter(Coordinate coord);
+  void filter(Coordinate coord);
 }
 

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/CoordinateList.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/CoordinateList.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/CoordinateList.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -43,7 +43,7 @@
  * 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 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 @@
   }
 
   /** 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;
   }
 }

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/CoordinateSequence.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/CoordinateSequence.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/CoordinateSequence.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -53,7 +53,7 @@
  * @see DefaultCoordinateSequenceFactory
  * @see TwoArrayCoordinateSequenceFactory
  *
- * @version 1.6
+ * @version 1.7
  */
 public interface CoordinateSequence
     extends Cloneable
@@ -61,12 +61,20 @@
   /**
    * 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.
    * Whether or not the Coordinate returned is the actual underlying
    * Coordinate or merely a copy depends on the implementation.
@@ -79,7 +87,7 @@
    * @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 @@
    * @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 @@
    * @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 @@
    * @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 @@
    * @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 @@
    * @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 @@
    * @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 @@
    *
    * @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 @@
    * @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 @@
    *
    * @return a copy of the coordinate sequence containing copies of all points
    */
-  public Object clone();
-}
+  Object clone();
+}
\ No newline at end of file

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/CoordinateSequenceComparator.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/CoordinateSequenceComparator.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/CoordinateSequenceComparator.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -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;
+  }
+}

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/CoordinateSequenceFactory.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/CoordinateSequenceFactory.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/CoordinateSequenceFactory.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -34,8 +34,10 @@
 
 /**
  * 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 @@
    *
    * @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 @@
    *
    * @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 @@
    * @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

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/CoordinateSequences.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/CoordinateSequences.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/CoordinateSequences.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -0,0 +1,73 @@
+/*
+ * 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.util.*;
+
+/**
+ * Utility functions for manipulating {@link CoordinateSequence}s
+ *
+ * @version 1.7
+ */
+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);
+    }
+  }
+
+  /**
+   * Swaps two coordinates in a sequence.
+   *
+   * @param seq
+   * @param i
+   * @param j
+   */
+  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

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/DefaultCoordinateSequence.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/DefaultCoordinateSequence.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/DefaultCoordinateSequence.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -39,7 +39,7 @@
  * 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 @@
   }
 
   /**
+   * @see com.vividsolutions.jts.geom.CoordinateSequence#getDimension()
+   */
+  public int getDimension() { return 3; }
+
+  /**
    * Get the Coordinate with index i.
    *
    * @param i
@@ -211,4 +216,4 @@
       return "()";
     }
   }
-}
+}
\ No newline at end of file

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/DefaultCoordinateSequenceFactory.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/DefaultCoordinateSequenceFactory.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/DefaultCoordinateSequenceFactory.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -38,7 +38,7 @@
 /**
  * Creates CoordinateSequences represented as an array of {@link Coordinate}s.
  *
- * @version 1.6
+ * @version 1.7
  *
  * @deprecated no longer used
  */
@@ -46,7 +46,7 @@
     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 @@
    * Returns the singleton instance of DefaultCoordinateSequenceFactory
    */
   public static DefaultCoordinateSequenceFactory instance() {
-    return instance;
+    return instanceObject;
   }
 
 
@@ -92,4 +92,4 @@
   public CoordinateSequence create(int size, int dimension) {
     return new DefaultCoordinateSequence(size);
   }
-}
+}
\ No newline at end of file

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/Dimension.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/Dimension.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/Dimension.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -39,7 +39,7 @@
  * 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 {
 

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/Envelope.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/Envelope.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/Envelope.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -47,12 +47,12 @@
  *  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,9 +356,40 @@
     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
    *  (x,y). Does nothing if (x,y) is already on or within the boundaries.
    *
@@ -422,6 +453,52 @@
   }
 
   /**
+   * 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 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 @@
     return maxx == otherEnvelope.getMaxX() &&
         maxy == otherEnvelope.getMaxY() &&
         minx == otherEnvelope.getMinX() &&
-        maxx == otherEnvelope.getMaxX();
+        miny == otherEnvelope.getMinY();
   }
 
   public String toString()

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/Geometry.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/Geometry.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/Geometry.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -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,10 +47,9 @@
 import com.vividsolutions.jts.operation.valid.IsValidOp;
 import com.vividsolutions.jts.util.Assert;
 /**
- *  Basic implementation of <code>Geometry</code>. <P>
+ * The base class for all geometric objects.
+ * <P>
  *
- *  <code>clone</code> returns a deep copy of the object.
- *
  *  <H3>Binary Predicates</H3>
  * Because it is not clear at this time
  * what semantics for spatial
@@ -126,7 +123,7 @@
  *  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 @@
    *@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 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 @@
   }
 
   /**
-   *  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 @@
   }
 
   /**
-   *  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 @@
   }
 
   /**
-   *  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 @@
   }
 
   /**
-   *  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 @@
   }
 
   /**
-   *  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 @@
   }
 
   /**
-   *  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 @@
    *      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 @@
   }
 
   /**
-   *  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 @@
   }
 
   /**
-   *  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, 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>
+   *@param  distance  the width of the buffer (may be positive, negative or 0)
+   *@return an area geometry representing the buffer region
+   *
+   * @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 @@
   }
 
   /**
-   *  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 @@
   }
 
   /**
-   *  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 @@
   }
 
   /**
-   *  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 @@
    *      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 @@
    *      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 @@
   }
 
   /**
-   *  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 @@
   }
 
   /**
+   *  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 @@
   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,

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/GeometryCollection.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/GeometryCollection.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/GeometryCollection.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -42,7 +42,7 @@
 /**
  *  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 @@
     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;
+
+  }
 }
 

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/GeometryCollectionIterator.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/GeometryCollectionIterator.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/GeometryCollectionIterator.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -45,7 +45,7 @@
  *  simple to ignore the <code>GeometryCollection</code> objects if they are not
  *  needed.
  *
- *@version 1.6
+ *@version 1.7
  */
 public class GeometryCollectionIterator implements Iterator {
 

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/GeometryComponentFilter.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/GeometryComponentFilter.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/GeometryComponentFilter.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -49,7 +49,7 @@
  *  <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 @@
    *
    *@param  geom  a <code>Geometry</code> to which the filter is applied.
    */
-  public void filter(Geometry geom);
+  void filter(Geometry geom);
 }
 

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/GeometryFactory.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/GeometryFactory.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/GeometryFactory.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -44,7 +44,7 @@
  * 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 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 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 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;
+  }
 
 }
 

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/GeometryFilter.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/GeometryFilter.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/GeometryFilter.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -44,7 +44,7 @@
  *  <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 @@
    *
    *@param  geom  a <code>Geometry</code> to which the filter is applied.
    */
-  public void filter(Geometry geom);
+  void filter(Geometry geom);
 }
 

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/IntersectionMatrix.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/IntersectionMatrix.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/IntersectionMatrix.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -52,7 +52,7 @@
  *  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 @@
   }
 
   /**
-   *  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 @@
 
   /**
    *  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>

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/LineSegment.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/LineSegment.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/LineSegment.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -49,7 +49,7 @@
  * 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 @@
     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 @@
   }
 
   /**
-   * @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 @@
   }
 
   /**
-   * 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 @@
   }
 
   /**
-   * 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 @@
   /**
    * 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 @@
   }
 
   /**
-   * 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)
   {

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/LineString.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/LineString.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/LineString.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -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 @@
 /**
  *  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 @@
         });
   }
 
+  /**
+   * 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 @@
       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 @@
 
   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);
   }
 
 }
-

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/LinearRing.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/LinearRing.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/LinearRing.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -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 @@
 
 
   /**
-   *  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 @@
   }
 
   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 @@
     return "LinearRing";
   }
 
-  public boolean isClosed() {
-    return true;
-  }
-
 }
-

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/Location.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/Location.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/Location.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -41,7 +41,7 @@
  *  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 @@
   /**
    *  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 @@
         return 'b';
       case INTERIOR:
         return 'i';
-      case NULL:
+      case NONE:
         return '-';
     }
     throw new IllegalArgumentException("Unknown location value: " + locationValue);

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/MultiLineString.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/MultiLineString.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/MultiLineString.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -41,7 +41,7 @@
 /**
  *  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 @@
     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;

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/MultiPoint.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/MultiPoint.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/MultiPoint.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -39,7 +39,7 @@
 /**
  *  Models a collection of <code>Point</code>s.
  *
- *@version 1.6
+ *@version 1.7
  */
 public class MultiPoint
   extends GeometryCollection

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/MultiPolygon.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/MultiPolygon.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/MultiPolygon.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -39,7 +39,7 @@
 /**
  *  Basic implementation of <code>MultiPolygon</code>.
  *
- *@version 1.6
+ *@version 1.7
  */
 public class MultiPolygon extends GeometryCollection {
   private static final long serialVersionUID = -551033529766975875L;

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/Point.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/Point.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/Point.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -39,7 +39,7 @@
 /**
  *  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 @@
     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;
+  }
 }
 

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/Polygon.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/Polygon.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/Polygon.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -49,7 +49,7 @@
  *  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 @@
 
     // 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 @@
     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;

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/PrecisionModel.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/PrecisionModel.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/PrecisionModel.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -79,7 +79,7 @@
  *<p>
  *  JTS methods currently do not handle inputs with different precision models.
  *
- *@version 1.6
+ *@version 1.7
  */
 public class PrecisionModel implements Serializable, Comparable
 {

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/TopologyException.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/TopologyException.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/TopologyException.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -38,7 +38,7 @@
 /**
  * Indicates an invalid or inconsistent topological situation encountered during processing
  *
- * @version 1.6
+ * @version 1.7
  */
 public class TopologyException
   extends RuntimeException

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/Triangle.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/Triangle.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/Triangle.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -37,7 +37,7 @@
  * Represents a planar triangle, and provides methods for calculating various
  * properties of triangles.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class Triangle
 {

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/impl/CoordinateArraySequence.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/impl/CoordinateArraySequence.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/impl/CoordinateArraySequence.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -41,7 +41,7 @@
  * 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 @@
   }
 
   /**
+   * @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 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 @@
       return "()";
     }
   }
-}
+}
\ No newline at end of file

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/impl/CoordinateArraySequenceFactory.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/impl/CoordinateArraySequenceFactory.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/impl/CoordinateArraySequenceFactory.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -37,15 +37,15 @@
 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 @@
    * Returns the singleton instance of {@link CoordinateArraySequenceFactory}
    */
   public static CoordinateArraySequenceFactory instance() {
-    return instance;
+    return instanceObject;
   }
 
   /**
@@ -85,8 +85,12 @@
 
   /**
    * @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

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/impl/PackedCoordinateSequence.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/impl/PackedCoordinateSequence.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/impl/PackedCoordinateSequence.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -15,7 +15,7 @@
  * 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 @@
 
   }
 
-}
+}
\ No newline at end of file

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/util/GeometryEditor.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/util/GeometryEditor.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/util/GeometryEditor.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -67,7 +67,7 @@
  *
  * @see Geometry#isValid
  *
- * @version 1.6
+ * @version 1.7
  */
 public class GeometryEditor
 {
@@ -106,6 +106,9 @@
    */
   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 @@
       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 @@
   /**
    * A interface which specifies an edit operation for Geometries.
    *
-   * @version 1.6
+   * @version 1.7
    */
   public interface GeometryEditorOperation
   {
@@ -218,7 +219,7 @@
      * (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 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));

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/util/GeometryTransformer.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/util/GeometryTransformer.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/util/GeometryTransformer.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -11,7 +11,7 @@
  * 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 @@
  * 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 @@
   }
 
   /**
-   * 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 @@
     return factory.buildGeometry(transGeomList);
   }
 
-}
+}
\ No newline at end of file

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/util/LinearComponentExtracter.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/util/LinearComponentExtracter.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/util/LinearComponentExtracter.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -39,7 +39,7 @@
 /**
  * Extracts all the 1-dimensional ({@link LineString}) components from a {@link Geometry}.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class LinearComponentExtracter
   implements GeometryComponentFilter

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/util/PointExtracter.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/util/PointExtracter.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/util/PointExtracter.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -39,7 +39,7 @@
 /**
  * Extracts all the 0-dimensional ({@link Point}) components from a {@link Geometry}.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class PointExtracter
   implements GeometryFilter

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/util/PolygonExtracter.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/util/PolygonExtracter.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/util/PolygonExtracter.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -39,7 +39,7 @@
 /**
  * Extracts all the 2-dimensional ({@link Polygon}) components from a {@link Geometry}.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class PolygonExtracter
   implements GeometryFilter

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/util/ShortCircuitedGeometryVisitor.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/util/ShortCircuitedGeometryVisitor.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/util/ShortCircuitedGeometryVisitor.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -6,7 +6,7 @@
  * 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 @@
   protected abstract void visit(Geometry element);
 
   protected abstract boolean isDone();
-}
+}
\ No newline at end of file

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/Depth.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/Depth.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/Depth.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -40,17 +40,17 @@
 /**
  * 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 @@
     // 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 @@
   {
     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 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)
   {

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/DirectedEdge.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/DirectedEdge.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/DirectedEdge.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -41,7 +41,7 @@
 
 
 /**
- * @version 1.6
+ * @version 1.7
  */
 public class DirectedEdge
   extends EdgeEnd
@@ -102,9 +102,9 @@
   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());

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/DirectedEdgeStar.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/DirectedEdgeStar.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/DirectedEdgeStar.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -45,7 +45,7 @@
  * 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 @@
 
     // 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 @@
      * - 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 @@
       }
     }
     // 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

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/Edge.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/Edge.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/Edge.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -45,7 +45,7 @@
 
 
 /**
- * @version 1.6
+ * @version 1.7
  */
 public class Edge
   extends GraphComponent

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/EdgeEnd.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/EdgeEnd.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/EdgeEnd.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -50,14 +50,11 @@
  * 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;
 

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/EdgeEndStar.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/EdgeEndStar.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/EdgeEndStar.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -46,7 +46,7 @@
  * 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 @@
   /**
    * 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 @@
 //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 @@
   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 @@
     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 @@
   {
     // 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 @@
         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 @@
            *  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);
         }

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/EdgeIntersection.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/EdgeIntersection.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/EdgeIntersection.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -39,16 +39,14 @@
 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 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)

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/EdgeIntersectionList.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/EdgeIntersectionList.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/EdgeIntersectionList.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -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 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 @@
     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 @@
    * 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)
   {

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/EdgeList.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/EdgeList.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/EdgeList.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -44,7 +44,7 @@
 /**
  * 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
 {

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/EdgeNodingValidator.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/EdgeNodingValidator.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/EdgeNodingValidator.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -42,7 +42,7 @@
  * 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 {
 

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/EdgeRing.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/EdgeRing.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/EdgeRing.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -45,7 +45,7 @@
 
 
 /**
- * @version 1.6
+ * @version 1.7
  */
 public abstract class EdgeRing {
 
@@ -53,7 +53,7 @@
   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 @@
   {
     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;
     }

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/GeometryGraph.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/GeometryGraph.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/GeometryGraph.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -43,7 +43,7 @@
 
 /**
  * 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 @@
     // 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++;
 

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/GraphComponent.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/GraphComponent.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/GraphComponent.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -45,7 +45,7 @@
  * 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 {
 

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/Label.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/Label.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/Label.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -58,7 +58,7 @@
  * <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 @@
   // 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 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 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 @@
       }
     }
   }
-  private void setGeometryLocation(int geomIndex, TopologyLocation tl)
-  {
-    if (tl == null) return;
-    elt[geomIndex].setLocations(tl);
-  }
   public int getGeometryCount()
   {
     int count = 0;

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/Node.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/Node.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/Node.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -46,7 +46,7 @@
 
 
 /**
- * @version 1.6
+ * @version 1.7
  */
 public class Node
   extends GraphComponent
@@ -58,12 +58,30 @@
   {
     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 @@
     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 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 @@
    */
   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);

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/NodeFactory.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/NodeFactory.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/NodeFactory.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -37,7 +37,7 @@
 
 
 /**
- * @version 1.6
+ * @version 1.7
  */
 public class NodeFactory {
 /**

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/NodeMap.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/NodeMap.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/NodeMap.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -44,7 +44,7 @@
 
 /**
  * A map of nodes, indexed by the coordinate of the node
- * @version 1.6
+ * @version 1.7
  */
 public class NodeMap
 

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/PlanarGraph.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/PlanarGraph.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/PlanarGraph.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -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 @@
  *   <LI>Computing the intersections between the edges and nodes of two different graphs
  * </UL>
  *
- * @version 1.6
+ * @version 1.7
  */
 public class PlanarGraph {
 

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/Position.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/Position.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/Position.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -38,7 +38,7 @@
 /**
  * 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 {
 

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/Quadrant.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/Quadrant.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/Quadrant.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -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 @@
  * 2 | 3
  * <pre>
  *
- * @version 1.6
+ * @version 1.7
  */
 public class Quadrant {
   /**

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/TopologyLocation.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/TopologyLocation.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/TopologyLocation.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -55,11 +55,11 @@
   * 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 @@
   /**
    * 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 @@
   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 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 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 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 @@
       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 @@
     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];
     }
   }

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/index/EdgeSetIntersector.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/index/EdgeSetIntersector.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/index/EdgeSetIntersector.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -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 @@
  * 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 {
 

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/index/MonotoneChain.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/index/MonotoneChain.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/index/MonotoneChain.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -36,7 +36,7 @@
 package com.vividsolutions.jts.geomgraph.index;
 
 /**
- * @version 1.6
+ * @version 1.7
  */
 public class MonotoneChain {
 

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/index/MonotoneChainEdge.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/index/MonotoneChainEdge.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/index/MonotoneChainEdge.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -57,7 +57,7 @@
  * 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 {
 

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/index/MonotoneChainIndexer.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/index/MonotoneChainIndexer.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/index/MonotoneChainIndexer.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -55,7 +55,7 @@
  * 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 {
 

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/index/SegmentIntersector.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/index/SegmentIntersector.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/index/SegmentIntersector.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -43,7 +43,7 @@
 
 
 /**
- * @version 1.6
+ * @version 1.7
  */
 public class SegmentIntersector {
 

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/index/SimpleEdgeSetIntersector.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/index/SimpleEdgeSetIntersector.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/index/SimpleEdgeSetIntersector.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -44,7 +44,7 @@
  * 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

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/index/SimpleMCSweepLineIntersector.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/index/SimpleMCSweepLineIntersector.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/index/SimpleMCSweepLineIntersector.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -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 @@
  * 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

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/index/SimpleSweepLineIntersector.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/index/SimpleSweepLineIntersector.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/index/SimpleSweepLineIntersector.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -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 @@
  * 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

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/index/SweepLineEvent.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/index/SweepLineEvent.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/index/SweepLineEvent.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -36,7 +36,7 @@
 package com.vividsolutions.jts.geomgraph.index;
 
 /**
- * @version 1.6
+ * @version 1.7
  */
 public class SweepLineEvent
   implements Comparable

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/index/SweepLineSegment.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/index/SweepLineSegment.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/index/SweepLineSegment.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -40,7 +40,7 @@
 
 
 /**
- * @version 1.6
+ * @version 1.7
  */
 public class SweepLineSegment {
 

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/ArrayListVisitor.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/ArrayListVisitor.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/ArrayListVisitor.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -4,7 +4,7 @@
 import com.vividsolutions.jts.index.ItemVisitor;
 
 /**
- * @version 1.6
+ * @version 1.7
  */
 public class ArrayListVisitor
     implements ItemVisitor
@@ -21,4 +21,4 @@
 
   public ArrayList getItems() { return items; }
 
-}
+}
\ No newline at end of file

Deleted: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/IndexVisitor.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/IndexVisitor.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/IndexVisitor.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -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);
-}

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/ItemVisitor.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/ItemVisitor.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/ItemVisitor.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -3,10 +3,10 @@
 /**
  * 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

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/SpatialIndex.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/SpatialIndex.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/SpatialIndex.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -37,15 +37,15 @@
 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 @@
   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

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/bintree/Bintree.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/bintree/Bintree.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/bintree/Bintree.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -52,7 +52,7 @@
  * 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
 {

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/bintree/Interval.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/bintree/Interval.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/bintree/Interval.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -36,7 +36,7 @@
 /**
  * Represents an (1-dimensional) closed interval on the Real number line.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class Interval {
 

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/bintree/Key.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/bintree/Key.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/bintree/Key.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -42,7 +42,7 @@
  * 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 {
 

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/bintree/Node.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/bintree/Node.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/bintree/Node.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -38,7 +38,7 @@
 /**
  * A node of a {@link Bintree}.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class Node
   extends NodeBase

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/bintree/NodeBase.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/bintree/NodeBase.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/bintree/NodeBase.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -41,7 +41,7 @@
 /**
  * The base class for nodes in a {@link Bintree}.
  *
- * @version 1.6
+ * @version 1.7
  */
 public abstract class NodeBase {
 

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/bintree/Root.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/bintree/Root.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/bintree/Root.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -41,7 +41,7 @@
  * It is centred at the origin,
  * and does not have a defined extent.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class Root
   extends NodeBase

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/chain/MonotoneChain.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/chain/MonotoneChain.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/chain/MonotoneChain.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -75,7 +75,7 @@
  * returned by the query.
  * However, it does mean that the queries are not thread-safe.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class MonotoneChain {
 

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/chain/MonotoneChainBuilder.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/chain/MonotoneChainBuilder.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/chain/MonotoneChainBuilder.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -42,7 +42,7 @@
  * A MonotoneChainBuilder implements functions to determine the monotone chains
  * in a sequence of points.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class MonotoneChainBuilder {
 

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/chain/MonotoneChainOverlapAction.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/chain/MonotoneChainOverlapAction.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/chain/MonotoneChainOverlapAction.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -40,7 +40,7 @@
  * The action for the internal iterator for performing
  * overlap queries on a MonotoneChain
  *
- * @version 1.6
+ * @version 1.7
  */
 public class MonotoneChainOverlapAction
 {

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/chain/MonotoneChainSelectAction.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/chain/MonotoneChainSelectAction.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/chain/MonotoneChainSelectAction.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -39,7 +39,7 @@
  * The action for the internal iterator for performing
  * envelope select queries on a MonotoneChain
  *
- * @version 1.6
+ * @version 1.7
  */
 public class MonotoneChainSelectAction
 {

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/quadtree/DoubleBits.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/quadtree/DoubleBits.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/quadtree/DoubleBits.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -43,7 +43,7 @@
  * 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 {
 

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/quadtree/IntervalSize.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/quadtree/IntervalSize.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/quadtree/IntervalSize.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -42,7 +42,7 @@
  * computing a midpoint value which does not lie strictly between the
  * endpoints.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class IntervalSize {
 

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/quadtree/Key.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/quadtree/Key.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/quadtree/Key.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -41,7 +41,7 @@
  * 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 {
 

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/quadtree/Node.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/quadtree/Node.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/quadtree/Node.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -42,7 +42,7 @@
  * 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

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/quadtree/NodeBase.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/quadtree/NodeBase.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/quadtree/NodeBase.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -42,7 +42,7 @@
 /**
  * The base class for nodes in a {@link Quadtree}.
  *
- * @version 1.6
+ * @version 1.7
  */
 public abstract class NodeBase {
 

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/quadtree/Quadtree.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/quadtree/Quadtree.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/quadtree/Quadtree.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -59,7 +59,7 @@
  * 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

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/quadtree/Root.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/quadtree/Root.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/quadtree/Root.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -41,7 +41,7 @@
  * 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

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/strtree/AbstractNode.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/strtree/AbstractNode.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/strtree/AbstractNode.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -41,7 +41,7 @@
  * (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();

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/strtree/AbstractSTRtree.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/strtree/AbstractSTRtree.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/strtree/AbstractSTRtree.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -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 @@
  * @see STRtree
  * @see SIRtree
  *
- * @version 1.6
+ * @version 1.7
  */
 public abstract class AbstractSTRtree {
 
@@ -64,7 +66,7 @@
      * @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 @@
   }
 
   /**
+   *  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 @@
     }
   }
 
+  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.
    */

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/strtree/Boundable.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/strtree/Boundable.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/strtree/Boundable.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -36,7 +36,7 @@
 /**
  * A spatial object in an AbstractSTRtree.
  *
- * @version 1.6
+ * @version 1.7
  */
 public interface Boundable {
   /**
@@ -48,5 +48,5 @@
    * (for other subclasses of AbstractSTRtree)
    * @see AbstractSTRtree.IntersectsOp
    */
-  public Object getBounds();
+  Object getBounds();
 }

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/strtree/Interval.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/strtree/Interval.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/strtree/Interval.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -39,7 +39,7 @@
  * A contiguous portion of 1D-space. Used internally by SIRtree.
  * @see SIRtree
  *
- * @version 1.6
+ * @version 1.7
  */
 public class Interval {
 

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/strtree/ItemBoundable.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/strtree/ItemBoundable.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/strtree/ItemBoundable.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -37,7 +37,7 @@
  * 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;

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/strtree/SIRtree.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/strtree/SIRtree.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/strtree/SIRtree.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -44,7 +44,7 @@
  * Application To GIS. Morgan Kaufmann, San Francisco, 2002.
  * @see STRtree
  *
- * @version 1.6
+ * @version 1.7
  */
 public class SIRtree extends AbstractSTRtree {
 

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/strtree/STRtree.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/strtree/STRtree.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/strtree/STRtree.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -41,18 +41,19 @@
 
 /**
  *  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>
+ *  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.
  *
- * Described in: P. Rigaux, Michel Scholl and Agnes Voisard. Spatial Databases With
- *  Application To GIS. Morgan Kaufmann, San Francisco, 2002.
- *
- * @version 1.6
+ * @version 1.7
  */
 public class STRtree extends AbstractSTRtree implements SpatialIndex {
 
@@ -192,6 +193,15 @@
   }
 
   /**
+   * 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

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/sweepline/SweepLineEvent.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/sweepline/SweepLineEvent.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/sweepline/SweepLineEvent.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -36,7 +36,7 @@
 package com.vividsolutions.jts.index.sweepline;
 
 /**
- * @version 1.6
+ * @version 1.7
  */
 public class SweepLineEvent
   implements Comparable

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/sweepline/SweepLineIndex.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/sweepline/SweepLineIndex.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/sweepline/SweepLineIndex.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -40,7 +40,7 @@
  * 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 {
 

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/sweepline/SweepLineInterval.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/sweepline/SweepLineInterval.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/sweepline/SweepLineInterval.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -35,7 +35,7 @@
 package com.vividsolutions.jts.index.sweepline;
 
 /**
- * @version 1.6
+ * @version 1.7
  */
 public class SweepLineInterval {
 

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/sweepline/SweepLineOverlapAction.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/sweepline/SweepLineOverlapAction.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/sweepline/SweepLineOverlapAction.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -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);
 }

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/io/ByteArrayInStream.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/io/ByteArrayInStream.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/io/ByteArrayInStream.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -0,0 +1,65 @@
+/*
+ * 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.*;
+
+/**
+ * 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 ByteArrayInStream
+	implements InStream
+{
+  private byte[] byteBuffer;
+  private ByteArrayInputStream bis;
+
+  public ByteArrayInStream(byte[] byteBuffer)
+  {
+    setBuffer(byteBuffer);
+  }
+
+  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);
+  }
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/io/ByteOrderDataInStream.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/io/ByteOrderDataInStream.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/io/ByteOrderDataInStream.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -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);
+  }
+
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/io/ByteOrderValues.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/io/ByteOrderValues.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/io/ByteOrderValues.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -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

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/io/InStream.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/io/InStream.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/io/InStream.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -0,0 +1,54 @@
+/*
+ * 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;
+
+/**
+ * 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.
+ *
+ */
+public interface InStream
+{
+  /**
+   * 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;
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/io/InputStreamInStream.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/io/InputStreamInStream.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/io/InputStreamInStream.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -0,0 +1,54 @@
+/*
+ * 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.*;
+
+/**
+ * An adapter to allow an {@link InputStream} to be used as an {@link InStream}
+ */
+public class InputStreamInStream
+	implements InStream
+{
+  private InputStream is;
+
+  public InputStreamInStream(InputStream is)
+  {
+    this.is = is;
+  }
+
+  public void read(byte[] buf) throws IOException
+  {
+    is.read(buf);
+  }
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/io/OutStream.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/io/OutStream.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/io/OutStream.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -0,0 +1,45 @@
+/*
+ * 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;
+
+/**
+ * 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 OutStream
+{
+  void write(byte[] buf, int len) throws IOException;
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/io/OutputStreamOutStream.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/io/OutputStreamOutStream.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/io/OutputStreamOutStream.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -0,0 +1,54 @@
+/*
+ * 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 java.io.OutputStream;
+
+/**
+ * An adapter to allow an {@link OutputStream} to be used as an {@link OutStream}
+ */
+public class OutputStreamOutStream
+	implements OutStream
+{
+  private OutputStream os;
+
+  public OutputStreamOutStream(OutputStream os)
+  {
+    this.os = os;
+  }
+  public void write(byte[] buf, int len) throws IOException
+  {
+    os.write(buf, 0, len);
+  }
+}

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/io/ParseException.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/io/ParseException.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/io/ParseException.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -37,7 +37,7 @@
 /**
  *  Thrown by a <code>WKTReader</code> when a parsing problem occurs.
  *
- *@version 1.6
+ *@version 1.7
  */
 public class ParseException extends Exception {
 

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/io/WKBConstants.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/io/WKBConstants.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/io/WKBConstants.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -0,0 +1,49 @@
+/*
+ * 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;
+
+/**
+ * Constant values used by the WKB format
+ */
+public interface WKBConstants {
+  int wkbXDR = 0;
+  int wkbNDR = 1;
+
+  int wkbPoint = 1;
+  int wkbLineString = 2;
+  int wkbPolygon = 3;
+  int wkbMultiPoint = 4;
+  int wkbMultiLineString = 5;
+  int wkbMultiPolygon = 6;
+  int wkbGeometryCollection = 7;
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/io/WKBReader.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/io/WKBReader.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/io/WKBReader.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -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

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/io/WKBWriter.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/io/WKBWriter.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/io/WKBWriter.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -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

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/io/WKTReader.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/io/WKTReader.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/io/WKTReader.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -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.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>
  *
- *@version 1.6
+ * <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>
+ *
+ * <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 @@
     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 @@
   }
 
   /**
-   *  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 @@
    *@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 @@
   }
 
   /**
-   *  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 @@
    *@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 @@
    *      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 @@
    *@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 @@
    *@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 @@
    *      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 @@
    *@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 @@
    *      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 @@
    *@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 @@
    *@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 @@
    *      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));
   }
+
 }
 

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/io/WKTWriter.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/io/WKTWriter.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/io/WKTWriter.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -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.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 @@
     // 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 @@
     }
     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 &lt;Point&gt; 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 @@
    *@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 &lt;LineString Text&gt; 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 @@
           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 @@
         if (i > 0) {
           writer.write(", ");
         }
-        appendCoordinate(((Point) multiPoint.getGeometryN(i)).getCoordinate(), writer,
-            multiPoint.getPrecisionModel());
+        appendCoordinate(((Point) multiPoint.getGeometryN(i)).getCoordinate(), writer);
       }
       writer.write(")");
     }

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/linearref/ExtractLineByLocation.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/linearref/ExtractLineByLocation.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/linearref/ExtractLineByLocation.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -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

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/linearref/LengthIndexOfPoint.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/linearref/LengthIndexOfPoint.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/linearref/LengthIndexOfPoint.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -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();
+  }
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/linearref/LengthIndexedLine.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/linearref/LengthIndexedLine.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/linearref/LengthIndexedLine.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -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

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/linearref/LengthLocationMap.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/linearref/LengthLocationMap.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/linearref/LengthLocationMap.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -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;
+  }
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/linearref/LinearGeometryBuilder.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/linearref/LinearGeometryBuilder.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/linearref/LinearGeometryBuilder.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -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

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/linearref/LinearIterator.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/linearref/LinearIterator.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/linearref/LinearIterator.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -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

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/linearref/LinearLocation.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/linearref/LinearLocation.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/linearref/LinearLocation.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -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

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/linearref/LocationIndexOfLine.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/linearref/LocationIndexOfLine.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/linearref/LocationIndexOfLine.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -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

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/linearref/LocationIndexOfPoint.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/linearref/LocationIndexOfPoint.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/linearref/LocationIndexOfPoint.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -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;
+  }
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/linearref/LocationIndexedLine.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/linearref/LocationIndexedLine.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/linearref/LocationIndexedLine.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -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

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/linearref/package.html
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/linearref/package.html	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/linearref/package.html	2007-06-15 20:03:42 UTC (rev 887)
@@ -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>

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/IntersectionAdder.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/IntersectionAdder.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/IntersectionAdder.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -0,0 +1,183 @@
+/*
+ * 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.*;
+import com.vividsolutions.jts.algorithm.LineIntersector;
+import com.vividsolutions.jts.util.Debug;
+
+/**
+ * Computes the intersections between two line segments in {@link SegmentString}s
+ * and adds them to each string.
+ * The {@link SegmentIntersector} is passed to a {@link Noder}.
+ * The {@link addIntersections} method is called whenever the {@link Noder}
+ * detects that two SegmentStrings <i>might</i> intersect.
+ * This class is an example of the <i>Strategy</i> pattern.
+ *
+ * @version 1.7
+ */
+public class IntersectionAdder
+    implements 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 isSelfIntersection;
+  //private boolean intersectionFound;
+  public int numIntersections = 0;
+  public int numInteriorIntersections = 0;
+  public int numProperIntersections = 0;
+
+  // testing only
+  public int numTests = 0;
+
+  public IntersectionAdder(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;
+  }
+
+  /**
+   * 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
+     )
+  {
+    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()) {
+      //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;
+        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;
+        }
+      }
+    }
+  }
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/IntersectionFinderAdder.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/IntersectionFinderAdder.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/IntersectionFinderAdder.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -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

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/IteratedNoder.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/IteratedNoder.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/IteratedNoder.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -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 @@
  * 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 @@
   }
 
   /**
+   * 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 @@
    * @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 @@
  * 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;
   }
 
 }

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/MCIndexNoder.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/MCIndexNoder.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/MCIndexNoder.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -0,0 +1,133 @@
+
+/*
+ * 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 com.vividsolutions.jts.index.*;
+import com.vividsolutions.jts.index.chain.*;
+import com.vividsolutions.jts.index.strtree.*;
+import java.util.*;
+
+/**
+ * Nodes a set of {@link SegmentStrings} using a index based
+ * on {@link MonotoneChain}s and a {@link SpatialIndex}.
+ * The {@link SpatialIndex} used should be something that supports
+ * envelope (range) queries efficiently (such as a {@link Quadtree}
+ * or {@link STRtree}.
+ *
+ * @version 1.7
+ */
+public class MCIndexNoder
+    extends SinglePassNoder
+{
+  private List monoChains = new ArrayList();
+  private SpatialIndex index= new STRtree();
+  private int idCounter = 0;
+  private Collection nodedSegStrings;
+  // statistics
+  private int nOverlaps = 0;
+
+  public MCIndexNoder()
+  {
+  }
+
+  public List getMonotoneChains() { return monoChains; }
+
+  public SpatialIndex getIndex() { return index; }
+
+  public Collection getNodedSubstrings()
+  {
+    return  SegmentString.getNodedSubstrings(nodedSegStrings);
+  }
+
+  public void computeNodes(Collection inputSegStrings)
+  {
+    this.nodedSegStrings = inputSegStrings;
+    for (Iterator i = inputSegStrings.iterator(); i.hasNext(); ) {
+      add((SegmentString) i.next());
+    }
+    intersectChains();
+//System.out.println("MCIndexNoder: # chain overlaps = " + nOverlaps);
+  }
+
+  private void intersectChains()
+  {
+    MonotoneChainOverlapAction overlapAction = new SegmentOverlapAction(segInt);
+
+    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(); ) {
+        MonotoneChain testChain = (MonotoneChain) j.next();
+        /**
+         * following test makes sure we only compare each pair of chains once
+         * and that we don't compare a chain to itself
+         */
+        if (testChain.getId() > queryChain.getId()) {
+          queryChain.computeOverlaps(testChain, overlapAction);
+          nOverlaps++;
+        }
+      }
+    }
+  }
+
+  private void add(SegmentString segStr)
+  {
+    List segChains = MonotoneChainBuilder.getChains(segStr.getCoordinates(), segStr);
+    for (Iterator i = segChains.iterator(); i.hasNext(); ) {
+      MonotoneChain mc = (MonotoneChain) i.next();
+      mc.setId(idCounter++);
+      index.insert(mc.getEnvelope(), mc);
+      monoChains.add(mc);
+    }
+  }
+
+  public class SegmentOverlapAction
+      extends MonotoneChainOverlapAction
+  {
+    private SegmentIntersector si = null;
+
+    public SegmentOverlapAction(SegmentIntersector si)
+    {
+      this.si = si;
+    }
+
+    public void overlap(MonotoneChain mc1, int start1, MonotoneChain mc2, int start2)
+    {
+      SegmentString ss1 = (SegmentString) mc1.getContext();
+      SegmentString ss2 = (SegmentString) mc2.getContext();
+      si.processIntersections(ss1, start1, ss2, start2);
+    }
+
+  }
+}

Deleted: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/MCQuadtreeNoder.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/MCQuadtreeNoder.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/MCQuadtreeNoder.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -1,127 +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;
-
-import java.util.*;
-import com.vividsolutions.jts.algorithm.LineIntersector;
-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;
-
-/**
- * Nodes a set of {@link SegmentStrings} using a index based
- * on {@link MonotoneChain}s and a {@link SpatialIndex}.
- * The {@link SpatialIndex} used should be something that supports
- * envelope (range) queries efficiently (such as a {@link Quadtree}
- * or {@link STRtree}.
- *
- * @version 1.6
- */
-public class MCQuadtreeNoder
-    extends Noder
-{
-  private Collection chains = new ArrayList();
-  private SpatialIndex index= new STRtree();
-  private int idCounter = 0;
-
-  // statistics
-  private int nOverlaps = 0;
-
-  public MCQuadtreeNoder()
-  {
-  }
-
-  public Collection node(Collection 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;
-  }
-
-  private void intersectChains()
-  {
-    MonotoneChainOverlapAction overlapAction = new SegmentOverlapAction(segInt);
-
-    for (Iterator i = chains.iterator(); i.hasNext(); ) {
-      MonotoneChain queryChain = (MonotoneChain) i.next();
-      List overlapChains = index.query(queryChain.getEnvelope());
-      for (Iterator j = overlapChains.iterator(); j.hasNext(); ) {
-        MonotoneChain testChain = (MonotoneChain) j.next();
-        /**
-         * following test makes sure we only compare each pair of chains once
-         * and that we don't compare a chain to itself
-         */
-        if (testChain.getId() > queryChain.getId()) {
-          queryChain.computeOverlaps(testChain, overlapAction);
-          nOverlaps++;
-        }
-      }
-    }
-  }
-
-  private void add(SegmentString segStr)
-  {
-    List segChains = MonotoneChainBuilder.getChains(segStr.getCoordinates(), segStr);
-    for (Iterator i = segChains.iterator(); i.hasNext(); ) {
-      MonotoneChain mc = (MonotoneChain) i.next();
-      mc.setId(idCounter++);
-      index.insert(mc.getEnvelope(), mc);
-      chains.add(mc);
-    }
-  }
-
-  public class SegmentOverlapAction
-      extends MonotoneChainOverlapAction
-  {
-    private SegmentIntersector si = null;
-
-    public SegmentOverlapAction(SegmentIntersector si)
-    {
-      this.si = si;
-    }
-
-    public void overlap(MonotoneChain mc1, int start1, MonotoneChain mc2, int start2)
-    {
-      SegmentString ss1 = (SegmentString) mc1.getContext();
-      SegmentString ss2 = (SegmentString) mc2.getContext();
-      si.processIntersections(ss1, start1, ss2, start2);
-    }
-
-  }
-}

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/Noder.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/Noder.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/Noder.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -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);
-    }
-  }
+  /**
+   * 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();
 
-  protected SegmentIntersector segInt;
-  //protected LineIntersector li;
-
-  public Noder() {
-  }
-
-  public void setSegmentIntersector(SegmentIntersector segInt)
-  {
-    this.segInt = segInt;
-  }
-
-  public abstract Collection node(Collection segStrings);
-
 }

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/NodingValidator.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/NodingValidator.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/NodingValidator.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -36,12 +36,13 @@
 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 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 checkProperIntersections()
+  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 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 @@
 
       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 @@
     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);
       }
     }
   }

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/Octant.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/Octant.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/Octant.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -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

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/OrientedCoordinateArray.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/OrientedCoordinateArray.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/OrientedCoordinateArray.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -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;
+    }
+  }
+
+
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/ScaledNoder.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/ScaledNoder.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/ScaledNoder.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -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

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/SegmentIntersector.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/SegmentIntersector.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/SegmentIntersector.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -45,143 +45,18 @@
  * 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;
-  }
 
+public interface SegmentIntersector
+{
   /**
-   * 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;
-  }
-
-  /**
    * 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;
-        }
-      }
-    }
-  }
+     );
 }

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/SegmentNode.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/SegmentNode.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/SegmentNode.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -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);
   }
 }

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/SegmentNodeList.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/SegmentNodeList.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/SegmentNodeList.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -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 @@
 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
+   * returns an iterator of SegmentNodes
    */
-  /*
-  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;
-  }
+  public Iterator iterator() { return nodeMap.values().iterator(); }
+
   /**
-   * returns an iterator of SegmentNodes
+   * Adds nodes for the first and last points of the edge
    */
-  public Iterator iterator() { return nodeMap.values().iterator(); }
-/*
-  public boolean isEmpty()
+  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 @@
     // 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 @@
    */
   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 @@
     // 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 @@
       pts[ipt++] = edge.getCoordinate(i);
     }
     if (useIntPt1) pts[ipt] = ei1.coord;
-    return new SegmentString(pts, edge.getContext());
+
+    return new SegmentString(pts, edge.getData());
   }
-/*
-  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);
-  }
-*/
   public void print(PrintStream out)
   {
     out.println("Intersections:");
@@ -265,3 +260,69 @@
     }
   }
 }
+
+// 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());
+  }
+
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/SegmentPointComparator.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/SegmentPointComparator.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/SegmentPointComparator.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -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

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/SegmentString.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/SegmentString.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/SegmentString.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -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;
+  }
 
-  private SegmentNodeList eiList = new SegmentNodeList(this);
+  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 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 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 @@
         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);
   }
-
 }

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/SegmentStringDissolver.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/SegmentStringDissolver.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/SegmentStringDissolver.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -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(); }
+}
+
+
+

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/SimpleNoder.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/SimpleNoder.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/SimpleNoder.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -42,17 +42,25 @@
  * 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 @@
         computeIntersects(edge0, edge1);
       }
     }
-    List nodedSegStrings = getNodedEdges(inputSegStrings);
-    return nodedSegStrings;
   }
 
   private void computeIntersects(SegmentString e0, SegmentString e1)

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/SinglePassNoder.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/SinglePassNoder.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/SinglePassNoder.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -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.jts.noding;
+
+import java.util.*;
+
+/**
+ * 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.7
+ */
+public abstract class SinglePassNoder
+    implements Noder
+{
+
+  protected SegmentIntersector segInt;
+
+  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;
+  }
+
+  /**
+   * 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();
+
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/snapround/HotPixel.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/snapround/HotPixel.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/snapround/HotPixel.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -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

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/snapround/MCIndexPointSnapper.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/snapround/MCIndexPointSnapper.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/snapround/MCIndexPointSnapper.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -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

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/snapround/MCIndexSnapRounder.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/snapround/MCIndexSnapRounder.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/snapround/MCIndexSnapRounder.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -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);
+      }
+  }
+}
+
+}

Deleted: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/snapround/SegmentSnapper.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/snapround/SegmentSnapper.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/snapround/SegmentSnapper.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -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;
-  }
-}

Deleted: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/snapround/SimpleSegmentStringsSnapper.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/snapround/SimpleSegmentStringsSnapper.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/snapround/SimpleSegmentStringsSnapper.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -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++;
-      }
-    }
-  }
-}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/snapround/SimpleSnapRounder.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/snapround/SimpleSnapRounder.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/snapround/SimpleSnapRounder.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -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;
+  }
+
+}

Deleted: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/snapround/SnapRounder.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/snapround/SnapRounder.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/snapround/SnapRounder.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -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;
-  }
-
-}

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/GeometryGraphOperation.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/GeometryGraphOperation.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/GeometryGraphOperation.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -44,7 +44,7 @@
 /**
  * The base class for operations that require {@link GeometryGraph)s.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class GeometryGraphOperation
 {

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/IsSimpleOp.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/IsSimpleOp.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/IsSimpleOp.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -41,13 +41,25 @@
 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 {
 

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/buffer/BufferBuilder.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/buffer/BufferBuilder.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/buffer/BufferBuilder.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -35,7 +35,7 @@
 package com.vividsolutions.jts.operation.buffer;
 
 /**
- * @version 1.6
+ * @version 1.7
  */
 
 import java.util.*;
@@ -58,7 +58,7 @@
  * 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 @@
   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 @@
     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 @@
     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);
     }

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/buffer/BufferOp.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/buffer/BufferOp.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/buffer/BufferOp.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -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 @@
  * <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 @@
   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 @@
     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 @@
     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 @@
 
     // 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);
   }
-
 }

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/buffer/BufferSubgraph.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/buffer/BufferSubgraph.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/buffer/BufferSubgraph.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -35,7 +35,7 @@
 package com.vividsolutions.jts.operation.buffer;
 
 /**
- * @version 1.6
+ * @version 1.7
  */
 
 import java.util.*;
@@ -55,7 +55,7 @@
  * </ul>
  *
  *
- * @version 1.6
+ * @version 1.7
  */
 public class BufferSubgraph
   implements Comparable
@@ -64,6 +64,7 @@
   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 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()

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/buffer/OffsetCurveBuilder.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/buffer/OffsetCurveBuilder.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/buffer/OffsetCurveBuilder.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -46,7 +46,7 @@
  * 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 @@
    * 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
    */

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/buffer/OffsetCurveSetBuilder.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/buffer/OffsetCurveSetBuilder.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/buffer/OffsetCurveSetBuilder.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -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 @@
  * 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 @@
     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 @@
 
       // 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 @@
    * @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 @@
     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 @@
      * 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);
+    */
   }
 
   /**

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/buffer/RightmostEdgeFinder.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/buffer/RightmostEdgeFinder.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/buffer/RightmostEdgeFinder.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -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 @@
  * 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 {
 

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/buffer/SubgraphDepthLocater.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/buffer/SubgraphDepthLocater.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/buffer/SubgraphDepthLocater.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -44,7 +44,7 @@
  * 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 @@
     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 @@
         depth = dirEdge.getDepth(Position.RIGHT);
       DepthSegment ds = new DepthSegment(seg, depth);
       stabbedSegments.add(ds);
-
-
     }
   }
 
@@ -167,7 +172,6 @@
   private class DepthSegment
       implements Comparable
   {
-
     private LineSegment upwardSeg;
     private int leftDepth;
 

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/distance/ConnectedElementLocationFilter.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/distance/ConnectedElementLocationFilter.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/distance/ConnectedElementLocationFilter.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -43,7 +43,7 @@
  * 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

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/distance/ConnectedElementPointFilter.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/distance/ConnectedElementPointFilter.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/distance/ConnectedElementPointFilter.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -42,7 +42,7 @@
  * (e.g. a polygon, linestring or point)
  * and returns them in a list
  *
- * @version 1.6
+ * @version 1.7
  */
 public class ConnectedElementPointFilter
   implements GeometryFilter

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/distance/DistanceOp.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/distance/DistanceOp.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/distance/DistanceOp.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -51,7 +51,7 @@
  * comparisons.  This worst-case performance could be improved on
  * by using Voronoi techniques.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class DistanceOp {
 

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/distance/GeometryLocation.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/distance/GeometryLocation.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/distance/GeometryLocation.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -43,7 +43,7 @@
  * 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
 {

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/linemerge/EdgeString.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/linemerge/EdgeString.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/linemerge/EdgeString.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -47,7 +47,7 @@
  * 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;

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/linemerge/LineMergeDirectedEdge.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/linemerge/LineMergeDirectedEdge.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/linemerge/LineMergeDirectedEdge.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -42,7 +42,7 @@
  * A {@link com.vividsolutions.jts.planargraph.DirectedEdge} of a 
  * {@link LineMergeGraph}. 
  *
- * @version 1.6
+ * @version 1.7
  */
 public class LineMergeDirectedEdge extends DirectedEdge {
   /**

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/linemerge/LineMergeEdge.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/linemerge/LineMergeEdge.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/linemerge/LineMergeEdge.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -40,7 +40,7 @@
  * 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;

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/linemerge/LineMergeGraph.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/linemerge/LineMergeGraph.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/linemerge/LineMergeGraph.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -47,7 +47,7 @@
  * 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 {
   /**

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/linemerge/LineMerger.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/linemerge/LineMerger.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/linemerge/LineMerger.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -56,7 +56,7 @@
  * 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 {
   /**

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/linemerge/LineSequencer.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/linemerge/LineSequencer.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/linemerge/LineSequencer.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -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);
+  }
+
+}

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/overlay/EdgeSetNoder.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/overlay/EdgeSetNoder.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/overlay/EdgeSetNoder.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -45,7 +45,7 @@
  * 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 {
 

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/overlay/LineBuilder.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/overlay/LineBuilder.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/overlay/LineBuilder.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -42,7 +42,7 @@
  * 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;

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/overlay/MaximalEdgeRing.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/overlay/MaximalEdgeRing.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/overlay/MaximalEdgeRing.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -54,7 +54,7 @@
  * 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

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/overlay/MinimalEdgeRing.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/overlay/MinimalEdgeRing.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/overlay/MinimalEdgeRing.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -43,7 +43,7 @@
  * 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

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/overlay/OverlayNodeFactory.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/overlay/OverlayNodeFactory.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/overlay/OverlayNodeFactory.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -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 @@
  * Creates nodes for use in the {@link PlanarGraph}s constructed during
  * overlay operations.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class OverlayNodeFactory
   extends NodeFactory

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/overlay/OverlayOp.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/overlay/OverlayOp.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/overlay/OverlayOp.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -46,7 +46,7 @@
  * 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 @@
   }
 
   /**
-   * 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 @@
    */
   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();

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/overlay/PointBuilder.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/overlay/PointBuilder.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/overlay/PointBuilder.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -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 @@
 
 /**
  * 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;
   }
 }

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/overlay/PolygonBuilder.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/overlay/PolygonBuilder.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/overlay/PolygonBuilder.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -43,14 +43,14 @@
  * 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 @@
       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) {

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/polygonize/EdgeRing.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/polygonize/EdgeRing.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/polygonize/EdgeRing.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -44,7 +44,7 @@
  * 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 {
 

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/polygonize/PolygonizeDirectedEdge.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/polygonize/PolygonizeDirectedEdge.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/polygonize/PolygonizeDirectedEdge.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -42,7 +42,7 @@
  * 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

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/polygonize/PolygonizeEdge.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/polygonize/PolygonizeEdge.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/polygonize/PolygonizeEdge.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -41,7 +41,7 @@
 /**
  * An edge of a polygonization graph.
  *
- * @version 1.6
+ * @version 1.7
  */
 class PolygonizeEdge
     extends Edge

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/polygonize/PolygonizeGraph.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/polygonize/PolygonizeGraph.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/polygonize/PolygonizeGraph.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -48,7 +48,7 @@
  * 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 @@
    * @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 @@
     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();

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/polygonize/Polygonizer.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/polygonize/Polygonizer.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/polygonize/Polygonizer.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -53,7 +53,7 @@
  * (e.g. the component lines contain a self-intersection)
  * </ul>
  *
- * @version 1.6
+ * @version 1.7
  */
 public class Polygonizer
 {

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/predicate/RectangleContains.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/predicate/RectangleContains.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/predicate/RectangleContains.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -10,7 +10,7 @@
  * 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 @@
     return false;
   }
 
-}
+}
\ No newline at end of file

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/predicate/RectangleIntersects.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/predicate/RectangleIntersects.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/predicate/RectangleIntersects.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -13,7 +13,7 @@
  * 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 @@
   }
 }
 
+/**
+ * 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 @@
     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 @@
     }
     /**
      * 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 @@
   }
 }
 
+/**
+ * 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 @@
     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 @@
   }
 }
 
+/**
+ * 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 @@
     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 @@
       intersects = rectangle.relate(geom).isIntersects();
       return;
     }
+    // if small enough, test for segment intersection directly
     computeSegmentIntersection(geom);
   }
 

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/predicate/SegmentIntersectionTester.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/predicate/SegmentIntersectionTester.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/predicate/SegmentIntersectionTester.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -9,7 +9,7 @@
  * 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 @@
     }
     return hasIntersection;
   }
-}
+}
\ No newline at end of file

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/relate/EdgeEndBuilder.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/relate/EdgeEndBuilder.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/relate/EdgeEndBuilder.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -40,7 +40,7 @@
  * 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 @@
 /**
  * Computes the {@link EdgeEnd}s which arise from a noded {@link Edge}.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class EdgeEndBuilder {
 

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/relate/EdgeEndBundle.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/relate/EdgeEndBundle.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/relate/EdgeEndBundle.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -38,7 +38,7 @@
 /**
  * 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 @@
 /**
  * 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 @@
       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 @@
       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);

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/relate/EdgeEndBundleStar.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/relate/EdgeEndBundleStar.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/relate/EdgeEndBundleStar.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -46,7 +46,7 @@
  * 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

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/relate/RelateComputer.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/relate/RelateComputer.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/relate/RelateComputer.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -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 @@
  * would first need to be noded and merged (if not explicitly, at least
  * implicitly).
  *
- * @version 1.6
+ * @version 1.7
  */
 public class RelateComputer
 {

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/relate/RelateNode.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/relate/RelateNode.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/relate/RelateNode.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -39,7 +39,7 @@
  * 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 @@
 /**
  * Represents a node in the topological graph used to compute spatial relationships.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class RelateNode
   extends Node

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/relate/RelateNodeFactory.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/relate/RelateNodeFactory.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/relate/RelateNodeFactory.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -40,7 +40,7 @@
 /**
  * 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

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/relate/RelateNodeGraph.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/relate/RelateNodeGraph.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/relate/RelateNodeGraph.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -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 @@
  * have their topology determined implicitly, without creating a Node object
  * to represent them.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class RelateNodeGraph {
 

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/relate/RelateOp.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/relate/RelateOp.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/relate/RelateOp.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -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 @@
 /**
  * Implements the relate() operation on {@link Geometry}s.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class RelateOp
   extends GeometryGraphOperation

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/valid/ConnectedInteriorTester.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/valid/ConnectedInteriorTester.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/valid/ConnectedInteriorTester.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -43,17 +43,17 @@
 
 /**
  * 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 {
 

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/valid/ConsistentAreaTester.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/valid/ConsistentAreaTester.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/valid/ConsistentAreaTester.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -45,17 +45,23 @@
 /**
  * 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 @@
   // 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 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 @@
   /**
    * 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 @@
     }
     return true;
   }
+
   /**
    * Checks for two duplicate rings in an area.
    * Duplicate rings are rings that are topologically equal

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/valid/IsValidOp.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/valid/IsValidOp.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/valid/IsValidOp.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -38,14 +38,14 @@
 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 @@
   }
 
   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 @@
     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 @@
   {
     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 @@
   {
     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 @@
   {
     checkInvalidCoordinates(g);
     if (validErr != null) return;
+    checkClosedRings(g);
+    if (validErr != null) return;
 
     GeometryGraph graph = new GeometryGraph(0, g);
 
@@ -182,8 +224,11 @@
     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 @@
     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 @@
     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 @@
                           TopologyValidationError.INVALID_COORDINATE,
                           coords[i]);
         return;
-
       }
     }
   }
+
   private void checkInvalidCoordinates(Polygon poly)
   {
     checkInvalidCoordinates(poly.getExteriorRing().getCoordinates());
@@ -254,6 +303,24 @@
     }
   }
 
+  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 @@
     }
   }
 
+  /**
+   * 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 @@
     }
   }
 
+  /**
+   * 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 @@
     }
   }
 
-  /* 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 @@
   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 @@
 
       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 ) {

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/valid/QuadtreeNestedRingTester.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/valid/QuadtreeNestedRingTester.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/valid/QuadtreeNestedRingTester.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -45,7 +45,7 @@
  * 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
 {

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/valid/RepeatedPointTester.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/valid/RepeatedPointTester.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/valid/RepeatedPointTester.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -41,7 +41,7 @@
  * (consecutive identical coordinates) as defined in the
  * JTS spec.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class RepeatedPointTester {
 

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/valid/SimpleNestedRingTester.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/valid/SimpleNestedRingTester.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/valid/SimpleNestedRingTester.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -44,11 +44,10 @@
  * 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 @@
         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;

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/valid/SweeplineNestedRingTester.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/valid/SweeplineNestedRingTester.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/valid/SweeplineNestedRingTester.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -45,16 +45,14 @@
  * 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 @@
     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;

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/valid/TopologyValidationError.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/valid/TopologyValidationError.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/valid/TopologyValidationError.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -40,22 +40,78 @@
  * 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 @@
     "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;
   }
 }

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/planargraph/DirectedEdge.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/planargraph/DirectedEdge.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/planargraph/DirectedEdge.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -31,8 +31,6 @@
  *     (250)385-6040
  *     www.vividsolutions.com
  */
-
-
 package com.vividsolutions.jts.planargraph;
 
 import java.util.*;
@@ -48,7 +46,7 @@
  * 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 @@
    * <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 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 @@
     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);
   }
 
   /**

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/planargraph/DirectedEdgeStar.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/planargraph/DirectedEdgeStar.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/planargraph/DirectedEdgeStar.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -42,7 +42,7 @@
  * 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
 {

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/planargraph/Edge.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/planargraph/Edge.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/planargraph/Edge.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -41,13 +41,16 @@
  * 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 @@
 
   /**
    * 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 @@
     // 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;
+  }
+
 }

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/planargraph/GraphComponent.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/planargraph/GraphComponent.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/planargraph/GraphComponent.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -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 @@
  * 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 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();
 }

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/planargraph/Node.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/planargraph/Node.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/planargraph/Node.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -45,7 +45,7 @@
  * 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 @@
     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;
+  }
+
 }

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/planargraph/NodeMap.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/planargraph/NodeMap.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/planargraph/NodeMap.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -44,7 +44,7 @@
 /**
  * A map of {@link Node}s, indexed by the coordinate of the node.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class NodeMap
 

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/planargraph/PlanarGraph.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/planargraph/PlanarGraph.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/planargraph/PlanarGraph.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -48,23 +48,28 @@
  * 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 Set edges = new HashSet();
+  protected Set dirEdges = new HashSet();
+  protected NodeMap nodeMap = new NodeMap();
 
-
-  protected List edges = new ArrayList();
-  protected List dirEdges = new ArrayList();
-  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,8 +112,30 @@
   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 @@
    * @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 @@
     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 @@
     }
     // remove the node from the graph
     nodeMap.remove(node.getCoordinate());
-    //nodes.remove(node);
+    node.remove();
   }
 
   /**

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/planargraph/Subgraph.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/planargraph/Subgraph.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/planargraph/Subgraph.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -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

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/planargraph/algorithm/ConnectedSubgraphFinder.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/planargraph/algorithm/ConnectedSubgraphFinder.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/planargraph/algorithm/ConnectedSubgraphFinder.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -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

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/precision/CommonBits.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/precision/CommonBits.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/precision/CommonBits.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -40,7 +40,7 @@
  * 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 {
 

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/precision/CommonBitsOp.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/precision/CommonBitsOp.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/precision/CommonBitsOp.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -44,7 +44,7 @@
  * 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 {
 

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/precision/CommonBitsRemover.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/precision/CommonBitsRemover.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/precision/CommonBitsRemover.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -38,7 +38,7 @@
 /**
  * Allow computing and removing common mantissa bits from one or more Geometries.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class CommonBitsRemover
 {

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/precision/EnhancedPrecisionOp.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/precision/EnhancedPrecisionOp.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/precision/EnhancedPrecisionOp.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -40,7 +40,7 @@
   * 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
 {

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/precision/SimpleGeometryPrecisionReducer.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/precision/SimpleGeometryPrecisionReducer.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/precision/SimpleGeometryPrecisionReducer.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -48,7 +48,7 @@
  * 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 @@
    * 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
    */

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/simplify/DouglasPeuckerLineSimplifier.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/simplify/DouglasPeuckerLineSimplifier.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/simplify/DouglasPeuckerLineSimplifier.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -7,7 +7,7 @@
  * Simplifies a linestring (sequence of points) using
  * the standard Douglas-Peucker algorithm.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class DouglasPeuckerLineSimplifier
 {

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/simplify/DouglasPeuckerSimplifier.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/simplify/DouglasPeuckerSimplifier.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/simplify/DouglasPeuckerSimplifier.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -16,7 +16,7 @@
  * 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 @@
    * 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;
   }
 

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/simplify/TaggedLineString.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/simplify/TaggedLineString.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/simplify/TaggedLineString.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -4,7 +4,7 @@
 import com.vividsolutions.jts.geom.*;
 
 /**
- * @version 1.6
+ * @version 1.7
  */
 public class TaggedLineString {
 
@@ -77,4 +77,4 @@
   }
 
 
-}
+}
\ No newline at end of file

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/simplify/TaggedLineStringSimplifier.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/simplify/TaggedLineStringSimplifier.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/simplify/TaggedLineStringSimplifier.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -10,7 +10,7 @@
  * (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 @@
       inputIndex.remove(seg);
     }
   }
-}
+}
\ No newline at end of file

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/simplify/TopologyPreservingSimplifier.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/simplify/TopologyPreservingSimplifier.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/simplify/TopologyPreservingSimplifier.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -46,10 +46,14 @@
    * 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);
   }
 

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/util/Assert.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/util/Assert.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/util/Assert.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -39,7 +39,7 @@
 /**
  *  A utility for making programming assertions.
  *
- *@version 1.6
+ *@version 1.7
  */
 public class Assert {
 

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/util/AssertionFailedException.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/util/AssertionFailedException.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/util/AssertionFailedException.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -38,7 +38,7 @@
  *  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 {
 

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/util/CollectionUtil.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/util/CollectionUtil.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/util/CollectionUtil.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -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

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/util/CoordinateArrayFilter.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/util/CoordinateArrayFilter.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/util/CoordinateArrayFilter.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -40,7 +40,7 @@
  *  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;

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/util/CoordinateCountFilter.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/util/CoordinateCountFilter.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/util/CoordinateCountFilter.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -40,7 +40,7 @@
  *  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;

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/util/Debug.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/util/Debug.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/util/Debug.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -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 @@
   }
 
 
+  /**
+   * 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 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;

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/util/GeometricShapeFactory.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/util/GeometricShapeFactory.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/util/GeometricShapeFactory.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -42,8 +42,17 @@
  * 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 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; }
 

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/util/Stopwatch.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/util/Stopwatch.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/util/Stopwatch.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -37,26 +37,59 @@
  * 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;
   }
 

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/util/UniqueCoordinateArrayFilter.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/util/UniqueCoordinateArrayFilter.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/util/UniqueCoordinateArrayFilter.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -44,7 +44,7 @@
  *  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();

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/geom/BasicExample.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/geom/BasicExample.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/geom/BasicExample.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -41,7 +41,7 @@
 /**
  * Shows basic ways of creating and operating on geometries
  *
- * @version 1.6
+ * @version 1.7
  */
 public class BasicExample
 {

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/geom/ConstructionExample.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/geom/ConstructionExample.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/geom/ConstructionExample.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -16,7 +16,7 @@
  * <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 @@
     System.out.println(mpt);
 
   }
-}
+}
\ No newline at end of file

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/geom/ExtendedCoordinate.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/geom/ExtendedCoordinate.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/geom/ExtendedCoordinate.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -37,7 +37,7 @@
 
 
 /**
- * @version 1.6
+ * @version 1.7
  */
 public class ExtendedCoordinate
     extends Coordinate

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/geom/ExtendedCoordinateExample.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/geom/ExtendedCoordinateExample.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/geom/ExtendedCoordinateExample.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -37,7 +37,7 @@
 
 
 /**
- * @version 1.6
+ * @version 1.7
  */
 public class ExtendedCoordinateExample
 {

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/geom/ExtendedCoordinateSequence.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/geom/ExtendedCoordinateSequence.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/geom/ExtendedCoordinateSequence.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -42,7 +42,7 @@
  * 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 @@
     }
   }
 
+  /**
+   * @see com.vividsolutions.jts.geom.CoordinateSequence#getDimension()
+   */
+  public int getDimension() { return 4; }
 
   public Coordinate getCoordinate(int i) {
     return coordinates[i];

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/geom/ExtendedCoordinateSequenceFactory.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/geom/ExtendedCoordinateSequenceFactory.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/geom/ExtendedCoordinateSequenceFactory.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -39,7 +39,7 @@
  * Creates ExtendedCoordinateSequenceFactory internally represented
  * as an array of {@link ExtendedCoordinate}s.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class ExtendedCoordinateSequenceFactory
     implements CoordinateSequenceFactory

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/geom/PrecisionModelExample.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/geom/PrecisionModelExample.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/geom/PrecisionModelExample.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -45,7 +45,7 @@
  * The output shows the effects of rounding in the single-precision and fixed-precision
  * models.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class PrecisionModelExample
 {

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/geom/SimpleMethodsExample.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/geom/SimpleMethodsExample.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/geom/SimpleMethodsExample.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -54,7 +54,7 @@
  * ----------------------------------------------------------
  * </pre>
  *
- * @version 1.6
+ * @version 1.7
  */
 public class SimpleMethodsExample
 {

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/linearref/LinearRefExample.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/linearref/LinearRefExample.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/linearref/LinearRefExample.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -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

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/operation/distance/ClosestPointExample.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/operation/distance/ClosestPointExample.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/operation/distance/ClosestPointExample.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -41,7 +41,7 @@
  * Example of computing distance and closest points between geometries
  * using the DistanceOp class.
  *
- * @version 1.6
+ * @version 1.7
  */
 public class ClosestPointExample
 {

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/operation/linemerge/LineMergeExample.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/operation/linemerge/LineMergeExample.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/operation/linemerge/LineMergeExample.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -45,7 +45,7 @@
  * 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();

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/operation/polygonize/PolygonizeExample.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/operation/polygonize/PolygonizeExample.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/operation/polygonize/PolygonizeExample.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -43,7 +43,7 @@
 /**
  *  Example of using Polygonizer class to polygonize a set of fully noded linestrings
  *
- * @version 1.6
+ * @version 1.7
  */
 public class PolygonizeExample
 {

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/precision/EnhancedPrecisionOpExample.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/precision/EnhancedPrecisionOpExample.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/precision/EnhancedPrecisionOpExample.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -42,7 +42,7 @@
 /**
  * Example of using {@link EnhancedPrecisionOp} to avoid robustness problems
  *
- * @version 1.6
+ * @version 1.7
  */
 public class EnhancedPrecisionOpExample
 {

Modified: packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/technique/LineStringSelfIntersections.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/technique/LineStringSelfIntersections.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/technique/LineStringSelfIntersections.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -8,7 +8,7 @@
  * 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 @@
   }
 
 
-}
+}
\ No newline at end of file

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/technique/PolygonUnionUsingBuffer.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/technique/PolygonUnionUsingBuffer.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/technique/PolygonUnionUsingBuffer.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -0,0 +1,50 @@
+package com.vividsolutions.jtsexample.technique;
+
+import com.vividsolutions.jts.geom.*;
+import com.vividsolutions.jts.io.WKTReader;
+import java.util.*;
+
+/**
+ * Shows a technique for using a zero-width buffer to compute
+ * 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 (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.7
+ */
+
+public class PolygonUnionUsingBuffer {
+
+  public static void main(String[] args)
+      throws Exception
+  {
+    WKTReader rdr = new WKTReader();
+
+    Geometry[] geom = new Geometry[3];
+    geom[0] = rdr.read("POLYGON (( 100 180, 100 260, 180 260, 180 180, 100 180 ))");
+    geom[1] = rdr.read("POLYGON (( 80 140, 80 200, 200 200, 200 140, 80 140 ))");
+    geom[2] = rdr.read("POLYGON (( 160 160, 160 240, 240 240, 240 160, 160 160 ))");
+    unionUsingBuffer(geom);
+
+  }
+
+  public static void unionUsingBuffer(Geometry[] geom)
+  {
+    GeometryFactory fact = geom[0].getFactory();
+    Geometry geomColl = fact.createGeometryCollection(geom);
+    Geometry union = geomColl.buffer(0.0);
+    System.out.println(union);
+  }
+
+
+
+}
\ No newline at end of file

Deleted: packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/technique/UnionUsingBuffer.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/technique/UnionUsingBuffer.java	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/technique/UnionUsingBuffer.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -1,49 +0,0 @@
-package com.vividsolutions.jtsexample.technique;
-
-import com.vividsolutions.jts.geom.*;
-import com.vividsolutions.jts.io.WKTReader;
-import java.util.*;
-
-/**
- * Shows a technique for using a zero-width buffer to compute
- * unions of 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
- * </ul>
- * Disadvantages are:
- * <ul>
- * <li>may not preserve input coordinate precision in some cases
- * </ul>
- *
- * @version 1.6
- */
-
-public class UnionUsingBuffer {
-
-  public static void main(String[] args)
-      throws Exception
-  {
-    WKTReader rdr = new WKTReader();
-
-    Geometry[] geom = new Geometry[3];
-    geom[0] = rdr.read("POLYGON (( 100 180, 100 260, 180 260, 180 180, 100 180 ))");
-    geom[1] = rdr.read("POLYGON (( 80 140, 80 200, 200 200, 200 140, 80 140 ))");
-    geom[2] = rdr.read("POLYGON (( 160 160, 160 240, 240 240, 240 160, 160 160 ))");
-    unionUsingBuffer(geom);
-
-  }
-
-  public static void unionUsingBuffer(Geometry[] geom)
-  {
-    GeometryFactory fact = geom[0].getFactory();
-    Geometry geomColl = fact.createGeometryCollection(geom);
-    Geometry union = geomColl.buffer(0.0);
-    System.out.println(union);
-  }
-
-
-
-}

Added: packages/jts/branches/upstream/current/src/jtsio/src/com/vividsolutions/jts/io/gml2/GMLConstants.java
===================================================================
--- packages/jts/branches/upstream/current/src/jtsio/src/com/vividsolutions/jts/io/gml2/GMLConstants.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/jtsio/src/com/vividsolutions/jts/io/gml2/GMLConstants.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -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

Added: packages/jts/branches/upstream/current/src/jtsio/src/com/vividsolutions/jts/io/gml2/GMLHandler.java
===================================================================
--- packages/jts/branches/upstream/current/src/jtsio/src/com/vividsolutions/jts/io/gml2/GMLHandler.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/jtsio/src/com/vividsolutions/jts/io/gml2/GMLHandler.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -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);
+	}
+
+}

Added: packages/jts/branches/upstream/current/src/jtsio/src/com/vividsolutions/jts/io/gml2/GMLReader.java
===================================================================
--- packages/jts/branches/upstream/current/src/jtsio/src/com/vividsolutions/jts/io/gml2/GMLReader.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/jtsio/src/com/vividsolutions/jts/io/gml2/GMLReader.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -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();
+	}
+	
+}

Added: packages/jts/branches/upstream/current/src/jtsio/src/com/vividsolutions/jts/io/gml2/GMLWriter.java
===================================================================
--- packages/jts/branches/upstream/current/src/jtsio/src/com/vividsolutions/jts/io/gml2/GMLWriter.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/jtsio/src/com/vividsolutions/jts/io/gml2/GMLWriter.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -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");
+	  }
+}

Added: packages/jts/branches/upstream/current/src/jtsio/src/com/vividsolutions/jts/io/gml2/GeometryStrategies.java
===================================================================
--- packages/jts/branches/upstream/current/src/jtsio/src/com/vividsolutions/jts/io/gml2/GeometryStrategies.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/jtsio/src/com/vividsolutions/jts/io/gml2/GeometryStrategies.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -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());
+	}
+}

Added: packages/jts/branches/upstream/current/src/jtsio/src/com/vividsolutions/jts/io/oracle/Constants.java
===================================================================
--- packages/jts/branches/upstream/current/src/jtsio/src/com/vividsolutions/jts/io/oracle/Constants.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/jtsio/src/com/vividsolutions/jts/io/oracle/Constants.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -0,0 +1,123 @@
+/*
+ * 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
+ */
+/*
+ *    Geotools2 - OpenSource mapping toolkit
+ *    http://geotools.org
+ *    (C) 2003, Geotools Project Managment Committee (PMC)
+ *
+ *    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;
+ *    version 2.1 of the License.
+ *
+ *    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.
+ *
+ */
+package com.vividsolutions.jts.io.oracle;
+
+/**
+ * Set of constants used to interact with MDSYS.GEOMETRY and JTS Geometries. 
+ * 
+ *
+ * @author David Zwiers, Vivid Solutions.
+ */
+class Constants {
+	
+	/**
+	 * Null SRID
+	 */
+	public static final int SRID_NULL = -1;
+	
+	/**
+	 * 
+	 * Extracted from the Oracle Documentation for SDO_ETYPE
+	 * 
+	 * This list may need to be expanded in the future to handle additional Geometry Types.
+	 *
+	 * @author David Zwiers, Vivid Solutions.
+	 * @author Jody Garnett, Refractions Research, Inc.
+	 */
+	static final class SDO_ETYPE{
+
+	    /** <code>ETYPE</code> code representing Point */
+	    public static final int POINT = 1;
+	
+	    /** <code>ETYPE</code> code representing Line */
+	    public static final int LINE = 2;
+	    
+		/** <code>ETYPE</code> code representing Polygon */
+		public static final int POLYGON = 3;
+	
+	    /** <code>ETYPE</code> code representing exterior counterclockwise  polygon ring */
+	    public static final int POLYGON_EXTERIOR = 1003;
+	
+	    /** <code>ETYPE</code> code representing interior clockwise  polygon ring */
+	    public static final int POLYGON_INTERIOR = 2003;
+	}
+    
+	/**
+	 * Extracted from the Oracle Documentation for SDO_GTYPE.
+	 * This represents the last two digits in a GTYPE, where the first id dimension and the second if LRS
+	 * 
+	 * This list may need to be expanded in the future to handle additional Geometry Types.
+	 *
+	 * @author David Zwiers, Vivid Solutions.
+	 * @author Brent Owens, The Open Planning Project.
+	 */
+	static final class SDO_GTEMPLATE{
+	
+		/** <code>TT</code> code representing Point */
+		public static final int POINT         = 01;
+	
+		/** <code>TT</code> code representing Line (or Curve) */
+		public static final int LINE          = 02;  
+	    
+		/** <code>TT</code> code representing Polygon */
+		public static final int POLYGON       = 03;
+	
+		/** <code>TT</code> code representing Collection */
+		public static final int COLLECTION    = 04;   
+	
+		/** <code>TT</code> code representing Multpoint */
+		public static final int MULTIPOINT    = 05;       
+	
+		/** <code>TT</code> code representing Multiline (or Multicurve) */
+		public static final int MULTILINE     = 06;
+	
+		/** <code>TT</code> code representing MULTIPOLYGON */
+		public static final int MULTIPOLYGON  = 07;
+	}
+}

Added: packages/jts/branches/upstream/current/src/jtsio/src/com/vividsolutions/jts/io/oracle/OraReader.java
===================================================================
--- packages/jts/branches/upstream/current/src/jtsio/src/com/vividsolutions/jts/io/oracle/OraReader.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/jtsio/src/com/vividsolutions/jts/io/oracle/OraReader.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -0,0 +1,821 @@
+/*
+ * 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
+ */
+/*
+ *    Geotools2 - OpenSource mapping toolkit
+ *    http://geotools.org
+ *    (C) 2003, Geotools Project Managment Committee (PMC)
+ *
+ *    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;
+ *    version 2.1 of the License.
+ *
+ *    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.
+ *
+ */
+package com.vividsolutions.jts.io.oracle;
+
+import java.sql.SQLException;
+import java.util.*;
+
+import oracle.sql.*;
+
+import com.vividsolutions.jts.algorithm.CGAlgorithms;
+import com.vividsolutions.jts.geom.*;
+
+/**
+ * Creates a Geometry from an MDSYS.GEOMETRY oracle object.
+ * 
+ * You may optionally provide a GeometryFactory. The GeometryFactory provided
+ * will be used, with the exception of the SRID field. This will be extracted
+ * from the Geometry.
+ * 
+ * @version 9i
+ * @author David Zwiers, Vivid Solutions.
+ */
+public class OraReader {
+	private GeometryFactory geometryFactory;
+
+	/**
+	 * Creates a default GeometryFactory
+	 * 
+	 * @see #OraReader(GeometryFactory)
+	 */
+	public OraReader() {
+		this(new GeometryFactory());
+	}
+
+	/**
+	 * Stores reference to the GeometryFactory provided for future use.
+	 * 
+	 * @param gf
+	 *            A non-null geometry factory for later use.
+	 * @throw NullPointerException when the geometry factory is null.
+	 */
+	public OraReader(GeometryFactory gf) {
+		if (gf == null)
+			throw new NullPointerException("Geometry Factory may not be Null");
+		this.geometryFactory = gf;
+	}
+
+	/**
+	 * This method will attempt to create a JTS Geometry for the MDSYS.GEOMETRY 
+	 * provided. The Type of gemetry returned will depend on the input datum, 
+	 * where the Geometry type is specified within the STRUCT.
+	 * 
+	 * @param struct The MDSYS.GEOMETRY Object to decode
+	 * @return A JTS Geometry if one could be created, null otherwise
+	 * @throws SQLException When a read error occured within the struct
+	 */
+	public Geometry read(STRUCT struct) throws SQLException {
+
+		// Note Returning null for null Datum
+		if (struct == null)
+			return null;
+
+		Datum data[] = struct.getOracleAttributes();
+		int gType = asInteger(data[0], 0);
+		int SRID = asInteger(data[1], Constants.SRID_NULL);
+		double point[] = asDoubleArray((STRUCT) data[2], Double.NaN);
+		int elemInfo[] = asIntArray((ARRAY) data[3], 0);
+		double ordinates[] = asDoubleArray((ARRAY) data[4], Double.NaN);
+		GeometryFactory gf = geometryFactory;
+		if(geometryFactory.getSRID() != SRID){
+			// clone it and use the geom's srid
+			gf = new GeometryFactory(geometryFactory.getPrecisionModel(),SRID,geometryFactory.getCoordinateSequenceFactory());
+		}
+
+		return create(gf, gType, point, elemInfo, ordinates);
+	}
+	
+	/**
+     * Decode geometry from provided SDO encoded information.
+     * 
+     * <p></p>
+     *
+     * @param gf Used to construct returned Geometry
+     * @param gType SDO_GTEMPLATE represents dimension, LRS, and geometry type
+     * @param point
+     * @param elemInfo
+     * @param ordinates
+     *
+     * @return Geometry as encoded
+     */
+    private Geometry create(GeometryFactory gf, int gType,
+        double[] point, int[] elemInfo, double[] ordinates) {
+    	
+        int lrs = (gType%1000)/100;
+        
+        // find the dimension: represented by the smaller of the two dimensions
+        int dim = Math.min(gType/1000,gf.getCoordinateSequenceFactory().create(0,0).getDimension()) ;
+        
+        // extract the geometry template type
+        // this is represented as the rightmost two digits
+        int geomTemplate = gType - (dim * 1000) - (lrs * 100);
+
+        CoordinateSequence coords = null;
+
+        if (lrs == 0 && geomTemplate == 1 && point != null && elemInfo == null) {
+            // Single Point Type Optimization
+            coords = coordinates(gf.getCoordinateSequenceFactory(), dim, lrs, geomTemplate, point);
+            elemInfo = new int[] { 1, Constants.SDO_ETYPE.POINT, 1 };
+        } else {
+            coords = coordinates(gf.getCoordinateSequenceFactory(), dim, lrs, geomTemplate, ordinates);
+        }
+        
+        switch (geomTemplate) {
+        case Constants.SDO_GTEMPLATE.POINT:
+            return createPoint(gf, dim, lrs, elemInfo, 0, coords);
+
+        case Constants.SDO_GTEMPLATE.LINE:
+            return createLine(gf, dim, lrs, elemInfo, 0, coords);
+
+        case Constants.SDO_GTEMPLATE.POLYGON:
+            return createPolygon(gf, dim, lrs, elemInfo, 0, coords);
+
+        case Constants.SDO_GTEMPLATE.MULTIPOINT:
+            return createMultiPoint(gf, dim, lrs, elemInfo, 0, coords);
+
+        case Constants.SDO_GTEMPLATE.MULTILINE:
+            return createMultiLine(gf, dim, lrs, elemInfo, 0, coords, -1);
+
+        case Constants.SDO_GTEMPLATE.MULTIPOLYGON:
+            return createMultiPolygon(gf, dim, lrs, elemInfo, 0, coords, -1);
+
+        case Constants.SDO_GTEMPLATE.COLLECTION:
+            return createCollection(gf, dim, lrs, elemInfo, 0, coords,-1);
+        
+        default:
+            return null;    
+        }  
+    }
+
+    /**
+     * Construct CoordinateList as described by GTYPE.
+     * 
+     * The number of ordinates per coordinate are taken to be lrs+dim, and the
+     * number of ordinates should be a multiple of this value.
+
+     * In the Special case of GTYPE 2001 and a three ordinates are interpreted
+     * as a single Coordinate rather than an error.
+     *
+     * @param f CoordinateSequenceFactory used to encode ordiantes for JTS 
+     * @param ordinates
+     *
+     * @return protected
+     *
+     * @throws IllegalArgumentException 
+     */
+    private CoordinateSequence coordinates(CoordinateSequenceFactory f,
+        int dim, int lrs, int gtemplate, double[] ordinates) {
+        if ((ordinates == null) || (ordinates.length == 0)) {
+            return f.create(new Coordinate[0]);
+        }
+
+        //      POINT_TYPE Special Case
+        //
+        if ((dim == 2) && (lrs == 0) && (gtemplate == 01) && (ordinates.length == 3)) {
+            return f.create(new Coordinate[] {
+                    new Coordinate(ordinates[0], ordinates[1], ordinates[2]),
+                });
+        }
+
+        int len = dim + lrs;
+
+        if ((ordinates.length % len) != 0) {
+            throw new IllegalArgumentException("Dimension D:" + dim + " and L:"
+                + lrs + " denote Coordiantes " + "of " + len
+                + " ordinates. This cannot be resolved with"
+                + "an ordinate array of length " + ordinates.length);
+        }
+
+        int length = ordinates.length / len;
+
+
+//        if (lrs != 0) {
+        	// for each coordinate there are some exactly [lrs] measures
+        	// we would use a tri-state array here to mask which dimension 
+        	// in JTS is a measure, and which is a dimension. 
+//        }
+
+        // we would have to ask for a dimension which represents all the requested 
+        // dimension and measures from a mask array in the future
+        CoordinateSequence cs = f.create(length,dim);
+        
+        int actualDim = cs.getDimension();
+        for(int i=0;i<length;i++){
+        	int j=0;
+        	// in the future change this condition to include ignored dimensions from mask array
+        	for(;j<actualDim && j<dim;j++){
+        		cs.setOrdinate(i,j,ordinates[i*len+j]);
+        		// may not always want to inc. j when we have a mask array
+        	}
+        	// in the future change this condition to include ignored dimensions from mask array
+        	for(int d = j;j<actualDim && (j-d)<lrs;j++){
+        		cs.setOrdinate(i,j,ordinates[i*len+j]);
+        		// may not always want to inc. j when we have a mask array
+        	}
+        }
+        return cs;
+    }
+    
+    /**
+     * Create MultiGeometry as encoded by elemInfo.
+     * 
+     * @param gf Used to construct MultiLineString
+     * @param elemInfo Interpretation of coords
+     * @param elemIndex Triplet in elemInfo to process as a Polygon
+     * @param coords Coordinates to interpret using elemInfo
+     * @param numGeom Number of triplets (or -1 for rest)
+     *
+     * @return GeometryCollection
+     *
+     * @throws IllegalArgumentException DWhen faced with an encoding error
+     */
+    private GeometryCollection createCollection(GeometryFactory gf, int dim, int lrs, int[] elemInfo, int elemIndex, CoordinateSequence coords, int numGeom) {
+
+    	int sOffset = StartingOffset(elemInfo, elemIndex);
+
+        int length = coords.size()*dim;
+        
+		if (!(sOffset <= length))
+		    throw new IllegalArgumentException("ELEM_INFO STARTING_OFFSET "+sOffset+" inconsistent with ORDINATES length "+coords.size());
+		
+        int endTriplet = (numGeom != -1) ? elemIndex + numGeom : elemInfo.length / 3 + 1;
+
+        List list = new LinkedList();
+        int etype;
+        int interpretation;
+        Geometry geom;
+
+        boolean cont = true;
+        for (int i = elemIndex; cont && i < endTriplet; i++) {
+            etype = eType(elemInfo, i);
+            interpretation = interpretation(elemInfo, i);
+
+            switch (etype) {
+            case -1:
+                cont = false; // We are the of the list - get out of here
+
+            case Constants.SDO_ETYPE.POINT:
+
+                if (interpretation == 1) {
+                    geom = createPoint(gf, dim, lrs, elemInfo, i, coords);
+                } else if (interpretation > 1) {
+                    geom = createMultiPoint(gf, dim, lrs, elemInfo, i, coords);
+                } else {
+                    throw new IllegalArgumentException(
+                        "ETYPE.POINT requires INTERPRETATION >= 1");
+                }
+
+                break;
+
+            case Constants.SDO_ETYPE.LINE:
+                geom = createLine(gf, dim, lrs, elemInfo, i, coords);
+
+                break;
+
+            case Constants.SDO_ETYPE.POLYGON:
+            case Constants.SDO_ETYPE.POLYGON_EXTERIOR:
+                geom = createPolygon(gf, dim, lrs, elemInfo, i, coords);
+                i += ((Polygon) geom).getNumInteriorRing();
+
+                break;
+
+            case Constants.SDO_ETYPE.POLYGON_INTERIOR:
+                throw new IllegalArgumentException(
+                    "ETYPE 2003 (Polygon Interior) no expected in a GeometryCollection"
+                    + "(2003 is used to represent polygon holes, in a 1003 polygon exterior)");
+
+            default:
+                throw new IllegalArgumentException("ETYPE " + etype
+                    + " not representable as a JTS Geometry."
+                    + "(Custom and Compound Straight and Curved Geometries not supported)");
+            }
+
+            list.add(geom);
+        }
+
+        GeometryCollection geoms = gf.createGeometryCollection((Geometry[]) list.toArray(new Geometry[list.size()]));
+
+        return geoms;
+    }
+
+    /**
+     * Create MultiPolygon as encoded by elemInfo.
+     * 
+     *
+     * @param gf Used to construct MultiLineString
+     * @param elemInfo Interpretation of coords
+     * @param elemIndex Triplet in elemInfo to process as a Polygon
+     * @param coords Coordinates to interpret using elemInfo
+     * @param numGeom Number of triplets (or -1 for rest)
+     *
+     * @return MultiPolygon
+     */
+    private MultiPolygon createMultiPolygon(GeometryFactory gf, int dim, int lrs, int[] elemInfo, int elemIndex, CoordinateSequence coords, int numGeom){
+
+    	int sOffset = StartingOffset(elemInfo, elemIndex);
+        int etype = eType(elemInfo, elemIndex);
+        int interpretation = interpretation(elemInfo, elemIndex); 
+
+        int length = coords.size()*dim;
+        
+		if (!(sOffset >= 1) || !(sOffset <= length))
+		    throw new IllegalArgumentException("ELEM_INFO STARTING_OFFSET "+sOffset+" inconsistent with ORDINATES length "+coords.size());
+		if(!(etype == Constants.SDO_ETYPE.POLYGON) && !(etype == Constants.SDO_ETYPE.POLYGON_EXTERIOR))
+		    throw new IllegalArgumentException("ETYPE "+etype+" inconsistent with expected POLYGON or POLYGON_EXTERIOR");
+		if (interpretation != 1 && interpretation != 3){
+			return null;
+		}
+
+        int endTriplet = (numGeom != -1) ? elemIndex + numGeom : (elemInfo.length / 3) + 1;
+
+        List list = new LinkedList();
+        boolean cont = true;
+        
+        for (int i = elemIndex; cont && i < endTriplet && (etype = eType(elemInfo, i)) != -1; i++) {
+            if ((etype == Constants.SDO_ETYPE.POLYGON) || (etype == Constants.SDO_ETYPE.POLYGON_EXTERIOR)) {
+                Polygon poly = createPolygon(gf, dim, lrs, elemInfo, i, coords);
+                i += poly.getNumInteriorRing(); // skip interior rings
+                list.add(poly);
+            } else { // not a Polygon - get out here
+            	cont = false;
+            }
+        }
+
+        MultiPolygon polys = gf.createMultiPolygon((Polygon[]) list.toArray(new Polygon[list.size()]));
+
+        return polys;
+    }
+
+    /**
+     * Create MultiLineString as encoded by elemInfo.
+     * 
+     *
+     * @param gf Used to construct MultiLineString
+     * @param elemInfo Interpretation of coords
+     * @param elemIndex Triplet in elemInfo to process as a Polygon
+     * @param coords Coordinates to interpret using elemInfo
+     * @param numGeom Number of triplets (or -1 for rest)
+     *
+     * @return MultiLineString
+     */
+    private MultiLineString createMultiLine(GeometryFactory gf, int dim, int lrs, int[] elemInfo, int elemIndex, CoordinateSequence coords, int numGeom) {
+
+    	int sOffset = StartingOffset(elemInfo, elemIndex);
+        int etype = eType(elemInfo, elemIndex);
+        int interpretation = interpretation(elemInfo, elemIndex); 
+
+        int length = coords.size()*dim;
+        
+		if (!(sOffset >= 1) || !(sOffset <= length))
+		    throw new IllegalArgumentException("ELEM_INFO STARTING_OFFSET "+sOffset+" inconsistent with ORDINATES length "+coords.size());
+		if(!(etype == Constants.SDO_ETYPE.LINE))
+		    throw new IllegalArgumentException("ETYPE "+etype+" inconsistent with expected LINE");
+		if (!(interpretation == 1)){
+            // we cannot represent INTERPRETATION > 1 
+			return null;
+		}
+
+        int endTriplet = (numGeom != -1) ? (elemIndex + numGeom) : (elemInfo.length / 3);
+
+        List list = new LinkedList();
+
+        boolean cont = true;
+        for (int i = elemIndex; cont && i < endTriplet && (etype = eType(elemInfo, i)) != -1 ;i++) {
+            if (etype == Constants.SDO_ETYPE.LINE) {
+                list.add(createLine(gf, dim, lrs, elemInfo, i, coords));
+            } else { // not a LineString - get out of here
+                cont = false;
+            }
+        }
+
+        MultiLineString lines = gf.createMultiLineString((LineString[]) list.toArray(new LineString[list.size()]));
+
+        return lines;
+    }
+
+    /**
+     * Create MultiPoint as encoded by elemInfo.
+     * 
+     *
+     * @param gf Used to construct polygon
+     * @param elemInfo Interpretation of coords
+     * @param elemIndex Triplet in elemInfo to process as a Polygon
+     * @param coords Coordinates to interpret using elemInfo
+     *
+     * @return MultiPoint
+     */
+    private MultiPoint createMultiPoint(GeometryFactory gf, int dim, int lrs, int[] elemInfo, int elemIndex, CoordinateSequence coords) {
+
+    	int sOffset = StartingOffset(elemInfo, elemIndex);
+        int etype = eType(elemInfo, elemIndex);
+        int interpretation = interpretation(elemInfo, elemIndex);   
+
+		if (!(sOffset >= 1) || !(sOffset <= coords.size()))
+		    throw new IllegalArgumentException("ELEM_INFO STARTING_OFFSET "+sOffset+" inconsistent with ORDINATES length "+coords.size());
+		if(!(etype == Constants.SDO_ETYPE.POINT))
+		    throw new IllegalArgumentException("ETYPE "+etype+" inconsistent with expected POINT");
+		if (!(interpretation > 1)){
+			return null;
+		}
+
+        int len = dim + lrs;
+
+        int start = (sOffset - 1) / len;
+        int end = start + interpretation;
+
+        MultiPoint points = gf.createMultiPoint(subList(gf.getCoordinateSequenceFactory(), coords, start, end));
+
+        return points;
+    }
+    
+    /**
+     * Create Polygon as encoded.
+     * 
+     * @see #interpretation(int[], int)
+     * 
+     * @param gf Used to construct polygon
+     * @param elemInfo Interpretation of coords
+     * @param elemIndex Triplet in elemInfo to process as a Polygon
+     * @param coords Coordinates to interpret using elemInfo
+     *
+     * @return Polygon as encoded by elemInfo, or null when faced with and
+     *         encoding that can not be captured by JTS
+     * @throws IllegalArgumentException When faced with an invalid SDO encoding
+     */
+    private Polygon createPolygon(GeometryFactory gf, int dim, int lrs, int[] elemInfo, int elemIndex, CoordinateSequence coords) {
+
+    	int sOffset = StartingOffset(elemInfo, elemIndex);
+        int etype = eType(elemInfo, elemIndex);
+        int interpretation = interpretation(elemInfo, elemIndex);     
+        
+        if( !(1 <= sOffset && sOffset <= (coords.size() * dim))){
+            throw new IllegalArgumentException(
+                    "ELEM_INFO STARTING_OFFSET "+sOffset+
+                    "inconsistent with COORDINATES length "+(coords.size() * dim) );
+        } 
+        
+		if(!(etype == Constants.SDO_ETYPE.POLYGON) && !(etype == Constants.SDO_ETYPE.POLYGON_EXTERIOR)){
+			throw new IllegalArgumentException("ETYPE "+etype+" inconsistent with expected POLYGON or POLYGON_EXTERIOR");
+		}        
+		if (!(interpretation == 1) && !(interpretation == 3)){
+			return null;
+		}
+
+        LinearRing exteriorRing = createLinearRing(gf, dim, lrs, elemInfo, elemIndex, coords);
+
+        List rings = new LinkedList();
+
+        boolean cont = true;
+        for (int i = elemIndex + 1; cont && (etype = eType(elemInfo, i)) != -1; i++) {
+            if (etype == Constants.SDO_ETYPE.POLYGON_INTERIOR) {
+                rings.add(createLinearRing(gf, dim, lrs, elemInfo, i, coords));
+            } else if (etype == Constants.SDO_ETYPE.POLYGON) { // need to test Clockwiseness of Ring to see if it is
+                                                 // interior or not - (use POLYGON_INTERIOR to avoid pain)
+
+                LinearRing ring = createLinearRing(gf, dim, lrs, elemInfo, i, coords);
+
+                if (CGAlgorithms.isCCW(ring.getCoordinates())) { // it is an Interior Hole
+                    rings.add(ring);
+                } else { // it is the next Polygon! - get out of here
+                    cont = false;
+                }
+            } else { // not a LinearRing - get out of here
+                cont = false;
+            }
+        }
+
+        Polygon poly = gf.createPolygon(exteriorRing, (LinearRing[]) rings.toArray(new LinearRing[rings.size()]));
+
+        return poly;
+    }
+    
+    /**
+     * Create Linear Ring for exterior/interior polygon ELEM_INFO triplets.
+     * 
+     * @param gf
+     * @param elemInfo
+     * @param elemIndex
+     * @param coords
+     *
+     * @return LinearRing
+     *
+     * @throws IllegalArgumentException If circle, or curve is requested
+     */
+    private LinearRing createLinearRing(GeometryFactory gf, int dim, int lrs, int[] elemInfo, int elemIndex, CoordinateSequence coords) {
+
+    	int sOffset = StartingOffset(elemInfo, elemIndex);
+        int etype = eType(elemInfo, elemIndex);
+        int interpretation = interpretation(elemInfo, elemIndex);  
+        int length = coords.size()*dim;
+        
+		if (!(sOffset <= length))
+		    throw new IllegalArgumentException("ELEM_INFO STARTING_OFFSET "+sOffset+" inconsistent with ORDINATES length "+coords.size());
+		if(!(etype == Constants.SDO_ETYPE.POLYGON) && !(etype == Constants.SDO_ETYPE.POLYGON_EXTERIOR) && !(etype == Constants.SDO_ETYPE.POLYGON_INTERIOR)){
+		    throw new IllegalArgumentException("ETYPE "+etype+" inconsistent with expected POLYGON, POLYGON_EXTERIOR or POLYGON_INTERIOR");
+		}
+		if (!(interpretation == 1) && !(interpretation == 3)){
+			return null;
+		}
+        LinearRing ring;
+
+
+		int len = (dim+lrs);
+		int start = (sOffset - 1) / len;
+		int eOffset = StartingOffset(elemInfo, elemIndex+1); // -1 for end
+        int end = (eOffset != -1) ? ((eOffset - 1) / len) : coords.size();
+        
+        if (interpretation == 1) {
+            ring = gf.createLinearRing(subList(gf.getCoordinateSequenceFactory(),coords, start,end));
+        } else { // interpretation == 3
+            // rectangle does not maintain measures
+            //
+            CoordinateSequence ext = subList(gf.getCoordinateSequenceFactory(),coords, start,end);
+            Coordinate min = ext.getCoordinate(0);
+            Coordinate max = ext.getCoordinate(1);
+            ring = gf.createLinearRing(new Coordinate[] {
+                        min, new Coordinate(max.x, min.y), max,
+                        new Coordinate(min.x, max.y), min
+                    });
+        }
+
+        return ring;
+    }
+    
+    /**
+     * Create LineString as encoded.
+     *
+     * @param gf
+     * @param elemInfo
+     * @param coords
+     *
+     * @return LineString
+     *
+     * @throws IllegalArgumentException If asked to create a curve
+     */
+    private LineString createLine(GeometryFactory gf, int dim, int lrs, int[] elemInfo, int elemIndex, CoordinateSequence coords) {
+    	
+    	int sOffset = StartingOffset(elemInfo, elemIndex);
+        int etype = eType(elemInfo, elemIndex);
+        int interpretation = interpretation(elemInfo, elemIndex);
+
+		if (etype != Constants.SDO_ETYPE.LINE)
+			return null;
+			
+        if (interpretation != 1) {
+            throw new IllegalArgumentException("ELEM_INFO INTERPRETAION "
+                + interpretation + " not supported"
+                + "by JTS LineString.  Straight edges"
+                + "( ELEM_INFO INTERPRETAION 1) is supported");
+        }
+
+		int len = (dim+lrs);
+		int start = (sOffset - 1) / len;
+		int eOffset = StartingOffset(elemInfo, elemIndex+1); // -1 for end
+        int end = (eOffset != -1) ? ((eOffset - 1) / len) : coords.size();
+        
+        
+        LineString line = gf.createLineString(subList(gf.getCoordinateSequenceFactory(),coords, start,end));
+
+        return line;
+    }
+
+    /**
+     * Create Point as encoded.
+     *
+     * @param gf
+     * @param dim The number of Dimensions
+     * @param elemInfo
+     * @param elemIndex
+     * @param coords
+     *
+     * @return Point
+     */
+    private Point createPoint(GeometryFactory gf, int dim, int lrs, int[] elemInfo, int elemIndex, CoordinateSequence coords) {
+    	int sOffset = StartingOffset(elemInfo, elemIndex);
+        int etype = eType(elemInfo, elemIndex);
+        int interpretation = interpretation(elemInfo, elemIndex);
+
+		if (!(sOffset >= 1) || !(sOffset <= coords.size()))
+		    throw new IllegalArgumentException("ELEM_INFO STARTING_OFFSET "+sOffset+" inconsistent with ORDINATES length "+coords.size());
+		if (etype != Constants.SDO_ETYPE.POINT)
+		    throw new IllegalArgumentException("ETYPE "+etype+" inconsistent with expected POINT");
+		if (interpretation != 1){
+			return null;
+		}
+
+		int len = (dim+lrs);
+		int start = (sOffset - 1) / len;
+		int eOffset = StartingOffset(elemInfo, elemIndex+1); // -1 for end
+
+		Point point = null;
+        if ((sOffset == 1) && (eOffset == -1)) {
+            // Use all Coordinates
+        	point = gf.createPoint( coords);
+        }else{
+	        int end = (eOffset != -1) ? ((eOffset - 1) / len) : coords.size();
+	        point = gf.createPoint(subList(gf.getCoordinateSequenceFactory(),coords,start,end));
+        }
+
+        return point;
+    }
+
+
+    /**
+     * Version of List.subList() that returns a CoordinateSequence.
+     * 
+     * <p>
+     * Returns from start (inclusive) to end (exlusive):
+     * </p>
+     *
+     * @param factory Manages CoordinateSequences for JTS
+     * @param coords coords to sublist
+     * @param start starting offset
+     * @param end upper bound of sublist 
+     *
+     * @return CoordianteSequence
+     */
+    private CoordinateSequence subList(CoordinateSequenceFactory factory, CoordinateSequence coords, int start, int end) {
+        if ((start == 0) && (end == coords.size())) {
+            return coords;
+        }
+
+        if (coords instanceof List) {
+            List sublist = ((List) coords).subList(start, end);
+
+            if (sublist instanceof CoordinateSequence) {
+                return (CoordinateSequence) sublist;
+            }
+        }
+
+        CoordinateList list = new CoordinateList(coords.toCoordinateArray());
+
+        Coordinate[] array = new Coordinate[end - start];
+        int index = 0;
+
+        for (Iterator i = list.subList(start, end).iterator(); i.hasNext(); index++) {
+            array[index] = (Coordinate) i.next();
+        }
+
+        return factory.create(array);
+    }
+    
+    /**
+     * ETYPE access for the elemInfo triplet indicated.
+     * <p>
+     * @see Constants.SDO_ETYPE for an indication of possible values
+     * 
+     * @param elemInfo
+     * @param tripletIndex
+     * @return ETYPE for indicated triplet
+     */ 
+    private int eType(int[] elemInfo, int tripletIndex) {
+        if (((tripletIndex * 3) + 1) >= elemInfo.length) {
+            return -1;
+        }
+
+        return elemInfo[(tripletIndex * 3) + 1];
+    }
+
+
+    /**
+     * Accesses the interpretation value for the current geometry
+     * 
+     * JTS valid interpretation is: 1 for strait edges, 3 for rectangle
+     * 
+     * Other interpretations include: 2 for arcs, 4 for circles
+     * 
+     * mostly useful for polygons
+     * 
+     * @param elemInfo
+     * @param tripletIndex
+     * @return Starting Offset for the ordinates of the geometry
+     */
+    private int interpretation(int[] elemInfo, int tripletIndex) {
+        if (((tripletIndex * 3) + 2) >= elemInfo.length) {
+            return -1;
+        }
+
+        return elemInfo[(tripletIndex * 3) + 2];
+    }
+
+    /**
+     * Accesses the starting index in the ordinate array for the current geometry
+     * 
+     * mostly useful for polygons
+     * 
+     * @param elemInfo
+     * @param tripletIndex
+     * @return Starting Offset for the ordinates of the geometry
+     */
+    private int StartingOffset(int[] elemInfo, int tripletIndex) {
+        if (((tripletIndex * 3) + 0) >= elemInfo.length) {
+            return -1;
+        }
+
+        return elemInfo[(tripletIndex * 3) + 0];
+    }
+
+	/** Presents datum as an int */
+	private int asInteger(Datum datum, final int DEFAULT)
+			throws SQLException {
+		if (datum == null)
+			return DEFAULT;
+		return ((NUMBER) datum).intValue();
+	}
+
+	/** Presents datum as a double */
+	private double asDouble(Datum datum, final double DEFAULT) {
+		if (datum == null)
+			return DEFAULT;
+		return ((NUMBER) datum).doubleValue();
+	}
+
+	/** Presents struct as a double[] */
+	private double[] asDoubleArray(STRUCT struct, final double DEFAULT)
+			throws SQLException {
+		if (struct == null)
+			return null;
+		return asDoubleArray(struct.getOracleAttributes(), DEFAULT);
+	}
+
+	/** Presents array as a double[] */
+	private double[] asDoubleArray(ARRAY array, final double DEFAULT)
+			throws SQLException {
+		if (array == null)
+			return null;
+		if (DEFAULT == 0)
+			return array.getDoubleArray();
+
+		return asDoubleArray(array.getOracleArray(), DEFAULT);
+	}
+
+	/** Presents Datum[] as a double[] */
+	private double[] asDoubleArray(Datum data[], final double DEFAULT) {
+		if (data == null)
+			return null;
+		double array[] = new double[data.length];
+		for (int i = 0; i < data.length; i++) {
+			array[i] = asDouble(data[i], DEFAULT);
+		}
+		return array;
+	}
+
+	private int[] asIntArray(ARRAY array, int DEFAULT)
+			throws SQLException {
+		if (array == null)
+			return null;
+		if (DEFAULT == 0)
+			return array.getIntArray();
+
+		return asIntArray(array.getOracleArray(), DEFAULT);
+	}
+
+	/** Presents Datum[] as a int[] */
+	private int[] asIntArray(Datum data[], final int DEFAULT)
+			throws SQLException {
+		if (data == null)
+			return null;
+		int array[] = new int[data.length];
+		for (int i = 0; i < data.length; i++) {
+			array[i] = asInteger(data[i], DEFAULT);
+		}
+		return array;
+	}
+
+}

Added: packages/jts/branches/upstream/current/src/jtsio/src/com/vividsolutions/jts/io/oracle/OraWriter.java
===================================================================
--- packages/jts/branches/upstream/current/src/jtsio/src/com/vividsolutions/jts/io/oracle/OraWriter.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/jtsio/src/com/vividsolutions/jts/io/oracle/OraWriter.java	2007-06-15 20:03:42 UTC (rev 887)
@@ -0,0 +1,782 @@
+/*
+ * 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
+ */
+/*
+ *    Geotools2 - OpenSource mapping toolkit
+ *    http://geotools.org
+ *    (C) 2003, Geotools Project Managment Committee (PMC)
+ *
+ *    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;
+ *    version 2.1 of the License.
+ *
+ *    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.
+ *
+ */
+package com.vividsolutions.jts.io.oracle;
+
+import java.sql.SQLException;
+import java.util.*;
+
+import com.vividsolutions.jts.algorithm.CGAlgorithms;
+import com.vividsolutions.jts.geom.*;
+
+import oracle.jdbc.OracleConnection;
+import oracle.sql.*;
+
+/**
+ * 
+ * Translates a JTS Geometry into an Oracle STRUCT representing an MDSYS.GEOMETRY object. 
+ * 
+ * A connection to an oracle instance with access to the definition of the MDSYS.GEOMETRY 
+ * object is required by the oracle driver.
+ * 
+ * @version 9i
+ * @author David Zwiers, Vivid Solutions.
+ */
+public class OraWriter {
+	private OracleConnection connection;
+	private int dimension = 2;
+	
+	private String DATATYPE = "MDSYS.SDO_GEOMETRY";
+	
+	/**
+	 * Initialize the Oracle MDSYS.GEOMETRY Encoder with a valid oracle connection. 
+	 * 
+	 * The connection should have sufficient priveledges to view the description of the MDSYS.GEOMETRY type.
+	 * 
+	 * The dimension is set to 2
+	 * 
+	 * @param con
+	 */
+	public OraWriter(OracleConnection con){
+		this.connection = con;
+	}
+	
+	/**
+	 * Initialize the Oracle MDSYS.GEOMETRY Encoder with a valid oracle connection. 
+	 * 
+	 * The connection should have sufficient priveledges to view the description of the MDSYS.GEOMETRY type.
+	 * 
+	 * @param con
+	 * @param dimension 
+	 */
+	public OraWriter(OracleConnection con, int dimension){
+		this.connection = con;
+		this.dimension = dimension;
+	}
+
+	/**
+	 * This routine will translate the JTS Geometry into an Oracle MDSYS.GEOMETRY STRUCT.
+	 * 
+	 * Although invalid geometries may be encoded, and inserted into an Oracle DB, this is 
+	 * not recomended. It is the responsibility of the user to ensure the geometry is valid 
+	 * prior to calling this method. The user should also ensure the the geometry's SRID 
+	 * field contains the correct value, if an SRID is desired. An incorrect SRID value may 
+	 * cause index exceptions during an insert or update. 
+	 * 
+	 * @param geom JTS Geometry to encode
+	 * @return Oracle MDSYS.GEOMETRY STRUCT
+	 * @throws SQLException 
+	 */
+	public STRUCT write(Geometry geom) throws SQLException{
+		
+		if( geom == null) return toSTRUCT( null, DATATYPE );
+        
+        int gtype = gType( geom);
+        NUMBER SDO_GTYPE = new NUMBER( gtype );
+        
+        int srid = geom.getFactory().getSRID();
+        NUMBER SDO_SRID = srid == Constants.SRID_NULL ? null : new NUMBER( srid );
+        
+        double[] point = point( geom );
+        
+        STRUCT SDO_POINT;
+        
+        ARRAY SDO_ELEM_INFO;
+        ARRAY SDO_ORDINATES;
+        
+        if( point == null ){
+            int elemInfo[] = elemInfo( geom , gtype);
+            
+            List list = new ArrayList();
+            coordinates(list, geom);
+                        
+            int dim = gtype / 1000;
+            int lrs = (gtype - dim*1000)/100;
+            int len = dim+lrs; // size per coordinate
+            double[] ordinates = new double[list.size()*len];
+            
+            int k=0;
+            for(int i=0;i<list.size() && k<ordinates.length;i++){
+            	int j=0;
+            	double[] ords = (double[]) list.get(i);
+            	for(;j<len && j<ords.length;j++){
+            		ordinates[k++] = ords[j];
+            	}
+            	for(;j<len;j++){ // mostly safety
+            		ordinates[k++] = Double.NaN;
+            	}
+            }
+            
+            list = null;
+            
+            SDO_POINT = null;
+            SDO_ELEM_INFO = toARRAY( elemInfo, "MDSYS.SDO_ELEM_INFO_ARRAY" );
+            SDO_ORDINATES = toARRAY( ordinates, "MDSYS.SDO_ORDINATE_ARRAY" );                        
+        }
+        else { // Point Optimization
+            Datum data[] = new Datum[]{
+                toNUMBER( point[0] ),
+                toNUMBER( point[1] ),
+                toNUMBER( point[2] ),
+            };
+            SDO_POINT = toSTRUCT( data, "MDSYS.SDO_POINT_TYPE"  );
+            SDO_ELEM_INFO = null;
+            SDO_ORDINATES = null;
+        }                
+        Datum attributes[] = new Datum[]{
+            SDO_GTYPE,
+            SDO_SRID,
+            SDO_POINT,
+            SDO_ELEM_INFO,
+            SDO_ORDINATES
+        };
+        return toSTRUCT( attributes, DATATYPE );      
+	}
+
+	/**
+     * Encode Geometry as described by GTYPE and ELEM_INFO
+     * 
+     * @param list Flat list of Double
+     * @param geom Geometry 
+     *
+     * @throws IllegalArgumentException If geometry cannot be encoded
+     */
+    private void coordinates(List list, Geometry geom) {
+        switch (template(geom)) {
+
+        case Constants.SDO_GTEMPLATE.POINT:
+            addCoordinates(list, ((Point)geom).getCoordinateSequence());
+            return;
+        case Constants.SDO_GTEMPLATE.LINE:
+            addCoordinates(list, ((LineString)geom).getCoordinateSequence());
+            return;
+        case Constants.SDO_GTEMPLATE.POLYGON:
+            switch (elemInfoInterpretation(geom,Constants.SDO_ETYPE.POLYGON_EXTERIOR)) {
+            case 3:
+                Envelope e = geom.getEnvelopeInternal();
+                list.add(new double[] { e.getMinX(), e.getMinY() });
+                list.add(new double[] { e.getMaxX(), e.getMaxY() });
+                return;
+            case 1:
+            	Polygon polygon = (Polygon) geom;
+                int holes = polygon.getNumInteriorRing();
+                
+                // check outer ring's direction
+                CoordinateSequence ring = polygon.getExteriorRing().getCoordinateSequence();
+                if (!CGAlgorithms.isCCW(ring.toCoordinateArray())) {
+                    ring = reverse(polygon.getFactory().getCoordinateSequenceFactory(), ring); 
+                }
+                addCoordinates(list,ring);
+
+                for (int i = 0; i < holes; i++) {
+                	// check inner ring's direction
+                	ring = polygon.getInteriorRingN(i).getCoordinateSequence();
+                	if (CGAlgorithms.isCCW(ring.toCoordinateArray())) {
+                        ring = reverse(polygon.getFactory().getCoordinateSequenceFactory(), ring); 
+                    }
+                    
+                    addCoordinates(list,ring);
+                }
+                return;
+            }
+            break; // interpretations 2,4 not supported
+        case Constants.SDO_GTEMPLATE.MULTIPOINT:
+        case Constants.SDO_GTEMPLATE.MULTILINE:
+        case Constants.SDO_GTEMPLATE.MULTIPOLYGON:
+        case Constants.SDO_GTEMPLATE.COLLECTION:
+            for (int i = 0; i < geom.getNumGeometries(); i++) {
+                coordinates(list,geom.getGeometryN(i));
+            }
+            return;
+        }
+
+        throw new IllegalArgumentException("Cannot encode JTS "
+            + geom.getGeometryType() + " as "
+            + "SDO_ORDINATRES (Limitied to Point, Line, Polygon, "
+            + "GeometryCollection, MultiPoint, MultiLineString and MultiPolygon)");
+    }
+
+    /**
+     * Adds a double array to list.
+     * 
+     * <p>
+     * The double array will contain all the ordinates in the Coordiante
+     * sequence.
+     * </p>
+     *
+     * @param list
+     * @param sequence
+     */
+    private static void addCoordinates(List list, CoordinateSequence sequence) {
+    	Coordinate coord = null;
+        for (int i = 0; i < sequence.size(); i++) {
+        	coord = sequence.getCoordinate(i);
+        	if(coord.z == Double.NaN)
+        		list.add( new double[] { coord.x, coord.y});
+        	else
+        		list.add( new double[] { coord.x, coord.y, coord.z });
+        }
+    }
+
+    /**
+     * Return SDO_ELEM_INFO array for geometry
+     * 
+     * <pre><code><b>
+     * # Name                Meaning</b>
+     * 0 SDO_STARTING_OFFSET Offsets start at one
+     * 1 SDO_ETYPE           Describes how ordinates are ordered
+     * 2 SDO_INTERPRETATION  SDO_ETYPE: 4, 1005, or 2005
+     *                       Number of triplets involved in compound geometry
+     *                       
+     *                       SDO_ETYPE: 1, 2, 1003, or 2003
+     *                       Describes ordering of ordinates in geometry  
+     * </code></pre>
+     * 
+     * <p>
+     * For compound elements (SDO_ETYPE values 4 and 5) the last element of one
+     * is the first element of the next.
+     * </p>
+     *
+     * @param geom Geometry being represented
+     *
+     * @return Descriptionof Ordinates representation
+     */
+	private int[] elemInfo(Geometry geom, int gtype) {
+		List list = new LinkedList();
+
+        elemInfo(list, geom, 1, gtype);
+        
+        int[] array = new int[list.size()];
+        int offset = 0;
+
+        for (Iterator i = list.iterator(); i.hasNext(); offset++) {
+            array[offset] = ((Number) i.next()).intValue();
+        }
+
+        return array;
+    }
+	
+    /**
+     * Add to SDO_ELEM_INFO list for geometry and GTYPE.
+     *
+     * @param elemInfoList List used to gather SDO_ELEM_INFO
+     * @param geom Geometry to encode
+     * @param sOffSet Starting offset in SDO_ORDINATES
+     *
+     * @throws IllegalArgumentException If geom cannot be encoded by ElemInfo
+     */
+    private void elemInfo(List elemInfoList, Geometry geom, int sOffSet, int gtype) {
+
+        switch (gtype - (gtype/100) * 100) { // removes right two digits
+        case Constants.SDO_GTEMPLATE.POINT:
+            addInt(elemInfoList, sOffSet);
+            addInt(elemInfoList, Constants.SDO_ETYPE.POINT);
+            addInt(elemInfoList, 1); // INTERPRETATION single point
+
+            return;
+
+        case Constants.SDO_GTEMPLATE.MULTIPOINT:
+            MultiPoint points = (MultiPoint) geom;
+
+            addInt(elemInfoList, sOffSet);
+            addInt(elemInfoList, Constants.SDO_ETYPE.POINT);
+            addInt(elemInfoList, elemInfoInterpretation(points, Constants.SDO_ETYPE.POINT));
+
+            return;
+
+        case Constants.SDO_GTEMPLATE.LINE:
+            addInt(elemInfoList, sOffSet);
+            addInt(elemInfoList, Constants.SDO_ETYPE.LINE);
+            addInt(elemInfoList, 1); // INTERPRETATION straight edges    
+
+            return;
+
+        case Constants.SDO_GTEMPLATE.MULTILINE:
+        	MultiLineString lines = (MultiLineString) geom;
+            LineString line;
+            int offset = sOffSet;
+            int dim = gtype/1000;
+            int len = dim + (gtype-dim*1000)/100;
+
+            for (int i = 0; i < lines.getNumGeometries(); i++) {
+                line = (LineString) lines.getGeometryN(i);
+                addInt(elemInfoList, offset);
+                addInt(elemInfoList, Constants.SDO_ETYPE.LINE);
+                addInt(elemInfoList, 1); // INTERPRETATION straight edges  
+                offset += (line.getNumPoints() * len);
+            }
+
+            return;
+
+        case Constants.SDO_GTEMPLATE.POLYGON:
+        	Polygon polygon = (Polygon)geom;
+            int holes = polygon.getNumInteriorRing();
+
+            if (holes == 0) {
+                addInt(elemInfoList, sOffSet);
+                addInt(elemInfoList, elemInfoEType(polygon));
+                addInt(elemInfoList, elemInfoInterpretation(polygon, Constants.SDO_ETYPE.POLYGON_EXTERIOR));
+                return;
+            }
+
+            dim = gtype/1000;
+            len = dim + (gtype-dim*1000)/100;
+            offset = sOffSet;
+            LineString ring;
+
+            ring = polygon.getExteriorRing();
+            addInt(elemInfoList, offset);
+            addInt(elemInfoList, elemInfoEType(polygon));
+            addInt(elemInfoList, elemInfoInterpretation(polygon, Constants.SDO_ETYPE.POLYGON_EXTERIOR));
+            offset += (ring.getNumPoints() * len);
+
+            for (int i = 1; i <= holes; i++) {
+                ring = polygon.getInteriorRingN(i - 1);
+                addInt(elemInfoList, offset);
+                addInt(elemInfoList, Constants.SDO_ETYPE.POLYGON_INTERIOR);
+                addInt(elemInfoList, elemInfoInterpretation(ring, Constants.SDO_ETYPE.POLYGON_INTERIOR));
+                offset += (ring.getNumPoints() * len);
+            }
+
+            return;
+
+        case Constants.SDO_GTEMPLATE.MULTIPOLYGON:
+        	MultiPolygon polys = (MultiPolygon) geom;
+            Polygon poly;
+            offset = sOffSet;
+
+            dim = gtype/1000;
+            len = dim + (gtype-dim*1000)/100;
+
+            for (int i = 0; i < polys.getNumGeometries(); i++) {
+                poly = (Polygon) polys.getGeometryN(i);
+                elemInfo(elemInfoList, poly, offset, gType(poly));
+                if( isRectangle( poly )){
+                    offset += (2 * len);                
+                }
+                else {
+                    offset += (poly.getNumPoints() * len);                
+                }            
+            }
+
+            return;
+
+        case Constants.SDO_GTEMPLATE.COLLECTION:
+        	GeometryCollection geoms = (GeometryCollection) geom;
+            offset = sOffSet;
+            dim = gtype/1000;
+            len = dim + (gtype-dim*1000)/100;
+
+            for (int i = 0; i < geoms.getNumGeometries(); i++) {
+                geom = geoms.getGeometryN(i);
+                elemInfo(elemInfoList, geom, offset, gtype);
+                if( geom instanceof Polygon && isRectangle( (Polygon) geom )){
+                    offset += (2 * len);                
+                }
+                else {
+                    offset += (geom.getNumPoints() * len);                
+                }                        
+            }
+
+            return;
+        }
+
+        throw new IllegalArgumentException("Cannot encode JTS "
+            + geom.getGeometryType() + " as SDO_ELEM_INFO "
+            + "(Limitied to Point, Line, Polygon, GeometryCollection, MultiPoint,"
+            + " MultiLineString and MultiPolygon)");
+    }
+
+    private void addInt(List list, int i) {
+        list.add(new Integer(i));
+    }
+
+    /**
+     * We need to check if a <code>polygon</code> a rectangle so we can produce
+     * the correct encoding.
+     * 
+     * Rectangles are only supported without a SRID!
+     *
+     * @param polygon
+     *
+     * @return <code>true</code> if polygon is SRID==0 and a rectangle
+     */
+    private boolean isRectangle(Polygon polygon) {
+        if (polygon.getFactory().getSRID() != Constants.SRID_NULL) {
+            // Rectangles only valid in CAD applications
+            // that do not have an SRID system
+            //
+            return false;
+        }
+
+        if (lrs(polygon) != 0) {
+            // cannot support LRS on a rectangle
+            return false;
+        }
+
+        Coordinate[] coords = polygon.getCoordinates();
+
+        if (coords.length != 5) {
+            return false;
+        }
+
+        if ((coords[0] == null) || (coords[1] == null) || (coords[2] == null)
+                || (coords[3] == null)) {
+            return false;
+        }
+
+        if (!coords[0].equals2D(coords[4])) {
+            return false;
+        }
+
+        double x1 = coords[0].x;
+        double y1 = coords[0].y;
+        double x2 = coords[1].x;
+        double y2 = coords[1].y;
+        double x3 = coords[2].x;
+        double y3 = coords[2].y;
+        double x4 = coords[3].x;
+        double y4 = coords[3].y;
+
+        if ((x1 == x4) && (y1 == y2) && (x3 == x2) && (y3 == y4)) {
+            // 1+-----+2
+            //  |     |
+            // 4+-----+3
+            return true;
+        }
+
+        if ((x1 == x2) && (y1 == y4) && (x3 == x4) && (y3 == y2)) {
+            // 2+-----+3
+            //  |     |
+            // 1+-----+4
+            return true;
+        }
+
+        return false;
+    }
+    /**
+     * Produce <code>SDO_ETYPE</code> for geometry description as stored in the
+     * <code>SDO_ELEM_INFO</code>.
+     * 
+     * <p>
+     * Describes how Ordinates are ordered:
+     * </p>
+     * <pre><code><b>
+     * Value Elements Meaning</b>
+     *    0           Custom Geometry (like spline) 
+     *    1  simple   Point (or Points)
+     *    2  simple   Line (or Lines)
+     *    3           polygon ring of unknown order (discouraged update to 1003 or 2003)
+     * 1003  simple   polygon ring (1 exterior counterclockwise order)
+     * 2003  simple   polygon ring (2 interior clockwise order)
+     *    4  compound series defines a linestring
+     *    5  compound series defines a polygon ring of unknown order (discouraged)
+     * 1005  compound series defines exterior polygon ring (counterclockwise order)
+     * 2005  compound series defines interior polygon ring (clockwise order)
+     * </code></pre>
+     * 
+     * @param geom Geometry being represented
+     *
+     * @return Descriptionof Ordinates representation
+     *
+     * @throws IllegalArgumentException
+     */
+    private int elemInfoEType(Geometry geom) {
+        switch (template(geom)) {
+
+        case Constants.SDO_GTEMPLATE.POINT:
+            return Constants.SDO_ETYPE.POINT;
+
+        case Constants.SDO_GTEMPLATE.LINE:
+            return Constants.SDO_ETYPE.LINE;
+
+        case Constants.SDO_GTEMPLATE.POLYGON:
+        	// jts convention
+            return Constants.SDO_ETYPE.POLYGON_EXTERIOR; // cc order
+
+        default:
+
+            // should never happen!
+            throw new IllegalArgumentException("Unknown encoding of SDO_GTEMPLATE");
+        }
+    }
+    
+    /**
+     * Allows specification of <code>INTERPRETATION</code> used to interpret
+     * <code>geom</code>.
+     * 
+     * @param geom Geometry to encode
+     * @param etype ETYPE value requiring an INTERPREATION
+     *
+     * @return INTERPRETATION ELEM_INFO entry for geom given etype
+     *
+     * @throws IllegalArgumentException If asked to encode a curve
+     */
+    private int elemInfoInterpretation(Geometry geom, int etype) {
+        switch (etype) {
+
+        case Constants.SDO_ETYPE.POINT:
+
+            if (geom instanceof Point) {
+                return 1;
+            }
+
+            if (geom instanceof MultiPoint) {
+                return ((MultiPoint) geom).getNumGeometries();
+            }
+
+            break;
+
+        case Constants.SDO_ETYPE.LINE:
+        	// always straight for jts
+            return 1;
+
+        case Constants.SDO_ETYPE.POLYGON:
+        case Constants.SDO_ETYPE.POLYGON_EXTERIOR:
+        case Constants.SDO_ETYPE.POLYGON_INTERIOR:
+
+            if (geom instanceof Polygon) {
+                Polygon polygon = (Polygon) geom;
+            	// always straight for jts
+                if (isRectangle(polygon)) {
+                    return 3;
+                }
+            }
+
+            return 1;
+        }
+
+        throw new IllegalArgumentException("Cannot encode JTS "
+            + geom.getGeometryType() + " as "
+            + "SDO_INTERPRETATION (Limitied to Point, Line, Polygon, "
+            + "GeometryCollection, MultiPoint, MultiLineString and MultiPolygon)");
+    }
+	
+    /**
+     * Return SDO_POINT_TYPE for geometry
+     * 
+     * Will return non null for Point objects. <code>null</code> is returned
+     * for all non point objects.
+
+     * You cannot use this with LRS Coordiantes
+     * Subclasses may wish to repress this method and force Points to be
+     * represented using SDO_ORDINATES.
+     *
+     * @param geom
+     *
+     * @return double[]
+     */
+	private double[] point(Geometry geom) {
+        if (geom instanceof Point && (lrs(geom) == 0)) {
+            Point point = (Point) geom;
+            Coordinate coord = point.getCoordinate();
+
+            return new double[] { coord.x, coord.y, coord.z };
+        }
+
+        // SDO_POINT_TYPE only used for non LRS Points
+        return null;
+    }
+
+    /**
+     * Produce SDO_GTEMPLATE representing provided Geometry.
+     * 
+     * <p>
+     * Encoding of Geometry type and dimension.
+     * </p>
+     * 
+     * <p>
+     * SDO_GTEMPLATE defined as for digits <code>[d][l][tt]</code>:
+     * </p>
+     * 
+     * @param geom
+     *
+     * @return SDO_GTEMPLATE
+     */
+	private int gType(Geometry geom) {
+        int d = dimension(geom) * 1000;
+        int l = lrs(geom) * 100;
+        int tt = template(geom);
+
+        return d + l + tt;
+    }
+
+    /**
+     * Return dimensions as defined by SDO_GTEMPLATE (either 2,3 or 4).
+     * 
+     *
+     * @param geom
+     *
+     * @return num dimensions
+     */
+    private int dimension(Geometry geom) {
+    	int d = Double.isNaN(geom.getCoordinate().z)?2:3;
+		return d<dimension?d:dimension;
+    }
+
+    /**
+     * Return LRS as defined by SDO_GTEMPLATE (either 3,4 or 0).
+     * 
+     * @param geom
+     *
+     * @return <code>0</code>
+     */
+    private int lrs(Geometry geom) {
+        // when measures are supported this may change
+    	// until then ... 
+    	return 0;
+    }
+    
+    /**
+     * Return TT as defined by SDO_GTEMPLATE (represents geometry type).
+     * 
+     * @see Constants.SDO_GTEMPLATE
+     *
+     * @param geom
+     *
+     * @return template code
+     */
+    private int template(Geometry geom) {
+        if (geom == null) {
+            return -1; // UNKNOWN
+        } else if (geom instanceof Point) {
+            return Constants.SDO_GTEMPLATE.POINT;
+        } else if (geom instanceof LineString) {
+            return Constants.SDO_GTEMPLATE.LINE;
+        } else if (geom instanceof Polygon) {
+            return Constants.SDO_GTEMPLATE.POLYGON;
+        } else if (geom instanceof MultiPoint) {
+            return Constants.SDO_GTEMPLATE.MULTIPOINT;
+        } else if (geom instanceof MultiLineString) {
+            return Constants.SDO_GTEMPLATE.MULTILINE;
+        } else if (geom instanceof MultiPolygon) {
+            return Constants.SDO_GTEMPLATE.MULTIPOLYGON;
+        } else if (geom instanceof GeometryCollection) {
+            return Constants.SDO_GTEMPLATE.COLLECTION;
+        }
+
+        throw new IllegalArgumentException("Cannot encode JTS "
+            + geom.getGeometryType() + " as SDO_GTEMPLATE "
+            + "(Limitied to Point, Line, Polygon, GeometryCollection, MultiPoint,"
+            + " MultiLineString and MultiPolygon)");
+    }
+	
+    /** Convience method for STRUCT construction. */
+    private STRUCT toSTRUCT( Datum attributes[], String dataType )
+            throws SQLException
+    {
+    	if( dataType.startsWith("*.")){
+    		dataType = "DRA."+dataType.substring(2);//TODO here
+    	}
+        StructDescriptor descriptor =
+            StructDescriptor.createDescriptor( dataType, connection );
+    
+         return new STRUCT( descriptor, connection, attributes );
+    }
+    
+    /** 
+     * Convience method for ARRAY construction.
+     * <p>
+     * Compare and contrast with toORDINATE - which treats <code>Double.NaN</code>
+     * as<code>NULL</code></p>
+     */
+    private ARRAY toARRAY( double doubles[], String dataType )
+            throws SQLException
+    {
+        ArrayDescriptor descriptor =
+            ArrayDescriptor.createDescriptor( dataType, connection );
+        
+         return new ARRAY( descriptor, connection, doubles );
+    }
+    
+    /** 
+     * Convience method for ARRAY construction.
+     */
+    private ARRAY toARRAY( int ints[], String dataType )
+        throws SQLException
+    {
+        ArrayDescriptor descriptor =
+            ArrayDescriptor.createDescriptor( dataType, connection );
+            
+         return new ARRAY( descriptor, connection, ints );
+    }
+
+    /** 
+     * Convience method for NUMBER construction.
+     * <p>
+     * Double.NaN is represented as <code>NULL</code> to agree
+     * with JTS use.</p>
+     */
+    private NUMBER toNUMBER( double number ) throws SQLException{
+        if( Double.isNaN( number )){
+            return null;
+        }
+        return new NUMBER( number );
+    }
+
+    /**
+     * reverses the coordinate order
+     *
+     * @param factory
+     * @param sequence
+     *
+     * @return CoordinateSequence reversed sequence
+     */
+    private CoordinateSequence reverse(CoordinateSequenceFactory factory, CoordinateSequence sequence) {
+    	CoordinateList list = new CoordinateList(sequence.toCoordinateArray());
+        Collections.reverse(list);
+        return factory.create(list.toCoordinateArray());
+    }
+
+	/**
+	 * @param dimension The dimension to set.
+	 */
+	public void setDimension(int dimension) {
+		this.dimension = dimension;
+	}
+}

Modified: packages/jts/branches/upstream/current/test/vivid/TestFunctionAA.xml
===================================================================
--- packages/jts/branches/upstream/current/test/vivid/TestFunctionAA.xml	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/test/vivid/TestFunctionAA.xml	2007-06-15 20:03:42 UTC (rev 887)
@@ -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">

Modified: packages/jts/branches/upstream/current/test/vivid/TestFunctionAAPrec.xml
===================================================================
--- packages/jts/branches/upstream/current/test/vivid/TestFunctionAAPrec.xml	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/test/vivid/TestFunctionAAPrec.xml	2007-06-15 20:03:42 UTC (rev 887)
@@ -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">

Modified: packages/jts/branches/upstream/current/test/vivid/TestRectanglePredicate.xml
===================================================================
--- packages/jts/branches/upstream/current/test/vivid/TestRectanglePredicate.xml	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/test/vivid/TestRectanglePredicate.xml	2007-06-15 20:03:42 UTC (rev 887)
@@ -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,9 +88,24 @@
     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>
   <desc>mL with one component contained</desc>
   <a>
     POLYGON((0 0, 100 0, 100 100, 0 100, 0 0))

Modified: packages/jts/branches/upstream/current/test/vivid/TestValid.xml
===================================================================
--- packages/jts/branches/upstream/current/test/vivid/TestValid.xml	2007-06-15 19:59:54 UTC (rev 886)
+++ packages/jts/branches/upstream/current/test/vivid/TestValid.xml	2007-06-15 20:03:42 UTC (rev 887)
@@ -474,13 +474,24 @@
    </case>
 
    <case>
-      <desc>A - duplicate holes </desc>
+      <desc>A - holes touch in one point </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 ((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 ((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>
       </test>
    </case>




More information about the Pkg-grass-devel mailing list