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

frankie at alioth.debian.org frankie at alioth.debian.org
Fri Jun 15 19:35:00 UTC 2007


Author: frankie
Date: 2007-06-15 19:34:59 +0000 (Fri, 15 Jun 2007)
New Revision: 877

Added:
   packages/jts/
   packages/jts/branches/
   packages/jts/branches/upstream/
   packages/jts/branches/upstream/current/
   packages/jts/branches/upstream/current/bin/
   packages/jts/branches/upstream/current/bin/jts.css
   packages/jts/branches/upstream/current/bin/test.bat
   packages/jts/branches/upstream/current/bin/test_robust.bat
   packages/jts/branches/upstream/current/bin/test_vivid.bat
   packages/jts/branches/upstream/current/bin/testbuilder.bat
   packages/jts/branches/upstream/current/bin/testrunner.bat
   packages/jts/branches/upstream/current/bin/testrunner.properties
   packages/jts/branches/upstream/current/doc/
   packages/jts/branches/upstream/current/doc/JTS Developer Guide.pdf
   packages/jts/branches/upstream/current/doc/JTS Technical Specs.pdf
   packages/jts/branches/upstream/current/doc/JTS TestBuilder & TestRunner User Guide.pdf
   packages/jts/branches/upstream/current/doc/JTS Version History.html
   packages/jts/branches/upstream/current/src/
   packages/jts/branches/upstream/current/src/Doxyfile
   packages/jts/branches/upstream/current/src/MANIFEST.MF
   packages/jts/branches/upstream/current/src/com/
   packages/jts/branches/upstream/current/src/com/vividsolutions/
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/
   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/
   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/
   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/impl/PackedCoordinateSequenceFactory.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/package.html
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/util/
   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/geom/util/package.html
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/
   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/
   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/geomgraph/index/package.html
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/package.html
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/ArrayListVisitor.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/IndexVisitor.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/
   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/bintree/package.html
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/chain/
   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/chain/package.html
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/package.html
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/quadtree/
   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/quadtree/package.html
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/strtree/
   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/strtree/package.html
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/sweepline/
   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/index/sweepline/package.html
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/io/
   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/io/package.html
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/IteratedNoder.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/MCQuadtreeNoder.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/noding/package.html
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/snapround/
   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/jts/noding/snapround/package.html
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/
   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/
   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/buffer/package.html
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/distance/
   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/distance/package.html
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/linemerge/
   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/
   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/overlay/package.html
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/package.html
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/polygonize/
   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/
   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/
   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/relate/package.html
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/valid/
   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/operation/valid/package.html
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/planargraph/
   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/planargraph/package.html
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/polygonize/
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/polygonize/package.html
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/precision/
   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/precision/package.html
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/simplify/
   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/LineSegmentIndex.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/simplify/TaggedLineSegment.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/TaggedLinesSimplifier.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/simplify/TopologyPreservingSimplifier.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jts/util/
   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/jts/util/package.html
   packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/
   packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/geom/
   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/
   packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/operation/distance/
   packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/operation/distance/ClosestPointExample.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/operation/linemerge/
   packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/operation/linemerge/LineMergeExample.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/operation/polygonize/
   packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/operation/polygonize/PolygonizeExample.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/package.html
   packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/precision/
   packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/precision/EnhancedPrecisionOpExample.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/technique/
   packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/technique/LineStringSelfIntersections.java
   packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/technique/UnionUsingBuffer.java
   packages/jts/branches/upstream/current/src/jump-workbench-properties.xml
   packages/jts/branches/upstream/current/test/
   packages/jts/branches/upstream/current/test/robust/
   packages/jts/branches/upstream/current/test/robust/TestRobustOverlayFixed.xml
   packages/jts/branches/upstream/current/test/robust/TestRobustOverlayFloat.xml
   packages/jts/branches/upstream/current/test/robust/TestRobustRelate.xml
   packages/jts/branches/upstream/current/test/validate/
   packages/jts/branches/upstream/current/test/validate/TestRelateAA-big.xml
   packages/jts/branches/upstream/current/test/validate/TestRelateAA.xml
   packages/jts/branches/upstream/current/test/validate/TestRelateAC.xml
   packages/jts/branches/upstream/current/test/validate/TestRelateLA.xml
   packages/jts/branches/upstream/current/test/validate/TestRelateLC.xml
   packages/jts/branches/upstream/current/test/validate/TestRelateLL.xml
   packages/jts/branches/upstream/current/test/validate/TestRelatePA.xml
   packages/jts/branches/upstream/current/test/validate/TestRelatePL.xml
   packages/jts/branches/upstream/current/test/validate/TestRelatePP.xml
   packages/jts/branches/upstream/current/test/vivid/
   packages/jts/branches/upstream/current/test/vivid/TestBoundary.xml
   packages/jts/branches/upstream/current/test/vivid/TestCentroid.xml
   packages/jts/branches/upstream/current/test/vivid/TestConvexHull-big.xml
   packages/jts/branches/upstream/current/test/vivid/TestConvexHull.xml
   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/TestFunctionLA.xml
   packages/jts/branches/upstream/current/test/vivid/TestFunctionLAPrec.xml
   packages/jts/branches/upstream/current/test/vivid/TestFunctionLL.xml
   packages/jts/branches/upstream/current/test/vivid/TestFunctionLLPrec.xml
   packages/jts/branches/upstream/current/test/vivid/TestFunctionPA.xml
   packages/jts/branches/upstream/current/test/vivid/TestFunctionPL.xml
   packages/jts/branches/upstream/current/test/vivid/TestFunctionPLPrec.xml
   packages/jts/branches/upstream/current/test/vivid/TestFunctionPP.xml
   packages/jts/branches/upstream/current/test/vivid/TestInteriorPoint.xml
   packages/jts/branches/upstream/current/test/vivid/TestRectanglePredicate.xml
   packages/jts/branches/upstream/current/test/vivid/TestRelateAA.xml
   packages/jts/branches/upstream/current/test/vivid/TestRelateAC.xml
   packages/jts/branches/upstream/current/test/vivid/TestRelateLA.xml
   packages/jts/branches/upstream/current/test/vivid/TestRelateLC.xml
   packages/jts/branches/upstream/current/test/vivid/TestRelateLL.xml
   packages/jts/branches/upstream/current/test/vivid/TestRelatePA.xml
   packages/jts/branches/upstream/current/test/vivid/TestRelatePL.xml
   packages/jts/branches/upstream/current/test/vivid/TestRelatePP.xml
   packages/jts/branches/upstream/current/test/vivid/TestSimple.xml
   packages/jts/branches/upstream/current/test/vivid/TestValid.xml
   packages/jts/branches/upstream/current/test/vivid/TestValid2-big.xml
   packages/jts/branches/upstream/current/test/vivid/TestValid2.xml
   packages/jts/branches/upstream/current/test/vivid/TestWithinDistance.xml
   packages/jts/tags/
Log:
[svn-inject] Installing original source of jts

Added: packages/jts/branches/upstream/current/bin/jts.css
===================================================================
--- packages/jts/branches/upstream/current/bin/jts.css	                        (rev 0)
+++ packages/jts/branches/upstream/current/bin/jts.css	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,139 @@
+BODY
+{
+    BACKGROUND-COLOR: lightblue;
+    FONT-FAMILY: Verdana, Arial;
+    FONT-SIZE: 10pt;
+    FONT-WEIGHT: normal
+}
+H2
+{
+    BACKGROUND-COLOR: darkblue;
+    COLOR: white;
+    FONT-FAMILY: Verdana, Arial;
+    FONT-SIZE: 12pt;
+    FONT-WEIGHT: bolder;
+    MARGIN-BOTTOM: 2pt;
+    PADDING-LEFT: 5px
+}
+H3
+{
+    BACKGROUND-COLOR: lightskyblue;
+    FONT-WEIGHT: normal;
+    MARGIN-BOTTOM: 2pt
+}
+H4
+{
+    BACKGROUND-COLOR: limegreen;
+    FONT-WEIGHT: normal
+}
+H5
+{
+    BACKGROUND-COLOR: burlywood;
+    FONT-WEIGHT: normal
+}
+H6
+{
+    FONT-SIZE: 12pt
+}
+TH
+{
+    BACKGROUND-COLOR: burlywood
+}
+.initialLetter
+{
+    COLOR: lightblue
+}
+.foo
+{
+    COLOR: goldenrod
+}
+.wktA
+{
+    COLOR: blue;
+    FONT-SIZE: 8pt
+}
+.wktB
+{
+    COLOR: red;
+    FONT-SIZE: 8pt
+}
+.methodTitle
+{
+    BACKGROUND-COLOR: steelblue;
+    COLOR: yellow
+}
+.wktR
+{
+    FONT-SIZE: 8pt
+}
+.precisionModel
+{
+    FONT-SIZE: 8pt
+}
+.resultTrue
+{
+    COLOR: green
+}
+.resultFalse
+{
+    COLOR: red
+}
+.MenuBody
+{
+    BACKGROUND-COLOR: midnightblue
+}
+.MenuItem
+{
+    COLOR: white
+}
+.MenuTitle
+{
+    COLOR: #3366ff;
+    FONT-SIZE: 14pt;
+    TEXT-ALIGN: center
+}
+TD
+{
+    FONT-SIZE: 10pt
+}
+.orgName
+{
+    FONT-SIZE: 8pt;
+    FONT-STYLE: italic;
+    FONT-WEIGHT: lighter
+}
+.creditName
+{
+    FONT-WEIGHT: bolder;
+    PADDING-LEFT: 10px
+}
+.creditTitle
+{
+    BACKGROUND-COLOR: steelblue;
+    COLOR: yellow;
+    FONT-WEIGHT: bolder;
+    PADDING-LEFT: 10px;
+    PADDING-RIGHT: 10px
+}
+H1
+{
+    BORDER-BOTTOM: darkblue thin solid;
+    BORDER-TOP: medium none;
+    FONT-STYLE: italic;
+    MARGIN-BOTTOM: 2px;
+    PADDING-BOTTOM: 4px
+}
+.testTitle
+{
+    FONT-SIZE: 16pt;
+    FONT-WEIGHT: bolder
+}
+.caption
+{
+    FONT-SIZE: 8pt;
+    FONT-STYLE: italic
+}
+.bigCaption
+{
+    FONT-WEIGHT: bolder
+}

Added: packages/jts/branches/upstream/current/bin/test.bat
===================================================================
--- packages/jts/branches/upstream/current/bin/test.bat	                        (rev 0)
+++ packages/jts/branches/upstream/current/bin/test.bat	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,25 @@
+ at echo off
+rem A batch file to run the XML test files written by Geographic Data BC.
+
+set CLASSPATH=
+for %%i in (..\lib\*.*) do (
+ set jarfile=%%i
+
+ rem If we append to a variable inside the for, only the last entry will
+ rem be kept. So append to the variable outside the for.
+ rem See http://www.experts-exchange.com/Operating_Systems/MSDOS/Q_20561701.html.
+ rem [Jon Aquino]
+
+ call :setclass
+)  
+
+java com.vividsolutions.jtstest.testrunner.TopologyTestApp -Files ..\test\validate
+pause
+
+goto :eof
+
+:setclass
+set CLASSPATH=%jarfile%;%CLASSPATH%
+set jarfile=
+
+:eof
\ No newline at end of file

Added: packages/jts/branches/upstream/current/bin/test_robust.bat
===================================================================
--- packages/jts/branches/upstream/current/bin/test_robust.bat	                        (rev 0)
+++ packages/jts/branches/upstream/current/bin/test_robust.bat	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,25 @@
+ at echo off
+rem A batch file to run the XML test files written by Geographic Data BC.
+
+set CLASSPATH=
+for %%i in (..\lib\*.*) do (
+ set jarfile=%%i
+
+ rem If we append to a variable inside the for, only the last entry will
+ rem be kept. So append to the variable outside the for.
+ rem See http://www.experts-exchange.com/Operating_Systems/MSDOS/Q_20561701.html.
+ rem [Jon Aquino]
+
+ call :setclass
+)  
+
+java com.vividsolutions.jtstest.testrunner.TopologyTestApp -Files ..\test\robust
+pause
+
+goto :eof
+
+:setclass
+set CLASSPATH=%jarfile%;%CLASSPATH%
+set jarfile=
+
+:eof
\ No newline at end of file

Added: packages/jts/branches/upstream/current/bin/test_vivid.bat
===================================================================
--- packages/jts/branches/upstream/current/bin/test_vivid.bat	                        (rev 0)
+++ packages/jts/branches/upstream/current/bin/test_vivid.bat	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,25 @@
+ at echo off
+rem A batch file to run the XML test files written by Geographic Data BC.
+
+set CLASSPATH=
+for %%i in (..\lib\*.*) do (
+ set jarfile=%%i
+
+ rem If we append to a variable inside the for, only the last entry will
+ rem be kept. So append to the variable outside the for.
+ rem See http://www.experts-exchange.com/Operating_Systems/MSDOS/Q_20561701.html.
+ rem [Jon Aquino]
+
+ call :setclass
+)  
+
+java com.vividsolutions.jtstest.testrunner.TopologyTestApp -Files ..\test\vivid
+pause
+
+goto :eof
+
+:setclass
+set CLASSPATH=%jarfile%;%CLASSPATH%
+set jarfile=
+
+:eof
\ No newline at end of file

Added: packages/jts/branches/upstream/current/bin/testbuilder.bat
===================================================================
--- packages/jts/branches/upstream/current/bin/testbuilder.bat	                        (rev 0)
+++ packages/jts/branches/upstream/current/bin/testbuilder.bat	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,24 @@
+ at echo off
+rem A batch file to run the JTS Test Builder
+
+set CLASSPATH=
+for %%i in (..\lib\*.*) do (
+ set jarfile=%%i
+
+ rem If we append to a variable inside the for, only the last entry will
+ rem be kept. So append to the variable outside the for.
+ rem See http://www.experts-exchange.com/Operating_Systems/MSDOS/Q_20561701.html.
+ rem [Jon Aquino]
+
+ call :setclass
+)  
+
+start javaw com.vividsolutions.jtstest.testbuilder.JTSTestBuilder
+
+goto :eof
+
+:setclass
+set CLASSPATH=%jarfile%;%CLASSPATH%
+set jarfile=
+
+:eof
\ No newline at end of file

Added: packages/jts/branches/upstream/current/bin/testrunner.bat
===================================================================
--- packages/jts/branches/upstream/current/bin/testrunner.bat	                        (rev 0)
+++ packages/jts/branches/upstream/current/bin/testrunner.bat	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,24 @@
+ at echo off
+rem A batch file to run the JTS test runner.
+
+set CLASSPATH=
+for %%i in (..\lib\*.*) do (
+ set jarfile=%%i
+
+ rem If we append to a variable inside the for, only the last entry will
+ rem be kept. So append to the variable outside the for.
+ rem See http://www.experts-exchange.com/Operating_Systems/MSDOS/Q_20561701.html.
+ rem [Jon Aquino]
+
+ call :setclass
+)  
+
+start javaw com.vividsolutions.jtstest.testrunner.TopologyTestApp -Properties testrunner.properties -GUI
+
+goto :eof
+
+:setclass
+set CLASSPATH=%jarfile%;%CLASSPATH%
+set jarfile=
+
+:eof
\ No newline at end of file

Added: packages/jts/branches/upstream/current/bin/testrunner.properties
===================================================================
--- packages/jts/branches/upstream/current/bin/testrunner.properties	                        (rev 0)
+++ packages/jts/branches/upstream/current/bin/testrunner.properties	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,3 @@
+#Properties file for class com.vividsolutions.jtstest.testrunner.TopologyTestApp
+#Fri May 24 10:18:11 PDT 2002
+TestFiles=

Added: packages/jts/branches/upstream/current/doc/JTS Developer Guide.pdf
===================================================================
(Binary files differ)


Property changes on: packages/jts/branches/upstream/current/doc/JTS Developer Guide.pdf
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/jts/branches/upstream/current/doc/JTS Technical Specs.pdf
===================================================================
(Binary files differ)


Property changes on: packages/jts/branches/upstream/current/doc/JTS Technical Specs.pdf
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/jts/branches/upstream/current/doc/JTS TestBuilder & TestRunner User Guide.pdf
===================================================================
(Binary files differ)


Property changes on: packages/jts/branches/upstream/current/doc/JTS TestBuilder & TestRunner User Guide.pdf
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: packages/jts/branches/upstream/current/doc/JTS Version History.html
===================================================================
--- packages/jts/branches/upstream/current/doc/JTS Version History.html	                        (rev 0)
+++ packages/jts/branches/upstream/current/doc/JTS Version History.html	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,301 @@
+<html>
+
+<head>
+<title>JTS Version History</title>
+</head>
+
+<body bgcolor='lightblue'>
+<h1 style='text-align:center;'>
+JTS TOPOLOGY SUITE
+<br>
+Version History
+</h1>
+
+
+This document lists the change history of release versions of the JTS Topology Suite
+
+<hr>
+<h2>Version 1.6</h2>
+
+Release Date: February 3, 2005
+
+<h3>API Changes</h3>
+<ul>
+<li>Changed to using <code>CoordinateArraySequence</code> instead of <code>DefaultCoordinateSequence</code>
+(to provide a more descriptive name).
+</ul>
+
+<h3>Semantics Changes</h3>
+<ul>
+<li>PrecisionModel#makePrecise changed to use Symmetric Arithmetic Rounding rather than Banker's Rounding
+</ul>
+
+<h3>Functionality Improvements</h3>
+<ul>
+<li>Added ability to enable <code>Debug</code> methods by setting a system property
+<li>Added <code>getNumGeometries</code> and <code>getGeometryN</code> methods to Geometry class, to make API more uniform
+<li>Improved API for <code>CoordinateSequence</code> allows more options for improving memory usage and handling custom coordinate storage methods
+<li>Added <code>PackedCoordinateSequence</code> to provide reduced memory footprint for geometry objects if desired
+<li>Added optimized spatial predicates for rectangles
+<li>Added Debug#isDebugging method
+</ul>
+
+<h3>Bug Fixes</h3>
+<ul>
+<li>Fixed bug in <code>Geometry#within()</code> short circuiting
+<li>Fixed bug causing <code>Geometry#isValid</code> to throw IllegalArgumentException for certain kinds of holes with invalid rings
+<li>Fixed bug causing redundant linestrings to be returned in the result of overlaying polygons containing holes touching their shell.
+<li><code>Polygon#getBoundary</code> now returns a <code>LinearRing</code> if the polygon does not have holes
+</ul>
+
+<h3>Architecture Changes</h3>
+<ul>
+<li>Removed a proliferation of references to the default <code>CoordinateSequenceFactory</code>
+</ul>
+
+<h2>Contributors</h2>
+<ul>
+<li>Andrea Aime
+</ul>
+
+<h2>Version 1.5</h2>
+Release Date: September 22, 2004
+<p>
+This version is upwards compatible with Version 1.4
+
+<h3>API Changes</h3>
+<ul>
+<li>None
+</ul>
+
+<h3>Semantics Changes</h3>
+<ul>
+<li>None
+</ul>
+
+<h3>Functionality Improvements</h3>
+<ul>
+<li>CGAlgorithms#isCCW now handles coordinate lists with repeated points.  Also throws an IllegalArgumentException if the input ring does not have 3 distinct points
+<li>isValid now checks for invalid coordinates (e.g. ones with Nan or infinite numbers)
+<li>added copyDeep() method to CoordinateArrays
+<li>added geometry simplification operations DouglasPeuckerSimplifier and TopologyPreservingSimplifier
+<li>added methods to Quadtree and STRtree to remove items and query using the Visitor pattern
+</ul>
+
+<h3>Performance Improvements</h3>
+<ul>
+<li>Added short-circuit tests in geometry named predicates based on envelope tests
+</ul>
+<h3>Bug Fixes</h3>
+<ul>
+<li>Fixed bugs in Geometry serialization
+<li>Fixed bug in ValidOp which reported some MultiPolygons with shells nested inside a hole as invalid
+<li>Fixed bug in buffer which caused buffers of some polygons with small & large holes to not contain any holes
+<li>Fixed bug in Polygonizer which caused exception if no lines were supplied
+</ul>
+<h3>Architecture Changes</h3>
+<ul>
+<li>Basic CG algorithm methods made static in the in CGAlgorithms class
+<li>Various utility methods made public in CoordinateArrays class
+</ul>
+<h3>Documentation</h3>
+<ul>
+<li>More examples provided in com.vividsolutions.jtsexamples package
+</ul>
+
+<h2>Version 1.4</h2>
+
+Release Date: November 4, 2003
+<h3>Semantics Changes</h3>
+<ul>
+<li>none
+</ul>
+<h3>Functionality Improvements</h3>
+<ul>
+<li>Added "LINEARRING" tag to WKT syntax
+<li>Added GeometryEditor class to allow easy copy/modify of Geometrys
+<li>Added GeometricShapeFactory class to easily create standard geometric shapes
+<li>Geometries can now carry arbitrary user-defined data objects (via Geometry#get/setUserData(Object) method)
+<li>Added CoordinateSequence and CoordinateSequenceFactory interfaces, and default implementations (BasicCoordinateSequence, BasicCoordinateSequenceFactory)
+<li>Added Geometry#getFactory
+<li>Added new PrecisionModel type of FLOATING_SINGLE, for rounding to single precision floating point
+<li>Added DistanceOp#getClosestPoints method, which returns the closest points between two Geometries
+<li>Added com.vividsolutions.jts.noding package containing classes to perform fast indexed noding of linestrings
+<li>Added com.vividsolutions.jts.operation.polygonize package containing classes to perform polygonization
+<li>Added com.vividsolutions.jts.operation.linemerge package containing classes to perform line merging
+<li>Added SimpleGeometryPrecisionReducer to allow reducing precision of coordinates of a Geometry
+<li>Added LineSegment#closestPoints method to compute the closest points between two line segments
+<li>Added MinimumDiameter class to compute minimum diameters of Geometries
+<li>Added geom.Triangle class to contain algorithms for Triangles
+<li>BufferOp now allows end cap styles to be specified.  Three types are supported: round, butt and square.
+</ul>
+<h3>Performance Improvements</h3>
+<ul>
+<li>EdgeList now provides a findEqualEdge method which is substantially faster than findEdgeIndex, for large lists
+<li>Buffering is now faster and much more robust
+<li>Overlap operations are now more robust
+</ul>
+<h3>Bug Fixes</h3>
+<ul>
+<li>Envelope#init(Envelope) now handles null Envelopes correctly
+<li>CoordinateList#add() now correctly ignores the z-value of Coordinates in determining equality
+<li>Geometry#isValid now correctly handles checking validity of LinearRings
+<li>Fixed infinite loop bug causing Out Of Memory errors during polygon intersection
+<li>Geometry#clone now correctly clones the Geometry's Envelope
+<li>LineIntersector#computeEdgeDistance now correctly computes a non-zero edge distance in certain situations when a fixed precision model was being used and the line segment was a single unit in length
+<li>Fixed incorrect calculation of depths in DirectedEdgeStar#computeDepths
+<li>Fixed BufferSubgraph#addReachable to use explicit stack to avoid stack overflow problems
+<li>Fixed various bugs causing some kinds of buffers to be computed incorrectly
+</ul>
+<h3>API Changes</h3>
+<ul>
+<li>WKTReader/Writer: changed protected members to private
+<li>PrecisionModel type is now an object rather than an int
+<li>ConvexHull API changed to remove requirement to pass in CGAlgorithms object
+</ul>
+<h3>Code Architecture Changes</h3>
+<ul>
+<li>geom.util package added for utility classes which parse and modify geometries
+</ul>
+<h3>Documentation</h3>
+<ul>
+<li>More examples provided in com.vividsolutions.jtsexamples package
+<li>Added JTS Developers Guide
+</ul>
+
+<h2>Version 1.3</h2>
+Release Date: April 4, 2003
+<h3>Semantics Changes</h3>
+<ul>
+<li>all Geometry methods are now reentrant (thread-safe)
+<li>Fixed-precision coordinates are now stored in a rounded but non-scaled form.  This makes them compatible with non-precise (Floating) coordinates, and simplifies working with precise coordinates directly.  Mixed precision models are now supported in Geometry methods; method results are in the more precise of the input precision models.
+<li>Offsets are no longer supported in the Fixed precision model.  This is necessary to allow storing fixed precision coordinates in a non-scaled form.  This does not reduce the total precision available, since coordinates are stored in a floating-point format.
+<li>SRID and Precision Model are no longer checked for equality during Geometry operations.  This removes a limitation which provided little semantic benefit.
+</ul>
+
+<h3>Functionality Improvements</h3>
+<ul>
+<li>added Geometry.isWithinDistance(Geometry g, double distance) method, to provide optimized proximity queries
+<li>added Geometry.buffer(double distance, int quadrantSegments) method, allowing control over accuracy of buffer approximation
+<li>added Geometry.getCentroid() method
+<li>added Geometry.getInteriorPoint() method, which uses heuristic methods to return a point in the interior of a Geometry
+<li>GeometryFactory.toGeometryArray now returns null if the argument is null
+</ul>
+<h3>Performance Improvements</h3>
+<ul>
+<li>Removed unnecessary string construction in EdgeEndStar.propagateSideLabels()
+<li>Eliminated unnecessary computation of self-intersections in rings during relate and spatial functions.  This provides a large increase in speed when working with large rings and polygons.  (Note that IsValid still checks for these self-intersections, which are illegal in LinearRings)
+<li>Add short-circuit code to RobustLineIntersector to detect non-intersections more efficiently
+</ul>
+<h3>Bug Fixes</h3>
+<ul>
+<li>Fixed ClassCastException occurring in GeometryCollection.getLength()
+<li>Fixed bug in Edge Intersection insertion (replaced Coordinate#equals with equals2D to ensure that intersection creation is not sensitive to Z-value).
+<li>Fixed handling LineStrings with too few points in GeometryGraph.addLineString
+<li>Fixed: was not checking that MultiPolygons don't contain components with too few points.
+<li>Fixed Envelope.distance() to return correct distance for all envelopes.
+<li>Fixed a few Geometry methods to make them re-entrant.
+<li>Fixed CoordinateList.closeRing() to ensure endpoints are not duplicated
+<li>Fixed CGAlgorithms.signedArea() to use a simpler algorithm which is more robust and faster.
+<li>Fixed bug preventing validating Rings containing an initial repeated point.
+</ul>
+<h3>API Changes</h3>
+<ul>
+<li>Added default constructor to WKTReader.  It uses the default GeometryFactory
+<li>Add two static intersects() methods to Envelope, to allow computing intersections with envelopes defined by points only.
+<li>Dropped BinaryPower; its functionality is provided by DoubleBits in a more robust fashion.
+<li>Removed a couple of redundant private static methods from Geometry; they have been replaced by methods in CoordinateArrays
+<li>The Geometry class is now marked as Serializable
+</ul>
+
+<h2>Version 1.2</h2>
+Release Date: 7 October 2002
+<h3>Semantics Changes</h3>
+<ul>
+<li>JTS now allows Geometrys to have repeated points.  All operations will continue to perform as before.  This removes a significant incompatibility with the OGC spatial data model.
+<li>TopologyExceptions may now be thrown by spatial overlay methods.  This helps to distinguish between code bugs and known robustness problems.  It also provides a machine-readable coordinate for the error location.
+</ul>
+<h3>Functionality Improvements</h3>
+<ul>
+<li>RobustLineIntersector now uses "normalized" coordinates to maximize the accuracy of intersection computation.
+<li>Upgraded Quadtree with more robust implementation
+<li>Replaced IntervalTree with a more robust implementation of BinTree
+<li>Added STRTree 2-D spatial index, which exhibits better performance than QuadTrees in many situations.
+<li>Added EnhancePrecisionOp, which uses precisioning enhancing techniques to reduce the number of failure cases due to robustness problems.
+</ul>
+<h3>Bug Fixes</h3>
+<ul>
+<li>fixed ConvexHull to use TreeSet instead of HashSet for coordinates
+<li>Fixed isValid for GeometryCollections containing Polygons, which were sometimes erroneously returning a validity failure for correct Geometrys.
+<li>Fixed bug in LineSegment.distancePointLine() which would return the incorrect distance for a LineSegment with two identical points
+<li>Improved error handling in CGAlgorithms.isCCW()
+<li>IsValid now checks for too few points in a geometry component (e.g. due to repeated points in a ring)
+</ul>
+<h3>API Changes</h3>
+<ul>
+<li>added Stopwatch class
+<li>added Geometry.getArea() and Geometry.getLength() methods
+<li>added CGAlgorithms.signedArea() method
+<li>added methods to LineSegment - closestPoint(), getLength()
+<li>added CoordinateArrrays and CoordinateLists utility classes
+<li>Added TopologyValidationError.getErrorType() method
+<li>Added Envelope#intersects; deprecated Envelope#overlaps.
+<li>Added Geometry#geometryChanged() method to allow signaling when Geometry coordinates have been mutated by a client class
+<li>Added STRTree class implementing a Sort-Tile-Recursive spatial index (a variant of a packed R-tree)
+<li>Deleted IntervalTree 1-D spatial index (replaced by BinTree)
+<li>Add BinTree 1-D spatial index
+</ul>
+
+<h2>Version 1.1.1</h2>
+Release Date: 9 April 2002
+<h3>Bug Fixes</h3>
+<ul>
+<li>fixed decimal-point symbol localization bug in WKTWriter
+<li>fixed bug in Envelope.int(Envelope env)
+<li>fixed filename case of SFSMultiLineString.java and IntervalTree.java
+</ul>
+<h3>API Changes</h3>
+<ul>
+<li>deleted TopologyException class
+<li>renamed CGAlgorithms.isPointInPolygon to isPointInRing (a more accurate description of what the method computes)
+</ul>
+<h3>API Additions</h3>
+<ul>
+<li>added Geometry.getCoordinate() method
+<li>added Geometry.distance() method
+<li>added GeometryComponentFilter interface and Geometry.apply(GeometryComponentFilter) method
+</ul>
+
+<h2>Version 1.1</h2>
+Release Date: 28 March 2002
+<h3>New Features</h3>
+<ul>
+<li>added Geometry.isSimple() and Geometry.isValid() methods
+<li>improved design of topological data structures
+<li>added Geometry.setSRID() method
+<li>improved functionality of the Envelope class
+<li>added ability to write to an arbitrary java.io.Writer object to WKTWriter
+<li>added Validate and Mark Location functionality to TestBuilder
+</ul>
+
+<h2>Version 1.0</h2>
+Release Date: 1 February 2002
+<ul>
+<li>Removed some non-compatibilities with Java 1.1
+<li>Fixed bug in constructing buffer outline around inside of angles
+<li>In TestBuilder vertices are now displayed with fixed size in view units
+<li>Improved code for WKTWriter.writeFormatted()
+<li>Fixed bug in constructor for LinearRing
+<li>Improved implementation of sweepline intersection algorithm to avoid use of dynamic set.
+<li>Fixed bug in ConvexHull.cleanRing()
+<li>Refactored RobustLineIntersector and NonRobustLineIntersector
+</ul>
+
+<h2>Version 0.0</h2>
+Release Date: 30 May 2001
+
+<p>
+<i>Baseline version</i>
+
+</body>
+</html>

Added: packages/jts/branches/upstream/current/src/Doxyfile
===================================================================
--- packages/jts/branches/upstream/current/src/Doxyfile	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/Doxyfile	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,200 @@
+# Doxyfile 1.3-rc1
+
+#---------------------------------------------------------------------------
+# General configuration options
+#---------------------------------------------------------------------------
+PROJECT_NAME           = JTS
+PROJECT_NUMBER         = 
+OUTPUT_DIRECTORY       = c:\public\doxygen\jts
+OUTPUT_LANGUAGE        = English
+EXTRACT_ALL            = YES
+EXTRACT_PRIVATE        = YES
+EXTRACT_STATIC         = YES
+EXTRACT_LOCAL_CLASSES  = YES
+HIDE_UNDOC_MEMBERS     = NO
+HIDE_UNDOC_CLASSES     = NO
+HIDE_FRIEND_COMPOUNDS  = NO
+HIDE_IN_BODY_DOCS      = NO
+BRIEF_MEMBER_DESC      = YES
+REPEAT_BRIEF           = YES
+ALWAYS_DETAILED_SEC    = NO
+INLINE_INHERITED_MEMB  = NO
+FULL_PATH_NAMES        = YES
+STRIP_FROM_PATH        = 
+INTERNAL_DOCS          = NO
+CASE_SENSE_NAMES       = YES
+SHORT_NAMES            = NO
+HIDE_SCOPE_NAMES       = NO
+VERBATIM_HEADERS       = YES
+SHOW_INCLUDE_FILES     = YES
+JAVADOC_AUTOBRIEF      = NO
+MULTILINE_CPP_IS_BRIEF = NO
+DETAILS_AT_TOP         = NO
+INHERIT_DOCS           = YES
+INLINE_INFO            = YES
+SORT_MEMBER_DOCS       = YES
+DISTRIBUTE_GROUP_DOC   = NO
+TAB_SIZE               = 8
+GENERATE_TODOLIST      = YES
+GENERATE_TESTLIST      = YES
+GENERATE_BUGLIST       = YES
+GENERATE_DEPRECATEDLIST= YES
+ALIASES                = 
+ENABLED_SECTIONS       = 
+MAX_INITIALIZER_LINES  = 30
+OPTIMIZE_OUTPUT_FOR_C  = NO
+OPTIMIZE_OUTPUT_JAVA   = YES
+SHOW_USED_FILES        = YES
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+QUIET                  = NO
+WARNINGS               = YES
+WARN_IF_UNDOCUMENTED   = YES
+WARN_FORMAT            = "$file:$line: $text"
+WARN_LOGFILE           = 
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+INPUT                  = U:/jaquino/CVS/jts/src/
+FILE_PATTERNS          = 
+RECURSIVE              = YES
+EXCLUDE                = 
+EXCLUDE_SYMLINKS       = NO
+EXCLUDE_PATTERNS       = 
+EXAMPLE_PATH           = 
+EXAMPLE_PATTERNS       = 
+EXAMPLE_RECURSIVE      = NO
+IMAGE_PATH             = 
+INPUT_FILTER           = 
+FILTER_SOURCE_FILES    = NO
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+SOURCE_BROWSER         = YES
+INLINE_SOURCES         = YES
+STRIP_CODE_COMMENTS    = YES
+REFERENCED_BY_RELATION = YES
+REFERENCES_RELATION    = YES
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+ALPHABETICAL_INDEX     = YES
+COLS_IN_ALPHA_INDEX    = 5
+IGNORE_PREFIX          = 
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+GENERATE_HTML          = YES
+HTML_OUTPUT            = html
+HTML_FILE_EXTENSION    = .html
+HTML_HEADER            = 
+HTML_FOOTER            = 
+HTML_STYLESHEET        = 
+HTML_ALIGN_MEMBERS     = YES
+GENERATE_HTMLHELP      = NO
+CHM_FILE               = 
+HHC_LOCATION           = 
+GENERATE_CHI           = NO
+BINARY_TOC             = NO
+TOC_EXPAND             = NO
+DISABLE_INDEX          = NO
+ENUM_VALUES_PER_LINE   = 4
+GENERATE_TREEVIEW      = NO
+TREEVIEW_WIDTH         = 250
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+GENERATE_LATEX         = NO
+LATEX_OUTPUT           = latex
+LATEX_CMD_NAME         = latex
+MAKEINDEX_CMD_NAME     = makeindex
+COMPACT_LATEX          = NO
+PAPER_TYPE             = a4wide
+EXTRA_PACKAGES         = 
+LATEX_HEADER           = 
+PDF_HYPERLINKS         = NO
+USE_PDFLATEX           = NO
+LATEX_BATCHMODE        = NO
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+GENERATE_RTF           = NO
+RTF_OUTPUT             = rtf
+COMPACT_RTF            = NO
+RTF_HYPERLINKS         = NO
+RTF_STYLESHEET_FILE    = 
+RTF_EXTENSIONS_FILE    = 
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+GENERATE_MAN           = NO
+MAN_OUTPUT             = man
+MAN_EXTENSION          = .3
+MAN_LINKS              = NO
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+GENERATE_XML           = NO
+XML_SCHEMA             = 
+XML_DTD                = 
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+GENERATE_AUTOGEN_DEF   = NO
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+GENERATE_PERLMOD       = NO
+PERLMOD_LATEX          = NO
+PERLMOD_PRETTY         = YES
+PERLMOD_MAKEVAR_PREFIX = 
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor   
+#---------------------------------------------------------------------------
+ENABLE_PREPROCESSING   = YES
+MACRO_EXPANSION        = NO
+EXPAND_ONLY_PREDEF     = NO
+SEARCH_INCLUDES        = YES
+INCLUDE_PATH           = 
+INCLUDE_FILE_PATTERNS  = 
+PREDEFINED             = 
+EXPAND_AS_DEFINED      = 
+SKIP_FUNCTION_MACROS   = YES
+#---------------------------------------------------------------------------
+# Configuration::addtions related to external references   
+#---------------------------------------------------------------------------
+TAGFILES               = 
+GENERATE_TAGFILE       = 
+ALLEXTERNALS           = NO
+EXTERNAL_GROUPS        = YES
+PERL_PATH              = /usr/bin/perl
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool   
+#---------------------------------------------------------------------------
+CLASS_DIAGRAMS         = YES
+HIDE_UNDOC_RELATIONS   = YES
+HAVE_DOT               = YES
+CLASS_GRAPH            = YES
+COLLABORATION_GRAPH    = YES
+TEMPLATE_RELATIONS     = YES
+INCLUDE_GRAPH          = YES
+INCLUDED_BY_GRAPH      = YES
+GRAPHICAL_HIERARCHY    = YES
+DOT_IMAGE_FORMAT       = png
+DOT_PATH               = h:/utilities
+DOTFILE_DIRS           = 
+MAX_DOT_GRAPH_WIDTH    = 1024
+MAX_DOT_GRAPH_HEIGHT   = 1024
+GENERATE_LEGEND        = YES
+DOT_CLEANUP            = YES
+#---------------------------------------------------------------------------
+# Configuration::addtions related to the search engine   
+#---------------------------------------------------------------------------
+SEARCHENGINE           = NO
+CGI_NAME               = search.cgi
+CGI_URL                = 
+DOC_URL                = 
+DOC_ABSPATH            = 
+BIN_ABSPATH            = /usr/local/bin/
+EXT_DOC_PATHS          = 

Added: packages/jts/branches/upstream/current/src/MANIFEST.MF
===================================================================
--- packages/jts/branches/upstream/current/src/MANIFEST.MF	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/MANIFEST.MF	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,4 @@
+Manifest-version: 1.0
+Implementation-Title: Java Topology Suite
+Implementation-Version: 1.6
+Implementation-Vendor: Vivid Solutions

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/CGAlgorithms.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/CGAlgorithms.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/CGAlgorithms.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,434 @@
+/*
+ * 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.Coordinate;
+import com.vividsolutions.jts.geom.CoordinateSequence;
+
+/**
+ * Specifies and implements various fundamental Computational Geometric algorithms.
+ * The algorithms supplied in this class are robust for double-precision floating point.
+ *
+ * @version 1.6
+ */
+public class CGAlgorithms
+{
+
+  /**
+   * A value that indicates an orientation of clockwise, or a right turn.
+   */
+  public static final int CLOCKWISE     = -1;
+  public static final int RIGHT         = CLOCKWISE;
+  /**
+   * A value that indicates an orientation of counterclockwise, or a left turn.
+   */
+  public static final int COUNTERCLOCKWISE  = 1;
+  public static final int LEFT              = COUNTERCLOCKWISE;
+  /**
+   * A value that indicates an orientation of collinear, or no turn (straight).
+   */
+  public static final int COLLINEAR         = 0;
+  public static final int STRAIGHT          = COLLINEAR;
+
+  /**
+   * Returns the index of the direction of the point <code>q</code>
+   * relative to a
+   * vector specified by <code>p1-p2</code>.
+   *
+   * @param p1 the origin point of the vector
+   * @param p2 the final point of the vector
+   * @param q the point to compute the direction to
+   *
+   * @return 1 if q is counter-clockwise (left) from p1-p2
+   * @return -1 if q is clockwise (right) from p1-p2
+   * @return 0 if q is collinear with p1-p2
+   */
+  public static int orientationIndex(Coordinate p1, Coordinate p2, Coordinate q) {
+    // travelling along p1->p2, turn counter clockwise to get to q return 1,
+    // travelling along p1->p2, turn clockwise to get to q return -1,
+    // p1, p2 and q are colinear return 0.
+    double dx1 = p2.x - p1.x;
+    double dy1 = p2.y - p1.y;
+    double dx2 = q.x - p2.x;
+    double dy2 = q.y - p2.y;
+    return RobustDeterminant.signOfDet2x2(dx1, dy1, dx2, dy2);
+  }
+
+  public CGAlgorithms() {
+  }
+
+  /**
+   * Test whether a point lies inside a ring.
+   * The ring may be oriented in either direction.
+   * If the point lies on the ring boundary the result of this method is unspecified.
+   * <p>
+   * This algorithm does not attempt to first check the point against the envelope
+   * of the ring.
+   *
+   * @param p point to check for ring inclusion
+   * @param ring assumed to have first point identical to last point
+   * @return <code>true</code> if p is inside ring
+   */
+  public static boolean isPointInRing(Coordinate p, Coordinate[] ring) {
+    /*
+     *  For each segment l = (i-1, i), see if it crosses ray from test point in positive x direction.
+     */
+    int crossings = 0;  // number of segment/ray crossings
+    for (int i = 1; i < ring.length; i++) {
+      int i1 = i - 1;
+      Coordinate p1 = ring[i];
+      Coordinate p2 = ring[i1];
+
+      if (((p1.y > p.y) && (p2.y <= p.y)) ||
+          ((p2.y > p.y) && (p1.y <= p.y))) {
+        double x1 = p1.x - p.x;
+        double y1 = p1.y - p.y;
+        double x2 = p2.x - p.x;
+        double y2 = p2.y - p.y;
+        /*
+        *  segment straddles x axis, so compute intersection with x-axis.
+         */
+        double xInt = RobustDeterminant.signOfDet2x2(x1, y1, x2, y2) / (y2 - y1);
+        //xsave = xInt;
+        /*
+        *  crosses ray if strictly positive intersection.
+         */
+        if (xInt > 0.0) {
+          crossings++;
+        }
+      }
+    }
+    /*
+     *  p is inside if number of crossings is odd.
+     */
+    if ((crossings % 2) == 1) {
+      return true;
+    }
+    else {
+      return false;
+    }
+  }
+
+  /**
+   * Test whether a point lies on the line segments defined by a
+   * list of coordinates.
+   *
+   * @return true true if
+   * the point is a vertex of the line or lies in the interior of a line
+   * segment in the linestring
+   */
+  public static boolean isOnLine(Coordinate p, Coordinate[] pt) {
+    LineIntersector lineIntersector = new RobustLineIntersector();
+    for (int i = 1; i < pt.length; i++) {
+      Coordinate p0 = pt[i - 1];
+      Coordinate p1 = pt[i];
+      lineIntersector.computeIntersection(p, p0, p1);
+      if (lineIntersector.hasIntersection()) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  /**
+   * Computes whether a ring defined by an array of {@link Coordinate} is
+   * oriented counter-clockwise.
+   * <ul>
+   * <li>The list of points is assumed to have the first and last points equal.
+   * <li>This will handle coordinate lists which contain repeated points.
+   * <li>If the ring is invalid, the answer returned may not be correct.
+   * </ul>
+   *
+   * @param ring an array of coordinates forming a ring
+   * @return <code>true</code> if the ring is oriented counter-clockwise.
+   */
+  public static boolean isCCW(Coordinate[] ring) {
+    // # of points without closing endpoint
+    int nPts = ring.length - 1;
+
+    // find highest point
+    Coordinate hip = ring[0];
+    int hii = 0;
+    for (int i = 1; i <= nPts; i++) {
+      Coordinate p = ring[i];
+      if (p.y > hip.y) {
+        hip = p;
+        hii = i;
+      }
+    }
+
+    // find distinct point before highest point
+    int iPrev = hii;
+    do {
+      iPrev = iPrev - 1;
+      if (iPrev < 0) iPrev = nPts;
+    } while (ring[iPrev].equals(hip) && iPrev != hii);
+
+    // find distinct point after highest point
+    int iNext = hii;
+    do {
+      iNext = (iNext + 1) % nPts;
+    } while (ring[iNext].equals(hip) && iNext != hii);
+
+    Coordinate prev = ring[iPrev];
+    Coordinate next = ring[iNext];
+
+    /**
+     * This check catches cases where the ring contains an A-B-A configuration of points.
+     * This can happen if the ring does not contain 3 distinct points
+     * (including the case where the input array has fewer than 4 elements),
+     * or it contains coincident line segments.
+     */
+    if (prev.equals(hip) || next.equals(hip) || prev.equals(next))
+      return false;
+    // MD - don't bother throwing exception, since this isn't a complete check for ring validity
+//  throw new IllegalArgumentException("Invalid ring (contains repeated line segments)");
+
+    int disc = computeOrientation(prev, hip, next);
+
+    /**
+     *  If disc is exactly 0, lines are collinear.  There are two possible cases:
+     *  (1) the lines lie along the x axis in opposite directions
+     *  (2) the lines lie on top of one another
+     *
+     *  (1) is handled by checking if next is left of prev ==> CCW
+     *  (2) will never happen if the ring is valid, so don't check for it
+     *  (Might want to assert this)
+     */
+    boolean isCCW = false;
+    if (disc == 0) {
+      // poly is CCW if prev x is right of next x
+      isCCW = (prev.x > next.x);
+    }
+    else {
+      // if area is positive, points are ordered CCW
+      isCCW = (disc > 0);
+    }
+    return isCCW;
+  }
+
+  /**
+   * Computes the orientation of a point q to the directed line segment p1-p2.
+   * The orientation of a point relative to a directed line segment indicates
+   * which way you turn to get to q after travelling from p1 to p2.
+   *
+   * @return 1 if q is counter-clockwise from p1-p2
+   * @return -1 if q is clockwise from p1-p2
+   * @return 0 if q is collinear with p1-p2
+   */
+  public static int computeOrientation(Coordinate p1, Coordinate p2, Coordinate q) {
+    return orientationIndex(p1, p2, q);
+  }
+
+
+  /**
+   * Computes the distance from a point p to a line segment AB
+   *
+   * Note: NON-ROBUST!
+   *
+   * @param p the point to compute the distance for
+   * @param A one point of the line
+   * @param B another point of the line (must be different to A)
+   * @return the distance from p to line segment AB
+   */
+  public static double distancePointLine(Coordinate p, Coordinate A, Coordinate B)
+  {
+    // if start==end, then use pt distance
+    if (  A.equals(B) ) return p.distance(A);
+
+    // otherwise use comp.graphics.algorithms Frequently Asked Questions method
+    /*(1)     	      AC dot AB
+                   r = ---------
+                         ||AB||^2
+		r has the following meaning:
+		r=0 P = A
+		r=1 P = B
+		r<0 P is on the backward extension of AB
+		r>1 P is on the forward extension of AB
+		0<r<1 P is interior to AB
+	*/
+
+    double r = ( (p.x - A.x) * (B.x - A.x) + (p.y - A.y) * (B.y - A.y) )
+              /
+            ( (B.x - A.x) * (B.x - A.x) + (B.y - A.y) * (B.y - A.y) );
+
+    if (r <= 0.0) return p.distance(A);
+    if (r >= 1.0) return p.distance(B);
+
+
+    /*(2)
+		     (Ay-Cy)(Bx-Ax)-(Ax-Cx)(By-Ay)
+		s = -----------------------------
+		             	L^2
+
+		Then the distance from C to P = |s|*L.
+	*/
+
+    double s = ((A.y - p.y) *(B.x - A.x) - (A.x - p.x)*(B.y - A.y) )
+              /
+            ((B.x - A.x) * (B.x - A.x) + (B.y - A.y) * (B.y - A.y) );
+
+    return
+      Math.abs(s) *
+      Math.sqrt(((B.x - A.x) * (B.x - A.x) + (B.y - A.y) * (B.y - A.y)));
+  }
+  /**
+   * Computes the perpendicular distance from a point p
+   * to the (infinite) line containing the points AB
+   *
+   * @param p the point to compute the distance for
+   * @param A one point of the line
+   * @param B another point of the line (must be different to A)
+   * @return the distance from p to line AB
+   */
+  public static double distancePointLinePerpendicular(Coordinate p, Coordinate A, Coordinate B)
+  {
+    // use comp.graphics.algorithms Frequently Asked Questions method
+    /*(2)
+                     (Ay-Cy)(Bx-Ax)-(Ax-Cx)(By-Ay)
+                s = -----------------------------
+                                     L^2
+
+                Then the distance from C to P = |s|*L.
+        */
+
+    double s = ((A.y - p.y) *(B.x - A.x) - (A.x - p.x)*(B.y - A.y) )
+              /
+            ((B.x - A.x) * (B.x - A.x) + (B.y - A.y) * (B.y - A.y) );
+
+    return
+      Math.abs(s) *
+      Math.sqrt(((B.x - A.x) * (B.x - A.x) + (B.y - A.y) * (B.y - A.y)));
+  }
+
+  /**
+   * Computes the distance from a line segment AB to a line segment CD
+   *
+   * Note: NON-ROBUST!
+   *
+   * @param A a point of one line
+   * @param B the second point of  (must be different to A)
+   * @param C one point of the line
+   * @param D another point of the line (must be different to A)
+   */
+  public static double distanceLineLine(Coordinate A, Coordinate B, Coordinate C, Coordinate D)
+  {
+    // check for zero-length segments
+    if (  A.equals(B) )	return distancePointLine(A,C,D);
+    if (  C.equals(D) )	return distancePointLine(D,A,B);
+
+    // AB and CD are line segments
+    /* from comp.graphics.algo
+
+	Solving the above for r and s yields
+				(Ay-Cy)(Dx-Cx)-(Ax-Cx)(Dy-Cy)
+	           r = ----------------------------- (eqn 1)
+				(Bx-Ax)(Dy-Cy)-(By-Ay)(Dx-Cx)
+
+		 	(Ay-Cy)(Bx-Ax)-(Ax-Cx)(By-Ay)
+		s = ----------------------------- (eqn 2)
+			(Bx-Ax)(Dy-Cy)-(By-Ay)(Dx-Cx)
+	Let P be the position vector of the intersection point, then
+		P=A+r(B-A) or
+		Px=Ax+r(Bx-Ax)
+		Py=Ay+r(By-Ay)
+	By examining the values of r & s, you can also determine some other
+limiting conditions:
+		If 0<=r<=1 & 0<=s<=1, intersection exists
+		r<0 or r>1 or s<0 or s>1 line segments do not intersect
+		If the denominator in eqn 1 is zero, AB & CD are parallel
+		If the numerator in eqn 1 is also zero, AB & CD are collinear.
+
+	*/
+    double r_top = (A.y-C.y)*(D.x-C.x) - (A.x-C.x)*(D.y-C.y) ;
+    double r_bot = (B.x-A.x)*(D.y-C.y) - (B.y-A.y)*(D.x-C.x) ;
+
+    double s_top = (A.y-C.y)*(B.x-A.x) - (A.x-C.x)*(B.y-A.y);
+    double s_bot = (B.x-A.x)*(D.y-C.y) - (B.y-A.y)*(D.x-C.x);
+
+    if  ( (r_bot==0) || (s_bot == 0) ) {
+      return
+        Math.min(distancePointLine(A,C,D),
+	  Math.min(distancePointLine(B,C,D),
+	    Math.min(distancePointLine(C,A,B),
+	      distancePointLine(D,A,B)    ) ) );
+
+    }
+    double s = s_top/s_bot;
+    double r=  r_top/r_bot;
+
+    if ((r < 0) || ( r > 1) || (s < 0) || (s > 1) )	{
+      //no intersection
+      return
+        Math.min(distancePointLine(A,C,D),
+	  Math.min(distancePointLine(B,C,D),
+	    Math.min(distancePointLine(C,A,B),
+	      distancePointLine(D,A,B)    ) ) );
+    }
+    return 0.0; //intersection exists
+  }
+
+  /**
+   * Returns the signed area for a ring.  The area is positive if
+   * the ring is oriented CW.
+   */
+  public static double signedArea(Coordinate[] ring)
+  {
+    if (ring.length < 3) return 0.0;
+    double sum = 0.0;
+    for (int i = 0; i < ring.length - 1; i++) {
+      double bx = ring[i].x;
+      double by = ring[i].y;
+      double cx = ring[i + 1].x;
+      double cy = ring[i + 1].y;
+      sum += (bx + cx) * (cy - by);
+    }
+    return -sum  / 2.0;
+  }
+
+  /**
+   * Computes the length of a linestring specified by a sequence of points.
+   *
+   * @param pts the points specifying the linestring
+   * @return the length of the linestring
+   */
+  public static double length(CoordinateSequence pts) {
+      if (pts.size() < 1) return 0.0;
+      double sum = 0.0;
+      for (int i = 1; i < pts.size(); i++) {
+        sum += pts.getCoordinate(i).distance(pts.getCoordinate(i - 1));
+      }
+      return sum;
+  }
+
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/CentroidArea.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/CentroidArea.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/CentroidArea.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,165 @@
+
+/*
+ * 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.*;
+
+/**
+ * Computes the centroid of an area geometry.
+ * <h2>Algorithm</h2>
+ * Based on the usual algorithm for calculating
+ * the centroid as a weighted sum of the centroids
+ * of a decomposition of the area into (possibly overlapping) triangles.
+ * The algorithm has been extended to handle holes and multi-polygons.
+ * See <code>http://www.faqs.org/faqs/graphics/algorithms-faq/</code>
+ * for further details of the basic approach.
+ *
+ * @version 1.6
+ */
+public class CentroidArea
+{
+
+  private Coordinate basePt = null;// the point all triangles are based at
+  private Coordinate triangleCent3 = new Coordinate();// temporary variable to hold centroid of triangle
+  private double  areasum2 = 0;        /* Partial area sum */
+  private Coordinate cg3 = new Coordinate(); // partial centroid sum
+
+  public CentroidArea()
+  {
+    basePt = null;
+  }
+
+  /**
+   * Adds the area defined by a Geometry to the centroid total.
+   * If the geometry has no area it does not contribute to the centroid.
+   *
+   * @param geom the geometry to add
+   */
+  public void add(Geometry geom)
+  {
+    if (geom instanceof Polygon) {
+      Polygon poly = (Polygon) geom;
+      setBasePoint(poly.getExteriorRing().getCoordinateN(0));
+      add(poly);
+    }
+    else if (geom instanceof GeometryCollection) {
+      GeometryCollection gc = (GeometryCollection) geom;
+      for (int i = 0; i < gc.getNumGeometries(); i++) {
+        add(gc.getGeometryN(i));
+      }
+    }
+  }
+
+  /**
+   * Adds the area defined by an array of
+   * coordinates.  The array must be a ring;
+   * i.e. end with the same coordinate as it starts with.
+   * @param ring an array of {@link Coordinate}s
+   */
+  public void add(Coordinate[] ring)
+  {
+    setBasePoint(ring[0]);
+    addShell(ring);
+  }
+
+  public Coordinate getCentroid()
+  {
+    Coordinate cent = new Coordinate();
+    cent.x = cg3.x / 3 / areasum2;
+    cent.y = cg3.y / 3 / areasum2;
+    return cent;
+  }
+
+  private void setBasePoint(Coordinate basePt)
+  {
+    if (this.basePt == null)
+      this.basePt = basePt;
+  }
+  private void add(Polygon poly)
+  {
+    addShell(poly.getExteriorRing().getCoordinates());
+    for (int i = 0; i < poly.getNumInteriorRing(); i++) {
+      addHole(poly.getInteriorRingN(i).getCoordinates());
+    }
+  }
+
+  private void addShell(Coordinate[] pts)
+  {
+
+    boolean isPositiveArea = ! CGAlgorithms.isCCW(pts);
+    for (int i = 0; i < pts.length - 1; i++) {
+      addTriangle(basePt, pts[i], pts[i+1], isPositiveArea);
+    }
+  }
+  private void addHole(Coordinate[] pts)
+  {
+    boolean isPositiveArea = CGAlgorithms.isCCW(pts);
+    for (int i = 0; i < pts.length - 1; i++) {
+      addTriangle(basePt, pts[i], pts[i+1], isPositiveArea);
+    }
+  }
+  private void addTriangle(Coordinate p0, Coordinate p1, Coordinate p2, boolean isPositiveArea)
+  {
+    double sign = (isPositiveArea) ? 1.0 : -1.0;
+    centroid3( p0, p1, p2, triangleCent3 );
+    double area2 =  area2( p0, p1, p2 );
+    cg3.x += sign * area2 * triangleCent3.x;
+    cg3.y += sign * area2 * triangleCent3.y;
+    areasum2 += sign * area2;
+  }
+  /**
+   * Returns three times the centroid of the triangle p1-p2-p3.
+   * The factor of 3 is
+   * left in to permit division to be avoided until later.
+   */
+  private static void centroid3( Coordinate p1, Coordinate p2, Coordinate p3, Coordinate c )
+  {
+    c.x = p1.x + p2.x + p3.x;
+    c.y = p1.y + p2.y + p3.y;
+    return;
+  }
+
+  /**
+   * Returns twice the signed area of the triangle p1-p2-p3,
+   * positive if a,b,c are oriented ccw, and negative if cw.
+   */
+  private static double area2( Coordinate p1, Coordinate p2, Coordinate p3 )
+  {
+    return
+    (p2.x - p1.x) * (p3.y - p1.y) -
+        (p3.x - p1.x) * (p2.y - p1.y);
+  }
+
+
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/CentroidLine.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/CentroidLine.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/CentroidLine.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,98 @@
+
+/*
+ * 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.*;
+
+/**
+ * Computes the centroid of a linear geometry.
+ * <h2>Algorithm</h2>
+ * Compute the average of the midpoints
+ * of all line segments weighted by the segment length.
+ *
+ * @version 1.6
+ */
+public class CentroidLine
+{
+  private Coordinate centSum = new Coordinate();
+  private double totalLength = 0.0;
+
+  public CentroidLine()
+  {
+  }
+
+  /**
+   * Adds the linestring(s) defined by a Geometry to the centroid total.
+   * If the geometry is not linear it does not contribute to the centroid
+   * @param geom the geometry to add
+   */
+  public void add(Geometry geom)
+  {
+    if (geom instanceof LineString) {
+      add(geom.getCoordinates());
+    }
+    else if (geom instanceof GeometryCollection) {
+      GeometryCollection gc = (GeometryCollection) geom;
+      for (int i = 0; i < gc.getNumGeometries(); i++) {
+        add(gc.getGeometryN(i));
+      }
+    }
+  }
+
+  public Coordinate getCentroid()
+  {
+    Coordinate cent = new Coordinate();
+    cent.x = centSum.x / totalLength;
+    cent.y = centSum.y / totalLength;
+    return cent;
+  }
+
+  /**
+   * Adds the length defined by an array of coordinates.
+   * @param pts an array of {@link Coordinate}s
+   */
+  public void add(Coordinate[] pts)
+  {
+    for (int i = 0; i < pts.length - 1; i++) {
+      double segmentLen = pts[i].distance(pts[i + 1]);
+      totalLength += segmentLen;
+
+      double midx = (pts[i].x + pts[i + 1].x) / 2;
+      centSum.x += segmentLen * midx;
+      double midy = (pts[i].y + pts[i + 1].y) / 2;
+      centSum.y += segmentLen * midy;
+    }
+  }
+
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/CentroidPoint.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/CentroidPoint.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/CentroidPoint.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,91 @@
+
+/*
+ * 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.*;
+
+/**
+ * Computes the centroid of a point geometry.
+ * <h2>Algorithm</h2>
+ * Compute the average of all points.
+ *
+ * @version 1.6
+ */
+public class CentroidPoint
+{
+  private int ptCount = 0;
+  private Coordinate centSum = new Coordinate();
+
+  public CentroidPoint()
+  {
+  }
+
+  /**
+   * Adds the point(s) defined by a Geometry to the centroid total.
+   * If the geometry is not of dimension 0 it does not contribute to the centroid.
+   * @param geom the geometry to add
+   */
+  public void add(Geometry geom)
+  {
+    if (geom instanceof Point) {
+      add(geom.getCoordinate());
+    }
+    else if (geom instanceof GeometryCollection) {
+      GeometryCollection gc = (GeometryCollection) geom;
+      for (int i = 0; i < gc.getNumGeometries(); i++) {
+        add(gc.getGeometryN(i));
+      }
+    }
+  }
+
+  /**
+   * Adds the length defined by an array of coordinates.
+   * @param pts an array of {@link Coordinate}s
+   */
+  public void add(Coordinate pt)
+  {
+    ptCount += 1;
+    centSum.x += pt.x;
+    centSum.y += pt.y;
+  }
+
+  public Coordinate getCentroid()
+  {
+    Coordinate cent = new Coordinate();
+    cent.x = centSum.x / ptCount;
+    cent.y = centSum.y / ptCount;
+    return cent;
+  }
+
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/ConvexHull.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/ConvexHull.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/ConvexHull.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,356 @@
+
+
+/*
+ * 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.*;
+import com.vividsolutions.jts.util.Assert;
+
+import java.util.*;
+import com.vividsolutions.jts.util.UniqueCoordinateArrayFilter;
+
+/**
+ * Computes the convex hull of a {@link Geometry}.
+ * The convex hull is the smallest convex Geometry that contains all the
+ * points in the input Geometry.
+ * Uses the Graham Scan algorithm.
+ *
+ *@version 1.6
+ */
+public class ConvexHull
+{
+  private PointLocator pointLocator = new PointLocator();
+  //private CGAlgorithms cgAlgorithms = new RobustCGAlgorithms();
+  private Geometry geometry;
+  private GeometryFactory factory;
+
+  /**
+   * Create a new convex hull construction for the input {@link Geometry}.
+   */
+  public ConvexHull(Geometry geometry)
+  {
+    this.geometry = geometry;
+  }
+
+  /**
+   * Returns a {@link Geometry} that represents the convex hull of the input
+   * geometry.
+   * The geometry will contain the minimal number of points needed to
+   * represent the convex hull.  In particular, no more than two consecutive
+   * points will be collinear.
+   *
+   * @return if the convex hull contains 3 or more points, a {@link Polygon};
+   * 2 points, a {@link LineString};
+   * 1 point, a {@link Point};
+   * 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 (pts.length == 1) {
+      return factory.createPoint(pts[0]);
+    }
+    if (pts.length == 2) {
+      return factory.createLineString(pts);
+    }
+
+    // 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);
+    }
+
+    // Use Graham scan to find convex hull.
+    Stack cHS = grahamScan(pspts);
+
+    // Convert stack to an array.
+    Coordinate[] cH = toCoordinateArray(cHS);
+
+    // Convert array to linear ring.
+    return lineOrPolygon(cH);
+  }
+
+  /**
+   * An alternative to Stack.toArray, which is not present in earlier versions
+   * of Java.
+   */
+  protected Coordinate[] toCoordinateArray(Stack stack) {
+    Coordinate[] coordinates = new Coordinate[stack.size()];
+    for (int i = 0; i < stack.size(); i++) {
+      Coordinate coordinate = (Coordinate) stack.get(i);
+      coordinates[i] = coordinate;
+    }
+    return coordinates;
+  }
+
+  private Coordinate[] reduce(Coordinate[] pts) {
+    BigQuad bigQuad = bigQuad(pts);
+
+    // Build a linear ring defining a big poly.
+    ArrayList bigPoly = new ArrayList();
+    bigPoly.add(bigQuad.westmost);
+    if (!bigPoly.contains(bigQuad.northmost)) {
+      bigPoly.add(bigQuad.northmost);
+    }
+    if (!bigPoly.contains(bigQuad.eastmost)) {
+      bigPoly.add(bigQuad.eastmost);
+    }
+    if (!bigPoly.contains(bigQuad.southmost)) {
+      bigPoly.add(bigQuad.southmost);
+    }
+    if (bigPoly.size() < 3) {
+      return pts;
+    }
+    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]);
+      }
+    }
+    Coordinate[] rP = (Coordinate[]) reducedSet.toArray(new Coordinate[0]);
+
+    // Return this array as the reduced problem.
+    return rP;
+  }
+
+  private Coordinate[] preSort(Coordinate[] pts) {
+    Coordinate t;
+
+    // find the lowest point in the set. If two or more points have
+    // the same minimum y coordinate choose the one with the minimu x.
+    // This focal point is put in array location pts[0].
+    for (int i = 1; i < pts.length; i++) {
+      if ((pts[i].y < pts[0].y) || ((pts[i].y == pts[0].y) && (pts[i].x < pts[0].x))) {
+        t = pts[0];
+        pts[0] = pts[i];
+        pts[i] = t;
+      }
+    }
+
+    // sort the points radially around the focal point.
+    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]);
+    p = (Coordinate) ps.push(c[2]);
+    for (int i = 3; i < c.length; i++) {
+      p = (Coordinate) ps.pop();
+      while (CGAlgorithms.computeOrientation((Coordinate) ps.peek(), p, c[i]) > 0) {
+        p = (Coordinate) ps.pop();
+      }
+      p = (Coordinate) ps.push(p);
+      p = (Coordinate) ps.push(c[i]);
+    }
+    p = (Coordinate) ps.push(c[0]);
+    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
+   */
+  private boolean isBetween(Coordinate c1, Coordinate c2, Coordinate c3) {
+    if (CGAlgorithms.computeOrientation(c1, c2, c3) != 0) {
+      return false;
+    }
+    if (c1.x != c3.x) {
+      if (c1.x <= c2.x && c2.x <= c3.x) {
+        return true;
+      }
+      if (c3.x <= c2.x && c2.x <= c1.x) {
+        return true;
+      }
+    }
+    if (c1.y != c3.y) {
+      if (c1.y <= c2.y && c2.y <= c3.y) {
+        return true;
+      }
+      if (c3.y <= c2.y && c2.y <= c1.y) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  private BigQuad bigQuad(Coordinate[] pts) {
+    BigQuad bigQuad = new BigQuad();
+    bigQuad.northmost = pts[0];
+    bigQuad.southmost = pts[0];
+    bigQuad.westmost = pts[0];
+    bigQuad.eastmost = pts[0];
+    for (int i = 1; i < pts.length; i++) {
+      if (pts[i].x < bigQuad.westmost.x) {
+        bigQuad.westmost = pts[i];
+      }
+      if (pts[i].x > bigQuad.eastmost.x) {
+        bigQuad.eastmost = pts[i];
+      }
+      if (pts[i].y < bigQuad.southmost.y) {
+        bigQuad.southmost = pts[i];
+      }
+      if (pts[i].y > bigQuad.northmost.y) {
+        bigQuad.northmost = pts[i];
+      }
+    }
+    return bigQuad;
+  }
+
+  /**
+   *@param  vertices  the vertices of a linear ring, which may or may not be
+   *      flattened (i.e. vertices collinear)
+   *@return           a 2-vertex <code>LineString</code> if the vertices are
+   *      collinear; otherwise, a <code>Polygon</code> with unnecessary
+   *      (collinear) vertices removed
+   */
+  private Geometry lineOrPolygon(Coordinate[] coordinates) {
+
+    coordinates = cleanRing(coordinates);
+    if (coordinates.length == 3) {
+     return factory.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);
+  }
+
+  /**
+   *@param  vertices  the vertices of a linear ring, which may or may not be
+   *      flattened (i.e. vertices collinear)
+   *@return           the coordinates with unnecessary (collinear) vertices
+   *      removed
+   */
+  private Coordinate[] cleanRing(Coordinate[] original) {
+    Assert.equals(original[0], original[original.length - 1]);
+    ArrayList cleanedRing = new ArrayList();
+    Coordinate previousDistinctCoordinate = null;
+    for (int i = 0; i <= original.length - 2; i++) {
+      Coordinate currentCoordinate = original[i];
+      Coordinate nextCoordinate = original[i+1];
+      if (currentCoordinate.equals(nextCoordinate)) {
+        continue;
+      }
+      if (previousDistinctCoordinate != null
+          && isBetween(previousDistinctCoordinate, currentCoordinate, nextCoordinate)) {
+        continue;
+      }
+      cleanedRing.add(currentCoordinate);
+      previousDistinctCoordinate = currentCoordinate;
+    }
+    cleanedRing.add(original[original.length - 1]);
+    Coordinate[] cleanedRingCoordinates = new Coordinate[cleanedRing.size()];
+    return (Coordinate[]) cleanedRing.toArray(cleanedRingCoordinates);
+  }
+
+  private static class BigQuad {
+    public Coordinate northmost;
+    public Coordinate southmost;
+    public Coordinate westmost;
+    public Coordinate eastmost;
+  }
+
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/HCoordinate.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/HCoordinate.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/HCoordinate.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,122 @@
+
+
+
+/*
+ * 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.
+ *
+ * @version 1.6
+ */
+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.
+ */
+  public static Coordinate intersection(
+      Coordinate p1, Coordinate p2,
+      Coordinate q1, Coordinate q2)
+    throws NotRepresentableException
+  {
+    HCoordinate l1 = new HCoordinate(new HCoordinate(p1), new HCoordinate(p2));
+    HCoordinate l2 = new HCoordinate(new HCoordinate(q1), new HCoordinate(q2));
+    HCoordinate intHCoord = new HCoordinate(l1, l2);
+    Coordinate intPt = intHCoord.getCoordinate();
+    return intPt;
+  }
+
+
+    public double x,y,w;
+
+    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(Coordinate p) {
+        x = p.x;
+        y = p.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 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 Coordinate getCoordinate() throws NotRepresentableException {
+      Coordinate p = new Coordinate();
+      p.x = getX();
+      p.y = getY();
+      return p;
+    }
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/InteriorPointArea.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/InteriorPointArea.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/InteriorPointArea.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,162 @@
+
+/*
+ * 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.*;
+
+/**
+ * Computes a point in the interior of an area geometry.
+ *
+ * <h2>Algorithm</h2>
+ * <ul>
+ *   <li>Find the intersections between the geometry
+ *       and the horizontal bisector of the area's envelope
+ *   <li>Pick the midpoint of the largest intersection (the intersections
+ *       will be lines and points)
+ * </ul>
+ *
+ * <b>
+ * Note: If a fixed precision model is used,
+ * in some cases this method may return a point
+ * which does not lie in the interior.
+ * </b>
+ *
+ * @version 1.6
+ */
+public class InteriorPointArea {
+
+  private static double avg(double a, double b)
+  {
+    return (a + b) / 2.0;
+  }
+
+  private GeometryFactory factory;
+  private Coordinate interiorPoint = null;
+  private double maxWidth = 0.0;
+
+  public InteriorPointArea(Geometry g)
+  {
+    factory = g.getFactory();
+    add(g);
+  }
+  public Coordinate getInteriorPoint()
+  {
+    return interiorPoint;
+  }
+
+  /**
+   * Tests the interior vertices (if any)
+   * defined by a linear Geometry for the best inside point.
+   * If a Geometry is not of dimension 1 it is not tested.
+   * @param geom the geometry to add
+   */
+  private void add(Geometry geom)
+  {
+    if (geom instanceof Polygon) {
+      addPolygon(geom);
+    }
+    else if (geom instanceof GeometryCollection) {
+      GeometryCollection gc = (GeometryCollection) geom;
+      for (int i = 0; i < gc.getNumGeometries(); i++) {
+        add(gc.getGeometryN(i));
+      }
+    }
+  }
+
+  /**
+   * Finds a reasonable point at which to label a Geometry.
+   * @param geometry the geometry to analyze
+   * @return the midpoint of the largest intersection between the geometry and
+   * a line halfway down its envelope
+   */
+  public void addPolygon(Geometry geometry) {
+      LineString bisector = horizontalBisector(geometry);
+
+      Geometry intersections = bisector.intersection(geometry);
+      Geometry widestIntersection = widestGeometry(intersections);
+
+      double width = widestIntersection.getEnvelopeInternal().getWidth();
+      if (interiorPoint == null || width > maxWidth) {
+        interiorPoint = centre(widestIntersection.getEnvelopeInternal());
+        maxWidth = width;
+      }
+  }
+
+  //@return if geometry is a collection, the widest sub-geometry; otherwise,
+  //the geometry itself
+  protected Geometry widestGeometry(Geometry geometry) {
+    if (!(geometry instanceof GeometryCollection)) {
+        return geometry;
+    }
+    return widestGeometry((GeometryCollection) geometry);
+  }
+
+  private Geometry widestGeometry(GeometryCollection gc) {
+    if (gc.isEmpty()) {
+        return gc;
+    }
+
+    Geometry widestGeometry = gc.getGeometryN(0);
+    for (int i = 1; i < gc.getNumGeometries(); i++) { //Start at 1
+        if (gc.getGeometryN(i).getEnvelopeInternal().getWidth() >
+            widestGeometry.getEnvelopeInternal().getWidth()) {
+            widestGeometry = gc.getGeometryN(i);
+        }
+    }
+    return widestGeometry;
+  }
+
+  protected LineString horizontalBisector(Geometry geometry) {
+    Envelope envelope = geometry.getEnvelopeInternal();
+
+    // Assert: for areas, minx <> maxx
+    double avgY = avg(envelope.getMinY(), envelope.getMaxY());
+    return factory.createLineString(new Coordinate[] {
+            new Coordinate(envelope.getMinX(), avgY),
+            new Coordinate(envelope.getMaxX(), avgY)
+        });
+  }
+
+  /**
+   * Returns the centre point of the envelope.
+   * @param envelope the envelope to analyze
+   * @return the centre of the envelope
+   */
+  public Coordinate centre(Envelope envelope) {
+      return new Coordinate(avg(envelope.getMinX(),
+              envelope.getMaxX()),
+          avg(envelope.getMinY(), envelope.getMaxY()));
+  }
+
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/InteriorPointLine.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/InteriorPointLine.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/InteriorPointLine.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,127 @@
+
+/*
+ * 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.*;
+
+/**
+ * Computes a point in the interior of an linear geometry.
+ * <h2>Algorithm</h2>
+ * <ul>
+ * <li>Find an interior vertex which is closest to
+ * the centroid of the linestring.
+ * <li>If there is no interior vertex, find the endpoint which is
+ * closest to the centroid.
+ * </ul>
+ *
+ * @version 1.6
+ */
+public class InteriorPointLine {
+
+  private Coordinate centroid;
+  private double minDistance = Double.MAX_VALUE;
+
+  private Coordinate interiorPoint = null;
+
+  public InteriorPointLine(Geometry g)
+  {
+    centroid = g.getCentroid().getCoordinate();
+    addInterior(g);
+    if (interiorPoint == null)
+      addEndpoints(g);
+  }
+
+  public Coordinate getInteriorPoint()
+  {
+    return interiorPoint;
+  }
+
+  /**
+   * Tests the interior vertices (if any)
+   * defined by a linear Geometry for the best inside point.
+   * If a Geometry is not of dimension 1 it is not tested.
+   * @param geom the geometry to add
+   */
+  private void addInterior(Geometry geom)
+  {
+    if (geom instanceof LineString) {
+      addInterior(geom.getCoordinates());
+    }
+    else if (geom instanceof GeometryCollection) {
+      GeometryCollection gc = (GeometryCollection) geom;
+      for (int i = 0; i < gc.getNumGeometries(); i++) {
+        addInterior(gc.getGeometryN(i));
+      }
+    }
+  }
+  private void addInterior(Coordinate[] pts)
+  {
+    for (int i = 1; i < pts.length - 1; i++) {
+      add(pts[i]);
+    }
+  }
+  /**
+   * Tests the endpoint vertices
+   * defined by a linear Geometry for the best inside point.
+   * If a Geometry is not of dimension 1 it is not tested.
+   * @param geom the geometry to add
+   */
+  private void addEndpoints(Geometry geom)
+  {
+    if (geom instanceof LineString) {
+      addEndpoints(geom.getCoordinates());
+    }
+    else if (geom instanceof GeometryCollection) {
+      GeometryCollection gc = (GeometryCollection) geom;
+      for (int i = 0; i < gc.getNumGeometries(); i++) {
+        addEndpoints(gc.getGeometryN(i));
+      }
+    }
+  }
+  private void addEndpoints(Coordinate[] pts)
+  {
+    add(pts[0]);
+    add(pts[pts.length - 1]);
+  }
+
+  private void add(Coordinate point)
+  {
+    double dist = point.distance(centroid);
+    if (dist < minDistance) {
+      interiorPoint = new Coordinate(point);
+      minDistance = dist;
+    }
+  }
+
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/InteriorPointPoint.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/InteriorPointPoint.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/InteriorPointPoint.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,88 @@
+
+/*
+ * 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.*;
+
+/**
+ * Computes a point in the interior of an point geometry.
+ * <h2>Algorithm</h2>
+ * Find a point which is closest to the centroid of the geometry.
+ *
+ * @version 1.6
+ */
+public class InteriorPointPoint {
+
+  private Coordinate centroid;
+  private double minDistance = Double.MAX_VALUE;
+
+  private Coordinate interiorPoint = null;
+
+  public InteriorPointPoint(Geometry g)
+  {
+    centroid = g.getCentroid().getCoordinate();
+    add(g);
+  }
+
+  /**
+   * Tests the point(s) defined by a Geometry for the best inside point.
+   * If a Geometry is not of dimension 0 it is not tested.
+   * @param geom the geometry to add
+   */
+  private void add(Geometry geom)
+  {
+    if (geom instanceof Point) {
+      add(geom.getCoordinate());
+    }
+    else if (geom instanceof GeometryCollection) {
+      GeometryCollection gc = (GeometryCollection) geom;
+      for (int i = 0; i < gc.getNumGeometries(); i++) {
+        add(gc.getGeometryN(i));
+      }
+    }
+  }
+  private void add(Coordinate point)
+  {
+    double dist = point.distance(centroid);
+    if (dist < minDistance) {
+      interiorPoint = new Coordinate(point);
+      minDistance = dist;
+    }
+  }
+
+  public Coordinate getInteriorPoint()
+  {
+    return interiorPoint;
+  }
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/LineIntersector.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/LineIntersector.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/LineIntersector.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,382 @@
+
+
+
+/*
+ * 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.*;
+import com.vividsolutions.jts.util.*;
+
+/**
+ * A LineIntersector is an algorithm that can both test whether
+ * two line segments intersect and compute the intersection point
+ * if they do.
+ * The intersection point may be computed in a precise or non-precise manner.
+ * Computing it precisely involves rounding it to an integer.  (This assumes
+ * that the input coordinates have been made precise by scaling them to
+ * an integer grid.)
+ *
+ * @version 1.6
+ */
+public abstract class LineIntersector {
+
+  public final static int DONT_INTERSECT = 0;
+  public final static int DO_INTERSECT = 1;
+  public final static int COLLINEAR = 2;
+
+  /**
+   * Computes the "edge distance" of an intersection point p along a segment.
+   * The edge distance is a metric of the point along the edge.
+   * The metric used is a robust and easy to compute metric function.
+   * It is <b>not</b> equivalent to the usual Euclidean metric.
+   * It relies on the fact that either the x or the y ordinates of the
+   * points in the edge are unique, depending on whether the edge is longer in
+   * the horizontal or vertical direction.
+   * <p>
+   * NOTE: This function may produce incorrect distances
+   *  for inputs where p is not precisely on p1-p2
+   * (E.g. p = (139,9) p1 = (139,10), p2 = (280,1) produces distanct 0.0, which is incorrect.
+   * <p>
+   * My hypothesis is that the function is safe to use for points which are the
+   * result of <b>rounding</b> points which lie on the line,
+   * but not safe to use for <b>truncated</b> points.
+   */
+  public static double computeEdgeDistance(
+        Coordinate p,
+        Coordinate p0,
+        Coordinate p1)
+  {
+    double dx = Math.abs(p1.x - p0.x);
+    double dy = Math.abs(p1.y - p0.y);
+
+    double dist = -1.0;   // sentinel value
+    if (p.equals(p0)) {
+      dist = 0.0;
+    }
+    else if (p.equals(p1)) {
+      if (dx > dy)
+        dist = dx;
+      else
+        dist = dy;
+    }
+    else {
+      double pdx = Math.abs(p.x - p0.x);
+      double pdy = Math.abs(p.y - p0.y);
+      if (dx > dy)
+        dist = pdx;
+      else
+        dist = pdy;
+      // <FIX>
+      // hack to ensure that non-endpoints always have a non-zero distance
+      if (dist == 0.0 && ! p.equals(p0))
+      {
+        dist = Math.max(pdx, pdy);
+      }
+    }
+    Assert.isTrue(! (dist == 0.0 && ! p.equals(p0)), "Bad distance calculation");
+    return dist;
+  }
+
+  /**
+   * This function is non-robust, since it may compute the square of large numbers.
+   * Currently not sure how to improve this.
+   */
+  public static double nonRobustComputeEdgeDistance(
+        Coordinate p,
+        Coordinate p1,
+        Coordinate p2)
+  {
+    double dx = p.x - p1.x;
+    double dy = p.y - p1.y;
+    double dist = Math.sqrt(dx * dx + dy * dy);   // dummy value
+    Assert.isTrue(! (dist == 0.0 && ! p.equals(p1)), "Invalid distance calculation");
+    return dist;
+  }
+
+  protected int result;
+  protected Coordinate[][] inputLines = new Coordinate[2][2];
+  protected Coordinate[] intPt = new Coordinate[2];
+  /**
+   * The indexes of the endpoints of the intersection lines, in order along
+   * the corresponding line
+   */
+  protected int[][] intLineIndex;
+  protected boolean isProper;
+  protected Coordinate pa;
+  protected Coordinate pb;
+  /**
+   * If makePrecise is true, computed intersection coordinates will be made precise
+   * using Coordinate#makePrecise
+   */
+  protected PrecisionModel precisionModel = null;
+//public int numIntersects = 0;
+
+  public LineIntersector() {
+    intPt[0] = new Coordinate();
+    intPt[1] = new Coordinate();
+    // alias the intersection points for ease of reference
+    pa = intPt[0];
+    pb = intPt[1];
+    result = 0;
+  }
+
+  /**
+   * Force computed intersection to be rounded to a given precision model
+   * @param precisionModel
+   * @deprecated use <code>setPrecisionModel</code> instead
+   */
+  public void setMakePrecise(PrecisionModel precisionModel)
+  {
+    this.precisionModel = precisionModel;
+  }
+
+  /**
+   * Force computed intersection to be rounded to a given precision model.
+   * No getter is provided, because the precision model is not required to be specified.
+   * @param precisionModel
+   */
+  public void setPrecisionModel(PrecisionModel precisionModel)
+  {
+    this.precisionModel = precisionModel;
+  }
+
+  /**
+   * Compute the intersection of a point p and the line p1-p2.
+   * This function computes the boolean value of the hasIntersection test.
+   * The actual value of the intersection (if there is one)
+   * is equal to the value of <code>p</code>.
+   */
+  public abstract void computeIntersection(
+        Coordinate p,
+        Coordinate p1, Coordinate p2);
+
+  protected boolean isCollinear() {
+    return result == COLLINEAR;
+  }
+
+  /**
+   * Computes the intersection of the lines p1-p2 and p3-p4.
+   * This function computes both the boolean value of the hasIntersection test
+   * and the (approximate) value of the intersection point itself (if there is one).
+   */
+  public void computeIntersection(
+                Coordinate p1, Coordinate p2,
+                Coordinate p3, Coordinate p4) {
+    inputLines[0][0] = p1;
+    inputLines[0][1] = p2;
+    inputLines[1][0] = p3;
+    inputLines[1][1] = p4;
+    result = computeIntersect(p1, p2, p3, p4);
+//numIntersects++;
+  }
+
+  protected abstract int computeIntersect(
+                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";
+    }
+    return str;
+  }
+
+  protected boolean isEndPoint() {
+    return hasIntersection() && !isProper;
+  }
+
+  /**
+   * Tests whether the input geometries intersect.
+   *
+   * @return true if the input geometries intersect
+   */
+  public boolean hasIntersection() {
+    return result != DONT_INTERSECT;
+  }
+
+  /**
+   * Returns the number of intersection points found.  This will be either 0, 1 or 2.
+   */
+  public int getIntersectionNum() { return result; }
+
+  /**
+   * Returns the intIndex'th intersection point
+   *
+   * @param intIndex is 0 or 1
+   *
+   * @return the intIndex'th intersection point
+   */
+  public Coordinate getIntersection(int intIndex)  { return intPt[intIndex]; }
+
+  protected void computeIntLineIndex() {
+    if (intLineIndex == null) {
+      intLineIndex = new int[2][2];
+      computeIntLineIndex(0);
+      computeIntLineIndex(1);
+    }
+  }
+
+  /**
+   * Test whether a point is a intersection point of two line segments.
+   * Note that if the intersection is a line segment, this method only tests for
+   * equality with the endpoints of the intersection segment.
+   * It does <b>not</b> return true if
+   * the input point is internal to the intersection segment.
+   *
+   * @return true if the input point is one of the intersection points.
+   */
+  public boolean isIntersection(Coordinate pt) {
+    for (int i = 0; i < result; i++) {
+      if (intPt[i].equals2D(pt)) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  /**
+   * Tests whether either intersection point is an interior point of one of the input segments.
+   *
+   * @return <code>true</code> if either intersection point is in the interior of one of the input segments
+   */
+  public boolean isInteriorIntersection()
+  {
+    if (isInteriorIntersection(0)) return true;
+    if (isInteriorIntersection(1)) return true;
+    return false;
+  }
+
+  /**
+   * Tests whether either intersection point is an interior point of the specified input segment.
+   *
+   * @return <code>true</code> if either intersection point is in the interior of the input segment
+   */
+  public boolean isInteriorIntersection(int inputLineIndex)
+  {
+    for (int i = 0; i < result; i++) {
+      if (! (   intPt[i].equals2D(inputLines[inputLineIndex][0])
+             || intPt[i].equals2D(inputLines[inputLineIndex][1]) )) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  /**
+   * Tests whether an intersection is proper.
+   * <br>
+   * The intersection between two line segments is considered proper if
+   * they intersect in a single point in the interior of both segments
+   * (e.g. the intersection is a single point and is not equal to any of the
+   * endpoints).
+   * <p>
+   * The intersection between a point and a line segment is considered proper
+   * if the point lies in the interior of the segment (e.g. is not equal to
+   * either of the endpoints).
+   *
+   * @return true if the intersection is proper
+   */
+  public boolean isProper() {
+    return hasIntersection() && isProper;
+  }
+
+  /**
+   * Computes the intIndex'th intersection point in the direction of
+   * a specified input line segment
+   *
+   * @param segmentIndex is 0 or 1
+   * @param intIndex is 0 or 1
+   *
+   * @return the intIndex'th intersection point in the direction of the specified input line segment
+   */
+  public Coordinate getIntersectionAlongSegment(int segmentIndex, int intIndex) {
+    // lazily compute int line array
+    computeIntLineIndex();
+    return intPt[intLineIndex[segmentIndex][intIndex]];
+  }
+
+  /**
+   * Computes the index of the intIndex'th intersection point in the direction of
+   * a specified input line segment
+   *
+   * @param segmentIndex is 0 or 1
+   * @param intIndex is 0 or 1
+   *
+   * @return the index of the intersection point along the segment (0 or 1)
+   */
+  public int getIndexAlongSegment(int segmentIndex, int intIndex) {
+    computeIntLineIndex();
+    return intLineIndex[segmentIndex][intIndex];
+  }
+
+  protected void computeIntLineIndex(int segmentIndex) {
+    double dist0 = getEdgeDistance(segmentIndex, 0);
+    double dist1 = getEdgeDistance(segmentIndex, 1);
+    if (dist0 > dist1) {
+      intLineIndex[segmentIndex][0] = 0;
+      intLineIndex[segmentIndex][1] = 1;
+    }
+    else {
+      intLineIndex[segmentIndex][0] = 1;
+      intLineIndex[segmentIndex][1] = 0;
+    }
+  }
+
+  /**
+   * Computes the "edge distance" of an intersection point along the specified input line segment.
+   *
+   * @param segmentIndex is 0 or 1
+   * @param intIndex is 0 or 1
+   *
+   * @return the edge distance of the intersection point
+   */
+  public double getEdgeDistance(int segmentIndex, int intIndex) {
+    double dist = computeEdgeDistance(intPt[intIndex], inputLines[segmentIndex][0],
+        inputLines[segmentIndex][1]);
+    return dist;
+  }
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/MCPointInRing.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/MCPointInRing.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/MCPointInRing.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,162 @@
+
+/*
+ * 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 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;
+
+/**
+ * Implements {@link PointInRing}
+ * using {@link MonotoneChain}s and a {@link BinTree} index to
+ * increase performance.
+ *
+ * @version 1.6
+ */
+public class MCPointInRing   implements PointInRing {
+
+  class MCSelecter extends MonotoneChainSelectAction
+  {
+    Coordinate p;
+
+    public MCSelecter(Coordinate p)
+    {
+      this.p = p;
+    }
+
+    public void select(LineSegment ls)
+    {
+      testLineSegment(p, ls);
+    }
+  }
+
+  private LinearRing ring;
+  private Bintree tree;
+  private int crossings = 0;  // number of segment/ray crossings
+
+  public MCPointInRing(LinearRing ring)
+  {
+    this.ring = ring;
+    buildIndex();
+  }
+
+  private void buildIndex()
+  {
+    Envelope env = ring.getEnvelopeInternal();
+    tree = new Bintree();
+
+    Coordinate[] pts = CoordinateArrays.removeRepeatedPoints(ring.getCoordinates());
+    List mcList = MonotoneChainBuilder.getChains(pts);
+
+    for (int i = 0; i < mcList.size(); i++) {
+      MonotoneChain mc = (MonotoneChain) mcList.get(i);
+      Envelope mcEnv = mc.getEnvelope();
+      interval.min = mcEnv.getMinY();
+      interval.max = mcEnv.getMaxY();
+      tree.insert(interval, mc);
+    }
+  }
+
+  private Interval interval = new Interval();
+
+  public boolean isInside(Coordinate pt)
+  {
+    crossings = 0;
+
+    // test all segments intersected by ray from pt in positive x direction
+    Envelope rayEnv = new Envelope(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, pt.y, pt.y);
+
+    interval.min = pt.y;
+    interval.max = pt.y;
+    List segs = tree.query(interval);
+//System.out.println("query size = " + segs.size());
+
+    MCSelecter mcSelecter = new MCSelecter(pt);
+    for (Iterator i = segs.iterator(); i.hasNext(); ) {
+      MonotoneChain mc = (MonotoneChain) i.next();
+      testMonotoneChain(rayEnv, mcSelecter, mc);
+    }
+
+    /*
+     *  p is inside if number of crossings is odd.
+     */
+    if ((crossings % 2) == 1) {
+      return true;
+    }
+    return false;
+  }
+
+
+  private void testMonotoneChain(Envelope rayEnv, MCSelecter mcSelecter, MonotoneChain mc)
+  {
+    mc.select(rayEnv, mcSelecter);
+  }
+
+  private void testLineSegment(Coordinate p, LineSegment seg) {
+    double xInt;  // x intersection of segment with ray
+    double x1;    // translated coordinates
+    double y1;
+    double x2;
+    double y2;
+
+    /*
+     *  Test if segment crosses ray from test point in positive x direction.
+     */
+    Coordinate p1 = seg.p0;
+    Coordinate p2 = seg.p1;
+    x1 = p1.x - p.x;
+    y1 = p1.y - p.y;
+    x2 = p2.x - p.x;
+    y2 = p2.y - p.y;
+
+    if (((y1 > 0) && (y2 <= 0)) ||
+        ((y2 > 0) && (y1 <= 0))) {
+        /*
+         *  segment straddles x axis, so compute intersection.
+         */
+      xInt = RobustDeterminant.signOfDet2x2(x1, y1, x2, y2) / (y2 - y1);
+        //xsave = xInt;
+        /*
+         *  crosses ray if strictly positive intersection.
+         */
+      if (0.0 < xInt) {
+        crossings++;
+      }
+    }
+  }
+
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/MinimumDiameter.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/MinimumDiameter.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/MinimumDiameter.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,241 @@
+
+/*
+ * 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.*;
+
+/**
+ * Computes the minimum diameter of a {@link Geometry}.
+ * The minimum diameter is defined to be the
+ * width of the smallest band that
+ * contains the geometry,
+ * where a band is a strip of the plane defined
+ * by two parallel lines.
+ * This can be thought of as the smallest hole that the geometry can be
+ * moved through, with a single rotation.
+ * <p>
+ * The first step in the algorithm is computing the convex hull of the Geometry.
+ * If the input Geometry is known to be convex, a hint can be supplied to
+ * avoid this computation.
+ *
+ * @see ConvexHull
+ *
+ * @version 1.6
+ */
+public class MinimumDiameter
+{
+  private final Geometry inputGeom;
+  private final boolean isConvex;
+
+  private LineSegment minBaseSeg = new LineSegment();
+  private Coordinate minWidthPt = null;
+  private int minPtIndex;
+  private double minWidth = 0.0;
+
+  /**
+   * Compute a minimum diameter for a giver {@link Geometry}.
+   *
+   * @param geom a Geometry
+   */
+  public MinimumDiameter(Geometry inputGeom)
+  {
+    this(inputGeom, false);
+  }
+
+  /**
+   * Compute a minimum diameter for a giver {@link Geometry},
+   * with a hint if
+   * the Geometry is convex
+   * (e.g. a convex Polygon or LinearRing,
+   * or a two-point LineString, or a Point).
+   *
+   * @param geom a Geometry which is convex
+   * @param isConvex <code>true</code> if the input geometry is convex
+   */
+  public MinimumDiameter(Geometry inputGeom, boolean isConvex)
+  {
+    this.inputGeom = inputGeom;
+    this.isConvex = isConvex;
+  }
+
+  /**
+   * Gets the length of the minimum diameter of the input Geometry
+   *
+   * @return the length of the minimum diameter
+   */
+  public double getLength()
+  {
+    computeMinimumDiameter();
+    return minWidth;
+  }
+
+  /**
+   * Gets the {@link Coordinate} forming one end of the minimum diameter
+   *
+   * @return a coordinate forming one end of the minimum diameter
+   */
+  public Coordinate getWidthCoordinate()
+  {
+    computeMinimumDiameter();
+    return minWidthPt;
+  }
+
+  /**
+   * Gets the segment forming the base of the minimum diameter
+   *
+   * @return the segment forming the base of the minimum diameter
+   */
+  public LineString getSupportingSegment()
+  {
+    computeMinimumDiameter();
+    return inputGeom.getFactory().createLineString(new Coordinate[] { minBaseSeg.p0, minBaseSeg.p1 } );
+  }
+
+  /**
+   * Gets a {@link LineString} which is a minimum diameter
+   *
+   * @return a {@link LineString} which is a minimum diameter
+   */
+  public LineString getDiameter()
+  {
+    computeMinimumDiameter();
+
+    // return empty linestring if no minimum width calculated
+    if (minWidthPt == null)
+      return inputGeom.getFactory().createLineString((Coordinate[])null);
+
+    Coordinate basePt = minBaseSeg.project(minWidthPt);
+    return inputGeom.getFactory().createLineString(new Coordinate[] { basePt, minWidthPt } );
+  }
+
+  private void computeMinimumDiameter()
+  {
+    // check if computation is cached
+    if (minWidthPt != null)
+      return;
+
+    if (isConvex)
+      computeWidthConvex(inputGeom);
+    else {
+      Geometry convexGeom = (new ConvexHull(inputGeom)).getConvexHull();
+      computeWidthConvex(convexGeom);
+    }
+  }
+
+  private void computeWidthConvex(Geometry geom)
+  {
+//System.out.println("Input = " + geom);
+    Coordinate[] pts = null;
+    if (geom instanceof Polygon)
+      pts = ((Polygon) geom).getExteriorRing().getCoordinates();
+    else
+      pts = geom.getCoordinates();
+
+    // special cases for lines or points or degenerate rings
+    if (pts.length == 0) {
+      minWidth = 0.0;
+      minWidthPt = null;
+      minBaseSeg = null;
+    }
+    else if (pts.length == 1) {
+      minWidth = 0.0;
+      minWidthPt = pts[0];
+      minBaseSeg.p0 = pts[0];
+      minBaseSeg.p1 = pts[0];
+    }
+    else if (pts.length == 2 || pts.length == 3) {
+      minWidth = 0.0;
+      minWidthPt = pts[0];
+      minBaseSeg.p0 = pts[0];
+      minBaseSeg.p1 = pts[1];
+    }
+    else
+      computeConvexRingMinDiameter(pts);
+  }
+
+  /**
+   * Compute the width information for a ring of {@link Coordinate}s.
+   * Leaves the width information in the instance variables.
+   *
+   * @param pts
+   * @return
+   */
+  private void computeConvexRingMinDiameter(Coordinate[] pts)
+  {
+    //if (
+    // for each segment in the ring
+    minWidth = Double.MAX_VALUE;
+    int currMaxIndex = 1;
+
+    LineSegment seg = new LineSegment();
+    // compute the max distance for all segments in the ring, and pick the minimum
+    for (int i = 0; i < pts.length - 1; i++) {
+      seg.p0 = pts[i];
+      seg.p1 = pts[i + 1];
+      currMaxIndex = findMaxPerpDistance(pts, seg, currMaxIndex);
+    }
+  }
+
+  private int findMaxPerpDistance(Coordinate[] pts, LineSegment seg, int startIndex)
+  {
+    double maxPerpDistance = seg.distancePerpendicular(pts[startIndex]);
+    double nextPerpDistance = maxPerpDistance;
+    int maxIndex = startIndex;
+    int nextIndex = maxIndex;
+    while (nextPerpDistance >= maxPerpDistance) {
+      maxPerpDistance = nextPerpDistance;
+      maxIndex = nextIndex;
+
+      nextIndex = nextIndex(pts, maxIndex);
+      nextPerpDistance = seg.distancePerpendicular(pts[nextIndex]);
+    }
+    // found maximum width for this segment - update global min dist if appropriate
+    if (maxPerpDistance < minWidth) {
+      minPtIndex = maxIndex;
+      minWidth = maxPerpDistance;
+      minWidthPt = pts[minPtIndex];
+      minBaseSeg = new LineSegment(seg);
+//      System.out.println(minBaseSeg);
+//      System.out.println(minWidth);
+    }
+    return maxIndex;
+  }
+
+  private static int nextIndex(Coordinate[] pts, int index)
+  {
+    index++;
+    if (index >= pts.length) index = 0;
+    return index;
+  }
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/NonRobustCGAlgorithms.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/NonRobustCGAlgorithms.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/NonRobustCGAlgorithms.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -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.algorithm;
+
+import com.vividsolutions.jts.geom.Coordinate;
+import com.vividsolutions.jts.geom.CoordinateSequence;
+
+/**
+ * Non-robust versions of various fundamental Computational Geometric algorithms,
+ * <b>FOR TESTING PURPOSES ONLY!</b>.
+ * The non-robustness is due to rounding error in floating point computation.
+ *
+ * @version 1.6
+ */
+public class NonRobustCGAlgorithms
+  extends CGAlgorithms
+{
+  public NonRobustCGAlgorithms() {
+  }
+
+  /**
+   * Computes whether a ring defined by an array of {@link Coordinate} is
+   * oriented counter-clockwise.
+   * <p>
+   * This will handle coordinate lists which contain repeated points.
+   *
+   * @param ring an array of coordinates forming a ring
+   * @return <code>true</code> if the ring is oriented counter-clockwise.
+   * @throws IllegalArgumentException if the ring is degenerate (does not contain 3 different points)
+   */
+  public static boolean isPointInRing(Coordinate p, Coordinate[] ring)
+  {
+    int		i, i1;		// point index; i1 = i-1 mod n
+    double	xInt;		// x intersection of e with ray
+    int		crossings = 0;	// number of edge/ray crossings
+    double	x1,y1,x2,y2;
+    int         nPts = ring.length;
+
+	/* For each line edge l = (i-1, i), see if it crosses ray from test point in positive x direction. */
+	for (i = 1; i < nPts; i++ ) {
+		i1 = i - 1;
+		Coordinate p1 = ring[i];
+		Coordinate p2 = ring[i1];
+		x1 = p1.x - p.x;
+		y1 = p1.y - p.y;
+		x2 = p2.x - p.x;
+		y2 = p2.y - p.y;
+
+		if( ( ( y1 > 0 ) && ( y2 <= 0 ) ) ||
+		    ( ( y2 > 0 ) && ( y1 <= 0 ) ) ) {
+			/* e straddles x axis, so compute intersection. */
+			xInt = (x1 * y2 - x2 * y1) / (y2 - y1);
+			//xsave = xInt;
+			/* crosses ray if strictly positive intersection. */
+			if (0.0 < xInt)
+				crossings++;
+		}
+	}
+	/* p is inside if an odd number of crossings. */
+	if( (crossings % 2) == 1 )
+		return	true;
+	else
+		return	false;
+  }
+
+  /**
+   * Computes whether a ring defined by an array of {@link Coordinate} is
+   * oriented counter-clockwise.
+   * <p>
+   * This will handle coordinate lists which contain repeated points.
+   *
+   * @param ring an array of coordinates forming a ring
+   * @return <code>true</code> if the ring is oriented counter-clockwise.
+   * @throws IllegalArgumentException if the ring is degenerate (does not contain 3 different points)
+   */
+  public static boolean isCCW(Coordinate[] ring)
+  {
+    // # of points without closing endpoint
+    int nPts = ring.length - 1;
+
+    // check that this is a valid ring - if not, simply return a dummy value
+    if (nPts < 4) return false;
+
+    // algorithm to check if a Ring is stored in CCW order
+    // find highest point
+    Coordinate hip = ring[0];
+    int hii = 0;
+    for (int i = 1; i <= nPts; i++) {
+      Coordinate p = ring[i];
+      if (p.y > hip.y) {
+        hip = p;
+        hii = i;
+      }
+    }
+
+    // find different point before highest point
+    int iPrev = hii;
+    do {
+      iPrev = (iPrev - 1) % nPts;
+    } while (ring[iPrev].equals(hip) && iPrev != hii);
+
+    // find different point after highest point
+    int iNext = hii;
+    do {
+      iNext = (iNext + 1) % nPts;
+    } while (ring[iNext].equals(hip) && iNext != hii);
+
+    Coordinate prev = ring[iPrev];
+    Coordinate next = ring[iNext];
+
+    if (prev.equals(hip) || next.equals(hip) || prev.equals(next))
+        throw new IllegalArgumentException("degenerate ring (does not contain 3 different points)");
+
+    // translate so that hip is at the origin.
+    // This will not affect the area calculation, and will avoid
+    // finite-accuracy errors (i.e very small vectors with very large coordinates)
+    // This also simplifies the discriminant calculation.
+    double prev2x = prev.x - hip.x;
+    double prev2y = prev.y - hip.y;
+    double next2x = next.x - hip.x;
+    double next2y = next.y - hip.y;
+    // compute cross-product of vectors hip->next and hip->prev
+    // (e.g. area of parallelogram they enclose)
+    double disc = next2x * prev2y - next2y * prev2x;
+    /* If disc is exactly 0, lines are collinear.  There are two possible cases:
+            (1) the lines lie along the x axis in opposite directions
+            (2) the line lie on top of one another
+      (2) should never happen, so we're going to ignore it!
+            (Might want to assert this)
+    (1) is handled by checking if next is left of prev ==> CCW
+    */
+    if (disc == 0.0) {
+            // poly is CCW if prev x is right of next x
+            return (prev.x > next.x);
+    }
+    else {
+            // if area is positive, points are ordered CCW
+            return (disc > 0.0);
+    }
+  }
+
+  public static int computeOrientation(Coordinate p1, Coordinate p2, Coordinate q) {
+        double dx1 = p2.x - p1.x;
+        double dy1 = p2.y - p1.y;
+        double dx2 = q.x - p2.x;
+        double dy2 = q.y - p2.y;
+        double det = dx1*dy2 - dx2*dy1;
+        if (det > 0.0) return 1;
+        if (det < 0.0) return -1;
+        return 0;
+  }
+
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/NonRobustLineIntersector.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/NonRobustLineIntersector.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/NonRobustLineIntersector.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,320 @@
+
+
+/*
+ * 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.algorithm.LineIntersector;
+
+/**
+ *@version 1.6
+ */
+
+import com.vividsolutions.jts.geom.*;
+import com.vividsolutions.jts.util.Debug;
+
+/**
+ * A non-robust version of {@LineIntersector}.
+ *
+ * @version 1.6
+ */
+public class NonRobustLineIntersector
+    extends LineIntersector
+{
+  /**
+   * @return true if both numbers are positive or if both numbers are negative.
+   * Returns false if both numbers are zero.
+   */
+  public static boolean isSameSignAndNonZero(double a, double b) {
+    if (a == 0 || b == 0) {
+      return false;
+    }
+    return (a < 0 && b < 0) || (a > 0 && b > 0);
+  }
+
+
+  public NonRobustLineIntersector() {
+  }
+
+  public void computeIntersection(
+      Coordinate p,
+      Coordinate p1,
+      Coordinate p2) {
+    double a1;
+    double b1;
+    double c1;
+    /*
+     *  Coefficients of line eqns.
+     */
+    double r;
+    /*
+     *  'Sign' values
+     */
+    isProper = false;
+
+    /*
+     *  Compute a1, b1, c1, where line joining points 1 and 2
+     *  is "a1 x  +  b1 y  +  c1  =  0".
+     */
+    a1 = p2.y - p1.y;
+    b1 = p1.x - p2.x;
+    c1 = p2.x * p1.y - p1.x * p2.y;
+
+    /*
+     *  Compute r3 and r4.
+     */
+    r = a1 * p.x + b1 * p.y + c1;
+
+    // if r != 0 the point does not lie on the line
+    if (r != 0) {
+      result = DONT_INTERSECT;
+      return;
+    }
+
+    // Point lies on line - check to see whether it lies in line segment.
+
+    double dist = rParameter(p1, p2, p);
+    if (dist < 0.0 || dist > 1.0) {
+      result = DONT_INTERSECT;
+      return;
+    }
+
+    isProper = true;
+    if (p.equals(p1) || p.equals(p2)) {
+      isProper = false;
+    }
+    result = DO_INTERSECT;
+  }
+
+  protected int computeIntersect(
+      Coordinate p1,
+      Coordinate p2,
+      Coordinate p3,
+      Coordinate p4) {
+    double a1;
+    double b1;
+    double c1;
+    /*
+     *  Coefficients of line eqns.
+     */
+    double a2;
+    /*
+     *  Coefficients of line eqns.
+     */
+    double b2;
+    /*
+     *  Coefficients of line eqns.
+     */
+    double c2;
+    double r1;
+    double r2;
+    double r3;
+    double r4;
+    /*
+     *  'Sign' values
+     */
+    //double denom, offset, num;     /* Intermediate values */
+
+    isProper = false;
+
+    /*
+     *  Compute a1, b1, c1, where line joining points 1 and 2
+     *  is "a1 x  +  b1 y  +  c1  =  0".
+     */
+    a1 = p2.y - p1.y;
+    b1 = p1.x - p2.x;
+    c1 = p2.x * p1.y - p1.x * p2.y;
+
+    /*
+     *  Compute r3 and r4.
+     */
+    r3 = a1 * p3.x + b1 * p3.y + c1;
+    r4 = a1 * p4.x + b1 * p4.y + c1;
+
+    /*
+     *  Check signs of r3 and r4.  If both point 3 and point 4 lie on
+     *  same side of line 1, the line segments do not intersect.
+     */
+    if (r3 != 0 &&
+        r4 != 0 &&
+        isSameSignAndNonZero(r3, r4)) {
+      return DONT_INTERSECT;
+    }
+
+    /*
+     *  Compute a2, b2, c2
+     */
+    a2 = p4.y - p3.y;
+    b2 = p3.x - p4.x;
+    c2 = p4.x * p3.y - p3.x * p4.y;
+
+    /*
+     *  Compute r1 and r2
+     */
+    r1 = a2 * p1.x + b2 * p1.y + c2;
+    r2 = a2 * p2.x + b2 * p2.y + c2;
+
+    /*
+     *  Check signs of r1 and r2.  If both point 1 and point 2 lie
+     *  on same side of second line segment, the line segments do
+     *  not intersect.
+     */
+    if (r1 != 0 &&
+        r2 != 0 &&
+        isSameSignAndNonZero(r1, r2)) {
+      return DONT_INTERSECT;
+    }
+
+    /**
+     *  Line segments intersect: compute intersection point.
+     */
+    double denom = a1 * b2 - a2 * b1;
+    if (denom == 0) {
+      return computeCollinearIntersection(p1, p2, p3, p4);
+    }
+    double numX = b1 * c2 - b2 * c1;
+    pa.x = numX / denom;
+    /*
+     *  TESTING ONLY
+     *  double valX = (( num < 0 ? num - offset : num + offset ) / denom);
+     *  double valXInt = (int) (( num < 0 ? num - offset : num + offset ) / denom);
+     *  if (valXInt != pa.x)     // TESTING ONLY
+     *  System.out.println(val + " - int: " + valInt + ", floor: " + pa.x);
+     */
+    double numY = a2 * c1 - a1 * c2;
+    pa.y = numY / denom;
+
+    // check if this is a proper intersection BEFORE truncating values,
+    // to avoid spurious equality comparisons with endpoints
+    isProper = true;
+    if (pa.equals(p1) || pa.equals(p2) || pa.equals(p3) || pa.equals(p4)) {
+      isProper = false;
+    }
+
+    // truncate computed point to precision grid
+    // TESTING - don't force coord to be precise
+    if (precisionModel != null) {
+      precisionModel.makePrecise(pa);
+    }
+    return DO_INTERSECT;
+  }
+
+
+  /*
+   *  p1-p2  and p3-p4 are assumed to be collinear (although
+   *  not necessarily intersecting). Returns:
+   *  DONT_INTERSECT	: the two segments do not intersect
+   *  COLLINEAR		: the segments intersect, in the
+   *  line segment pa-pb.  pa-pb is in
+   *  the same direction as p1-p2
+   *  DO_INTERSECT		: the inputLines intersect in a single point
+   *  only, pa
+   */
+  private int computeCollinearIntersection(
+      Coordinate p1,
+      Coordinate p2,
+      Coordinate p3,
+      Coordinate p4) {
+    double r1;
+    double r2;
+    double r3;
+    double r4;
+    Coordinate q3;
+    Coordinate q4;
+    double t3;
+    double t4;
+    r1 = 0;
+    r2 = 1;
+    r3 = rParameter(p1, p2, p3);
+    r4 = rParameter(p1, p2, p4);
+    // make sure p3-p4 is in same direction as p1-p2
+    if (r3 < r4) {
+      q3 = p3;
+      t3 = r3;
+      q4 = p4;
+      t4 = r4;
+    }
+    else {
+      q3 = p4;
+      t3 = r4;
+      q4 = p3;
+      t4 = r3;
+    }
+    // check for no intersection
+    if (t3 > r2 || t4 < r1) {
+      return DONT_INTERSECT;
+    }
+
+    // check for single point intersection
+    if (q4 == p1) {
+      pa.setCoordinate(p1);
+      return DO_INTERSECT;
+    }
+    if (q3 == p2) {
+      pa.setCoordinate(p2);
+      return DO_INTERSECT;
+    }
+
+    // intersection MUST be a segment - compute endpoints
+    pa.setCoordinate(p1);
+    if (t3 > r1) {
+      pa.setCoordinate(q3);
+    }
+    pb.setCoordinate(p2);
+    if (t4 < r2) {
+      pb.setCoordinate(q4);
+    }
+    return COLLINEAR;
+  }
+
+  /**
+   *  RParameter computes the parameter for the point p
+   *  in the parameterized equation
+   *  of the line from p1 to p2.
+   *  This is equal to the 'distance' of p along p1-p2
+   */
+  private double rParameter(Coordinate p1, Coordinate p2, Coordinate p) {
+    double r;
+    // compute maximum delta, for numerical stability
+    // also handle case of p1-p2 being vertical or horizontal
+    double dx = Math.abs(p2.x - p1.x);
+    double dy = Math.abs(p2.y - p1.y);
+    if (dx > dy) {
+      r = (p.x - p1.x) / (p2.x - p1.x);
+    }
+    else {
+      r = (p.y - p1.y) / (p2.y - p1.y);
+    }
+    return r;
+  }
+
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/NotRepresentableException.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/NotRepresentableException.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/NotRepresentableException.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,51 @@
+
+
+
+/*
+ * 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;
+
+/**
+ * Indicates that a {@link HCoordinate} has been computed which is
+ * not representable on the Cartesian plane.
+ *
+ * @version 1.6
+ * @see HCoordinate
+ */
+public class NotRepresentableException extends Exception {
+
+  public NotRepresentableException() {
+    super("Projective point not representable on the Cartesian plane.");
+  }
+
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/PointInRing.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/PointInRing.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/PointInRing.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,47 @@
+
+/*
+ * 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.Coordinate;
+
+/**
+ * An interface for classes which test whether a {@link Coordinate} lies inside
+ * a ring.
+ *
+ * @version 1.6
+ */
+public interface PointInRing {
+
+  boolean isInside(Coordinate pt);
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/PointLocator.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/PointLocator.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/PointLocator.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,188 @@
+
+
+
+/*
+ * 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 java.util.Iterator;
+import com.vividsolutions.jts.geom.*;
+import com.vividsolutions.jts.geomgraph.GeometryGraph;
+
+/**
+ * Computes the topological relationship ({@link Location})
+ * of a single point to a Geometry.
+ * The algorithm obeys the SFS boundaryDetermination rule to correctly determine
+ * whether the point lies on the boundary or not.
+ * Note that instances of this class are not reentrant.
+ * @version 1.6
+ */
+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
+
+  public PointLocator() {
+  }
+
+  /**
+   * Convenience method to test a point for intersection with
+   * a Geometry
+   * @param p the coordinate to test
+   * @param geom the Geometry to test
+   * @return <code>true</code> if the point is in the interior or boundary of the Geometry
+   */
+  public boolean intersects(Coordinate p, Geometry geom)
+  {
+    return locate(p, geom) != Location.EXTERIOR;
+  }
+
+  /**
+   * Computes the topological relationship ({@link Location}) of a single point
+   * to a Geometry.
+   * It handles both single-element
+   * and multi-element Geometries.
+   * The algorithm for multi-part Geometries
+   * takes into account the boundaryDetermination rule.
+   *
+   * @return the {@link Location} of the point relative to the input Geometry
+   */
+  public int locate(Coordinate p, Geometry geom)
+  {
+    if (geom.isEmpty()) return Location.EXTERIOR;
+
+    if (geom instanceof LineString) {
+      return locate(p, (LineString) geom);
+    }
+    if (geom instanceof LinearRing) {
+      return locate(p, (LinearRing) geom);
+    }
+    else if (geom instanceof Polygon) {
+      return locate(p, (Polygon) geom);
+    }
+
+    isIn = false;
+    numBoundaries = 0;
+    computeLocation(p, geom);
+    if (GeometryGraph.isInBoundary(numBoundaries)) return Location.BOUNDARY;
+    if (numBoundaries > 0 || isIn) return Location.INTERIOR;
+    return Location.EXTERIOR;
+  }
+
+  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));
+    }
+    else if (geom instanceof Polygon) {
+      updateLocationInfo(locate(p, (Polygon) geom));
+    }
+    else if (geom instanceof MultiLineString) {
+      MultiLineString ml = (MultiLineString) geom;
+      for (int i = 0; i < ml.getNumGeometries(); i++) {
+        LineString l = (LineString) ml.getGeometryN(i);
+        updateLocationInfo(locate(p, l));
+      }
+    }
+    else if (geom instanceof MultiPolygon) {
+      MultiPolygon mpoly = (MultiPolygon) geom;
+      for (int i = 0; i < mpoly.getNumGeometries(); i++) {
+        Polygon poly = (Polygon) mpoly.getGeometryN(i);
+        updateLocationInfo(locate(p, poly));
+      }
+    }
+    else if (geom instanceof GeometryCollection) {
+      Iterator geomi = new GeometryCollectionIterator((GeometryCollection) geom);
+      while (geomi.hasNext()) {
+        Geometry g2 = (Geometry) geomi.next();
+        if (g2 != geom)
+          computeLocation(p, g2);
+      }
+    }
+  }
+
+  private void updateLocationInfo(int loc)
+  {
+    if (loc == Location.INTERIOR) isIn = true;
+    if (loc == Location.BOUNDARY) numBoundaries++;
+  }
+
+  private int locate(Coordinate p, LineString l)
+  {
+    Coordinate[] pt = l.getCoordinates();
+    if (! l.isClosed()) {
+      if (p.equals(pt[0])
+          || p.equals(pt[pt.length - 1]) ) {
+        return Location.BOUNDARY;
+      }
+    }
+    if (CGAlgorithms.isOnLine(p, pt))
+      return Location.INTERIOR;
+    return Location.EXTERIOR;
+  }
+
+  private int locate(Coordinate p, LinearRing ring)
+  {
+    // can this test be folded into isPointInRing ?
+    if (CGAlgorithms.isOnLine(p, ring.getCoordinates())) {
+      return Location.BOUNDARY;
+    }
+    if (CGAlgorithms.isPointInRing(p, ring.getCoordinates()))
+      return Location.INTERIOR;
+    return Location.EXTERIOR;
+  }
+
+  private int locate(Coordinate p, Polygon poly)
+  {
+    if (poly.isEmpty()) return Location.EXTERIOR;
+    LinearRing shell = (LinearRing) poly.getExteriorRing();
+
+    int shellLoc = locate(p, shell);
+    if (shellLoc == Location.EXTERIOR) return Location.EXTERIOR;
+    if (shellLoc == Location.BOUNDARY) return Location.BOUNDARY;
+    // now test if the point lies in or on the holes
+    for (int i = 0; i < poly.getNumInteriorRing(); i++) {
+      LinearRing hole = (LinearRing) poly.getInteriorRingN(i);
+      int holeLoc = locate(p, hole);
+      if (holeLoc == Location.INTERIOR) return Location.EXTERIOR;
+      if (holeLoc == Location.BOUNDARY) return Location.BOUNDARY;
+    }
+    return Location.INTERIOR;
+  }
+
+
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/RobustCGAlgorithms.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/RobustCGAlgorithms.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/RobustCGAlgorithms.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,48 @@
+
+
+/*
+ * 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.*;
+
+/**
+ * Stub version of RobustCGAlgorithms for backwards compatibility.
+ * Will be deprecated in next release - use CGAlgorithms instead.
+ *
+ * @version 1.6
+ *
+ */
+public class RobustCGAlgorithms extends CGAlgorithms {
+
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/RobustDeterminant.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/RobustDeterminant.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/RobustDeterminant.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,332 @@
+
+
+/*
+ * 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
+ */
+
+/**
+ * Implements an algorithm to compute the
+ * sign of a 2x2 determinant for double precision values robustly.
+ * It is a direct translation of code developed by Olivier Devillers.
+ * <p>
+ * The original code carries the following copyright notice:
+ *
+ * <pre>
+ *************************************************************************
+ * Author : Olivier Devillers
+ * Olivier.Devillers at sophia.inria.fr
+ * http:/www.inria.fr:/prisme/personnel/devillers/anglais/determinant.html
+ **************************************************************************
+ *
+ **************************************************************************
+ *              Copyright (c) 1995  by  INRIA Prisme Project
+ *                  BP 93 06902 Sophia Antipolis Cedex, France.
+ *                           All rights reserved
+ **************************************************************************
+ * </pre>
+ *
+ * @version 1.6
+ */
+public class RobustDeterminant {
+
+  //public static int callCount = 0; // debugging only
+
+  public static int signOfDet2x2(double x1, double y1, double x2, double y2) {
+    // returns -1 if the determinant is negative,
+    // returns  1 if the determinant is positive,
+    // retunrs  0 if the determinant is null.
+    int sign;
+    double swap;
+    double k;
+    long count = 0;
+
+    //callCount++; // debugging only
+
+    sign = 1;
+
+    /*
+     *  testing null entries
+     */
+    if ((x1 == 0.0) || (y2 == 0.0)) {
+      if ((y1 == 0.0) || (x2 == 0.0)) {
+        return 0;
+      }
+      else if (y1 > 0) {
+        if (x2 > 0) {
+          return -sign;
+        }
+        else {
+          return sign;
+        }
+      }
+      else {
+        if (x2 > 0) {
+          return sign;
+        }
+        else {
+          return -sign;
+        }
+      }
+    }
+    if ((y1 == 0.0) || (x2 == 0.0)) {
+      if (y2 > 0) {
+        if (x1 > 0) {
+          return sign;
+        }
+        else {
+          return -sign;
+        }
+      }
+      else {
+        if (x1 > 0) {
+          return -sign;
+        }
+        else {
+          return sign;
+        }
+      }
+    }
+
+    /*
+     *  making y coordinates positive and permuting the entries
+     */
+    /*
+     *  so that y2 is the biggest one
+     */
+    if (0.0 < y1) {
+      if (0.0 < y2) {
+        if (y1 <= y2) {
+          ;
+        }
+        else {
+          sign = -sign;
+          swap = x1;
+          x1 = x2;
+          x2 = swap;
+          swap = y1;
+          y1 = y2;
+          y2 = swap;
+        }
+      }
+      else {
+        if (y1 <= -y2) {
+          sign = -sign;
+          x2 = -x2;
+          y2 = -y2;
+        }
+        else {
+          swap = x1;
+          x1 = -x2;
+          x2 = swap;
+          swap = y1;
+          y1 = -y2;
+          y2 = swap;
+        }
+      }
+    }
+    else {
+      if (0.0 < y2) {
+        if (-y1 <= y2) {
+          sign = -sign;
+          x1 = -x1;
+          y1 = -y1;
+        }
+        else {
+          swap = -x1;
+          x1 = x2;
+          x2 = swap;
+          swap = -y1;
+          y1 = y2;
+          y2 = swap;
+        }
+      }
+      else {
+        if (y1 >= y2) {
+          x1 = -x1;
+          y1 = -y1;
+          x2 = -x2;
+          y2 = -y2;
+          ;
+        }
+        else {
+          sign = -sign;
+          swap = -x1;
+          x1 = -x2;
+          x2 = swap;
+          swap = -y1;
+          y1 = -y2;
+          y2 = swap;
+        }
+      }
+    }
+
+    /*
+     *  making x coordinates positive
+     */
+    /*
+     *  if |x2| < |x1| one can conclude
+     */
+    if (0.0 < x1) {
+      if (0.0 < x2) {
+        if (x1 <= x2) {
+          ;
+        }
+        else {
+          return sign;
+        }
+      }
+      else {
+        return sign;
+      }
+    }
+    else {
+      if (0.0 < x2) {
+        return -sign;
+      }
+      else {
+        if (x1 >= x2) {
+          sign = -sign;
+          x1 = -x1;
+          x2 = -x2;
+          ;
+        }
+        else {
+          return -sign;
+        }
+      }
+    }
+
+    /*
+     *  all entries strictly positive   x1 <= x2 and y1 <= y2
+     */
+    while (true) {
+      count = count + 1;
+      k = Math.floor(x2 / x1);
+      x2 = x2 - k * x1;
+      y2 = y2 - k * y1;
+
+      /*
+       *  testing if R (new U2) is in U1 rectangle
+       */
+      if (y2 < 0.0) {
+        return -sign;
+      }
+      if (y2 > y1) {
+        return sign;
+      }
+
+      /*
+       *  finding R'
+       */
+      if (x1 > x2 + x2) {
+        if (y1 < y2 + y2) {
+          return sign;
+        }
+      }
+      else {
+        if (y1 > y2 + y2) {
+          return -sign;
+        }
+        else {
+          x2 = x1 - x2;
+          y2 = y1 - y2;
+          sign = -sign;
+        }
+      }
+      if (y2 == 0.0) {
+        if (x2 == 0.0) {
+          return 0;
+        }
+        else {
+          return -sign;
+        }
+      }
+      if (x2 == 0.0) {
+        return sign;
+      }
+
+      /*
+       *  exchange 1 and 2 role.
+       */
+      k = Math.floor(x1 / x2);
+      x1 = x1 - k * x2;
+      y1 = y1 - k * y2;
+
+      /*
+       *  testing if R (new U1) is in U2 rectangle
+       */
+      if (y1 < 0.0) {
+        return sign;
+      }
+      if (y1 > y2) {
+        return -sign;
+      }
+
+      /*
+       *  finding R'
+       */
+      if (x2 > x1 + x1) {
+        if (y2 < y1 + y1) {
+          return -sign;
+        }
+      }
+      else {
+        if (y2 > y1 + y1) {
+          return sign;
+        }
+        else {
+          x1 = x2 - x1;
+          y1 = y2 - y1;
+          sign = -sign;
+        }
+      }
+      if (y1 == 0.0) {
+        if (x1 == 0.0) {
+          return 0;
+        }
+        else {
+          return sign;
+        }
+      }
+      if (x1 == 0.0) {
+        return -sign;
+      }
+    }
+
+  }
+
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/RobustLineIntersector.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/RobustLineIntersector.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/RobustLineIntersector.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,279 @@
+
+
+/*
+ * 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.*;
+import com.vividsolutions.jts.util.Assert;
+
+/**
+ * A robust version of {@LineIntersector}.
+ *
+ * @version 1.6
+ * @see RobustDeterminant
+ */
+public class RobustLineIntersector
+    extends LineIntersector
+{
+
+  public RobustLineIntersector() {
+  }
+
+  public void computeIntersection(Coordinate p, Coordinate p1, Coordinate p2) {
+    isProper = false;
+    // do between check first, since it is faster than the orientation test
+    if (Envelope.intersects(p1, p2, p)) {
+      if ((CGAlgorithms.orientationIndex(p1, p2, p) == 0)
+          && (CGAlgorithms.orientationIndex(p2, p1, p) == 0)) {
+        isProper = true;
+        if (p.equals(p1) || p.equals(p2)) {
+          isProper = false;
+        }
+        result = DO_INTERSECT;
+        return;
+      }
+    }
+    result = DONT_INTERSECT;
+  }
+
+  public int computeIntersect(
+                Coordinate p1, Coordinate p2,
+                Coordinate q1, Coordinate q2  ) {
+    isProper = false;
+
+    // first try a fast test to see if the envelopes of the lines intersect
+    if (! Envelope.intersects(p1, p2, q1, q2))
+      return DONT_INTERSECT;
+
+    // for each endpoint, compute which side of the other segment it lies
+    // if both endpoints lie on the same side of the other segment,
+    // the segments do not intersect
+    int Pq1 = CGAlgorithms.orientationIndex(p1, p2, q1);
+    int Pq2 = CGAlgorithms.orientationIndex(p1, p2, q2);
+
+    if ((Pq1>0 && Pq2>0) || (Pq1<0 && Pq2<0)) {
+      return DONT_INTERSECT;
+    }
+
+    int Qp1 = CGAlgorithms.orientationIndex(q1, q2, p1);
+    int Qp2 = CGAlgorithms.orientationIndex(q1, q2, p2);
+
+    if ((Qp1>0 && Qp2>0) || (Qp1<0 && Qp2<0)) {
+        return DONT_INTERSECT;
+    }
+
+    boolean collinear = Pq1 == 0
+         && Pq2 == 0
+         && Qp1 == 0
+         && Qp2 == 0;
+    if (collinear) {
+      return computeCollinearIntersection(p1, p2, q1, q2);
+    }
+    /**
+     *  Check if the intersection is an endpoint. If it is, copy the endpoint as
+     *  the intersection point. Copying the point rather than computing it
+     *  ensures the point has the exact value, which is important for
+     *  robustness. It is sufficient to simply check for an endpoint which is on
+     *  the other line, since at this point we know that the inputLines must
+     *  intersect.
+     */
+    if (Pq1 == 0 || Pq2 == 0 || Qp1 == 0 || Qp2 == 0) {
+      isProper = false;
+      if (Pq1 == 0) {
+        intPt[0] = new Coordinate(q1);
+      }
+      if (Pq2 == 0) {
+        intPt[0] = new Coordinate(q2);
+      }
+      if (Qp1 == 0) {
+        intPt[0] = new Coordinate(p1);
+      }
+      if (Qp2 == 0) {
+        intPt[0] = new Coordinate(p2);
+      }
+    }
+    else {
+      isProper = true;
+      intPt[0] = intersection(p1, p2, q1, q2);
+    }
+    return DO_INTERSECT;
+  }
+
+/*
+  private boolean intersectsEnvelope(Coordinate p1, Coordinate p2, Coordinate q) {
+    if (((q.x >= Math.min(p1.x, p2.x)) && (q.x <= Math.max(p1.x, p2.x))) &&
+        ((q.y >= Math.min(p1.y, p2.y)) && (q.y <= Math.max(p1.y, p2.y)))) {
+      return true;
+    }
+    return false;
+  }
+*/
+
+  private int computeCollinearIntersection(Coordinate p1, Coordinate p2,
+      Coordinate q1, Coordinate q2) {
+    boolean p1q1p2 = Envelope.intersects(p1, p2, q1);
+    boolean p1q2p2 = Envelope.intersects(p1, p2, q2);
+    boolean q1p1q2 = Envelope.intersects(q1, q2, p1);
+    boolean q1p2q2 = Envelope.intersects(q1, q2, p2);
+
+    if (p1q1p2 && p1q2p2) {
+      intPt[0] = q1;
+      intPt[1] = q2;
+      return COLLINEAR;
+    }
+    if (q1p1q2 && q1p2q2) {
+      intPt[0] = p1;
+      intPt[1] = p2;
+      return COLLINEAR;
+    }
+    if (p1q1p2 && q1p1q2) {
+      intPt[0] = q1;
+      intPt[1] = p1;
+      return q1.equals(p1) && !p1q2p2 && !q1p2q2 ? DO_INTERSECT : COLLINEAR;
+    }
+    if (p1q1p2 && q1p2q2) {
+      intPt[0] = q1;
+      intPt[1] = p2;
+      return q1.equals(p2) && !p1q2p2 && !q1p1q2 ? DO_INTERSECT : COLLINEAR;
+    }
+    if (p1q2p2 && q1p1q2) {
+      intPt[0] = q2;
+      intPt[1] = p1;
+      return q2.equals(p1) && !p1q1p2 && !q1p2q2 ? DO_INTERSECT : COLLINEAR;
+    }
+    if (p1q2p2 && q1p2q2) {
+      intPt[0] = q2;
+      intPt[1] = p2;
+      return q2.equals(p2) && !p1q1p2 && !q1p1q2 ? DO_INTERSECT : COLLINEAR;
+    }
+    return DONT_INTERSECT;
+  }
+
+  /**
+   * This method computes the actual value of the intersection point.
+   * To obtain the maximum precision from the intersection calculation,
+   * the coordinates are normalized by subtracting the minimum
+   * ordinate values (in absolute value).  This has the effect of
+   * removing common significant digits from the calculation to
+   * maintain more bits of precision.
+   */
+  private Coordinate intersection(
+    Coordinate p1, Coordinate p2, Coordinate q1, Coordinate q2)
+  {
+    Coordinate n1 = new Coordinate(p1);
+    Coordinate n2 = new Coordinate(p2);
+    Coordinate n3 = new Coordinate(q1);
+    Coordinate n4 = new Coordinate(q2);
+    Coordinate normPt = new Coordinate();
+    normalize(n1, n2, n3, n4, normPt);
+
+    Coordinate intPt = null;
+    try {
+      intPt = HCoordinate.intersection(n1, n2, n3, n4);
+    }
+    catch (NotRepresentableException e) {
+      Assert.shouldNeverReachHere("Coordinate for intersection is not calculable");
+    }
+
+    intPt.x += normPt.x;
+    intPt.y += normPt.y;
+
+    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(
+    Coordinate n1,
+    Coordinate n2,
+    Coordinate n3,
+    Coordinate n4,
+    Coordinate normPt)
+  {
+    normPt.x = smallestInAbsValue(n1.x, n2.x, n3.x, n4.x);
+    normPt.y = smallestInAbsValue(n1.y, n2.y, n3.y, n4.y);
+    n1.x -= normPt.x;    n1.y -= normPt.y;
+    n2.x -= normPt.x;    n2.y -= normPt.y;
+    n3.x -= normPt.x;    n3.y -= normPt.y;
+    n4.x -= normPt.x;    n4.y -= normPt.y;
+  }
+
+  private double smallestInAbsValue(double x1, double x2, double x3, double x4)
+  {
+    double x = x1;
+    double xabs = Math.abs(x);
+    if (Math.abs(x2) < xabs) {
+      x = x2;
+      xabs = Math.abs(x2);
+    }
+    if (Math.abs(x3) < xabs) {
+      x = x3;
+      xabs = Math.abs(x3);
+    }
+    if (Math.abs(x4) < xabs) {
+      x = x4;
+    }
+    return x;
+  }
+
+  /**
+   * Test whether a point lies in the envelopes of both input segments.
+   * A correctly computed intersection point should return <code>true</code>
+   * for this test.
+   * Since this test is for debugging purposes only, no attempt is
+   * made to optimize the envelope test.
+   *
+   * @return <code>true</code> if the input point lies within both input segment envelopes
+   */
+  private boolean isInSegmentEnvelopes(Coordinate intPt)
+  {
+    Envelope env0 = new Envelope(inputLines[0][0], inputLines[0][1]);
+    Envelope env1 = new Envelope(inputLines[1][0], inputLines[1][1]);
+    return env0.contains(intPt) && env1.contains(intPt);
+  }
+
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/SIRtreePointInRing.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/SIRtreePointInRing.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/SIRtreePointInRing.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,127 @@
+
+/*
+ * 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 java.util.*;
+import com.vividsolutions.jts.geom.*;
+import com.vividsolutions.jts.index.strtree.*;
+
+/**
+ * Implements {@link PointInRing}
+ * using a {@link SIRtree} index to
+ * increase performance.
+ *
+ * @version 1.6
+ */
+public class SIRtreePointInRing implements PointInRing {
+
+  private LinearRing ring;
+  private SIRtree sirTree;
+  private int crossings = 0;  // number of segment/ray crossings
+
+  public SIRtreePointInRing(LinearRing ring)
+  {
+    this.ring = ring;
+    buildIndex();
+  }
+
+  private void buildIndex()
+  {
+    Envelope env = ring.getEnvelopeInternal();
+    sirTree = new SIRtree();
+
+    Coordinate[] pts = ring.getCoordinates();
+    for (int i = 1; i < pts.length; i++) {
+      if (pts[i-1].equals(pts[i])) { continue; } //Optimization suggested by MD. [Jon Aquino]
+      LineSegment seg = new LineSegment(pts[i - 1], pts[i]);
+      sirTree.insert(seg.p0.y, seg.p1.y, seg);
+    }
+  }
+
+  public boolean isInside(Coordinate pt)
+  {
+    crossings = 0;
+
+    // test all segments intersected by vertical ray at pt
+
+    List segs = sirTree.query(pt.y);
+//System.out.println("query size = " + segs.size());
+
+    for (Iterator i = segs.iterator(); i.hasNext(); ) {
+      LineSegment seg = (LineSegment) i.next();
+      testLineSegment(pt, seg);
+    }
+
+    /*
+     *  p is inside if number of crossings is odd.
+     */
+    if ((crossings % 2) == 1) {
+      return true;
+    }
+    return false;
+  }
+
+  private void testLineSegment(Coordinate p, LineSegment seg) {
+    double xInt;  // x intersection of segment with ray
+    double x1;    // translated coordinates
+    double y1;
+    double x2;
+    double y2;
+
+    /*
+     *  Test if segment crosses ray from test point in positive x direction.
+     */
+    Coordinate p1 = seg.p0;
+    Coordinate p2 = seg.p1;
+    x1 = p1.x - p.x;
+    y1 = p1.y - p.y;
+    x2 = p2.x - p.x;
+    y2 = p2.y - p.y;
+
+    if (((y1 > 0) && (y2 <= 0)) ||
+        ((y2 > 0) && (y1 <= 0))) {
+        /*
+         *  segment straddles x axis, so compute intersection.
+         */
+      xInt = RobustDeterminant.signOfDet2x2(x1, y1, x2, y2) / (y2 - y1);
+        //xsave = xInt;
+        /*
+         *  crosses ray if strictly positive intersection.
+         */
+      if (0.0 < xInt) {
+        crossings++;
+      }
+    }
+  }
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/SimplePointInAreaLocator.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/SimplePointInAreaLocator.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/SimplePointInAreaLocator.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,97 @@
+
+
+
+/*
+ * 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 java.util.Iterator;
+import com.vividsolutions.jts.geom.*;
+
+/**
+ * Computes whether a point
+ * lies in the interior of an area {@link Geometry}.
+ * 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
+ */
+public class SimplePointInAreaLocator
+{
+
+  /**
+   * locate is the main location function.  It handles both single-element
+   * and multi-element Geometries.  The algorithm for multi-element Geometries
+   * is more complex, since it has to take into account the boundaryDetermination rule
+   */
+  public static int locate(Coordinate p, Geometry geom)
+  {
+    if (geom.isEmpty()) return Location.EXTERIOR;
+
+    if (containsPoint(p, geom))
+      return Location.INTERIOR;
+    return Location.EXTERIOR;
+  }
+
+  private static boolean containsPoint(Coordinate p, Geometry geom)
+  {
+    if (geom instanceof Polygon) {
+      return containsPointInPolygon(p, (Polygon) geom);
+    }
+    else if (geom instanceof GeometryCollection) {
+      Iterator geomi = new GeometryCollectionIterator((GeometryCollection) geom);
+      while (geomi.hasNext()) {
+        Geometry g2 = (Geometry) geomi.next();
+        if (g2 != geom)
+          if (containsPoint(p, g2))
+            return true;
+      }
+    }
+    return false;
+  }
+
+  public static boolean containsPointInPolygon(Coordinate p, Polygon poly)
+  {
+    if (poly.isEmpty()) return false;
+    LinearRing shell = (LinearRing) poly.getExteriorRing();
+    if (! CGAlgorithms.isPointInRing(p, shell.getCoordinates())) return false;
+    // now test if the point lies in or on the holes
+    for (int i = 0; i < poly.getNumInteriorRing(); i++) {
+      LinearRing hole = (LinearRing) poly.getInteriorRingN(i);
+      if (CGAlgorithms.isPointInRing(p, hole.getCoordinates())) return false;
+    }
+    return true;
+  }
+
+
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/SimplePointInRing.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/SimplePointInRing.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/SimplePointInRing.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,59 @@
+
+/*
+ * 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.*;
+
+/**
+ * Tests whether a {@link Coordinate} lies inside
+ * a ring, using a linear-time algorithm.
+ *
+ * @version 1.6
+ */
+public class SimplePointInRing
+  implements PointInRing
+{
+
+  private Coordinate[] pts;
+
+  public SimplePointInRing(LinearRing ring)
+  {
+    pts = ring.getCoordinates();
+  }
+
+  public boolean isInside(Coordinate pt)
+  {
+    return CGAlgorithms.isPointInRing(pt, pts);
+  }
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/package.html
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/package.html	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/algorithm/package.html	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,54 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+<!--
+
+-->
+</head>
+<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 
+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 
+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.  
+<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 
+gains for common types of input data.
+
+
+<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/geom/Coordinate.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/Coordinate.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/Coordinate.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,272 @@
+/*
+ * 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 com.vividsolutions.jts.util.Assert;
+
+
+/**
+ * A lightweight class used to store coordinates
+ * on the 2-dimensional Cartesian plane.
+ *  It is distinct from <code>Point</code>, which is a subclass of <code>Geometry</code>
+ *  . Unlike objects of type <code>Point</code> (which contain additional
+ *  information such as an envelope, a precision model, and spatial reference
+ *  system information), a <code>Coordinate</code> only contains ordinate values
+ *  and accessor methods. <P>
+ *
+ *  <code>Coordinate</code>s are two-dimensional points, with an additional
+ *  z-ordinate. JTS does not support any operations on the z-ordinate except
+ *  the basic accessor functions. Constructed coordinates will have a
+ *  z-ordinate of <code>NaN</code>.  The standard comparison functions will ignore
+ *  the z-ordinate.
+ *
+ *@version 1.6
+ */
+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;
+
+    /**
+     *  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> 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;
+        }
+
+        if (y != other.y) {
+            return false;
+        }
+
+        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 object with the specified object for order.
+     *  Since Coordinates are 2.5D, this routine ignores the z value when making the comparison.
+     *  Returns
+     *  <UL>
+     *    <LI> -1 : this.x < other.x || ((this.x == other.x) && (this.y <
+     *    other.y))
+     *    <LI> 0 : this.x == other.x && this.y = other.y
+     *    <LI> 1 : this.x > other.x || ((this.x == other.x) && (this.y > other.y))
+     *
+     *  </UL>
+     *
+     *
+     *@param  o  the <code>Coordinate</code> with which this <code>Coordinate</code>
+     *      is being compared
+     *@return    a negative integer, zero, or a positive integer as this <code>Coordinate</code>
+     *      is less than, equal to, or greater than the specified <code>Coordinate</code>
+     */
+    public int compareTo(Object o) {
+        Coordinate other = (Coordinate) o;
+
+        if (x < other.x) {
+            return -1;
+        }
+
+        if (x > other.x) {
+            return 1;
+        }
+
+        if (y < other.y) {
+            return -1;
+        }
+
+        if (y > other.y) {
+            return 1;
+        }
+
+        return 0;
+    }
+
+    /**
+     *  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 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 + ")";
+    }
+
+    public Object clone() {
+        try {
+            Coordinate coord = (Coordinate) super.clone();
+
+            return coord; // return the clone
+        } catch (CloneNotSupportedException e) {
+            Assert.shouldNeverReachHere(
+                "this shouldn't happen because this class is Cloneable");
+
+            return null;
+        }
+    }
+
+    /**
+     * "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);
+    }
+
+    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));
+    }
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/CoordinateArrays.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/CoordinateArrays.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/CoordinateArrays.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,188 @@
+
+
+/*
+ * 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.List;
+
+/**
+ * Useful utility functions for handling Coordinate arrays
+ *
+ * @version 1.6
+ */
+public class CoordinateArrays {
+
+  private final static Coordinate[] coordArrayType = new Coordinate[0];
+
+  /**
+   * Creates a deep copy of the argument {@link Coordinate) array.
+   *
+   * @param coordinates an array of Coordinates
+   * @return a deep copy of the input
+   */
+  public static Coordinate[] copyDeep(Coordinate[] coordinates) {
+    Coordinate[] copy = new Coordinate[coordinates.length];
+    for (int i = 0; i < coordinates.length; i++) {
+      copy[i] = new Coordinate(coordinates[i]);
+    }
+    return copy;
+  }
+
+  /**
+   * Converts the given List of Coordinates into a Coordinate array.
+   */
+  public static Coordinate[] toCoordinateArray(List coordList)
+  {
+    return (Coordinate[]) coordList.toArray(coordArrayType);
+  }
+
+  /**
+   * Returns whether #equals returns true for any two consecutive Coordinates
+   * in the given array.
+   */
+  public static boolean hasRepeatedPoints(Coordinate[] coord)
+  {
+    for (int i = 1; i < coord.length; i++) {
+      if (coord[i - 1].equals(coord[i]) ) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  /**
+   * Returns either the given coordinate array if its length is greater than the
+   * given amount, or an empty coordinate array.
+   */
+  public static Coordinate[] atLeastNCoordinatesOrNothing(int n, Coordinate[] c) {
+      return c.length >= n ? c : new Coordinate[] {  };
+  }
+
+  /**
+   * If the coordinate array argument has repeated points,
+   * constructs a new array containing no repeated points.
+   * Otherwise, returns the argument.
+   * @see #hasRepeatedPoints(Coordinate[])
+   */
+  public static Coordinate[] removeRepeatedPoints(Coordinate[] coord)
+  {
+    if (! hasRepeatedPoints(coord)) return coord;
+    CoordinateList coordList = new CoordinateList(coord, false);
+    return coordList.toCoordinateArray();
+  }
+
+  /**
+   * Reverses the coordinates in an array in-place.
+   */
+  public static void reverse(Coordinate[] coord)
+  {
+    int last = coord.length - 1;
+    int mid = last / 2;
+    for (int i = 0; i <= mid; i++) {
+      Coordinate tmp = coord[i];
+      coord[i] = coord[last - i];
+      coord[last - i] = tmp;
+    }
+  }
+
+  /**
+   * Returns true if the two arrays are identical, both null, or pointwise
+   * equal (as compared using Coordinate#equals)
+   * @see Coordinate#equals(Object)
+   */
+  public static boolean equals(
+    Coordinate[] coord1,
+    Coordinate[] coord2)
+  {
+    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 (! coord1[i].equals(coord2[i])) return false;
+    }
+    return true;
+  }
+
+  /**
+   *  Returns the minimum coordinate, using the usual lexicographic comparison.
+   *
+   *@param  coordinates  the array to search
+   *@return              the minimum coordinate in the array, found using <code>compareTo</code>
+   *@see Coordinate#compareTo(Object)
+   */
+  public static Coordinate minCoordinate(Coordinate[] coordinates)
+  {
+    Coordinate minCoord = null;
+    for (int i = 0; i < coordinates.length; i++) {
+      if (minCoord == null || minCoord.compareTo(coordinates[i]) > 0) {
+        minCoord = coordinates[i];
+      }
+    }
+    return minCoord;
+  }
+  /**
+   *  Shifts the positions of the coordinates until <code>firstCoordinate</code>
+   *  is first.
+   *
+   *@param  coordinates      the array to rearrange
+   *@param  firstCoordinate  the coordinate to make first
+   */
+  public static void scroll(Coordinate[] coordinates, Coordinate firstCoordinate) {
+    int i = indexOf(firstCoordinate, coordinates);
+    if (i < 0) return;
+    Coordinate[] newCoordinates = new Coordinate[coordinates.length];
+    System.arraycopy(coordinates, i, newCoordinates, 0, coordinates.length - i);
+    System.arraycopy(coordinates, 0, newCoordinates, coordinates.length - i, i);
+    System.arraycopy(newCoordinates, 0, coordinates, 0, coordinates.length);
+  }
+
+  /**
+   *  Returns the index of <code>coordinate</code> in <code>coordinates</code>.
+   *  The first position is 0; the second, 1; etc.
+   *
+   *@param  coordinate   the <code>Coordinate</code> to search for
+   *@param  coordinates  the array to search
+   *@return              the position of <code>coordinate</code>, or -1 if it is
+   *      not found
+   */
+  public static int indexOf(Coordinate coordinate, Coordinate[] coordinates) {
+    for (int i = 0; i < coordinates.length; i++) {
+      if (coordinate.equals(coordinates[i])) {
+        return i;
+      }
+    }
+    return -1;
+  }
+
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/CoordinateFilter.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/CoordinateFilter.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/CoordinateFilter.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,59 @@
+
+
+/*
+ * 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;
+
+
+/**
+ *  <code>Geometry</code> classes support the concept of applying a
+ *  coordinate filter to every coordinate in the <code>Geometry</code>. A
+ *  coordinate filter can either record information about each coordinate or
+ *  change the coordinate in some way. Coordinate filters implement the
+ *  interface <code>CoordinateFilter</code>. (<code>CoordinateFilter</code> is
+ *  an example of the Gang-of-Four Visitor pattern). Coordinate filters can be
+ *  used to implement such things as coordinate transformations, centroid and
+ *  envelope computation, and many other functions.
+ *
+ *@version 1.6
+ */
+public interface CoordinateFilter {
+
+  /**
+   *  Performs an operation with or on <code>coord</code>.
+   *
+   *@param  coord  a <code>Coordinate</code> to which the filter is applied.
+   */
+  public void filter(Coordinate coord);
+}
+

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/CoordinateList.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/CoordinateList.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/CoordinateList.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,191 @@
+
+
+/*
+ * 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.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+
+/**
+ * A list of {@link Coordinate}s, which may
+ * be set to prevent repeated coordinates from occuring in the list.
+ *
+ *
+ * @version 1.6
+ */
+public class CoordinateList
+  extends ArrayList
+{
+  //With contributions from Markus Schaber [schabios at logi-track.com]
+  //[Jon Aquino 2004-03-25]
+  private final static Coordinate[] coordArrayType = new Coordinate[0];
+
+  /**
+   * Constructs a new list without any coordinates
+   */
+  public CoordinateList()
+  { super();
+  }
+
+  /**
+   * The basic constructor for a CoordinateArray allows repeated points
+   * (i.e produces a CoordinateList with exactly the same set of points)
+   * @param coord the initial coordinates
+   */
+  public CoordinateList(Coordinate[] coord)
+  {
+    add(coord, true);
+  }
+
+  /**
+   * Constructs a new list from an array of Coordinates,
+   * allowing caller to specify if repeated points are to be removed.
+   *
+   * @param coord the array of coordinates to load into the list
+   * @param allowRepeated if <code>false</code>, repeated points are removed
+   */
+  public CoordinateList(Coordinate[] coord, boolean allowRepeated)
+  {
+    add(coord, allowRepeated);
+  }
+
+  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
+   * @param direction if false, the array is added in reverse order
+   * @return true (as by general collection contract)
+   */
+  public boolean add(Coordinate[] coord, boolean allowRepeated, boolean direction)
+  {
+    if (direction) {
+      for (int i = 0; i < coord.length; i++) {
+        add(coord[i], allowRepeated);
+      }
+    }
+    else {
+      for (int i = coord.length - 1; i >= 0; i--) {
+        add(coord[i], allowRepeated);
+      }
+    }
+    return true;
+  }
+
+
+  /** Add an array of coordinates
+   * @param coord The coordinates
+   * @param allowRepeated if set to false, repeated coordinates are collapsed
+   * @return true (as by general collection contract)
+   */
+  public boolean add(Coordinate[] coord, boolean allowRepeated)
+  {
+    add(coord, allowRepeated, true);
+    return true;
+  }
+
+  /** Add a coordinate
+   * @param obj The coordinate to add
+   * @param allowRepeated if set to false, repeated coordinates are collapsed
+   * @return true (as by general collection contract)
+   */
+  public boolean add(Object obj, boolean allowRepeated)
+  {
+    add((Coordinate) obj, allowRepeated);
+    return true;
+  }
+
+  /** Add a coordinate
+   * @param coord The coordinates
+   * @param allowRepeated if set to false, repeated coordinates are collapsed
+   */
+  public void add(Coordinate coord, boolean allowRepeated)
+  {
+    // don't add duplicate coordinates
+    if (! allowRepeated) {
+      if (size() >= 1) {
+        Coordinate last = (Coordinate) get(size() - 1);
+        if (last.equals2D(coord)) return;
+      }
+    }
+    super.add(coord);
+  }
+
+  /** Add an array of coordinates
+   * @param coll The coordinates
+   * @param allowRepeated if set to false, repeated coordinates are collapsed
+   * @return true (as by general collection contract)
+   */
+  public boolean addAll(Collection coll, boolean allowRepeated)
+  {
+    boolean isChanged = false;
+    for (Iterator i = coll.iterator(); i.hasNext(); ) {
+      add((Coordinate) i.next(), allowRepeated);
+      isChanged = true;
+    }
+    return isChanged;
+  }
+
+  /**
+   * Ensure this coordList is a ring, by adding the start point if necessary
+   */
+  public void closeRing()
+  {
+    if (size() > 0)
+      add(get(0), false);
+  }
+
+  /** 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
+   */
+  public Object clone() {
+      CoordinateList result = (CoordinateList) super.clone();
+      for (int i=0; i<result.size(); i++) {
+          this.add(i, ((Coordinate)this.get(i)).clone());
+      }
+      return result;
+  }
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/CoordinateSequence.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/CoordinateSequence.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/CoordinateSequence.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,174 @@
+/*
+* 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;
+
+/**
+ * The internal representation of a list of coordinates inside a Geometry.
+ * <p>
+ * There are some cases in which you might want Geometries to store their
+ * points using something other than the JTS Coordinate class. For example, you
+ * may want to experiment with another implementation, such as an array of x's
+ * and an array of y's. Or you might want to use your own coordinate class, one
+ * that supports extra attributes like M-values.
+ * <p>
+ * You can do this by implementing the CoordinateSequence and
+ * CoordinateSequenceFactory interfaces. You would then create a
+ * GeometryFactory parameterized by your CoordinateSequenceFactory, and use
+ * this GeometryFactory to create new Geometries. All of these new Geometries
+ * will use your CoordinateSequence implementation.
+ * <p>
+ * For an example, see the code for
+ * {@link com.vividsolutions.jtsexample.geom.TwoArrayCoordinateSequenceExample}.
+ *
+ * @see DefaultCoordinateSequenceFactory
+ * @see TwoArrayCoordinateSequenceFactory
+ *
+ * @version 1.6
+ */
+public interface CoordinateSequence
+    extends Cloneable
+{
+  /**
+   * 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;
+
+  /**
+   * 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.
+   * <p>
+   * Note that in the future the semantics of this method may change
+   * to guarantee that the Coordinate returned is always a copy.
+   * Callers should not to assume that they can modify a CoordinateSequence by
+   * modifying the object returned by this method.
+   *
+   * @param i the index of the coordinate to retrieve
+   * @return the i'th coordinate in the sequence
+   */
+  public Coordinate getCoordinate(int i);
+
+  /**
+   * Returns a copy of the i'th coordinate in this sequence.
+   * This method optimizes the situation where the caller is
+   * going to make a copy anyway - if the implementation
+   * has already created a new Coordinate object, no further copy is needed.
+   *
+   * @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);
+
+  /**
+   * Copies the i'th coordinate in the sequence to the supplied
+   * {@link Coordinate}.  Only the first two dimensions are copied.
+   *
+   * @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);
+
+  /**
+   * Returns ordinate X (0) of the specified coordinate.
+   *
+   * @param index
+   * @return the value of the X ordinate in the index'th coordinate
+   */
+  public double getX(int index);
+
+  /**
+   * Returns ordinate Y (1) of the specified coordinate.
+   *
+   * @param index
+   * @return the value of the Y ordinate in the index'th coordinate
+   */
+  public double getY(int index);
+
+  /**
+   * Returns the ordinate of a coordinate in this sequence.
+   * Ordinate indices 0 and 1 are assumed to be X and Y.
+   * Ordinates indices greater than 1 have user-defined semantics
+   * (for instance, they may contain other dimensions or measure values).
+   *
+   * @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);
+
+  /**
+   * Returns the number of coordinates in this sequence.
+   * @return the size of the sequence
+   */
+  public int size();
+
+  /**
+   * Sets the value for a given ordinate of a coordinate in this sequence.
+   *
+   * @param index  the coordinate index in the sequence
+   * @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);
+
+  /**
+   * Returns (possibly copies of) the Coordinates in this collection.
+   * Whether or not the Coordinates returned are the actual underlying
+   * Coordinates or merely copies depends on the implementation. Note that
+   * if this implementation does not store its data as an array of Coordinates,
+   * this method will incur a performance penalty because the array needs to
+   * be built from scratch.
+   *
+   * @return a array of coordinates containing the point values in this sequence
+   */
+  public Coordinate[] toCoordinateArray();
+
+  /**
+   * Expands the given {@link Envelope} to include the coordinates in the sequence.
+   * Allows implementing classes to optimize access to coordinate values.
+   *
+   * @param env the envelope to expand
+   * @return a ref to the expanded envelope
+   */
+  public Envelope expandEnvelope(Envelope env);
+
+  /**
+   * Returns a deep copy of this collection.
+   * Called by Geometry#clone.
+   *
+   * @return a copy of the coordinate sequence containing copies of all points
+   */
+  public Object clone();
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/CoordinateSequenceFactory.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/CoordinateSequenceFactory.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/CoordinateSequenceFactory.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -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;
+
+/**
+ * A factory to create concrete instances of {@link CoordinateSequence}s.
+ *
+ * @version 1.6
+ */
+public interface CoordinateSequenceFactory
+{
+
+  /**
+   * Returns a {@link CoordinateSequence} based on the given array.
+   * Whether the array is copied or simply referenced
+   * is implementation-dependent.
+   * This method must handle null arguments by creating an empty sequence.
+   *
+   * @param coordinates the coordinates
+   */
+  public CoordinateSequence create(Coordinate[] coordinates);
+
+  /**
+   * Creates a {@link CoordinateSequence} which is a copy
+   * of the given {@link CoordinateSequence}.
+   * This method must handle null arguments by creating an empty sequence.
+   *
+   * @param coordSeq the coordinate sequence to copy
+   */
+  public CoordinateSequence create(CoordinateSequence coordSeq);
+
+  /**
+   * Creates a {@link CoordinateSequence} of the specified size and dimension.
+   * For this to be useful, the {@link CoordinateSequence} implementation must
+   * be mutable.
+   *
+   * @param size the number of coordinates in the sequence
+   * @param dimension the dimension of the coordinates in the sequence (if user-specifiable,
+   * otherwise ignored)
+   */
+  public CoordinateSequence create(int size, int dimension);
+
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/DefaultCoordinateSequence.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/DefaultCoordinateSequence.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/DefaultCoordinateSequence.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,214 @@
+/*
+* 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;
+
+/**
+ * The CoordinateSequence implementation that Geometries use by default. In
+ * this implementation, Coordinates returned by #toArray and #get are live --
+ * parties that change them are actually changing the
+ * DefaultCoordinateSequence's underlying data.
+ *
+ * @version 1.6
+ *
+ * @deprecated no longer used
+ */
+class DefaultCoordinateSequence
+    implements CoordinateSequence, Serializable
+{
+  //With contributions from Markus Schaber [schabios at logi-track.com] 2004-03-26
+  private static final long serialVersionUID = -915438501601840650L;
+  private Coordinate[] coordinates;
+
+  /**
+   * Constructs a DefaultCoordinateSequence based on the given array (the
+   * array is not copied).
+   *
+   * @param coordinates the coordinate array that will be referenced.
+   */
+  public DefaultCoordinateSequence(Coordinate[] coordinates) {
+    if (Geometry.hasNullElements(coordinates)) {
+      throw new IllegalArgumentException("Null coordinate");
+    }
+    this.coordinates = coordinates;
+  }
+
+  /**
+   * Constructs a DefaultCoordinateSequence based on the given array (the
+   * array is not copied).
+   *
+   * @param coordinates the coordinate array that will be referenced.
+   */
+  public DefaultCoordinateSequence(CoordinateSequence coordSeq) {
+    coordinates = new Coordinate[coordSeq.size()];
+    for (int i = 0; i < coordinates.length; i++) {
+      coordinates[i] = coordSeq.getCoordinateCopy(i);
+    }
+  }
+
+  /**
+   * Constructs a sequence of a given size, populated
+   * with new {@link Coordinate}s.
+   *
+   * @param size the size of the sequence to create
+   */
+  public DefaultCoordinateSequence(int size) {
+    coordinates = new Coordinate[size];
+    for (int i = 0; i < size; i++) {
+      coordinates[i] = new Coordinate();
+    }
+  }
+
+  /**
+   * Get the Coordinate with index i.
+   *
+   * @param i
+   *                  the index of the coordinate
+   * @return the requested Coordinate instance
+   */
+  public Coordinate getCoordinate(int i) {
+    return coordinates[i];
+  }
+  /**
+   * Get a copy of the Coordinate with index i.
+   *
+   * @param i  the index of the coordinate
+   * @return a copy of the requested Coordinate
+   */
+  public Coordinate getCoordinateCopy(int i) {
+    return new Coordinate(coordinates[i]);
+  }
+
+  /**
+   * @see com.vividsolutions.jts.geom.CoordinateSequence#getX(int)
+   */
+  public void getCoordinate(int index, Coordinate coord) {
+    coord.x = coordinates[index].x;
+    coord.y = coordinates[index].y;
+  }
+
+  /**
+   * @see com.vividsolutions.jts.geom.CoordinateSequence#getX(int)
+   */
+  public double getX(int index) {
+    return coordinates[index].x;
+  }
+
+  /**
+   * @see com.vividsolutions.jts.geom.CoordinateSequence#getY(int)
+   */
+  public double getY(int index) {
+    return coordinates[index].y;
+  }
+
+  /**
+   * @see com.vividsolutions.jts.geom.CoordinateSequence#getOrdinate(int, int)
+   */
+  public double getOrdinate(int index, int ordinateIndex)
+  {
+    switch (ordinateIndex) {
+      case CoordinateSequence.X:  return coordinates[index].x;
+      case CoordinateSequence.Y:  return coordinates[index].y;
+      case CoordinateSequence.Z:  return coordinates[index].z;
+    }
+    return Double.NaN;
+  }
+  /**
+   * @see com.vividsolutions.jts.geom.CoordinateSequence#setOrdinate(int, int, double)
+   */
+  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;
+    }
+  }
+  /**
+   * Creates a deep copy of the Object
+   *
+   * @return The deep copy
+   */
+  public Object clone() {
+    Coordinate[] cloneCoordinates = new Coordinate[size()];
+    for (int i = 0; i < coordinates.length; i++) {
+      cloneCoordinates[i] = (Coordinate) coordinates[i].clone();
+    }
+    return new DefaultCoordinateSequence(cloneCoordinates);
+  }
+  /**
+   * Returns the size of the coordinate sequence
+   *
+   * @return the number of coordinates
+   */
+  public int size() {
+    return coordinates.length;
+  }
+  /**
+   * This method exposes the internal Array of Coordinate Objects
+   *
+   * @return the Coordinate[] array.
+   */
+  public Coordinate[] toCoordinateArray() {
+    return coordinates;
+  }
+
+  public Envelope expandEnvelope(Envelope env)
+  {
+    for (int i = 0; i < coordinates.length; i++ ) {
+      env.expandToInclude(coordinates[i]);
+    }
+    return env;
+  }
+
+  /**
+   * Returns the string Representation of the coordinate array
+   *
+   * @return The string
+   */
+  public String toString() {
+    if (coordinates.length > 0) {
+      StringBuffer strBuf = new StringBuffer(17 * coordinates.length);
+      strBuf.append('(');
+      strBuf.append(coordinates[0]);
+      for (int i = 1; i < coordinates.length; i++) {
+        strBuf.append(", ");
+        strBuf.append(coordinates[i]);
+      }
+      strBuf.append(')');
+      return strBuf.toString();
+    } else {
+      return "()";
+    }
+  }
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/DefaultCoordinateSequenceFactory.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/DefaultCoordinateSequenceFactory.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/DefaultCoordinateSequenceFactory.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,95 @@
+
+/*
+* 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;
+
+/**
+ * Creates CoordinateSequences represented as an array of {@link Coordinate}s.
+ *
+ * @version 1.6
+ *
+ * @deprecated no longer used
+ */
+public class DefaultCoordinateSequenceFactory
+    implements CoordinateSequenceFactory, Serializable
+{
+  private static final long serialVersionUID = -4099577099607551657L;
+  private static final DefaultCoordinateSequenceFactory instance = new DefaultCoordinateSequenceFactory();
+
+  public DefaultCoordinateSequenceFactory() {
+  }
+
+  /**
+   * @see http://www.javaworld.com/javaworld/javatips/jw-javatip122.html
+   */
+  private Object readResolve() {
+    return DefaultCoordinateSequenceFactory.instance();
+  }
+
+  /**
+   * Returns the singleton instance of DefaultCoordinateSequenceFactory
+   */
+  public static DefaultCoordinateSequenceFactory instance() {
+    return instance;
+  }
+
+
+
+  /**
+   * Returns a DefaultCoordinateSequence based on the given array (the array is
+   * not copied).
+   *
+   * @param coordinates
+   *            the coordinates, which may not be null nor contain null
+   *            elements
+   */
+  public CoordinateSequence create(Coordinate[] coordinates) {
+    return new DefaultCoordinateSequence(coordinates);
+  }
+
+  /**
+   * @see com.vividsolutions.jts.geom.CoordinateSequenceFactory#create(com.vividsolutions.jts.geom.CoordinateSequence)
+   */
+  public CoordinateSequence create(CoordinateSequence coordSeq) {
+    return new DefaultCoordinateSequence(coordSeq);
+  }
+
+  /**
+   * @see com.vividsolutions.jts.geom.CoordinateSequenceFactory#create(int, int)
+   */
+  public CoordinateSequence create(int size, int dimension) {
+    return new DefaultCoordinateSequence(size);
+  }
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/Dimension.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/Dimension.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/Dimension.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -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.geom;
+
+/**
+ * Constants representing the dimensions of a point, a curve and a surface.
+ * Also, constants representing the dimensions of the empty geometry and
+ * non-empty geometries, and a wildcard dimension meaning "any dimension".
+ * 
+ * @version 1.6
+ */
+public class Dimension {
+
+  /**
+   *  Dimension value of a point (0).
+   */
+  public final static int P = 0;
+
+  /**
+   *  Dimension value of a curve (1).
+   */
+  public final static int L = 1;
+
+  /**
+   *  Dimension value of a surface (2).
+   */
+  public final static int A = 2;
+
+  /**
+   *  Dimension value of the empty geometry (-1).
+   */
+  public final static int FALSE = -1;
+
+  /**
+   *  Dimension value of non-empty geometries (= {P, L, A}).
+   */
+  public final static int TRUE = -2;
+
+  /**
+   *  Dimension value for any dimension (= {FALSE, TRUE}).
+   */
+  public final static int DONTCARE = -3;
+
+  /**
+   *  Converts the dimension value to a dimension symbol, for example, <code>TRUE => 'T'</code>
+   *  .
+   *
+   *@param  dimensionValue  a number that can be stored in the <code>IntersectionMatrix</code>
+   *      . Possible values are <code>{TRUE, FALSE, DONTCARE, 0, 1, 2}</code>.
+   *@return                 a character for use in the string representation of
+   *      an <code>IntersectionMatrix</code>. Possible values are <code>{T, F, * , 0, 1, 2}</code>
+   *      .
+   */
+  public static char toDimensionSymbol(int dimensionValue) {
+    switch (dimensionValue) {
+      case FALSE:
+        return 'F';
+      case TRUE:
+        return 'T';
+      case DONTCARE:
+        return '*';
+      case P:
+        return '0';
+      case L:
+        return '1';
+      case A:
+        return '2';
+    }
+    throw new IllegalArgumentException("Unknown dimension value: " + dimensionValue);
+  }
+
+  /**
+   *  Converts the dimension symbol to a dimension value, for example, <code>'*' => DONTCARE</code>
+   *  .
+   *
+   *@param  dimensionSymbol  a character for use in the string representation of
+   *      an <code>IntersectionMatrix</code>. Possible values are <code>{T, F, * , 0, 1, 2}</code>
+   *      .
+   *@return                  a number that can be stored in the <code>IntersectionMatrix</code>
+   *      . Possible values are <code>{TRUE, FALSE, DONTCARE, 0, 1, 2}</code>.
+   */
+  public static int toDimensionValue(char dimensionSymbol) {
+    switch (Character.toUpperCase(dimensionSymbol)) {
+      case 'F':
+        return FALSE;
+      case 'T':
+        return TRUE;
+      case '*':
+        return DONTCARE;
+      case '0':
+        return P;
+      case '1':
+        return L;
+      case '2':
+        return A;
+    }
+    throw new IllegalArgumentException("Unknown dimension symbol: " + dimensionSymbol);
+  }
+}
+
+

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/Envelope.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/Envelope.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/Envelope.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,572 @@
+
+
+/*
+ * 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;
+
+/**
+ *  Defines a rectangular region of the 2D coordinate plane.
+ *  It is often used to represent the bounding box of a {@link Geometry},
+ *  e.g. the minimum and maximum x and y values of the {@link Coordinate}s.
+ *  <p>
+ *  Note that Envelopes support infinite or half-infinite regions, by using the values of
+ *  <code>Double.POSITIVE_INFINITY</code> and <code>Double.NEGATIVE_INFINITY</code>.
+ *  <p>
+ *  When Envelope objects are created or initialized,
+ *  the supplies extent values are automatically sorted into the correct order.
+ *
+ *@version 1.6
+ */
+public class Envelope
+    implements Serializable
+{
+    private static final long serialVersionUID = 5873921885273102420L;    
+
+    public int hashCode() {
+        //Algorithm from Effective Java by Joshua Bloch [Jon Aquino]
+        int result = 17;
+        result = 37 * result + Coordinate.hashCode(minx);
+        result = 37 * result + Coordinate.hashCode(maxx);
+        result = 37 * result + Coordinate.hashCode(miny);
+        result = 37 * result + Coordinate.hashCode(maxy);
+        return result;
+    }
+
+  /**
+   * Test the point q to see whether it intersects the Envelope defined by p1-p2
+   * @param p1 one extremal point of the envelope
+   * @param p2 another extremal point of the envelope
+   * @param q the point to test for intersection
+   * @return <code>true</code> if q intersects the envelope p1-p2
+   */
+  public static boolean intersects(Coordinate p1, Coordinate p2, Coordinate q)
+  {
+	//OptimizeIt shows that Math#min and Math#max here are a bottleneck.
+    //Replace with direct comparisons. [Jon Aquino]
+    if (((q.x >= (p1.x < p2.x ? p1.x : p2.x)) && (q.x <= (p1.x > p2.x ? p1.x : p2.x))) &&
+        ((q.y >= (p1.y < p2.y ? p1.y : p2.y)) && (q.y <= (p1.y > p2.y ? p1.y : p2.y)))) {
+      return true;
+    }
+    return false;
+  }
+
+  /**
+   * Test the envelope defined by p1-p2 for intersection
+   * with the envelope defined by q1-q2
+   * @param p1 one extremal point of the envelope P
+   * @param p2 another extremal point of the envelope P
+   * @param q1 one extremal point of the envelope Q
+   * @param q2 another extremal point of the envelope Q
+   * @return <code>true</code> if Q intersects P
+   */
+  public static boolean intersects(Coordinate p1, Coordinate p2, Coordinate q1, Coordinate q2)
+  {
+    double minq = Math.min(q1.x, q2.x);
+    double maxq = Math.max(q1.x, q2.x);
+    double minp = Math.min(p1.x, p2.x);
+    double maxp = Math.max(p1.x, p2.x);
+
+    if( minp > maxq )
+        return false;
+    if( maxp < minq )
+        return false;
+
+    minq = Math.min(q1.y, q2.y);
+    maxq = Math.max(q1.y, q2.y);
+    minp = Math.min(p1.y, p2.y);
+    maxp = Math.max(p1.y, p2.y);
+
+    if( minp > maxq )
+        return false;
+    if( maxp < minq )
+        return false;
+    return true;
+  }
+
+  /**
+   *  the minimum x-coordinate
+   */
+  private double minx;
+
+  /**
+   *  the maximum x-coordinate
+   */
+  private double maxx;
+
+  /**
+   *  the minimum y-coordinate
+   */
+  private double miny;
+
+  /**
+   *  the maximum y-coordinate
+   */
+  private double maxy;
+
+  /**
+   *  Creates a null <code>Envelope</code>.
+   */
+  public Envelope() {
+    init();
+  }
+
+  /**
+   *  Creates an <code>Envelope</code> for a region defined by maximum and minimum values.
+   *
+   *@param  x1  the first x-value
+   *@param  x2  the second x-value
+   *@param  y1  the first y-value
+   *@param  y2  the second y-value
+   */
+  public Envelope(double x1, double x2, double y1, double y2)
+  {
+    init(x1, x2, y1, y2);
+  }
+
+  /**
+   *  Creates an <code>Envelope</code> for a region defined by two Coordinates.
+   *
+   *@param  p1  the first Coordinate
+   *@param  p2  the second Coordinate
+   */
+  public Envelope(Coordinate p1, Coordinate p2)
+  {
+    init(p1, p2);
+  }
+
+  /**
+   *  Creates an <code>Envelope</code> for a region defined by a single Coordinate.
+   *
+   *@param  p1  the Coordinate
+   */
+  public Envelope(Coordinate p)
+  {
+    init(p);
+  }
+
+  /**
+   *  Create an <code>Envelope</code> from an existing Envelope.
+   *
+   *@param  env  the Envelope to initialize from
+   */
+  public Envelope(Envelope env)
+  {
+    init(env);
+  }
+
+  /**
+   *  Initialize to a null <code>Envelope</code>.
+   */
+  public void init()
+  {
+    setToNull();
+  }
+
+  /**
+   *  Initialize an <code>Envelope</code> for a region defined by maximum and minimum values.
+   *
+   *@param  x1  the first x-value
+   *@param  x2  the second x-value
+   *@param  y1  the first y-value
+   *@param  y2  the second y-value
+   */
+  public void init(double x1, double x2, double y1, double y2)
+  {
+    if (x1 < x2) {
+      minx = x1;
+      maxx = x2;
+    }
+    else {
+      minx = x2;
+      maxx = x1;
+    }
+    if (y1 < y2) {
+      miny = y1;
+      maxy = y2;
+    }
+    else {
+      miny = y2;
+      maxy = y1;
+    }
+  }
+
+  /**
+   *  Initialize an <code>Envelope</code> to a region defined by two Coordinates.
+   *
+   *@param  p1  the first Coordinate
+   *@param  p2  the second Coordinate
+   */
+  public void init(Coordinate p1, Coordinate p2)
+  {
+    init(p1.x, p2.x, p1.y, p2.y);
+  }
+
+  /**
+   *  Initialize an <code>Envelope</code> to a region defined by a single Coordinate.
+   *
+   *@param  p1  the first Coordinate
+   *@param  p2  the second Coordinate
+   */
+  public void init(Coordinate p)
+  {
+    init(p.x, p.x, p.y, p.y);
+  }
+
+  /**
+   *  Initialize an <code>Envelope</code> from an existing Envelope.
+   *
+   *@param  env  the Envelope to initialize from
+   */
+  public void init(Envelope env)
+  {
+    this.minx = env.minx;
+    this.maxx = env.maxx;
+    this.miny = env.miny;
+    this.maxy = env.maxy;
+  }
+
+
+  /**
+   *  Makes this <code>Envelope</code> a "null" envelope, that is, the envelope
+   *  of the empty geometry.
+   */
+  public void setToNull() {
+    minx = 0;
+    maxx = -1;
+    miny = 0;
+    maxy = -1;
+  }
+
+  /**
+   *  Returns <code>true</code> if this <code>Envelope</code> is a "null"
+   *  envelope.
+   *
+   *@return    <code>true</code> if this <code>Envelope</code> is uninitialized
+   *      or is the envelope of the empty geometry.
+   */
+  public boolean isNull() {
+    return maxx < minx;
+  }
+
+  /**
+   *  Returns the difference between the maximum and minimum x values.
+   *
+   *@return    max x - min x, or 0 if this is a null <code>Envelope</code>
+   */
+  public double getWidth() {
+    if (isNull()) {
+      return 0;
+    }
+    return maxx - minx;
+  }
+
+  /**
+   *  Returns the difference between the maximum and minimum y values.
+   *
+   *@return    max y - min y, or 0 if this is a null <code>Envelope</code>
+   */
+  public double getHeight() {
+    if (isNull()) {
+      return 0;
+    }
+    return maxy - miny;
+  }
+
+  /**
+   *  Returns the <code>Envelope</code>s minimum x-value. min x > max x
+   *  indicates that this is a null <code>Envelope</code>.
+   *
+   *@return    the minimum x-coordinate
+   */
+  public double getMinX() {
+    return minx;
+  }
+
+  /**
+   *  Returns the <code>Envelope</code>s maximum x-value. min x > max x
+   *  indicates that this is a null <code>Envelope</code>.
+   *
+   *@return    the maximum x-coordinate
+   */
+  public double getMaxX() {
+    return maxx;
+  }
+
+  /**
+   *  Returns the <code>Envelope</code>s minimum y-value. min y > max y
+   *  indicates that this is a null <code>Envelope</code>.
+   *
+   *@return    the minimum y-coordinate
+   */
+  public double getMinY() {
+    return miny;
+  }
+
+  /**
+   *  Returns the <code>Envelope</code>s maximum y-value. min y > max y
+   *  indicates that this is a null <code>Envelope</code>.
+   *
+   *@return    the maximum y-coordinate
+   */
+  public double getMaxY() {
+    return maxy;
+  }
+
+  /**
+   *  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.
+   *
+   *@param  x  the value to lower the minimum x to or to raise the maximum x to
+   *@param  y  the value to lower the minimum y to or to raise the maximum y to
+   */
+  public void expandToInclude(Coordinate p)
+  {
+    expandToInclude(p.x, p.y);
+  }
+
+  //<<TODO:FEATURE>> #expandBy(double distance) [Jon Aquino]
+
+  /**
+   *  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.
+   *
+   *@param  x  the value to lower the minimum x to or to raise the maximum x to
+   *@param  y  the value to lower the minimum y to or to raise the maximum y to
+   */
+  public void expandToInclude(double x, double y) {
+    if (isNull()) {
+      minx = x;
+      maxx = x;
+      miny = y;
+      maxy = y;
+    }
+    else {
+      if (x < minx) {
+        minx = x;
+      }
+      if (x > maxx) {
+        maxx = x;
+      }
+      if (y < miny) {
+        miny = y;
+      }
+      if (y > maxy) {
+        maxy = y;
+      }
+    }
+  }
+
+  /**
+   *  Enlarges the boundary of the <code>Envelope</code> so that it contains
+   *  <code>other</code>. Does nothing if <code>other</code> is wholly on or
+   *  within the boundaries.
+   *
+   *@param  other  the <code>Envelope</code> to merge with
+   */
+  public void expandToInclude(Envelope other) {
+    if (other.isNull()) {
+      return;
+    }
+    if (isNull()) {
+      minx = other.getMinX();
+      maxx = other.getMaxX();
+      miny = other.getMinY();
+      maxy = other.getMaxY();
+    }
+    else {
+      if (other.minx < minx) {
+        minx = other.minx;
+      }
+      if (other.maxx > maxx) {
+        maxx = other.maxx;
+      }
+      if (other.miny < miny) {
+        miny = other.miny;
+      }
+      if (other.maxy > maxy) {
+        maxy = other.maxy;
+      }
+    }
+  }
+
+  /**
+   *  Returns <code>true</code> if the given point lies in or on the envelope.
+   *
+   *@param  p  the point which this <code>Envelope</code> is
+   *      being checked for containing
+   *@return    <code>true</code> if the point lies in the interior or
+   *      on the boundary of this <code>Envelope</code>.
+   */
+  public boolean contains(Coordinate p) {
+    return contains(p.x, p.y);
+  }
+
+  /**
+   *  Returns <code>true</code> if the given point lies in or on the envelope.
+   *
+   *@param  x  the x-coordinate of the point which this <code>Envelope</code> is
+   *      being checked for containing
+   *@param  y  the y-coordinate of the point which this <code>Envelope</code> is
+   *      being checked for containing
+   *@return    <code>true</code> if <code>(x, y)</code> lies in the interior or
+   *      on the boundary of this <code>Envelope</code>.
+   */
+  public boolean contains(double x, double y) {
+    return x >= minx &&
+        x <= maxx &&
+        y >= miny &&
+        y <= maxy;
+  }
+
+  /**
+   *  Check if the region defined by <code>other</code>
+   *  overlaps (intersects) the region of this <code>Envelope</code>.
+   *
+   *@param  other  the <code>Envelope</code> which this <code>Envelope</code> is
+   *          being checked for overlapping
+   *@return        <code>true</code> if the <code>Envelope</code>s overlap
+   */
+  public boolean intersects(Envelope other) {
+      if (isNull() || other.isNull()) { return false; }
+    return !(other.getMinX() > maxx ||
+        other.getMaxX() < minx ||
+        other.getMinY() > maxy ||
+        other.getMaxY() < miny);
+  }
+  /**
+   * @deprecated Use #intersects instead. In the future, #overlaps may be
+   * changed to be a true overlap check; that is, whether the intersection is
+   * two-dimensional.
+   */
+  public boolean overlaps(Envelope other) {
+    return intersects(other);
+  }
+
+  /**
+   *  Check if the point <code>p</code>
+   *  overlaps (lies inside) the region of this <code>Envelope</code>.
+   *
+   *@param  other  the <code>Coordinate</code> to be tested
+   *@return        <code>true</code> if the point overlaps this <code>Envelope</code>
+   */
+  public boolean intersects(Coordinate p) {
+    return intersects(p.x, p.y);
+  }
+  /**
+   * @deprecated Use #intersects instead.
+   */
+  public boolean overlaps(Coordinate p) {
+    return intersects(p);
+  }
+  /**
+   *  Check if the point <code>(x, y)</code>
+   *  overlaps (lies inside) the region of this <code>Envelope</code>.
+   *
+   *@param  x  the x-ordinate of the point
+   *@param  y  the y-ordinate of the point
+   *@return        <code>true</code> if the point overlaps this <code>Envelope</code>
+   */
+  public boolean intersects(double x, double y) {
+    return ! (x > maxx ||
+        x < minx ||
+        y > maxy ||
+        y < miny);
+  }
+  /**
+   * @deprecated Use #intersects instead.
+   */
+  public boolean overlaps(double x, double y) {
+    return intersects(x, y);
+  }
+
+  /**
+   *  Returns <code>true</code> if the <code>Envelope other</code>
+   *  lies wholely inside this <code>Envelope</code> (inclusive of the boundary).
+   *
+   *@param  other  the <code>Envelope</code> which this <code>Envelope</code> is
+   *        being checked for containing
+   *@return        <code>true</code> if <code>other</code>
+   *              is contained in this <code>Envelope</code>
+   */
+  public boolean contains(Envelope other) {
+    if (isNull() || other.isNull()) { return false; }
+    return other.getMinX() >= minx &&
+        other.getMaxX() <= maxx &&
+        other.getMinY() >= miny &&
+        other.getMaxY() <= maxy;
+  }
+
+  /**
+   * Computes the distance between this and another
+   * <code>Envelope</code>.
+   * The distance between overlapping Envelopes is 0.  Otherwise, the
+   * distance is the Euclidean distance between the closest points.
+   */
+  public double distance(Envelope env)
+  {
+    if (intersects(env)) return 0;
+    double dx = 0.0;
+    if (maxx < env.minx) dx = env.minx - maxx;
+    if (minx > env.maxx) dx = minx - env.maxx;
+    double dy = 0.0;
+    if (maxy < env.miny) dy = env.miny - maxy;
+    if (miny > env.maxy) dy = miny - env.maxy;
+
+    // if either is zero, the envelopes overlap either vertically or horizontally
+    if (dx == 0.0) return dy;
+    if (dy == 0.0) return dx;
+    return Math.sqrt(dx * dx + dy * dy);
+  }
+
+  public boolean equals(Object other) {
+    if (!(other instanceof Envelope)) {
+      return false;
+    }
+    Envelope otherEnvelope = (Envelope) other;
+    if (isNull()) {
+      return otherEnvelope.isNull();
+    }
+    return maxx == otherEnvelope.getMaxX() &&
+        maxy == otherEnvelope.getMaxY() &&
+        minx == otherEnvelope.getMinX() &&
+        maxx == otherEnvelope.getMaxX();
+  }
+
+  public String toString()
+  {
+    return "Env[" + minx + " : " + maxx + ", " + miny + " : " + maxy + "]";
+  }
+}
+

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/Geometry.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/Geometry.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/Geometry.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,1161 @@
+
+
+/*
+ * 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.Collection;
+import java.util.Iterator;
+
+import com.vividsolutions.jts.algorithm.*;
+import com.vividsolutions.jts.io.WKTWriter;
+import com.vividsolutions.jts.operation.buffer.BufferOp;
+import com.vividsolutions.jts.operation.distance.DistanceOp;
+import com.vividsolutions.jts.operation.overlay.OverlayOp;
+import com.vividsolutions.jts.operation.predicate.RectangleIntersects;
+import com.vividsolutions.jts.operation.predicate.RectangleContains;
+import com.vividsolutions.jts.operation.relate.RelateOp;
+import com.vividsolutions.jts.operation.valid.IsValidOp;
+import com.vividsolutions.jts.util.Assert;
+/**
+ *  Basic implementation of <code>Geometry</code>. <P>
+ *
+ *  <code>clone</code> returns a deep copy of the object.
+ *
+ *  <H3>Binary Predicates</H3>
+ * Because it is not clear at this time
+ * what semantics for spatial
+ *  analysis methods involving <code>GeometryCollection</code>s would be useful,
+ *  <code>GeometryCollection</code>s are not supported as arguments to binary
+ *  predicates (other than <code>convexHull</code>) or the <code>relate</code>
+ *  method.
+ *
+ *  <H3>Set-Theoretic Methods</H3>
+ *
+ *  The spatial analysis methods will
+ *  return the most specific class possible to represent the result. If the
+ *  result is homogeneous, a <code>Point</code>, <code>LineString</code>, or
+ *  <code>Polygon</code> will be returned if the result contains a single
+ *  element; otherwise, a <code>MultiPoint</code>, <code>MultiLineString</code>,
+ *  or <code>MultiPolygon</code> will be returned. If the result is
+ *  heterogeneous a <code>GeometryCollection</code> will be returned. <P>
+ *
+ *  Because it is not clear at this time what semantics for set-theoretic
+ *  methods involving <code>GeometryCollection</code>s would be useful,
+ * <code>GeometryCollections</code>
+ *  are not supported as arguments to the set-theoretic methods.
+ *
+ *  <H4>Representation of Computed Geometries </H4>
+ *
+ *  The SFS states that the result
+ *  of a set-theoretic method is the "point-set" result of the usual
+ *  set-theoretic definition of the operation (SFS 3.2.21.1). However, there are
+ *  sometimes many ways of representing a point set as a <code>Geometry</code>.
+ *  <P>
+ *
+ *  The SFS does not specify an unambiguous representation of a given point set
+ *  returned from a spatial analysis method. One goal of JTS is to make this
+ *  specification precise and unambiguous. JTS will use a canonical form for
+ *  <code>Geometry</code>s returned from spatial analysis methods. The canonical
+ *  form is a <code>Geometry</code> which is simple and noded:
+ *  <UL>
+ *    <LI> Simple means that the Geometry returned will be simple according to
+ *    the JTS definition of <code>isSimple</code>.
+ *    <LI> Noded applies only to overlays involving <code>LineString</code>s. It
+ *    means that all intersection points on <code>LineString</code>s will be
+ *    present as endpoints of <code>LineString</code>s in the result.
+ *  </UL>
+ *  This definition implies that non-simple geometries which are arguments to
+ *  spatial analysis methods must be subjected to a line-dissolve process to
+ *  ensure that the results are simple.
+ *
+ *  <H4> Constructed Points And The Precision Model </H4>
+ *
+ *  The results computed by the set-theoretic methods may
+ *  contain constructed points which are not present in the input <code>Geometry</code>
+ *  s. These new points arise from intersections between line segments in the
+ *  edges of the input <code>Geometry</code>s. In the general case it is not
+ *  possible to represent constructed points exactly. This is due to the fact
+ *  that the coordinates of an intersection point may contain twice as many bits
+ *  of precision as the coordinates of the input line segments. In order to
+ *  represent these constructed points explicitly, JTS must truncate them to fit
+ *  the <code>PrecisionModel</code>. <P>
+ *
+ *  Unfortunately, truncating coordinates moves them slightly. Line segments
+ *  which would not be coincident in the exact result may become coincident in
+ *  the truncated representation. This in turn leads to "topology collapses" --
+ *  situations where a computed element has a lower dimension than it would in
+ *  the exact result. <P>
+ *
+ *  When JTS detects topology collapses during the computation of spatial
+ *  analysis methods, it will throw an exception. If possible the exception will
+ *  report the location of the collapse. <P>
+ *
+ *  #equals(Object) and #hashCode are not overridden, so that when two
+ *  topologically equal Geometries are added to HashMaps and HashSets, they
+ *  remain distinct. This behaviour is desired in many cases.
+ *
+ *@version 1.6
+ */
+public abstract class Geometry
+    implements Cloneable, Comparable, Serializable
+{
+    private static final long serialVersionUID = 8763622679187376702L;
+
+  /**
+   *  The bounding box of this <code>Geometry</code>.
+   */
+  protected Envelope envelope;
+
+  private final static Class[] sortedClasses = new Class[] {
+      Point.class,
+      MultiPoint.class,
+      LineString.class,
+      LinearRing.class,
+      MultiLineString.class,
+      Polygon.class,
+      MultiPolygon.class,
+      GeometryCollection.class
+      };
+
+  private final static GeometryComponentFilter geometryChangedFilter = new GeometryComponentFilter() {
+    public void filter(Geometry geom) {
+      geom.geometryChangedAction();
+    }
+  };
+
+  public Geometry(GeometryFactory factory) {
+    this.factory = factory;
+    this.SRID = factory.getSRID();
+  }
+
+  private GeometryFactory factory;
+
+  // MD - no longer used.  Remove in next version
+  //private static final GeometryFactory INTERNAL_GEOMETRY_FACTORY = new GeometryFactory();
+
+  /**
+   *  The ID of the Spatial Reference System used by this <code>Geometry</code>
+   */
+  protected int SRID;
+
+  /**
+   *  Returns the name of this object's <code>com.vivid.jts.geom</code>
+   *  interface.
+   *
+   *@return    the name of this <code>Geometry</code>s most specific <code>com.vividsolutions.jts.geom</code>
+   *      interface
+   */
+  //I wonder if we need this method, now that we have renamed the classes to
+  //what their old interfaces were named. Now we can perhaps simply use
+  //getClass().getName(). Who calls this method anyway? [Jon Aquino]
+  public abstract String getGeometryType();
+
+  /**
+   *  Returns true if the array contains any non-empty <code>Geometry</code>s.
+   *
+   *@param  geometries  an array of <code>Geometry</code>s; no elements may be
+   *      <code>null</code>
+   *@return             <code>true</code> if any of the <code>Geometry</code>s
+   *      <code>isEmpty</code> methods return <code>false</code>
+   */
+  protected static boolean hasNonEmptyElements(Geometry[] geometries) {
+    for (int i = 0; i < geometries.length; i++) {
+      if (!geometries[i].isEmpty()) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  /**
+   *  Returns true if the array contains any <code>null</code> elements.
+   *
+   *@param  array  an array to validate
+   *@return        <code>true</code> if any of <code>array</code>s elements are
+   *      <code>null</code>
+   */
+  protected static boolean hasNullElements(Object[] array) {
+    for (int i = 0; i < array.length; i++) {
+      if (array[i] == null) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+
+
+  /**
+   *  Returns the ID of the Spatial Reference System used by the <code>Geometry</code>.
+   *  <P>
+   *
+   *  JTS supports Spatial Reference System information in the simple way
+   *  defined in the SFS. A Spatial Reference System ID (SRID) is present in
+   *  each <code>Geometry</code> object. <code>Geometry</code> provides basic
+   *  accessor operations for this field, but no others. The SRID is represented
+   *  as an integer.
+   *
+   *@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;
+  }
+
+  private Object userData = null;
+
+  /**
+   * Gets the factory which contains the context in which this geometry was created.
+   *
+   * @return the factory for this geometry
+   */
+  public GeometryFactory getFactory() {
+         return factory;
+  }
+
+  /**
+   * Gets the user data object for this geometry, if any.
+   *
+   * @return the user data object, or <code>null</code> if none set
+   */
+  public Object getUserData() {
+        return userData;
+  }
+
+  /**
+   * Returns the number of {@link Geometry}s in a {@link GeometryCollection}
+   * (or 1, if the geometry is not a collection).
+   *
+   * @return the number of geometries contained in this geometry
+   */
+  public int getNumGeometries() {
+    return 1;
+  }
+
+  /**
+   * Returns an element {@link Geometry} from a {@link GeometryCollection}
+   * (or <code>this</code>, if the geometry is not a collection).
+   *
+   * @param n the index of the geometry element
+   * @return the n'th geometry contained in this geometry
+   */
+  public Geometry getGeometryN(int n) {
+    return this;
+  }
+
+
+  /**
+   * A simple scheme for applications to add their own custom data to a Geometry.
+   * An example use might be to add an object representing a Coordinate Reference System.
+   * <p>
+   * Note that user data objects are not present in geometries created by
+   * construction methods.
+   *
+   * @param userData an object, the semantics for which are defined by the
+   * application using this Geometry
+   */
+  public void setUserData(Object userData) {
+        this.userData = userData;
+  }
+
+
+  /**
+   *  Returns the <code>PrecisionModel</code> used by the <code>Geometry</code>.
+   *
+   *@return    the specification of the grid of allowable points, for this
+   *      <code>Geometry</code> and all other <code>Geometry</code>s
+   */
+  public PrecisionModel getPrecisionModel() {
+    return factory.getPrecisionModel();
+  }
+
+  /**
+   *  Returns a vertex of this <code>Geometry</code>.
+   *
+   *@return    a {@link Coordinate} which is a vertex of this <code>Geometry</code>.
+   *          Returns <code>null</code> if this Geometry is empty
+   */
+  public abstract Coordinate getCoordinate();
+  /**
+   *  Returns this <code>Geometry</code> s vertices. If you modify the coordinates
+   *  in this array, be sure to call #geometryChanged afterwards.
+   *  The <code>Geometry</code>s contained by composite <code>Geometry</code>s
+   *  must be Geometry's; that is, they must implement <code>getCoordinates</code>.
+   *
+   *@return    the vertices of this <code>Geometry</code>
+   */
+  public abstract Coordinate[] getCoordinates();
+
+  /**
+   *  Returns the count of this <code>Geometry</code>s vertices. The <code>Geometry</code>
+   *  s contained by composite <code>Geometry</code>s must be
+   *  Geometry's; that is, they must implement <code>getNumPoints</code>
+   *
+   *@return    the number of vertices in this <code>Geometry</code>
+   */
+  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:
+   *  <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>.
+   *
+   *@return    <code>true</code> if this <code>Geometry</code> has any points of
+   *      self-tangency, self-intersection or other anomalous points
+   */
+  public abstract boolean isSimple();
+
+  /**
+   *  Tests the validity of this <code>Geometry</code>.
+   *  Subclasses provide their own definition of "valid".
+   *
+   *@return    <code>true</code> if this <code>Geometry</code> is valid
+   *
+   * @see IsValidOp
+   */
+  public boolean isValid()
+  {
+    IsValidOp isValidOp = new IsValidOp(this);
+    return isValidOp.isValid();
+  }
+
+  /**
+   *  Returns whether or not the set of points in this <code>Geometry</code> is
+   *  empty.
+   *
+   *@return    <code>true</code> if this <code>Geometry</code> equals the empty
+   *      geometry
+   */
+  public abstract boolean isEmpty();
+
+  /**
+   *  Returns the minimum distance between this <code>Geometry</code>
+   *  and the <code>Geometry</code> g
+   *
+   *@param  g  the <code>Geometry</code> from which to compute the distance
+   */
+  public double distance(Geometry g)
+  {
+    return DistanceOp.distance(this, g);
+  }
+
+  /**
+   * Tests whether the distance from this <code>Geometry</code>
+   * to another is less than or equal to a specified value.
+   *
+   * @param geom the Geometry to check the distance to
+   * @param distance the distance value to compare
+   * @return <code>true</code> if the geometries are less than <code>distance</code> apart.
+   */
+  public boolean isWithinDistance(Geometry geom, double distance)
+  {
+    double envDist = getEnvelopeInternal().distance(geom.getEnvelopeInternal());
+    if (envDist > distance)
+      return false;
+    // NOTE: this could be implemented more efficiently
+    double geomDist = this.distance(geom);
+    if (geomDist > distance)
+      return false;
+    return true;
+  }
+
+  public boolean isRectangle()
+  {
+    // Polygon overrides to check for actual rectangle
+    return false;
+  }
+
+  /**
+   *  Returns the area of this <code>Geometry</code>.
+   *  Areal Geometries have a non-zero area.
+   *  They override this function to compute the area.
+   *  Others return 0.0
+   *
+   *@return the area of the Geometry
+   */
+  public double getArea()
+  {
+    return 0.0;
+  }
+
+  /**
+   *  Returns the length of this <code>Geometry</code>.
+   *  Linear geometries return their length.
+   *  Areal geometries return their perimeter.
+   *  They override this function to compute the area.
+   *  Others return 0.0
+   *
+   *@return the length of the Geometry
+   */
+  public double getLength()
+  {
+    return 0.0;
+  }
+
+  /**
+   * Computes the centroid of this <code>Geometry</code>.
+   * The centroid
+   * is equal to the centroid of the set of component Geometries of highest
+   * dimension (since the lower-dimension geometries contribute zero
+   * "weight" to the centroid)
+   *
+   * @return a {@link Point} which is the centroid of this Geometry
+   */
+  public Point getCentroid()
+  {
+    if (isEmpty()) { return null; }
+    Coordinate centPt = null;
+    int dim = getDimension();
+    if (dim == 0) {
+      CentroidPoint cent = new CentroidPoint();
+      cent.add(this);
+      centPt = cent.getCentroid();
+    }
+    else if (dim == 1) {
+      CentroidLine cent = new CentroidLine();
+      cent.add(this);
+      centPt = cent.getCentroid();
+    }
+    else {
+      CentroidArea cent = new CentroidArea();
+      cent.add(this);
+      centPt = cent.getCentroid();
+    }
+    return createPointFromInternalCoord(centPt, this);
+
+  }
+
+  /**
+   * Computes an interior point of this <code>Geometry</code>.
+   * An interior point is guaranteed to lie in the interior of the Geometry,
+   * if it possible to calculate such a point exactly. Otherwise,
+   * the point may lie on the boundary of the geometry.
+   *
+   * @return a {@link Point} which is in the interior of this Geometry
+   */
+  public Point getInteriorPoint()
+  {
+    Coordinate interiorPt = null;
+    int dim = getDimension();
+    if (dim == 0) {
+      InteriorPointPoint intPt = new InteriorPointPoint(this);
+      interiorPt = intPt.getInteriorPoint();
+    }
+    else if (dim == 1) {
+      InteriorPointLine intPt = new InteriorPointLine(this);
+      interiorPt = intPt.getInteriorPoint();
+    }
+    else {
+      InteriorPointArea intPt = new InteriorPointArea(this);
+      interiorPt = intPt.getInteriorPoint();
+    }
+    return createPointFromInternalCoord(interiorPt, this);
+  }
+
+  /**
+   *  Returns the dimension of this <code>Geometry</code>.
+   *
+   *@return    the dimension of the class implementing this interface, whether
+   *      or not this object is the empty geometry
+   */
+  public abstract int getDimension();
+  /**
+   *  Returns the boundary, or the empty geometry if this <code>Geometry</code>
+   *  is empty. For a discussion of this function, see the OpenGIS Simple
+   *  Features Specification. As stated in SFS Section 2.1.13.1, "the boundary
+   *  of a Geometry is a set of Geometries of the next lower dimension."
+   *
+   *@return    the closure of the combinatorial boundary of this <code>Geometry</code>
+   */
+  public abstract Geometry getBoundary();
+
+  /**
+   *  Returns the dimension of this <code>Geometry</code>s inherent boundary.
+   *
+   *@return    the dimension of the boundary of the class implementing this
+   *      interface, whether or not this object is the empty geometry. Returns
+   *      <code>Dimension.FALSE</code> if the boundary is the empty geometry.
+   */
+  public abstract int getBoundaryDimension();
+
+  /**
+   *  Returns this <code>Geometry</code>s bounding box. If this <code>Geometry</code>
+   *  is the empty geometry, returns an empty <code>Point</code>. If the <code>Geometry</code>
+   *  is a point, returns a non-empty <code>Point</code>. Otherwise, returns a
+   *  <code>Polygon</code> whose points are (minx, miny), (maxx, miny), (maxx,
+   *  maxy), (minx, maxy), (minx, miny).
+   *
+   *@return    an empty <code>Point</code> (for empty <code>Geometry</code>s), a
+   *      <code>Point</code> (for <code>Point</code>s) or a <code>Polygon</code>
+   *      (in all other cases)
+   */
+  public Geometry getEnvelope() {
+    return getFactory().toGeometry(getEnvelopeInternal());
+  }
+
+  /**
+   *  Returns the minimum and maximum x and y values in this <code>Geometry</code>
+   *  , or a null <code>Envelope</code> if this <code>Geometry</code> is empty.
+   *
+   *@return    this <code>Geometry</code>s bounding box; if the <code>Geometry</code>
+   *      is empty, <code>Envelope#isNull</code> will return <code>true</code>
+   */
+  public Envelope getEnvelopeInternal() {
+    if (envelope == null) {
+      envelope = computeEnvelopeInternal();
+    }
+    return envelope;
+  }
+
+  /**
+   * Notifies this Geometry that its Coordinates have been changed by an external
+   * party (using a CoordinateFilter, for example). The Geometry will flush
+   * and/or update any information it has cached (such as its {@link Envelope} ).
+   */
+  public void geometryChanged() {
+    apply(geometryChangedFilter);
+  }
+
+  /**
+   * Notifies this Geometry that its Coordinates have been changed by an external
+   * party. When #geometryChanged is called, this method will be called for
+   * this Geometry and its component Geometries.
+   * @see #apply(GeometryComponentFilter)
+   */
+  protected void geometryChangedAction() {
+    envelope = null;
+  }
+
+  /**
+   *  Returns <code>true</code> if the DE-9IM intersection matrix for the two
+   *  <code>Geometry</code>s is FF*FF****.
+   *
+   *@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
+   *      disjoint
+   */
+  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****.
+   *
+   *@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 touch;
+   *      Returns false if both <code>Geometry</code>s are points
+   */
+  public boolean touches(Geometry g) {
+    // short-circuit test
+    if (! getEnvelopeInternal().intersects(g.getEnvelopeInternal()))
+      return false;
+    return relate(g).isTouches(getDimension(), g.getDimension());
+  }
+
+  /**
+   *  Returns <code>true</code> if <code>disjoint</code> returns false.
+   *
+   *@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 intersect
+   */
+  public boolean intersects(Geometry g) {
+    // short-circuit envelope test
+    if (! getEnvelopeInternal().intersects(g.getEnvelopeInternal()))
+      return false;
+    // optimizations for rectangle arguments
+    if (isRectangle()) {
+      return RectangleIntersects.intersects((Polygon) this, g);
+    }
+    if (g.isRectangle()) {
+      return RectangleIntersects.intersects((Polygon) g, this);
+    }
+    // general case
+    return relate(g).isIntersects();
+  }
+
+  /**
+   *  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>
+   *  .
+   *
+   *@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 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
+    if (! getEnvelopeInternal().intersects(g.getEnvelopeInternal()))
+      return false;
+    return relate(g).isCrosses(getDimension(), g.getDimension());
+  }
+
+  /**
+   *  Returns <code>true</code> if the DE-9IM intersection matrix for the two
+   *  <code>Geometry</code>s is T*F**F***.
+   *
+   *@param  other  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>
+   */
+  public boolean within(Geometry g) {
+    return g.contains(this);
+  }
+
+  /**
+   *  Returns <code>true</code> if <code>other.within(this)</code> returns
+   *  <code>true</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>
+   */
+  public boolean contains(Geometry g) {
+    // short-circuit test
+    if (! getEnvelopeInternal().contains(g.getEnvelopeInternal()))
+      return false;
+    // optimizations for rectangle arguments
+    if (isRectangle()) {
+      return RectangleContains.contains((Polygon) this, g);
+    }
+    // general case
+    return relate(g).isContains();
+  }
+
+  /**
+   *  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>
+   *  .
+   *
+   *@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 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
+    if (! getEnvelopeInternal().intersects(g.getEnvelopeInternal()))
+      return false;
+    return relate(g).isOverlaps(getDimension(), g.getDimension());
+  }
+
+  /**
+   *  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:
+   *  <UL>
+   *    <LI> 0
+   *    <LI> 1
+   *    <LI> 2
+   *    <LI> T ( = 0, 1 or 2)
+   *    <LI> F ( = -1)
+   *    <LI> * ( = -1, 0, 1 or 2)
+   *  </UL>
+   *  For more information on the DE-9IM, see the OpenGIS Simple Features
+   *  Specification.
+   *
+   *@param  other                the <code>Geometry</code> with which to compare
+   *      this <code>Geometry</code>
+   *@param  intersectionPattern  the pattern against which to check the
+   *      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>
+   */
+  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.
+   *
+   *@param  other  the <code>Geometry</code> with which to compare this <code>Geometry</code>
+   *@return        a matrix describing the intersections of the interiors,
+   *      boundaries and exteriors of the two <code>Geometry</code>s
+   */
+  public IntersectionMatrix relate(Geometry g) {
+    checkNotGeometryCollection(this);
+    checkNotGeometryCollection(g);
+    return RelateOp.relate(this, g);
+  }
+
+  /**
+   *  Returns <code>true</code> if the DE-9IM intersection matrix for the two
+   *  <code>Geometry</code>s is T*F**FFF*.
+   *
+   *@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
+   */
+  public boolean equals(Geometry g) {
+    // short-circuit test
+    if (! getEnvelopeInternal().equals(g.getEnvelopeInternal()))
+      return false;
+    return relate(g).isEquals(getDimension(), g.getDimension());
+  }
+
+  //<<PERHAPS:DESIGN>> Override Object#equals [Jon Aquino]
+
+  public String toString() {
+    return toText();
+  }
+
+  /**
+   *  Returns the Well-known Text representation of this <code>Geometry</code>.
+   *  For a definition of the Well-known Text format, see the OpenGIS Simple
+   *  Features Specification.
+   *
+   *@return    the Well-known Text representation of this <code>Geometry</code>
+   */
+  public String toText() {
+    WKTWriter writer = new WKTWriter();
+    return writer.write(this);
+  }
+
+  /**
+   *  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>.
+   *
+   *@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>
+   */
+  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.
+   *
+   *@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>
+   */
+  public Geometry buffer(double distance, int quadrantSegments) {
+    return BufferOp.bufferOp(this, distance, quadrantSegments);
+  }
+
+  /**
+   *  Returns 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:
+   *  <TABLE>
+   *    <TR>
+   *      <TH>    Number of <code>Point</code>s in argument <code>Geometry</code>   </TH>
+   *      <TH>    <code>Geometry</code> class of result     </TH>
+   *    </TR>
+   *    <TR>
+   *      <TD>        0      </TD>
+   *      <TD>        empty <code>GeometryCollection</code>      </TD>
+   *    </TR>
+   *    <TR>  <TD>      1     </TD>
+   *      <TD>     <code>Point</code>     </TD>
+   *    </TR>
+   *    <TR>
+   *      <TD>      2     </TD>
+   *      <TD>     <code>LineString</code>     </TD>
+   *    </TR>
+   *    <TR>
+   *      <TD>       3 or more     </TD>
+   *      <TD>      <code>Polygon</code>     </TD>
+   *    </TR>
+   *  </TABLE>
+   *
+   *@return    the minimum-area convex polygon containing this <code>Geometry</code>'
+   *      s points
+   */
+  public Geometry convexHull() {
+    return (new ConvexHull(this)).getConvexHull();
+  }
+
+  /**
+   *  Returns 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
+   *      intersection
+   *@return        the points common to the two <code>Geometry</code>s
+   */
+  public Geometry intersection(Geometry other) {
+    checkNotGeometryCollection(this);
+    checkNotGeometryCollection(other);
+    return OverlayOp.overlayOp(this, other, OverlayOp.INTERSECTION);
+  }
+
+  /**
+   *  Returns 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>
+   */
+  public Geometry union(Geometry other) {
+    checkNotGeometryCollection(this);
+    checkNotGeometryCollection(other);
+    return OverlayOp.overlayOp(this, other, OverlayOp.UNION);
+  }
+
+  /**
+   *  Returns 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>.
+   *
+   *@param  other  the <code>Geometry</code> with which to compute the
+   *      difference
+   *@return        the point set difference of this <code>Geometry</code> with
+   *      <code>other</code>
+   */
+  public Geometry difference(Geometry other) {
+    checkNotGeometryCollection(this);
+    checkNotGeometryCollection(other);
+    return OverlayOp.overlayOp(this, other, OverlayOp.DIFFERENCE);
+  }
+
+  /**
+   *  Returns a set combining the points in this <code>Geometry</code> not in
+   *  <code>other</code>, and the points in <code>other</code> not in this
+   *  <code>Geometry</code>. This method returns the closure of the resultant
+   *  <code>Geometry</code>.
+   *
+   *@param  other  the <code>Geometry</code> with which to compute the symmetric
+   *      difference
+   *@return        the point set symmetric difference of this <code>Geometry</code>
+   *      with <code>other</code>
+   */
+  public Geometry symDifference(Geometry other) {
+    checkNotGeometryCollection(this);
+    checkNotGeometryCollection(other);
+    return OverlayOp.overlayOp(this, other, OverlayOp.SYMDIFFERENCE);
+  }
+
+  /**
+   *  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:
+   * <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.
+   * </ul>
+   * If this and the other <code>Geometry</code>s are
+   *  composites and any children are not <code>Geometry</code>s, returns
+   *  false.
+   *
+   *@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.
+   */
+  public abstract boolean equalsExact(Geometry other, double tolerance);
+
+  /**
+   *  Returns true if the two <code>Geometry</code>s are exactly equal.
+   * Two Geometries are exactly equal iff:
+   * <ul>
+   * <li>they have the same class
+   * <li>they have the same values of Coordinates in their internal
+   * Coordinate lists, 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.
+   * <p>
+   *  This provides a stricter test of equality than
+   *  <code>equals</code>.
+   *
+   *@param  other  the <code>Geometry</code> with which to compare this <code>Geometry</code>
+   *@return        <code>true</code> if this and the other <code>Geometry</code>
+   *      are of the same class and have equal internal data.
+   */
+  public boolean equalsExact(Geometry other) { return equalsExact(other, 0); }
+
+  /**
+   *  Performs an operation with or on this <code>Geometry</code>'s
+   *  coordinates. If you are using this method to modify the geometry, be sure
+   *  to call #geometryChanged() afterwards. Note that you cannot use this
+   *  method to
+   *  modify this Geometry if its underlying CoordinateSequence's #get method
+   *  returns a copy of the Coordinate, rather than the actual Coordinate stored
+   *  (if it even stores Coordinates at all).
+   *
+   *@param  filter  the filter to apply to this <code>Geometry</code>'s
+   *      coordinates
+   */
+  public abstract void apply(CoordinateFilter filter);
+
+  /**
+   *  Performs an operation with or on this <code>Geometry</code> and its
+   *  subelement <code>Geometry</code>s (if any).
+   *  Only GeometryCollections and subclasses
+   *  have subelement Geometry's.
+   *
+   *@param  filter  the filter to apply to this <code>Geometry</code> (and
+   *      its children, if it is a <code>GeometryCollection</code>).
+   */
+  public abstract void apply(GeometryFilter filter);
+
+  /**
+   *  Performs an operation with or on this Geometry and its
+   *  component Geometry's.  Only GeometryCollections and
+   *  Polygons have component Geometry's; for Polygons they are the LinearRings
+   *  of the shell and holes.
+   *
+   *@param  filter  the filter to apply to this <code>Geometry</code>.
+   */
+  public abstract void apply(GeometryComponentFilter filter);
+
+  public Object clone() {
+    try {
+      Geometry clone = (Geometry) super.clone();
+      if (clone.envelope != null) { clone.envelope = new Envelope(clone.envelope); }
+      return clone;
+    }
+    catch (CloneNotSupportedException e) {
+      Assert.shouldNeverReachHere();
+      return null;
+    }
+  }
+
+  /**
+   *  Converts this <code>Geometry</code> to <b>normal form</b> (or <b>
+   *  canonical form</b> ). Normal form is a unique representation for <code>Geometry</code>
+   *  s. It can be used to test whether two <code>Geometry</code>s are equal
+   *  in a way that is independent of the ordering of the coordinates within
+   *  them. Normal form equality is a stronger condition than topological
+   *  equality, but weaker than pointwise equality. The definitions for normal
+   *  form use the standard lexicographical ordering for coordinates. "Sorted in
+   *  order of coordinates" means the obvious extension of this ordering to
+   *  sequences of coordinates.
+   */
+  public abstract void normalize();
+
+  /**
+   *  Returns whether this <code>Geometry</code> is greater than, equal to,
+   *  or less than another <code>Geometry</code>. <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>
+   *@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) {
+    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);
+  }
+
+  /**
+   *  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
+   *  "equivalent" only if they are the same class. An exception is <code>LineString</code>
+   *  , which is considered to be equivalent to its subclasses.
+   *
+   *@param  other  the <code>Geometry</code> with which to compare this <code>Geometry</code>
+   *      for equality
+   *@return        <code>true</code> if the classes of the two <code>Geometry</code>
+   *      s are considered to be equal by the <code>equalsExact</code> method.
+   */
+  protected boolean isEquivalentClass(Geometry other) {
+    return this.getClass().getName().equals(other.getClass().getName());
+  }
+
+  /**
+   *  Throws an exception if <code>g</code>'s class is <code>GeometryCollection</code>
+   *  . (Its subclasses do not trigger an exception).
+   *
+   *@param  g                          the <code>Geometry</code> to check
+   *@throws  IllegalArgumentException  if <code>g</code> is a <code>GeometryCollection</code>
+   *      but not one of its subclasses
+   */
+  protected void checkNotGeometryCollection(Geometry g) {
+    //Don't use instanceof because we want to allow subclasses
+    if (g.getClass().getName().equals("com.vividsolutions.jts.geom.GeometryCollection")) {
+      throw new IllegalArgumentException("This method does not support GeometryCollection arguments");
+    }
+  }
+
+
+  /**
+   *  Returns the minimum and maximum x and y values in this <code>Geometry</code>
+   *  , or a null <code>Envelope</code> if this <code>Geometry</code> is empty.
+   *  Unlike <code>getEnvelopeInternal</code>, this method calculates the <code>Envelope</code>
+   *  each time it is called; <code>getEnvelopeInternal</code> caches the result
+   *  of this method.
+   *
+   *@return    this <code>Geometry</code>s bounding box; if the <code>Geometry</code>
+   *      is empty, <code>Envelope#isNull</code> will return <code>true</code>
+   */
+  protected abstract Envelope computeEnvelopeInternal();
+
+  /**
+   *  Returns whether this <code>Geometry</code> is greater than, equal to,
+   *  or less than another <code>Geometry</code> having the same class.
+   *
+   *@param  o  a <code>Geometry</code> having the same class as this <code>Geometry</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);
+
+  /**
+   *  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,
+   *  returns 0 if the other iteration is also complete. If <code>b</code>
+   *  completes before <code>a</code>, a positive number is returned; if a
+   *  before b, a negative number.
+   *
+   *@param  a  a <code>Collection</code> of <code>Comparable</code>s
+   *@param  b  a <code>Collection</code> of <code>Comparable</code>s
+   *@return    the first non-zero <code>compareTo</code> result, if any;
+   *      otherwise, zero
+   */
+  protected int compare(Collection a, Collection b) {
+    Iterator i = a.iterator();
+    Iterator j = b.iterator();
+    while (i.hasNext() && j.hasNext()) {
+      Comparable aElement = (Comparable) i.next();
+      Comparable bElement = (Comparable) j.next();
+      int comparison = aElement.compareTo(bElement);
+      if (comparison != 0) {
+        return comparison;
+      }
+    }
+    if (i.hasNext()) {
+      return 1;
+    }
+    if (j.hasNext()) {
+      return -1;
+    }
+    return 0;
+  }
+
+  protected boolean equal(Coordinate a, Coordinate b, double tolerance) {
+    if (tolerance == 0) { return a.equals(b); }
+    return a.distance(b) <= tolerance;
+  }
+
+  private int getClassSortIndex() {
+    for (int i = 0; i < sortedClasses.length; i++) {
+      if (sortedClasses[i].isInstance(this)) {
+        return i;
+      }
+    }
+    Assert.shouldNeverReachHere("Class not supported: " + this.getClass());
+    return -1;
+  }
+
+  private Point createPointFromInternalCoord(Coordinate coord, Geometry exemplar)
+  {
+    exemplar.getPrecisionModel().makePrecise(coord);
+    return exemplar.getFactory().createPoint(coord);
+  }
+
+
+}
+

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/GeometryCollection.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/GeometryCollection.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/GeometryCollection.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,252 @@
+
+
+/*
+ * 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.Arrays;
+import java.util.TreeSet;
+
+import com.vividsolutions.jts.util.Assert;
+
+/**
+ *  Basic implementation of <code>GeometryCollection</code>.
+ *
+ *@version 1.6
+ */
+public class GeometryCollection extends Geometry {
+//  With contributions from Markus Schaber [schabios at logi-track.com] 2004-03-26
+  private static final long serialVersionUID = -5694727726395021467L;
+  /**
+   *  Internal representation of this <code>GeometryCollection</code>.
+   */
+  protected Geometry[] geometries;
+
+  /** @deprecated Use GeometryFactory instead */
+  public GeometryCollection(Geometry[] geometries, PrecisionModel precisionModel, int SRID) {
+      this(geometries, new GeometryFactory(precisionModel, SRID));
+  }
+
+
+  /**
+   * @param geometries
+   *            the <code>Geometry</code>s for this <code>GeometryCollection</code>,
+   *            or <code>null</code> or an empty array to create the empty
+   *            geometry. Elements may be empty <code>Geometry</code>s,
+   *            but not <code>null</code>s.
+   */
+  public GeometryCollection(Geometry[] geometries, GeometryFactory factory) {
+    super(factory);
+    if (geometries == null) {
+      geometries = new Geometry[]{};
+    }
+    if (hasNullElements(geometries)) {
+      throw new IllegalArgumentException("geometries must not contain null elements");
+    }
+    this.geometries = geometries;
+  }
+
+  public Coordinate getCoordinate() {
+    if (isEmpty()) return null;
+    return geometries[0].getCoordinate();
+  }
+
+  /**
+   * Collects all coordinates of all subgeometries into an Array.
+   *
+   * Note that while changes to the coordinate objects themselves
+   * may modify the Geometries in place, the returned Array as such
+   * is only a temporary container which is not synchronized back.
+   *
+   * @return the collected coordinates
+   *    */
+  public Coordinate[] getCoordinates() {
+    Coordinate[] coordinates = new Coordinate[getNumPoints()];
+    int k = -1;
+    for (int i = 0; i < geometries.length; i++) {
+      Coordinate[] childCoordinates = geometries[i].getCoordinates();
+      for (int j = 0; j < childCoordinates.length; j++) {
+        k++;
+        coordinates[k] = childCoordinates[j];
+      }
+    }
+    return coordinates;
+  }
+
+  public boolean isEmpty() {
+    for (int i = 0; i < geometries.length; i++) {
+      if (!geometries[i].isEmpty()) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  public int getDimension() {
+    int dimension = Dimension.FALSE;
+    for (int i = 0; i < geometries.length; i++) {
+      dimension = Math.max(dimension, geometries[i].getDimension());
+    }
+    return dimension;
+  }
+
+  public int getBoundaryDimension() {
+    int dimension = Dimension.FALSE;
+    for (int i = 0; i < geometries.length; i++) {
+      dimension = Math.max(dimension, ((Geometry) geometries[i]).getBoundaryDimension());
+    }
+    return dimension;
+  }
+
+  public int getNumGeometries() {
+    return geometries.length;
+  }
+
+  public Geometry getGeometryN(int n) {
+    return geometries[n];
+  }
+
+  public int getNumPoints() {
+    int numPoints = 0;
+    for (int i = 0; i < geometries.length; i++) {
+      numPoints += ((Geometry) geometries[i]).getNumPoints();
+    }
+    return numPoints;
+  }
+
+  public String getGeometryType() {
+    return "GeometryCollection";
+  }
+
+  public boolean isSimple() {
+    checkNotGeometryCollection(this);
+    Assert.shouldNeverReachHere();
+    return false;
+  }
+
+  public Geometry getBoundary() {
+    checkNotGeometryCollection(this);
+    Assert.shouldNeverReachHere();
+    return null;
+  }
+
+  /**
+   *  Returns the area of this <code>GeometryCollection</code>
+   *
+   *@return the area of the polygon
+   */
+  public double getArea()
+  {
+    double area = 0.0;
+    for (int i = 0; i < geometries.length; i++) {
+      area += geometries[i].getArea();
+    }
+    return area;
+  }
+
+  public double getLength()
+  {
+    double sum = 0.0;
+    for (int i = 0; i < geometries.length; i++) {
+      sum += (geometries[i]).getLength();
+    }
+    return sum;
+  }
+
+  public boolean equalsExact(Geometry other, double tolerance) {
+    if (!isEquivalentClass(other)) {
+      return false;
+    }
+    GeometryCollection otherCollection = (GeometryCollection) other;
+    if (geometries.length != otherCollection.geometries.length) {
+      return false;
+    }
+    for (int i = 0; i < geometries.length; i++) {
+      if (!((Geometry) geometries[i]).equalsExact(otherCollection.geometries[i], tolerance)) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  public void apply(CoordinateFilter filter) {
+    for (int i = 0; i < geometries.length; i++) {
+      geometries[i].apply(filter);
+    }
+  }
+
+  public void apply(GeometryFilter filter) {
+    filter.filter(this);
+    for (int i = 0; i < geometries.length; i++) {
+      geometries[i].apply(filter);
+    }
+  }
+
+  public void apply(GeometryComponentFilter filter) {
+    filter.filter(this);
+    for (int i = 0; i < geometries.length; i++) {
+      geometries[i].apply(filter);
+    }
+  }
+
+  public Object clone() {
+    GeometryCollection gc = (GeometryCollection) super.clone();
+    gc.geometries = new Geometry[geometries.length];
+    for (int i = 0; i < geometries.length; i++) {
+      gc.geometries[i] = (Geometry) geometries[i].clone();
+    }
+    return gc;// return the clone
+  }
+
+  public void normalize() {
+    for (int i = 0; i < geometries.length; i++) {
+      geometries[i].normalize();
+    }
+    Arrays.sort(geometries);
+  }
+
+  protected Envelope computeEnvelopeInternal() {
+    Envelope envelope = new Envelope();
+    for (int i = 0; i < geometries.length; i++) {
+      envelope.expandToInclude(geometries[i].getEnvelopeInternal());
+    }
+    return envelope;
+  }
+
+  protected int compareToSameClass(Object o) {
+    TreeSet theseElements = new TreeSet(Arrays.asList(geometries));
+    TreeSet otherElements = new TreeSet(Arrays.asList(((GeometryCollection) o).geometries));
+    return compare(theseElements, otherElements);
+  }
+}
+

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/GeometryCollectionIterator.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/GeometryCollectionIterator.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/GeometryCollectionIterator.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,142 @@
+
+
+/*
+ * 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.Iterator;
+import java.util.NoSuchElementException;
+
+/**
+ *  Iterates over all {@link Geometry}s in a {@link GeometryCollection}.
+ *  . Implements a pre-order depth-first traversal of the <code>GeometryCollection</code>
+ *  (which may be nested). The original <code>GeometryCollection</code> is
+ *  returned as well (as the first object), as are all sub-collections. It is
+ *  simple to ignore the <code>GeometryCollection</code> objects if they are not
+ *  needed.
+ *
+ *@version 1.6
+ */
+public class GeometryCollectionIterator implements Iterator {
+
+  /**
+   *  The <code>GeometryCollection</code> being iterated over.
+   */
+  private Geometry parent;
+  /**
+   *  Indicates whether or not the first element (the <code>GeometryCollection</code>
+   *  ) has been returned.
+   */
+  private boolean atStart;
+  /**
+   *  The number of <code>Geometry</code>s in the the <code>GeometryCollection</code>
+   *  .
+   */
+  private int max;
+  /**
+   *  The index of the <code>Geometry</code> that will be returned when <code>next</code>
+   *  is called.
+   */
+  private int index;
+  /**
+   *  The iterator over a nested <code>GeometryCollection</code>, or <code>null</code>
+   *  if this <code>GeometryCollectionIterator</code> is not currently iterating
+   *  over a nested <code>GeometryCollection</code>.
+   */
+  private GeometryCollectionIterator subcollectionIterator;
+
+  /**
+   *  Constructs an iterator over the given <code>GeometryCollection</code>.
+   *
+   *@param  parent  the collection over which to iterate; also, the first
+   *      element returned by the iterator.
+   */
+  public GeometryCollectionIterator(Geometry parent) {
+    this.parent = parent;
+    atStart = true;
+    index = 0;
+    max = parent.getNumGeometries();
+  }
+
+  public boolean hasNext() {
+    if (atStart) {
+      return true;
+    }
+    if (subcollectionIterator != null) {
+      if (subcollectionIterator.hasNext()) {
+        return true;
+      }
+      subcollectionIterator = null;
+    }
+    if (index >= max) {
+      return false;
+    }
+    return true;
+  }
+
+  public Object next() {
+    // the parent GeometryCollection is the first object returned
+    if (atStart) {
+      atStart = false;
+      return parent;
+    }
+    if (subcollectionIterator != null) {
+      if (subcollectionIterator.hasNext()) {
+        return subcollectionIterator.next();
+      }
+      else {
+        subcollectionIterator = null;
+      }
+    }
+    if (index >= max) {
+      throw new NoSuchElementException();
+    }
+    Geometry obj = parent.getGeometryN(index++);
+    if (obj instanceof GeometryCollection) {
+      subcollectionIterator = new GeometryCollectionIterator((GeometryCollection) obj);
+      // there will always be at least one element in the sub-collection
+      return subcollectionIterator.next();
+    }
+    return obj;
+  }
+
+  /**
+   *  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/geom/GeometryComponentFilter.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/GeometryComponentFilter.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/GeometryComponentFilter.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,63 @@
+
+
+/*
+ * 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;
+
+
+/**
+ *  <code>Geometry</code> classes support the concept of applying
+ *  a <code>GeometryComponentFilter</code>
+ *  filter to the <code>Geometry</code>.
+ *  The filter is applied to every component of the <code>Geometry</code>
+ *  which is itself a <code>Geometry</code>.
+ * (For instance, all the LinearRings in Polygons are visited.)
+ *  <p>
+ *  A <code>GeometryComponentFilter</code> filter can either
+ *  record information about the <code>Geometry</code>
+ *  or change the <code>Geometry</code> in some way.
+ *  <code>GeometryComponentFilter</code>
+ *  is an example of the Gang-of-Four Visitor pattern.
+ *
+ *@version 1.6
+ */
+public interface GeometryComponentFilter {
+
+  /**
+   *  Performs an operation with or on <code>geom</code>.
+   *
+   *@param  geom  a <code>Geometry</code> to which the filter is applied.
+   */
+  public void filter(Geometry geom);
+}
+

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/GeometryFactory.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/GeometryFactory.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/GeometryFactory.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,489 @@
+
+
+/*
+ * 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.*;
+import java.io.Serializable;
+import com.vividsolutions.jts.geom.impl.*;
+import com.vividsolutions.jts.geom.util.*;
+import com.vividsolutions.jts.util.Assert;
+
+/**
+ * Supplies a set of utility methods for building Geometry objects from lists
+ * of Coordinates.
+ *
+ * @version 1.6
+ */
+public class GeometryFactory
+    implements Serializable
+{
+  private static final long serialVersionUID = -6820524753094095635L;
+  private PrecisionModel precisionModel;
+
+  private CoordinateSequenceFactory coordinateSequenceFactory;
+
+
+  public static Point createPointFromInternalCoord(Coordinate coord, Geometry exemplar)
+  {
+    exemplar.getPrecisionModel().makePrecise(coord);
+    return exemplar.getFactory().createPoint(coord);
+  }
+
+  /**
+   * Constructs a GeometryFactory that generates Geometries having the given
+   * PrecisionModel, spatial-reference ID, and CoordinateSequence implementation.
+   */
+  public GeometryFactory(PrecisionModel precisionModel, int SRID,
+                         CoordinateSequenceFactory coordinateSequenceFactory) {
+      this.precisionModel = precisionModel;
+      this.coordinateSequenceFactory = coordinateSequenceFactory;
+      this.SRID = SRID;
+  }
+
+  /**
+   * Constructs a GeometryFactory that generates Geometries having the given
+   * CoordinateSequence implementation, a double-precision floating PrecisionModel and a
+   * spatial-reference ID of 0.
+   */
+  public GeometryFactory(CoordinateSequenceFactory coordinateSequenceFactory) {
+    this(new PrecisionModel(), 0, coordinateSequenceFactory);
+  }
+
+  /**
+   * Constructs a GeometryFactory that generates Geometries having the given
+   * {@link PrecisionModel} and the default CoordinateSequence
+   * implementation.
+   *
+   * @param precisionModel the PrecisionModel to use
+   */
+  public GeometryFactory(PrecisionModel precisionModel) {
+    this(precisionModel, 0, getDefaultCoordinateSequenceFactory());
+  }
+
+  /**
+   * Constructs a GeometryFactory that generates Geometries having the given
+   * {@link PrecisionModel} and spatial-reference ID, and the default CoordinateSequence
+   * implementation.
+   *
+   * @param precisionModel the PrecisionModel to use
+   * @param SRID the SRID to use
+   */
+  public GeometryFactory(PrecisionModel precisionModel, int SRID) {
+    this(precisionModel, SRID, getDefaultCoordinateSequenceFactory());
+  }
+
+  /**
+   * Constructs a GeometryFactory that generates Geometries having a floating
+   * PrecisionModel and a spatial-reference ID of 0.
+   */
+  public GeometryFactory() {
+    this(new PrecisionModel(), 0);
+  }
+
+  private static CoordinateSequenceFactory getDefaultCoordinateSequenceFactory()
+  {
+    return CoordinateArraySequenceFactory.instance();
+  }
+
+  /**
+   *  Converts the <code>List</code> to an array.
+   *
+   *@param  points  the <code>List</code> of Points to convert
+   *@return         the <code>List</code> in array format
+   */
+  public static Point[] toPointArray(Collection points) {
+    Point[] pointArray = new Point[points.size()];
+    return (Point[]) points.toArray(pointArray);
+  }
+
+  /**
+   *  Converts the <code>List</code> to an array.
+   *
+   *@param  geometries  the list of <code>Geometry's</code> to convert
+   *@return            the <code>List</code> in array format
+   */
+  public static Geometry[] toGeometryArray(Collection geometries) {
+    if (geometries == null) return null;
+    Geometry[] geometryArray = new Geometry[geometries.size()];
+    return (Geometry[]) geometries.toArray(geometryArray);
+  }
+
+  /**
+   *  Converts the <code>List</code> to an array.
+   *
+   *@param  linearRings  the <code>List</code> of LinearRings to convert
+   *@return              the <code>List</code> in array format
+   */
+  public static LinearRing[] toLinearRingArray(Collection linearRings) {
+    LinearRing[] linearRingArray = new LinearRing[linearRings.size()];
+    return (LinearRing[]) linearRings.toArray(linearRingArray);
+  }
+
+  /**
+   *  Converts the <code>List</code> to an array.
+   *
+   *@param  lineStrings  the <code>List</code> of LineStrings to convert
+   *@return              the <code>List</code> in array format
+   */
+  public static LineString[] toLineStringArray(Collection lineStrings) {
+    LineString[] lineStringArray = new LineString[lineStrings.size()];
+    return (LineString[]) lineStrings.toArray(lineStringArray);
+  }
+
+  /**
+   *  Converts the <code>List</code> to an array.
+   *
+   *@param  polygons  the <code>List</code> of Polygons to convert
+   *@return           the <code>List</code> in array format
+   */
+  public static Polygon[] toPolygonArray(Collection polygons) {
+    Polygon[] polygonArray = new Polygon[polygons.size()];
+    return (Polygon[]) polygons.toArray(polygonArray);
+  }
+
+  /**
+   *  Converts the <code>List</code> to an array.
+   *
+   *@param  multiPolygons  the <code>List</code> of MultiPolygons to convert
+   *@return                the <code>List</code> in array format
+   */
+  public static MultiPolygon[] toMultiPolygonArray(Collection multiPolygons) {
+    MultiPolygon[] multiPolygonArray = new MultiPolygon[multiPolygons.size()];
+    return (MultiPolygon[]) multiPolygons.toArray(multiPolygonArray);
+  }
+
+  /**
+   *  Converts the <code>List</code> to an array.
+   *
+   *@param  multiLineStrings  the <code>List</code> of MultiLineStrings to convert
+   *@return                   the <code>List</code> in array format
+   */
+  public static MultiLineString[] toMultiLineStringArray(Collection multiLineStrings) {
+    MultiLineString[] multiLineStringArray = new MultiLineString[multiLineStrings.size()];
+    return (MultiLineString[]) multiLineStrings.toArray(multiLineStringArray);
+  }
+
+  /**
+   *  Converts the <code>List</code> to an array.
+   *
+   *@param  multiPoints  the <code>List</code> of MultiPoints to convert
+   *@return              the <code>List</code> in array format
+   */
+  public static MultiPoint[] toMultiPointArray(Collection multiPoints) {
+    MultiPoint[] multiPointArray = new MultiPoint[multiPoints.size()];
+    return (MultiPoint[]) multiPoints.toArray(multiPointArray);
+  }
+
+  /**
+   *  If the <code>Envelope</code> is a null <code>Envelope</code>, returns an
+   *  empty <code>Point</code>. If the <code>Envelope</code> is a point, returns
+   *  a non-empty <code>Point</code>. If the <code>Envelope</code> is a
+   *  rectangle, returns a <code>Polygon</code> whose points are (minx, miny),
+   *  (maxx, miny), (maxx, maxy), (minx, maxy), (minx, miny).
+   *
+   *@param  envelope        the <code>Envelope</code> to convert to a <code>Geometry</code>
+   *@param  precisionModel  the specification of the grid of allowable points
+   *      for the new <code>Geometry</code>
+   *@param  SRID            the ID of the Spatial Reference System used by the
+   *      <code>Envelope</code>
+   *@return                 an empty <code>Point</code> (for null <code>Envelope</code>
+   *      s), a <code>Point</code> (when min x = max x and min y = max y) or a
+   *      <code>Polygon</code> (in all other cases)
+   *@throws  <code>         TopologyException</code> if <code>coordinates</code>
+   *      is not a closed linestring, that is, if the first and last coordinates
+   *      are not equal
+   */
+  public Geometry toGeometry(Envelope envelope) {
+    if (envelope.isNull()) {
+      return createPoint((CoordinateSequence)null);
+    }
+    if (envelope.getMinX() == envelope.getMaxX() && envelope.getMinY() == envelope.getMaxY()) {
+      return createPoint(new Coordinate(envelope.getMinX(), envelope.getMinY()));
+    }
+    return createPolygon(createLinearRing(new Coordinate[]{
+        new Coordinate(envelope.getMinX(), envelope.getMinY()),
+        new Coordinate(envelope.getMaxX(), envelope.getMinY()),
+        new Coordinate(envelope.getMaxX(), envelope.getMaxY()),
+        new Coordinate(envelope.getMinX(), envelope.getMaxY()),
+        new Coordinate(envelope.getMinX(), envelope.getMinY())
+        }), null);
+  }
+
+  /**
+   * Returns the PrecisionModel that Geometries created by this factory
+   * will be associated with.
+   */
+  public PrecisionModel getPrecisionModel() {
+    return precisionModel;
+  }
+
+  /**
+   * Creates a Point using the given Coordinate; a null Coordinate will create
+   * an empty Geometry.
+   */
+  public Point createPoint(Coordinate coordinate) {
+    return createPoint(coordinate != null ? getCoordinateSequenceFactory().create(new Coordinate[]{coordinate}) : null);
+  }
+
+  /**
+   * Creates a Point using the given CoordinateSequence; a null or empty
+   * CoordinateSequence will create an empty Point.
+   */
+  public Point createPoint(CoordinateSequence coordinates) {
+  	return new Point(coordinates, this);
+  }
+
+  /**
+   * Creates a MultiLineString using the given LineStrings; a null or empty
+   * array will create an empty MultiLineString.
+   * @param lineStrings LineStrings, each of which may be empty but not null
+   */
+  public MultiLineString createMultiLineString(LineString[] lineStrings) {
+  	return new MultiLineString(lineStrings, this);
+  }
+
+  /**
+   * Creates a GeometryCollection using the given Geometries; a null or empty
+   * array will create an empty GeometryCollection.
+   * @param geometries Geometries, each of which may be empty but not null
+   */
+  public GeometryCollection createGeometryCollection(Geometry[] geometries) {
+  	return new GeometryCollection(geometries, this);
+  }
+
+
+
+  /**
+   * Creates a MultiPolygon using the given Polygons; a null or empty array
+   * will create an empty Polygon. The polygons must conform to the
+   * assertions specified in the <A
+   * HREF="http://www.opengis.org/techno/specs.htm">OpenGIS Simple Features
+   * Specification for SQL</A>.
+   *
+   * @param polygons
+   *            Polygons, each of which may be empty but not null
+   */
+  public MultiPolygon createMultiPolygon(Polygon[] polygons) {
+    return new MultiPolygon(polygons, this);
+  }
+
+  /**
+   * Creates a LinearRing using the given Coordinates; a null or empty array will
+   * create an empty LinearRing. The points must form a closed and simple
+   * linestring. Consecutive points must not be equal.
+   * @param coordinates an array without null elements, or an empty array, or null
+   */
+  public LinearRing createLinearRing(Coordinate[] coordinates) {
+    return createLinearRing(coordinates != null ? getCoordinateSequenceFactory().create(coordinates) : null);
+  }
+
+  /**
+   * Creates a LinearRing using the given CoordinateSequence; a null or empty CoordinateSequence will
+   * create an empty LinearRing. The points must form a closed and simple
+   * linestring. Consecutive points must not be equal.
+   * @param coordinates a CoordinateSequence possibly empty, or null
+   */
+  public LinearRing createLinearRing(CoordinateSequence coordinates) {
+    return new LinearRing(coordinates, this);
+  }
+
+  /**
+   * Creates a MultiPoint using the given Points; a null or empty array will
+   * create an empty MultiPoint.
+   * @param coordinates an array without null elements, or an empty array, or null
+   */
+  public MultiPoint createMultiPoint(Point[] point) {
+  	return new MultiPoint(point, this);
+  }
+
+  /**
+   * Creates a MultiPoint using the given Coordinates; a null or empty array
+   * will create an empty MultiPoint.
+   *
+   * @param coordinates
+   *            an array without null elements, or an empty array, or null
+   */
+
+  public MultiPoint createMultiPoint(Coordinate[] coordinates) {
+      return createMultiPoint(coordinates != null ? getCoordinateSequenceFactory().create(coordinates) : null);
+  }
+
+  /**
+   * Creates a MultiPoint using the given CoordinateSequence; a null or empty CoordinateSequence will
+   * create an empty MultiPoint.
+   * @param coordinates a CoordinateSequence possibly empty, or null
+   */
+  public MultiPoint createMultiPoint(CoordinateSequence coordinates) {
+    if (coordinates == null) {
+      coordinates = getCoordinateSequenceFactory().create(new Coordinate[]{});
+    }
+    ArrayList points = new ArrayList();
+    for (int i = 0; i < coordinates.size(); i++) {
+      points.add(createPoint(coordinates.getCoordinate(i)));
+    }
+    return createMultiPoint((Point[]) points.toArray(new Point[]{}));
+  }
+
+
+
+  /**
+   * Constructs a <code>Polygon</code> with the given exterior boundary and
+   * interior boundaries.
+   *
+   * @param shell
+   *            the outer boundary of the new <code>Polygon</code>, or
+   *            <code>null</code> or an empty <code>LinearRing</code> if
+   *            the empty geometry is to be created.
+   * @param holes
+   *            the inner boundaries of the new <code>Polygon</code>, or
+   *            <code>null</code> or empty <code>LinearRing</code> s if
+   *            the empty geometry is to be created.
+   */
+  public Polygon createPolygon(LinearRing shell, LinearRing[] holes) {
+    return new Polygon(shell, holes, this);
+  }
+
+  /**
+   *  Build an appropriate <code>Geometry</code>, <code>MultiGeometry</code>, or
+   *  <code>GeometryCollection</code> to contain the <code>Geometry</code>s in
+   *  it.
+   * For example:<br>
+   *
+   *  <ul>
+   *    <li> If <code>geomList</code> contains a single <code>Polygon</code>,
+   *    the <code>Polygon</code> is returned.
+   *    <li> If <code>geomList</code> contains several <code>Polygon</code>s, a
+   *    <code>MultiPolygon</code> is returned.
+   *    <li> If <code>geomList</code> contains some <code>Polygon</code>s and
+   *    some <code>LineString</code>s, a <code>GeometryCollection</code> is
+   *    returned.
+   *    <li> If <code>geomList</code> is empty, an empty <code>GeometryCollection</code>
+   *    is returned
+   *  </ul>
+   *
+   * Note that this method does not "flatten" Geometries in the input, and hence if
+   * any MultiGeometries are contained in the input a GeometryCollection containing
+   * them will be returned.
+   *
+   *@param  geomList  the <code>Geometry</code>s to combine
+   *@return           a <code>Geometry</code> of the "smallest", "most
+   *      type-specific" class that can contain the elements of <code>geomList</code>
+   *      .
+   */
+  public Geometry buildGeometry(Collection geomList) {
+    Class geomClass = null;
+    boolean isHeterogeneous = false;
+    for (Iterator i = geomList.iterator(); i.hasNext(); ) {
+      Geometry geom = (Geometry) i.next();
+      Class partClass = geom.getClass();
+      if (geomClass == null) {
+        geomClass = partClass;
+      }
+      if (partClass != geomClass) {
+        isHeterogeneous = true;
+      }
+    }
+    // for the empty geometry, return an empty GeometryCollection
+    if (geomClass == null) {
+      return createGeometryCollection(null);
+    }
+    if (isHeterogeneous) {
+      return createGeometryCollection(toGeometryArray(geomList));
+    }
+    // at this point we know the collection is hetereogenous.
+    // Determine the type of the result from the first Geometry in the list
+    // this should always return a geometry, since otherwise an empty collection would have already been returned
+    Geometry geom0 = (Geometry) geomList.iterator().next();
+    boolean isCollection = geomList.size() > 1;
+    if (isCollection) {
+      if (geom0 instanceof Polygon) {
+        return createMultiPolygon(toPolygonArray(geomList));
+      }
+      else if (geom0 instanceof LineString) {
+        return createMultiLineString(toLineStringArray(geomList));
+      }
+      else if (geom0 instanceof Point) {
+        return createMultiPoint(toPointArray(geomList));
+      }
+      Assert.shouldNeverReachHere();
+    }
+    return geom0;
+  }
+
+  /**
+   * Creates a LineString using the given Coordinates; a null or empty array will
+   * create an empty LineString. Consecutive points must not be equal.
+   * @param coordinates an array without null elements, or an empty array, or null
+   */
+  public LineString createLineString(Coordinate[] coordinates) {
+    return createLineString(coordinates != null ? getCoordinateSequenceFactory().create(coordinates) : null);
+  }
+  /**
+   * Creates a LineString using the given CoordinateSequence; a null or empty CoordinateSequence will
+   * create an empty LineString. Consecutive points must not be equal.
+   * @param coordinates a CoordinateSequence possibly empty, or null
+   */
+  public LineString createLineString(CoordinateSequence coordinates) {
+	return new LineString(coordinates, this);
+  }
+
+  /**
+   * @return a clone of g based on a CoordinateSequence created by this
+   * GeometryFactory's CoordinateSequenceFactory
+   */
+  public Geometry createGeometry(Geometry g)
+  {
+    // could this be cached to make this more efficient? Or maybe it isn't enough overhead to bother
+    GeometryEditor editor = new GeometryEditor(this);
+    return editor.edit(g, new GeometryEditor.CoordinateOperation() {
+      public Coordinate[] edit(Coordinate[] coordinates, Geometry geometry) {
+                  return coordinates;
+          }
+    });
+  }
+
+
+    public int getSRID() {
+		return SRID;
+	}
+
+    private int SRID;
+
+    public CoordinateSequenceFactory getCoordinateSequenceFactory() {
+        return coordinateSequenceFactory;
+    }
+
+}
+

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/GeometryFilter.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/GeometryFilter.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/GeometryFilter.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,58 @@
+
+
+/*
+ * 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;
+
+
+/**
+ *  <code>GeometryCollection</code> classes support the concept of
+ *  applying a <code>GeometryFilter</code> to the <code>Geometry</code>.
+ *  The filter is applied to every element <code>Geometry</code>.
+ *  A <code>GeometryFilter</code> can either record information about the <code>Geometry</code>
+ *  or change the <code>Geometry</code> in some way.
+ *  <code>GeometryFilter</code>
+ *  is an example of the Gang-of-Four Visitor pattern.
+ *
+ *@version 1.6
+ */
+public interface GeometryFilter {
+
+  /**
+   *  Performs an operation with or on <code>geom</code>.
+   *
+   *@param  geom  a <code>Geometry</code> to which the filter is applied.
+   */
+  public void filter(Geometry geom);
+}
+

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/IntersectionMatrix.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/IntersectionMatrix.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/IntersectionMatrix.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,508 @@
+
+
+/*
+ * 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;
+
+/**
+ *  A Dimensionally Extended Nine-Intersection Model (DE-9IM) matrix. This class
+ *  can used to represent both computed DE-9IM's (like 212FF1FF2) as well as
+ *  patterns for matching them (like T*T******). <P>
+ *
+ *  Methods are provided to:
+ *  <UL>
+ *    <LI> set and query the elements of the matrix in a convenient fashion
+ *    <LI> convert to and from the standard string representation (specified in
+ *    SFS Section 2.1.13.2).
+ *    <LI> test to see if a matrix matches a given pattern string.
+ *  </UL>
+ *  <P>
+ *
+ *  For a description of the DE-9IM, see the <A
+ *  HREF="http://www.opengis.org/techno/specs.htm">OpenGIS Simple Features
+ *  Specification for SQL</A> .
+ *
+ *@version 1.6
+ */
+public class IntersectionMatrix implements Cloneable {
+  /**
+   *  Internal representation of this <code>IntersectionMatrix</code>.
+   */
+  private int[][] matrix;
+
+  /**
+   *  Creates an <code>IntersectionMatrix</code> with <code>FALSE</code>
+   *  dimension values.
+   */
+  public IntersectionMatrix() {
+    matrix = new int[3][3];
+    setAll(Dimension.FALSE);
+  }
+
+  /**
+   *  Creates an <code>IntersectionMatrix</code> with the given dimension
+   *  symbols.
+   *
+   *@param  elements  a String of nine dimension symbols in row major order
+   */
+  public IntersectionMatrix(String elements) {
+    this();
+    set(elements);
+  }
+
+  /**
+   *  Creates an <code>IntersectionMatrix</code> with the same elements as
+   *  <code>other</code>.
+   *
+   *@param  other  an <code>IntersectionMatrix</code> to copy
+   */
+  public IntersectionMatrix(IntersectionMatrix other) {
+    this();
+    matrix[Location.INTERIOR][Location.INTERIOR] = other.matrix[Location.INTERIOR][Location.INTERIOR];
+    matrix[Location.INTERIOR][Location.BOUNDARY] = other.matrix[Location.INTERIOR][Location.BOUNDARY];
+    matrix[Location.INTERIOR][Location.EXTERIOR] = other.matrix[Location.INTERIOR][Location.EXTERIOR];
+    matrix[Location.BOUNDARY][Location.INTERIOR] = other.matrix[Location.BOUNDARY][Location.INTERIOR];
+    matrix[Location.BOUNDARY][Location.BOUNDARY] = other.matrix[Location.BOUNDARY][Location.BOUNDARY];
+    matrix[Location.BOUNDARY][Location.EXTERIOR] = other.matrix[Location.BOUNDARY][Location.EXTERIOR];
+    matrix[Location.EXTERIOR][Location.INTERIOR] = other.matrix[Location.EXTERIOR][Location.INTERIOR];
+    matrix[Location.EXTERIOR][Location.BOUNDARY] = other.matrix[Location.EXTERIOR][Location.BOUNDARY];
+    matrix[Location.EXTERIOR][Location.EXTERIOR] = other.matrix[Location.EXTERIOR][Location.EXTERIOR];
+  }
+
+  /**
+   * Adds one matrix to another.
+   * Addition is defined by taking the maximum dimension value of each position
+   * in the summand matrices.
+   *
+   * @param im the matrix to add
+   */
+  public void add(IntersectionMatrix im)
+  {
+    for (int i = 0; i < 3; i++) {
+      for (int j = 0; j < 3; j++) {
+        setAtLeast(i, j, im.get(i, j));
+      }
+    }
+  }
+
+  /**
+   *  Returns true if the dimension value satisfies the dimension symbol.
+   *
+   *@param  actualDimensionValue     a number that can be stored in the <code>IntersectionMatrix</code>
+   *      . Possible values are <code>{TRUE, FALSE, DONTCARE, 0, 1, 2}</code>.
+   *@param  requiredDimensionSymbol  a character used in the string
+   *      representation of an <code>IntersectionMatrix</code>. Possible values
+   *      are <code>{T, F, * , 0, 1, 2}</code>.
+   *@return                          true if the dimension symbol encompasses
+   *      the dimension value
+   */
+  public static boolean matches(int actualDimensionValue, char requiredDimensionSymbol) {
+    if (requiredDimensionSymbol == '*') {
+      return true;
+    }
+    if (requiredDimensionSymbol == 'T' && (actualDimensionValue >= 0 || actualDimensionValue
+         == Dimension.TRUE)) {
+      return true;
+    }
+    if (requiredDimensionSymbol == 'F' && actualDimensionValue == Dimension.FALSE) {
+      return true;
+    }
+    if (requiredDimensionSymbol == '0' && actualDimensionValue == Dimension.P) {
+      return true;
+    }
+    if (requiredDimensionSymbol == '1' && actualDimensionValue == Dimension.L) {
+      return true;
+    }
+    if (requiredDimensionSymbol == '2' && actualDimensionValue == Dimension.A) {
+      return true;
+    }
+    return false;
+  }
+
+  /**
+   *  Returns true if each of the actual dimension symbols satisfies the
+   *  corresponding required dimension symbol.
+   *
+   *@param  actualDimensionSymbols    nine dimension symbols to validate.
+   *      Possible values are <code>{T, F, * , 0, 1, 2}</code>.
+   *@param  requiredDimensionSymbols  nine dimension symbols to validate
+   *      against. Possible values are <code>{T, F, * , 0, 1, 2}</code>.
+   *@return                           true if each of the required dimension
+   *      symbols encompass the corresponding actual dimension symbol
+   */
+  public static boolean matches(String actualDimensionSymbols, String requiredDimensionSymbols) {
+    IntersectionMatrix m = new IntersectionMatrix(actualDimensionSymbols);
+    return m.matches(requiredDimensionSymbols);
+  }
+
+  /**
+   *  Changes the value of one of this <code>IntersectionMatrix</code>s
+   *  elements.
+   *
+   *@param  row             the row of this <code>IntersectionMatrix</code>,
+   *      indicating the interior, boundary or exterior of the first <code>Geometry</code>
+   *@param  column          the column of this <code>IntersectionMatrix</code>,
+   *      indicating the interior, boundary or exterior of the second <code>Geometry</code>
+   *@param  dimensionValue  the new value of the element
+   */
+  public void set(int row, int column, int dimensionValue) {
+    matrix[row][column] = dimensionValue;
+  }
+
+  /**
+   *  Changes the elements of this <code>IntersectionMatrix</code> to the
+   *  dimension symbols in <code>dimensionSymbols</code>.
+   *
+   *@param  dimensionSymbols  nine dimension symbols to which to set this <code>IntersectionMatrix</code>
+   *      s elements. Possible values are <code>{T, F, * , 0, 1, 2}</code>
+   */
+  public void set(String dimensionSymbols) {
+    for (int i = 0; i < dimensionSymbols.length(); i++) {
+      int row = i / 3;
+      int col = i % 3;
+      matrix[row][col] = Dimension.toDimensionValue(dimensionSymbols.charAt(i));
+    }
+  }
+
+  /**
+   *  Changes the specified element to <code>minimumDimensionValue</code> if the
+   *  element is less.
+   *
+   *@param  row                    the row of this <code>IntersectionMatrix</code>
+   *      , indicating the interior, boundary or exterior of the first <code>Geometry</code>
+   *@param  column                 the column of this <code>IntersectionMatrix</code>
+   *      , indicating the interior, boundary or exterior of the second <code>Geometry</code>
+   *@param  minimumDimensionValue  the dimension value with which to compare the
+   *      element. The order of dimension values from least to greatest is
+   *      <code>{DONTCARE, TRUE, FALSE, 0, 1, 2}</code>.
+   */
+  public void setAtLeast(int row, int column, int minimumDimensionValue) {
+    if (matrix[row][column] < minimumDimensionValue) {
+      matrix[row][column] = minimumDimensionValue;
+    }
+  }
+
+  /**
+   *  If row >= 0 and column >= 0, changes the specified element to <code>minimumDimensionValue</code>
+   *  if the element is less. Does nothing if row <0 or column < 0.
+   *
+   *@param  row                    the row of this <code>IntersectionMatrix</code>
+   *      , indicating the interior, boundary or exterior of the first <code>Geometry</code>
+   *@param  column                 the column of this <code>IntersectionMatrix</code>
+   *      , indicating the interior, boundary or exterior of the second <code>Geometry</code>
+   *@param  minimumDimensionValue  the dimension value with which to compare the
+   *      element. The order of dimension values from least to greatest is
+   *      <code>{DONTCARE, TRUE, FALSE, 0, 1, 2}</code>.
+   */
+  public void setAtLeastIfValid(int row, int column, int minimumDimensionValue) {
+    if (row >= 0 && column >= 0) {
+      setAtLeast(row, column, minimumDimensionValue);
+    }
+  }
+
+  /**
+   *  For each element in this <code>IntersectionMatrix</code>, changes the
+   *  element to the corresponding minimum dimension symbol if the element is
+   *  less.
+   *
+   *@param  minimumDimensionSymbols  nine dimension symbols with which to
+   *      compare the elements of this <code>IntersectionMatrix</code>. The
+   *      order of dimension values from least to greatest is <code>{DONTCARE, TRUE, FALSE, 0, 1, 2}</code>
+   *      .
+   */
+  public void setAtLeast(String minimumDimensionSymbols) {
+    for (int i = 0; i < minimumDimensionSymbols.length(); i++) {
+      int row = i / 3;
+      int col = i % 3;
+      setAtLeast(row, col, Dimension.toDimensionValue(minimumDimensionSymbols.charAt(i)));
+    }
+  }
+
+  /**
+   *  Changes the elements of this <code>IntersectionMatrix</code> to <code>dimensionValue</code>
+   *  .
+   *
+   *@param  dimensionValue  the dimension value to which to set this <code>IntersectionMatrix</code>
+   *      s elements. Possible values <code>{TRUE, FALSE, DONTCARE, 0, 1, 2}</code>
+   *      .
+   */
+  public void setAll(int dimensionValue) {
+    for (int ai = 0; ai < 3; ai++) {
+      for (int bi = 0; bi < 3; bi++) {
+        matrix[ai][bi] = dimensionValue;
+      }
+    }
+  }
+
+  /**
+   *  Returns the value of one of this <code>IntersectionMatrix</code>s
+   *  elements.
+   *
+   *@param  row     the row of this <code>IntersectionMatrix</code>, indicating
+   *      the interior, boundary or exterior of the first <code>Geometry</code>
+   *@param  column  the column of this <code>IntersectionMatrix</code>,
+   *      indicating the interior, boundary or exterior of the second <code>Geometry</code>
+   *@return         the dimension value at the given matrix position.
+   */
+  public int get(int row, int column) {
+    return matrix[row][column];
+  }
+
+  /**
+   *  Returns <code>true</code> if this <code>IntersectionMatrix</code> is
+   *  FF*FF****.
+   *
+   *@return    <code>true</code> if the two <code>Geometry</code>s related by
+   *      this <code>IntersectionMatrix</code> are disjoint
+   */
+  public boolean isDisjoint() {
+    return
+        matrix[Location.INTERIOR][Location.INTERIOR] == Dimension.FALSE &&
+        matrix[Location.INTERIOR][Location.BOUNDARY] == Dimension.FALSE &&
+        matrix[Location.BOUNDARY][Location.INTERIOR] == Dimension.FALSE &&
+        matrix[Location.BOUNDARY][Location.BOUNDARY] == Dimension.FALSE;
+  }
+
+  /**
+   *  Returns <code>true</code> if <code>isDisjoint</code> returns false.
+   *
+   *@return    <code>true</code> if the two <code>Geometry</code>s related by
+   *      this <code>IntersectionMatrix</code> intersect
+   */
+  public boolean isIntersects() {
+    return !isDisjoint();
+  }
+
+  /**
+   *  Returns <code>true</code> if this <code>IntersectionMatrix</code> is
+   *  FT*******, F**T***** or F***T****.
+   *
+   *@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> touch; Returns false
+   *      if both <code>Geometry</code>s are points.
+   */
+  public boolean isTouches(int dimensionOfGeometryA, int dimensionOfGeometryB) {
+    if (dimensionOfGeometryA > dimensionOfGeometryB) {
+      //no need to get transpose because pattern matrix is symmetrical
+      return isTouches(dimensionOfGeometryB, dimensionOfGeometryA);
+    }
+    if ((dimensionOfGeometryA == Dimension.A && dimensionOfGeometryB == Dimension.A) ||
+        (dimensionOfGeometryA == Dimension.L && dimensionOfGeometryB == Dimension.L) ||
+        (dimensionOfGeometryA == Dimension.L && dimensionOfGeometryB == Dimension.A) ||
+        (dimensionOfGeometryA == Dimension.P && dimensionOfGeometryB == Dimension.A) ||
+        (dimensionOfGeometryA == Dimension.P && dimensionOfGeometryB == Dimension.L)) {
+      return matrix[Location.INTERIOR][Location.INTERIOR] == Dimension.FALSE &&
+          (matches(matrix[Location.INTERIOR][Location.BOUNDARY], 'T')
+           || matches(matrix[Location.BOUNDARY][Location.INTERIOR], 'T')
+           || matches(matrix[Location.BOUNDARY][Location.BOUNDARY], 'T'));
+    }
+    return false;
+  }
+
+  /**
+   *  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>
+   *  .
+   *
+   *@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.
+   */
+  public boolean isCrosses(int dimensionOfGeometryA, int dimensionOfGeometryB) {
+    if ((dimensionOfGeometryA == Dimension.P && dimensionOfGeometryB == Dimension.L) ||
+        (dimensionOfGeometryA == Dimension.P && dimensionOfGeometryB == Dimension.A) ||
+        (dimensionOfGeometryA == Dimension.L && dimensionOfGeometryB == Dimension.A)) {
+      return matches(matrix[Location.INTERIOR][Location.INTERIOR], 'T') &&
+          matches(matrix[Location.INTERIOR][Location.EXTERIOR], 'T');
+    }
+    if ((dimensionOfGeometryA == Dimension.L && dimensionOfGeometryB == Dimension.P) ||
+        (dimensionOfGeometryA == Dimension.A && dimensionOfGeometryB == Dimension.P) ||
+        (dimensionOfGeometryA == Dimension.A && dimensionOfGeometryB == Dimension.L)) {
+      return matches(matrix[Location.INTERIOR][Location.INTERIOR], 'T') &&
+          matches(matrix[Location.EXTERIOR][Location.INTERIOR], 'T');
+    }
+    if (dimensionOfGeometryA == Dimension.L && dimensionOfGeometryB == Dimension.L) {
+      return matrix[Location.INTERIOR][Location.INTERIOR] == 0;
+    }
+    return false;
+  }
+
+  /**
+   *  Returns <code>true</code> if this <code>IntersectionMatrix</code> is
+   *  T*F**F***.
+   *
+   *@return    <code>true</code> if the first <code>Geometry</code> is within
+   *      the second
+   */
+  public boolean isWithin() {
+    return matches(matrix[Location.INTERIOR][Location.INTERIOR], 'T') &&
+        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*****FF*.
+   *
+   *@return    <code>true</code> if the first <code>Geometry</code> contains the
+   *      second
+   */
+  public boolean isContains() {
+    return matches(matrix[Location.INTERIOR][Location.INTERIOR], 'T') &&
+        matrix[Location.EXTERIOR][Location.INTERIOR] == Dimension.FALSE &&
+        matrix[Location.EXTERIOR][Location.BOUNDARY] == 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>
+   *@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> are equal; the
+   *      <code>Geometry</code>s must have the same dimension for this function
+   *      to return <code>true</code>
+   */
+  public boolean isEquals(int dimensionOfGeometryA, int dimensionOfGeometryB) {
+    if (dimensionOfGeometryA != dimensionOfGeometryB) {
+      return false;
+    }
+    return matches(matrix[Location.INTERIOR][Location.INTERIOR], 'T') &&
+        matrix[Location.EXTERIOR][Location.INTERIOR] == Dimension.FALSE &&
+        matrix[Location.INTERIOR][Location.EXTERIOR] == Dimension.FALSE &&
+        matrix[Location.EXTERIOR][Location.BOUNDARY] == Dimension.FALSE &&
+        matrix[Location.BOUNDARY][Location.EXTERIOR] == Dimension.FALSE;
+  }
+
+  /**
+   *  Returns <code>true</code> if this <code>IntersectionMatrix</code> is
+   *  <UL>
+   *    <LI> T*T***T** (for two points or two surfaces)
+   *    <LI> 1*T***T** (for two curves)
+   *  </UL>
+   *  .
+   *
+   *@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> 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 isOverlaps(int dimensionOfGeometryA, int dimensionOfGeometryB) {
+    if ((dimensionOfGeometryA == Dimension.P && dimensionOfGeometryB == Dimension.P) ||
+        (dimensionOfGeometryA == Dimension.A && dimensionOfGeometryB == Dimension.A)) {
+      return matches(matrix[Location.INTERIOR][Location.INTERIOR], 'T') &&
+          matches(matrix[Location.INTERIOR][Location.EXTERIOR], 'T') && matches(matrix[Location.EXTERIOR][Location.INTERIOR],
+          'T');
+    }
+    if (dimensionOfGeometryA == Dimension.L && dimensionOfGeometryB == Dimension.L) {
+      return matrix[Location.INTERIOR][Location.INTERIOR] == 1 &&
+          matches(matrix[Location.INTERIOR][Location.EXTERIOR], 'T') &&
+          matches(matrix[Location.EXTERIOR][Location.INTERIOR], 'T');
+    }
+    return false;
+  }
+
+  /**
+   *  Returns whether the elements of this <code>IntersectionMatrix</code>
+   *  satisfies the required dimension symbols.
+   *
+   *@param  requiredDimensionSymbols  nine dimension symbols with which to
+   *      compare the elements of this <code>IntersectionMatrix</code>. Possible
+   *      values are <code>{T, F, * , 0, 1, 2}</code>.
+   *@return                           <code>true</code> if this <code>IntersectionMatrix</code>
+   *      matches the required dimension symbols
+   */
+  public boolean matches(String requiredDimensionSymbols) {
+    if (requiredDimensionSymbols.length() != 9) {
+      throw new IllegalArgumentException("Should be length 9: " + requiredDimensionSymbols);
+    }
+    for (int ai = 0; ai < 3; ai++) {
+      for (int bi = 0; bi < 3; bi++) {
+        if (!matches(matrix[ai][bi], requiredDimensionSymbols.charAt(3 * ai +
+            bi))) {
+          return false;
+        }
+      }
+    }
+    return true;
+  }
+
+  /**
+   *  Transposes this IntersectionMatrix.
+   *
+   *@return    this <code>IntersectionMatrix</code> as a convenience
+   */
+  public IntersectionMatrix transpose() {
+    int temp = matrix[1][0];
+    matrix[1][0] = matrix[0][1];
+    matrix[0][1] = temp;
+    temp = matrix[2][0];
+    matrix[2][0] = matrix[0][2];
+    matrix[0][2] = temp;
+    temp = matrix[2][1];
+    matrix[2][1] = matrix[1][2];
+    matrix[1][2] = temp;
+    return this;
+  }
+
+  /**
+   *  Returns a nine-character <code>String</code> representation of this <code>IntersectionMatrix</code>
+   *  .
+   *
+   *@return    the nine dimension symbols of this <code>IntersectionMatrix</code>
+   *      in row-major order.
+   */
+  public String toString() {
+    StringBuffer buf = new StringBuffer("123456789");
+    for (int ai = 0; ai < 3; ai++) {
+      for (int bi = 0; bi < 3; bi++) {
+        buf.setCharAt(3 * ai + bi, Dimension.toDimensionSymbol(matrix[ai][bi]));
+      }
+    }
+    return buf.toString();
+  }
+}
+

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/LineSegment.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/LineSegment.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/LineSegment.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,423 @@
+
+
+/*
+ * 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 com.vividsolutions.jts.algorithm.*;
+
+/**
+ * Represents a line segment defined by two {@link Coordinate}s.
+ * Provides methods to compute various geometric properties
+ * and relationships of line segments.
+ * <p>
+ * This class is designed to be easily mutable (to the extent of
+ * having its contained points public).
+ * This supports a common pattern of reusing a single LineSegment
+ * object as a way of computing segment properties on the
+ * segments defined by arrays or lists of {@link Coordinate}s.
+ *
+ *@version 1.6
+ */
+public class LineSegment
+  implements Comparable, Serializable
+{
+  private static final long serialVersionUID = 3252005833466256227L;
+
+  public Coordinate p0, p1;
+
+  public LineSegment(Coordinate p0, Coordinate p1) {
+    this.p0 = p0;
+    this.p1 = p1;
+  }
+
+  public LineSegment(LineSegment ls) {
+    this(ls.p0, ls.p1);
+  }
+
+  public LineSegment() {
+    this(new Coordinate(), new Coordinate());
+  }
+
+  public Coordinate getCoordinate(int i)
+  {
+    if (i == 0) return p0;
+    return p1;
+  }
+
+  public void setCoordinates(LineSegment ls)
+  {
+    setCoordinates(ls.p0, ls.p1);
+  }
+
+  public void setCoordinates(Coordinate p0, Coordinate p1)
+  {
+    this.p0.x = p0.x;
+    this.p0.y = p0.y;
+    this.p1.x = p1.x;
+    this.p1.y = p1.y;
+  }
+
+  /**
+   * Computes the length of the line segment.
+   * @return the length of the line segment
+   */
+  public double getLength()
+  {
+    return p0.distance(p1);
+  }
+
+  /**
+   * Tests whether the segment is horizontal.
+   *
+   * @return <code>true</code> if the segment is horizontal
+   */
+  public boolean isHorizontal() { return p0.y == p1.y; }
+
+  /**
+   * Tests whether the segment is vertical.
+   *
+   * @return <code>true</code> if the segment is vertical
+   */
+  public boolean isVertical() { return p0.x == p1.x; }
+
+  /**
+   * Determines the orientation of a LineSegment relative to this segment.
+   * The concept of orientation is specified as follows:
+   * Given two line segments A and L,
+   * <ul
+   * <li>A is to the left of a segment L if A lies wholly in the
+   * closed half-plane lying to the left of L
+   * <li>A is to the right of a segment L if A lies wholly in the
+   * closed half-plane lying to the right of L
+   * <li>otherwise, A has indeterminate orientation relative to L. This
+   * happens if A is collinear with L or if A crosses the line determined by L.
+   * </ul>
+   *
+   * @param seg the LineSegment to compare
+   *
+   * @return 1 if <code>seg</code> is to the left of this segment
+   * @return -1 if <code>seg</code> is to the right of this segment
+   * @return 0 if <code>seg</code> has indeterminate orientation relative to this segment
+   */
+  public int orientationIndex(LineSegment seg)
+  {
+    int orient0 = CGAlgorithms.orientationIndex(p0, p1, seg.p0);
+    int orient1 = CGAlgorithms.orientationIndex(p0, p1, seg.p1);
+    // this handles the case where the points are L or collinear
+    if (orient0 >= 0 && orient1 >= 0)
+      return Math.max(orient0, orient1);
+    // this handles the case where the points are R or collinear
+    if (orient0 <= 0 && orient1 <= 0)
+      return Math.max(orient0, orient1);
+    // points lie on opposite sides ==> indeterminate orientation
+    return 0;
+  }
+  /**
+   * Reverses the direction of the line segment.
+   */
+  public void reverse()
+  {
+    Coordinate temp = p0;
+    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.
+   */
+  public void normalize()
+  {
+    if (p1.compareTo(p0) < 0) reverse();
+  }
+
+  /**
+   * @return the angle this segment makes with the x-axis (in radians)
+   */
+  public double angle()
+  {
+    return Math.atan2(p1.y - p0.y, p1.x - p0.x);
+  }
+
+  /**
+   * Computes the distance between this line segment and another one.
+   */
+  public double distance(LineSegment ls)
+  {
+    return CGAlgorithms.distanceLineLine(p0, p1, ls.p0, ls.p1);
+  }
+
+  /**
+   * Computes the distance between this line segment and a point.
+   */
+  public double distance(Coordinate p)
+  {
+    return CGAlgorithms.distancePointLine(p, p0, p1);
+  }
+
+  /**
+   * Computes the perpendicular distance between the (infinite) line defined
+   * by this line segment and a point.
+   */
+  public double distancePerpendicular(Coordinate p)
+  {
+    return CGAlgorithms.distancePointLinePerpendicular(p, p0, p1);
+  }
+
+  /**
+   * Compute the projection factor for the projection of the point p
+   * onto this LineSegment.  The projection factor is the constant k
+   * by which the vector for this segment must be multiplied to
+   * equal the vector for the projection of p.
+   */
+  public double projectionFactor(Coordinate p)
+  {
+    if (p.equals(p0)) return 0.0;
+    if (p.equals(p1)) return 1.0;
+    // Otherwise, use comp.graphics.algorithms Frequently Asked Questions method
+    /*     	      AC dot AB
+                   r = ---------
+                         ||AB||^2
+                r has the following meaning:
+                r=0 P = A
+                r=1 P = B
+                r<0 P is on the backward extension of AB
+                r>1 P is on the forward extension of AB
+                0<r<1 P is interior to AB
+        */
+    double dx = p1.x - p0.x;
+    double dy = p1.y - p0.y;
+    double len2 = dx * dx + dy * dy;
+    double r = ( (p.x - p0.x) * dx + (p.y - p0.y) * dy )
+              / len2;
+    return r;
+  }
+
+  /**
+   * Compute the projection of a point onto the line determined
+   * by this line segment.
+   * <p>
+   * Note that the projected point
+   * may lie outside the line segment.  If this is the case,
+   * the projection factor will lie outside the range [0.0, 1.0].
+   */
+  public Coordinate project(Coordinate p)
+  {
+    if (p.equals(p0) || p.equals(p1)) return new Coordinate(p);
+
+    double r = projectionFactor(p);
+    Coordinate coord = new Coordinate();
+    coord.x = p0.x + r * (p1.x - p0.x);
+    coord.y = p0.y + r * (p1.y - p0.y);
+    return coord;
+  }
+  /**
+   * Project a line segment onto this line segment and return the resulting
+   * line segment.  The returned line segment will be a subset of
+   * the target line line segment.  This subset may be null, if
+   * the segments are oriented in such a way that there is no projection.
+   * <p>
+   * Note that the returned line may have zero length (i.e. the same endpoints).
+   * This can happen for instance if the lines are perpendicular to one another.
+   *
+   * @param seg the line segment to project
+   * @return the projected line segment, or <code>null</code> if there is no overlap
+   */
+  public LineSegment project(LineSegment seg)
+  {
+    double pf0 = projectionFactor(seg.p0);
+    double pf1 = projectionFactor(seg.p1);
+    // check if segment projects at all
+    if (pf0 >= 1.0 && pf1 >= 1.0) return null;
+    if (pf0 <= 0.0 && pf1 <= 0.0) return null;
+
+    Coordinate newp0 = project(seg.p0);
+    if (pf0 < 0.0) newp0 = p0;
+    if (pf0 > 1.0) newp0 = p1;
+
+    Coordinate newp1 = project(seg.p1);
+    if (pf1 < 0.0) newp1 = p0;
+    if (pf1 > 1.0) newp1 = p1;
+
+    return new LineSegment(newp0, newp1);
+  }
+  /**
+   * Computes the closest point on this line segment to another point.
+   * @param p the point to find the closest point to
+   * @return a Coordinate which is the closest point on the line segment to the point p
+   */
+  public Coordinate closestPoint(Coordinate p)
+  {
+    double factor = projectionFactor(p);
+    if (factor > 0 && factor < 1) {
+      return project(p);
+    }
+    double dist0 = p0.distance(p);
+    double dist1 = p1.distance(p);
+    if (dist0 < dist1)
+      return p0;
+    return p1;
+  }
+  /**
+   * Computes the closest points on two line segments.
+   * @param p the point to find the closest point to
+   * @return a pair of Coordinates which are the closest points on the line segments
+   */
+  public Coordinate[] closestPoints(LineSegment line)
+  {
+    // test for intersection
+    Coordinate intPt = intersection(line);
+    if (intPt != null) {
+      return new Coordinate[] { intPt, intPt };
+    }
+
+    /**
+     *  if no intersection closest pair contains at least one endpoint.
+     * Test each endpoint in turn.
+     */
+    Coordinate[] closestPt = new Coordinate[2];
+    double minDistance = Double.MAX_VALUE;
+    double dist;
+
+    Coordinate close00 = closestPoint(line.p0);
+    minDistance = close00.distance(line.p0);
+    closestPt[0] = close00;
+    closestPt[1] = line.p0;
+
+    Coordinate close01 = closestPoint(line.p1);
+    dist = close01.distance(line.p1);
+    if (dist < minDistance) {
+      minDistance = dist;
+      closestPt[0] = close01;
+      closestPt[1] = line.p1;
+    }
+
+    Coordinate close10 = line.closestPoint(p0);
+    dist = close10.distance(p0);
+    if (dist < minDistance) {
+      minDistance = dist;
+      closestPt[0] = p0;
+      closestPt[1] = close10;
+    }
+
+    Coordinate close11 = line.closestPoint(p1);
+    dist = close11.distance(p1);
+    if (dist < minDistance) {
+      minDistance = dist;
+      closestPt[0] = p1;
+      closestPt[1] = close11;
+    }
+
+    return closestPt;
+  }
+
+  /**
+   * Computes an intersection point between two segments, if there is one.
+   * There may be 0, 1 or many intersection points between two segments.
+   * If there are 0, null is returned. If there is 1 or more, a single one
+   * is returned (chosen at the discretion of the algorithm).  If
+   * more information is required about the details of the intersection,
+   * the {@link RobustLineIntersector} class should be used.
+   *
+   * @param line
+   * @return an intersection point, or <code>null</code> if there is none
+   */
+  public Coordinate intersection(LineSegment line)
+  {
+    LineIntersector li = new RobustLineIntersector();
+    li.computeIntersection(p0, p1, line.p0, line.p1);
+    if (li.hasIntersection())
+      return li.getIntersection(0);
+    return null;
+  }
+
+  /**
+   *  Returns <code>true</code> if <code>other</code> has the same values for
+   *  its points.
+   *
+   *@param  other  a <code>LineSegment</code> with which to do the comparison.
+   *@return        <code>true</code> if <code>other</code> is a <code>LineSegment</code>
+   *      with the same values for the x and y ordinates.
+   */
+  public boolean equals(Object o) {
+    if (!(o instanceof LineSegment)) {
+      return false;
+    }
+    LineSegment other = (LineSegment) o;
+    return p0.equals(other.p0) && p1.equals(other.p1);
+  }
+
+
+  /**
+   *  Compares this object with the specified object for order.
+   *  Uses the standard lexicographic ordering for the points in the LineSegment.
+   *
+   *@param  o  the <code>LineSegment</code> with which this <code>LineSegment</code>
+   *      is being compared
+   *@return    a negative integer, zero, or a positive integer as this <code>LineSegment</code>
+   *      is less than, equal to, or greater than the specified <code>LineSegment</code>
+   */
+  public int compareTo(Object o) {
+    LineSegment other = (LineSegment) o;
+    int comp0 = p0.compareTo(other.p0);
+    if (comp0 != 0) return comp0;
+    return p1.compareTo(other.p1);
+  }
+
+  /**
+   *  Returns <code>true</code> if <code>other</code> is
+   *  topologically equal to this LineSegment (e.g. irrespective
+   *  of orientation).
+   *
+   *@param  other  a <code>LineSegment</code> with which to do the comparison.
+   *@return        <code>true</code> if <code>other</code> is a <code>LineSegment</code>
+   *      with the same values for the x and y ordinates.
+   */
+  public boolean equalsTopo(LineSegment other)
+  {
+    return
+      p0.equals(other.p0) && p1.equals(other.p1)
+      || p0.equals(other.p1) && p1.equals(other.p0);
+  }
+
+  public String toString()
+  {
+    return "LINESTRING( " +
+        p0.x + " " + p0.y
+        + ", " +
+        p1.x + " " + p1.y + ")";
+  }
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/LineString.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/LineString.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/LineString.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,319 @@
+
+
+/*
+ * 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 com.vividsolutions.jts.algorithm.CGAlgorithms;
+import com.vividsolutions.jts.operation.IsSimpleOp;
+
+/**
+ *  Basic implementation of <code>LineString</code>.
+ *
+ *@version 1.6
+ */
+public class LineString extends Geometry {
+  private static final long serialVersionUID = 3110669828065365560L;
+  /**
+   *  The points of this <code>LineString</code>.
+   */
+  private CoordinateSequence points;
+
+  /**
+   *  Constructs a <code>LineString</code> with the given points.
+   *
+   *@param  points          the points of the linestring, or <code>null</code>
+   *      to create the empty geometry. This array must not contain <code>null</code>
+   *      elements. Consecutive points may not be equal.
+   *@param  precisionModel  the specification of the grid of allowable points
+   *      for this <code>LineString</code>
+   *@param  SRID            the ID of the Spatial Reference System used by this
+   *      <code>LineString</code>
+   */
+  /** @deprecated Use GeometryFactory instead */
+  public LineString(Coordinate points[], PrecisionModel precisionModel, int SRID)
+  {
+    super(new GeometryFactory(precisionModel, SRID));
+    init(getFactory().getCoordinateSequenceFactory().create(points));
+  }
+
+  /**
+   *@param  points          the points of the linestring, or <code>null</code>
+   *      to create the empty geometry. Consecutive points may not be equal.
+   */
+  public LineString(CoordinateSequence points, GeometryFactory factory) {
+    super(factory);
+    init(points);
+  }
+
+  private void init(CoordinateSequence points)
+  {
+    if (points == null) {
+      points = getFactory().getCoordinateSequenceFactory().create(new Coordinate[]{});
+    }
+    if (points.size() == 1) {
+      throw new IllegalArgumentException("point array must contain 0 or >1 elements");
+    }
+    this.points = points;
+  }
+  public Coordinate[] getCoordinates() {
+    return points.toCoordinateArray();
+  }
+
+  public CoordinateSequence getCoordinateSequence() {
+      return points;
+  }
+
+  public Coordinate getCoordinateN(int n) {
+      return points.getCoordinate(n);
+  }
+
+  public Coordinate getCoordinate()
+  {
+    if (isEmpty()) return null;
+    return points.getCoordinate(0);
+  }
+
+  public int getDimension() {
+    return 1;
+  }
+
+  public int getBoundaryDimension() {
+    if (isClosed()) {
+      return Dimension.FALSE;
+    }
+    return 0;
+  }
+
+  public boolean isEmpty() {
+      return points.size() == 0;
+  }
+
+  public int getNumPoints() {
+      return points.size();
+  }
+
+  public Point getPointN(int n) {
+      return getFactory().createPoint(points.getCoordinate(n));
+  }
+
+  public Point getStartPoint() {
+    if (isEmpty()) {
+      return null;
+    }
+    return getPointN(0);
+  }
+
+  public Point getEndPoint() {
+    if (isEmpty()) {
+      return null;
+    }
+    return getPointN(getNumPoints() - 1);
+  }
+
+  public boolean isClosed() {
+    if (isEmpty()) {
+      return false;
+    }
+    return getCoordinateN(0).equals2D(getCoordinateN(getNumPoints() - 1));
+  }
+
+  public boolean isRing() {
+    return isClosed() && isSimple();
+  }
+
+  public String getGeometryType() {
+    return "LineString";
+  }
+
+  /**
+   *  Returns the length of this <code>LineString</code>
+   *
+   *@return the area of the polygon
+   */
+  public double getLength()
+  {
+   return CGAlgorithms.length(points);
+  }
+
+  public boolean isSimple()
+  {
+    return (new IsSimpleOp()).isSimple(this);
+  }
+
+  public Geometry getBoundary() {
+    if (isEmpty()) {
+      return getFactory().createGeometryCollection(null);
+    }
+    if (isClosed()) {
+      return getFactory().createMultiPoint((Coordinate[])null);
+    }
+    return getFactory().createMultiPoint(new Point[]{
+        getStartPoint(), getEndPoint()
+        });
+  }
+
+
+  /**
+   *  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>
+   *      's vertices
+   */
+  public boolean isCoordinate(Coordinate pt) {
+      for (int i = 0; i < points.size(); i++) {
+        if (points.getCoordinate(i).equals(pt)) {
+          return true;
+        }
+      }
+    return false;
+  }
+
+  protected Envelope computeEnvelopeInternal() {
+    if (isEmpty()) {
+      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) {
+    if (!isEquivalentClass(other)) {
+      return false;
+    }
+    LineString otherLineString = (LineString) other;
+    if (points.size() != otherLineString.points.size()) {
+      return false;
+    }
+    for (int i = 0; i < points.size(); i++) {
+      if (!equal(points.getCoordinate(i), otherLineString.points.getCoordinate(i), tolerance)) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  public void apply(CoordinateFilter filter) {
+      for (int i = 0; i < points.size(); i++) {
+        filter.filter(points.getCoordinate(i));
+      }
+  }
+
+  public void apply(GeometryFilter filter) {
+    filter.filter(this);
+  }
+
+  public void apply(GeometryComponentFilter filter) {
+    filter.filter(this);
+  }
+
+  public Object clone() {
+    LineString ls = (LineString) super.clone();
+    ls.points = (CoordinateSequence) points.clone();
+    return ls;
+  }
+
+  /**
+   * Normalizes a LineString.  A normalized linestring
+   * has the first point which is not equal to it's reflected point
+   * less than the reflected point.
+   */
+  public void normalize()
+  {
+      for (int i = 0; i < points.size() / 2; i++) {
+        int j = points.size() - 1 - i;
+        // skip equal points on both ends
+        if (!points.getCoordinate(i).equals(points.getCoordinate(j))) {
+          if (points.getCoordinate(i).compareTo(points.getCoordinate(j)) > 0) {
+            CoordinateArrays.reverse(getCoordinates());
+          }
+          return;
+        }
+      }
+  }
+
+  protected boolean isEquivalentClass(Geometry other) {
+    return other instanceof LineString;
+  }
+
+  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++;
+      }
+      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);
+    */
+  }
+
+}
+

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/LinearRing.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/LinearRing.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/LinearRing.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,112 @@
+
+
+/*
+ * 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;
+
+/**
+ *  Basic implementation of <code>LinearRing</code>.
+ * The first and last point in the coordinate sequence must be equal.
+ * Either orientation of the ring is allowed.
+ * A valid ring must not self-intersect.
+ *
+ *@version 1.6
+ */
+public class LinearRing extends LineString
+{
+  private static final long serialVersionUID = -4261142084085851829L;
+  /**
+   *  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.
+   *      This array must not contain <code>null</code> elements.
+   *
+   *@param  precisionModel  the specification of the grid of allowable points
+   *      for this <code>LinearRing</code>
+   *@param  SRID            the ID of the Spatial Reference System used by this
+   *      <code>LinearRing</code>
+   * @deprecated Use GeometryFactory instead
+   */
+  public LinearRing(Coordinate points[], PrecisionModel precisionModel,
+                    int SRID) {
+    this(points, new GeometryFactory(precisionModel, SRID));
+    validateConstruction();
+  }
+
+  /**
+   * This method is ONLY used to avoid deprecation warnings.
+   * @param points
+   * @param factory
+   */
+  private LinearRing(Coordinate points[], GeometryFactory factory) {
+    this(factory.getCoordinateSequenceFactory().create(points), factory);
+  }
+
+
+  /**
+   *  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.
+   *      This array must not contain <code>null</code> elements.
+   *
+   */
+  public LinearRing(CoordinateSequence points, GeometryFactory factory) {
+    super(points, factory);
+    validateConstruction();
+  }
+
+  private void validateConstruction() {
+	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() {
+    return true;
+  }
+
+  public String getGeometryType() {
+    return "LinearRing";
+  }
+
+  public boolean isClosed() {
+    return true;
+  }
+
+}
+

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/Location.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/Location.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/Location.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,93 @@
+
+
+/*
+ * 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;
+
+/**
+ *  Constants representing the location of a point relative to a geometry. They
+ *  can also be thought of as the row or column index of a DE-9IM matrix. For a
+ *  description of the DE-9IM, see the <A
+ *  HREF="http://www.opengis.org/techno/specs.htm">OpenGIS Simple Features
+ *  Specification for SQL</A> .
+ *
+ *@version 1.6
+ */
+public class Location {
+  /**
+   *  DE-9IM row index of the interior of the first geometry and column index of
+   *  the interior of the second geometry. Location value for the interior of a
+   *  geometry.
+   */
+  public final static int INTERIOR = 0;
+  /**
+   *  DE-9IM row index of the boundary of the first geometry and column index of
+   *  the boundary of the second geometry. Location value for the boundary of a
+   *  geometry.
+   */
+  public final static int BOUNDARY = 1;
+  /**
+   *  DE-9IM row index of the exterior of the first geometry and column index of
+   *  the exterior of the second geometry. Location value for the exterior of a
+   *  geometry.
+   */
+  public final static int EXTERIOR = 2;
+
+  /**
+   *  Used for uninitialized location values.
+   */
+  public final static int NULL = -1;
+
+  /**
+   *  Converts the location value to a location symbol, for example, <code>EXTERIOR => 'e'</code>
+   *  .
+   *
+   *@param  locationValue  either EXTERIOR, BOUNDARY, INTERIOR or NULL
+   *@return                either 'e', 'b', 'i' or '-'
+   */
+  public static char toLocationSymbol(int locationValue) {
+    switch (locationValue) {
+      case EXTERIOR:
+        return 'e';
+      case BOUNDARY:
+        return 'b';
+      case INTERIOR:
+        return 'i';
+      case NULL:
+        return '-';
+    }
+    throw new IllegalArgumentException("Unknown location value: " + locationValue);
+  }
+}
+
+

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/MultiLineString.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/MultiLineString.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/MultiLineString.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,127 @@
+
+
+/*
+ * 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 com.vividsolutions.jts.geomgraph.GeometryGraph;
+import com.vividsolutions.jts.operation.IsSimpleOp;
+
+
+/**
+ *  Basic implementation of <code>MultiLineString</code>.
+ *
+ *@version 1.6
+ */
+public class MultiLineString extends GeometryCollection {
+  private static final long serialVersionUID = 8166665132445433741L;
+  /**
+   *  Constructs a <code>MultiLineString</code>.
+   *
+   *@param  lineStrings     the <code>LineString</code>s for this <code>MultiLineString</code>
+   *      , or <code>null</code> or an empty array to create the empty geometry.
+   *      Elements may be empty <code>LineString</code>s, but not <code>null</code>
+   *      s.
+   *@param  precisionModel  the specification of the grid of allowable points
+   *      for this <code>MultiLineString</code>
+   *@param  SRID            the ID of the Spatial Reference System used by this
+   *      <code>MultiLineString</code>
+   * @deprecated Use GeometryFactory instead
+   */
+  public MultiLineString(LineString[] lineStrings, PrecisionModel precisionModel, int SRID) {
+    super(lineStrings, new GeometryFactory(precisionModel, SRID));
+  }
+
+
+
+  /**
+   * @param lineStrings
+   *            the <code>LineString</code>s for this <code>MultiLineString</code>,
+   *            or <code>null</code> or an empty array to create the empty
+   *            geometry. Elements may be empty <code>LineString</code>s,
+   *            but not <code>null</code>s.
+   */
+  public MultiLineString(LineString[] lineStrings, GeometryFactory factory) {
+    super(lineStrings, factory);
+  }
+
+  public int getDimension() {
+    return 1;
+  }
+
+  public int getBoundaryDimension() {
+    if (isClosed()) {
+      return Dimension.FALSE;
+    }
+    return 0;
+  }
+
+  public String getGeometryType() {
+    return "MultiLineString";
+  }
+
+  public boolean isClosed() {
+    if (isEmpty()) {
+      return false;
+    }
+    for (int i = 0; i < geometries.length; i++) {
+      if (!((LineString) geometries[i]).isClosed()) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  public boolean isSimple()
+  {
+    return (new IsSimpleOp()).isSimple(this);
+  }
+
+  public Geometry getBoundary() {
+    if (isEmpty()) {
+      return getFactory().createGeometryCollection(null);
+    }
+    GeometryGraph g = new GeometryGraph(0, this);
+    Coordinate[] pts = g.getBoundaryPoints();
+    return getFactory().createMultiPoint(pts);
+  }
+
+  public boolean equalsExact(Geometry other, double tolerance) {
+    if (!isEquivalentClass(other)) {
+      return false;
+    }
+    return super.equalsExact(other, tolerance);
+  }
+
+}
+

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/MultiPoint.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/MultiPoint.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/MultiPoint.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,118 @@
+
+
+/*
+ * 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 com.vividsolutions.jts.operation.IsSimpleOp;
+
+/**
+ *  Models a collection of <code>Point</code>s.
+ *
+ *@version 1.6
+ */
+public class MultiPoint
+  extends GeometryCollection
+{
+
+  private static final long serialVersionUID = -8048474874175355449L;
+
+  /**
+   *  Constructs a <code>MultiPoint</code>.
+   *
+   *@param  points          the <code>Point</code>s for this <code>MultiPoint</code>
+   *      , or <code>null</code> or an empty array to create the empty geometry.
+   *      Elements may be empty <code>Point</code>s, but not <code>null</code>s.
+   *@param  precisionModel  the specification of the grid of allowable points
+   *      for this <code>MultiPoint</code>
+   *@param  SRID            the ID of the Spatial Reference System used by this
+   *      <code>MultiPoint</code>
+   * @deprecated Use GeometryFactory instead
+   */
+  public MultiPoint(Point[] points, PrecisionModel precisionModel, int SRID) {
+    super(points, new GeometryFactory(precisionModel, SRID));
+  }
+
+  /**
+   *@param  points          the <code>Point</code>s for this <code>MultiPoint</code>
+   *      , or <code>null</code> or an empty array to create the empty geometry.
+   *      Elements may be empty <code>Point</code>s, but not <code>null</code>s.
+   */
+  public MultiPoint(Point[] points, GeometryFactory factory) {
+    super(points, factory);
+  }
+
+  public int getDimension() {
+    return 0;
+  }
+
+  public int getBoundaryDimension() {
+    return Dimension.FALSE;
+  }
+
+  public String getGeometryType() {
+    return "MultiPoint";
+  }
+
+  public Geometry getBoundary() {
+    return getFactory().createGeometryCollection(null);
+  }
+
+  public boolean isSimple() {
+    return (new IsSimpleOp()).isSimple(this);
+  }
+
+  public boolean isValid() {
+    return true;
+  }
+
+  public boolean equalsExact(Geometry other, double tolerance) {
+    if (!isEquivalentClass(other)) {
+      return false;
+    }
+    return super.equalsExact(other, tolerance);
+  }
+
+  /**
+   *  Returns the <code>Coordinate</code> at the given position.
+   *
+   *@param  n  the index of the <code>Coordinate</code> to retrieve, beginning
+   *      at 0
+   *@return    the <code>n</code>th <code>Coordinate</code>
+   */
+  protected Coordinate getCoordinate(int n) {
+    return ((Point) geometries[n]).getCoordinate();
+  }
+
+}
+

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/MultiPolygon.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/MultiPolygon.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/MultiPolygon.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,120 @@
+
+
+/*
+ * 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.ArrayList;
+
+/**
+ *  Basic implementation of <code>MultiPolygon</code>.
+ *
+ *@version 1.6
+ */
+public class MultiPolygon extends GeometryCollection {
+  private static final long serialVersionUID = -551033529766975875L;
+  /**
+   *  Constructs a <code>MultiPolygon</code>.
+   *
+   *@param  polygons        the <code>Polygon</code>s for this <code>MultiPolygon</code>
+   *      , or <code>null</code> or an empty array to create the empty geometry.
+   *      Elements may be empty <code>Polygon</code>s, but not <code>null</code>
+   *      s. The polygons must conform to the assertions specified in the <A
+   *      HREF="http://www.opengis.org/techno/specs.htm">OpenGIS Simple Features
+   *      Specification for SQL</A> .
+   *@param  precisionModel  the specification of the grid of allowable points
+   *      for this <code>MultiPolygon</code>
+   *@param  SRID            the ID of the Spatial Reference System used by this
+   *      <code>MultiPolygon</code>
+   * @deprecated Use GeometryFactory instead
+   */
+  public MultiPolygon(Polygon[] polygons, PrecisionModel precisionModel, int SRID) {
+    this(polygons, new GeometryFactory(precisionModel, SRID));
+  }
+
+
+  /**
+   * @param polygons
+   *            the <code>Polygon</code>s for this <code>MultiPolygon</code>,
+   *            or <code>null</code> or an empty array to create the empty
+   *            geometry. Elements may be empty <code>Polygon</code>s, but
+   *            not <code>null</code>s. The polygons must conform to the
+   *            assertions specified in the <A
+   *            HREF="http://www.opengis.org/techno/specs.htm">OpenGIS Simple
+   *            Features Specification for SQL</A>.
+   */
+  public MultiPolygon(Polygon[] polygons, GeometryFactory factory) {
+    super(polygons, factory);
+  }
+
+  public int getDimension() {
+    return 2;
+  }
+
+  public int getBoundaryDimension() {
+    return 1;
+  }
+
+  public String getGeometryType() {
+    return "MultiPolygon";
+  }
+
+  public boolean isSimple() {
+    return true;
+  }
+
+  public Geometry getBoundary() {
+    if (isEmpty()) {
+      return getFactory().createGeometryCollection(null);
+    }
+    ArrayList allRings = new ArrayList();
+    for (int i = 0; i < geometries.length; i++) {
+      Polygon polygon = (Polygon) geometries[i];
+      Geometry rings = polygon.getBoundary();
+      for (int j = 0; j < rings.getNumGeometries(); j++) {
+        allRings.add(rings.getGeometryN(j));
+      }
+    }
+    LineString[] allRingsArray = new LineString[allRings.size()];
+    return getFactory().createMultiLineString((LineString[]) allRings.toArray(allRingsArray));
+  }
+
+  public boolean equalsExact(Geometry other, double tolerance) {
+    if (!isEquivalentClass(other)) {
+      return false;
+    }
+    return super.equalsExact(other, tolerance);
+  }
+}
+
+

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/Point.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/Point.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/Point.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,192 @@
+
+
+/*
+ * 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 com.vividsolutions.jts.util.Assert;
+
+/**
+ *  Basic implementation of <code>Point</code>.
+ *
+ *@version 1.6
+ */
+public class Point extends Geometry {
+  private static final long serialVersionUID = 4902022702746614570L;
+  /**
+   *  The <code>Coordinate</code> wrapped by this <code>Point</code>.
+   */
+  private CoordinateSequence coordinates;
+
+  /**
+   *  Constructs a <code>Point</code> with the given coordinate.
+   *
+   *@param  coordinate      the coordinate on which to base this <code>Point</code>
+   *      , or <code>null</code> to create the empty geometry.
+   *@param  precisionModel  the specification of the grid of allowable points
+   *      for this <code>Point</code>
+   *@param  SRID            the ID of the Spatial Reference System used by this
+   *      <code>Point</code>
+   * @deprecated Use GeometryFactory instead
+   */
+  public Point(Coordinate coordinate, PrecisionModel precisionModel, int SRID) {
+    super(new GeometryFactory(precisionModel, SRID));
+    init(getFactory().getCoordinateSequenceFactory().create(
+          coordinate != null ? new Coordinate[]{coordinate} : new Coordinate[]{}));
+  }
+
+  /**
+   *@param  coordinates      contains the single coordinate on which to base this <code>Point</code>
+   *      , or <code>null</code> to create the empty geometry.
+   */
+  public Point(CoordinateSequence coordinates, GeometryFactory factory) {
+    super(factory);
+    init(coordinates);
+  }
+
+  private void init(CoordinateSequence coordinates)
+  {
+    if (coordinates == null) {
+      coordinates = getFactory().getCoordinateSequenceFactory().create(new Coordinate[]{});
+    }
+    Assert.isTrue(coordinates.size() <= 1);
+    this.coordinates = coordinates;
+  }
+
+  public Coordinate[] getCoordinates() {
+    return isEmpty() ? new Coordinate[]{} : new Coordinate[]{
+        getCoordinate()
+        };
+  }
+
+  public int getNumPoints() {
+    return isEmpty() ? 0 : 1;
+  }
+
+  public boolean isEmpty() {
+    return getCoordinate() == null;
+  }
+
+  public boolean isSimple() {
+    return true;
+  }
+
+  public boolean isValid() {
+    return true;
+  }
+
+  public int getDimension() {
+    return 0;
+  }
+
+  public int getBoundaryDimension() {
+    return Dimension.FALSE;
+  }
+
+  public double getX() {
+    if (getCoordinate() == null) {
+      throw new IllegalStateException("getX called on empty Point");
+    }
+    return getCoordinate().x;
+  }
+
+  public double getY() {
+    if (getCoordinate() == null) {
+      throw new IllegalStateException("getY called on empty Point");
+    }
+    return getCoordinate().y;
+  }
+
+  public Coordinate getCoordinate() {
+    return coordinates.size() != 0 ? coordinates.getCoordinate(0): null;
+  }
+
+  public String getGeometryType() {
+    return "Point";
+  }
+
+  public Geometry getBoundary() {
+    return getFactory().createGeometryCollection(null);
+  }
+
+  protected Envelope computeEnvelopeInternal() {
+    if (isEmpty()) {
+      return new Envelope();
+    }
+    Envelope env = new Envelope();
+    env.expandToInclude(coordinates.getX(0), coordinates.getY(0));
+    return env;
+  }
+
+  public boolean equalsExact(Geometry other, double tolerance) {
+    if (!isEquivalentClass(other)) {
+      return false;
+    }
+    if (isEmpty() && other.isEmpty()) {
+      return true;
+    }
+    return equal(((Point) other).getCoordinate(), this.getCoordinate(), tolerance);
+  }
+
+  public void apply(CoordinateFilter filter) {
+    if (isEmpty()) { return; }
+    filter.filter(getCoordinate());
+  }
+
+  public void apply(GeometryFilter filter) {
+    filter.filter(this);
+  }
+
+  public void apply(GeometryComponentFilter filter) {
+    filter.filter(this);
+  }
+
+  public Object clone() {
+    Point p = (Point) super.clone();
+    p.coordinates = (CoordinateSequence) coordinates.clone();
+    return p;// return the clone
+  }
+
+  public void normalize() { }
+
+  protected int compareToSameClass(Object other) {
+    Point point = (Point) other;
+    return getCoordinate().compareTo(point.getCoordinate());
+  }
+
+    public CoordinateSequence getCoordinateSequence() {
+        return coordinates;
+    }
+
+}
+

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/Polygon.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/Polygon.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/Polygon.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,369 @@
+
+
+/*
+ * 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.Arrays;
+
+import com.vividsolutions.jts.algorithm.*;
+
+/**
+ * Represents a linear polygon, which may include holes.
+ * The shell and holes of the polygon are represented by {@link LinearRing}s.
+ * In a valid polygon, holes may touch the shell or other holes at a single point.
+ * However, no sequence of touching holes may split the polygon into two pieces.
+ * The orientation of the rings in the polygon does not matter.
+ * <p>
+ *  The shell and holes must conform to the assertions specified in the <A
+ *  HREF="http://www.opengis.org/techno/specs.htm">OpenGIS Simple Features
+ *  Specification for SQL</A> .
+ *
+ *@version 1.6
+ */
+public class Polygon extends Geometry {
+  private static final long serialVersionUID = -3494792200821764533L;
+
+  /**
+   *  The exterior boundary,
+   * or <code>null</code> if this <code>Polygon</code>
+   *  is empty.
+   */
+  protected LinearRing shell = null;
+
+  /**
+   * The interior boundaries, if any.
+   * This instance var is never null.
+   * If there are no holes, the array is of zero length.
+   */
+  protected LinearRing[] holes;
+
+  /**
+   *  Constructs a <code>Polygon</code> with the given exterior boundary.
+   *
+   *@param  shell           the outer boundary of the new <code>Polygon</code>,
+   *      or <code>null</code> or an empty <code>LinearRing</code> if the empty
+   *      geometry is to be created.
+   *@param  precisionModel  the specification of the grid of allowable points
+   *      for this <code>Polygon</code>
+   *@param  SRID            the ID of the Spatial Reference System used by this
+   *      <code>Polygon</code>
+   * @deprecated Use GeometryFactory instead
+   */
+  public Polygon(LinearRing shell, PrecisionModel precisionModel, int SRID) {
+    this(shell, new LinearRing[]{}, new GeometryFactory(precisionModel, SRID));
+  }
+
+  /**
+   *  Constructs a <code>Polygon</code> with the given exterior boundary and
+   *  interior boundaries.
+   *
+   *@param  shell           the outer boundary of the new <code>Polygon</code>,
+   *      or <code>null</code> or an empty <code>LinearRing</code> if the empty
+   *      geometry is to be created.
+   *@param  holes           the inner boundaries of the new <code>Polygon</code>
+   *      , or <code>null</code> or empty <code>LinearRing</code>s if the empty
+   *      geometry is to be created.
+   *@param  precisionModel  the specification of the grid of allowable points
+   *      for this <code>Polygon</code>
+   *@param  SRID            the ID of the Spatial Reference System used by this
+   *      <code>Polygon</code>
+   * @deprecated Use GeometryFactory instead
+   */
+  public Polygon(LinearRing shell, LinearRing[] holes, PrecisionModel precisionModel, int SRID) {
+      this(shell, holes, new GeometryFactory(precisionModel, SRID));
+  }
+
+  /**
+   *  Constructs a <code>Polygon</code> with the given exterior boundary and
+   *  interior boundaries.
+   *
+   *@param  shell           the outer boundary of the new <code>Polygon</code>,
+   *      or <code>null</code> or an empty <code>LinearRing</code> if the empty
+   *      geometry is to be created.
+   *@param  holes           the inner boundaries of the new <code>Polygon</code>
+   *      , or <code>null</code> or empty <code>LinearRing</code>s if the empty
+   *      geometry is to be created.
+   */
+  public Polygon(LinearRing shell, LinearRing[] holes, GeometryFactory factory) {
+    super(factory);
+    if (shell == null) {
+      shell = getFactory().createLinearRing((CoordinateSequence)null);
+    }
+    if (holes == null) {
+      holes = new LinearRing[]{};
+    }
+    if (hasNullElements(holes)) {
+      throw new IllegalArgumentException("holes must not contain null elements");
+    }
+    if (shell.isEmpty() && hasNonEmptyElements(holes)) {
+      throw new IllegalArgumentException("shell is empty but holes are not");
+    }
+    this.shell = shell;
+    this.holes = holes;
+  }
+
+  public Coordinate getCoordinate() {
+    return shell.getCoordinate();
+  }
+
+  public Coordinate[] getCoordinates() {
+    if (isEmpty()) {
+      return new Coordinate[]{};
+    }
+    Coordinate[] coordinates = new Coordinate[getNumPoints()];
+    int k = -1;
+    Coordinate[] shellCoordinates = shell.getCoordinates();
+    for (int x = 0; x < shellCoordinates.length; x++) {
+      k++;
+      coordinates[k] = shellCoordinates[x];
+    }
+    for (int i = 0; i < holes.length; i++) {
+      Coordinate[] childCoordinates = holes[i].getCoordinates();
+      for (int j = 0; j < childCoordinates.length; j++) {
+        k++;
+        coordinates[k] = childCoordinates[j];
+      }
+    }
+    return coordinates;
+  }
+
+  public int getNumPoints() {
+    int numPoints = shell.getNumPoints();
+    for (int i = 0; i < holes.length; i++) {
+      numPoints += holes[i].getNumPoints();
+    }
+    return numPoints;
+  }
+
+  public int getDimension() {
+    return 2;
+  }
+
+  public int getBoundaryDimension() {
+    return 1;
+  }
+
+  public boolean isEmpty() {
+    return shell.isEmpty();
+  }
+
+  public boolean isSimple() {
+    return true;
+  }
+
+  public boolean isRectangle()
+  {
+    if (getNumInteriorRing() != 0) return false;
+    if (shell == null) return false;
+    if (shell.getNumPoints() != 5) return false;
+
+    CoordinateSequence seq = shell.getCoordinateSequence();
+
+    // check vertices have correct values
+    Envelope env = getEnvelopeInternal();
+    for (int i = 0; i < 5; i++) {
+      double x = seq.getX(i);
+      if (! (x == env.getMinX() || x == env.getMaxX())) return false;
+      double y = seq.getX(i);
+      if (! (y == env.getMinY() || y == env.getMaxY())) return false;
+    }
+
+    // check vertices are in right order
+    double prevX = seq.getX(0);
+    double prevY = seq.getX(0);
+    for (int i = 1; i <= 4; i++) {
+      double x = seq.getX(i);
+      double y = seq.getY(i);
+      boolean xChanged = x != prevX;
+      boolean yChanged = y != prevY;
+      if (xChanged == yChanged)
+        return false;
+      prevX = x;
+      prevY = y;
+    }
+    return true;
+  }
+
+  public LineString getExteriorRing() {
+    return shell;
+  }
+
+  public int getNumInteriorRing() {
+    return holes.length;
+  }
+
+  public LineString getInteriorRingN(int n) {
+    return holes[n];
+  }
+
+  public String getGeometryType() {
+    return "Polygon";
+  }
+
+  /**
+   *  Returns the area of this <code>Polygon</code>
+   *
+   *@return the area of the polygon
+   */
+  public double getArea()
+  {
+    double area = 0.0;
+    area += Math.abs(CGAlgorithms.signedArea(shell.getCoordinates()));
+    for (int i = 0; i < holes.length; i++) {
+      area -= Math.abs(CGAlgorithms.signedArea(holes[i].getCoordinates()));
+    }
+    return area;
+  }
+
+  /**
+   *  Returns the perimeter of this <code>Polygon</code>
+   *
+   *@return the perimeter of the polygon
+   */
+  public double getLength()
+  {
+    double len = 0.0;
+    len += shell.getLength();
+    for (int i = 0; i < holes.length; i++) {
+      len += holes[i].getLength();
+    }
+    return len;
+  }
+
+  public Geometry getBoundary() {
+    if (isEmpty()) {
+      return getFactory().createGeometryCollection(null);
+    }
+    LinearRing[] rings = new LinearRing[holes.length + 1];
+    rings[0] = shell;
+    for (int i = 0; i < holes.length; i++) {
+      rings[i + 1] = holes[i];
+    }
+    if (rings.length <= 1)
+      return getFactory().createLinearRing(rings[0].getCoordinateSequence());
+    return getFactory().createMultiLineString(rings);
+  }
+
+  protected Envelope computeEnvelopeInternal() {
+    return shell.getEnvelopeInternal();
+  }
+
+  public boolean equalsExact(Geometry other, double tolerance) {
+    if (!isEquivalentClass(other)) {
+      return false;
+    }
+    Polygon otherPolygon = (Polygon) other;
+    Geometry thisShell = shell;
+    Geometry otherPolygonShell = otherPolygon.shell;
+    if (!thisShell.equalsExact(otherPolygonShell, tolerance)) {
+      return false;
+    }
+    if (holes.length != otherPolygon.holes.length) {
+      return false;
+    }
+    if (holes.length != otherPolygon.holes.length) {
+      return false;
+    }
+    for (int i = 0; i < holes.length; i++) {
+      if (!((Geometry) holes[i]).equalsExact(otherPolygon.holes[i], tolerance)) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  public void apply(CoordinateFilter filter) {
+    shell.apply(filter);
+    for (int i = 0; i < holes.length; i++) {
+      holes[i].apply(filter);
+    }
+  }
+
+  public void apply(GeometryFilter filter) {
+    filter.filter(this);
+  }
+
+  public void apply(GeometryComponentFilter filter) {
+    filter.filter(this);
+    shell.apply(filter);
+    for (int i = 0; i < holes.length; i++) {
+      holes[i].apply(filter);
+    }
+  }
+
+  public Object clone() {
+    Polygon poly = (Polygon) super.clone();
+    poly.shell = (LinearRing) shell.clone();
+    poly.holes = new LinearRing[holes.length];
+    for (int i = 0; i < holes.length; i++) {
+      poly.holes[i] = (LinearRing) holes[i].clone();
+    }
+    return poly;// return the clone
+  }
+
+  public Geometry convexHull() {
+    return getExteriorRing().convexHull();
+  }
+
+  public void normalize() {
+    normalize(shell, true);
+    for (int i = 0; i < holes.length; i++) {
+      normalize(holes[i], false);
+    }
+    Arrays.sort(holes);
+  }
+
+  protected int compareToSameClass(Object o) {
+    LinearRing thisShell = shell;
+    LinearRing otherShell = ((Polygon) o).shell;
+    return thisShell.compareToSameClass(otherShell);
+  }
+
+  private void normalize(LinearRing ring, boolean clockwise) {
+    if (ring.isEmpty()) {
+      return;
+    }
+    Coordinate[] uniqueCoordinates = new Coordinate[ring.getCoordinates().length - 1];
+    System.arraycopy(ring.getCoordinates(), 0, uniqueCoordinates, 0, uniqueCoordinates.length);
+    Coordinate minCoordinate = CoordinateArrays.minCoordinate(ring.getCoordinates());
+    CoordinateArrays.scroll(uniqueCoordinates, minCoordinate);
+    System.arraycopy(uniqueCoordinates, 0, ring.getCoordinates(), 0, uniqueCoordinates.length);
+    ring.getCoordinates()[uniqueCoordinates.length] = uniqueCoordinates[0];
+    if (CGAlgorithms.isCCW(ring.getCoordinates()) == clockwise) {
+      CoordinateArrays.reverse(ring.getCoordinates());
+    }
+  }
+
+}
+

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/PrecisionModel.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/PrecisionModel.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/PrecisionModel.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,454 @@
+
+
+/*
+ * 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.HashMap;
+import java.util.Map;
+
+/**
+ * Specifies the precision model of the {@link Coordinate}s in a {@link Geometry}.
+ * In other words, specifies the grid of allowable
+ *  points for all <code>Geometry</code>s.
+ * <p>
+ * The {@link makePrecise} method allows rounding a coordinate to
+ * a "precise" value; that is, one whose
+ *  precision is known exactly.
+ *<p>
+ * Coordinates are assumed to be precise in geometries.
+ * That is, the coordinates are assumed to be rounded to the
+ * precision model given for the geometry.
+ * JTS input routines automatically round coordinates to the precision model
+ * before creating Geometries.
+ * All internal operations
+ * assume that coordinates are rounded to the precision model.
+ * Constructive methods (such as boolean operations) always round computed
+ * coordinates to the appropriate precision model.
+ * <p>
+ * Currently three types of precision model are supported:
+ * <ul>
+ * <li>FLOATING - represents full double precision floating point.
+ * This is the default precision model used in JTS
+ * <li>FLOATING_SINGLE - represents single precision floating point.
+ * <li>FIXED - represents a model with a fixed number of decimal places.
+ *  A Fixed Precision Model is specified by a scale factor.
+ *  The scale factor specifies the grid which numbers are rounded to.
+ *  Input coordinates are mapped to fixed coordinates according to the following
+ *  equations:
+ *    <UL>
+ *      <LI> jtsPt.x = round( (inputPt.x * scale ) / scale
+ *      <LI> jtsPt.y = round( (inputPt.y * scale ) / scale
+ *    </UL>
+ * </ul>
+ *  Coordinates are represented internally as Java double-precision values.
+ * Since Java uses the IEEE-394 floating point standard, this
+ *  provides 53 bits of precision. (Thus the maximum precisely representable
+ *  integer is 9,007,199,254,740,992).
+ *<p>
+ *  JTS methods currently do not handle inputs with different precision models.
+ *
+ *@version 1.6
+ */
+public class PrecisionModel implements Serializable, Comparable
+{
+  private static final long serialVersionUID = 7777263578777803835L;
+
+  /**
+   * The types of Precision Model which JTS supports.
+   * <p>
+   * This class is only for use to support the "enums" for the types of precision model.
+   * <p>
+   * <i>
+   * Note: Type should be declared as private to this class,
+   * but JBuilder 7 throws a compiler exception
+   * when trying to compile with the "private" keyword.  Package-private is safe enough.
+   * </i>
+   */
+  static class Type
+      implements Serializable
+  {
+    private static final long serialVersionUID = -5528602631731589822L;
+    private static Map nameToTypeMap = new HashMap();
+    public Type(String name) {
+        this.name = name;
+        nameToTypeMap.put(name, this);
+    }
+    private String name;
+    public String toString() { return name; }
+    /**
+     * @see http://www.javaworld.com/javaworld/javatips/jw-javatip122.html
+     */
+    private Object readResolve() {
+        return nameToTypeMap.get(name);
+    }
+  }
+
+  /**
+   * Fixed Precision indicates that coordinates have a fixed number of decimal places.
+   * The number of decimal places is determined by the log10 of the scale factor.
+   */
+  public static final Type FIXED = new Type("FIXED");
+  /**
+   * Floating precision corresponds to the standard Java
+   * double-precision floating-point representation, which is
+   * based on the IEEE-754 standard
+   */
+  public static final Type FLOATING = new Type("FLOATING");
+  /**
+   * Floating single precision corresponds to the standard Java
+   * single-precision floating-point representation, which is
+   * based on the IEEE-754 standard
+   */
+  public static final Type FLOATING_SINGLE = new Type("FLOATING SINGLE");
+
+
+  /**
+   *  The maximum precise value representable in a double. Since IEE754
+   *  double-precision numbers allow 53 bits of mantissa, the value is equal to
+   *  2^53 - 1.  This provides <i>almost</i> 16 decimal digits of precision.
+   */
+  public final static double maximumPreciseValue = 9007199254740992.0;
+
+  /**
+   * The type of PrecisionModel this represents.
+   */
+  private Type modelType;
+  /**
+   * The scale factor which determines the number of decimal places in fixed precision.
+   */
+  private double scale;
+
+  /**
+   * Creates a <code>PrecisionModel</code> with a default precision
+   * of FLOATING.
+   */
+  public PrecisionModel() {
+    // default is floating precision
+    modelType = FLOATING;
+  }
+
+  /**
+   * Creates a <code>PrecisionModel</code> that specifies
+   * an explicit precision model type.
+   * If the model type is FIXED the scale factor will default to 1.
+   *
+   * @param modelType the type of the precision model
+   */
+  public PrecisionModel(Type modelType)
+  {
+    this.modelType = modelType;
+    if (modelType == FIXED)
+    {
+      setScale(1.0);
+    }
+  }
+  /**
+   *  Creates a <code>PrecisionModel</code> that specifies Fixed precision.
+   *  Fixed-precision coordinates are represented as precise internal coordinates,
+   *  which are rounded to the grid defined by the scale factor.
+   *
+   *@param  scale    amount by which to multiply a coordinate after subtracting
+   *      the offset, to obtain a precise coordinate
+   *@param  offsetX  not used.
+   *@param  offsetY  not used.
+   *
+   * @deprecated offsets are no longer supported, since internal representation is rounded floating point
+   */
+  public PrecisionModel(double scale, double offsetX, double offsetY) {
+    modelType = FIXED;
+    setScale(scale);
+  }
+  /**
+   *  Creates a <code>PrecisionModel</code> that specifies Fixed precision.
+   *  Fixed-precision coordinates are represented as precise internal coordinates,
+   *  which are rounded to the grid defined by the scale factor.
+   *
+   *@param  scale    amount by which to multiply a coordinate after subtracting
+   *      the offset, to obtain a precise coordinate
+   */
+  public PrecisionModel(double scale) {
+    modelType = FIXED;
+    setScale(scale);
+  }
+  /**
+   *  Copy constructor to create a new <code>PrecisionModel</code>
+   *  from an existing one.
+   */
+  public PrecisionModel(PrecisionModel pm) {
+    modelType = pm.modelType;
+    scale = pm.scale;
+  }
+
+
+  /**
+   * Tests whether the precision model supports floating point
+   * @return <code>true</code> if the precision model supports floating point
+   */
+  public boolean isFloating()
+  {
+    return modelType == FLOATING || modelType == FLOATING_SINGLE;
+  }
+
+  /**
+   * Returns the maximum number of significant digits provided by this
+   * precision model.
+   * Intended for use by routines which need to print out precise values.
+   *
+   * @return the maximum number of decimal places provided by this precision model
+   */
+  	public int getMaximumSignificantDigits() {
+		int maxSigDigits = 16;
+		if (modelType == FLOATING) {
+			maxSigDigits = 16;
+		} else if (modelType == FLOATING_SINGLE) {
+			maxSigDigits = 6;
+		} else if (modelType == FIXED) {
+			maxSigDigits = 1 + (int) Math.ceil(Math.log(getScale()) / Math.log(10));
+		}
+		return maxSigDigits;
+	}
+
+  /**
+   *  Returns the multiplying factor used to obtain a precise coordinate.
+   * This method is private because PrecisionModel is intended to
+   * be an immutable (value) type.
+   *
+   *@return    the amount by which to multiply a coordinate after subtracting
+   *      the offset
+   */
+  public double getScale() {
+    return scale;
+  }
+
+  /**
+   * Gets the type of this PrecisionModel
+   * @return the type of this PrecisionModel
+   */
+  public Type getType()
+  {
+    return modelType;
+  }
+  /**
+   *  Sets the multiplying factor used to obtain a precise coordinate.
+   * This method is private because PrecisionModel is intended to
+   * be an immutable (value) type.
+   *
+   */
+  private void setScale(double scale)
+  {
+    this.scale = Math.abs(scale);
+  }
+
+  /**
+   * Returns the x-offset used to obtain a precise coordinate.
+   *
+   * @return the amount by which to subtract the x-coordinate before
+   *         multiplying by the scale
+   * @deprecated Offsets are no longer used
+   */
+  public double getOffsetX() {
+    //We actually don't use offsetX and offsetY anymore ... [Jon Aquino]
+    return 0;
+  }
+
+
+
+  /**
+   * Returns the y-offset used to obtain a precise coordinate.
+   *
+   * @return the amount by which to subtract the y-coordinate before
+   *         multiplying by the scale
+   * @deprecated Offsets are no longer used
+   */
+  public double getOffsetY() {
+    return 0;
+  }
+
+  /**
+   *  Sets <code>internal</code> to the precise representation of <code>external</code>.
+   *
+   * @param external the original coordinate
+   * @param internal the coordinate whose values will be changed to the
+   *                 precise representation of <code>external</code>
+   * @deprecated use makePrecise instead
+   */
+  public void toInternal (Coordinate external, Coordinate internal) {
+    if (isFloating()) {
+      internal.x = external.x;
+      internal.y = external.y;
+    }
+    else {
+      internal.x = makePrecise(external.x);
+      internal.y = makePrecise(external.y);
+    }
+    internal.z = external.z;
+  }
+
+  /**
+   *  Returns the precise representation of <code>external</code>.
+   *
+   *@param  external  the original coordinate
+   *@return           the coordinate whose values will be changed to the precise
+   *      representation of <code>external</code>
+   * @deprecated use makePrecise instead
+   */
+  public Coordinate toInternal(Coordinate external) {
+    Coordinate internal = new Coordinate(external);
+    makePrecise(internal);
+    return internal;
+  }
+
+  /**
+   *  Returns the external representation of <code>internal</code>.
+   *
+   *@param  internal  the original coordinate
+   *@return           the coordinate whose values will be changed to the
+   *      external representation of <code>internal</code>
+   * @deprecated no longer needed, since internal representation is same as external representation
+   */
+  public Coordinate toExternal(Coordinate internal) {
+    Coordinate external = new Coordinate(internal);
+    return external;
+  }
+
+  /**
+   *  Sets <code>external</code> to the external representation of <code>internal</code>
+   *  .
+   *
+   *@param  internal  the original coordinate
+   *@param  external  the coordinate whose values will be changed to the
+   *      external representation of <code>internal</code>
+   * @deprecated no longer needed, since internal representation is same as external representation
+   */
+  public void toExternal(Coordinate internal, Coordinate external) {
+      external.x = internal.x;
+      external.y = internal.y;
+  }
+
+  /**
+   * Rounds a numeric value to the PrecisionModel grid.
+   * Symmetric Arithmetic Rounding is used, to provide
+   * uniform rounding behaviour no matter where the number is
+   * on the number line.
+   * <p>
+   * <b>Note:</b> Java's <code>Math#rint</code> uses the "banker's rounding" algorithm,
+   * which is not suitable for precision operations elsewhere in JTS.
+   */
+  public double makePrecise(double val) {
+  	if (modelType == FLOATING_SINGLE) {
+  		float floatSingleVal = (float) val;
+  		return (double) floatSingleVal;
+  	}
+  	if (modelType == FIXED) {
+            return Math.round(val * scale) / scale;
+//  		return Math.rint(val * scale) / scale;
+  	}
+  	// modelType == FLOATING - no rounding necessary
+  	return val;
+  }
+
+  /**
+   * Rounds a Coordinate to the PrecisionModel grid.
+   */
+  public void makePrecise(Coordinate coord)
+  {
+    // optimization for full precision
+    if (modelType == FLOATING) return;
+
+    coord.x = makePrecise(coord.x);
+    coord.y = makePrecise(coord.y);
+    //MD says it's OK that we're not makePrecise'ing the z [Jon Aquino]
+  }
+
+
+  public String toString() {
+  	String description = "UNKNOWN";
+  	if (modelType == FLOATING) {
+  		description = "Floating";
+  	} else if (modelType == FLOATING_SINGLE) {
+  		description = "Floating-Single";
+  	} else if (modelType == FIXED) {
+  		description = "Fixed (Scale=" + getScale() + ")";
+  	}
+  	return description;
+  }
+
+  public boolean equals(Object other) {
+    if (! (other instanceof PrecisionModel)) {
+      return false;
+    }
+    PrecisionModel otherPrecisionModel = (PrecisionModel) other;
+    return modelType == otherPrecisionModel.modelType
+        && scale == otherPrecisionModel.scale;
+  }
+  /**
+   *  Compares this {@link PrecisionModel} object with the specified object for order.
+   * A PrecisionModel is greater than another if it provides greater precision.
+   * The comparison is based on the value returned by the
+   * {@link getMaximumSignificantDigits) method.
+   * This comparison is not strictly accurate when comparing floating precision models
+   * to fixed models; however, it is correct when both models are either floating or fixed.
+   *
+   *@param  o  the <code>PrecisionModel</code> with which this <code>PrecisionModel</code>
+   *      is being compared
+   *@return    a negative integer, zero, or a positive integer as this <code>PrecisionModel</code>
+   *      is less than, equal to, or greater than the specified <code>PrecisionModel</code>
+   */
+  public int compareTo(Object o) {
+    PrecisionModel other = (PrecisionModel) o;
+
+    int sigDigits = getMaximumSignificantDigits();
+    int otherSigDigits = other.getMaximumSignificantDigits();
+    return (new Integer(sigDigits)).compareTo(new Integer(otherSigDigits));
+//    if (sigDigits > otherSigDigits)
+//      return 1;
+//    else if
+//    if (modelType == FLOATING && other.modelType == FLOATING) return 0;
+//    if (modelType == FLOATING && other.modelType != FLOATING) return 1;
+//    if (modelType != FLOATING && other.modelType == FLOATING) return -1;
+//    if (modelType == FIXED && other.modelType == FIXED) {
+//      if (scale > other.scale)
+//        return 1;
+//      else if (scale < other.scale)
+//        return -1;
+//      else
+//        return 0;
+//    }
+//    Assert.shouldNeverReachHere("Unknown Precision Model type encountered");
+//    return 0;
+  }
+}
+
+

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/TopologyException.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/TopologyException.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/TopologyException.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,68 @@
+
+
+
+/*
+ * 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;
+
+/**
+ * Indicates an invalid or inconsistent topological situation encountered during processing
+ *
+ * @version 1.6
+ */
+public class TopologyException
+  extends RuntimeException
+{
+  private static String msgWithCoord(String msg, Coordinate pt)
+  {
+    if (pt != null)
+      return msg + " [ " + pt + " ]";
+    return msg;
+  }
+
+  private Coordinate pt = null;
+
+  public TopologyException(String msg)
+  {
+    super(msg);
+  }
+
+  public TopologyException(String msg, Coordinate pt)
+  {
+    super(msgWithCoord(msg, pt));
+    this.pt = new Coordinate(pt);
+  }
+
+  public Coordinate getCoordinate() { return pt; }
+
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/Triangle.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/Triangle.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/Triangle.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,72 @@
+
+/*
+ * 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;
+
+/**
+ * Represents a planar triangle, and provides methods for calculating various
+ * properties of triangles.
+ *
+ * @version 1.6
+ */
+public class Triangle
+{
+  public Coordinate p0, p1, p2;
+
+  public Triangle(Coordinate p0, Coordinate p1, Coordinate p2)
+  {
+    this.p0 = p0;
+    this.p1 = p1;
+    this.p2 = p2;
+  }
+
+  /**
+   * The inCentre of a triangle is the point which is equidistant
+   * from the sides of the triangle.  This is also the point at which the bisectors
+   * of the angles meet.
+   *
+   * @return the point which is the inCentre of the triangle
+   */
+  public Coordinate inCentre()
+  {
+    // the lengths of the sides, labelled by their opposite vertex
+    double len0 = p1.distance(p2);
+    double len1 = p0.distance(p2);
+    double len2 = p0.distance(p1);
+    double circum = len0 + len1 + len2;
+
+    double inCentreX = (len0 * p0.x + len1 * p1.x +len2 * p2.x)  / circum;
+    double inCentreY = (len0 * p0.y + len1 * p1.y +len2 * p2.y)  / circum;
+    return new Coordinate(inCentreX, inCentreY);
+  }
+}

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/impl/CoordinateArraySequence.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,223 @@
+/*
+* 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.impl;
+
+import java.io.Serializable;
+import com.vividsolutions.jts.geom.*;
+
+/**
+ * The {@link CoordinateSequence} implementation that {@link Geometry}s use by default.
+ * In this implementation, Coordinates returned by #toArray and #getCoordinate are live --
+ * modifications to them are actually changing the
+ * CoordinateSequence's underlying data.
+ *
+ * @version 1.6
+ */
+public class CoordinateArraySequence
+    implements CoordinateSequence, Serializable
+{
+  //With contributions from Markus Schaber [schabios at logi-track.com] 2004-03-26
+  private static final long serialVersionUID = -915438501601840650L;
+
+  private Coordinate[] coordinates;
+
+  /**
+   * Constructs a sequence based on the given array (the
+   * array is not copied).
+   *
+   * @param coordinates the coordinate array that will be referenced.
+   */
+  public CoordinateArraySequence(Coordinate[] coordinates) {
+    this.coordinates = coordinates;
+    if (coordinates == null)
+      this.coordinates = new Coordinate[0];
+  }
+
+  /**
+   * Constructs a sequence of a given size, populated
+   * with new {@link Coordinate}s.
+   *
+   * @param size the size of the sequence to create
+   */
+  public CoordinateArraySequence(int size) {
+    coordinates = new Coordinate[size];
+    for (int i = 0; i < size; i++) {
+      coordinates[i] = new Coordinate();
+    }
+  }
+
+  /**
+   * Constructs a sequence based on the given array (the
+   * array is not copied).
+   *
+   * @param coordinates the coordinate array that will be referenced.
+   */
+  public CoordinateArraySequence(CoordinateSequence coordSeq)
+  {
+    if (coordSeq != null)
+      coordinates = new Coordinate[coordSeq.size()];
+    else
+      coordinates = new Coordinate[0];
+
+    for (int i = 0; i < coordinates.length; i++) {
+      coordinates[i] = coordSeq.getCoordinateCopy(i);
+    }
+  }
+
+  /**
+   * Get the Coordinate with index i.
+   *
+   * @param i
+   *                  the index of the coordinate
+   * @return the requested Coordinate instance
+   */
+  public Coordinate getCoordinate(int i) {
+    return coordinates[i];
+  }
+
+  /**
+   * Get a copy of the Coordinate with index i.
+   *
+   * @param i  the index of the coordinate
+   * @return a copy of the requested Coordinate
+   */
+  public Coordinate getCoordinateCopy(int i) {
+    return new Coordinate(coordinates[i]);
+  }
+
+  /**
+   * @see com.vividsolutions.jts.geom.CoordinateSequence#getX(int)
+   */
+  public void getCoordinate(int index, Coordinate coord) {
+    coord.x = coordinates[index].x;
+    coord.y = coordinates[index].y;
+  }
+
+  /**
+   * @see com.vividsolutions.jts.geom.CoordinateSequence#getX(int)
+   */
+  public double getX(int index) {
+    return coordinates[index].x;
+  }
+
+  /**
+   * @see com.vividsolutions.jts.geom.CoordinateSequence#getY(int)
+   */
+  public double getY(int index) {
+    return coordinates[index].y;
+  }
+
+  /**
+   * @see com.vividsolutions.jts.geom.CoordinateSequence#getOrdinate(int, int)
+   */
+  public double getOrdinate(int index, int ordinateIndex)
+  {
+    switch (ordinateIndex) {
+      case CoordinateSequence.X:  return coordinates[index].x;
+      case CoordinateSequence.Y:  return coordinates[index].y;
+      case CoordinateSequence.Z:  return coordinates[index].z;
+    }
+    return Double.NaN;
+  }
+
+  /**
+   * Creates a deep copy of the Object
+   *
+   * @return The deep copy
+   */
+  public Object clone() {
+    Coordinate[] cloneCoordinates = new Coordinate[size()];
+    for (int i = 0; i < coordinates.length; i++) {
+      cloneCoordinates[i] = (Coordinate) coordinates[i].clone();
+    }
+    return new CoordinateArraySequence(cloneCoordinates);
+  }
+  /**
+   * Returns the size of the coordinate sequence
+   *
+   * @return the number of coordinates
+   */
+  public int size() {
+    return coordinates.length;
+  }
+
+  /**
+   * @see com.vividsolutions.jts.geom.CoordinateSequence#setOrdinate(int, int, double)
+   */
+  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;
+    }
+  }
+
+  /**
+   * This method exposes the internal Array of Coordinate Objects
+   *
+   * @return the Coordinate[] array.
+   */
+  public Coordinate[] toCoordinateArray() {
+    return coordinates;
+  }
+
+  public Envelope expandEnvelope(Envelope env)
+  {
+    for (int i = 0; i < coordinates.length; i++ ) {
+      env.expandToInclude(coordinates[i]);
+    }
+    return env;
+  }
+
+  /**
+   * Returns the string Representation of the coordinate array
+   *
+   * @return The string
+   */
+  public String toString() {
+    if (coordinates.length > 0) {
+      StringBuffer strBuf = new StringBuffer(17 * coordinates.length);
+      strBuf.append('(');
+      strBuf.append(coordinates[0]);
+      for (int i = 1; i < coordinates.length; i++) {
+        strBuf.append(", ");
+        strBuf.append(coordinates[i]);
+      }
+      strBuf.append(')');
+      return strBuf.toString();
+    } else {
+      return "()";
+    }
+  }
+}

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/impl/CoordinateArraySequenceFactory.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,92 @@
+
+/*
+* 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.impl;
+
+import java.io.Serializable;
+import com.vividsolutions.jts.geom.*;
+
+/**
+ * Creates CoordinateSequences represented as an array of {@link Coordinate}s.
+ *
+ * @version 1.6
+ */
+public final class CoordinateArraySequenceFactory
+    implements CoordinateSequenceFactory, Serializable
+{
+  private static final long serialVersionUID = -4099577099607551657L;
+  private static CoordinateArraySequenceFactory instance = new CoordinateArraySequenceFactory();
+
+  private CoordinateArraySequenceFactory() {
+  }
+
+  /**
+   * @see http://www.javaworld.com/javaworld/javatips/jw-javatip122.html
+   */
+  private Object readResolve() {
+    return CoordinateArraySequenceFactory.instance();
+  }
+
+  /**
+   * Returns the singleton instance of {@link CoordinateArraySequenceFactory}
+   */
+  public static CoordinateArraySequenceFactory instance() {
+    return instance;
+  }
+
+  /**
+   * Returns a {@link CoordinateArraySequence} based on the given array (the array is
+   * not copied).
+   *
+   * @param coordinates
+   *            the coordinates, which may not be null nor contain null
+   *            elements
+   */
+  public CoordinateSequence create(Coordinate[] coordinates) {
+    return new CoordinateArraySequence(coordinates);
+  }
+
+  /**
+   * @see com.vividsolutions.jts.geom.CoordinateSequenceFactory#create(com.vividsolutions.jts.geom.CoordinateSequence)
+   */
+  public CoordinateSequence create(CoordinateSequence coordSeq) {
+    return new CoordinateArraySequence(coordSeq);
+  }
+
+  /**
+   * @see com.vividsolutions.jts.geom.CoordinateSequenceFactory#create(int, int)
+   */
+  public CoordinateSequence create(int size, int dimension) {
+    return new CoordinateArraySequence(size);
+  }
+}

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/impl/PackedCoordinateSequence.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,435 @@
+package com.vividsolutions.jts.geom.impl;
+
+import com.vividsolutions.jts.geom.*;
+
+import java.lang.ref.SoftReference;
+
+/**
+ * A {@link CoordinateSequence} implementation based on a packed arrays.
+ * In this implementation, {@link Coordinate}s returned by #toArray and #get are copies
+ * of the internal values.
+ * To change the actual values, use the provided setters.
+ * <p>
+ * For efficiency, created Coordinate arrays
+ * are cached using a soft reference.
+ * The cache is cleared each time the coordinate sequence contents are
+ * modified through a setter method.
+ *
+ * @version 1.6
+ */
+public abstract class PackedCoordinateSequence
+    implements CoordinateSequence
+{
+  /**
+   * The dimensions of the coordinates hold in the packed array
+   */
+  protected int dimension;
+
+  /**
+   * A soft reference to the Coordinate[] representation of this sequence.
+   * Makes repeated coordinate array accesses more efficient.
+   */
+  protected SoftReference coordRef;
+
+  /**
+   * @see com.vividsolutions.jts.geom.CoordinateSequence#getDimension()
+   */
+  public int getDimension() {
+    return this.dimension;
+  }
+
+  /**
+   * @see com.vividsolutions.jts.geom.CoordinateSequence#getCoordinate(int)
+   */
+  public Coordinate getCoordinate(int i) {
+    Coordinate[] coords = getCachedCoords();
+    if(coords != null)
+      return coords[i];
+    else
+      return getCoordinateInternal(i);
+  }
+  /**
+   * @see com.vividsolutions.jts.geom.CoordinateSequence#getCoordinate(int)
+   */
+  public Coordinate getCoordinateCopy(int i) {
+    return getCoordinateInternal(i);
+  }
+
+  /**
+   * @see com.vividsolutions.jts.geom.CoordinateSequence#getCoordinate(int)
+   */
+  public void getCoordinate(int i, Coordinate coord) {
+    coord.x = getOrdinate(i, 0);
+    coord.y = getOrdinate(i, 1);
+  }
+
+  /**
+   * @see com.vividsolutions.jts.geom.CoordinateSequence#toCoordinateArray()
+   */
+  public Coordinate[] toCoordinateArray() {
+    Coordinate[] coords = getCachedCoords();
+// testing - never cache
+    if (coords != null)
+      return coords;
+
+    coords = new Coordinate[size()];
+    for (int i = 0; i < coords.length; i++) {
+      coords[i] = getCoordinateInternal(i);
+    }
+    coordRef = new SoftReference(coords);
+
+    return coords;
+  }
+
+  /**
+   * @return
+   */
+  private Coordinate[] getCachedCoords() {
+    if (coordRef != null) {
+      Coordinate[] coords = (Coordinate[]) coordRef.get();
+      if (coords != null) {
+        return coords;
+      } else {
+        // System.out.print("-");
+        coordRef = null;
+        return null;
+      }
+    } else {
+      // System.out.print("-");
+      return null;
+    }
+
+  }
+
+  /**
+   * @see com.vividsolutions.jts.geom.CoordinateSequence#getX(int)
+   */
+  public double getX(int index) {
+    return getOrdinate(index, 0);
+  }
+
+  /**
+   * @see com.vividsolutions.jts.geom.CoordinateSequence#getY(int)
+   */
+  public double getY(int index) {
+    return getOrdinate(index, 1);
+  }
+
+  /**
+   * @see com.vividsolutions.jts.geom.CoordinateSequence#getOrdinate(int, int)
+   */
+  public abstract double getOrdinate(int index, int ordinateIndex);
+
+  /**
+   * Sets the first ordinate of a coordinate in this sequence.
+   *
+   * @param index  the coordinate index
+   * @param value  the new ordinate value
+   */
+  public void setX(int index, double value) {
+    coordRef = null;
+    setOrdinate(index, 0, value);
+  }
+
+  /**
+   * Sets the second ordinate of a coordinate in this sequence.
+   *
+   * @param index  the coordinate index
+   * @param value  the new ordinate value
+   */
+  public void setY(int index, double value) {
+    coordRef = null;
+    setOrdinate(index, 1, value);
+  }
+
+  /**
+   * Returns a Coordinate representation of the specified coordinate, by always
+   * building a new Coordinate object
+   *
+   * @param index
+   * @return
+   */
+  protected abstract Coordinate getCoordinateInternal(int index);
+
+  /**
+   * @see java.lang.Object#clone()
+   */
+  public abstract Object clone();
+
+  /**
+   * Sets the ordinate of a coordinate in this sequence.
+   * <br>
+   * Warning: for performance reasons the ordinate index is not checked
+   * - if it is over dimensions you may not get an exception but a meaningless value.
+   *
+   * @param index
+   *          the coordinate index
+   * @param ordinate
+   *          the ordinate index in the coordinate, 0 based, smaller than the
+   *          number of dimensions
+   * @param value
+   *          the new ordinate value
+   */
+  public abstract void setOrdinate(int index, int ordinate, double value);
+
+  /**
+   * Packed coordinate sequence implementation based on doubles
+   */
+  public static class Double extends PackedCoordinateSequence {
+
+    /**
+     * The packed coordinate array
+     */
+    double[] coords;
+
+    /**
+     * Builds a new packed coordinate sequence
+     *
+     * @param coords
+     * @param dimensions
+     */
+    public Double(double[] coords, int dimensions) {
+      if (dimensions < 2) {
+        throw new IllegalArgumentException("Must have at least 2 dimensions");
+      }
+      if (coords.length % dimensions != 0) {
+        throw new IllegalArgumentException("Packed array does not contain "
+            + "an integral number of coordinates");
+      }
+      this.dimension = dimensions;
+      this.coords = coords;
+    }
+
+    /**
+     * Builds a new packed coordinate sequence out of a float coordinate array
+     *
+     * @param coordinates
+     */
+    public Double(float[] coordinates, int dimensions) {
+      this.coords = new double[coordinates.length];
+      this.dimension = dimensions;
+      for (int i = 0; i < coordinates.length; i++) {
+        this.coords[i] = coordinates[i];
+      }
+    }
+
+    /**
+     * Builds a new packed coordinate sequence out of a coordinate array
+     *
+     * @param coordinates
+     */
+    public Double(Coordinate[] coordinates, int dimension) {
+      if (coordinates == null)
+        coordinates = new Coordinate[0];
+      this.dimension = dimension;
+
+      coords = new double[coordinates.length * this.dimension];
+      for (int i = 0; i < coordinates.length; i++) {
+        coords[i * this.dimension] = coordinates[i].x;
+        if (this.dimension >= 2)
+          coords[i * this.dimension + 1] = coordinates[i].y;
+        if (this.dimension >= 3)
+          coords[i * this.dimension + 2] = coordinates[i].z;
+      }
+    }
+    /**
+     * Builds a new packed coordinate sequence out of a coordinate array
+     *
+     * @param coordinates
+     */
+    public Double(Coordinate[] coordinates) {
+      this(coordinates, 3);
+    }
+
+    /**
+     * Builds a new empty packed coordinate sequence of a given size and dimension
+     *
+     * @param coordinates
+     */
+    public Double(int size, int dimension) {
+      this.dimension = dimension;
+      coords = new double[size * this.dimension];
+    }
+
+    /**
+     * @see com.vividsolutions.jts.geom.CoordinateSequence#getCoordinate(int)
+     */
+    public Coordinate getCoordinateInternal(int i) {
+      double x = coords[i * dimension];
+      double y = coords[i * dimension + 1];
+      double z = dimension == 2 ? 0.0 : coords[i * dimension + 2];
+      return new Coordinate(x, y, z);
+    }
+
+    /**
+     * @see com.vividsolutions.jts.geom.CoordinateSequence#size()
+     */
+    public int size() {
+      return coords.length / dimension;
+    }
+
+    /**
+     * @see java.lang.Object#clone()
+     */
+    public Object clone() {
+      double[] clone = new double[coords.length];
+      System.arraycopy(coords, 0, clone, 0, coords.length);
+      return new Double(clone, dimension);
+    }
+
+    /**
+     * @see com.vividsolutions.jts.geom.CoordinateSequence#getOrdinate(int, int)
+     *      Beware, for performace reasons the ordinate index is not checked, if
+     *      it's over dimensions you may not get an exception but a meaningless
+     *      value.
+     */
+    public double getOrdinate(int index, int ordinate) {
+      return coords[index * dimension + ordinate];
+    }
+
+    /**
+     * @see com.vividsolutions.jts.geom.PackedCoordinateSequence#setOrdinate(int,
+     *      int, double)
+     */
+    public void setOrdinate(int index, int ordinate, double value) {
+      coordRef = null;
+      coords[index * dimension + ordinate] = value;
+    }
+
+    public Envelope expandEnvelope(Envelope env)
+    {
+      for (int i = 0; i < coords.length; i += dimension ) {
+        env.expandToInclude(coords[i], coords[i + 1]);
+      }
+      return env;
+    }
+  }
+
+  /**
+   * Packed coordinate sequence implementation based on floats
+   */
+  public static class Float extends PackedCoordinateSequence {
+
+    /**
+     * The packed coordinate array
+     */
+    float[] coords;
+
+    /**
+     * Constructs a packed coordinate sequence from an array of <code>float<code>s
+     *
+     * @param coords
+     * @param dimensions
+     */
+    public Float(float[] coords, int dimensions) {
+      if (dimensions < 2) {
+        throw new IllegalArgumentException("Must have at least 2 dimensions");
+      }
+      if (coords.length % dimensions != 0) {
+        throw new IllegalArgumentException("Packed array does not contain "
+            + "an integral number of coordinates");
+      }
+      this.dimension = dimensions;
+      this.coords = coords;
+    }
+
+    /**
+     * Constructs a packed coordinate sequence from an array of <code>double<code>s
+     *
+     * @param coordinates
+     * @param dimension
+     */
+    public Float(double[] coordinates, int dimensions) {
+      this.coords = new float[coordinates.length];
+      this.dimension = dimensions;
+      for (int i = 0; i < coordinates.length; i++) {
+        this.coords[i] = (float) coordinates[i];
+      }
+    }
+
+    /**
+     * Constructs a packed coordinate sequence out of a coordinate array
+     *
+     * @param coordinates
+     */
+    public Float(Coordinate[] coordinates, int dimension) {
+      if (coordinates == null)
+        coordinates = new Coordinate[0];
+      this.dimension = dimension;
+
+      coords = new float[coordinates.length * this.dimension];
+      for (int i = 0; i < coordinates.length; i++) {
+        coords[i * this.dimension] = (float) coordinates[i].x;
+        if (this.dimension >= 2)
+          coords[i * this.dimension + 1] = (float) coordinates[i].y;
+        if (this.dimension >= 3)
+          coords[i * this.dimension + 2] = (float) coordinates[i].z;
+      }
+    }
+
+    /**
+     * Constructs an empty packed coordinate sequence of a given size and dimension
+     *
+     * @param coordinates
+     */
+    public Float(int size, int dimension) {
+      this.dimension = dimension;
+      coords = new float[size * this.dimension];
+    }
+
+    /**
+     * @see com.vividsolutions.jts.geom.CoordinateSequence#getCoordinate(int)
+     */
+    public Coordinate getCoordinateInternal(int i) {
+      double x = coords[i * dimension];
+      double y = coords[i * dimension + 1];
+      double z = dimension == 2 ? 0.0 : coords[i * dimension + 2];
+      return new Coordinate(x, y, z);
+    }
+
+    /**
+     * @see com.vividsolutions.jts.geom.CoordinateSequence#size()
+     */
+    public int size() {
+      return coords.length / dimension;
+    }
+
+    /**
+     * @see java.lang.Object#clone()
+     */
+    public Object clone() {
+      float[] clone = new float[coords.length];
+      System.arraycopy(coords, 0, clone, 0, coords.length);
+      return new Float(clone, dimension);
+    }
+
+    /**
+     * @see com.vividsolutions.jts.geom.CoordinateSequence#getOrdinate(int, int)
+     *      Beware, for performace reasons the ordinate index is not checked, if
+     *      it's over dimensions you may not get an exception but a meaningless
+     *      value.
+     */
+    public double getOrdinate(int index, int ordinate) {
+      return coords[index * dimension + ordinate];
+    }
+
+    /**
+     * @see com.vividsolutions.jts.geom.PackedCoordinateSequence#setOrdinate(int,
+     *      int, double)
+     */
+    public void setOrdinate(int index, int ordinate, double value) {
+      coordRef = null;
+      coords[index * dimension + ordinate] = (float) value;
+    }
+
+    public Envelope expandEnvelope(Envelope env)
+    {
+      for (int i = 0; i < coords.length; i += dimension ) {
+        env.expandToInclude(coords[i], coords[i + 1]);
+      }
+      return env;
+    }
+
+  }
+
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/impl/PackedCoordinateSequenceFactory.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/impl/PackedCoordinateSequenceFactory.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/impl/PackedCoordinateSequenceFactory.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,137 @@
+package com.vividsolutions.jts.geom.impl;
+
+import com.vividsolutions.jts.geom.*;
+
+/**
+ * Builds packed array coordinate sequences. The array data type can be either
+ * double or float, and defaults to float.
+ */
+public class PackedCoordinateSequenceFactory implements
+    CoordinateSequenceFactory
+{
+  public static final int DOUBLE = 0;
+  public static final int FLOAT = 1;
+
+  public static final PackedCoordinateSequenceFactory DOUBLE_FACTORY =
+      new PackedCoordinateSequenceFactory(DOUBLE);
+  public static final PackedCoordinateSequenceFactory FLOAT_FACTORY =
+      new PackedCoordinateSequenceFactory(FLOAT);
+
+  private int type = DOUBLE;
+  private int dimension = 3;
+
+  /**
+   * Creates a new PackedCoordinateSequenceFactory
+   * of type DOUBLE.
+   */
+  public PackedCoordinateSequenceFactory()
+  {
+    this(DOUBLE);
+  }
+
+  /**
+   * Creates a new PackedCoordinateSequenceFactory
+   * of the given type.
+   * Acceptable type values are
+   * {@linkplain PackedCoordinateSequenceFactory#Float}or
+   * {@linkplain PackedCoordinateSequenceFactory#Double}
+   */
+  public PackedCoordinateSequenceFactory(int type)
+  {
+    this(type, 3);
+  }
+  /**
+   * Creates a new PackedCoordinateSequenceFactory
+   * of the given type.
+   * Acceptable type values are
+   * {@linkplain PackedCoordinateSequenceFactory#FLOAT}or
+   * {@linkplain PackedCoordinateSequenceFactory#DOUBLE}
+   */
+  public PackedCoordinateSequenceFactory(int type, int dimension)
+  {
+    setType(type);
+    setDimension(dimension);
+  }
+
+  /**
+   * Returns the type of packed coordinate sequences this factory builds, either
+   * {@linkplain PackedCoordinateSequenceFactory#Float} or
+   * {@linkplain PackedCoordinateSequenceFactory#Double}
+   */
+  public int getType() {
+    return type;
+  }
+
+  /**
+   * Sets the type of packed coordinate sequences this factory builds,
+   * acceptable values are {@linkplain PackedCoordinateSequenceFactory#Float}or
+   * {@linkplain PackedCoordinateSequenceFactory#Double}
+   */
+  public void setType(int type) {
+    if (type != DOUBLE && type != FLOAT)
+      throw new IllegalArgumentException("Unknown type " + type);
+    this.type = type;
+  }
+
+
+  public int getDimension() { return dimension; }
+
+  public void setDimension(int dimension) { this.dimension = dimension; }
+
+  /**
+   * @see com.vividsolutions.jts.geom.CoordinateSequenceFactory#create(com.vividsolutions.jts.geom.Coordinate[])
+   */
+  public CoordinateSequence create(Coordinate[] coordinates) {
+    if (type == DOUBLE) {
+      return new PackedCoordinateSequence.Double(coordinates, dimension);
+    } else {
+      return new PackedCoordinateSequence.Float(coordinates, dimension);
+    }
+  }
+
+  /**
+   * @see com.vividsolutions.jts.geom.CoordinateSequenceFactory#create(com.vividsolutions.jts.geom.CoordinateSequence)
+   */
+  public CoordinateSequence create(CoordinateSequence coordSeq) {
+    if (type == DOUBLE) {
+      return new PackedCoordinateSequence.Double(coordSeq.toCoordinateArray(), dimension);
+    } else {
+      return new PackedCoordinateSequence.Float(coordSeq.toCoordinateArray(), dimension);
+    }
+  }
+
+  /**
+   * @see com.vividsolutions.jts.geom.CoordinateSequenceFactory#create(double[],
+   *      int)
+   */
+  public CoordinateSequence create(double[] packedCoordinates, int dimension) {
+    if (type == DOUBLE) {
+      return new PackedCoordinateSequence.Double(packedCoordinates, dimension);
+    } else {
+      return new PackedCoordinateSequence.Float(packedCoordinates, dimension);
+    }
+  }
+
+  /**
+   * @see com.vividsolutions.jts.geom.CoordinateSequenceFactory#create(float[],
+   *      int)
+   */
+  public CoordinateSequence create(float[] packedCoordinates, int dimension) {
+    if (type == DOUBLE) {
+      return new PackedCoordinateSequence.Double(packedCoordinates, dimension);
+    } else {
+      return new PackedCoordinateSequence.Float(packedCoordinates, dimension);
+    }
+  }
+
+  /**
+   * @see com.vividsolutions.jts.geom.CoordinateSequenceFactory#create(int, int)
+   */
+  public CoordinateSequence create(int size, int dimension) {
+    if (type == DOUBLE) {
+      return new PackedCoordinateSequence.Double(size, dimension);
+    } else {
+      return new PackedCoordinateSequence.Float(size, dimension);
+    }
+  }
+}
\ No newline at end of file

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/package.html
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/package.html	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/package.html	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,25 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+<!--
+
+-->
+</head>
+<body bgcolor="white">
+
+Contains the <CODE>Geometry</CODE> interface hierarchy and supporting classes.
+<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.
+
+<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/geom/util/GeometryEditor.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/util/GeometryEditor.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/util/GeometryEditor.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,263 @@
+
+/*
+ * 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.util;
+
+import com.vividsolutions.jts.geom.*;
+import com.vividsolutions.jts.util.Assert;
+
+import java.util.ArrayList;
+
+
+/**
+ * Supports creating a new {@link Geometry} which is a modification of an existing one.
+ * Geometry objects are intended to be treated as immutable.
+ * This class allows you to "modify" a Geometry
+ * by traversing it and creating a new Geometry with the same overall structure but
+ * possibly modified components.
+ * The following kinds of modifications can be made:
+ * <ul>
+ * <li>the values of the coordinates may be changed.
+ * Changing coordinate values may make the result Geometry invalid;
+ * this is not checked by the GeometryEditor
+ * <li>the coordinate lists may be changed
+ * (e.g. by adding or deleting coordinates).
+ * The modifed coordinate lists must be consistent with their original parent component
+ * (e.g. a LinearRing must always have at least 4 coordinates, and the first and last
+ * coordinate must be equal)
+ * <li>components of the original geometry may be deleted
+ * (e.g. holes may be removed from a Polygon, or LineStrings removed from a MultiLineString).
+ * Deletions will be propagated up the component tree appropriately.
+ * </ul>
+ * Note that all changes must be consistent with the original Geometry's structure
+ * (e.g. a Polygon cannot be collapsed into a LineString).
+ * <p>
+ * The resulting Geometry is not checked for validity.
+ * If validity needs to be enforced, the new Geometry's #isValid should be checked.
+ *
+ * @see Geometry#isValid
+ *
+ * @version 1.6
+ */
+public class GeometryEditor
+{
+  /**
+   * The factory used to create the modified Geometry
+   */
+  private GeometryFactory factory = null;
+
+  /**
+   * Creates a new GeometryEditor object which will create
+   * an edited {@link Geometry} with the same {@link GeometryFactory} as the input Geometry.
+   */
+  public GeometryEditor()
+  {
+  }
+
+  /**
+   * Creates a new GeometryEditor object which will create
+   * the edited Geometry with the given {@link GeometryFactory}
+   *
+   * @param factory the GeometryFactory to create the edited Geometry with
+   */
+  public GeometryEditor(GeometryFactory factory)
+  {
+    this.factory = factory;
+  }
+
+  /**
+   * Edit the input {@link Geometry} with the given edit operation.
+   * Clients will create subclasses of {@link GeometryEditorOperation} or
+   * {@link CoordinateOperation} to perform required modifications.
+   *
+   * @param geometry the Geometry to edit
+   * @param operation the edit operation to carry out
+   * @return a new {@link Geometry} which is the result of the editing
+   */
+  public Geometry edit(Geometry geometry, GeometryEditorOperation operation)
+  {
+    // if client did not supply a GeometryFactory, use the one from the input Geometry
+    if (factory == null)
+      factory = geometry.getFactory();
+
+    if (geometry instanceof GeometryCollection) {
+      return editGeometryCollection((GeometryCollection) geometry,
+                                    operation);
+    }
+
+    if (geometry instanceof Polygon) {
+      return editPolygon((Polygon) geometry, operation);
+    }
+
+    if (geometry instanceof Point) {
+      return operation.edit(geometry, factory);
+    }
+
+    if (geometry instanceof LineString) {
+      return operation.edit(geometry, factory);
+    }
+
+    Assert.shouldNeverReachHere(
+        "Unsupported Geometry classes should be caught in the GeometryEditorOperation.");
+
+    return null;
+  }
+
+  private Polygon editPolygon(Polygon polygon,
+                              GeometryEditorOperation operation) {
+    Polygon newPolygon = (Polygon) operation.edit(polygon, factory);
+
+    if (newPolygon.isEmpty()) {
+      //RemoveSelectedPlugIn relies on this behaviour. [Jon Aquino]
+      return newPolygon;
+    }
+
+    LinearRing shell = (LinearRing) edit(newPolygon.getExteriorRing(),
+        operation);
+
+    if (shell.isEmpty()) {
+      //RemoveSelectedPlugIn relies on this behaviour. [Jon Aquino]
+      return factory.createPolygon(null, null);
+    }
+
+    ArrayList holes = new ArrayList();
+
+    for (int i = 0; i < newPolygon.getNumInteriorRing(); i++) {
+      LinearRing hole = (LinearRing) edit(newPolygon.getInteriorRingN(i),
+      operation);
+
+      if (hole.isEmpty()) {
+        continue;
+      }
+
+      holes.add(hole);
+    }
+
+    return factory.createPolygon(shell,
+                                 (LinearRing[]) holes.toArray(new LinearRing[] {  }));
+  }
+
+  private GeometryCollection editGeometryCollection(
+      GeometryCollection collection, GeometryEditorOperation operation) {
+    GeometryCollection newCollection = (GeometryCollection) operation.edit(collection,
+        factory);
+    ArrayList geometries = new ArrayList();
+
+    for (int i = 0; i < newCollection.getNumGeometries(); i++) {
+      Geometry geometry = edit(newCollection.getGeometryN(i), operation);
+
+      if (geometry.isEmpty()) {
+        continue;
+      }
+
+      geometries.add(geometry);
+    }
+
+    if (newCollection.getClass() == MultiPoint.class) {
+      return factory.createMultiPoint((Point[]) geometries.toArray(
+            new Point[] {  }));
+    }
+
+    if (newCollection.getClass() == MultiLineString.class) {
+      return factory.createMultiLineString((LineString[]) geometries.toArray(
+            new LineString[] {  }));
+    }
+
+    if (newCollection.getClass() == MultiPolygon.class) {
+      return factory.createMultiPolygon((Polygon[]) geometries.toArray(
+            new Polygon[] {  }));
+    }
+
+    return factory.createGeometryCollection((Geometry[]) geometries.toArray(
+          new Geometry[] {  }));
+  }
+
+  /**
+   * A interface which specifies an edit operation for Geometries.
+   *
+   * @version 1.6
+   */
+  public interface GeometryEditorOperation
+  {
+    /**
+     * Edits a Geometry by returning a new Geometry with a modification.
+     * The returned Geometry might be the same as the Geometry passed in.
+     *
+     * @param geometry the Geometry to modify
+     * @param factory the factory with which to construct the modified Geometry
+     * (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);
+  }
+
+  /**
+   * A {@link GeometryEditorOperation} which modifies the coordinate list of a {@link Geometry}.
+   * Operates on Geometry subclasses which contains a single coordinate list.
+   */
+  public abstract static class CoordinateOperation
+      implements GeometryEditorOperation
+  {
+    public Geometry edit(Geometry geometry, GeometryFactory factory) {
+      if (geometry instanceof LinearRing) {
+        return factory.createLinearRing(edit(geometry.getCoordinates(),
+            geometry));
+      }
+
+      if (geometry instanceof LineString) {
+        return factory.createLineString(edit(geometry.getCoordinates(),
+            geometry));
+      }
+
+      if (geometry instanceof Point) {
+        Coordinate[] newCoordinates = edit(geometry.getCoordinates(),
+            geometry);
+
+        return factory.createPoint((newCoordinates.length > 0)
+                                   ? newCoordinates[0] : null);
+      }
+
+      return geometry;
+    }
+
+    /**
+     * Edits the array of {@link Coordinate}s from a {@link Geometry}.
+     *
+     * @param coordinates the coordinate array to operate on
+     * @param geometry the geometry containing the coordinate list
+     * @return an edited coordinate array (which may be the same as the input)
+     */
+    public abstract Coordinate[] edit(Coordinate[] coordinates,
+                                      Geometry geometry);
+  }
+}

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/util/GeometryTransformer.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,229 @@
+package com.vividsolutions.jts.geom.util;
+
+import java.util.*;
+import com.vividsolutions.jts.geom.*;
+
+/**
+ * A framework for processes which transform an input {@link Geometry} into
+ * an output {@link Geometry}, possibly changing its structure and type(s).
+ * This class is a framework for implementing subclasses
+ * which perform transformations on
+ * 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
+ * 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.
+ * <p>
+ * A typically usage would be a transformation that may transform Polygons into
+ * Polygons, LineStrings
+ * or Points.  This class would likely need to override the {@link transformMultiPolygon}
+ * method to ensure that if input Polygons change type the result is a GeometryCollection,
+ * not a MultiPolygon
+ * <p>
+ * The default behaviour of this class is to simply recursively transform
+ * each Geometry component into an identical object by copying.
+ * <p>
+ * Note that all <code>transformX</code> methods may return <code>null</code>,
+ * to avoid creating empty geometry objects. This will be handled correctly
+ * by the transformer.
+ * The @link transform} method itself will always
+ * return a geometry object.
+ *
+ * @version 1.6
+ *
+ * @see GeometryEditor
+ */
+public class GeometryTransformer {
+
+  /**
+   * Possible extensions:
+   * getParent() method to return immediate parent e.g. of LinearRings in Polygons
+   */
+
+  private Geometry inputGeom;
+
+  protected GeometryFactory factory = null;
+
+  // these could eventually be exposed to clients
+  /**
+   * <code>true</code> if empty geometries should not be included in the result
+   */
+  private boolean pruneEmptyGeometry = true;
+
+  /**
+   * <code>true</code> if a homogenous collection result
+   * from a {@link GeometryCollection} should still
+   * be a general GeometryCollection
+   */
+  private boolean preserveGeometryCollectionType = true;
+
+  /**
+   * <code>true</code> if the output from a collection argument should still be a collection
+   */
+  private boolean preserveCollections = false;
+
+  /**
+   * <code>true</code> if the type of the input should be preserved
+   */
+  private boolean preserveType = false;
+
+  public GeometryTransformer() {
+  }
+
+  public Geometry getInputGeometry() { return inputGeom; }
+
+  public final Geometry transform(Geometry inputGeom)
+  {
+    this.inputGeom = inputGeom;
+    this.factory = inputGeom.getFactory();
+
+    if (inputGeom instanceof Point)
+      return transformPoint((Point) inputGeom, null);
+    if (inputGeom instanceof MultiPoint)
+      return transformMultiPoint((MultiPoint) inputGeom, null);
+    if (inputGeom instanceof LinearRing)
+      return transformLinearRing((LinearRing) inputGeom, null);
+    if (inputGeom instanceof LineString)
+      return transformLineString((LineString) inputGeom, null);
+    if (inputGeom instanceof MultiLineString)
+      return transformMultiLineString((MultiLineString) inputGeom, null);
+    if (inputGeom instanceof Polygon)
+      return transformPolygon((Polygon) inputGeom, null);
+    if (inputGeom instanceof MultiPolygon)
+      return transformMultiPolygon((MultiPolygon) inputGeom, null);
+    if (inputGeom instanceof GeometryCollection)
+      return transformGeometryCollection((GeometryCollection) inputGeom, null);
+
+    throw new IllegalArgumentException("Unknown Geometry subtype: " + inputGeom.getClass().getName());
+  }
+
+  /**
+   * Convenience method which provides statndard way of
+   * creating a {@link CoordinateSequence}
+   *
+   * @param coords the coordinate array to copy
+   * @return a coordinate sequence for the array
+   */
+  protected final CoordinateSequence createCoordinateSequence(Coordinate[] coords)
+  {
+    return factory.getCoordinateSequenceFactory().create(coords);
+  }
+
+  /**
+   * Convenience method which provides statndard way of copying {@link CoordinateSequence}s
+   * @param seq the sequence to copy
+   * @return a deep copy of the sequence
+   */
+  protected final CoordinateSequence copy(CoordinateSequence seq)
+  {
+    return (CoordinateSequence) seq.clone();
+  }
+
+  protected CoordinateSequence transformCoordinates(CoordinateSequence coords, Geometry parent)
+  {
+    return copy(coords);
+  }
+
+  protected Geometry transformPoint(Point geom, Geometry parent) {
+    return factory.createPoint(
+        transformCoordinates(geom.getCoordinateSequence(), geom));
+  }
+
+  protected Geometry transformMultiPoint(MultiPoint geom, Geometry parent) {
+    List transGeomList = new ArrayList();
+    for (int i = 0; i < geom.getNumGeometries(); i++) {
+      Geometry transformGeom = transformPoint((Point) geom.getGeometryN(i), geom);
+      if (transformGeom == null) continue;
+      if (transformGeom.isEmpty()) continue;
+      transGeomList.add(transformGeom);
+    }
+    return factory.buildGeometry(transGeomList);
+  }
+
+  protected Geometry transformLinearRing(LinearRing geom, Geometry parent) {
+    CoordinateSequence seq = transformCoordinates(geom.getCoordinateSequence(), geom);
+    int seqSize = seq.size();
+    // ensure a valid LinearRing
+    if (seqSize > 0 && seqSize < 4 && ! preserveType)
+      return factory.createLineString(seq);
+    return factory.createLinearRing(seq);
+
+  }
+
+  protected Geometry transformLineString(LineString geom, Geometry parent) {
+    // should check for 1-point sequences and downgrade them to points
+    return factory.createLineString(
+        transformCoordinates(geom.getCoordinateSequence(), geom));
+  }
+
+  protected Geometry transformMultiLineString(MultiLineString geom, Geometry parent) {
+    List transGeomList = new ArrayList();
+    for (int i = 0; i < geom.getNumGeometries(); i++) {
+      Geometry transformGeom = transformLineString((LineString) geom.getGeometryN(i), geom);
+      if (transformGeom == null) continue;
+      if (transformGeom.isEmpty()) continue;
+      transGeomList.add(transformGeom);
+    }
+    return factory.buildGeometry(transGeomList);
+  }
+
+  protected Geometry transformPolygon(Polygon geom, Geometry parent) {
+    boolean isAllValidLinearRings = true;
+    Geometry shell = transformLinearRing((LinearRing) geom.getExteriorRing(), geom);
+
+    if (shell == null
+        || ! (shell instanceof LinearRing)
+        || shell.isEmpty() )
+      isAllValidLinearRings = false;
+//return factory.createPolygon(null, null);
+
+    ArrayList holes = new ArrayList();
+    for (int i = 0; i < geom.getNumInteriorRing(); i++) {
+      Geometry hole = transformLinearRing((LinearRing) geom.getInteriorRingN(i), geom);
+      if (hole == null || hole.isEmpty()) {
+        continue;
+      }
+      if (! (hole instanceof LinearRing))
+        isAllValidLinearRings = false;
+
+      holes.add(hole);
+    }
+
+    if (isAllValidLinearRings)
+      return factory.createPolygon((LinearRing) shell,
+                                   (LinearRing[]) holes.toArray(new LinearRing[] {  }));
+    else {
+      List components = new ArrayList();
+      if (shell != null) components.add(shell);
+      components.addAll(holes);
+      return factory.buildGeometry(components);
+    }
+  }
+
+  protected Geometry transformMultiPolygon(MultiPolygon geom, Geometry parent) {
+    List transGeomList = new ArrayList();
+    for (int i = 0; i < geom.getNumGeometries(); i++) {
+      Geometry transformGeom = transformPolygon((Polygon) geom.getGeometryN(i), geom);
+      if (transformGeom == null) continue;
+      if (transformGeom.isEmpty()) continue;
+      transGeomList.add(transformGeom);
+    }
+    return factory.buildGeometry(transGeomList);
+  }
+
+  protected Geometry transformGeometryCollection(GeometryCollection geom, Geometry parent) {
+    List transGeomList = new ArrayList();
+    for (int i = 0; i < geom.getNumGeometries(); i++) {
+      Geometry transformGeom = transform(geom.getGeometryN(i));
+      if (transformGeom == null) continue;
+      if (pruneEmptyGeometry && transformGeom.isEmpty()) continue;
+      transGeomList.add(transformGeom);
+    }
+    if (preserveGeometryCollectionType)
+      return factory.createGeometryCollection(GeometryFactory.toGeometryArray(transGeomList));
+    return factory.buildGeometry(transGeomList);
+  }
+
+}

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/util/LinearComponentExtracter.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,79 @@
+
+/*
+ * 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.util;
+
+import java.util.*;
+import com.vividsolutions.jts.geom.*;
+
+/**
+ * Extracts all the 1-dimensional ({@link LineString}) components from a {@link Geometry}.
+ *
+ * @version 1.6
+ */
+public class LinearComponentExtracter
+  implements GeometryComponentFilter
+{
+
+  /**
+   * Extracts the linear components from a single geometry.
+   * If more than one geometry is to be processed, it is more
+   * efficient to create a single {@link LineExtracterFilter} instance
+   * and pass it to multiple geometries.
+   *
+   * @param geom the geometry from which to extract linear components
+   * @return the list of linear components
+   */
+  public static List getLines(Geometry geom)
+  {
+    List lines = new ArrayList();
+    geom.apply(new LinearComponentExtracter(lines));
+    return lines;
+  }
+
+  private List lines;
+
+  /**
+   * Constructs a LineExtracterFilter with a list in which to store LineStrings found.
+   */
+  public LinearComponentExtracter(List lines)
+  {
+    this.lines = lines;
+  }
+
+  public void filter(Geometry geom)
+  {
+    if (geom instanceof LineString) lines.add(geom);
+  }
+
+}

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/util/PointExtracter.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,74 @@
+
+/*
+ * 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.util;
+
+import java.util.*;
+import com.vividsolutions.jts.geom.*;
+
+/**
+ * Extracts all the 0-dimensional ({@link Point}) components from a {@link Geometry}.
+ *
+ * @version 1.6
+ */
+public class PointExtracter
+  implements GeometryFilter
+{
+  /**
+   * Returns the Point components from a single geometry.
+   * If more than one geometry is to be processed, it is more
+   * efficient to create a single {@link PointExtracterFilter} instance
+   * and pass it to multiple geometries.
+   */
+  public static List getPoints(Geometry geom)
+  {
+    List pts = new ArrayList();
+    geom.apply(new PointExtracter(pts));
+    return pts;
+  }
+
+  private List pts;
+  /**
+   * Constructs a PointExtracterFilter with a list in which to store Points found.
+   */
+  public PointExtracter(List pts)
+  {
+    this.pts = pts;
+  }
+
+  public void filter(Geometry geom)
+  {
+    if (geom instanceof Point) pts.add(geom);
+  }
+
+}

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/util/PolygonExtracter.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,74 @@
+
+/*
+ * 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.util;
+
+import java.util.*;
+import com.vividsolutions.jts.geom.*;
+
+/**
+ * Extracts all the 2-dimensional ({@link Polygon}) components from a {@link Geometry}.
+ *
+ * @version 1.6
+ */
+public class PolygonExtracter
+  implements GeometryFilter
+{
+  /**
+   * Returns the Polygon components from a single geometry.
+   * If more than one geometry is to be processed, it is more
+   * efficient to create a single {@link PolygonExtracterFilter} instance
+   * and pass it to multiple geometries.
+   */
+  public static List getPolygons(Geometry geom)
+  {
+    List comps = new ArrayList();
+    geom.apply(new PolygonExtracter(comps));
+    return comps;
+  }
+
+  private List comps;
+  /**
+   * Constructs a PolygonExtracterFilter with a list in which to store Polygons found.
+   */
+  public PolygonExtracter(List comps)
+  {
+    this.comps = comps;
+  }
+
+  public void filter(Geometry geom)
+  {
+    if (geom instanceof Polygon) comps.add(geom);
+  }
+
+}

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/util/ShortCircuitedGeometryVisitor.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,36 @@
+package com.vividsolutions.jts.geom.util;
+
+import com.vividsolutions.jts.geom.*;
+
+/**
+ * A visitor to {@link Geometry} elements which can
+ * be short-circuited by a given condition
+ *
+ * @version 1.6
+ */
+public abstract class ShortCircuitedGeometryVisitor
+{
+  private boolean isDone = false;
+
+  public ShortCircuitedGeometryVisitor() {
+  }
+
+  public void applyTo(Geometry geom) {
+    for (int i = 0; i < geom.getNumGeometries() && ! isDone; i++) {
+      Geometry element = geom.getGeometryN(i);
+      if (! (element instanceof GeometryCollection)) {
+        visit(element);
+        if (isDone()) {
+          isDone = true;
+          return;
+        }
+      }
+      else
+        applyTo(element);
+    }
+  }
+
+  protected abstract void visit(Geometry element);
+
+  protected abstract boolean isDone();
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/util/package.html
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/util/package.html	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geom/util/package.html	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,13 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+<!--
+
+-->
+</head>
+<body bgcolor="white">
+
+Provides classes that parse and modify Geometry objects.
+
+</body>
+</html>

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/Depth.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/Depth.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/Depth.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,159 @@
+
+
+/*
+ * 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.geomgraph;
+
+import com.vividsolutions.jts.geomgraph.Position;
+import com.vividsolutions.jts.geom.Location;
+
+/**
+ * A Depth object records the topological depth of the sides
+ * of an Edge for up to two Geometries.
+ * @version 1.6
+ */
+public class Depth {
+
+  private final static int NULL = -1;
+
+  public static int depthAtLocation(int location)
+  {
+    if (location == Location.EXTERIOR) return 0;
+    if (location == Location.INTERIOR) return 1;
+    return NULL;
+  }
+
+  private int[][] depth = new int[2][3];
+
+  public Depth() {
+    // initialize depth array to a sentinel value
+    for (int i = 0; i < 2; i++) {
+      for (int j = 0; j < 3; j++) {
+        depth[i][j] = NULL;
+      }
+    }
+  }
+
+  public int getDepth(int geomIndex, int posIndex)
+  {
+    return depth[geomIndex][posIndex];
+  }
+  public void setDepth(int geomIndex, int posIndex, int depthValue)
+  {
+    depth[geomIndex][posIndex] = depthValue;
+  }
+  public int getLocation(int geomIndex, int posIndex)
+  {
+    if (depth[geomIndex][posIndex] <= 0) return Location.EXTERIOR;
+    return Location.INTERIOR;
+  }
+  public void add(int geomIndex, int posIndex, int location)
+  {
+    if (location == Location.INTERIOR)
+      depth[geomIndex][posIndex]++;
+  }
+  /**
+   * A Depth object is null (has never been initialized) if all depths are null.
+   */
+  public boolean isNull()
+  {
+    for (int i = 0; i < 2; i++) {
+      for (int j = 0; j < 3; j++) {
+        if (depth[i][j] != NULL)
+          return false;
+      }
+    }
+    return true;
+  }
+  public boolean isNull(int geomIndex)
+  {
+    return depth[geomIndex][1] == NULL;
+  }
+  public boolean isNull(int geomIndex, int posIndex)
+  {
+    return depth[geomIndex][posIndex] == NULL;
+  }
+  public void add(Label lbl)
+  {
+    for (int i = 0; i < 2; i++) {
+      for (int j = 1; j < 3; j++) {
+        int loc = lbl.getLocation(i, j);
+        if (loc == Location.EXTERIOR || loc == Location.INTERIOR) {
+          // initialize depth if it is null, otherwise add this location value
+          if (isNull(i, j)) {
+            depth[i][j] = depthAtLocation(loc);
+          }
+          else
+            depth[i][j] += depthAtLocation(loc);
+        }
+      }
+    }
+  }
+  public int getDelta(int geomIndex)
+  {
+    return depth[geomIndex][Position.RIGHT] - depth[geomIndex][Position.LEFT];
+  }
+  /**
+   * Normalize the depths for each geometry, if they are non-null.
+   * A normalized depth
+   * has depth values in the set { 0, 1 }.
+   * Normalizing the depths
+   * involves reducing the depths by the same amount so that at least
+   * one of them is 0.  If the remaining value is > 0, it is set to 1.
+   */
+  public void normalize()
+  {
+    for (int i = 0; i < 2; i++) {
+      if (! isNull(i)) {
+        int minDepth = depth[i][1];
+        if (depth[i][2] < minDepth)
+          minDepth = depth[i][2];
+
+        if (minDepth < 0) minDepth = 0;
+        for (int j = 1; j < 3; j++) {
+          int newValue = 0;
+          if (depth[i][j] > minDepth)
+            newValue = 1;
+          depth[i][j] = newValue;
+        }
+      }
+    }
+  }
+
+  public String toString()
+  {
+    return
+        "A: " + depth[0][1] + "," + depth[0][2]
+      + " B: " + depth[1][1] + "," + depth[1][2];
+  }
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/DirectedEdge.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/DirectedEdge.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/DirectedEdge.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,239 @@
+
+
+
+/*
+ * 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.geomgraph;
+
+import java.io.PrintStream;
+import com.vividsolutions.jts.util.*;
+import com.vividsolutions.jts.geom.*;
+
+
+/**
+ * @version 1.6
+ */
+public class DirectedEdge
+  extends EdgeEnd
+{
+
+  /**
+   * Computes the factor for the change in depth when moving from one location to another.
+   * E.g. if crossing from the INTERIOR to the EXTERIOR the depth decreases, so the factor is -1
+   */
+  public static int depthFactor(int currLocation, int nextLocation)
+  {
+    if (currLocation == Location.EXTERIOR && nextLocation == Location.INTERIOR)
+       return 1;
+    else if (currLocation == Location.INTERIOR && nextLocation == Location.EXTERIOR)
+       return -1;
+    return 0;
+  }
+
+  protected boolean isForward;
+  private boolean isInResult = false;
+  private boolean isVisited = false;
+
+  private DirectedEdge sym; // the symmetric edge
+  private DirectedEdge next;  // the next edge in the edge ring for the polygon containing this edge
+  private DirectedEdge nextMin;  // the next edge in the MinimalEdgeRing that contains this edge
+  private EdgeRing edgeRing;  // the EdgeRing that this edge is part of
+  private EdgeRing minEdgeRing;  // the MinimalEdgeRing that this edge is part of
+  /**
+   * The depth of each side (position) of this edge.
+   * The 0 element of the array is never used.
+   */
+  private int[] depth = { 0, -999, -999 };
+
+  public DirectedEdge(Edge edge, boolean isForward)
+  {
+    super(edge);
+    this.isForward = isForward;
+    if (isForward) {
+      init(edge.getCoordinate(0), edge.getCoordinate(1));
+    }
+    else {
+      int n = edge.getNumPoints() - 1;
+      init(edge.getCoordinate(n), edge.getCoordinate(n-1));
+    }
+    computeDirectedLabel();
+  }
+  public Edge getEdge() { return edge; }
+  public void setInResult(boolean isInResult) { this.isInResult = isInResult; }
+  public boolean isInResult() { return isInResult; }
+  public boolean isVisited() { return isVisited; }
+  public void setVisited(boolean isVisited) { this.isVisited = isVisited; }
+  public void setEdgeRing(EdgeRing edgeRing) { this.edgeRing = edgeRing; }
+  public EdgeRing getEdgeRing() { return edgeRing; }
+  public void setMinEdgeRing(EdgeRing minEdgeRing) { this.minEdgeRing = minEdgeRing; }
+  public EdgeRing getMinEdgeRing() { return minEdgeRing; }
+  public int getDepth(int position) { return depth[position]; }
+
+  public void setDepth(int position, int depthVal)
+  {
+    if (depth[position] != -999) {
+      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());
+    }
+    depth[position] = depthVal;
+  }
+
+  public int getDepthDelta()
+  {
+    int depthDelta = edge.getDepthDelta();
+    if (! isForward) depthDelta = -depthDelta;
+    return depthDelta;
+  }
+
+  /**
+   * setVisitedEdge marks both DirectedEdges attached to a given Edge.
+   * This is used for edges corresponding to lines, which will only
+   * appear oriented in a single direction in the result.
+   */
+  public void setVisitedEdge(boolean isVisited)
+  {
+    setVisited(isVisited);
+    sym.setVisited(isVisited);
+  }
+  /**
+   * Each Edge gives rise to a pair of symmetric DirectedEdges, in opposite
+   * directions.
+   * @return the DirectedEdge for the same Edge but in the opposite direction
+   */
+  public DirectedEdge getSym() { return sym; }
+  public boolean isForward() { return isForward; }
+  public void setSym(DirectedEdge de)
+  {
+    sym = de;
+  }
+  public DirectedEdge getNext() { return next; }
+  public void setNext(DirectedEdge next) { this.next = next; }
+  public DirectedEdge getNextMin() { return nextMin; }
+  public void setNextMin(DirectedEdge nextMin) { this.nextMin = nextMin; }
+
+  /**
+   * This edge is a line edge if
+   * <ul>
+   * <li> at least one of the labels is a line label
+   * <li> any labels which are not line labels have all Locations = EXTERIOR
+   * </ul>
+   */
+  public boolean isLineEdge()
+  {
+    boolean isLine = label.isLine(0) || label.isLine(1);
+    boolean isExteriorIfArea0 =
+      ! label.isArea(0) || label.allPositionsEqual(0, Location.EXTERIOR);
+    boolean isExteriorIfArea1 =
+      ! label.isArea(1) || label.allPositionsEqual(1, Location.EXTERIOR);
+
+    return isLine && isExteriorIfArea0 && isExteriorIfArea1;
+  }
+  /**
+   * This is an interior Area edge if
+   * <ul>
+   * <li> its label is an Area label for both Geometries
+   * <li> and for each Geometry both sides are in the interior.
+   * </ul>
+   *
+   * @return true if this is an interior Area edge
+   */
+  public boolean isInteriorAreaEdge()
+  {
+    boolean isInteriorAreaEdge = true;
+    for (int i = 0; i < 2; i++) {
+      if (! ( label.isArea(i)
+            && label.getLocation(i, Position.LEFT ) == Location.INTERIOR
+            && label.getLocation(i, Position.RIGHT) == Location.INTERIOR) ) {
+        isInteriorAreaEdge = false;
+      }
+    }
+    return isInteriorAreaEdge;
+  }
+
+  /**
+   * Compute the label in the appropriate orientation for this DirEdge
+   */
+  private void computeDirectedLabel()
+  {
+    label = new Label(edge.getLabel());
+    if (! isForward)
+      label.flip();
+  }
+
+  /**
+   * Set both edge depths.  One depth for a given side is provided.  The other is
+   * computed depending on the Location transition and the depthDelta of the edge.
+   */
+  public void setEdgeDepths(int position, int depth)
+  {
+    // get the depth transition delta from R to L for this directed Edge
+    int depthDelta = getEdge().getDepthDelta();
+    if (! isForward) depthDelta = -depthDelta;
+
+    // if moving from L to R instead of R to L must change sign of delta
+    int directionFactor = 1;
+    if (position == Position.LEFT)
+      directionFactor = -1;
+
+    int oppositePos = Position.opposite(position);
+    int delta = depthDelta * directionFactor;
+    //TESTINGint delta = depthDelta * DirectedEdge.depthFactor(loc, oppositeLoc);
+    int oppositeDepth = depth + delta;
+    setDepth(position, depth);
+    setDepth(oppositePos, oppositeDepth);
+  }
+
+  public void print(PrintStream out)
+  {
+    super.print(out);
+    out.print(" " + depth[Position.LEFT] + "/" + depth[Position.RIGHT]);
+    out.print(" (" + getDepthDelta() + ")");
+    //out.print(" " + this.hashCode());
+    //if (next != null) out.print(" next:" + next.hashCode());
+    if (isInResult) out.print(" inResult");
+  }
+  public void printEdge(PrintStream out)
+  {
+    print(out);
+    out.print(" ");
+    if (isForward)
+      edge.print(out);
+    else
+      edge.printReverse(out);
+  }
+
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/DirectedEdgeStar.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/DirectedEdgeStar.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/DirectedEdgeStar.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,406 @@
+
+
+
+/*
+ * 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.geomgraph;
+
+import java.io.PrintStream;
+import java.util.*;
+import com.vividsolutions.jts.geom.*;
+import com.vividsolutions.jts.util.*;
+
+/**
+ * A DirectedEdgeStar is an ordered list of <b>outgoing</b> DirectedEdges around a node.
+ * It supports labelling the edges as well as linking the edges to form both
+ * MaximalEdgeRings and MinimalEdgeRings.
+ *
+ * @version 1.6
+ */
+public class DirectedEdgeStar
+  extends EdgeEndStar
+{
+
+  /**
+   * A list of all outgoing edges in the result, in CCW order
+   */
+  private List resultAreaEdgeList;
+  private Label label;
+
+  public DirectedEdgeStar() {
+  }
+  /**
+   * Insert a directed edge in the list
+   */
+  public void insert(EdgeEnd ee)
+  {
+    DirectedEdge de = (DirectedEdge) ee;
+    insertEdgeEnd(de, de);
+  }
+
+  public Label getLabel() { return label; }
+
+  public int getOutgoingDegree()
+  {
+    int degree = 0;
+    for (Iterator it = iterator(); it.hasNext(); ) {
+      DirectedEdge de = (DirectedEdge) it.next();
+      if (de.isInResult()) degree++;
+    }
+    return degree;
+  }
+  public int getOutgoingDegree(EdgeRing er)
+  {
+    int degree = 0;
+    for (Iterator it = iterator(); it.hasNext(); ) {
+      DirectedEdge de = (DirectedEdge) it.next();
+      if (de.getEdgeRing() == er) degree++;
+    }
+    return degree;
+  }
+
+  public DirectedEdge getRightmostEdge()
+  {
+    List edges = getEdges();
+    int size = edges.size();
+    if (size < 1) return null;
+    DirectedEdge de0 = (DirectedEdge) edges.get(0);
+    if (size == 1) return de0;
+    DirectedEdge deLast = (DirectedEdge) edges.get(size - 1);
+
+    int quad0 = de0.getQuadrant();
+    int quad1 = deLast.getQuadrant();
+    if (Quadrant.isNorthern(quad0) && Quadrant.isNorthern(quad1))
+      return de0;
+    else if (! Quadrant.isNorthern(quad0) && ! Quadrant.isNorthern(quad1))
+      return deLast;
+    else {
+      // edges are in different hemispheres - make sure we return one that is non-horizontal
+      //Assert.isTrue(de0.getDy() != 0, "should never return horizontal edge!");
+      DirectedEdge nonHorizontalEdge = null;
+      if (de0.getDy() != 0)
+        return de0;
+      else if (deLast.getDy() != 0)
+        return deLast;
+    }
+    Assert.shouldNeverReachHere("found two horizontal edges incident on node");
+    return null;
+
+  }
+  /**
+   * Compute the labelling for all dirEdges in this star, as well
+   * as the overall labelling
+   */
+  public void computeLabelling(GeometryGraph[] geom)
+  {
+//Debug.print(this);
+    super.computeLabelling(geom);
+
+    // determine the overall labelling for this DirectedEdgeStar
+    // (i.e. for the node it is based at)
+    label = new Label(Location.NULL);
+    for (Iterator it = iterator(); it.hasNext(); ) {
+      EdgeEnd ee = (EdgeEnd) it.next();
+      Edge e = ee.getEdge();
+      Label eLabel = e.getLabel();
+      for (int i = 0; i < 2; i++) {
+        int eLoc = eLabel.getLocation(i);
+        if (eLoc == Location.INTERIOR || eLoc == Location.BOUNDARY)
+          label.setLocation(i, Location.INTERIOR);
+      }
+    }
+//Debug.print(this);
+  }
+
+  /**
+   * For each dirEdge in the star,
+   * merge the label from the sym dirEdge into the label
+   */
+  public void mergeSymLabels()
+  {
+    for (Iterator it = iterator(); it.hasNext(); ) {
+      DirectedEdge de = (DirectedEdge) it.next();
+      Label label = de.getLabel();
+      label.merge(de.getSym().getLabel());
+    }
+  }
+
+  /**
+   * Update incomplete dirEdge labels from the labelling for the node
+   */
+  public void updateLabelling(Label nodeLabel)
+  {
+    for (Iterator it = iterator(); it.hasNext(); ) {
+      DirectedEdge de = (DirectedEdge) it.next();
+      Label label = de.getLabel();
+      label.setAllLocationsIfNull(0, nodeLabel.getLocation(0));
+      label.setAllLocationsIfNull(1, nodeLabel.getLocation(1));
+    }
+  }
+
+  private List getResultAreaEdges()
+  {
+//print(System.out);
+    if (resultAreaEdgeList != null) return resultAreaEdgeList;
+    resultAreaEdgeList = new ArrayList();
+    for (Iterator it = iterator(); it.hasNext(); ) {
+      DirectedEdge de = (DirectedEdge) it.next();
+      if (de.isInResult() || de.getSym().isInResult() )
+        resultAreaEdgeList.add(de);
+    }
+    return resultAreaEdgeList;
+  }
+
+  private final int SCANNING_FOR_INCOMING = 1;
+  private final int LINKING_TO_OUTGOING = 2;
+  /**
+   * Traverse the star of DirectedEdges, linking the included edges together.
+   * To link two dirEdges, the <next> pointer for an incoming dirEdge
+   * is set to the next outgoing edge.
+   * <p>
+   * DirEdges are only linked if:
+   * <ul>
+   * <li>they belong to an area (i.e. they have sides)
+   * <li>they are marked as being in the result
+   * </ul>
+   * <p>
+   * Edges are linked in CCW order (the order they are stored).
+   * This means that rings have their face on the Right
+   * (in other words,
+   * the topological location of the face is given by the RHS label of the DirectedEdge)
+   * <p>
+   * PRECONDITION: No pair of dirEdges are both marked as being in the result
+   */
+  public void linkResultDirectedEdges()
+  {
+    // make sure edges are copied to resultAreaEdges list
+    getResultAreaEdges();
+    // find first area edge (if any) to start linking at
+    DirectedEdge firstOut = null;
+    DirectedEdge incoming = null;
+    int state = SCANNING_FOR_INCOMING;
+    // link edges in CCW order
+    for (int i = 0; i < resultAreaEdgeList.size(); i++) {
+      DirectedEdge nextOut = (DirectedEdge) resultAreaEdgeList.get(i);
+      DirectedEdge nextIn = nextOut.getSym();
+
+      // skip de's that we're not interested in
+      if (! nextOut.getLabel().isArea()) continue;
+
+      // record first outgoing edge, in order to link the last incoming edge
+      if (firstOut == null && nextOut.isInResult()) firstOut = nextOut;
+      // assert: sym.isInResult() == false, since pairs of dirEdges should have been removed already
+
+      switch (state) {
+      case SCANNING_FOR_INCOMING:
+        if (! nextIn.isInResult()) continue;
+        incoming = nextIn;
+        state = LINKING_TO_OUTGOING;
+        break;
+      case LINKING_TO_OUTGOING:
+        if (! nextOut.isInResult()) continue;
+        incoming.setNext(nextOut);
+        state = SCANNING_FOR_INCOMING;
+        break;
+      }
+    }
+//Debug.print(this);
+    if (state == LINKING_TO_OUTGOING) {
+//Debug.print(firstOut == null, this);
+      if (firstOut == null)
+        throw new TopologyException("no outgoing dirEdge found", getCoordinate());
+      //Assert.isTrue(firstOut != null, "no outgoing dirEdge found (at " + getCoordinate() );
+      Assert.isTrue(firstOut.isInResult(), "unable to link last incoming dirEdge");
+      incoming.setNext(firstOut);
+    }
+  }
+  public void linkMinimalDirectedEdges(EdgeRing er)
+  {
+    // find first area edge (if any) to start linking at
+    DirectedEdge firstOut = null;
+    DirectedEdge incoming = null;
+    int state = SCANNING_FOR_INCOMING;
+    // link edges in CW order
+    for (int i = resultAreaEdgeList.size() - 1; i >= 0; i--) {
+      DirectedEdge nextOut = (DirectedEdge) resultAreaEdgeList.get(i);
+      DirectedEdge nextIn = nextOut.getSym();
+
+      // record first outgoing edge, in order to link the last incoming edge
+      if (firstOut == null && nextOut.getEdgeRing() == er) firstOut = nextOut;
+
+      switch (state) {
+      case SCANNING_FOR_INCOMING:
+        if (nextIn.getEdgeRing() != er) continue;
+        incoming = nextIn;
+        state = LINKING_TO_OUTGOING;
+        break;
+      case LINKING_TO_OUTGOING:
+        if (nextOut.getEdgeRing() != er) continue;
+        incoming.setNextMin(nextOut);
+        state = SCANNING_FOR_INCOMING;
+        break;
+      }
+    }
+//print(System.out);
+    if (state == LINKING_TO_OUTGOING) {
+      Assert.isTrue(firstOut != null, "found null for first outgoing dirEdge");
+      Assert.isTrue(firstOut.getEdgeRing() == er, "unable to link last incoming dirEdge");
+      incoming.setNextMin(firstOut);
+    }
+  }
+  public void linkAllDirectedEdges()
+  {
+    getEdges();
+    // find first area edge (if any) to start linking at
+    DirectedEdge prevOut = null;
+    DirectedEdge firstIn = null;
+    // link edges in CW order
+    for (int i = edgeList.size() - 1; i >= 0; i--) {
+      DirectedEdge nextOut = (DirectedEdge) edgeList.get(i);
+      DirectedEdge nextIn = nextOut.getSym();
+      if (firstIn == null) firstIn = nextIn;
+      if (prevOut != null) nextIn.setNext(prevOut);
+      // record outgoing edge, in order to link the last incoming edge
+      prevOut = nextOut;
+    }
+    firstIn.setNext(prevOut);
+//Debug.print(this);
+  }
+
+  /**
+   * Traverse the star of edges, maintaing the current location in the result
+   * area at this node (if any).
+   * If any L edges are found in the interior of the result, mark them as covered.
+   */
+  public void findCoveredLineEdges()
+  {
+//Debug.print("findCoveredLineEdges");
+//Debug.print(this);
+    // 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
+
+    /**
+     * Find first DirectedEdge of result area (if any).
+     * The interior of the result is on the RHS of the edge,
+     * so the start location will be:
+     * - INTERIOR if the edge is outgoing
+     * - EXTERIOR if the edge is incoming
+     */
+    int startLoc = Location.NULL ;
+    for (Iterator it = iterator(); it.hasNext(); ) {
+      DirectedEdge nextOut  = (DirectedEdge) it.next();
+      DirectedEdge nextIn   = nextOut.getSym();
+      if (! nextOut.isLineEdge()) {
+        if (nextOut.isInResult()) {
+          startLoc = Location.INTERIOR;
+          break;
+        }
+        if (nextIn.isInResult()) {
+          startLoc = Location.EXTERIOR;
+          break;
+        }
+      }
+    }
+    // no A edges found, so can't determine if L edges are covered or not
+    if (startLoc == Location.NULL) return;
+
+    /**
+     * move around ring, keeping track of the current location
+     * (Interior or Exterior) for the result area.
+     * If L edges are found, mark them as covered if they are in the interior
+     */
+    int currLoc = startLoc;
+    for (Iterator it = iterator(); it.hasNext(); ) {
+      DirectedEdge nextOut  = (DirectedEdge) it.next();
+      DirectedEdge nextIn   = nextOut.getSym();
+      if (nextOut.isLineEdge()) {
+        nextOut.getEdge().setCovered(currLoc == Location.INTERIOR);
+//Debug.println(nextOut);
+      }
+      else {  // edge is an Area edge
+        if (nextOut.isInResult())
+          currLoc = Location.EXTERIOR;
+        if (nextIn.isInResult())
+          currLoc = Location.INTERIOR;
+      }
+    }
+  }
+
+  public void computeDepths(DirectedEdge de)
+  {
+    int edgeIndex = findIndex(de);
+    Label label = de.getLabel();
+    int startDepth = de.getDepth(Position.LEFT);
+    int targetLastDepth = de.getDepth(Position.RIGHT);
+    // compute the depths from this edge up to the end of the edge array
+    int nextDepth = computeDepths(edgeIndex + 1, edgeList.size(), startDepth);
+    // compute the depths for the initial part of the array
+    int lastDepth = computeDepths(0, edgeIndex, nextDepth);
+//Debug.print(lastDepth != targetLastDepth, this);
+//Debug.print(lastDepth != targetLastDepth, "mismatch: " + lastDepth + " / " + targetLastDepth);
+    if (lastDepth != targetLastDepth)
+      throw new TopologyException("depth mismatch at " + de.getCoordinate());
+    //Assert.isTrue(lastDepth == targetLastDepth, "depth mismatch at " + de.getCoordinate());
+  }
+
+  /**
+   * Compute the DirectedEdge depths for a subsequence of the edge array.
+   *
+   * @return the last depth assigned (from the R side of the last edge visited)
+   */
+  private int computeDepths(int startIndex, int endIndex, int startDepth)
+  {
+    int currDepth = startDepth;
+    for (int i = startIndex; i < endIndex ; i++) {
+      DirectedEdge nextDe = (DirectedEdge) edgeList.get(i);
+      Label label = nextDe.getLabel();
+      nextDe.setEdgeDepths(Position.RIGHT, currDepth);
+      currDepth = nextDe.getDepth(Position.LEFT);
+    }
+    return currDepth;
+  }
+
+  public void print(PrintStream out)
+  {
+    System.out.println("DirectedEdgeStar: " + getCoordinate());
+    for (Iterator it = iterator(); it.hasNext(); ) {
+      DirectedEdge de = (DirectedEdge) it.next();
+      out.print("out ");
+      de.print(out);
+      out.println();
+      out.print("in ");
+      de.getSym().print(out);
+      out.println();
+    }
+  }
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/Edge.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/Edge.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/Edge.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,279 @@
+
+
+
+/*
+ * 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.geomgraph;
+
+import java.io.PrintStream;
+import java.util.Iterator;
+import com.vividsolutions.jts.algorithm.LineIntersector;
+import com.vividsolutions.jts.geom.*;
+import com.vividsolutions.jts.util.*;
+import com.vividsolutions.jts.geomgraph.*;
+import com.vividsolutions.jts.geomgraph.index.*;
+
+
+/**
+ * @version 1.6
+ */
+public class Edge
+  extends GraphComponent
+{
+
+  /**
+   * Updates an IM from the label for an edge.
+   * Handles edges from both L and A geometries.
+   */
+  public static void updateIM(Label label, IntersectionMatrix im)
+  {
+    im.setAtLeastIfValid(label.getLocation(0, Position.ON), label.getLocation(1, Position.ON), 1);
+    if (label.isArea()) {
+      im.setAtLeastIfValid(label.getLocation(0, Position.LEFT),  label.getLocation(1, Position.LEFT),   2);
+      im.setAtLeastIfValid(label.getLocation(0, Position.RIGHT), label.getLocation(1, Position.RIGHT),  2);
+    }
+  }
+
+  Coordinate[] pts;
+  private Envelope env;
+  EdgeIntersectionList eiList = new EdgeIntersectionList(this);
+  private String name;
+  private MonotoneChainEdge mce;
+  private boolean isIsolated = true;
+  private Depth depth = new Depth();
+  private int depthDelta = 0;   // the change in area depth from the R to L side of this edge
+
+  public Edge(Coordinate[] pts, Label label)
+  {
+    this.pts = pts;
+    this.label = label;
+  }
+  public Edge(Coordinate[] pts)
+  {
+    this(pts, null);
+  }
+
+  public int getNumPoints() { return pts.length; }
+  public void setName(String name) { this.name = name; }
+  public Coordinate[] getCoordinates()  {    return pts;  }
+  public Coordinate getCoordinate(int i)
+  {
+    return pts[i];
+  }
+  public Coordinate getCoordinate()
+  {
+    if (pts.length > 0) return pts[0];
+    return null;
+  }
+  public Envelope getEnvelope()
+  {
+    // compute envelope lazily
+    if (env == null) {
+      env = new Envelope();
+      for (int i = 0; i < pts.length; i++) {
+        env.expandToInclude(pts[i]);
+      }
+    }
+    return env;
+  }
+
+  public Depth getDepth() { return depth; }
+
+  /**
+   * The depthDelta is the change in depth as an edge is crossed from R to L
+   * @return the change in depth as the edge is crossed from R to L
+   */
+  public int getDepthDelta()  { return depthDelta;  }
+  public void setDepthDelta(int depthDelta)  { this.depthDelta = depthDelta;  }
+
+  public int getMaximumSegmentIndex()
+  {
+    return pts.length - 1;
+  }
+  public EdgeIntersectionList getEdgeIntersectionList() { return eiList; }
+
+  public MonotoneChainEdge getMonotoneChainEdge()
+  {
+    if (mce == null) mce = new MonotoneChainEdge(this);
+    return mce;
+  }
+
+  public boolean isClosed()
+  {
+    return pts[0].equals(pts[pts.length - 1]);
+  }
+  /**
+   * An Edge is collapsed if it is an Area edge and it consists of
+   * two segments which are equal and opposite (eg a zero-width V).
+   */
+  public boolean isCollapsed()
+  {
+    if (! label.isArea()) return false;
+    if (pts.length != 3) return false;
+    if (pts[0].equals(pts[2]) ) return true;
+    return false;
+  }
+  public Edge getCollapsedEdge()
+  {
+    Coordinate newPts[] = new Coordinate[2];
+    newPts[0] = pts[0];
+    newPts[1] = pts[1];
+    Edge newe = new Edge(newPts, Label.toLineLabel(label));
+    return newe;
+  }
+
+  public void setIsolated(boolean isIsolated)
+  {
+    this.isIsolated = isIsolated;
+  }
+  public boolean isIsolated()
+  {
+    return isIsolated;
+  }
+
+  /**
+   * Adds EdgeIntersections for one or both
+   * intersections found for a segment of an edge to the edge intersection list.
+   */
+  public void addIntersections(LineIntersector li, int segmentIndex, int geomIndex)
+  {
+    for (int i = 0; i < li.getIntersectionNum(); i++) {
+      addIntersection(li, segmentIndex, geomIndex, i);
+    }
+  }
+  /**
+   * 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(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.
+      */
+      EdgeIntersection ei = eiList.add(intPt, normalizedSegmentIndex, dist);
+//ei.print(System.out);
+
+  }
+
+  /**
+   * Update the IM with the contribution for this component.
+   * A component only contributes if it has a labelling for both parent geometries
+   */
+  public void computeIM(IntersectionMatrix im)
+  {
+    updateIM(label, im);
+  }
+
+  /**
+   * equals is defined to be:
+   * <p>
+   * e1 equals e2
+   * <b>iff</b>
+   * the coordinates of e1 are the same or the reverse of the coordinates in e2
+   */
+  public boolean equals(Object o)
+  {
+    if (! (o instanceof Edge)) return false;
+    Edge e = (Edge) o;
+
+    if (pts.length != e.pts.length) return false;
+
+    boolean isEqualForward = true;
+    boolean isEqualReverse = true;
+    int iRev = pts.length;
+    for (int i = 0; i < pts.length; i++) {
+      if (! pts[i].equals2D(e.pts[i])) {
+         isEqualForward = false;
+      }
+      if (! pts[i].equals2D(e.pts[--iRev])) {
+         isEqualReverse = false;
+      }
+      if (! isEqualForward && ! isEqualReverse) return false;
+    }
+    return true;
+  }
+
+  /**
+   * @return true if the coordinate sequences of the Edges are identical
+   */
+  public boolean isPointwiseEqual(Edge e)
+  {
+    if (pts.length != e.pts.length) return false;
+
+    for (int i = 0; i < pts.length; i++) {
+      if (! pts[i].equals2D(e.pts[i])) {
+         return false;
+      }
+    }
+    return true;
+  }
+
+  public void print(PrintStream out)
+  {
+    out.print("edge " + name + ": ");
+    out.print("LINESTRING (");
+    for (int i = 0; i < pts.length; i++) {
+      if (i > 0) out.print(",");
+      out.print(pts[i].x + " " + pts[i].y);
+    }
+    out.print(")  " + label + " " + depthDelta);
+  }
+  public void printReverse(PrintStream out)
+  {
+    out.print("edge " + name + ": ");
+    for (int i = pts.length - 1; i >= 0; i--) {
+      out.print(pts[i] + " ");
+    }
+    out.println("");
+  }
+
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/EdgeEnd.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/EdgeEnd.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/EdgeEnd.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,145 @@
+
+
+
+/*
+ * 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.geomgraph;
+
+import java.io.PrintStream;
+import com.vividsolutions.jts.geom.*;
+import com.vividsolutions.jts.algorithm.*;
+import com.vividsolutions.jts.util.*;
+import com.vividsolutions.jts.geomgraph.Label;
+import com.vividsolutions.jts.geomgraph.Edge;
+
+/**
+ * Models the end of an edge incident on a node.
+ * EdgeEnds have a direction
+ * determined by the direction of the ray from the initial
+ * point to the next point.
+ * 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
+ */
+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;
+
+  private Node node;          // the node this edge end originates at
+  private Coordinate p0, p1;  // points of initial line segment
+  private double dx, dy;      // the direction vector for this edge from its starting point
+  private int quadrant;
+
+  protected EdgeEnd(Edge edge)
+  {
+    this.edge = edge;
+  }
+  public EdgeEnd(Edge edge, Coordinate p0, Coordinate p1) {
+    this(edge, p0, p1, null);
+  }
+  public EdgeEnd(Edge edge, Coordinate p0, Coordinate p1, Label label) {
+    this(edge);
+    init(p0, p1);
+    this.label = label;
+  }
+
+  protected void init(Coordinate p0, Coordinate p1)
+  {
+    this.p0 = p0;
+    this.p1 = p1;
+    dx = p1.x - p0.x;
+    dy = p1.y - p0.y;
+    quadrant = Quadrant.quadrant(dx, dy);
+    Assert.isTrue(! (dx == 0 && dy == 0), "EdgeEnd with identical endpoints found");
+  }
+
+  public Edge getEdge() { return edge; }
+  public Label getLabel() { return label; }
+  public Coordinate getCoordinate() { return p0; }
+  public Coordinate getDirectedCoordinate() { return p1; }
+  public int getQuadrant() { return quadrant; }
+  public double getDx() { return dx; }
+  public double getDy() { return dy; }
+
+  public void setNode(Node node) { this.node = node; }
+  public Node getNode() { return node; }
+
+  public int compareTo(Object obj)
+  {
+      EdgeEnd e = (EdgeEnd) obj;
+      return compareDirection(e);
+  }
+  /**
+   * Implements the total order relation:
+   * <p>
+   *    a has a greater angle with the positive x-axis than b
+   * <p>
+   * Using the obvious algorithm of simply computing the angle is not robust,
+   * since the angle calculation is obviously susceptible to roundoff.
+   * A robust algorithm is:
+   * - first compare the quadrant.  If the quadrants
+   * are different, it it trivial to determine which vector is "greater".
+   * - if the vectors lie in the same quadrant, the computeOrientation function
+   * can be used to decide the relative orientation of the vectors.
+   */
+  public int compareDirection(EdgeEnd e)
+  {
+    if (dx == e.dx && dy == e.dy)
+      return 0;
+    // if the rays are in different quadrants, determining the ordering is trivial
+    if (quadrant > e.quadrant) return 1;
+    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 CGAlgorithms.computeOrientation(e.p0, e.p1, p1);
+  }
+
+  public void computeLabel()
+  {
+    // subclasses should override this if they are using labels
+  }
+  public void print(PrintStream out)
+  {
+    double angle = Math.atan2(dy, dx);
+    String className = getClass().getName();
+    int lastDotPos = className.lastIndexOf('.');
+    String name = className.substring(lastDotPos + 1);
+    out.print("  " + name + ": " + p0 + " - " + p1 + " " + quadrant + ":" + angle + "   " + label);
+  }
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/EdgeEndStar.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/EdgeEndStar.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/EdgeEndStar.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,336 @@
+
+
+
+/*
+ * 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.geomgraph;
+
+import java.io.PrintStream;
+import java.util.*;
+import com.vividsolutions.jts.geom.*;
+import com.vividsolutions.jts.algorithm.SimplePointInAreaLocator;
+import com.vividsolutions.jts.util.*;
+
+/**
+ * A EdgeEndStar is an ordered list of EdgeEnds around a node.
+ * They are maintained in CCW order (starting with the positive x-axis) around the node
+ * for efficient lookup and topology building.
+ *
+ * @version 1.6
+ */
+abstract public class EdgeEndStar
+{
+
+  /**
+   * A map which maintains the edges in sorted order around the node
+   */
+  protected Map edgeMap = new TreeMap();
+  /**
+   * A list of all outgoing edges in the result, in CCW order
+   */
+  protected List edgeList;
+  /**
+   * The location of the point for this star in Geometry i Areas
+   */
+  private int[] ptInAreaLocation = { Location.NULL, Location.NULL };
+
+  public EdgeEndStar()
+  {
+
+  }
+
+  /**
+   * Insert a EdgeEnd into this EdgeEndStar
+   */
+  abstract public void insert(EdgeEnd e);
+
+  /**
+   * Insert an EdgeEnd into the map, and clear the edgeList cache,
+   * since the list of edges has now changed
+   */
+  protected void insertEdgeEnd(EdgeEnd e, Object obj)
+  {
+    edgeMap.put(e, obj);
+    edgeList = null;  // edge list has changed - clear the cache
+  }
+
+  /**
+   * @return the coordinate for the node this star is based at
+   */
+  public Coordinate getCoordinate()
+  {
+    Iterator it = iterator();
+    if (! it.hasNext()) return null;
+    EdgeEnd e = (EdgeEnd) it.next();
+    return e.getCoordinate();
+  }
+  public int getDegree()
+  {
+    return edgeMap.size();
+  }
+
+  /**
+   * Iterator access to the ordered list of edges is optimized by
+   * copying the map collection to a list.  (This assumes that
+   * once an iterator is requested, it is likely that insertion into
+   * the map is complete).
+   */
+  public Iterator iterator()
+  {
+    return getEdges().iterator();
+  }
+  public List getEdges()
+  {
+    if (edgeList == null) {
+      edgeList = new ArrayList(edgeMap.values());
+    }
+    return edgeList;
+  }
+  public EdgeEnd getNextCW(EdgeEnd ee)
+  {
+    getEdges();
+    int i = edgeList.indexOf(ee);
+    int iNextCW = i - 1;
+    if (i == 0)
+      iNextCW = edgeList.size() - 1;
+    return (EdgeEnd) edgeList.get(iNextCW);
+  }
+
+  public void computeLabelling(GeometryGraph[] geom)
+  {
+    computeEdgeEndLabels();
+    // Propagate side labels  around the edges in the star
+    // for each parent Geometry
+//Debug.print(this);
+    propagateSideLabels(0);
+//Debug.print(this);
+//Debug.printIfWatch(this);
+    propagateSideLabels(1);
+//Debug.print(this);
+//Debug.printIfWatch(this);
+
+    /**
+     * If there are edges that still have null labels for a geometry
+     * this must be because there are no area edges for that geometry incident on this node.
+     * In this case, to label the edge for that geometry we must test whether the
+     * edge is in the interior of the geometry.
+     * To do this it suffices to determine whether the node for the edge is in the interior of an area.
+     * If so, the edge has location INTERIOR for the geometry.
+     * In all other cases (e.g. the node is on a line, on a point, or not on the geometry at all) the edge
+     * has the location EXTERIOR for the geometry.
+     * <p>
+     * Note that the edge cannot be on the BOUNDARY of the geometry, since then
+     * there would have been a parallel edge from the Geometry at this node also labelled BOUNDARY
+     * and this edge would have been labelled in the previous step.
+     * <p>
+     * This code causes a problem when dimensional collapses are present, since it may try and
+     * determine the location of a node where a dimensional collapse has occurred.
+     * The point should be considered to be on the EXTERIOR
+     * of the polygon, but locate() will return INTERIOR, since it is passed
+     * the original Geometry, not the collapsed version.
+     *
+     * If there are incident edges which are Line edges labelled BOUNDARY,
+     * then they must be edges resulting from dimensional collapses.
+     * In this case the other edges can be labelled EXTERIOR for this Geometry.
+     *
+     * MD 8/11/01 - NOT TRUE!  The collapsed edges may in fact be in the interior of the Geometry,
+     * which means the other edges should be labelled INTERIOR for this Geometry.
+     * Not sure how solve this...  Possibly labelling needs to be split into several phases:
+     * area label propagation, symLabel merging, then finally null label resolution.
+     */
+    boolean[] hasDimensionalCollapseEdge = { false, false };
+    for (Iterator it = iterator(); it.hasNext(); ) {
+      EdgeEnd e = (EdgeEnd) it.next();
+      Label label = e.getLabel();
+      for (int geomi = 0; geomi < 2; geomi++) {
+        if (label.isLine(geomi) && label.getLocation(geomi) == Location.BOUNDARY)
+          hasDimensionalCollapseEdge[geomi] = true;
+      }
+    }
+//Debug.print(this);
+    for (Iterator it = iterator(); it.hasNext(); ) {
+      EdgeEnd e = (EdgeEnd) it.next();
+      Label label = e.getLabel();
+//Debug.println(e);
+      for (int geomi = 0; geomi < 2; geomi++) {
+        if (label.isAnyNull(geomi)) {
+          int loc = Location.NULL;
+          if (hasDimensionalCollapseEdge[geomi]) {
+            loc = Location.EXTERIOR;
+          }
+          else {
+            Coordinate p = e.getCoordinate();
+            loc = getLocation(geomi, p, geom);
+          }
+          label.setAllLocationsIfNull(geomi, loc);
+        }
+      }
+//Debug.println(e);
+    }
+//Debug.print(this);
+//Debug.printIfWatch(this);
+  }
+
+  private void computeEdgeEndLabels()
+  {
+    // Compute edge label for each EdgeEnd
+    for (Iterator it = iterator(); it.hasNext(); ) {
+      EdgeEnd ee = (EdgeEnd) it.next();
+      ee.computeLabel();
+    }
+  }
+  int getLocation(int geomIndex, Coordinate p, GeometryGraph[] geom)
+  {
+    // compute location only on demand
+    if (ptInAreaLocation[geomIndex] == Location.NULL) {
+      ptInAreaLocation[geomIndex] = SimplePointInAreaLocator.locate(p, geom[geomIndex].getGeometry());
+    }
+    return ptInAreaLocation[geomIndex];
+  }
+
+  public boolean isAreaLabelsConsistent()
+  {
+    computeEdgeEndLabels();
+    return checkAreaLabelsConsistent(0);
+  }
+
+  private boolean checkAreaLabelsConsistent(int geomIndex)
+  {
+    // 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
+    List edges = getEdges();
+    // if no edges, trivially consistent
+    if (edges.size() <= 0)
+      return true;
+    // initialize startLoc to location of last L side (if any)
+    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");
+
+    int currLoc = startLoc;
+    for (Iterator it = iterator(); it.hasNext(); ) {
+      EdgeEnd e = (EdgeEnd) it.next();
+      Label label = e.getLabel();
+      // we assume that we are only checking a area
+      Assert.isTrue(label.isArea(geomIndex), "Found non-area edge");
+      int leftLoc   = label.getLocation(geomIndex, Position.LEFT);
+      int rightLoc  = label.getLocation(geomIndex, Position.RIGHT);
+//System.out.println(leftLoc + " " + rightLoc);
+//Debug.print(this);
+      // check that edge is really a boundary between inside and outside!
+      if (leftLoc == rightLoc) {
+        return false;
+      }
+      // check side location conflict
+      //Assert.isTrue(rightLoc == currLoc, "side location conflict " + locStr);
+      if (rightLoc != currLoc) {
+//Debug.print(this);
+        return false;
+      }
+      currLoc = leftLoc;
+    }
+    return true;
+  }
+  void propagateSideLabels(int geomIndex)
+  {
+    // 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 ;
+    // 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)
+        startLoc = label.getLocation(geomIndex, Position.LEFT);
+    }
+    // no labelled sides found, so no labels to propagate
+    if (startLoc == Location.NULL) 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)
+          label.setLocation(geomIndex, Position.ON, currLoc);
+      // set side labels (if any)
+     // if (label.isArea()) {   //ORIGINAL
+      if (label.isArea(geomIndex)) {
+        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) {
+//Debug.print(rightLoc != currLoc, this);
+          if (rightLoc != currLoc)
+            throw new TopologyException("side location conflict", e.getCoordinate());
+          if (leftLoc == Location.NULL) {
+            Assert.shouldNeverReachHere("found single null side (at " + e.getCoordinate() + ")");
+          }
+          currLoc = leftLoc;
+        }
+        else {
+          /** RHS is null - LHS must be null too.
+           *  This must be an edge from the other geometry, which has no location
+           *  labelling for this geometry.  This edge must lie wholly inside or outside
+           *  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");
+          label.setLocation(geomIndex, Position.RIGHT, currLoc);
+          label.setLocation(geomIndex, Position.LEFT, currLoc);
+        }
+      }
+    }
+  }
+
+  public int findIndex(EdgeEnd eSearch)
+  {
+    iterator();   // force edgelist to be computed
+    for (int i = 0; i < edgeList.size(); i++ ) {
+      EdgeEnd e = (EdgeEnd) edgeList.get(i);
+      if (e == eSearch) return i;
+    }
+    return -1;
+  }
+
+  public void print(PrintStream out)
+  {
+    System.out.println("EdgeEndStar:   " + getCoordinate());
+    for (Iterator it = iterator(); it.hasNext(); ) {
+      EdgeEnd e = (EdgeEnd) it.next();
+      e.print(out);
+    }
+  }
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/EdgeIntersection.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/EdgeIntersection.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/EdgeIntersection.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,102 @@
+
+
+
+/*
+ * 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.geomgraph;
+
+import java.io.PrintStream;
+import com.vividsolutions.jts.geom.Coordinate;
+
+/**
+ * An EdgeIntersection 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
+ */
+public class EdgeIntersection
+    implements Comparable
+{
+
+  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)
+  {
+    EdgeIntersection other = (EdgeIntersection) obj;
+    return compare(other.segmentIndex, other.dist);
+  }
+  /**
+   * @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 isEndPoint(int maxSegmentIndex)
+  {
+    if (segmentIndex == 0 && dist == 0.0) return true;
+    if (segmentIndex == maxSegmentIndex) return true;
+    return false;
+  }
+
+  public void print(PrintStream out)
+  {
+    out.print(coord);
+    out.print(" seg # = " + segmentIndex);
+    out.println(" dist = " + dist);
+  }
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/EdgeIntersectionList.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/EdgeIntersectionList.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/EdgeIntersectionList.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -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.geomgraph;
+
+import java.io.PrintStream;
+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
+ */
+public class EdgeIntersectionList
+{
+  // a List of EdgeIntersections
+  //List list = new ArrayList();    // more efficient to use a LinkedList, but ArrayList is easier for debugging
+  private Map nodeMap = new TreeMap();
+  Edge edge;  // the parent edge
+
+  public EdgeIntersectionList(Edge edge)
+  {
+    this.edge = 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 EdgeIntersection found or added
+   */
+  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;
+    }
+    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
+   */
+  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.
+   *
+   * @return true if this intersection is already in the list
+   */
+  /*
+  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(); ) {
+      EdgeIntersection ei = (EdgeIntersection) it.next();
+      if (ei.coord.equals(pt))
+       return true;
+    }
+    return false;
+  }
+
+  /**
+   * Adds entries for the first and last points of the edge to the list
+   */
+  public void addEndpoints()
+  {
+    int maxSegIndex = edge.pts.length - 1;
+    add(edge.pts[0], 0, 0.0);
+    add(edge.pts[maxSegIndex], maxSegIndex, 0.0);
+  }
+
+  /**
+   * 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).
+   */
+  public void addSplitEdges(List edgeList)
+  {
+    // ensure that the list has entries for the first and last point of the edge
+    addEndpoints();
+
+    Iterator it = iterator();
+    // there should always be at least two entries in the list
+    EdgeIntersection eiPrev = (EdgeIntersection) it.next();
+    while (it.hasNext()) {
+      EdgeIntersection ei = (EdgeIntersection) it.next();
+      Edge newEdge = createSplitEdge(eiPrev, ei);
+      edgeList.add(newEdge);
+
+      eiPrev = ei;
+    }
+  }
+  /**
+   * Create a new "split edge" with the section of points between
+   * (and including) the two intersections.
+   * The label for the new edge is the same as the label for the parent edge.
+   */
+  Edge createSplitEdge(EdgeIntersection ei0, EdgeIntersection ei1)
+  {
+//Debug.print("\ncreateSplitEdge"); Debug.print(ei0); Debug.print(ei1);
+    int npts = ei1.segmentIndex - ei0.segmentIndex + 2;
+
+    Coordinate lastSegStartPt = edge.pts[ei1.segmentIndex];
+    // if the last intersection point is not equal to the its segment start pt,
+    // 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);
+    if (! useIntPt1) {
+      npts--;
+    }
+
+    Coordinate[] pts = new Coordinate[npts];
+    int ipt = 0;
+    pts[ipt++] = new Coordinate(ei0.coord);
+    for (int i = ei0.segmentIndex + 1; i <= ei1.segmentIndex; i++) {
+      pts[ipt++] = edge.pts[i];
+    }
+    if (useIntPt1) pts[ipt] = ei1.coord;
+    return new Edge(pts, new Label(edge.label));
+  }
+
+  public void print(PrintStream out)
+  {
+    out.println("Intersections:");
+    for (Iterator it = iterator(); it.hasNext(); ) {
+      EdgeIntersection ei = (EdgeIntersection) it.next();
+      ei.print(out);
+    }
+  }
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/EdgeList.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/EdgeList.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/EdgeList.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,137 @@
+
+
+
+/*
+ * 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.geomgraph;
+
+import java.io.PrintStream;
+import java.util.*;
+import com.vividsolutions.jts.geom.Coordinate;
+import com.vividsolutions.jts.index.SpatialIndex;
+import com.vividsolutions.jts.index.quadtree.Quadtree;
+
+/**
+ * A EdgeList is a list of Edges.  It supports locating edges
+ * that are pointwise equals to a target edge.
+ * @version 1.6
+ */
+public class EdgeList
+{
+  private List edges = new ArrayList();
+  /**
+   * An index of the edges, for fast lookup.
+   *
+   * a Quadtree is used, because this index needs to be dynamic
+   * (e.g. allow insertions after queries).
+   * An alternative would be to use an ordered set based on the values
+   * of the edge coordinates
+   *
+   */
+  private SpatialIndex index = new Quadtree();
+
+  public EdgeList() {
+  }
+
+  /**
+   * Insert an edge unless it is already in the list
+   */
+  public void add(Edge e)
+  {
+    edges.add(e);
+    index.insert(e.getEnvelope(), e);
+  }
+
+  public void addAll(Collection edgeColl)
+  {
+    for (Iterator i = edgeColl.iterator(); i.hasNext(); ) {
+      add((Edge) i.next());
+    }
+  }
+
+  public List getEdges() { return edges; }
+
+// <FIX> fast lookup for edges
+  /**
+   * If there is an edge equal to e already in the list, return it.
+   * Otherwise return null.
+   * @return  equal edge, if there is one already in the list
+   *          null otherwise
+   */
+  public Edge findEqualEdge(Edge e)
+  {
+    Collection testEdges = index.query(e.getEnvelope());
+
+    for (Iterator i = testEdges.iterator(); i.hasNext(); ) {
+      Edge testEdge = (Edge) i.next();
+      if (testEdge.equals(e) ) return testEdge;
+    }
+    return null;
+  }
+
+  public Iterator iterator() { return edges.iterator(); }
+
+  public Edge get(int i) { return (Edge) edges.get(i); }
+
+  /**
+   * If the edge e is already in the list, return its index.
+   * @return  index, if e is already in the list
+   *          -1 otherwise
+   */
+  public int findEdgeIndex(Edge e)
+  {
+    for (int i = 0; i < edges.size(); i++) {
+      if ( ((Edge) edges.get(i)).equals(e) ) return i;
+    }
+    return -1;
+  }
+
+  public void print(PrintStream out)
+  {
+    out.print("MULTILINESTRING ( ");
+    for (int j = 0; j < edges.size(); j++) {
+      Edge e = (Edge) edges.get(j);
+      if (j > 0) out.print(",");
+      out.print("(");
+      Coordinate[] pts = e.getCoordinates();
+      for (int i = 0; i < pts.length; i++) {
+        if (i > 0) out.print(",");
+        out.print(pts[i].x + " " + pts[i].y);
+      }
+      out.println(")");
+    }
+    out.print(")  ");
+  }
+
+
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/EdgeNodingValidator.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/EdgeNodingValidator.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/EdgeNodingValidator.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,72 @@
+
+/*
+ * 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.geomgraph;
+
+import java.util.*;
+import com.vividsolutions.jts.algorithm.*;
+import com.vividsolutions.jts.geom.*;
+import com.vividsolutions.jts.noding.*;
+
+/**
+ * Validates that a collection of SegmentStrings is correctly noded.
+ * Throws an appropriate exception if an noding error is found.
+ *
+ * @version 1.6
+ */
+public class EdgeNodingValidator {
+
+  private static Collection toSegmentStrings(Collection edges)
+  {
+    // convert Edges to SegmentStrings
+    Collection segStrings = new ArrayList();
+    for (Iterator i = edges.iterator(); i.hasNext(); ) {
+      Edge e = (Edge) i.next();
+      segStrings.add(new SegmentString(e.getCoordinates(), e));
+    }
+    return segStrings;
+  }
+
+  private NodingValidator nv;
+
+  public EdgeNodingValidator(Collection edges)
+  {
+    nv = new NodingValidator(toSegmentStrings(edges));
+  }
+
+  public void checkValid()
+  {
+    nv.checkValid();
+  }
+
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/EdgeRing.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/EdgeRing.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/EdgeRing.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,243 @@
+
+
+
+/*
+ * 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.geomgraph;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import com.vividsolutions.jts.algorithm.CGAlgorithms;
+import com.vividsolutions.jts.geom.*;
+import com.vividsolutions.jts.util.Assert;
+
+
+/**
+ * @version 1.6
+ */
+public abstract class EdgeRing {
+
+  protected DirectedEdge startDe; // the directed edge which starts the list of edges for this EdgeRing
+  private int maxNodeDegree = -1;
+  private List edges = new ArrayList(); // the DirectedEdges making up this EdgeRing
+  private List pts = new ArrayList();
+  private Label label = new Label(Location.NULL); // label stores the locations of each geometry on the face surrounded by this ring
+  private 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
+  private ArrayList holes = new ArrayList(); // a list of EdgeRings which are holes in this EdgeRing
+
+  protected GeometryFactory geometryFactory;
+  protected CGAlgorithms cga;
+
+  public EdgeRing(DirectedEdge start, GeometryFactory geometryFactory, CGAlgorithms cga) {
+    this.geometryFactory = geometryFactory;
+    this.cga = cga;
+    computePoints(start);
+    computeRing();
+  }
+
+  public boolean isIsolated()
+  {
+    return (label.getGeometryCount() == 1);
+  }
+  public boolean isHole()
+  {
+    //computePoints();
+    return isHole;
+  }
+
+  public Coordinate getCoordinate(int i) { return (Coordinate) pts.get(i);  }
+  public LinearRing getLinearRing() { return ring; }
+  public Label getLabel() { return label; }
+  public boolean isShell() { return shell == null; }
+  public EdgeRing getShell() { return shell; }
+  public void setShell(EdgeRing shell)
+  {
+    this.shell = shell;
+    if (shell != null) shell.addHole(this);
+  }
+  public void addHole(EdgeRing ring) { holes.add(ring); }
+
+  public Polygon toPolygon(GeometryFactory geometryFactory)
+  {
+    LinearRing[] holeLR = new LinearRing[holes.size()];
+    for (int i = 0; i < holes.size(); i++) {
+      holeLR[i] = ((EdgeRing) holes.get(i)).getLinearRing();
+    }
+    Polygon poly = geometryFactory.createPolygon(getLinearRing(), holeLR);
+    return poly;
+  }
+  /**
+   * Compute a LinearRing from the point list previously collected.
+   * Test if the ring is a hole (i.e. if it is CCW) and set the hole flag
+   * accordingly.
+   */
+  public void computeRing()
+  {
+    if (ring != null) return;   // don't compute more than once
+    Coordinate[] coord = new Coordinate[pts.size()];
+    for (int i = 0; i < pts.size(); i++) {
+      coord[i] = (Coordinate) pts.get(i);
+    }
+    ring = geometryFactory.createLinearRing(coord);
+    isHole = cga.isCCW(ring.getCoordinates());
+  }
+  abstract public DirectedEdge getNext(DirectedEdge de);
+  abstract public void setEdgeRing(DirectedEdge de, EdgeRing er);
+
+  /**
+   * Returns the list of DirectedEdges that make up this EdgeRing
+   */
+  public List getEdges() { return edges; }
+
+  /**
+   * Collect all the points from the DirectedEdges of this ring into a contiguous list
+   */
+  protected void computePoints(DirectedEdge start)
+  {
+//System.out.println("buildRing");
+    startDe = start;
+    DirectedEdge de = start;
+    boolean isFirstEdge = true;
+    do {
+      Assert.isTrue(de != null, "found null Directed Edge");
+      if (de.getEdgeRing() == this)
+        throw new TopologyException("Directed Edge visited twice during ring-building at " + de.getCoordinate());
+
+      edges.add(de);
+//Debug.println(de);
+//Debug.println(de.getEdge());
+      Label label = de.getLabel();
+      Assert.isTrue(label.isArea());
+      mergeLabel(label);
+      addPoints(de.getEdge(), de.isForward(), isFirstEdge);
+      isFirstEdge = false;
+      setEdgeRing(de, this);
+      de = getNext(de);
+    } while (de != startDe);
+  }
+
+  public int getMaxNodeDegree()
+  {
+    if (maxNodeDegree < 0) computeMaxNodeDegree();
+    return maxNodeDegree;
+  }
+
+  private void computeMaxNodeDegree()
+  {
+    maxNodeDegree = 0;
+    DirectedEdge de = startDe;
+    do {
+      Node node = de.getNode();
+      int degree = ((DirectedEdgeStar) node.getEdges()).getOutgoingDegree(this);
+      if (degree > maxNodeDegree) maxNodeDegree = degree;
+      de = getNext(de);
+    } while (de != startDe);
+    maxNodeDegree *= 2;
+  }
+
+
+  public void setInResult()
+  {
+    DirectedEdge de = startDe;
+    do {
+      de.getEdge().setInResult(true);
+      de = de.getNext();
+    } while (de != startDe);
+  }
+
+  protected void mergeLabel(Label deLabel)
+  {
+    mergeLabel(deLabel, 0);
+    mergeLabel(deLabel, 1);
+  }
+  /**
+   * Merge the RHS label from a DirectedEdge into the label for this EdgeRing.
+   * The DirectedEdge label may be null.  This is acceptable - it results
+   * from a node which is NOT an intersection node between the Geometries
+   * (e.g. the end node of a LinearRing).  In this case the DirectedEdge label
+   * does not contribute any information to the overall labelling, and is simply skipped.
+   */
+  protected void mergeLabel(Label deLabel, int geomIndex)
+  {
+    int loc = deLabel.getLocation(geomIndex, Position.RIGHT);
+    // no information to be had from this label
+    if (loc == Location.NULL) return;
+    // if there is no current RHS value, set it
+    if (label.getLocation(geomIndex) == Location.NULL) {
+      label.setLocation(geomIndex, loc);
+      return;
+    }
+  }
+  protected void addPoints(Edge edge, boolean isForward, boolean isFirstEdge)
+  {
+    Coordinate[] edgePts = edge.getCoordinates();
+    if (isForward) {
+      int startIndex = 1;
+      if (isFirstEdge) startIndex = 0;
+      for (int i = startIndex; i < edgePts.length; i++) {
+        pts.add(edgePts[i]);
+      }
+    }
+    else { // is backward
+      int startIndex = edgePts.length - 2;
+      if (isFirstEdge) startIndex = edgePts.length - 1;
+      for (int i = startIndex; i >= 0; i--) {
+        pts.add(edgePts[i]);
+      }
+    }
+  }
+
+  /**
+   * This method will cause the ring to be computed.
+   * It will also check any holes, if they have been assigned.
+   */
+  public boolean containsPoint(Coordinate p)
+  {
+    LinearRing shell = getLinearRing();
+    Envelope env = shell.getEnvelopeInternal();
+    if (! env.contains(p)) return false;
+    if (! cga.isPointInRing(p, shell.getCoordinates()) ) return false;
+
+    for (Iterator i = holes.iterator(); i.hasNext(); ) {
+      EdgeRing hole = (EdgeRing) i.next();
+      if (hole.containsPoint(p) )
+        return false;
+    }
+    return true;
+  }
+
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/GeometryGraph.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/GeometryGraph.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/GeometryGraph.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,415 @@
+
+
+
+/*
+ * 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.geomgraph;
+
+import java.util.*;
+import com.vividsolutions.jts.algorithm.LineIntersector;
+import com.vividsolutions.jts.geom.*;
+import com.vividsolutions.jts.geomgraph.index.*;
+import com.vividsolutions.jts.util.*;
+
+/**
+ * A GeometryGraph is a graph that models a given Geometry
+ * @version 1.6
+ */
+public class GeometryGraph
+  extends PlanarGraph
+{
+/**
+ * This method implements the Boundary Determination Rule
+ * for determining whether
+ * a component (node or edge) that appears multiple times in elements
+ * of a MultiGeometry is in the boundary or the interior of the Geometry
+ * <br>
+ * The SFS uses the "Mod-2 Rule", which this function implements
+ * <br>
+ * An alternative (and possibly more intuitive) rule would be
+ * the "At Most One Rule":
+ *    isInBoundary = (componentCount == 1)
+ */
+  public static boolean isInBoundary(int boundaryCount)
+  {
+    // the "Mod-2 Rule"
+    return boundaryCount % 2 == 1;
+  }
+  public static int determineBoundary(int boundaryCount)
+  {
+    return isInBoundary(boundaryCount) ? Location.BOUNDARY : Location.INTERIOR;
+  }
+
+  private Geometry parentGeom;
+  // the precision model of the Geometry represented by this graph
+  //private PrecisionModel precisionModel = null;
+  //private int SRID;
+  /**
+   * The lineEdgeMap is a map of the linestring components of the
+   * parentGeometry to the edges which are derived from them.
+   * This is used to efficiently perform findEdge queries
+   */
+  private Map lineEdgeMap = new HashMap();
+
+  //private PrecisionModel newPM = null;
+  /**
+   * If this flag is true, the Boundary Determination Rule will used when deciding
+   * whether nodes are in the boundary or not
+   */
+  private boolean useBoundaryDeterminationRule = false;
+  private int argIndex;  // the index of this geometry as an argument to a spatial function (used for labelling)
+  private Collection boundaryNodes;
+  private boolean hasTooFewPoints = false;
+  private Coordinate invalidPoint = null;
+
+  private EdgeSetIntersector 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 SimpleMCSweepLineIntersector();
+  }
+
+  public GeometryGraph(int argIndex, Geometry parentGeom) {
+    this.argIndex = argIndex;
+    this.parentGeom = parentGeom;
+    if (parentGeom != null) {
+//      precisionModel = parentGeom.getPrecisionModel();
+//      SRID = parentGeom.getSRID();
+      add(parentGeom);
+    }
+  }
+
+  /**
+   * This constructor is used by clients that wish to add Edges explicitly,
+   * rather than adding a Geometry.  (An example is BufferOp).
+   */
+  // no longer used
+//  public GeometryGraph(int argIndex, PrecisionModel precisionModel, int SRID) {
+//    this(argIndex, null);
+//    this.precisionModel = precisionModel;
+//    this.SRID = SRID;
+//  }
+//  public PrecisionModel getPrecisionModel()
+//  {
+//    return precisionModel;
+//  }
+//  public int getSRID() { return SRID; }
+
+  public boolean hasTooFewPoints() { return hasTooFewPoints; }
+  public Coordinate getInvalidPoint() { return invalidPoint; }
+
+  public Geometry getGeometry() { return parentGeom; }
+
+  public Collection getBoundaryNodes()
+  {
+    if (boundaryNodes == null)
+      boundaryNodes = nodes.getBoundaryNodes(argIndex);
+    return boundaryNodes;
+  }
+
+  public Coordinate[] getBoundaryPoints()
+  {
+    Collection coll = getBoundaryNodes();
+    Coordinate[] pts = new Coordinate[coll.size()];
+    int i = 0;
+    for (Iterator it = coll.iterator(); it.hasNext(); ) {
+      Node node = (Node) it.next();
+      pts[i++] = (Coordinate) node.getCoordinate().clone();
+    }
+    return pts;
+  }
+
+  public Edge findEdge(LineString line)
+  {
+    return (Edge) lineEdgeMap.get(line);
+  }
+
+  public void computeSplitEdges(List edgelist)
+  {
+    for (Iterator i = edges.iterator(); i.hasNext(); ) {
+      Edge e = (Edge) i.next();
+      e.eiList.addSplitEdges(edgelist);
+    }
+  }
+  private void add(Geometry g)
+  {
+    if (g.isEmpty()) return;
+
+    // check if this Geometry should obey the Boundary Determination Rule
+    // all collections except MultiPolygons obey the rule
+    if (g instanceof GeometryCollection
+        && ! (g instanceof MultiPolygon))
+            useBoundaryDeterminationRule = true;
+
+    if (g instanceof Polygon)                 addPolygon((Polygon) g);
+                        // LineString also handles LinearRings
+    else if (g instanceof LineString)         addLineString((LineString) g);
+    else if (g instanceof Point)              addPoint((Point) g);
+    else if (g instanceof MultiPoint)         addCollection((MultiPoint) g);
+    else if (g instanceof MultiLineString)    addCollection((MultiLineString) g);
+    else if (g instanceof MultiPolygon)       addCollection((MultiPolygon) g);
+    else if (g instanceof GeometryCollection) addCollection((GeometryCollection) g);
+    else  throw new UnsupportedOperationException(g.getClass().getName());
+  }
+
+  private void addCollection(GeometryCollection gc)
+  {
+    for (int i = 0; i < gc.getNumGeometries(); i++) {
+      Geometry g = gc.getGeometryN(i);
+      add(g);
+    }
+  }
+  /**
+   * Add a Point to the graph.
+   */
+  private void addPoint(Point p)
+  {
+    Coordinate coord = p.getCoordinate();
+    insertPoint(argIndex, coord, Location.INTERIOR);
+  }
+  /**
+   * The left and right topological location arguments assume that the ring is oriented CW.
+   * If the ring is in the opposite orientation,
+   * the left and right locations must be interchanged.
+   */
+  private void addPolygonRing(LinearRing lr, int cwLeft, int cwRight)
+  {
+    Coordinate[] coord = CoordinateArrays.removeRepeatedPoints(lr.getCoordinates());
+
+    if (coord.length < 4) {
+      hasTooFewPoints = true;
+      invalidPoint = coord[0];
+      return;
+    }
+
+    int left  = cwLeft;
+    int right = cwRight;
+    if (cga.isCCW(coord)) {
+      left = cwRight;
+      right = cwLeft;
+    }
+    Edge e = new Edge(coord,
+                        new Label(argIndex, Location.BOUNDARY, left, right));
+    lineEdgeMap.put(lr, e);
+
+    insertEdge(e);
+    // insert the endpoint as a node, to mark that it is on the boundary
+    insertPoint(argIndex, coord[0], Location.BOUNDARY);
+  }
+
+  private void addPolygon(Polygon p)
+  {
+    addPolygonRing(
+            (LinearRing) p.getExteriorRing(),
+            Location.EXTERIOR,
+            Location.INTERIOR);
+
+    for (int i = 0; i < p.getNumInteriorRing(); i++) {
+      // Holes are topologically labelled opposite to the shell, since
+      // the interior of the polygon lies on their opposite side
+      // (on the left, if the hole is oriented CW)
+      addPolygonRing(
+            (LinearRing) p.getInteriorRingN(i),
+            Location.INTERIOR,
+            Location.EXTERIOR);
+    }
+  }
+
+  private void addLineString(LineString line)
+  {
+    Coordinate[] coord = CoordinateArrays.removeRepeatedPoints(line.getCoordinates());
+
+    if (coord.length < 2) {
+      hasTooFewPoints = true;
+      invalidPoint = coord[0];
+      return;
+    }
+
+    // add the edge for the LineString
+    // line edges do not have locations for their left and right sides
+    Edge e = new Edge(coord, new Label(argIndex, Location.INTERIOR));
+    lineEdgeMap.put(line, e);
+    insertEdge(e);
+    /**
+     * Add the boundary points of the LineString, if any.
+     * Even if the LineString is closed, add both points as if they were endpoints.
+     * This allows for the case that the node already exists and is a boundary point.
+     */
+    Assert.isTrue(coord.length >= 2, "found LineString with single point");
+    insertBoundaryPoint(argIndex, coord[0]);
+    insertBoundaryPoint(argIndex, coord[coord.length - 1]);
+
+  }
+
+  /**
+   * Add an Edge computed externally.  The label on the Edge is assumed
+   * to be correct.
+   */
+  public void addEdge(Edge e)
+  {
+    insertEdge(e);
+    Coordinate[] coord = e.getCoordinates();
+    // insert the endpoint as a node, to mark that it is on the boundary
+    insertPoint(argIndex, coord[0], Location.BOUNDARY);
+    insertPoint(argIndex, coord[coord.length - 1], Location.BOUNDARY);
+  }
+
+  /**
+   * Add a point computed externally.  The point is assumed to be a
+   * Point Geometry part, which has a location of INTERIOR.
+   */
+  public void addPoint(Coordinate pt)
+  {
+    insertPoint(argIndex, pt, Location.INTERIOR);
+  }
+
+  /**
+   * Compute self-nodes, taking advantage of the Geometry type to
+   * minimize the number of intersection tests.  (E.g. rings are
+   * not tested for self-intersection, since they are assumed to be valid).
+   * @param li the LineIntersector to use
+   * @param computeRingSelfNodes if <false>, intersection checks are optimized to not test rings for self-intersection
+   * @return the SegmentIntersector used, containing information about the intersections found
+   */
+  public SegmentIntersector computeSelfNodes(LineIntersector li, boolean computeRingSelfNodes)
+  {
+    SegmentIntersector si = new SegmentIntersector(li, true, false);
+    EdgeSetIntersector esi = createEdgeSetIntersector();
+    // optimized test for Polygons and Rings
+    if (! computeRingSelfNodes
+        && (parentGeom instanceof LinearRing
+        || parentGeom instanceof Polygon
+        || parentGeom instanceof MultiPolygon)) {
+      esi.computeIntersections(edges, si, false);
+    }
+    else {
+      esi.computeIntersections(edges, si, true);
+    }
+//System.out.println("SegmentIntersector # tests = " + si.numTests);
+    addSelfIntersectionNodes(argIndex);
+    return si;
+  }
+
+/* NOT USED
+  public SegmentIntersector computeSelfNodes(LineIntersector li)
+  {
+    return computeSelfNodes(li, false);
+  }
+*/
+  public SegmentIntersector computeEdgeIntersections(
+    GeometryGraph g,
+    LineIntersector li,
+    boolean includeProper)
+  {
+    SegmentIntersector si = new SegmentIntersector(li, includeProper, true);
+    si.setBoundaryNodes(this.getBoundaryNodes(), g.getBoundaryNodes());
+
+    EdgeSetIntersector esi = createEdgeSetIntersector();
+    esi.computeIntersections(edges, g.edges, si);
+/*
+for (Iterator i = g.edges.iterator(); i.hasNext();) {
+Edge e = (Edge) i.next();
+Debug.print(e.getEdgeIntersectionList());
+}
+*/
+    return si;
+  }
+
+  private void insertPoint(int argIndex, Coordinate coord, int onLocation)
+  {
+    Node n = nodes.addNode(coord);
+    Label lbl = n.getLabel();
+    if (lbl == null) {
+      n.label = new Label(argIndex, onLocation);
+    }
+    else
+      lbl.setLocation(argIndex, onLocation);
+  }
+
+  /**
+   * Adds points using the mod-2 rule of SFS.  This is used to add the boundary
+   * points of dim-1 geometries (Curves/MultiCurves).  According to the SFS,
+   * an endpoint of a Curve is on the boundary
+   * iff if it is in the boundaries of an odd number of Geometries
+   */
+  private void insertBoundaryPoint(int argIndex, Coordinate coord)
+  {
+    Node n = nodes.addNode(coord);
+    Label lbl = n.getLabel();
+    // 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;
+    if (lbl != null) loc = lbl.getLocation(argIndex, Position.ON);
+    if (loc == Location.BOUNDARY) boundaryCount++;
+
+    // determine the boundary status of the point according to the Boundary Determination Rule
+    int newLoc = determineBoundary(boundaryCount);
+    lbl.setLocation(argIndex, newLoc);
+  }
+
+  private void addSelfIntersectionNodes(int argIndex)
+  {
+    for (Iterator i = edges.iterator(); i.hasNext(); ) {
+      Edge e = (Edge) i.next();
+      int eLoc = e.getLabel().getLocation(argIndex);
+      for (Iterator eiIt = e.eiList.iterator(); eiIt.hasNext(); ) {
+        EdgeIntersection ei = (EdgeIntersection) eiIt.next();
+        addSelfIntersectionNode(argIndex, ei.coord, eLoc);
+      }
+    }
+  }
+  /**
+   * Add a node for a self-intersection.
+   * If the node is a potential boundary node (e.g. came from an edge which
+   * is a boundary) then insert it as a potential boundary node.
+   * Otherwise, just add it as a regular node.
+   */
+  private void addSelfIntersectionNode(int argIndex, Coordinate coord, int loc)
+  {
+    // if this node is already a boundary node, don't change it
+    if (isBoundaryNode(argIndex, coord)) return;
+    if (loc == Location.BOUNDARY && useBoundaryDeterminationRule)
+        insertBoundaryPoint(argIndex, coord);
+    else
+      insertPoint(argIndex, coord, loc);
+  }
+
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/GraphComponent.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/GraphComponent.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/GraphComponent.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,107 @@
+
+
+
+/*
+ * 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.geomgraph;
+
+import com.vividsolutions.jts.util.*;
+import com.vividsolutions.jts.geomgraph.Label;
+import com.vividsolutions.jts.geom.Coordinate;
+import com.vividsolutions.jts.geom.IntersectionMatrix;
+import com.vividsolutions.jts.util.Assert;
+
+/**
+ * A GraphComponent is the parent class for the objects'
+ * that form a graph.  Each GraphComponent can carry a
+ * Label.
+ * @version 1.6
+ */
+abstract public class GraphComponent {
+
+  protected Label label;
+  /**
+   * isInResult indicates if this component has already been included in the result
+   */
+  private boolean isInResult = false;
+  private boolean isCovered = false;
+  private boolean isCoveredSet = false;
+  private boolean isVisited = false;
+
+  public GraphComponent() {
+  }
+
+  public GraphComponent(Label label) {
+    this.label = label;
+  }
+
+  public Label getLabel() { return label; }
+  public void setLabel(Label label) { this.label = label; }
+  public void setInResult(boolean isInResult) { this.isInResult = isInResult; }
+  public boolean isInResult() { return isInResult; }
+  public void setCovered(boolean isCovered)
+  {
+    this.isCovered = isCovered;
+    this.isCoveredSet = true;
+  }
+  public boolean isCovered()    { return isCovered; }
+  public boolean isCoveredSet() { return isCoveredSet; }
+  public boolean isVisited() { return isVisited; }
+  public void setVisited(boolean isVisited) { this.isVisited = isVisited; }
+  /**
+   * @return a coordinate in this component (or null, if there are none)
+   */
+  abstract public Coordinate getCoordinate();
+  /**
+   * compute the contribution to an IM for this component
+   */
+  abstract protected void computeIM(IntersectionMatrix im);
+  /**
+   * An isolated component is one that does not intersect or touch any other
+   * component.  This is the case if the label has valid locations for
+   * only a single Geometry.
+   *
+   * @return true if this component is isolated
+   */
+  abstract public boolean isIsolated();
+  /**
+   * Update the IM with the contribution for this component.
+   * A component only contributes if it has a labelling for both parent geometries
+   */
+  public void updateIM(IntersectionMatrix im)
+  {
+    Assert.isTrue(label.getGeometryCount() >= 2, "found partial label");
+    computeIM(im);
+  }
+
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/Label.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/Label.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/Label.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,230 @@
+
+
+
+/*
+ * 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.geomgraph;
+
+import com.vividsolutions.jts.geom.Location;
+
+ /**
+ * A <code>Label</code> indicates the topological relationship of a component
+ * of a topology graph to a given <code>Geometry</code>.
+ * This class supports labels for relationships to two <code>Geometry</code>s,
+ * which is sufficient for algorithms for binary operations.
+ * <P>
+ * Topology graphs support the concept of labeling nodes and edges in the graph.
+ * The label of a node or edge specifies its topological relationship to one or
+ * more geometries.  (In fact, since JTS operations have only two arguments labels
+ * are required for only two geometries).  A label for a node or edge has one or
+ * two elements, depending on whether the node or edge occurs in one or both of the
+ * input <code>Geometry</code>s.  Elements contain attributes which categorize the
+ * topological location of the node or edge relative to the parent
+ * <code>Geometry</code>; that is, whether the node or edge is in the interior,
+ * boundary or exterior of the <code>Geometry</code>.  Attributes have a value
+ * from the set <code>{Interior, Boundary, Exterior}</code>.  In a node each
+ * element has  a single attribute <code>&lt;On&gt;</code>.  For an edge each element has a
+ * triplet of attributes <code>&lt;Left, On, Right&gt;</code>.
+ * <P>
+ * It is up to the client code to associate the 0 and 1 <code>TopologyLocation</code>s
+ * with specific geometries.
+ * @version 1.6
+ *
+ */
+public class Label {
+
+  // converts a Label to a Line label (that is, one with no side Locations)
+  public static Label toLineLabel(Label label)
+  {
+    Label lineLabel = new Label(Location.NULL);
+    for (int i = 0; i < 2; i++) {
+      lineLabel.setLocation(i, label.getLocation(i));
+    }
+    return lineLabel;
+  }
+
+  TopologyLocation elt[] = new TopologyLocation[2];
+
+  /**
+   * Construct a Label with a single location for both Geometries.
+   * Initialize the locations to Null
+   */
+  public Label(int onLoc)
+  {
+    elt[0] = new TopologyLocation(onLoc);
+    elt[1] = new TopologyLocation(onLoc);
+  }
+  /**
+   * Construct a Label with a single location for both Geometries.
+   * Initialize the location for the Geometry index.
+   */
+  public Label(int geomIndex, int onLoc)
+  {
+    elt[0] = new TopologyLocation(Location.NULL);
+    elt[1] = new TopologyLocation(Location.NULL);
+    elt[geomIndex].setLocation(onLoc);
+  }
+  /**
+   * Construct a Label with On, Left and Right locations for both Geometries.
+   * Initialize the locations for both Geometries to the given values.
+   */
+  public Label(int onLoc, int leftLoc, int rightLoc)
+  {
+    elt[0] = new TopologyLocation(onLoc, leftLoc, rightLoc);
+    elt[1] = new TopologyLocation(onLoc, leftLoc, rightLoc);
+  }
+  /**
+   * Construct a Label with On, Left and Right locations for both Geometries.
+   * Initialize the locations for the given Geometry index.
+   */
+  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[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)
+  {
+    elt[0] = new TopologyLocation(lbl.elt[0]);
+    elt[1] = new TopologyLocation(lbl.elt[1]);
+  }
+
+  public void flip()
+  {
+    elt[0].flip();
+    elt[1].flip();
+  }
+
+  public int getLocation(int geomIndex, int posIndex) { return elt[geomIndex].get(posIndex); }
+  public int getLocation(int geomIndex) { return elt[geomIndex].get(Position.ON); }
+  public void setLocation(int geomIndex, int posIndex, int location)
+  {
+    elt[geomIndex].setLocation(posIndex, location);
+  }
+  public void setLocation(int geomIndex, int location)
+  {
+    elt[geomIndex].setLocation(Position.ON, location);
+  }
+  public void setAllLocations(int geomIndex, int location)
+  {
+    elt[geomIndex].setAllLocations(location);
+  }
+  public void setAllLocationsIfNull(int geomIndex, int location)
+  {
+    elt[geomIndex].setAllLocationsIfNull(location);
+  }
+  public void setAllLocationsIfNull(int location)
+  {
+    setAllLocationsIfNull(0, location);
+    setAllLocationsIfNull(1, location);
+  }
+  /**
+   * Merge this label with another one.
+   * Merging updates any null attributes of this label with the attributes from lbl
+   */
+  public void merge(Label lbl)
+  {
+    for (int i = 0; i < 2; i++) {
+      if (elt[i] == null && lbl.elt[i] != null) {
+        elt[i] = new TopologyLocation(lbl.elt[i]);
+      }
+      else {
+        elt[i].merge(lbl.elt[i]);
+      }
+    }
+  }
+  private void setGeometryLocation(int geomIndex, TopologyLocation tl)
+  {
+    if (tl == null) return;
+    elt[geomIndex].setLocations(tl);
+  }
+  public int getGeometryCount()
+  {
+    int count = 0;
+    if (! elt[0].isNull()) count++;
+    if (! elt[1].isNull()) count++;
+    return count;
+  }
+  public boolean isNull(int geomIndex) { return elt[geomIndex].isNull(); }
+  public boolean isAnyNull(int geomIndex) { return elt[geomIndex].isAnyNull(); }
+
+  public boolean isArea()               { return elt[0].isArea() || elt[1].isArea();   }
+  public boolean isArea(int geomIndex)  { return elt[geomIndex].isArea();   }
+  public boolean isLine(int geomIndex)  { return elt[geomIndex].isLine();   }
+
+  public boolean isEqualOnSide(Label lbl, int side)
+  {
+    return
+          this.elt[0].isEqualOnSide(lbl.elt[0], side)
+      &&  this.elt[1].isEqualOnSide(lbl.elt[1], side);
+  }
+  public boolean allPositionsEqual(int geomIndex, int loc)
+  {
+    return elt[geomIndex].allPositionsEqual(loc);
+  }
+  /**
+   * Converts one GeometryLocation to a Line location
+   */
+  public void toLine(int geomIndex)
+  {
+    if (elt[geomIndex].isArea())
+      elt[geomIndex] = new TopologyLocation(elt[geomIndex].location[0]);
+  }
+  public String toString()
+  {
+    StringBuffer buf = new StringBuffer();
+    if (elt[0] != null) {
+      buf.append("a:");
+      buf.append(elt[0].toString());
+    }
+    if (elt[1] != null) {
+      buf.append(" b:");
+      buf.append(elt[1].toString());
+    }
+    return buf.toString();
+  }
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/Node.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/Node.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/Node.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,156 @@
+
+
+
+/*
+ * 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.geomgraph;
+
+import java.io.PrintStream;
+import java.util.*;
+import com.vividsolutions.jts.geom.Coordinate;
+import com.vividsolutions.jts.geom.IntersectionMatrix;
+import com.vividsolutions.jts.geom.Location;
+import com.vividsolutions.jts.geomgraph.GraphComponent;
+import com.vividsolutions.jts.geomgraph.Label;
+import com.vividsolutions.jts.util.*;
+
+
+/**
+ * @version 1.6
+ */
+public class Node
+  extends GraphComponent
+{
+  protected Coordinate coord; // only non-null if this node is precise
+  protected EdgeEndStar edges;
+
+  public Node(Coordinate coord, EdgeEndStar edges)
+  {
+    this.coord = coord;
+    this.edges = edges;
+    label = new Label(0, Location.NULL);
+  }
+
+  public Coordinate getCoordinate() { return coord; }
+  public EdgeEndStar getEdges() { return edges; }
+
+  public boolean isIsolated()
+  {
+    return (label.getGeometryCount() == 1);
+  }
+  /**
+   * Basic nodes do not compute IMs
+   */
+  protected void computeIM(IntersectionMatrix im) {}
+  /**
+   * Add the edge to the list of edges at this node
+   */
+  public void add(EdgeEnd e)
+  {
+    // Assert: start pt of e is equal to node point
+    edges.insert(e);
+    e.setNode(this);
+  }
+
+  public void mergeLabel(Node n)
+  {
+    mergeLabel(n.label);
+  }
+
+  /**
+   * To merge labels for two nodes,
+   * the merged location for each LabelElement is computed.
+   * The location for the corresponding node LabelElement is set to the result,
+   * as long as the location is non-null.
+   */
+
+  public void mergeLabel(Label label2)
+  {
+    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);
+    }
+  }
+
+  public void setLabel(int argIndex, int onLocation)
+  {
+    if (label == null) {
+      label = new Label(argIndex, onLocation);
+    }
+    else
+      label.setLocation(argIndex, onLocation);
+  }
+  /**
+   * Updates the label of a node to BOUNDARY,
+   * obeying the mod-2 boundaryDetermination rule.
+   */
+  public void setLabelBoundary(int argIndex)
+  {
+    // determine the current location for the point (if any)
+    int loc = Location.NULL;
+    if (label != null)
+      loc = label.getLocation(argIndex);
+    // flip the loc
+    int newLoc;
+    switch (loc) {
+    case Location.BOUNDARY: newLoc = Location.INTERIOR; break;
+    case Location.INTERIOR: newLoc = Location.BOUNDARY; break;
+    default: newLoc = Location.BOUNDARY;  break;
+    }
+    label.setLocation(argIndex, newLoc);
+  }
+
+  /**
+   * The location for a given eltIndex for a node will be one
+   * of { null, INTERIOR, BOUNDARY }.
+   * A node may be on both the boundary and the interior of a geometry;
+   * in this case, the rule is that the node is considered to be in the boundary.
+   * The merged location is the maximum of the two input values.
+   */
+  int computeMergedLocation(Label label2, int eltIndex)
+  {
+    int loc = Location.NULL;
+    loc = label.getLocation(eltIndex);
+    if (! label2.isNull(eltIndex)) {
+        int nLoc = label2.getLocation(eltIndex);
+        if (loc != Location.BOUNDARY) loc = nLoc;
+    }
+    return loc;
+  }
+
+  public void print(PrintStream out)
+  {
+    out.println("node " + coord + " lbl: " + label);
+  }
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/NodeFactory.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/NodeFactory.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/NodeFactory.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,50 @@
+
+/*
+ * 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.geomgraph;
+
+import com.vividsolutions.jts.geom.Coordinate;
+
+
+/**
+ * @version 1.6
+ */
+public class NodeFactory {
+/**
+ * The basic node constructor does not allow for incident edges
+ */
+  public Node createNode(Coordinate coord)
+  {
+    return new Node(coord, null);
+  }
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/NodeMap.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/NodeMap.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/NodeMap.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,137 @@
+
+
+
+/*
+ * 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.geomgraph;
+
+
+import java.io.PrintStream;
+import java.util.*;
+import com.vividsolutions.jts.geom.Coordinate;
+import com.vividsolutions.jts.geom.Location;
+import com.vividsolutions.jts.geomgraph.Node;
+
+/**
+ * A map of nodes, indexed by the coordinate of the node
+ * @version 1.6
+ */
+public class NodeMap
+
+{
+  //Map nodeMap = new HashMap();
+  Map nodeMap = new TreeMap();
+  NodeFactory nodeFact;
+
+  public NodeMap(NodeFactory nodeFact) {
+    this.nodeFact = nodeFact;
+  }
+
+  /**
+   * Factory function - subclasses can override to create their own types of nodes
+   */
+   /*
+  protected Node createNode(Coordinate coord)
+  {
+    return new Node(coord);
+  }
+  */
+  /**
+   * This method expects that a node has a coordinate value.
+   */
+  public Node addNode(Coordinate coord)
+  {
+    Node node = (Node) nodeMap.get(coord);
+    if (node == null) {
+      node = nodeFact.createNode(coord);
+      nodeMap.put(coord, node);
+    }
+    return node;
+  }
+
+  public Node addNode(Node n)
+  {
+    Node node = (Node) nodeMap.get(n.getCoordinate());
+    if (node == null) {
+      nodeMap.put(n.getCoordinate(), n);
+      return n;
+    }
+    node.mergeLabel(n);
+    return node;
+  }
+
+  /**
+   * Adds a node for the start point of this EdgeEnd
+   * (if one does not already exist in this map).
+   * Adds the EdgeEnd to the (possibly new) node.
+   */
+  public void add(EdgeEnd e)
+  {
+    Coordinate p = e.getCoordinate();
+    Node n = addNode(p);
+    n.add(e);
+  }
+  /**
+   * @return the node if found; null otherwise
+   */
+  public Node find(Coordinate coord)  {    return (Node) nodeMap.get(coord);  }
+
+  public Iterator iterator()
+  {
+    return nodeMap.values().iterator();
+  }
+  public Collection values()
+  {
+    return nodeMap.values();
+  }
+
+  public Collection getBoundaryNodes(int geomIndex)
+  {
+    Collection bdyNodes = new ArrayList();
+    for (Iterator i = iterator(); i.hasNext(); ) {
+      Node node = (Node) i.next();
+      if (node.getLabel().getLocation(geomIndex) == Location.BOUNDARY)
+        bdyNodes.add(node);
+    }
+    return bdyNodes;
+  }
+
+  public void print(PrintStream out)
+  {
+    for (Iterator it = iterator(); it.hasNext(); )
+    {
+      Node n = (Node) it.next();
+      n.print(out);
+    }
+  }
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/PlanarGraph.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/PlanarGraph.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/PlanarGraph.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,261 @@
+
+
+
+/*
+ * 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.geomgraph;
+
+/**
+ * @version 1.6
+ */
+import java.io.PrintStream;
+import java.util.*;
+import com.vividsolutions.jts.util.*;
+import com.vividsolutions.jts.algorithm.*;
+import com.vividsolutions.jts.geom.*;
+
+/**
+ * The computation of the <code>IntersectionMatrix</code> relies on the use of a structure
+ * called a "topology graph".  The topology graph contains nodes and edges
+ * corresponding to the nodes and line segments of a <code>Geometry</code>. Each
+ * node and edge in the graph is labeled with its topological location relative to
+ * the source geometry.
+ * <P>
+ * Note that there is no requirement that points of self-intersection be a vertex.
+ * Thus to obtain a correct topology graph, <code>Geometry</code>s must be
+ * self-noded before constructing their graphs.
+ * <P>
+ * Two fundamental operations are supported by topology graphs:
+ * <UL>
+ *   <LI>Computing the intersections between all the edges and nodes of a single graph
+ *   <LI>Computing the intersections between the edges and nodes of two different graphs
+ * </UL>
+ *
+ * @version 1.6
+ */
+public class PlanarGraph {
+
+  public static final CGAlgorithms cga = new CGAlgorithms();
+  //public static final LineIntersector li = new RobustLineIntersector();
+
+  /**
+   * For nodes in the Collection, link the DirectedEdges at the node that are in the result.
+   * This allows clients to link only a subset of nodes in the graph, for
+   * efficiency (because they know that only a subset is of interest).
+   */
+  public static void linkResultDirectedEdges(Collection nodes)
+  {
+    for (Iterator nodeit = nodes.iterator(); nodeit.hasNext(); ) {
+      Node node = (Node) nodeit.next();
+      ((DirectedEdgeStar) node.getEdges()).linkResultDirectedEdges();
+    }
+  }
+
+  protected List edges        = new ArrayList();
+  protected NodeMap nodes;
+  protected List edgeEndList  = new ArrayList();
+
+  public PlanarGraph(NodeFactory nodeFact) {
+    nodes = new NodeMap(nodeFact);
+  }
+
+  public PlanarGraph() {
+    nodes = new NodeMap(new NodeFactory());
+  }
+
+  public Iterator getEdgeIterator() { return edges.iterator(); }
+  public Collection getEdgeEnds() { return edgeEndList; }
+
+  public boolean isBoundaryNode(int geomIndex, Coordinate coord)
+  {
+    Node node = nodes.find(coord);
+    if (node == null) return false;
+    Label label = node.getLabel();
+    if (label != null && label.getLocation(geomIndex) == Location.BOUNDARY) return true;
+    return false;
+  }
+  protected void insertEdge(Edge e)
+  {
+    edges.add(e);
+  }
+  public void add(EdgeEnd e)
+  {
+    nodes.add(e);
+    edgeEndList.add(e);
+  }
+
+  public Iterator getNodeIterator() { return nodes.iterator(); }
+  public Collection getNodes() { return nodes.values(); }
+  public Node addNode(Node node) { return nodes.addNode(node); }
+  public Node addNode(Coordinate coord) { return nodes.addNode(coord); }
+  /**
+   * @return the node if found; null otherwise
+   */
+  public Node find(Coordinate coord) { return nodes.find(coord); }
+
+  /**
+   * Add a set of edges to the graph.  For each edge two DirectedEdges
+   * will be created.  DirectedEdges are NOT linked by this method.
+   */
+  public void addEdges(List edgesToAdd)
+  {
+    // create all the nodes for the edges
+    for (Iterator it = edgesToAdd.iterator(); it.hasNext(); ) {
+      Edge e = (Edge) it.next();
+      edges.add(e);
+
+      DirectedEdge de1 = new DirectedEdge(e, true);
+      DirectedEdge de2 = new DirectedEdge(e, false);
+      de1.setSym(de2);
+      de2.setSym(de1);
+
+      add(de1);
+      add(de2);
+    }
+  }
+
+  /**
+   * Link the DirectedEdges at the nodes of the graph.
+   * This allows clients to link only a subset of nodes in the graph, for
+   * efficiency (because they know that only a subset is of interest).
+   */
+  public void linkResultDirectedEdges()
+  {
+    for (Iterator nodeit = nodes.iterator(); nodeit.hasNext(); ) {
+      Node node = (Node) nodeit.next();
+      ((DirectedEdgeStar) node.getEdges()).linkResultDirectedEdges();
+    }
+  }
+  /**
+   * Link the DirectedEdges at the nodes of the graph.
+   * This allows clients to link only a subset of nodes in the graph, for
+   * efficiency (because they know that only a subset is of interest).
+   */
+  public void linkAllDirectedEdges()
+  {
+    for (Iterator nodeit = nodes.iterator(); nodeit.hasNext(); ) {
+      Node node = (Node) nodeit.next();
+      ((DirectedEdgeStar) node.getEdges()).linkAllDirectedEdges();
+    }
+  }
+  /**
+   * Returns the EdgeEnd which has edge e as its base edge
+   * (MD 18 Feb 2002 - this should return a pair of edges)
+   *
+   * @return the edge, if found
+   *    <code>null</code> if the edge was not found
+   */
+  public EdgeEnd findEdgeEnd(Edge e)
+  {
+    for (Iterator i = getEdgeEnds().iterator(); i.hasNext(); ) {
+      EdgeEnd ee = (EdgeEnd) i.next();
+      if (ee.getEdge() == e)
+        return ee;
+    }
+    return null;
+  }
+
+  /**
+   * Returns the edge whose first two coordinates are p0 and p1
+   *
+   * @return the edge, if found
+   *    <code>null</code> if the edge was not found
+   */
+  public Edge findEdge(Coordinate p0, Coordinate p1)
+  {
+    for (int i = 0; i < edges.size(); i++) {
+      Edge e = (Edge) edges.get(i);
+      Coordinate[] eCoord = e.getCoordinates();
+      if (p0.equals(eCoord[0]) && p1.equals(eCoord[1]) )
+        return e;
+    }
+    return null;
+  }
+  /**
+   * Returns the edge which starts at p0 and whose first segment is
+   * parallel to p1
+   *
+   * @return the edge, if found
+   *    <code>null</code> if the edge was not found
+   */
+  public Edge findEdgeInSameDirection(Coordinate p0, Coordinate p1)
+  {
+    for (int i = 0; i < edges.size(); i++) {
+      Edge e = (Edge) edges.get(i);
+
+      Coordinate[] eCoord = e.getCoordinates();
+      if (matchInSameDirection(p0, p1, eCoord[0], eCoord[1]) )
+        return e;
+
+      if (matchInSameDirection(p0, p1, eCoord[eCoord.length - 1], eCoord[eCoord.length - 2]) )
+        return e;
+    }
+    return null;
+  }
+
+  /**
+   * The coordinate pairs match if they define line segments lying in the same direction.
+   * E.g. the segments are parallel and in the same quadrant
+   * (as opposed to parallel and opposite!).
+   */
+  private boolean matchInSameDirection(Coordinate p0, Coordinate p1, Coordinate ep0, Coordinate ep1)
+  {
+    if (! p0.equals(ep0))
+      return false;
+
+    if (CGAlgorithms.computeOrientation(p0, p1, ep1) == CGAlgorithms.COLLINEAR
+         && Quadrant.quadrant(p0, p1) == Quadrant.quadrant(ep0, ep1) )
+      return true;
+    return false;
+  }
+
+  public void printEdges(PrintStream out)
+  {
+    out.println("Edges:");
+    for (int i = 0; i < edges.size(); i++) {
+      out.println("edge " + i + ":");
+      Edge e = (Edge) edges.get(i);
+      e.print(out);
+      e.eiList.print(out);
+    }
+  }
+  void debugPrint(Object o)
+  {
+    System.out.print(o);
+  }
+  void debugPrintln(Object o)
+  {
+    System.out.println(o);
+  }
+
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/Position.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/Position.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/Position.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,61 @@
+
+
+
+/*
+ * 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.geomgraph;
+
+/**
+ * A Position indicates the position of a Location relative to a graph component
+ * (Node, Edge, or Area).
+ * @version 1.6
+ */
+public class Position {
+
+  /** An indicator that a Location is <i>on</i> a GraphComponent */
+  public static final int ON      = 0;
+  /** An indicator that a Location is to the <i>left</i> of a GraphComponent */  
+  public static final int LEFT    = 1;
+  /** An indicator that a Location is to the <i>right</i> of a GraphComponent */  
+  public static final int RIGHT   = 2;
+  /**
+   * Returns LEFT if the position is RIGHT, RIGHT if the position is LEFT, or the position
+   * otherwise.
+   */
+  public static final int opposite(int position)
+  {
+    if (position == LEFT) return RIGHT;
+    if (position == RIGHT) return LEFT;
+    return position;
+  }
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/Quadrant.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/Quadrant.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/Quadrant.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,140 @@
+
+
+
+/*
+ * 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.geomgraph;
+
+/**
+ * @version 1.6
+ */
+import com.vividsolutions.jts.geom.Coordinate;
+
+/**
+ * Utility functions for working with quadrants, which are numbered as follows:
+ * <pre>
+ * 1 | 0
+ * --+--
+ * 2 | 3
+ * <pre>
+ *
+ * @version 1.6
+ */
+public class Quadrant {
+  /**
+   * Returns the quadrant of a directed line segment (specified as x and y
+   * displacements, which cannot both be 0).
+   */
+  public static int quadrant(double dx, double dy)
+  {
+    if (dx == 0.0 && dy == 0.0)
+      throw new IllegalArgumentException("Cannot compute the quadrant for point ( "+ dx + ", " + dy + " )" );
+    if (dx >= 0) {
+      if (dy >= 0)
+        return 0;
+      else
+        return 3;
+    }
+    else {
+	if (dy >= 0)
+          return 1;
+	else
+          return 2;
+    }
+  }
+
+  /**
+   * Returns the quadrant of a directed line segment from p0 to p1.
+   */
+  public static int quadrant(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 quadrant for two identical points " + p0);
+    return quadrant(dx, dy);
+  }
+
+  /**
+   * Returns true if the quadrants are 1 and 3, or 2 and 4
+   */
+  public static boolean isOpposite(int quad1, int quad2)
+  {
+    if (quad1 == quad2) return false;
+    int diff = (quad1 - quad2 + 4) % 4;
+    // if quadrants are not adjacent, they are opposite
+    if (diff == 2) return true;
+    return false;
+  }
+
+  /** 
+   * Returns the right-hand quadrant of the halfplane defined by the two quadrants,
+   * or -1 if the quadrants are opposite, or the quadrant if they are identical.
+   */
+  public static int commonHalfPlane(int quad1, int quad2)
+  {
+    // if quadrants are the same they do not determine a unique common halfplane.
+    // Simply return one of the two possibilities
+    if (quad1 == quad2) return quad1;
+    int diff = (quad1 - quad2 + 4) % 4;
+    // if quadrants are not adjacent, they do not share a common halfplane
+    if (diff == 2) return -1;
+    //
+    int min = (quad1 < quad2) ? quad1 : quad2;
+    int max = (quad1 > quad2) ? quad1 : quad2;
+    // for this one case, the righthand plane is NOT the minimum index;
+    if (min == 0 && max == 3) return 3;
+    // in general, the halfplane index is the minimum of the two adjacent quadrants
+    return min;
+  }
+
+  /**
+   * Returns whether the given quadrant lies within the given halfplane (specified
+   * by its right-hand quadrant).
+   */
+  public static boolean isInHalfPlane(int quad, int halfPlane)
+  {
+    if (halfPlane == 3) {
+      return quad == 3 || quad == 0;
+    }
+    return quad == halfPlane || quad == halfPlane + 1;
+  }
+    
+  /**
+   * Returns true if the given quadrant is 0 or 1.
+   */
+  public static boolean isNorthern(int quad)
+  {
+    return quad == 0 || quad == 1;
+  }
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/TopologyLocation.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/TopologyLocation.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/TopologyLocation.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,212 @@
+
+
+
+/*
+ * 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.geomgraph;
+
+
+
+import com.vividsolutions.jts.geomgraph.Position;
+import com.vividsolutions.jts.geom.Location;
+
+/**
+  * A TopologyLocation is the labelling of a
+  * GraphComponent's topological relationship to a single Geometry.
+  * <p>
+  * If the parent component is an area edge, each side and the edge itself
+  * have a topological location.  These locations are named
+  * <ul>
+  * <li> ON: on the edge
+  * <li> LEFT: left-hand side of the edge
+  * <li> RIGHT: right-hand side
+  * </ul>
+  * If the parent component is a line edge or node, there is a single
+  * topological relationship attribute, ON.
+  * <p>
+  * The possible values of a topological location are
+  * {Location.NULL, 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
+ */
+public class TopologyLocation {
+
+  int location[];
+
+  public TopologyLocation(int[] location)
+  {
+    init(location.length);
+  }
+  /**
+   * 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, 
+   * and Location.INTERIOR.
+   * @see Location
+   */
+  public TopologyLocation(int on, int left, int right) {
+   init(3);
+   location[Position.ON] = on;
+   location[Position.LEFT] = left;
+   location[Position.RIGHT] = right;
+  }
+
+  public TopologyLocation(int on) {
+   init(1);
+   location[Position.ON] = on;
+  }
+  public TopologyLocation(TopologyLocation gl) {
+    init(gl.location.length);
+    if (gl != null) {
+      for (int i = 0; i < location.length; i++) {
+        location[i] = gl.location[i];
+      }
+    }
+  }
+  private void init(int size)
+  {
+    location = new int[size];
+    setAllLocations(Location.NULL);
+  }
+  public int get(int posIndex)
+  {
+    if (posIndex < location.length) return location[posIndex];
+    return Location.NULL;
+  }
+  /**
+   * @return true if all locations are NULL
+   */
+  public boolean isNull()
+  {
+    for (int i = 0; i < location.length; i++) {
+      if (location[i] != Location.NULL) return false;
+    }
+    return true;
+  }
+  /**
+   * @return true if any locations are NULL
+   */
+  public boolean isAnyNull()
+  {
+    for (int i = 0; i < location.length; i++) {
+      if (location[i] == Location.NULL) return true;
+    }
+    return false;
+  }
+  public boolean isEqualOnSide(TopologyLocation le, int locIndex)
+  {
+    return location[locIndex] == le.location[locIndex];
+  }
+  public boolean isArea() { return location.length > 1; }
+  public boolean isLine() { return location.length == 1; }
+
+  public void flip()
+  {
+    if (location.length <= 1) return;
+    int temp = location[Position.LEFT];
+    location[Position.LEFT] = location[Position.RIGHT];
+    location[Position.RIGHT] = temp;
+  }
+
+
+  public void setAllLocations(int locValue)
+  {
+    for (int i = 0; i < location.length; i++) {
+      location[i]     = locValue;
+    }
+  }
+  public void setAllLocationsIfNull(int locValue)
+  {
+    for (int i = 0; i < location.length; i++) {
+      if (location[i] == Location.NULL) location[i]     = locValue;
+    }
+  }
+
+  public void setLocation(int locIndex, int locValue)
+  {
+      location[locIndex] = locValue;
+  }
+  public void setLocation(int locValue)
+  {
+    setLocation(Position.ON, locValue);
+  }
+  public int[] getLocations() { return location; }
+  public void setLocations(int on, int left, int right) {
+      location[Position.ON] = on;
+      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++) {
+      if (location[i] != loc) return false;
+    }
+    return true;
+  }
+
+  /**
+   * merge updates only the NULL attributes of this object
+   * with the attributes of another.
+   */
+  public void merge(TopologyLocation gl)
+  {
+    // if the src is an Area label & and the dest is not, increase the dest to be an Area
+    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;
+      location = newLoc;
+    }
+    for (int i = 0; i < location.length; i++) {
+      if (location[i] == Location.NULL && i < gl.location.length)
+        location[i] = gl.location[i];
+    }
+  }
+
+  public String toString()
+  {
+    StringBuffer buf = new StringBuffer();
+    if (location.length > 1) buf.append(Location.toLocationSymbol(location[Position.LEFT]));
+    buf.append(Location.toLocationSymbol(location[Position.ON]));
+    if (location.length > 1) buf.append(Location.toLocationSymbol(location[Position.RIGHT]));
+    return buf.toString();
+  }
+}

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/index/EdgeSetIntersector.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,82 @@
+
+
+
+/*
+ * 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.geomgraph.index;
+
+/**
+ * @version 1.6
+ */
+import java.util.*;
+import com.vividsolutions.jts.geomgraph.*;
+
+/**
+ * An EdgeSetIntersector computes all the intersections between the
+ * edges in the set.  It adds the computed intersections to each edge
+ * they are found on.  It may be used in two scenarios:
+ * <ul>
+ * <li>determining the internal intersections between a single set of edges
+ * <li>determining the mutual intersections between two different sets of edges
+ * </ul>
+ * 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
+ */
+public abstract class EdgeSetIntersector {
+
+  List edges0 = null;
+  List edges1 = null;
+
+  public EdgeSetIntersector() {
+  }
+
+  /**
+   * Computes all self-intersections between edges in a set of edges,
+   * allowing client to choose whether self-intersections are computed.
+   *
+   * @param edges a list of edges to test for intersections
+   * @param si the SegmentIntersector to use
+   * @param testAllSegments true if self-intersections are to be tested as well
+   */
+  abstract public void computeIntersections(List edges, SegmentIntersector si, boolean testAllSegments);
+
+  /**
+   * Computes all mutual intersections between two sets of edges.
+   */
+  abstract public void computeIntersections(List edges0, List edges1, SegmentIntersector si);
+
+
+
+}

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/index/MonotoneChain.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,55 @@
+
+
+
+/*
+ * 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.geomgraph.index;
+
+/**
+ * @version 1.6
+ */
+public class MonotoneChain {
+
+  MonotoneChainEdge mce;
+  int chainIndex;
+
+  public MonotoneChain(MonotoneChainEdge mce, int chainIndex) {
+    this.mce = mce;
+    this.chainIndex = chainIndex;
+  }
+
+  public void computeIntersections(MonotoneChain mc, SegmentIntersector si)
+  {
+    this.mce.computeIntersectsForChain(chainIndex, mc.mce, mc.chainIndex, si);
+  }
+}

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/index/MonotoneChainEdge.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,154 @@
+
+
+
+/*
+ * 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.geomgraph.index;
+
+import java.util.*;
+
+import com.vividsolutions.jts.geom.Coordinate;
+import com.vividsolutions.jts.geom.Envelope;
+import com.vividsolutions.jts.geomgraph.*;
+import com.vividsolutions.jts.util.Debug;
+
+/**
+ * MonotoneChains are a way of partitioning the segments of an edge to
+ * allow for fast searching of intersections.
+ * They have the following properties:
+ * <ol>
+ * <li>the segments within a monotone chain will never intersect each other
+ * <li>the envelope of any contiguous subset of the segments in a monotone chain
+ * is simply the envelope of the endpoints of the subset.
+ * </ol>
+ * Property 1 means that there is no need to test pairs of segments from within
+ * the same monotone chain for intersection.
+ * Property 2 allows
+ * 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
+ */
+public class MonotoneChainEdge {
+
+  Edge e;
+  Coordinate[] pts; // cache a reference to the coord array, for efficiency
+  // the lists of start/end indexes of the monotone chains.
+  // Includes the end point of the edge as a sentinel
+  int[] startIndex;
+  // these envelopes are created once and reused
+  Envelope env1 = new Envelope();
+  Envelope env2 = new Envelope();
+
+  public MonotoneChainEdge(Edge e) {
+    this.e = e;
+    pts = e.getCoordinates();
+    MonotoneChainIndexer mcb = new MonotoneChainIndexer();
+    startIndex = mcb.getChainStartIndices(pts);
+  }
+
+  public Coordinate[] getCoordinates() { return pts; }
+  public int[] getStartIndexes() { return startIndex; }
+
+  public double getMinX(int chainIndex)
+  {
+    double x1 = pts[startIndex[chainIndex]].x;
+    double x2 = pts[startIndex[chainIndex + 1]].x;
+    return x1 < x2 ? x1 : x2;
+  }
+  public double getMaxX(int chainIndex)
+  {
+    double x1 = pts[startIndex[chainIndex]].x;
+    double x2 = pts[startIndex[chainIndex + 1]].x;
+    return x1 > x2 ? x1 : x2;
+  }
+
+  public void computeIntersects(MonotoneChainEdge mce, SegmentIntersector si)
+  {
+    for (int i = 0; i < startIndex.length - 1; i++) {
+      for (int j = 0; j < mce.startIndex.length - 1; j++) {
+        computeIntersectsForChain(  i,
+                                    mce,  j,
+                                    si );
+      }
+    }
+  }
+  public void computeIntersectsForChain(
+    int chainIndex0,
+    MonotoneChainEdge mce,
+    int chainIndex1,
+    SegmentIntersector si)
+  {
+    computeIntersectsForChain(startIndex[chainIndex0], startIndex[chainIndex0 + 1],
+                            mce,
+                            mce.startIndex[chainIndex1], mce.startIndex[chainIndex1 + 1],
+                            si );
+  }
+
+  private void computeIntersectsForChain(
+    int start0, int end0,
+    MonotoneChainEdge mce,
+    int start1, int end1,
+    SegmentIntersector ei)
+  {
+    Coordinate p00 = pts[start0];
+    Coordinate p01 = pts[end0];
+    Coordinate p10 = mce.pts[start1];
+    Coordinate p11 = mce.pts[end1];
+//Debug.println("computeIntersectsForChain:" + p00 + p01 + p10 + p11);
+    // terminating condition for the recursion
+    if (end0 - start0 == 1 && end1 - start1 == 1) {
+      ei.addIntersections(e, start0, mce.e, start1);
+      return;
+    }
+    // nothing to do if the envelopes of these chains don't overlap
+    env1.init(p00, p01);
+    env2.init(p10, p11);
+    if (! env1.intersects(env2)) return;
+
+    // the chains overlap, so split each in half and iterate  (binary search)
+    int mid0 = (start0 + end0) / 2;
+    int mid1 = (start1 + end1) / 2;
+
+    // Assert: mid != start or end (since we checked above for end - start <= 1)
+    // check terminating conditions before recursing
+    if (start0 < mid0) {
+      if (start1 < mid1) computeIntersectsForChain(start0, mid0, mce, start1,  mid1, ei);
+      if (mid1 < end1)   computeIntersectsForChain(start0, mid0, mce, mid1,    end1, ei);
+    }
+    if (mid0 < end0) {
+      if (start1 < mid1) computeIntersectsForChain(mid0,   end0, mce, start1,  mid1, ei);
+      if (mid1 < end1)   computeIntersectsForChain(mid0,   end0, mce, mid1,    end1, ei);
+    }
+  }
+}

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/index/MonotoneChainIndexer.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,109 @@
+
+
+
+/*
+ * 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.geomgraph.index;
+
+import java.util.*;
+
+import com.vividsolutions.jts.geom.Coordinate;
+import com.vividsolutions.jts.geomgraph.Quadrant;
+/**
+ * MonotoneChains are a way of partitioning the segments of an edge to
+ * allow for fast searching of intersections.
+ * They have the following properties:
+ * <ol>
+ * <li>the segments within a monotone chain will never intersect each other
+ * <li>the envelope of any contiguous subset of the segments in a monotone chain
+ * is simply the envelope of the endpoints of the subset.
+ * </ol>
+ * Property 1 means that there is no need to test pairs of segments from within
+ * the same monotone chain for intersection.
+ * Property 2 allows
+ * 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
+ */
+public class MonotoneChainIndexer {
+
+  public static int[] toIntArray(List list)
+  {
+    int[] array = new int[list.size()];
+    for (int i = 0; i < array.length; i++) {
+      array[i] = ((Integer) list.get(i)).intValue();
+    }
+    return array;
+  }
+
+  public MonotoneChainIndexer() {
+  }
+
+  public int[] getChainStartIndices(Coordinate[] pts)
+  {
+    // find the startpoint (and endpoints) of all monotone chains in this edge
+    int start = 0;
+    List startIndexList = new ArrayList();
+    startIndexList.add(new Integer(start));
+    do {
+      int last = findChainEnd(pts, start);
+      startIndexList.add(new Integer(last));
+      start = last;
+    } while (start < pts.length - 1);
+    // copy list to an array of ints, for efficiency
+    int[] startIndex = toIntArray(startIndexList);
+    return startIndex;
+  }
+
+  /**
+   * @return the index of the last point in the monotone chain
+   */
+  private int findChainEnd(Coordinate[] pts, int start)
+  {
+    // determine quadrant for chain
+    int chainQuad = Quadrant.quadrant(pts[start], pts[start + 1]);
+    int last = start + 1;
+    while (last < pts.length) {
+      // compute quadrant for next possible segment in chain
+      int quad = Quadrant.quadrant(pts[last - 1], pts[last]);
+      if (quad != chainQuad) break;
+      last++;
+    }
+    return last - 1;
+  }
+
+
+
+}

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/index/SegmentIntersector.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,212 @@
+
+
+
+/*
+ * 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.geomgraph.index;
+
+import java.util.*;
+import com.vividsolutions.jts.geom.*;
+import com.vividsolutions.jts.geomgraph.*;
+import com.vividsolutions.jts.algorithm.LineIntersector;
+import com.vividsolutions.jts.util.Debug;
+
+
+/**
+ * @version 1.6
+ */
+public class SegmentIntersector {
+
+  public static boolean isAdjacentSegments(int i1, int i2)
+  {
+    return Math.abs(i1 - i2) == 1;
+  }
+
+  /**
+   * These variables keep track of what types of intersections were
+   * found during ALL edges that have been intersected.
+   */
+  private boolean hasIntersection = false;
+  private boolean hasProper = false;
+  private boolean hasProperInterior = false;
+  // the proper intersection point found
+  private Coordinate properIntersectionPoint = null;
+
+  private LineIntersector li;
+  private boolean includeProper;
+  private boolean recordIsolated;
+  private boolean isSelfIntersection;
+  //private boolean intersectionFound;
+  private int numIntersections = 0;
+
+  // testing only
+  public int numTests = 0;
+
+  private Collection[] bdyNodes;
+/*
+  public SegmentIntersector()
+  {
+  }
+*/
+  public SegmentIntersector(LineIntersector li,  boolean includeProper, boolean recordIsolated)
+  {
+    this.li = li;
+    this.includeProper = includeProper;
+    this.recordIsolated = recordIsolated;
+  }
+
+  public void setBoundaryNodes( Collection bdyNodes0,
+                              Collection bdyNodes1)
+  {
+      bdyNodes = new Collection[2];
+      bdyNodes[0] = bdyNodes0;
+      bdyNodes[1] = bdyNodes1;
+  }
+
+  /**
+   * @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; }
+
+
+  /**
+   * 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(Edge e0, int segIndex0, Edge e1, int segIndex1)
+  {
+    if (e0 == e1) {
+      if (li.getIntersectionNum() == 1) {
+        if (isAdjacentSegments(segIndex0, segIndex1))
+          return true;
+        if (e0.isClosed()) {
+          int maxSegIndex = e0.getNumPoints() - 1;
+          if (    (segIndex0 == 0 && segIndex1 == maxSegIndex)
+              ||  (segIndex1 == 0 && segIndex0 == maxSegIndex) ) {
+            return true;
+          }
+        }
+      }
+    }
+    return false;
+  }
+
+  /**
+   * This method is called by clients of the EdgeIntersector class to test for and add
+   * intersections for two segments of the edges being intersected.
+   * Note that clients (such as MonotoneChainEdges) may choose not to intersect
+   * certain pairs of segments for efficiency reasons.
+   */
+  public void addIntersections(
+    Edge e0,  int segIndex0,
+    Edge 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);
+    /**
+     *  Always record any non-proper intersections.
+     *  If includeProper is true, record any proper intersections as well.
+     */
+    if (li.hasIntersection()) {
+      if (recordIsolated) {
+        e0.setIsolated(false);
+        e1.setIsolated(false);
+      }
+      //intersectionFound = true;
+      numIntersections++;
+      // 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;
+        if (includeProper || ! li.isProper() ) {
+//Debug.println(li);
+          e0.addIntersections(li, segIndex0, 0);
+          e1.addIntersections(li, segIndex1, 1);
+        }
+        if (li.isProper()) {
+          properIntersectionPoint = (Coordinate) li.getIntersection(0).clone();
+          hasProper = true;
+          if (! isBoundaryPoint(li, bdyNodes))
+            hasProperInterior = true;
+        }
+        //if (li.isCollinear())
+          //hasCollinear = true;
+      }
+    }
+  }
+
+  private boolean isBoundaryPoint(LineIntersector li, Collection[] bdyNodes)
+  {
+    if (bdyNodes == null) return false;
+    if (isBoundaryPoint(li, bdyNodes[0])) return true;
+    if (isBoundaryPoint(li, bdyNodes[1])) return true;
+    return false;
+  }
+
+  private boolean isBoundaryPoint(LineIntersector li, Collection bdyNodes)
+  {
+    for (Iterator i = bdyNodes.iterator(); i.hasNext(); ) {
+      Node node = (Node) i.next();
+      Coordinate pt = node.getCoordinate();
+      if (li.isIntersection(pt)) return true;
+    }
+    return false;
+  }
+
+}

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/index/SimpleEdgeSetIntersector.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,101 @@
+
+
+
+/*
+ * 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.geomgraph.index;
+
+import java.util.*;
+import com.vividsolutions.jts.geom.*;
+import com.vividsolutions.jts.geomgraph.*;
+
+/**
+ * Finds all intersections in one or two sets of edges,
+ * 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
+ */
+public class SimpleEdgeSetIntersector
+  extends EdgeSetIntersector
+{
+  // statistics information
+  int nOverlaps;
+
+  public SimpleEdgeSetIntersector() {
+  }
+
+  public void computeIntersections(List edges, SegmentIntersector si, boolean testAllSegments)
+  {
+    nOverlaps = 0;
+
+    for (Iterator i0 = edges.iterator(); i0.hasNext(); ) {
+      Edge edge0 = (Edge) i0.next();
+      for (Iterator i1 = edges.iterator(); i1.hasNext(); ) {
+        Edge edge1 = (Edge) i1.next();
+        if (testAllSegments || edge0 != edge1)
+          computeIntersects(edge0, edge1, si);
+      }
+    }
+  }
+
+
+  public void computeIntersections(List edges0, List edges1, SegmentIntersector si)
+  {
+    nOverlaps = 0;
+
+    for (Iterator i0 = edges0.iterator(); i0.hasNext(); ) {
+      Edge edge0 = (Edge) i0.next();
+      for (Iterator i1 = edges1.iterator(); i1.hasNext(); ) {
+        Edge edge1 = (Edge) i1.next();
+        computeIntersects(edge0, edge1, si);
+      }
+    }
+  }
+
+  /**
+   * Performs a brute-force comparison of every segment in each Edge.
+   * This has n^2 performance, and is about 100 times slower than using
+   * monotone chains.
+   */
+  private void computeIntersects(Edge e0, Edge e1, SegmentIntersector si)
+  {
+   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++) {
+        si.addIntersections(e0, i0, e1, i1);
+      }
+    }
+  }
+}

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/index/SimpleMCSweepLineIntersector.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,165 @@
+
+
+
+/*
+ * 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.geomgraph.index;
+
+/**
+ * @version 1.6
+ */
+import java.util.*;
+import com.vividsolutions.jts.geomgraph.*;
+
+/**
+ * Finds all intersections in one or two sets of edges,
+ * using an x-axis sweepline algorithm in conjunction with Monotone Chains.
+ * While still O(n^2) in the worst case, this algorithm
+ * drastically improves the average-case time.
+ * 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
+ */
+public class SimpleMCSweepLineIntersector
+  extends EdgeSetIntersector
+{
+
+  List events = new ArrayList();
+  // statistics information
+  int nOverlaps;
+
+  /**
+   * A SimpleMCSweepLineIntersector creates monotone chains from the edges
+   * and compares them using a simple sweep-line along the x-axis.
+   */
+  public SimpleMCSweepLineIntersector() {
+  }
+
+  public void computeIntersections(List edges, SegmentIntersector si, boolean testAllSegments)
+  {
+    if (testAllSegments)
+      add(edges, null);
+    else
+      add(edges);
+    computeIntersections(si);
+  }
+
+  public void computeIntersections(List edges0, List edges1, SegmentIntersector si)
+  {
+    add(edges0, edges0);
+    add(edges1, edges1);
+    computeIntersections(si);
+  }
+
+  private void add(List edges)
+  {
+    for (Iterator i = edges.iterator(); i.hasNext(); ) {
+      Edge edge = (Edge) i.next();
+      // edge is its own group
+      add(edge, edge);
+    }
+  }
+  private void add(List edges, Object edgeSet)
+  {
+    for (Iterator i = edges.iterator(); i.hasNext(); ) {
+      Edge edge = (Edge) i.next();
+      add(edge, edgeSet);
+    }
+  }
+
+  private void add(Edge edge, Object edgeSet)
+  {
+    MonotoneChainEdge mce = edge.getMonotoneChainEdge();
+    int[] startIndex = mce.getStartIndexes();
+    for (int i = 0; i < startIndex.length - 1; i++) {
+      MonotoneChain mc = new MonotoneChain(mce, i);
+      SweepLineEvent insertEvent = new SweepLineEvent(edgeSet, mce.getMinX(i), null, mc);
+      events.add(insertEvent);
+      events.add(new SweepLineEvent(edgeSet, mce.getMaxX(i), insertEvent, mc));
+    }
+  }
+
+  /**
+   * Because Delete Events have a link to their corresponding Insert event,
+   * it is possible to compute exactly the range of events which must be
+   * compared to a given Insert event object.
+   */
+  private void prepareEvents()
+  {
+    Collections.sort(events);
+    for (int i = 0; i < events.size(); i++ )
+    {
+      SweepLineEvent ev = (SweepLineEvent) events.get(i);
+      if (ev.isDelete()) {
+        ev.getInsertEvent().setDeleteEventIndex(i);
+      }
+    }
+  }
+
+  private void computeIntersections(SegmentIntersector si)
+  {
+    nOverlaps = 0;
+    prepareEvents();
+
+    for (int i = 0; i < events.size(); i++ )
+    {
+      SweepLineEvent ev = (SweepLineEvent) events.get(i);
+      if (ev.isInsert()) {
+        processOverlaps(i, ev.getDeleteEventIndex(), ev, si);
+      }
+    }
+  }
+
+  private void processOverlaps(int start, int end, SweepLineEvent ev0, SegmentIntersector si)
+  {
+    MonotoneChain mc0 = (MonotoneChain) ev0.getObject();
+    /**
+     * Since we might need to test for self-intersections,
+     * include current insert event object in list of event objects to test.
+     * Last index can be skipped, because it must be a Delete event.
+     */
+    for (int i = start; i < end; i++ ) {
+      SweepLineEvent ev1 = (SweepLineEvent) events.get(i);
+      if (ev1.isInsert()) {
+        MonotoneChain mc1 = (MonotoneChain) ev1.getObject();
+        // don't compare edges in same group
+        // null group indicates that edges should be compared
+        if (ev0.edgeSet == null || (ev0.edgeSet != ev1.edgeSet)) {
+          mc0.computeIntersections(mc1, si);
+          nOverlaps++;
+        }
+      }
+    }
+  }
+}

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/index/SimpleSweepLineIntersector.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,159 @@
+
+
+
+/*
+ * 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.geomgraph.index;
+
+/**
+ * @version 1.6
+ */
+import java.util.*;
+import com.vividsolutions.jts.geom.Coordinate;
+import com.vividsolutions.jts.geomgraph.*;
+
+/**
+ * Finds all intersections in one or two sets of edges,
+ * using a simple x-axis sweepline algorithm.
+ * While still O(n^2) in the worst case, this algorithm
+ * drastically improves the average-case time.
+ *
+ * @version 1.6
+ */
+public class SimpleSweepLineIntersector
+  extends EdgeSetIntersector
+{
+
+  List events = new ArrayList();
+  // statistics information
+  int nOverlaps;
+
+  public SimpleSweepLineIntersector() {
+  }
+
+  public void computeIntersections(List edges, SegmentIntersector si, boolean testAllSegments)
+  {
+    if (testAllSegments)
+      add(edges, null);
+    else
+      add(edges);
+    computeIntersections(si);
+  }
+
+  public void computeIntersections(List edges0, List edges1, SegmentIntersector si)
+  {
+    add(edges0, edges0);
+    add(edges1, edges1);
+    computeIntersections(si);
+  }
+
+  private void add(List edges)
+  {
+    for (Iterator i = edges.iterator(); i.hasNext(); ) {
+      Edge edge = (Edge) i.next();
+      // edge is its own group
+      add(edge, edge);
+    }
+  }
+  private void add(List edges, Object edgeSet)
+  {
+    for (Iterator i = edges.iterator(); i.hasNext(); ) {
+      Edge edge = (Edge) i.next();
+      add(edge, edgeSet);
+    }
+  }
+
+
+  private void add(Edge edge, Object edgeSet)
+  {
+    Coordinate[] pts = edge.getCoordinates();
+    for (int i = 0; i < pts.length - 1; i++) {
+      SweepLineSegment ss = new SweepLineSegment(edge, i);
+      SweepLineEvent insertEvent = new SweepLineEvent(edgeSet, ss.getMinX(), null, ss);
+      events.add(insertEvent);
+      events.add(new SweepLineEvent(edgeSet, ss.getMaxX(), insertEvent, ss));
+    }
+  }
+
+  /**
+   * Because Delete Events have a link to their corresponding Insert event,
+   * it is possible to compute exactly the range of events which must be
+   * compared to a given Insert event object.
+   */
+  private void prepareEvents()
+  {
+    Collections.sort(events);
+    for (int i = 0; i < events.size(); i++ )
+    {
+      SweepLineEvent ev = (SweepLineEvent) events.get(i);
+      if (ev.isDelete()) {
+        ev.getInsertEvent().setDeleteEventIndex(i);
+      }
+    }
+  }
+
+  private void computeIntersections(SegmentIntersector si)
+  {
+    nOverlaps = 0;
+    prepareEvents();
+
+    for (int i = 0; i < events.size(); i++ )
+    {
+      SweepLineEvent ev = (SweepLineEvent) events.get(i);
+      if (ev.isInsert()) {
+        processOverlaps(i, ev.getDeleteEventIndex(), ev, si);
+      }
+    }
+  }
+
+  private void processOverlaps(int start, int end, SweepLineEvent ev0, SegmentIntersector si)
+  {
+    SweepLineSegment ss0 = (SweepLineSegment) ev0.getObject();
+    /**
+     * Since we might need to test for self-intersections,
+     * include current insert event object in list of event objects to test.
+     * Last index can be skipped, because it must be a Delete event.
+     */
+    for (int i = start; i < end; i++ ) {
+      SweepLineEvent ev1 = (SweepLineEvent) events.get(i);
+      if (ev1.isInsert()) {
+        SweepLineSegment ss1 = (SweepLineSegment) ev1.getObject();
+        if (ev0.edgeSet == null || (ev0.edgeSet != ev1.edgeSet)) {
+          ss0.computeIntersections(ss1, si);
+          nOverlaps++;
+        }
+      }
+    }
+
+  }
+}

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/index/SweepLineEvent.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,89 @@
+
+
+
+/*
+ * 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.geomgraph.index;
+
+/**
+ * @version 1.6
+ */
+public class SweepLineEvent
+  implements Comparable
+{
+  public static final int INSERT = 1;
+  public static final int DELETE = 2;
+
+  Object edgeSet;    // used for red-blue intersection detection
+  private double xValue;
+  private int eventType;
+  private SweepLineEvent insertEvent; // null if this is an INSERT event
+  private int deleteEventIndex;
+  private Object obj;
+
+  public SweepLineEvent(Object edgeSet, double x, SweepLineEvent insertEvent, Object obj)
+  {
+    this.edgeSet = edgeSet;
+    xValue = x;
+    this.insertEvent = insertEvent;
+    this.eventType = INSERT;
+    if (insertEvent != null)
+      eventType = DELETE;
+    this.obj = obj;
+  }
+
+  public boolean isInsert() { return insertEvent == null; }
+  public boolean isDelete() { return insertEvent != null; }
+  public SweepLineEvent getInsertEvent() { return insertEvent; }
+  public int getDeleteEventIndex() { return deleteEventIndex; }
+  public void setDeleteEventIndex(int deleteEventIndex) { this.deleteEventIndex = deleteEventIndex; }
+
+  public Object getObject() { return obj; }
+
+  /**
+   * ProjectionEvents are ordered first by their x-value, and then by their eventType.
+   * It is important that Insert events are sorted before Delete events, so that
+   * items whose Insert and Delete events occur at the same x-value will be
+   * correctly handled.
+   */
+  public int compareTo(Object o) {
+    SweepLineEvent pe = (SweepLineEvent) o;
+    if (xValue < pe.xValue) return  -1;
+    if (xValue > pe.xValue) return   1;
+    if (eventType < pe.eventType) return  -1;
+    if (eventType > pe.eventType) return   1;
+    return 0;
+  }
+
+
+}

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/index/SweepLineSegment.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,74 @@
+
+
+
+/*
+ * 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.geomgraph.index;
+
+import com.vividsolutions.jts.geom.Coordinate;
+import com.vividsolutions.jts.geomgraph.*;
+
+
+/**
+ * @version 1.6
+ */
+public class SweepLineSegment {
+
+  Edge edge;
+  Coordinate[] pts;
+  int ptIndex;
+
+  public SweepLineSegment(Edge edge,  int ptIndex) {
+    this.edge = edge;
+    this.ptIndex = ptIndex;
+    pts = edge.getCoordinates();
+  }
+
+  public double getMinX()
+  {
+    double x1 = pts[ptIndex].x;
+    double x2 = pts[ptIndex + 1].x;
+    return x1 < x2 ? x1 : x2;
+  }
+  public double getMaxX()
+  {
+    double x1 = pts[ptIndex].x;
+    double x2 = pts[ptIndex + 1].x;
+    return x1 > x2 ? x1 : x2;
+  }
+  public void computeIntersections(SweepLineSegment ss, SegmentIntersector si)
+  {
+    si.addIntersections(edge, ptIndex, ss.edge, ss.ptIndex);
+  }
+
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/index/package.html
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/index/package.html	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/index/package.html	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,13 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+<!--
+
+-->
+</head>
+<body bgcolor="white">
+
+Contains classes that implement indexes for performing noding on geometry graph edges.
+
+</body>
+</html>

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/package.html
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/package.html	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/geomgraph/package.html	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,25 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+<!--
+
+-->
+</head>
+<body bgcolor="white">
+
+Contains classes that implement topology graphs.
+<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.
+
+<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/index/ArrayListVisitor.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/ArrayListVisitor.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/ArrayListVisitor.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,24 @@
+package com.vividsolutions.jts.index;
+
+import java.util.*;
+import com.vividsolutions.jts.index.ItemVisitor;
+
+/**
+ * @version 1.6
+ */
+public class ArrayListVisitor
+    implements ItemVisitor
+{
+
+  private ArrayList items = new ArrayList();
+  public ArrayListVisitor() {
+  }
+
+  public void visitItem(Object item)
+  {
+    items.add(item);
+  }
+
+  public ArrayList getItems() { return items; }
+
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/IndexVisitor.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/IndexVisitor.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/IndexVisitor.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,11 @@
+package com.vividsolutions.jts.index;
+
+/**
+ * A visitor for nodes and items in an index.
+ *
+ * @version 1.6
+ */
+
+public interface IndexVisitor {
+  void visitItem(Object item);
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/ItemVisitor.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/ItemVisitor.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/ItemVisitor.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,12 @@
+package com.vividsolutions.jts.index;
+
+/**
+ * A visitor for items in an index.
+ *
+ * @version 1.6
+ */
+
+public interface ItemVisitor
+{
+  void visitItem(Object item);
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/SpatialIndex.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/SpatialIndex.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/SpatialIndex.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -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.index;
+
+import java.util.*;
+import com.vividsolutions.jts.geom.Envelope;
+
+/**
+ * The basic insertion and query 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.
+ *
+ * @version 1.6
+ */
+public interface SpatialIndex
+{
+  /**
+   * Adds a spatial item with an extent specified by the given {@link Envelope} to the index
+   */
+  void insert(Envelope itemEnv, Object item);
+
+  /**
+   * Queries the index for all items whose extents intersect the given search {@link Envelope}
+   * 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
+   * @return a list of the items found by the query
+   */
+  List query(Envelope searchEnv);
+
+  /**
+   * Removes a single item from the tree.
+   *
+   * @param itemEnv the Envelope of the item to remove
+   * @param item the item to remove
+   * @return <code>true</code> if the item was found
+   */
+  boolean remove(Envelope itemEnv, Object item);
+
+}

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/bintree/Bintree.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,172 @@
+
+/*
+ * 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.index.bintree;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+/**
+ * An <code>BinTree</code> (or "Binary Interval Tree")
+ * is a 1-dimensional version of a quadtree.
+ * It indexes 1-dimensional intervals (which of course may
+ * be the projection of 2-D objects on an axis).
+ * It supports range searching
+ * (where the range may be a single point).
+ * <p>
+ * This implementation does not require specifying the extent of the inserted
+ * items beforehand.  It will automatically expand to accomodate any extent
+ * of dataset.
+ * <p>
+ * This index is different to the Interval Tree of Edelsbrunner
+ * or the Segment Tree of Bentley.
+ *
+ * @version 1.6
+ */
+public class Bintree
+{
+  /**
+   * Ensure that the Interval for the inserted item has non-zero extents.
+   * Use the current minExtent to pad it, if necessary
+   */
+  public static Interval ensureExtent(Interval itemInterval, double minExtent)
+  {
+    double min = itemInterval.getMin();
+    double max = itemInterval.getMax();
+    // has a non-zero extent
+    if (min != max) return itemInterval;
+
+    // pad extent
+    if (min == max) {
+      min = min - minExtent / 2.0;
+      max = min + minExtent / 2.0;
+    }
+    return new Interval(min, max);
+  }
+
+  private Root root;
+  /**
+  *  Statistics
+  *
+  * minExtent is the minimum extent of all items
+  * inserted into the tree so far. It is used as a heuristic value
+  * to construct non-zero extents for features with zero extent.
+  * Start with a non-zero extent, in case the first feature inserted has
+  * a zero extent in both directions.  This value may be non-optimal, but
+  * only one feature will be inserted with this value.
+  **/
+  private double minExtent = 1.0;
+
+  public Bintree()
+  {
+    root = new Root();
+  }
+
+  public int depth()
+  {
+    if (root != null) return root.depth();
+    return 0;
+  }
+  public int size()
+  {
+    if (root != null) return root.size();
+    return 0;
+  }
+  /**
+   * Compute the total number of nodes in the tree
+   *
+   * @return the number of nodes in the tree
+   */
+  public int nodeSize()
+  {
+    if (root != null) return root.nodeSize();
+    return 0;
+  }
+
+  public void insert(Interval itemInterval, Object item)
+  {
+    collectStats(itemInterval);
+    Interval insertInterval = ensureExtent(itemInterval, minExtent);
+//int oldSize = size();
+    root.insert(insertInterval, item);
+    /* DEBUG
+int newSize = size();
+System.out.println("BinTree: size = " + newSize + "   node size = " + nodeSize());
+if (newSize <= oldSize) {
+      System.out.println("Lost item!");
+      root.insert(insertInterval, item);
+      System.out.println("reinsertion size = " + size());
+}
+    */
+  }
+
+  public Iterator iterator()
+  {
+    List foundItems = new ArrayList();
+    root.addAllItems(foundItems);
+    return foundItems.iterator();
+  }
+
+  public List query(double x)
+  {
+    return query(new Interval(x, x));
+  }
+
+  /**
+   * min and max may be the same value
+   */
+  public List query(Interval interval)
+  {
+    /**
+     * the items that are matched are all items in intervals
+     * which overlap the query interval
+     */
+    List foundItems = new ArrayList();
+    query(interval, foundItems);
+    return foundItems;
+  }
+
+  public void query(Interval interval, Collection foundItems)
+  {
+    root.addAllItemsFromOverlapping(interval, foundItems);
+  }
+
+  private void collectStats(Interval interval)
+  {
+    double del = interval.getWidth();
+    if (del < minExtent && del > 0.0)
+      minExtent = del;
+  }
+
+}

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/bintree/Interval.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,100 @@
+
+/*
+ * 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.index.bintree;
+
+/**
+ * Represents an (1-dimensional) closed interval on the Real number line.
+ *
+ * @version 1.6
+ */
+public class Interval {
+
+  public double min, max;
+
+  public Interval()
+  {
+    min = 0.0;
+    max = 0.0;
+  }
+
+  public Interval(double min, double max)
+  {
+    init(min, max);
+  }
+  public Interval(Interval interval)
+  {
+    init(interval.min, interval.max);
+  }
+  public void init(double min, double max)
+  {
+    this.min = min;
+    this.max = max;
+    if (min > max) {
+      this.min = max;
+      this.max = min;
+    }
+  }
+  public double getMin() { return min; }
+  public double getMax() { return max; }
+  public double getWidth() { return max - min; }
+
+  public void expandToInclude(Interval interval)
+  {
+    if (interval.max > max) max = interval.max;
+    if (interval.min < min) min = interval.min;
+  }
+  public boolean overlaps(Interval interval)
+  {
+    return overlaps(interval.min, interval.max);
+  }
+
+  public boolean overlaps(double min, double max)
+  {
+    if (this.min > max || this.max < min) return false;
+    return true;
+  }
+
+  public boolean contains(Interval interval)
+  {
+    return contains(interval.min, interval.max);
+  }
+  public boolean contains(double min, double max)
+  {
+    return (min >= this.min && max <= this.max);
+  }
+  public boolean contains(double p)
+  {
+    return (p >= this.min && p <= this.max);
+  }
+}

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/bintree/Key.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,96 @@
+
+/*
+ * 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.index.bintree;
+
+
+
+import com.vividsolutions.jts.index.quadtree.DoubleBits;
+
+/**
+ * A Key is a unique identifier for a node in a tree.
+ * 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
+ */
+public class Key {
+
+  public static int computeLevel(Interval interval)
+  {
+    double dx = interval.getWidth();
+    //int level = BinaryPower.exponent(dx) + 1;
+    int level = DoubleBits.exponent(dx) + 1;
+    return level;
+  }
+
+
+  // the fields which make up the key
+  private double pt = 0.0;
+  private int level = 0;
+  // auxiliary data which is derived from the key for use in computation
+  private Interval interval;
+
+  public Key(Interval interval)
+  {
+    computeKey(interval);
+  }
+
+  public double getPoint() { return pt; }
+  public int getLevel() { return level; }
+  public Interval getInterval() { return interval; }
+
+  /**
+   * return a square envelope containing the argument envelope,
+   * whose extent is a power of two and which is based at a power of 2
+   */
+  public void computeKey(Interval itemInterval)
+  {
+    level = computeLevel(itemInterval);
+    interval = new Interval();
+    computeInterval(level, itemInterval);
+    // MD - would be nice to have a non-iterative form of this algorithm
+    while (! interval.contains(itemInterval)) {
+      level += 1;
+      computeInterval(level, itemInterval);
+    }
+  }
+
+  private void computeInterval(int level, Interval itemInterval)
+  {
+    double size = DoubleBits.powerOf2(level);
+    //double size = pow2.power(level);
+    pt = Math.floor(itemInterval.getMin() / size) * size;
+    interval.init(pt, pt + size);
+  }
+}

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/bintree/Node.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,171 @@
+
+/*
+ * 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.index.bintree;
+
+import com.vividsolutions.jts.util.Assert;
+
+/**
+ * A node of a {@link Bintree}.
+ *
+ * @version 1.6
+ */
+public class Node
+  extends NodeBase
+{
+  public static Node createNode(Interval itemInterval)
+  {
+    Key key = new Key(itemInterval);
+
+//System.out.println("input: " + env + "  binaryEnv: " + key.getEnvelope());
+    Node node = new Node(key.getInterval(), key.getLevel());
+    return node;
+  }
+
+  public static Node createExpanded(Node node, Interval addInterval)
+  {
+    Interval expandInt = new Interval(addInterval);
+    if (node != null) expandInt.expandToInclude(node.interval);
+
+    Node largerNode = createNode(expandInt);
+    if (node != null) largerNode.insert(node);
+    return largerNode;
+  }
+
+  private Interval interval;
+  private double centre;
+  private int level;
+
+  public Node(Interval interval, int level)
+  {
+    this.interval = interval;
+    this.level = level;
+    centre = (interval.getMin() + interval.getMax()) / 2;
+  }
+
+  public Interval getInterval() { return interval; }
+
+  protected boolean isSearchMatch(Interval itemInterval)
+  {
+    return itemInterval.overlaps(interval);
+  }
+
+  /**
+   * Returns the subnode containing the envelope.
+   * Creates the node if
+   * it does not already exist.
+   */
+  public Node getNode(Interval searchInterval)
+  {
+    int subnodeIndex = getSubnodeIndex(searchInterval, centre);
+    // if index is -1 searchEnv is not contained in a subnode
+    if (subnodeIndex != -1) {
+      // create the node if it does not exist
+      Node node = getSubnode(subnodeIndex);
+      // recursively search the found/created node
+      return node.getNode(searchInterval);
+    }
+    else {
+      return this;
+    }
+  }
+
+  /**
+   * Returns the smallest <i>existing</i>
+   * node containing the envelope.
+   */
+  public NodeBase find(Interval searchInterval)
+  {
+    int subnodeIndex = getSubnodeIndex(searchInterval, centre);
+    if (subnodeIndex == -1)
+      return this;
+    if (subnode[subnodeIndex] != null) {
+      // query lies in subnode, so search it
+      Node node = subnode[subnodeIndex];
+      return node.find(searchInterval);
+    }
+    // no existing subnode, so return this one anyway
+    return this;
+  }
+
+  void insert(Node node)
+  {
+    Assert.isTrue(interval == null || interval.contains(node.interval));
+    int index = getSubnodeIndex(node.interval, centre);
+    if (node.level == level - 1) {
+      subnode[index] = node;
+    }
+    else {
+      // the node is not a direct child, so make a new child node to contain it
+      // and recursively insert the node
+      Node childNode = createSubnode(index);
+      childNode.insert(node);
+      subnode[index] = childNode;
+    }
+  }
+
+  /**
+   * get the subnode for the index.
+   * If it doesn't exist, create it
+   */
+  private Node getSubnode(int index)
+  {
+    if (subnode[index] == null) {
+      subnode[index] = createSubnode(index);
+    }
+    return subnode[index];
+  }
+
+  private Node createSubnode(int index)
+  {
+        // create a new subnode in the appropriate interval
+
+      double min = 0.0;
+      double max = 0.0;
+
+      switch (index) {
+      case 0:
+        min = interval.getMin();
+        max = centre;
+        break;
+      case 1:
+        min = centre;
+        max = interval.getMax();
+        break;
+      }
+      Interval subInt = new Interval(min, max);
+      Node node = new Node(subInt, level - 1);
+    return node;
+  }
+
+}

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/bintree/NodeBase.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,140 @@
+
+/*
+ * 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.index.bintree;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+
+/**
+ * The base class for nodes in a {@link Bintree}.
+ *
+ * @version 1.6
+ */
+public abstract class NodeBase {
+
+  /**
+   * Returns the index of the subnode that wholely contains the given interval.
+   * If none does, returns -1.
+   */
+  public static int getSubnodeIndex(Interval interval, double centre)
+  {
+    int subnodeIndex = -1;
+    if (interval.min >= centre) subnodeIndex = 1;
+    if (interval.max <= centre) subnodeIndex = 0;
+    return subnodeIndex;
+  }
+
+  protected List items = new ArrayList();
+
+  /**
+   * subnodes are numbered as follows:
+   *
+   *  0 | 1
+   */
+  protected Node[] subnode = new Node[2];
+
+  public NodeBase() {
+  }
+
+  public List getItems() { return items; }
+
+  public void add(Object item)
+  {
+    items.add(item);
+  }
+  public List addAllItems(List items)
+  {
+    items.addAll(this.items);
+    for (int i = 0; i < 2; i++) {
+      if (subnode[i] != null) {
+        subnode[i].addAllItems(items);
+      }
+    }
+    return items;
+  }
+  protected abstract boolean isSearchMatch(Interval interval);
+
+  public List addAllItemsFromOverlapping(Interval interval, Collection resultItems)
+  {
+    if (! isSearchMatch(interval))
+      return items;
+
+    resultItems.addAll(items);
+
+    for (int i = 0; i < 2; i++) {
+      if (subnode[i] != null) {
+        subnode[i].addAllItemsFromOverlapping(interval, resultItems);
+      }
+    }
+    return items;
+  }
+
+  int depth()
+  {
+    int maxSubDepth = 0;
+    for (int i = 0; i < 2; i++) {
+      if (subnode[i] != null) {
+        int sqd = subnode[i].depth();
+        if (sqd > maxSubDepth)
+          maxSubDepth = sqd;
+      }
+    }
+    return maxSubDepth + 1;
+  }
+
+  int size()
+  {
+    int subSize = 0;
+    for (int i = 0; i < 2; i++) {
+      if (subnode[i] != null) {
+        subSize += subnode[i].size();
+      }
+    }
+    return subSize + items.size();
+  }
+
+  int nodeSize()
+  {
+    int subSize = 0;
+    for (int i = 0; i < 2; i++) {
+      if (subnode[i] != null) {
+        subSize += subnode[i].nodeSize();
+      }
+    }
+    return subSize + 1;
+  }
+
+}

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/bintree/Root.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -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.index.bintree;
+
+import com.vividsolutions.jts.index.quadtree.IntervalSize;
+import com.vividsolutions.jts.util.Assert;
+
+/**
+ * The root node of a single {@link Bintree}.
+ * It is centred at the origin,
+ * and does not have a defined extent.
+ *
+ * @version 1.6
+ */
+public class Root
+  extends NodeBase
+{
+
+  // the singleton root node is centred at the origin.
+  private static final double origin = 0.0;
+
+  public Root()
+  {
+  }
+
+  /**
+   * Insert an item into the tree this is the root of.
+   */
+  public void insert(Interval itemInterval, Object item)
+  {
+    int index = getSubnodeIndex(itemInterval, origin);
+    // if index is -1, itemEnv must contain the origin.
+    if (index == -1) {
+      add(item);
+      return;
+    }
+    /**
+     * the item must be contained in one interval, so insert it into the
+     * tree for that interval (which may not yet exist)
+     */
+    Node node = subnode[index];
+    /**
+     *  If the subnode doesn't exist or this item is not contained in it,
+     *  have to expand the tree upward to contain the item.
+     */
+
+    if (node == null || ! node.getInterval().contains(itemInterval)) {
+       Node largerNode = Node.createExpanded(node, itemInterval);
+       subnode[index] = largerNode;
+    }
+    /**
+     * At this point we have a subnode which exists and must contain
+     * contains the env for the item.  Insert the item into the tree.
+     */
+    insertContained(subnode[index], itemInterval, item);
+//System.out.println("depth = " + root.depth() + " size = " + root.size());
+  }
+
+  /**
+   * insert an item which is known to be contained in the tree rooted at
+   * the given Node.  Lower levels of the tree will be created
+   * if necessary to hold the item.
+   */
+  private void insertContained(Node tree, Interval itemInterval, Object item)
+  {
+    Assert.isTrue(tree.getInterval().contains(itemInterval));
+   /**
+    * Do NOT create a new node for zero-area intervals - this would lead
+    * to infinite recursion. Instead, use a heuristic of simply returning
+    * the smallest existing node containing the query
+    */
+    boolean isZeroArea = IntervalSize.isZeroWidth(itemInterval.getMin(), itemInterval.getMax());
+    NodeBase node;
+    if (isZeroArea)
+      node = tree.find(itemInterval);
+    else
+      node = tree.getNode(itemInterval);
+    node.add(item);
+  }
+
+  /**
+   * The root node matches all searches
+   */
+  protected boolean isSearchMatch(Interval interval)
+  {
+    return true;
+  }
+
+
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/bintree/package.html
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/bintree/package.html	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/bintree/package.html	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,13 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+<!--
+
+-->
+</head>
+<body bgcolor="white">
+
+Contains classes that implement a Binary Interval Tree index
+
+</body>
+</html>

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/chain/MonotoneChain.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,216 @@
+
+
+/*
+ * 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.index.chain;
+
+import com.vividsolutions.jts.geom.*;
+
+
+/**
+ * MonotoneChains are a way of partitioning the segments of a linestring to
+ * allow for fast searching of intersections.
+ * They have the following properties:
+ * <ol>
+ * <li>the segments within a monotone chain will never intersect each other
+ * <li>the envelope of any contiguous subset of the segments in a monotone chain
+ * is equal to the envelope of the endpoints of the subset.
+ * </ol>
+ * Property 1 means that there is no need to test pairs of segments from within
+ * the same monotone chain for intersection.
+ * Property 2 allows
+ * 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.
+ * <p>
+ * One of the goals of this implementation of MonotoneChains is to be
+ * as space and time efficient as possible. One design choice that aids this
+ * is that a MonotoneChain is based on a subarray of a list of points.
+ * This means that new arrays of points (potentially very large) do not
+ * have to be allocated.
+ * <p>
+ *
+ * MonotoneChains support the following kinds of queries:
+ * <ul>
+ * <li>Envelope select: determine all the segments in the chain which
+ * intersect a given envelope
+ * <li>Overlap: determine all the pairs of segments in two chains whose
+ * envelopes overlap
+ * </ul>
+ *
+ * This implementation of MonotoneChains uses the concept of internal iterators
+ * to return the resultsets for the above queries.
+ * This has time and space advantages, since it
+ * is not necessary to build lists of instantiated objects to represent the segments
+ * returned by the query.
+ * However, it does mean that the queries are not thread-safe.
+ *
+ * @version 1.6
+ */
+public class MonotoneChain {
+
+  private Coordinate[] pts;
+  private int start, end;
+  private Envelope env = null;
+  private Object context = null;// user-defined information
+  private int id;// useful for optimizing chain comparisons
+
+  public MonotoneChain(Coordinate[] pts, int start, int end, Object context)
+  {
+    this.pts    = pts;
+    this.start  = start;
+    this.end    = end;
+    this.context = context;
+  }
+
+  public void setId(int id) { this.id = id; }
+  public int getId() { return id; }
+
+  public Object getContext() { return context; }
+
+  public Envelope getEnvelope()
+  {
+    if (env == null) {
+      Coordinate p0 = pts[start];
+      Coordinate p1 = pts[end];
+      env = new Envelope(p0, p1);
+    }
+    return env;
+  }
+
+  public int getStartIndex()  { return start; }
+  public int getEndIndex()    { return end; }
+
+  public void getLineSegment(int index, LineSegment ls)
+  {
+    ls.p0 = pts[index];
+    ls.p1 = pts[index + 1];
+  }
+  /**
+   * Return the subsequence of coordinates forming this chain.
+   * Allocates a new array to hold the Coordinates
+   */
+  public Coordinate[] getCoordinates()
+  {
+    Coordinate coord[] = new Coordinate[end - start + 1];
+    int index = 0;
+    for (int i = start; i <= end; i++) {
+      coord[index++] = pts[i];
+    }
+    return coord;
+  }
+
+  /**
+   * Determine all the line segments in the chain whose envelopes overlap
+   * the searchEnvelope, and process them
+   */
+  public void select(Envelope searchEnv, MonotoneChainSelectAction mcs)
+  {
+    computeSelect(searchEnv, start, end, mcs);
+  }
+
+  private void computeSelect(
+    Envelope searchEnv,
+    int start0, int end0,
+    MonotoneChainSelectAction mcs )
+  {
+    Coordinate p0 = pts[start0];
+    Coordinate p1 = pts[end0];
+    mcs.tempEnv1.init(p0, p1);
+
+//Debug.println("trying:" + p0 + p1 + " [ " + start0 + ", " + end0 + " ]");
+    // terminating condition for the recursion
+    if (end0 - start0 == 1) {
+      //Debug.println("computeSelect:" + p0 + p1);
+      mcs.select(this, start0);
+      return;
+    }
+    // nothing to do if the envelopes don't overlap
+    if (! searchEnv.intersects(mcs.tempEnv1))
+      return;
+
+    // the chains overlap, so split each in half and iterate  (binary search)
+    int mid = (start0 + end0) / 2;
+
+    // Assert: mid != start or end (since we checked above for end - start <= 1)
+    // check terminating conditions before recursing
+    if (start0 < mid) {
+      computeSelect(searchEnv, start0, mid, mcs);
+    }
+    if (mid < end0) {
+      computeSelect(searchEnv, mid, end0, mcs);
+    }
+  }
+
+  public void computeOverlaps(MonotoneChain mc, MonotoneChainOverlapAction mco)
+  {
+    computeOverlaps(start, end, mc, mc.start, mc.end, mco);
+  }
+
+  private void computeOverlaps(
+    int start0, int end0,
+    MonotoneChain mc,
+    int start1, int end1,
+    MonotoneChainOverlapAction mco)
+  {
+    Coordinate p00 = pts[start0];
+    Coordinate p01 = pts[end0];
+    Coordinate p10 = mc.pts[start1];
+    Coordinate p11 = mc.pts[end1];
+//Debug.println("computeIntersectsForChain:" + p00 + p01 + p10 + p11);
+    // terminating condition for the recursion
+    if (end0 - start0 == 1 && end1 - start1 == 1) {
+      mco.overlap(this, start0, mc, start1);
+      return;
+    }
+    // nothing to do if the envelopes of these chains don't overlap
+    mco.tempEnv1.init(p00, p01);
+    mco.tempEnv2.init(p10, p11);
+    if (! mco.tempEnv1.intersects(mco.tempEnv2)) return;
+
+    // the chains overlap, so split each in half and iterate  (binary search)
+    int mid0 = (start0 + end0) / 2;
+    int mid1 = (start1 + end1) / 2;
+
+    // Assert: mid != start or end (since we checked above for end - start <= 1)
+    // check terminating conditions before recursing
+    if (start0 < mid0) {
+      if (start1 < mid1) computeOverlaps(start0, mid0, mc, start1,  mid1, mco);
+      if (mid1 < end1)   computeOverlaps(start0, mid0, mc, mid1,    end1, mco);
+    }
+    if (mid0 < end0) {
+      if (start1 < mid1) computeOverlaps(mid0,   end0, mc, start1,  mid1, mco);
+      if (mid1 < end1)   computeOverlaps(mid0,   end0, mc, mid1,    end1, mco);
+    }
+  }
+}

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/chain/MonotoneChainBuilder.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,120 @@
+
+
+/*
+ * 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.index.chain;
+
+import java.util.*;
+import com.vividsolutions.jts.geom.Coordinate;
+import com.vividsolutions.jts.geomgraph.Quadrant;
+
+/**
+ * A MonotoneChainBuilder implements functions to determine the monotone chains
+ * in a sequence of points.
+ *
+ * @version 1.6
+ */
+public class MonotoneChainBuilder {
+
+  public static int[] toIntArray(List list)
+  {
+    int[] array = new int[list.size()];
+    for (int i = 0; i < array.length; i++) {
+      array[i] = ((Integer) list.get(i)).intValue();
+    }
+    return array;
+  }
+
+  public static List getChains(Coordinate[] pts)
+  {
+    return getChains(pts, null);
+  }
+
+  /**
+   * Return a list of the {@link MonotoneChain}s
+   * for the given list of coordinates.
+   */
+  public static List getChains(Coordinate[] pts, Object context)
+  {
+    List mcList = new ArrayList();
+    int[] startIndex = getChainStartIndices(pts);
+    for (int i = 0; i < startIndex.length - 1; i++) {
+      MonotoneChain mc = new MonotoneChain(pts, startIndex[i], startIndex[i + 1], context);
+      mcList.add(mc);
+    }
+    return mcList;
+  }
+
+  /**
+   * Return an array containing lists of start/end indexes of the monotone chains
+   * for the given list of coordinates.
+   * The last entry in the array points to the end point of the point array,
+   * for use as a sentinel.
+   */
+  public static int[] getChainStartIndices(Coordinate[] pts)
+  {
+    // find the startpoint (and endpoints) of all monotone chains in this edge
+    int start = 0;
+    List startIndexList = new ArrayList();
+    startIndexList.add(new Integer(start));
+    do {
+      int last = findChainEnd(pts, start);
+      startIndexList.add(new Integer(last));
+      start = last;
+    } while (start < pts.length - 1);
+    // copy list to an array of ints, for efficiency
+    int[] startIndex = toIntArray(startIndexList);
+    return startIndex;
+  }
+
+  /**
+   * @return the index of the last point in the monotone chain starting at <code>start</code>.
+   */
+  private static int findChainEnd(Coordinate[] pts, int start)
+  {
+    // determine quadrant for chain
+    int chainQuad = Quadrant.quadrant(pts[start], pts[start + 1]);
+    int last = start + 1;
+    while (last < pts.length) {
+      // compute quadrant for next possible segment in chain
+      int quad = Quadrant.quadrant(pts[last - 1], pts[last]);
+      if (quad != chainQuad) break;
+      last++;
+    }
+    return last - 1;
+  }
+
+
+  public MonotoneChainBuilder() {
+  }
+}

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/chain/MonotoneChainOverlapAction.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -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.index.chain;
+
+import com.vividsolutions.jts.geom.*;
+
+/**
+ * The action for the internal iterator for performing
+ * overlap queries on a MonotoneChain
+ *
+ * @version 1.6
+ */
+public class MonotoneChainOverlapAction
+{
+  // these envelopes are used during the MonotoneChain search process
+  Envelope tempEnv1 = new Envelope();
+  Envelope tempEnv2 = new Envelope();
+
+  protected LineSegment overlapSeg1 = new LineSegment();
+  protected LineSegment overlapSeg2 = new LineSegment();
+
+  /**
+   * This function can be overridden if the original chains are needed
+   *
+   * @param start1 the index of the start of the overlapping segment from mc1
+   * @param start2 the index of the start of the overlapping segment from mc2
+   */
+  public void overlap(MonotoneChain mc1, int start1, MonotoneChain mc2, int start2)
+  {
+    mc1.getLineSegment(start1, overlapSeg1);
+    mc2.getLineSegment(start2, overlapSeg2);
+    overlap(overlapSeg1, overlapSeg2);
+  }
+
+  /**
+   * This is a convenience function which can be overridden to obtain the actual
+   * line segments which overlap
+   * @param seg1
+   * @param seg2
+   */
+  public void overlap(LineSegment seg1, LineSegment seg2)
+  {
+  }
+}

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/chain/MonotoneChainSelectAction.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,68 @@
+
+
+/*
+ * 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.index.chain;
+
+import com.vividsolutions.jts.geom.*;
+/**
+ * The action for the internal iterator for performing
+ * envelope select queries on a MonotoneChain
+ *
+ * @version 1.6
+ */
+public class MonotoneChainSelectAction
+{
+  // these envelopes are used during the MonotoneChain search process
+  Envelope tempEnv1 = new Envelope();
+
+  LineSegment selectedSegment = new LineSegment();
+
+  /**
+   * This function can be overridden if the original chain is needed
+   */
+  public void select(MonotoneChain mc, int start)
+  {
+    mc.getLineSegment(start, selectedSegment);
+    select(selectedSegment);
+  }
+
+  /**
+   * This is a convenience function which can be overridden to obtain the actual
+   * line segment which is selected
+   * @param seg
+   */
+  public void select(LineSegment seg)
+  {
+  }
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/chain/package.html
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/chain/package.html	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/chain/package.html	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,13 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+<!--
+
+-->
+</head>
+<body bgcolor="white">
+
+Contains classes that implement Monotone Chains
+
+</body>
+</html>

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/package.html
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/package.html	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/package.html	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,13 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+<!--
+
+-->
+</head>
+<body bgcolor="white">
+
+Provides classes for various kinds of spatial indexes.
+
+</body>
+</html>

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/quadtree/DoubleBits.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,174 @@
+
+/*
+ * 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.index.quadtree;
+
+/**
+ * DoubleBits manipulates Double numbers
+ * by using bit manipulation and bit-field extraction.
+ * For some operations (such as determining the exponent)
+ * this is more accurate than using mathematical operations
+ * (which suffer from round-off error).
+ * <p>
+ * The algorithms and constants in this class
+ * apply only to IEEE-754 double-precision floating point format.
+ *
+ * @version 1.6
+ */
+public class DoubleBits {
+
+  public static final int EXPONENT_BIAS = 1023;
+
+  public static double powerOf2(int exp)
+  {
+    if (exp > 1023 || exp < -1022)
+      throw new IllegalArgumentException("Exponent out of bounds");
+    long expBias = exp + EXPONENT_BIAS;
+    long bits = (long) expBias << 52;
+    return Double.longBitsToDouble(bits);
+  }
+
+  public static int exponent(double d)
+  {
+    DoubleBits db = new DoubleBits(d);
+    return db.getExponent();
+  }
+
+  public static double truncateToPowerOfTwo(double d)
+  {
+    DoubleBits db = new DoubleBits(d);
+    db.zeroLowerBits(52);
+    return db.getDouble();
+  }
+
+  public static String toBinaryString(double d)
+  {
+    DoubleBits db = new DoubleBits(d);
+    return db.toString();
+  }
+
+  public static double maximumCommonMantissa(double d1, double d2)
+  {
+    if (d1 == 0.0 || d2 == 0.0) return 0.0;
+
+    DoubleBits db1 = new DoubleBits(d1);
+    DoubleBits db2 = new DoubleBits(d2);
+
+    if (db1.getExponent() != db2.getExponent()) return 0.0;
+
+    int maxCommon = db1.numCommonMantissaBits(db2);
+    db1.zeroLowerBits(64 - (12 + maxCommon));
+    return db1.getDouble();
+  }
+
+  private double x;
+  private long xBits;
+
+  public DoubleBits(double x)
+  {
+    this.x = x;
+    xBits = Double.doubleToLongBits(x);
+  }
+
+  public double getDouble()
+  {
+    return Double.longBitsToDouble(xBits);
+  }
+
+  /**
+   * Determines the exponent for the number
+   */
+  public int biasedExponent()
+  {
+    int signExp = (int) (xBits >> 52);
+    int exp = signExp & 0x07ff;
+    return exp;
+  }
+
+  /**
+   * Determines the exponent for the number
+   */
+  public int getExponent()
+  {
+    return biasedExponent() - EXPONENT_BIAS;
+  }
+
+  public void zeroLowerBits(int nBits)
+  {
+    long invMask = (1L << nBits) - 1L;
+    long mask = ~ invMask;
+    xBits &= mask;
+  }
+
+  public int getBit(int i)
+  {
+    long mask = (1L << i);
+    return (xBits & mask) != 0 ? 1 : 0;
+  }
+
+  /**
+   * This computes the number of common most-significant bits in the mantissa.
+   * It does not count the hidden bit, which is always 1.
+   * It does not determine whether the numbers have the same exponent - if they do
+   * not, the value computed by this function is meaningless.
+   * @param db
+   * @return the number of common most-significant mantissa bits
+   */
+  public int numCommonMantissaBits(DoubleBits db)
+  {
+    for (int i = 0; i < 52; i++)
+    {
+      int bitIndex = i + 12;
+      if (getBit(i) != db.getBit(i))
+        return i;
+    }
+    return 52;
+  }
+
+  /**
+   * A representation of the Double bits formatted for easy readability
+   */
+  public String toString()
+  {
+    String numStr = Long.toBinaryString(xBits);
+    // 64 zeroes!
+    String zero64 = "0000000000000000000000000000000000000000000000000000000000000000";
+    String padStr =  zero64 + numStr;
+    String bitStr = padStr.substring(padStr.length() - 64);
+    String str = bitStr.substring(0, 1) + "  "
+        + bitStr.substring(1, 12) + "(" + getExponent() + ") "
+        + bitStr.substring(12)
+        + " [ " + x + " ]";
+    return str;
+  }
+}

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/quadtree/IntervalSize.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -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.index.quadtree;
+
+/**
+ * Provides a test for whether an interval is
+ * so small it should be considered as zero for the purposes of
+ * inserting it into a binary tree.
+ * The reason this check is necessary is that round-off error can
+ * cause the algorithm used to subdivide an interval to fail, by
+ * computing a midpoint value which does not lie strictly between the
+ * endpoints.
+ *
+ * @version 1.6
+ */
+public class IntervalSize {
+
+  /**
+   * This value is chosen to be a few powers of 2 less than the
+   * number of bits available in the double representation (i.e. 53).
+   * This should allow enough extra precision for simple computations to be correct,
+   * at least for comparison purposes.
+   */
+  public static final int MIN_BINARY_EXPONENT = -50;
+
+  /**
+   * Computes whether the interval [min, max] is effectively zero width.
+   * I.e. the width of the interval is so much less than the
+   * location of the interval that the midpoint of the interval cannot be
+   * represented precisely.
+   */
+  public static boolean isZeroWidth(double min, double max)
+  {
+    double width = max - min;
+    if (width == 0.0) return true;
+
+    double maxAbs = Math.max(Math.abs(min), Math.abs(max));
+    double scaledInterval = width / maxAbs;
+    int level = DoubleBits.exponent(scaledInterval);
+    return level <= MIN_BINARY_EXPONENT;
+  }
+}

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/quadtree/Key.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,102 @@
+
+/*
+ * 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.index.quadtree;
+
+import com.vividsolutions.jts.geom.Coordinate;
+import com.vividsolutions.jts.geom.Envelope;
+
+/**
+ * A Key is a unique identifier for a node in a quadtree.
+ * 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
+ */
+public class Key {
+
+  public static int computeQuadLevel(Envelope env)
+  {
+    double dx = env.getWidth();
+    double dy = env.getHeight();
+    double dMax = dx > dy ? dx : dy;
+    int level = DoubleBits.exponent(dMax) + 1;
+    return level;
+  }
+
+  // the fields which make up the key
+  private Coordinate pt = new Coordinate();
+  private int level = 0;
+  // auxiliary data which is derived from the key for use in computation
+  private Envelope env = null;
+
+  public Key(Envelope itemEnv)
+  {
+    computeKey(itemEnv);
+  }
+
+  public Coordinate getPoint() { return pt; }
+  public int getLevel() { return level; }
+  public Envelope getEnvelope() { return env; }
+
+  public Coordinate getCentre()
+  {
+    return new Coordinate(
+      (env.getMinX() + env.getMaxX()) / 2,
+      (env.getMinY() + env.getMaxY()) / 2
+      );
+  }
+  /**
+   * return a square envelope containing the argument envelope,
+   * whose extent is a power of two and which is based at a power of 2
+   */
+  public void computeKey(Envelope itemEnv)
+  {
+    level = computeQuadLevel(itemEnv);
+    env = new Envelope();
+    computeKey(level, itemEnv);
+    // MD - would be nice to have a non-iterative form of this algorithm
+    while (! env.contains(itemEnv)) {
+      level += 1;
+      computeKey(level, itemEnv);
+    }
+  }
+
+  private void computeKey(int level, Envelope itemEnv)
+  {
+    double quadSize = DoubleBits.powerOf2(level);
+    pt.x = Math.floor(itemEnv.getMinX() / quadSize) * quadSize;
+    pt.y = Math.floor(itemEnv.getMinY() / quadSize) * quadSize;
+    env.init(pt.x, pt.x + quadSize, pt.y, pt.y + quadSize);
+  }
+}

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/quadtree/Node.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,198 @@
+
+/*
+ * 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.index.quadtree;
+
+import com.vividsolutions.jts.geom.Coordinate;
+import com.vividsolutions.jts.geom.Envelope;
+import com.vividsolutions.jts.util.Assert;
+
+/**
+ * Represents a node of a {@link Quadtree}.  Nodes contain
+ * items which have a spatial extent corresponding to the node's position
+ * in the quadtree.
+ *
+ * @version 1.6
+ */
+public class Node
+  extends NodeBase
+{
+  public static Node createNode(Envelope env)
+  {
+    Key key = new Key(env);
+    Node node = new Node(key.getEnvelope(), key.getLevel());
+    return node;
+  }
+
+  public static Node createExpanded(Node node, Envelope addEnv)
+  {
+    Envelope expandEnv = new Envelope(addEnv);
+    if (node != null) expandEnv.expandToInclude(node.env);
+
+    Node largerNode = createNode(expandEnv);
+    if (node != null) largerNode.insertNode(node);
+    return largerNode;
+  }
+
+  private Envelope env;
+  private Coordinate centre;
+  private int level;
+
+  public Node(Envelope env, int level)
+  {
+    //this.parent = parent;
+    this.env = env;
+    this.level = level;
+    centre = new Coordinate();
+    centre.x = (env.getMinX() + env.getMaxX()) / 2;
+    centre.y = (env.getMinY() + env.getMaxY()) / 2;
+  }
+
+  public Envelope getEnvelope() { return env; }
+
+  protected boolean isSearchMatch(Envelope searchEnv)
+  {
+    return env.intersects(searchEnv);
+  }
+
+  /**
+   * Returns the subquad containing the envelope.
+   * Creates the subquad if
+   * it does not already exist.
+   */
+  public Node getNode(Envelope searchEnv)
+  {
+    int subnodeIndex = getSubnodeIndex(searchEnv, centre);
+    // if subquadIndex is -1 searchEnv is not contained in a subquad
+    if (subnodeIndex != -1) {
+      // create the quad if it does not exist
+      Node node = getSubnode(subnodeIndex);
+      // recursively search the found/created quad
+      return node.getNode(searchEnv);
+    }
+    else {
+      return this;
+    }
+  }
+
+  /**
+   * Returns the smallest <i>existing</i>
+   * node containing the envelope.
+   */
+  public NodeBase find(Envelope searchEnv)
+  {
+    int subnodeIndex = getSubnodeIndex(searchEnv, centre);
+    if (subnodeIndex == -1)
+      return this;
+    if (subnode[subnodeIndex] != null) {
+      // query lies in subquad, so search it
+      Node node = subnode[subnodeIndex];
+      return node.find(searchEnv);
+    }
+    // no existing subquad, so return this one anyway
+    return this;
+  }
+
+  void insertNode(Node node)
+  {
+    Assert.isTrue(env == null || env.contains(node.env));
+//System.out.println(env);
+//System.out.println(quad.env);
+    int index = getSubnodeIndex(node.env, centre);
+//System.out.println(index);
+    if (node.level == level - 1) {
+      subnode[index] = node;
+//System.out.println("inserted");
+    }
+    else {
+      // the quad is not a direct child, so make a new child quad to contain it
+      // and recursively insert the quad
+      Node childNode = createSubnode(index);
+      childNode.insertNode(node);
+      subnode[index] = childNode;
+    }
+  }
+
+  /**
+   * get the subquad for the index.
+   * If it doesn't exist, create it
+   */
+  private Node getSubnode(int index)
+  {
+    if (subnode[index] == null) {
+      subnode[index] = createSubnode(index);
+    }
+    return subnode[index];
+  }
+
+  private Node createSubnode(int index)
+  {
+        // create a new subquad in the appropriate quadrant
+
+      double minx = 0.0;
+      double maxx = 0.0;
+      double miny = 0.0;
+      double maxy = 0.0;
+
+      switch (index) {
+      case 0:
+        minx = env.getMinX();
+        maxx = centre.x;
+        miny = env.getMinY();
+        maxy = centre.y;
+        break;
+      case 1:
+        minx = centre.x;
+        maxx = env.getMaxX();
+        miny = env.getMinY();
+        maxy = centre.y;
+        break;
+      case 2:
+        minx = env.getMinX();
+        maxx = centre.x;
+        miny = centre.y;
+        maxy = env.getMaxY();
+        break;
+      case 3:
+        minx = centre.x;
+        maxx = env.getMaxX();
+        miny = centre.y;
+        maxy = env.getMaxY();
+        break;
+      }
+      Envelope sqEnv = new Envelope(minx, maxx, miny, maxy);
+      Node node = new Node(sqEnv, level - 1);
+    return node;
+  }
+
+}

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/quadtree/NodeBase.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,256 @@
+
+/*
+ * 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.index.quadtree;
+
+import java.util.*;
+
+import com.vividsolutions.jts.geom.Coordinate;
+import com.vividsolutions.jts.geom.Envelope;
+import com.vividsolutions.jts.index.*;
+
+/**
+ * The base class for nodes in a {@link Quadtree}.
+ *
+ * @version 1.6
+ */
+public abstract class NodeBase {
+
+//<<TODO:REFACTOR?>> Several classes in the various tree packages have the
+//same name and duplicate code. This suggests that there should be a generic
+//tree package containing the code that is duplicated, perhaps in abstract
+//base classes. [Jon Aquino]
+
+//<<TODO:RENAME?>> This little class hierarchy has some naming/conceptual
+//problems. A root node is conceptually a kind of node, yet a Root is not a Node.
+//NodeBase begs to be called BaseNode, but not all BaseNodes would be Nodes
+//(for example, Root). [Jon Aquino]
+
+//DEBUG private static int itemCount = 0;  // debugging
+  /**
+   * Returns the index of the subquad that wholly contains the given envelope.
+   * If none does, returns -1.
+   */
+  public static int getSubnodeIndex(Envelope env, Coordinate centre)
+  {
+    int subnodeIndex = -1;
+    if (env.getMinX() >= centre.x) {
+      if (env.getMinY() >= centre.y) subnodeIndex = 3;
+      if (env.getMaxY() <= centre.y) subnodeIndex = 1;
+    }
+    if (env.getMaxX() <= centre.x) {
+      if (env.getMinY() >= centre.y) subnodeIndex = 2;
+      if (env.getMaxY() <= centre.y) subnodeIndex = 0;
+    }
+    return subnodeIndex;
+  }
+
+  protected List items = new ArrayList();
+
+  /**
+   * subquads are numbered as follows:
+   * <pre>
+   *  2 | 3
+   *  --+--
+   *  0 | 1
+   * </pre>
+   */
+  protected Node[] subnode = new Node[4];
+
+  public NodeBase() {
+  }
+
+  public List getItems() { return items; }
+
+  public boolean hasItems() { return ! items.isEmpty(); }
+
+  public void add(Object item)
+  {
+    items.add(item);
+//DEBUG itemCount++;
+//DEBUG System.out.print(itemCount);
+  }
+
+  /**
+   * Removes a single item from this subtree.
+   *
+   * @param searchEnv the envelope containing the item
+   * @param item the item to remove
+   * @return <code>true</code> if the item was found and removed
+   */
+  public boolean remove(Envelope itemEnv, Object item)
+  {
+    // use envelope to restrict nodes scanned
+    if (! isSearchMatch(itemEnv))
+      return false;
+
+    boolean found = false;
+    for (int i = 0; i < 4; i++) {
+      if (subnode[i] != null) {
+        found = subnode[i].remove(itemEnv, item);
+        if (found) {
+          // trim subtree if empty
+          if (subnode[i].isPrunable())
+            subnode[i] = null;
+          break;
+        }
+      }
+    }
+    // if item was found lower down, don't need to search for it here
+    if (found) return found;
+    // otherwise, try and remove the item from the list of items in this node
+    found = items.remove(item);
+    return found;
+  }
+
+  public boolean isPrunable()
+  {
+    return ! (hasChildren() || hasItems());
+  }
+
+  public boolean hasChildren()
+  {
+    for (int i = 0; i < 4; i++) {
+      if (subnode[i] != null)
+        return true;
+    }
+    return false;
+  }
+
+  public boolean isEmpty()
+  {
+    boolean isEmpty = true;
+    if (! items.isEmpty()) isEmpty = false;
+    for (int i = 0; i < 4; i++) {
+      if (subnode[i] != null) {
+        if (! subnode[i].isEmpty() )
+          isEmpty = false;
+      }
+    }
+    return isEmpty;
+  }
+
+  //<<TODO:RENAME?>> Sounds like this method adds resultItems to items
+  //(like List#addAll). Perhaps it should be renamed to "addAllItemsTo" [Jon Aquino]
+  public List addAllItems(List resultItems)
+  {
+    // this node may have items as well as subnodes (since items may not
+    // be wholely contained in any single subnode
+    resultItems.addAll(this.items);
+    for (int i = 0; i < 4; i++) {
+      if (subnode[i] != null) {
+        subnode[i].addAllItems(resultItems);
+      }
+    }
+    return resultItems;
+  }
+  protected abstract boolean isSearchMatch(Envelope searchEnv);
+
+  public void addAllItemsFromOverlapping(Envelope searchEnv, List resultItems)
+  {
+    if (! isSearchMatch(searchEnv))
+      return;
+
+    // this node may have items as well as subnodes (since items may not
+    // be wholely contained in any single subnode
+    resultItems.addAll(items);
+
+    for (int i = 0; i < 4; i++) {
+      if (subnode[i] != null) {
+        subnode[i].addAllItemsFromOverlapping(searchEnv, resultItems);
+      }
+    }
+  }
+
+  public void visit(Envelope searchEnv, ItemVisitor visitor)
+  {
+    if (! isSearchMatch(searchEnv))
+      return;
+
+    // this node may have items as well as subnodes (since items may not
+    // be wholely contained in any single subnode
+    visitItems(searchEnv, visitor);
+
+    for (int i = 0; i < 4; i++) {
+      if (subnode[i] != null) {
+        subnode[i].visit(searchEnv, visitor);
+      }
+    }
+  }
+
+  private void visitItems(Envelope searchEnv, ItemVisitor visitor)
+  {
+    // would be nice to filter items based on search envelope, but can't until they contain an envelope
+    for (Iterator i = items.iterator(); i.hasNext(); ) {
+      visitor.visitItem(i.next());
+    }
+  }
+
+//<<TODO:RENAME?>> In Samet's terminology, I think what we're returning here is
+//actually level+1 rather than depth. (See p. 4 of his book) [Jon Aquino]
+  int depth()
+  {
+    int maxSubDepth = 0;
+    for (int i = 0; i < 4; i++) {
+      if (subnode[i] != null) {
+        int sqd = subnode[i].depth();
+        if (sqd > maxSubDepth)
+          maxSubDepth = sqd;
+      }
+    }
+    return maxSubDepth + 1;
+  }
+
+  int size()
+  {
+    int subSize = 0;
+    for (int i = 0; i < 4; i++) {
+      if (subnode[i] != null) {
+        subSize += subnode[i].size();
+      }
+    }
+    return subSize + items.size();
+  }
+
+  int getNodeCount()
+  {
+    int subSize = 0;
+    for (int i = 0; i < 4; i++) {
+      if (subnode[i] != null) {
+        subSize += subnode[i].size();
+      }
+    }
+    return subSize + 1;
+  }
+
+}

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/quadtree/Quadtree.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,214 @@
+
+/*
+ * 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.index.quadtree;
+
+import java.util.ArrayList;
+import java.util.List;
+import com.vividsolutions.jts.geom.Envelope;
+import com.vividsolutions.jts.index.*;
+/**
+ * A Quadtree is a spatial index structure for efficient querying
+ * of 2D rectangles.  If other kinds of spatial objects
+ * need to be indexed they can be represented by their
+ * envelopes
+ * <p>
+ * The quadtree structure is used to provide a primary filter
+ * for range rectangle queries.  The query() method returns a list of
+ * all objects which <i>may</i> intersect the query rectangle.  Note that
+ * it may return objects which do not in fact intersect.
+ * 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.
+ *
+ * <p>
+ * This implementation does not require specifying the extent of the inserted
+ * items beforehand.  It will automatically expand to accomodate any extent
+ * of dataset.
+ * <p>
+ * This data structure is also known as an <i>MX-CIF quadtree</i>
+ * following the usage of Samet and others.
+ *
+ * @version 1.6
+ */
+public class Quadtree
+    implements SpatialIndex
+{
+  /**
+   * Ensure that the envelope for the inserted item has non-zero extents.
+   * Use the current minExtent to pad the envelope, if necessary
+   */
+  public static Envelope ensureExtent(Envelope itemEnv, double minExtent)
+  {
+    //The names "ensureExtent" and "minExtent" are misleading -- sounds like
+    //this method ensures that the extents are greater than minExtent.
+    //Perhaps we should rename them to "ensurePositiveExtent" and "defaultExtent".
+    //[Jon Aquino]
+    double minx = itemEnv.getMinX();
+    double maxx = itemEnv.getMaxX();
+    double miny = itemEnv.getMinY();
+    double maxy = itemEnv.getMaxY();
+    // has a non-zero extent
+    if (minx != maxx && miny != maxy) return itemEnv;
+
+    // pad one or both extents
+    if (minx == maxx) {
+      minx = minx - minExtent / 2.0;
+      maxx = minx + minExtent / 2.0;
+    }
+    if (miny == maxy) {
+      miny = miny - minExtent / 2.0;
+      maxy = miny + minExtent / 2.0;
+    }
+    return new Envelope(minx, maxx, miny, maxy);
+  }
+
+  private Root root;
+  /**
+
+  * minExtent is the minimum envelope extent of all items
+  * inserted into the tree so far. It is used as a heuristic value
+  * to construct non-zero envelopes for features with zero X and/or Y extent.
+  * Start with a non-zero extent, in case the first feature inserted has
+  * a zero extent in both directions.  This value may be non-optimal, but
+  * only one feature will be inserted with this value.
+  **/
+  private double minExtent = 1.0;
+
+  /**
+   * Constructs a Quadtree with zero items.
+   */
+  public Quadtree()
+  {
+    root = new Root();
+  }
+
+  /**
+   * Returns the number of levels in the tree.
+   */
+  public int depth()
+  {
+    //I don't think it's possible for root to be null. Perhaps we should
+    //remove the check. [Jon Aquino]
+    //Or make an assertion [Jon Aquino 10/29/2003]
+    if (root != null) return root.depth();
+    return 0;
+  }
+
+  /**
+   * Returns the number of items in the tree.
+   *
+   * @return the number of items in the tree
+   */
+  public int size()
+  {
+    if (root != null) return root.size();
+    return 0;
+  }
+
+  public void insert(Envelope itemEnv, Object item)
+  {
+    collectStats(itemEnv);
+    Envelope insertEnv = ensureExtent(itemEnv, minExtent);
+    root.insert(insertEnv, item);
+  }
+
+  /**
+   * Removes a single item from the tree.
+   *
+   * @param itemEnv the Envelope of the item to remove
+   * @param item the item to remove
+   * @return <code>true</code> if the item was found
+   */
+  public boolean remove(Envelope itemEnv, Object item)
+  {
+    Envelope posEnv = ensureExtent(itemEnv, minExtent);
+    return root.remove(posEnv, item);
+  }
+
+/*
+  public List OLDquery(Envelope searchEnv)
+  {
+    /**
+     * the items that are matched are the items in quads which
+     * overlap the search envelope
+     */
+    /*
+    List foundItems = new ArrayList();
+    root.addAllItemsFromOverlapping(searchEnv, foundItems);
+    return foundItems;
+  }
+*/
+
+  public List query(Envelope searchEnv)
+  {
+    /**
+     * the items that are matched are the items in quads which
+     * overlap the search envelope
+     */
+    ArrayListVisitor visitor = new ArrayListVisitor();
+    query(searchEnv, visitor);
+    return visitor.getItems();
+  }
+
+  public void query(Envelope searchEnv, ItemVisitor visitor)
+  {
+    /**
+     * the items that are matched are the items in quads which
+     * overlap the search envelope
+     */
+    root.visit(searchEnv, visitor);
+  }
+
+  /**
+   * Return a list of all items in the Quadtree
+   */
+  public List queryAll()
+  {
+    List foundItems = new ArrayList();
+    root.addAllItems(foundItems);
+    return foundItems;
+  }
+
+  private void collectStats(Envelope itemEnv)
+  {
+    double delX = itemEnv.getWidth();
+    if (delX < minExtent && delX > 0.0)
+      minExtent = delX;
+
+    double delY = itemEnv.getWidth();
+    if (delY < minExtent && delY > 0.0)
+      minExtent = delY;
+  }
+
+}

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/quadtree/Root.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,120 @@
+
+/*
+ * 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.index.quadtree;
+
+import com.vividsolutions.jts.geom.Coordinate;
+import com.vividsolutions.jts.geom.Envelope;
+import com.vividsolutions.jts.util.Assert;
+
+/**
+ * QuadRoot is the root of a single Quadtree.  It is centred at the origin,
+ * and does not have a defined extent.
+ *
+ * @version 1.6
+ */
+public class Root
+  extends NodeBase
+{
+
+  // the singleton root quad is centred at the origin.
+  private static final Coordinate origin = new Coordinate(0.0, 0.0);
+
+  public Root()
+  {
+  }
+
+  /**
+   * Insert an item into the quadtree this is the root of.
+   */
+  public void insert(Envelope itemEnv, Object item)
+  {
+    int index = getSubnodeIndex(itemEnv, origin);
+    // if index is -1, itemEnv must cross the X or Y axis.
+    if (index == -1) {
+      add(item);
+      return;
+    }
+    /**
+     * the item must be contained in one quadrant, so insert it into the
+     * tree for that quadrant (which may not yet exist)
+     */
+    Node node = subnode[index];
+    /**
+     *  If the subquad doesn't exist or this item is not contained in it,
+     *  have to expand the tree upward to contain the item.
+     */
+
+    if (node == null || ! node.getEnvelope().contains(itemEnv)) {
+       Node largerNode = Node.createExpanded(node, itemEnv);
+       subnode[index] = largerNode;
+    }
+    /**
+     * At this point we have a subquad which exists and must contain
+     * contains the env for the item.  Insert the item into the tree.
+     */
+    insertContained(subnode[index], itemEnv, item);
+    //System.out.println("depth = " + root.depth() + " size = " + root.size());
+    //System.out.println(" size = " + size());
+  }
+
+  /**
+   * insert an item which is known to be contained in the tree rooted at
+   * the given QuadNode root.  Lower levels of the tree will be created
+   * if necessary to hold the item.
+   */
+  private void insertContained(Node tree, Envelope itemEnv, Object item)
+  {
+    Assert.isTrue(tree.getEnvelope().contains(itemEnv));
+   /**
+    * Do NOT create a new quad for zero-area envelopes - this would lead
+    * to infinite recursion. Instead, use a heuristic of simply returning
+    * the smallest existing quad containing the query
+    */
+    boolean isZeroX = IntervalSize.isZeroWidth(itemEnv.getMinX(), itemEnv.getMaxX());
+    boolean isZeroY = IntervalSize.isZeroWidth(itemEnv.getMinX(), itemEnv.getMaxX());
+    NodeBase node;
+    if (isZeroX || isZeroY)
+      node = tree.find(itemEnv);
+    else
+      node = tree.getNode(itemEnv);
+    node.add(item);
+  }
+
+  protected boolean isSearchMatch(Envelope searchEnv)
+  {
+    return true;
+  }
+
+
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/quadtree/package.html
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/quadtree/package.html	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/quadtree/package.html	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,13 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+<!--
+
+-->
+</head>
+<body bgcolor="white">
+
+Contains classes that implement a Quadtree spatial index
+
+</body>
+</html>

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/strtree/AbstractNode.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,103 @@
+
+/*
+ * 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.index.strtree;
+import com.vividsolutions.jts.util.*;
+
+import java.util.*;
+
+/**
+ * A node of the STR tree. The children of this node are either more nodes
+ * (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
+ */
+public abstract class AbstractNode implements Boundable {
+  private ArrayList childBoundables = new ArrayList();
+  private Object bounds = null;
+  private int level;
+
+  /**
+   * Constructs an AbstractNode at the given level in the tree
+   * @param level 0 if this node is a leaf, 1 if a parent of a leaf, and so on; the
+   * root node will have the highest level
+   */
+  public AbstractNode(int level) {
+    this.level = level;
+  }
+
+  /**
+   * Returns either child AbstractNodes, or if this is a leaf node, real data (wrapped
+   * in ItemBoundables).
+   */
+  public List getChildBoundables() {
+    return childBoundables;
+  }
+
+  /**
+   * Returns a representation of space that encloses this Boundable,
+   * preferably not much bigger than this Boundable's boundary yet fast to
+   * test for intersection with the bounds of other Boundables. The class of
+   * object returned depends on the subclass of AbstractSTRtree.
+   *
+   * @return an Envelope (for STRtrees), an Interval (for SIRtrees), or other
+   *         object (for other subclasses of AbstractSTRtree)
+   * @see AbstractSTRtree.IntersectsOp
+   */
+  protected abstract Object computeBounds();
+
+  public Object getBounds() {
+    if (bounds == null) {
+      bounds = computeBounds();
+    }
+    return bounds;
+  }
+
+  /**
+   * Returns 0 if this node is a leaf, 1 if a parent of a leaf, and so on; the
+   * root node will have the highest level
+   */
+  public int getLevel() {
+    return level;
+  }
+
+  /**
+   * Adds either an AbstractNode, or if this is a leaf node, a data object
+   * (wrapped in an ItemBoundable)
+   */
+  public void addChildBoundable(Boundable childBoundable) {
+    Assert.isTrue(bounds == null);
+    childBoundables.add(childBoundable);
+  }
+}

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/strtree/AbstractSTRtree.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,341 @@
+
+/*
+ * 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.index.strtree;
+import com.vividsolutions.jts.util.*;
+import java.util.*;
+import java.util.List;
+
+/**
+ * Base class for STRtree and SIRtree. STR-packed R-trees are described in:
+ * P. Rigaux, Michel Scholl and Agnes Voisard. Spatial Databases With
+ * Application To GIS. Morgan Kaufmann, San Francisco, 2002.
+ * <p>
+ * This implementation is based on Boundables rather than just AbstractNodes,
+ * because the STR algorithm operates on both nodes and
+ * data, both of which are treated here as Boundables.
+ *
+ * @see STRtree
+ * @see SIRtree
+ *
+ * @version 1.6
+ */
+public abstract class AbstractSTRtree {
+
+  /**
+   * A test for intersection between two bounds, necessary because subclasses
+   * of AbstractSTRtree have different implementations of bounds.
+   */
+  protected static interface IntersectsOp {
+    /**
+     * For STRtrees, the bounds will be Envelopes; for SIRtrees, Intervals;
+     * for other subclasses of AbstractSTRtree, some other class.
+     * @param aBounds the bounds of one spatial object
+     * @param bBounds the bounds of another spatial object
+     * @return whether the two bounds intersect
+     */
+    public boolean intersects(Object aBounds, Object bBounds);
+  }
+
+  protected AbstractNode root;
+
+  private boolean built = false;
+  private ArrayList itemBoundables = new ArrayList();
+  private int nodeCapacity;
+
+  /**
+   * Constructs an AbstractSTRtree with the specified maximum number of child
+   * nodes that a node may have
+   */
+  public AbstractSTRtree(int nodeCapacity) {
+    Assert.isTrue(nodeCapacity > 1, "Node capacity must be greater than 1");
+    this.nodeCapacity = nodeCapacity;
+  }
+
+  /**
+   * Creates parent nodes, grandparent nodes, and so forth up to the root
+   * node, for the data that has been inserted into the tree. Can only be
+   * called once, and thus can be called only after all of the data has been
+   * inserted into the tree.
+   */
+  public void build() {
+    Assert.isTrue(!built);
+    root = itemBoundables.isEmpty()
+           ?createNode(0)
+           :createHigherLevels(itemBoundables, -1);
+    built = true;
+  }
+
+  protected abstract AbstractNode createNode(int level);
+
+  /**
+   * Sorts the childBoundables then divides them into groups of size M, where
+   * M is the node capacity.
+   */
+  protected List createParentBoundables(List childBoundables, int newLevel) {
+    Assert.isTrue(!childBoundables.isEmpty());
+    ArrayList parentBoundables = new ArrayList();
+    parentBoundables.add(createNode(newLevel));
+    ArrayList sortedChildBoundables = new ArrayList(childBoundables);
+    Collections.sort(sortedChildBoundables, getComparator());
+    for (Iterator i = sortedChildBoundables.iterator(); i.hasNext(); ) {
+      Boundable childBoundable = (Boundable) i.next();
+      if (lastNode(parentBoundables).getChildBoundables().size() == getNodeCapacity()) {
+        parentBoundables.add(createNode(newLevel));
+      }
+      lastNode(parentBoundables).addChildBoundable(childBoundable);
+    }
+    return parentBoundables;
+  }
+
+  protected AbstractNode lastNode(List nodes) {
+    return (AbstractNode) nodes.get(nodes.size() - 1);
+  }
+
+  protected int compareDoubles(double a, double b) {
+    return a > b ? 1
+         : a < b ? -1
+         : 0;
+  }
+
+  /**
+   * Creates the levels higher than the given level
+   *
+   * @param boundablesOfALevel
+   *            the level to build on
+   * @param level
+   *            the level of the Boundables, or -1 if the boundables are item
+   *            boundables (that is, below level 0)
+   * @return the root, which may be a ParentNode or a LeafNode
+   */
+  private AbstractNode createHigherLevels(List boundablesOfALevel, int level) {
+    Assert.isTrue(!boundablesOfALevel.isEmpty());
+    List parentBoundables = createParentBoundables(boundablesOfALevel, level + 1);
+    if (parentBoundables.size() == 1) {
+      return (AbstractNode) parentBoundables.get(0);
+    }
+    return createHigherLevels(parentBoundables, level + 1);
+  }
+
+  protected AbstractNode getRoot() { return root; }
+
+  /**
+   * Returns the maximum number of child nodes that a node may have
+   */
+  public int getNodeCapacity() { return nodeCapacity; }
+
+  protected int size() {
+    if (!built) { build(); }
+    if (itemBoundables.isEmpty()) {
+      return 0;
+    }
+    return size(root);
+  }
+
+  protected int size(AbstractNode node)
+  {
+    int size = 0;
+    for (Iterator i = node.getChildBoundables().iterator(); i.hasNext(); ) {
+      Boundable childBoundable = (Boundable) i.next();
+      if (childBoundable instanceof AbstractNode) {
+        size += size((AbstractNode) childBoundable);
+      }
+      else if (childBoundable instanceof ItemBoundable) {
+        size += 1;
+      }
+    }
+    return size;
+  }
+
+  protected int depth() {
+    if (!built) { build(); }
+    if (itemBoundables.isEmpty()) {
+      return 0;
+    }
+    return depth(root);
+  }
+
+  protected int depth(AbstractNode node)
+  {
+    int maxChildDepth = 0;
+    for (Iterator i = node.getChildBoundables().iterator(); i.hasNext(); ) {
+      Boundable childBoundable = (Boundable) i.next();
+      if (childBoundable instanceof AbstractNode) {
+        int childDepth = depth((AbstractNode) childBoundable);
+        if (childDepth > maxChildDepth)
+          maxChildDepth = childDepth;
+      }
+    }
+    return maxChildDepth + 1;
+  }
+
+
+  protected void insert(Object bounds, Object item) {
+    Assert.isTrue(!built, "Cannot insert items into an STR packed R-tree after it has been built.");
+    itemBoundables.add(new ItemBoundable(bounds, item));
+  }
+
+  /**
+   *  Also builds the tree, if necessary.
+   */
+  protected List query(Object searchBounds) {
+    if (!built) { build(); }
+    ArrayList matches = new ArrayList();
+    if (itemBoundables.isEmpty()) {
+      Assert.isTrue(root.getBounds() == null);
+      return matches;
+    }
+    if (getIntersectsOp().intersects(root.getBounds(), searchBounds)) {
+      query(searchBounds, root, matches);
+    }
+    return matches;
+  }
+
+  /**
+   * @return a test for intersection between two bounds, necessary because subclasses
+   * of AbstractSTRtree have different implementations of bounds.
+   * @see IntersectsOp
+   */
+  protected abstract IntersectsOp getIntersectsOp();
+
+  private void query(Object searchBounds, AbstractNode node, List matches) {
+    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, matches);
+      }
+      else if (childBoundable instanceof ItemBoundable) {
+        matches.add(((ItemBoundable)childBoundable).getItem());
+      }
+      else {
+        Assert.shouldNeverReachHere();
+      }
+    }
+  }
+
+  /**
+   *  Also builds the tree, if necessary.
+   */
+  protected boolean remove(Object searchBounds, Object item) {
+    if (!built) { build(); }
+    if (itemBoundables.isEmpty()) {
+      Assert.isTrue(root.getBounds() == null);
+    }
+    if (getIntersectsOp().intersects(root.getBounds(), searchBounds)) {
+      return remove(searchBounds, root, item);
+    }
+    return false;
+  }
+
+  private boolean removeItem(AbstractNode node, Object item)
+  {
+    Boundable childToRemove = null;
+    for (Iterator i = node.getChildBoundables().iterator(); i.hasNext(); ) {
+      Boundable childBoundable = (Boundable) i.next();
+      if (childBoundable instanceof ItemBoundable) {
+        if ( ((ItemBoundable) childBoundable).getItem() == item)
+          childToRemove = childBoundable;
+      }
+    }
+    if (childToRemove != null) {
+      node.getChildBoundables().remove(childToRemove);
+      return true;
+    }
+    return false;
+  }
+
+  private boolean remove(Object searchBounds, AbstractNode node, Object item) {
+    // first try removing item from this node
+    boolean found = removeItem(node, item);
+    if (found)
+      return true;
+
+    AbstractNode childToPrune = null;
+    // next try removing item from lower nodes
+    for (Iterator i = node.getChildBoundables().iterator(); i.hasNext(); ) {
+      Boundable childBoundable = (Boundable) i.next();
+      if (!getIntersectsOp().intersects(childBoundable.getBounds(), searchBounds)) {
+        continue;
+      }
+      if (childBoundable instanceof AbstractNode) {
+        found = remove(searchBounds, (AbstractNode) childBoundable, item);
+        // if found, record child for pruning and exit
+        if (found) {
+          childToPrune = (AbstractNode) childBoundable;
+          break;
+        }
+      }
+    }
+    // prune child if possible
+    if (childToPrune != null) {
+      if (childToPrune.getChildBoundables().isEmpty()) {
+        node.getChildBoundables().remove(childToPrune);
+      }
+    }
+    return found;
+  }
+
+  protected List boundablesAtLevel(int level) {
+    ArrayList boundables = new ArrayList();
+    boundablesAtLevel(level, root, boundables);
+    return boundables;
+  }
+
+  /**
+   * @param level -1 to get items
+   */
+  private void boundablesAtLevel(int level, AbstractNode top, Collection boundables) {
+    Assert.isTrue(level > -2);
+    if (top.getLevel() == level) {
+      boundables.add(top);
+      return;
+    }
+    for (Iterator i = top.getChildBoundables().iterator(); i.hasNext(); ) {
+      Boundable boundable = (Boundable) i.next();
+      if (boundable instanceof AbstractNode) {
+        boundablesAtLevel(level, (AbstractNode)boundable, boundables);
+      }
+      else {
+        Assert.isTrue(boundable instanceof ItemBoundable);
+        if (level == -1) { boundables.add(boundable); }
+      }
+    }
+    return;
+  }
+
+  protected abstract Comparator getComparator();
+
+}

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/strtree/Boundable.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,52 @@
+
+/*
+ * 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.index.strtree;
+
+/**
+ * A spatial object in an AbstractSTRtree.
+ *
+ * @version 1.6
+ */
+public interface Boundable {
+  /**
+   * Returns a representation of space that encloses this Boundable, preferably
+   * not much bigger than this Boundable's boundary yet fast to test for intersection
+   * with the bounds of other Boundables. The class of object returned depends
+   * on the subclass of AbstractSTRtree.
+   * @return an Envelope (for STRtrees), an Interval (for SIRtrees), or other object
+   * (for other subclasses of AbstractSTRtree)
+   * @see AbstractSTRtree.IntersectsOp
+   */
+  public Object getBounds();
+}

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/strtree/Interval.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,78 @@
+
+/*
+ * 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.index.strtree;
+
+import com.vividsolutions.jts.util.*;
+
+/**
+ * A contiguous portion of 1D-space. Used internally by SIRtree.
+ * @see SIRtree
+ *
+ * @version 1.6
+ */
+public class Interval {
+
+  public Interval(Interval other) {
+    this(other.min, other.max);
+  }
+
+  public Interval(double min, double max) {
+    Assert.isTrue(min <= max);
+    this.min = min;
+    this.max = max;
+  }
+
+  private double min;
+  private double max;
+
+  public double getCentre() { return (min+max)/2; }
+
+  /**
+   * @return this
+   */
+  public Interval expandToInclude(Interval other) {
+    max = Math.max(max, other.max);
+    min = Math.min(min, other.min);
+    return this;
+  }
+
+  public boolean intersects(Interval other) {
+    return !(other.min > max || other.max < min);
+  }
+  public boolean equals(Object o) {
+    if (! (o instanceof Interval)) { return false; }
+    Interval other = (Interval) o;
+    return min == other.min && max == other.max;
+  }
+}

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/strtree/ItemBoundable.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,56 @@
+
+/*
+ * 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.index.strtree;
+
+/**
+ * Boundable wrapper for a non-Boundable spatial object. Used internally by
+ * AbstractSTRtree.
+ *
+ * @version 1.6
+ */
+public class ItemBoundable implements Boundable {
+  private Object bounds;
+  private Object item;
+
+  public ItemBoundable(Object bounds, Object item) {
+    this.bounds = bounds;
+    this.item = item;
+  }
+
+  public Object getBounds() {
+    return bounds;
+  }
+
+  public Object getItem() { return item; }
+}

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/strtree/SIRtree.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,126 @@
+
+/*
+ * 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.index.strtree;
+
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * One-dimensional version of an STR-packed R-tree. SIR stands for
+ * "Sort-Interval-Recursive". STR-packed R-trees are described in:
+ * P. Rigaux, Michel Scholl and Agnes Voisard. Spatial Databases With
+ * Application To GIS. Morgan Kaufmann, San Francisco, 2002.
+ * @see STRtree
+ *
+ * @version 1.6
+ */
+public class SIRtree extends AbstractSTRtree {
+
+  private Comparator comparator = new Comparator() {
+    public int compare(Object o1, Object o2) {
+      return compareDoubles(
+          ((Interval)((Boundable)o1).getBounds()).getCentre(),
+          ((Interval)((Boundable)o2).getBounds()).getCentre());
+    }
+  };
+
+  private IntersectsOp intersectsOp = new IntersectsOp() {
+    public boolean intersects(Object aBounds, Object bBounds) {
+      return ((Interval)aBounds).intersects((Interval)bBounds);
+    }
+  };
+  
+  /**
+   * Constructs an SIRtree with the default node capacity.
+   */
+  public SIRtree() { this(10); }
+   
+  /**
+   * Constructs an SIRtree with the given maximum number of child nodes that
+   * a node may have
+   */
+  public SIRtree(int nodeCapacity) {
+    super(nodeCapacity);
+  }
+
+  protected AbstractNode createNode(int level) {
+    return new AbstractNode(level) {
+      protected Object computeBounds() {
+        Interval bounds = null;
+        for (Iterator i = getChildBoundables().iterator(); i.hasNext(); ) {
+          Boundable childBoundable = (Boundable) i.next();
+          if (bounds == null) {
+            bounds = new Interval((Interval)childBoundable.getBounds());
+          }
+          else {
+            bounds.expandToInclude((Interval)childBoundable.getBounds());
+          }
+        }
+        return bounds;
+      }
+    };
+  }
+
+  /**
+   * Inserts an item having the given bounds into the tree.
+   */
+  public void insert(double x1, double x2, Object item) {
+    super.insert(new Interval(Math.min(x1, x2), Math.max(x1, x2)), item);
+  }
+
+  /**
+   * Returns items whose bounds intersect the given value.
+   */
+  public List query(double x) {
+    return query(x, x);
+  }
+
+  /**
+   * Returns items whose bounds intersect the given bounds.
+   * @param x1 possibly equal to x2
+   */
+  public List query(double x1, double x2) {
+    return super.query(new Interval(Math.min(x1, x2), Math.max(x1, x2)));
+  }
+
+  protected IntersectsOp getIntersectsOp() {
+    return intersectsOp;
+  }
+
+  protected Comparator getComparator() {
+    return comparator;
+  }
+
+}

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/strtree/STRtree.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,229 @@
+
+/*
+ * 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.index.strtree;
+
+import com.vividsolutions.jts.index.strtree.AbstractSTRtree;
+import java.util.*;
+import com.vividsolutions.jts.geom.*;
+import com.vividsolutions.jts.util.*;
+import com.vividsolutions.jts.index.*;
+
+/**
+ *  A query-only R-tree created using the Sort-Tile-Recursive (STR) algorithm.
+ *  For two-dimensional spatial data. <P>
+ *
+ *  The STR packed R-tree is simple to implement and maximizes space
+ *  utilization; that is, as many leaves as possible are filled to capacity.
+ *  Overlap between nodes is far less than in a basic R-tree. However, once the
+ *  tree has been built (explicitly or on the first call to #query), items may
+ *  not be added or removed. <P>
+ *
+ * Described in: P. Rigaux, Michel Scholl and Agnes Voisard. Spatial Databases With
+ *  Application To GIS. Morgan Kaufmann, San Francisco, 2002.
+ *
+ * @version 1.6
+ */
+public class STRtree extends AbstractSTRtree implements SpatialIndex {
+
+  private Comparator xComparator =
+    new Comparator() {
+      public int compare(Object o1, Object o2) {
+        return compareDoubles(
+            centreX((Envelope)((Boundable)o1).getBounds()),
+            centreX((Envelope)((Boundable)o2).getBounds()));
+      }
+    };
+  private Comparator yComparator =
+    new Comparator() {
+      public int compare(Object o1, Object o2) {
+        return compareDoubles(
+            centreY((Envelope)((Boundable)o1).getBounds()),
+            centreY((Envelope)((Boundable)o2).getBounds()));
+      }
+    };
+
+  private double centreX(Envelope e) {
+    return avg(e.getMinX(), e.getMaxX());
+  }
+
+  private double avg(double a, double b) { return (a + b) / 2d; }
+
+  private double centreY(Envelope e) {
+    return avg(e.getMinY(), e.getMaxY());
+  }
+
+  private IntersectsOp intersectsOp = new IntersectsOp() {
+    public boolean intersects(Object aBounds, Object bBounds) {
+      return ((Envelope)aBounds).intersects((Envelope)bBounds);
+    }
+  };
+
+  /**
+   * Creates the parent level for the given child level. First, orders the items
+   * by the x-values of the midpoints, and groups them into vertical slices.
+   * For each slice, orders the items by the y-values of the midpoints, and
+   * group them into runs of size M (the node capacity). For each run, creates
+   * a new (parent) node.
+   */
+  protected List createParentBoundables(List childBoundables, int newLevel) {
+    Assert.isTrue(!childBoundables.isEmpty());
+    int minLeafCount = (int) Math.ceil((childBoundables.size() / (double) getNodeCapacity()));
+    ArrayList sortedChildBoundables = new ArrayList(childBoundables);
+    Collections.sort(sortedChildBoundables, xComparator);
+    List[] verticalSlices = verticalSlices(sortedChildBoundables,
+        (int) Math.ceil(Math.sqrt(minLeafCount)));
+    return createParentBoundablesFromVerticalSlices(verticalSlices, newLevel);
+  }
+
+  private List createParentBoundablesFromVerticalSlices(List[] verticalSlices, int newLevel) {
+    Assert.isTrue(verticalSlices.length > 0);
+    List parentBoundables = new ArrayList();
+    for (int i = 0; i < verticalSlices.length; i++) {
+      parentBoundables.addAll(
+            createParentBoundablesFromVerticalSlice(verticalSlices[i], newLevel));
+    }
+    return parentBoundables;
+  }
+
+  protected List createParentBoundablesFromVerticalSlice(List childBoundables, int newLevel) {
+    return super.createParentBoundables(childBoundables, newLevel);
+  }
+
+  /**
+   * @param childBoundables Must be sorted by the x-value of the envelope midpoints
+   */
+  protected List[] verticalSlices(List childBoundables, int sliceCount) {
+    int sliceCapacity = (int) Math.ceil(childBoundables.size() / (double) sliceCount);
+    List[] slices = new List[sliceCount];
+    Iterator i = childBoundables.iterator();
+    for (int j = 0; j < sliceCount; j++) {
+      slices[j] = new ArrayList();
+      int boundablesAddedToSlice = 0;
+      while (i.hasNext() && boundablesAddedToSlice < sliceCapacity) {
+        Boundable childBoundable = (Boundable) i.next();
+        slices[j].add(childBoundable);
+        boundablesAddedToSlice++;
+      }
+    }
+    return slices;
+  }
+
+  /**
+   * Constructs an STRtree with the default node capacity.
+   */
+  public STRtree() { this(10); }
+
+  /**
+   * Constructs an STRtree with the given maximum number of child nodes that
+   * a node may have
+   */
+  public STRtree(int nodeCapacity) {
+    super(nodeCapacity);
+  }
+
+  protected AbstractNode createNode(int level) {
+    return new AbstractNode(level) {
+      protected Object computeBounds() {
+        Envelope bounds = null;
+        for (Iterator i = getChildBoundables().iterator(); i.hasNext(); ) {
+          Boundable childBoundable = (Boundable) i.next();
+          if (bounds == null) {
+            bounds = new Envelope((Envelope)childBoundable.getBounds());
+          }
+          else {
+            bounds.expandToInclude((Envelope)childBoundable.getBounds());
+          }
+        }
+        return bounds;
+      }
+    };
+  }
+
+  protected IntersectsOp getIntersectsOp() {
+    return intersectsOp;
+  }
+
+  /**
+   * Inserts an item having the given bounds into the tree.
+   */
+  public void insert(Envelope itemEnv, Object item) {
+    if (itemEnv.isNull()) { return; }
+    super.insert(itemEnv, item);
+  }
+
+  /**
+   * Returns items whose bounds intersect the given envelope.
+   */
+  public List query(Envelope searchEnv) {
+    //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]
+    return super.query(searchEnv);
+  }
+
+  /**
+   * Removes a single item from the tree.
+   *
+   * @param itemEnv the Envelope of the item to remove
+   * @param item the item to remove
+   * @return <code>true</code> if the item was found
+   */
+  public boolean remove(Envelope itemEnv, Object item) {
+    return super.remove(itemEnv, item);
+  }
+
+  /**
+   * Returns the number of items in the tree.
+   *
+   * @return the number of items in the tree
+   */
+  public int size()
+  {
+    return super.size();
+  }
+
+  /**
+   * Returns the number of items in the tree.
+   *
+   * @return the number of items in the tree
+   */
+  public int depth()
+  {
+    return super.depth();
+  }
+
+  protected Comparator getComparator() {
+    return yComparator;
+  }
+
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/strtree/package.html
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/strtree/package.html	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/strtree/package.html	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,8 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+</head>
+<body bgcolor="white">
+Contains 2-D and 1-D versions of the Sort-Tile-Recursive (STR) tree, a query-only R-tree.
+</body>
+</html>

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/sweepline/SweepLineEvent.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -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.index.sweepline;
+
+/**
+ * @version 1.6
+ */
+public class SweepLineEvent
+  implements Comparable
+{
+  public static final int INSERT = 1;
+  public static final int DELETE = 2;
+
+  private double xValue;
+  private int eventType;
+  private SweepLineEvent insertEvent; // null if this is an INSERT event
+  private int deleteEventIndex;
+
+  SweepLineInterval sweepInt;
+  public SweepLineEvent(double x, SweepLineEvent insertEvent, SweepLineInterval sweepInt)
+  {
+    xValue = x;
+    this.insertEvent = insertEvent;
+    this.eventType = INSERT;
+    if (insertEvent != null)
+      eventType = DELETE;
+    this.sweepInt = sweepInt;
+  }
+
+  public boolean isInsert() { return insertEvent == null; }
+  public boolean isDelete() { return insertEvent != null; }
+  public SweepLineEvent getInsertEvent() { return insertEvent; }
+  public int getDeleteEventIndex() { return deleteEventIndex; }
+  public void setDeleteEventIndex(int deleteEventIndex) { this.deleteEventIndex = deleteEventIndex; }
+
+  SweepLineInterval getInterval() { return sweepInt; }
+
+  /**
+   * ProjectionEvents are ordered first by their x-value, and then by their eventType.
+   * It is important that Insert events are sorted before Delete events, so that
+   * items whose Insert and Delete events occur at the same x-value will be
+   * correctly handled.
+   */
+  public int compareTo(Object o) {
+    SweepLineEvent pe = (SweepLineEvent) o;
+    if (xValue < pe.xValue) return  -1;
+    if (xValue > pe.xValue) return   1;
+    if (eventType < pe.eventType) return  -1;
+    if (eventType > pe.eventType) return   1;
+    return 0;
+  }
+
+
+}

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/sweepline/SweepLineIndex.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,114 @@
+
+
+/*
+ * 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.index.sweepline;
+
+import java.util.*;
+
+/**
+ * A sweepline implements a sorted index on a set of intervals.
+ * It is used to compute all overlaps between the interval in the index.
+ *
+ * @version 1.6
+ */
+public class SweepLineIndex {
+
+  List events = new ArrayList();
+  private boolean indexBuilt;
+  // statistics information
+  private int nOverlaps;
+
+  public SweepLineIndex() {
+  }
+
+  public void add(SweepLineInterval sweepInt)
+  {
+    SweepLineEvent insertEvent = new SweepLineEvent(sweepInt.getMin(), null, sweepInt);
+    events.add(insertEvent);
+    events.add(new SweepLineEvent(sweepInt.getMax(), insertEvent, sweepInt));
+  }
+
+  /**
+   * Because Delete Events have a link to their corresponding Insert event,
+   * it is possible to compute exactly the range of events which must be
+   * compared to a given Insert event object.
+   */
+  private void buildIndex()
+  {
+    if (indexBuilt) return;
+    Collections.sort(events);
+    for (int i = 0; i < events.size(); i++ )
+    {
+      SweepLineEvent ev = (SweepLineEvent) events.get(i);
+      if (ev.isDelete()) {
+        ev.getInsertEvent().setDeleteEventIndex(i);
+      }
+    }
+    indexBuilt = true;
+  }
+
+  public void computeOverlaps(SweepLineOverlapAction action)
+  {
+    nOverlaps = 0;
+    buildIndex();
+
+    for (int i = 0; i < events.size(); i++ )
+    {
+      SweepLineEvent ev = (SweepLineEvent) events.get(i);
+      if (ev.isInsert()) {
+        processOverlaps(i, ev.getDeleteEventIndex(), ev.getInterval(), action);
+      }
+    }
+  }
+
+  private void processOverlaps(int start, int end, SweepLineInterval s0, SweepLineOverlapAction action)
+  {
+    /**
+     * Since we might need to test for self-intersections,
+     * include current insert event object in list of event objects to test.
+     * Last index can be skipped, because it must be a Delete event.
+     */
+    for (int i = start; i < end; i++ ) {
+      SweepLineEvent ev = (SweepLineEvent) events.get(i);
+      if (ev.isInsert()) {
+        SweepLineInterval s1 = ev.getInterval();
+        action.overlap(s0, s1);
+        nOverlaps++;
+      }
+    }
+
+  }
+
+
+}

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/sweepline/SweepLineInterval.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,61 @@
+
+
+/*
+ * 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.index.sweepline;
+
+/**
+ * @version 1.6
+ */
+public class SweepLineInterval {
+
+  private double min, max;
+  private Object item;
+
+  public SweepLineInterval(double min, double max)
+  {
+    this(min, max, null);
+  }
+
+  public SweepLineInterval(double min, double max, Object item)
+  {
+    this.min = min < max ? min : max;
+    this.max = max > min ? max : min;
+    this.item = item;
+  }
+
+  public double getMin() { return min;  }
+  public double getMax() { return max;  }
+  public Object getItem() { return item; }
+
+}

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/sweepline/SweepLineOverlapAction.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -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.index.sweepline;
+
+
+
+/**
+ * @version 1.6
+ */
+public interface SweepLineOverlapAction {
+
+  public void overlap(SweepLineInterval s0, SweepLineInterval s1);
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/sweepline/package.html
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/sweepline/package.html	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/index/sweepline/package.html	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,13 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+<!--
+
+-->
+</head>
+<body bgcolor="white">
+
+Contains classes which implement a sweepline algorithm for scanning geometric data structures.
+
+</body>
+</html>

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/io/ParseException.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/io/ParseException.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/io/ParseException.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +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
+ */
+package com.vividsolutions.jts.io;
+
+/**
+ *  Thrown by a <code>WKTReader</code> when a parsing problem occurs.
+ *
+ *@version 1.6
+ */
+public class ParseException extends Exception {
+
+  /**
+   *  Creates a <code>ParseException</code> with the given detail message.
+   *
+   *@param  message  a description of this <code>ParseException</code>
+   */
+  public ParseException(String message) {
+    super(message);
+  }
+
+  /**
+   *  Creates a <code>ParseException</code> with <code>e</code>s detail message.
+   *
+   *@param  e  an exception that occurred while a <code>WKTReader</code> was
+   *      parsing a Well-known Text string
+   */
+  public ParseException(Exception e) {
+    this(e.toString());
+  }
+}
+
+

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/io/WKTReader.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/io/WKTReader.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/io/WKTReader.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,552 @@
+
+
+/*
+ * 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 com.vividsolutions.jts.geom.*;
+import com.vividsolutions.jts.io.ParseException;
+
+import com.vividsolutions.jts.util.Assert;
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StreamTokenizer;
+import java.io.StringReader;
+import java.util.ArrayList;
+
+/**
+ *  Converts a Well-Known Text string to a <code>Geometry</code>.
+ * <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>
+ *  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
+ *  used. <P>
+ *
+ *  The <code>WKTReader</code> will convert the input numbers to the precise
+ *  internal representation.
+ *
+ *  Reads non-standard "LINEARRING" tags.
+ *
+ *@version 1.6
+ */
+public class WKTReader {
+  private GeometryFactory geometryFactory;
+  private PrecisionModel precisionModel;
+
+  /**
+   * Creates a WKTReader that creates objects using a basic GeometryFactory.
+   */
+  public WKTReader() {
+    this(new GeometryFactory());
+  }
+
+  /**
+   *  Creates a <code>WKTReader</code> that creates objects using the given
+   *  <code>GeometryFactory</code>.
+   *
+   *@param  geometryFactory  the factory used to create <code>Geometry</code>s.
+   */
+  public WKTReader(GeometryFactory geometryFactory) {
+    this.geometryFactory = geometryFactory;
+    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
+	 */
+  public Geometry read(String wellKnownText) throws ParseException {
+    StringReader reader = new StringReader(wellKnownText);
+    try {
+      return read(reader);
+    }
+    finally {
+      reader.close();
+    }
+  }
+
+  /**
+   *  Converts a Well-known Text representation to a <code>Geometry</code>.
+   *
+   *@param  reader           a Reader which will return a <Geometry Tagged Text>
+   *      string (see the OpenGIS Simple Features Specification)
+   *@return                  a <code>Geometry</code> read from <code>reader</code>
+   *@throws  ParseException  if a parsing problem occurs
+   */
+  public Geometry read(Reader reader) throws ParseException {
+    StreamTokenizer tokenizer = new StreamTokenizer(reader);
+    try {
+      return readGeometryTaggedText(tokenizer);
+    }
+    catch (IOException e) {
+      throw new ParseException(e.toString());
+    }
+  }
+
+  /**
+   *  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".
+   *@return                  the next array of <code>Coordinate</code>s in the
+   *      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)
+      throws IOException, ParseException
+  {
+    String nextToken = getNextEmptyOrOpener(tokenizer);
+    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);
+    }
+    Coordinate[] array = new Coordinate[coordinates.size()];
+    return (Coordinate[]) coordinates.toArray(array);
+  }
+
+  private Coordinate getPreciseCoordinate(StreamTokenizer tokenizer)
+      throws IOException, ParseException
+  {
+    Coordinate coord = new Coordinate();
+    coord.x = getNextNumber(tokenizer);
+    coord.y = getNextNumber(tokenizer);
+    if (isNumberNext(tokenizer)) {
+        coord.z = getNextNumber(tokenizer);
+    }
+    precisionModel.makePrecise(coord);
+    return coord;
+  }
+  private boolean isNumberNext(StreamTokenizer tokenizer) throws IOException {
+      try {
+          return tokenizer.nextToken() == StreamTokenizer.TT_NUMBER;
+      }
+      finally {
+          tokenizer.pushBack();
+      }
+  }
+  /**
+   *  Returns the next number in the stream.
+   *
+   *@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  IOException     if an I/O error occurs
+   */
+  private double getNextNumber(StreamTokenizer tokenizer) 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 ','");
+    }
+    Assert.shouldNeverReachHere("Encountered unexpected StreamTokenizer type: "
+         + type);
+    return 0;
+  }
+
+  /**
+   *  Returns the next "EMPTY" or "(" 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
+   *      text.
+   *@throws  ParseException  if the next token is not "EMPTY" or "("
+   *@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("(")) {
+      return nextWord;
+    }
+    throw new ParseException("Expected 'EMPTY' or '(' but encountered '" +
+        nextWord + "'");
+  }
+
+  /**
+   *  Returns the next ")" or "," 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 ","
+   *@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(")")) {
+      return nextWord;
+    }
+    throw new ParseException("Expected ')' or ',' but encountered '" + nextWord
+         + "'");
+  }
+
+  /**
+   *  Returns the next ")" 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 ")"
+   *@throws  IOException     if an I/O error occurs
+   */
+  private String getNextCloser(StreamTokenizer tokenizer) throws IOException, ParseException {
+    String nextWord = getNextWord(tokenizer);
+    if (nextWord.equals(")")) {
+      return nextWord;
+    }
+    throw new ParseException("Expected ')' but encountered '" + nextWord + "'");
+  }
+
+  /**
+   *  Returns the next word in the stream as uppercase text.
+   *
+   *@param  tokenizer        tokenizer over a stream of text in Well-known Text
+   *      format. The next token must be a word.
+   *@return                  the next word in the stream as uppercase text
+   *@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 {
+    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 ",";
+    }
+    Assert.shouldNeverReachHere("Encountered unexpected StreamTokenizer type: " + type);
+    return null;
+  }
+
+  /**
+   *  Creates a <code>Geometry</code> using the next token in the stream.
+   *
+   *@param  tokenizer        tokenizer over a stream of text in Well-known Text
+   *      format. The next tokens must form a &lt;Geometry Tagged Text&gt;.
+   *@return                  a <code>Geometry</code> specified by the next token
+   *      in the stream
+   *@throws  ParseException  if the coordinates used to create a <code>Polygon</code>
+   *      shell and holes do not form closed linestrings, or if an unexpected
+   *      token was encountered
+   *@throws  IOException     if an I/O error occurs
+   */
+  private Geometry readGeometryTaggedText(StreamTokenizer tokenizer) throws IOException, ParseException {
+    String type = getNextWord(tokenizer);
+    if (type.equals("POINT")) {
+      return readPointText(tokenizer);
+    }
+    else if (type.equals("LINESTRING")) {
+      return readLineStringText(tokenizer);
+    }
+    else if (type.equals("LINEARRING")) {
+      return readLinearRingText(tokenizer);
+    }
+    else if (type.equals("POLYGON")) {
+      return readPolygonText(tokenizer);
+    }
+    else if (type.equals("MULTIPOINT")) {
+      return readMultiPointText(tokenizer);
+    }
+    else if (type.equals("MULTILINESTRING")) {
+      return readMultiLineStringText(tokenizer);
+    }
+    else if (type.equals("MULTIPOLYGON")) {
+      return readMultiPolygonText(tokenizer);
+    }
+    else if (type.equals("GEOMETRYCOLLECTION")) {
+      return readGeometryCollectionText(tokenizer);
+    }
+    throw new ParseException("Unknown type: " + type);
+  }
+
+  /**
+   *  Creates a <code>Point</code> using the next token in the stream.
+   *
+   *@param  tokenizer        tokenizer over a stream of text in Well-known Text
+   *      format. The next tokens must form a &lt;Point Text&gt;.
+   *@return                  a <code>Point</code> specified by the next token in
+   *      the stream
+   *@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")) {
+      return geometryFactory.createPoint((Coordinate)null);
+    }
+    Point point = geometryFactory.createPoint(getPreciseCoordinate(tokenizer));
+    getNextCloser(tokenizer);
+    return point;
+  }
+
+  /**
+   *  Creates a <code>LineString</code> using the next token in the stream.
+   *
+   *@param  tokenizer        tokenizer over a stream of text in Well-known Text
+   *      format. The next tokens must form a &lt;LineString Text&gt;.
+   *@return                  a <code>LineString</code> specified by the next
+   *      token in the stream
+   *@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));
+  }
+
+  /**
+   *  Creates a <code>LinearRing</code> using the next token in the stream.
+   *
+   *@param  tokenizer        tokenizer over a stream of text in Well-known Text
+   *      format. The next tokens must form a &lt;LineString Text&gt;.
+   *@return                  a <code>LinearRing</code> specified by the next
+   *      token in the stream
+   *@throws  IOException     if an I/O error occurs
+   *@throws  ParseException  if the coordinates used to create the <code>LinearRing</code>
+   *      do not form a closed linestring, or if an unexpected token was
+   *      encountered
+   */
+  private LinearRing readLinearRingText(StreamTokenizer tokenizer)
+    throws IOException, ParseException
+  {
+    return geometryFactory.createLinearRing(getCoordinates(tokenizer));
+  }
+
+  /**
+   *  Creates a <code>MultiPoint</code> using the next token in the stream.
+   *
+   *@param  tokenizer        tokenizer over a stream of text in Well-known Text
+   *      format. The next tokens must form a &lt;MultiPoint Text&gt;.
+   *@return                  a <code>MultiPoint</code> specified by the next
+   *      token in the stream
+   *@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)));
+  }
+
+  /**
+   *  Creates an array of <code>Point</code>s having the given <code>Coordinate</code>
+   *  s.
+   *
+   *@param  coordinates  the <code>Coordinate</code>s with which to create the
+   *      <code>Point</code>s
+   *@return              <code>Point</code>s created using this <code>WKTReader</code>
+   *      s <code>GeometryFactory</code>
+   */
+  private Point[] toPoints(Coordinate[] coordinates) {
+    ArrayList points = new ArrayList();
+    for (int i = 0; i < coordinates.length; i++) {
+      points.add(geometryFactory.createPoint(coordinates[i]));
+    }
+    return (Point[]) points.toArray(new Point[]{});
+  }
+
+  /**
+   *  Creates a <code>Polygon</code> using the next token in the stream.
+   *
+   *@param  tokenizer        tokenizer over a stream of text in Well-known Text
+   *      format. The next tokens must form a &lt;Polygon Text&gt;.
+   *@return                  a <code>Polygon</code> specified by the next token
+   *      in the stream
+   *@throws  ParseException  if the coordinates used to create the <code>Polygon</code>
+   *      shell and holes do not form closed linestrings, or if an unexpected
+   *      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")) {
+        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);
+      holes.add(hole);
+      nextToken = getNextCloserOrComma(tokenizer);
+    }
+    LinearRing[] array = new LinearRing[holes.size()];
+    return geometryFactory.createPolygon(shell, (LinearRing[]) holes.toArray(array));
+  }
+
+  /**
+   *  Creates a <code>MultiLineString</code> using the next token in the stream.
+   *
+   *@param  tokenizer        tokenizer over a stream of text in Well-known Text
+   *      format. The next tokens must form a &lt;MultiLineString Text&gt;.
+   *@return                  a <code>MultiLineString</code> specified by the
+   *      next token in the stream
+   *@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")) {
+      return geometryFactory.createMultiLineString(new LineString[]{});
+    }
+    ArrayList lineStrings = new ArrayList();
+    LineString lineString = readLineStringText(tokenizer);
+    lineStrings.add(lineString);
+    nextToken = getNextCloserOrComma(tokenizer);
+    while (nextToken.equals(",")) {
+      lineString = readLineStringText(tokenizer);
+      lineStrings.add(lineString);
+      nextToken = getNextCloserOrComma(tokenizer);
+    }
+    LineString[] array = new LineString[lineStrings.size()];
+    return geometryFactory.createMultiLineString((LineString[]) lineStrings.toArray(array));
+  }
+
+  /**
+   *  Creates a <code>MultiPolygon</code> using the next token in the stream.
+   *
+   *@param  tokenizer        tokenizer over a stream of text in Well-known Text
+   *      format. The next tokens must form a &lt;MultiPolygon Text&gt;.
+   *@return                  a <code>MultiPolygon</code> specified by the next
+   *      token in the stream, or if if the coordinates used to create the
+   *      <code>Polygon</code> shells and holes do not form closed linestrings.
+   *@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")) {
+      return geometryFactory.createMultiPolygon(new Polygon[]{});
+    }
+    ArrayList polygons = new ArrayList();
+    Polygon polygon = readPolygonText(tokenizer);
+    polygons.add(polygon);
+    nextToken = getNextCloserOrComma(tokenizer);
+    while (nextToken.equals(",")) {
+      polygon = readPolygonText(tokenizer);
+      polygons.add(polygon);
+      nextToken = getNextCloserOrComma(tokenizer);
+    }
+    Polygon[] array = new Polygon[polygons.size()];
+    return geometryFactory.createMultiPolygon((Polygon[]) polygons.toArray(array));
+  }
+
+  /**
+   *  Creates a <code>GeometryCollection</code> using the next token in the
+   *  stream.
+   *
+   *@param  tokenizer        tokenizer over a stream of text in Well-known Text
+   *      format. The next tokens must form a &lt;GeometryCollection Text&gt;.
+   *@return                  a <code>GeometryCollection</code> specified by the
+   *      next token in the stream
+   *@throws  ParseException  if the coordinates used to create a <code>Polygon</code>
+   *      shell and holes do not form closed linestrings, or if an unexpected
+   *      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")) {
+      return geometryFactory.createGeometryCollection(new Geometry[]{});
+    }
+    ArrayList geometries = new ArrayList();
+    Geometry geometry = readGeometryTaggedText(tokenizer);
+    geometries.add(geometry);
+    nextToken = getNextCloserOrComma(tokenizer);
+    while (nextToken.equals(",")) {
+      geometry = readGeometryTaggedText(tokenizer);
+      geometries.add(geometry);
+      nextToken = getNextCloserOrComma(tokenizer);
+    }
+    Geometry[] array = new Geometry[geometries.size()];
+    return geometryFactory.createGeometryCollection((Geometry[]) geometries.toArray(array));
+  }
+}
+

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/io/WKTWriter.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/io/WKTWriter.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/io/WKTWriter.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,574 @@
+
+
+/*
+ * 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 com.vividsolutions.jts.geom.*;
+
+import com.vividsolutions.jts.util.*;
+import java.io.*;
+import java.text.DecimalFormat;
+import java.text.DecimalFormatSymbols;
+
+/**
+ * Outputs the textual representation of a {@link Geometry}.
+ * <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".
+ *
+ * @version 1.6
+ */
+public class WKTWriter {
+
+  private static int INDENT = 2;
+  /**
+   *  Creates the <code>DecimalFormat</code> used to write <code>double</code>s
+   *  with a sufficient number of decimal places.
+   *
+   *@param  precisionModel  the <code>PrecisionModel</code> used to determine
+   *      the number of decimal places to write.
+   *@return                 a <code>DecimalFormat</code> that write <code>double</code>
+   *      s without scientific notation.
+   */
+  private static DecimalFormat createFormatter(PrecisionModel precisionModel) {
+    // the default number of decimal places is 16, which is sufficient
+    // to accomodate the maximum precision of a double.
+    int decimalPlaces = precisionModel.getMaximumSignificantDigits();
+    // specify decimal separator explicitly to avoid problems in other locales
+    DecimalFormatSymbols symbols = new DecimalFormatSymbols();
+    symbols.setDecimalSeparator('.');
+    return new DecimalFormat("#" + (decimalPlaces > 0 ? "." : "")
+         + stringOfChar('#', decimalPlaces), symbols);
+  }
+
+  /**
+   *  Returns a <code>String</code> of repeated characters.
+   *
+   *@param  ch     the character to repeat
+   *@param  count  the number of times to repeat the character
+   *@return        a <code>String</code> of characters
+   */
+  public static String stringOfChar(char ch, int count) {
+    StringBuffer buf = new StringBuffer();
+    for (int i = 0; i < count; i++) {
+      buf.append(ch);
+    }
+    return buf.toString();
+  }
+
+  private DecimalFormat formatter;
+  private boolean isFormatted = false;
+  private int level = 0;
+
+  public WKTWriter()
+  {
+  }
+
+  /**
+   *  Converts a <code>Geometry</code> to its Well-known Text representation.
+   *
+   *@param  geometry  a <code>Geometry</code> to process
+   *@return           a <Geometry Tagged Text> string (see the OpenGIS Simple
+   *      Features Specification)
+   */
+  public String write(Geometry geometry)
+  {
+    Writer sw = new StringWriter();
+    try {
+      writeFormatted(geometry, false, sw);
+    }
+    catch (IOException ex) {
+      Assert.shouldNeverReachHere();
+    }
+    return sw.toString();
+  }
+
+  /**
+   *  Converts a <code>Geometry</code> to its Well-known Text representation.
+   *
+   *@param  geometry  a <code>Geometry</code> to process
+   *@return           a <Geometry Tagged Text> string (see the OpenGIS Simple
+   *      Features Specification)
+   */
+  public void write(Geometry geometry, Writer writer)
+    throws IOException
+  {
+    writeFormatted(geometry, false, writer);
+  }
+
+  /**
+   *  Same as <code>write</code>, but with newlines and spaces to make the
+   *  well-known text more readable.
+   *
+   *@param  geometry  a <code>Geometry</code> to process
+   *@return           a <Geometry Tagged Text> string (see the OpenGIS Simple
+   *      Features Specification), with newlines and spaces
+   */
+  public String writeFormatted(Geometry geometry)
+  {
+    Writer sw = new StringWriter();
+    try {
+      writeFormatted(geometry, true, sw);
+    }
+    catch (IOException ex) {
+      Assert.shouldNeverReachHere();
+    }
+    return sw.toString();
+  }
+  /**
+   *  Same as <code>write</code>, but with newlines and spaces to make the
+   *  well-known text more readable.
+   *
+   *@param  geometry  a <code>Geometry</code> to process
+   *@return           a <Geometry Tagged Text> string (see the OpenGIS Simple
+   *      Features Specification), with newlines and spaces
+   */
+  public void writeFormatted(Geometry geometry, Writer writer)
+    throws IOException
+  {
+    writeFormatted(geometry, true, writer);
+  }
+  /**
+   *  Converts a <code>Geometry</code> to its Well-known Text representation.
+   *
+   *@param  geometry  a <code>Geometry</code> to process
+   *@return           a <Geometry Tagged Text> string (see the OpenGIS Simple
+   *      Features Specification)
+   */
+  private void writeFormatted(Geometry geometry, boolean isFormatted, Writer writer)
+    throws IOException
+  {
+    this.isFormatted = isFormatted;
+    formatter = createFormatter(geometry.getPrecisionModel());
+    appendGeometryTaggedText(geometry, 0, writer);
+  }
+
+
+  /**
+   *  Converts a <code>Geometry</code> to &lt;Geometry Tagged Text&gt; format,
+   *  then appends it to the writer.
+   *
+   *@param  geometry  the <code>Geometry</code> to process
+   *@param  writer    the output writer to append to
+   */
+  private void appendGeometryTaggedText(Geometry geometry, int level, Writer writer)
+    throws IOException
+  {
+    indent(level, writer);
+
+    if (geometry instanceof Point) {
+      Point point = (Point) geometry;
+      appendPointTaggedText(point.getCoordinate(), level, writer, point.getPrecisionModel());
+    }
+    else if (geometry instanceof LinearRing) {
+      appendLinearRingTaggedText((LinearRing) geometry, level, writer);
+    }
+    else if (geometry instanceof LineString) {
+      appendLineStringTaggedText((LineString) geometry, level, writer);
+    }
+    else if (geometry instanceof Polygon) {
+      appendPolygonTaggedText((Polygon) geometry, level, writer);
+    }
+    else if (geometry instanceof MultiPoint) {
+      appendMultiPointTaggedText((MultiPoint) geometry, level, writer);
+    }
+    else if (geometry instanceof MultiLineString) {
+      appendMultiLineStringTaggedText((MultiLineString) geometry, level, writer);
+    }
+    else if (geometry instanceof MultiPolygon) {
+      appendMultiPolygonTaggedText((MultiPolygon) geometry, level, writer);
+    }
+    else if (geometry instanceof GeometryCollection) {
+      appendGeometryCollectionTaggedText((GeometryCollection) geometry, level, writer);
+    }
+    else {
+      Assert.shouldNeverReachHere("Unsupported Geometry implementation:"
+           + geometry.getClass());
+    }
+  }
+
+  /**
+   *  Converts a <code>Coordinate</code> to &lt;Point Tagged Text&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 appendPointTaggedText(Coordinate coordinate, int level, Writer writer,
+      PrecisionModel precisionModel)
+    throws IOException
+  {
+    writer.write("POINT ");
+    appendPointText(coordinate, level, writer, precisionModel);
+  }
+
+  /**
+   *  Converts a <code>LineString</code> to &lt;LineString Tagged 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 appendLineStringTaggedText(LineString lineString, int level, Writer writer)
+    throws IOException
+  {
+    writer.write("LINESTRING ");
+    appendLineStringText(lineString, level, false, writer);
+  }
+
+  /**
+   *  Converts a <code>LinearRing</code> to &lt;LinearRing Tagged Text&gt;
+   *  format, then appends it to the writer.
+   *
+   *@param  linearRing  the <code>LinearRing</code> to process
+   *@param  writer      the output writer to append to
+   */
+  private void appendLinearRingTaggedText(LinearRing linearRing, int level, Writer writer)
+    throws IOException
+  {
+    writer.write("LINEARRING ");
+    appendLineStringText(linearRing, level, false, writer);
+  }
+
+  /**
+   *  Converts a <code>Polygon</code> to &lt;Polygon Tagged Text&gt; format,
+   *  then appends it to the writer.
+   *
+   *@param  polygon  the <code>Polygon</code> to process
+   *@param  writer   the output writer to append to
+   */
+  private void appendPolygonTaggedText(Polygon polygon, int level, Writer writer)
+    throws IOException
+  {
+    writer.write("POLYGON ");
+    appendPolygonText(polygon, level, false, writer);
+  }
+
+  /**
+   *  Converts a <code>MultiPoint</code> to &lt;MultiPoint Tagged Text&gt;
+   *  format, then appends it to the writer.
+   *
+   *@param  multipoint  the <code>MultiPoint</code> to process
+   *@param  writer      the output writer to append to
+   */
+  private void appendMultiPointTaggedText(MultiPoint multipoint, int level, Writer writer)
+    throws IOException
+  {
+    writer.write("MULTIPOINT ");
+    appendMultiPointText(multipoint, level, writer);
+  }
+
+  /**
+   *  Converts a <code>MultiLineString</code> to &lt;MultiLineString Tagged
+   *  Text&gt; format, then appends it to the writer.
+   *
+   *@param  multiLineString  the <code>MultiLineString</code> to process
+   *@param  writer           the output writer to append to
+   */
+  private void appendMultiLineStringTaggedText(MultiLineString multiLineString, int level,
+      Writer writer)
+    throws IOException
+  {
+    writer.write("MULTILINESTRING ");
+    appendMultiLineStringText(multiLineString, level, false, writer);
+  }
+
+  /**
+   *  Converts a <code>MultiPolygon</code> to &lt;MultiPolygon Tagged Text&gt;
+   *  format, then appends it to the writer.
+   *
+   *@param  multiPolygon  the <code>MultiPolygon</code> to process
+   *@param  writer        the output writer to append to
+   */
+  private void appendMultiPolygonTaggedText(MultiPolygon multiPolygon, int level, Writer writer)
+    throws IOException
+  {
+    writer.write("MULTIPOLYGON ");
+    appendMultiPolygonText(multiPolygon, level, writer);
+  }
+
+  /**
+   *  Converts a <code>GeometryCollection</code> to &lt;GeometryCollection
+   *  Tagged Text&gt; format, then appends it to the writer.
+   *
+   *@param  geometryCollection  the <code>GeometryCollection</code> to process
+   *@param  writer              the output writer to append to
+   */
+  private void appendGeometryCollectionTaggedText(GeometryCollection geometryCollection, int level,
+      Writer writer)
+    throws IOException
+  {
+    writer.write("GEOMETRYCOLLECTION ");
+    appendGeometryCollectionText(geometryCollection, level, writer);
+  }
+
+  /**
+   *  Converts a <code>Coordinate</code> to &lt;Point Text&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 appendPointText(Coordinate coordinate, int level, Writer writer,
+      PrecisionModel precisionModel)
+    throws IOException
+  {
+    if (coordinate == null) {
+      writer.write("EMPTY");
+    }
+    else {
+      writer.write("(");
+      appendCoordinate(coordinate, writer, precisionModel);
+      writer.write(")");
+    }
+  }
+
+  /**
+   *  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)
+    throws IOException
+  {
+    //Coordinate externalCoordinate = new Coordinate();
+    //precisionModel.toExternal(coordinate, externalCoordinate);
+    writer.write(writeNumber(coordinate.x) + " " + writeNumber(coordinate.y));
+  }
+
+  /**
+   *  Converts a <code>double</code> to a <code>String</code>, not in scientific
+   *  notation.
+   *
+   *@param  d  the <code>double</code> to convert
+   *@return    the <code>double</code> as a <code>String</code>, not in
+   *      scientific notation
+   */
+  private String writeNumber(double d) {
+    return formatter.format(d);
+  }
+
+  /**
+   *  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
+  {
+    if (lineString.isEmpty()) {
+      writer.write("EMPTY");
+    }
+    else {
+      if (doIndent) indent(level, writer);
+      writer.write("(");
+      for (int i = 0; i < lineString.getNumPoints(); i++) {
+        if (i > 0) {
+          writer.write(", ");
+          if (i % 10 == 0) indent(level + 2, writer);
+        }
+        appendCoordinate(lineString.getCoordinateN(i), writer, lineString.getPrecisionModel());
+      }
+      writer.write(")");
+    }
+  }
+
+  /**
+   *  Converts a <code>Polygon</code> to &lt;Polygon Text&gt; format, then
+   *  appends it to the writer.
+   *
+   *@param  polygon  the <code>Polygon</code> to process
+   *@param  writer   the output writer to append to
+   */
+  private void appendPolygonText(Polygon polygon, int level, boolean indentFirst, Writer writer)
+    throws IOException
+  {
+    if (polygon.isEmpty()) {
+      writer.write("EMPTY");
+    }
+    else {
+      if (indentFirst) indent(level, writer);
+      writer.write("(");
+      appendLineStringText(polygon.getExteriorRing(), level, false, writer);
+      for (int i = 0; i < polygon.getNumInteriorRing(); i++) {
+        writer.write(", ");
+        appendLineStringText(polygon.getInteriorRingN(i), level + 1, true, writer);
+      }
+      writer.write(")");
+    }
+  }
+
+  /**
+   *  Converts a <code>MultiPoint</code> to &lt;MultiPoint Text&gt; format, then
+   *  appends it to the writer.
+   *
+   *@param  multiPoint  the <code>MultiPoint</code> to process
+   *@param  writer      the output writer to append to
+   */
+  private void appendMultiPointText(MultiPoint multiPoint, int level, Writer writer)
+    throws IOException
+  {
+    if (multiPoint.isEmpty()) {
+      writer.write("EMPTY");
+    }
+    else {
+      writer.write("(");
+      for (int i = 0; i < multiPoint.getNumGeometries(); i++) {
+        if (i > 0) {
+          writer.write(", ");
+        }
+        appendCoordinate(((Point) multiPoint.getGeometryN(i)).getCoordinate(), writer,
+            multiPoint.getPrecisionModel());
+      }
+      writer.write(")");
+    }
+  }
+
+  /**
+   *  Converts a <code>MultiLineString</code> to &lt;MultiLineString Text&gt;
+   *  format, then appends it to the writer.
+   *
+   *@param  multiLineString  the <code>MultiLineString</code> to process
+   *@param  writer           the output writer to append to
+   */
+  private void appendMultiLineStringText(MultiLineString multiLineString, int level, boolean indentFirst,
+      Writer writer)
+    throws IOException
+  {
+    if (multiLineString.isEmpty()) {
+      writer.write("EMPTY");
+    }
+    else {
+      int level2 = level;
+      boolean doIndent = indentFirst;
+      writer.write("(");
+      for (int i = 0; i < multiLineString.getNumGeometries(); i++) {
+        if (i > 0) {
+          writer.write(", ");
+          level2 = level + 1;
+          doIndent = true;
+        }
+        appendLineStringText((LineString) multiLineString.getGeometryN(i), level2, doIndent, writer);
+      }
+      writer.write(")");
+    }
+  }
+
+  /**
+   *  Converts a <code>MultiPolygon</code> to &lt;MultiPolygon Text&gt; format,
+   *  then appends it to the writer.
+   *
+   *@param  multiPolygon  the <code>MultiPolygon</code> to process
+   *@param  writer        the output writer to append to
+   */
+  private void appendMultiPolygonText(MultiPolygon multiPolygon, int level, Writer writer)
+    throws IOException
+  {
+    if (multiPolygon.isEmpty()) {
+      writer.write("EMPTY");
+    }
+    else {
+      int level2 = level;
+      boolean doIndent = false;
+      writer.write("(");
+      for (int i = 0; i < multiPolygon.getNumGeometries(); i++) {
+        if (i > 0) {
+          writer.write(", ");
+          level2 = level + 1;
+          doIndent = true;
+        }
+        appendPolygonText((Polygon) multiPolygon.getGeometryN(i), level2, doIndent, writer);
+      }
+      writer.write(")");
+    }
+  }
+
+  /**
+   *  Converts a <code>GeometryCollection</code> to &lt;GeometryCollectionText&gt;
+   *  format, then appends it to the writer.
+   *
+   *@param  geometryCollection  the <code>GeometryCollection</code> to process
+   *@param  writer              the output writer to append to
+   */
+  private void appendGeometryCollectionText(GeometryCollection geometryCollection, int level,
+      Writer writer)
+    throws IOException
+  {
+    if (geometryCollection.isEmpty()) {
+      writer.write("EMPTY");
+    }
+    else {
+      int level2 = level;
+      writer.write("(");
+      for (int i = 0; i < geometryCollection.getNumGeometries(); i++) {
+        if (i > 0) {
+          writer.write(", ");
+          level2 = level + 1;
+        }
+        appendGeometryTaggedText(geometryCollection.getGeometryN(i), level2, writer);
+      }
+      writer.write(")");
+    }
+  }
+
+  private void indent(int level, Writer writer)
+    throws IOException
+  {
+    if (! isFormatted || level <= 0) return;
+    writer.write("\n");
+    writer.write(stringOfChar(' ', INDENT * level));
+  }
+
+
+}
+

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/io/package.html
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/io/package.html	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/io/package.html	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,25 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+<!--
+
+-->
+</head>
+<body bgcolor="white">
+
+Contains the interfaces for converting JTS objects to and from other formats.
+<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.
+
+<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/IteratedNoder.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/IteratedNoder.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/IteratedNoder.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,117 @@
+
+/*
+ * 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.*;
+import com.vividsolutions.jts.geom.*;
+
+/**
+ * Nodes a set of SegmentStrings completely.
+ * The set of segmentStrings is fully noded;
+ * i.e. noding is repeated until no further
+ * intersections are detected.
+ * <p>
+ * Iterated noding using a FLOATING precision model is not guaranteed to converge,
+ * 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
+ */
+public class IteratedNoder
+{
+  private PrecisionModel pm;
+  private LineIntersector li;
+
+  public IteratedNoder(PrecisionModel pm)
+  {
+    li = new RobustLineIntersector();
+    this.pm = pm;
+    li.setPrecisionModel(pm);
+  }
+
+  /**
+   * 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
+   * the noding.
+   *
+   * @param segStrings a collection of SegmentStrings to be noded
+   * @return a collection of the noded SegmentStrings
+   * @throws TopologyException if the iterated noding fails to converge.
+   */
+  public Collection node(Collection segStrings)
+    throws TopologyException
+  {
+    int[] numInteriorIntersections = new int[1];
+    Collection nodedEdges = segStrings;
+    int nodingIterationCount = 0;
+    int lastNodesCreated = -1;
+    do {
+      nodedEdges = node(nodedEdges, numInteriorIntersections);
+      nodingIterationCount++;
+      int nodesCreated = numInteriorIntersections[0];
+//System.out.println("# nodes created: " + nodesCreated);
+      if (lastNodesCreated > 0 && nodesCreated > lastNodesCreated) {
+        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;
+  }
+
+
+/**
+ * Node the input segment strings once
+ * and create the split edges between the nodes
+ */
+  private Collection node(Collection segStrings, int[] numInteriorIntersections)
+  {
+    SegmentIntersector si = new SegmentIntersector(li);
+    MCQuadtreeNoder noder = new MCQuadtreeNoder();
+    noder.setSegmentIntersector(si);
+
+    // perform the noding
+    Collection nodedSegStrings = noder.node(segStrings);
+    numInteriorIntersections[0] = si.numInteriorIntersections;
+//System.out.println("# intersection tests: " + si.numTests);
+    return nodedSegStrings;
+  }
+
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/MCQuadtreeNoder.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/MCQuadtreeNoder.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/MCQuadtreeNoder.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,127 @@
+
+/*
+ * 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);
+    }
+
+  }
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/Noder.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/Noder.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/Noder.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,79 @@
+
+/*
+ * 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.geom.*;
+import com.vividsolutions.jts.geomgraph.index.*;
+
+/**
+ * Computes all intersections between segments in a set of {@link SegmentString}s.
+ * Intersections found are represented as {@link SegmentNode}s and add to the
+ * {@link SegmentString}s in which they occur.
+ *
+ * @version 1.6
+ */
+public abstract class Noder
+{
+
+  public static List getNodedEdges(Collection segStrings)
+  {
+    List resultEdgelist = new ArrayList();
+    getNodedEdges(segStrings, resultEdgelist);
+    return resultEdgelist;
+  }
+
+  public static void getNodedEdges(Collection segStrings, Collection resultEdgelist)
+  {
+    for (Iterator i = segStrings.iterator(); i.hasNext(); ) {
+      SegmentString ss = (SegmentString) i.next();
+      ss.getIntersectionList().addSplitEdges(resultEdgelist);
+    }
+  }
+
+  protected SegmentIntersector segInt;
+  //protected LineIntersector li;
+
+  public Noder() {
+  }
+
+  public void setSegmentIntersector(SegmentIntersector segInt)
+  {
+    this.segInt = segInt;
+  }
+
+  public abstract Collection node(Collection segStrings);
+
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/NodingValidator.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/NodingValidator.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/NodingValidator.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,145 @@
+
+/*
+ * 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.*;
+import com.vividsolutions.jts.geom.*;
+
+/**
+ * Validates that a collection of {@link SegmentString}s is correctly noded.
+ * Throws an appropriate exception if an noding error is found.
+ *
+ * @version 1.6
+ */
+public class NodingValidator {
+
+  private LineIntersector li = new RobustLineIntersector();
+
+  private Collection segStrings;
+
+  public NodingValidator(Collection segStrings)
+  {
+    this.segStrings = segStrings;
+  }
+
+  public void checkValid()
+  {
+    checkNoInteriorPointsSame();
+    checkProperIntersections();
+  }
+
+
+  private void checkProperIntersections()
+  {
+    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);
+      }
+    }
+  }
+
+  private void checkProperIntersections(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);
+       }
+     }
+  }
+
+  private void checkProperIntersections(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()) {
+
+      if (li.isProper()
+          || hasInteriorIntersection(li, p00, p01)
+          || hasInteriorIntersection(li, p00, p01)) {
+        throw new RuntimeException("found non-noded intersection at "
+                                   + p00 + "-" + p01
+                                   + " and "
+                                   + p10 + "-" + p11);
+      }
+    }
+  }
+  /**
+   *@return true if there is an intersection point which is not an endpoint of the segment p0-p1
+   */
+  private boolean hasInteriorIntersection(LineIntersector li, Coordinate p0, Coordinate p1)
+  {
+    for (int i = 0; i < li.getIntersectionNum(); i++) {
+      Coordinate intPt = li.getIntersection(i);
+      if (! (intPt.equals(p0) || intPt.equals(p1)))
+          return true;
+    }
+    return false;
+  }
+
+  private void checkNoInteriorPointsSame()
+  {
+    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);
+    }
+  }
+
+  private void checkNoInteriorPointsSame(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);
+      }
+    }
+  }
+
+
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/SegmentIntersector.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/SegmentIntersector.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/SegmentIntersector.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,187 @@
+/*
+ * 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.6
+ */
+public class SegmentIntersector
+{
+  public static boolean isAdjacentSegments(int i1, int i2)
+  {
+    return Math.abs(i1 - i2) == 1;
+  }
+
+  /**
+   * These variables keep track of what types of intersections were
+   * found during ALL edges that have been intersected.
+   */
+  private boolean hasIntersection = false;
+  private boolean hasProper = false;
+  private boolean hasProperInterior = false;
+  private boolean hasInterior = false;
+
+  // the proper intersection point found
+  private Coordinate properIntersectionPoint = null;
+
+  private LineIntersector li;
+  private boolean recordIsolated;
+  private boolean isSelfIntersection;
+  //private boolean intersectionFound;
+  public int numIntersections = 0;
+  public int numInteriorIntersections = 0;
+  public int numProperIntersections = 0;
+
+  // testing only
+  public int numTests = 0;
+
+  public SegmentIntersector(LineIntersector li)
+  {
+    this.li = li;
+  }
+
+  public LineIntersector getLineIntersector() { return li; }
+
+  /**
+   * @return the proper intersection point, or <code>null</code> if none was found
+   */
+  public Coordinate getProperIntersectionPoint()  {    return properIntersectionPoint;  }
+
+  public boolean hasIntersection() { return hasIntersection; }
+  /**
+   * A proper intersection is an intersection which is interior to at least two
+   * line segments.  Note that a proper intersection is not necessarily
+   * in the interior of the entire Geometry, since another edge may have
+   * an endpoint equal to the intersection, which according to SFS semantics
+   * can result in the point being on the Boundary of the Geometry.
+   */
+  public boolean hasProperIntersection() { return hasProper; }
+  /**
+   * A proper interior intersection is a proper intersection which is <b>not</b>
+   * contained in the set of boundary nodes set for this SegmentIntersector.
+   */
+  public boolean hasProperInteriorIntersection() { return hasProperInterior; }
+  /**
+   * An interior intersection is an intersection which is
+   * in the interior of some segment.
+   */
+  public boolean hasInteriorIntersection() { return hasInterior; }
+
+  /**
+   * A trivial intersection is an apparent self-intersection which in fact
+   * is simply the point shared by adjacent line segments.
+   * Note that closed edges require a special check for the point shared by the beginning
+   * and end segments.
+   */
+  private boolean isTrivialIntersection(SegmentString e0, int segIndex0, SegmentString e1, int segIndex1)
+  {
+    if (e0 == e1) {
+      if (li.getIntersectionNum() == 1) {
+        if (isAdjacentSegments(segIndex0, segIndex1))
+          return true;
+        if (e0.isClosed()) {
+          int maxSegIndex = e0.size() - 1;
+          if (    (segIndex0 == 0 && segIndex1 == maxSegIndex)
+              ||  (segIndex1 == 0 && segIndex0 == maxSegIndex) ) {
+            return true;
+          }
+        }
+      }
+    }
+    return false;
+  }
+
+  /**
+   * 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()) {
+      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;
+        }
+      }
+    }
+  }
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/SegmentNode.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/SegmentNode.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/SegmentNode.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -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.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
+ */
+public class SegmentNode
+    implements Comparable
+{
+
+  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;
+    this.coord = new Coordinate(coord);
+    this.segmentIndex = segmentIndex;
+    this.dist = dist;
+    //label = new Label();
+  }
+
+  /**
+   * @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 isEndPoint(int maxSegmentIndex)
+  {
+    if (segmentIndex == 0 && dist == 0.0) return true;
+    if (segmentIndex == maxSegmentIndex) return true;
+    return false;
+  }
+
+  public int compareTo(Object obj)
+  {
+    SegmentNode other = (SegmentNode) obj;
+    return compare(other.segmentIndex, other.dist);
+  }
+
+  public void print(PrintStream out)
+  {
+    out.print(coord);
+    out.print(" seg # = " + segmentIndex);
+    out.println(" dist = " + dist);
+  }
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/SegmentNodeList.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/SegmentNodeList.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/SegmentNodeList.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,267 @@
+
+
+
+/*
+ * 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.io.PrintStream;
+import java.util.*;
+import com.vividsolutions.jts.geom.*;
+import com.vividsolutions.jts.util.Debug;
+
+/**
+ * A list of the {@link SegmentNode}s present along a noded {@link SegmentString}.
+ *
+ * @version 1.6
+ */
+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;
+  }
+
+  /**
+   * 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)
+  {
+    SegmentNode eiNew = new SegmentNode(intPt, segmentIndex, dist);
+    Object obj = nodeMap.get(eiNew);
+    SegmentNode ei = (SegmentNode) nodeMap.get(eiNew);
+    if (ei != null) {
+      return ei;
+    }
+    nodeMap.put(eiNew, eiNew);
+    return eiNew;
+  }
+  /**
+   * Adds an intersection into the list, if it isn't already there.
+   * The input segmentIndex and dist are expected to be normalized.
+   * @return the SegmentIntersection found or added
+   */
+  /*
+  public SegmentNode OLDadd(Coordinate intPt, int segmentIndex, double dist)
+  {
+//Debug.println("adding edgeInt " + intPt + " " + segmentIndex + " " + dist);
+    ListIterator insertIt = list.listIterator();
+    boolean isInList = findInsertionPoint(segmentIndex, dist, insertIt);
+    SegmentNode ei;
+    if (! isInList) {
+      ei = new SegmentNode(intPt, segmentIndex, dist);
+      insertIt.add(ei);
+    }
+    else
+      ei = (SegmentNode) insertIt.next();
+    return ei;
+  }
+  /**
+   * returns an iterator of SegmentNodes
+   */
+  public Iterator iterator() { return nodeMap.values().iterator(); }
+/*
+  public boolean isEmpty()
+  {
+    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.
+   *
+   * @return true if this intersection is already in the list
+   */
+  /*
+  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()) {
+      SegmentNode ei = (SegmentNode) 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 = list.iterator(); it.hasNext(); ) {
+      SegmentNode ei = (SegmentNode) it.next();
+      if (ei.coord.equals(pt))
+       return true;
+    }
+    return false;
+  }
+  */
+  /**
+   * Adds entries for the first and last points of the edge to the list
+   */
+  public void addEndpoints()
+  {
+    int maxSegIndex = edge.size() - 1;
+    add(edge.getCoordinate(0), 0, 0.0);
+    add(edge.getCoordinate(maxSegIndex), maxSegIndex, 0.0);
+  }
+
+  /**
+   * 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).
+   */
+  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();
+
+    Iterator it = iterator();
+    // there should always be at least two entries in the list
+    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);
+  }
+
+  private void checkSplitEdgesCorrectness(List splitEdges)
+  {
+    Coordinate[] edgePts = edge.getCoordinates();
+
+    // 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]))
+      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]))
+      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.
+   * The label for the new edge is the same as the label for the parent edge.
+   */
+  SegmentString createSplitEdge(SegmentNode ei0, SegmentNode ei1)
+  {
+//Debug.print("\ncreateSplitEdge"); Debug.print(ei0); Debug.print(ei1);
+    int npts = ei1.segmentIndex - ei0.segmentIndex + 2;
+
+    Coordinate lastSegStartPt = edge.getCoordinate(ei1.segmentIndex);
+    // if the last intersection point is not equal to the its segment start pt,
+    // 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);
+    if (! useIntPt1) {
+      npts--;
+    }
+
+    Coordinate[] pts = new Coordinate[npts];
+    int ipt = 0;
+    pts[ipt++] = new Coordinate(ei0.coord);
+    for (int i = ei0.segmentIndex + 1; i <= ei1.segmentIndex; i++) {
+      pts[ipt++] = edge.getCoordinate(i);
+    }
+    if (useIntPt1) pts[ipt] = ei1.coord;
+    return new SegmentString(pts, edge.getContext());
+  }
+/*
+  public Coordinate[] getCoordinates()
+  {
+    List pts = new ArrayList();
+    Iterator it = list.iterator();
+    int nextIndex = 0;
+    while (it.hasNext()) {
+      SegmentNode ei = (SegmentNode) it.next();
+
+      // add points up to this intersection
+
+      for (int i = nextIndex; i <= ei.segmentIndex; i++) {
+        pts.add(edge.getCoordinate(i));
+      }
+      nextIndex = ei.segmentIndex + 1;
+      if (ei.dist > 0.0) pts.add(ei.coord);
+    }
+    // add remaining edge coordinates, if any
+    for (int i = nextIndex; i < edge.getCoordinates().length; i++) {
+      pts.add(edge.getCoordinate(i));
+    }
+    return CoordinateArrays.toCoordinateArray(pts);
+  }
+*/
+  public void print(PrintStream out)
+  {
+    out.println("Intersections:");
+    for (Iterator it = iterator(); it.hasNext(); ) {
+      SegmentNode ei = (SegmentNode) it.next();
+      ei.print(out);
+    }
+  }
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/SegmentString.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/SegmentString.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/SegmentString.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,160 @@
+
+/*
+ * 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.algorithm.LineIntersector;
+import com.vividsolutions.jts.geom.Coordinate;
+
+/**
+ * Contains a list of consecutive line segments which can be used to node the segments.
+ * The line segments are represented by an array of {@link Coordinate}s.
+ *
+ *
+ * @version 1.6
+ */
+public class SegmentString {
+
+  private SegmentNodeList eiList = new SegmentNodeList(this);
+  private Coordinate[] pts;
+  private Object context;
+  private boolean isIsolated;
+
+  public SegmentString(Coordinate[] pts, Object context)
+  {
+    this.pts = pts;
+    this.context = context;
+  }
+
+  public Object getContext() { return context; }
+  public SegmentNodeList getIntersectionList() { return eiList; }
+  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]);
+  }
+
+  /**
+   * Adds EdgeIntersections for one or both
+   * intersections found for a segment of an edge to the edge intersection list.
+   */
+  public void addIntersections(LineIntersector li, int segmentIndex, int geomIndex)
+  {
+    for (int i = 0; i < li.getIntersectionNum(); i++) {
+      addIntersection(li, segmentIndex, geomIndex, i);
+    }
+  }
+  /**
+   * Add an SegmentNode for intersection intIndex.
+   * An intersection that falls exactly on a vertex
+   * of the SegmentString is normalized
+   * to use the higher of the two possible segmentIndexes
+   */
+  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);
+  }
+
+  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
+      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);
+
+  }
+
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/SimpleNoder.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/SimpleNoder.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/SimpleNoder.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,78 @@
+
+/*
+ * 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.*;
+
+/**
+ * Nodes a set of {@link SegmentString}s by
+ * performing a brute-force comparison of every segment to every other one.
+ * This has n^2 performance, so is too slow for use on large numbers
+ * of segments.
+ *
+ * @version 1.6
+ */
+public class SimpleNoder
+    extends Noder
+{
+
+  public SimpleNoder() {
+  }
+
+  public Collection node(Collection inputSegStrings)
+  {
+    for (Iterator i0 = inputSegStrings.iterator(); i0.hasNext(); ) {
+      SegmentString edge0 = (SegmentString) i0.next();
+      for (Iterator i1 = inputSegStrings.iterator(); i1.hasNext(); ) {
+        SegmentString edge1 = (SegmentString) i1.next();
+        computeIntersects(edge0, edge1);
+      }
+    }
+    List nodedSegStrings = getNodedEdges(inputSegStrings);
+    return nodedSegStrings;
+  }
+
+  private void computeIntersects(SegmentString e0, SegmentString e1)
+  {
+    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++) {
+        segInt.processIntersections(e0, i0, e1, i1);
+      }
+    }
+  }
+
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/package.html
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/package.html	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/package.html	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,13 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+<!--
+
+-->
+</head>
+<body bgcolor="white">
+
+Classes to compute nodings for arrangements of line segments and line segment sequences.
+
+</body>
+</html>

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/snapround/SegmentSnapper.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,118 @@
+
+/*
+ * 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;
+  }
+}

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/snapround/SimpleSegmentStringsSnapper.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,83 @@
+
+/*
+ * 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/SnapRounder.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/snapround/SnapRounder.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/snapround/SnapRounder.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,124 @@
+
+/*
+ * 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;
+  }
+
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/snapround/package.html
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/snapround/package.html	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/noding/snapround/package.html	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,13 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+<!--
+
+-->
+</head>
+<body bgcolor="white">
+
+Contains classes to implement the Snap Rounding algorithm for noding linestrings.
+
+</body>
+</html>

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/GeometryGraphOperation.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/GeometryGraphOperation.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/GeometryGraphOperation.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,85 @@
+
+
+/*
+ * 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;
+
+import java.io.PrintStream;
+import java.util.*;
+import com.vividsolutions.jts.util.*;
+import com.vividsolutions.jts.algorithm.*;
+import com.vividsolutions.jts.geom.*;
+import com.vividsolutions.jts.geomgraph.GeometryGraph;
+
+/**
+ * The base class for operations that require {@link GeometryGraph)s.
+ *
+ * @version 1.6
+ */
+public class GeometryGraphOperation
+{
+  protected final CGAlgorithms cga = new CGAlgorithms();
+  protected final LineIntersector li = new RobustLineIntersector();
+  protected PrecisionModel resultPrecisionModel;
+  /**
+   * The operation args into an array so they can be accessed by index
+   */
+  protected GeometryGraph[] arg;  // the arg(s) of the operation
+
+  public GeometryGraphOperation(Geometry g0, Geometry g1)
+  {
+    // use the most precise model for the result
+    if (g0.getPrecisionModel().compareTo(g1.getPrecisionModel()) >= 0)
+      setComputationPrecision(g0.getPrecisionModel());
+    else
+      setComputationPrecision(g1.getPrecisionModel());
+
+    arg = new GeometryGraph[2];
+    arg[0] = new GeometryGraph(0, g0);
+    arg[1] = new GeometryGraph(1, g1);
+  }
+  public GeometryGraphOperation(Geometry g0) {
+    setComputationPrecision(g0.getPrecisionModel());
+
+    arg = new GeometryGraph[1];
+    arg[0] = new GeometryGraph(0, g0);;
+  }
+
+  public Geometry getArgGeometry(int i) { return arg[i].getGeometry(); }
+
+  protected void setComputationPrecision(PrecisionModel pm)
+  {
+    resultPrecisionModel = pm;
+    li.setPrecisionModel(resultPrecisionModel);
+  }
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/IsSimpleOp.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/IsSimpleOp.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/IsSimpleOp.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,175 @@
+
+
+/*
+ * 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;
+
+import java.util.*;
+import com.vividsolutions.jts.geom.*;
+import com.vividsolutions.jts.geomgraph.*;
+import com.vividsolutions.jts.algorithm.*;
+import com.vividsolutions.jts.geomgraph.index.SegmentIntersector;
+
+/**
+ * Tests whether a {@link Geometry} is simple.
+ * Only {@link Geometry}s whose definition allows them
+ * to be simple or non-simple are tested.  (E.g. Polygons must be simple
+ * by definition, so no test is provided.  To test whether a given Polygon is valid,
+ * use <code>Geometry#isValid</code>)
+ *
+ * @version 1.6
+ */
+public class IsSimpleOp {
+
+  public IsSimpleOp() {
+  }
+
+  public boolean isSimple(LineString geom)
+  {
+    return isSimpleLinearGeometry(geom);
+  }
+
+  public boolean isSimple(MultiLineString geom)
+  {
+    return isSimpleLinearGeometry(geom);
+  }
+
+  /**
+   * A MultiPoint is simple iff it has no repeated points
+   */
+  public boolean isSimple(MultiPoint mp)
+  {
+    if (mp.isEmpty()) return true;
+    Set points = new TreeSet();
+    for (int i = 0; i < mp.getNumGeometries(); i++) {
+      Point pt = (Point) mp.getGeometryN(i);
+      Coordinate p = pt.getCoordinate();
+      if (points.contains(p))
+        return false;
+      points.add(p);
+    }
+    return true;
+  }
+
+  private boolean isSimpleLinearGeometry(Geometry geom)
+  {
+    if (geom.isEmpty()) return true;
+    GeometryGraph graph = new GeometryGraph(0, geom);
+    LineIntersector li = new RobustLineIntersector();
+    SegmentIntersector si = graph.computeSelfNodes(li, true);
+    // if no self-intersection, must be simple
+    if (! si.hasIntersection()) return true;
+    if (si.hasProperIntersection()) return false;
+    if (hasNonEndpointIntersection(graph)) return false;
+    if (hasClosedEndpointIntersection(graph)) return false;
+    return true;
+  }
+
+  /**
+   * For all edges, check if there are any intersections which are NOT at an endpoint.
+   * The Geometry is not simple if there are intersections not at endpoints.
+   */
+  private boolean hasNonEndpointIntersection(GeometryGraph graph)
+  {
+    for (Iterator i = graph.getEdgeIterator(); i.hasNext(); ) {
+      Edge e = (Edge) i.next();
+      int maxSegmentIndex = e.getMaximumSegmentIndex();
+      for (Iterator eiIt = e.getEdgeIntersectionList().iterator(); eiIt.hasNext(); ) {
+        EdgeIntersection ei = (EdgeIntersection) eiIt.next();
+        if (! ei.isEndPoint(maxSegmentIndex))
+          return true;
+      }
+    }
+    return false;
+  }
+
+  class EndpointInfo {
+    Coordinate pt;
+    boolean isClosed;
+    int degree;
+
+    EndpointInfo(Coordinate pt)
+    {
+      this.pt = pt;
+      isClosed = false;
+      degree = 0;
+    }
+
+    void addEndpoint(boolean isClosed)
+    {
+      degree++;
+      this.isClosed |= isClosed;
+    }
+  }
+
+  /**
+   * Test that no edge intersection is the
+   * endpoint of a closed line.  To check this we compute the
+   * degree of each endpoint. The degree of endpoints of closed lines
+   * must be exactly 2.
+   */
+  private boolean hasClosedEndpointIntersection(GeometryGraph graph)
+  {
+    Map endPoints = new TreeMap();
+    for (Iterator i = graph.getEdgeIterator(); i.hasNext(); ) {
+      Edge e = (Edge) i.next();
+      int maxSegmentIndex = e.getMaximumSegmentIndex();
+      boolean isClosed = e.isClosed();
+      Coordinate p0 = e.getCoordinate(0);
+      addEndpoint(endPoints, p0, isClosed);
+      Coordinate p1 = e.getCoordinate(e.getNumPoints() - 1);
+      addEndpoint(endPoints, p1, isClosed);
+    }
+
+    for (Iterator i = endPoints.values().iterator(); i.hasNext(); ) {
+      EndpointInfo eiInfo = (EndpointInfo) i.next();
+      if (eiInfo.isClosed && eiInfo.degree != 2)
+        return true;
+    }
+    return false;
+  }
+
+  /**
+   * Add an endpoint to the map, creating an entry for it if none exists
+   */
+  private void addEndpoint(Map endPoints, Coordinate p, boolean isClosed)
+  {
+    EndpointInfo eiInfo = (EndpointInfo) endPoints.get(p);
+    if (eiInfo == null) {
+      eiInfo = new EndpointInfo(p);
+      endPoints.put(p, eiInfo);
+    }
+    eiInfo.addEndpoint(isClosed);
+  }
+
+}

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/buffer/BufferBuilder.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,274 @@
+
+
+/*
+ * 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.buffer;
+
+/**
+ * @version 1.6
+ */
+
+import java.util.*;
+import com.vividsolutions.jts.geom.*;
+import com.vividsolutions.jts.algorithm.*;
+import com.vividsolutions.jts.geomgraph.*;
+import com.vividsolutions.jts.operation.overlay.*;
+import com.vividsolutions.jts.noding.*;
+
+//import debug.*;
+
+/**
+ * Builds the buffer geometry for a given input geometry and precision model.
+ * Allows setting the level of approximation for circular arcs,
+ * and the precision model in which to carry out the computation.
+ * <p>
+ * When computing buffers in floating point double-precision
+ * it can happen that the process of iterated noding can fail to converge (terminate).
+ * In this case a TopologyException will be thrown.
+ * Retrying the computation in a fixed precision
+ * can produce more robust results.
+ *
+ * @version 1.6
+ */
+public class BufferBuilder
+{
+  /**
+   * Compute the change in depth as an edge is crossed from R to L
+   */
+  private static int depthDelta(Label label)
+  {
+    int lLoc = label.getLocation(0, Position.LEFT);
+    int rLoc = label.getLocation(0, Position.RIGHT);
+    if (lLoc == Location.INTERIOR && rLoc == Location.EXTERIOR)
+      return 1;
+    else if (lLoc == Location.EXTERIOR && rLoc == Location.INTERIOR)
+      return -1;
+    return 0;
+  }
+
+  private final CGAlgorithms cga = new RobustCGAlgorithms();
+
+  private int quadrantSegments = OffsetCurveBuilder.DEFAULT_QUADRANT_SEGMENTS;
+  private int endCapStyle = BufferOp.CAP_ROUND;
+
+  private PrecisionModel workingPrecisionModel;
+  private GeometryFactory geomFact;
+  private PlanarGraph graph;
+  private EdgeList edgeList     = new EdgeList();
+
+  /**
+   * Creates a new BufferBuilder
+   */
+  public BufferBuilder()
+  {
+  }
+
+  /**
+   * Sets the number of segments used to approximate a angle fillet
+   *
+   * @param quadrantSegments the number of segments in a fillet for a quadrant
+   */
+  public void setQuadrantSegments(int quadrantSegments)
+  {
+    this.quadrantSegments = quadrantSegments;
+  }
+
+  /**
+   * Sets the precision model to use during the curve computation and noding,
+   * if it is different to the precision model of the Geometry.
+   * If the precision model is less than the precision of the Geometry precision model,
+   * the Geometry must have previously been rounded to that precision.
+   *
+   * @param pm the precision model to use
+   */
+  public void setWorkingPrecisionModel(PrecisionModel pm)
+  {
+    workingPrecisionModel = pm;
+  }
+
+  public void setEndCapStyle(int endCapStyle)
+  {
+    this.endCapStyle = endCapStyle;
+  }
+
+  public Geometry buffer(Geometry g, double distance)
+  {
+    PrecisionModel precisionModel = workingPrecisionModel;
+    if (precisionModel == null)
+      precisionModel = g.getPrecisionModel();
+
+    // factory must be the same as the one used by the input
+    geomFact = g.getFactory();
+
+    OffsetCurveBuilder curveBuilder = new OffsetCurveBuilder(precisionModel, quadrantSegments);
+    curveBuilder.setEndCapStyle(endCapStyle);
+    OffsetCurveSetBuilder curveSetBuilder = new OffsetCurveSetBuilder(g, distance, curveBuilder);
+
+    List bufferSegStrList = curveSetBuilder.getCurves();
+
+    // short-circuit test
+    if (bufferSegStrList.size() <= 0) {
+      Geometry emptyGeom = geomFact.createGeometryCollection(new Geometry[0]);
+      return emptyGeom;
+    }
+
+//BufferDebug.runCount++;
+//String filename = "run" + BufferDebug.runCount + "_curves";
+//System.out.println("saving " + filename);
+//BufferDebug.saveEdges(bufferEdgeList, filename);
+// DEBUGGING ONLY
+//WKTWriter wktWriter = new WKTWriter();
+//Debug.println("Rings: " + wktWriter.write(toLineStrings(bufferEdgeList.iterator())));
+    computeNodedEdges(bufferSegStrList, precisionModel);
+    graph = new PlanarGraph(new OverlayNodeFactory());
+    graph.addEdges(edgeList.getEdges());
+
+    List subgraphList = createSubgraphs(graph);
+    PolygonBuilder polyBuilder = new PolygonBuilder(geomFact, cga);
+    buildSubgraphs(subgraphList, polyBuilder);
+    List resultPolyList = polyBuilder.getPolygons();
+
+    Geometry resultGeom = geomFact.buildGeometry(resultPolyList);
+    return resultGeom;
+  }
+
+  private void computeNodedEdges(List bufferSegStrList, PrecisionModel precisionModel)
+  {
+    //BufferCurveGraphNoder noder = new BufferCurveGraphNoder(geomFact.getPrecisionModel());
+    IteratedNoder noder = new IteratedNoder(precisionModel);
+    Collection nodedSegStrings = noder.node(bufferSegStrList);
+// 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();
+      Edge edge = new Edge(segStr.getCoordinates(), new Label(oldLabel));
+      insertEdge(edge);
+    }
+    //saveEdges(edgeList.getEdges(), "run" + runCount + "_collapsedEdges");
+  }
+
+
+  /**
+   * Inserted edges are checked to see if an identical edge already exists.
+   * If so, the edge is not inserted, but its label is merged
+   * with the existing edge.
+   */
+  protected void insertEdge(Edge 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 (existingEdge != null) {
+      Label existingLabel = existingEdge.getLabel();
+
+      Label labelToMerge = e.getLabel();
+      // check if new edge is in reverse direction to existing edge
+      // if so, must flip the label before merging it
+      if (! existingEdge.isPointwiseEqual(e)) {
+        labelToMerge = new Label(e.getLabel());
+        labelToMerge.flip();
+      }
+      existingLabel.merge(labelToMerge);
+
+      // compute new depth delta of sum of edges
+      int mergeDelta = depthDelta(labelToMerge);
+      int existingDelta = existingEdge.getDepthDelta();
+      int newDelta = existingDelta + mergeDelta;
+      existingEdge.setDepthDelta(newDelta);
+    }
+    else {   // no matching existing edge was found
+      // add this new edge to the list of edges in this graph
+      //e.setName(name + edges.size());
+      edgeList.add(e);
+      e.setDepthDelta(depthDelta(e.getLabel()));
+    }
+  }
+
+  private List createSubgraphs(PlanarGraph graph)
+  {
+    List subgraphList = new ArrayList();
+    for (Iterator i = graph.getNodes().iterator(); i.hasNext(); ) {
+      Node node = (Node) i.next();
+      if (! node.isVisited()) {
+        BufferSubgraph subgraph = new BufferSubgraph(cga);
+        subgraph.create(node);
+        subgraphList.add(subgraph);
+      }
+    }
+    /**
+     * Sort the subgraphs in descending order of their rightmost coordinate.
+     * This ensures that when the Polygons for the subgraphs are built,
+     * subgraphs for shells will have been built before the subgraphs for
+     * any holes they contain.
+     */
+    Collections.sort(subgraphList, Collections.reverseOrder());
+    return subgraphList;
+  }
+
+  /**
+   * Completes the building of the input subgraphs by depth-labelling them,
+   * and adds them to the PolygonBuilder.
+   * The subgraph list must be sorted in rightmost-coordinate order.
+   *
+   * @param subgraphList the subgraphs to build
+   * @param polyBuilder the PolygonBuilder which will build the final polygons
+   */
+  private void buildSubgraphs(List subgraphList, PolygonBuilder polyBuilder)
+  {
+    List processedGraphs = new ArrayList();
+    for (Iterator i = subgraphList.iterator(); i.hasNext(); ) {
+      BufferSubgraph subgraph = (BufferSubgraph) i.next();
+      Coordinate p = subgraph.getRightmostCoordinate();
+//      int outsideDepth = 0;
+//      if (polyBuilder.containsPoint(p))
+//        outsideDepth = 1;
+      SubgraphDepthLocater locater = new SubgraphDepthLocater(processedGraphs);
+      int outsideDepth = locater.getDepth(p);
+//      try {
+      subgraph.computeDepth(outsideDepth);
+//      }
+//      catch (RuntimeException ex) {
+//        // debugging only
+//        //subgraph.saveDirEdges();
+//        throw ex;
+//      }
+      subgraph.findResultEdges();
+      processedGraphs.add(subgraph);
+      polyBuilder.add(subgraph.getDirectedEdges(), subgraph.getNodes());
+    }
+  }
+}

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/buffer/BufferOp.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,278 @@
+/*
+ * 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.buffer;
+
+/**
+ * @version 1.6
+ */
+import com.vividsolutions.jts.geom.*;
+import com.vividsolutions.jts.precision.SimpleGeometryPrecisionReducer;
+
+//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
+ * 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>.
+ * <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.
+ * <p>
+ * The <b>end cap style</b> of a linear buffer may be specified. The
+ * following end cap styles are supported:
+ * <ul
+ * <li>{@link CAP_ROUND} - the usual round end caps
+ * <li>{@link CAP_BUTT} - end caps are truncated flat at the line ends
+ * <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
+ */
+public class BufferOp
+{
+  /**
+   * Specifies a round line buffer end cap style.
+   */
+  public static final int CAP_ROUND = 1;
+  /**
+   * Specifies a butt (or flat) line buffer end cap style.
+   */
+  public static final int CAP_BUTT = 2;
+  /**
+   * Specifies a square line buffer end cap style.
+   */
+  public static final int CAP_SQUARE = 3;
+
+  private static int MAX_PRECISION_DIGITS = 12;
+
+  /**
+   * Compute a reasonable scale factor to limit the precision of
+   * a given combination of Geometry and buffer distance.
+   * The scale factor is based on a heuristic.
+   *
+   * @param g the Geometry being buffered
+   * @param distance the buffer distance
+   * @param maxPrecisionDigits the mzx # 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
+   */
+  private static double precisionScaleFactor(Geometry g,
+      double distance,
+    int maxPrecisionDigits)
+  {
+    Envelope env = g.getEnvelopeInternal();
+    double envSize = Math.max(env.getHeight(), env.getWidth());
+    double expandByDistance = distance > 0.0 ? distance : 0.0;
+    double bufEnvSize = envSize + 2 * expandByDistance;
+
+    // the smallest power of 10 greater than the buffer envelope
+    int bufEnvLog10 = (int) (Math.log(bufEnvSize) / Math.log(10) + 1.0);
+    int minUnitLog10 = bufEnvLog10 - maxPrecisionDigits;
+    // scale factor is inverse of min Unit size, so flip sign of exponent
+    double scaleFactor = Math.pow(10.0, -minUnitLog10);
+    return scaleFactor;
+  }
+
+  /**
+   * Computes the buffer of a geometry for a given buffer distance.
+   *
+   * @param g the geometry to buffer
+   * @param distance the buffer distance
+   * @return the buffer of the input geometry
+   */
+  public static Geometry bufferOp(Geometry g, double distance)
+  {
+    BufferOp gBuf = new BufferOp(g);
+    Geometry geomBuf = gBuf.getResultGeometry(distance);
+//BufferDebug.saveBuffer(geomBuf);
+    //BufferDebug.runCount++;
+    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
+   * @return the buffer of the input geometry
+   *
+   */
+  public static Geometry bufferOp(Geometry g, double distance, int quadrantSegments)
+  {
+    BufferOp bufOp = new BufferOp(g);
+    bufOp.setQuadrantSegments(quadrantSegments);
+    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
+
+  /**
+   * Initializes a buffer computation for the given geometry
+   *
+   * @param g the geometry to buffer
+   */
+  public BufferOp(Geometry g) {
+    argGeom = g;
+  }
+
+  /**
+   * Specifies the end cap style of the generated buffer.
+   * The styles supported are {@link CAP_ROUND}, {@link CAP_BUTT}, and {@link CAP_SQUARE}.
+   * The default is CAP_ROUND.
+   *
+   * @param endCapStyle the end cap style to specify
+   */
+  public void setEndCapStyle(int endCapStyle)
+  {
+    this.endCapStyle = endCapStyle;
+  }
+
+  /**
+   * Specifies the end cap style of the generated buffer.
+   * The styles supported are {@link CAP_ROUND}, {@link CAP_BUTT}, and {@link CAP_SQUARE}.
+   * The default is CAP_ROUND.
+   *
+   * @param endCapStyle the end cap style to specify
+   */
+  public void setQuadrantSegments(int quadrantSegments)
+  {
+    this.quadrantSegments = quadrantSegments;
+  }
+
+  /**
+   * Returns the buffer computed for a geometry for a given buffer distance.
+   *
+   * @param g the geometry to buffer
+   * @param distance the buffer distance
+   * @return the buffer of the input geometry
+   */
+  public Geometry getResultGeometry(double distance)
+  {
+    this.distance = distance;
+    computeGeometry();
+    return resultGeometry;
+  }
+
+  /**
+   * 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
+   * @return the buffer of the input geometry
+   *
+   * @deprecated use setQuadrantSegments instead
+   */
+  public Geometry getResultGeometry(double distance, int quadrantSegments)
+  {
+    this.distance = distance;
+    setQuadrantSegments(quadrantSegments);
+    computeGeometry();
+    return resultGeometry;
+  }
+
+  private void computeGeometry()
+  {
+    bufferOriginalPrecision();
+    if (resultGeometry != null) return;
+
+    // try and compute with decreasing precision
+    for (int precDigits = MAX_PRECISION_DIGITS; precDigits >= 0; precDigits--) {
+      try {
+        bufferFixedPrecision(precDigits);
+      }
+      catch (TopologyException ex) {
+        saveException = ex;
+        // don't propagate the exception - it will be detected by fact that resultGeometry is null
+      }
+      if (resultGeometry != null) return;
+    }
+
+    // tried everything - have to bail
+    throw saveException;
+    //return resultGeometry;
+  }
+
+  private void bufferOriginalPrecision()
+  {
+    try {
+      BufferBuilder bufBuilder = new BufferBuilder();
+      bufBuilder.setQuadrantSegments(quadrantSegments);
+      bufBuilder.setEndCapStyle(endCapStyle);
+      resultGeometry = bufBuilder.buffer(argGeom, distance);
+    }
+    catch (TopologyException ex) {
+      saveException = ex;
+      // don't propagate the exception - it will be detected by fact that resultGeometry is null
+    }
+  }
+
+  private void bufferFixedPrecision(int precisionDigits)
+  {
+    double sizeBasedScaleFactor = precisionScaleFactor(argGeom, distance, precisionDigits);
+
+    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);
+
+//System.out.println("recomputing with precision scale factor = " + sizeBasedScaleFactor);
+
+    BufferBuilder bufBuilder = new BufferBuilder();
+    bufBuilder.setWorkingPrecisionModel(fixedPM);
+    bufBuilder.setQuadrantSegments(quadrantSegments);
+    // this may throw an exception, if robustness errors are encountered
+    resultGeometry = bufBuilder.buffer(reducedGeom, distance);
+  }
+
+}

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/buffer/BufferSubgraph.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,304 @@
+
+
+/*
+ * 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.buffer;
+
+/**
+ * @version 1.6
+ */
+
+import java.util.*;
+import com.vividsolutions.jts.geom.*;
+import com.vividsolutions.jts.algorithm.*;
+import com.vividsolutions.jts.geomgraph.*;
+import com.vividsolutions.jts.util.*;
+//import debug.*;
+
+/**
+ * A connected subset of the graph of
+ * {@link DirectedEdges} and {@link Node}s.
+ * Its edges will generate either
+ * <ul>
+ * <li> a single polygon in the complete buffer, with zero or more holes, or
+ * <li> one or more connected holes
+ * </ul>
+ *
+ *
+ * @version 1.6
+ */
+public class BufferSubgraph
+  implements Comparable
+{
+  private RightmostEdgeFinder finder;
+  private List dirEdgeList  = new ArrayList();
+  private List nodes        = new ArrayList();
+  private Coordinate rightMostCoord = null;
+
+  public BufferSubgraph(CGAlgorithms cga)
+  {
+    finder = new RightmostEdgeFinder(cga);
+  }
+
+  public List getDirectedEdges() { return dirEdgeList; }
+  public List getNodes() { return nodes; }
+
+  /**
+   * Gets the rightmost coordinate in the edges of the subgraph
+   */
+  public Coordinate getRightmostCoordinate()
+  {
+    return rightMostCoord;
+  }
+
+  /**
+   * Creates the subgraph consisting of all edges reachable from this node.
+   * Finds the edges in the graph and the rightmost coordinate.
+   *
+   * @param node a node to start the graph traversal from
+   */
+  public void create(Node node)
+  {
+    addReachable(node);
+    finder.findEdge(dirEdgeList);
+    rightMostCoord = finder.getCoordinate();
+  }
+
+  /**
+   * 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)
+  {
+    Stack nodeStack = new Stack();
+    nodeStack.add(startNode);
+    while (! nodeStack.empty()) {
+      Node node = (Node) nodeStack.pop();
+      add(node, nodeStack);
+    }
+  }
+
+  /**
+   * 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 add(Node node, Stack nodeStack)
+  {
+    node.setVisited(true);
+    nodes.add(node);
+    for (Iterator i = ((DirectedEdgeStar) node.getEdges()).iterator(); i.hasNext(); ) {
+      DirectedEdge de = (DirectedEdge) i.next();
+      dirEdgeList.add(de);
+      DirectedEdge sym = de.getSym();
+      Node symNode = sym.getNode();
+      /**
+       * NOTE: this is a depth-first traversal of the graph.
+       * This will cause a large depth of recursion.
+       * It might be better to do a breadth-first traversal.
+       */
+      if (! symNode.isVisited()) nodeStack.push(symNode);
+    }
+  }
+
+  private void clearVisitedEdges()
+  {
+    for (Iterator it = dirEdgeList.iterator(); it.hasNext(); ) {
+      DirectedEdge de = (DirectedEdge) it.next();
+      de.setVisited(false);
+    }
+  }
+
+  public void computeDepth(int outsideDepth)
+  {
+    clearVisitedEdges();
+    // find an outside edge to assign depth to
+    DirectedEdge de = finder.getEdge();
+    Node n = de.getNode();
+    Label label = de.getLabel();
+    // right side of line returned by finder is on the outside
+    de.setEdgeDepths(Position.RIGHT, outsideDepth);
+    copySymDepths(de);
+
+    //computeNodeDepth(n, de);
+    computeDepths(de);
+  }
+
+  /**
+   * Compute depths for all dirEdges via breadth-first traversal of nodes in graph
+   * @param startEdge edge to start processing with
+   */
+  // <FIX> MD - use iteration & queue rather than recursion, for speed and robustness
+  private void computeDepths(DirectedEdge startEdge)
+  {
+    Set nodesVisited = new HashSet();
+    LinkedList nodeQueue = new LinkedList();
+
+    Node startNode = startEdge.getNode();
+    nodeQueue.addLast(startNode);
+    nodesVisited.add(startNode);
+    startEdge.setVisited(true);
+
+    while (! nodeQueue.isEmpty()) {
+//System.out.println(nodes.size() + " queue: " + nodeQueue.size());
+      Node n = (Node) nodeQueue.removeFirst();
+      nodesVisited.add(n);
+      // compute depths around node, starting at this edge since it has depths assigned
+      computeNodeDepth(n);
+
+      // add all adjacent nodes to process queue,
+      // unless the node has been visited already
+      for (Iterator i = ((DirectedEdgeStar) n.getEdges()).iterator(); i.hasNext(); ) {
+        DirectedEdge de = (DirectedEdge) i.next();
+        DirectedEdge sym = de.getSym();
+        if (sym.isVisited()) continue;
+        Node adjNode = sym.getNode();
+        if (! (nodesVisited.contains(adjNode)) ) {
+          nodeQueue.addLast(adjNode);
+          nodesVisited.add(adjNode);
+        }
+      }
+    }
+  }
+
+  private void computeNodeDepth(Node n)
+  {
+    // find a visited dirEdge to start at
+    DirectedEdge startEdge = null;
+    for (Iterator i = ((DirectedEdgeStar) n.getEdges()).iterator(); i.hasNext(); ) {
+      DirectedEdge de = (DirectedEdge) i.next();
+      if (de.isVisited() || de.getSym().isVisited()) {
+        startEdge = de;
+        break;
+      }
+    }
+    // MD - testing  Result: breaks algorithm
+    //if (startEdge == null) return;
+    Assert.isTrue(startEdge != null, "unable to find edge to compute depths at " + n.getCoordinate());
+
+    ((DirectedEdgeStar) n.getEdges()).computeDepths(startEdge);
+
+    // copy depths to sym edges
+    for (Iterator i = ((DirectedEdgeStar) n.getEdges()).iterator(); i.hasNext(); ) {
+      DirectedEdge de = (DirectedEdge) i.next();
+      de.setVisited(true);
+      copySymDepths(de);
+    }
+  }
+
+  private void copySymDepths(DirectedEdge de)
+  {
+    DirectedEdge sym = de.getSym();
+    sym.setDepth(Position.LEFT, de.getDepth(Position.RIGHT));
+    sym.setDepth(Position.RIGHT, de.getDepth(Position.LEFT));
+  }
+
+  /**
+   * Find all edges whose depths indicates that they are in the result area(s).
+   * Since we want polygon shells to be
+   * oriented CW, choose dirEdges with the interior of the result on the RHS.
+   * Mark them as being in the result.
+   * Interior Area edges are the result of dimensional collapses.
+   * They do not form part of the result area boundary.
+   */
+  public void findResultEdges()
+  {
+    for (Iterator it = dirEdgeList.iterator(); it.hasNext(); ) {
+      DirectedEdge de = (DirectedEdge) it.next();
+      /**
+       * Select edges which have an interior depth on the RHS
+       * and an exterior depth on the LHS.
+       * Note that because of weird rounding effects there may be
+       * edges which have negative depths!  Negative depths
+       * count as "outside".
+       */
+      // <FIX> - handle negative depths
+      if (    de.getDepth(Position.RIGHT) >= 1
+          &&  de.getDepth(Position.LEFT)  <= 0
+          &&  ! de.isInteriorAreaEdge()) {
+        de.setInResult(true);
+//Debug.print("in result "); Debug.println(de);
+      }
+    }
+  }
+
+  /**
+   * BufferSubgraphs are compared on the x-value of their rightmost Coordinate.
+   * This defines a partial ordering on the graphs such that:
+   * <p>
+   * g1 >= g2 <==> Ring(g2) does not contain Ring(g1)
+   * <p>
+   * where Polygon(g) is the buffer polygon that is built from g.
+   * <p>
+   * This relationship is used to sort the BufferSubgraphs so that shells are guaranteed to
+   * be built before holes.
+   */
+  public int compareTo(Object o) {
+    BufferSubgraph graph = (BufferSubgraph) o;
+    if (this.rightMostCoord.x < graph.rightMostCoord.x) {
+      return -1;
+    }
+    if (this.rightMostCoord.x > graph.rightMostCoord.x) {
+      return 1;
+    }
+    return 0;
+  }
+
+/*
+// DEBUGGING only - comment out
+  private static final String SAVE_DIREDGES = "saveDirEdges";
+  private static int saveCount = 0;
+  public void saveDirEdges()
+  {
+    GeometryFactory fact = new GeometryFactory();
+    for (Iterator it = dirEdgeList.iterator(); it.hasNext(); ) {
+      DirectedEdge de = (DirectedEdge) it.next();
+      double dx = de.getDx();
+      double dy = de.getDy();
+      Coordinate p0 = de.getCoordinate();
+      double ang = Math.atan2(dy, dx);
+      Coordinate p1 = new Coordinate(
+          p0.x + .4 * Math.cos(ang),
+          p0.y + .4 * Math.sin(ang));
+//      DebugFeature.add(SAVE_DIREDGES,
+//                       fact.createLineString(new Coordinate[] { p0, p1 } ),
+//                       de.getDepth(Position.LEFT) + "/" + de.getDepth(Position.RIGHT)
+//                       );
+    }
+  String filepath = "x:\\jts\\testBuffer\\dirEdges" + saveCount++ + ".jml";
+    DebugFeature.saveFeatures(SAVE_DIREDGES, filepath);
+  }
+  */
+}

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/buffer/OffsetCurveBuilder.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,507 @@
+/*
+ * 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.buffer;
+
+import java.util.*;
+import com.vividsolutions.jts.geom.*;
+import com.vividsolutions.jts.algorithm.*;
+import com.vividsolutions.jts.geomgraph.*;
+
+/**
+ * Computes the raw offset curve for a
+ * single {@link Geometry} component (ring, line or point).
+ * A raw offset curve line is not noded -
+ * it may contain self-intersections (and usually will).
+ * The final buffer polygon is computed by forming a topological graph
+ * 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
+ */
+public class OffsetCurveBuilder {
+
+  private static double PI_OVER_2 = Math.PI / 2.0;
+
+  /**
+   * The default number of facets into which to divide a fillet of 90 degrees.
+   * A value of 8 gives less than 2% max error in the buffer distance.
+   * For a max error of < 1%, use QS = 12
+   */
+  public static final int DEFAULT_QUADRANT_SEGMENTS = 8;
+
+  private static final Coordinate[] arrayTypeCoordinate = new Coordinate[0];
+  private CGAlgorithms cga = new RobustCGAlgorithms();
+  private LineIntersector li;
+
+  /**
+   * The angle quantum with which to approximate a fillet curve
+   * (based on the input # of quadrant segments)
+   */
+  private double filletAngleQuantum;
+  /**
+   * the max error of approximation between a quad segment and the true fillet curve
+   */
+  private double maxCurveSegmentError = 0.0;
+  private ArrayList ptList;
+  private double distance = 0.0;
+  private PrecisionModel precisionModel;
+  private int endCapStyle = BufferOp.CAP_ROUND;
+  private int joinStyle;
+
+  public OffsetCurveBuilder(
+                PrecisionModel precisionModel)
+  {
+    this(precisionModel, DEFAULT_QUADRANT_SEGMENTS);
+  }
+
+  public OffsetCurveBuilder(
+                PrecisionModel precisionModel,
+                int quadrantSegments)
+  {
+    this.precisionModel = precisionModel;
+    // compute intersections in full precision, to provide accuracy
+    // the points are rounded as they are inserted into the curve line
+    li = new RobustLineIntersector();
+
+    int limitedQuadSegs = quadrantSegments < 1 ? 1 : quadrantSegments;
+    filletAngleQuantum = Math.PI / 2.0 / limitedQuadSegs;
+  }
+
+  public void setEndCapStyle(int endCapStyle)
+  {
+    this.endCapStyle = endCapStyle;
+  }
+  /**
+   * This method handles single points as well as lines.
+   * Lines are assumed to <b>not</b> be closed (the function will not
+   * fail for closed lines, but will generate superfluous line caps).
+   *
+   * @return a List of Coordinate[]
+   */
+  public List getLineCurve(Coordinate[] inputPts, double distance)
+  {
+    List lineList = new ArrayList();
+    // a zero or negative width buffer of a line/point is empty
+    if (distance <= 0.0) return lineList;
+
+    init(distance);
+    if (inputPts.length <= 1) {
+      switch (endCapStyle) {
+        case BufferOp.CAP_ROUND:
+          addCircle(inputPts[0], distance);
+          break;
+        case BufferOp.CAP_SQUARE:
+          addSquare(inputPts[0], distance);
+          break;
+          // default is for buffer to be empty (e.g. for a butt line cap);
+      }
+    }
+    else
+      computeLineBufferCurve(inputPts);
+    Coordinate[] lineCoord = getCoordinates();
+    lineList.add(lineCoord);
+    return lineList;
+  }
+
+  /**
+   * This method handles the degenerate cases of single points and lines,
+   * as well as rings.
+   *
+   * @return a List of Coordinate[]
+   */
+  public List getRingCurve(Coordinate[] inputPts, int side, double distance)
+  {
+    List lineList = new ArrayList();
+    init(distance);
+    if (inputPts.length <= 2)
+      return getLineCurve(inputPts, distance);
+
+    // optimize creating ring for for zero distance
+    if (distance == 0.0) {
+      lineList.add(copyCoordinates(inputPts));
+      return lineList;
+    }
+    computeRingBufferCurve(inputPts, side);
+    lineList.add(getCoordinates());
+    return lineList;
+  }
+
+  private static Coordinate[] copyCoordinates(Coordinate[] pts)
+  {
+    Coordinate[] copy = new Coordinate[pts.length];
+    for (int i = 0; i < copy.length; i++) {
+      copy[i] = new Coordinate(pts[i]);
+    }
+    return copy;
+  }
+  private void init(double distance)
+  {
+    this.distance = distance;
+    maxCurveSegmentError = distance * (1 - Math.cos(filletAngleQuantum / 2.0));
+    ptList = new ArrayList();
+  }
+  private Coordinate[] getCoordinates()
+  {
+    // check that points are a ring - add the startpoint again if they are not
+    if (ptList.size() > 1) {
+      Coordinate start  = (Coordinate) ptList.get(0);
+      Coordinate end    = (Coordinate) ptList.get(1);
+      if (! start.equals(end) ) addPt(start);
+    }
+
+    Coordinate[] coord = (Coordinate[]) ptList.toArray(arrayTypeCoordinate);
+    return coord;
+  }
+
+  private void computeLineBufferCurve(Coordinate[] inputPts)
+  {
+    int n = inputPts.length - 1;
+
+    // compute points for left side of line
+    initSideSegments(inputPts[0], inputPts[1], Position.LEFT);
+    for (int i = 2; i <= n; i++) {
+      addNextSegment(inputPts[i], true);
+    }
+    addLastSegment();
+    // add line cap for end of line
+    addLineEndCap(inputPts[n - 1], inputPts[n]);
+
+    // compute points for right side of line
+    initSideSegments(inputPts[n], inputPts[n - 1], Position.LEFT);
+    for (int i = n - 2; i >= 0; i--) {
+      addNextSegment(inputPts[i], true);
+    }
+    addLastSegment();
+    // add line cap for start of line
+    addLineEndCap(inputPts[1], inputPts[0]);
+
+    closePts();
+  }
+
+  private void computeRingBufferCurve(Coordinate[] inputPts, int side)
+  {
+    int n = inputPts.length - 1;
+    initSideSegments(inputPts[n - 1], inputPts[0], side);
+    for (int i = 1; i <= n; i++) {
+      boolean addStartPoint = i != 1;
+      addNextSegment(inputPts[i], addStartPoint);
+    }
+    closePts();
+  }
+
+  private void addPt(Coordinate pt)
+  {
+    Coordinate bufPt = new Coordinate(pt);
+    precisionModel.makePrecise(bufPt);
+    // don't add duplicate points
+    Coordinate lastPt = null;
+    if (ptList.size() >= 1)
+      lastPt = (Coordinate) ptList.get(ptList.size() - 1);
+    if (lastPt != null && bufPt.equals(lastPt)) return;
+
+    ptList.add(bufPt);
+//System.out.println(bufPt);
+  }
+  private void closePts()
+  {
+    if (ptList.size() < 1) return;
+    Coordinate startPt = new Coordinate((Coordinate) ptList.get(0));
+    Coordinate lastPt = (Coordinate) ptList.get(ptList.size() - 1);
+    Coordinate last2Pt = null;
+    if (ptList.size() >= 2)
+      last2Pt = (Coordinate) ptList.get(ptList.size() - 2);
+    if (startPt.equals(lastPt)) return;
+    ptList.add(startPt);
+  }
+
+  private Coordinate s0, s1, s2;
+  private LineSegment seg0 = new LineSegment();
+  private LineSegment seg1 = new LineSegment();
+  private LineSegment offset0 = new LineSegment();
+  private LineSegment offset1 = new LineSegment();
+  private int side = 0;
+
+  private void initSideSegments(Coordinate s1, Coordinate s2, int side)
+  {
+    this.s1 = s1;
+    this.s2 = s2;
+    this.side = side;
+    seg1.setCoordinates(s1, s2);
+    computeOffsetSegment(seg1, side, distance, offset1);
+  }
+
+  private static double MAX_CLOSING_SEG_LEN = 3.0;
+
+  private void addNextSegment(Coordinate p, boolean addStartPoint)
+  {
+    // s0-s1-s2 are the coordinates of the previous segment and the current one
+    s0 = s1;
+    s1 = s2;
+    s2 = p;
+    seg0.setCoordinates(s0, s1);
+    computeOffsetSegment(seg0, side, distance, offset0);
+    seg1.setCoordinates(s1, s2);
+    computeOffsetSegment(seg1, side, distance, offset1);
+
+    // do nothing if points are equal
+    if (s1.equals(s2)) return;
+
+    int orientation = cga.computeOrientation(s0, s1, s2);
+    boolean outsideTurn =
+          (orientation == CGAlgorithms.CLOCKWISE        && side == Position.LEFT)
+      ||  (orientation == CGAlgorithms.COUNTERCLOCKWISE && side == Position.RIGHT);
+
+    if (orientation == 0) { // lines are collinear
+      li.computeIntersection( s0, s1,
+                              s1, s2  );
+      int numInt = li.getIntersectionNum();
+      /**
+       * if numInt is < 2, the lines are parallel and in the same direction.
+       * In this case the point can be ignored, since the offset lines will also be
+       * parallel.
+       */
+      if (numInt >= 2) {
+      /**
+       * segments are collinear but reversing.  Have to add an "end-cap" fillet
+       * all the way around to other direction
+       * This case should ONLY happen for LineStrings, so the orientation is always CW.
+       * (Polygons can never have two consecutive segments which are parallel but reversed,
+       * because that would be a self intersection.
+       */
+        addFillet(s1, offset0.p1, offset1.p0, CGAlgorithms.CLOCKWISE, distance);
+      }
+    }
+    else if (outsideTurn) {
+        // add a fillet to connect the endpoints of the offset segments
+        if (addStartPoint) addPt(offset0.p1);
+        // TESTING - comment out to produce beveled joins
+        addFillet(s1, offset0.p1, offset1.p0, orientation, distance);
+        addPt(offset1.p0);
+    }
+    else { // inside turn
+      /**
+       * add intersection point of offset segments (if any)
+       */
+        li.computeIntersection( offset0.p0, offset0.p1,
+                              offset1.p0, offset1.p1  );
+        if (li.hasIntersection()) {
+          addPt(li.getIntersection(0));
+        }
+        else {
+      /**
+       * If no intersection, it means the angle is so small and/or the offset so large
+       * that the offsets segments don't intersect.
+       * In this case we must add a offset joining curve to make sure the buffer line
+       * is continuous and tracks the buffer correctly around the corner.
+       * Note that the joining curve won't appear in the final buffer.
+       *
+       * The intersection test above is vulnerable to robustness errors;
+       * i.e. it may be that the offsets should intersect very close to their
+       * endpoints, but don't due to rounding.  To handle this situation
+       * appropriately, we use the following test:
+       * If the offset points are very close, don't add a joining curve
+       * but simply use one of the offset points
+       */
+            if (offset0.p1.distance(offset1.p0) < distance / 1000.0) {
+              addPt(offset0.p1);
+            }
+            else {
+              // add endpoint of this segment offset
+              addPt(offset0.p1);
+// <FIX> MD - add in centre point of corner, to make sure offset closer lines have correct topology
+              addPt(s1);
+              addPt(offset1.p0);
+            }
+        }
+    }
+  }
+  /**
+   * Add last offset point
+   */
+  private void addLastSegment()
+  {
+    addPt(offset1.p1);
+  }
+
+  /**
+   * Compute an offset segment for an input segment on a given side and at a given distance.
+   * 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 distance the offset distance
+   * @param offset the points computed for the offset segment
+   */
+  private void computeOffsetSegment(LineSegment seg, int side, double distance, LineSegment offset)
+  {
+    int sideSign = side == Position.LEFT ? 1 : -1;
+    double dx = seg.p1.x - seg.p0.x;
+    double dy = seg.p1.y - seg.p0.y;
+    double len = Math.sqrt(dx * dx + dy * dy);
+    // u is the vector that is the length of the offset, in the direction of the segment
+    double ux = sideSign * distance * dx / len;
+    double uy = sideSign * distance * dy / len;
+    offset.p0.x = seg.p0.x - uy;
+    offset.p0.y = seg.p0.y + ux;
+    offset.p1.x = seg.p1.x - uy;
+    offset.p1.y = seg.p1.y + ux;
+  }
+
+  /**
+   * Add an end cap around point p1, terminating a line segment coming from p0
+   */
+  private void addLineEndCap(Coordinate p0, Coordinate p1)
+  {
+    LineSegment seg = new LineSegment(p0, p1);
+
+    LineSegment offsetL = new LineSegment();
+    computeOffsetSegment(seg, Position.LEFT, distance, offsetL);
+    LineSegment offsetR = new LineSegment();
+    computeOffsetSegment(seg, Position.RIGHT, distance, offsetR);
+
+    double dx = p1.x - p0.x;
+    double dy = p1.y - p0.y;
+    double angle = Math.atan2(dy, dx);
+
+    switch (endCapStyle) {
+      case BufferOp.CAP_ROUND:
+        // add offset seg points with a fillet between them
+        addPt(offsetL.p1);
+        addFillet(p1, angle + Math.PI / 2, angle - Math.PI / 2, CGAlgorithms.CLOCKWISE, distance);
+        addPt(offsetR.p1);
+        break;
+      case BufferOp.CAP_BUTT:
+        // only offset segment points are added
+        addPt(offsetL.p1);
+        addPt(offsetR.p1);
+        break;
+      case BufferOp.CAP_SQUARE:
+        // add a square defined by extensions of the offset segment endpoints
+        Coordinate squareCapSideOffset = new Coordinate();
+        squareCapSideOffset.x = Math.abs(distance) * Math.cos(angle);
+        squareCapSideOffset.y = Math.abs(distance) * Math.sin(angle);
+
+        Coordinate squareCapLOffset = new Coordinate(
+            offsetL.p1.x + squareCapSideOffset.x,
+            offsetL.p1.y + squareCapSideOffset.y);
+        Coordinate squareCapROffset = new Coordinate(
+            offsetR.p1.x + squareCapSideOffset.x,
+            offsetR.p1.y + squareCapSideOffset.y);
+        addPt(squareCapLOffset);
+        addPt(squareCapROffset);
+        break;
+
+    }
+  }
+  /**
+   * @param p base point of curve
+   * @param p0 start point of fillet curve
+   * @param p1 endpoint of fillet curve
+   */
+  private void addFillet(Coordinate p, Coordinate p0, Coordinate p1, int direction, double distance)
+  {
+    double dx0 = p0.x - p.x;
+    double dy0 = p0.y - p.y;
+    double startAngle = Math.atan2(dy0, dx0);
+    double dx1 = p1.x - p.x;
+    double dy1 = p1.y - p.y;
+    double endAngle = Math.atan2(dy1, dx1);
+
+    if (direction == CGAlgorithms.CLOCKWISE) {
+      if (startAngle <= endAngle) startAngle += 2.0 * Math.PI;
+    }
+    else {    // direction == COUNTERCLOCKWISE
+      if (startAngle >= endAngle) startAngle -= 2.0 * Math.PI;
+    }
+    addPt(p0);
+    addFillet(p, startAngle, endAngle, direction, distance);
+    addPt(p1);
+  }
+
+  /**
+   * Adds points for a fillet.  The start and end point for the fillet are not added -
+   * the caller must add them if required.
+   *
+   * @param direction is -1 for a CW angle, 1 for a CCW angle
+   */
+  private void addFillet(Coordinate p, double startAngle, double endAngle, int direction, double distance)
+  {
+    int directionFactor = direction == CGAlgorithms.CLOCKWISE ? -1 : 1;
+
+    double totalAngle = Math.abs(startAngle - endAngle);
+    int nSegs = (int) (totalAngle / filletAngleQuantum + 0.5);
+
+    if (nSegs < 1) return;    // no segments because angle is less than increment - nothing to do!
+
+    double initAngle, currAngleInc;
+
+    // choose angle increment so that each segment has equal length
+    initAngle = 0.0;
+    currAngleInc = totalAngle / nSegs;
+
+    double currAngle = initAngle;
+    Coordinate pt = new Coordinate();
+    while (currAngle < totalAngle) {
+      double angle = startAngle + directionFactor * currAngle;
+      pt.x = p.x + distance * Math.cos(angle);
+      pt.y = p.y + distance * Math.sin(angle);
+      addPt(pt);
+      currAngle += currAngleInc;
+    }
+  }
+
+
+  /**
+   * Adds a CW circle around a point
+   */
+  private void addCircle(Coordinate p, double distance)
+  {
+    // add start point
+    Coordinate pt = new Coordinate(p.x + distance, p.y);
+    addPt(pt);
+    addFillet(p, 0.0, 2.0 * Math.PI, -1, distance);
+  }
+
+  /**
+   * Adds a CW square around a point
+   */
+  private void addSquare(Coordinate p, double distance)
+  {
+    // add start point
+    addPt(new Coordinate(p.x + distance, p.y + distance));
+    addPt(new Coordinate(p.x + distance, p.y - distance));
+    addPt(new Coordinate(p.x - distance, p.y - distance));
+    addPt(new Coordinate(p.x - distance, p.y + distance));
+    addPt(new Coordinate(p.x + distance, p.y + distance));
+  }
+}

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/buffer/OffsetCurveSetBuilder.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,292 @@
+
+
+
+/*
+ * 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.buffer;
+
+/**
+ * @version 1.6
+ */
+import java.util.*;
+import com.vividsolutions.jts.geom.*;
+import com.vividsolutions.jts.algorithm.*;
+import com.vividsolutions.jts.geomgraph.*;
+import com.vividsolutions.jts.noding.SegmentString;
+
+/**
+ * Creates all the raw offset curves for a buffer of a {@link Geometry}.
+ * Raw curves need to be noded together and polygonized to form the final buffer area.
+ *
+ * @version 1.6
+ */
+public class OffsetCurveSetBuilder {
+
+  private CGAlgorithms cga = new RobustCGAlgorithms();
+
+  private Geometry inputGeom;
+  private double distance;
+  private OffsetCurveBuilder curveBuilder;
+
+  private List curveList = new ArrayList();
+
+  public OffsetCurveSetBuilder(
+      Geometry inputGeom,
+          double distance,
+          OffsetCurveBuilder curveBuilder)
+  {
+    this.inputGeom = inputGeom;
+    this.distance = distance;
+    this.curveBuilder = curveBuilder;
+  }
+
+  /**
+   * Computes the set of raw offset curves for the buffer.
+   * Each offset curve has an attached {@link Label} indicating
+   * its left and right location.
+   *
+   * @return a Collection of SegmentStrings representing the raw buffer curves
+   */
+  public List getCurves()
+  {
+    add(inputGeom);
+    return curveList;
+  }
+
+  private void addCurves(List lineList, int leftLoc, int rightLoc)
+  {
+    for (Iterator i = lineList.iterator(); i.hasNext(); ) {
+      Coordinate[] coords = (Coordinate[]) i.next();
+      addCurve(coords, leftLoc, rightLoc);
+    }
+  }
+
+  /**
+   * Creates a {@link SegmentString} for a coordinate list which is a raw offset curve,
+   * and adds it to the list of buffer curves.
+   * The SegmentString is tagged with a Label giving the topology of the curve.
+   * The curve may be oriented in either direction.
+   * If the curve is oriented CW, the locations will be:
+   * <br>Left: Location.EXTERIOR
+   * <br>Right: Location.INTERIOR
+   */
+  private void addCurve(Coordinate[] coord, int leftLoc, int rightLoc)
+  {
+    // don't add null curves!
+    if (coord.length < 2) return;
+    // add the edge for a coordinate list which is a raw offset curve
+    SegmentString e = new SegmentString(coord,
+                        new Label(0, Location.BOUNDARY, leftLoc, rightLoc));
+    curveList.add(e);
+  }
+
+
+  private void add(Geometry g)
+  {
+    if (g.isEmpty()) return;
+
+    if (g instanceof Polygon)                 addPolygon((Polygon) g);
+                        // LineString also handles LinearRings
+    else if (g instanceof LineString)         addLineString((LineString) g);
+    else if (g instanceof Point)              addPoint((Point) g);
+    else if (g instanceof MultiPoint)         addCollection((MultiPoint) g);
+    else if (g instanceof MultiLineString)    addCollection((MultiLineString) g);
+    else if (g instanceof MultiPolygon)       addCollection((MultiPolygon) g);
+    else if (g instanceof GeometryCollection) addCollection((GeometryCollection) g);
+    else  throw new UnsupportedOperationException(g.getClass().getName());
+  }
+  private void addCollection(GeometryCollection gc)
+  {
+    for (int i = 0; i < gc.getNumGeometries(); i++) {
+      Geometry g = gc.getGeometryN(i);
+      add(g);
+    }
+  }
+  /**
+   * Add a Point to the graph.
+   */
+  private void addPoint(Point p)
+  {
+    if (distance <= 0.0) return;
+    Coordinate[] coord = p.getCoordinates();
+    List lineList = curveBuilder.getLineCurve(coord, distance);
+    addCurves(lineList, Location.EXTERIOR, Location.INTERIOR);
+  }
+  private void addLineString(LineString line)
+  {
+    if (distance <= 0.0) return;
+    Coordinate[] coord = CoordinateArrays.removeRepeatedPoints(line.getCoordinates());
+    List lineList = curveBuilder.getLineCurve(coord, distance);
+    addCurves(lineList, Location.EXTERIOR, Location.INTERIOR);
+  }
+
+  private void addPolygon(Polygon p)
+  {
+    double offsetDistance = distance;
+    int offsetSide = Position.LEFT;
+    if (distance < 0.0) {
+      offsetDistance = -distance;
+      offsetSide = Position.RIGHT;
+    }
+
+    LinearRing shell = (LinearRing) p.getExteriorRing();
+    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))
+        return;
+
+    addPolygonRing(
+            shellCoord,
+            offsetDistance,
+            offsetSide,
+            Location.EXTERIOR,
+            Location.INTERIOR);
+
+    for (int i = 0; i < p.getNumInteriorRing(); i++) {
+
+      LinearRing hole = (LinearRing) p.getInteriorRingN(i);
+      Coordinate[] holeCoord = CoordinateArrays.removeRepeatedPoints(hole.getCoordinates());
+
+      // optimization - don't bother computing buffer for this hole
+      // if the hole would be completely covered
+      if (distance > 0.0 && isErodedCompletely(holeCoord, -distance))
+          continue;
+
+      // Holes are topologically labelled opposite to the shell, since
+      // the interior of the polygon lies on their opposite side
+      // (on the left, if the hole is oriented CCW)
+      addPolygonRing(
+            holeCoord,
+            offsetDistance,
+            Position.opposite(offsetSide),
+            Location.INTERIOR,
+            Location.EXTERIOR);
+    }
+  }
+  /**
+   * Add an offset curve for a ring.
+   * The side and left and right topological location arguments
+   * assume that the ring is oriented CW.
+   * If the ring is in the opposite orientation,
+   * the left and right locations must be interchanged and the side flipped.
+   *
+   * @param coord the coordinates of the ring (must not contain repeated points)
+   * @param offsetDistance the distance at which to create the buffer
+   * @param side the side of the ring on which to construct the buffer line
+   * @param cwLeftLoc the location on the L side of the ring (if it is CW)
+   * @param cwRightLoc the location on the R side of the ring (if it is CW)
+   */
+  private void addPolygonRing(Coordinate[] coord, double offsetDistance, int side, int cwLeftLoc, int cwRightLoc)
+  {
+    //Coordinate[] coord = CoordinateArrays.removeRepeatedPoints(lr.getCoordinates());
+    int leftLoc  = cwLeftLoc;
+    int rightLoc = cwRightLoc;
+    if (cga.isCCW(coord)) {
+      leftLoc = cwRightLoc;
+      rightLoc = cwLeftLoc;
+      side = Position.opposite(side);
+    }
+    List lineList = curveBuilder.getRingCurve(coord, side, offsetDistance);
+    addCurves(lineList, leftLoc, rightLoc);
+  }
+
+  /**
+   * The ringCoord is assumed to contain no repeated points.
+   * It may be degenerate (i.e. contain only 1, 2, or 3 points).
+   * In this case it has no area, and hence has a minimum diameter of 0.
+   *
+   * @param ringCoord
+   * @param offsetDistance
+   * @return
+   */
+  private boolean isErodedCompletely(Coordinate[] ringCoord, double bufferDistance)
+  {
+    double minDiam = 0.0;
+    // degenerate ring has no area
+    if (ringCoord.length < 4)
+      return bufferDistance < 0;
+
+    // important test to eliminate inverted triangle bug
+    // also optimizes erosion test for triangles
+    if (ringCoord.length == 4)
+      return isTriangleErodedCompletely(ringCoord, bufferDistance);
+
+    /**
+     * The following is a heuristic test to determine whether an
+     * inside buffer will be eroded completely.
+     * It is based on the fact that the minimum diameter of the ring pointset
+     * provides an upper bound on the buffer distance which would erode the
+     * ring.
+     * If the buffer distance is less than the minimum diameter, the ring
+     * may still be eroded, but this will be determined by
+     * a full topological computation.
+     *
+     */
+    LinearRing ring = inputGeom.getFactory().createLinearRing(ringCoord);
+    MinimumDiameter md = new MinimumDiameter(ring);
+    minDiam = md.getLength();
+    //System.out.println(md.getDiameter());
+    return minDiam < 2 * Math.abs(bufferDistance);
+  }
+
+  /**
+   * Tests whether a triangular ring would be eroded completely by the given
+   * buffer distance.
+   * This is a precise test.  It uses the fact that the inner buffer of a
+   * triangle converges on the inCentre of the triangle (the point
+   * equidistant from all sides).  If the buffer distance is greater than the
+   * distance of the inCentre from a side, the triangle will be eroded completely.
+   *
+   * This test is important, since it removes a problematic case where
+   * the buffer distance is slightly larger than the inCentre distance.
+   * In this case the triangle buffer curve "inverts" with incorrect topology,
+   * producing an incorrect hole in the buffer.
+   *
+   * @param triangleCoord
+   * @param bufferDistance
+   * @return
+   */
+  private boolean isTriangleErodedCompletely(
+      Coordinate[] triangleCoord,
+      double bufferDistance)
+  {
+    Triangle tri = new Triangle(triangleCoord[0], triangleCoord[1], triangleCoord[2]);
+    Coordinate inCentre = tri.inCentre();
+    double distToCentre = cga.distancePointLine(inCentre, tri.p0, tri.p1);
+    return distToCentre < Math.abs(bufferDistance);
+  }
+
+
+
+}

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/buffer/RightmostEdgeFinder.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,189 @@
+
+
+/*
+ * 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.buffer;
+
+/**
+ * @version 1.6
+ */
+import java.util.*;
+import com.vividsolutions.jts.geom.*;
+import com.vividsolutions.jts.algorithm.*;
+import com.vividsolutions.jts.geomgraph.*;
+import com.vividsolutions.jts.operation.overlay.*;
+import com.vividsolutions.jts.util.*;
+
+/**
+ * A RightmostEdgeFinder find the DirectedEdge in a list which has the highest coordinate,
+ * and which is oriented L to R at that point. (I.e. the right side is on the RHS of the edge.)
+ *
+ * @version 1.6
+ */
+public class RightmostEdgeFinder {
+
+  private CGAlgorithms cga;
+
+  //private Coordinate extremeCoord;
+  private int minIndex = -1;
+  private Coordinate minCoord = null;
+  private DirectedEdge minDe = null;
+  private DirectedEdge orientedDe = null;
+  /**
+   * A RightmostEdgeFinder finds the DirectedEdge with the rightmost coordinate.
+   * The DirectedEdge returned is guaranteed to have the R of the world on its RHS.
+   */
+  public RightmostEdgeFinder(CGAlgorithms cga)
+  {
+    this.cga = cga;
+  }
+  public DirectedEdge getEdge()  {    return orientedDe;  }
+  public Coordinate getCoordinate()  {    return minCoord;  }
+
+  public void findEdge(List dirEdgeList)
+  {
+    /**
+     * Check all forward DirectedEdges only.  This is still general,
+     * because each edge has a forward DirectedEdge.
+     */
+    for (Iterator i = dirEdgeList.iterator(); i.hasNext();) {
+      DirectedEdge de = (DirectedEdge) i.next();
+      if (! de.isForward())
+        continue;
+      checkForRightmostCoordinate(de);
+    }
+
+    /**
+     * If the rightmost point is a node, we need to identify which of
+     * the incident edges is rightmost.
+     */
+    Assert.isTrue(minIndex != 0 || minCoord.equals(minDe.getCoordinate()) , "inconsistency in rightmost processing");
+    if (minIndex == 0 ) {
+      findRightmostEdgeAtNode();
+    }
+    else {
+      findRightmostEdgeAtVertex();
+    }
+    /**
+     * now check that the extreme side is the R side.
+     * If not, use the sym instead.
+     */
+    orientedDe = minDe;
+    int rightmostSide = getRightmostSide(minDe, minIndex);
+    if (rightmostSide == Position.LEFT) {
+      orientedDe = minDe.getSym();
+    }
+  }
+  private void findRightmostEdgeAtNode()
+  {
+      Node node = minDe.getNode();
+      DirectedEdgeStar star = (DirectedEdgeStar) node.getEdges();
+      minDe = star.getRightmostEdge();
+      // the DirectedEdge returned by the previous call is not
+      // necessarily in the forward direction. Use the sym edge if it isn't.
+      if (! minDe.isForward()) {
+        minDe = minDe.getSym();
+        minIndex = minDe.getEdge().getCoordinates().length - 1;
+      }
+  }
+  private void findRightmostEdgeAtVertex()
+  {
+      /**
+       * The rightmost point is an interior vertex, so it has a segment on either side of it.
+       * If these segments are both above or below the rightmost point, we need to
+       * determine their relative orientation to decide which is rightmost.
+       */
+      Coordinate[] pts = minDe.getEdge().getCoordinates();
+      Assert.isTrue(minIndex > 0 && minIndex < pts.length, "rightmost point expected to be interior vertex of edge");
+      Coordinate pPrev = pts[minIndex - 1];
+      Coordinate pNext = pts[minIndex + 1];
+      int orientation = cga.computeOrientation(minCoord, pNext, pPrev);
+      boolean usePrev = false;
+        // both segments are below min point
+      if (pPrev.y < minCoord.y && pNext.y < minCoord.y
+         && orientation == CGAlgorithms.COUNTERCLOCKWISE) {
+          usePrev = true;
+      }
+      else if (pPrev.y > minCoord.y && pNext.y > minCoord.y
+                && orientation == CGAlgorithms.CLOCKWISE) {
+          usePrev = true;
+      }
+      // if both segments are on the same side, do nothing - either is safe
+      // to select as a rightmost segment
+      if (usePrev) {
+        minIndex = minIndex - 1;
+      }
+  }
+  private void checkForRightmostCoordinate(DirectedEdge de)
+  {
+    Coordinate[] coord = de.getEdge().getCoordinates();
+    for (int i = 0; i < coord.length - 1; i++) {
+      // only check vertices which are the start or end point of a non-horizontal segment
+     // <FIX> MD 19 Sep 03 - NO!  we can test all vertices, since the rightmost must have a non-horiz segment adjacent to it
+        if (minCoord == null || coord[i].x > minCoord.x ) {
+          minDe = de;
+          minIndex = i;
+          minCoord = coord[i];
+        }
+      //}
+    }
+  }
+
+  private int getRightmostSide(DirectedEdge de, int index)
+  {
+    int side = getRightmostSideOfSegment(de, index);
+    if (side < 0)
+      side = getRightmostSideOfSegment(de, index - 1);
+    if (side < 0) {
+      // reaching here can indicate that segment is horizontal
+      //Assert.shouldNeverReachHere("problem with finding rightmost side of segment at " + de.getCoordinate());
+      // testing only
+      minCoord = null;
+      checkForRightmostCoordinate(de);
+    }
+    return side;
+  }
+
+  private int getRightmostSideOfSegment(DirectedEdge de, int i)
+  {
+    Edge e = de.getEdge();
+    Coordinate coord[] = e.getCoordinates();
+
+    if (i < 0 || i + 1 >= coord.length) return -1;
+    if (coord[i].y == coord[i + 1].y) return -1;    // indicates edge is parallel to x-axis
+
+    int pos = Position.LEFT;
+    if (coord[i].y < coord[i + 1].y) pos = Position.RIGHT;
+    return pos;
+  }
+}

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/buffer/SubgraphDepthLocater.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,241 @@
+
+/*
+ * 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.buffer;
+
+import java.util.*;
+import com.vividsolutions.jts.geom.*;
+import com.vividsolutions.jts.geomgraph.*;
+import com.vividsolutions.jts.algorithm.*;
+
+/**
+ * Locates a subgraph inside a set of subgraphs,
+ * in order to determine the outside depth of the subgraph.
+ * The input subgraphs are assumed to have had depths
+ * already calculated for their edges.
+ *
+ * @version 1.6
+ */
+public class SubgraphDepthLocater
+{
+  private Collection subgraphs;
+  private LineSegment seg = new LineSegment();
+  private CGAlgorithms cga = new RobustCGAlgorithms();
+
+  public SubgraphDepthLocater(List subgraphs)
+  {
+    this.subgraphs = subgraphs;
+  }
+
+  public int getDepth(Coordinate p)
+  {
+    List stabbedSegments = findStabbedSegments(p);
+    // if no segments on stabbing line subgraph must be outside all others.
+    if (stabbedSegments.size() == 0)
+      return 0;
+    Collections.sort(stabbedSegments);
+    DepthSegment ds = (DepthSegment) stabbedSegments.get(0);
+    return ds.leftDepth;
+  }
+
+  /**
+   * Finds all non-horizontal segments intersecting the stabbing line.
+   * The stabbing line is the ray to the right of stabbingRayLeftPt.
+   *
+   * @param stabbingRayLeftPt the left-hand origin of the stabbing line
+   * @return a List of {@link DepthSegments} intersecting the stabbing line
+   */
+  private List findStabbedSegments(Coordinate stabbingRayLeftPt)
+  {
+    List stabbedSegments = new ArrayList();
+    for (Iterator i = subgraphs.iterator(); i.hasNext(); ) {
+      BufferSubgraph bsg = (BufferSubgraph) i.next();
+      findStabbedSegments(stabbingRayLeftPt, bsg.getDirectedEdges(), stabbedSegments);
+    }
+    return stabbedSegments;
+  }
+
+  /**
+   * Finds all non-horizontal segments intersecting the stabbing line
+   * in the list of dirEdges.
+   * The stabbing line is the ray to the right of stabbingRayLeftPt.
+   *
+   * @param stabbingRayLeftPt the left-hand origin of the stabbing line
+   * @param stabbedSegments the current list of {@link DepthSegments} intersecting the stabbing line
+   */
+  private void findStabbedSegments(Coordinate stabbingRayLeftPt,
+                                   List dirEdges,
+                                   List stabbedSegments)
+  {
+    /**
+     * Check all forward DirectedEdges only.  This is still general,
+     * because each Edge has a forward DirectedEdge.
+     */
+    for (Iterator i = dirEdges.iterator(); i.hasNext();) {
+      DirectedEdge de = (DirectedEdge) i.next();
+      if (! de.isForward())
+        continue;
+      findStabbedSegments(stabbingRayLeftPt, de, stabbedSegments);
+    }
+  }
+
+  /**
+   * Finds all non-horizontal segments intersecting the stabbing line
+   * in the input dirEdge.
+   * The stabbing line is the ray to the right of stabbingRayLeftPt.
+   *
+   * @param stabbingRayLeftPt the left-hand origin of the stabbing line
+   * @param stabbedSegments the current list of {@link DepthSegments} intersecting the stabbing line
+   */
+  private void findStabbedSegments(Coordinate stabbingRayLeftPt,
+                                   DirectedEdge dirEdge,
+                                   List stabbedSegments)
+  {
+    Coordinate[] pts = dirEdge.getEdge().getCoordinates();
+    for (int i = 0; i < pts.length - 1; i++) {
+      seg.p0 = pts[i];
+      seg.p1 = pts[i + 1];
+      // ensure segment always points upwards
+      if (seg.p0.y > seg.p1.y)
+        seg.reverse();
+
+      // skip segment if it is left of the stabbing line
+      double maxx = Math.max(seg.p0.x, seg.p1.x);
+      if (maxx < stabbingRayLeftPt.x)
+        continue;
+
+      // skip horizontal segments (there will be a non-horizontal one carrying the same depth info
+      if (seg.isHorizontal())
+        continue;
+
+      // skip if segment is above or below stabbing line
+      if (stabbingRayLeftPt.y < seg.p0.y || stabbingRayLeftPt.y > seg.p1.y)
+        continue;
+
+      // skip if stabbing ray is right of the segment
+      if (cga.computeOrientation(seg.p0, seg.p1, stabbingRayLeftPt)
+          == CGAlgorithms.RIGHT)
+        continue;
+
+      // stabbing line cuts this segment, so record it
+      int depth = dirEdge.getDepth(Position.LEFT);
+      // if segment direction was flipped, use RHS depth instead
+      if (! seg.p0.equals(pts[i]))
+        depth = dirEdge.getDepth(Position.RIGHT);
+      DepthSegment ds = new DepthSegment(seg, depth);
+      stabbedSegments.add(ds);
+
+
+    }
+  }
+
+
+  /**
+   * A segment from a directed edge which has been assigned a depth value
+   * for its sides.
+   */
+  private class DepthSegment
+      implements Comparable
+  {
+
+    private LineSegment upwardSeg;
+    private int leftDepth;
+
+    public DepthSegment(LineSegment seg, int depth)
+    {
+      // input seg is assumed to be normalized
+      upwardSeg = new LineSegment(seg);
+      //upwardSeg.normalize();
+      this.leftDepth = depth;
+    }
+    /**
+     * Defines a comparision operation on DepthSegments
+     * which orders them left to right
+     *
+     * <pre>
+     * DS1 < DS2   if   DS1.seg is left of DS2.seg
+     * DS1 > DS2   if   DS1.seg is right of DS2.seg
+     * </pre>
+     *
+     * @param obj
+     * @return
+     */
+    public int compareTo(Object obj)
+    {
+      DepthSegment other = (DepthSegment) obj;
+      /**
+       * try and compute a determinate orientation for the segments.
+       * Test returns 1 if other is left of this (i.e. this > other)
+       */
+      int orientIndex = upwardSeg.orientationIndex(other.upwardSeg);
+
+      /**
+       * If comparison between this and other is indeterminate,
+       * try the opposite call order.
+       * orientationIndex value is 1 if this is left of other,
+       * so have to flip sign to get proper comparison value of
+       * -1 if this is leftmost
+       */
+      if (orientIndex == 0)
+        orientIndex = -1 * other.upwardSeg.orientationIndex(upwardSeg);
+
+      // if orientation is determinate, return it
+      if (orientIndex != 0)
+        return orientIndex;
+
+      // otherwise, segs must be collinear - sort based on minimum X value
+      return compareX(this.upwardSeg, other.upwardSeg);
+    }
+
+    /**
+     * Compare two collinear segments for left-most ordering.
+     * If segs are vertical, use vertical ordering for comparison.
+     * If segs are equal, return 0.
+     * Segments are assumed to be directed so that the second coordinate is >= to the first
+     * (e.g. up and to the right).
+     *
+     * @param seg0 a segment to compare
+     * @param seg1 a segment to compare
+     * @return
+     */
+    private int compareX(LineSegment seg0, LineSegment seg1)
+    {
+      int compare0 = seg0.p0.compareTo(seg1.p0);
+      if (compare0 != 0)
+        return compare0;
+      return seg0.p1.compareTo(seg1.p1);
+
+    }
+
+  }
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/buffer/package.html
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/buffer/package.html	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/buffer/package.html	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,13 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+<!--
+
+-->
+</head>
+<body bgcolor="white">
+
+Provides classes for computing buffers of geometries
+
+</body>
+</html>

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/distance/ConnectedElementLocationFilter.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,80 @@
+
+/*
+ * 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.distance;
+
+import java.util.*;
+import com.vividsolutions.jts.geom.*;
+
+/**
+ * A ConnectedElementPointFilter extracts a single point
+ * from each connected element in a Geometry
+ * (e.g. a polygon, linestring or point)
+ * and returns them in a list. The elements of the list are 
+ * {@link com.vividsolutions.jts.operation.distance.GeometryLocation}s.
+ *
+ * @version 1.6
+ */
+public class ConnectedElementLocationFilter
+  implements GeometryFilter
+{
+
+  /**
+   * Returns a list containing a point from each Polygon, LineString, and Point
+   * found inside the specified geometry. Thus, if the specified geometry is
+   * not a GeometryCollection, an empty list will be returned. The elements of the list 
+   * are {@link com.vividsolutions.jts.operation.distance.GeometryLocation}s.
+   */  
+  public static List getLocations(Geometry geom)
+  {
+    List locations = new ArrayList();
+    geom.apply(new ConnectedElementLocationFilter(locations));
+    return locations;
+  }
+
+  private List locations;
+
+  ConnectedElementLocationFilter(List locations)
+  {
+    this.locations = locations;
+  }
+
+  public void filter(Geometry geom)
+  {
+    if (geom instanceof Point
+      || geom instanceof LineString
+      || geom instanceof Polygon )
+      locations.add(new GeometryLocation(geom, 0, geom.getCoordinate()));
+  }
+
+}

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/distance/ConnectedElementPointFilter.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,78 @@
+
+/*
+ * 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.distance;
+
+import java.util.*;
+import com.vividsolutions.jts.geom.*;
+
+/**
+ * Extracts a single point
+ * from each connected element in a Geometry
+ * (e.g. a polygon, linestring or point)
+ * and returns them in a list
+ *
+ * @version 1.6
+ */
+public class ConnectedElementPointFilter
+  implements GeometryFilter
+{
+
+  /**
+   * Returns a list containing a Coordinate from each Polygon, LineString, and Point
+   * found inside the specified geometry. Thus, if the specified geometry is
+   * not a GeometryCollection, an empty list will be returned.
+   */
+  public static List getCoordinates(Geometry geom)
+  {
+    List pts = new ArrayList();
+    geom.apply(new ConnectedElementPointFilter(pts));
+    return pts;
+  }
+
+  private List pts;
+
+  ConnectedElementPointFilter(List pts)
+  {
+    this.pts = pts;
+  }
+
+  public void filter(Geometry geom)
+  {
+    if (geom instanceof Point
+      || geom instanceof LineString
+      || geom instanceof Polygon )
+      pts.add(geom.getCoordinate());
+  }
+
+}

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/distance/DistanceOp.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,356 @@
+/*
+ * 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.distance;
+
+import java.util.*;
+import com.vividsolutions.jts.geom.*;
+import com.vividsolutions.jts.geom.util.*;
+import com.vividsolutions.jts.algorithm.*;
+
+/**
+ * Computes the distance and
+ * closest points between two {@link Geometry}s.
+ * <p>
+ * The distance computation finds a pair of points in the input geometries
+ * which have minimum distance between them.  These points may
+ * not be vertices of the geometries, but may lie in the interior of
+ * a line segment. In this case the coordinate computed is a close
+ * approximation to the exact point.
+ * <p>
+ * The algorithms used are straightforward O(n^2)
+ * comparisons.  This worst-case performance could be improved on
+ * by using Voronoi techniques.
+ *
+ * @version 1.6
+ */
+public class DistanceOp {
+
+  /**
+   * Compute the distance between the closest points of two geometries.
+   * @param g0 a {@link Geometry}
+   * @param g1 another {@link Geometry}
+   * @return the distance between the geometries
+   */
+  public static double distance(Geometry g0, Geometry g1)
+  {
+    DistanceOp distOp = new DistanceOp(g0, g1);
+    return distOp.distance();
+  }
+
+  /**
+   * Compute the the closest points of two geometries.
+   * The points are presented in the same order as the input Geometries.
+   *
+   * @param g0 a {@link Geometry}
+   * @param g1 another {@link Geometry}
+   * @return the closest points in the geometries
+   */
+  public static Coordinate[] closestPoints(Geometry g0, Geometry g1)
+  {
+    DistanceOp distOp = new DistanceOp(g0, g1);
+    return distOp.closestPoints();
+  }
+
+  private PointLocator ptLocator = new PointLocator();
+  private Geometry[] geom;
+  private GeometryLocation[] minDistanceLocation;
+  private double minDistance = Double.MAX_VALUE;
+
+  /**
+   * Constructs a DistanceOp that computes the distance and closest points between
+   * the two specified geometries.
+   */
+  public DistanceOp(Geometry g0, Geometry g1)
+  {
+    this.geom = new Geometry[2];
+    geom[0] = g0;
+    geom[1] = g1;
+  }
+
+  /**
+   * Report the distance between the closest points on the input geometries.
+   *
+   * @return the distance between the geometries
+   */
+  public double distance()
+  {
+    computeMinDistance();
+    return minDistance;
+  }
+
+  /**
+   * Report the coordinates of the closest points in the input geometries.
+   * The points are presented in the same order as the input Geometries.
+   *
+   * @return a pair of {@link Coordinate}s of the closest points
+   */
+  public Coordinate[] closestPoints()
+  {
+    computeMinDistance();
+    Coordinate[] closestPts
+        = new Coordinate[] {
+          minDistanceLocation[0].getCoordinate(),
+          minDistanceLocation[1].getCoordinate() };
+    return closestPts;
+  }
+
+  /**
+   * Report the locations of the closest points in the input geometries.
+   * The locations are presented in the same order as the input Geometries.
+   *
+   * @return a pair of {@link GeometryLocation}s for the closest points
+   */
+  public GeometryLocation[] closestLocations()
+  {
+    computeMinDistance();
+    return minDistanceLocation;
+  }
+
+  private void updateMinDistance(double dist)
+  {
+    if (dist < minDistance)
+      minDistance = dist;
+  }
+
+  private void updateMinDistance(GeometryLocation[] locGeom, boolean flip)
+  {
+    // if not set then don't update
+    if (locGeom[0] == null) return;
+
+    if (flip) {
+      minDistanceLocation[0] = locGeom[1];
+      minDistanceLocation[1] = locGeom[0];
+    }
+    else {
+      minDistanceLocation[0] = locGeom[0];
+      minDistanceLocation[1] = locGeom[1];
+    }
+  }
+
+  private void computeMinDistance()
+  {
+    if (minDistanceLocation != null) return;
+
+    minDistanceLocation = new GeometryLocation[2];
+    computeContainmentDistance();
+    if (minDistance <= 0.0) return;
+    computeLineDistance();
+  }
+
+  private void computeContainmentDistance()
+  {
+    List polys0 = PolygonExtracter.getPolygons(geom[0]);
+    List polys1 = PolygonExtracter.getPolygons(geom[1]);
+
+    GeometryLocation[] locPtPoly = new GeometryLocation[2];
+    // test if either geometry is wholely inside the other
+    if (polys1.size() > 0) {
+      List insideLocs0 = ConnectedElementLocationFilter.getLocations(geom[0]);
+      computeInside(insideLocs0, polys1, locPtPoly);
+      if (minDistance <= 0.0) {
+        minDistanceLocation[0] = locPtPoly[0];
+        minDistanceLocation[1] = locPtPoly[1];
+        return;
+      }
+    }
+    if (polys0.size() > 0) {
+      List insideLocs1 = ConnectedElementLocationFilter.getLocations(geom[1]);
+      computeInside(insideLocs1, polys0, locPtPoly);
+      if (minDistance <= 0.0) {
+        // flip locations, since we are testing geom 1 VS geom 0
+        minDistanceLocation[0] = locPtPoly[1];
+        minDistanceLocation[1] = locPtPoly[0];
+        return;
+      }
+    }
+  }
+  private void computeInside(List locs, List polys, GeometryLocation[] locPtPoly)
+  {
+    for (int i = 0; i < locs.size(); i++) {
+      GeometryLocation loc = (GeometryLocation) locs.get(i);
+      for (int j = 0; j < polys.size(); j++) {
+        Polygon poly = (Polygon) polys.get(j);
+        computeInside(loc, poly, locPtPoly);
+        if (minDistance <= 0.0) {
+          return;
+        }
+      }
+    }
+  }
+
+  private void computeInside(GeometryLocation ptLoc,
+      Polygon poly,
+      GeometryLocation[] locPtPoly)
+  {
+    Coordinate pt = ptLoc.getCoordinate();
+    if (Location.EXTERIOR != ptLocator.locate(pt, poly)) {
+      minDistance = 0.0;
+      locPtPoly[0] = ptLoc;
+      GeometryLocation locPoly = new GeometryLocation(poly, pt);
+      locPtPoly[1] = locPoly;
+      return;
+    }
+  }
+
+  private void computeLineDistance()
+  {
+    GeometryLocation[] locGeom = new GeometryLocation[2];
+
+    /**
+     * Geometries are not wholely inside, so compute distance from lines and points
+     * of one to lines and points of the other
+     */
+    List lines0 = LinearComponentExtracter.getLines(geom[0]);
+    List lines1 = LinearComponentExtracter.getLines(geom[1]);
+
+    List pts0 = PointExtracter.getPoints(geom[0]);
+    List pts1 = PointExtracter.getPoints(geom[1]);
+
+    // bail whenever minDistance goes to zero, since it can't get any less
+    computeMinDistanceLines(lines0, lines1, locGeom);
+    updateMinDistance(locGeom, false);
+    if (minDistance <= 0.0) return;
+
+    locGeom[0] = null;
+    locGeom[1] = null;
+    computeMinDistanceLinesPoints(lines0, pts1, locGeom);
+    updateMinDistance(locGeom, false);
+    if (minDistance <= 0.0) return;
+
+    locGeom[0] = null;
+    locGeom[1] = null;
+    computeMinDistanceLinesPoints(lines1, pts0, locGeom);
+    updateMinDistance(locGeom, true);
+    if (minDistance <= 0.0) return;
+
+    locGeom[0] = null;
+    locGeom[1] = null;
+    computeMinDistancePoints(pts0, pts1, locGeom);
+    updateMinDistance(locGeom, false);
+  }
+
+  private void computeMinDistanceLines(List lines0, List lines1, GeometryLocation[] locGeom)
+  {
+    for (int i = 0; i < lines0.size(); i++) {
+      LineString line0 = (LineString) lines0.get(i);
+      for (int j = 0; j < lines1.size(); j++) {
+        LineString line1 = (LineString) lines1.get(j);
+        computeMinDistance(line0, line1, locGeom);
+        if (minDistance <= 0.0) return;
+      }
+    }
+  }
+
+  private void computeMinDistancePoints(List points0, List points1, GeometryLocation[] locGeom)
+  {
+    for (int i = 0; i < points0.size(); i++) {
+      Point pt0 = (Point) points0.get(i);
+      for (int j = 0; j < points1.size(); j++) {
+        Point pt1 = (Point) points1.get(j);
+        double dist = pt0.getCoordinate().distance(pt1.getCoordinate());
+        if (dist < minDistance) {
+          minDistance = dist;
+          // this is wrong - need to determine closest points on both segments!!!
+          locGeom[0] = new GeometryLocation(pt0, 0, pt0.getCoordinate());
+          locGeom[1] = new GeometryLocation(pt1, 0, pt1.getCoordinate());
+        }
+        if (minDistance <= 0.0) return;
+      }
+    }
+  }
+
+  private void computeMinDistanceLinesPoints(List lines, List points,
+      GeometryLocation[] locGeom)
+  {
+    for (int i = 0; i < lines.size(); i++) {
+      LineString line = (LineString) lines.get(i);
+      for (int j = 0; j < points.size(); j++) {
+        Point pt = (Point) points.get(j);
+        computeMinDistance(line, pt, locGeom);
+        if (minDistance <= 0.0) return;
+      }
+    }
+  }
+
+  private void computeMinDistance(LineString line0, LineString line1,
+                                  GeometryLocation[] locGeom)
+  {
+    if (line0.getEnvelopeInternal().distance(line1.getEnvelopeInternal())
+        > minDistance)
+          return;
+    Coordinate[] coord0 = line0.getCoordinates();
+    Coordinate[] coord1 = line1.getCoordinates();
+      // brute force approach!
+    for (int i = 0; i < coord0.length - 1; i++) {
+      for (int j = 0; j < coord1.length - 1; j++) {
+        double dist = CGAlgorithms.distanceLineLine(
+                                        coord0[i], coord0[i + 1],
+                                        coord1[j], coord1[j + 1] );
+        if (dist < minDistance) {
+          minDistance = dist;
+          LineSegment seg0 = new LineSegment(coord0[i], coord0[i + 1]);
+          LineSegment seg1 = new LineSegment(coord1[j], coord1[j + 1]);
+          Coordinate[] closestPt = seg0.closestPoints(seg1);
+          locGeom[0] = new GeometryLocation(line0, i, closestPt[0]);
+          locGeom[1] = new GeometryLocation(line1, j, closestPt[1]);
+        }
+        if (minDistance <= 0.0) return;
+      }
+    }
+  }
+
+  private void computeMinDistance(LineString line, Point pt,
+                                  GeometryLocation[] locGeom)
+  {
+    if (line.getEnvelopeInternal().distance(pt.getEnvelopeInternal())
+        > minDistance)
+          return;
+    Coordinate[] coord0 = line.getCoordinates();
+    Coordinate coord = pt.getCoordinate();
+      // brute force approach!
+    for (int i = 0; i < coord0.length - 1; i++) {
+        double dist = CGAlgorithms.distancePointLine(
+            coord, coord0[i], coord0[i + 1] );
+        if (dist < minDistance) {
+          minDistance = dist;
+          LineSegment seg = new LineSegment(coord0[i], coord0[i + 1]);
+          Coordinate segClosestPoint = seg.closestPoint(coord);
+          locGeom[0] = new GeometryLocation(line, i, segClosestPoint);
+          locGeom[1] = new GeometryLocation(pt, 0, coord);
+        }
+        if (minDistance <= 0.0) return;
+
+    }
+  }
+
+}

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/distance/GeometryLocation.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,99 @@
+
+/*
+ * 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.distance;
+
+import com.vividsolutions.jts.geom.*;
+
+/**
+ * Represents the location of a point on a Geometry.
+ * Maintains both the actual point location (which of course
+ * may not be exact) as well as information about the component
+ * and segment index where the point occurs.
+ * 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
+ */
+public class GeometryLocation
+{
+  
+  /**
+   * Special value of segment-index for locations inside area geometries. These
+   * locations do not have an associated segment index.
+   */
+  public static final int INSIDE_AREA = -1;
+
+  private Geometry component = null;
+  private int segIndex;
+  private Coordinate pt = null;
+
+  /**
+   * Constructs a GeometryLocation specifying a point on a geometry, as well as the 
+   * segment that the point is on (or INSIDE_AREA if the point is not on a segment).
+   */
+  public GeometryLocation(Geometry component, int segIndex, Coordinate pt)
+  {
+    this.component = component;
+    this.segIndex = segIndex;
+    this.pt = pt;
+  }
+
+  /**
+   * Constructs a GeometryLocation specifying a point inside an area geometry.
+   */  
+  public GeometryLocation(Geometry component,Coordinate pt)
+  {
+    this(component, INSIDE_AREA, pt);
+  }
+
+  /**
+   * Returns the geometry associated with this location.
+   */
+  public Geometry getGeometryComponent() { return component; }
+  /**
+   * Returns the segment index for this location. If the location is inside an
+   * area, the index will have the value INSIDE_AREA;
+   *
+   * @return the segment index for the location, or INSIDE_AREA
+   */
+  public int getSegmentIndex() { return segIndex; }
+  /**
+   * Returns the location.
+   */
+  public Coordinate getCoordinate() { return pt; }
+  /**
+   * Returns whether this GeometryLocation represents a point inside an area geometry.
+   */
+  public boolean isInsideArea() { return segIndex == INSIDE_AREA; }
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/distance/package.html
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/distance/package.html	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/distance/package.html	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,13 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+<!--
+
+-->
+</head>
+<body bgcolor="white">
+
+Provides classes for computing the distance between geometries
+
+</body>
+</html>

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/linemerge/EdgeString.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,103 @@
+
+/*
+ * 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 com.vividsolutions.jts.geom.*;
+import com.vividsolutions.jts.geom.Coordinate;
+import com.vividsolutions.jts.geom.CoordinateList;
+import com.vividsolutions.jts.geom.GeometryFactory;
+import com.vividsolutions.jts.geom.LineString;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * A sequence of {@link LineMergeDirectedEdge}s forming one of the lines that will
+ * be output by the line-merging process.
+ *
+ * @version 1.6
+ */
+public class EdgeString {
+  private GeometryFactory factory;
+  private List directedEdges = new ArrayList();
+  private Coordinate[] coordinates = null;
+  /**
+   * Constructs an EdgeString with the given factory used to convert this EdgeString
+   * to a LineString
+   */
+  public EdgeString(GeometryFactory factory) {
+    this.factory = factory;
+  }
+
+  /**
+   * Adds a directed edge which is known to form part of this line.
+   */
+  public void add(LineMergeDirectedEdge directedEdge) {
+    directedEdges.add(directedEdge);
+  }
+
+  private Coordinate[] getCoordinates() {
+    if (coordinates == null) {
+      int forwardDirectedEdges = 0;
+      int reverseDirectedEdges = 0;
+      CoordinateList coordinateList = new CoordinateList();
+      for (Iterator i = directedEdges.iterator(); i.hasNext();) {
+        LineMergeDirectedEdge directedEdge = (LineMergeDirectedEdge) i.next();
+        if (directedEdge.getEdgeDirection()) {
+          forwardDirectedEdges++;
+        }
+        else {
+          reverseDirectedEdges++;
+        }
+        coordinateList.add(((LineMergeEdge) directedEdge.getEdge()).getLine()
+                            .getCoordinates(), false,
+          directedEdge.getEdgeDirection());
+      }
+      coordinates = coordinateList.toCoordinateArray();
+      if (reverseDirectedEdges > forwardDirectedEdges) {
+        CoordinateArrays.reverse(coordinates);
+      }
+    }
+
+    return coordinates;
+  }
+
+  /**
+   * Converts this EdgeString into a LineString.
+   */
+  public LineString toLineString() {
+    return factory.createLineString(getCoordinates());
+  }
+}

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/linemerge/LineMergeDirectedEdge.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,80 @@
+
+/*
+ * 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 com.vividsolutions.jts.geom.Coordinate;
+import com.vividsolutions.jts.planargraph.DirectedEdge;
+import com.vividsolutions.jts.planargraph.Node;
+import com.vividsolutions.jts.util.Assert;
+
+/**
+ * A {@link com.vividsolutions.jts.planargraph.DirectedEdge} of a 
+ * {@link LineMergeGraph}. 
+ *
+ * @version 1.6
+ */
+public class LineMergeDirectedEdge extends DirectedEdge {
+  /**
+   * Constructs a LineMergeDirectedEdge connecting the <code>from</code> node to the
+   * <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>)
+   * @param edgeDirection
+   *                  whether this DirectedEdge's direction is the same as or
+   *                  opposite to that of the parent Edge (if any)
+   */  
+  public LineMergeDirectedEdge(Node from, Node to, Coordinate directionPt,
+    boolean edgeDirection) {
+    super(from, to, directionPt, edgeDirection);
+  }
+
+  /**
+   * Returns the directed edge that starts at this directed edge's end point, or null
+   * if there are zero or multiple directed edges starting there.  
+   * @return
+   */
+  public LineMergeDirectedEdge getNext() {
+    if (getToNode().getDegree() != 2) {
+      return null;
+    }
+    if (getToNode().getOutEdges().getEdges().get(0) == getSym()) {
+      return (LineMergeDirectedEdge) getToNode().getOutEdges().getEdges().get(1);
+    }
+    Assert.isTrue(getToNode().getOutEdges().getEdges().get(1) == getSym());
+
+    return (LineMergeDirectedEdge) getToNode().getOutEdges().getEdges().get(0);
+  }
+}

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/linemerge/LineMergeEdge.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,59 @@
+
+/*
+ * 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 com.vividsolutions.jts.geom.LineString;
+import com.vividsolutions.jts.planargraph.Edge;
+
+/**
+ * An edge of a {@link LineMergeGraph}. The <code>marked</code> field indicates
+ * whether this Edge has been logically deleted from the graph.
+ *
+ * @version 1.6
+ */
+public class LineMergeEdge extends Edge {
+  private LineString line;
+  /**
+   * Constructs a LineMergeEdge with vertices given by the specified LineString.
+   */
+  public LineMergeEdge(LineString line) {
+    this.line = line;
+  }
+  /**
+   * Returns the LineString specifying the vertices of this edge.
+   */
+  public LineString getLine() {
+    return line;
+  }
+}

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/linemerge/LineMergeGraph.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,82 @@
+
+/*
+ * 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 com.vividsolutions.jts.geom.Coordinate;
+import com.vividsolutions.jts.geom.CoordinateArrays;
+import com.vividsolutions.jts.geom.LineString;
+import com.vividsolutions.jts.planargraph.DirectedEdge;
+import com.vividsolutions.jts.planargraph.Edge;
+import com.vividsolutions.jts.planargraph.Node;
+import com.vividsolutions.jts.planargraph.PlanarGraph;
+
+/**
+ * A planar graph of edges that is analyzed to sew the edges together. The 
+ * <code>marked</code> flag on @{link com.vividsolutions.planargraph.Edge}s 
+ * and @{link com.vividsolutions.planargraph.Node}s indicates whether they have been
+ * logically deleted from the graph.
+ *
+ * @version 1.6
+ */
+public class LineMergeGraph extends PlanarGraph {
+  /**
+   * Adds an Edge, DirectedEdges, and Nodes for the given LineString representation
+   * of an edge. 
+   */
+  public void addEdge(LineString lineString) {
+    if (lineString.isEmpty()) { return; }
+    Coordinate[] coordinates = CoordinateArrays.removeRepeatedPoints(lineString.getCoordinates());
+    Coordinate startCoordinate = coordinates[0];
+    Coordinate endCoordinate = coordinates[coordinates.length - 1];
+    Node startNode = getNode(startCoordinate);
+    Node endNode = getNode(endCoordinate);
+    DirectedEdge directedEdge0 = new LineMergeDirectedEdge(startNode, endNode,
+        coordinates[1], true);
+    DirectedEdge directedEdge1 = new LineMergeDirectedEdge(endNode, startNode,
+        coordinates[coordinates.length - 2], false);
+    Edge edge = new LineMergeEdge(lineString);
+    edge.setDirectedEdges(directedEdge0, directedEdge1);
+    add(edge);
+  }
+
+  private Node getNode(Coordinate coordinate) {
+    Node node = findNode(coordinate);
+    if (node == null) {
+      node = new Node(coordinate);
+      add(node);
+    }
+
+    return node;
+  }
+}

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/linemerge/LineMerger.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,157 @@
+
+/*
+ * 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.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+
+import com.vividsolutions.jts.geom.Geometry;
+import com.vividsolutions.jts.geom.GeometryComponentFilter;
+import com.vividsolutions.jts.geom.GeometryFactory;
+import com.vividsolutions.jts.geom.LineString;
+import com.vividsolutions.jts.planargraph.Node;
+import com.vividsolutions.jts.util.Assert;
+
+/**
+ * Sews together a set of fully noded LineStrings. Sewing stops at nodes of degree 1
+ * or 3 or more -- the exception is an isolated loop, which only has degree-2 nodes,
+ * in which case a node is simply chosen as a starting point. The direction of each
+ * merged LineString will be that of the majority of the LineStrings from which it
+ * was derived.
+ * <p>
+ * Any dimension of Geometry is handled -- the constituent linework is extracted to 
+ * form the edges. The edges must be correctly noded; that is, they must only meet
+ * at their endpoints.  The LineMerger will still run on incorrectly noded input
+ * but will not form polygons from incorrected noded edges.
+ *
+ * @version 1.6
+ */
+public class LineMerger {
+  /**
+   * Adds a collection of Geometries to be processed. May be called multiple times.
+   * Any dimension of Geometry may be added; the constituent linework will be
+   * extracted.
+   */
+  public void add(Collection geometries) {
+    for (Iterator i = geometries.iterator(); i.hasNext(); ) {
+      Geometry geometry = (Geometry) i.next();
+      add(geometry);
+    }
+  }
+  private LineMergeGraph graph = new LineMergeGraph();
+  private Collection mergedLineStrings = null;
+  private GeometryFactory factory = null;
+  /**
+   * Adds a Geometry to be processed. May be called multiple times.
+   * Any dimension of Geometry may be added; the constituent linework will be
+   * extracted.
+   */  
+  public void add(Geometry geometry) {
+    geometry.apply(new GeometryComponentFilter() {
+      public void filter(Geometry component) {
+        if (component instanceof LineString) {
+          add((LineString)component);
+        }
+      }      
+    });
+  }
+  private void add(LineString lineString) {
+    if (factory == null) {
+      this.factory = lineString.getFactory();
+    }
+    graph.addEdge(lineString);
+  }
+  private Collection edgeStrings = null;
+  private void merge() {
+    if (mergedLineStrings != null) { return; }
+    edgeStrings = new ArrayList();
+    buildEdgeStringsForObviousStartNodes();
+    buildEdgeStringsForIsolatedLoops();
+    mergedLineStrings = new ArrayList();    
+    for (Iterator i = edgeStrings.iterator(); i.hasNext(); ) {
+      EdgeString edgeString = (EdgeString) i.next();
+      mergedLineStrings.add(edgeString.toLineString());
+    }    
+  }
+  private void buildEdgeStringsForObviousStartNodes() {
+    buildEdgeStringsForNonDegree2Nodes();
+  }
+  private void buildEdgeStringsForIsolatedLoops() {
+    buildEdgeStringsForUnprocessedNodes();
+  }  
+  private void buildEdgeStringsForUnprocessedNodes() {
+    for (Iterator i = graph.getNodes().iterator(); i.hasNext(); ) {
+      Node node = (Node) i.next();
+      if (!node.isMarked()) { 
+        Assert.isTrue(node.getDegree() == 2);
+        buildEdgeStringsStartingAt(node);
+        node.setMarked(true);
+      }
+    }
+  }  
+  private void buildEdgeStringsForNonDegree2Nodes() {
+    for (Iterator i = graph.getNodes().iterator(); i.hasNext(); ) {
+      Node node = (Node) i.next();
+      if (node.getDegree() != 2) { 
+        buildEdgeStringsStartingAt(node);
+        node.setMarked(true);
+      }
+    }
+  }
+  private void buildEdgeStringsStartingAt(Node node) {
+    for (Iterator i = node.getOutEdges().iterator(); i.hasNext(); ) {
+      LineMergeDirectedEdge directedEdge = (LineMergeDirectedEdge) i.next();
+      if (directedEdge.getEdge().isMarked()) { continue; }
+      edgeStrings.add(buildEdgeStringStartingWith(directedEdge));
+    }
+  }
+  private EdgeString buildEdgeStringStartingWith(LineMergeDirectedEdge start) {    
+    EdgeString edgeString = new EdgeString(factory);
+    LineMergeDirectedEdge current = start;
+    do {
+      edgeString.add(current);
+      current.getEdge().setMarked(true);
+      current = current.getNext();      
+    } while (current != null && current != start);
+    return edgeString;
+  }
+  /**
+   * Returns the LineStrings built by the merging process.
+   */
+  public Collection getMergedLineStrings() {
+    merge();
+    return mergedLineStrings;
+  }
+}

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/overlay/EdgeSetNoder.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,78 @@
+
+
+/*
+ * 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.overlay;
+
+import java.util.*;
+import com.vividsolutions.jts.algorithm.LineIntersector;
+import com.vividsolutions.jts.geomgraph.*;
+import com.vividsolutions.jts.geomgraph.index.*;
+import com.vividsolutions.jts.util.*;
+
+/**
+ * Nodes a set of edges.
+ * 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
+ */
+public class EdgeSetNoder {
+
+  private LineIntersector li;
+  private List inputEdges = new ArrayList();
+
+  public EdgeSetNoder(LineIntersector li) {
+    this.li = li;
+  }
+
+  public void addEdges(List edges)
+  {
+    inputEdges.addAll(edges);
+  }
+
+  public List getNodedEdges()
+  {
+    EdgeSetIntersector esi = new SimpleMCSweepLineIntersector();
+    SegmentIntersector si = new SegmentIntersector(li, true, false);
+    esi.computeIntersections(inputEdges, si, true);
+//Debug.println("has proper int = " + si.hasProperIntersection());
+
+    List splitEdges = new ArrayList();
+    for (Iterator i = inputEdges.iterator(); i.hasNext(); ) {
+      Edge e = (Edge) i.next();
+      e.getEdgeIntersectionList().addSplitEdges(splitEdges);
+    }
+    return splitEdges;
+  }
+}

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/overlay/LineBuilder.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,201 @@
+/*
+ * 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.overlay;
+
+import java.util.*;
+import com.vividsolutions.jts.geom.*;
+import com.vividsolutions.jts.geomgraph.*;
+import com.vividsolutions.jts.algorithm.*;
+import com.vividsolutions.jts.util.*;
+
+/**
+ * Forms JTS LineStrings out of a the graph of {@link DirectedEdge}s
+ * created by an {@link OverlayOp}.
+ *
+ * @version 1.6
+ */
+public class LineBuilder {
+  private OverlayOp op;
+  private GeometryFactory geometryFactory;
+  private PointLocator ptLocator;
+
+  private List lineEdgesList    = new ArrayList();
+  private List resultLineList   = new ArrayList();
+
+  public LineBuilder(OverlayOp op, GeometryFactory geometryFactory, PointLocator ptLocator) {
+    this.op = op;
+    this.geometryFactory = geometryFactory;
+    this.ptLocator = ptLocator;
+  }
+  /**
+   * @return a list of the LineStrings in the result of the specified overlay operation
+   */
+  public List build(int opCode)
+  {
+    findCoveredLineEdges();
+    collectLines(opCode);
+    //labelIsolatedLines(lineEdgesList);
+    buildLines(opCode);
+    return resultLineList;
+  }
+  /**
+   * Find and mark L edges which are "covered" by the result area (if any).
+   * L edges at nodes which also have A edges can be checked by checking
+   * their depth at that node.
+   * L edges at nodes which do not have A edges can be checked by doing a
+   * point-in-polygon test with the previously computed result areas.
+   */
+  private void findCoveredLineEdges()
+  {
+    // first set covered for all L edges at nodes which have A edges too
+    for (Iterator nodeit = op.getGraph().getNodes().iterator(); nodeit.hasNext(); ) {
+      Node node = (Node) nodeit.next();
+//node.print(System.out);
+      ((DirectedEdgeStar) node.getEdges()).findCoveredLineEdges();
+    }
+
+    /**
+     * For all L edges which weren't handled by the above,
+     * use a point-in-poly test to determine whether they are covered
+     */
+    for (Iterator it = op.getGraph().getEdgeEnds().iterator(); it.hasNext(); ) {
+      DirectedEdge de = (DirectedEdge) it.next();
+      Edge e = de.getEdge();
+      if (de.isLineEdge() && ! e.isCoveredSet()) {
+        boolean isCovered = op.isCoveredByA(de.getCoordinate());
+        e.setCovered(isCovered);
+      }
+    }
+  }
+
+  private void collectLines(int opCode)
+  {
+    for (Iterator it = op.getGraph().getEdgeEnds().iterator(); it.hasNext(); ) {
+      DirectedEdge de = (DirectedEdge) it.next();
+      collectLineEdge(de, opCode, lineEdgesList);
+      collectBoundaryTouchEdge(de, opCode, lineEdgesList);
+    }
+  }
+
+  /**
+   * Collect line edges which are in the result.
+   * Line edges are in the result if they are not part of
+   * an area boundary, if they are in the result of the overlay operation,
+   * and if they are not covered by a result area.
+   *
+   * @param de the directed edge to test
+   * @param opCode the overlap operation
+   * @param edges the list of included line edges
+   */
+  private void collectLineEdge(DirectedEdge de, int opCode, List edges)
+  {
+    Label label = de.getLabel();
+    Edge e = de.getEdge();
+    // include L edges which are in the result
+    if (de.isLineEdge()) {
+      if (! de.isVisited() && OverlayOp.isResultOfOp(label, opCode) && ! e.isCovered()) {
+//Debug.println("de: " + de.getLabel());
+//Debug.println("edge: " + e.getLabel());
+
+        edges.add(e);
+        de.setVisitedEdge(true);
+      }
+    }
+  }
+
+  /**
+   * Collect edges from Area inputs which should be in the result but
+   * which have not been included in a result area.
+   * This happens ONLY:
+   * <ul>
+   * <li>during an intersection when the boundaries of two
+   * areas touch in a line segment
+   * <li> OR as a result of a dimensional collapse.
+   * </ul>
+   */
+  private void collectBoundaryTouchEdge(DirectedEdge de, int opCode, List edges)
+  {
+    Label label = de.getLabel();
+    if (de.isLineEdge()) return;  // only interested in area edges
+    if (de.isVisited()) return;  // already processed
+    if (de.isInteriorAreaEdge()) return;  // added to handle dimensional collapses
+    if (de.getEdge().isInResult()) return;  // if the edge linework is already included, don't include it again
+
+    // sanity check for labelling of result edgerings
+    Assert.isTrue(! (de.isInResult() || de.getSym().isInResult()) || ! de.getEdge().isInResult());
+
+    // include the linework if it's in the result of the operation
+    if (OverlayOp.isResultOfOp(label, opCode)
+          && opCode == OverlayOp.INTERSECTION)
+    {
+      edges.add(de.getEdge());
+      de.setVisitedEdge(true);
+    }
+  }
+
+  private void buildLines(int opCode)
+  {
+    for (Iterator it = lineEdgesList.iterator(); it.hasNext(); ) {
+      Edge e = (Edge) it.next();
+      Label label = e.getLabel();
+        LineString line = geometryFactory.createLineString(e.getCoordinates());
+        resultLineList.add(line);
+        e.setInResult(true);
+    }
+  }
+
+  private void labelIsolatedLines(List edgesList)
+  {
+    for (Iterator it = edgesList.iterator(); it.hasNext(); ) {
+      Edge e = (Edge) it.next();
+      Label label = e.getLabel();
+//n.print(System.out);
+      if (e.isIsolated()) {
+        if (label.isNull(0))
+          labelIsolatedLine(e, 0);
+        else
+          labelIsolatedLine(e, 1);
+      }
+    }
+  }
+  /**
+   * Label an isolated node with its relationship to the target geometry.
+   */
+  private void labelIsolatedLine(Edge e, int targetIndex)
+  {
+    int loc = ptLocator.locate(e.getCoordinate(), op.getArgGeometry(targetIndex));
+    e.getLabel().setLocation(targetIndex, loc);
+  }
+
+
+}

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/overlay/MaximalEdgeRing.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,105 @@
+
+
+/*
+ * 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.overlay;
+
+import java.util.*;
+import com.vividsolutions.jts.geom.*;
+import com.vividsolutions.jts.algorithm.*;
+import com.vividsolutions.jts.geomgraph.*;
+
+/**
+ * A ring of {@link edges} which may contain nodes of degree > 2.
+ * A MaximalEdgeRing may represent two different spatial entities:
+ * <ul>
+ * <li>a single polygon possibly containing inversions (if the ring is oriented CW)
+ * <li>a single hole possibly containing exversions (if the ring is oriented CCW)
+ * </ul>
+ * If the MaximalEdgeRing represents a polygon,
+ * the interior of the polygon is strongly connected.
+ * <p>
+ * These are the form of rings used to define polygons under some spatial data models.
+ * However, under the OGC SFS model, {@link MinimalEdgeRings} are required.
+ * A MaximalEdgeRing can be converted to a list of MinimalEdgeRings using the
+ * {@link #buildMinimalRings() } method.
+ *
+ * @version 1.6
+ * @see com.vividsolutions.jts.operation.overlay.MinimalEdgeRing
+ */
+public class MaximalEdgeRing
+  extends EdgeRing
+{
+
+  public MaximalEdgeRing(DirectedEdge start, GeometryFactory geometryFactory, CGAlgorithms cga) {
+    super(start, geometryFactory, cga);
+  }
+
+  public DirectedEdge getNext(DirectedEdge de)
+  {
+    return de.getNext();
+  }
+  public void setEdgeRing(DirectedEdge de, EdgeRing er)
+  {
+    de.setEdgeRing(er);
+  }
+
+  /**
+   * For all nodes in this EdgeRing,
+   * link the DirectedEdges at the node to form minimalEdgeRings
+   */
+  void linkDirectedEdgesForMinimalEdgeRings()
+  {
+    DirectedEdge de = startDe;
+    do {
+      Node node = de.getNode();
+      ((DirectedEdgeStar) node.getEdges()).linkMinimalDirectedEdges(this);
+      de = de.getNext();
+    } while (de != startDe);
+  }
+
+  List buildMinimalRings()
+  {
+    List minEdgeRings = new ArrayList();
+    DirectedEdge de = startDe;
+    do {
+      if (de.getMinEdgeRing() == null) {
+        EdgeRing minEr = new MinimalEdgeRing(de, geometryFactory, cga);
+        minEdgeRings.add(minEr);
+      }
+      de = de.getNext();
+    } while (de != startDe);
+    return minEdgeRings;
+  }
+
+}

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/overlay/MinimalEdgeRing.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,66 @@
+
+
+/*
+ * 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.overlay;
+
+import com.vividsolutions.jts.geom.*;
+import com.vividsolutions.jts.algorithm.*;
+import com.vividsolutions.jts.geomgraph.*;
+
+/**
+ * A ring of {@link Edge}s with the property that no node
+ * 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
+ * @see com.vividsolutions.jts.operation.overlay.MaximalEdgeRing
+ */
+public class MinimalEdgeRing
+  extends EdgeRing
+{
+
+  public MinimalEdgeRing(DirectedEdge start, GeometryFactory geometryFactory, CGAlgorithms cga) {
+    super(start, geometryFactory, cga);
+  }
+
+  public DirectedEdge getNext(DirectedEdge de)
+  {
+    return de.getNextMin();
+  }
+  public void setEdgeRing(DirectedEdge de, EdgeRing er)
+  {
+    de.setMinEdgeRing(er);
+  }
+
+}

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/overlay/OverlayNodeFactory.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,56 @@
+
+
+/*
+ * 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.overlay;
+
+/**
+ * @version 1.6
+ */
+import com.vividsolutions.jts.geom.Coordinate;
+import com.vividsolutions.jts.geomgraph.*;
+
+/**
+ * Creates nodes for use in the {@link PlanarGraph}s constructed during
+ * overlay operations.
+ *
+ * @version 1.6
+ */
+public class OverlayNodeFactory
+  extends NodeFactory
+{
+  public Node createNode(Coordinate coord)
+  {
+    return new Node(coord, new DirectedEdgeStar());
+  }
+}

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/overlay/OverlayOp.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,527 @@
+
+
+/*
+ * 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.overlay;
+
+import java.util.*;
+import com.vividsolutions.jts.geom.*;
+import com.vividsolutions.jts.util.*;
+import com.vividsolutions.jts.algorithm.*;
+import com.vividsolutions.jts.geomgraph.*;
+import com.vividsolutions.jts.geomgraph.index.SegmentIntersector;
+import com.vividsolutions.jts.operation.GeometryGraphOperation;
+
+/**
+ * Computes the overlay of two {@link Geometry}s.  The overlay
+ * can be used to determine any boolean combination of the geometries.
+ *
+ * @version 1.6
+ */
+public class OverlayOp
+  extends GeometryGraphOperation
+{
+/**
+ * The spatial functions supported by this class.
+ * These operations implement various boolean combinations of the resultants of the overlay.
+ */
+  public static final int INTERSECTION  = 1;
+  public static final int UNION         = 2;
+  public static final int DIFFERENCE    = 3;
+  public static final int SYMDIFFERENCE = 4;
+
+  public static Geometry overlayOp(Geometry geom0, Geometry geom1, int opCode)
+  {
+    OverlayOp gov = new OverlayOp(geom0, geom1);
+    Geometry geomOv = gov.getResultGeometry(opCode);
+    return geomOv;
+  }
+
+  public static boolean isResultOfOp(Label label, int opCode)
+  {
+    int loc0 = label.getLocation(0);
+    int loc1 = label.getLocation(1);
+    return isResultOfOp(loc0, loc1, opCode);
+  }
+
+  /**
+   * This method will handle arguments of Location.NULL correctly
+   *
+   * @return true if the locations correspond to the opCode
+   */
+  public static boolean isResultOfOp(int loc0, int loc1, int opCode)
+  {
+    if (loc0 == Location.BOUNDARY) loc0 = Location.INTERIOR;
+    if (loc1 == Location.BOUNDARY) loc1 = Location.INTERIOR;
+    switch (opCode) {
+    case INTERSECTION:
+      return loc0 == Location.INTERIOR
+          && loc1 == Location.INTERIOR;
+    case UNION:
+      return loc0 == Location.INTERIOR
+          || loc1 == Location.INTERIOR;
+    case DIFFERENCE:
+      return loc0 == Location.INTERIOR
+          && loc1 != Location.INTERIOR;
+    case SYMDIFFERENCE:
+      return   (     loc0 == Location.INTERIOR &&  loc1 != Location.INTERIOR)
+            || (     loc0 != Location.INTERIOR &&  loc1 == Location.INTERIOR);
+    }
+    return false;
+  }
+
+  private final PointLocator ptLocator = new PointLocator();
+  private GeometryFactory geomFact;
+  private Geometry resultGeom;
+
+  private PlanarGraph graph;
+  private EdgeList edgeList     = new EdgeList();
+
+  private List resultPolyList   = new ArrayList();
+  private List resultLineList   = new ArrayList();
+  private List resultPointList  = new ArrayList();
+
+  public OverlayOp(Geometry g0, Geometry g1) {
+    super(g0, g1);
+    graph = new PlanarGraph(new OverlayNodeFactory());
+    /**
+     * Use factory of primary geometry.
+     * Note that this does NOT handle mixed-precision arguments
+     * where the second arg has greater precision than the first.
+     */
+    geomFact = g0.getFactory();
+  }
+
+  public Geometry getResultGeometry(int funcCode)
+  {
+    computeOverlay(funcCode);
+    return resultGeom;
+  }
+
+  public PlanarGraph getGraph() { return graph; }
+
+  private void computeOverlay(int opCode)
+  {
+    // copy points from input Geometries.
+    // This ensures that any Point geometries
+    // in the input are considered for inclusion in the result set
+    copyPoints(0);
+    copyPoints(1);
+
+    // node the input Geometries
+    arg[0].computeSelfNodes(li, false);
+    arg[1].computeSelfNodes(li, false);
+
+    // compute intersections between edges of the two input geometries
+    arg[0].computeEdgeIntersections(arg[1], li, true);
+
+    List baseSplitEdges = new ArrayList();
+    arg[0].computeSplitEdges(baseSplitEdges);
+    arg[1].computeSplitEdges(baseSplitEdges);
+    List splitEdges = baseSplitEdges;
+    // add the noded edges to this result graph
+    insertUniqueEdges(baseSplitEdges);
+
+    computeLabelsFromDepths();
+    replaceCollapsedEdges();
+
+//Debug.println(edgeList);
+
+    // debugging only
+    //NodingValidator nv = new NodingValidator(edgeList.getEdges());
+    //nv.checkValid();
+
+    graph.addEdges(edgeList.getEdges());
+    computeLabelling();
+//Debug.printWatch();
+    labelIncompleteNodes();
+//Debug.printWatch();
+//nodeMap.print(System.out);
+
+    /**
+     * The ordering of building the result Geometries is important.
+     * Areas must be built before lines, which must be built before points.
+     * This is so that lines which are covered by areas are not included
+     * explicitly, and similarly for points.
+     */
+    findResultAreaEdges(opCode);
+    cancelDuplicateResultEdges();
+    PolygonBuilder polyBuilder = new PolygonBuilder(geomFact, cga);
+    polyBuilder.add(graph);
+    resultPolyList = polyBuilder.getPolygons();
+
+    LineBuilder lineBuilder = new LineBuilder(this, geomFact, ptLocator);
+    resultLineList = lineBuilder.build(opCode);
+
+    PointBuilder pointBuilder = new PointBuilder(this, geomFact, ptLocator);
+    resultPointList = pointBuilder.build(opCode);
+
+    // gather the results from all calculations into a single Geometry for the result set
+    resultGeom = computeGeometry(resultPointList, resultLineList, resultPolyList);
+  }
+
+  private void insertUniqueEdges(List edges)
+  {
+    for (Iterator i = edges.iterator(); i.hasNext(); ) {
+      Edge e = (Edge) i.next();
+      insertUniqueEdge(e);
+    }
+  }
+  /**
+   * Insert an edge from one of the noded input graphs.
+   * Checks edges that are inserted to see if an
+   * identical edge already exists.
+   * If so, the edge is not inserted, but its label is merged
+   * with the existing edge.
+   */
+  protected void insertUniqueEdge(Edge e)
+  {
+//Debug.println(e);
+    int foundIndex = edgeList.findEdgeIndex(e);
+    // If an identical edge already exists, simply update its label
+    if (foundIndex >= 0) {
+      Edge existingEdge = (Edge) edgeList.get(foundIndex);
+      Label existingLabel = existingEdge.getLabel();
+
+      Label labelToMerge = e.getLabel();
+      // check if new edge is in reverse direction to existing edge
+      // if so, must flip the label before merging it
+      if (! existingEdge.isPointwiseEqual(e)) {
+        labelToMerge = new Label(e.getLabel());
+        labelToMerge.flip();
+      }
+      Depth depth = existingEdge.getDepth();
+      // if this is the first duplicate found for this edge, initialize the depths
+      ///*
+      if (depth.isNull()) {
+        depth.add(existingLabel);
+      }
+      //*/
+      depth.add(labelToMerge);
+      existingLabel.merge(labelToMerge);
+//Debug.print("inserted edge: "); Debug.println(e);
+//Debug.print("existing edge: "); Debug.println(existingEdge);
+
+    }
+    else {  // no matching existing edge was found
+      // add this new edge to the list of edges in this graph
+      //e.setName(name + edges.size());
+      //e.getDepth().add(e.getLabel());
+      edgeList.add(e);
+    }
+  }
+
+  /**
+   * If either of the GeometryLocations for the existing label is
+   * exactly opposite to the one in the labelToMerge,
+   * this indicates a dimensional collapse has happened.
+   * In this case, convert the label for that Geometry to a Line label
+   */
+   /* NOT NEEDED?
+  private void checkDimensionalCollapse(Label labelToMerge, Label existingLabel)
+  {
+    if (existingLabel.isArea() && labelToMerge.isArea()) {
+      for (int i = 0; i < 2; i++) {
+        if (! labelToMerge.isNull(i)
+            &&  labelToMerge.getLocation(i, Position.LEFT)  == existingLabel.getLocation(i, Position.RIGHT)
+            &&  labelToMerge.getLocation(i, Position.RIGHT) == existingLabel.getLocation(i, Position.LEFT) )
+        {
+          existingLabel.toLine(i);
+        }
+      }
+    }
+  }
+  */
+  /**
+   * Update the labels for edges according to their depths.
+   * For each edge, the depths are first normalized.
+   * Then, if the depths for the edge are equal,
+   * this edge must have collapsed into a line edge.
+   * If the depths are not equal, update the label
+   * with the locations corresponding to the depths
+   * (i.e. a depth of 0 corresponds to a Location of EXTERIOR,
+   * a depth of 1 corresponds to INTERIOR)
+   */
+  private void computeLabelsFromDepths()
+  {
+    for (Iterator it = edgeList.iterator(); it.hasNext(); ) {
+      Edge e = (Edge) it.next();
+      Label lbl = e.getLabel();
+      Depth depth = e.getDepth();
+      /**
+       * Only check edges for which there were duplicates,
+       * since these are the only ones which might
+       * be the result of dimensional collapses.
+       */
+      if (! depth.isNull()) {
+        depth.normalize();
+        for (int i = 0; i < 2; i++) {
+          if (! lbl.isNull(i) && lbl.isArea() && ! depth.isNull(i)) {
+          /**
+           * if the depths are equal, this edge is the result of
+           * the dimensional collapse of two or more edges.
+           * It has the same location on both sides of the edge,
+           * so it has collapsed to a line.
+           */
+            if (depth.getDelta(i) == 0) {
+              lbl.toLine(i);
+            }
+            else {
+            /**
+             * This edge may be the result of a dimensional collapse,
+             * but it still has different locations on both sides.  The
+             * label of the edge must be updated to reflect the resultant
+             * side locations indicated by the depth values.
+             */
+              Assert.isTrue(! depth.isNull(i, Position.LEFT), "depth of LEFT side has not been initialized");
+              lbl.setLocation(i, Position.LEFT,   depth.getLocation(i, Position.LEFT));
+              Assert.isTrue(! depth.isNull(i, Position.RIGHT), "depth of RIGHT side has not been initialized");
+              lbl.setLocation(i, Position.RIGHT,  depth.getLocation(i, Position.RIGHT));
+            }
+          }
+        }
+      }
+    }
+  }
+  /**
+   * If edges which have undergone dimensional collapse are found,
+   * replace them with a new edge which is a L edge
+   */
+  private void replaceCollapsedEdges()
+  {
+    List newEdges = new ArrayList();
+    for (Iterator it = edgeList.iterator(); it.hasNext(); ) {
+      Edge e = (Edge) it.next();
+      if (e.isCollapsed()) {
+//Debug.print(e);
+        it.remove();
+        newEdges.add(e.getCollapsedEdge());
+      }
+    }
+    edgeList.addAll(newEdges);
+  }
+  /**
+   * Copy all nodes from an arg geometry into this graph.
+   * The node label in the arg geometry overrides any previously computed
+   * label for that argIndex.
+   * (E.g. a node may be an intersection node with
+   * a previously computed label of BOUNDARY,
+   * but in the original arg Geometry it is actually
+   * in the interior due to the Boundary Determination Rule)
+   */
+  private void copyPoints(int argIndex)
+  {
+    for (Iterator i = arg[argIndex].getNodeIterator(); i.hasNext(); ) {
+      Node graphNode = (Node) i.next();
+      Node newNode = graph.addNode(graphNode.getCoordinate());
+      newNode.setLabel(argIndex, graphNode.getLabel().getLocation(argIndex));
+    }
+  }
+
+  /**
+   * Compute initial labelling for all DirectedEdges at each node.
+   * In this step, DirectedEdges will acquire a complete labelling
+   * (i.e. one with labels for both Geometries)
+   * only if they
+   * are incident on a node which has edges for both Geometries
+   */
+  private void computeLabelling()
+  {
+    for (Iterator nodeit = graph.getNodes().iterator(); nodeit.hasNext(); ) {
+      Node node = (Node) nodeit.next();
+//if (node.getCoordinate().equals(new Coordinate(222, 100)) ) Debug.addWatch(node.getEdges());
+      node.getEdges().computeLabelling(arg);
+    }
+    mergeSymLabels();
+    updateNodeLabelling();
+  }
+  /**
+   * For nodes which have edges from only one Geometry incident on them,
+   * the previous step will have left their dirEdges with no labelling for the other
+   * Geometry.  However, the sym dirEdge may have a labelling for the other
+   * Geometry, so merge the two labels.
+   */
+  private void mergeSymLabels()
+  {
+    for (Iterator nodeit = graph.getNodes().iterator(); nodeit.hasNext(); ) {
+      Node node = (Node) nodeit.next();
+      ((DirectedEdgeStar) node.getEdges()).mergeSymLabels();
+//node.print(System.out);
+    }
+  }
+  private void updateNodeLabelling()
+  {
+    // update the labels for nodes
+    // The label for a node is updated from the edges incident on it
+    // (Note that a node may have already been labelled
+    // because it is a point in one of the input geometries)
+    for (Iterator nodeit = graph.getNodes().iterator(); nodeit.hasNext(); ) {
+      Node node = (Node) nodeit.next();
+      Label lbl = ((DirectedEdgeStar) node.getEdges()).getLabel();
+      node.getLabel().merge(lbl);
+    }
+  }
+
+  /**
+   * Incomplete nodes are nodes whose labels are incomplete.
+   * (e.g. the location for one Geometry is null).
+   * These are either isolated nodes,
+   * or nodes which have edges from only a single Geometry incident on them.
+   *
+   * Isolated nodes are found because nodes in one graph which don't intersect
+   * nodes in the other are not completely labelled by the initial process
+   * of adding nodes to the nodeList.
+   * To complete the labelling we need to check for nodes that lie in the
+   * interior of edges, and in the interior of areas.
+   * <p>
+   * When each node labelling is completed, the labelling of the incident
+   * edges is updated, to complete their labelling as well.
+   */
+  private void labelIncompleteNodes()
+  {
+    for (Iterator ni = graph.getNodes().iterator(); ni.hasNext(); ) {
+      Node n = (Node) ni.next();
+      Label label = n.getLabel();
+      if (n.isIsolated()) {
+        if (label.isNull(0))
+          labelIncompleteNode(n, 0);
+        else
+          labelIncompleteNode(n, 1);
+      }
+      // now update the labelling for the DirectedEdges incident on this node
+      ((DirectedEdgeStar) n.getEdges()).updateLabelling(label);
+//n.print(System.out);
+    }
+  }
+
+  /**
+   * Label an isolated node with its relationship to the target geometry.
+   */
+  private void labelIncompleteNode(Node n, int targetIndex)
+  {
+    int loc = ptLocator.locate(n.getCoordinate(), arg[targetIndex].getGeometry());
+    n.getLabel().setLocation(targetIndex, loc);
+  }
+
+  /**
+   * Find all edges whose label indicates that they are in the result area(s),
+   * according to the operation being performed.  Since we want polygon shells to be
+   * oriented CW, choose dirEdges with the interior of the result on the RHS.
+   * Mark them as being in the result.
+   * Interior Area edges are the result of dimensional collapses.
+   * They do not form part of the result area boundary.
+   */
+  private void findResultAreaEdges(int opCode)
+  {
+    for (Iterator it = graph.getEdgeEnds().iterator(); it.hasNext(); ) {
+      DirectedEdge de = (DirectedEdge) it.next();
+    // mark all dirEdges with the appropriate label
+      Label label = de.getLabel();
+      if (label.isArea()
+          && ! de.isInteriorAreaEdge()
+          && isResultOfOp(
+                label.getLocation(0, Position.RIGHT),
+                label.getLocation(1, Position.RIGHT),
+                opCode)) {
+        de.setInResult(true);
+//Debug.print("in result "); Debug.println(de);
+      }
+    }
+  }
+  /**
+   * If both a dirEdge and its sym are marked as being in the result, cancel
+   * them out.
+   */
+  private void cancelDuplicateResultEdges()
+  {
+    // remove any dirEdges whose sym is also included
+    // (they "cancel each other out")
+    for (Iterator it = graph.getEdgeEnds().iterator(); it.hasNext(); ) {
+      DirectedEdge de = (DirectedEdge) it.next();
+      DirectedEdge sym = de.getSym();
+      if (de.isInResult() && sym.isInResult()) {
+        de.setInResult(false);
+        sym.setInResult(false);
+//Debug.print("cancelled "); Debug.println(de); Debug.println(sym);
+      }
+    }
+  }
+  /**
+   * This method is used to decide if a point node should be included in the result or not.
+   *
+   * @return true if the coord point is covered by a result Line or Area geometry
+   */
+  public boolean isCoveredByLA(Coordinate coord)
+  {
+    if (isCovered(coord, resultLineList)) return true;
+    if (isCovered(coord, resultPolyList)) return true;
+    return false;
+  }
+  /**
+   * This method is used to decide if an L edge should be included in the result or not.
+   *
+   * @return true if the coord point is covered by a result Area geometry
+   */
+  public boolean isCoveredByA(Coordinate coord)
+  {
+    if (isCovered(coord, resultPolyList)) return true;
+    return false;
+  }
+  /**
+   * @return true if the coord is located in the interior or boundary of
+   * a geometry in the list.
+   */
+  private boolean isCovered(Coordinate coord, List geomList)
+  {
+    for (Iterator it = geomList.iterator(); it.hasNext(); ) {
+      Geometry geom = (Geometry) it.next();
+      int loc = ptLocator.locate(coord, geom);
+      if (loc != Location.EXTERIOR) return true;
+    }
+    return false;
+  }
+
+  private Geometry computeGeometry( List resultPointList,
+                                        List resultLineList,
+                                        List resultPolyList)
+  {
+    List geomList = new ArrayList();
+    // element geometries of the result are always in the order P,L,A
+    geomList.addAll(resultPointList);
+    geomList.addAll(resultLineList);
+    geomList.addAll(resultPolyList);
+    // build the most specific geometry possible
+    return geomFact.buildGeometry(geomList);
+  }
+
+}

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/overlay/PointBuilder.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,101 @@
+
+
+/*
+ * 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.overlay;
+
+import java.util.*;
+import com.vividsolutions.jts.geom.*;
+import com.vividsolutions.jts.algorithm.*;
+import com.vividsolutions.jts.geomgraph.*;
+
+/**
+ * Constructs {@link Point}s from the nodes of an overlay graph.
+ * @version 1.6
+ */
+public class PointBuilder {
+  private OverlayOp op;
+  private GeometryFactory geometryFactory;
+  private PointLocator ptLocator;
+
+  public PointBuilder(OverlayOp op, GeometryFactory geometryFactory, PointLocator ptLocator) {
+    this.op = op;
+    this.geometryFactory = geometryFactory;
+    this.ptLocator = ptLocator;
+  }
+  /**
+   * @return a list of the Points in the result of the specified overlay operation
+   */
+  public List build(int opCode)
+  {
+    List nodeList = collectNodes(opCode);
+    List resultPointList = simplifyPoints(nodeList);
+    return resultPointList;
+  }
+
+  private List collectNodes(int opCode)
+  {
+    List resultNodeList = new ArrayList();
+    // add nodes from edge intersections which have not already been included in the result
+    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);
+        }
+      }
+    }
+    return resultNodeList;
+  }
+  /**
+   * 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).
+   */
+  private List simplifyPoints(List resultNodeList)
+  {
+    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);
+      }
+    }
+    return nonCoveredPointList;
+  }
+}

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/overlay/PolygonBuilder.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,296 @@
+/*
+ * 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.overlay;
+
+import java.util.*;
+import com.vividsolutions.jts.geom.*;
+import com.vividsolutions.jts.algorithm.*;
+import com.vividsolutions.jts.geomgraph.*;
+import com.vividsolutions.jts.util.*;
+
+/**
+ * Forms {@link Polygon}s out of a graph of {@link DirectedEdge}s.
+ * The edges to use are marked as being in the result Area.
+ * <p>
+ *
+ * @version 1.6
+ */
+public class PolygonBuilder {
+
+  private GeometryFactory geometryFactory;
+  private CGAlgorithms cga;
+  private List dirEdgeList;
+  private NodeMap nodes;
+  private List shellList        = new ArrayList();
+
+  public PolygonBuilder(GeometryFactory geometryFactory, CGAlgorithms cga)
+  {
+    this.geometryFactory = geometryFactory;
+    this.cga = cga;
+  }
+
+  /**
+   * Add a complete graph.
+   * The graph is assumed to contain one or more polygons,
+   * possibly with holes.
+   */
+  public void add(PlanarGraph graph)
+  {
+    add(graph.getEdgeEnds(), graph.getNodes());
+  }
+
+  /**
+   * Add a set of edges and nodes, which form a graph.
+   * The graph is assumed to contain one or more polygons,
+   * possibly with holes.
+   */
+  public void add(Collection dirEdges, Collection nodes)
+  {
+    PlanarGraph.linkResultDirectedEdges(nodes);
+    List maxEdgeRings = buildMaximalEdgeRings(dirEdges);
+    List freeHoleList = new ArrayList();
+    List edgeRings = buildMinimalEdgeRings(maxEdgeRings, shellList, freeHoleList);
+    sortShellsAndHoles(edgeRings, shellList, freeHoleList);
+    placeFreeHoles(shellList, freeHoleList);
+    //Assert: every hole on freeHoleList has a shell assigned to it
+  }
+
+  public List getPolygons()
+  {
+    List resultPolyList = computePolygons(shellList);
+    return resultPolyList;
+  }
+
+
+  /**
+   * for all DirectedEdges in result, form them into MaximalEdgeRings
+   */
+  private List buildMaximalEdgeRings(Collection dirEdges)
+  {
+    List maxEdgeRings     = new ArrayList();
+    for (Iterator it = dirEdges.iterator(); it.hasNext(); ) {
+      DirectedEdge de = (DirectedEdge) it.next();
+      if (de.isInResult() && de.getLabel().isArea() ) {
+        // if this edge has not yet been processed
+        if (de.getEdgeRing() == null) {
+          MaximalEdgeRing er = new MaximalEdgeRing(de, geometryFactory, cga);
+          maxEdgeRings.add(er);
+          er.setInResult();
+//System.out.println("max node degree = " + er.getMaxDegree());
+        }
+      }
+    }
+    return maxEdgeRings;
+  }
+
+  private List buildMinimalEdgeRings(List maxEdgeRings, List shellList, List freeHoleList)
+  {
+    List edgeRings = new ArrayList();
+    for (Iterator it = maxEdgeRings.iterator(); it.hasNext(); ) {
+      MaximalEdgeRing er = (MaximalEdgeRing) it.next();
+      if (er.getMaxNodeDegree() > 2) {
+        er.linkDirectedEdgesForMinimalEdgeRings();
+        List minEdgeRings = er.buildMinimalRings();
+        // at this point we can go ahead and attempt to place holes, if this EdgeRing is a polygon
+        EdgeRing shell = findShell(minEdgeRings);
+        if (shell != null) {
+          placePolygonHoles(shell, minEdgeRings);
+          shellList.add(shell);
+        }
+        else {
+          freeHoleList.addAll(minEdgeRings);
+        }
+      }
+      else {
+        edgeRings.add(er);
+      }
+    }
+    return edgeRings;
+  }
+
+  /**
+   * This method takes a list of MinimalEdgeRings derived from a MaximalEdgeRing,
+   * and tests whether they form a Polygon.  This is the case if there is a single shell
+   * in the list.  In this case the shell is returned.
+   * The other possibility is that they are a series of connected holes, in which case
+   * no shell is returned.
+   *
+   * @return the shell EdgeRing, if there is one
+   * @return null, if all the rings are holes
+   */
+  private EdgeRing findShell(List minEdgeRings)
+  {
+    int shellCount = 0;
+    EdgeRing shell = null;
+    for (Iterator it = minEdgeRings.iterator(); it.hasNext(); ) {
+      EdgeRing er = (MinimalEdgeRing) it.next();
+      if (! er.isHole()) {
+        shell = er;
+        shellCount++;
+      }
+    }
+    Assert.isTrue(shellCount <= 1, "found two shells in MinimalEdgeRing list");
+    return shell;
+  }
+  /**
+   * This method assigns the holes for a Polygon (formed from a list of
+   * MinimalEdgeRings) to its shell.
+   * Determining the holes for a MinimalEdgeRing polygon serves two purposes:
+   * <ul>
+   * <li>it is faster than using a point-in-polygon check later on.
+   * <li>it ensures correctness, since if the PIP test was used the point
+   * chosen might lie on the shell, which might return an incorrect result from the
+   * PIP test
+   * </ul>
+   */
+  private void placePolygonHoles(EdgeRing shell, List minEdgeRings)
+  {
+    for (Iterator it = minEdgeRings.iterator(); it.hasNext(); ) {
+      MinimalEdgeRing er = (MinimalEdgeRing) it.next();
+      if (er.isHole()) {
+        er.setShell(shell);
+      }
+    }
+  }
+  /**
+   * For all rings in the input list,
+   * determine whether the ring is a shell or a hole
+   * and add it to the appropriate list.
+   * Due to the way the DirectedEdges were linked,
+   * a ring is a shell if it is oriented CW, a hole otherwise.
+   */
+  private void sortShellsAndHoles(List edgeRings, List shellList, List freeHoleList)
+  {
+    for (Iterator it = edgeRings.iterator(); it.hasNext(); ) {
+      EdgeRing er = (EdgeRing) it.next();
+//      er.setInResult();
+      if (er.isHole() ) {
+        freeHoleList.add(er);
+      }
+      else {
+        shellList.add(er);
+      }
+    }
+  }
+  /**
+   * This method determines finds a containing shell for all holes
+   * which have not yet been assigned to a shell.
+   * These "free" holes should
+   * all be <b>properly</b> contained in their parent shells, so it is safe to use the
+   * <code>findEdgeRingContaining</code> method.
+   * (This is the case because any holes which are NOT
+   * properly contained (i.e. are connected to their
+   * parent shell) would have formed part of a MaximalEdgeRing
+   * and been handled in a previous step).
+   */
+  private void placeFreeHoles(List shellList, List freeHoleList)
+  {
+    for (Iterator it = freeHoleList.iterator(); it.hasNext(); ) {
+      EdgeRing hole = (EdgeRing) it.next();
+      // only place this hole if it doesn't yet have a shell
+      if (hole.getShell() == null) {
+        EdgeRing shell = findEdgeRingContaining(hole, shellList);
+        Assert.isTrue(shell != null, "unable to assign hole to a shell");
+        hole.setShell(shell);
+      }
+    }
+  }
+  /**
+   * Find the innermost enclosing shell EdgeRing containing the argument EdgeRing, if any.
+   * The innermost enclosing ring is the <i>smallest</i> enclosing ring.
+   * The algorithm used depends on the fact that:
+   * <br>
+   *  ring A contains ring B iff envelope(ring A) contains envelope(ring B)
+   * <br>
+   * This routine is only safe to use if the chosen point of the hole
+   * is known to be properly contained in a shell
+   * (which is guaranteed to be the case if the hole does not touch its shell)
+   *
+   * @return containing EdgeRing, if there is one
+   * @return null if no containing EdgeRing is found
+   */
+  private EdgeRing findEdgeRingContaining(EdgeRing testEr, List shellList)
+  {
+    LinearRing testRing = testEr.getLinearRing();
+    Envelope testEnv = testRing.getEnvelopeInternal();
+    Coordinate testPt = testRing.getCoordinateN(0);
+
+    EdgeRing minShell = null;
+    Envelope minEnv = null;
+    for (Iterator it = shellList.iterator(); it.hasNext(); ) {
+      EdgeRing tryShell = (EdgeRing) it.next();
+      LinearRing tryRing = tryShell.getLinearRing();
+      Envelope tryEnv = tryRing.getEnvelopeInternal();
+      if (minShell != null) minEnv = minShell.getLinearRing().getEnvelopeInternal();
+      boolean isContained = false;
+      if (tryEnv.contains(testEnv)
+          && cga.isPointInRing(testPt, tryRing.getCoordinates()) )
+        isContained = true;
+      // check if this new containing ring is smaller than the current minimum ring
+      if (isContained) {
+        if (minShell == null
+            || minEnv.contains(tryEnv)) {
+          minShell = tryShell;
+        }
+      }
+    }
+    return minShell;
+  }
+  private List computePolygons(List shellList)
+  {
+    List resultPolyList   = new ArrayList();
+    // add Polygons for all shells
+    for (Iterator it = shellList.iterator(); it.hasNext(); ) {
+      EdgeRing er = (EdgeRing) it.next();
+      Polygon poly = er.toPolygon(geometryFactory);
+      resultPolyList.add(poly);
+    }
+    return resultPolyList;
+  }
+
+  /**
+   * Checks the current set of shells (with their associated holes) to
+   * see if any of them contain the point.
+   */
+  public boolean containsPoint(Coordinate p)
+  {
+    for (Iterator it = shellList.iterator(); it.hasNext(); ) {
+      EdgeRing er = (EdgeRing) it.next();
+      if (er.containsPoint(p))
+       return true;
+    }
+    return false;
+  }
+
+
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/overlay/package.html
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/overlay/package.html	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/overlay/package.html	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,41 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+<!--
+
+-->
+</head>
+<body bgcolor="white">
+
+Contains classes that perform a topological overlay to compute boolean spatial functions.
+<P>
+The Overlay Algorithm is used in spatial analysis methods for computing set-theoretic 
+operations (boolean combinations) of input {@link Geometry}s. The algorithm for 
+computing the overlay uses the intersection operations supported by topology graphs.  
+To compute an overlay it is necessary to explicitly compute the resultant graph formed 
+by the computed intersections.
+<P>
+The algorithm to compute a set-theoretic spatial analysis method has the following steps:
+<UL>
+  <LI>Build topology graphs of the two input geometries.  For each geometry all 
+      self-intersection nodes are computed and added to the graph.
+  <LI>Compute nodes for all intersections between edges and nodes of the graphs.
+  <LI>Compute the labeling for the computed nodes by merging the labels from the input graphs. 
+  <LI>Compute new edges between the compute intersection nodes.  Label the edges appropriately.
+  <LI>Build the resultant graph from the new nodes and edges.
+  <LI>Compute the labeling for isolated components of the graph.  Add the 
+      isolated components to the resultant graph.
+  <LI>Compute the result of the boolean combination by selecting the node and edges 
+      with the appropriate labels. Polygonize areas and sew linear geometries together.
+</UL>
+
+<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/operation/package.html
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/package.html	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/package.html	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,13 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+<!--
+
+-->
+</head>
+<body bgcolor="white">
+
+Provides classes for implementing operations on geometries
+
+</body>
+</html>

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/polygonize/EdgeRing.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,274 @@
+
+/*
+ * 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.polygonize;
+
+import java.util.*;
+import com.vividsolutions.jts.algorithm.*;
+import com.vividsolutions.jts.geom.*;
+import com.vividsolutions.jts.planargraph.*;
+
+/**
+ * Represents a ring of {@link PolygonizeDirectedEdge}s which form
+ * a ring of a polygon.  The ring may be either an outer shell or a hole.
+ *
+ * @version 1.6
+ */
+public class EdgeRing {
+
+  /**
+   * Find the innermost enclosing shell EdgeRing containing the argument EdgeRing, if any.
+   * The innermost enclosing ring is the <i>smallest</i> enclosing ring.
+   * The algorithm used depends on the fact that:
+   * <br>
+   *  ring A contains ring B iff envelope(ring A) contains envelope(ring B)
+   * <br>
+   * This routine is only safe to use if the chosen point of the hole
+   * is known to be properly contained in a shell
+   * (which is guaranteed to be the case if the hole does not touch its shell)
+   *
+   * @return containing EdgeRing, if there is one
+   * @return null if no containing EdgeRing is found
+   */
+  public static EdgeRing findEdgeRingContaining(EdgeRing testEr, List shellList)
+  {
+    LinearRing testRing = testEr.getRing();
+    Envelope testEnv = testRing.getEnvelopeInternal();
+    Coordinate testPt = testRing.getCoordinateN(0);
+
+    EdgeRing minShell = null;
+    Envelope minEnv = null;
+    for (Iterator it = shellList.iterator(); it.hasNext(); ) {
+      EdgeRing tryShell = (EdgeRing) it.next();
+      LinearRing tryRing = tryShell.getRing();
+      Envelope tryEnv = tryRing.getEnvelopeInternal();
+      if (minShell != null) minEnv = minShell.getRing().getEnvelopeInternal();
+      boolean isContained = false;
+      // the hole envelope cannot equal the shell envelope
+      if (tryEnv.equals(testEnv))
+        continue;
+
+      testPt = ptNotInList(testRing.getCoordinates(), tryRing.getCoordinates());
+      if (tryEnv.contains(testEnv)
+          && cga.isPointInRing(testPt, tryRing.getCoordinates()) )
+        isContained = true;
+      // check if this new containing ring is smaller than the current minimum ring
+      if (isContained) {
+        if (minShell == null
+            || minEnv.contains(tryEnv)) {
+          minShell = tryShell;
+        }
+      }
+    }
+    return minShell;
+  }
+
+  /**
+   * 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 (isInList(testPt, pts))
+          return testPt;
+    }
+    return null;
+  }
+
+  /**
+   * Tests whether a given point is in an array of points.
+   * Uses a value-based test.
+   *
+   * @param pt a {@link Coordinate} for the test point
+   * @param pts an array of {@link Coordinate}s to test
+   * @return <code>true</code> if the point is in the array
+   */
+  public static boolean isInList(Coordinate pt, Coordinate[] pts)
+  {
+    for (int i = 0; i < pts.length; i++) {
+        if (pt.equals(pts[i]))
+            return false;
+    }
+    return true;
+  }
+  private GeometryFactory factory;
+  private static CGAlgorithms cga = new CGAlgorithms();
+
+
+  private List deList = new ArrayList();
+
+  // cache the following data for efficiency
+  private LinearRing ring = null;
+
+  private Coordinate[] ringPts = null;
+  private List holes;
+
+  public EdgeRing(GeometryFactory factory)
+  {
+    this.factory = factory;
+  }
+
+  /**
+   * Adds a {@link DirectedEdge} which is known to form part of this ring.
+   * @param de the {@link DirectedEdge} to add.
+   */
+  public void add(DirectedEdge de)
+  {
+    deList.add(de);
+  }
+
+  /**
+   * Tests whether this ring is a hole.
+   * Due to the way the edges in the polyongization graph are linked,
+   * a ring is a hole if it is oriented counter-clockwise.
+   * @return <code>true</code> if this ring is a hole
+   */
+  public boolean isHole()
+  {
+    LinearRing ring = getRing();
+    return cga.isCCW(ring.getCoordinates());
+  }
+
+  /**
+   * Adds a hole to the polygon formed by this ring.
+   * @param hole the {@link LinearRing} forming the hole.
+   */
+  public void addHole(LinearRing hole) {
+    if (holes == null)
+      holes = new ArrayList();
+    holes.add(hole);
+  }
+
+  /**
+   * Computes the {@link Polygon formed by this ring and any contained holes.
+   *
+   * @return the {@link Polygon} formed by this ring and its holes.
+   */
+  public Polygon getPolygon()
+  {
+    LinearRing[] holeLR = null;
+    if (holes != null) {
+      holeLR = new LinearRing[holes.size()];
+      for (int i = 0; i < holes.size(); i++) {
+        holeLR[i] = (LinearRing) holes.get(i);
+      }
+    }
+    Polygon poly = factory.createPolygon(ring, holeLR);
+    return poly;
+  }
+
+  /**
+   * Tests if the {@link LinearRing} ring formed by this edge ring is topologically valid.
+   * @return
+   */
+  public boolean isValid()
+  {
+    getCoordinates();
+    if (ringPts.length <= 3) return false;
+    getRing();
+    return ring.isValid();
+  }
+
+  /**
+   * Computes the list of coordinates which are contained in this ring.
+   * The coordinatea are computed once only and cached.
+   *
+   * @return an array of the {@link Coordinate}s in this ring
+   */
+  private Coordinate[] getCoordinates()
+  {
+    if (ringPts == null) {
+      CoordinateList coordList = new CoordinateList();
+      for (Iterator i = deList.iterator(); i.hasNext(); ) {
+        DirectedEdge de = (DirectedEdge) i.next();
+        PolygonizeEdge edge = (PolygonizeEdge) de.getEdge();
+        addEdge(edge.getLine().getCoordinates(), de.getEdgeDirection(), coordList);
+      }
+      ringPts = coordList.toCoordinateArray();
+    }
+    return ringPts;
+  }
+
+  /**
+   * Gets the coordinates for this ring as a {@link LineString}.
+   * Used to return the coordinates in this ring
+   * as a valid geometry, when it has been detected that the ring is topologically
+   * invalid.
+   * @return a {@link LineString} containing the coordinates in this ring
+   */
+  public LineString getLineString()
+  {
+    getCoordinates();
+    return factory.createLineString(ringPts);
+  }
+
+  /**
+   * Returns this ring as a {@link LinearRing}, or null if an Exception occurs while
+   * creating it (such as a topology problem). Details of problems are written to
+   * standard output.
+   */
+  public LinearRing getRing()
+  {
+    if (ring != null) return ring;
+    getCoordinates();
+    if (ringPts.length < 3) System.out.println(ringPts);
+    try {
+      ring = factory.createLinearRing(ringPts);
+    }
+    catch (Exception ex) {
+      System.out.println(ringPts);
+    }
+    return ring;
+  }
+
+  private static void addEdge(Coordinate[] coords, boolean isForward, CoordinateList coordList)
+  {
+    if (isForward) {
+      for (int i = 0; i < coords.length; i++) {
+        coordList.add(coords[i], false);
+      }
+    }
+    else {
+      for (int i = coords.length - 1; i >= 0; i--) {
+        coordList.add(coords[i], false);
+      }
+    }
+  }
+}

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/polygonize/PolygonizeDirectedEdge.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,105 @@
+
+/*
+ * 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.polygonize;
+
+import com.vividsolutions.jts.geom.Coordinate;
+import com.vividsolutions.jts.planargraph.DirectedEdge;
+import com.vividsolutions.jts.planargraph.Node;
+
+/**
+ * A {@link DirectedEdge} of a {@link PolygonizeGraph}, which represents
+ * 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
+ */
+public class PolygonizeDirectedEdge
+    extends DirectedEdge
+{
+
+  private EdgeRing edgeRing = null;
+  private PolygonizeDirectedEdge next = null;
+  private long label = -1;
+
+  /**
+   * Constructs a directed edge connecting the <code>from</code> node to the
+   * <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>)
+   * @param edgeDirection
+   *                  whether this DirectedEdge's direction is the same as or
+   *                  opposite to that of the parent Edge (if any)
+   */
+  public PolygonizeDirectedEdge(Node from, Node to, Coordinate directionPt,
+      boolean edgeDirection)
+  {
+    super(from, to, directionPt, edgeDirection);
+  }
+
+  /**
+   * Returns the identifier attached to this directed edge.
+   */
+  public long getLabel() { return label; }
+  /**
+   * Attaches an identifier to this directed edge.
+   */
+  public void setLabel(long label) { this.label = label; }
+  /**
+   * Returns the next directed edge in the EdgeRing that this directed edge is a member
+   * of.
+   */
+  public PolygonizeDirectedEdge getNext()  {    return next;  }
+  /**
+   * Sets the next directed edge in the EdgeRing that this directed edge is a member
+   * of.
+   */
+  public void setNext(PolygonizeDirectedEdge next)  {   this.next = next;  }
+  /**
+   * Returns the ring of directed edges that this directed edge is
+   * a member of, or null if the ring has not been set.
+   * @see #setRing(EdgeRing)
+   */
+  public boolean isInRing() { return edgeRing != null; }
+  /**
+   * Sets the ring of directed edges that this directed edge is
+   * a member of.
+   */
+  public void setRing(EdgeRing edgeRing)
+  {
+      this.edgeRing = edgeRing;
+  }
+
+}

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/polygonize/PolygonizeEdge.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,56 @@
+
+/*
+ * 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.polygonize;
+
+import com.vividsolutions.jts.geom.*;
+import com.vividsolutions.jts.planargraph.*;
+
+/**
+ * An edge of a polygonization graph.
+ *
+ * @version 1.6
+ */
+class PolygonizeEdge
+    extends Edge
+{
+  private LineString line;
+
+  public PolygonizeEdge(LineString line)
+  {
+    this.line = line;
+  }
+  public LineString getLine() { return line; }
+}

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/polygonize/PolygonizeGraph.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,438 @@
+
+/*
+ * 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.polygonize;
+
+import java.util.*;
+import com.vividsolutions.jts.geom.*;
+import com.vividsolutions.jts.util.Assert;
+import com.vividsolutions.jts.planargraph.*;
+
+/**
+ * Represents a planar graph of edges that can be used to compute a
+ * polygonization, and implements the algorithms to compute the
+ * {@link EdgeRings} formed by the graph.
+ * <p>
+ * 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
+ */
+class PolygonizeGraph
+    extends PlanarGraph
+{
+
+  private static int getDegreeNonDeleted(Node node)
+  {
+    List edges = node.getOutEdges().getEdges();
+    int degree = 0;
+    for (Iterator i = edges.iterator(); i.hasNext(); ) {
+      PolygonizeDirectedEdge de = (PolygonizeDirectedEdge) i.next();
+      if (! de.isMarked()) degree++;
+    }
+    return degree;
+  }
+
+  private static int getDegree(Node node, long label)
+  {
+    List edges = node.getOutEdges().getEdges();
+    int degree = 0;
+    for (Iterator i = edges.iterator(); i.hasNext(); ) {
+      PolygonizeDirectedEdge de = (PolygonizeDirectedEdge) i.next();
+      if (de.getLabel() == label) degree++;
+    }
+    return degree;
+  }
+
+  /**
+   * Deletes all edges at a node
+   */
+  public static void deleteAllEdges(Node node)
+  {
+    List edges = node.getOutEdges().getEdges();
+    for (Iterator i = edges.iterator(); i.hasNext(); ) {
+      PolygonizeDirectedEdge de = (PolygonizeDirectedEdge) i.next();
+      de.setMarked(true);
+      PolygonizeDirectedEdge sym = (PolygonizeDirectedEdge) de.getSym();
+      if (sym != null)
+        sym.setMarked(true);
+    }
+  }
+
+  private GeometryFactory factory;
+
+  //private List labelledRings;
+
+  /**
+   * Create a new polygonization graph.
+   */
+  public PolygonizeGraph(GeometryFactory factory)
+  {
+    this.factory = factory;
+  }
+
+  /**
+   * Add a {@link LineString} forming an edge of the polygon graph.
+   * @param line the line to add
+   */
+  public void addEdge(LineString line)
+  {
+    if (line.isEmpty()) { return; }
+    Coordinate[] linePts = CoordinateArrays.removeRepeatedPoints(line.getCoordinates());
+    Coordinate startPt = linePts[0];
+    Coordinate endPt = linePts[linePts.length - 1];
+
+    Node nStart = getNode(startPt);
+    Node nEnd = getNode(endPt);
+
+    DirectedEdge de0 = new PolygonizeDirectedEdge(nStart, nEnd, linePts[1], true);
+    DirectedEdge de1 = new PolygonizeDirectedEdge(nEnd, nStart, linePts[linePts.length - 2], false);
+    Edge edge = new PolygonizeEdge(line);
+    edge.setDirectedEdges(de0, de1);
+    add(edge);
+  }
+
+  private Node getNode(Coordinate pt)
+  {
+    Node node = findNode(pt);
+    if (node == null) {
+      node = new Node(pt);
+      // ensure node is only added once to graph
+      add(node);
+    }
+    return node;
+  }
+
+  private void computeNextCWEdges()
+  {
+    // set the next pointers for the edges around each node
+    for (Iterator iNode = nodeIterator(); iNode.hasNext(); ) {
+      Node node = (Node) iNode.next();
+      computeNextCWEdges(node);
+    }
+  }
+
+  /**
+   * Convert the maximal edge rings found by the initial graph traversal
+   * into the minimal edge rings required by JTS polygon topology rules.
+   *
+   * @param ringEdges the list of start edges for the edgeRings to convert.
+   */
+  private void convertMaximalToMinimalEdgeRings(List ringEdges)
+  {
+    for (Iterator i = ringEdges.iterator(); i.hasNext(); ) {
+      PolygonizeDirectedEdge de = (PolygonizeDirectedEdge) i.next();
+      long label = de.getLabel();
+      List intNodes = findIntersectionNodes(de, label);
+
+      if (intNodes == null) continue;
+      // flip the next pointers on the intersection nodes to create minimal edge rings
+      for (Iterator iNode = intNodes.iterator(); iNode.hasNext(); ) {
+        Node node = (Node) iNode.next();
+        computeNextCCWEdges(node, label);
+      }
+    }
+  }
+
+  /**
+   * Finds all nodes in a maximal edgering which are self-intersection nodes
+   * @param startDE
+   * @param label
+   * @return the list of intersection nodes found,
+   * or <code>null</code> if no intersection nodes were found
+   */
+  private static List findIntersectionNodes(PolygonizeDirectedEdge startDE, long label)
+  {
+    PolygonizeDirectedEdge de = startDE;
+    List intNodes = null;
+    do {
+      Node node = de.getFromNode();
+      if (getDegree(node, label) > 1) {
+        if (intNodes == null)
+          intNodes = new ArrayList();
+        intNodes.add(node);
+      }
+
+      de = de.getNext();
+      Assert.isTrue(de != null, "found null DE in ring");
+      Assert.isTrue(de == startDE || ! de.isInRing(), "found DE already in ring");
+    } while (de != startDE);
+
+    return intNodes;
+  }
+
+  /**
+   * Computes the EdgeRings formed by the edges in this graph.
+   * @return a list of the {@link EdgeRing}s found by the polygonization process.
+   */
+  public List getEdgeRings()
+  {
+    // maybe could optimize this, since most of these pointers should be set correctly already
+    // by deleteCutEdges()
+    computeNextCWEdges();
+    // clear labels of all edges in graph
+    label(dirEdges, -1);
+    List maximalRings = findLabeledEdgeRings(dirEdges);
+    convertMaximalToMinimalEdgeRings(maximalRings);
+
+    // find all edgerings
+    List edgeRingList = new ArrayList();
+    for (Iterator i = dirEdges.iterator(); i.hasNext(); ) {
+      PolygonizeDirectedEdge de = (PolygonizeDirectedEdge) i.next();
+      if (de.isMarked()) continue;
+      if (de.isInRing()) continue;
+
+      EdgeRing er = findEdgeRing(de);
+      edgeRingList.add(er);
+    }
+    return edgeRingList;
+  }
+
+  /**
+   *
+   * @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)
+  {
+    List edgeRingStarts = new ArrayList();
+    // label the edge rings formed
+    long currLabel = 1;
+    for (Iterator i = dirEdges.iterator(); i.hasNext(); ) {
+      PolygonizeDirectedEdge de = (PolygonizeDirectedEdge) i.next();
+      if (de.isMarked()) continue;
+      if (de.getLabel() >= 0) continue;
+
+      edgeRingStarts.add(de);
+      List edges = findDirEdgesInRing(de);
+
+      label(edges, currLabel);
+      currLabel++;
+    }
+    return edgeRingStarts;
+  }
+
+  /**
+   * Finds and removes all cut edges from the graph.
+   * @return a list of the {@link LineString}s forming the removed cut edges
+   */
+  public List deleteCutEdges()
+  {
+    computeNextCWEdges();
+    // label the current set of edgerings
+    findLabeledEdgeRings(dirEdges);
+
+    /**
+     * Cut Edges are edges where both dirEdges have the same label.
+     * Delete them, and record them
+     */
+    List cutLines = new ArrayList();
+    for (Iterator i = dirEdges.iterator(); i.hasNext(); ) {
+      PolygonizeDirectedEdge de = (PolygonizeDirectedEdge) i.next();
+      if (de.isMarked()) continue;
+
+      PolygonizeDirectedEdge sym = (PolygonizeDirectedEdge) de.getSym();
+
+      if (de.getLabel() == sym.getLabel()) {
+        de.setMarked(true);
+        sym.setMarked(true);
+
+        // save the line as a cut edge
+        PolygonizeEdge e = (PolygonizeEdge) de.getEdge();
+        cutLines.add(e.getLine());
+      }
+    }
+    return cutLines;
+  }
+
+  private static void label(List dirEdges, long label)
+  {
+    for (Iterator i = dirEdges.iterator(); i.hasNext(); ) {
+      PolygonizeDirectedEdge de = (PolygonizeDirectedEdge) i.next();
+      de.setLabel(label);
+    }
+  }
+  private static void computeNextCWEdges(Node node)
+  {
+    DirectedEdgeStar deStar = node.getOutEdges();
+    PolygonizeDirectedEdge startDE = null;
+    PolygonizeDirectedEdge prevDE = null;
+
+    // the edges are stored in CCW order around the star
+    for (Iterator i = deStar.getEdges().iterator(); i.hasNext(); ) {
+      PolygonizeDirectedEdge outDE = (PolygonizeDirectedEdge) i.next();
+      if (outDE.isMarked()) continue;
+
+      if (startDE == null)
+        startDE = outDE;
+      if (prevDE != null) {
+        PolygonizeDirectedEdge sym = (PolygonizeDirectedEdge) prevDE.getSym();
+        sym.setNext(outDE);
+      }
+      prevDE = outDE;
+    }
+    if (prevDE != null) {
+      PolygonizeDirectedEdge sym = (PolygonizeDirectedEdge) prevDE.getSym();
+      sym.setNext(startDE);
+    }
+  }
+  /**
+   * Computes the next edge pointers going CCW around the given node, for the
+   * given edgering label.
+   * This algorithm has the effect of converting maximal edgerings into minimal edgerings
+   */
+  private static void computeNextCCWEdges(Node node, long label)
+  {
+    DirectedEdgeStar deStar = node.getOutEdges();
+    //PolyDirectedEdge lastInDE = null;
+    PolygonizeDirectedEdge firstOutDE = null;
+    PolygonizeDirectedEdge prevInDE = null;
+
+    // the edges are stored in CCW order around the star
+    List edges = deStar.getEdges();
+    //for (Iterator i = deStar.getEdges().iterator(); i.hasNext(); ) {
+    for (int i = edges.size() - 1; i >= 0; i--) {
+      PolygonizeDirectedEdge de = (PolygonizeDirectedEdge) edges.get(i);
+      PolygonizeDirectedEdge sym = (PolygonizeDirectedEdge) de.getSym();
+
+      PolygonizeDirectedEdge outDE = null;
+      if (  de.getLabel() == label) outDE = de;
+      PolygonizeDirectedEdge inDE = null;
+      if (  sym.getLabel() == label) inDE =  sym;
+
+      if (outDE == null && inDE == null) continue;  // this edge is not in edgering
+
+      if (inDE != null) {
+        prevInDE = inDE;
+      }
+
+      if (outDE != null) {
+        if (prevInDE != null) {
+          prevInDE.setNext(outDE);
+          prevInDE = null;
+        }
+        if (firstOutDE == null)
+          firstOutDE = outDE;
+      }
+    }
+    if (prevInDE != null) {
+      Assert.isTrue(firstOutDE != null);
+      prevInDE.setNext(firstOutDE);
+    }
+  }
+
+  /**
+   * Traverse a ring of DirectedEdges, accumulating them into a list.
+   * This assumes that all dangling directed edges have been removed
+   * from the graph, so that there is always a next dirEdge.
+   *
+   * @param startDE the DirectedEdge to start traversing at
+   * @return a List of DirectedEdges that form a ring
+   */
+  private static List findDirEdgesInRing(PolygonizeDirectedEdge startDE)
+  {
+    PolygonizeDirectedEdge de = startDE;
+    List edges = new ArrayList();
+    do {
+      edges.add(de);
+      de = de.getNext();
+      Assert.isTrue(de != null, "found null DE in ring");
+      Assert.isTrue(de == startDE || ! de.isInRing(), "found DE already in ring");
+    } while (de != startDE);
+
+    return edges;
+  }
+
+  private EdgeRing findEdgeRing(PolygonizeDirectedEdge startDE)
+  {
+    PolygonizeDirectedEdge de = startDE;
+    EdgeRing er = new EdgeRing(factory);
+    do {
+      er.add(de);
+      de.setRing(er);
+      de = de.getNext();
+      Assert.isTrue(de != null, "found null DE in ring");
+      Assert.isTrue(de == startDE || ! de.isInRing(), "found DE already in ring");
+    } while (de != startDE);
+
+    return er;
+  }
+
+  /**
+   * Marks all edges from the graph which are "dangles".
+   * Dangles are which are incident on a node with degree 1.
+   * This process is recursive, since removing a dangling edge
+   * may result in another edge becoming a dangle.
+   * In order to handle large recursion depths efficiently,
+   * an explicit recursion stack is used
+   *
+   * @return a List containing the {@link LineStrings} that formed dangles
+   */
+  public Collection deleteDangles()
+  {
+    List nodesToRemove = findNodesOfDegree(1);
+    Set dangleLines = new HashSet();
+
+    Stack nodeStack = new Stack();
+    for (Iterator i = nodesToRemove.iterator(); i.hasNext(); ) {
+      nodeStack.push(i.next());
+    }
+
+    while (! nodeStack.isEmpty()) {
+      Node node = (Node) nodeStack.pop();
+
+      deleteAllEdges(node);
+      List nodeOutEdges = node.getOutEdges().getEdges();
+      for (Iterator i = nodeOutEdges.iterator(); i.hasNext(); ) {
+        PolygonizeDirectedEdge de = (PolygonizeDirectedEdge) i.next();
+        // delete this edge and its sym
+        de.setMarked(true);
+        PolygonizeDirectedEdge sym = (PolygonizeDirectedEdge) de.getSym();
+        if (sym != null)
+          sym.setMarked(true);
+
+        // save the line as a dangle
+        PolygonizeEdge e = (PolygonizeEdge) de.getEdge();
+        dangleLines.add(e.getLine());
+
+        Node toNode = de.getToNode();
+        // add the toNode to the list to be processed, if it is now a dangle
+        if (getDegreeNonDeleted(toNode) == 1)
+          nodeStack.push(toNode);
+      }
+    }
+    return dangleLines;
+  }
+}

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/polygonize/Polygonizer.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,247 @@
+
+/*
+ * 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.polygonize;
+
+import java.util.*;
+import com.vividsolutions.jts.geom.*;
+
+/**
+ * Polygonizes a set of Geometrys which contain linework that
+ * represents the edges of a planar graph.
+ * Any dimension of Geometry is handled - the constituent linework is extracted
+ * to form the edges.
+ * The edges must be correctly noded; that is, they must only meet
+ * at their endpoints.  The Polygonizer will still run on incorrectly noded input
+ * but will not form polygons from incorrected noded edges.
+ * <p>
+ * The Polygonizer reports the follow kinds of errors:
+ * <ul>
+ * <li><b>Dangles</b> - edges which have one or both ends which are not incident on another edge endpoint
+ * <li><b>Cut Edges</b> - edges which are connected at both ends but which do not form part of polygon
+ * <li><b>Invalid Ring Lines</b> - edges which form rings which are invalid
+ * (e.g. the component lines contain a self-intersection)
+ * </ul>
+ *
+ * @version 1.6
+ */
+public class Polygonizer
+{
+
+  /**
+   * Add every linear element in a geometry into the polygonizer graph.
+   */
+  private class LineStringAdder
+      implements GeometryComponentFilter
+  {
+    public void filter(Geometry g) {
+      if (g instanceof LineString)
+        add((LineString) g);
+    }
+  }
+
+  // default factory
+  private LineStringAdder lineStringAdder = new LineStringAdder();
+
+  protected PolygonizeGraph graph;
+  // initialize with empty collections, in case nothing is computed
+  protected Collection dangles = new ArrayList();
+  protected List cutEdges = new ArrayList();
+  protected List invalidRingLines = new ArrayList();
+
+  protected List holeList = null;
+  protected List shellList = null;
+  protected List polyList = null;
+
+  /**
+   * Create a polygonizer with the same {@link GeometryFactory}
+   * as the input {@link Geometry}s
+   */
+  public Polygonizer()
+  {
+  }
+
+  /**
+   * Add a collection of geometries to be polygonized.
+   * May be called multiple times.
+   * Any dimension of Geometry may be added;
+   * the constituent linework will be extracted and used
+   *
+   * @param geomList a list of {@link Geometry}s with linework to be polygonized
+   */
+  public void add(Collection geomList)
+  {
+    for (Iterator i = geomList.iterator(); i.hasNext(); ) {
+      Geometry geometry = (Geometry) i.next();
+      add(geometry);
+    }
+  }
+
+  /**
+   * Add a geometry to the linework to be polygonized.
+   * May be called multiple times.
+   * Any dimension of Geometry may be added;
+   * the constituent linework will be extracted and used
+   *
+   * @param g a {@link Geometry} with linework to be polygonized
+   */
+  public void add(Geometry g)
+  {
+    g.apply(lineStringAdder);
+  }
+
+  /**
+   * Add a linestring to the graph of polygon edges.
+   *
+   * @param line the {@link LineString} to add
+   */
+  private void add(LineString line)
+  {
+    // create a new graph using the factory from the input Geometry
+    if (graph == null)
+      graph = new PolygonizeGraph(line.getFactory());
+    graph.addEdge(line);
+  }
+
+  /**
+   * Gets the list of polygons formed by the polygonization.
+   * @return a collection of {@link Polygons}
+   */
+  public Collection getPolygons()
+  {
+    polygonize();
+    return polyList;
+  }
+
+  /**
+   * Get the list of dangling lines found during polygonization.
+   * @return a collection of the input {@LineStrings} which are dangles
+   */
+  public Collection getDangles()
+  {
+    polygonize();
+    return dangles;
+  }
+
+  /**
+   * Get the list of cut edges found during polygonization.
+   * @return a collection of the input {@LineStrings} which are cut edges
+   */
+  public Collection getCutEdges()
+  {
+    polygonize();
+    return cutEdges;
+  }
+
+  /**
+   * Get the list of lines forming invalid rings found during polygonization.
+   * @return a collection of the input {@LineStrings} which form invalid rings
+   */
+  public Collection getInvalidRingLines()
+  {
+    polygonize();
+    return invalidRingLines;
+  }
+
+  /**
+   * Perform the polygonization, if it has not already been carried out.
+   */
+  private void polygonize()
+  {
+    // check if already computed
+    if (polyList != null) return;
+    polyList = new ArrayList();
+
+    // if no geometries were supplied it's possible graph could be null
+    if (graph == null) return;
+
+    dangles = graph.deleteDangles();
+    cutEdges = graph.deleteCutEdges();
+    List edgeRingList = graph.getEdgeRings();
+
+    List validEdgeRingList = new ArrayList();
+    invalidRingLines = new ArrayList();
+    findValidRings(edgeRingList, validEdgeRingList, invalidRingLines);
+
+    findShellsAndHoles(validEdgeRingList);
+    assignHolesToShells(holeList, shellList);
+
+    polyList = new ArrayList();
+    for (Iterator i = shellList.iterator(); i.hasNext(); ) {
+      EdgeRing er = (EdgeRing) i.next();
+      polyList.add(er.getPolygon());
+    }
+  }
+
+  private void findValidRings(List edgeRingList, List validEdgeRingList, List invalidRingList)
+  {
+    for (Iterator i = edgeRingList.iterator(); i.hasNext(); ) {
+      EdgeRing er = (EdgeRing) i.next();
+      if (er.isValid())
+        validEdgeRingList.add(er);
+      else
+        invalidRingList.add(er.getLineString());
+    }
+  }
+
+  private void findShellsAndHoles(List edgeRingList)
+  {
+    holeList = new ArrayList();
+    shellList = new ArrayList();
+    for (Iterator i = edgeRingList.iterator(); i.hasNext(); ) {
+      EdgeRing er = (EdgeRing) i.next();
+      if (er.isHole())
+        holeList.add(er);
+      else
+        shellList.add(er);
+
+    }
+  }
+
+  private static void assignHolesToShells(List holeList, List shellList)
+  {
+    for (Iterator i = holeList.iterator(); i.hasNext(); ) {
+      EdgeRing holeER = (EdgeRing) i.next();
+      assignHoleToShell(holeER, shellList);
+    }
+  }
+
+  private static void assignHoleToShell(EdgeRing holeER, List shellList)
+  {
+    EdgeRing shell = EdgeRing.findEdgeRingContaining(holeER, shellList);
+    if (shell != null)
+      shell.addHole(holeER.getRing());
+  }
+
+
+}

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/predicate/RectangleContains.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,122 @@
+package com.vividsolutions.jts.operation.predicate;
+
+import com.vividsolutions.jts.geom.*;
+
+/**
+ * Optimized implementation of spatial predicate "contains"
+ * for cases where the first {@link Geometry} is a rectangle.
+ * <p>
+ * As a further optimization,
+ * this class can be used directly to test many geometries against a single
+ * rectangle.
+ *
+ * @version 1.6
+ */
+public class RectangleContains {
+
+  public static boolean contains(Polygon rectangle, Geometry b)
+  {
+    RectangleContains rc = new RectangleContains(rectangle);
+    return rc.contains(b);
+  }
+
+  private Polygon rectangle;
+  private Envelope rectEnv;
+
+  /**
+   * Create a new contains computer for two geometries.
+   *
+   * @param rectangle a rectangular geometry
+   */
+  public RectangleContains(Polygon rectangle) {
+    this.rectangle = rectangle;
+    rectEnv = rectangle.getEnvelopeInternal();
+  }
+
+  public boolean contains(Geometry geom)
+  {
+    if (! rectEnv.contains(geom.getEnvelopeInternal()))
+      return false;
+    // check that geom is not contained entirely in the rectangle boundary
+    if (isContainedInBoundary(geom))
+      return false;
+    return true;
+  }
+
+  private boolean isContainedInBoundary(Geometry geom)
+  {
+    // polygons can never be wholely contained in the boundary
+    if (geom instanceof Polygon) return false;
+    if (geom instanceof Point) return isPointContainedInBoundary((Point) geom);
+    if (geom instanceof LineString) return isLineStringContainedInBoundary((LineString) geom);
+
+    for (int i = 0; i < geom.getNumGeometries(); i++) {
+      Geometry comp = geom.getGeometryN(i);
+      if (! isContainedInBoundary(comp))
+        return false;
+    }
+    return true;
+  }
+
+  private boolean isPointContainedInBoundary(Point point)
+  {
+    return isPointContainedInBoundary(point.getCoordinate());
+  }
+
+  private boolean isPointContainedInBoundary(Coordinate pt)
+  {
+    // we already know that the point is contained in the rectangle envelope
+
+    if (! (pt.x == rectEnv.getMinX() ||
+           pt.x == rectEnv.getMaxX()) )
+      return false;
+    if (! (pt.y == rectEnv.getMinY() ||
+           pt.y == rectEnv.getMaxY()) )
+      return false;
+
+    return true;
+  }
+
+  private boolean isLineStringContainedInBoundary(LineString line)
+  {
+    CoordinateSequence seq = line.getCoordinateSequence();
+    Coordinate p0 = new Coordinate();
+    Coordinate p1 = new Coordinate();
+    for (int i = 0; i < seq.size() - 1; i++) {
+      seq.getCoordinate(i, p0);
+      seq.getCoordinate(i + 1, p1);
+
+      if (! isLineSegmentContainedInBoundary(p0, p1))
+        return false;
+    }
+    return true;
+  }
+
+  private boolean isLineSegmentContainedInBoundary(Coordinate p0, Coordinate p1)
+  {
+    if (p0.equals(p1))
+      return isPointContainedInBoundary(p0);
+
+    // we already know that the segment is contained in the rectangle envelope
+    if (p0.x == p1.x) {
+      if (p0.x == rectEnv.getMinX() ||
+          p0.x == rectEnv.getMaxX() )
+        return true;
+    }
+    else if (p0.y == p1.y) {
+      if (p0.y == rectEnv.getMinY() ||
+          p0.y == rectEnv.getMaxY() )
+        return true;
+    }
+    /**
+     * Either
+     *   both x and y values are different
+     * or
+     *   one of x and y are the same, but the other ordinate is not the same as a boundary ordinate
+     *
+     * In either case, the segment is not wholely in the boundary
+     */
+    return false;
+  }
+
+}

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/predicate/RectangleIntersects.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,211 @@
+package com.vividsolutions.jts.operation.predicate;
+
+import java.util.*;
+import com.vividsolutions.jts.geom.*;
+import com.vividsolutions.jts.algorithm.*;
+import com.vividsolutions.jts.geom.util.*;
+
+/**
+ * Optimized implementation of spatial predicate "intersects"
+ * for cases where the first {@link Geometry} is a rectangle.
+ * <p>
+ * As a further optimization,
+ * this class can be used directly to test many geometries against a single
+ * rectangle.
+ *
+ * @version 1.6
+ */
+public class RectangleIntersects {
+
+  /**
+   * Crossover size at which brute-force intersection scanning
+   * is slower than indexed intersection detection.
+   * Must be determined empirically.  Should err on the
+   * safe side by making value smaller rather than larger.
+   */
+  public static final int MAXIMUM_SCAN_SEGMENT_COUNT = 200;
+
+  public static boolean intersects(Polygon rectangle, Geometry b)
+  {
+    RectangleIntersects rp = new RectangleIntersects(rectangle);
+    return rp.intersects(b);
+  }
+
+  private Polygon rectangle;
+  private Envelope rectEnv;
+
+  /**
+   * Create a new intersects computer for a rectangle.
+   *
+   * @param rectangle a rectangular geometry
+   */
+  public RectangleIntersects(Polygon rectangle) {
+    this.rectangle = rectangle;
+    rectEnv = rectangle.getEnvelopeInternal();
+  }
+
+  public boolean intersects(Geometry geom)
+  {
+    if (! rectEnv.intersects(geom.getEnvelopeInternal()))
+        return false;
+    // test envelope relationships
+    EnvelopeIntersectsVisitor visitor = new EnvelopeIntersectsVisitor(rectEnv);
+    visitor.applyTo(geom);
+    if (visitor.intersects())
+      return true;
+
+    // test if any rectangle corner is contained in the target
+    ContainsPointVisitor ecpVisitor = new ContainsPointVisitor(rectangle);
+    ecpVisitor.applyTo(geom);
+    if (ecpVisitor.containsPoint())
+      return true;
+
+    // test if any lines intersect
+    LineIntersectsVisitor liVisitor = new LineIntersectsVisitor(rectangle);
+    liVisitor.applyTo(geom);
+    if (liVisitor.intersects())
+      return true;
+
+    return false;
+  }
+}
+
+class EnvelopeIntersectsVisitor
+    extends ShortCircuitedGeometryVisitor
+{
+  private Envelope rectEnv;
+  private boolean intersects = false;
+
+  public EnvelopeIntersectsVisitor(Envelope rectEnv)
+  {
+    this.rectEnv = rectEnv;
+  }
+
+  public boolean intersects() { return intersects; }
+
+  protected void visit(Geometry element)
+  {
+    Envelope elementEnv = element.getEnvelopeInternal();
+    // disjoint
+    if (! rectEnv.intersects(elementEnv)) {
+      return;
+    }
+    // fully contained - must intersect
+    if (rectEnv.contains(elementEnv)) {
+      intersects = true;
+      return;
+    }
+    /**
+     * 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 (elementEnv.getMinX() >= rectEnv.getMinX()
+        && elementEnv.getMaxX() <= rectEnv.getMaxX()) {
+      intersects = true;
+      return;
+    }
+    if (elementEnv.getMinY() >= rectEnv.getMinY()
+        && elementEnv.getMaxY() <= rectEnv.getMaxY()) {
+      intersects = true;
+      return;
+    }
+  }
+
+  protected boolean isDone() {
+    return intersects == true;
+  }
+}
+
+class ContainsPointVisitor
+    extends ShortCircuitedGeometryVisitor
+{
+  private CoordinateSequence rectSeq;
+  private Envelope rectEnv;
+  private boolean containsPoint = false;
+
+  public ContainsPointVisitor(Polygon rectangle)
+  {
+    this.rectSeq = rectangle.getExteriorRing().getCoordinateSequence();
+    rectEnv = rectangle.getEnvelopeInternal();
+  }
+
+  public boolean containsPoint() { return containsPoint; }
+
+  protected void visit(Geometry geom)
+  {
+    if (! (geom instanceof Polygon))
+      return;
+    Envelope elementEnv = geom.getEnvelopeInternal();
+    if (! rectEnv.intersects(elementEnv))
+      return;
+    // test each corner of rectangle for inclusion
+    Coordinate rectPt = new Coordinate();
+    for (int i = 0; i < 4; i++) {
+      rectSeq.getCoordinate(i, rectPt);
+      if (! elementEnv.contains(rectPt))
+        continue;
+      // check rect point in poly (rect is known not to touch polygon at this point)
+      if (SimplePointInAreaLocator.containsPointInPolygon(rectPt, (Polygon) geom)) {
+        containsPoint = true;
+        return;
+      }
+    }
+  }
+
+  protected boolean isDone() {
+    return containsPoint == true;
+  }
+}
+
+class LineIntersectsVisitor
+    extends ShortCircuitedGeometryVisitor
+{
+  private Polygon rectangle;
+  private CoordinateSequence rectSeq;
+  private Envelope rectEnv;
+  private boolean intersects = false;
+
+  public LineIntersectsVisitor(Polygon rectangle)
+  {
+    this.rectangle = rectangle;
+    this.rectSeq = rectangle.getExteriorRing().getCoordinateSequence();
+    rectEnv = rectangle.getEnvelopeInternal();
+  }
+
+  public boolean intersects() { return intersects; }
+
+  protected void visit(Geometry geom)
+  {
+    Envelope elementEnv = geom.getEnvelopeInternal();
+    if (! rectEnv.intersects(elementEnv))
+      return;
+    // check if general relate algorithm should be used, since it's faster for large inputs
+    if (geom.getNumPoints() > RectangleIntersects.MAXIMUM_SCAN_SEGMENT_COUNT) {
+      intersects = rectangle.relate(geom).isIntersects();
+      return;
+    }
+    computeSegmentIntersection(geom);
+  }
+
+  private void computeSegmentIntersection(Geometry geom)
+  {
+    // check segment intersection
+    // get all lines from geom (e.g. if it's a multi-ring polygon)
+    List lines = LinearComponentExtracter.getLines(geom);
+    SegmentIntersectionTester si = new SegmentIntersectionTester();
+    boolean hasIntersection = si.hasIntersectionWithLineStrings(rectSeq, lines);
+    if (hasIntersection) {
+      intersects = true;
+      return;
+    }
+  }
+
+  protected boolean isDone() {
+    return intersects == true;
+  }
+}
+

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/predicate/SegmentIntersectionTester.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,54 @@
+package com.vividsolutions.jts.operation.predicate;
+
+import java.util.*;
+import com.vividsolutions.jts.algorithm.*;
+import com.vividsolutions.jts.geom.*;
+
+/**
+ * Tests if any line segments in two sets of CoordinateSequences intersect.
+ * Optimized for small geometry size.
+ * Short-circuited to return as soon an intersection is found.
+ *
+ * @version 1.6
+ */
+public class SegmentIntersectionTester {
+
+  // for purposes of intersection testing, don't need to set precision model
+  private LineIntersector li = new RobustLineIntersector();
+
+  private boolean hasIntersection = false;
+  private Coordinate pt00 = new Coordinate();
+  private Coordinate pt01 = new Coordinate();
+  private Coordinate pt10 = new Coordinate();
+  private Coordinate pt11 = new Coordinate();
+
+  public SegmentIntersectionTester() {
+  }
+
+  public boolean hasIntersectionWithLineStrings(CoordinateSequence seq, List lines)
+  {
+    for (Iterator i = lines.iterator(); i.hasNext(); ) {
+      LineString line = (LineString) i.next();
+      hasIntersection(seq, line.getCoordinateSequence());
+      if (hasIntersection)
+        break;
+    }
+    return hasIntersection;
+  }
+
+  public boolean hasIntersection(CoordinateSequence seq0, CoordinateSequence seq1) {
+    for (int i = 1; i < seq0.size() && ! hasIntersection; i++) {
+      seq0.getCoordinate(i - 1, pt00);
+      seq0.getCoordinate(i, pt01);
+      for (int j = 1; j < seq1.size() && ! hasIntersection; j++) {
+        seq1.getCoordinate(j - 1, pt10);
+        seq1.getCoordinate(j, pt11);
+
+        li.computeIntersection(pt00, pt01, pt10, pt11);
+        if (li.hasIntersection())
+          hasIntersection = true;
+      }
+    }
+    return hasIntersection;
+  }
+}

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/relate/EdgeEndBuilder.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,165 @@
+
+
+
+/*
+ * 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.relate;
+
+/**
+ * An EdgeEndBuilder creates EdgeEnds for all the "split edges"
+ * created by the
+ * intersections determined for an Edge.
+ *
+ * @version 1.6
+ */
+import java.util.*;
+import com.vividsolutions.jts.geom.*;
+import com.vividsolutions.jts.geomgraph.*;
+import com.vividsolutions.jts.util.*;
+
+/**
+ * Computes the {@link EdgeEnd}s which arise from a noded {@link Edge}.
+ *
+ * @version 1.6
+ */
+public class EdgeEndBuilder {
+
+  public EdgeEndBuilder() {
+  }
+
+  public List computeEdgeEnds(Iterator edges)
+  {
+    List l = new ArrayList();
+    for (Iterator i = edges; i.hasNext(); ) {
+      Edge e = (Edge) i.next();
+      computeEdgeEnds(e, l);
+    }
+    return l;
+  }
+
+  /**
+   * Creates stub edges for all the intersections in this
+   * Edge (if any) and inserts them into the graph.
+   */
+  public void computeEdgeEnds(Edge edge, List l)
+  {
+    EdgeIntersectionList eiList = edge.getEdgeIntersectionList();
+//Debug.print(eiList);
+    // ensure that the list has entries for the first and last point of the edge
+    eiList.addEndpoints();
+
+    Iterator it = eiList.iterator();
+    EdgeIntersection eiPrev = null;
+    EdgeIntersection eiCurr = null;
+    // no intersections, so there is nothing to do
+    if (! it.hasNext()) return;
+    EdgeIntersection eiNext = (EdgeIntersection) it.next();
+    do {
+      eiPrev = eiCurr;
+      eiCurr = eiNext;
+      eiNext = null;
+      if (it.hasNext()) eiNext = (EdgeIntersection) it.next();
+
+      if (eiCurr != null) {
+        createEdgeEndForPrev(edge, l, eiCurr, eiPrev);
+        createEdgeEndForNext(edge, l, eiCurr, eiNext);
+      }
+
+    } while (eiCurr != null);
+
+  }
+
+  /**
+   * Create a EdgeStub for the edge before the intersection eiCurr.
+   * The previous intersection is provided
+   * in case it is the endpoint for the stub edge.
+   * Otherwise, the previous point from the parent edge will be the endpoint.
+   * <br>
+   * eiCurr will always be an EdgeIntersection, but eiPrev may be null.
+   */
+  void createEdgeEndForPrev(
+                      Edge edge,
+                      List l,
+                      EdgeIntersection eiCurr,
+                      EdgeIntersection eiPrev)
+  {
+
+    int iPrev = eiCurr.segmentIndex;
+    if (eiCurr.dist == 0.0) {
+      // if at the start of the edge there is no previous edge
+      if (iPrev == 0) return;
+      iPrev--;
+    }
+    Coordinate pPrev = edge.getCoordinate(iPrev);
+    // if prev intersection is past the previous vertex, use it instead
+    if (eiPrev != null && eiPrev.segmentIndex >= iPrev)
+      pPrev = eiPrev.coord;
+
+    Label label = new Label(edge.getLabel());
+    // since edgeStub is oriented opposite to it's parent edge, have to flip sides for edge label
+    label.flip();
+    EdgeEnd e = new EdgeEnd(edge, eiCurr.coord, pPrev, label);
+//e.print(System.out);  System.out.println();
+    l.add(e);
+  }
+    /**
+     * Create a StubEdge for the edge after the intersection eiCurr.
+     * The next intersection is provided
+     * in case it is the endpoint for the stub edge.
+     * Otherwise, the next point from the parent edge will be the endpoint.
+     * <br>
+     * eiCurr will always be an EdgeIntersection, but eiNext may be null.
+     */
+  void createEdgeEndForNext(
+                      Edge edge,
+                      List l,
+                      EdgeIntersection eiCurr,
+                      EdgeIntersection eiNext)
+  {
+
+    int iNext = eiCurr.segmentIndex + 1;
+    // if there is no next edge there is nothing to do
+    if (iNext >= edge.getNumPoints() && eiNext == null) return;
+
+    Coordinate pNext = edge.getCoordinate(iNext);
+
+    // if the next intersection is in the same segment as the current, use it as the endpoint
+    if (eiNext != null && eiNext.segmentIndex == eiCurr.segmentIndex)
+      pNext = eiNext.coord;
+
+    EdgeEnd e = new EdgeEnd(edge, eiCurr.coord, pNext, new Label(edge.getLabel()));
+//Debug.println(e);
+    l.add(e);
+  }
+
+}

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/relate/EdgeEndBundle.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,199 @@
+
+
+
+/*
+ * 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.relate;
+
+/**
+ * A collection of EdgeStubs which obey the following invariant:
+ * They originate at the same node and have the same direction.
+ * @version 1.6
+ */
+import java.io.PrintStream;
+import java.util.*;
+import com.vividsolutions.jts.geom.*;
+import com.vividsolutions.jts.geomgraph.*;
+import com.vividsolutions.jts.util.Assert;
+
+
+/**
+ * Contains all {@link EdgeEnd}s which start at the same point and are parallel.
+ *
+ * @version 1.6
+ */
+public class EdgeEndBundle
+  extends EdgeEnd
+{
+  //private Label label;
+  private List edgeEnds = new ArrayList();
+
+  public EdgeEndBundle(EdgeEnd e)
+  {
+    super(e.getEdge(), e.getCoordinate(), e.getDirectedCoordinate(), new Label(e.getLabel()));
+    insert(e);
+  }
+
+  public Label getLabel() { return label; }
+  public Iterator iterator() { return edgeEnds.iterator(); }
+  public List getEdgeEnds() { return edgeEnds; }
+
+  public void insert(EdgeEnd e)
+  {
+    // Assert: start point is the same
+    // Assert: direction is the same
+    edgeEnds.add(e);
+  }
+  /**
+   * This computes the overall edge label for the set of
+   * edges in this EdgeStubBundle.  It essentially merges
+   * the ON and side labels for each edge.  These labels must be compatible
+   */
+  public void computeLabel()
+  {
+    // create the label.  If any of the edges belong to areas,
+    // the label must be an area label
+    boolean isArea = false;
+    for (Iterator it = iterator(); it.hasNext(); ) {
+      EdgeEnd e = (EdgeEnd) it.next();
+      if (e.getLabel().isArea()) isArea = true;
+    }
+    if (isArea)
+      label = new Label(Location.NULL, Location.NULL, Location.NULL);
+    else
+      label = new Label(Location.NULL);
+
+    // compute the On label, and the side labels if present
+    for (int i = 0; i < 2; i++) {
+      computeLabelOn(i);
+      if (isArea)
+        computeLabelSides(i);
+    }
+
+  }
+  /**
+   * Compute the overall ON location for the list of EdgeStubs.
+   * (This is essentially equivalent to computing the self-overlay of a single Geometry)
+   * edgeStubs can be either on the boundary (eg Polygon edge)
+   * OR in the interior (e.g. segment of a LineString)
+   * of their parent Geometry.
+   * In addition, GeometryCollections use the mod-2 rule to determine
+   * whether a segment is on the boundary or not.
+   * Finally, in GeometryCollections it can still occur that an edge is both
+   * on the boundary and in the interior (e.g. a LineString segment lying on
+   * top of a Polygon edge.) In this case as usual the Boundary is given precendence.
+   * <br>
+   * These observations result in the following rules for computing the ON location:
+   * <ul>
+   * <li> if there are an odd number of Bdy edges, the attribute is Bdy
+   * <li> if there are an even number >= 2 of Bdy edges, the attribute is Int
+   * <li> if there are any Int edges, the attribute is Int
+   * <li> otherwise, the attribute is NULL.
+   * </ul>
+   */
+  private void computeLabelOn(int geomIndex)
+  {
+    // compute the ON location value
+    int boundaryCount = 0;
+    boolean foundInterior = false;
+
+    for (Iterator it = iterator(); it.hasNext(); ) {
+      EdgeEnd e = (EdgeEnd) it.next();
+      int loc = e.getLabel().getLocation(geomIndex);
+      if (loc == Location.BOUNDARY) boundaryCount++;
+      if (loc == Location.INTERIOR) foundInterior = true;
+    }
+    int loc = Location.NULL;
+    if (foundInterior)  loc = Location.INTERIOR;
+    if (boundaryCount > 0) {
+        loc = GeometryGraph.determineBoundary(boundaryCount);
+    }
+    label.setLocation(geomIndex, loc);
+
+  }
+  /**
+   * Compute the labelling for each side
+   */
+  private void computeLabelSides(int geomIndex)
+  {
+    computeLabelSide(geomIndex, Position.LEFT);
+    computeLabelSide(geomIndex, Position.RIGHT);
+  }
+
+  /**
+   * To compute the summary label for a side, the algorithm is:
+   *   FOR all edges
+   *     IF any edge's location is INTERIOR for the side, side location = INTERIOR
+   *     ELSE IF there is at least one EXTERIOR attribute, side location = EXTERIOR
+   *     ELSE  side location = NULL
+   *  <br>
+   *  Note that it is possible for two sides to have apparently contradictory information
+   *  i.e. one edge side may indicate that it is in the interior of a geometry, while
+   *  another edge side may indicate the exterior of the same geometry.  This is
+   *  not an incompatibility - GeometryCollections may contain two Polygons that touch
+   *  along an edge.  This is the reason for Interior-primacy rule above - it
+   *  results in the summary label having the Geometry interior on <b>both</b> sides.
+   */
+  private void computeLabelSide(int geomIndex, int side)
+  {
+    for (Iterator it = iterator(); it.hasNext(); ) {
+      EdgeEnd e = (EdgeEnd) it.next();
+      if (e.getLabel().isArea()) {
+        int loc = e.getLabel().getLocation(geomIndex, side);
+        if (loc == Location.INTERIOR) {
+            label.setLocation(geomIndex, side, Location.INTERIOR);
+            return;
+        }
+        else if (loc == Location.EXTERIOR)
+              label.setLocation(geomIndex, side, Location.EXTERIOR);
+      }
+    }
+  }
+
+  /**
+   * Update the IM with the contribution for the computed label for the EdgeStubs.
+   */
+  void updateIM(IntersectionMatrix im)
+  {
+    Edge.updateIM(label, im);
+  }
+  public void print(PrintStream out)
+  {
+    out.println("EdgeEndBundle--> Label: " + label);
+    for (Iterator it = iterator(); it.hasNext(); ) {
+      EdgeEnd ee = (EdgeEnd) it.next();
+      ee.print(out);
+      out.println();
+    }
+  }
+}

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/relate/EdgeEndBundleStar.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,88 @@
+
+
+
+/*
+ * 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.relate;
+
+import java.io.PrintStream;
+import java.util.*;
+import com.vividsolutions.jts.geom.*;
+import com.vividsolutions.jts.util.*;
+import com.vividsolutions.jts.geomgraph.*;
+import com.vividsolutions.jts.util.Assert;
+
+/**
+ * An ordered list of {@link EdgeEndBundle}s around a {@link RelateNode}.
+ * They are maintained in CCW order (starting with the positive x-axis) around the node
+ * for efficient lookup and topology building.
+ * @version 1.6
+ */
+public class EdgeEndBundleStar
+  extends EdgeEndStar
+{
+
+  public EdgeEndBundleStar() {
+  }
+
+  /**
+   * Insert a EdgeEnd in order in the list.
+   * If there is an existing EdgeStubBundle which is parallel, the EdgeEnd is
+   * added to the bundle.  Otherwise, a new EdgeEndBundle is created
+   * to contain the EdgeEnd.
+   * <br>
+   */
+  public void insert(EdgeEnd e)
+  {
+    EdgeEndBundle eb = (EdgeEndBundle) edgeMap.get(e);
+    if (eb == null) {
+      eb = new EdgeEndBundle(e);
+      insertEdgeEnd(e, eb);
+    }
+    else {
+      eb.insert(e);
+    }
+  }
+
+  /**
+   * Update the IM with the contribution for the EdgeStubs around the node.
+   */
+  void updateIM(IntersectionMatrix im)
+  {
+    for (Iterator it = iterator(); it.hasNext(); ) {
+      EdgeEndBundle esb = (EdgeEndBundle) it.next();
+      esb.updateIM(im);
+    }
+  }
+
+}

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/relate/RelateComputer.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,396 @@
+
+
+
+/*
+ * 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.relate;
+
+/**
+ * @version 1.6
+ */
+import java.util.*;
+import com.vividsolutions.jts.geom.*;
+import com.vividsolutions.jts.util.*;
+import com.vividsolutions.jts.algorithm.*;
+import com.vividsolutions.jts.geomgraph.*;
+import com.vividsolutions.jts.geomgraph.index.SegmentIntersector;
+
+/**
+ * Computes the topological relationship between two Geometries.
+ * <p>
+ * RelateComputer does not need to build a complete graph structure to compute
+ * the IntersectionMatrix.  The relationship between the geometries can
+ * be computed by simply examining the labelling of edges incident on each node.
+ * <p>
+ * RelateComputer does not currently support arbitrary GeometryCollections.
+ * This is because GeometryCollections can contain overlapping Polygons.
+ * In order to correct compute relate on overlapping Polygons, they
+ * would first need to be noded and merged (if not explicitly, at least
+ * implicitly).
+ *
+ * @version 1.6
+ */
+public class RelateComputer
+{
+  private LineIntersector li = new RobustLineIntersector();
+  private PointLocator ptLocator = new PointLocator();
+  private GeometryGraph[] arg;  // the arg(s) of the operation
+  private NodeMap nodes = new NodeMap(new RelateNodeFactory());
+  // this intersection matrix will hold the results compute for the relate
+  private IntersectionMatrix im = null;
+  private ArrayList isolatedEdges = new ArrayList();
+
+  // the intersection point found (if any)
+  private Coordinate invalidPoint;
+
+  public RelateComputer(GeometryGraph[] arg) {
+    this.arg = arg;
+  }
+
+  public IntersectionMatrix computeIM()
+  {
+    IntersectionMatrix im = new IntersectionMatrix();
+    // since Geometries are finite and embedded in a 2-D space, the EE element must always be 2
+    im.set(Location.EXTERIOR, Location.EXTERIOR, 2);
+
+    // if the Geometries don't overlap there is nothing to do
+    if (! arg[0].getGeometry().getEnvelopeInternal().intersects(
+            arg[1].getGeometry().getEnvelopeInternal()) ) {
+      computeDisjointIM(im);
+      return im;
+    }
+    arg[0].computeSelfNodes(li, false);
+    arg[1].computeSelfNodes(li, false);
+
+    // compute intersections between edges of the two input geometries
+    SegmentIntersector intersector = arg[0].computeEdgeIntersections(arg[1], li, false);
+//System.out.println("computeIM: # segment intersection tests: " + intersector.numTests);
+    computeIntersectionNodes(0);
+    computeIntersectionNodes(1);
+    /**
+     * Copy the labelling for the nodes in the parent Geometries.  These override
+     * any labels determined by intersections between the geometries.
+     */
+    copyNodesAndLabels(0);
+    copyNodesAndLabels(1);
+
+    // complete the labelling for any nodes which only have a label for a single geometry
+//Debug.addWatch(nodes.find(new Coordinate(110, 200)));
+//Debug.printWatch();
+    labelIsolatedNodes();
+//Debug.printWatch();
+
+    // If a proper intersection was found, we can set a lower bound on the IM.
+    computeProperIntersectionIM(intersector, im);
+
+    /**
+     * Now process improper intersections
+     * (eg where one or other of the geometries has a vertex at the intersection point)
+     * We need to compute the edge graph at all nodes to determine the IM.
+     */
+
+    // build EdgeEnds for all intersections
+    EdgeEndBuilder eeBuilder = new EdgeEndBuilder();
+    List ee0 = eeBuilder.computeEdgeEnds(arg[0].getEdgeIterator());
+    insertEdgeEnds(ee0);
+    List ee1 = eeBuilder.computeEdgeEnds(arg[1].getEdgeIterator());
+    insertEdgeEnds(ee1);
+
+//Debug.println("==== NodeList ===");
+//Debug.print(nodes);
+
+    labelNodeEdges();
+
+  /**
+   * Compute the labeling for isolated components
+   * <br>
+   * Isolated components are components that do not touch any other components in the graph.
+   * They can be identified by the fact that they will
+   * contain labels containing ONLY a single element, the one for their parent geometry.
+   * We only need to check components contained in the input graphs, since
+   * isolated components will not have been replaced by new components formed by intersections.
+   */
+//debugPrintln("Graph A isolated edges - ");
+    labelIsolatedEdges(0, 1);
+//debugPrintln("Graph B isolated edges - ");
+    labelIsolatedEdges(1, 0);
+
+    // update the IM from all components
+    updateIM(im);
+    return im;
+  }
+
+  private void insertEdgeEnds(List ee)
+  {
+    for (Iterator i = ee.iterator(); i.hasNext(); ) {
+      EdgeEnd e = (EdgeEnd) i.next();
+      nodes.add(e);
+    }
+  }
+
+  private void computeProperIntersectionIM(SegmentIntersector intersector, IntersectionMatrix im)
+  {
+    // If a proper intersection is found, we can set a lower bound on the IM.
+    int dimA = arg[0].getGeometry().getDimension();
+    int dimB = arg[1].getGeometry().getDimension();
+    boolean hasProper         = intersector.hasProperIntersection();
+    boolean hasProperInterior = intersector.hasProperInteriorIntersection();
+
+      // For Geometry's of dim 0 there can never be proper intersections.
+
+      /**
+       * If edge segments of Areas properly intersect, the areas must properly overlap.
+       */
+    if (dimA == 2 && dimB == 2) {
+      if (hasProper) im.setAtLeast("212101212");
+    }
+      /**
+       * If an Line segment properly intersects an edge segment of an Area,
+       * it follows that the Interior of the Line intersects the Boundary of the Area.
+       * If the intersection is a proper <i>interior</i> intersection, then
+       * there is an Interior-Interior intersection too.
+       * Note that it does not follow that the Interior of the Line intersects the Exterior
+       * of the Area, since there may be another Area component which contains the rest of the Line.
+       */
+    else if (dimA == 2 && dimB == 1) {
+      if (hasProper)          im.setAtLeast("FFF0FFFF2");
+      if (hasProperInterior)  im.setAtLeast("1FFFFF1FF");
+    }
+    else if (dimA == 1 && dimB == 2) {
+      if (hasProper)          im.setAtLeast("F0FFFFFF2");
+      if (hasProperInterior)  im.setAtLeast("1F1FFFFFF");
+    }
+    /* If edges of LineStrings properly intersect *in an interior point*, all
+        we can deduce is that
+        the interiors intersect.  (We can NOT deduce that the exteriors intersect,
+        since some other segments in the geometries might cover the points in the
+        neighbourhood of the intersection.)
+        It is important that the point be known to be an interior point of
+        both Geometries, since it is possible in a self-intersecting geometry to
+        have a proper intersection on one segment that is also a boundary point of another segment.
+    */
+    else if (dimA == 1 && dimB == 1) {
+      if (hasProperInterior)    im.setAtLeast("0FFFFFFFF");
+    }
+  }
+
+    /**
+     * Copy all nodes from an arg geometry into this graph.
+     * The node label in the arg geometry overrides any previously computed
+     * label for that argIndex.
+     * (E.g. a node may be an intersection node with
+     * a computed label of BOUNDARY,
+     * but in the original arg Geometry it is actually
+     * in the interior due to the Boundary Determination Rule)
+     */
+  private void copyNodesAndLabels(int argIndex)
+  {
+    for (Iterator i = arg[argIndex].getNodeIterator(); i.hasNext(); ) {
+      Node graphNode = (Node) i.next();
+      Node newNode = nodes.addNode(graphNode.getCoordinate());
+      newNode.setLabel(argIndex, graphNode.getLabel().getLocation(argIndex));
+//node.print(System.out);
+    }
+  }
+  /**
+   * Insert nodes for all intersections on the edges of a Geometry.
+   * Label the created nodes the same as the edge label if they do not already have a label.
+   * This allows nodes created by either self-intersections or
+   * mutual intersections to be labelled.
+   * Endpoint nodes will already be labelled from when they were inserted.
+   */
+  private void computeIntersectionNodes(int argIndex)
+  {
+    for (Iterator i = arg[argIndex].getEdgeIterator(); i.hasNext(); ) {
+      Edge e = (Edge) i.next();
+      int eLoc = e.getLabel().getLocation(argIndex);
+      for (Iterator eiIt = e.getEdgeIntersectionList().iterator(); eiIt.hasNext(); ) {
+        EdgeIntersection ei = (EdgeIntersection) eiIt.next();
+        RelateNode n = (RelateNode) nodes.addNode(ei.coord);
+        if (eLoc == Location.BOUNDARY)
+          n.setLabelBoundary(argIndex);
+        else {
+          if (n.getLabel().isNull(argIndex))
+            n.setLabel(argIndex, Location.INTERIOR);
+        }
+//Debug.println(n);
+      }
+    }
+  }
+  /**
+   * For all intersections on the edges of a Geometry,
+   * label the corresponding node IF it doesn't already have a label.
+   * This allows nodes created by either self-intersections or
+   * mutual intersections to be labelled.
+   * Endpoint nodes will already be labelled from when they were inserted.
+   */
+  private void labelIntersectionNodes(int argIndex)
+  {
+    for (Iterator i = arg[argIndex].getEdgeIterator(); i.hasNext(); ) {
+      Edge e = (Edge) i.next();
+      int eLoc = e.getLabel().getLocation(argIndex);
+      for (Iterator eiIt = e.getEdgeIntersectionList().iterator(); eiIt.hasNext(); ) {
+        EdgeIntersection ei = (EdgeIntersection) eiIt.next();
+        RelateNode n = (RelateNode) nodes.find(ei.coord);
+        if (n.getLabel().isNull(argIndex)) {
+          if (eLoc == Location.BOUNDARY)
+            n.setLabelBoundary(argIndex);
+          else
+            n.setLabel(argIndex, Location.INTERIOR);
+        }
+//n.print(System.out);
+      }
+    }
+  }
+  /**
+   * If the Geometries are disjoint, we need to enter their dimension and
+   * boundary dimension in the Ext rows in the IM
+   */
+  private void computeDisjointIM(IntersectionMatrix im)
+  {
+    Geometry ga = arg[0].getGeometry();
+    if (! ga.isEmpty()) {
+      im.set(Location.INTERIOR, Location.EXTERIOR, ga.getDimension());
+      im.set(Location.BOUNDARY, Location.EXTERIOR, ga.getBoundaryDimension());
+    }
+    Geometry gb = arg[1].getGeometry();
+    if (! gb.isEmpty()) {
+      im.set(Location.EXTERIOR, Location.INTERIOR, gb.getDimension());
+      im.set(Location.EXTERIOR, Location.BOUNDARY, gb.getBoundaryDimension());
+    }
+  }
+
+  private void labelNodeEdges()
+  {
+    for (Iterator ni = nodes.iterator(); ni.hasNext(); ) {
+      RelateNode node = (RelateNode) ni.next();
+      node.getEdges().computeLabelling(arg);
+//Debug.print(node.getEdges());
+//node.print(System.out);
+    }
+  }
+
+  /**
+   * update the IM with the sum of the IMs for each component
+   */
+  private void updateIM(IntersectionMatrix im)
+  {
+//Debug.println(im);
+    for (Iterator ei = isolatedEdges.iterator(); ei.hasNext(); ) {
+      Edge e = (Edge) ei.next();
+      e.updateIM(im);
+//Debug.println(im);
+    }
+    for (Iterator ni = nodes.iterator(); ni.hasNext(); ) {
+      RelateNode node = (RelateNode) ni.next();
+      node.updateIM(im);
+//Debug.println(im);
+      node.updateIMFromEdges(im);
+//Debug.println(im);
+//node.print(System.out);
+    }
+  }
+
+  /**
+   * Processes isolated edges by computing their labelling and adding them
+   * to the isolated edges list.
+   * Isolated edges are guaranteed not to touch the boundary of the target (since if they
+   * did, they would have caused an intersection to be computed and hence would
+   * not be isolated)
+   */
+  private void labelIsolatedEdges(int thisIndex, int targetIndex)
+  {
+    for (Iterator ei = arg[thisIndex].getEdgeIterator(); ei.hasNext(); ) {
+      Edge e = (Edge) ei.next();
+      if (e.isIsolated()) {
+        labelIsolatedEdge(e, targetIndex, arg[targetIndex].getGeometry());
+        isolatedEdges.add(e);
+      }
+    }
+  }
+  /**
+   * Label an isolated edge of a graph with its relationship to the target geometry.
+   * If the target has dim 2 or 1, the edge can either be in the interior or the exterior.
+   * If the target has dim 0, the edge must be in the exterior
+   */
+  private void labelIsolatedEdge(Edge e, int targetIndex, Geometry target)
+  {
+    // this won't work for GeometryCollections with both dim 2 and 1 geoms
+    if ( target.getDimension() > 0) {
+    // since edge is not in boundary, may not need the full generality of PointLocator?
+    // Possibly should use ptInArea locator instead?  We probably know here
+    // that the edge does not touch the bdy of the target Geometry
+      int loc = ptLocator.locate(e.getCoordinate(), target);
+      e.getLabel().setAllLocations(targetIndex, loc);
+    }
+    else {
+      e.getLabel().setAllLocations(targetIndex, Location.EXTERIOR);
+    }
+//System.out.println(e.getLabel());
+  }
+
+  /**
+   * Isolated nodes are nodes whose labels are incomplete
+   * (e.g. the location for one Geometry is null).
+   * This is the case because nodes in one graph which don't intersect
+   * nodes in the other are not completely labelled by the initial process
+   * of adding nodes to the nodeList.
+   * To complete the labelling we need to check for nodes that lie in the
+   * interior of edges, and in the interior of areas.
+   */
+  private void labelIsolatedNodes()
+  {
+    for (Iterator ni = nodes.iterator(); ni.hasNext(); ) {
+      Node n = (Node) ni.next();
+      Label label = n.getLabel();
+      // isolated nodes should always have at least one geometry in their label
+      Assert.isTrue(label.getGeometryCount() > 0, "node with empty label found");
+      if (n.isIsolated()) {
+        if (label.isNull(0))
+          labelIsolatedNode(n, 0);
+        else
+          labelIsolatedNode(n, 1);
+      }
+    }
+  }
+
+  /**
+   * Label an isolated node with its relationship to the target geometry.
+   */
+  private void labelIsolatedNode(Node n, int targetIndex)
+  {
+    int loc = ptLocator.locate(n.getCoordinate(), arg[targetIndex].getGeometry());
+    n.getLabel().setAllLocations(targetIndex, loc);
+//debugPrintln(n.getLabel());
+  }
+}

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/relate/RelateNode.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,81 @@
+
+
+
+/*
+ * 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.relate;
+
+/**
+ * A RelateNode is a Node that maintains a list of EdgeStubs
+ * for the edges that are incident on it.
+ *
+ * @version 1.6
+ */
+
+import java.io.PrintStream;
+import java.util.*;
+import com.vividsolutions.jts.geom.*;
+import com.vividsolutions.jts.util.*;
+import com.vividsolutions.jts.geomgraph.*;
+
+/**
+ * Represents a node in the topological graph used to compute spatial relationships.
+ *
+ * @version 1.6
+ */
+public class RelateNode
+  extends Node
+{
+
+  public RelateNode(Coordinate coord, EdgeEndStar edges)
+  {
+    super(coord, edges);
+  }
+
+  /**
+   * Update the IM with the contribution for this component.
+   * A component only contributes if it has a labelling for both parent geometries
+   */
+  protected void computeIM(IntersectionMatrix im)
+  {
+    im.setAtLeastIfValid(label.getLocation(0), label.getLocation(1), 0);
+  }
+  /**
+   * Update the IM with the contribution for the EdgeEnds incident on this node.
+   */
+  void updateIMFromEdges(IntersectionMatrix im)
+  {
+    ((EdgeEndBundleStar) edges).updateIM(im);
+  }
+
+}

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/relate/RelateNodeFactory.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,52 @@
+
+
+/*
+ * 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.relate;
+
+import com.vividsolutions.jts.geom.Coordinate;
+import com.vividsolutions.jts.geomgraph.*;
+
+/**
+ * Used by the {@link NodeMap} in a {@link RelateNodeGraph} to create {@link RelateNode}s.
+ *
+ * @version 1.6
+ */
+public class RelateNodeFactory
+  extends NodeFactory
+{
+  public Node createNode(Coordinate coord)
+  {
+    return new RelateNode(coord, new EdgeEndBundleStar());
+  }
+}

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/relate/RelateNodeGraph.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,150 @@
+
+/*
+ * 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.relate;
+
+/**
+ * @version 1.6
+ */
+import java.util.*;
+import com.vividsolutions.jts.geom.*;
+import com.vividsolutions.jts.util.*;
+import com.vividsolutions.jts.geomgraph.*;
+
+/**
+ * Implements the simple graph of Nodes and EdgeEnd which is all that is
+ * required to determine topological relationships between Geometries.
+ * Also supports building a topological graph of a single Geometry, to
+ * allow verification of valid topology.
+ * <p>
+ * It is <b>not</b> necessary to create a fully linked
+ * PlanarGraph to determine relationships, since it is sufficient
+ * to know how the Geometries interact locally around the nodes.
+ * In fact, this is not even feasible, since it is not possible to compute
+ * exact intersection points, and hence the topology around those nodes
+ * cannot be computed robustly.
+ * The only Nodes that are created are for improper intersections;
+ * that is, nodes which occur at existing vertices of the Geometries.
+ * Proper intersections (e.g. ones which occur between the interior of line segments)
+ * have their topology determined implicitly, without creating a Node object
+ * to represent them.
+ *
+ * @version 1.6
+ */
+public class RelateNodeGraph {
+
+  private NodeMap nodes = new NodeMap(new RelateNodeFactory());
+
+  public RelateNodeGraph() {
+  }
+
+  public Iterator getNodeIterator() { return nodes.iterator(); }
+
+  public void build(GeometryGraph geomGraph)
+  {
+      // compute nodes for intersections between previously noded edges
+    computeIntersectionNodes(geomGraph, 0);
+    /**
+     * Copy the labelling for the nodes in the parent Geometry.  These override
+     * any labels determined by intersections.
+     */
+    copyNodesAndLabels(geomGraph, 0);
+
+    /**
+     * Build EdgeEnds for all intersections.
+     */
+    EdgeEndBuilder eeBuilder = new EdgeEndBuilder();
+    List eeList = eeBuilder.computeEdgeEnds(geomGraph.getEdgeIterator());
+    insertEdgeEnds(eeList);
+
+//Debug.println("==== NodeList ===");
+//Debug.print(nodes);
+  }
+
+  /**
+   * Insert nodes for all intersections on the edges of a Geometry.
+   * Label the created nodes the same as the edge label if they do not already have a label.
+   * This allows nodes created by either self-intersections or
+   * mutual intersections to be labelled.
+   * Endpoint nodes will already be labelled from when they were inserted.
+   * <p>
+   * Precondition: edge intersections have been computed.
+   */
+  public void computeIntersectionNodes(GeometryGraph geomGraph, int argIndex)
+  {
+    for (Iterator edgeIt = geomGraph.getEdgeIterator(); edgeIt.hasNext(); ) {
+      Edge e = (Edge) edgeIt.next();
+      int eLoc = e.getLabel().getLocation(argIndex);
+      for (Iterator eiIt = e.getEdgeIntersectionList().iterator(); eiIt.hasNext(); ) {
+        EdgeIntersection ei = (EdgeIntersection) eiIt.next();
+        RelateNode n = (RelateNode) nodes.addNode(ei.coord);
+        if (eLoc == Location.BOUNDARY)
+          n.setLabelBoundary(argIndex);
+        else {
+          if (n.getLabel().isNull(argIndex))
+            n.setLabel(argIndex, Location.INTERIOR);
+        }
+//Debug.println(n);
+      }
+    }
+  }
+
+    /**
+     * Copy all nodes from an arg geometry into this graph.
+     * The node label in the arg geometry overrides any previously computed
+     * label for that argIndex.
+     * (E.g. a node may be an intersection node with
+     * a computed label of BOUNDARY,
+     * but in the original arg Geometry it is actually
+     * in the interior due to the Boundary Determination Rule)
+     */
+  public void copyNodesAndLabels(GeometryGraph geomGraph, int argIndex)
+  {
+    for (Iterator nodeIt = geomGraph.getNodeIterator(); nodeIt.hasNext(); ) {
+      Node graphNode = (Node) nodeIt.next();
+      Node newNode = nodes.addNode(graphNode.getCoordinate());
+      newNode.setLabel(argIndex, graphNode.getLabel().getLocation(argIndex));
+//node.print(System.out);
+    }
+  }
+
+  public void insertEdgeEnds(List ee)
+  {
+    for (Iterator i = ee.iterator(); i.hasNext(); ) {
+      EdgeEnd e = (EdgeEnd) i.next();
+      nodes.add(e);
+    }
+  }
+
+
+}

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/relate/RelateOp.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,74 @@
+
+
+
+/*
+ * 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.relate;
+
+/**
+ * @version 1.6
+ */
+
+import com.vividsolutions.jts.geomgraph.*;
+import com.vividsolutions.jts.geom.*;
+import com.vividsolutions.jts.operation.GeometryGraphOperation;
+import java.util.*;
+
+/**
+ * Implements the relate() operation on {@link Geometry}s.
+ *
+ * @version 1.6
+ */
+public class RelateOp
+  extends GeometryGraphOperation
+{
+  public static IntersectionMatrix relate(Geometry a, Geometry b)
+  {
+      RelateOp relOp = new RelateOp(a, b);
+      IntersectionMatrix im = relOp.getIntersectionMatrix();
+      return im;
+  }
+
+  private RelateComputer relate;
+
+  public RelateOp(Geometry g0, Geometry g1) {
+    super(g0, g1);
+    relate = new RelateComputer(arg);
+  }
+
+  public IntersectionMatrix getIntersectionMatrix()
+  {
+    return relate.computeIM();
+  }
+
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/relate/package.html
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/relate/package.html	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/relate/package.html	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,52 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+<!--
+
+-->
+</head>
+<body bgcolor="white">
+
+Contains classes to implement the computation of the spatial relationships of <CODE>Geometry</CODE>s.
+<P>
+The <code>relate</code> algorithm computes the <code>IntersectionMatrix</code> describing the 
+relationship of two <code>Geometry</code>s.  The algorithm for computing <code>relate</code> 
+uses the intersection operations supported by topology graphs.  Although the <code>relate</code> 
+result depends on the resultant graph formed by the computed intersections, there is 
+no need to explicitly compute the entire graph.  
+It is sufficient to compute the local structure of the graph 
+at each intersection node. 
+<P>
+The algorithm to compute <code>relate</code> has the following steps:
+<UL>
+  <LI>Build topology graphs of the two input geometries. For each geometry 
+      all self-intersection nodes are computed and added to the graph.
+  <LI>Compute nodes for all intersections between edges and nodes of the graphs.
+  <LI>Compute the labeling for the computed nodes by merging the labels from the input graphs. 
+  <LI>Compute the labeling for isolated components of the graph (see below)
+  <LI>Compute the <code>IntersectionMatrix</code> from the labels on the nodes and edges.
+</UL>
+
+<H3>Labeling isolated components</H3>
+
+Isolated components are components (edges or nodes) of an input <code>Geometry</code> which 
+do not contain any intersections with the other input <code>Geometry</code>.  The 
+topological relationship of these components to the other input <code>Geometry</code> 
+must be computed in order to determine the complete labeling of the component.  This can 
+be done by testing whether the component lies in the interior or exterior of the other 
+<code>Geometry</code>.  If the other <code>Geometry</code> is 1-dimensional, the isolated 
+component must lie in the exterior (since otherwise it would have an intersection with an 
+edge of the <code>Geometry</code>).  If the other <code>Geometry</code> is 2-dimensional, 
+a Point-In-Polygon test can be used to determine whether the isolated component is in the 
+interior or exterior. 
+
+<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/operation/valid/ConnectedInteriorTester.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/valid/ConnectedInteriorTester.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/valid/ConnectedInteriorTester.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,226 @@
+
+
+/*
+ * 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.valid;
+
+import java.util.*;
+import com.vividsolutions.jts.algorithm.*;
+import com.vividsolutions.jts.geomgraph.*;
+import com.vividsolutions.jts.operation.overlay.*;
+import com.vividsolutions.jts.geom.*;
+import com.vividsolutions.jts.util.*;
+
+/**
+ * This class tests that the interior of an area {@link Geometry}
+ *  ({@link Polygon}  or {@link MultiPolygon} )
+ * is connected.  An area Geometry is invalid if the interior is disconnected.
+ * 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
+ * </ul>
+ * If an inconsistency if found the location of the problem
+ * is recorded.
+ *
+ * @version 1.6
+ */
+public class ConnectedInteriorTester {
+
+  public static Coordinate findDifferentPoint(Coordinate[] coord, Coordinate pt)
+  {
+    for (int i = 0; i < coord.length; i++) {
+      if (! coord[i].equals(pt))
+        return coord[i];
+    }
+    return null;
+  }
+
+  private GeometryFactory geometryFactory = new GeometryFactory();
+  private CGAlgorithms cga = new CGAlgorithms();
+
+  private GeometryGraph geomGraph;
+  // save a coordinate for any disconnected interior found
+  // the coordinate will be somewhere on the ring surrounding the disconnected interior
+  private Coordinate disconnectedRingcoord;
+
+  public ConnectedInteriorTester(GeometryGraph geomGraph)
+  {
+    this.geomGraph = geomGraph;
+  }
+
+  public Coordinate getCoordinate() { return disconnectedRingcoord; }
+
+  public boolean isInteriorsConnected()
+  {
+    // node the edges, in case holes touch the shell
+    List splitEdges = new ArrayList();
+    geomGraph.computeSplitEdges(splitEdges);
+
+    // polygonize the edges
+    PlanarGraph graph = new PlanarGraph(new OverlayNodeFactory());
+    graph.addEdges(splitEdges);
+    setAllEdgesInResult(graph);
+    graph.linkAllDirectedEdges();
+    List edgeRings = buildEdgeRings(graph.getEdgeEnds());
+
+    /**
+     * Mark all the edges for the edgeRings corresponding to the shells
+     * of the input polygons.  Note only ONE ring gets marked for each shell.
+     */
+    visitShellInteriors(geomGraph.getGeometry(), graph);
+
+    /**
+     * If there are any unvisited shell edges
+     * (i.e. a ring which is not a hole and which has the interior
+     * of the parent area on the RHS)
+     * this means that one or more holes must have split the interior of the
+     * polygon into at least two pieces.  The polygon is thus invalid.
+     */
+    return ! hasUnvisitedShellEdge(edgeRings);
+  }
+
+  private void setAllEdgesInResult(PlanarGraph graph)
+  {
+    for (Iterator it = graph.getEdgeEnds().iterator(); it.hasNext(); ) {
+      DirectedEdge de = (DirectedEdge) it.next();
+      de.setInResult(true);
+    }
+  }
+
+  /**
+   * for all DirectedEdges in result, form them into EdgeRings
+   */
+  private List buildEdgeRings(Collection dirEdges)
+  {
+    List edgeRings = new ArrayList();
+    for (Iterator it = dirEdges.iterator(); it.hasNext(); ) {
+      DirectedEdge de = (DirectedEdge) it.next();
+      // if this edge has not yet been processed
+      if (de.getEdgeRing() == null) {
+        EdgeRing er = new MaximalEdgeRing(de, geometryFactory, cga);
+        edgeRings.add(er);
+      }
+    }
+    return edgeRings;
+  }
+
+  /**
+   * Mark all the edges for the edgeRings corresponding to the shells
+   * of the input polygons.  Note only ONE ring gets marked for each shell.
+   */
+  private void visitShellInteriors(Geometry g, PlanarGraph graph)
+  {
+    if (g instanceof Polygon) {
+      Polygon p = (Polygon) g;
+      visitInteriorRing(p.getExteriorRing(), graph);
+    }
+    if (g instanceof MultiPolygon) {
+      MultiPolygon mp = (MultiPolygon) g;
+      for (int i = 0; i < mp.getNumGeometries(); i++) {
+        Polygon p = (Polygon) mp.getGeometryN(i);
+        visitInteriorRing(p.getExteriorRing(), graph);
+      }
+    }
+  }
+
+  private void visitInteriorRing(LineString ring, PlanarGraph graph)
+  {
+    Coordinate[] pts = ring.getCoordinates();
+    Coordinate pt0 = pts[0];
+    /**
+     * Find first point in coord list different to initial point.
+     * Need special check since the first point may be repeated.
+     */
+    Coordinate pt1 = findDifferentPoint(pts, pt0);
+    Edge e = graph.findEdgeInSameDirection(pt0, pt1);
+    DirectedEdge de = (DirectedEdge) graph.findEdgeEnd(e);
+    DirectedEdge intDe = null;
+    if (de.getLabel().getLocation(0, Position.RIGHT) == Location.INTERIOR) {
+      intDe = de;
+    }
+    else if (de.getSym().getLabel().getLocation(0, Position.RIGHT) == Location.INTERIOR) {
+      intDe = de.getSym();
+    }
+    Assert.isTrue(intDe != null, "unable to find dirEdge with Interior on RHS");
+
+    visitLinkedDirectedEdges(intDe);
+  }
+  protected void visitLinkedDirectedEdges(DirectedEdge start)
+  {
+    DirectedEdge startDe = start;
+    DirectedEdge de = start;
+//Debug.println(de);
+    do {
+      Assert.isTrue(de != null, "found null Directed Edge");
+      de.setVisited(true);
+      de = de.getNext();
+//Debug.println(de);
+    } while (de != startDe);
+  }
+
+  /**
+   * Check if any shell ring has an unvisited edge.
+   * A shell ring is a ring which is not a hole and which has the interior
+   * of the parent area on the RHS.
+   * (Note that there may be non-hole rings with the interior on the LHS,
+   * since the interior of holes will also be polygonized into CW rings
+   * by the linkAllDirectedEdges() step)
+   *
+   * @return true if there is an unvisited edge in a non-hole ring
+   */
+  private boolean hasUnvisitedShellEdge(List edgeRings)
+  {
+    for (int i = 0; i < edgeRings.size(); i++) {
+      EdgeRing er = (EdgeRing) edgeRings.get(i);
+      if (er.isHole()) continue;
+      List edges = er.getEdges();
+      DirectedEdge de = (DirectedEdge) edges.get(0);
+      // don't check CW rings which are holes
+      if (de.getLabel().getLocation(0, Position.RIGHT) != Location.INTERIOR) continue;
+
+      // must have a CW ring which surrounds the INT of the area, so check all
+      // edges have been visited
+      for (int j = 0; j < edges.size(); j++) {
+        de = (DirectedEdge) edges.get(j);
+//Debug.print("visted? "); Debug.println(de);
+        if (! de.isVisited()) {
+//Debug.print("not visited "); Debug.println(de);
+          disconnectedRingcoord = de.getCoordinate();
+          return true;
+        }
+      }
+    }
+    return false;
+  }
+}

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/valid/ConsistentAreaTester.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,143 @@
+
+
+/*
+ * 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.valid;
+
+import java.util.*;
+import com.vividsolutions.jts.algorithm.*;
+import com.vividsolutions.jts.geom.*;
+import com.vividsolutions.jts.geomgraph.*;
+import com.vividsolutions.jts.geomgraph.index.SegmentIntersector;
+import com.vividsolutions.jts.operation.relate.*;
+import com.vividsolutions.jts.util.*;
+
+/**
+ * Checks that a {@link GeometryGraph} representing an area
+ * (a {@link Polygon} or {@link MultiPolygon} )
+ * is consistent with the SFS semantics for area geometries.
+ * Checks include:
+ * <ul>
+ * <li>testing for rings which self-intersect (both properly
+ * and at nodes)
+ * <li>testing for duplicate rings
+ * </ul>
+ * If an inconsistency if found the location of the problem
+ * is recorded.
+ *
+ * @version 1.6
+ */
+public class ConsistentAreaTester {
+
+  private final LineIntersector li = new RobustLineIntersector();
+  private GeometryGraph geomGraph;
+  private RelateNodeGraph nodeGraph = new RelateNodeGraph();
+
+  // the intersection point found (if any)
+  private Coordinate invalidPoint;
+
+  public ConsistentAreaTester(GeometryGraph geomGraph)
+  {
+    this.geomGraph = geomGraph;
+  }
+
+    /**
+   * @return the intersection point, or <code>null</code> if none was found
+   */
+  public Coordinate getInvalidPoint() { return invalidPoint; }
+
+  public boolean isNodeConsistentArea()
+  {
+    /**
+     * To fully check validity, it is necessary to
+     * compute ALL intersections, including self-intersections within a single edge.
+     */
+    SegmentIntersector intersector = geomGraph.computeSelfNodes(li, true);
+    if (intersector.hasProperIntersection()) {
+      invalidPoint = intersector.getProperIntersectionPoint();
+      return false;
+    }
+
+    nodeGraph.build(geomGraph);
+
+    return isNodeEdgeAreaLabelsConsistent();
+  }
+
+  /**
+   * Check all nodes to see if their labels are consistent.
+   * If any are not, return false
+   */
+  private boolean isNodeEdgeAreaLabelsConsistent()
+  {
+    for (Iterator nodeIt = nodeGraph.getNodeIterator(); nodeIt.hasNext(); ) {
+      RelateNode node = (RelateNode) nodeIt.next();
+      if (! node.getEdges().isAreaLabelsConsistent()) {
+        invalidPoint = (Coordinate) node.getCoordinate().clone();
+        return false;
+      }
+    }
+    return true;
+  }
+  /**
+   * Checks for two duplicate rings in an area.
+   * Duplicate rings are rings that are topologically equal
+   * (that is, which have the same sequence of points up to point order).
+   * If the area is topologically consistent (determined by calling the
+   * <code>isNodeConsistentArea</code>,
+   * duplicate rings can be found by checking for EdgeBundles which contain
+   * more than one EdgeEnd.
+   * (This is because topologically consistent areas cannot have two rings sharing
+   * the same line segment, unless the rings are equal).
+   * The start point of one of the equal rings will be placed in
+   * invalidPoint.
+   *
+   * @return true if this area Geometry is topologically consistent but has two duplicate rings
+   */
+  public boolean hasDuplicateRings()
+  {
+    for (Iterator nodeIt = nodeGraph.getNodeIterator(); nodeIt.hasNext(); ) {
+      RelateNode node = (RelateNode) nodeIt.next();
+      for (Iterator i = node.getEdges().iterator(); i.hasNext(); ) {
+        EdgeEndBundle eeb = (EdgeEndBundle) i.next();
+        if (eeb.getEdgeEnds().size() > 1) {
+          invalidPoint = eeb.getEdge().getCoordinate(0);
+          return true;
+        }
+      }
+    }
+    return false;
+  }
+
+
+
+}

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/valid/IsValidOp.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,567 @@
+
+
+/*
+ * 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.valid;
+
+import java.util.*;
+import com.vividsolutions.jts.algorithm.*;
+import com.vividsolutions.jts.geomgraph.*;
+import com.vividsolutions.jts.geom.*;
+import com.vividsolutions.jts.operation.GeometryGraphOperation;
+import com.vividsolutions.jts.util.*;
+
+/**
+ * Implements the algorithsm required to compute the <code>isValid()</code> method
+ * for {@link Geometry}s.
+ *
+ * @version 1.6
+ */
+public class IsValidOp
+{
+
+  /**
+   * Checks whether a coordinate is valid for processing.
+   * Coordinates are valid iff their x and y ordinates are in the
+   * range of the floating point representation.
+   *
+   * @param coord the coordinate to validate
+   * @return <code>true</code> if the coordinate is valid
+   */
+  public static boolean isValid(Coordinate coord)
+  {
+    if (Double.isNaN(coord.x)) return false;
+    if (Double.isInfinite(coord.x)) return false;
+    if (Double.isNaN(coord.y)) return false;
+    if (Double.isInfinite(coord.y)) return false;
+    return true;
+  }
+  /**
+   * Find a point from the list of testCoords
+   * that is NOT a node in the edge for the list of searchCoords
+   *
+   * @return the point found, or <code>null</code> if none found
+   */
+  public static Coordinate findPtNotNode(
+                          Coordinate[] testCoords,
+                          LinearRing searchRing,
+                          GeometryGraph graph)
+  {
+    // find edge corresponding to searchRing.
+    Edge searchEdge = graph.findEdge(searchRing);
+    // find a point in the testCoords which is not a node of the searchRing
+    EdgeIntersectionList eiList = searchEdge.getEdgeIntersectionList();
+    // somewhat inefficient - is there a better way? (Use a node map, for instance?)
+    for (int i = 0 ; i < testCoords.length; i++) {
+      Coordinate pt = testCoords[i];
+      if (! eiList.isIntersection(pt))
+        return pt;
+    }
+    return null;
+  }
+
+  private Geometry parentGeometry;  // the base Geometry to be validated
+  private boolean isChecked = false;
+  private TopologyValidationError validErr;
+
+  public IsValidOp(Geometry parentGeometry)
+  {
+    this.parentGeometry = parentGeometry;
+  }
+
+  public boolean isValid()
+  {
+    checkValid(parentGeometry);
+    return validErr == null;
+  }
+
+  public TopologyValidationError getValidationError()
+  {
+    checkValid(parentGeometry);
+    return validErr;
+  }
+
+  private void checkValid(Geometry g)
+  {
+    if (isChecked) return;
+    validErr = null;
+    if (g.isEmpty()) return;
+    if (g instanceof Point)                   checkValid((Point) g);
+    else if (g instanceof MultiPoint)         checkValid((MultiPoint) g);
+                        // LineString also handles LinearRings
+    else if (g instanceof LinearRing)         checkValid( (LinearRing) g);
+    else if (g instanceof LineString)         checkValid( (LineString) g);
+    else if (g instanceof Polygon)            checkValid( (Polygon) g);
+    else if (g instanceof MultiPolygon)       checkValid( (MultiPolygon) g);
+    else if (g instanceof GeometryCollection) checkValid( (GeometryCollection) g);
+    else  throw new UnsupportedOperationException(g.getClass().getName());
+  }
+
+  /**
+   * Checks validity of a Point.
+   */
+  private void checkValid(Point g)
+  {
+    checkInvalidCoordinates(g.getCoordinates());
+  }
+  /**
+   * Checks validity of a MultiPoint.
+   */
+  private void checkValid(MultiPoint g)
+  {
+    checkInvalidCoordinates(g.getCoordinates());
+  }
+
+  /**
+   * Checks validity of a LineString.  Almost anything goes for linestrings!
+   */
+  private void checkValid(LineString g)
+  {
+    checkInvalidCoordinates(g.getCoordinates());
+    if (validErr != null) return;
+    GeometryGraph graph = new GeometryGraph(0, g);
+    checkTooFewPoints(graph);
+  }
+  /**
+   * Checks validity of a LinearRing.
+   */
+  private void checkValid(LinearRing g)
+  {
+    checkInvalidCoordinates(g.getCoordinates());
+    if (validErr != null) return;
+    GeometryGraph graph = new GeometryGraph(0, g);
+    checkTooFewPoints(graph);
+    if (validErr != null) return;
+    LineIntersector li = new RobustLineIntersector();
+    graph.computeSelfNodes(li, true);
+    checkNoSelfIntersectingRings(graph);
+  }
+
+  /**
+   * Checks the validity of a polygon.
+   * Sets the validErr flag.
+   */
+  private void checkValid(Polygon g)
+  {
+    checkInvalidCoordinates(g);
+    if (validErr != null) return;
+
+    GeometryGraph graph = new GeometryGraph(0, g);
+
+    checkTooFewPoints(graph);
+    if (validErr != null) return;
+    checkConsistentArea(graph);
+    if (validErr != null) return;
+    checkNoSelfIntersectingRings(graph);
+    if (validErr != null) return;
+    checkHolesInShell(g, graph);
+    if (validErr != null) return;
+    //SLOWcheckHolesNotNested(g);
+    checkHolesNotNested(g, graph);
+    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;
+    }
+
+    GeometryGraph graph = new GeometryGraph(0, g);
+
+    checkTooFewPoints(graph);
+    if (validErr != null) return;
+    checkConsistentArea(graph);
+    if (validErr != null) return;
+    checkNoSelfIntersectingRings(graph);
+    if (validErr != null) return;
+
+    for (int i = 0; i < g.getNumGeometries(); i++) {
+      Polygon p = (Polygon) g.getGeometryN(i);
+      checkHolesInShell(p, graph);
+      if (validErr != null) return;
+    }
+    for (int i = 0; i < g.getNumGeometries(); i++) {
+      Polygon p = (Polygon) g.getGeometryN(i);
+      checkHolesNotNested(p, graph);
+      if (validErr != null) return;
+    }
+    checkShellsNotNested(g, graph);
+    if (validErr != null) return;
+    checkConnectedInteriors(graph);
+  }
+
+  private void checkValid(GeometryCollection gc)
+  {
+    for (int i = 0; i < gc.getNumGeometries(); i++) {
+      Geometry g = gc.getGeometryN(i);
+      checkValid(g);
+      if (validErr != null) return;
+    }
+  }
+
+  private void checkInvalidCoordinates(Coordinate[] coords)
+  {
+    for (int i = 0; i < coords.length; i++) {
+      if (! isValid(coords[i])) {
+        validErr = new TopologyValidationError(
+                          TopologyValidationError.INVALID_COORDINATE,
+                          coords[i]);
+        return;
+
+      }
+    }
+  }
+  private void checkInvalidCoordinates(Polygon poly)
+  {
+    checkInvalidCoordinates(poly.getExteriorRing().getCoordinates());
+    if (validErr != null) return;
+    for (int i = 0; i < poly.getNumInteriorRing(); i++) {
+      checkInvalidCoordinates(poly.getInteriorRingN(i).getCoordinates());
+      if (validErr != null) return;
+    }
+  }
+
+  private void checkTooFewPoints(GeometryGraph graph)
+  {
+    if (graph.hasTooFewPoints()) {
+      validErr = new TopologyValidationError(
+                        TopologyValidationError.TOO_FEW_POINTS,
+                        graph.getInvalidPoint());
+      return;
+    }
+  }
+
+  private void checkConsistentArea(GeometryGraph graph)
+  {
+    ConsistentAreaTester cat = new ConsistentAreaTester(graph);
+    boolean isValidArea = cat.isNodeConsistentArea();
+    if (! isValidArea) {
+      validErr = new TopologyValidationError(
+                        TopologyValidationError.SELF_INTERSECTION,
+                        cat.getInvalidPoint());
+      return;
+    }
+    if (cat.hasDuplicateRings()) {
+      validErr = new TopologyValidationError(
+                        TopologyValidationError.DUPLICATE_RINGS,
+                        cat.getInvalidPoint());
+    }
+  }
+
+  private void checkNoSelfIntersectingRings(GeometryGraph graph)
+  {
+    for (Iterator i = graph.getEdgeIterator(); i.hasNext(); ) {
+      Edge e = (Edge) i.next();
+      checkSelfIntersectingRing(e.getEdgeIntersectionList());
+      if (validErr != null)
+        return;
+    }
+  }
+
+  /**
+   * 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)
+  {
+    Set nodeSet = new TreeSet();
+    boolean isFirst = true;
+    for (Iterator i = eiList.iterator(); i.hasNext(); ) {
+      EdgeIntersection ei = (EdgeIntersection) i.next();
+      if (isFirst) {
+        isFirst = false;
+        continue;
+      }
+      if (nodeSet.contains(ei.coord)) {
+        validErr = new TopologyValidationError(
+                          TopologyValidationError.RING_SELF_INTERSECTION,
+                          ei.coord);
+        return;
+      }
+      else {
+        nodeSet.add(ei.coord);
+      }
+    }
+  }
+
+  /* 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.
+   *
+   * @param p the polygon to be tested for hole inclusion
+   * @param graph a GeometryGraph incorporating the polygon
+   */
+  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);
+    PointInRing pir = new MCPointInRing(shell);
+
+    for (int i = 0; i < p.getNumInteriorRing(); i++) {
+
+      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");
+
+      boolean outside = ! pir.isInside(holePt);
+      if ( outside ) {
+        validErr = new TopologyValidationError(
+                          TopologyValidationError.HOLE_OUTSIDE_SHELL,
+                          holePt);
+        return;
+      }
+    }
+  }
+
+  /*
+  private void OLDcheckHolesInShell(Polygon p)
+  {
+    LinearRing shell =  (LinearRing) p.getExteriorRing();
+    Coordinate[] shellPts = shell.getCoordinates();
+    for (int i = 0; i < p.getNumInteriorRing(); i++) {
+      Coordinate holePt = findPtNotNode(p.getInteriorRingN(i).getCoordinates(), shell, arg[0]);
+      Assert.isTrue(holePt != null, "Unable to find a hole point not a vertex of the shell");
+      boolean onBdy = cga.isOnLine(holePt, shellPts);
+      boolean inside = cga.isPointInRing(holePt, shellPts);
+      boolean outside = ! (onBdy || inside);
+      if ( outside ) {
+        validErr = new TopologyValidationError(
+                          TopologyValidationError.HOLE_OUTSIDE_SHELL,
+                          holePt);
+        return;
+      }
+    }
+  }
+  */
+  /**
+   * Tests that no hole is nested inside another hole.
+   * This routine assumes that the holes are disjoint.
+   * To ensure this, holes have previously been tested
+   * to ensure that:
+   * <ul>
+   * <li>they do not partially overlap
+   *      (checked by <code>checkRelateConsistency</code>)
+   * <li>they are not identical
+   *      (checked by <code>checkRelateConsistency</code>)
+   * </ul>
+   */
+  private void checkHolesNotNested(Polygon p, GeometryGraph graph)
+  {
+    QuadtreeNestedRingTester nestedTester = new QuadtreeNestedRingTester(graph);
+    //SimpleNestedRingTester nestedTester = new SimpleNestedRingTester(arg[0]);
+    //SweeplineNestedRingTester nestedTester = new SweeplineNestedRingTester(arg[0]);
+
+    for (int i = 0; i < p.getNumInteriorRing(); i++) {
+      LinearRing innerHole = (LinearRing) p.getInteriorRingN(i);
+      nestedTester.add(innerHole);
+    }
+    boolean isNonNested = nestedTester.isNonNested();
+    if ( ! isNonNested ) {
+      validErr = new TopologyValidationError(
+                            TopologyValidationError.NESTED_HOLES,
+                            nestedTester.getNestedPoint());
+    }
+  }
+
+  /*
+  private void SLOWcheckHolesNotNested(Polygon p)
+  {
+    for (int i = 0; i < p.getNumInteriorRing(); i++) {
+      LinearRing innerHole = (LinearRing) p.getInteriorRingN(i);
+      Coordinate[] innerHolePts = innerHole.getCoordinates();
+      for (int j = 0; j < p.getNumInteriorRing(); j++) {
+        // don't test hole against itself!
+        if (i == j) continue;
+
+        LinearRing searchHole = (LinearRing) p.getInteriorRingN(j);
+
+        // if envelopes don't overlap, holes are not nested
+        if (! innerHole.getEnvelopeInternal().overlaps(searchHole.getEnvelopeInternal()))
+          continue;
+
+        Coordinate[] searchHolePts = searchHole.getCoordinates();
+        Coordinate innerholePt = findPtNotNode(innerHolePts, searchHole, arg[0]);
+        Assert.isTrue(innerholePt != null, "Unable to find a hole point not a node of the search hole");
+        boolean inside = cga.isPointInRing(innerholePt, searchHolePts);
+        if ( inside ) {
+          validErr = new TopologyValidationError(
+                            TopologyValidationError.NESTED_HOLES,
+                            innerholePt);
+          return;
+        }
+      }
+    }
+  }
+  */
+  /**
+   * Tests that no element polygon is wholly in the interior of another element polygon.
+   * <p>
+   * Preconditions:
+   * <ul>
+   * <li>shells do not partially overlap
+   * <li>shells do not touch along an edge
+   * <li>no duplicate rings exist
+   * </ul>
+   * This routine relies on the fact that while polygon shells may touch at one or
+   * more vertices, they cannot touch at ALL vertices.
+   */
+  private void checkShellsNotNested(MultiPolygon mp, GeometryGraph graph)
+  {
+    for (int i = 0; i < mp.getNumGeometries(); i++) {
+      Polygon p = (Polygon) mp.getGeometryN(i);
+      LinearRing shell = (LinearRing) p.getExteriorRing();
+      for (int j = 0; j < mp.getNumGeometries(); j++) {
+        if (i == j) continue;
+        Polygon p2 = (Polygon) mp.getGeometryN(j);
+        checkShellNotNested(shell, p2, graph);
+        if (validErr != null) return;
+      }
+    }
+  }
+
+  /**
+   * Check if a shell is incorrectly nested within a polygon.  This is the case
+   * if the shell is inside the polygon shell, but not inside a polygon hole.
+   * (If the shell is inside a polygon hole, the nesting is valid.)
+   * <p>
+   * The algorithm used relies on the fact that the rings must be properly contained.
+   * E.g. they cannot partially overlap (this has been previously checked by
+   * <code>checkRelateConsistency</code> )
+   */
+  private void checkShellNotNested(LinearRing shell, Polygon p, GeometryGraph graph)
+  {
+    Coordinate[] shellPts = shell.getCoordinates();
+    // test if shell is inside polygon shell
+    LinearRing polyShell =  (LinearRing) p.getExteriorRing();
+    Coordinate[] polyPts = polyShell.getCoordinates();
+    Coordinate shellPt = findPtNotNode(shellPts, polyShell, graph);
+    // if no point could be found, we can assume that the shell is outside the polygon
+    if (shellPt == null)
+      return;
+    boolean insidePolyShell = CGAlgorithms.isPointInRing(shellPt, polyPts);
+    if (! insidePolyShell) return;
+
+    // if no holes, this is an error!
+    if (p.getNumInteriorRing() <= 0) {
+      validErr = new TopologyValidationError(
+                            TopologyValidationError.NESTED_SHELLS,
+                            shellPt);
+      return;
+    }
+
+    /**
+     * Check if the shell is inside one of the holes.
+     * This is the case if one of the calls to checkShellInsideHole
+     * returns a null coordinate.
+     * Otherwise, the shell is not properly contained in a hole, which is an error.
+     */
+    Coordinate badNestedPt = null;
+    for (int i = 0; i < p.getNumInteriorRing(); i++) {
+      LinearRing hole = (LinearRing) p.getInteriorRingN(i);
+      badNestedPt = checkShellInsideHole(shell, hole, graph);
+      if (badNestedPt == null)
+        return;
+    }
+    validErr = new TopologyValidationError(
+                          TopologyValidationError.NESTED_SHELLS,
+                          badNestedPt);
+  }
+
+  /**
+   * This routine checks to see if a shell is properly contained in a hole.
+   * It assumes that the edges of the shell and hole do not
+   * properly intersect.
+   *
+   * @return <code>null</code> if the shell is properly contained, or
+   *   a Coordinate which is not inside the hole if it is not
+   *
+   */
+  private Coordinate checkShellInsideHole(LinearRing shell, LinearRing hole, GeometryGraph graph)
+  {
+    Coordinate[] shellPts = shell.getCoordinates();
+    Coordinate[] holePts = hole.getCoordinates();
+    // TODO: improve performance of this - by sorting pointlists for instance?
+    Coordinate shellPt = findPtNotNode(shellPts, hole, graph);
+    // if point is on shell but not hole, check that the shell is inside the hole
+    if (shellPt != null) {
+      boolean insideHole = CGAlgorithms.isPointInRing(shellPt, holePts);
+      if (! insideHole) {
+        return shellPt;
+      }
+    }
+    Coordinate holePt = findPtNotNode(holePts, shell, graph);
+    // if point is on hole but not shell, check that the hole is outside the shell
+    if (holePt != null) {
+      boolean insideShell = CGAlgorithms.isPointInRing(holePt, shellPts);
+      if (insideShell) {
+        return holePt;
+      }
+      return null;
+    }
+    Assert.shouldNeverReachHere("points in shell and hole appear to be equal");
+    return null;
+  }
+
+  private void checkConnectedInteriors(GeometryGraph graph)
+  {
+    ConnectedInteriorTester cit = new ConnectedInteriorTester(graph);
+    if (! cit.isInteriorsConnected())
+      validErr = new TopologyValidationError(
+                        TopologyValidationError.DISCONNECTED_INTERIOR,
+                        cit.getCoordinate());
+  }
+
+}

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/valid/QuadtreeNestedRingTester.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,116 @@
+
+/*
+ * 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.valid;
+
+import java.util.*;
+import com.vividsolutions.jts.algorithm.*;
+import com.vividsolutions.jts.geom.*;
+import com.vividsolutions.jts.geomgraph.*;
+import com.vividsolutions.jts.index.quadtree.Quadtree;
+import com.vividsolutions.jts.util.*;
+
+/**
+ * Tests whether any of a set of {@link LinearRing}s are
+ * nested inside another ring in the set, using a {@link Quadtree}
+ * index to speed up the comparisons.
+ *
+ * @version 1.6
+ */
+public class QuadtreeNestedRingTester
+{
+
+  private GeometryGraph graph;  // used to find non-node vertices
+  private List rings = new ArrayList();
+  private Envelope totalEnv = new Envelope();
+  private Quadtree quadtree;
+  private Coordinate nestedPt;
+
+  public QuadtreeNestedRingTester(GeometryGraph graph)
+  {
+    this.graph = graph;
+  }
+
+  public Coordinate getNestedPoint() { return nestedPt; }
+
+  public void add(LinearRing ring)
+  {
+    rings.add(ring);
+    totalEnv.expandToInclude(ring.getEnvelopeInternal());
+  }
+
+  public boolean isNonNested()
+  {
+    buildQuadtree();
+
+    for (int i = 0; i < rings.size(); i++) {
+      LinearRing innerRing = (LinearRing) rings.get(i);
+      Coordinate[] innerRingPts = innerRing.getCoordinates();
+
+      List results = quadtree.query(innerRing.getEnvelopeInternal());
+//System.out.println(results.size());
+      for (int j = 0; j < results.size(); j++) {
+        LinearRing searchRing = (LinearRing) results.get(j);
+        Coordinate[] searchRingPts = searchRing.getCoordinates();
+
+        if (innerRing == searchRing)
+          continue;
+
+        if (! innerRing.getEnvelopeInternal().intersects(searchRing.getEnvelopeInternal()))
+          continue;
+
+        Coordinate innerRingPt = IsValidOp.findPtNotNode(innerRingPts, searchRing, graph);
+        Assert.isTrue(innerRingPt != null, "Unable to find a ring point not a node of the search ring");
+        //Coordinate innerRingPt = innerRingPts[0];
+
+        boolean isInside = CGAlgorithms.isPointInRing(innerRingPt, searchRingPts);
+        if (isInside) {
+          nestedPt = innerRingPt;
+          return false;
+        }
+      }
+    }
+    return true;
+  }
+
+  private void buildQuadtree()
+  {
+    quadtree = new Quadtree();
+
+    for (int i = 0; i < rings.size(); i++) {
+      LinearRing ring = (LinearRing) rings.get(i);
+      Envelope env = ring.getEnvelopeInternal();
+      quadtree.insert(env, ring);
+    }
+  }
+}

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/valid/RepeatedPointTester.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,96 @@
+
+
+/*
+ * 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.valid;
+
+import com.vividsolutions.jts.geom.*;
+
+/**
+ * Implements the appropriate checks for repeated points
+ * (consecutive identical coordinates) as defined in the
+ * JTS spec.
+ *
+ * @version 1.6
+ */
+public class RepeatedPointTester {
+
+  // save the repeated coord found (if any)
+  private Coordinate repeatedCoord;
+
+  public RepeatedPointTester() {
+  }
+
+  public Coordinate getCoordinate() { return repeatedCoord; }
+
+  public boolean hasRepeatedPoint(Geometry g)
+  {
+    if (g.isEmpty()) return false;
+    if (g instanceof Point)                   return false;
+    else if (g instanceof MultiPoint)         return false;
+                        // LineString also handles LinearRings
+    else if (g instanceof LineString)         return hasRepeatedPoint(((LineString) g).getCoordinates());
+    else if (g instanceof Polygon)            return hasRepeatedPoint((Polygon) g);
+    else if (g instanceof GeometryCollection) return hasRepeatedPoint((GeometryCollection) g);
+    else  throw new UnsupportedOperationException(g.getClass().getName());
+  }
+
+  public boolean hasRepeatedPoint(Coordinate[] coord)
+  {
+    for (int i = 1; i < coord.length; i++) {
+      if (coord[i - 1].equals(coord[i]) ) {
+        repeatedCoord = coord[i];
+        return true;
+      }
+    }
+    return false;
+  }
+  private boolean hasRepeatedPoint(Polygon p)
+  {
+    if (hasRepeatedPoint(p.getExteriorRing().getCoordinates())) return true;
+    for (int i = 0; i < p.getNumInteriorRing(); i++) {
+      if (hasRepeatedPoint(p.getInteriorRingN(i).getCoordinates())) return true;
+    }
+    return false;
+  }
+  private boolean hasRepeatedPoint(GeometryCollection gc)
+  {
+    for (int i = 0; i < gc.getNumGeometries(); i++) {
+      Geometry g = gc.getGeometryN(i);
+      if (hasRepeatedPoint(g)) return true;
+    }
+    return false;
+  }
+
+
+}

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/valid/SimpleNestedRingTester.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,99 @@
+
+/*
+ * 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.valid;
+
+import java.util.*;
+import com.vividsolutions.jts.algorithm.*;
+import com.vividsolutions.jts.geom.*;
+import com.vividsolutions.jts.geomgraph.*;
+import com.vividsolutions.jts.util.*;
+
+/**
+ * Tests whether any of a set of {@link LinearRing}s are
+ * nested inside another ring in the set, using a simple O(n^2)
+ * comparison.
+ *
+ * @version 1.6
+ */
+public class SimpleNestedRingTester
+{
+  private final CGAlgorithms cga = new CGAlgorithms();
+
+  private GeometryGraph graph;  // used to find non-node vertices
+  private List rings = new ArrayList();
+  private Coordinate nestedPt;
+
+  public SimpleNestedRingTester(GeometryGraph graph)
+  {
+    this.graph = graph;
+  }
+
+  public void add(LinearRing ring)
+  {
+    rings.add(ring);
+  }
+
+  public Coordinate getNestedPoint() { return nestedPt; }
+
+  public boolean isNonNested()
+  {
+    for (int i = 0; i < rings.size(); i++) {
+      LinearRing innerRing = (LinearRing) rings.get(i);
+      Coordinate[] innerRingPts = innerRing.getCoordinates();
+
+      for (int j = 0; j < rings.size(); j++) {
+        LinearRing searchRing = (LinearRing) rings.get(j);
+        Coordinate[] searchRingPts = searchRing.getCoordinates();
+
+        if (innerRing == searchRing)
+          continue;
+
+        if (! innerRing.getEnvelopeInternal().intersects(searchRing.getEnvelopeInternal()))
+          continue;
+
+        Coordinate innerRingPt = IsValidOp.findPtNotNode(innerRingPts, searchRing, graph);
+        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);
+        if (isInside) {
+          nestedPt = innerRingPt;
+          return false;
+        }
+      }
+    }
+    return true;
+  }
+
+}

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/valid/SweeplineNestedRingTester.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -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.operation.valid;
+
+import java.util.*;
+import com.vividsolutions.jts.algorithm.*;
+import com.vividsolutions.jts.geom.*;
+import com.vividsolutions.jts.geomgraph.*;
+import com.vividsolutions.jts.index.sweepline.*;
+import com.vividsolutions.jts.util.*;
+
+/**
+ * Tests whether any of a set of {@link LinearRing}s are
+ * nested inside another ring in the set, using a {@link SweepLineIndex}
+ * index to speed up the comparisons.
+ *
+ * @version 1.6
+ */
+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 SweepLineIndex sweepLine;
+  private Coordinate nestedPt = null;
+
+  public SweeplineNestedRingTester(GeometryGraph graph)
+  {
+    this.graph = graph;
+  }
+
+  public Coordinate getNestedPoint() { return nestedPt; }
+
+  public void add(LinearRing ring)
+  {
+    rings.add(ring);
+  }
+
+  public boolean isNonNested()
+  {
+    buildIndex();
+
+    OverlapAction action = new OverlapAction();
+
+    sweepLine.computeOverlaps(action);
+    return action.isNonNested;
+  }
+
+  private void buildIndex()
+  {
+    sweepLine = new SweepLineIndex();
+
+    for (int i = 0; i < rings.size(); i++) {
+      LinearRing ring = (LinearRing) rings.get(i);
+      Envelope env = ring.getEnvelopeInternal();
+      SweepLineInterval sweepInt = new SweepLineInterval(env.getMinX(), env.getMaxX(), ring);
+      sweepLine.add(sweepInt);
+    }
+  }
+
+  private boolean isInside(LinearRing innerRing, LinearRing searchRing)
+  {
+    Coordinate[] innerRingPts = innerRing.getCoordinates();
+    Coordinate[] searchRingPts = searchRing.getCoordinates();
+
+    if (! innerRing.getEnvelopeInternal().intersects(searchRing.getEnvelopeInternal()))
+      return false;
+
+    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);
+    if (isInside) {
+      nestedPt = innerRingPt;
+      return true;
+    }
+    return false;
+  }
+
+
+  class OverlapAction
+    implements SweepLineOverlapAction
+  {
+    boolean isNonNested = true;
+
+  public void overlap(SweepLineInterval s0, SweepLineInterval s1)
+  {
+    LinearRing innerRing = (LinearRing) s0.getItem();
+    LinearRing searchRing = (LinearRing) s1.getItem();
+    if (innerRing == searchRing) return;
+
+    if (isInside(innerRing, searchRing))
+      isNonNested = false;
+  }
+
+  }
+}

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/valid/TopologyValidationError.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,98 @@
+
+
+/*
+ * 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.valid;
+
+import com.vividsolutions.jts.geom.Coordinate;
+
+/**
+ * Contains information about the nature and location of a {@link Geometry}
+ * validation error
+ *
+ * @version 1.6
+ */
+public class TopologyValidationError {
+
+  public static final int ERROR                   = 0;
+  public static final int REPEATED_POINT          = 1;
+  public static final int HOLE_OUTSIDE_SHELL      = 2;
+  public static final int NESTED_HOLES            = 3;
+  public static final int DISCONNECTED_INTERIOR   = 4;
+  public static final int SELF_INTERSECTION       = 5;
+  public static final int RING_SELF_INTERSECTION  = 6;
+  public static final int NESTED_SHELLS           = 7;
+  public static final int DUPLICATE_RINGS         = 8;
+  public static final int TOO_FEW_POINTS          = 9;
+  public static final int INVALID_COORDINATE      = 10;
+
+  // these messages must synch up with the indexes above
+  private static String[] errMsg = {
+    "Topology Validation Error",
+    "Repeated Point",
+    "Hole lies outside shell",
+    "Holes are nested",
+    "Interior is disconnected",
+    "Self-intersection",
+    "Ring Self-intersection",
+    "Nested shells",
+    "Duplicate Rings",
+    "Too few points in geometry component",
+    "Invalid Coordinate"
+  };
+
+
+  private int errorType;
+  private Coordinate pt;
+
+  public TopologyValidationError(int errorType, Coordinate pt)
+  {
+    this.errorType = errorType;
+    this.pt = (Coordinate) pt.clone();
+  }
+  public TopologyValidationError(int errorType)
+  {
+    this(errorType, null);
+  }
+
+  public Coordinate getCoordinate() { return pt; }
+
+  public int getErrorType() { return errorType; }
+
+  public String getMessage() { return errMsg[errorType]; }
+
+  public String toString()
+  {
+    return getMessage() + " at or near point " + pt;
+  }
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/valid/package.html
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/valid/package.html	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/operation/valid/package.html	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,13 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+<!--
+
+-->
+</head>
+<body bgcolor="white">
+
+Provides classes for testing the validity of geometries.
+
+</body>
+</html>

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/planargraph/DirectedEdge.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/planargraph/DirectedEdge.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/planargraph/DirectedEdge.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -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.planargraph;
+
+import java.util.*;
+import java.io.PrintStream;
+import com.vividsolutions.jts.algorithm.CGAlgorithms;
+import com.vividsolutions.jts.geom.Coordinate;
+import com.vividsolutions.jts.geomgraph.Quadrant;
+
+/**
+ * Represents a directed edge in a {@link PlanarGraph}. A DirectedEdge may or
+ * may not have a reference to a parent {@link Edge} (some applications of
+ * planar graphs may not require explicit Edge objects to be created). Usually
+ * a client using a <code>PlanarGraph</code> will subclass <code>DirectedEdge</code>
+ * to add its own application-specific data and methods.
+ *
+ * @version 1.6
+ */
+public class DirectedEdge
+    extends GraphComponent
+    implements Comparable
+{
+  /**
+   * Returns a List containing the parent Edge (possibly null) for each of the given
+   * DirectedEdges.
+   */
+  public static List toEdges(Collection dirEdges)
+  {
+    List edges = new ArrayList();
+    for (Iterator i = dirEdges.iterator(); i.hasNext(); ) {
+      edges.add( ((DirectedEdge) i.next()).parentEdge);
+    }
+    return edges;
+  }
+
+  protected static final CGAlgorithms cga = new CGAlgorithms();
+
+  protected Edge parentEdge;
+  protected Node from;
+  protected Node to;
+  protected Coordinate p0, p1;
+  protected DirectedEdge sym = null;  // optional
+  protected boolean edgeDirection;
+  protected int quadrant;
+  protected double angle;
+
+  /**
+   * Constructs a DirectedEdge connecting the <code>from</code> node to the
+   * <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>)
+   * @param edgeDirection
+   *                  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)
+  {
+    this.from = from;
+    this.to = to;
+    this.edgeDirection = edgeDirection;
+    p0 = from.getCoordinate();
+    p1 = directionPt;
+    double dx = p1.x - p0.x;
+    double dy = p1.y - p0.y;
+    quadrant = Quadrant.quadrant(dx, dy);
+    angle = Math.atan2(dy, dx);
+    //Assert.isTrue(! (dx == 0 && dy == 0), "EdgeEnd with identical endpoints found");
+  }
+
+  /**
+   * Returns this DirectedEdge's parent Edge, or null if it has none.
+   */
+  public Edge getEdge() { return parentEdge; }
+  /**
+   * Associates this DirectedEdge with an Edge (possibly null, indicating no associated
+   * Edge).
+   */
+  public void setEdge(Edge parentEdge) { this.parentEdge = parentEdge; }
+  /**
+   * Returns 0, 1, 2, or 3, indicating the quadrant in which this DirectedEdge's
+   * orientation lies.
+   */
+  public int getQuadrant() { return quadrant; }
+  /**
+   * Returns a point to which an imaginary line is drawn from the from-node to
+   * specify this DirectedEdge's orientation.
+   */
+  public Coordinate getDirectionPt() { return p1; }
+  /**
+   * Returns whether the direction of the parent Edge (if any) is the same as that
+   * of this Directed Edge.
+   */
+  public boolean getEdgeDirection() { return edgeDirection; }
+  /**
+   * Returns the node from which this DirectedEdge leaves.
+   */
+  public Node getFromNode() { return from; }
+  /**
+   * Returns the node to which this DirectedEdge goes.
+   */
+  public Node getToNode() { return to; }
+  /**
+   * Returns the coordinate of the from-node.
+   */
+  public Coordinate getCoordinate() { return from.getCoordinate(); }
+  /**
+   * Returns the angle that the start of this DirectedEdge makes with the
+   * positive x-axis, in radians.
+   */
+  public double getAngle() { return angle; }
+  /**
+   * Returns the symmetric DirectedEdge -- the other DirectedEdge associated with
+   * this DirectedEdge's parent Edge.
+   */
+  public DirectedEdge getSym() { return sym; }
+  /**
+   * Sets this DirectedEdge's symmetric DirectedEdge, which runs in the opposite
+   * direction.
+   */
+  public void setSym(DirectedEdge sym) { this.sym = sym; }
+
+  /**
+   * 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>
+   * Using the obvious algorithm of simply computing the angle is not robust,
+   * since the angle calculation is susceptible to roundoff. A robust algorithm
+   * is:
+   * <ul>
+   * <li>first compare the quadrants. If the quadrants are different, it it
+   * trivial to determine which vector is "greater".
+   * <li>if the vectors lie in the same quadrant, the robust
+   * {@link RobustCGAlgorithms#computeOrientation(Coordinate, Coordinate, Coordinate)}
+   * function can be used to decide the relative orientation of the vectors.
+   * </ul>
+   */
+  public int compareTo(Object obj)
+  {
+      DirectedEdge de = (DirectedEdge) obj;
+      return compareDirection(de);
+  }
+
+  /**
+   * 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>
+   * Using the obvious algorithm of simply computing the angle is not robust,
+   * since the angle calculation is susceptible to roundoff. A robust algorithm
+   * is:
+   * <ul>
+   * <li>first compare the quadrants. If the quadrants are different, it it
+   * trivial to determine which vector is "greater".
+   * <li>if the vectors lie in the same quadrant, the robust
+   * {@link RobustCGAlgorithms#computeOrientation(Coordinate, Coordinate, Coordinate)}
+   * function can be used to decide the relative orientation of the vectors.
+   * </ul>
+   */
+  public int compareDirection(DirectedEdge e)
+  {
+    // if the rays are in different quadrants, determining the ordering is trivial
+    if (quadrant > e.quadrant) return 1;
+    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);
+  }
+
+  /**
+   * Prints a detailed string representation of this DirectedEdge to the given PrintStream.
+   */
+  public void print(PrintStream out)
+  {
+    String className = getClass().getName();
+    int lastDotPos = className.lastIndexOf('.');
+    String name = className.substring(lastDotPos + 1);
+    out.print("  " + name + ": " + p0 + " - " + p1 + " " + quadrant + ":" + angle);
+  }
+
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/planargraph/DirectedEdgeStar.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/planargraph/DirectedEdgeStar.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/planargraph/DirectedEdgeStar.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,166 @@
+
+/*
+ * 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.planargraph;
+
+import java.util.*;
+import com.vividsolutions.jts.geom.Coordinate;
+
+/**
+ * A sorted collection of {@link DirectedEdge}s which leave a {@link Node}
+ * in a {@link PlanarGraph}.
+ *
+ * @version 1.6
+ */
+public class DirectedEdgeStar
+{
+
+  /**
+   * The underlying list of outgoing DirectedEdges
+   */
+  protected List outEdges = new ArrayList();
+  private boolean sorted = false;
+
+  /**
+   * Constructs a DirectedEdgeStar with no edges.
+   */
+  public DirectedEdgeStar() {
+  }
+  /**
+   * Adds a new member to this DirectedEdgeStar.
+   */
+  public void add(DirectedEdge de)
+  {
+    outEdges.add(de);
+    sorted = false;
+  }
+  /**
+   * Drops a member of this DirectedEdgeStar.
+   */
+  public void remove(DirectedEdge de)
+  {
+    outEdges.remove(de);
+  }
+  /**
+   * Returns an Iterator over the DirectedEdges, in ascending order by angle with the positive x-axis.
+   */
+  public Iterator iterator()
+  {
+    sortEdges();
+    return outEdges.iterator();
+  }
+
+  /**
+   * Returns the number of edges around the Node associated with this DirectedEdgeStar.
+   */
+  public int getDegree() { return outEdges.size(); }
+
+  /**
+   * Returns the coordinate for the node at wich this star is based
+   */
+  public Coordinate getCoordinate()
+  {
+    Iterator it = iterator();
+    if (! it.hasNext()) return null;
+    DirectedEdge e = (DirectedEdge) it.next();
+    return e.getCoordinate();
+  }
+
+  /**
+   * Returns the DirectedEdges, in ascending order by angle with the positive x-axis.
+   */
+  public List getEdges()
+  {
+    sortEdges();
+    return outEdges;
+  }
+
+  private void sortEdges()
+  {
+    if (! sorted) {
+      Collections.sort(outEdges);
+      sorted = true;
+    }
+  }
+  /**
+   * Returns the zero-based index of the given Edge, after sorting in ascending order
+   * by angle with the positive x-axis.
+   */
+  public int getIndex(Edge edge)
+  {
+    sortEdges();
+    for (int i = 0; i < outEdges.size(); i++) {
+      DirectedEdge de = (DirectedEdge) outEdges.get(i);
+      if (de.getEdge() == edge)
+        return i;
+    }
+    return -1;
+  }
+  /**
+   * Returns the zero-based index of the given DirectedEdge, after sorting in ascending order
+   * by angle with the positive x-axis.
+   */  
+  public int getIndex(DirectedEdge dirEdge)
+  {
+    sortEdges();
+    for (int i = 0; i < outEdges.size(); i++) {
+      DirectedEdge de = (DirectedEdge) outEdges.get(i);
+      if (de == dirEdge)
+        return i;
+    }
+    return -1;
+  }
+  /**
+   * Returns the remainder when i is divided by the number of edges in this
+   * DirectedEdgeStar. 
+   */
+  public int getIndex(int i)
+  {
+    int modi = i % outEdges.size();
+    //I don't think modi can be 0 (assuming i is positive) [Jon Aquino 10/28/2003] 
+    if (modi < 0) modi += outEdges.size();
+    return modi;
+  }
+
+  /**
+   * Returns the DirectedEdge on the left-hand side of the given DirectedEdge (which
+   * must be a member of this DirectedEdgeStar). 
+   */
+  public DirectedEdge getNextEdge(DirectedEdge dirEdge)
+  {
+    int i = getIndex(dirEdge);
+    return (DirectedEdge) outEdges.get(getIndex(i + 1));
+  }
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/planargraph/Edge.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/planargraph/Edge.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/planargraph/Edge.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,120 @@
+
+/*
+ * 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.planargraph;
+
+/**
+ * Represents an undirected edge of a {@link PlanarGraph}. An undirected edge
+ * in fact simply acts as a central point of reference for two opposite
+ * {@link DirectedEdge}s.
+ * <p>
+ * 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
+ */
+public class Edge
+    extends GraphComponent
+{
+
+  /** The two DirectedEdges associated with this Edge */
+  protected DirectedEdge[] dirEdge;
+
+  /**
+   * Constructs an Edge whose DirectedEdges are not yet set. Be sure to call
+   * {@link #setDirectedEdges(DirectedEdge, DirectedEdge)}
+   */
+  public Edge()
+  {
+  }
+
+  /**
+   * Constructs an Edge initialized with the given DirectedEdges, and for each
+   * DirectedEdge: sets the Edge, sets the symmetric DirectedEdge, and adds
+   * this Edge to its from-Node.
+   */
+  public Edge(DirectedEdge de0, DirectedEdge de1)
+  {
+    setDirectedEdges(de0, de1);
+  }
+
+  /**
+   * Initializes this Edge's two DirectedEdges, and for each DirectedEdge: sets the
+   * Edge, sets the symmetric DirectedEdge, and adds this Edge to its from-Node.
+   */
+  public void setDirectedEdges(DirectedEdge de0, DirectedEdge de1)
+  {
+    dirEdge = new DirectedEdge[] { de0, de1 };
+    de0.setEdge(this);
+    de1.setEdge(this);
+    de0.setSym(de1);
+    de1.setSym(de0);
+    de0.getFromNode().addOutEdge(de0);
+    de1.getFromNode().addOutEdge(de1);
+  }
+
+  /**
+   * Returns one of the DirectedEdges associated with this Edge.
+   * @param i 0 or 1
+   */
+  public DirectedEdge getDirEdge(int i)
+  {
+    return dirEdge[i];
+  }
+
+  /**
+   * Returns the {@link DirectedEdge} that starts from the given node, or null if the
+   * node is not one of the two nodes associated with this Edge.
+   */
+  public DirectedEdge getDirEdge(Node fromNode)
+  {
+    if (dirEdge[0].getFromNode() == fromNode) return dirEdge[0];
+    if (dirEdge[1].getFromNode() == fromNode) return dirEdge[1];
+    // node not found
+    // possibly should throw an exception here?
+    return null;
+  }
+
+  /**
+   * If <code>node</code> is one of the two nodes associated with this Edge,
+   * returns the other node; otherwise returns null.
+   */
+  public Node getOppositeNode(Node node)
+  {
+    if (dirEdge[0].getFromNode() == node) return dirEdge[0].getToNode();
+    if (dirEdge[1].getFromNode() == node) return dirEdge[1].getToNode();
+    // node not found
+    // possibly should throw an exception here?
+    return null;
+  }
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/planargraph/GraphComponent.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/planargraph/GraphComponent.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/planargraph/GraphComponent.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,84 @@
+
+/*
+ * 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.planargraph;
+
+/**
+ * The base class for all graph component classes.
+ * Maintains flags of use in generic graph algorithms.
+ * Provides two flags:
+ * <ul>
+ * <li><b>marked</b> - typically this is used to indicate a state that persists
+ * for the course of the graph's lifetime.  For instance, it can be
+ * used to indicate that a component has been logically deleted from the graph.
+ * <li><b>visited</b> - this is used to indicate that a component has been processed
+ * or visited by an single graph algorithm.  For instance, a breadth-first traversal of the
+ * 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
+ */
+public class GraphComponent {
+
+  protected boolean isMarked = false;
+  protected boolean isVisited = false;
+
+  public GraphComponent() {
+  }
+
+  /**
+   * Tests if a component has been visited during the course of a graph algorithm
+   * @return <code>true</code> if the component has been visited
+   */
+  public boolean isVisited() { return isVisited; }
+
+  /**
+   * Sets the visited flag for this component.
+   * @param isVisited the desired value of the visited flag
+   */
+  public void setVisited(boolean isVisited) { this.isVisited = isVisited; }
+
+  /**
+   * Tests if a component has been marked at some point during the processing
+   * involving this graph.
+   * @return <code>true</code> if the component has been marked
+   */
+  public boolean isMarked() { return isMarked; }
+
+  /**
+   * Sets the marked flag for this component.
+   * @param isMarked the desired value of the marked flag
+   */
+  public void setMarked(boolean isMarked) { this.isMarked = isMarked; }
+
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/planargraph/Node.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/planargraph/Node.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/planargraph/Node.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,118 @@
+
+/*
+ * 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.planargraph;
+
+import java.util.*;
+import com.vividsolutions.jts.geom.Coordinate;
+
+/**
+ * A node in a {@link PlanarGraph}is a location where 0 or more {@link Edge}s
+ * meet. A node is connected to each of its incident Edges via an outgoing
+ * DirectedEdge. Some clients using a <code>PlanarGraph</code> may want to
+ * subclass <code>Node</code> to add their own application-specific
+ * data and methods.
+ *
+ * @version 1.6
+ */
+public class Node
+    extends GraphComponent
+{
+  /**
+   * Returns all Edges that connect the two nodes (which are assumed to be different).
+   */
+  public static Collection getEdgesBetween(Node node0, Node node1)
+  {
+    List edges0 = DirectedEdge.toEdges(node0.getOutEdges().getEdges());
+    Set commonEdges = new HashSet(edges0);
+    List edges1 = DirectedEdge.toEdges(node1.getOutEdges().getEdges());
+    commonEdges.retainAll(edges1);
+    return commonEdges;
+  }
+
+  /** The location of this Node */
+  protected Coordinate pt;
+
+  /** The collection of DirectedEdges that leave this Node */
+  protected DirectedEdgeStar deStar;
+
+  /**
+   * Constructs a Node with the given location.
+   */
+  public Node(Coordinate pt)
+  {
+    this(pt, new DirectedEdgeStar());
+  }
+
+  /**
+   * Constructs a Node with the given location and collection of outgoing DirectedEdges.
+   */
+  public Node(Coordinate pt, DirectedEdgeStar deStar)
+  {
+    this.pt = pt;
+    this.deStar = deStar;
+  }
+
+  /**
+   * Returns the location of this Node.
+   */
+  public Coordinate getCoordinate() { return pt; }
+
+  /**
+   * Adds an outgoing DirectedEdge to this Node.
+   */
+  public void addOutEdge(DirectedEdge de)
+  {
+    deStar.add(de);
+  }
+
+  /**
+   * Returns the collection of DirectedEdges that leave this Node.
+   */
+  public DirectedEdgeStar getOutEdges() { return deStar; }
+  /**
+   * Returns the number of edges around this Node.
+   */
+  public int getDegree() { return deStar.getDegree(); }
+  /**
+   * Returns the zero-based index of the given Edge, after sorting in ascending order
+   * by angle with the positive x-axis.
+   */
+  public int getIndex(Edge edge)
+  {
+    return deStar.getIndex(edge);
+  }
+
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/planargraph/NodeMap.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/planargraph/NodeMap.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/planargraph/NodeMap.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,101 @@
+/*
+ * 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.planargraph;
+
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.TreeMap;
+
+import com.vividsolutions.jts.geom.Coordinate;
+
+/**
+ * A map of {@link Node}s, indexed by the coordinate of the node.
+ *
+ * @version 1.6
+ */
+public class NodeMap
+
+{
+
+  private Map nodeMap = new TreeMap();
+  
+  /**
+   * Constructs a NodeMap without any Nodes.
+   */
+  public NodeMap() {
+  }
+
+  /**
+   * Adds a node to the map, replacing any that is already at that location.
+   * @return the added node
+   */
+  public Node add(Node n)
+  {
+    nodeMap.put(n.getCoordinate(), n);
+    return n;
+  }
+
+  /**
+   * Removes the Node at the given location, and returns it (or null if no Node was there).
+   */
+  public Node remove(Coordinate pt)
+  {
+    return (Node) nodeMap.remove(pt);
+  }
+
+  /**
+   * Returns the Node at the given location, or null if no Node was there.
+   */
+  public Node find(Coordinate coord)  {    return (Node) nodeMap.get(coord);  }
+
+  /**
+   * Returns an Iterator over the Nodes in this NodeMap, sorted in ascending order
+   * by angle with the positive x-axis.
+   */
+  public Iterator iterator()
+  {
+    return nodeMap.values().iterator();
+  }
+  /**
+   * Returns the Nodes in this NodeMap, sorted in ascending order
+   * by angle with the positive x-axis.
+   */
+  public Collection values()
+  {
+    return nodeMap.values();
+  }
+
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/planargraph/PlanarGraph.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/planargraph/PlanarGraph.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/planargraph/PlanarGraph.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,202 @@
+
+/*
+ * 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.planargraph;
+
+import java.util.*;
+import com.vividsolutions.jts.geom.Coordinate;
+
+/**
+ * Represents a directed graph which is embeddable in a planar surface.
+ * <p>
+ * This class and the other classes in this package serve as a framework for
+ * building planar graphs for specific algorithms. This class must be
+ * subclassed to expose appropriate methods to construct the graph. This allows
+ * controlling the types of graph components ({@link DirectedEdge}s,
+ * {@link Edge}s and {@link Node}s) which can be added to the graph. An
+ * application which uses the graph framework will almost always provide
+ * subclasses for one or more graph components, which hold application-specific
+ * data and graph algorithms.
+ *
+ * @version 1.6
+ */
+public abstract class PlanarGraph
+{
+
+
+  protected List edges = new ArrayList();
+  protected List dirEdges = new ArrayList();
+  protected NodeMap nodeMap = new NodeMap();
+  /**
+   * Constructs a PlanarGraph without any Edges, DirectedEdges, or Nodes.
+   */
+  public PlanarGraph()
+  {
+  }
+  /**
+   * Returns the Node at the given location, or null if no Node was there.
+   */
+  public Node findNode(Coordinate pt)
+  {
+    return (Node) nodeMap.find(pt);
+  }
+
+  /**
+   * Adds a node to the map, replacing any that is already at that location.
+   * Only subclasses can add Nodes, to ensure Nodes are of the right type.
+   * @return the added node
+   */
+  protected void add(Node node)
+  {
+    nodeMap.add(node);
+  }
+
+  /**
+   * Adds the Edge and its DirectedEdges with this PlanarGraph.
+   * Assumes that the Edge has already been created with its associated DirectEdges.
+   * Only subclasses can add Edges, to ensure the edges added are of the right class.
+   */
+  protected void add(Edge edge)
+  {
+    edges.add(edge);
+    add(edge.getDirEdge(0));
+    add(edge.getDirEdge(1));
+  }
+
+  /**
+   * Adds the Edge to this PlanarGraph; only subclasses can add DirectedEdges,
+   * to ensure the edges added are of the right class.
+   */
+  protected void add(DirectedEdge dirEdge)
+  {
+    dirEdges.add(dirEdge);
+  }
+  /**
+   * Returns an Iterator over the Nodes in this PlanarGraph.
+   */
+  public Iterator nodeIterator()  {    return nodeMap.iterator();  }
+  /**
+   * Returns the Nodes in this PlanarGraph.
+   */  
+
+  public Collection getNodes()  {    return nodeMap.values();  }
+
+  /**
+   * Returns an Iterator over the DirectedEdges in this PlanarGraph, in the order in which they
+   * were added.
+   *
+   * @see #add(Edge)
+   * @see #add(DirectedEdge)
+   */
+  public Iterator dirEdgeIterator()  {    return dirEdges.iterator();  }
+  /**
+   * Returns an Iterator over the Edges in this PlanarGraph, in the order in which they
+   * were added.
+   *
+   * @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;  }
+
+  /**
+   * 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.
+   */
+  public void remove(Edge edge)
+  {
+    remove(edge.getDirEdge(0));
+    remove(edge.getDirEdge(1));
+    edges.remove(edge);
+  }
+
+  /**
+   * Removes DirectedEdge from its from-Node and from this PlanarGraph. Note:
+   * 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.
+   */
+  public void remove(DirectedEdge de)
+  {
+    DirectedEdge sym = de.getSym();
+    if (sym != null) sym.setSym(null);
+    de.getFromNode().getOutEdges().remove(de);
+    dirEdges.remove(de);
+  }
+  /**
+   * Removes a node from the graph, along with any associated DirectedEdges and
+   * Edges.
+   */
+  public void remove(Node node)
+  {
+    // unhook all directed edges
+    List outEdges = node.getOutEdges().getEdges();
+    for (Iterator i = outEdges.iterator(); i.hasNext(); ) {
+      DirectedEdge de = (DirectedEdge) i.next();
+      DirectedEdge sym = de.getSym();
+      // remove the diredge that points to this node
+      if (sym != null) remove(sym);
+      // remove this diredge from the graph collection
+      dirEdges.remove(de);
+
+      Edge edge = de.getEdge();
+      if (edge != null) {
+        edges.remove(edge);
+      }
+
+    }
+    // remove the node from the graph
+    nodeMap.remove(node.getCoordinate());
+    //nodes.remove(node);
+  }
+
+  /**
+   * Returns all Nodes with the given number of Edges around it.
+   */
+  public List findNodesOfDegree(int degree)
+  {
+    List nodesFound = new ArrayList();
+    for (Iterator i = nodeIterator(); i.hasNext(); ) {
+      Node node = (Node) i.next();
+      if (node.getDegree() == degree)
+        nodesFound.add(node);
+    }
+    return nodesFound;
+  }
+
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/planargraph/package.html
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/planargraph/package.html	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/planargraph/package.html	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,13 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+<!--
+
+-->
+</head>
+<body bgcolor="white">
+
+Contains classes to implement a planar graph data structure.
+
+</body>
+</html>

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/polygonize/package.html
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/polygonize/package.html	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/polygonize/package.html	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,14 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+<!--
+
+-->
+</head>
+<body bgcolor="white">
+
+Contains classes to implement a polygonization algorithm.
+This package can be used to create polygons from fully noded linework.
+
+</body>
+</html>

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/precision/CommonBits.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/precision/CommonBits.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/precision/CommonBits.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,156 @@
+
+/*
+ * 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.precision;
+
+/**
+ * Determines the maximum number of common most-significant
+ * bits in the mantissa of one or numbers.
+ * Can be used to compute the double-precision number which
+ * is represented by the common bits.
+ * If there are no common bits, the number computed is 0.0.
+ *
+ * @version 1.6
+ */
+public class CommonBits {
+
+  /**
+   * Computes the bit pattern for the sign and exponent of a
+   * double-precision number.
+   * @param num
+   * @return the bit pattern for the sign and exponent
+   */
+  public static long signExpBits(long num)
+  {
+    return num >> 52;
+  }
+
+  /**
+ * This computes the number of common most-significant bits in the mantissas
+ * of two double-precision numbers.
+ * It does not count the hidden bit, which is always 1.
+ * It does not determine whether the numbers have the same exponent - if they do
+ * not, the value computed by this function is meaningless.
+ * @param db
+ * @return the number of common most-significant mantissa bits
+ */
+  public static int numCommonMostSigMantissaBits(long num1, long num2)
+  {
+    int count = 0;
+    for (int i = 52; i >= 0; i--)
+    {
+      if (getBit(num1, i) != getBit(num2, i))
+        return count;
+      count++;
+    }
+    return 52;
+  }
+
+  /**
+   * Zeroes the lower n bits of a bitstring.
+   * @param bits the bitstring to alter
+   * @param i the number of bits to zero
+   * @return the zeroed bitstring
+   */
+  public static long zeroLowerBits(long bits, int nBits)
+  {
+    long invMask = (1L << nBits) - 1L;
+    long mask = ~ invMask;
+    long zeroed = bits & mask;
+    return zeroed;
+  }
+
+  /**
+   * Extracts the i'th bit of a bitstring.
+   * @param bits the bitstring to extract from
+   * @param i the bit to extract
+   * @return the value of the extracted bit
+   */
+  public static int getBit(long bits, int i)
+  {
+    long mask = (1L << i);
+    return (bits & mask) != 0 ? 1 : 0;
+  }
+
+  private boolean isFirst = true;
+  private int commonMantissaBitsCount = 53;
+  private long commonBits = 0;
+  private long commonSignExp;
+
+  public CommonBits() {
+  }
+
+  public void add(double num)
+  {
+    long numBits = Double.doubleToLongBits(num);
+    if (isFirst) {
+      commonBits = numBits;
+      commonSignExp = signExpBits(commonBits);
+      isFirst = false;
+      return;
+    }
+
+    long numSignExp = signExpBits(numBits);
+    if (numSignExp != commonSignExp) {
+      commonBits = 0;
+      return;
+    }
+
+//    System.out.println(toString(commonBits));
+//    System.out.println(toString(numBits));
+    commonMantissaBitsCount = numCommonMostSigMantissaBits(commonBits, numBits);
+    commonBits = zeroLowerBits(commonBits, 64 - (12 + commonMantissaBitsCount));
+//    System.out.println(toString(commonBits));
+  }
+
+  public double getCommon()
+  {
+    return Double.longBitsToDouble(commonBits);
+  }
+  /**
+   * A representation of the Double bits formatted for easy readability
+   */
+  public String toString(long bits)
+  {
+    double x = Double.longBitsToDouble(bits);
+    String numStr = Long.toBinaryString(bits);
+    String padStr = "0000000000000000000000000000000000000000000000000000000000000000" + numStr;
+    String bitStr = padStr.substring(padStr.length() - 64);
+    String str = bitStr.substring(0, 1) + "  "
+        + bitStr.substring(1, 12) + "(exp) "
+        + bitStr.substring(12)
+        + " [ " + x + " ]";
+    return str;
+  }
+
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/precision/CommonBitsOp.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/precision/CommonBitsOp.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/precision/CommonBitsOp.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,184 @@
+
+/*
+ * 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.precision;
+
+import com.vividsolutions.jts.geom.*;
+
+/**
+ * Provides versions of Geometry spatial functions which use
+ * common bit removal to reduce the likelihood of robustness problems.
+ * <p>
+ * In the current implementation no rounding is performed on the
+ * reshifted result geometry, which means that it is possible
+ * that the returned Geometry is invalid.
+ * Client classes should check the validity of the returned result themselves.
+ *
+ * @version 1.6
+ */
+public class CommonBitsOp {
+
+  private boolean returnToOriginalPrecision = true;
+  private CommonBitsRemover cbr;
+
+  /**
+   * Creates a new instance of class, which reshifts result {@link Geometry}s.
+   */
+  public CommonBitsOp()
+  {
+    this(true);
+  }
+
+  /**
+   * Creates a new instance of class, specifying whether
+   * the result {@link Geometry}s should be reshifted.
+   *
+   * @param returnToOriginalPrecision
+   */
+  public CommonBitsOp(boolean returnToOriginalPrecision)
+  {
+    this.returnToOriginalPrecision = returnToOriginalPrecision;
+  }
+
+  /**
+   * Computes the set-theoretic intersection of two {@link Geometry}s, using enhanced precision.
+   * @param geom0 the first Geometry
+   * @param geom1 the second Geometry
+   * @return the Geometry representing the set-theoretic intersection of the input Geometries.
+   */
+  public Geometry intersection(Geometry geom0, Geometry geom1)
+  {
+    Geometry[] geom = removeCommonBits(geom0, geom1);
+    return computeResultPrecision(geom[0].intersection(geom[1]));
+  }
+
+  /**
+   * Computes the set-theoretic union of two {@link Geometry}s, using enhanced precision.
+   * @param geom0 the first Geometry
+   * @param geom1 the second Geometry
+   * @return the Geometry representing the set-theoretic union of the input Geometries.
+   */
+  public Geometry union(Geometry geom0, Geometry geom1)
+  {
+    Geometry[] geom = removeCommonBits(geom0, geom1);
+    return computeResultPrecision(geom[0].union(geom[1]));
+  }
+
+  /**
+   * Computes the set-theoretic difference of two {@link Geometry}s, using enhanced precision.
+   * @param geom0 the first Geometry
+   * @param geom1 the second Geometry, to be subtracted from the first
+   * @return the Geometry representing the set-theoretic difference of the input Geometries.
+   */
+  public Geometry difference(Geometry geom0, Geometry geom1)
+  {
+    Geometry[] geom = removeCommonBits(geom0, geom1);
+    return computeResultPrecision(geom[0].difference(geom[1]));
+  }
+
+  /**
+   * Computes the set-theoretic symmetric difference of two geometries,
+   * using enhanced precision.
+   * @param geom0 the first Geometry
+   * @param geom1 the second Geometry
+   * @return the Geometry representing the set-theoretic symmetric difference of the input Geometries.
+   */
+  public Geometry symDifference(Geometry geom0, Geometry geom1)
+  {
+    Geometry[] geom = removeCommonBits(geom0, geom1);
+    return computeResultPrecision(geom[0].symDifference(geom[1]));
+  }
+
+  /**
+   * Computes the buffer a geometry,
+   * using enhanced precision.
+   * @param geom0 the Geometry to buffer
+   * @param distance the buffer distance
+   * @return the Geometry representing the buffer of the input Geometry.
+   */
+  public Geometry buffer(Geometry geom0, double distance)
+  {
+    Geometry geom = removeCommonBits(geom0);
+    return computeResultPrecision(geom.buffer(distance));
+  }
+
+  /**
+   * If required, returning the result to the orginal precision if required.
+   * <p>
+   * In this current implementation, no rounding is performed on the
+   * reshifted result geometry, which means that it is possible
+   * that the returned Geometry is invalid.
+   *
+   * @param result the result Geometry to modify
+   * @return the result Geometry with the required precision
+   */
+  private Geometry computeResultPrecision(Geometry result)
+  {
+    if (returnToOriginalPrecision)
+      cbr.addCommonBits(result);
+    return result;
+  }
+
+  /**
+   * Computes a copy of the input {@link Geometry} with the calculated common bits
+   * removed from each coordinate.
+   * @param geom0 the Geometry to remove common bits from
+   * @return a copy of the input Geometry with common bits removed
+   */
+  private Geometry removeCommonBits(Geometry geom0)
+  {
+    cbr = new CommonBitsRemover();
+    cbr.add(geom0);
+    Geometry geom = cbr.removeCommonBits((Geometry) geom0.clone());
+    return geom;
+  }
+
+  /**
+   * Computes a copy of each input {@link Geometry}s with the calculated common bits
+   * removed from each coordinate.
+   * @param geom0 a Geometry to remove common bits from
+   * @param geom1 a Geometry to remove common bits from
+   * @return an array containing copies
+   * of the input Geometry's with common bits removed
+   */
+  private Geometry[] removeCommonBits(Geometry geom0, Geometry geom1)
+  {
+    cbr = new CommonBitsRemover();
+    cbr.add(geom0);
+    cbr.add(geom1);
+    Geometry geom[] = new Geometry[2];
+    geom[0] = cbr.removeCommonBits((Geometry) geom0.clone());
+    geom[1] = cbr.removeCommonBits((Geometry) geom1.clone());
+    return geom;
+  }
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/precision/CommonBitsRemover.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/precision/CommonBitsRemover.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/precision/CommonBitsRemover.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,142 @@
+
+/*
+ * 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.precision;
+
+import com.vividsolutions.jts.geom.*;
+
+/**
+ * Allow computing and removing common mantissa bits from one or more Geometries.
+ *
+ * @version 1.6
+ */
+public class CommonBitsRemover
+{
+  private Coordinate commonCoord;
+  private CommonCoordinateFilter ccFilter = new CommonCoordinateFilter();
+
+  public CommonBitsRemover()
+  {
+  }
+
+  /**
+   * Add a geometry to the set of geometries whose common bits are
+   * being computed.  After this method has executed the
+   * common coordinate reflects the common bits of all added
+   * geometries.
+   *
+   * @param geom a Geometry to test for common bits
+   */
+  public void add(Geometry geom)
+  {
+    geom.apply(ccFilter);
+    commonCoord = ccFilter.getCommonCoordinate();
+  }
+
+  /**
+   * The common bits of the Coordinates in the supplied Geometries.
+   */
+  public Coordinate getCommonCoordinate() { return commonCoord; }
+
+  /**
+   * Removes the common coordinate bits from a Geometry.
+   * The coordinates of the Geometry are changed.
+   *
+   * @param geom the Geometry from which to remove the common coordinate bits
+   * @return the shifted Geometry
+   */
+  public Geometry removeCommonBits(Geometry geom)
+  {
+    if (commonCoord.x == 0.0 && commonCoord.y == 0.0)
+      return geom;
+    Coordinate invCoord = new Coordinate(commonCoord);
+    invCoord.x = -invCoord.x;
+    invCoord.y = -invCoord.y;
+    Translater trans = new Translater(invCoord);
+    geom.apply(trans);
+    geom.geometryChanged();
+    return geom;
+  }
+
+  /**
+   * Adds the common coordinate bits back into a Geometry.
+   * The coordinates of the Geometry are changed.
+   *
+   * @param geom the Geometry to which to add the common coordinate bits
+   * @return the shifted Geometry
+   */
+  public void addCommonBits(Geometry geom)
+  {
+    Translater trans = new Translater(commonCoord);
+    geom.apply(trans);
+    geom.geometryChanged();
+  }
+
+  class CommonCoordinateFilter
+      implements CoordinateFilter
+  {
+    private CommonBits commonBitsX = new CommonBits();
+    private CommonBits commonBitsY = new CommonBits();
+
+    public void filter(Coordinate coord)
+    {
+      commonBitsX.add(coord.x);
+      commonBitsY.add(coord.y);
+    }
+
+    public Coordinate getCommonCoordinate()
+    {
+      return new Coordinate(
+          commonBitsX.getCommon(),
+          commonBitsY.getCommon());
+    }
+  }
+
+  class Translater
+      implements CoordinateFilter
+  {
+    Coordinate trans = null;
+
+    public Translater(Coordinate trans)
+    {
+      this.trans = trans;
+    }
+    public void filter(Coordinate coord)
+    {
+      coord.x += trans.x;
+      coord.y += trans.y;
+    }
+
+  }
+
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/precision/EnhancedPrecisionOp.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/precision/EnhancedPrecisionOp.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/precision/EnhancedPrecisionOp.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,226 @@
+
+/*
+ * 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.precision;
+
+import com.vividsolutions.jts.geom.*;
+import com.vividsolutions.jts.operation.overlay.OverlayOp;
+
+/**
+  * Provides versions of Geometry spatial functions which use
+  * enhanced precision techniques to reduce the likelihood of robustness problems.
+ *
+ * @version 1.6
+ */
+public class EnhancedPrecisionOp
+{
+  /**
+   * Computes the set-theoretic intersection of two {@link Geometry}s, using enhanced precision.
+   * @param geom0 the first Geometry
+   * @param geom1 the second Geometry
+   * @return the Geometry representing the set-theoretic intersection of the input Geometries.
+   */
+  public static Geometry intersection(Geometry geom0, Geometry geom1)
+  {
+    RuntimeException originalEx;
+    try {
+      Geometry result = geom0.intersection(geom1);
+      return result;
+    }
+    catch (RuntimeException ex)
+    {
+      originalEx = ex;
+    }
+    /*
+     * If we are here, the original op encountered a precision problem
+     * (or some other problem).  Retry the operation with
+     * enhanced precision to see if it succeeds
+     */
+    try {
+      CommonBitsOp cbo = new CommonBitsOp(true);
+      Geometry resultEP = cbo.intersection(geom0, geom1);
+      // check that result is a valid geometry after the reshift to orginal precision
+      if (! resultEP.isValid())
+        throw originalEx;
+      return resultEP;
+    }
+    catch (RuntimeException ex2)
+    {
+      throw originalEx;
+    }
+  }
+  /**
+   * Computes the set-theoretic union of two {@link Geometry}s, using enhanced precision.
+   * @param geom0 the first Geometry
+   * @param geom1 the second Geometry
+   * @return the Geometry representing the set-theoretic union of the input Geometries.
+   */
+  public static Geometry union(Geometry geom0, Geometry geom1)
+  {
+    RuntimeException originalEx;
+    try {
+      Geometry result = geom0.union(geom1);
+      return result;
+    }
+    catch (RuntimeException ex)
+    {
+      originalEx = ex;
+    }
+    /*
+     * If we are here, the original op encountered a precision problem
+     * (or some other problem).  Retry the operation with
+     * enhanced precision to see if it succeeds
+     */
+    try {
+      CommonBitsOp cbo = new CommonBitsOp(true);
+      Geometry resultEP = cbo.union(geom0, geom1);
+      // check that result is a valid geometry after the reshift to orginal precision
+      if (! resultEP.isValid())
+        throw originalEx;
+      return resultEP;
+    }
+    catch (RuntimeException ex2)
+    {
+      throw originalEx;
+    }
+  }
+  /**
+   * Computes the set-theoretic difference of two {@link Geometry}s, using enhanced precision.
+   * @param geom0 the first Geometry
+   * @param geom1 the second Geometry
+   * @return the Geometry representing the set-theoretic difference of the input Geometries.
+   */
+  public static Geometry difference(Geometry geom0, Geometry geom1)
+  {
+    RuntimeException originalEx;
+    try {
+      Geometry result = geom0.difference(geom1);
+      return result;
+    }
+    catch (RuntimeException ex)
+    {
+      originalEx = ex;
+    }
+    /*
+     * If we are here, the original op encountered a precision problem
+     * (or some other problem).  Retry the operation with
+     * enhanced precision to see if it succeeds
+     */
+    try {
+      CommonBitsOp cbo = new CommonBitsOp(true);
+      Geometry resultEP = cbo.difference(geom0, geom1);
+      // check that result is a valid geometry after the reshift to orginal precision
+      if (! resultEP.isValid())
+        throw originalEx;
+      return resultEP;
+    }
+    catch (RuntimeException ex2)
+    {
+      throw originalEx;
+    }
+  }
+  /**
+   * Computes the set-theoretic symmetric difference of two {@link Geometry}s, using enhanced precision.
+   * @param geom0 the first Geometry
+   * @param geom1 the second Geometry
+   * @return the Geometry representing the set-theoretic symmetric difference of the input Geometries.
+   */
+  public static Geometry symDifference(Geometry geom0, Geometry geom1)
+  {
+    RuntimeException originalEx;
+    try {
+      Geometry result = geom0.symDifference(geom1);
+      return result;
+    }
+    catch (RuntimeException ex)
+    {
+      originalEx = ex;
+    }
+    /*
+     * If we are here, the original op encountered a precision problem
+     * (or some other problem).  Retry the operation with
+     * enhanced precision to see if it succeeds
+     */
+    try {
+      CommonBitsOp cbo = new CommonBitsOp(true);
+      Geometry resultEP = cbo.symDifference(geom0, geom1);
+      // check that result is a valid geometry after the reshift to orginal precision
+      if (! resultEP.isValid())
+        throw originalEx;
+      return resultEP;
+    }
+    catch (RuntimeException ex2)
+    {
+      throw originalEx;
+    }
+  }
+  /**
+   * Computes the buffer of a {@link Geometry}, using enhanced precision.
+   * This method should no longer be necessary, since the buffer algorithm
+   * now is highly robust.
+   *
+   * @param geom0 the first Geometry
+   * @param distance the buffer distance
+   * @return the Geometry representing the buffer of the input Geometry.
+   */
+  public static Geometry buffer(Geometry geom, double distance)
+  {
+    RuntimeException originalEx;
+    try {
+      Geometry result = geom.buffer(distance);
+      return result;
+    }
+    catch (RuntimeException ex)
+    {
+      originalEx = ex;
+    }
+    /*
+     * If we are here, the original op encountered a precision problem
+     * (or some other problem).  Retry the operation with
+     * enhanced precision to see if it succeeds
+     */
+    try {
+      CommonBitsOp cbo = new CommonBitsOp(true);
+      Geometry resultEP = cbo.buffer(geom, distance);
+      // check that result is a valid geometry after the reshift to orginal precision
+      if (! resultEP.isValid())
+        throw originalEx;
+      return resultEP;
+    }
+    catch (RuntimeException ex2)
+    {
+      throw originalEx;
+    }
+  }
+
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/precision/SimpleGeometryPrecisionReducer.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/precision/SimpleGeometryPrecisionReducer.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/precision/SimpleGeometryPrecisionReducer.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,147 @@
+
+/*
+ * 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.precision;
+
+import com.vividsolutions.jts.geom.*;
+import com.vividsolutions.jts.geom.util.*;
+
+/**
+ * Reduces the precision of a {@link Geometry}
+ * according to the supplied {@link PrecisionModel}, without
+ * attempting to preserve valid topology.
+ * <p>
+ * The topology of the resulting geometry may be invalid if
+ * topological collapse occurs due to coordinates being shifted.
+ * It is up to the client to check this and handle it if necessary.
+ * Collapses may not matter for some uses.  An example
+ * is simplifying the input to the buffer algorithm.
+ * The buffer algorithm does not depend on the validity of the input geometry.
+ *
+ * @version 1.6
+ */
+public class SimpleGeometryPrecisionReducer
+{
+  private PrecisionModel newPrecisionModel;
+  private boolean removeCollapsed = true;
+  private boolean changePrecisionModel = false;
+
+  public SimpleGeometryPrecisionReducer(PrecisionModel pm)
+  {
+    newPrecisionModel = pm;
+  }
+
+  /**
+   * Sets whether the reduction will result in collapsed components
+   * being removed completely, or simply being collapsed to an (invalid)
+   * Geometry of the same type.
+   *
+   * @param removeCollapsed if <code>true</code> collapsed components will be removed
+   */
+  public void setRemoveCollapsedComponents(boolean removeCollapsed)
+  {
+    this.removeCollapsed = removeCollapsed;
+  }
+
+  /**
+   * Sets whether the {@link PrecisionModel} of the new reduced Geometry
+   * will be changed to be the {@link PrecisionModel} supplied to
+   * specify the reduction.  The default is to not change the precision model
+   *
+   * @param changePrecisionModel if <code>true</code> the precision model of the created Geometry will be the
+   * the precisionModel supplied in the constructor.
+   */
+  public void setChangePrecisionModel(boolean changePrecisionModel)
+  {
+    this.changePrecisionModel = changePrecisionModel;
+  }
+
+  public Geometry reduce(Geometry geom)
+  {
+    GeometryEditor geomEdit;
+    if (changePrecisionModel) {
+      GeometryFactory newFactory = new GeometryFactory(newPrecisionModel, geom.getFactory().getSRID());
+      geomEdit = new GeometryEditor(newFactory);
+    }
+    else
+      // don't change geometry factory
+      geomEdit = new GeometryEditor();
+
+    return geomEdit.edit(geom, new PrecisionReducerCoordinateOperation());
+  }
+
+  private class PrecisionReducerCoordinateOperation
+      extends GeometryEditor.CoordinateOperation
+  {
+    public Coordinate[] edit(Coordinate[] coordinates, Geometry geom)
+    {
+      if (coordinates.length == 0) return null;
+
+      Coordinate[] reducedCoords = new Coordinate[coordinates.length];
+      // copy coordinates and reduce
+      for (int i = 0; i < coordinates.length; i++) {
+        Coordinate coord = new Coordinate(coordinates[i]);
+        newPrecisionModel.makePrecise(coord);
+        reducedCoords[i] = coord;
+      }
+      // remove repeated points, to simplify returned geometry as much as possible
+      CoordinateList noRepeatedCoordList = new CoordinateList(reducedCoords, false);
+      Coordinate[] noRepeatedCoords = noRepeatedCoordList.toCoordinateArray();
+
+      /**
+       * Check to see if the removal of repeated points
+       * collapsed the coordinate List to an invalid length
+       * for the type of the parent geometry.
+       * It is not necessary to check for Point collapses, since the coordinate list can
+       * never collapse to less than one point.
+       * If the length is invalid, return the full-length coordinate array
+       * first computed, or null if collapses are being removed.
+       * (This may create an invalid geometry - the client must handle this.)
+       */
+      int minLength = 0;
+      if (geom instanceof LineString) minLength = 2;
+      if (geom instanceof LinearRing) minLength = 4;
+
+      Coordinate[] collapsedCoords = reducedCoords;
+      if (removeCollapsed) collapsedCoords = null;
+
+      // return null or orginal length coordinate array
+      if (noRepeatedCoords.length < minLength) {
+          return collapsedCoords;
+      }
+
+      // ok to return shorter coordinate array
+      return noRepeatedCoords;
+    }
+  }
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/precision/package.html
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/precision/package.html	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/precision/package.html	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,13 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+<!--
+
+-->
+</head>
+<body bgcolor="white">
+
+Provides classes for manipulating the precision model of Geometries
+
+</body>
+</html>

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/simplify/DouglasPeuckerLineSimplifier.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/simplify/DouglasPeuckerLineSimplifier.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/simplify/DouglasPeuckerLineSimplifier.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,84 @@
+package com.vividsolutions.jts.simplify;
+
+import java.util.*;
+import com.vividsolutions.jts.geom.*;
+
+/**
+ * Simplifies a linestring (sequence of points) using
+ * the standard Douglas-Peucker algorithm.
+ *
+ * @version 1.6
+ */
+public class DouglasPeuckerLineSimplifier
+{
+  public static Coordinate[] simplify(Coordinate[] pts, double distanceTolerance)
+  {
+    DouglasPeuckerLineSimplifier simp = new DouglasPeuckerLineSimplifier(pts);
+    simp.setDistanceTolerance(distanceTolerance);
+    return simp.simplify();
+  }
+
+  private Coordinate[] pts;
+  private boolean[] usePt;
+  private double distanceTolerance;
+
+  public DouglasPeuckerLineSimplifier(Coordinate[] pts)
+  {
+    this.pts = pts;
+  }
+  /**
+   * Sets the distance tolerance for the simplification.
+   * All vertices in the simplified linestring will be within this
+   * distance of the original linestring.
+   *
+   * @param distanceTolerance the approximation tolerance to use
+   */
+  public void setDistanceTolerance(double distanceTolerance) {
+    this.distanceTolerance = distanceTolerance;
+  }
+
+  public Coordinate[] simplify()
+  {
+    usePt = new boolean[pts.length];
+    for (int i = 0; i < pts.length; i++) {
+      usePt[i] = true;
+    }
+    simplifySection(0, pts.length - 1);
+    CoordinateList coordList = new CoordinateList();
+    for (int i = 0; i < pts.length; i++) {
+      if (usePt[i])
+        coordList.add(new Coordinate(pts[i]));
+    }
+    return coordList.toCoordinateArray();
+  }
+
+  private LineSegment seg = new LineSegment();
+
+  private void simplifySection(int i, int j)
+  {
+    if((i+1) == j) {
+      return;
+    }
+    seg.p0 = pts[i];
+    seg.p1 = pts[j];
+    double maxDistance = -1.0;
+    int maxIndex = i;
+    for (int k = i + 1; k < j; k++) {
+      double distance = seg.distance(pts[k]);
+      if (distance > maxDistance) {
+        maxDistance = distance;
+        maxIndex = k;
+      }
+    }
+    if (maxDistance <= distanceTolerance) {
+      for(int k = i + 1; k < j; k++) {
+        usePt[k] = false;
+      }
+    }
+    else {
+      simplifySection(i, maxIndex);
+      simplifySection(maxIndex, j);
+    }
+  }
+
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/simplify/DouglasPeuckerSimplifier.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/simplify/DouglasPeuckerSimplifier.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/simplify/DouglasPeuckerSimplifier.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,100 @@
+package com.vividsolutions.jts.simplify;
+
+import java.util.*;
+import com.vividsolutions.jts.geom.*;
+import com.vividsolutions.jts.geom.util.*;
+
+/**
+ * Simplifies a {@link Geometry} using the standard Douglas-Peucker algorithm.
+ * Ensures that any polygonal geometries returned are valid.
+ * Simple lines are not guaranteed to remain simple after simplification.
+ * <p>
+ * Note that in general D-P does not preserve topology -
+ * e.g. polygons can be split, collapse to lines or disappear
+ * holes can be created or disappear,
+ * and lines can cross.
+ * To simplify geometry while preserving topology use {@link TopologyPreservingSimplifier}.
+ * (However, using D-P is significantly faster).
+ *
+ * @version 1.6
+ */
+public class DouglasPeuckerSimplifier
+{
+
+  public static Geometry simplify(Geometry geom, double distanceTolerance)
+  {
+    DouglasPeuckerSimplifier tss = new DouglasPeuckerSimplifier(geom);
+    tss.setDistanceTolerance(distanceTolerance);
+    return tss.getResultGeometry();
+  }
+
+  private Geometry inputGeom;
+  private double distanceTolerance;
+
+  public DouglasPeuckerSimplifier(Geometry inputGeom)
+  {
+    this.inputGeom = inputGeom;
+  }
+
+  /**
+   * Sets the distance tolerance for the simplification.
+   * All vertices in the simplified geometry will be within this
+   * distance of the original geometry.
+   *
+   * @param distanceTolerance the approximation tolerance to use
+   */
+  public void setDistanceTolerance(double distanceTolerance) {
+    this.distanceTolerance = distanceTolerance;
+  }
+
+  public Geometry getResultGeometry()
+  {
+    return (new DPTransformer()).transform(inputGeom);
+  }
+
+class DPTransformer
+    extends GeometryTransformer
+{
+  protected CoordinateSequence transformCoordinates(CoordinateSequence coords, Geometry parent)
+  {
+    Coordinate[] inputPts = coords.toCoordinateArray();
+    Coordinate[] newPts = DouglasPeuckerLineSimplifier.simplify(inputPts, distanceTolerance);
+    return factory.getCoordinateSequenceFactory().create(newPts);
+  }
+
+  protected Geometry transformPolygon(Polygon geom, Geometry parent) {
+    Geometry roughGeom = super.transformPolygon(geom, parent);
+    // don't try and correct if the parent is going to do this
+    if (parent instanceof MultiPolygon) {
+      return roughGeom;
+    }
+    return createValidArea(roughGeom);
+  }
+
+  protected Geometry transformMultiPolygon(MultiPolygon geom, Geometry parent) {
+    Geometry roughGeom = super.transformMultiPolygon(geom, parent);
+    return createValidArea(roughGeom);
+  }
+
+  /**
+   * Creates a valid area geometry from one that possibly has
+   * bad topology (i.e. self-intersections).
+   * Since buffer can handle invalid topology, but always returns
+   * valid geometry, constructing a 0-width buffer "corrects" the
+   * topology.
+   * Note this only works for area geometries, since buffer always returns
+   * areas.  This also may return empty geometries, if the input
+   * has no actual area.
+   *
+   * @param roughAreaGeom an area geometry possibly containing self-intersections
+   * @return a valid area geometry
+   */
+  private Geometry createValidArea(Geometry roughAreaGeom)
+  {
+    return roughAreaGeom.buffer(0.0);
+  }
+}
+
+}
+
+

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/simplify/LineSegmentIndex.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/simplify/LineSegmentIndex.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/simplify/LineSegmentIndex.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,77 @@
+package com.vividsolutions.jts.simplify;
+
+import java.util.*;
+import com.vividsolutions.jts.geom.*;
+import com.vividsolutions.jts.index.*;
+import com.vividsolutions.jts.index.quadtree.Quadtree;
+
+/**
+ * An index of {@link LineSegments}.
+ */
+public class LineSegmentIndex
+{
+  private Quadtree index = new Quadtree();
+
+  public LineSegmentIndex()
+  {
+  }
+
+  public void add(TaggedLineString line) {
+    TaggedLineSegment[] segs = line.getSegments();
+    for (int i = 0; i < segs.length - 1; i++) {
+      TaggedLineSegment seg = segs[i];
+      add(seg);
+    }
+  }
+
+  public void add(LineSegment seg)
+  {
+    index.insert(new Envelope(seg.p0, seg.p1), seg);
+  }
+
+  public void remove(LineSegment seg)
+  {
+    index.remove(new Envelope(seg.p0, seg.p1), seg);
+  }
+
+  public List query(LineSegment querySeg)
+  {
+    Envelope env = new Envelope(querySeg.p0, querySeg.p1);
+
+    LineSegmentVisitor visitor = new LineSegmentVisitor(querySeg);
+    index.query(env, visitor);
+    List itemsFound = visitor.getItems();
+
+//    List listQueryItems = index.query(env);
+//    System.out.println("visitor size = " + itemsFound.size()
+//                       + "  query size = " + listQueryItems.size());
+//    List itemsFound = index.query(env);
+
+    return itemsFound;
+  }
+}
+
+/**
+ * ItemVisitor subclass to reduce volume of query results.
+ */
+class LineSegmentVisitor
+    implements ItemVisitor
+{
+// MD - only seems to make about a 10% difference in overall time.
+
+  private LineSegment querySeg;
+  private ArrayList items = new ArrayList();
+
+  public LineSegmentVisitor(LineSegment querySeg) {
+    this.querySeg = querySeg;
+  }
+
+  public void visitItem(Object item)
+  {
+    LineSegment seg = (LineSegment) item;
+    if (Envelope.intersects(seg.p0, seg.p1, querySeg.p0, querySeg.p1))
+      items.add(item);
+  }
+
+  public ArrayList getItems() { return items; }
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/simplify/TaggedLineSegment.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/simplify/TaggedLineSegment.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/simplify/TaggedLineSegment.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,28 @@
+package com.vividsolutions.jts.simplify;
+
+import com.vividsolutions.jts.geom.*;
+
+/**
+ * A {@link LineSegment} which is tagged with its location in a {@link Geometry}.
+ * Used to index the segments in a geometry and recover the segment locations
+ * from the index.
+ */
+public class TaggedLineSegment
+    extends LineSegment
+{
+  private Geometry parent;
+  private int index;
+
+  public TaggedLineSegment(Coordinate p0, Coordinate p1, Geometry parent, int index) {
+    super(p0, p1);
+    this.parent = parent;
+    this.index = index;
+  }
+
+  public TaggedLineSegment(Coordinate p0, Coordinate p1) {
+    this(p0, p1, null, -1);
+  }
+
+  public Geometry getParent() { return parent; }
+  public int getIndex() { return index; }
+}
\ No newline at end of file

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/simplify/TaggedLineString.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/simplify/TaggedLineString.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/simplify/TaggedLineString.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,80 @@
+package com.vividsolutions.jts.simplify;
+
+import java.util.*;
+import com.vividsolutions.jts.geom.*;
+
+/**
+ * @version 1.6
+ */
+public class TaggedLineString {
+
+  private LineString parentLine;
+  private TaggedLineSegment[] segs;
+  private List resultSegs = new ArrayList();
+  private int minimumSize;
+
+  public TaggedLineString(LineString parentLine) {
+    this(parentLine, 2);
+  }
+
+  public TaggedLineString(LineString parentLine, int minimumSize) {
+    this.parentLine = parentLine;
+    this.minimumSize = minimumSize;
+    init();
+  }
+
+  public int getMinimumSize()  {    return minimumSize;  }
+  public LineString getParent() { return parentLine; }
+  public Coordinate[] getParentCoordinates() { return parentLine.getCoordinates(); }
+  public Coordinate[] getResultCoordinates() { return extractCoordinates(resultSegs); }
+
+  public int getResultSize()
+  {
+    int resultSegsSize = resultSegs.size();
+    return resultSegsSize == 0 ? 0 : resultSegsSize + 1;
+  }
+
+  public TaggedLineSegment getSegment(int i) { return segs[i]; }
+
+  private void init()
+  {
+    Coordinate[] pts = parentLine.getCoordinates();
+    segs = new TaggedLineSegment[pts.length - 1];
+    for (int i = 0; i < pts.length - 1; i++) {
+      TaggedLineSegment seg
+               = new TaggedLineSegment(pts[i], pts[i + 1], parentLine, i);
+      segs[i] = seg;
+    }
+  }
+
+  public TaggedLineSegment[] getSegments() { return segs; }
+
+  public void addToResult(LineSegment seg)
+  {
+    resultSegs.add(seg);
+  }
+
+  public LineString asLineString()
+  {
+    return parentLine.getFactory().createLineString(extractCoordinates(resultSegs));
+  }
+
+  public LinearRing asLinearRing() {
+    return parentLine.getFactory().createLinearRing(extractCoordinates(resultSegs));
+  }
+
+  private static Coordinate[] extractCoordinates(List segs)
+  {
+    Coordinate[] pts = new Coordinate[segs.size() + 1];
+    LineSegment seg = null;
+    for (int i = 0; i < segs.size(); i++) {
+      seg = (LineSegment) segs.get(i);
+      pts[i] = seg.p0;
+    }
+    // add last point
+    pts[pts.length - 1] = seg.p1;
+    return pts;
+  }
+
+
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/simplify/TaggedLineStringSimplifier.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/simplify/TaggedLineStringSimplifier.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/simplify/TaggedLineStringSimplifier.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,212 @@
+package com.vividsolutions.jts.simplify;
+
+import java.util.*;
+import com.vividsolutions.jts.algorithm.*;
+import com.vividsolutions.jts.geom.*;
+import com.vividsolutions.jts.util.Debug;
+
+/**
+ * Simplifies a TaggedLineString, preserving topology
+ * (in the sense that no new intersections are introduced).
+ * Uses the recursive Douglas-Peucker algorithm.
+ *
+ * @version 1.6
+ */
+public class TaggedLineStringSimplifier
+{
+  private static LineIntersector li = new RobustLineIntersector();
+  private LineSegmentIndex inputIndex = new LineSegmentIndex();
+  private LineSegmentIndex outputIndex = new LineSegmentIndex();
+  private TaggedLineString line;
+  private Coordinate[] linePts;
+  private double distanceTolerance = 0.0;
+
+  public TaggedLineStringSimplifier(LineSegmentIndex inputIndex,
+                                     LineSegmentIndex outputIndex)
+  {
+    this.line = line;
+    this.inputIndex = inputIndex;
+    this.outputIndex = outputIndex;
+  }
+
+  /**
+   * Sets the distance tolerance for the simplification.
+   * All vertices in the simplified geometry will be within this
+   * distance of the original geometry.
+   *
+   * @param distanceTolerance the approximation tolerance to use
+   */
+  public void setDistanceTolerance(double distanceTolerance) {
+    this.distanceTolerance = distanceTolerance;
+  }
+
+  public void simplify(TaggedLineString line)
+  {
+    this.line = line;
+    linePts = line.getParentCoordinates();
+    simplifySection(0, linePts.length - 1, 0);
+  }
+
+  private void simplifySection(int i, int j, int depth)
+  {
+    depth += 1;
+    int[] sectionIndex = new int[2];
+    if((i+1) == j) {
+      LineSegment newSeg = line.getSegment(i);
+      line.addToResult(newSeg);
+      // leave this segment in the input index, for efficiency
+      return;
+    }
+
+    boolean isValidToSimplify = true;
+
+    /**
+     * Following logic ensures that there is enough points in the output line.
+     * If there is already more points than the minimum, there's nothing to check.
+     * Otherwise, if in the worst case there wouldn't be enough points,
+     * don't flatten this segment (which avoids the worst case scenario)
+     */
+    if (line.getResultSize() < line.getMinimumSize()) {
+      int worstCaseSize = depth + 1;
+      if (worstCaseSize < line.getMinimumSize())
+        isValidToSimplify = false;
+    }
+
+    double[] distance = new double[1];
+    int furthestPtIndex = findFurthestPoint(linePts, i, j, distance);
+    // flattening must be less than distanceTolerance
+    if (distance[0] > distanceTolerance) isValidToSimplify = false;
+    // test if flattened section would cause intersection
+    LineSegment candidateSeg = new LineSegment();
+    candidateSeg.p0 = linePts[i];
+    candidateSeg.p1 = linePts[j];
+    sectionIndex[0] = i;
+    sectionIndex[1] = j;
+    if (hasBadIntersection(line, sectionIndex, candidateSeg)) isValidToSimplify = false;
+
+    if (isValidToSimplify) {
+      LineSegment newSeg = flatten(i, j);
+      line.addToResult(newSeg);
+      return;
+    }
+    simplifySection(i, furthestPtIndex, depth);
+    simplifySection(furthestPtIndex, j, depth);
+  }
+
+  private int findFurthestPoint(Coordinate[] pts, int i, int j, double[] maxDistance)
+  {
+    LineSegment seg = new LineSegment();
+    seg.p0 = pts[i];
+    seg.p1 = pts[j];
+    double maxDist = -1.0;
+    int maxIndex = i;
+    for (int k = i + 1; k < j; k++) {
+      Coordinate midPt = pts[k];
+      double distance = seg.distance(midPt);
+      if (distance > maxDist) {
+        maxDist = distance;
+        maxIndex = k;
+      }
+    }
+    maxDistance[0] = maxDist;
+    return maxIndex;
+  }
+
+  private LineSegment flatten(int start, int end)
+  {
+    // make a new segment for the simplified geometry
+    Coordinate p0 = linePts[start];
+    Coordinate p1 = linePts[end];
+    LineSegment newSeg = new LineSegment(p0, p1);
+// update the indexes
+    remove(line, start, end);
+    outputIndex.add(newSeg);
+    return newSeg;
+  }
+
+  /**
+   * Index of section to be tested for flattening - reusable
+   */
+  private int[] validSectionIndex = new int[2];
+
+
+  private boolean hasBadIntersection(TaggedLineString parentLine,
+                       int[] sectionIndex,
+                       LineSegment candidateSeg)
+  {
+    if (hasBadOutputIntersection(candidateSeg)) return true;
+    if (hasBadInputIntersection(parentLine, sectionIndex, candidateSeg)) return true;
+    return false;
+  }
+
+  private boolean hasBadOutputIntersection(LineSegment candidateSeg)
+  {
+    List querySegs = outputIndex.query(candidateSeg);
+    for (Iterator i = querySegs.iterator(); i.hasNext(); ) {
+      LineSegment querySeg = (LineSegment) i.next();
+      if (hasInteriorIntersection(querySeg, candidateSeg)) {
+          return true;
+      }
+    }
+    return false;
+  }
+
+  private boolean hasBadInputIntersection(TaggedLineString parentLine,
+                       int[] sectionIndex,
+                       LineSegment candidateSeg)
+  {
+    List querySegs = inputIndex.query(candidateSeg);
+    for (Iterator i = querySegs.iterator(); i.hasNext(); ) {
+      TaggedLineSegment querySeg = (TaggedLineSegment) i.next();
+      if (hasInteriorIntersection(querySeg, candidateSeg)) {
+          if (isInLineSection(parentLine, sectionIndex, querySeg))
+            continue;
+          return true;
+      }
+    }
+    return false;
+  }
+
+  /**
+   * Tests whether a segment is in a section of a TaggedLineString
+   * @param line
+   * @param sectionIndex
+   * @param seg
+   * @return
+   */
+  private static boolean isInLineSection(
+      TaggedLineString line,
+      int[] sectionIndex,
+      TaggedLineSegment seg)
+  {
+    // not in this line
+    if (seg.getParent() != line.getParent())
+      return false;
+    int segIndex = seg.getIndex();
+    if (segIndex >= sectionIndex[0] && segIndex < sectionIndex[1])
+      return true;
+    return false;
+  }
+
+  private boolean hasInteriorIntersection(LineSegment seg0, LineSegment seg1)
+  {
+    li.computeIntersection(seg0.p0, seg0.p1, seg1.p0, seg1.p1);
+    return li.isInteriorIntersection();
+  }
+
+  /**
+   * Remove the segs in the section of the line
+   * @param line
+   * @param pts
+   * @param sectionStartIndex
+   * @param sectionEndIndex
+   */
+  private void remove(TaggedLineString line,
+                       int start, int end)
+  {
+    for (int i = start; i < end; i++) {
+      TaggedLineSegment seg = line.getSegment(i);
+      inputIndex.remove(seg);
+    }
+  }
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/simplify/TaggedLinesSimplifier.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/simplify/TaggedLinesSimplifier.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/simplify/TaggedLinesSimplifier.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,49 @@
+package com.vividsolutions.jts.simplify;
+
+import java.util.*;
+import com.vividsolutions.jts.algorithm.*;
+
+/**
+ * Simplifies a collection of TaggedLineStrings, preserving topology
+ * (in the sense that no new intersections are introduced).
+ */
+public class TaggedLinesSimplifier
+{
+  private LineSegmentIndex inputIndex = new LineSegmentIndex();
+  private LineSegmentIndex outputIndex = new LineSegmentIndex();
+  private double distanceTolerance = 0.0;
+
+  public TaggedLinesSimplifier()
+  {
+
+  }
+
+  /**
+   * Sets the distance tolerance for the simplification.
+   * All vertices in the simplified geometry will be within this
+   * distance of the original geometry.
+   *
+   * @param distanceTolerance the approximation tolerance to use
+   */
+  public void setDistanceTolerance(double distanceTolerance) {
+    this.distanceTolerance = distanceTolerance;
+  }
+
+  /**
+   * Simplify a collection of {@link TaggedLineString}s
+   *
+   * @param taggedLines the collection of lines to simplify
+   */
+  public void simplify(Collection taggedLines) {
+    for (Iterator i = taggedLines.iterator(); i.hasNext(); ) {
+      inputIndex.add((TaggedLineString) i.next());
+    }
+    for (Iterator i = taggedLines.iterator(); i.hasNext(); ) {
+      TaggedLineStringSimplifier tlss
+                    = new TaggedLineStringSimplifier(inputIndex, outputIndex);
+      tlss.setDistanceTolerance(distanceTolerance);
+      tlss.simplify((TaggedLineString) i.next());
+    }
+  }
+
+}
\ No newline at end of file

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/simplify/TopologyPreservingSimplifier.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/simplify/TopologyPreservingSimplifier.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/simplify/TopologyPreservingSimplifier.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,95 @@
+package com.vividsolutions.jts.simplify;
+
+import java.util.*;
+import com.vividsolutions.jts.geom.*;
+import com.vividsolutions.jts.geom.util.*;
+import com.vividsolutions.jts.util.Debug;
+
+/**
+ * Simplifies a geometry, ensuring that
+ * the result is a valid geometry having the
+ * same dimension and number of components as the input.
+ * The simplification uses a maximum distance difference algorithm
+ * similar to the one used in the Douglas-Peucker algorithm.
+ * <p>
+ * In particular, if the input is an areal geometry
+ * ( {@link Polygon} or {@link MultiPolygon} )
+ * <ul>
+ * <li>The result has the same number of shells and holes (rings) as the input,
+ * in the same order
+ * <li>The result rings touch at <b>no more</b> than the number of touching point in the input
+ * (although they may touch at fewer points)
+ * </ul>
+ *
+ */
+public class TopologyPreservingSimplifier
+{
+  public static Geometry simplify(Geometry geom, double distanceTolerance)
+  {
+    TopologyPreservingSimplifier tss = new TopologyPreservingSimplifier(geom);
+    tss.setDistanceTolerance(distanceTolerance);
+    return tss.getResultGeometry();
+  }
+
+  private Geometry inputGeom;
+  private TaggedLinesSimplifier lineSimplifier = new TaggedLinesSimplifier();
+  private Map linestringMap;
+
+  public TopologyPreservingSimplifier(Geometry inputGeom)
+  {
+    this.inputGeom = inputGeom;
+    //TESTING: slightly faster, but probably not enough to bother with
+    //inputIndex = new LineSegmentIndex(new STRtree());
+  }
+
+  /**
+   * Sets the distance tolerance for the simplification.
+   * All vertices in the simplified geometry will be within this
+   * distance of the original geometry.
+   *
+   * @param distanceTolerance the approximation tolerance to use
+   */
+  public void setDistanceTolerance(double distanceTolerance) {
+    lineSimplifier.setDistanceTolerance(distanceTolerance);
+  }
+
+  public Geometry getResultGeometry() {
+    linestringMap = new HashMap();
+    inputGeom.apply(new LineStringMapBuilderFilter());
+    lineSimplifier.simplify(linestringMap.values());
+    Geometry result = (new LineStringTransformer()).transform(inputGeom);
+    return result;
+  }
+
+  class LineStringTransformer
+      extends GeometryTransformer
+  {
+    protected CoordinateSequence transformCoordinates(CoordinateSequence coords, Geometry parent)
+    {
+      if (parent instanceof LineString) {
+        TaggedLineString taggedLine = (TaggedLineString) linestringMap.get(parent);
+        return createCoordinateSequence(taggedLine.getResultCoordinates());
+      }
+      // for anything else (e.g. points) just copy the coordinates
+      return super.transformCoordinates(coords, parent);
+    }
+  }
+
+  class LineStringMapBuilderFilter
+      implements GeometryComponentFilter
+  {
+    public void filter(Geometry geom)
+    {
+      if (geom instanceof LinearRing) {
+        TaggedLineString taggedLine = new TaggedLineString((LineString) geom, 4);
+        linestringMap.put(geom, taggedLine);
+      }
+      else if (geom instanceof LineString) {
+        TaggedLineString taggedLine = new TaggedLineString((LineString) geom, 2);
+        linestringMap.put(geom, taggedLine);
+      }
+    }
+  }
+
+}
+

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/util/Assert.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/util/Assert.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/util/Assert.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,126 @@
+
+
+/*
+ * 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.util;
+
+import com.vividsolutions.jts.util.AssertionFailedException;
+
+/**
+ *  A utility for making programming assertions.
+ *
+ *@version 1.6
+ */
+public class Assert {
+
+  /**
+   *  Throws an <code>AssertionFailedException</code> if the given assertion is
+   *  not true.
+   *
+   *@param  assertion                  a condition that is supposed to be true
+   *@throws  AssertionFailedException  if the condition is false
+   */
+  public static void isTrue(boolean assertion) {
+    isTrue(assertion, null);
+  }
+
+  /**
+   *  Throws an <code>AssertionFailedException</code> with the given message if
+   *  the given assertion is not true.
+   *
+   *@param  assertion                  a condition that is supposed to be true
+   *@param  message                    a description of the assertion
+   *@throws  AssertionFailedException  if the condition is false
+   */
+  public static void isTrue(boolean assertion, String message) {
+    if (!assertion) {
+      if (message == null) {
+        throw new AssertionFailedException();
+      }
+      else {
+        throw new AssertionFailedException(message);
+      }
+    }
+  }
+
+  /**
+   *  Throws an <code>AssertionFailedException</code> if the given objects are
+   *  not equal, according to the <code>equals</code> method.
+   *
+   *@param  expectedValue              the correct value
+   *@param  actualValue                the value being checked
+   *@throws  AssertionFailedException  if the two objects are not equal
+   */
+  public static void equals(Object expectedValue, Object actualValue) {
+    equals(expectedValue, actualValue, null);
+  }
+
+  /**
+   *  Throws an <code>AssertionFailedException</code> with the given message if
+   *  the given objects are not equal, according to the <code>equals</code>
+   *  method.
+   *
+   *@param  expectedValue              the correct value
+   *@param  actualValue                the value being checked
+   *@param  message                    a description of the assertion
+   *@throws  AssertionFailedException  if the two objects are not equal
+   */
+  public static void equals(Object expectedValue, Object actualValue, String message) {
+    if (!actualValue.equals(expectedValue)) {
+      throw new AssertionFailedException("Expected " + expectedValue + " but encountered "
+           + actualValue + (message != null ? ": " + message : ""));
+    }
+  }
+
+  /**
+   *  Always throws an <code>AssertionFailedException</code>.
+   *
+   *@throws  AssertionFailedException  thrown always
+   */
+  public static void shouldNeverReachHere() {
+    shouldNeverReachHere(null);
+  }
+
+  /**
+   *  Always throws an <code>AssertionFailedException</code> with the given
+   *  message.
+   *
+   *@param  message                    a description of the assertion
+   *@throws  AssertionFailedException  thrown always
+   */
+  public static void shouldNeverReachHere(String message) {
+    throw new AssertionFailedException("Should never reach here"
+         + (message != null ? ": " + message : ""));
+  }
+}
+

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/util/AssertionFailedException.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/util/AssertionFailedException.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/util/AssertionFailedException.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,63 @@
+
+
+/*
+ * 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.util;
+
+/**
+ *  Thrown when the application is in an inconsistent state. Indicates a problem
+ *  with the code.
+ *
+ *@version 1.6
+ */
+public class AssertionFailedException extends RuntimeException {
+
+  /**
+   *  Creates an <code>AssertionFailedException</code>.
+   */
+  public AssertionFailedException() {
+    super();
+  }
+
+  /**
+   *  Creates a <code>AssertionFailedException</code> with the given detail
+   *  message.
+   *
+   *@param  message  a description of the assertion
+   */
+  public AssertionFailedException(String message) {
+    super(message);
+  }
+}
+
+

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/util/CoordinateArrayFilter.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/util/CoordinateArrayFilter.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/util/CoordinateArrayFilter.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,72 @@
+
+
+/*
+ * 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.util;
+
+import com.vividsolutions.jts.geom.*;
+
+/**
+ *  A {@link CoordinateFilter} that creates an array containing every
+ *  coordinate in a {@link Geometry}.
+ *
+ *@version 1.6
+ */
+public class CoordinateArrayFilter implements CoordinateFilter {
+  Coordinate[] pts = null;
+  int n = 0;
+
+  /**
+   *  Constructs a <code>CoordinateArrayFilter</code>.
+   *
+   *@param  size  the number of points that the <code>CoordinateArrayFilter</code>
+   *      will collect
+   */
+  public CoordinateArrayFilter(int size) {
+    pts = new Coordinate[size];
+  }
+
+  /**
+   *  Returns the gathered <code>Coordinate</code>s.
+   *
+   *@return    the <code>Coordinate</code>s collected by this <code>CoordinateArrayFilter</code>
+   */
+  public Coordinate[] getCoordinates() {
+    return pts;
+  }
+
+  public void filter(Coordinate coord) {
+    pts[n++] = coord;
+  }
+}
+

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/util/CoordinateCountFilter.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/util/CoordinateCountFilter.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/util/CoordinateCountFilter.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,63 @@
+
+
+/*
+ * 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.util;
+
+import com.vividsolutions.jts.geom.*;
+
+/**
+ *  A {@link CoordinateFilter} that counts the total number of coordinates
+ *  in a <code>Geometry</code>.
+ *
+ *@version 1.6
+ */
+public class CoordinateCountFilter implements CoordinateFilter {
+  private int n = 0;
+
+  public CoordinateCountFilter() { }
+
+  /**
+   *  Returns the result of the filtering.
+   *
+   *@return    the number of points found by this <code>CoordinateCountFilter</code>
+   */
+  public int getCount() {
+    return n;
+  }
+
+  public void filter(Coordinate coord) {
+    n++;
+  }
+}
+

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/util/Debug.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/util/Debug.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/util/Debug.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,203 @@
+
+
+/*
+ * 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.util;
+
+/**
+ *@version 1.6
+ */
+import java.io.*;
+import java.util.*;
+import java.lang.reflect.*;
+
+/**
+ * Provides routines to simplify and localize debugging output.
+ *
+ * @version 1.6
+ */
+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";
+
+  private static boolean debugOn = false;
+
+  static {
+    String debugValue = System.getProperty(DEBUG_PROPERTY_NAME);
+    if (debugValue != null) {
+      if (debugValue.equalsIgnoreCase(DEBUG_PROPERTY_VALUE_ON)
+          || debugValue.equalsIgnoreCase(DEBUG_PROPERTY_VALUE_TRUE) )
+        debugOn = true;
+    }
+  }
+
+
+  public static void main(String[] args)
+  {
+    Debug.println("Debugging is ON");
+  }
+
+  private static Debug debug = new Debug();
+
+  private static final String DEBUG_LINE_TAG = "D! ";
+
+  private PrintStream out;
+  private Class[] printArgs;
+  private Object watchObj = null;
+  private Object[] args = new Object[1];
+
+  public static boolean isDebugging() { return debugOn; }
+
+  public static void print(String str) {
+    if (!debugOn) {
+      return;
+    }
+    debug.instancePrint(str);
+  }
+/*
+  public static void println(String str) {
+    if (! debugOn) return;
+    debug.instancePrint(str);
+    debug.println();
+  }
+*/
+  public static void print(Object obj) {
+    if (! debugOn) return;
+    debug.instancePrint(obj);
+  }
+
+  public static void print(boolean isTrue, Object obj) {
+    if (! debugOn) return;
+    if (! isTrue) return;
+    debug.instancePrint(obj);
+  }
+
+  public static void println(Object obj) {
+    if (!debugOn) {
+      return;
+    }
+    debug.instancePrint(obj);
+    debug.println();
+  }
+
+  public static void addWatch(Object obj) {
+    debug.instanceAddWatch(obj);
+  }
+
+  public static void printWatch() {
+    debug.instancePrintWatch();
+  }
+
+  public static void printIfWatch(Object obj) {
+    debug.instancePrintIfWatch(obj);
+  }
+
+  private Debug() {
+    out = System.out;
+    printArgs = new Class[1];
+    try {
+      printArgs[0] = Class.forName("java.io.PrintStream");
+    }
+    catch (Exception ex) {
+      // ignore this exception - it will fail later anyway
+    }
+  }
+
+
+  public void instancePrintWatch() {
+    if (watchObj == null) return;
+    instancePrint(watchObj);
+  }
+
+  public void instancePrintIfWatch(Object obj) {
+    if (obj != watchObj) return;
+    if (watchObj == null) return;
+    instancePrint(watchObj);
+  }
+
+  public void instancePrint(Object obj)
+  {
+    if (obj instanceof Collection) {
+      instancePrint(((Collection) obj).iterator());
+    }
+    else if (obj instanceof Iterator) {
+      instancePrint((Iterator) obj);
+    }
+    else {
+      instancePrintObject(obj);
+    }
+  }
+
+  public void instancePrint(Iterator it)
+  {
+    for (; it.hasNext(); ) {
+      Object obj = it.next();
+      instancePrintObject(obj);
+    }
+  }
+  public void instancePrintObject(Object obj) {
+    //if (true) throw new RuntimeException("DEBUG TRAP!");
+    Method printMethod = null;
+    try {
+      Class cls = obj.getClass();
+      try {
+        printMethod = cls.getMethod("print", printArgs);
+        args[0] = out;
+        out.print(DEBUG_LINE_TAG);
+        printMethod.invoke(obj, args);
+      }
+      catch (NoSuchMethodException ex) {
+        instancePrint(obj.toString());
+      }
+    }
+    catch (Exception ex) {
+      ex.printStackTrace(out);
+    }
+  }
+
+  public void println() {
+    out.println();
+  }
+
+  private void instanceAddWatch(Object obj) {
+    watchObj = obj;
+  }
+
+  private void instancePrint(String str) {
+    out.print(DEBUG_LINE_TAG);
+    out.print(str);
+  }
+
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/util/GeometricShapeFactory.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/util/GeometricShapeFactory.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/util/GeometricShapeFactory.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,259 @@
+
+/*
+ * 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.util;
+
+/**
+ * Methods to create various geometry shapes
+ */
+import com.vividsolutions.jts.geom.*;
+
+/**
+ * Computes various kinds of common geometric shapes.
+ * Allows various ways of specifying the location and extent of the shapes,
+ * as well as number of line segments used to form them.
+ *
+ * @version 1.6
+ */
+public class GeometricShapeFactory
+{
+  private GeometryFactory geomFact;
+  private Dimensions dim = new Dimensions();
+  private int nPts = 100;
+
+  /**
+   * Create a shape factory which will create shapes using the default
+   * {@link GeometryFactory}.
+   */
+  public GeometricShapeFactory()
+  {
+    this(new GeometryFactory());
+  }
+
+  /**
+   * Create a shape factory which will create shapes using the given
+   * {@link GeometryFactory}.
+   *
+   * @param geomFact the factory to use
+   */
+  public GeometricShapeFactory(GeometryFactory geomFact)
+  {
+    this.geomFact = geomFact;
+  }
+
+  /**
+   * Sets the location of the shape by specifying the base coordinate
+   * (which in most cases is the
+   * lower left point of the envelope containing the shape).
+   *
+   * @param base the base coordinate of the shape
+   */
+  public void setBase(Coordinate base)  {  dim.setBase(base);    }
+  /**
+   * Sets the location of the shape by specifying the centre of
+   * the shape's bounding box
+   *
+   * @param centre the centre coordinate of the shape
+   */
+  public void setCentre(Coordinate centre)  {  dim.setCentre(centre);    }
+
+  /**
+   * Sets the total number of points in the created Geometry
+   */
+  public void setNumPoints(int nPts) { this.nPts = nPts; }
+
+  /**
+   * Sets the size of the extent of the shape in both x and y directions.
+   *
+   * @param size the size of the shape's extent
+   */
+  public void setSize(double size) { dim.setSize(size); }
+
+  /**
+   * Sets the width of the shape.
+   *
+   * @param width the width of the shape
+   */
+  public void setWidth(double width) { dim.setWidth(width); }
+
+  /**
+   * Sets the height of the shape.
+   *
+   * @param height the height of the shape
+   */
+  public void setHeight(double height) { dim.setHeight(height); }
+
+  /**
+   * Creates a rectangular {@link Polygon}.
+   *
+   * @return a rectangular Polygon
+   *
+   */
+  public Polygon createRectangle()
+  {
+    int i;
+    int ipt = 0;
+    int nSide = nPts / 4;
+    if (nSide < 1) nSide = 1;
+    double XsegLen = dim.getEnvelope().getWidth() / nSide;
+    double YsegLen = dim.getEnvelope().getHeight() / nSide;
+
+    Coordinate[] pts = new Coordinate[4 * nSide + 1];
+    Envelope env = dim.getEnvelope();
+
+    double maxx = env.getMinX() + nSide * XsegLen;
+    double maxy = env.getMinY() + nSide * XsegLen;
+
+    for (i = 0; i < nSide; i++) {
+      double x = env.getMinX() + i * XsegLen;
+      double y = env.getMinY();
+      pts[ipt++] = new Coordinate(x, y);
+    }
+    for (i = 0; i < nSide; i++) {
+      double x = env.getMaxX();
+      double y = env.getMinY() + i * YsegLen;
+      pts[ipt++] = new Coordinate(x, y);
+    }
+    for (i = 0; i < nSide; i++) {
+      double x = env.getMaxX() - i * XsegLen;
+      double y = env.getMaxY();
+      pts[ipt++] = new Coordinate(x, y);
+    }
+    for (i = 0; i < nSide; i++) {
+      double x = env.getMinX();
+      double y = env.getMaxY() - i * YsegLen;
+      pts[ipt++] = new Coordinate(x, y);
+    }
+    pts[ipt++] = new Coordinate(pts[0]);
+
+    LinearRing ring = geomFact.createLinearRing(pts);
+    Polygon poly = geomFact.createPolygon(ring, null);
+    return poly;
+  }
+
+  /**
+   * Creates a circular {@link Polygon}.
+   *
+   * @return a circle
+   */
+  public Polygon createCircle()
+  {
+
+    Envelope env = dim.getEnvelope();
+    double xRadius = env.getWidth() / 2.0;
+    double yRadius = env.getHeight() / 2.0;
+
+    double centreX = env.getMinX() + xRadius;
+    double centreY = env.getMinY() + yRadius;
+
+    Coordinate[] pts = new Coordinate[nPts + 1];
+    int iPt = 0;
+    for (int i = 0; i < nPts; i++) {
+        double ang = i * (2 * Math.PI / nPts);
+        double x = xRadius * Math.cos(ang) + centreX;
+        double y = yRadius * Math.sin(ang) + centreY;
+        Coordinate pt = new Coordinate(x, y);
+        pts[iPt++] = pt;
+    }
+    pts[iPt] = pts[0];
+
+    LinearRing ring = geomFact.createLinearRing(pts);
+    Polygon poly = geomFact.createPolygon(ring, null);
+    return poly;
+  }
+
+   /**
+    * Creates a elliptical arc, as a LineString.
+    *
+    * @return an elliptical arc
+    */
+  public LineString createArc(
+     double startAng,
+     double endAng)
+  {
+    Envelope env = dim.getEnvelope();
+    double xRadius = env.getWidth() / 2.0;
+    double yRadius = env.getHeight() / 2.0;
+
+    double centreX = env.getMinX() + xRadius;
+    double centreY = env.getMinY() + yRadius;
+
+     double angSize = (endAng - startAng);
+     if (angSize <= 0.0 || angSize > 2 * Math.PI)
+       angSize = 2 * Math.PI;
+     double angInc = angSize / nPts;
+
+     Coordinate[] pts = new Coordinate[nPts];
+     int iPt = 0;
+     for (int i = 0; i < nPts; i++) {
+         double ang = startAng + i * angInc;
+         double x = xRadius * Math.cos(ang) + centreX;
+         double y = yRadius * Math.sin(ang) + centreY;
+         Coordinate pt = new Coordinate(x, y);
+         geomFact.getPrecisionModel().makePrecise(pt);
+         pts[iPt++] = pt;
+     }
+     LineString line = geomFact.createLineString(pts);
+     return line;
+   }
+
+  private class Dimensions
+  {
+    public Coordinate base;
+    public Coordinate centre;
+    public double width;
+    public double height;
+
+    public void setBase(Coordinate base)  {  this.base = base;    }
+    public void setCentre(Coordinate centre)  {  this.centre = centre;    }
+    public void setSize(double size)
+    {
+      height = size;
+      width = size;
+    }
+
+    public void setWidth(double width) { this.width = width; }
+    public void setHeight(double height) { this.height = height; }
+
+    public Envelope getEnvelope() {
+      if (base != null) {
+        return new Envelope(base.x, base.x + width, base.y, base.y + height);
+      }
+      if (centre != null) {
+        return new Envelope(centre.x - width/2, centre.x + width/2,
+                            centre.y - height/2, centre.y + height/2);
+      }
+      return new Envelope(0, width, 0, height);
+    }
+  }
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/util/Stopwatch.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/util/Stopwatch.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/util/Stopwatch.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,69 @@
+
+/*
+ * 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.util;
+
+/**
+ * Implements a timer function which can compute
+ * elapsed time as well as split times.
+ *
+ * @version 1.6
+ */
+public class Stopwatch {
+
+  private long startTime;
+
+  public Stopwatch()
+  {
+    startTime = System.currentTimeMillis();
+  }
+
+  public void start()
+  {
+    startTime = System.currentTimeMillis();
+  }
+
+  public long getTime()
+  {
+    long endTime = System.currentTimeMillis();
+    long totalTime = endTime - startTime;
+    return totalTime;
+  }
+
+  public String getTimeString()
+  {
+    long totalTime = getTime();
+    String totalTimeStr = totalTime < 10000 ? totalTime + " ms" : (double) totalTime / 1000.0 + " s";
+    return totalTimeStr;
+  }
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/util/UniqueCoordinateArrayFilter.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/util/UniqueCoordinateArrayFilter.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/util/UniqueCoordinateArrayFilter.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,72 @@
+
+
+/*
+ * 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.util;
+
+import java.util.ArrayList;
+import java.util.TreeSet;
+
+import com.vividsolutions.jts.geom.Coordinate;
+import com.vividsolutions.jts.geom.CoordinateFilter;
+
+/**
+ *  A {@link CoordinateFilter} that builds a set of <code>Coordinate</code>s.
+ *  The set of coordinates contains no duplicate points.
+ *
+ *@version 1.6
+ */
+public class UniqueCoordinateArrayFilter implements CoordinateFilter {
+  TreeSet treeSet = new TreeSet();
+  ArrayList list = new ArrayList();
+
+  public UniqueCoordinateArrayFilter() { }
+
+  /**
+   *  Returns the gathered <code>Coordinate</code>s.
+   *
+   *@return    the <code>Coordinate</code>s collected by this <code>CoordinateArrayFilter</code>
+   */
+  public Coordinate[] getCoordinates() {
+    Coordinate[] coordinates = new Coordinate[list.size()];
+    return (Coordinate[]) list.toArray(coordinates);
+  }
+
+  public void filter(Coordinate coord) {
+    if (!treeSet.contains(coord)) {
+      list.add(coord);
+      treeSet.add(coord);
+    }
+  }
+}
+

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jts/util/package.html
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jts/util/package.html	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jts/util/package.html	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,8 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+</head>
+<body bgcolor="white">
+Contains support classes for the Java Topology Suite.
+</body>
+</html>

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/geom/BasicExample.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/geom/BasicExample.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/geom/BasicExample.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,66 @@
+
+/*
+ * 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.geom;
+
+import com.vividsolutions.jts.geom.Coordinate;
+import com.vividsolutions.jts.geom.Geometry;
+import com.vividsolutions.jts.geom.GeometryFactory;
+import com.vividsolutions.jts.io.WKTReader;
+
+/**
+ * Shows basic ways of creating and operating on geometries
+ *
+ * @version 1.6
+ */
+public class BasicExample
+{
+  public static void main(String[] args)
+      throws Exception
+  {
+    // read a geometry from a WKT string (using the default geometry factory)
+    Geometry g1 = new WKTReader().read("LINESTRING (0 0, 10 10, 20 20)");
+    System.out.println("Geometry 1: " + g1);
+
+    // create a geometry by specifying the coordinates directly
+    Coordinate[] coordinates = new Coordinate[]{new Coordinate(0, 0),
+      new Coordinate(10, 10), new Coordinate(20, 20)};
+    // use the default factory, which gives full double-precision
+    Geometry g2 = new GeometryFactory().createLineString(coordinates);
+    System.out.println("Geometry 2: " + g2);
+
+    // compute the intersection of the two geometries
+    Geometry g3 = g1.intersection(g2);
+    System.out.println("G1 intersection G2: " + g3);
+  }
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/geom/ConstructionExample.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/geom/ConstructionExample.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/geom/ConstructionExample.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,40 @@
+package com.vividsolutions.jtsexample.geom;
+
+import com.vividsolutions.jts.geom.*;
+
+/**
+ * Examples of constructing Geometries programmatically.
+ * <p>
+ * The Best Practice moral here is:
+ * <quote>
+ * Use the GeometryFactory to construct Geometries whenever possible.
+ * </quote>
+ * This has several advantages:
+ * <ol>
+ * <li>Simplifies your code
+ * <li>allows you to take advantage of convenience methods provided by GeometryFactory
+ * <li>Insulates your code from changes in the signature of JTS constructors
+ * </ol>
+ *
+ * @version 1.6
+ */
+public class ConstructionExample
+{
+
+  public static void main(String[] args)
+      throws Exception
+  {
+    // create a factory using default values (e.g. floating precision)
+    GeometryFactory fact = new GeometryFactory();
+
+    Point p1 = fact.createPoint(new Coordinate(0,0));
+    System.out.println(p1);
+
+    Point p2 = fact.createPoint(new Coordinate(1,1));
+    System.out.println(p1);
+
+    MultiPoint mpt = fact.createMultiPoint(new Coordinate[] { new Coordinate(0,0), new Coordinate(1,1) } );
+    System.out.println(mpt);
+
+  }
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/geom/ExtendedCoordinate.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/geom/ExtendedCoordinate.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/geom/ExtendedCoordinate.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,93 @@
+
+/*
+ * 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.geom;
+
+import com.vividsolutions.jts.geom.*;
+
+
+/**
+ * @version 1.6
+ */
+public class ExtendedCoordinate
+    extends Coordinate
+{
+    private static final long serialVersionUID = 8527484784733305576L;
+  // A Coordinate subclass should provide all of these methods
+
+  /**
+   * Default constructor
+   */
+  public ExtendedCoordinate()
+  {
+    super();
+    this.m = 0.0;
+  }
+
+  public ExtendedCoordinate(double x, double y, double z, double m)
+  {
+    super(x, y, z);
+    this.m = m;
+  }
+
+  public ExtendedCoordinate(Coordinate coord)
+  {
+    super(coord);
+    if (coord instanceof ExtendedCoordinate)
+      m = ((ExtendedCoordinate) coord).m;
+    else
+      m = Double.NaN;
+  }
+
+  public ExtendedCoordinate(ExtendedCoordinate coord)
+  {
+    super(coord);
+    m = coord.m;
+  }
+
+  /**
+   * An example of extended data.
+   * The m variable holds a measure value for linear referencing
+   */
+
+  private double m;
+
+  public double getM() { return m; }
+  public void setM(double m) { this.m = m; }
+
+  public String toString()
+  {
+    String stringRep = x + " " + y + " m=" + m;
+    return stringRep;
+  }
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/geom/ExtendedCoordinateExample.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/geom/ExtendedCoordinateExample.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/geom/ExtendedCoordinateExample.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,89 @@
+
+/*
+ * 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.geom;
+
+import com.vividsolutions.jts.geom.*;
+
+
+/**
+ * @version 1.6
+ */
+public class ExtendedCoordinateExample
+{
+
+  public static void main(String args[])
+  {
+    ExtendedCoordinateSequenceFactory seqFact = ExtendedCoordinateSequenceFactory.instance();
+
+    ExtendedCoordinate[] array1 = new ExtendedCoordinate[] {
+      new ExtendedCoordinate(0, 0, 0, 91),
+      new ExtendedCoordinate(10, 0, 0, 92),
+      new ExtendedCoordinate(10, 10, 0, 93),
+      new ExtendedCoordinate(0, 10, 0, 94),
+      new ExtendedCoordinate(0, 0, 0, 91),
+    };
+    CoordinateSequence seq1 = seqFact.create(array1);
+
+    CoordinateSequence seq2 = seqFact.create(
+    new ExtendedCoordinate[] {
+      new ExtendedCoordinate(5, 5, 0, 91),
+      new ExtendedCoordinate(15, 5, 0, 92),
+      new ExtendedCoordinate(15, 15, 0, 93),
+      new ExtendedCoordinate(5, 15, 0, 94),
+      new ExtendedCoordinate(5, 5, 0, 91),
+    });
+
+    GeometryFactory fact = new GeometryFactory(
+        ExtendedCoordinateSequenceFactory.instance());
+
+    Geometry g1 = fact.createPolygon(fact.createLinearRing(seq1), null);
+    Geometry g2 = fact.createPolygon(fact.createLinearRing(seq2), null);
+
+    System.out.println("WKT for g1: " + g1);
+    System.out.println("Internal rep for g1: " + ((Polygon) g1).getExteriorRing().getCoordinateSequence());
+
+    System.out.println("WKT for g2: " + g2);
+    System.out.println("Internal rep for g2: " + ((Polygon) g2).getExteriorRing().getCoordinateSequence());
+
+    Geometry gInt = g1.intersection(g2);
+
+    System.out.println("WKT for gInt: " + gInt);
+    System.out.println("Internal rep for gInt: " + ((Polygon) gInt).getExteriorRing().getCoordinateSequence());
+  }
+
+  public ExtendedCoordinateExample() {
+  }
+
+
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/geom/ExtendedCoordinateSequence.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/geom/ExtendedCoordinateSequence.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/geom/ExtendedCoordinateSequence.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,203 @@
+
+/*
+ * 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.geom;
+
+import com.vividsolutions.jts.geom.*;
+
+/**
+ * Demonstrates how to implement a CoordinateSequence for a new kind of
+ * coordinate (an {@link ExtendedCoordinate} in this example). In this
+ * implementation, Coordinates returned by #toArray and #get are live -- parties
+ * that change them are actually changing the ExtendedCoordinateSequence's
+ * underlying data.
+ *
+ * @version 1.6
+ */
+public class ExtendedCoordinateSequence
+    implements CoordinateSequence
+{
+  public static ExtendedCoordinate[] copy(Coordinate[] coordinates)
+  {
+    ExtendedCoordinate[] copy = new ExtendedCoordinate[coordinates.length];
+    for (int i = 0; i < coordinates.length; i++) {
+      copy[i] = new ExtendedCoordinate(coordinates[i]);
+    }
+    return copy;
+  }
+
+  public static ExtendedCoordinate[] copy(CoordinateSequence coordSeq)
+  {
+    ExtendedCoordinate[] copy = new ExtendedCoordinate[coordSeq.size()];
+    for (int i = 0; i < coordSeq.size(); i++) {
+      copy[i] = new ExtendedCoordinate(coordSeq.getCoordinate(i));
+    }
+    return copy;
+  }
+
+  private ExtendedCoordinate[] coordinates;
+
+  /**
+   * Copy constructor -- simply aliases the input array, for better performance.
+   */
+  public ExtendedCoordinateSequence(ExtendedCoordinate[] coordinates) {
+    this.coordinates = coordinates;
+  }
+
+  /**
+   * Constructor that makes a copy of an array of Coordinates.
+   * Always makes a copy of the input array, since the actual class
+   * of the Coordinates in the input array may be different from ExtendedCoordinate.
+   */
+  public ExtendedCoordinateSequence(Coordinate[] copyCoords) {
+    coordinates = copy(copyCoords);
+  }
+
+  /**
+   * Constructor that makes a copy of a CoordinateSequence.
+   */
+  public ExtendedCoordinateSequence(CoordinateSequence coordSeq) {
+    coordinates = copy(coordSeq);
+  }
+
+  /**
+   * Constructs a sequence of a given size, populated
+   * with new {@link ExtendedCoordinate}s.
+   *
+   * @param size the size of the sequence to create
+   */
+  public ExtendedCoordinateSequence(int size) {
+    coordinates = new ExtendedCoordinate[size];
+    for (int i = 0; i < size; i++) {
+      coordinates[i] = new ExtendedCoordinate();
+    }
+  }
+
+
+  public Coordinate getCoordinate(int i) {
+    return coordinates[i];
+  }
+
+  /**
+   * @see com.vividsolutions.jts.geom.CoordinateSequence#getX(int)
+   */
+  public Coordinate getCoordinateCopy(int index) {
+    return new Coordinate(coordinates[index]);
+  }
+  /**
+   * @see com.vividsolutions.jts.geom.CoordinateSequence#getX(int)
+   */
+  public void getCoordinate(int index, Coordinate coord) {
+    coord.x = coordinates[index].x;
+    coord.y = coordinates[index].y;
+  }
+
+
+  /**
+   * @see com.vividsolutions.jts.geom.CoordinateSequence#getX(int)
+   */
+  public double getX(int index) {
+      return coordinates[index].x;
+  }
+
+  /**
+   * @see com.vividsolutions.jts.geom.CoordinateSequence#getY(int)
+   */
+  public double getY(int index) {
+      return coordinates[index].y;
+  }
+
+  /**
+   * @see com.vividsolutions.jts.geom.CoordinateSequence#getOrdinate(int, int)
+   */
+  public double getOrdinate(int index, int ordinateIndex)
+  {
+    switch (ordinateIndex) {
+      case CoordinateSequence.X:  return coordinates[index].x;
+      case CoordinateSequence.Y:  return coordinates[index].y;
+      case CoordinateSequence.Z:  return coordinates[index].z;
+      case 4:  return coordinates[index].getM();
+    }
+    return Double.NaN;
+  }
+
+  /**
+   * @see com.vividsolutions.jts.geom.CoordinateSequence#setOrdinate(int, int, double)
+   */
+  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 4:  coordinates[index].setM(value);
+    }
+  }
+
+  public Object clone() {
+    ExtendedCoordinate[] cloneCoordinates = new ExtendedCoordinate[size()];
+    for (int i = 0; i < coordinates.length; i++) {
+      cloneCoordinates[i] = (ExtendedCoordinate) coordinates[i].clone();
+    }
+
+    return new ExtendedCoordinateSequence(cloneCoordinates);
+  }
+
+  public int size() {
+    return coordinates.length;
+  }
+
+  public Coordinate[] toCoordinateArray() {
+    return coordinates;
+  }
+
+  public Envelope expandEnvelope(Envelope env)
+  {
+    for (int i = 0; i < coordinates.length; i++ ) {
+      env.expandToInclude(coordinates[i]);
+    }
+    return env;
+  }
+
+  public String toString()
+  {
+    StringBuffer strBuf = new StringBuffer();
+    strBuf.append("ExtendedCoordinateSequence [");
+    for (int i = 0; i < coordinates.length; i++) {
+      if (i > 0) strBuf.append(", ");
+      strBuf.append(coordinates[i]);
+    }
+    strBuf.append("]");
+    return strBuf.toString();
+  }
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/geom/ExtendedCoordinateSequenceFactory.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/geom/ExtendedCoordinateSequenceFactory.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/geom/ExtendedCoordinateSequenceFactory.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,83 @@
+
+/*
+ * 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.geom;
+
+import com.vividsolutions.jts.geom.*;
+
+/**
+ * Creates ExtendedCoordinateSequenceFactory internally represented
+ * as an array of {@link ExtendedCoordinate}s.
+ *
+ * @version 1.6
+ */
+public class ExtendedCoordinateSequenceFactory
+    implements CoordinateSequenceFactory
+{
+    private static ExtendedCoordinateSequenceFactory instance = new ExtendedCoordinateSequenceFactory();
+
+    private ExtendedCoordinateSequenceFactory() {
+    }
+
+    /**
+     * Returns the singleton instance of ExtendedCoordinateSequenceFactory
+     */
+    public static ExtendedCoordinateSequenceFactory instance() {
+        return instance;
+    }
+
+    /**
+     * Returns an ExtendedCoordinateSequence based on the given array -- the array is used
+     * directly if it is an instance of ExtendedCoordinate[]; otherwise it is
+     * copied.
+     */
+    public CoordinateSequence create(Coordinate[] coordinates) {
+      return coordinates instanceof ExtendedCoordinate[]
+          ? new ExtendedCoordinateSequence((ExtendedCoordinate[]) coordinates)
+          : new ExtendedCoordinateSequence(coordinates);
+    }
+
+    public CoordinateSequence create(CoordinateSequence coordSeq) {
+      return coordSeq instanceof ExtendedCoordinateSequence
+          ? new ExtendedCoordinateSequence((ExtendedCoordinateSequence) coordSeq)
+          : new ExtendedCoordinateSequence(coordSeq);
+    }
+
+    /**
+     * @see com.vividsolutions.jts.geom.CoordinateSequenceFactory#create(int, int)
+     */
+    public CoordinateSequence create(int size, int dimension) {
+      return new ExtendedCoordinateSequence(size);
+    }
+
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/geom/PrecisionModelExample.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/geom/PrecisionModelExample.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/geom/PrecisionModelExample.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,130 @@
+
+/*
+ * 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.geom;
+
+import com.vividsolutions.jts.geom.*;
+import com.vividsolutions.jts.io.*;
+
+/**
+ * An example showing the results of using different precision models
+ * in computations involving geometric constructions.
+ * A simple intersection computation is carried out in three different
+ * precision models (Floating, FloatingSingle and Fixed with 0 decimal places).
+ * The input is the same in all cases (since it is precise in all three models),
+ * The output shows the effects of rounding in the single-precision and fixed-precision
+ * models.
+ *
+ * @version 1.6
+ */
+public class PrecisionModelExample
+{
+  public static void main(String[] args) {
+    PrecisionModelExample example = new PrecisionModelExample();
+    try {
+      example.run();
+    }
+    catch (Exception ex) {
+      ex.printStackTrace();
+    }
+  }
+
+  public PrecisionModelExample() {
+  }
+
+  public void run()
+    throws ParseException
+  {
+    example1();
+    example2();
+  }
+
+  public void example1()
+    throws ParseException
+  {
+    System.out.println("-------------------------------------------");
+    System.out.println("Example 1 shows roundoff from computing in different precision models");
+    String wktA = "POLYGON ((60 180, 160 260, 240 80, 60 180))";
+    String wktB = "POLYGON ((200 260, 280 160, 80 100, 200 260))";
+    System.out.println("A = " + wktA);
+    System.out.println("B = " + wktB);
+
+    intersection(wktA, wktB, new PrecisionModel());
+    intersection(wktA, wktB, new PrecisionModel(PrecisionModel.FLOATING_SINGLE));
+    intersection(wktA, wktB, new PrecisionModel(1));
+  }
+
+  public void example2()
+    throws ParseException
+  {
+    System.out.println("-------------------------------------------");
+    System.out.println("Example 2 shows that roundoff can change the topology of geometry computed in different precision models");
+    String wktA = "POLYGON ((0 0, 160 0, 160 1, 0 0))";
+    String wktB = "POLYGON ((40 60, 40 -20, 140 -20, 140 60, 40 60))";
+    System.out.println("A = " + wktA);
+    System.out.println("B = " + wktB);
+
+    difference(wktA, wktB, new PrecisionModel());
+    difference(wktA, wktB, new PrecisionModel(1));
+  }
+
+
+  public void intersection(String wktA, String wktB, PrecisionModel pm)
+      throws ParseException
+  {
+    System.out.println("Running example using Precision Model = " + pm);
+    GeometryFactory fact = new GeometryFactory(pm);
+    WKTReader wktRdr = new WKTReader(fact);
+
+    Geometry A = wktRdr.read(wktA);
+    Geometry B = wktRdr.read(wktB);
+    Geometry C = A.intersection(B);
+
+    System.out.println("A intersection B = " + C);
+  }
+
+  public void difference(String wktA, String wktB, PrecisionModel pm)
+      throws ParseException
+  {
+    System.out.println("-------------------------------------------");
+    System.out.println("Running example using Precision Model = " + pm);
+    GeometryFactory fact = new GeometryFactory(pm);
+    WKTReader wktRdr = new WKTReader(fact);
+
+    Geometry A = wktRdr.read(wktA);
+    Geometry B = wktRdr.read(wktB);
+    Geometry C = A.difference(B);
+
+    System.out.println("A intersection B = " + C);
+  }
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/geom/SimpleMethodsExample.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/geom/SimpleMethodsExample.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/geom/SimpleMethodsExample.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,91 @@
+/*
+ * 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.geom;
+
+import com.vividsolutions.jts.geom.*;
+import com.vividsolutions.jts.io.*;
+
+/**
+ * An example showing a simple use of JTS methods for:
+ * <ul>
+ * <li>WKT reading
+ * <li>intersection
+ * <li>relate
+ * <li>WKT output
+ * </ul>
+ * <p>
+ * The expected output from this program is:
+ * <pre>
+ * ----------------------------------------------------------
+ * A = POLYGON ((40 100, 40 20, 120 20, 120 100, 40 100))
+ * B = LINESTRING (20 80, 80 60, 100 140)
+ * A intersection B = LINESTRING (40 73.33333333333334, 80 60, 90 100)
+ * A relate C = 1F20F1102
+ * ----------------------------------------------------------
+ * </pre>
+ *
+ * @version 1.6
+ */
+public class SimpleMethodsExample
+{
+  public static void main(String[] args) {
+    SimpleMethodsExample example = new SimpleMethodsExample();
+    try {
+      example.run();
+    }
+    catch (Exception ex) {
+      ex.printStackTrace();
+    }
+  }
+
+  public SimpleMethodsExample() {
+  }
+
+  public void run()
+    throws ParseException
+  {
+    GeometryFactory fact = new GeometryFactory();
+    WKTReader wktRdr = new WKTReader(fact);
+
+    String wktA = "POLYGON((40 100, 40 20, 120 20, 120 100, 40 100))";
+    String wktB = "LINESTRING(20 80, 80 60, 100 140)";
+    Geometry A = wktRdr.read(wktA);
+    Geometry B = wktRdr.read(wktB);
+    Geometry C = A.intersection(B);
+    System.out.println("A = " + A);
+    System.out.println("B = " + B);
+    System.out.println("A intersection B = " + C);
+    System.out.println("A relate C = " + A.relate(B));
+  }
+
+}

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/operation/distance/ClosestPointExample.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -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.jtsexample.operation.distance;
+
+import com.vividsolutions.jts.geom.*;
+import com.vividsolutions.jts.io.*;
+import com.vividsolutions.jts.operation.distance.DistanceOp;
+
+/**
+ * Example of computing distance and closest points between geometries
+ * using the DistanceOp class.
+ *
+ * @version 1.6
+ */
+public class ClosestPointExample
+{
+  static GeometryFactory fact = new GeometryFactory();
+  static WKTReader wktRdr = new WKTReader(fact);
+
+  public static void main(String[] args) {
+    ClosestPointExample example = new ClosestPointExample();
+    example.run();
+  }
+
+  public ClosestPointExample()
+  {
+  }
+
+  public void run()
+  {
+    findClosestPoint(
+        "POLYGON ((200 180, 60 140, 60 260, 200 180))",
+       "POINT (140 280)");
+    findClosestPoint(
+        "POLYGON ((200 180, 60 140, 60 260, 200 180))",
+       "MULTIPOINT (140 280, 140 320)");
+    findClosestPoint(
+        "LINESTRING (100 100, 200 100, 200 200, 100 200, 100 100)",
+       "POINT (10 10)");
+    findClosestPoint(
+        "LINESTRING (100 100, 200 200)",
+       "LINESTRING (100 200, 200 100)");
+    findClosestPoint(
+        "LINESTRING (100 100, 200 200)",
+       "LINESTRING (150 121, 200 0)");
+    findClosestPoint(
+        "POLYGON (( 76 185, 125 283, 331 276, 324 122, 177 70, 184 155, 69 123, 76 185 ), ( 267 237, 148 248, 135 185, 223 189, 251 151, 286 183, 267 237 ))",
+       "LINESTRING ( 153 204, 185 224, 209 207, 238 222, 254 186 )");
+    findClosestPoint(
+        "POLYGON (( 76 185, 125 283, 331 276, 324 122, 177 70, 184 155, 69 123, 76 185 ), ( 267 237, 148 248, 135 185, 223 189, 251 151, 286 183, 267 237 ))",
+       "LINESTRING ( 120 215, 185 224, 209 207, 238 222, 254 186 )");
+  }
+
+  public void findClosestPoint(String wktA, String wktB)
+  {
+    System.out.println("-------------------------------------");
+    try {
+      Geometry A = wktRdr.read(wktA);
+      Geometry B = wktRdr.read(wktB);
+      System.out.println("Geometry A: " + A);
+      System.out.println("Geometry B: " + B);
+      DistanceOp distOp = new DistanceOp(A, B);
+
+      double distance = distOp.distance();
+      System.out.println("Distance = " + distance);
+
+      Coordinate[] closestPt = distOp.closestPoints();
+      LineString closestPtLine = fact.createLineString(closestPt);
+      System.out.println("Closest points: " + closestPtLine
+                         + " (distance = " + closestPtLine.getLength() + ")");
+    }
+    catch (Exception ex) {
+      ex.printStackTrace();
+    }
+  }
+
+}

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/operation/linemerge/LineMergeExample.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,106 @@
+
+/*
+ * 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.operation.linemerge;
+
+import com.vividsolutions.jts.geom.Geometry;
+import com.vividsolutions.jts.io.WKTReader;
+import com.vividsolutions.jts.operation.linemerge.LineMerger;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+
+/**
+ * Example of using the LineMerger class to sew together a set of fully noded 
+ * linestrings.
+ *
+ * @version 1.6
+ */
+public class LineMergeExample {
+  private WKTReader reader = new WKTReader();
+
+  public LineMergeExample() {
+  }
+
+  public static void main(String[] args) throws Exception {
+    LineMergeExample test = new LineMergeExample();
+    try {
+      test.run();
+    } catch (Exception ex) {
+      ex.printStackTrace();
+    }
+  }
+
+  void run() throws Exception {
+    Collection lineStrings = getData();
+    
+    LineMerger lineMerger = new LineMerger();
+    lineMerger.add(lineStrings);
+    Collection mergedLineStrings = lineMerger.getMergedLineStrings();
+    
+    System.out.println("Lines formed (" + mergedLineStrings.size() + "):");
+    System.out.println(mergedLineStrings);
+  }
+
+  Collection getData() {
+    Collection lines = new ArrayList();
+    lines.add(read("LINESTRING (220 160, 240 150, 270 150, 290 170)"));
+    lines.add(read("LINESTRING (60 210, 30 190, 30 160)"));
+    lines.add(read("LINESTRING (70 430, 100 430, 120 420, 140 400)"));
+    lines.add(read("LINESTRING (160 310, 160 280, 160 250, 170 230)"));
+    lines.add(read("LINESTRING (170 230, 180 210, 200 180, 220 160)"));
+    lines.add(read("LINESTRING (30 160, 40 150, 70 150)"));
+    lines.add(read("LINESTRING (160 310, 200 330, 220 340, 240 360)"));
+    lines.add(read("LINESTRING (140 400, 150 370, 160 340, 160 310)"));
+    lines.add(read("LINESTRING (160 310, 130 300, 100 290, 70 270)"));
+    lines.add(read("LINESTRING (240 360, 260 390, 260 410, 250 430)"));
+    lines.add(read("LINESTRING (70 150, 100 180, 100 200)"));
+    lines.add(read("LINESTRING (70 270, 60 260, 50 240, 50 220, 60 210)"));
+    lines.add(read("LINESTRING (100 200, 90 210, 60 210)"));
+
+    return lines;
+  }
+
+  Geometry read(String lineWKT) {
+    try {
+      Geometry geom = reader.read(lineWKT);
+
+      return geom;
+    } catch (Exception ex) {
+      ex.printStackTrace();
+    }
+
+    return null;
+  }
+}

Added: 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	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/operation/polygonize/PolygonizeExample.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -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.operation.polygonize;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+import com.vividsolutions.jts.geom.Geometry;
+import com.vividsolutions.jts.io.WKTReader;
+import com.vividsolutions.jts.operation.polygonize.Polygonizer;
+
+/**
+ *  Example of using Polygonizer class to polygonize a set of fully noded linestrings
+ *
+ * @version 1.6
+ */
+public class PolygonizeExample
+{
+  public static void main(String[] args) throws Exception
+  {
+    PolygonizeExample test = new PolygonizeExample();
+    try {
+      test.run();
+    }
+    catch (Exception ex) {
+      ex.printStackTrace();
+    }
+  }
+
+
+  public PolygonizeExample() {
+  }
+
+  void run()
+      throws Exception
+  {
+    WKTReader rdr = new WKTReader();
+    Collection lines = new ArrayList();
+
+    lines.add(rdr.read("LINESTRING (0 0 , 10 10)"));   // isolated edge
+    lines.add(rdr.read("LINESTRING (185 221, 100 100)"));   //dangling edge
+    lines.add(rdr.read("LINESTRING (185 221, 88 275, 180 316)"));
+    lines.add(rdr.read("LINESTRING (185 221, 292 281, 180 316)"));
+    lines.add(rdr.read("LINESTRING (189 98, 83 187, 185 221)"));
+    lines.add(rdr.read("LINESTRING (189 98, 325 168, 185 221)"));
+
+    Polygonizer polygonizer = new Polygonizer();
+    polygonizer.add(lines);
+
+    Collection polys = polygonizer.getPolygons();
+
+    System.out.println("Polygons formed (" + polys.size() + "):");
+    System.out.println(polys);
+  }
+
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/package.html
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/package.html	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/package.html	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,14 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+<!--
+
+-->
+</head>
+<body bgcolor="white">
+
+Contains classes with examples of using the JTS API.
+
+
+</body>
+</html>

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/precision/EnhancedPrecisionOpExample.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/precision/EnhancedPrecisionOpExample.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/precision/EnhancedPrecisionOpExample.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,89 @@
+
+/*
+ * 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.precision;
+
+import java.io.*;
+import java.util.*;
+import com.vividsolutions.jts.io.WKTReader;
+import com.vividsolutions.jts.geom.*;
+import com.vividsolutions.jts.precision.EnhancedPrecisionOp;
+
+/**
+ * Example of using {@link EnhancedPrecisionOp} to avoid robustness problems
+ *
+ * @version 1.6
+ */
+public class EnhancedPrecisionOpExample
+{
+  public static void main(String[] args) throws Exception
+  {
+    EnhancedPrecisionOpExample example = new EnhancedPrecisionOpExample();
+    try {
+      example.run();
+    }
+    catch (Exception ex) {
+      ex.printStackTrace();
+    }
+  }
+
+  private WKTReader reader = new WKTReader();
+
+  public EnhancedPrecisionOpExample() {
+  }
+
+  void run()
+      throws Exception
+  {
+    String wkt1, wkt2;
+    // two geometries which cause robustness problems
+    wkt1 = "POLYGON ((708653.498611049 2402311.54647056, 708708.895756966 2402203.47250014, 708280.326454234 2402089.6337791, 708247.896591321 2402252.48269854, 708367.379593851 2402324.00761653, 708248.882609455 2402253.07294874, 708249.523621829 2402244.3124463, 708261.854734465 2402182.39086576, 708262.818392579 2402183.35452387, 708653.498611049 2402311.54647056))";
+    wkt2 = "POLYGON ((708258.754920656 2402197.91172757, 708257.029447455 2402206.56901508, 708652.961095455 2402312.65463437, 708657.068786251 2402304.6356364, 708258.754920656 2402197.91172757))";
+    Geometry g1 = reader.read(wkt1);
+    Geometry g2 = reader.read(wkt2);
+
+    System.out.println("This call to intersection will throw a topology exception due to robustness problems:");
+    try {
+      Geometry result = g1.intersection(g2);
+    }
+    catch (TopologyException ex) {
+      ex.printStackTrace();
+    }
+
+    System.out.println("Using EnhancedPrecisionOp allows the intersection to be performed with no errors:");
+    Geometry result = EnhancedPrecisionOp.intersection(g1, g2);
+    System.out.println(result);
+  }
+
+
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/technique/LineStringSelfIntersections.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/technique/LineStringSelfIntersections.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/technique/LineStringSelfIntersections.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,65 @@
+package com.vividsolutions.jtsexample.technique;
+
+import com.vividsolutions.jts.geom.*;
+import com.vividsolutions.jts.io.WKTReader;
+import java.util.*;
+
+/**
+ * Shows a technique for identifying the location of self-intersections
+ * in a non-simple LineString.
+ *
+ * @version 1.6
+ */
+
+public class LineStringSelfIntersections {
+
+  public static void main(String[] args)
+      throws Exception
+  {
+    WKTReader rdr = new WKTReader();
+
+    LineString line1 = (LineString) (rdr.read("LINESTRING (0 0, 10 10, 20 20)"));
+    showSelfIntersections(line1);
+    LineString line2 = (LineString) (rdr.read("LINESTRING (0 40, 60 40, 60 0, 20 0, 20 60)"));
+    showSelfIntersections(line2);
+
+  }
+
+  public static void showSelfIntersections(LineString line)
+  {
+    System.out.println("Line: " + line);
+    System.out.println("Self Intersections: " + lineStringSelfIntersections(line));
+  }
+
+  public static Geometry lineStringSelfIntersections(LineString line)
+  {
+    Geometry lineEndPts = getEndPoints(line);
+    Geometry nodedLine = line.union(lineEndPts);
+    Geometry nodedEndPts = getEndPoints(nodedLine);
+    Geometry selfIntersections = nodedEndPts.difference(lineEndPts);
+    return selfIntersections;
+  }
+
+  public static Geometry getEndPoints(Geometry g)
+  {
+    List endPtList = new ArrayList();
+    if (g instanceof LineString) {
+      LineString line = (LineString) g;
+
+      endPtList.add(line.getCoordinateN(0));
+      endPtList.add(line.getCoordinateN(line.getNumPoints() - 1));
+    }
+    else if (g instanceof MultiLineString) {
+      MultiLineString mls = (MultiLineString) g;
+      for (int i = 0; i < mls.getNumGeometries(); i++) {
+        LineString line = (LineString) mls.getGeometryN(i);
+        endPtList.add(line.getCoordinateN(0));
+        endPtList.add(line.getCoordinateN(line.getNumPoints() - 1));
+      }
+    }
+    Coordinate[] endPts = CoordinateArrays.toCoordinateArray(endPtList);
+    return (new GeometryFactory()).createMultiPoint(endPts);
+  }
+
+
+}

Added: packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/technique/UnionUsingBuffer.java
===================================================================
--- packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/technique/UnionUsingBuffer.java	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/com/vividsolutions/jtsexample/technique/UnionUsingBuffer.java	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,49 @@
+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/jump-workbench-properties.xml
===================================================================
--- packages/jts/branches/upstream/current/src/jump-workbench-properties.xml	                        (rev 0)
+++ packages/jts/branches/upstream/current/src/jump-workbench-properties.xml	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,3 @@
+<jump-workbench>
+   <extension>com.vividsolutions.jtsplugin.JTSExtension</extension> 
+</jump-workbench>

Added: packages/jts/branches/upstream/current/test/robust/TestRobustOverlayFixed.xml
===================================================================
--- packages/jts/branches/upstream/current/test/robust/TestRobustOverlayFixed.xml	                        (rev 0)
+++ packages/jts/branches/upstream/current/test/robust/TestRobustOverlayFixed.xml	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,18 @@
+<run>
+  <precisionModel scale="1.0" offsetx="0.0" offsety="0.0"/>
+
+<case>
+  <desc>AA</desc>
+  <a>
+POLYGON ((545 317, 617 379, 581 321, 545 317))
+	  </a>
+  <b>
+POLYGON ((484 290, 558 359, 543 309, 484 290))
+
+  	</b>
+<test>
+  <op name="intersection">    GEOMETRYCOLLECTION EMPTY  </op>
+</test>
+</case>
+
+</run>

Added: packages/jts/branches/upstream/current/test/robust/TestRobustOverlayFloat.xml
===================================================================
--- packages/jts/branches/upstream/current/test/robust/TestRobustOverlayFloat.xml	                        (rev 0)
+++ packages/jts/branches/upstream/current/test/robust/TestRobustOverlayFloat.xml	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,94 @@
+<run>
+	<precisionModel type="FLOATING" />
+<case>
+  <desc>AA - robustness failure</desc>
+  <a>
+POLYGON ((301949.68 2767249.16, 301936.52 2767241.28, 301938.87 
+2767237.43, 301952.47 2767245.59, 301950.74 2767247.81, 301949.68 
+2767249.16))
+  </a>
+  <b>
+POLYGON ((302041.321 2767264.675, 301938.823 2767237.507, 301941.21 2767233.59, 301943.821 2767229.304, 
+302048.886 2767243.046, 302041.321 2767264.675))  
+  </b>
+<test>
+  <op name="intersection">    GEOMETRYCOLLECTION EMPTY  </op>
+</test>
+</case>
+
+<case>
+  <desc>AA - robustness failure</desc>
+  <a>
+POLYGON ((301936.52 2767241.28, 301933.22 2767239.3, 301934.9 
+2767236.51, 301935.54 2767235.44, 301938.87 2767237.43, 301936.52 
+2767241.28))
+  </a>
+  <b>
+POLYGON ((302041.321 2767264.675, 301938.823 2767237.507, 301941.21 2767233.59, 301943.821 2767229.304, 
+302048.886 2767243.046, 302041.321 2767264.675))  
+</b>
+<test>
+  <op name="intersection">    GEOMETRYCOLLECTION EMPTY  </op>
+</test>
+</case>
+
+<case>
+  <desc>AA - robustness failure (fails with EnhancedPrecisionOp)</desc>
+  <a>
+POLYGON ((464664.782646596 5362148.87380619, 464664.713299 5362148.758128, 464686.806220838 5362136.92416521, 
+464713.650216607 5362122.5453135, 464711.113332785 5362117.30158834, 464707.408813375 5362110.21553566, 
+464703.323866879 5362103.23305736, 464698.945488413 5362096.31213576, 464694.461274991 5362089.42505804, 
+464625.876674576 5361951.92914952, 464622.430583893 5361944.69388208, 464535.3572 5361970.739, 
+464648.194399372 5362157.89548451, 464664.782646596 5362148.87380619))  </a>
+  <b>
+POLYGON ((464769.977147523 5362187.88829332, 464765.146147008 5362180.84587461, 464754.387021019 5362169.93629911, 
+464747.786455245 5362160.11104076, 464734.810564627 5362148.45253107, 464725.386626381 5362135.71065214, 
+464712.646269 5362123.083073, 464727.794520848 5362149.37983229, 464738.165719397 5362165.72994593, 
+464746.257208116 5362179.45514151, 464752.378040379 5362191.80978275, 464769.977147523 5362187.88829332))  
+</b>
+<test>
+  <op name="intersection">    GEOMETRYCOLLECTION EMPTY  </op>
+</test>
+</case>
+
+<case>
+  <desc>AA - robustness failure (fails with EnhancedPrecisionOp)</desc>
+  <a>
+POLYGON ((698400.5682737827 2388494.3828697307, 698402.3209180075 
+2388497.0819257903, 698415.3598714538 2388498.764371397, 
+698413.5003455497 2388495.90071853, 698400.5682737827 
+2388494.3828697307))
+	</a>
+  <b>
+POLYGON ((698231.847335025 2388474.57994264, 698440.416211779 
+2388499.05985776, 698432.582638943 2388300.28294705, 698386.666515791 
+2388303.40346027, 698328.29462841 2388312.88889197, 698231.847335025 
+2388474.57994264))
+	</b>
+<test>
+  <op name="intersection">    GEOMETRYCOLLECTION EMPTY  </op>
+</test>
+</case>
+
+
+<case>
+  <desc>AA - robustness failure (fails with EnhancedPrecisionOp)</desc>
+  <a>
+POLYGON ((698265.5760207245 2388415.007869463, 698266.5171698363 
+2388416.456984281, 698272.2367250263 2388406.868318228, 
+698271.2748419731 2388405.3872787533, 698265.5760207245 
+2388415.007869463))
+	</a>
+  <b>
+POLYGON ((698230.86813842 2388473.60074604, 698104.551776442 
+2388363.93072634, 698321.933422637 2388319.86687914, 698230.86813842 
+2388473.60074604))
+	</b>
+<test>
+  <op name="intersection">    GEOMETRYCOLLECTION EMPTY  </op>
+</test>
+</case>
+
+
+
+</run>

Added: packages/jts/branches/upstream/current/test/robust/TestRobustRelate.xml
===================================================================
--- packages/jts/branches/upstream/current/test/robust/TestRobustRelate.xml	                        (rev 0)
+++ packages/jts/branches/upstream/current/test/robust/TestRobustRelate.xml	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,19 @@
+<run>
+  <precisionModel scale="1.0" offsetx="0.0" offsety="0.0"/>
+
+<case>
+  <desc>PP - Point is not on line. Non-robust algorithms fail by erroneously reporting intersects=true.</desc>
+  <a>
+    LINESTRING(-123456789 -40, 381039468754763 123456789)
+  </a>
+  <b>
+    POINT(0 0)
+  </b>
+<test>
+  <op name="intersects">
+    false
+  </op>
+</test>
+</case>
+
+</run>

Added: packages/jts/branches/upstream/current/test/validate/TestRelateAA-big.xml
===================================================================
--- packages/jts/branches/upstream/current/test/validate/TestRelateAA-big.xml	                        (rev 0)
+++ packages/jts/branches/upstream/current/test/validate/TestRelateAA-big.xml	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,34 @@
+<run>
+<precisionModel type="FLOATING"/>
+
+<case>
+<desc>A/A-6-18: a polygon overlapping a very skinny polygon [dim(2){A.A.Int = B.A.Int}, dim(1){A.A.Bdy.V-EP = B.A.Bdy.NV-EP}, dim(0){A.A.Bdy.CP = B.A.Bdy.CP}, dim(0){A.A.Bdy.NV = B.A.Bdy.NV}]</desc>
+  <a>
+    POLYGON(
+      (100 100, 100 200, 200 200, 200 100, 100 100))
+  </a>
+  <b>
+    POLYGON(
+      (100 100, 1000000000000000 110, 1000000000000000 100, 100 100))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="212111212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/A-6-24: a polygon overlapping a very skinny polygon [dim(2){A.A.Int = B.A.Int}, dim(1){A.A.Bdy.V-EP = B.A.Bdy.NV-NV}, dim(0){A.A.Bdy.NV = B.A.Bdy.NV}]</desc>
+  <a>
+    POLYGON(
+      (120 100, 120 200, 200 200, 200 100, 120 100))
+  </a>
+  <b>
+    POLYGON(
+      (100 100, 1000000000000000 110, 1000000000000000 100, 100 100))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="212111212">true</op>
+  </test>
+</case>
+
+</run>

Added: packages/jts/branches/upstream/current/test/validate/TestRelateAA.xml
===================================================================
--- packages/jts/branches/upstream/current/test/validate/TestRelateAA.xml	                        (rev 0)
+++ packages/jts/branches/upstream/current/test/validate/TestRelateAA.xml	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,1793 @@
+<run>
+<precisionModel type="FLOATING"/>
+
+<case>
+<desc>A/A-1-1: same polygons [dim(2){A.A.Int = B.A.Int}, dim(1){A.A.Bdy.SP-EP = B.A.Bdy.SP-EP}]</desc>
+  <a>
+    POLYGON(
+      (20 20, 20 100, 120 100, 140 20, 20 20))
+  </a>
+  <b>
+    POLYGON(
+      (20 20, 20 100, 120 100, 140 20, 20 20))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="2FFF1FFF2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/A-1-2: same polygons with reverse sequence of points [dim(2){A.A.Int = B.A.Int}, dim(1){A.A.Bdy.SP-EP = B.A.Bdy.EP-SP}]</desc>
+  <a>
+    POLYGON(
+      (20 20, 20 100, 120 100, 140 20, 20 20))
+  </a>
+  <b>
+    POLYGON(
+      (20 20, 140 20, 120 100, 20 100, 20 20))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="2FFF1FFF2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/A-1-3: same polygons with different sequence of points [dim(2){A.A.Int = B.A.Int}, dim(1){A.A.Bdy.SP-EP = B.A.Bdy.SP-EP}]</desc>
+  <a>
+    POLYGON(
+      (20 20, 20 100, 120 100, 140 20, 20 20))
+  </a>
+  <b>
+    POLYGON(
+      (120 100, 140 20, 20 20, 20 100, 120 100))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="2FFF1FFF2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/A-1-4: same polygons with different number of points [dim(2){A.A.Int = B.A.Int}, dim(1){A.A.Bdy.SP-EP = B.A.Bdy.SP-EP}]</desc>
+  <a>
+    POLYGON(
+      (20 20, 20 100, 120 100, 140 20, 20 20))
+  </a>
+  <b>
+    POLYGON(
+      (20 100, 60 100, 120 100, 140 20, 80 20, 20 20, 20 100))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="2FFF1FFF2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/A-2: different polygons [dim(2){A.A.Int = B.A.Ext}]</desc>
+  <a>
+    POLYGON(
+      (0 0, 80 0, 80 80, 0 80, 0 0))
+  </a>
+  <b>
+    POLYGON(
+      (100 200, 100 140, 180 140, 180 200, 100 200))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF2FF1212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/A-3-1-1: the closing point of a polygon touching the closing point of another polygon [dim(0){A.A.Bdy.CP = B.A.Bdy.CP}]</desc>
+  <a>
+    POLYGON(
+      (140 120, 160 20, 20 20, 20 120, 140 120))
+  </a>
+  <b>
+    POLYGON(
+      (140 120, 140 200, 240 200, 240 120, 140 120))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF2F01212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/A-3-1-2: the closing point of a polygon touching the boundary (at a non-vertex) of another polygon [dim(0){A.A.Bdy.CP = B.A.Bdy.NV}]</desc>
+  <a>
+    POLYGON(
+      (140 120, 160 20, 20 20, 20 120, 140 120))
+  </a>
+  <b>
+    POLYGON(
+      (80 180, 140 260, 260 200, 200 60, 80 180))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF2F01212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/A-3-1-3: the closing point of a polygon touching the boundary (at a vertex) of another polygon [dim(0){A.A.Bdy.CP = B.A.Bdy.V}]</desc>
+  <a>
+    POLYGON(
+      (140 120, 160 20, 20 20, 20 120, 140 120))
+  </a>
+  <b>
+    POLYGON(
+      (240 80, 140 120, 180 240, 280 200, 240 80))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF2F01212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/A-3-1-4: the boundary (at a non-vertex) of a polygon touching the closing point of another polygon [dim(0){A.A.Bdy.NV = B.A.Bdy.CP}]</desc>
+  <a>
+    POLYGON(
+      (140 160, 20 20, 270 20, 150 160, 230 40, 60 40, 140 160))
+  </a>
+  <b>
+    POLYGON(
+      (140 40, 180 80, 120 100, 140 40))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF2F01212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/A-3-1-5: the boundary (at a non-vertex) of a polygon touching the boundary (at a vertex) of another polygon [dim(0){A.A.Bdy.NV = B.A.Bdy.V}]</desc>
+  <a>
+    POLYGON(
+      (140 160, 20 20, 270 20, 150 160, 230 40, 60 40, 140 160))
+  </a>
+  <b>
+    POLYGON(
+      (120 100, 180 80, 130 40, 120 100))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF2F01212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/A-3-1-6: the boundary (at a vertex) of a polygon touching the boundary (at a non-vertex) of another polygon [dim(0){A.A.Bdy.V = B.A.Bdy.NV}]</desc>
+  <a>
+    POLYGON(
+      (20 20, 180 20, 140 140, 20 140, 20 20))
+  </a>
+  <b>
+    POLYGON(
+      (180 100, 80 200, 180 280, 260 200, 180 100))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF2F01212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/A-3-1-7: the boundary (at a vertex) of a polygon touching the boundary (at a vertex) of another polygon [dim(0){A.A.Bdy.V = B.A.Bdy.V}]</desc>
+  <a>
+    POLYGON(
+      (140 120, 160 20, 20 20, 20 120, 140 120))
+  </a>
+  <b>
+    POLYGON(
+      (140 140, 20 120, 0 220, 120 240, 140 140))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF2F01212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/A-3-1-8: the closing point of a polygon touching the boundary of another polygon where the closing point touching the boundary at a vertex [dim(0){A.A.Bdy.CP = B.A.Bdy.TP}]</desc>
+  <a>
+    POLYGON(
+      (160 200, 210 70, 120 70, 160 200))
+  </a>
+  <b>
+    POLYGON(
+      (160 200, 260 40, 70 40, 160 200, 20 20, 310 20, 160 200))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF2F01212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/A-3-1-9: the closing point of a polygon touching the boundary of another polygon where the closing point intersecting the boundary at a non-vertex [dim(0){A.A.Bdy.CP = B.A.Bdy.TP}]</desc>
+  <a>
+    POLYGON(
+      (110 140, 200 70, 200 160, 110 140))
+  </a>
+  <b>
+    POLYGON(
+      (110 140, 110 50, 60 50, 60 90, 160 190, 20 110, 20 20, 200 20, 110 140))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF2F01212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/A-3-2-1: two polygons touching at multiple points [dim(0){A.A.Bdy.CP = B.A.Bdy.CP}, dim(0){A.A.Bdy.V = B.A.Bdy.V}]</desc>
+  <a>
+    POLYGON(
+      (20 120, 20 20, 260 20, 260 120, 200 40, 140 120, 80 40, 20 120))
+  </a>
+  <b>
+    POLYGON(
+      (20 120, 20 240, 260 240, 260 120, 200 200, 140 120, 80 200, 20 120))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF2F01212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/A-3-2-2: two polygons touching at multiple points [dim(0){A.A.Bdy.CP = B.A.Bdy.CP}, dim(0){A.A.Bdy.V = B.A.Bdy.NV}]</desc>
+  <a>
+    POLYGON(
+      (20 120, 20 20, 260 20, 260 120, 180 40, 140 120, 100 40, 20 120))
+  </a>
+  <b>
+    POLYGON(
+      (20 120, 300 120, 140 240, 20 120))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF2F01212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/A-3-2-3: two polygons touching at multiple points [dim(0){A.A.Bdy.CP = B.A.Bdy.NV}, dim(0){A.A.Bdy.V = B.A.Bdy.NV}]</desc>
+  <a>
+    POLYGON(
+      (20 20, 20 300, 280 300, 280 260, 220 260, 60 100, 60 60, 280 60, 280 20, 
+      20 20))
+  </a>
+  <b>
+    POLYGON(
+      (100 140, 160 80, 280 180, 200 240, 220 160, 160 200, 180 120, 100 140))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF2F01212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/A-3-2-4: two polygons touching at multiple points [dim(0){A.A.Bdy.V = B.A.Bdy.NV}]</desc>
+  <a>
+    POLYGON(
+      (20 20, 20 300, 280 300, 280 260, 220 260, 60 100, 60 60, 280 60, 280 20, 
+      20 20))
+  </a>
+  <b>
+    POLYGON(
+      (260 200, 180 80, 120 160, 200 160, 180 220, 260 200))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF2F01212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/A-3-2-5: two polygons touching at multiple points [dim(0){A.A.Bdy.V = B.A.Bdy.NV}]</desc>
+  <a>
+    POLYGON(
+      (20 20, 280 20, 280 140, 220 60, 140 140, 80 60, 20 140, 20 20))
+  </a>
+  <b>
+    POLYGON(
+      (0 140, 300 140, 140 240, 0 140))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF2F01212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/A-3-2-6: two polygons touching at multiple points [dim(0){A.A.Bdy.V = B.A.Bdy.V}, dim(0){A.A.Bdy.V = B.A.Bdy.NV}]</desc>
+  <a>
+    POLYGON(
+      (20 20, 280 20, 280 140, 220 60, 140 140, 80 60, 20 140, 20 20))
+  </a>
+  <b>
+    POLYGON(
+      (20 240, 20 140, 320 140, 180 240, 20 240))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF2F01212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/A-3-2-7: two polygons touching at multiple points [dim(0){A.A.Bdy.V = B.A.Bdy.V}]</desc>
+  <a>
+    POLYGON(
+      (20 20, 280 20, 280 140, 220 60, 140 140, 80 60, 20 140, 20 20))
+  </a>
+  <b>
+    POLYGON(
+      (20 240, 20 140, 80 180, 140 140, 220 180, 280 140, 280 240, 20 240))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF2F01212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/A-3-3-1: two polygons touching along a boundary [dim(1){A.A.Bdy.SP-V = B.A.Bdy.SP-NV}]</desc>
+  <a>
+    POLYGON(
+      (120 120, 180 60, 20 20, 20 120, 120 120))
+  </a>
+  <b>
+    POLYGON(
+      (120 120, 220 20, 280 20, 240 160, 120 120))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF2F11212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/A-3-3-2: two polygons touching along a boundary [dim(1){A.A.Bdy.SP-V = B.A.Bdy.SP-V}]</desc>
+  <a>
+    POLYGON(
+      (140 120, 160 20, 20 20, 20 120, 140 120))
+  </a>
+  <b>
+    POLYGON(
+      (140 120, 160 20, 260 120, 220 200, 140 120))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF2F11212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/A-3-3-3: two polygons touching along a boundary [dim(1){A.A.Bdy.SP-V = B.A.Bdy.NV-V}]</desc>
+  <a>
+    POLYGON(
+      (20 140, 120 40, 20 40, 20 140))
+  </a>
+  <b>
+    POLYGON(
+      (190 140, 190 20, 140 20, 20 140, 190 140))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF2F11212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/A-3-3-4: two polygons touching along a boundary [dim(1){A.A.Bdy.SP-V = B.A.Bdy.NV-V}]</desc>
+  <a>
+    POLYGON(
+      (120 120, 180 60, 20 20, 20 120, 120 120))
+  </a>
+  <b>
+    POLYGON(
+      (300 20, 220 20, 120 120, 260 160, 300 20))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF2F11212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/A-3-3-5: two polygons touching along a boundary [dim(1){A.A.Bdy.SP-V = B.A.Bdy.V-EP}]</desc>
+  <a>
+    POLYGON(
+      (140 120, 160 20, 20 20, 20 120, 140 120))
+  </a>
+  <b>
+    POLYGON(
+      (140 120, 240 160, 280 60, 160 20, 140 120))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF2F11212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/A-3-3-6: two polygons touching along a boundary [dim(1){A.A.Bdy.SP-V = B.A.Bdy.V-V}]</desc>
+  <a>
+    POLYGON(
+      (120 120, 180 60, 20 20, 20 120, 120 120))
+  </a>
+  <b>
+    POLYGON(
+      (280 60, 180 60, 120 120, 260 180, 280 60))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF2F11212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/A-3-3-7: two polygons touching along a boundary [dim(1){A.A.Bdy.NV-NV = B.A.Bdy.V-V}]</desc>
+  <a>
+    POLYGON(
+      (140 120, 160 20, 20 20, 20 120, 140 120))
+  </a>
+  <b>
+    POLYGON(
+      (120 200, 120 120, 40 120, 40 200, 120 200))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF2F11212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/A-3-3-8: two polygons touching along a boundary [dim(1){A.A.Bdy.NV-EP = B.A.Bdy.V-V}]</desc>
+  <a>
+    POLYGON(
+      (140 120, 160 20, 20 20, 20 120, 140 120))
+  </a>
+  <b>
+    POLYGON(
+      (160 220, 140 120, 60 120, 40 220, 160 220))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF2F11212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/A-3-3-9: two polygons touching along a boundary [dim(1){A.A.Bdy.V-EP = B.A.Bdy.V-SP}]</desc>
+  <a>
+    POLYGON(
+      (140 120, 160 20, 20 20, 20 120, 140 120))
+  </a>
+  <b>
+    POLYGON(
+      (140 120, 20 120, 20 220, 140 220, 140 120))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF2F11212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/A-3-3-10: two polygons touching along a boundary [dim(1){A.A.Bdy.V-V = B.A.Bdy.NV-NV}]</desc>
+  <a>
+    POLYGON(
+      (120 120, 180 60, 20 20, 20 120, 120 120))
+  </a>
+  <b>
+    POLYGON(
+      (320 20, 220 20, 80 160, 240 140, 320 20))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF2F11212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/A-5-1: a polygon containing another polygon [dim(2){A.A.Int = B.A.Int}, dim(1){A.A.Bdy.SP-EP = B.A.Int}]</desc>
+  <a>
+    POLYGON(
+      (20 20, 20 180, 220 180, 220 20, 20 20))
+  </a>
+  <b>
+    POLYGON(
+      (60 40, 60 140, 180 140, 180 40, 60 40))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="212FF1FF2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/A-5-2-1: a polygon containing another polygon [dim(2){A.A.Int = B.A.Int}, dim(0){A.A.Bdy.CP = B.A.Bdy.CP}]</desc>
+  <a>
+    POLYGON(
+      (20 20, 20 180, 220 180, 220 20, 20 20))
+  </a>
+  <b>
+    POLYGON(
+      (20 20, 80 140, 160 60, 20 20))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="212F01FF2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/A-5-2-2: a polygon containing another polygon [dim(2){A.A.Int = B.A.Int}, dim(0){A.A.Bdy.CP = B.A.Bdy.V}]</desc>
+  <a>
+    POLYGON(
+      (20 20, 20 180, 220 180, 220 20, 20 20))
+  </a>
+  <b>
+    POLYGON(
+      (160 60, 20 20, 100 140, 160 60))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="212F01FF2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/A-5-2-3: a polygon containing another polygon [dim(2){A.A.Int = B.A.Int}, dim(0){A.A.Bdy.NV = B.A.Bdy.CP}]</desc>
+  <a>
+    POLYGON(
+      (20 20, 20 180, 220 180, 220 20, 20 20))
+  </a>
+  <b>
+    POLYGON(
+      (20 100, 140 160, 160 40, 20 100))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="212F01FF2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/A-5-2-4: a polygon containing another polygon [dim(2){A.A.Int = B.A.Int}, dim(0){A.A.Bdy.NV = B.A.Bdy.V}]</desc>
+  <a>
+    POLYGON(
+      (20 20, 20 180, 220 180, 220 20, 20 20))
+  </a>
+  <b>
+    POLYGON(
+      (160 40, 20 100, 160 160, 160 40))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="212F01FF2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/A-5-2-5: a polygon containing another polygon [dim(2){A.A.Int = B.A.Int}, dim(0){A.A.Bdy.V = B.A.Bdy.CP}]</desc>
+  <a>
+    POLYGON(
+      (20 20, 20 180, 220 180, 220 20, 20 20))
+  </a>
+  <b>
+    POLYGON(
+      (20 180, 180 120, 80 40, 20 180))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="212F01FF2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/A-5-2-6: a polygon containing another polygon [dim(2){A.A.Int = B.A.Int}, dim(0){A.A.Bdy.V = B.A.Bdy.V}]</desc>
+  <a>
+    POLYGON(
+      (20 20, 20 180, 220 180, 220 20, 20 20))
+  </a>
+  <b>
+    POLYGON(
+      (180 120, 100 40, 20 180, 180 120))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="212F01FF2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/A-5-3-1: a polygon containing another polygon [dim(2){A.A.Int = B.A.Int}, dim(0){A.A.Bdy.CP = B.A.Bdy.CP}, dim(0){A.A.Bdy.NV = B.A.Bdy.V}]</desc>
+  <a>
+    POLYGON(
+      (20 20, 20 180, 220 180, 220 20, 20 20))
+  </a>
+  <b>
+    POLYGON(
+      (20 20, 140 40, 140 120, 20 160, 80 80, 20 20))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="212F01FF2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/A-5-3-2: a polygon containing another polygon [dim(2){A.A.Int = B.A.Int}, dim(0){A.A.Bdy.CP = B.A.Bdy.CP}, dim(0){A.A.Bdy.V = B.A.Bdy.V}]</desc>
+  <a>
+    POLYGON(
+      (20 20, 20 180, 220 180, 220 20, 20 20))
+  </a>
+  <b>
+    POLYGON(
+      (20 20, 140 40, 140 140, 20 180, 80 100, 20 20))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="212F01FF2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/A-5-3-3: a polygon containing another polygon [dim(2){A.A.Int = B.A.Int}, dim(0){A.A.Bdy.NV = B.A.Bdy.V}, dim(0){A.A.Bdy.NV = B.A.Bdy.V}]</desc>
+  <a>
+    POLYGON(
+      (20 20, 20 180, 220 180, 220 20, 20 20))
+  </a>
+  <b>
+    POLYGON(
+      (40 180, 60 100, 180 100, 200 180, 120 120, 40 180))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="212F01FF2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/A-5-3-4: a polygon containing another polygon [dim(2){A.A.Int = B.A.Int}, dim(0){A.A.Bdy.V = B.A.Bdy.CP}, dim(0){A.A.Bdy.V = B.A.Bdy.V}]</desc>
+  <a>
+    POLYGON(
+      (20 20, 20 180, 220 180, 220 20, 20 20))
+  </a>
+  <b>
+    POLYGON(
+      (20 180, 60 80, 180 80, 220 180, 120 120, 20 180))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="212F01FF2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/A-5-3-5: a polygon containing another polygon [dim(2){A.A.Int = B.A.Int}, dim(0){A.A.Bdy.V = B.A.Bdy.V}, dim(0){A.A.Bdy.NV = B.A.Bdy.V}, dim(0){A.A.Bdy.NV = B.A.Bdy.V}]</desc>
+  <a>
+    POLYGON(
+      (20 20, 20 180, 220 180, 220 20, 20 20))
+  </a>
+  <b>
+    POLYGON(
+      (40 60, 20 180, 100 100, 140 180, 160 120, 220 100, 140 40, 40 60))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="212F01FF2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/A-5-3-6: a polygon containing another polygon [dim(2){A.A.Int = B.A.Int}, dim(0){A.A.Bdy.V = B.A.Bdy.V}, dim(0){A.A.Bdy.V = B.A.Bdy.V}]</desc>
+  <a>
+    POLYGON(
+      (20 20, 20 180, 220 180, 220 20, 20 20))
+  </a>
+  <b>
+    POLYGON(
+      (60 100, 180 100, 220 180, 120 140, 20 180, 60 100))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="212F01FF2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/A-5-4-1: a polygon containing another polygon [dim(2){A.A.Int = B.A.Int}, dim(1){A.A.Bdy.SP-NV = B.A.Bdy.SP-V}]</desc>
+  <a>
+    POLYGON(
+      (20 20, 20 180, 220 180, 220 20, 20 20))
+  </a>
+  <b>
+    POLYGON(
+      (20 20, 20 140, 120 120, 120 40, 20 20))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="212F11FF2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/A-5-4-2: a polygon containing another polygon [dim(2){A.A.Int = B.A.Int}, dim(1){A.A.Bdy.SP-V = B.A.Bdy.SP-V)}]</desc>
+  <a>
+    POLYGON(
+      (20 20, 20 180, 220 180, 220 20, 20 20))
+  </a>
+  <b>
+    POLYGON(
+      (20 20, 20 180, 140 140, 140 60, 20 20))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="212F11FF2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/A-5-4-3: a polygon containing another polygon [dim(2){A.A.Int = B.A.Int}, dim(1){A.A.Bdy.SP-NV = B.A.Bdy.V-EP}]</desc>
+  <a>
+    POLYGON(
+      (20 20, 20 180, 220 180, 220 20, 20 20))
+  </a>
+  <b>
+    POLYGON(
+      (20 20, 120 40, 120 120, 20 140, 20 20))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="212F11FF2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/A-5-4-4: a polygon containing another polygon [dim(2){A.A.Int = B.A.Int}, dim(1){A.A.Bdy.SP-NV = B.A.Bdy.V-V}]</desc>
+  <a>
+    POLYGON(
+      (20 20, 20 180, 220 180, 220 20, 20 20))
+  </a>
+  <b>
+    POLYGON(
+      (120 40, 20 20, 20 140, 120 120, 120 40))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="212F11FF2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/A-5-4-5: a polygon containing another polygon [dim(2){A.A.Int = B.A.Int}, dim(1){A.A.Bdy.SP-V = B.A.Bdy.V-EP}]</desc>
+  <a>
+    POLYGON(
+      (20 20, 20 180, 220 180, 220 20, 20 20))
+  </a>
+  <b>
+    POLYGON(
+      (20 20, 140 60, 140 140, 20 180, 20 20))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="212F11FF2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/A-5-4-6: a polygon containing another polygon [dim(2){A.A.Int = B.A.Int}, dim(1){A.A.Bdy.SP-V = B.A.Bdy.V-V}]</desc>
+  <a>
+    POLYGON(
+      (20 20, 20 180, 220 180, 220 20, 20 20))
+  </a>
+  <b>
+    POLYGON(
+      (140 60, 20 20, 20 180, 140 140, 140 60))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="212F11FF2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/A-5-4-7: a polygon containing another polygon [dim(2){A.A.Int = B.A.Int}, dim(1){A.A.Bdy.NV-EP = B.A.Bdy.V-EP}]</desc>
+  <a>
+    POLYGON(
+      (20 20, 20 180, 220 180, 220 20, 20 20))
+  </a>
+  <b>
+    POLYGON(
+      (20 20, 60 120, 140 120, 180 20, 20 20))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="212F11FF2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/A-5-4-8: a polygon containing another polygon [dim(2){A.A.Int = B.A.Int}, dim(1){A.A.Bdy.NV-NV = B.A.Bdy.V-EP}]</desc>
+  <a>
+    POLYGON(
+      (20 20, 20 180, 220 180, 220 20, 20 20))
+  </a>
+  <b>
+    POLYGON(
+      (20 40, 120 40, 120 120, 20 140, 20 40))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="212F11FF2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/A-5-5-1: a polygon containing another polygon [dim(2){A.A.Int = B.A.Int}, dim(1){A.A.Bdy.SP-V = B.A.Bdy.SP-V},  dim(1){A.A.Bdy.(NV, V) = B.A.Bdy.(V, V)}]</desc>
+  <a>
+    POLYGON(
+      (20 20, 20 180, 220 180, 220 20, 20 20))
+  </a>
+  <b>
+    POLYGON(
+      (20 20, 20 180, 60 120, 100 180, 140 120, 220 180, 200 120, 140 60, 20 20))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="212F11FF2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/A-6-1: a polygon overlapping another polygon [dim(2){A.A.Int = B.A.Int}]</desc>
+  <a>
+    POLYGON(
+      (150 150, 330 150, 250 70, 70 70, 150 150))
+  </a>
+  <b>
+    POLYGON(
+      (150 150, 270 150, 140 20, 20 20, 150 150))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="212111212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/A-6-2: a polygon overlapping another polygon [dim(2){A.A.Int = B.A.Int}]</desc>
+  <a>
+    POLYGON(
+      (150 150, 270 150, 330 150, 250 70, 190 70, 70 70, 150 150))
+  </a>
+  <b>
+    POLYGON(
+      (150 150, 270 150, 190 70, 140 20, 20 20, 70 70, 150 150))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="212111212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/A-6-3: spiky polygons overlapping; boundary &lt;-&gt; boundary intersecting at 0 dimension [dim(2){A.A.Int = B.A.Int}]</desc>
+  <a>
+    POLYGON(
+      (20 20, 60 50, 20 40, 60 70, 20 60, 60 90, 20 90, 70 110, 20 130, 
+      80 130, 20 150, 80 160, 20 170, 80 180, 20 200, 80 200, 30 240, 80 220, 50 260, 
+      100 220, 100 260, 120 220, 130 260, 140 220, 150 280, 150 190, 160 280, 170 190, 180 280, 
+      190 190, 200 280, 210 190, 220 280, 230 190, 240 260, 250 230, 260 260, 260 220, 290 270, 
+      290 220, 330 260, 300 210, 340 240, 290 180, 340 210, 290 170, 350 170, 240 150, 350 150, 
+      240 140, 350 130, 240 120, 350 120, 240 110, 350 110, 240 100, 350 100, 240 90, 350 90, 
+      240 80, 350 80, 300 70, 340 60, 290 60, 340 40, 300 50, 340 20, 270 60, 310 20, 
+      250 60, 270 20, 230 60, 240 20, 210 60, 210 20, 190 70, 190 20, 180 90, 170 20, 
+      160 90, 150 20, 140 90, 130 20, 120 90, 110 20, 100 90, 100 20, 90 60, 80 20, 
+      70 40, 20 20))
+  </a>
+  <b>
+    POLYGON(
+      (190 140, 140 130, 200 160, 130 150, 210 170, 130 170, 210 180, 120 190, 220 200, 
+      120 200, 250 210, 120 210, 250 220, 120 220, 250 230, 120 240, 230 240, 120 250, 240 260, 
+      120 260, 240 270, 120 270, 270 290, 120 290, 230 300, 150 310, 250 310, 180 320, 250 320, 
+      200 360, 260 330, 240 360, 280 320, 290 370, 290 320, 320 360, 310 320, 360 360, 310 310, 
+      380 340, 310 290, 390 330, 310 280, 410 310, 310 270, 420 280, 310 260, 430 250, 300 250, 
+      440 240, 300 240, 450 230, 280 220, 440 220, 280 210, 440 210, 300 200, 430 190, 300 190, 
+      440 180, 330 180, 430 150, 320 180, 420 130, 300 180, 410 120, 280 180, 400 110, 280 170, 
+      390 90, 280 160, 400 70, 270 160, 450 30, 260 160, 420 30, 250 160, 390 30, 240 160, 
+      370 30, 230 160, 360 30, 230 150, 330 50, 240 130, 330 30, 230 130, 310 30, 220 130, 
+      280 30, 230 100, 270 40, 220 110, 250 30, 210 130, 240 30, 210 100, 220 40, 200 90, 
+      200 20, 190 100, 180 30, 20 20, 180 40, 20 30, 180 50, 20 50, 180 60, 30 60, 
+      180 70, 20 70, 170 80, 80 80, 170 90, 20 80, 180 100, 40 100, 200 110, 60 110, 
+      200 120, 120 120, 190 140))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="212101212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/A-6-4: spiky polygons overlapping; boundary &lt;-&gt; boundary intersecting at 1 dimension at a few locations [dim(2){A.A.Int = B.A.Int}]</desc>
+  <a>
+    POLYGON(
+      (70 150, 20 160, 110 160, 20 180, 100 200, 20 200, 190 210, 20 210, 160 220, 
+      20 220, 150 230, 60 240, 180 250, 20 260, 170 260, 60 270, 160 270, 100 310, 170 280, 
+      200 260, 180 230, 210 260, 130 330, 230 250, 210 290, 240 250, 230 210, 260 300, 250 230, 
+      270 300, 270 240, 300 340, 280 250, 320 330, 290 250, 340 350, 290 240, 350 360, 270 190, 
+      350 340, 290 200, 350 330, 300 190, 360 320, 310 190, 360 300, 320 200, 360 280, 330 200, 
+      360 260, 340 200, 370 260, 340 180, 390 290, 340 170, 400 260, 350 170, 400 250, 350 160, 
+      410 240, 350 150, 400 170, 350 140, 310 170, 340 140, 270 180, 330 140, 260 170, 310 140, 
+      240 170, 290 140, 200 190, 270 140, 180 190, 260 140, 170 190, 260 130, 170 180, 250 130, 
+      170 170, 240 120, 170 160, 210 120, 170 150, 210 110, 340 130, 230 110, 420 140, 220 100, 
+      410 130, 220 90, 400 120, 220 80, 390 110, 220 70, 420 110, 240 70, 420 100, 260 70, 
+      420 90, 280 70, 430 80, 230 60, 430 60, 270 50, 450 40, 210 50, 370 40, 260 40, 
+      460 30, 160 40, 210 60, 200 110, 190 60, 190 120, 170 50, 180 130, 150 30, 170 130, 
+      140 20, 160 120, 130 20, 160 150, 120 20, 160 170, 110 20, 160 190, 100 20, 150 190, 
+      90 20, 140 180, 80 20, 120 140, 70 20, 120 150, 60 20, 110 150, 50 20, 100 140, 
+      50 30, 90 130, 40 30, 80 120, 30 30, 80 130, 30 40, 80 140, 20 40, 70 140, 
+      40 90, 60 130, 20 90, 60 140, 20 130, 70 150))
+  </a>
+  <b>
+    POLYGON(
+      (190 140, 140 130, 200 160, 130 150, 210 170, 130 170, 210 180, 120 190, 220 200, 
+      120 200, 250 210, 120 210, 250 220, 120 220, 250 230, 120 240, 230 240, 120 250, 240 260, 
+      120 260, 240 270, 120 270, 270 290, 120 290, 230 300, 150 310, 250 310, 180 320, 250 320, 
+      200 360, 260 330, 240 360, 280 320, 290 370, 290 320, 320 360, 310 320, 360 360, 310 310, 
+      380 340, 310 290, 390 330, 310 280, 410 310, 310 270, 420 280, 310 260, 430 250, 300 250, 
+      440 240, 300 240, 450 230, 280 220, 440 220, 280 210, 440 210, 300 200, 430 190, 300 190, 
+      440 180, 330 180, 430 150, 320 180, 420 130, 300 180, 410 120, 280 180, 400 110, 280 170, 
+      390 90, 280 160, 400 70, 270 160, 450 30, 260 160, 420 30, 250 160, 390 30, 240 160, 
+      370 30, 230 160, 360 30, 230 150, 330 50, 240 130, 330 30, 230 130, 310 30, 220 130, 
+      280 30, 230 100, 270 40, 220 110, 250 30, 210 130, 240 30, 210 100, 220 40, 200 90, 
+      200 20, 190 100, 180 30, 20 20, 180 40, 20 30, 180 50, 20 50, 180 60, 30 60, 
+      180 70, 20 70, 170 80, 80 80, 170 90, 20 80, 180 100, 40 100, 200 110, 60 110, 
+      200 120, 120 120, 190 140))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="212111212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/A-6-5: a polygon overlapping another polygon [dim(2){A.A.Int = B.A.Int}, dim(0){A.A.Bdy.CP = B.A.Bdy.CP}, dim(0){A.A.Bdy.V = B.A.Bdy.V}]</desc>
+  <a>
+    POLYGON(
+      (60 160, 220 160, 220 20, 60 20, 60 160))
+  </a>
+  <b>
+    POLYGON(
+      (60 160, 20 200, 260 200, 220 160, 140 80, 60 160))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="212101212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/A-6-6: a polygon overlapping another polygon [dim(2){A.A.Int = B.A.Int}, dim(0){A.A.Bdy.CP = B.A.Bdy.CP}, dim(0){A.A.Bdy.V = B.A.Bdy.NV}]</desc>
+  <a>
+    POLYGON(
+      (60 160, 220 160, 220 20, 60 20, 60 160))
+  </a>
+  <b>
+    POLYGON(
+      (60 160, 20 200, 260 200, 140 80, 60 160))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="212101212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/A-6-7: a polygon overlapping another polygon [dim(2){A.A.Int = B.A.Int}, dim(0){A.A.Bdy.CP = B.A.Bdy.NV}, dim(0){A.A.Bdy.V = B.A.Bdy.NV}]</desc>
+  <a>
+    POLYGON(
+      (60 160, 220 160, 220 20, 60 20, 60 160))
+  </a>
+  <b>
+    POLYGON(
+      (20 200, 140 80, 260 200, 20 200))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="212101212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/A-6-8: a polygon overlapping another polygon [dim(2){A.A.Int = B.A.Int}, dim(0){A.A.Bdy.CP = B.A.Bdy.V}, dim(0){A.A.Bdy.V = B.A.Bdy.V}]</desc>
+  <a>
+    POLYGON(
+      (60 160, 220 160, 220 20, 60 20, 60 160))
+  </a>
+  <b>
+    POLYGON(
+      (20 200, 60 160, 140 80, 220 160, 260 200, 20 200))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="212101212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/A-6-9: a polygon overlapping another polygon [dim(2){A.A.Int = B.A.Int}, dim(0){A.A.Bdy.CP = B.A.Bdy.V}, dim(0){A.A.Bdy.V = B.A.Bdy.NV}]</desc>
+  <a>
+    POLYGON(
+      (60 160, 220 160, 220 20, 60 20, 60 160))
+  </a>
+  <b>
+    POLYGON(
+      (20 200, 60 160, 140 80, 260 200, 20 200))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="212101212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/A-6-10: a polygon overlapping a skinny polygon [dim(2){A.A.Int = B.A.Int}, dim(0){A.A.Bdy.NV = B.A.Bdy.NV}]</desc>
+  <a>
+    POLYGON(
+      (0 0, 0 200, 200 200, 200 0, 0 0))
+  </a>
+  <b>
+    POLYGON(
+      (100 100, 1000000 110, 10000000 100, 100 100))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="212101212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/A-6-11: a polygon overlapping a skinny polygon [dim(2){A.A.Int = B.A.Int}, dim(0){A.A.Bdy.NV = B.A.Bdy.CP}, dim(0){A.A.Bdy.NV = B.A.Bdy.NV}]</desc>
+  <a>
+    POLYGON(
+      (100 0, 100 200, 200 200, 200 0, 100 0))
+  </a>
+  <b>
+    POLYGON(
+      (100 100, 1000000 110, 10000000 100, 100 100))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="212101212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/A-6-12: a polygon overlapping a skinny polygon [dim(2){A.A.Int = B.A.Int},  dim(0){A.A.Bdy.NV = B.A.Bdy.NV}]</desc>
+  <a>
+    POLYGON(
+      (120 0, 120 200, 200 200, 200 0, 120 0))
+  </a>
+  <b>
+    POLYGON(
+      (100 100, 1000000 110, 10000000 100, 100 100))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="212101212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/A-6-13: a polygon overlapping a skinny polygon [dim(2){A.A.Int = B.A.Int}, dim(0){A.A.Bdy.NV = B.A.Bdy.NV}]</desc>
+  <a>
+    POLYGON(
+      (0 0, 0 200, 110 200, 110 0, 0 0))
+  </a>
+  <b>
+    POLYGON(
+      (100 100, 1000000 110, 10000000 100, 100 100))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="212101212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/A-6-14: a polygon overlapping a skinny polygon [dim(2){A.A.Int = B.A.Int}, dim(1){A.A.Bdy.V-EP = B.A.Bdy.NV-EP}, dim(0){A.A.Bdy.CP = B.A.Bdy.CP}, dim(0){A.A.Bdy.NV = B.A.Bdy.NV}]</desc>
+  <a>
+    POLYGON(
+      (100 100, 100 200, 200 200, 200 100, 100 100))
+  </a>
+  <b>
+    POLYGON(
+      (100 100, 2100 110, 2100 100, 100 100))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="212111212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/A-6-15: a polygon overlapping a skinny polygon [dim(2){A.A.Int = B.A.Int}, dim(1){A.A.Bdy.V-EP = B.A.Bdy.NV-EP}, dim(0){A.A.Bdy.CP = B.A.Bdy.CP}, dim(0){A.A.Bdy.NV = B.A.Bdy.NV}]</desc>
+  <a>
+    POLYGON(
+      (100 100, 100 200, 200 200, 200 100, 100 100))
+  </a>
+  <b>
+    POLYGON(
+      (100 100, 2101 110, 2101 100, 100 100))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="212111212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/A-6-16: two skinny polygons overlapping [dim(2){A.A.Int = B.A.Int}, dim(1){A.A.Bdy.V-EP = B.A.Bdy.NV-EP}, dim(0){A.A.Bdy.CP = B.A.Bdy.CP}, dim(0){A.A.Bdy.NV = B.A.Bdy.NV}]</desc>
+  <a>
+    POLYGON(
+      (100 100, 200 200, 200 100, 100 100))
+  </a>
+  <b>
+    POLYGON(
+      (100 100, 2101 110, 2101 100, 100 100))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="212111212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/A-6-17: a polygon overlapping a skinny polygon [dim(2){A.A.Int = B.A.Int}, dim(1){A.A.Bdy.V-EP = B.A.Bdy.NV-EP}, dim(0){A.A.Bdy.CP = B.A.Bdy.CP}, dim(0){A.A.Bdy.NV = B.A.Bdy.NV}]</desc>
+  <a>
+    POLYGON(
+      (100 100, 100 200, 200 200, 200 100, 100 100))
+  </a>
+  <b>
+    POLYGON(
+      (100 100, 1000000 110, 1000000 100, 100 100))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="212111212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/A-6-19: a polygon overlapping a skinny polygon [dim(2){A.A.Int = B.A.Int}, dim(1){A.A.Bdy.V-EP = B.A.Bdy.NV-NV}, dim(0){A.A.Bdy.NV = B.A.Bdy.NV}]</desc>
+  <a>
+    POLYGON(
+      (120 100, 120 200, 200 200, 200 100, 120 100))
+  </a>
+  <b>
+    POLYGON(
+      (100 100, 500 110, 500 100, 100 100))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="212111212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/A-6-20: a polygon overlapping a skinny polygon [dim(2){A.A.Int = B.A.Int}, dim(1){A.A.Bdy.V-EP = B.A.Bdy.NV-NV}, dim(0){A.A.Bdy.NV = B.A.Bdy.NV}]</desc>
+  <a>
+    POLYGON(
+      (120 100, 120 200, 200 200, 200 100, 120 100))
+  </a>
+  <b>
+    POLYGON(
+      (100 100, 501 110, 501 100, 100 100))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="212111212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/A-6-21: a polygon overlapping a skinny polygon [dim(2){A.A.Int = B.A.Int}, dim(1){A.A.Bdy.V-EP = B.A.Bdy.NV-NV}, dim(0){A.A.Bdy.NV = B.A.Bdy.NV}]</desc>
+  <a>
+    POLYGON(
+      (120 100, 130 200, 200 200, 200 100, 120 100))
+  </a>
+  <b>
+    POLYGON(
+      (100 100, 501 110, 501 100, 100 100))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="212111212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/A-6-22: a polygon overlapping a skinny polygon [dim(2){A.A.Int = B.A.Int}, dim(1){A.A.Bdy.V-EP = B.A.Bdy.NV-NV}, dim(0){A.A.Bdy.NV = B.A.Bdy.NV}]</desc>
+  <a>
+    POLYGON(
+      (120 100, 17 200, 200 200, 200 100, 120 100))
+  </a>
+  <b>
+    POLYGON(
+      (100 100, 501 110, 501 100, 100 100))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="212111212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/A-6-23: a polygon overlapping a skinny polygon [dim(2){A.A.Int = B.A.Int}, dim(1){A.A.Bdy.V-EP = B.A.Bdy.NV-NV}, dim(0){A.A.Bdy.NV = B.A.Bdy.NV}]</desc>
+  <a>
+    POLYGON(
+      (120 100, 120 200, 200 200, 200 100, 120 100))
+  </a>
+  <b>
+    POLYGON(
+      (100 100, 1000000 110, 1000000 100, 100 100))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="212111212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/A-6-25: two skinny polygons overlapping [dim(2){A.A.Int = B.A.Int}, dim(0){A.A.Bdy.NV = B.A.Bdy.NV}]</desc>
+  <a>
+    POLYGON(
+      (101 99, 101 1000000, 102 1000000, 101 99))
+  </a>
+  <b>
+    POLYGON(
+      (100 100, 1000000 110, 1000000 100, 100 100))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="212101212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/A-6-26: two skinny polygons overlapping [dim(2){A.A.Int = B.A.Int}, dim(1){A.A.Bdy.V-EP = B.A.Bdy.NV-EP}, dim(0){A.A.Bdy.CP = B.A.Bdy.CP}, dim(0){A.A.Bdy.NV = B.A.Bdy.NV}]</desc>
+  <a>
+    POLYGON(
+      (100 100, 200 101, 200 100, 100 100))
+  </a>
+  <b>
+    POLYGON(
+      (100 100, 2101 110, 2101 100, 100 100))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="212111212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/A-6-26: two polygons overlapping [dim(2){A.A.Int = B.A.Int}, dim(0){A.A.Bdy.NV = B.A.Bdy.NV}]</desc>
+  <a>
+    POLYGON(
+      (16 319, 150 39, 25 302, 160 20, 265 20, 127 317, 16 319))
+  </a>
+  <b>
+    POLYGON(
+      (10 307, 22 307, 153 34, 22 34, 10 307))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="212101212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/Ah-3-1: the closing point of a polygon touching the closing points of another polygon and its hole [dim(0){A.A.Bdy.CP = B.A.oBdy.CP}, dim(0){A.A.Bdy.CP = B.A.iBdy.CP}]</desc>
+  <a>
+    POLYGON(
+      (160 200, 210 70, 120 70, 160 200))
+  </a>
+  <b>
+    POLYGON(
+      (160 200, 310 20, 20 20, 160 200), 
+      (160 200, 260 40, 70 40, 160 200))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF2F01212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/Ah-3-2: the boundary of a polygon touching the inner boundary of another polygon at two spots [dim(2){A.A.Int = B.A.Ext.h}, dim(0){A.A.oBdy.SP = B.A.iBdy.SP}, dim(0){A.A.oBdy.V = B.A.iBdy.V}]</desc>
+  <a>
+    POLYGON(
+      (170 120, 240 100, 260 50, 190 70, 170 120))
+  </a>
+  <b>
+    POLYGON(
+      (150 150, 410 150, 280 20, 20 20, 150 150), 
+      (170 120, 330 120, 260 50, 100 50, 170 120))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF2F01212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/Ah-3-3: the boundary of a polygon touching the inner boundary of another polygon at two spots [dim(2){A.A.Int = B.A.Ext.h}, dim(0){A.A.oBdy.SP = B.A.iBdy.SP}, dim(0){A.A.oBdy.V = B.A.iBdy.V}]</desc>
+  <a>
+    POLYGON(
+      (270 90, 200 50, 150 80, 210 120, 270 90))
+  </a>
+  <b>
+    POLYGON(
+      (150 150, 410 150, 280 20, 20 20, 150 150), 
+      (170 120, 330 120, 260 50, 100 50, 170 120))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF2F01212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/Ah-3-4: the boundary of a polygon touching the inner boundary of another polygon at one spot [dim(2){A.A.Int = B.A.Ext.h}, dim(0){A.A.oBdy.SP = B.A.iBdy.SP}]</desc>
+  <a>
+    POLYGON(
+      (170 120, 260 100, 240 60, 150 80, 170 120))
+  </a>
+  <b>
+    POLYGON(
+      (150 150, 410 150, 280 20, 20 20, 150 150), 
+      (170 120, 330 120, 260 50, 100 50, 170 120))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF2F01212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/Ah-3-5: the boundary of a polygon touching the inner boundary of another polygon at one spot [dim(2){A.A.Int = B.A.Ext.h}, dim(0){A.A.oBdy.SP = B.A.iBdy.NV}]</desc>
+  <a>
+    POLYGON(
+      (220 120, 270 80, 200 60, 160 100, 220 120))
+  </a>
+  <b>
+    POLYGON(
+      (150 150, 410 150, 280 20, 20 20, 150 150), 
+      (170 120, 330 120, 260 50, 100 50, 170 120))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF2F01212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/Ah-3-6: the boundary of a polygon touching the inner boundary of another polygon at one spot [dim(2){A.A.Int = B.A.Ext.h}, dim(0){A.A.oBdy.SP = B.A.iBdy.V}]</desc>
+  <a>
+    POLYGON(
+      (260 50, 180 70, 180 110, 260 90, 260 50))
+  </a>
+  <b>
+    POLYGON(
+      (150 150, 410 150, 280 20, 20 20, 150 150), 
+      (170 120, 330 120, 260 50, 100 50, 170 120))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF2F01212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/Ah-3-7: the boundary of a polygon touching the inner boundary of another polygon at two spots [dim(2){A.A.Int = B.A.Ext.h}, dim(0){A.A.oBdy.V = B.A.iBdy.NV}, dim(0){A.A.oBdy.V = B.A.iBdy.NV}]</desc>
+  <a>
+    POLYGON(
+      (230 110, 290 80, 190 60, 140 90, 230 110))
+  </a>
+  <b>
+    POLYGON(
+      (150 150, 410 150, 280 20, 20 20, 150 150), 
+      (170 120, 330 120, 260 50, 100 50, 170 120))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF2F01212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/Ah-3-8: the boundary of a polygon touching the inner boundary of another polygon [dim(2){A.A.Int = B.A.Ext.h}, dim(1){A.A.oBdy.SP-EP = B.A.iBdy.SP-EP}]</desc>
+  <a>
+    POLYGON(
+      (170 120, 330 120, 260 50, 100 50, 170 120))
+  </a>
+  <b>
+    POLYGON(
+      (150 150, 410 150, 280 20, 20 20, 150 150), 
+      (170 120, 330 120, 260 50, 100 50, 170 120))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF2F1F212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/Ah-3-9: part of the boundary of a polygon touching part of the inner boundary of another polygon [dim(2){A.A.Int = B.A.Ext.h}, dim(1){A.A.oBdy.SP-V = B.A.iBdy.SP-NV}, dim(1){A.A.oBdy.V-EP = B.A.iBdy.NV-EP}]</desc>
+  <a>
+    POLYGON(
+      (170 120, 330 120, 280 70, 120 70, 170 120))
+  </a>
+  <b>
+    POLYGON(
+      (150 150, 410 150, 280 20, 20 20, 150 150), 
+      (170 120, 330 120, 260 50, 100 50, 170 120))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF2F11212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/Ah-3-10: part of the boundary of a polygon touching part of the inner boundary of another polygon [dim(2){A.A.Int = B.A.Ext.h}, dim(1){A.A.oBdy.SP-V = B.A.iBdy.SP-NV}, dim(1){A.A.oBdy.V-EP = B.A.iBdy.NV-EP}]</desc>
+  <a>
+    POLYGON(
+      (170 120, 300 120, 250 70, 120 70, 170 120))
+  </a>
+  <b>
+    POLYGON(
+      (150 150, 410 150, 280 20, 20 20, 150 150), 
+      (170 120, 330 120, 260 50, 100 50, 170 120))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF2F11212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/Ah-3-11: part of the boundary of a polygon touching part of the inner boundary of another polygon [dim(2){A.A.Int = B.A.Ext.h}, dim(1){A.A.oBdy.V-V-V = B.A.iBdy.NV-V-NV}]</desc>
+  <a>
+    POLYGON(
+      (190 100, 310 100, 260 50, 140 50, 190 100))
+  </a>
+  <b>
+    POLYGON(
+      (150 150, 410 150, 280 20, 20 20, 150 150), 
+      (170 120, 330 120, 260 50, 100 50, 170 120))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF2F11212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/Ah-5-1: an entire polygon within another polygon which has a hole [dim(2){A.A.Ext = B.A.Int}, dim(2){A.A.Int = B.A.Int}]</desc>
+  <a>
+    POLYGON(
+      (280 130, 360 130, 270 40, 190 40, 280 130))
+  </a>
+  <b>
+    POLYGON(
+      (150 150, 410 150, 280 20, 20 20, 150 150), 
+      (170 120, 250 120, 180 50, 100 50, 170 120))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="2FF1FF212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/Ah-5-2: an entire polygon within another polygon which has a hole [dim(2){A.A.Int = B.A.Int}, dim(2){A.A.Ext = B.A.Int}]</desc>
+  <a>
+    POLYGON(
+      (220 80, 180 40, 80 40, 170 130, 270 130, 230 90, 300 90, 250 30, 280 30, 
+      390 140, 150 140, 40 30, 230 30, 280 80, 220 80))
+  </a>
+  <b>
+    POLYGON(
+      (150 150, 410 150, 280 20, 20 20, 150 150), 
+      (170 120, 250 120, 180 50, 100 50, 170 120))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="2FF1FF212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/Ah-5-3: polygon A within polygon B, the boundary of A touching the inner boundary of B [dim(2){A.A.Int = B.A.Int}, dim(2){A.A.Ext = B.A.Int}, dim(1){A.A.Bdy.NV-NV = B.A.iBdy.V-V}]</desc>
+  <a>
+    POLYGON(
+      (260 130, 360 130, 280 40, 170 40, 260 130))
+  </a>
+  <b>
+    POLYGON(
+      (150 150, 410 150, 280 20, 20 20, 150 150), 
+      (170 120, 250 120, 180 50, 100 50, 170 120))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="2FF11F212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/Ah-5-4: polygon A within polygon B, the boundary of A touching the inner boundary of B [dim(2){A.A.Int = B.A.Int}, dim(2){A.A.Ext = B.A.Int}, dim(1){A.A.Bdy.V-V = B.A.iBdy.NV-NV}]</desc>
+  <a>
+    POLYGON(
+      (240 110, 340 110, 290 60, 190 60, 240 110))
+  </a>
+  <b>
+    POLYGON(
+      (150 150, 410 150, 280 20, 20 20, 150 150), 
+      (170 120, 250 120, 180 50, 100 50, 170 120))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="2FF11F212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/Ah-5-5: polygon A within polygon B, the boundary of A touching the inner boundary of B [dim(2){A.A.Int = B.A.Int}, dim(2){A.A.Ext = B.A.Int}, dim(1){A.A.Bdy.V-V = B.A.iBdy.V-V}]</desc>
+  <a>
+    POLYGON(
+      (250 120, 350 120, 280 50, 180 50, 250 120))
+  </a>
+  <b>
+    POLYGON(
+      (150 150, 410 150, 280 20, 20 20, 150 150), 
+      (170 120, 250 120, 180 50, 100 50, 170 120))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="2FF11F212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>Ah/Ah-1-1: same polygons (with a hole) [dim(2){A.A.Int = B.A.Int}, dim(1){A.A.oBdy.SP-EP = B.A.oBdy.SP-EP}, dim(1){A.A.iBdy.SP-EP = B.A.iBdy.SP-EP}]</desc>
+  <a>
+    POLYGON(
+      (230 210, 230 20, 20 20, 20 210, 230 210), 
+      (120 180, 50 50, 200 50, 120 180))
+  </a>
+  <b>
+    POLYGON(
+      (230 210, 230 20, 20 20, 20 210, 230 210), 
+      (120 180, 50 50, 200 50, 120 180))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="2FFF1FFF2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A2h/A2h-1-1: same polygons (with two holes) [dim(2){A.A.Int = B.A.Int}, dim(1){A.A.oBdy.SP-EP = B.A.oBdy.SP-EP}, dim(1){A.A.iBdy.SP-EP = B.A.iBdy.SP-EP}]</desc>
+  <a>
+    POLYGON(
+      (230 210, 230 20, 20 20, 20 210, 230 210), 
+      (140 40, 40 40, 40 170, 140 40), 
+      (110 190, 210 190, 210 50, 110 190))
+  </a>
+  <b>
+    POLYGON(
+      (230 210, 230 20, 20 20, 20 210, 230 210), 
+      (140 40, 40 40, 40 170, 140 40), 
+      (110 190, 210 190, 210 50, 110 190))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="2FFF1FFF2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/mA-3-1: a polygon touching multipolygon at two points [dim(2){A.A.Int = B.2A.Ext}, dim(0){A.A.oBdy.CP = B.2A2.oBdy.NV}, dim(0){A.A.oBdy.V = B.2A1.oBdy.NV}]</desc>
+  <a>
+    POLYGON(
+      (280 190, 330 150, 200 110, 150 150, 280 190))
+  </a>
+  <b>
+    MULTIPOLYGON(
+      (
+        (140 110, 260 110, 170 20, 50 20, 140 110)), 
+      (
+        (300 270, 420 270, 340 190, 220 190, 300 270)))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF2F01212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/mA-3-2: a polygon touching multipolygon at two points [dim(2){A.A.Int = B.2A.Ext}, dim(0){A.A.oBdy.V = B.2A1.oBdy.CP}, dim(0){A.A.oBdy.V = B.2A2.oBdy.V}]</desc>
+  <a>
+    POLYGON(
+      (80 190, 220 190, 140 110, 0 110, 80 190))
+  </a>
+  <b>
+    MULTIPOLYGON(
+      (
+        (140 110, 260 110, 170 20, 50 20, 140 110)), 
+      (
+        (300 270, 420 270, 340 190, 220 190, 300 270)))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF2F01212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/mA-3-3: a polygon touching multipolygon at two points [dim(2){A.A.Int = B.2A.Ext}, dim(0){A.A.oBdy.V = B.2A2.oBdy.NV}, dim(0){A.A.oBdy.V = B.2A1.oBdy.NV}]</desc>
+  <a>
+    POLYGON(
+      (330 150, 200 110, 150 150, 280 190, 330 150))
+  </a>
+  <b>
+    MULTIPOLYGON(
+      (
+        (140 110, 260 110, 170 20, 50 20, 140 110)), 
+      (
+        (300 270, 420 270, 340 190, 220 190, 300 270)))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF2F01212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/mA-3-4: a polygon touching multipolygon at one spoint [dim(2){A.A.Int = B.2A.Ext}, dim(0){A.A.oBdy.V = B.2A2.oBdy.NV}]</desc>
+  <a>
+    POLYGON(
+      (290 190, 340 150, 220 120, 170 170, 290 190))
+  </a>
+  <b>
+    MULTIPOLYGON(
+      (
+        (140 110, 260 110, 170 20, 50 20, 140 110)), 
+      (
+        (300 270, 420 270, 340 190, 220 190, 300 270)))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF2F01212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/mA-3-5: a polygon touching multipolygon along boundaries [dim(2){A.A.Int = B.2A.Ext}, dim(1){A.A.oBdy.SP-V = B.2A2.oBdy.V-V}, dim(1){A.A.oBdy.V-V = B.2A1.oBdy.V-SP}]</desc>
+  <a>
+    POLYGON(
+      (220 190, 340 190, 260 110, 140 110, 220 190))
+  </a>
+  <b>
+    MULTIPOLYGON(
+      (
+        (140 110, 260 110, 170 20, 50 20, 140 110)), 
+      (
+        (300 270, 420 270, 340 190, 220 190, 300 270)))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF2F11212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/mA-3-6: a polygon touching multipolygon along boundaries and at a point [dim(2){A.A.Int = B.2A.Ext}, dim(1){A.A.oBdy.V-NV = B.2A1.oBdy.NV-SP}, dim(0){A.A.oBdy.V = B.2A2.oBdy.V}]</desc>
+  <a>
+    POLYGON(
+      (140 190, 220 190, 100 70, 20 70, 140 190))
+  </a>
+  <b>
+    MULTIPOLYGON(
+      (
+        (140 110, 260 110, 170 20, 50 20, 140 110)), 
+      (
+        (300 270, 420 270, 340 190, 220 190, 300 270)))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF2F11212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>A/mA-6-1: a polygon overlapping multipolygon [dim(2){A.A.Int = B.4A.Int}, dim(0){A.A.Bdy.NV = B.A.Bdy.V}, dim(0){A.A.Bdy.NV = B.A.Bdy.CP}, dim(0){A.A.Bdy.NV = B.A.Bdy.V}, dim(0){A.A.Bdy.NV = B.A.Bdy.CP}]</desc>
+  <a>
+    POLYGON(
+      (140 220, 60 140, 140 60, 220 140, 140 220))
+  </a>
+  <b>
+    MULTIPOLYGON(
+      (
+        (100 20, 180 20, 180 100, 100 100, 100 20)), 
+      (
+        (20 100, 100 100, 100 180, 20 180, 20 100)), 
+      (
+        (100 180, 180 180, 180 260, 100 260, 100 180)), 
+      (
+        (180 100, 260 100, 260 180, 180 180, 180 100)))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="21210F212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>mA/mA-3-1: MultiPolygon touching MultiPolygon [dim(0){A.mA.Bdy.TP = B.mA.Bdy.TP}]</desc>
+  <a>
+    MULTIPOLYGON(
+      (
+        (110 110, 70 200, 150 200, 110 110)), 
+      (
+        (110 110, 150 20, 70 20, 110 110)))
+  </a>
+  <b>
+    MULTIPOLYGON(
+      (
+        (110 110, 160 160, 210 110, 160 60, 110 110)), 
+      (
+        (110 110, 60 60, 10 110, 60 160, 110 110)))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF2F01212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>mAh/mAh-3-1: MultiPolygon touching MultiPolygon [dim(0){A.mA.Bdy.TP = B.mA.Bdy.TP}]</desc>
+  <a>
+    MULTIPOLYGON(
+      (
+        (110 110, 70 200, 150 200, 110 110), 
+        (110 110, 100 180, 120 180, 110 110)), 
+      (
+        (110 110, 150 20, 70 20, 110 110), 
+        (110 110, 120 40, 100 40, 110 110)))
+  </a>
+  <b>
+    MULTIPOLYGON(
+      (
+        (110 110, 160 160, 210 110, 160 60, 110 110), 
+        (110 110, 160 130, 160 90, 110 110)), 
+      (
+        (110 110, 60 60, 10 110, 60 160, 110 110), 
+        (110 110, 60 90, 60 130, 110 110)))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF2F01212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>mAh/mAh-3-2: MultiPolygon touching MultiPolygon [dim(1){A.mA.Bdy.NV-EP = B.mA.Bdy.V-SP}, dim(1){A.mA.Bdy.SP-NV = B.mA.Bdy.EP-V}]</desc>
+  <a>
+    MULTIPOLYGON(
+      (
+        (110 110, 70 200, 200 200, 110 110), 
+        (110 110, 100 180, 120 180, 110 110)), 
+      (
+        (110 110, 200 20, 70 20, 110 110), 
+        (110 110, 120 40, 100 40, 110 110)))
+  </a>
+  <b>
+    MULTIPOLYGON(
+      (
+        (110 110, 160 160, 210 110, 160 60, 110 110), 
+        (110 110, 160 130, 160 90, 110 110)), 
+      (
+        (110 110, 60 60, 10 110, 60 160, 110 110), 
+        (110 110, 60 90, 60 130, 110 110)))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF2F11212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>mAh/mAh-3-3: MultiPolygon touching MultiPolygon [dim(1){A.mA.Bdy.SP-NV = B.mA.Bdy.EP-V}, dim(1){A.mA.Bdy.NV-EP = B.mA.Bdy.V-SP},   dim(1){A.mA.Bdy.NV-EP = B.mA.Bdy.V-SP}, dim(1){A.mA.Bdy.SP-NV = B.mA.Bdy.EP-V}]</desc>
+  <a>
+    MULTIPOLYGON(
+      (
+        (110 110, 20 200, 200 200, 110 110), 
+        (110 110, 100 180, 120 180, 110 110)), 
+      (
+        (110 110, 200 20, 20 20, 110 110), 
+        (110 110, 120 40, 100 40, 110 110)))
+  </a>
+  <b>
+    MULTIPOLYGON(
+      (
+        (110 110, 160 160, 210 110, 160 60, 110 110), 
+        (110 110, 160 130, 160 90, 110 110)), 
+      (
+        (110 110, 60 60, 10 110, 60 160, 110 110), 
+        (110 110, 60 90, 60 130, 110 110)))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF2F11212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>mAh/mAh-6-1: MultiPolygon touching MultiPolygon [dim(2){A.mA.Int = B.mA.Int}]</desc>
+  <a>
+    MULTIPOLYGON(
+      (
+        (110 110, 70 200, 200 200, 110 110), 
+        (110 110, 100 180, 120 180, 110 110)), 
+      (
+        (110 110, 200 20, 70 20, 110 110), 
+        (110 110, 120 40, 100 40, 110 110)))
+  </a>
+  <b>
+    MULTIPOLYGON(
+      (
+        (110 110, 160 160, 210 110, 160 60, 110 110), 
+        (110 110, 160 130, 160 90, 110 110)), 
+      (
+        (110 110, 60 60, 10 110, 60 160, 110 110), 
+        (110 110, 60 90, 60 130, 110 110)))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF2F11212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>mAh/mAh-6-2: MultiPolygon touching MultiPolygon [dim(2){A.mA.Int = B.mA.Int}]</desc>
+  <a>
+    MULTIPOLYGON(
+      (
+        (110 110, 70 200, 200 200, 110 110), 
+        (110 110, 100 180, 120 180, 110 110)), 
+      (
+        (110 110, 200 20, 70 20, 110 110), 
+        (110 110, 120 40, 100 40, 110 110)))
+  </a>
+  <b>
+    MULTIPOLYGON(
+      (
+        (110 110, 70 200, 210 110, 70 20, 110 110), 
+        (110 110, 110 140, 150 110, 110 80, 110 110)), 
+      (
+        (110 110, 60 60, 10 110, 60 160, 110 110), 
+        (110 110, 60 90, 60 130, 110 110)))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="212111212">true</op>
+  </test>
+</case>
+
+</run>

Added: packages/jts/branches/upstream/current/test/validate/TestRelateAC.xml
===================================================================
--- packages/jts/branches/upstream/current/test/validate/TestRelateAC.xml	                        (rev 0)
+++ packages/jts/branches/upstream/current/test/validate/TestRelateAC.xml	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,26 @@
+<run>
+<precisionModel type="FLOATING"/>
+
+<case>
+<desc>AC A-shells overlapping B-shell at A-vertex</desc>
+  <a>
+    POLYGON(
+      (100 60, 140 100, 100 140, 60 100, 100 60))
+  </a>
+  <b>
+    MULTIPOLYGON(
+      (
+        (80 40, 120 40, 120 80, 80 80, 80 40)), 
+      (
+        (120 80, 160 80, 160 120, 120 120, 120 80)), 
+      (
+        (80 120, 120 120, 120 160, 80 160, 80 120)), 
+      (
+        (40 80, 80 80, 80 120, 40 120, 40 80)))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="21210F212">true</op>
+  </test>
+</case>
+
+</run>

Added: packages/jts/branches/upstream/current/test/validate/TestRelateLA.xml
===================================================================
--- packages/jts/branches/upstream/current/test/validate/TestRelateLA.xml	                        (rev 0)
+++ packages/jts/branches/upstream/current/test/validate/TestRelateLA.xml	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,1162 @@
+<run>
+<precisionModel type="FLOATING"/>
+
+<case>
+<desc>L/A-3-1: a line touching the closing point of a polygon [dim(0){A.L.Bdy.SP = B.oBdy.CP}]</desc>
+  <a>
+    LINESTRING(150 150, 40 230)
+  </a>
+  <b>
+    POLYGON(
+      (150 150, 410 150, 280 20, 20 20, 150 150))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF1F00212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/A-3-2: the start and end points of a LineString touching the boundary (at non-vertices) of a polygon [dim(0){A.L.Bdy.SP = B.oBdy.NV}, dim(0){A.L.Bdy.EP = B.oBdy.NV}]</desc>
+  <a>
+    LINESTRING(40 40, 50 130, 130 130)
+  </a>
+  <b>
+    POLYGON(
+      (150 150, 410 150, 280 20, 20 20, 150 150))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF1F0F212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/A-3-3: the end point of a line touching the closing point of a polygon [dim(0){A.L.Bdy.EP = B.oBdy.CP}]</desc>
+  <a>
+    LINESTRING(40 230, 150 150)
+  </a>
+  <b>
+    POLYGON(
+      (150 150, 410 150, 280 20, 20 20, 150 150))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF1F00212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/A-3-4: an entire LineString touching the boundary (at non-vertices) of a polygon [dim(1){A.L.Int.SP-EP = B.oBdy.NV-NV}]</desc>
+  <a>
+    LINESTRING(210 150, 330 150)
+  </a>
+  <b>
+    POLYGON(
+      (150 150, 410 150, 280 20, 20 20, 150 150))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F1FF0F212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/A-3-5: the start portion of a LineString touching the boundary (at non-vertices) of a polygon [dim(1){A.L.Int.SP-V = B.oBdy.NV-NV}]</desc>
+  <a>
+    LINESTRING(200 150, 310 150, 360 220)
+  </a>
+  <b>
+    POLYGON(
+      (150 150, 410 150, 280 20, 20 20, 150 150))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F11F00212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/A-3-6: the start portion and the end point of a LineString touching the boundary of a polygon [dim(1){A.L.Int.SP-V = B.oBdy.NV-NV}, dim(0){A.L.Bdy.EP = B.A.oBdy.V}]</desc>
+  <a>
+    LINESTRING(180 150, 250 150, 230 250, 370 250, 410 150)
+  </a>
+  <b>
+    POLYGON(
+      (150 150, 410 150, 280 20, 20 20, 150 150))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F11F0F212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/A-3-7: the middle portion of a LineString touching the boundary (at non-vertices) of a polygon [dim(1){A.L.Int.V-V = B.oBdy.NV-NV}]</desc>
+  <a>
+    LINESTRING(210 210, 220 150, 320 150, 370 210)
+  </a>
+  <b>
+    POLYGON(
+      (150 150, 410 150, 280 20, 20 20, 150 150))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F11FF0212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/A-4-1: a line at non-vertex crossing non-vertex boundary of polygon [dim(0){A.L.Int.NV = B.A.oBdy.NV}, dim(1){A.L.Int.NV-EP = B.A.Int}]</desc>
+  <a>
+    LINESTRING(20 60, 150 60)
+  </a>
+  <b>
+    POLYGON(
+      (150 150, 410 150, 280 20, 20 20, 150 150))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="1010F0212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/A-4-2: a line at non-vertex crossing non-vertex boundaries of polygon twice [dim(0){A.L.Int.NV = B.A.oBdy.NV}, dim(1){A.L.Int.NV-NV = B.A.Int}]</desc>
+  <a>
+    LINESTRING(60 90, 310 180)
+  </a>
+  <b>
+    POLYGON(
+      (150 150, 410 150, 280 20, 20 20, 150 150))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="101FF0212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/A-4-3: a line at non-vertex crossing vertex boundary of polygon [dim(0){A.L.Int.NV = B.A.oBdy.V}, dim(1){A.L.Int.NV-EP = B.A.Int}]</desc>
+  <a>
+    LINESTRING(90 210, 210 90)
+  </a>
+  <b>
+    POLYGON(
+      (150 150, 410 150, 280 20, 20 20, 150 150))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="1010F0212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/A-4-4: a line at non-vertex crossing vertex boundaries of polygon twice [dim(0){A.L.Int.NV = B.A.oBdy.V}, dim(1){A.L.Int.NV-NV = B.A.Int}, dim(0){A.L.Int.NV = B.A.oBdy.CP}]</desc>
+  <a>
+    LINESTRING(290 10, 130 170)
+  </a>
+  <b>
+    POLYGON(
+      (150 150, 410 150, 280 20, 20 20, 150 150))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="101FF0212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/A-4-5: a line at vertex crossing non-vertex boundary of polygon [dim(0){A.L.Int.V = B.A.oBdy.NV}, dim(1){A.L.Int.V-EP = B.A.Int}]</desc>
+  <a>
+    LINESTRING(30 100, 100 100, 180 100)
+  </a>
+  <b>
+    POLYGON(
+      (150 150, 410 150, 280 20, 20 20, 150 150))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="1010F0212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/A-4-6: a line at vertex crossing non-vertex boundaries of polygon twice [dim(0){A.L.Int.V = B.A.oBdy.NV}, dim(1){A.L.Int.V-V = B.A.Int}]</desc>
+  <a>
+    LINESTRING(20 100, 100 100, 360 100, 410 100)
+  </a>
+  <b>
+    POLYGON(
+      (150 150, 410 150, 280 20, 20 20, 150 150))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="101FF0212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/A-4-7: a line at vertex crossing vertex boundary of polygon [dim(0){A.L.Int.V = B.A.oBdy.V}, dim(1){A.L.Int.V-EP = B.A.Int}]</desc>
+  <a>
+    LINESTRING(90 210, 150 150, 210 90)
+  </a>
+  <b>
+    POLYGON(
+      (150 150, 410 150, 280 20, 20 20, 150 150))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="1010F0212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/A-5-1: an entire line within a polygon [dim(1){A.L.Int.SP-EP = B.A.Int}]</desc>
+  <a>
+    LINESTRING(180 90, 280 120)
+  </a>
+  <b>
+    POLYGON(
+      (150 150, 410 150, 280 20, 20 20, 150 150))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="1FF0FF212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/A-5-2: a line within a polygon but the line's both ends touching the boundary of the polygon [dim(1){A.L.Int.SP-EP = B.A.Int}, dim(0){A.L.Bdy.SP = B.oBdy.NV}, dim(0){A.L.Bdy.EP = B.oBdy.NV}]</desc>
+  <a>
+    LINESTRING(70 70, 80 20)
+  </a>
+  <b>
+    POLYGON(
+      (150 150, 410 150, 280 20, 20 20, 150 150))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="1FFF0F212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/A-5-3: a line within a polygon but the line's start point touching the boundary of the polygon [dim(1){A.L.Int.SP-EP = B.A.Int}, dim(0){A.L.Bdy.SP = B.oBdy.NV}]</desc>
+  <a>
+    LINESTRING(130 20, 150 60)
+  </a>
+  <b>
+    POLYGON(
+      (150 150, 410 150, 280 20, 20 20, 150 150))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="1FF00F212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/A-5-4: a line within a polygon but the line's start point and middle portion touching the boundary of the polygon [dim(1){A.L.Int.SP-V = B.A.Int}, dim(1){A.L.Int.V-V = B.oBdy.NV-NV}, dim(1){A.L.Int.V-EP = B.A.Int}, dim(0){A.L.Bdy.SP = B.A.oBdy.NV}]</desc>
+  <a>
+    LINESTRING(70 70, 80 20, 140 20, 150 60)
+  </a>
+  <b>
+    POLYGON(
+      (150 150, 410 150, 280 20, 20 20, 150 150))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="11F00F212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/A-5-5: a line within a polygon but the line's middle portion touching the boundary of the polygon [dim(1){A.L.Int.SP-V = B.A.Int}, dim(1){A.L.Int.V-V = B.A.oBdy.NV-NV}, dim(1){A.L.Int.V-EP = B.A.Int}]</desc>
+  <a>
+    LINESTRING(170 50, 170 20, 240 20, 260 60)
+  </a>
+  <b>
+    POLYGON(
+      (150 150, 410 150, 280 20, 20 20, 150 150))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="11F0FF212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/Ah-2-1: a line outside a polygon [dim(1){A.L.Int.SP-EP = B.A.Ext}]</desc>
+  <a>
+    LINESTRING(50 100, 140 190, 280 190)
+  </a>
+  <b>
+    POLYGON(
+      (150 150, 410 150, 280 20, 20 20, 150 150), 
+      (170 120, 330 120, 260 50, 100 50, 170 120))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF1FF0212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/Ah-2-2: a line inside a polygon's hole [dim(1){A.L.Int.SP-EP = B.A.Ext.h}]</desc>
+  <a>
+    LINESTRING(140 60, 180 100, 290 100)
+  </a>
+  <b>
+    POLYGON(
+      (150 150, 410 150, 280 20, 20 20, 150 150), 
+      (170 120, 330 120, 260 50, 100 50, 170 120))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF1FF0212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/Ah-3-1: the start point of a line touching the inner boundary of a polygon [dim(0){A.L.Bdy.SP = B.A.iBdy.CP}, dim(1){A.L.Int.SP-EP = B.A.Ext.h}]</desc>
+  <a>
+    LINESTRING(170 120, 210 80, 270 80)
+  </a>
+  <b>
+    POLYGON(
+      (150 150, 410 150, 280 20, 20 20, 150 150), 
+      (170 120, 330 120, 260 50, 100 50, 170 120))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF1F00212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/Ah-3-2: both ends of a line touching the inner boundary of a polygon [dim(0){A.L.Bdy.SP = B.A.iBdy.CP}, dim(1){A.L.Int.SP-EP = B.A.Ext.h}, dim(0){A.L.Bdy.SP = B.A.iBdy.CP}]</desc>
+  <a>
+    LINESTRING(170 120, 260 50)
+  </a>
+  <b>
+    POLYGON(
+      (150 150, 410 150, 280 20, 20 20, 150 150), 
+      (170 120, 330 120, 260 50, 100 50, 170 120))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF1F0F212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/Ah-3-1: both ends of a line touching the inner boundary of a polygon [dim(0){A.L.Int.NV = B.A.Bdy.TP}]</desc>
+  <a>
+    LINESTRING(190 90, 190 270)
+  </a>
+  <b>
+    POLYGON(
+      (190 190, 360 20, 20 20, 190 190), 
+      (190 190, 280 50, 100 50, 190 190))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F01FF0212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/Ah-3-2: a line at a non-vertex crossing the boundary of a polygon where the closing point of the hole touches the shell at a non-vertex [dim(0){A.L.Int.NV = B.A.Bdy.TP}]</desc>
+  <a>
+    LINESTRING(60 160, 150 70)
+  </a>
+  <b>
+    POLYGON(
+      (190 190, 360 20, 20 20, 190 190), 
+      (110 110, 250 100, 140 30, 110 110))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F01FF0212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/Ah-3-3: a line at a non-vertex crossing the boundary of a polygon where the hole at a vertex touches the shell at a non-vertex [dim(0){A.L.Int.NV = B.A.Bdy.TP}]</desc>
+  <a>
+    LINESTRING(60 160, 150 70)
+  </a>
+  <b>
+    POLYGON(
+      (190 190, 20 20, 360 20, 190 190), 
+      (250 100, 110 110, 140 30, 250 100))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F01FF0212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/Ah-3-4: a line at a non-vertex crossing the boundary of a polygon where the hole at a vertex touches the shell at a vertex [dim(0){A.L.Int.NV = B.A.Bdy.TP}]</desc>
+  <a>
+    LINESTRING(60 160, 150 70)
+  </a>
+  <b>
+    POLYGON(
+      (190 190, 20 20, 360 20, 190 190), 
+      (250 100, 110 110, 140 30, 250 100))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F01FF0212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/Ah-3-5: a line crossing polygon boundary where the closing point of the hole touches the shell at a vertex [dim(0){A.L.Int.V = B.A.Bdy.TP}]</desc>
+  <a>
+    LINESTRING(190 90, 190 190, 190 270)
+  </a>
+  <b>
+    POLYGON(
+      (190 190, 360 20, 20 20, 190 190), 
+      (190 190, 280 50, 100 50, 190 190))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F01FF0212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/Ah-3-6: a line at a vertex crossing the boundary of a polygon where closing point of the hole touches the shell at a non-vertex [dim(0){A.L.Int.V = B.A.Bdy.TP}]</desc>
+  <a>
+    LINESTRING(60 160, 110 110, 150 70)
+  </a>
+  <b>
+    POLYGON(
+      (190 190, 360 20, 20 20, 190 190), 
+      (110 110, 250 100, 140 30, 110 110))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F01FF0212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/Ah-3-7: a line at a vertex crossing the boundary of a polygon where the hole at a vertex touches the shell at a non-vertex [dim(0){A.L.Int.V = B.A.Bdy.TP}]</desc>
+  <a>
+    LINESTRING(60 160, 110 110, 150 70)
+  </a>
+  <b>
+    POLYGON(
+      (190 190, 20 20, 360 20, 190 190), 
+      (250 100, 110 110, 140 30, 250 100))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F01FF0212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/Ah-3-8: a line at a non-vertex crossing the boundary of a polygon where the hole at a vertex touches the shell at a vertex [dim(0){A.L.Int.V = B.A.Bdy.TP}]</desc>
+  <a>
+    LINESTRING(60 160, 110 110, 150 70)
+  </a>
+  <b>
+    POLYGON(
+      (190 190, 110 110, 20 20, 360 20, 190 190), 
+      (250 100, 110 110, 140 30, 250 100))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F01FF0212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/A2h-3-1: the start point a line touching the closing points of two connected holes in a polygon [dim(0){A.L.Int.SP = B.A.iBdy.TP}]</desc>
+  <a>
+    LINESTRING(130 110, 180 110, 190 60)
+  </a>
+  <b>
+    POLYGON(
+      (20 200, 240 200, 240 20, 20 20, 20 200), 
+      (130 110, 60 180, 60 40, 130 110), 
+      (130 110, 200 40, 200 180, 130 110))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF1F00212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/A2h-3-2: the interior (at a non-vertex) of a line touching the closing points of two connected holes in a polygon [dim(0){A.L.Int.NV = B.A.iBdy.TP}]</desc>
+  <a>
+    LINESTRING(80 110, 180 110)
+  </a>
+  <b>
+    POLYGON(
+      (20 200, 240 200, 240 20, 20 20, 20 200), 
+      (130 110, 60 180, 60 40, 130 110), 
+      (130 110, 200 40, 200 180, 130 110))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F01FF0212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/A2h-3-3: the interior (at a non-vertex) of a line touching the closing point and at a vertex of two connected holes in a polygon [dim(0){A.L.Int.NV = B.A.iBdy1.TP}]</desc>
+  <a>
+    LINESTRING(80 110, 180 110)
+  </a>
+  <b>
+    POLYGON(
+      (20 200, 20 20, 240 20, 240 200, 20 200), 
+      (60 180, 130 110, 60 40, 60 180), 
+      (130 110, 200 40, 200 180, 130 110))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F01FF0212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/A2h-3-4: the interior (at a non-vertex) of a line touching the closing point and at a non-vertex of two connected holes in a polygon [dim(0){A.L.Int.NV = B.A.iBdy.TP}]</desc>
+  <a>
+    LINESTRING(80 110, 170 110)
+  </a>
+  <b>
+    POLYGON(
+      (20 200, 20 20, 240 20, 240 200, 20 200), 
+      (130 110, 60 40, 60 180, 130 110), 
+      (130 180, 130 40, 200 110, 130 180))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F01FF0212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/A2h-3-5: the start point a line touching the closing point and a non-vertex of two connected holes in a polygon [dim(0){A.L.Int.V = B.A.iBdy.TP}]</desc>
+  <a>
+    LINESTRING(80 110, 130 110, 170 110)
+  </a>
+  <b>
+    POLYGON(
+      (20 200, 20 20, 240 20, 240 200, 20 200), 
+      (130 110, 60 40, 60 180, 130 110), 
+      (130 180, 130 40, 200 110, 130 180))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F01FF0212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/A2h-3-6: the interior (at a vertex) of a line touching the closing points of two connected holes in a polygon [dim(0){A.L.Int.V = B.A.iBdy.TP}]</desc>
+  <a>
+    LINESTRING(80 110, 130 110, 180 110)
+  </a>
+  <b>
+    POLYGON(
+      (20 200, 240 200, 240 20, 20 20, 20 200), 
+      (130 110, 60 180, 60 40, 130 110), 
+      (130 110, 200 40, 200 180, 130 110))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F01FF0212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/A2h-3-7: the interior (at a vertex) of a line touching the closing point and at a vertex of two connected holes in a polygon [dim(0){A.L.Int.V = B.A.iBdy1.TP}]</desc>
+  <a>
+    LINESTRING(80 110, 130 110, 180 110)
+  </a>
+  <b>
+    POLYGON(
+      (20 200, 20 20, 240 20, 240 200, 20 200), 
+      (60 180, 130 110, 60 40, 60 180), 
+      (130 110, 200 40, 200 180, 130 110))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F01FF0212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/A2h-3-8: the interior (at a vertex) of a line touching the closing point and at a non-vertex of two connected holes in a polygon [dim(0){A.L.Int.V = B.A.iBdy.TP}]</desc>
+  <a>
+    LINESTRING(80 110, 130 110, 170 110)
+  </a>
+  <b>
+    POLYGON(
+      (20 200, 20 20, 240 20, 240 200, 20 200), 
+      (130 110, 60 40, 60 180, 130 110), 
+      (130 180, 130 40, 200 110, 130 180))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F01FF0212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/mA-4-1: a line intersecting the interior and exterior of MultiPolygon [dim(1){A.L.Int.SP-NV = B.2A1.Int}, dim (1){A.L.Int.NV-EP = B.2A2.Int}]</desc>
+  <a>
+    LINESTRING(160 70, 320 230)
+  </a>
+  <b>
+    MULTIPOLYGON(
+      (
+        (140 110, 260 110, 170 20, 50 20, 140 110)), 
+      (
+        (300 270, 420 270, 340 190, 220 190, 300 270)))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="1010FF212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/mA-4-2: a line intersecting the interior and exterior of MultiPolygon [dim(1){A.L.Int.SP-V = B.2A1.Int}, dim (1){A.L.Int.V-EP = B.2A2.Int}]</desc>
+  <a>
+    LINESTRING(160 70, 200 110, 280 190, 320 230)
+  </a>
+  <b>
+    MULTIPOLYGON(
+      (
+        (140 110, 260 110, 170 20, 50 20, 140 110)), 
+      (
+        (300 270, 420 270, 340 190, 220 190, 300 270)))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="1010FF212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/mA-5-1: a line within two connected polygons [dim(1){A.L.Int = B.2A.Int}, dim(0){A.L.Int.NV = B.2A.Bdy.TP]</desc>
+  <a>
+    LINESTRING(70 50, 70 150)
+  </a>
+  <b>
+    MULTIPOLYGON(
+      (
+        (0 0, 0 100, 140 100, 140 0, 0 0)), 
+      (
+        (20 170, 70 100, 130 170, 20 170)))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="10F0FF212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>RL/A-3-1: a LinearRing touching a polygon's closing point [dim(0){A.RL.Int.CP = B.A.Bdy.CP}]</desc>
+  <a>
+    LINESTRING(110 110, 20 200, 200 200, 110 110)
+  </a>
+  <b>
+    POLYGON(
+      (20 20, 200 20, 110 110, 20 20))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F01FFF212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>RL/A-3-2: a LinearRing touching a polygon's boundary at a non-vertex [dim(0){A.RL.Int.CP = B.A.Bdy.NV}]</desc>
+  <a>
+    LINESTRING(150 70, 160 110, 200 60, 150 70)
+  </a>
+  <b>
+    POLYGON(
+      (20 20, 200 20, 110 110, 20 20))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F01FFF212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>RL/A-3-3: a LinearRing touching a polygon's boundary at a non-vertex [dim(0){A.RL.Int.CP = B.A.iBdy.NV}]</desc>
+  <a>
+    LINESTRING(80 60, 120 40, 120 70, 80 60)
+  </a>
+  <b>
+    POLYGON(
+      (110 110, 200 20, 20 20, 110 110), 
+      (110 90, 50 30, 170 30, 110 90))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F01FFF212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>RL/A-3-4: a LinearRing on the boundary of a polygon [dim(1){A.RL.Int.SP-EP = B.A.Bdy.SP-EP}]</desc>
+  <a>
+    LINESTRING(20 20, 200 20, 110 110, 20 20)
+  </a>
+  <b>
+    POLYGON(
+      (20 20, 200 20, 110 110, 20 20))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F1FFFF2F2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>RL/A-3-5: a LinearRing on the inner boundary of a polygon [dim(1){A.RL.Int.SP-EP = B.A.iBdy.SP-EP}]</desc>
+  <a>
+    LINESTRING(110 90, 170 30, 50 30, 110 90)
+  </a>
+  <b>
+    POLYGON(
+      (110 110, 200 20, 20 20, 110 110), 
+      (110 90, 50 30, 170 30, 110 90))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F1FFFF212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>RL/A-3-6: a LinearRing on the inner boundary of a polygon [dim(1){A.RL.Int.SP-V = B.A.oBdy.SP-NV}]</desc>
+  <a>
+    LINESTRING(110 110, 170 50, 170 110, 110 110)
+  </a>
+  <b>
+    POLYGON(
+      (110 110, 200 20, 20 20, 110 110), 
+      (110 90, 50 30, 170 30, 110 90))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F11FFF212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>RL/A-3-7: a LinearRing on the inner boundary of a polygon [dim(1){A.RL.Int.SP-V = B.A.iBdy.SP-NV}]</desc>
+  <a>
+    LINESTRING(110 90, 70 50, 130 50, 110 90)
+  </a>
+  <b>
+    POLYGON(
+      (110 110, 200 20, 20 20, 110 110), 
+      (110 90, 50 30, 170 30, 110 90))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F11FFF212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>RL/A-4-1: a LinearRing crossing a polygon [dim(1){A.RL.Int.CP-NV = B.A.Int}, dim(0){A.L.Int.NV = B.A.Bdy.NV}]</desc>
+  <a>
+    LINESTRING(110 60, 20 150, 200 150, 110 60)
+  </a>
+  <b>
+    POLYGON(
+      (20 20, 200 20, 110 110, 20 20))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="101FFF212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>RL/A-4-2: a LinearRing crossing a polygon with a hole [dim(1){A.RL.Int.NV-NV = B.A.Int}, dim(0){A.RL.Int.NV = B.A.oBdy.CP}, dim(0){A.RL.Int.NV = B.A.iBdy.CP}, dim(0){A.RL.Int.NV = B.A.oBdy.NV}, dim(0){A.RL.Int.NV = B.A.iBdy.NV}]</desc>
+  <a>
+    LINESTRING(110 130, 110 70, 200 100, 110 130)
+  </a>
+  <b>
+    POLYGON(
+      (110 110, 200 20, 20 20, 110 110), 
+      (110 90, 50 30, 170 30, 110 90))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="101FFF212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>RL/A-5-1: a LinearRing within a polygon [dim(1){A.RL.Int.SP-EP = B.A.Int}]</desc>
+  <a>
+    LINESTRING(110 90, 160 40, 60 40, 110 90)
+  </a>
+  <b>
+    POLYGON(
+      (20 20, 200 20, 110 110, 20 20))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="1FFFFF212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>RL/A-5-2: a LinearRing within a polygon with a hole [dim(1){A.RL.Int.SP-EP = B.A.Int}]</desc>
+  <a>
+    LINESTRING(110 100, 40 30, 180 30, 110 100)
+  </a>
+  <b>
+    POLYGON(
+      (110 110, 200 20, 20 20, 110 110), 
+      (110 90, 60 40, 160 40, 110 90))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="1FFFFF212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>RL/A-5-3: a LinearRing within a polygon with a hole [dim(1){A.RL.Int.SP-EP = B.A.Int}, dim(0){A.L.Int.CP = B.A.oBdy.CP}]</desc>
+  <a>
+    LINESTRING(110 110, 180 30, 40 30, 110 110)
+  </a>
+  <b>
+    POLYGON(
+      (110 110, 200 20, 20 20, 110 110), 
+      (110 90, 60 40, 160 40, 110 90))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="10FFFF212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>RL/A-5-4: a LinearRing within a polygon with a hole [dim(1){A.RL.Int.SP-EP = B.A.Int}, dim(0){A.RL.Int.CP = B.A.iBdy.CP}]</desc>
+  <a>
+    LINESTRING(110 90, 180 30, 40 30, 110 90)
+  </a>
+  <b>
+    POLYGON(
+      (110 110, 200 20, 20 20, 110 110), 
+      (110 90, 60 40, 160 40, 110 90))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="10FFFF212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>RL/A-5-5: a LinearRing within a polygon with a hole [dim(1){A.RL.Int.SP-EP = B.A.Int}, dim(1){A.RL.Int.SP-NV = B.A.Bdy.iBdy.SP-V}]</desc>
+  <a>
+    LINESTRING(110 90, 50 30, 180 30, 110 90)
+  </a>
+  <b>
+    POLYGON(
+      (110 110, 200 20, 20 20, 110 110), 
+      (110 90, 60 40, 160 40, 110 90))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="11FFFF212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>nsL/A-3-1: a non-simple LineString touching a polygon [dim(0){A.nsL.Bdy.SP = B.A.Bdy.CP}]</desc>
+  <a>
+    LINESTRING(110 110, 200 200, 200 110, 110 200)
+  </a>
+  <b>
+    POLYGON(
+      (110 110, 200 20, 20 20, 110 110))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF1F00212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>nsL/A-3-2: a non-simple LineString touching a polygon [dim(0){A.nsL.Bdy.SPb = B.A.Bdy.CP}]</desc>
+  <a>
+    LINESTRING(110 110, 200 200, 110 110, 20 200, 20 110, 200 110)
+  </a>
+  <b>
+    POLYGON(
+      (110 110, 200 20, 20 20, 110 110))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF1F00212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>nsL/A-3-3: a non-simple LineString touching a polygon [dim(0){A.nsL.Bdy.SPo = B.A.Bdy.CP}]</desc>
+  <a>
+    LINESTRING(110 110, 20 110, 200 110, 50 110, 110 170)
+  </a>
+  <b>
+    POLYGON(
+      (110 110, 200 20, 20 20, 110 110))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF1F00212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>nsL/A-3-4: a non-simple LineString touching a polygon [dim(0){A.nsL.Bdy.SPx = B.A.Bdy.CP}]</desc>
+  <a>
+    LINESTRING(110 110, 20 200, 110 200, 110 110, 200 200)
+  </a>
+  <b>
+    POLYGON(
+      (110 110, 200 20, 20 20, 110 110))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF1F00212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>nsL/A-3-5: a non-simple LineString touching a polygon [dim(1){A.nsL.Int.SPb-Vo = B.A.Bdy.SP-NV}]</desc>
+  <a>
+    LINESTRING(110 110, 170 50, 20 200, 20 110, 200 110)
+  </a>
+  <b>
+    POLYGON(
+      (110 110, 200 20, 20 20, 110 110))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F11F00212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>nsL/A-4-1: a non-simple LineString crossing a polygon [dim(1){A.nsL.Int.V-V-NV = B.A.Int}, dim(1){A.nsL.SPx-V = B.A.Bdy.SP-NV}]</desc>
+  <a>
+    LINESTRING(110 110, 180 40, 110 40, 110 180)
+  </a>
+  <b>
+    POLYGON(
+      (110 110, 200 20, 20 20, 110 110))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="111F00212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>nsL/A-5-1: a non-simple LineString within a polygon [dim(1){A.nsL.Int.SPx-EP = B.A.Int}]</desc>
+  <a>
+    LINESTRING(110 60, 50 30, 170 30, 90 70)
+  </a>
+  <b>
+    POLYGON(
+      (110 110, 200 20, 20 20, 110 110))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="1FF0FF212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>nsL/A-5-2: a non-simple LineString within a polygon [dim(1){A.nsL.Int.SPx-EP = B.A.Int}, dim(1){A.nsL.Int.SPx-V = B.A.Bdy.SP-NV}]</desc>
+  <a>
+    LINESTRING(110 110, 180 40, 110 40, 110 110, 70 40)
+  </a>
+  <b>
+    POLYGON(
+      (110 110, 200 20, 20 20, 110 110))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="11F00F212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>nsL/Ah: the self-crossing point of a non-simple LineString touching the closing point of the inner boundary of a polygon [dim(0){A.nsL.Int.V = B.A.iBdy.CP}]</desc>
+  <a>
+    LINESTRING(230 70, 170 120, 190 60, 140 60, 170 120, 270 90)
+  </a>
+  <b>
+    POLYGON(
+      (150 150, 410 150, 280 20, 20 20, 150 150), 
+      (170 120, 330 120, 260 50, 100 50, 170 120))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F01FF0212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>mL/A-3-1: MultiLineString touching a polygon's closing point [dim(0){A.mL.Bdy.SPb = B.A.Bdy.CP}]</desc>
+  <a>
+    MULTILINESTRING(
+      (20 110, 200 110), 
+      (200 200, 110 110, 20 210, 110 110))
+  </a>
+  <b>
+    POLYGON(
+      (110 110, 200 20, 20 20, 110 110))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF1F00212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>mL/A-3-2: MultiLineString touching a polygon's closing point [dim(0){A.mL.Bdy.SPo = B.A.Bdy.CP}]</desc>
+  <a>
+    MULTILINESTRING(
+      (20 110, 200 110), 
+      (60 180, 60 110, 160 110, 110 110))
+  </a>
+  <b>
+    POLYGON(
+      (110 110, 200 20, 20 20, 110 110))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF1F00212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>mL/A-3-3: MultiLineString touching a polygon's closing point [dim(0){A.mL.Bdy.SPx = B.A.Bdy.CP}]</desc>
+  <a>
+    MULTILINESTRING(
+      (20 110, 200 110), 
+      (200 200, 110 110, 20 200, 110 200, 110 110))
+  </a>
+  <b>
+    POLYGON(
+      (110 110, 200 20, 20 20, 110 110))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF1F00212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>mL/A-4-1: MultiLineString crossing a polygon [dim(1){A.mL.Int.SP-NVb = B.A.Int}, dim(0){A.mL.Int.NVb = B.A.Bdy.CP}]</desc>
+  <a>
+    MULTILINESTRING(
+      (20 110, 200 110), 
+      (110 50, 110 170, 110 70, 110 150, 200 150))
+  </a>
+  <b>
+    POLYGON(
+      (110 110, 200 20, 20 20, 110 110))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="1010F0212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>mL/A-4-2: MultiLineString crossing a polygon [dim(1){A.mL.Int.SP-NVo = B.A.Int}, dim(0){A.mL.Int.NVo = B.A.Bdy.CP}]</desc>
+  <a>
+    MULTILINESTRING(
+      (20 110, 200 110), 
+      (50 110, 170 110, 110 170, 110 50, 110 170, 110 50))
+  </a>
+  <b>
+    POLYGON(
+      (110 110, 200 20, 20 20, 110 110))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="1010F0212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>mL/A-4-3: MultiLineString crossing a polygon [dim(1){A.mL.Int.SP-NVx = B.A.Int}, dim(0){A.mL.Int.NVx = B.A.Bdy.CP}]</desc>
+  <a>
+    MULTILINESTRING(
+      (20 110, 200 110), 
+      (110 60, 110 160, 200 160))
+  </a>
+  <b>
+    POLYGON(
+      (110 110, 200 20, 20 20, 110 110))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="1010F0212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>mL/A-4-4: MultiLineString crossing a polygon [dim(1){A.mL.Int.Vb-Vb = B.A.Int}, dim(0){A.mL.Int.Vb = B.A.oBdy.CP}, dim(0){A.mL.Int.Vb = B.A.iBdy.CP}]</desc>
+  <a>
+    MULTILINESTRING(
+      (20 110, 200 110), 
+      (110 60, 110 160, 200 160))
+  </a>
+  <b>
+    POLYGON(
+      (110 110, 200 20, 20 20, 110 110))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="1010F0212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>mL/A-5-1: MultiLineString within a polygon [dim(1){A.mL.Int.SP-EP = B.A.Int}]</desc>
+  <a>
+    MULTILINESTRING(
+      (110 100, 40 30, 180 30), 
+      (170 30, 110 90, 50 30))
+  </a>
+  <b>
+    POLYGON(
+      (110 110, 200 20, 20 20, 110 110))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="1FF0FF212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>mL/A-5-2: MultiLineString within a polygon [dim(1){A.mL.Int.SP-EP = B.A.Int}]</desc>
+  <a>
+    MULTILINESTRING(
+      (110 110, 60 40, 70 20, 150 20, 170 40), 
+      (180 30, 40 30, 110 80))
+  </a>
+  <b>
+    POLYGON(
+      (110 110, 200 20, 20 20, 110 110))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="11F00F212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>mL/mA-3-1: MultiLineString within a MultiPolygon [dim(0){A.mL.Bdy.SPb = B.mA.Bdy.TP}]</desc>
+  <a>
+    MULTILINESTRING(
+      (20 110, 200 110, 200 160), 
+      (110 110, 200 110, 200 70, 20 150))
+  </a>
+  <b>
+    MULTIPOLYGON(
+      (
+        (110 110, 20 20, 200 20, 110 110)), 
+      (
+        (110 110, 20 200, 200 200, 110 110)))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF1F00212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>mL/mA-3-2: MultiLineString within a MultiPolygon [dim(0){A.mL.Bdy.SPo = B.mA.Bdy.TP}]</desc>
+  <a>
+    MULTILINESTRING(
+      (20 160, 70 110, 150 110, 200 160), 
+      (110 110, 20 110, 50 80, 70 110, 200 110))
+  </a>
+  <b>
+    MULTIPOLYGON(
+      (
+        (110 110, 20 20, 200 20, 110 110)), 
+      (
+        (110 110, 20 200, 200 200, 110 110)))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF1F00212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>mL/mA-3-3: MultiLineString within a MultiPolygon [dim(0){A.mL.Bdy.SPx = B.mA.Bdy.TP}]</desc>
+  <a>
+    MULTILINESTRING(
+      (20 110, 200 110), 
+      (110 110, 20 170, 20 130, 200 90))
+  </a>
+  <b>
+    MULTIPOLYGON(
+      (
+        (110 110, 20 20, 200 20, 110 110)), 
+      (
+        (110 110, 20 200, 200 200, 110 110)))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF1F00212">true</op>
+  </test>
+</case>
+
+</run>

Added: packages/jts/branches/upstream/current/test/validate/TestRelateLC.xml
===================================================================
--- packages/jts/branches/upstream/current/test/validate/TestRelateLC.xml	                        (rev 0)
+++ packages/jts/branches/upstream/current/test/validate/TestRelateLC.xml	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,71 @@
+<run>
+<precisionModel type="FLOATING"/>
+
+<case>
+<desc>LC - topographically equal with no boundary</desc>
+  <a>
+    LINESTRING(0 0, 0 50, 50 50, 50 0, 0 0)
+  </a>
+  <b>
+    MULTILINESTRING(
+      (0 0, 0 50), 
+      (0 50, 50 50), 
+      (50 50, 50 0), 
+      (50 0, 0 0))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="1FFFFFFF2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>LC - intersection (containment) along mod-2 A-Int line segment</desc>
+  <a>
+    LINESTRING(40 180, 140 180)
+  </a>
+  <b>
+    MULTIPOLYGON(
+      (
+        (20 320, 180 320, 180 180, 20 180, 20 320)), 
+      (
+        (20 180, 20 80, 180 80, 180 180, 20 180)))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="1FF0FF212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>LC - intersection (overlap) along mod-2 A-Int line segment</desc>
+  <a>
+    LINESTRING(40 180, 140 180)
+  </a>
+  <b>
+    MULTIPOLYGON(
+      (
+        (20 320, 180 320, 180 180, 20 180, 20 320)), 
+      (
+        (60 180, 60 80, 180 80, 180 180, 60 180)))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="11F00F212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>LC - equal with boundary intersection</desc>
+  <a>
+    LINESTRING(0 0, 60 0, 60 60, 60 0, 120 0)
+  </a>
+  <b>
+    MULTILINESTRING(
+      (0 0, 60 0), 
+      (60 0, 120 0), 
+      (60 0, 60 60))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="10FF0FFF2">true</op>
+  </test>
+</case>
+
+</run>

Added: packages/jts/branches/upstream/current/test/validate/TestRelateLL.xml
===================================================================
--- packages/jts/branches/upstream/current/test/validate/TestRelateLL.xml	                        (rev 0)
+++ packages/jts/branches/upstream/current/test/validate/TestRelateLL.xml	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,1948 @@
+<run>
+<precisionModel type="FLOATING"/>
+
+<case>
+<desc>L/L.1-3-1: touching at the start points of two lines [dim(0){A.L.Bdy.SP = B.L.Bdy.SP}]</desc>
+  <a>
+    LINESTRING(40 40, 120 120)
+  </a>
+  <b>
+    LINESTRING(40 40, 60 120)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF1F00102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/L.1-3-2: start point of one line touching end point of another line [dim(0){A.L.Bdy.SP = B.L.Bdy.EP}]</desc>
+  <a>
+    LINESTRING(40 40, 120 120)
+  </a>
+  <b>
+    LINESTRING(60 240, 40 40)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF1F00102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/L.1-3-3: start point of a line touching the interior of another line at a non-vertex [dim(0){A.L.Bdy.SP = B.L.Int.NV}]</desc>
+  <a>
+    LINESTRING(40 40, 180 180)
+  </a>
+  <b>
+    LINESTRING(120 120, 20 200)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F01FF0102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/L.1-3-4: touching at the end points of two lines [dim(0){A.L.Bdy.EP = B.L.Bdy.EP}]</desc>
+  <a>
+    LINESTRING(40 40, 120 120)
+  </a>
+  <b>
+    LINESTRING(60 240, 120 120)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF1F00102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/L.1-3-5: end point of a line touching the interior of another line at a non-vertex [dim(0){A.L.Bdy.EP = B.L.Int.NV}]</desc>
+  <a>
+    LINESTRING(40 40, 180 180)
+  </a>
+  <b>
+    LINESTRING(20 180, 140 140)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F01FF0102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/L.1-4-1: two lines crossing at non-vertex [dim(0){A.L.Int.NV = B.L.Int.NV}]</desc>
+  <a>
+    LINESTRING(40 40, 120 120)
+  </a>
+  <b>
+    LINESTRING(40 120, 120 40)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0F1FF0102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/L.1-1-1: equal pointwise [dim(1){A.L.Int.SP-EP = B.L.Int.SP-EP}]</desc>
+  <a>
+    LINESTRING(40 40, 100 100)
+  </a>
+  <b>
+    LINESTRING(40 40, 100 100)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="1FFF0FFF2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/L.1-1-2: equal lines but points in reverse sequence [dim(1){A.L.Int.SP-EP = B.L.Int.EP-SP}]</desc>
+  <a>
+    LINESTRING(40 40, 100 100)
+  </a>
+  <b>
+    LINESTRING(100 100, 40 40)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="1FFF0FFF2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/L.1-2-1: dim(1){A.L.Int.SP-EP = B.L.Ext}</desc>
+  <a>
+    LINESTRING(40 40, 120 120)
+  </a>
+  <b>
+    LINESTRING(40 120, 120 160)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF1FF0102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/L.1-5-1: line A containing line B [dim(1){A.L.Int.SP-EP = B.L.Int.SP-EP}]</desc>
+  <a>
+    LINESTRING(20 20, 180 180)
+  </a>
+  <b>
+    LINESTRING(20 20, 180 180)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="1FFF0FFF2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/L.1-5-2: line B is part of line A [dim(1){A.L.Int.SP-NV) = B.L.Int.SP-EP}]</desc>
+  <a>
+    LINESTRING(20 20, 180 180)
+  </a>
+  <b>
+    LINESTRING(20 20, 110 110)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="101F00FF2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/L.1-5-3: Line B is part of line A (in the middle portion) [dim(1){A.L.Int.NV-NV = B.L.Int.SP-EP}]</desc>
+  <a>
+    LINESTRING(20 20, 180 180)
+  </a>
+  <b>
+    LINESTRING(50 50, 140 140)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="101FF0FF2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/L.1-6-1: start portions of two lines overlapping [dim(1){A.L.Int.SP-NV = B.L.Int.SP-NV]</desc>
+  <a>
+    LINESTRING(180 180, 40 40)
+  </a>
+  <b>
+    LINESTRING(120 120, 260 260)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="1010F0102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/L.1-6-2: end portions of two lines overlapping [dim(1){A.L.Int.NV-EP = B.L.Int.NV-EP]</desc>
+  <a>
+    LINESTRING(40 40, 180 180)
+  </a>
+  <b>
+    LINESTRING(260 260, 120 120)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="1010F0102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/L.1-6-3: end portion of line A overlapping the start portion of line B [dim(1){A.L.Int.NV-EP = B.L.Int.SP-NV]</desc>
+  <a>
+    LINESTRING(40 40, 180 180)
+  </a>
+  <b>
+    LINESTRING(120 120, 260 260)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="1010F0102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/L.2-3-1: two LineStrings touching at start points [dim(0){A.L.Bdy.SP = B.L.Bdy.SP}]</desc>
+  <a>
+    LINESTRING(40 40, 100 100, 200 120, 80 240)
+  </a>
+  <b>
+    LINESTRING(40 40, 20 100, 40 160, 20 200)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF1F00102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/L.2-3-2: start point of LineStrings A touching the end point of LineString B [dim(0){A.L.Bdy.SP = B.L.Bdy.EP}]</desc>
+  <a>
+    LINESTRING(40 40, 100 100, 200 120, 80 240)
+  </a>
+  <b>
+    LINESTRING(20 200, 40 160, 20 100, 40 40)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF1F00102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/L.2-3-3: two LineStrings touching at end points [dim(0){A.L.Bdy.EP = B.L.Bdy.EP}]</desc>
+  <a>
+    LINESTRING(80 240, 200 120, 100 100, 40 40)
+  </a>
+  <b>
+    LINESTRING(20 200, 40 160, 20 100, 40 40)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF1F00102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/L.2-3-4: both the start and end points of LineString A touching the interior of LineString B at two vertices  [dim(0){A.L.Bdy.SP = B.L.Int.V}, dim(0){A.L.Bdy.EP = B.L.Int.V}]</desc>
+  <a>
+    LINESTRING(60 60, 60 230, 140 230, 250 160)
+  </a>
+  <b>
+    LINESTRING(20 20, 60 60, 250 160, 310 230)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF10FF102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/L.2-3-5: both the start and end points of LineString A touching the interior of LineString B at two non-vertices  [dim(0){A.L.Bdy.SP = B.L.Int.NV}, dim(0){A.L.Bdy.EP = B.L.Int.NV}]</desc>
+  <a>
+    LINESTRING(60 60, 60 230, 140 230, 250 160)
+  </a>
+  <b>
+    LINESTRING(20 20, 110 110, 200 110, 320 230)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF10FF102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/L.2-3-6: the start and end points of two LineStrings touching each other [dim(0){A.L.Bdy.SP = B.L.Bdy.SP}, dim(0){A.L.Bdy.EP = B.L.Bdy.EP}]</desc>
+  <a>
+    LINESTRING(60 110, 60 250, 360 210)
+  </a>
+  <b>
+    LINESTRING(60 110, 110 160, 250 160, 310 160, 360 210)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF1F0F1F2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/L.2-3-7: the start and end points of two LineStrings touching each other [dim(0){A.L.Bdy.SP = B.L.Bdy.EP}, dim(0){A.L.Bdy.EP = B.L.Bdy.SP}]</desc>
+  <a>
+    LINESTRING(60 110, 60 250, 360 210)
+  </a>
+  <b>
+    LINESTRING(360 210, 310 160, 110 160, 60 110)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF1F0F1F2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/L.2-3-8: start point of LineString B touching LineString A at a non-vertex [dim(0){A.L.Int.NV = B.L.Bdy.SP}]</desc>
+  <a>
+    LINESTRING(40 40, 100 100, 200 120, 80 240)
+  </a>
+  <b>
+    LINESTRING(160 160, 240 240)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F01FF0102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/L.2-3-9: end point of LineString B touching LineString A at a non-vertex [dim(0){A.L.Int.NV = B.L.Bdy.EP}]</desc>
+  <a>
+    LINESTRING(40 40, 100 100, 200 120, 80 240)
+  </a>
+  <b>
+    LINESTRING(240 240, 160 160)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F01FF0102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/L.2-3-10: both the start and end points of LineString B touching the interior of LineString A at two non-vertices  [dim(0){A.L.Int.NV = B.L.Bdy.SP}, dim(0){A.L.Int.NV = B.L.Bdy.EP}]</desc>
+  <a>
+    LINESTRING(60 60, 60 230, 140 230, 250 160)
+  </a>
+  <b>
+    LINESTRING(60 150, 110 100, 170 100, 110 230)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F01FF01F2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/L.2-3-11: the start point of LineString B touching the interior of LineString A at a non-vertex and the end point of LineString A touching the interior of LineString B at a vertex  [dim(0){A.L.Int.NV = B.L.Bdy.SP}, dim(0){A.L.Bdy.EP = B.L.Int.V}]</desc>
+  <a>
+    LINESTRING(60 60, 60 230, 140 230, 250 160)
+  </a>
+  <b>
+    LINESTRING(60 110, 110 160, 250 160, 310 160, 360 210)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F010F0102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/L.2-3-12: start point of LineString B touching LineString A at a vertex [dim(0){A.L.Int.V = B.L.Bdy.SP}]</desc>
+  <a>
+    LINESTRING(40 40, 100 100, 200 120, 80 240)
+  </a>
+  <b>
+    LINESTRING(200 120, 200 190, 150 240, 200 240)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F01FF0102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/L.2-3-13: end point of LineString B touching LineString A at a vertex [dim(0){A.L.Int.V = B.L.Bdy.EP}]</desc>
+  <a>
+    LINESTRING(40 40, 100 100, 200 120, 80 240)
+  </a>
+  <b>
+    LINESTRING(200 240, 150 240, 200 200, 200 120)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F01FF0102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/L.2-3-14: both the start and end points of LineString B touching the interior of LineString A at two vertices  [dim(0){A.L.Int.V = B.L.Bdy.SP}, dim(0){A.L.Int.V = B.L.Bdy.EP}]</desc>
+  <a>
+    LINESTRING(60 60, 60 230, 140 230, 250 160)
+  </a>
+  <b>
+    LINESTRING(60 230, 80 140, 120 140, 140 230)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F01FF01F2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/L.2-4-1: two LineStrings crossing at two points [dim(0){A.L.Bdy.SP = B.L.Bdy.SP}, dim(0){A.L.Int.V = B.L.Int.V}]</desc>
+  <a>
+    LINESTRING(60 110, 200 110, 250 160, 300 210)
+  </a>
+  <b>
+    LINESTRING(60 110, 110 160, 250 160, 310 160, 360 210)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0F1F00102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/L.2-4-2: two LineStrings crossing at two points [dim(0){A.L.Bdy.SP = B.L.Int.SP}, dim(0){A.L.Int.V = B.L.Int.V}, dim(0){A.L.Bdy.EP = B.L.Int.EP}]</desc>
+  <a>
+    LINESTRING(60 110, 200 110, 250 160, 300 210, 360 210)
+  </a>
+  <b>
+    LINESTRING(60 110, 110 160, 250 160, 310 160, 360 210)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0F1F0F1F2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/L.2-4-3: two LineStrings crossing on one side [dim(0){A.L.Bdy.SP = B.L.Bdy.SP}, dim(0){A.L.Int.V = B.L.Int.V}]</desc>
+  <a>
+    LINESTRING(60 110, 220 110, 250 160, 280 110)
+  </a>
+  <b>
+    LINESTRING(60 110, 110 160, 250 160, 310 160, 360 210)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0F1F00102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/L.2-4-4: two LineStrings crossing on one side [dim(0){A.L.Bdy.SP = B.L.Int.SP}, dim(0){A.L.Int.V = B.L.Int.NV}, dim(0){A.L.Bdy.EP = B.L.Int.EP}]</desc>
+  <a>
+    LINESTRING(60 110, 150 110, 200 160, 250 110, 360 110, 360 210)
+  </a>
+  <b>
+    LINESTRING(60 110, 110 160, 250 160, 310 160, 360 210)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0F1F0F1F2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/L.2-4-5: two LineStrings crossing at two points [dim(0){A.L.Bdy.SP = B.L.Int.NV}, dim(0){A.L.Int.V = B.L.Int.V}]</desc>
+  <a>
+    LINESTRING(130 160, 160 110, 220 110, 250 160, 250 210)
+  </a>
+  <b>
+    LINESTRING(60 110, 110 160, 250 160, 310 160, 360 210)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0F10F0102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/L.2-4-6: two LineStrings crossing at two points [dim(0){A.L.Bdy.SP = B.L.Int.NV}, dim(0){A.L.Int.NV = B.L.Int.NV}]</desc>
+  <a>
+    LINESTRING(130 160, 160 110, 190 110, 230 210)
+  </a>
+  <b>
+    LINESTRING(60 110, 110 160, 250 160, 310 160, 360 210)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0F10F0102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/L.2-4-7: two LineStrings crossing at two points [dim(0){A.L.Bdy.SP = B.L.Int.NV}, dim(0){A.L.Int.V = B.L.Int.NV}, dim(0){A.L.Bdy.SP = B.L.Bdy.EP}]</desc>
+  <a>
+    LINESTRING(130 160, 160 110, 200 110, 230 160, 260 210, 360 210)
+  </a>
+  <b>
+    LINESTRING(60 110, 110 160, 250 160, 310 160, 360 210)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0F100F102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/L.2-4-8: two LineStrings crossing at two points [dim(0){A.L.Bdy.SP = B.L.Int.NV}, dim(0){A.L.Int.V = B.L.Int.NV}, dim(0){A.L.Int.V = B.L.Bdy.EP}]</desc>
+  <a>
+    LINESTRING(130 160, 160 110, 200 110, 230 160, 260 210, 360 210, 380 210)
+  </a>
+  <b>
+    LINESTRING(60 110, 110 160, 250 160, 310 160, 360 210)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0010F0102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/L.2-4-9: two LineStrings crossing at three points [dim(0){A.L.Bdy.SP = B.L.Int.NV}, dim(0){A.L.Int.V = B.L.Int.NV}, dim(0){A.L.Int.NV = B.L.Bdy.EP}]</desc>
+  <a>
+    LINESTRING(130 160, 160 110, 200 110, 230 160, 260 210, 380 210)
+  </a>
+  <b>
+    LINESTRING(60 110, 110 160, 250 160, 310 160, 360 210)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0010F0102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/L.2-4-10: two LineStrings crossing at two points [dim(0){A.L.Bdy.SP = B.L.Int.V}, dim(0){A.L.Int.V = B.L.Int.V}]</desc>
+  <a>
+    LINESTRING(110 160, 160 110, 200 110, 250 160, 250 210)
+  </a>
+  <b>
+    LINESTRING(60 110, 110 160, 250 160, 310 160, 360 210)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0F10F0102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/L.2-4-11: two LineStrings crossing on one side [dim(0){A.L.Bdy.SP = B.L.Int.V}, dim(0){A.L.Int.V = B.L.Int.V}]</desc>
+  <a>
+    LINESTRING(110 160, 180 110, 250 160, 320 110)
+  </a>
+  <b>
+    LINESTRING(60 110, 110 160, 250 160, 310 160, 360 210)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0F10F0102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/L.2-4-12: two LineStrings crossing on one side [dim(0){A.L.Bdy.SP = B.L.Int.NV}, dim(0){A.L.Int.V = B.L.Int.NV}]</desc>
+  <a>
+    LINESTRING(140 160, 180 80, 220 160, 250 80)
+  </a>
+  <b>
+    LINESTRING(60 110, 110 160, 250 160, 310 160, 360 210)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0F10F0102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/L.2-4-13: two LineStrings crossing at a vertex for one of the LineStrings [dim(0){A.L.Int.V = B.L.Int.NV}]</desc>
+  <a>
+    LINESTRING(40 40, 100 100, 200 120, 130 190)
+  </a>
+  <b>
+    LINESTRING(20 130, 70 130, 160 40)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0F1FF0102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/L.2-4-14: two LineStrings crossing at non-vertices for both of the LineStrings [dim(0){A.L.Int.NV = B.L.Int.NV}]</desc>
+  <a>
+    LINESTRING(40 40, 100 100, 200 120, 130 190)
+  </a>
+  <b>
+    LINESTRING(40 160, 40 100, 110 40, 170 40)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0F1FF0102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/L.2-4-15: two LineStrings crossing on one side [dim(0){A.L.Int.V = B.L.Int.NV}, dim(0){A.L.Int.V = B.L.Int.NV}]</desc>
+  <a>
+    LINESTRING(130 110, 180 160, 230 110, 280 160, 330 110)
+  </a>
+  <b>
+    LINESTRING(60 110, 110 160, 250 160, 310 160, 360 210)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0F1FF0102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/L.2-4-16: two LineStrings crossing at vertices for both LineString  [dim(0){A.L.Int.V = B.L.Int.V}]</desc>
+  <a>
+    LINESTRING(40 40, 100 100, 200 120, 130 190)
+  </a>
+  <b>
+    LINESTRING(30 140, 80 140, 100 100, 200 30)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0F1FF0102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/L.2-4-17: two LineStrings crossing on one side [dim(0){A.L.Int.V = B.L.Int.V}, dim(0){A.L.Int.V = B.L.Int.V}]</desc>
+  <a>
+    LINESTRING(110 110, 110 160, 180 110, 250 160, 250 110)
+  </a>
+  <b>
+    LINESTRING(60 110, 110 160, 250 160, 310 160, 360 210)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0F1FF0102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/L.2-4-18: multiple crossings [dim(0){A.L.Int.V = B.L.Int.V}, dim(0){A.L.Int.NV = B.L.Int.NV}]</desc>
+  <a>
+    LINESTRING(20 20, 80 80, 160 80, 240 80, 300 140)
+  </a>
+  <b>
+    LINESTRING(20 60, 60 60, 60 140, 80 80, 100 20, 140 140, 180 20, 200 80, 220 20, 
+    240 80, 300 80, 270 110, 200 110)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0F1FF0102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/L.2-4-19: spiky LineStrings with multiple crossing [dim(0){A.L.Int.V = B.L.Int.V}]</desc>
+  <a>
+    LINESTRING(20 20, 230 20, 20 30, 170 30, 20 40, 230 40, 20 50, 230 60, 60 60, 
+    230 70, 20 70, 180 80, 60 80, 230 90, 20 90, 230 100, 30 100, 210 110, 20 110, 
+    80 120, 20 130, 170 130, 90 120, 230 130, 170 140, 230 140, 80 150, 160 140, 20 140, 
+    70 150, 20 150, 230 160, 80 160, 230 170, 20 160, 180 170, 20 170, 230 180, 20 180, 
+    40 190, 230 190, 20 200, 230 200)
+  </a>
+  <b>
+    LINESTRING(30 210, 30 60, 40 210, 40 30, 50 190, 50 20, 60 160, 60 50, 70 220, 
+    70 50, 80 20, 80 210, 90 50, 90 150, 100 30, 100 210, 110 20, 110 190, 120 50, 
+    120 180, 130 210, 120 20, 140 210, 130 50, 150 210, 130 20, 160 210, 140 30, 170 210, 
+    150 20, 180 210, 160 20, 190 210, 180 80, 170 50, 170 20, 180 70, 180 20, 190 190, 
+    190 30, 200 210, 200 30, 210 210, 210 20, 220 150, 220 20)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="001FF0102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/L.2-1-1: two equal LineStrings with equal pointwise [dim(1){A.L.Int.SP-EP = B.L.Int.SP-EP}]</desc>
+  <a>
+    LINESTRING(40 40, 100 100, 200 120, 80 240)
+  </a>
+  <b>
+    LINESTRING(40 40, 100 100, 200 120, 80 240)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="1FFF0FFF2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/L.2-1-2: two equal LineStrings with points in reverse sequence [dim(1){A.L.Int.SP-EP = B.L.Int.EP-SP}]</desc>
+  <a>
+    LINESTRING(40 40, 100 100, 200 120, 80 240)
+  </a>
+  <b>
+    LINESTRING(80 240, 200 120, 100 100, 40 40)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="1FFF0FFF2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/L.2-1-3: two equal LineStrings with different number of points [dim(1){A.L.Int.SP-EP = B.L.Int.EP-SP}]</desc>
+  <a>
+    LINESTRING(40 40, 100 100, 200 120, 80 240)
+  </a>
+  <b>
+    LINESTRING(80 240, 120 200, 200 120, 100 100, 80 80, 40 40)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="1FFF0FFF2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/L.2-2-1: disjoint [dim(1){A.L.Int.SP-EP = B.L.Ext}]</desc>
+  <a>
+    LINESTRING(40 40, 100 100, 200 120, 80 240)
+  </a>
+  <b>
+    LINESTRING(260 210, 240 130, 280 120, 260 40)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF1FF0102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/L.2-2-2: wrapping around but still disjoint [dim(1){A.L.Int.SP-EP = B.L.Ext}]</desc>
+  <a>
+    LINESTRING(100 20, 20 20, 20 160, 210 160, 210 20, 110 20, 50 120, 120 150, 200 150)
+  </a>
+  <b>
+    LINESTRING(140 130, 100 110, 120 60, 170 60)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF1FF0102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/L.2-5-1: LineString A containing LineString B, same pointwise [dim(1){A.L.Int.SP-EP = B.L.Int.SP-EP}]</desc>
+  <a>
+    LINESTRING(60 110, 110 160, 250 160, 310 160, 360 210)
+  </a>
+  <b>
+    LINESTRING(60 110, 110 160, 250 160, 310 160, 360 210)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="1FFF0FFF2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/L.2-5-2: LineString A containing LineString B, LineString A with less points [dim(1){A.L.Int.SP-V = B.L.Int.SP-EP}]</desc>
+  <a>
+    LINESTRING(60 110, 110 160, 310 160, 360 210)
+  </a>
+  <b>
+    LINESTRING(60 110, 110 160, 250 160, 310 160, 360 210)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="1FFF0FFF2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/L.2-5-3: LineString A containing LineString B [dim(1){A.L.Int.SP-V = B.L.Int.SP-EP}]</desc>
+  <a>
+    LINESTRING(60 110, 110 160, 250 160, 310 160, 360 210)
+  </a>
+  <b>
+    LINESTRING(60 110, 110 160, 250 160)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="101F00FF2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/L.2-5-4: LineString A containing LineString B [dim(1){A.L.Int.NV-NV = B.L.Int.SP-EP}]</desc>
+  <a>
+    LINESTRING(60 110, 110 160, 250 160, 310 160, 360 210)
+  </a>
+  <b>
+    LINESTRING(110 160, 310 160, 340 190)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="101FF0FF2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/L.2-5-5: LineString A containing LineString B [dim(1){A.L.Int.V-NV = B.L.Int.SP-EP}]</desc>
+  <a>
+    LINESTRING(60 110, 110 160, 250 160, 310 160, 360 210)
+  </a>
+  <b>
+    LINESTRING(140 160, 250 160, 310 160, 340 190)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="101FF0FF2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/L.2-5-6: LineString A containing LineString B [dim(1){A.L.Int.V-V = B.L.Int.SP-EP}]</desc>
+  <a>
+    LINESTRING(60 110, 110 160, 250 160, 310 160, 360 210)
+  </a>
+  <b>
+    LINESTRING(110 160, 250 160, 310 160)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="101FF0FF2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/L.2-6-1: start portions of two LineStrings overlapping [dim(1){A.L.Int.SP-V = B.L.Int.SP-V}]</desc>
+  <a>
+    LINESTRING(40 40, 100 100, 200 120, 80 240)
+  </a>
+  <b>
+    LINESTRING(200 120, 100 100, 40 40, 140 80, 200 40)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="1010F0102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/L.2-6-2: start portion of LineString A overlapping end portion of LineString B, intersecting at the middle of LineString A [dim(1){A.L.Int.SP-V = B.L.Int.V-EP}]</desc>
+  <a>
+    LINESTRING(40 40, 100 100, 200 120, 80 240)
+  </a>
+  <b>
+    LINESTRING(280 240, 240 140, 200 120, 100 100, 40 40)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="1F1F00102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/L.2-6-3: start portion of LineString A overlapping end portion of LineString B, intersecting at the middle of LineString A [dim(1){A.L.Int.SP-V = B.L.Int.NV-EP}]</desc>
+  <a>
+    LINESTRING(40 40, 100 100, 200 120, 80 240)
+  </a>
+  <b>
+    LINESTRING(80 190, 140 140, 40 40)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="1F1F00102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/L.2-6-4: end portions of two LineStrings overlapping [dim(1){A.L.Int.NV-EP = B.L.Int.V-EP}]</desc>
+  <a>
+    LINESTRING(40 40, 100 100, 200 120, 80 240)
+  </a>
+  <b>
+    LINESTRING(240 200, 200 260, 80 240, 140 180)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="1010F0102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/L.2-6-5: end portion of LineString A overlapping start portion of LineString B [dim(1){A.L.Int.NV-EP = B.L.Int.SP-V}]</desc>
+  <a>
+    LINESTRING(40 40, 100 100, 200 120, 80 240)
+  </a>
+  <b>
+    LINESTRING(140 180, 80 240, 200 260, 240 200)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="1010F0102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/L.2-6-6: end portion of LineString A overlapping end portion of LineString B, intersecting at the middle of LineString A [dim(1){A.L.Int.V-EP = B.L.Int.V-EP}]</desc>
+  <a>
+    LINESTRING(40 40, 100 100, 200 120, 80 240)
+  </a>
+  <b>
+    LINESTRING(280 240, 240 140, 200 120, 80 240)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="1F1F00102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/L.2-6-7: middle portions of two LineStrings overlapping [dim(1){A.L.Int.V-NV = B.L.Int.NV-V}]</desc>
+  <a>
+    LINESTRING(20 20, 80 80, 160 80, 240 80, 300 140)
+  </a>
+  <b>
+    LINESTRING(20 80, 120 80, 200 80, 260 20)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="1F1FF0102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/L.2-6-8: middle portion of LineString A overlapping start portion of LineString B [dim(1){A.L.Int.V-V = B.L.Int.SP-V}]</desc>
+  <a>
+    LINESTRING(40 40, 100 100, 200 120, 80 240)
+  </a>
+  <b>
+    LINESTRING(100 100, 200 120, 240 140, 280 240)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="101FF0102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/L.2-6-9: middle portion of LineString A overlapping end portion of LineString B [dim(1){A.L.Int.V-V = B.L.Int.V-EP}]</desc>
+  <a>
+    LINESTRING(40 40, 100 100, 200 120, 80 240)
+  </a>
+  <b>
+    LINESTRING(280 240, 240 140, 200 120, 100 100)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="101FF0102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/L.2-6-10: middle portions of two LineStrings overlapping [dim(1){A.L.Int.V-V = B.L.Int.V-V}]</desc>
+  <a>
+    LINESTRING(20 20, 80 80, 160 80, 240 80, 300 140)
+  </a>
+  <b>
+    LINESTRING(80 20, 80 80, 240 80, 300 20)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="1F1FF0102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/L.2-6-11: middle portions of two LineStrings overlapping, multiple intersects [dim(1){A.L.Int.V-V = B.L.Int.V-NV}, dim(1){A.L.Int.V-V = B.L.Int.V-NV}, dim(1){A.L.Int.V-V = B.L.Int.V-NV}]</desc>
+  <a>
+    LINESTRING(20 20, 80 80, 160 80, 240 80, 300 140)
+  </a>
+  <b>
+    LINESTRING(20 80, 80 80, 120 80, 140 140, 160 80, 200 80, 220 20, 240 80, 270 110, 
+    300 80)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="1F1FF0102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/LR-3-1: a LineString touching a LinearRing [dim(0){A.L.Bdy.SP = B.LR.Int.CP}]</desc>
+  <a>
+    LINESTRING(100 100, 20 180, 180 180)
+  </a>
+  <b>
+    LINESTRING(100 100, 180 20, 20 20, 100 100)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF10F01F2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/LR-4-1: a LineString crossing a LinearRing [dim(0){A.L.Int.NV = B.LR.Int.CP}]</desc>
+  <a>
+    LINESTRING(20 100, 180 100, 100 180)
+  </a>
+  <b>
+    LINESTRING(100 100, 180 20, 20 20, 100 100)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0F1FF01F2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/LR-4-2: a LineString crossing a LinearRing [dim(0){A.L.Int.NV = B.LR.Int.CP}]</desc>
+  <a>
+    LINESTRING(100 40, 100 160, 180 160)
+  </a>
+  <b>
+    LINESTRING(100 100, 180 20, 20 20, 100 100)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0F1FF01F2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/LR-4-3: a LineString crossing a LinearRing [dim(0){A.L.Int.V = B.LR.Int.CP}]</desc>
+  <a>
+    LINESTRING(20 100, 100 100, 180 100, 100 180)
+  </a>
+  <b>
+    LINESTRING(100 100, 180 20, 20 20, 100 100)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0F1FF01F2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/LR-5-1: a LineString within a LinearRing [dim(1){A.L.Int.SP-EP = B.LR.Int.SP-NV}]</desc>
+  <a>
+    LINESTRING(100 100, 160 40)
+  </a>
+  <b>
+    LINESTRING(100 100, 180 20, 20 20, 100 100)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="1FF0FF1F2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/LR-5-2: a LineString within a LinearRing [dim(1){A.L.Int.SP-EP = B.LR.Int.SP-NV}]</desc>
+  <a>
+    LINESTRING(100 100, 180 20)
+  </a>
+  <b>
+    LINESTRING(100 100, 180 20, 20 20, 100 100)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="1FF0FF1F2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/LR-5-3: a LineString within a LinearRing [dim(1){A.L.Int.SP-V-EP = B.LR.Int.NV-CP-NV}]</desc>
+  <a>
+    LINESTRING(60 60, 100 100, 140 60)
+  </a>
+  <b>
+    LINESTRING(100 100, 180 20, 20 20, 100 100)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="1FF0FF1F2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/LR-6-1: a LineString crossing a LinearRing [dim(1){A.L.Int.SP-NV = B.LR.Int.SP-V}]</desc>
+  <a>
+    LINESTRING(100 100, 190 10, 190 100)
+  </a>
+  <b>
+    LINESTRING(100 100, 180 20, 20 20, 100 100)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="1F10F01F2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/LR-6-2: a LineString crossing a LinearRing [dim(1){A.L.Int.SP-V = B.LR.Int.SP-NV}]</desc>
+  <a>
+    LINESTRING(100 100, 160 40, 160 100)
+  </a>
+  <b>
+    LINESTRING(100 100, 180 20, 20 20, 100 100)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="1F10F01F2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/LR-6-3: a LineString crossing a LinearRing [dim(1){A.L.Int.NV-V = B.LR.Int.SP-NV}]</desc>
+  <a>
+    LINESTRING(60 140, 160 40, 160 140)
+  </a>
+  <b>
+    LINESTRING(100 100, 180 20, 20 20, 100 100)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="1F1FF01F2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/nsL: A line's interior at a non-vertex intersecting a non-simple linestring's end point with both crossing and overlapping line segments [dim(0){A.L.Int.NV = B.nsL.Bdy.EPb}]</desc>
+  <a>
+    LINESTRING(20 20, 140 140)
+  </a>
+  <b>
+    LINESTRING(80 80, 20 80, 140 80, 80 20, 80 140)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F01FF0102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/nsL: A line's interior at a non-vertex intersecting a non-simple linestring's end point with overlapping line segments [dim(0){A.L.Int.NV = B.nsL.Bdy.EPo}]</desc>
+  <a>
+    LINESTRING(20 20, 140 140)
+  </a>
+  <b>
+    LINESTRING(80 80, 20 80, 140 80)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F01FF0102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/nsL: A line's interior at a non-vertex intersecting a non-simple linestring's end point with crossing line segments [dim(0){A.L.Int.NV = B.nsL.Bdy.EPx}]</desc>
+  <a>
+    LINESTRING(20 20, 140 140)
+  </a>
+  <b>
+    LINESTRING(80 80, 140 80, 80 20, 80 140)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F01FF0102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/nsL: A line's interior at a non-vertex intersecting a non-simple linestring's closing point with both crossing and overlapping line segments [dim(0){A.L.Int.NV = B.nsL.Int.CPb}]</desc>
+  <a>
+    LINESTRING(20 20, 140 140)
+  </a>
+  <b>
+    LINESTRING(80 80, 20 80, 140 80, 80 20, 80 80)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0F1FF01F2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/nsL: A line's interior at a non-vertex intersecting a non-simple linestring's closing point with overlapping line segments [dim(0){A.L.Int.NV = B.nsL.Int.CPo}]</desc>
+  <a>
+    LINESTRING(20 20, 140 140)
+  </a>
+  <b>
+    LINESTRING(80 80, 20 80, 140 80, 80 80)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0F1FF01F2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/nsL: A line's interior at a non-vertex intersecting a non-simple linestring's closing point with crossing line segments [dim(0){A.L.Int.NV = B.nsL.Int.CPx}]</desc>
+  <a>
+    LINESTRING(20 20, 140 140)
+  </a>
+  <b>
+    LINESTRING(80 80, 20 80, 20 140, 140 20, 80 20, 80 80)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0F1FF01F2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/nsL: A line's interior at a non-vertex intersecting a non-simple linestring's interior at a non-vertex [dim(0){A.L.Int.NV = B.nsL.Int.NV}]</desc>
+  <a>
+    LINESTRING(20 20, 140 140)
+  </a>
+  <b>
+    LINESTRING(20 140, 140 20, 100 20, 100 80)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0F1FF0102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/nsL: A line's interior at a non-vertex intersecting a non-simple linestring's interior at a non-vertex with both crossing and overlapping line segments [dim(0){A.L.Int.NV = B.nsL.Int.NVb}]</desc>
+  <a>
+    LINESTRING(20 20, 140 140)
+  </a>
+  <b>
+    LINESTRING(140 80, 20 80, 120 80, 80 20, 80 140)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0F1FF0102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/nsL: A line's interior at a non-vertex intersecting a non-simple linestring's interior at a non-vertex with overlapping line segments [dim(0){A.L.Int.NV = B.nsL.Int.NVo}]</desc>
+  <a>
+    LINESTRING(20 20, 140 140)
+  </a>
+  <b>
+    LINESTRING(140 80, 20 80, 140 80)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0F1FF01F2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/nsL: A line's interior at a non-vertex intersecting a non-simple linestring's interior at a non-vertex with crossing line segments [dim(0){A.L.Int.NV = B.nsL.Int.NVx}]</desc>
+  <a>
+    LINESTRING(20 20, 140 140)
+  </a>
+  <b>
+    LINESTRING(140 80, 20 80, 80 140, 80 20)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0F1FF0102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/nsL: A line's interior at a non-vertex intersecting a non-simple linestring's interior at a vertex [dim(0){A.L.Int.NV = B.nsL.Int.V}]</desc>
+  <a>
+    LINESTRING(20 20, 140 140)
+  </a>
+  <b>
+    LINESTRING(140 80, 80 80, 20 80, 50 140, 50 60)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0F1FF0102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/nsL: A line's interior at a non-vertex intersecting a non-simple linestring's interior at a vertex with both crossing and overlapping line segments [dim(0){A.L.Int.NV = B.nsL.Int.Vb}]</desc>
+  <a>
+    LINESTRING(20 20, 140 140)
+  </a>
+  <b>
+    LINESTRING(140 80, 20 80, 120 80, 80 20, 80 80, 80 140)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0F1FF0102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/nsL: A line's interior at a non-vertex intersecting a non-simple linestring's interior at a vertex with overlapping line segments [dim(0){A.L.Int.NV = B.nsL.Int.Vo}]</desc>
+  <a>
+    LINESTRING(20 20, 140 140)
+  </a>
+  <b>
+    LINESTRING(140 80, 20 80, 80 80, 140 80)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0F1FF01F2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/nsL: A line's interior at a non-vertex intersecting a non-simple linestring's interior at a vertex with crossing line segments [dim(0){A.L.Int.NV = B.nsL.Int.Vx}]</desc>
+  <a>
+    LINESTRING(20 20, 140 140)
+  </a>
+  <b>
+    LINESTRING(140 80, 20 80, 80 140, 80 80, 80 20)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0F1FF0102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/nsL.1-3-1: start point of a LineString touching the self-intersecting point of a non-simple LineString [dim(0){A.L.Bdy.SP = B.nsL.Bdy.EPx}]</desc>
+  <a>
+    LINESTRING(130 150, 220 150, 220 240)
+  </a>
+  <b>
+    LINESTRING(130 240, 130 150, 220 20, 50 20, 130 150)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF1F00102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/nsL.1-3-2: the interior of a LineString touching the self-intersecting point of a non-simple LineString [dim(0){A.L.Int.V = B.nsL.Bdy.EPx}]</desc>
+  <a>
+    LINESTRING(30 150, 130 150, 250 150)
+  </a>
+  <b>
+    LINESTRING(130 240, 130 150, 220 20, 50 20, 130 150)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F01FF0102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/nsL.1-3-3: the interior of a LineString touching the self-intersecting point of a non-simple LineString [dim(0){A.L.Int.NV = B.nsL.Bdy.EPx}]</desc>
+  <a>
+    LINESTRING(30 150, 250 150)
+  </a>
+  <b>
+    LINESTRING(130 240, 130 150, 220 20, 50 20, 130 150)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F01FF0102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/nsL.1-3-4: the interior of a LineString touching the self-intersecting point of a non-simple LineString [dim(0){A.L.Int.V = B.nsL.Bdy.EPx}]</desc>
+  <a>
+    LINESTRING(30 150, 130 150, 250 150)
+  </a>
+  <b>
+    LINESTRING(130 240, 130 20, 30 20, 130 150)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F01FF0102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/nsL.1-4: a Line crossing a non-simple LineString at non-vertices [dim(0){A.L.Int.NV = B.nsL.Int.NV}]</desc>
+  <a>
+    LINESTRING(30 150, 250 150)
+  </a>
+  <b>
+    LINESTRING(120 240, 120 20, 20 20, 120 170)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0F1FF0102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>nsL.5/L-3-1: switching the geometries for case L/nsL.5-3-1 [dim(0){A.nsL.Bdy.EPx = B.L.Bdy.SP}]</desc>
+  <a>
+    LINESTRING(200 200, 20 20, 200 20, 110 110, 20 200, 110 200, 110 110)
+  </a>
+  <b>
+    LINESTRING(110 110, 200 110)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF1F00102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/nsL.5-3-2: the start point of a line touching the self-intersecting and self-crossing point of a non-simple LineString [dim(0){A.L.Bdy.SP = B.nsL.Bdy.EPx}]</desc>
+  <a>
+    LINESTRING(110 110, 200 110)
+  </a>
+  <b>
+    LINESTRING(200 200, 20 20, 200 20, 110 110, 20 200, 110 200, 110 110)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF1F00102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/nsL.5-3-3: the interior of a line touching the self-intersecting and self-crossing point of a non-simple LineString [dim(0){A.L.Int.NV = B.nsL.Bdy.EPx}]</desc>
+  <a>
+    LINESTRING(20 110, 200 110)
+  </a>
+  <b>
+    LINESTRING(200 200, 20 20, 200 20, 110 110, 20 200, 110 200, 110 110)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F01FF0102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>nsL.5/L-3-4 touches dim(0){A.nsL.Bdy.EPx = B.L.Int.NV}</desc>
+  <a>
+    LINESTRING(200 200, 20 20, 200 20, 110 110, 20 200, 110 200, 110 110)
+  </a>
+  <b>
+    LINESTRING(20 110, 200 110)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF10F0102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/nsL.10-6-1: the middle portion of a line overlapping from the self-intersecting to the self-crossing a non-simple LineString [dim(1){A.L.Int.V-V = B.nsL.Int.EPx-NVx}]</desc>
+  <a>
+    LINESTRING(90 200, 90 130, 110 110, 150 200)
+  </a>
+  <b>
+    LINESTRING(200 200, 20 20, 200 20, 20 200, 20 130, 90 130)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="101FF0102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/nsL.10-6-2: the middle portion of a line overlapping from the self-intersecting to the self-crossing a non-simple LineString [dim(1){A.L.Int.V-V = B.nsL.Int.NVx-EPx}]</desc>
+  <a>
+    LINESTRING(200 110, 110 110, 90 130, 90 200)
+  </a>
+  <b>
+    LINESTRING(200 200, 20 20, 200 20, 20 200, 20 130, 90 130)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="101FF0102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>L/mL-3-1: a line's end point touching a non-vertex with crossing line segments of a MultiLineString [dim(0){A.L.Bdy.SP = B.mL.Int.NVx]</desc>
+  <a>
+    LINESTRING(80 80, 150 80, 210 80)
+  </a>
+  <b>
+    MULTILINESTRING(
+      (20 20, 140 140), 
+      (20 140, 140 20))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF10F0102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>LR/LR-1-1: two equal LinearRings, pointwise [dim(1){A.LR.Int.SP-EP = B.LR.Int.SP-EP}, dim(0){A.LR.Int.CP = B.LR.Int.CP}]</desc>
+  <a>
+    LINESTRING(40 80, 160 200, 260 20, 40 80)
+  </a>
+  <b>
+    LINESTRING(40 80, 160 200, 260 20, 40 80)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="1FFFFFFF2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>LR/LR-1-2: two equal LinearRings with points in reverse sequence [dim(1){A.LR.Int.SP-EP = B.LR.Int.EP-SP}, dim(0){A.LR.Int.CP = B.LR.Int.CP}]</desc>
+  <a>
+    LINESTRING(40 80, 160 200, 260 20, 40 80)
+  </a>
+  <b>
+    LINESTRING(40 80, 260 20, 160 200, 40 80)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="1FFFFFFF2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>LR/LR-1-3: two equal LinearRings with points in different sequence [dim(1){A.LR.Int.SP-EP = B.LR.Int.SP-EP}, dim(0){A.LR.Int.CP = B.LR.Int.V}, dim(0){A.LR.Int.V = B.LR.Int.CP}]</desc>
+  <a>
+    LINESTRING(40 80, 160 200, 260 20, 40 80)
+  </a>
+  <b>
+    LINESTRING(260 20, 40 80, 160 200, 260 20)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="1FFFFFFF2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>LR/LR-1-4: two equal LinearRings with different number of points [dim(1){A.LR.Int.SP-EP = B.LR.Int.SP-EP}, dim(0){A.LR.Int.CP = B.LR.Int.V}, dim(0){A.LR.Int.NV = B.LR.Int.CP}]</desc>
+  <a>
+    LINESTRING(40 80, 160 200, 260 20, 40 80)
+  </a>
+  <b>
+    LINESTRING(100 140, 160 200, 260 20, 40 80, 100 140)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="1FFFFFFF2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>LR/LR-4-1: two LinearRings crossing at closing points [dim(0){A.LR.Int.CP = B.LR.Int.CP}]</desc>
+  <a>
+    LINESTRING(100 100, 180 20, 20 20, 100 100)
+  </a>
+  <b>
+    LINESTRING(100 100, 180 180, 20 180, 100 100)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0F1FFF1F2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>LR/LR-4-2: two LinearRings crossing at two points [dim(0){A.LR.Int.CP = B.LR.Int.CP}, dim(0){A.LR.Int.V = B.LR.Int.V},]</desc>
+  <a>
+    LINESTRING(40 150, 40 40, 150 40, 150 150, 40 150)
+  </a>
+  <b>
+    LINESTRING(40 150, 150 40, 170 20, 170 190, 40 150)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0F1FFF1F2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>LR/LR-4-3: two LinearRings crossing at the closing and a non-vertex [dim(0){A.LR.Int.CP = B.LR.Int.NV}]</desc>
+  <a>
+    LINESTRING(100 100, 180 20, 20 20, 100 100)
+  </a>
+  <b>
+    LINESTRING(180 100, 20 100, 100 180, 180 100)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0F1FFF1F2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>LR/LR-4-4: two LinearRings crossing at the closing and a vertex [dim(0){A.LR.Int.CP = B.LR.Int.V}]</desc>
+  <a>
+    LINESTRING(100 100, 180 20, 20 20, 100 100)
+  </a>
+  <b>
+    LINESTRING(180 180, 100 100, 20 180, 180 180)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0F1FFF1F2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>LR/LR-4-5: two LinearRings crossing at a vertex and a non-vertex [dim(0){A.LR.Int.V = B.LR.Int.NV}]</desc>
+  <a>
+    LINESTRING(20 180, 100 100, 20 20, 20 180)
+  </a>
+  <b>
+    LINESTRING(100 20, 100 180, 180 100, 100 20)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0F1FFF1F2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>LR/LR-4-6: two LinearRings crossing at two points [dim(0){A.LR.Int.V = B.LR.Int.NV}, dim(0){A.LR.Int.V = B.LR.Int.NV},]</desc>
+  <a>
+    LINESTRING(40 150, 40 40, 150 40, 150 150, 40 150)
+  </a>
+  <b>
+    LINESTRING(170 20, 20 170, 170 170, 170 20)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0F1FFF1F2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>LR/LR-6-1: two LinearRings overlapping [dim(1){A.LR.Int.CP-V = B.LR.Int.CP-V}]</desc>
+  <a>
+    LINESTRING(40 150, 40 40, 150 40, 150 150, 40 150)
+  </a>
+  <b>
+    LINESTRING(40 150, 150 150, 90 210, 40 150)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="1F1FFF1F2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>LR/LR-6-2: two LinearRings overlapping [dim(1){A.LR.Int.CP-V = B.LR.Int.NV-NV}]</desc>
+  <a>
+    LINESTRING(40 150, 40 40, 150 40, 150 150, 40 150)
+  </a>
+  <b>
+    LINESTRING(20 150, 170 150, 90 230, 20 150)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="1F1FFF1F2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>LR/LR-6-3: two LinearRings overlapping [dim(1){A.LR.Int.(V-V-V-EP) = B.LR.Int.(NV-V-V-SP)}]</desc>
+  <a>
+    LINESTRING(40 150, 40 40, 150 40, 150 150, 40 150)
+  </a>
+  <b>
+    LINESTRING(40 150, 150 150, 150 40, 20 40, 20 150, 40 150)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="1F1FFF1F2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>LR/nsL-3-1: a LinearRing touching a non-simple LineString [dim(0){A.nsL.Int.CP = B.nsL.Bdy.SPb}]</desc>
+  <a>
+    LINESTRING(110 110, 200 20, 20 20, 110 110)
+  </a>
+  <b>
+    LINESTRING(110 110, 200 200, 110 110, 20 200, 20 110, 200 110)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F01FFF102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>LR/nsL-3-1: a LinearRing touching a non-simple LineString [dim(0){A.nsL.Int.CP = B.nsL.Bdy.SPo}]</desc>
+  <a>
+    LINESTRING(110 110, 200 20, 20 20, 110 110)
+  </a>
+  <b>
+    LINESTRING(110 110, 20 110, 200 110, 50 110, 110 170)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F01FFF102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>LR/nsL-3-1: a LinearRing touching a non-simple LineString [dim(0){A.nsL.Int.CP = B.nsL.Bdy.SPx}]</desc>
+  <a>
+    LINESTRING(110 110, 200 20, 20 20, 110 110)
+  </a>
+  <b>
+    LINESTRING(110 110, 20 200, 110 200, 110 110, 200 200)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F01FFF102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>LR/nsL-6-1: a LinearRing and a non-simple LineString overlapping [dim(1){A.nsL.Int.SP-V = B.nsL.Int.NVx-SP}]</desc>
+  <a>
+    LINESTRING(110 110, 200 20, 20 20, 110 110)
+  </a>
+  <b>
+    LINESTRING(200 20, 20 200, 200 200, 110 110, 110 40)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="101FFF102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>LR/nsL-6-2: a LinearRing and a non-simple LineString overlapping [dim(1){A.nsL.Int.SP-V = B.nsL.Int.NVx-SP}, dim(1){A.nsL.Int.V-EP = B.nsL.Int.EP-NVx}]</desc>
+  <a>
+    LINESTRING(110 110, 200 20, 20 20, 110 110)
+  </a>
+  <b>
+    LINESTRING(200 20, 20 200, 200 200, 20 20)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="101FFF1F2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>nsL/nsL-4-1: non-simple LineStrings crossing at closing points [dim(0){A.nsL.Int.CP = B.nsL.Int.CP}]</desc>
+  <a>
+    LINESTRING(110 110, 20 110, 110 20, 20 20, 110 110)
+  </a>
+  <b>
+    LINESTRING(110 110, 200 200, 110 200, 200 110, 110 110)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0F1FFF1F2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>nsL/nsL-4-2: non-simple LineStrings crossing at two points without vertices [dim(0){A.nsL.Int.NV = B.nsL.Int.NV}]</desc>
+  <a>
+    LINESTRING(20 120, 120 120, 20 20, 120 20, 20 120)
+  </a>
+  <b>
+    LINESTRING(170 100, 70 100, 170 170, 70 170, 170 100)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0F1FFF1F2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>nsL/nsL-4-3: non-simple LineStrings crossing at a point [dim(0){A.nsL.Int.NV = B.nsL.Int.V}]</desc>
+  <a>
+    LINESTRING(20 110, 110 110, 20 20, 110 20, 20 110)
+  </a>
+  <b>
+    LINESTRING(110 160, 70 110, 60 160, 20 130, 110 160)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0F1FFF1F2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>nsL/nsL-4-4: non-simple LineStrings crossing at self-crossing points [dim(0){A.nsL.Int.NVx = B.nsL.Int.NVx}]</desc>
+  <a>
+    LINESTRING(20 200, 200 200, 20 20, 200 20, 20 200)
+  </a>
+  <b>
+    LINESTRING(20 110, 200 110, 200 160, 20 60, 20 110)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0F1FFF1F2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>nsL/nsL-4-5: non-simple LineStrings crossing at vertices [dim(0){A.nsL.Int.V = B.nsL.Int.V}]</desc>
+  <a>
+    LINESTRING(20 110, 110 110, 20 20, 110 20, 20 110)
+  </a>
+  <b>
+    LINESTRING(200 200, 110 110, 200 110, 110 200, 200 200)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0F1FFF1F2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>nsL/nsL-4-6: non-simple LineStrings crossing at two points with vertices [dim(0){A.nsL.Int.V = B.nsL.Int.V}]</desc>
+  <a>
+    LINESTRING(20 120, 120 120, 20 20, 120 20, 20 120)
+  </a>
+  <b>
+    LINESTRING(220 120, 120 20, 220 20, 120 120, 220 120)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0F1FFF1F2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>mL/mL-1: MultiLineString [dim(1){A.mL.Int.SP-EP = B.mL.Int.SP-EP}]</desc>
+  <a>
+    MULTILINESTRING(
+      (70 20, 20 90, 70 170), 
+      (70 170, 120 90, 70 20))
+  </a>
+  <b>
+    MULTILINESTRING(
+      (70 20, 20 90, 70 170), 
+      (70 170, 120 90, 70 20))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="1FFFFFFF2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>mL/mL-1-1: non-simple MultiLineString  [dim(1){A.mL.Int.SP-EP = B.mL.Int.SP-EP}]</desc>
+  <a>
+    MULTILINESTRING(
+      (20 20, 90 20, 170 20), 
+      (90 20, 90 80, 90 140))
+  </a>
+  <b>
+    MULTILINESTRING(
+      (20 20, 90 20, 170 20), 
+      (90 20, 90 80, 90 140))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="1FFF0FFF2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>mL/mL-1-2: equal non-simple MultiLineString with different sequence of lines and points [dim(1){A.mL.Int.SP-EP = B.mL.Int.EP-SP}]</desc>
+  <a>
+    MULTILINESTRING(
+      (20 20, 90 20, 170 20), 
+      (90 20, 90 80, 90 140))
+  </a>
+  <b>
+    MULTILINESTRING(
+      (90 140, 90 60, 90 20), 
+      (170 20, 130 20, 20 20))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="1FFF0FFF2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>mL/mL-3-1: non-simple MultiLineStrings touching at boundaries [dim(0){A.mL.Bdy.SPx = B.mL.Bdy.SPb}]</desc>
+  <a>
+    MULTILINESTRING(
+      (20 20, 90 20, 170 20), 
+      (90 20, 90 80, 90 140))
+  </a>
+  <b>
+    MULTILINESTRING(
+      (90 20, 170 100, 170 140), 
+      (170 60, 90 20, 20 60), 
+      (130 100, 130 60, 90 20, 50 90))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF1F00102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>mL/mL-3-2: non-simple MultiLineStrings touching at boundaries [dim(0){A.mL.Bdy.SPx = B.mL.Bdy.SPo}]</desc>
+  <a>
+    MULTILINESTRING(
+      (20 20, 90 20, 170 20), 
+      (90 20, 90 80, 90 140))
+  </a>
+  <b>
+    MULTILINESTRING(
+      (90 20, 170 100, 170 140), 
+      (130 140, 130 60, 90 20, 20 90, 90 20, 130 60, 170 60))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF1F00102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>mL/mL-3-3: non-simple MultiLineStrings touching at boundaries [dim(0){A.mL.Bdy.SPx = B.mL.Bdy.SPx}]</desc>
+  <a>
+    MULTILINESTRING(
+      (20 20, 90 20, 170 20), 
+      (90 20, 90 80, 90 140))
+  </a>
+  <b>
+    MULTILINESTRING(
+      (90 20, 170 100, 170 140), 
+      (170 60, 90 20, 20 60))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF1F00102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>mL/mL-3-4: non-simple MultiLineStrings touching at boundaries [dim(0){A.mL.Bdy.SPx = B.mL.Bdy.SPx}]</desc>
+  <a>
+    MULTILINESTRING(
+      (20 20, 90 20, 170 20), 
+      (90 20, 90 80, 90 140))
+  </a>
+  <b>
+    MULTILINESTRING(
+      (90 20, 170 100, 170 140), 
+      (170 60, 90 20, 20 60), 
+      (130 100, 90 20))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF10F0102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>mL/mL-3-5: non-simple MultiLineStrings touching at boundaries [dim(0){A.mL.Bdy.SPx = B.mL.Bdy.SPx}]</desc>
+  <a>
+    MULTILINESTRING(
+      (20 20, 90 20, 170 20), 
+      (90 20, 90 80, 90 140))
+  </a>
+  <b>
+    MULTILINESTRING(
+      (90 20, 170 100, 170 140), 
+      (170 60, 90 20, 20 60), 
+      (120 100, 170 100, 90 20))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF10F0102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>mL/mL-3-6: non-simple MultiLineStrings touching at boundaries [dim(0){A.mL.Bdy.SPx = B.mL.Int.SPb}]</desc>
+  <a>
+    MULTILINESTRING(
+      (20 20, 90 20, 170 20), 
+      (90 20, 90 80, 90 140))
+  </a>
+  <b>
+    MULTILINESTRING(
+      (90 20, 170 100, 170 140), 
+      (170 60, 90 20, 20 60), 
+      (120 100, 170 100, 90 20))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF10F0102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>mL/mL-3-7: non-simple MultiLineStrings touching at boundaries [dim(0){A.mL.Bdy.SPx = B.mL.Int.SPo}]</desc>
+  <a>
+    MULTILINESTRING(
+      (20 20, 90 20, 170 20), 
+      (90 20, 90 80, 90 140))
+  </a>
+  <b>
+    MULTILINESTRING(
+      (90 20, 170 100, 170 140), 
+      (130 140, 130 60, 90 20, 20 90, 90 20))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF10F0102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>mL/mL-3-8: non-simple MultiLineStrings touching at boundaries [dim(0){A.mL.Bdy.SPx = B.mL.Int.SPx}]</desc>
+  <a>
+    MULTILINESTRING(
+      (20 20, 90 20, 170 20), 
+      (90 20, 90 80, 90 140))
+  </a>
+  <b>
+    MULTILINESTRING(
+      (90 20, 170 100, 170 140), 
+      (170 60, 90 20, 20 60, 20 140, 90 20))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF10F0102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>mL/mL-4-1: non-simple MultiLineStrings crossing [dim(0){A.mL.Int.Vx = B.mL.Int.Vb}]</desc>
+  <a>
+    MULTILINESTRING(
+      (20 20, 90 90, 20 160), 
+      (90 160, 90 20))
+  </a>
+  <b>
+    MULTILINESTRING(
+      (160 160, 90 90, 160 20), 
+      (160 120, 120 120, 90 90, 160 60))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0F1FF0102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>mL/mL-4-2: non-simple MultiLineStrings crossing [dim(0){A.mL.Int.Vx = B.mL.Int.Vo}]</desc>
+  <a>
+    MULTILINESTRING(
+      (20 20, 90 90, 20 160), 
+      (90 160, 90 20))
+  </a>
+  <b>
+    MULTILINESTRING(
+      (160 160, 90 90, 160 20), 
+      (160 120, 120 120, 90 90, 120 60, 160 60))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0F1FF0102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>mL/mL-4-3: non-simple MultiLineStrings crossing [dim(0){A.mL.Int.Vx = B.mL.Int.Vx}]</desc>
+  <a>
+    MULTILINESTRING(
+      (20 20, 90 90, 20 160), 
+      (90 160, 90 20))
+  </a>
+  <b>
+    MULTILINESTRING(
+      (160 160, 90 90, 160 20), 
+      (160 120, 90 90, 160 60))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0F1FF0102">true</op>
+  </test>
+</case>
+
+</run>
\ No newline at end of file

Added: packages/jts/branches/upstream/current/test/validate/TestRelatePA.xml
===================================================================
--- packages/jts/branches/upstream/current/test/validate/TestRelatePA.xml	                        (rev 0)
+++ packages/jts/branches/upstream/current/test/validate/TestRelatePA.xml	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,622 @@
+<run>
+<precisionModel type="FLOATING"/>
+
+<case>
+<desc>P/A-2-1: a point outside a polygon [dim(0){A.P.Int = B.A.Ext}]</desc>
+  <a>
+    POINT(20 20)
+  </a>
+  <b>
+    POLYGON(
+      (60 120, 60 40, 160 40, 160 120, 60 120))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF0FFF212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/A-2-2: a point outside a converx polygon [dim(0){A.P.Int = B.A.Ext}]</desc>
+  <a>
+    POINT(70 170)
+  </a>
+  <b>
+    POLYGON(
+      (110 230, 80 160, 20 160, 20 20, 200 20, 200 160, 140 160, 110 230))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF0FFF212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/A-2-3: a point outside a concave polygon [dim(0){A.P.Int = B.A.Ext}]</desc>
+  <a>
+    POINT(110 130)
+  </a>
+  <b>
+    POLYGON(
+      (20 160, 80 160, 110 100, 140 160, 200 160, 200 20, 20 20, 20 160))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF0FFF212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/A-2-4: dim(0){A.P.Int = B.A.Ext}</desc>
+  <a>
+    POINT(100 70)
+  </a>
+  <b>
+    POLYGON(
+      (20 150, 100 150, 40 50, 170 50, 110 150, 190 150, 190 20, 20 20, 20 150))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF0FFF212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/A-2-5: a point outside a concave polygon [dim(0){A.P.Int = B.A.Ext}]</desc>
+  <a>
+    POINT(100 70)
+  </a>
+  <b>
+    POLYGON(
+      (20 150, 100 150, 40 50, 160 50, 100 150, 180 150, 180 20, 20 20, 20 150))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF0FFF212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/A-3-1: a point on the closing point of a polygon [dim(0){A.P.Int = B.A.Bdy.CP}]</desc>
+  <a>
+    POINT(60 120)
+  </a>
+  <b>
+    POLYGON(
+      (60 120, 60 40, 160 40, 160 120, 60 120))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F0FFFF212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/A-3-2: a point on the boudary of a polygon at a non-vertex [dim(0){A.P.Int = B.A.Bdy.NV}]</desc>
+  <a>
+    POINT(110 120)
+  </a>
+  <b>
+    POLYGON(
+      (60 120, 60 40, 160 40, 160 120, 60 120))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F0FFFF212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/A-3-3: a point on the boundary of a polygon at a vertex [dim(0){A.P.Int = B.A.Bdy.V]</desc>
+  <a>
+    POINT(160 120)
+  </a>
+  <b>
+    POLYGON(
+      (60 120, 60 40, 160 40, 160 120, 60 120))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F0FFFF212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/A-3-4: a point on the touching point of boudary [dim(0){A.P.Int = B.A.Bdy.TP}]</desc>
+  <a>
+    POINT(100 150)
+  </a>
+  <b>
+    POLYGON(
+      (20 150, 100 150, 40 50, 160 50, 100 150, 180 150, 180 20, 20 20, 20 150))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F0FFFF212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/A-5: a point on the interior of a polygon [dim(0){A.P.Int = B.A.Int}]</desc>
+  <a>
+    POINT(100 80)
+  </a>
+  <b>
+    POLYGON(
+      (60 120, 60 40, 160 40, 160 120, 60 120))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0FFFFF212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/Ah-2-1: a point outside of polygon with a hole [dim(0){A.P.Int = B.A.Ext}]</desc>
+  <a>
+    POINT(60 160)
+  </a>
+  <b>
+    POLYGON(
+      (190 190, 360 20, 20 20, 190 190), 
+      (280 50, 100 50, 190 140, 280 50))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF0FFF212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/Ah-2-2: a point inside the hole of the polygon [dim(0){A.P.Int = B.A.Ext.h}]</desc>
+  <a>
+    POINT(190 90)
+  </a>
+  <b>
+    POLYGON(
+      (190 190, 360 20, 20 20, 190 190), 
+      (280 50, 100 50, 190 140, 280 50))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF0FFF212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/Ah-3-1: a point on the closing point of the outer boundary of a polygon with a hole [dim(0){A.P.Int = B.A.oBdy.CP}]</desc>
+  <a>
+    POINT(190 190)
+  </a>
+  <b>
+    POLYGON(
+      (190 190, 360 20, 20 20, 190 190), 
+      (280 50, 100 50, 190 140, 280 50))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F0FFFF212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/Ah-3-2: a point on the outer boundary of a polygon at a vertex [dim(0){A.P.Int = B.A.oBdy.V}]</desc>
+  <a>
+    POINT(360 20)
+  </a>
+  <b>
+    POLYGON(
+      (190 190, 360 20, 20 20, 190 190), 
+      (280 50, 100 50, 190 140, 280 50))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F0FFFF212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/Ah-3-3: a point on the outer boundary of a polygon at a non-vertex [dim(0){A.P.Int = B.A.oBdy.NV}]</desc>
+  <a>
+    POINT(130 130)
+  </a>
+  <b>
+    POLYGON(
+      (190 190, 360 20, 20 20, 190 190), 
+      (280 50, 100 50, 190 140, 280 50))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F0FFFF212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/Ah-3-4: a point on the closing point of the inner boundary of a polygon [dim(0){A.P.Int = B.A.iBdy.CP}]</desc>
+  <a>
+    POINT(280 50)
+  </a>
+  <b>
+    POLYGON(
+      (190 190, 360 20, 20 20, 190 190), 
+      (280 50, 100 50, 190 140, 280 50))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F0FFFF212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/Ah-3-5: a point on the inner boundary of a polygon at a non-vertex [dim(0){A.P.Int = B.A.iBdy.NV}]</desc>
+  <a>
+    POINT(150 100)
+  </a>
+  <b>
+    POLYGON(
+      (190 190, 360 20, 20 20, 190 190), 
+      (280 50, 100 50, 190 140, 280 50))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F0FFFF212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/Ah-3-6: a point on the inner boundary of a polygon at a vertex [dim(0){A.P.Int = B.A.iBdy.V}]</desc>
+  <a>
+    POINT(100 50)
+  </a>
+  <b>
+    POLYGON(
+      (190 190, 360 20, 20 20, 190 190), 
+      (280 50, 100 50, 190 140, 280 50))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F0FFFF212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/Ah-5: a point inside the interior of a polygon with a hole [dim(0){A.P.Int = B.A.Int}]</desc>
+  <a>
+    POINT(140 120)
+  </a>
+  <b>
+    POLYGON(
+      (190 190, 360 20, 20 20, 190 190), 
+      (280 50, 100 50, 190 140, 280 50))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0FFFFF212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/A2h-3-1: a point on the touching point of two holes in a polygon [dim(0){A.P.Int = B.A.iBdy.TP}]</desc>
+  <a>
+    POINT(190 50)
+  </a>
+  <b>
+    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))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F0FFFF212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/A2h-3-2: a point on the touching point of two holes in a polygon [dim(0){A.P.Int = B.A.iBdy.TP}]</desc>
+  <a>
+    POINT(180 90)
+  </a>
+  <b>
+    POLYGON(
+      (190 190, 360 20, 20 20, 190 190), 
+      (180 140, 180 40, 80 40, 180 140), 
+      (180 90, 210 140, 310 40, 230 40, 180 90))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F0FFFF212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>mP/A-2: 3 points outside a polygon [dim(0){A.2P.Int = B.A.Ext}]</desc>
+  <a>
+    MULTIPOINT(20 80, 110 160, 20 160)
+  </a>
+  <b>
+    POLYGON(
+      (60 120, 60 40, 160 40, 160 120, 60 120))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF0FFF212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>mP/A-3-1: one of 3 points on the closing point of the boundary of a polygon [dim(0){A.3P1.Int = B.A.Bdy.CP}]</desc>
+  <a>
+    MULTIPOINT(20 80, 60 120, 20 160)
+  </a>
+  <b>
+    POLYGON(
+      (60 120, 60 40, 160 40, 160 120, 60 120))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F00FFF212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>mP/A-3-2: one of 3 points on the boundary of a polygon at a non-vertex [dim(0){A.3P3 = B.A.Bdy.NV}]</desc>
+  <a>
+    MULTIPOINT(10 80, 110 170, 110 120)
+  </a>
+  <b>
+    POLYGON(
+      (60 120, 60 40, 160 40, 160 120, 60 120))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F00FFF212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>mP/A-3-3: one of 3 points on the boundary of a polygon at a vertex [dim(0){A.3P1.Int = B.A.Bdy.V}]</desc>
+  <a>
+    MULTIPOINT(10 80, 110 170, 160 120)
+  </a>
+  <b>
+    POLYGON(
+      (60 120, 60 40, 160 40, 160 120, 60 120))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F00FFF212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>mP/A-3-4: 3 of the 5 points on the boundary of a polygon [dim(0){A.5P2.Int = B.A.Bdy.CP}, dim(0){A.5P3.Int = B.A.Bdy.NV}, dim(0){A.5P4.Int = B.A.Bdy.V}]</desc>
+  <a>
+    MULTIPOINT(20 120, 60 120, 110 120, 160 120, 200 120)
+  </a>
+  <b>
+    POLYGON(
+      (60 120, 60 40, 160 40, 160 120, 60 120))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F00FFF212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>mP/A-3-5: all 3 points on the boundary of a polygon [dim(0){A.3P1.Int = B.A.Bdy.CP}, dim(0){A.3P2.Int = B.A.Bdy.NV}, dim(0){A.3P3.Int = B.A.Bdy.V}]</desc>
+  <a>
+    MULTIPOINT(60 120, 110 120, 160 120)
+  </a>
+  <b>
+    POLYGON(
+      (60 120, 60 40, 160 40, 160 120, 60 120))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F0FFFF212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>mP/A-3-6: all 4 points on the boundary of a polygon [dim(0){A.4P = B.A.Bdy}]</desc>
+  <a>
+    MULTIPOINT(60 120, 160 120, 160 40, 60 40)
+  </a>
+  <b>
+    POLYGON(
+      (60 120, 60 40, 160 40, 160 120, 60 120))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F0FFFF212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>mP/A-4-1: 1 point outside a polygon, 1 point on the boundary and 1 point inside [dim(0){A.3P1.Int = B.A.Ext}, dim(0){A.3P2.Int = B.A.Bdy.CP}, dim(0){A.3P3.Int = B.A.Int}]</desc>
+  <a>
+    MULTIPOINT(20 150, 60 120, 110 80)
+  </a>
+  <b>
+    POLYGON(
+      (60 120, 60 40, 160 40, 160 120, 60 120))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="000FFF212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>mP/A-4-2: 1 point outside a polygon, 1 point on the boundary and 1 point inside [dim(0){A.3P1.Int = B.A.Ext}, dim(0){A.3P2.Int = B.A.Bdy.V}, dim(0){A.3P3.Int = B.A.Int}]</desc>
+  <a>
+    MULTIPOINT(110 80, 160 120, 200 160)
+  </a>
+  <b>
+    POLYGON(
+      (60 120, 60 40, 160 40, 160 120, 60 120))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="000FFF212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>mP/A-4-3: 1 point outside a polygon, 1 point on the boundary and 1 point inside [dim(0){A.3P1.Int = B.A.Ext}, dim(0){A.3P2.Int = B.A.Bdy.NV}, dim(0){A.3P3.Int = B.A.Int}]</desc>
+  <a>
+    MULTIPOINT(110 80, 110 120, 110 160)
+  </a>
+  <b>
+    POLYGON(
+      (60 120, 60 40, 160 40, 160 120, 60 120))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="000FFF212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>mP/A-4-4: 1 point outside a polygon, 1 point inside [dim(0){A.2P1.Int = B.A.Ext}, dim(0){A.2P2.Int = B.A.Int}]</desc>
+  <a>
+    MULTIPOINT(110 170, 110 80)
+  </a>
+  <b>
+    POLYGON(
+      (60 120, 60 40, 160 40, 160 120, 60 120))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0F0FFF212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>mP/A-4-5: 1 point outside a polygon, 2 points on the boundary and 1 point inside [dim(0){A.4P1.Int = B.A.Ext}, dim(0){A.4P2.Int = B.A.Bdy.CP}, dim(0){A.4P3.Int = B.A.Bdy.V}, dim(0){A.4P4.Int = B.A.Int}]</desc>
+  <a>
+    MULTIPOINT(60 120, 160 120, 110 80, 110 170)
+  </a>
+  <b>
+    POLYGON(
+      (60 120, 60 40, 160 40, 160 120, 60 120))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="000FFF212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>mP/A-5-1: 2 points within a polygon [dim(0){A.2P.Int = B.A.Int]</desc>
+  <a>
+    MULTIPOINT(90 80, 130 80)
+  </a>
+  <b>
+    POLYGON(
+      (60 120, 60 40, 160 40, 160 120, 60 120))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0FFFFF212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>mP/A-5-2: 1 point on the boundary and 1 point inside a polygon [dim(0){A.2P1.Int = B.A.Bdy.CP}, dim(0){A.2P2.Int = B.A.Int}]</desc>
+  <a>
+    MULTIPOINT(60 120, 160 120, 110 80)
+  </a>
+  <b>
+    POLYGON(
+      (60 120, 60 40, 160 40, 160 120, 60 120))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="00FFFF212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>mP/Ah-2-1: 3 points outside a polygon [dim(0){A.3P.Int = B.Ah.Ext}]</desc>
+  <a>
+    MULTIPOINT(40 170, 40 90, 130 170)
+  </a>
+  <b>
+    POLYGON(
+      (190 190, 360 20, 20 20, 190 190), 
+      (280 50, 100 50, 190 140, 280 50))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF0FFF212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>mP/Ah-2-2: 2 points outside a polygon and 1 point inside the hole of the polygon [dim(0){A.3P1.Int = B.Ah.Ext}, dim(0){A.3P2.Int = B.Ah.Ext}, dim(0){A.3P3.Int = B.Ah.Ext.h}]</desc>
+  <a>
+    MULTIPOINT(90 170, 280 170, 190 90)
+  </a>
+  <b>
+    POLYGON(
+      (190 190, 360 20, 20 20, 190 190), 
+      (280 50, 100 50, 190 140, 280 50))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF0FFF212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>mP/Ah-2-3: all 3 points in polygon's hole [dim(0){A.3P.Int = B.Ah.Ext.h}]</desc>
+  <a>
+    MULTIPOINT(190 110, 150 70, 230 70)
+  </a>
+  <b>
+    POLYGON(
+      (190 190, 360 20, 20 20, 190 190), 
+      (280 50, 100 50, 190 140, 280 50))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF0FFF212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/mA-3-1: a point on the touching point of two polygons [dim(0){A.P.Int = B.2A.Bdy}]</desc>
+  <a>
+    POINT(100 100)
+  </a>
+  <b>
+    MULTIPOLYGON(
+      (
+        (20 100, 20 20, 100 20, 100 100, 20 100)), 
+      (
+        (100 180, 100 100, 180 100, 180 180, 100 180)))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F0FFFF212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/mA-3-2: a point on the boundary of one of the 2 polygons [dim(0){A.P.Int = B.2A1.Bdy.CP}]</desc>
+  <a>
+    POINT(20 100)
+  </a>
+  <b>
+    MULTIPOLYGON(
+      (
+        (20 100, 20 20, 100 20, 100 100, 20 100)), 
+      (
+        (100 180, 100 100, 180 100, 180 180, 100 180)))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F0FFFF212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/mA-3-3: a point on the boundary of one of the 2 polygons [dim(0){A.P.Int = B.2A1.Bdy.V}]</desc>
+  <a>
+    POINT(60 100)
+  </a>
+  <b>
+    MULTIPOLYGON(
+      (
+        (20 100, 20 20, 100 20, 100 100, 20 100)), 
+      (
+        (100 180, 100 100, 180 100, 180 180, 100 180)))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F0FFFF212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/mA-3-4: a point touching a polygon's boundary where the boundaries touch at a point [dim(0){A.P.Int = B.2A.Bdy.TP}]</desc>
+  <a>
+    POINT(110 110)
+  </a>
+  <b>
+    MULTIPOLYGON(
+      (
+        (110 110, 20 200, 200 200, 110 110), 
+        (110 110, 80 180, 140 180, 110 110)), 
+      (
+        (110 110, 20 20, 200 20, 110 110), 
+        (110 110, 80 40, 140 40, 110 110)))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F0FFFF212">true</op>
+  </test>
+</case>
+
+</run>
\ No newline at end of file

Added: packages/jts/branches/upstream/current/test/validate/TestRelatePL.xml
===================================================================
--- packages/jts/branches/upstream/current/test/validate/TestRelatePL.xml	                        (rev 0)
+++ packages/jts/branches/upstream/current/test/validate/TestRelatePL.xml	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,1283 @@
+<run>
+<precisionModel type="FLOATING"/>
+
+<case>
+<desc>P/L-2: a point and a line disjoint [dim(0){A.P.Int = B.L.Ext}]</desc>
+  <a>
+    POINT(110 200)
+  </a>
+  <b>
+    LINESTRING(90 80, 160 150, 300 150, 340 150, 340 240)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF0FFF102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/L-3-1: a point touching the start point of a line  [dim(0){A.P.Int = B.L.Bdy.SP}]</desc>
+  <a>
+    POINT(90 80)
+  </a>
+  <b>
+    LINESTRING(90 80, 160 150, 300 150, 340 150, 340 240)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F0FFFF102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/L-3-2: a point touching the end point of a line [dim(0){A.P.Int = B.L.Bdy.EP}]</desc>
+  <a>
+    POINT(340 240)
+  </a>
+  <b>
+    LINESTRING(90 80, 160 150, 300 150, 340 150, 340 240)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F0FFFF102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/L-5-1: a point on the line at a non-vertex [dim(0){A.P.Int = B.L.Int.NV}]</desc>
+  <a>
+    POINT(230 150)
+  </a>
+  <b>
+    LINESTRING(90 80, 160 150, 300 150, 340 150, 340 240)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0FFFFF102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/L-5-2: a point on the line at a vertex [dim(0){A.P.Int = B.L.Int.V}]</desc>
+  <a>
+    POINT(160 150)
+  </a>
+  <b>
+    LINESTRING(90 80, 160 150, 300 150, 340 150, 340 240)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0FFFFF102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/LR-2-1: a point outside a LinearRing [dim(0){A.P.Int = B.LR.Ext}]</desc>
+  <a>
+    POINT(90 150)
+  </a>
+  <b>
+    LINESTRING(150 150, 20 20, 280 20, 150 150)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF0FFF1F2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/LR-2-2: a point inside a LinearRing  [dim(0){A.P.Int = B.LR.Ext}]</desc>
+  <a>
+    POINT(150 80)
+  </a>
+  <b>
+    LINESTRING(150 150, 20 20, 280 20, 150 150)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF0FFF1F2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/LR-5-1: a point on the closing point of a LinearRing [dim(0){A.P.Int = B.LR.Int.CP}]</desc>
+  <a>
+    POINT(150 150)
+  </a>
+  <b>
+    LINESTRING(150 150, 20 20, 280 20, 150 150)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0FFFFF1F2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/LR-5-2: a point on a LinearRing at a non-vertex [dim(0){A.P.Int = B.L.Int.NV}]</desc>
+  <a>
+    POINT(100 20)
+  </a>
+  <b>
+    LINESTRING(150 150, 20 20, 280 20, 150 150)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0FFFFF1F2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/LR-5-3: a point on a LinearRing at a vertex [dim(0){A.P.Int = B.L.Int.V}]</desc>
+  <a>
+    POINT(20 20)
+  </a>
+  <b>
+    LINESTRING(150 150, 20 20, 280 20, 150 150)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0FFFFF1F2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/nsL.1-3-1: a point on a non-simple LineString's end point [dim(0){A.P.Int = B.nsL.Bdy.EP}]</desc>
+  <a>
+    POINT(220 220)
+  </a>
+  <b>
+    LINESTRING(110 110, 220 20, 20 20, 110 110, 220 220)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F0FFFF102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/nsL.1-5-1: a point on a non-simple LineString's start point with crossing line segments [dim(0){A.P.Int = B.nsL.Bdy.SPx}]</desc>
+  <a>
+    POINT(110 110)
+  </a>
+  <b>
+    LINESTRING(110 110, 220 20, 20 20, 110 110, 220 220)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F0FFFF102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/nsL.1-5-2: a point a non-simple LineString's start point with crossing line segments [dim(0){A.P.Int = B.nsL.Bdy.SPx}]</desc>
+  <a>
+    POINT(110 110)
+  </a>
+  <b>
+    LINESTRING(110 110, 220 20, 20 20, 220 220)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F0FFFF102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/nsL.1-5-3: a point on a non-simple LineString's interior at a non-vertex [dim(0){A.P.Int = B.nsL.Int.NV}]</desc>
+  <a>
+    POINT(110 20)
+  </a>
+  <b>
+    LINESTRING(110 110, 220 20, 20 20, 220 220)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0FFFFF102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/nsL.1-5-4: a point on a non-simple LineString's interior at a vertex [dim(0){A.P.Int = B.nsL.Int.V}]</desc>
+  <a>
+    POINT(220 20)
+  </a>
+  <b>
+    LINESTRING(110 110, 220 20, 20 20, 220 220)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0FFFFF102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/nsL.2-5-2: a point on a non-simple LineString's interior at a vertex [dim(0){A.P.Int = B.nsL.Int.NV}]</desc>
+  <a>
+    POINT(110 20)
+  </a>
+  <b>
+    LINESTRING(220 220, 20 20, 220 20, 110 110)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0FFFFF102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/nsL.2-5-3: a point on a non-simple LineString's interior at a vertex [dim(0){A.P.Int = B.nsL.Int.V}]</desc>
+  <a>
+    POINT(20 20)
+  </a>
+  <b>
+    LINESTRING(220 220, 20 20, 220 20, 110 110)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0FFFFF102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/nsL.2-5-4: a point on a non-simple LineString's interior at a vertex with crossing line segments [dim(0){A.P.Int = B.nsL.Int.Vx}]</desc>
+  <a>
+    POINT(20 110)
+  </a>
+  <b>
+    LINESTRING(20 200, 20 20, 110 20, 20 110, 110 200)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0FFFFF102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/nsL.3-3-1: a point on a non-simple LineString's start point [dim(0){A.P.Int = B.nsL.Bdy.SP}]</desc>
+  <a>
+    POINT(20 200)
+  </a>
+  <b>
+    LINESTRING(20 200, 200 20, 20 20, 200 200)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F0FFFF102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/nsL.3-5-1: a point on a non-simple LineString's interior at a non-vertex with overlapping line segments [dim(0){A.P.Int = B.nsL.Int.NVo}]</desc>
+  <a>
+    POINT(110 110)
+  </a>
+  <b>
+    LINESTRING(20 200, 200 20, 140 20, 140 80, 80 140, 20 140)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0FFFFF102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/nsL.3-5-2: a point on a non-simple LineString's interior at a non-vertex with crossing line segments [dim(0){A.P.Int = B.nsL.Int.NVx}]</desc>
+  <a>
+    POINT(110 110)
+  </a>
+  <b>
+    LINESTRING(20 200, 200 20, 20 20, 200 200)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0FFFFF102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/nsL.3-5-3: a point on a non-simple LineString's interior at a vertex with both crossing and overlapping line segments [dim(0){A.P.Int = B.nsL.Int.Vb}]</desc>
+  <a>
+    POINT(80 140)
+  </a>
+  <b>
+    LINESTRING(20 200, 110 110, 200 20, 140 20, 140 80, 110 110, 80 140, 20 140)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0FFFFF102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/nsL.3-5-4: a point on a non-simple LineString's interior at a two-vertex point with overlapping line segments [dim(0){A.P.Int = B.nsL.Int.Vo}]</desc>
+  <a>
+    POINT(110 110)
+  </a>
+  <b>
+    LINESTRING(20 200, 110 110, 200 20, 140 20, 140 80, 110 110, 80 140, 20 140)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0FFFFF102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/nsL.3-5-5: a point on a non-simple LineString's interior at a vertex with overlapping line segments [dim(0){A.P.Int = B.nsL.Int.Vo}]</desc>
+  <a>
+    POINT(110 110)
+  </a>
+  <b>
+    LINESTRING(20 200, 200 20, 140 20, 140 80, 110 110, 80 140, 20 140)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0FFFFF102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/nsL.3-5-6: a point on a non-simple LineString's interior at a two-vertex point with crossing line segments [dim(0){A.P.Int = B.nsL.Int.Vx}]</desc>
+  <a>
+    POINT(110 110)
+  </a>
+  <b>
+    LINESTRING(20 200, 110 110, 200 20, 20 20, 110 110, 200 200)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0FFFFF102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/nsL.3-5-7: a point on a non-simple LineString's interior at a vertex with crossing line segments [dim(0){A.P.Int = B.nsL.Int.Vx}]</desc>
+  <a>
+    POINT(110 110)
+  </a>
+  <b>
+    LINESTRING(20 200, 200 20, 20 20, 110 110, 200 200)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0FFFFF102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/nsL.3-5-8: a point on a non-simple LineString's interior at a two-vertex point with crossing line segments [dim(0){A.P.Int = B.nsL.Int.Vx}]</desc>
+  <a>
+    POINT(110 110)
+  </a>
+  <b>
+    LINESTRING(20 200, 110 110, 20 20, 200 20, 110 110, 200 200)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0FFFFF102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/nsL.4-3-1: a point on a non-simple LineString's start point with crossing and overlapping line segments [dim(0){A.P.Int = B.nsL.Bdy.SPb}]</desc>
+  <a>
+    POINT(110 110)
+  </a>
+  <b>
+    LINESTRING(110 110, 110 200, 20 200, 110 110, 200 20, 140 20, 140 80, 110 110, 80 140, 
+    20 140)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F0FFFF102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/nsL.4-3-2: a point on a non-simple LineString's start point with crossing and overlapping line segments [dim(0){A.P.Int = B.nsL.Bdy.SPb}]</desc>
+  <a>
+    POINT(110 110)
+  </a>
+  <b>
+    LINESTRING(110 110, 110 200, 20 200, 200 20, 140 20, 140 80, 110 110, 80 140, 20 140)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F0FFFF102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/nsL.4-3-3:a point on a non-simple LineString's start point with crossing and overlapping line segments [dim(0){A.P.Int = B.nsL.Bdy.SPb}]</desc>
+  <a>
+    POINT(110 110)
+  </a>
+  <b>
+    LINESTRING(110 110, 110 200, 20 200, 200 20, 140 20, 140 80, 80 140, 20 140)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F0FFFF102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/nsL.4-3-4: a point on a non-simple LineString's start point with crossing line segments [dim(0){A.P.Int = B.nsL.Bdy.SPx}]</desc>
+  <a>
+    POINT(110 110)
+  </a>
+  <b>
+    LINESTRING(110 110, 110 200, 20 200, 110 110, 200 20, 20 20, 110 110, 200 200)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F0FFFF102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/nsL.4-3-5: a point on a non-simple LineString's start point with crossing line segments [dim(0){A.P.Int = B.nsL.Bdy.SPx}]</desc>
+  <a>
+    POINT(110 110)
+  </a>
+  <b>
+    LINESTRING(110 110, 110 200, 20 200, 200 20, 20 20, 110 110, 200 200)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F0FFFF102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/nsL.4-3-6: a point on a non-simple LineString's start point with crossing line segments [dim(0){A.P.Int = B.nsL.Bdy.SPx}]</desc>
+  <a>
+    POINT(110 110)
+  </a>
+  <b>
+    LINESTRING(110 110, 110 200, 20 200, 200 20, 20 20, 200 200)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F0FFFF102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/nsL.4-3-7: a point on a non-simple LineString's start point with crossing line segments [dim(0){A.P.Int = B.nsL.Bdy.SPx}]</desc>
+  <a>
+    POINT(110 110)
+  </a>
+  <b>
+    LINESTRING(110 110, 110 200, 20 200, 110 110, 20 20, 200 20, 110 110, 200 200)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F0FFFF102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/nsL.4-3-8: a point on a non-simple LineString's start point with crossing line segments [dim(0){A.P.Int = B.nsL.Bdy.SPx}]</desc>
+  <a>
+    POINT(110 110)
+  </a>
+  <b>
+    LINESTRING(110 110, 110 200, 20 200, 200 20, 200 110, 110 110, 200 200)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F0FFFF102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/nsL.5-3-1: a point on a non-simple LineString's end point with crossing line segments [dim(0){A.P.Int = B.nsL.Bdy.EPx}]</desc>
+  <a>
+    POINT(110 110)
+  </a>
+  <b>
+    LINESTRING(200 200, 110 110, 20 20, 200 20, 110 110, 20 200, 110 200, 110 110)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F0FFFF102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/nsL.5-3-2: a point on a non-simple LineString's end point with crossing line segments [dim(0){A.P.Int = B.nsL.Bdy.EPx}]</desc>
+  <a>
+    POINT(110 110)
+  </a>
+  <b>
+    LINESTRING(200 200, 20 20, 200 20, 110 110, 20 200, 110 200, 110 110)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F0FFFF102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/nsL.5-3-3: a point on a non-simple LineString's end point with crossing line segments [dim(0){A.P.Int = B.nsL.Bdy.EPx}]</desc>
+  <a>
+    POINT(110 110)
+  </a>
+  <b>
+    LINESTRING(200 200, 20 20, 200 20, 20 200, 110 200, 110 110)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F0FFFF102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/nsL.5-3-4: a point on a non-simple LineString's end point with crossing line segments [dim(0){A.P.Int = B.nsL.Bdy.EPx}]</desc>
+  <a>
+    POINT(110 110)
+  </a>
+  <b>
+    LINESTRING(200 200, 110 110, 200 20, 20 20, 110 110, 20 200, 110 200, 110 110)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F0FFFF102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/nsL.5-3-5: a point on a non-simple LineString's end point with crossing line segments [dim(0){A.P.Int = B.nsL.Bdy.EPx}]</desc>
+  <a>
+    POINT(110 110)
+  </a>
+  <b>
+    LINESTRING(200 200, 20 20, 20 110, 110 110, 20 200, 110 200, 110 110)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F0FFFF102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/nsL.6-3-1: a point on a non-simple LineString's start point with crossing line segments [dim(0){A.P.Int = B.nsL.Bdy.SPx}]</desc>
+  <a>
+    POINT(110 160)
+  </a>
+  <b>
+    LINESTRING(110 160, 200 250, 110 250, 110 160, 110 110, 110 20, 20 20, 110 110)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F0FFFF102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/nsL.6-3-2: a point on a non-simple LineString's start point with crossing line segments [dim(0){A.P.Int = B.nsL.Bdy.SPx}]</desc>
+  <a>
+    POINT(110 160)
+  </a>
+  <b>
+    LINESTRING(110 160, 200 250, 110 250, 110 110, 110 20, 20 20, 110 110)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F0FFFF102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/nsL.6-3-3: a point on a non-simple LineString's end point with crossing line segments [dim(0){A.P.Int = B.nsL.Bdy.EPx}]</desc>
+  <a>
+    POINT(110 110)
+  </a>
+  <b>
+    LINESTRING(110 160, 200 250, 110 250, 110 160, 110 110, 110 20, 20 20, 110 110)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F0FFFF102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/nsL.6-3-4: a point on a non-simple LineString's end point with crossing line segments [dim(0){A.P.Int = B.nsL.Bdy.EPx}]</desc>
+  <a>
+    POINT(110 110)
+  </a>
+  <b>
+    LINESTRING(110 160, 200 250, 110 250, 110 160, 110 20, 20 20, 110 110)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F0FFFF102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/nsL.7-5-1: a point on a closed non-simple LineString's closing point with crossing line segments [dim(0){A.P.Int = B.nsL.Int.CPx}]</desc>
+  <a>
+    POINT(110 110)
+  </a>
+  <b>
+    LINESTRING(110 110, 200 200, 110 200, 110 110, 110 20, 20 20, 110 110)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0FFFFF1F2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/nsL.7-5-2: a point on a closed non-simple LineString's closing point with crossing line segments [dim(0){A.P.Int = B.nsL.Int.CPx}]</desc>
+  <a>
+    POINT(110 110)
+  </a>
+  <b>
+    LINESTRING(110 110, 200 200, 110 200, 110 20, 20 20, 110 110)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0FFFFF1F2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/nsL.7-5-3: a point on a closed non-simple LineString's interior at a non-vertex [dim(0){A.P.Int = B.nsL.Int.NV}]</desc>
+  <a>
+    POINT(140 200)
+  </a>
+  <b>
+    LINESTRING(110 110, 200 200, 110 200, 110 110, 110 20, 20 20, 110 110)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0FFFFF1F2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/nsL.7-5-4: a point on a closed non-simple LineString's interior at a vertex [dim(0){A.P.Int = B.nsL.Int.V}]</desc>
+  <a>
+    POINT(110 200)
+  </a>
+  <b>
+    LINESTRING(110 110, 200 200, 110 200, 110 110, 110 20, 20 20, 110 110)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0FFFFF1F2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/nsL.8-5-1: a point on a closed non-simple LineString's closing point with crossing line segments [dim(0){A.P.Int = B.nsL.Int.CPx}]</desc>
+  <a>
+    POINT(110 110)
+  </a>
+  <b>
+    LINESTRING(110 110, 200 200, 110 200, 110 110, 110 20, 200 20, 110 110)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0FFFFF1F2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/nsL.8-5-2: a point on the interior (at a non-vertex) of a closed non-simple LineString [dim(0){A.P.Int = B.nsL.Int.NV}]</desc>
+  <a>
+    POINT(140 200)
+  </a>
+  <b>
+    LINESTRING(110 110, 200 200, 110 200, 110 110, 110 20, 200 20, 110 110)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0FFFFF1F2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/nsL.8-5-3: a point on a closed non-simple LineString's interior at a vertex [dim(0){A.P.Int = B.nsL.Int.V}]</desc>
+  <a>
+    POINT(110 200)
+  </a>
+  <b>
+    LINESTRING(110 110, 200 200, 110 200, 110 110, 110 20, 200 20, 110 110)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0FFFFF1F2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/nsL.9-3-1: a point on a non-simple LineString's start point with crossing line segments [dim(0){A.P.Int = B.nsL.Bdy.SPx}]</desc>
+  <a>
+    POINT(90 130)
+  </a>
+  <b>
+    LINESTRING(90 130, 20 130, 20 200, 90 130, 200 20, 20 20, 200 200)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F0FFFF102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/nsL.9-5-1: a point on a non-simple LineString's interior at a non-vertex with crossing line segments [dim(0){A.P.Int = B.nsL.Int.NVx}]</desc>
+  <a>
+    POINT(110 110)
+  </a>
+  <b>
+    LINESTRING(90 130, 20 130, 20 200, 90 130, 200 20, 20 20, 200 200)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0FFFFF102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/nsL.10-3-1: a point on a non-simple LineString's start point with crossing line segments [dim(0){A.P.Int = B.nsL.Bdy.SPx}]</desc>
+  <a>
+    POINT(90 130)
+  </a>
+  <b>
+    LINESTRING(90 130, 20 130, 20 200, 200 20, 20 20, 200 200)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F0FFFF102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/nsL.10-5-1: a point on a non-simple LineString's interior at a non-vertex with crossing line segments [dim(0){A.P.Int = B.nsL.Int.NVx}]</desc>
+  <a>
+    POINT(110 110)
+  </a>
+  <b>
+    LINESTRING(90 130, 20 130, 20 200, 200 20, 20 20, 200 200)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0FFFFF102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/nsL.11-3-1: a point on a closed non-simple LineString's end point with crossing line segments [dim(0){A.P.Int = B.nsL.Bdy.EPx}]</desc>
+  <a>
+    POINT(90 130)
+  </a>
+  <b>
+    LINESTRING(200 200, 20 20, 200 20, 90 130, 20 200, 20 130, 90 130)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F0FFFF102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/nsL.11-5-1: a point on a closed non-simple LineString's interior at a non-vertex with crossing line segments [dim(0){A.P.Int = B.nsL.Int.NVx}]</desc>
+  <a>
+    POINT(110 110)
+  </a>
+  <b>
+    LINESTRING(200 200, 20 20, 200 20, 90 130, 20 200, 20 130, 90 130)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0FFFFF102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/nsL.12-3-1: a point on a closed non-simple LineString's end point with crossing line segments [dim(0){A.P.Int = B.nsL.Bdy.SPx}]</desc>
+  <a>
+    POINT(90 130)
+  </a>
+  <b>
+    LINESTRING(200 200, 20 20, 200 20, 20 200, 20 130, 90 130)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F0FFFF102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/nsL.12-5-1: a point on a closed non-simple LineString's interior at a non-vertex with crossing line segments [dim(0){A.P.Int = B.nsL.Int.NVx}]</desc>
+  <a>
+    POINT(110 110)
+  </a>
+  <b>
+    LINESTRING(200 200, 20 20, 200 20, 20 200, 20 130, 90 130)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0FFFFF102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/nsL.13-5-1: a point on a closed non-simple LineString's closing point with crossing line segments [dim(0){A.P.Int = B.nsL.Int.CPx}]</desc>
+  <a>
+    POINT(110 110)
+  </a>
+  <b>
+    LINESTRING(110 110, 20 130, 20 200, 110 110, 200 20, 20 20, 110 110, 200 200, 200 130, 
+    110 110)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0FFFFF1F2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/nsL.13-5-2: a point on a closed non-simple LineString's closing point with crossing line segments [dim(0){A.P.Int = B.nsL.Int.CPx}]</desc>
+  <a>
+    POINT(110 110)
+  </a>
+  <b>
+    LINESTRING(110 110, 20 130, 20 200, 200 20, 20 20, 200 200, 200 130, 110 110)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0FFFFF1F2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/nsL.14-5-1: a point on a closed non-simple LineString's closing point with crossing line segments [dim(0){A.P.Int = B.nsL.Int.CPx}]</desc>
+  <a>
+    POINT(110 110)
+  </a>
+  <b>
+    LINESTRING(110 110, 80 200, 20 200, 110 110, 200 20, 20 20, 110 110, 200 200, 140 200, 
+    110 110)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0FFFFF1F2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/nsL.14-5-2: a point on a closed non-simple LineString's closing point with crossing line segments [dim(0){A.P.Int = B.nsL.Int.CPx}]</desc>
+  <a>
+    POINT(110 110)
+  </a>
+  <b>
+    LINESTRING(110 110, 80 200, 20 200, 200 20, 20 20, 200 200, 140 200, 110 110)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0FFFFF1F2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/nsL.15-5-1: a point on a closed non-simple LineString's interior at a non-vertex with crossing line segments [dim(0){A.P.Int = B.nsL.Int.NVx}]</desc>
+  <a>
+    POINT(110 110)
+  </a>
+  <b>
+    LINESTRING(200 200, 20 20, 200 20, 20 200, 200 200)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0FFFFF1F2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/nsL.15-5-2: a point on a closed non-simple LineString's interior at a vertex with crossing line segments [dim(0){A.P.Int = B.nsL.Int.Vx}]</desc>
+  <a>
+    POINT(110 110)
+  </a>
+  <b>
+    LINESTRING(200 200, 110 110, 20 20, 200 20, 110 110, 20 200, 200 200)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0FFFFF1F2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/nsL.15-5-3: a point on a closed non-simple LineString's interior at a vertex with crossing line segments [dim(0){A.P.Int = B.nsL.Int.Vx}]</desc>
+  <a>
+    POINT(110 110)
+  </a>
+  <b>
+    LINESTRING(200 200, 110 110, 200 20, 20 20, 110 110, 20 200, 200 200)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0FFFFF1F2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/nsL.16-5-1: a point on a closed non-simple LineString's closing point with crossing line segments [dim(0){A.P.Int = B.nsL.Int.CPx}]</desc>
+  <a>
+    POINT(90 130)
+  </a>
+  <b>
+    LINESTRING(90 130, 20 130, 20 200, 90 130, 110 110, 200 20, 20 20, 110 110, 200 200, 
+    90 130)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0FFFFF1F2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/nsL.16-5-2: a point on a closed non-simple LineString's closing point with crossing line segments [dim(0){A.P.Int = B.nsL.Int.CPx}]</desc>
+  <a>
+    POINT(90 130)
+  </a>
+  <b>
+    LINESTRING(90 130, 20 130, 20 200, 110 110, 200 20, 20 20, 110 110, 200 200, 90 130)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0FFFFF1F2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/nsL.17-5-1: a point on a closed non-simple LineString's closing point with crossing line segments [dim(0){A.P.Int = B.nsL.Int.CPx}]</desc>
+  <a>
+    POINT(90 130)
+  </a>
+  <b>
+    LINESTRING(90 130, 90 200, 20 200, 90 130, 110 110, 200 20, 20 20, 110 110, 200 200, 
+    90 130)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0FFFFF1F2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/nsL.17-5-2: a point on a closed non-simple LineString's closing point with crossing line segments [dim(0){A.P.Int = B.nsL.Int.CPx}]</desc>
+  <a>
+    POINT(90 130)
+  </a>
+  <b>
+    LINESTRING(90 130, 90 200, 20 200, 200 20, 20 20, 200 200, 90 130)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0FFFFF1F2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/nsL.17-5-3: a point on a closed non-simple LineString's closing point with crossing line segments [dim(0){A.P.Int = B.nsL.Int.CPx}]</desc>
+  <a>
+    POINT(90 130)
+  </a>
+  <b>
+    LINESTRING(90 130, 90 200, 20 200, 110 110, 200 20, 20 20, 110 110, 200 200, 90 130)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0FFFFF1F2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/nsL.17-5-4: a point on a closed non-simple LineString's closing point with crossing line segments [dim(0){A.P.Int = B.nsL.Int.CPx}]</desc>
+  <a>
+    POINT(90 130)
+  </a>
+  <b>
+    LINESTRING(90 130, 90 200, 20 200, 200 20, 20 20, 200 200, 90 130)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0FFFFF1F2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/nsL.17-5-5: a point on a closed non-simple LineString's interior at a non-vertex with crossing line segments [dim(0){A.P.Int = B.nsL.Int.NVx}]</desc>
+  <a>
+    POINT(110 110)
+  </a>
+  <b>
+    LINESTRING(90 130, 90 200, 20 200, 200 20, 20 20, 200 200, 90 130)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0FFFFF1F2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/nsL.18-5-1: a point on a non-simple LineString's start point with both crossing and overlapping line segments [dim(0){A.P.Int = B.nsL.Bdy.SPb)}]</desc>
+  <a>
+    POINT(110 200)
+  </a>
+  <b>
+    LINESTRING(110 200, 110 110, 20 20, 200 20, 110 110, 110 200, 200 200)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F0FFFF102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/nsL.18-5-2: a point on a non-simple LineString's interior at a non-vertex with overlapping line segments [dim(0){A.P.Int = B.nsL.Int.NVo}]</desc>
+  <a>
+    POINT(110 150)
+  </a>
+  <b>
+    LINESTRING(110 200, 110 110, 20 20, 200 20, 110 110, 110 200, 200 200)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0FFFFF102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/nsL.18-5-3: a point on a non-simple LineString's interior at a vertex with both crossing and overlapping line segments [dim(0){A.P.Int = B.nsL.Int.Vb}]</desc>
+  <a>
+    POINT(110 110)
+  </a>
+  <b>
+    LINESTRING(110 200, 110 110, 20 20, 200 20, 110 110, 110 200, 200 200)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0FFFFF102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/nsL.19-5-1: a point on a non-simple LineString's closing point with overlapping line segments [dim(0){A.P.Int = B.nsL.Int.CPo}]</desc>
+  <a>
+    POINT(110 200)
+  </a>
+  <b>
+    LINESTRING(110 200, 110 110, 20 20, 200 20, 110 110, 110 200)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0FFFFF1F2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/nsL.19-5-2: a point on a non-simple LineString's interior at a non-vertex overlapping line segments [dim(0){A.P.Int = B.nsL.Int.NVo}]</desc>
+  <a>
+    POINT(110 150)
+  </a>
+  <b>
+    LINESTRING(110 200, 110 110, 20 20, 200 20, 110 110, 110 200)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0FFFFF1F2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/nsL.19-5-3: a point on a non-simple LineString interior at a vertex with both crossing and overlapping line segments [dim(0){A.P.Int = B.nsL.Int.Vb}]</desc>
+  <a>
+    POINT(110 110)
+  </a>
+  <b>
+    LINESTRING(110 200, 110 110, 20 20, 200 20, 110 110, 110 200)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0FFFFF1F2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/nsL.20-5-1: a point on a non-simple LineString's interior at a non-vertex with overlapping line segments [dim(0){A.P.Int = B.nsL.Int.NVo}]</desc>
+  <a>
+    POINT(110 150)
+  </a>
+  <b>
+    LINESTRING(20 200, 110 200, 110 110, 20 20, 200 20, 110 110, 110 200, 200 200)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0FFFFF102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/nsL.20-5-2: a point on a non-simple LineString's interior at a vertex with both crossing and overlapping line segments [dim(0){A.P.Int = B.nsL.Int.Vb}]</desc>
+  <a>
+    POINT(110 110)
+  </a>
+  <b>
+    LINESTRING(20 200, 110 200, 110 110, 20 20, 200 20, 110 110, 110 200, 200 200)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0FFFFF102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/nsl.20-5-3: a point on a non-simple LineString's interior at a vertex with both crossing and overlapping line segments [dim(0){A.P.Int = B.nsL.Int.Vb}]</desc>
+  <a>
+    POINT(110 200)
+  </a>
+  <b>
+    LINESTRING(20 200, 110 200, 110 110, 20 20, 200 20, 110 110, 110 200, 200 200)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0FFFFF102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>mP/L-2-1: MultiPoint and a line disjoint (points on one side of the line) [dim(0){A.3P.Int = B.L.Ext}]</desc>
+  <a>
+    MULTIPOINT(50 250, 90 220, 130 190)
+  </a>
+  <b>
+    LINESTRING(90 80, 160 150, 300 150, 340 150, 340 240)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF0FFF102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>mP/L-2-2: MultiPoint and a line disjoint (points over the line but no intersection) [dim(0){A.3P.Int = B.L.Ext}]</desc>
+  <a>
+    MULTIPOINT(180 180, 230 130, 280 80)
+  </a>
+  <b>
+    LINESTRING(90 80, 160 150, 300 150, 340 150, 340 240)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF0FFF102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>mP/L-3-1: one of the points intersecting the start point of a line [dim(0){A.3P2.Int = B.L.Bdy.SP}]</desc>
+  <a>
+    MULTIPOINT(50 120, 90 80, 130 40)
+  </a>
+  <b>
+    LINESTRING(90 80, 160 150, 300 150, 340 150, 340 240)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F00FFF102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>mP/L-3-2: one of the points intersecting the end point of a line [dim(0){A.3P2 = B.L.Bdy.EP}]</desc>
+  <a>
+    MULTIPOINT(300 280, 340 240, 380 200)
+  </a>
+  <b>
+    LINESTRING(90 80, 160 150, 300 150, 340 150, 340 240)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="F00FFF102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>mP/L-4-1: one of the points intersecting the interior of a line at a non-vertex (points on one side of the line) [dim(0){A.3P1.Int = B.L.Int.NV]</desc>
+  <a>
+    MULTIPOINT(230 150, 260 120, 290 90)
+  </a>
+  <b>
+    LINESTRING(90 80, 160 150, 300 150, 340 150, 340 240)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0F0FFF102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>mP/L-4-2: one of the points intersecting the interior of a line at a non-vertex (points over the line) [dim(0){A.3P2.Int = B.L.Int.NV]</desc>
+  <a>
+    MULTIPOINT(200 190, 240 150, 270 110)
+  </a>
+  <b>
+    LINESTRING(90 80, 160 150, 300 150, 340 150, 340 240)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0F0FFF102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>mP/L-4-3: one of the points intersecting the interior of a line at a vertex (points on one side of the line) [dim(0){A.3P1.Int = B.L.Int.V]</desc>
+  <a>
+    MULTIPOINT(160 150, 190 120, 220 90)
+  </a>
+  <b>
+    LINESTRING(90 80, 160 150, 300 150, 340 150, 340 240)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0F0FFF102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>mP/L-4-4: one of the points intersecting the interior of a line at a vertex (points over the line) [dim(0){A.3P2.Int = B.L.Int.V]</desc>
+  <a>
+    MULTIPOINT(120 190, 160 150, 200 110)
+  </a>
+  <b>
+    LINESTRING(90 80, 160 150, 300 150, 340 150, 340 240)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0F0FFF102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>mP/L-5-1: all the points on a line [dim(0){A.3P1.Int = B.L.Bdy.SP}, dim(0){A.3P2.Int = B.L.Int.V}, dim(0){A.3P3.Int = B.Bdy.EP}]</desc>
+  <a>
+    MULTIPOINT(90 80, 160 150, 340 240)
+  </a>
+  <b>
+    LINESTRING(90 80, 160 150, 300 150, 340 150, 340 240)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="00FFFF1F2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>mP/L-5-2: all the points on a line [dim(0){A.3P1.Int = B.L.Bdy.SP}, dim(0){A.3P2.Int = B.L.Int.V}, dim(0){A.3P3.Int = B.Int.V}]</desc>
+  <a>
+    MULTIPOINT(90 80, 160 150, 300 150)
+  </a>
+  <b>
+    LINESTRING(90 80, 160 150, 300 150, 340 150, 340 240)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="00FFFF102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>mP/L-5-3: all the points on a line [dim(0){A.3P1.Int = B.L.Bdy.SP}, dim(0){A.3P2.Int = B.L.Int.V}, dim(0){A.3P3.Int = B.Int.NV}]</desc>
+  <a>
+    MULTIPOINT(90 80, 160 150, 240 150)
+  </a>
+  <b>
+    LINESTRING(90 80, 160 150, 300 150, 340 150, 340 240)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="00FFFF102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>mP/L-5-4: all the points on a line [dim(0){A.3P1.Int = B.L.Bdy.SP}, dim(0){A.3P2.Int = B.L.Int.NV}, dim(0){A.3P3.Int = B.Int.NV}]</desc>
+  <a>
+    MULTIPOINT(90 80, 130 120, 210 150)
+  </a>
+  <b>
+    LINESTRING(90 80, 160 150, 300 150, 340 150, 340 240)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="00FFFF102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>mP/L-5-5: all the points on a line [dim(0){A.3P1.Int = B.L.Int.NV}, dim(0){A.3P2.Int = B.L.Int.NV}, dim(0){A.3P3.Int = B.Int.NV}]</desc>
+  <a>
+    MULTIPOINT(130 120, 210 150, 340 200)
+  </a>
+  <b>
+    LINESTRING(90 80, 160 150, 300 150, 340 150, 340 240)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0FFFFF102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>mP/L-5-6: all the points on a line [dim(0){A.3P1.Int = B.L.Int.V}, dim(0){A.3P2.Int = B.L.Int.V}, dim(0){A.3P3.Int = B.Int.NV}]</desc>
+  <a>
+    MULTIPOINT(160 150, 240 150, 340 210)
+  </a>
+  <b>
+    LINESTRING(90 80, 160 150, 300 150, 340 150, 340 240)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0FFFFF102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>mP/L-5-7: all the points on a line [dim(0){A.3P1.Int = B.L.Int.V}, dim(0){A.3P2.Int = B.L.Int.V}, dim(0){A.3P3.Int = B.Int.V}]</desc>
+  <a>
+    MULTIPOINT(160 150, 300 150, 340 150)
+  </a>
+  <b>
+    LINESTRING(90 80, 160 150, 300 150, 340 150, 340 240)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0FFFFF102">true</op>
+  </test>
+</case>
+
+<case>
+<desc>mP/L-5-8: all the points on a line [dim(0){A.3P1.Int = B.L.Int.V}, dim(0){A.3P2.Int = B.L.Int.NV}, dim(0){A.3P3.Int = B.Bdy.EP}]</desc>
+  <a>
+    MULTIPOINT(160 150, 240 150, 340 240)
+  </a>
+  <b>
+    LINESTRING(90 80, 160 150, 300 150, 340 150, 340 240)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="00FFFF102">true</op>
+  </test>
+</case>
+
+</run>
\ No newline at end of file

Added: packages/jts/branches/upstream/current/test/validate/TestRelatePP.xml
===================================================================
--- packages/jts/branches/upstream/current/test/validate/TestRelatePP.xml	                        (rev 0)
+++ packages/jts/branches/upstream/current/test/validate/TestRelatePP.xml	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,173 @@
+<run>
+<precisionModel type="FLOATING"/>
+
+<case>
+<desc>P/P: same point [dim(0){A.P.Int = B.P.Int}]</desc>
+  <a>
+    POINT(20 20)
+  </a>
+  <b>
+    POINT(20 20)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0FFFFFFF2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/P: different point [dim(0){A.P.Int = B.P.Ext}]</desc>
+  <a>
+    POINT(20 20)
+  </a>
+  <b>
+    POINT(40 60)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF0FFF0F2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/mP: different points [dim(0){A.P.Int = B.3P.Ext}]</desc>
+  <a>
+    POINT(40 40)
+  </a>
+  <b>
+    MULTIPOINT(20 20, 80 80, 20 120)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF0FFF0F2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>P/mP: point A within one of B points [dim(0){A.P.Int = B.3P1.Int}]</desc>
+  <a>
+    POINT(20 20)
+  </a>
+  <b>
+    MULTIPOINT(20 20, 80 80, 20 120)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0FFFFF0F2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>mP/mP-1-1: same points [dim(0){A.3P1.Int = B.3P1.Int}, dim(0){A.3P2.Int = B.3P2.Int}, dim(0){A.3P3.Int = B.3P3.Int}]</desc>
+  <a>
+    MULTIPOINT(40 40, 80 60, 120 100)
+  </a>
+  <b>
+    MULTIPOINT(40 40, 80 60, 120 100)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0FFFFFFF2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>mP/mP-1-2: same but different sequence of points [dim(0){A.3P1.Int = B.3P1.Int}, dim(0){A.3P1.Int = B.3P3.Int}, dim(0){A.3P3.Int = B.3P2.Int}]</desc>
+  <a>
+    MULTIPOINT(40 40, 80 60, 120 100)
+  </a>
+  <b>
+    MULTIPOINT(40 40, 120 100, 80 60)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0FFFFFFF2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>mP/mP-2: different points [dim(0){A.4P.Int = B.4P.Ext}]</desc>
+  <a>
+    MULTIPOINT(40 40, 60 100, 100 60, 120 120)
+  </a>
+  <b>
+    MULTIPOINT(20 120, 60 60, 100 100, 140 40)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="FF0FFF0F2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>mP/mP-5-1: same points [dim(0){A.4P.Int = B.4P.Int}]</desc>
+  <a>
+    MULTIPOINT(20 20, 80 70, 140 120, 200 170)
+  </a>
+  <b>
+    MULTIPOINT(20 20, 80 70, 140 120, 200 170)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0FFFFFFF2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>mP/mP-5-2: same points but different sequence [dim(0){A.4P.Int = B.4P.Int}]</desc>
+  <a>
+    MULTIPOINT(20 20, 140 120, 80 70, 200 170)
+  </a>
+  <b>
+    MULTIPOINT(80 70, 20 20, 200 170, 140 120)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0FFFFFFF2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>mP/mP-5-3: some points same [dim(0){A.4P2.Int = B.2P1.Int}, dim(0){A.4P3.Int = B.2P2.Int}]</desc>
+  <a>
+    MULTIPOINT(20 20, 80 70, 140 120, 200 170)
+  </a>
+  <b>
+    MULTIPOINT(80 70, 140 120)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0F0FFFFF2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>mP/mP-5-4: some points same, in a different sequence [dim(0){A.4P1.Int = B.2P2.Int}, dim(0){A.4P4.Int = B.2P1.Int}]</desc>
+  <a>
+    MULTIPOINT(80 70, 20 20, 200 170, 140 120)
+  </a>
+  <b>
+    MULTIPOINT(140 120, 80 70)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0F0FFFFF2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>mP/mP-6-1: some points same, some different [dim(0){A.4P4.Int = B.3P2.Int}]</desc>
+  <a>
+    MULTIPOINT(80 70, 20 20, 200 170, 140 120)
+  </a>
+  <b>
+    MULTIPOINT(80 170, 140 120, 200 80)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0F0FFF0F2">true</op>
+  </test>
+</case>
+
+<case>
+<desc>mP/mP-6-2: dim(0){A.4P1.Int = B.4P4.Int}, dim(0){A.4P4.Int = B.4P2.Int}</desc>
+  <a>
+    MULTIPOINT(80 70, 20 20, 200 170, 140 120)
+  </a>
+  <b>
+    MULTIPOINT(80 170, 140 120, 200 80, 80 70)
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="0F0FFF0F2">true</op>
+  </test>
+</case>
+
+</run>
\ No newline at end of file

Added: packages/jts/branches/upstream/current/test/vivid/TestBoundary.xml
===================================================================
--- packages/jts/branches/upstream/current/test/vivid/TestBoundary.xml	                        (rev 0)
+++ packages/jts/branches/upstream/current/test/vivid/TestBoundary.xml	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,165 @@
+<run>
+  <precisionModel scale="1.0" offsetx="0.0" offsety="0.0"/>
+
+<case>
+  <desc>P - point</desc>
+  <a>
+    POINT(10 10)
+  </a>
+<test>
+  <op name="getboundary" arg1="A">
+    GEOMETRYCOLLECTION EMPTY
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>mP - MultiPoint</desc>
+  <a>
+    MULTIPOINT(10 10, 20 20)
+  </a>
+<test>
+  <op name="getboundary" arg1="A">
+    GEOMETRYCOLLECTION EMPTY
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>L - Line</desc>
+  <a>
+    LINESTRING(10 10, 20 20)
+  </a>
+<test>
+  <op name="getboundary" arg1="A">
+    MULTIPOINT(10 10, 20 20)
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>L - closed</desc>
+  <a>
+    LINESTRING(10 10, 20 20, 20 10, 10 10)
+  </a>
+<test>
+  <op name="getboundary" arg1="A">
+    MULTIPOINT EMPTY
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>L - self-intersecting with boundary</desc>
+  <a>
+    LINESTRING(40 40, 100 100, 180 100, 180 180, 100 180, 100 100)
+  </a>
+<test>
+  <op name="getboundary" arg1="A">
+    MULTIPOINT(40 40, 100 100)
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>mL - 2 lines with common endpoint</desc>
+  <a>
+    MULTILINESTRING(
+      (10 10, 20 20), 
+      (20 20, 30 30))
+  </a>
+<test>
+  <op name="getboundary" arg1="A">
+    MULTIPOINT(10 10, 30 30)
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>mL - 3 lines with common endpoint</desc>
+  <a>
+    MULTILINESTRING(
+      (10 10, 20 20), 
+      (20 20, 30 20), 
+      (20 20, 30 30))
+  </a>
+<test>
+  <op name="getboundary" arg1="A">
+    MULTIPOINT(10 10, 20 20, 30 20, 30 30)
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>mL - 4 lines with common endpoint</desc>
+  <a>
+    MULTILINESTRING(
+      (10 10, 20 20), 
+      (20 20, 30 20), 
+      (20 20, 30 30), 
+      (20 20, 30 40))
+  </a>
+<test>
+  <op name="getboundary" arg1="A">
+    MULTIPOINT(10 10, 30 20, 30 30, 30 40)
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>mL - 2 lines, one closed, with common endpoint</desc>
+  <a>
+    MULTILINESTRING(
+      (10 10, 20 20), 
+      (20 20, 20 30, 30 30, 30 20, 20 20))
+  </a>
+<test>
+  <op name="getboundary" arg1="A">
+    MULTIPOINT(10 10, 20 20)
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>L - 1 line, self-intersecting, topologically equal to prev case</desc>
+  <a>
+    MULTILINESTRING(
+      (10 10, 20 20, 20 30, 30 30, 30 20, 20 20))
+  </a>
+<test>
+  <op name="getboundary" arg1="A">
+    MULTIPOINT(10 10, 20 20)
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>A - polygon with no holes</desc>
+  <a>
+    POLYGON(
+      (40 60, 420 60, 420 320, 40 320, 40 60))
+  </a>
+<test>
+  <op name="getboundary" arg1="A">
+    LINESTRING(40 60, 420 60, 420 320, 40 320, 40 60)
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>A - polygon with 1 hole</desc>
+  <a>
+    POLYGON(
+      (40 60, 420 60, 420 320, 40 320, 40 60), 
+      (200 140, 160 220, 260 200, 200 140))
+  </a>
+<test>
+  <op name="getboundary" arg1="A">
+    MULTILINESTRING(
+      (40 60, 420 60, 420 320, 40 320, 40 60), 
+      (200 140, 160 220, 260 200, 200 140))
+  </op>
+</test>
+</case>
+
+</run>

Added: packages/jts/branches/upstream/current/test/vivid/TestCentroid.xml
===================================================================
--- packages/jts/branches/upstream/current/test/vivid/TestCentroid.xml	                        (rev 0)
+++ packages/jts/branches/upstream/current/test/vivid/TestCentroid.xml	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,149 @@
+<run>
+  <precisionModel scale="1.0" offsetx="0.0" offsety="0.0"/>
+
+<case>
+  <desc>P - single point</desc>
+  <a>    POINT(10 10)  </a>
+<test><op name="getCentroid" arg1="A" >    POINT(10 10)   </op></test>
+</case>
+
+<case>
+  <desc>mP - two points</desc>
+  <a>    MULTIPOINT(10 10, 20 20 )  </a>
+<test><op name="getCentroid" arg1="A" >    POINT(15 15)   </op></test>
+</case>
+
+<case>
+  <desc>mP - 4 points</desc>
+  <a>    MULTIPOINT(10 10, 20 20, 10 20, 20 10)  </a>
+<test><op name="getCentroid" arg1="A" >    POINT(15 15)   </op></test>
+</case>
+
+<case>
+  <desc>L - single segment</desc>
+  <a>    LINESTRING(10 10, 20 20)  </a>
+<test><op name="getCentroid" arg1="A" >    POINT(15 15)   </op></test>
+</case>
+
+<case>
+  <desc>L - two segments</desc>
+  <a>    LINESTRING (60 180, 120 100, 180 180)  </a>
+<test><op name="getCentroid" arg1="A" >    POINT (120 140)   </op></test>
+</case>
+
+<case>
+  <desc>L - elongated horseshoe</desc>
+  <a>    LINESTRING (80 0, 80 120, 120 120, 120 0))
+	</a>
+<test><op name="getCentroid" arg1="A" >    POINT (100 69)   </op></test>
+</case>
+
+
+<case>
+  <desc>mL - two single-segment lines</desc>
+  <a>    MULTILINESTRING ((0 0, 0 100), (100 0, 100 100))  </a>
+<test><op name="getCentroid" arg1="A" >    POINT (50 50)   </op></test>
+</case>
+
+<case>
+  <desc>mL - two concentric rings, offset</desc>
+  <a>    MULTILINESTRING ((0 0, 0 200, 200 200, 200 0, 0 0), 
+  (60 180, 20 180, 20 140, 60 140, 60 180))  
+</a>
+<test><op name="getCentroid" arg1="A" >    POINT (90 110)   </op></test>
+</case>
+
+<case>
+  <desc>mL - complicated symmetrical collection of lines</desc>
+  <a>    MULTILINESTRING ((20 20, 60 60), 
+  (20 -20, 60 -60), 
+  (-20 -20, -60 -60), 
+  (-20 20, -60 60), 
+  (-80 0, 0 80, 80 0, 0 -80, -80 0), 
+  (-40 20, -40 -20), 
+  (-20 40, 20 40), 
+  (40 20, 40 -20), 
+  (20 -40, -20 -40)) </a>
+<test><op name="getCentroid" arg1="A" >    POINT (0 0)   </op></test>
+</case>
+
+<case>
+  <desc>A - box</desc>
+  <a>    POLYGON ((40 160, 160 160, 160 40, 40 40, 40 160))  </a>
+<test><op name="getCentroid" arg1="A" >    POINT (100 100)   </op></test>
+</case>
+
+<case>
+  <desc>A - box with hole</desc>
+  <a>    POLYGON ((0 200, 200 200, 200 0, 0 0, 0 200), (20 180, 80 180, 80 20, 20 20, 20 180)) </a>
+<test><op name="getCentroid" arg1="A" >    POINT (116 100)   </op></test>
+</case>
+
+<case>
+  <desc>A - box with offset hole (showing difference between area and line centroid)</desc>
+  <a>    POLYGON ((0 0, 0 200, 200 200, 200 0, 0 0), 
+  (60 180, 20 180, 20 140, 60 140, 60 180))
+	 </a>
+<test><op name="getCentroid" arg1="A" >    POINT (103 98)   </op></test>
+</case>
+
+<case>
+  <desc>A - box with 2 symmetric holes </desc>
+  <a>    POLYGON ((0 0, 0 200, 200 200, 200 0, 0 0), 
+  (60 180, 20 180, 20 140, 60 140, 60 180), 
+  (180 60, 140 60, 140 20, 180 20, 180 60))
+	 </a>
+<test><op name="getCentroid" arg1="A" >   POINT (100 100)   </op></test>
+</case>
+
+<case>
+  <desc>mA - symmetric angles</desc>
+  <a>    MULTIPOLYGON (((0 40, 0 140, 140 140, 140 120, 20 120, 20 40, 0 40)), 
+  ((0 0, 0 20, 120 20, 120 100, 140 100, 140 0, 0 0))) 
+	</a>
+<test><op name="getCentroid" arg1="A" >    POINT (70 70)   </op></test>
+</case>
+
+<case>
+  <desc>GC - two adjacent polygons (showing that centroids are additive) </desc>
+  <a>    GEOMETRYCOLLECTION (POLYGON ((0 200, 20 180, 20 140, 60 140, 200 0, 0 0, 0 200)), 
+  POLYGON ((200 200, 0 200, 20 180, 60 180, 60 140, 200 0, 200 200))) 
+	</a>
+<test><op name="getCentroid" arg1="A" >    POINT (103 98)   </op></test>
+</case>
+
+<case>
+  <desc>GC - heterogeneous collection of lines, points</desc>
+  <a>    GEOMETRYCOLLECTION (LINESTRING (80 0, 80 120, 120 120, 120 0), 
+  MULTIPOINT (20 60, 40 80, 60 60))
+	</a>
+<test><op name="getCentroid" arg1="A" >    POINT (100 69)   </op></test>
+</case>
+
+<case>
+  <desc>GC - heterogeneous collection of polygons, line</desc>
+  <a>    GEOMETRYCOLLECTION (POLYGON ((0 40, 40 40, 40 0, 0 0, 0 40)), 
+  LINESTRING (80 0, 80 80, 120 40)) 
+	</a>
+<test><op name="getCentroid" arg1="A" >    POINT (20 20)   </op></test>
+</case>
+
+<case>
+  <desc>GC - heterogeneous collection of polygons, lines, points</desc>
+  <a>    GEOMETRYCOLLECTION (POLYGON ((0 40, 40 40, 40 0, 0 0, 0 40)), 
+  LINESTRING (80 0, 80 80, 120 40), 
+  MULTIPOINT (20 60, 40 80, 60 60))
+	</a>
+<test><op name="getCentroid" arg1="A" >    POINT (20 20)   </op></test>
+</case>
+
+<case>
+  <desc>GC - overlapping polygons </desc>
+  <a>    GEOMETRYCOLLECTION (POLYGON ((20 100, 20 -20, 60 -20, 60 100, 20 100)), 
+  POLYGON ((-20 60, 100 60, 100 20, -20 20, -20 60)))
+	</a>
+<test><op name="getCentroid" arg1="A" >    POINT (40 40)   </op></test>
+</case>
+
+
+</run>

Added: packages/jts/branches/upstream/current/test/vivid/TestConvexHull-big.xml
===================================================================
--- packages/jts/branches/upstream/current/test/vivid/TestConvexHull-big.xml	                        (rev 0)
+++ packages/jts/branches/upstream/current/test/vivid/TestConvexHull-big.xml	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,17 @@
+<run>
+  <precisionModel scale="1.0" offsetx="0.0" offsety="0.0"/>
+
+<case>
+  <desc>Big convex hull</desc>
+  <a>
+    MULTIPOINT(-1000000000000000000000000 -1000000000000000000000000, 1000000000000000000000000 -1000000000000000000000000, 1000000000000000000000000 1000000000000000000000000, -1000000000000000000000000 1000000000000000000000000, 0 0)
+  </a>
+<test>
+  <op name="convexhull" arg1="A">
+    POLYGON(
+      (-1000000000000000000000000 -1000000000000000000000000, -1000000000000000000000000 1000000000000000000000000, 1000000000000000000000000 1000000000000000000000000, 1000000000000000000000000 -1000000000000000000000000, -1000000000000000000000000 -1000000000000000000000000))
+  </op>
+</test>
+</case>
+
+</run>

Added: packages/jts/branches/upstream/current/test/vivid/TestConvexHull.xml
===================================================================
--- packages/jts/branches/upstream/current/test/vivid/TestConvexHull.xml	                        (rev 0)
+++ packages/jts/branches/upstream/current/test/vivid/TestConvexHull.xml	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,181 @@
+<run>
+  <precisionModel scale="1.0" offsetx="0.0" offsety="0.0"/>
+
+<case>
+  <desc>Several points collinear and overlapping</desc>
+  <a>
+    MULTIPOINT(130 240, 130 240, 130 240, 570 240, 570 240, 570 240, 650 240)
+  </a>
+<test>
+  <op name="convexhull" arg1="A">
+    LINESTRING(130 240, 650 240)
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>Convex hull</desc>
+  <a>
+    POLYGON(
+      (40 60, 420 60, 420 320, 40 320, 40 60), 
+      (200 140, 160 220, 260 200, 200 140))
+  </a>
+<test>
+  <op name="convexhull" arg1="A">
+    POLYGON(
+      (40 60, 40 320, 420 320, 420 60, 40 60))
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>Convex hull</desc>
+  <a>
+    POLYGON(
+      (10 10, 100 10, 100 100, 10 100, 10 10))
+  </a>
+<test>
+  <op name="convexhull" arg1="A">
+    POLYGON(
+      (10 10, 10 100, 100 100, 100 10, 10 10))
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>Point</desc>
+  <a>
+    POINT(20 20)
+  </a>
+<test>
+  <op name="convexhull" arg1="A">
+    POINT(20 20)
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>Horizontal Line</desc>
+  <a>
+    LINESTRING(30 220, 240 220, 240 220)
+  </a>
+<test>
+  <op name="convexhull" arg1="A">
+    LINESTRING(30 220, 240 220)
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>Vertical Line</desc>
+  <a>
+    LINESTRING(110 290, 110 100, 110 100)
+  </a>
+<test>
+  <op name="convexhull" arg1="A">
+    LINESTRING(110 290, 110 100)
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>Spiral</desc>
+  <a>
+    LINESTRING(120 230, 120 200, 150 180, 180 220, 160 260, 90 250, 80 190, 140 110, 230 150, 
+    240 230, 180 320, 60 310, 40 160, 140 50, 280 140)
+  </a>
+<test>
+  <op name="convexhull" arg1="A">
+    POLYGON(
+      (140 50, 40 160, 60 310, 180 320, 240 230, 280 140, 140 50))
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>Starlike Polygon</desc>
+  <a>
+    POLYGON(
+      (200 360, 230 210, 100 190, 270 150, 360 10, 320 200, 490 230, 280 240, 200 360), 
+      (220 300, 250 200, 150 190, 290 150, 330 70, 310 210, 390 230, 280 230, 220 300))
+  </a>
+<test>
+  <op name="convexhull" arg1="A">
+    POLYGON(
+      (360 10, 100 190, 200 360, 490 230, 360 10))
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>Most of the points in one area</desc>
+  <a>
+    MULTIPOINT(70 340, 70 50, 430 50, 420 340, 340 120, 390 110, 390 70, 350 100, 350 50, 
+    370 90, 320 80, 360 120, 350 80, 390 90, 420 80, 410 60, 410 100, 370 100, 380 60, 
+    370 80, 380 100, 360 80, 370 80, 380 70, 390 80, 390 70, 410 70, 400 60, 410 60, 
+    410 60, 410 60, 370 70, 410 50, 410 50, 410 50, 410 50, 410 50, 410 50, 410 50)
+  </a>
+<test>
+  <op name="convexhull" arg1="A">
+    POLYGON(
+      (70 50, 70 340, 420 340, 430 50, 70 50))
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>Multipoint</desc>
+  <a>
+    MULTIPOINT(140 350, 510 140, 110 140, 250 290, 250 50, 300 370, 450 310, 440 160, 290 280, 
+    220 160, 100 260, 320 230, 200 280, 360 130, 330 210, 380 80, 220 210, 380 310, 260 150, 
+    260 110, 170 130)
+  </a>
+<test>
+  <op name="convexhull" arg1="A">
+    POLYGON(
+      (250 50, 110 140, 100 260, 140 350, 300 370, 450 310, 510 140, 380 80, 250 50))
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>GeometryCollection</desc>
+  <a>
+    GEOMETRYCOLLECTION(
+      POINT(110 300), 
+      POINT(100 110), 
+      POINT(130 210), 
+      POINT(150 210), 
+      POINT(150 180), 
+      POINT(130 170), 
+      POINT(140 190), 
+      POINT(130 200), 
+      LINESTRING(240 50, 210 120, 270 80, 250 140, 330 70, 300 160, 340 130, 340 130), 
+      POLYGON(
+        (210 340, 220 260, 150 270, 230 220, 230 140, 270 210, 360 240, 260 250, 260 280, 
+        240 270, 210 340), 
+        (230 270, 230 250, 200 250, 240 220, 240 190, 260 220, 290 230, 250 230, 230 270)))
+  </a>
+<test>
+  <op name="convexhull" arg1="A">
+    POLYGON(
+      (240 50, 100 110, 110 300, 210 340, 360 240, 330 70, 240 50))
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>Collinear L</desc>
+  <a>
+    MULTIPOINT(50 320, 50 280, 50 230, 50 160, 50 120, 100 120, 160 120, 210 120, 210 180, 
+    210 150, 180 180, 140 180, 140 210, 140 260, 160 180, 140 300, 140 320, 110 320, 80 320)
+  </a>
+<test>
+  <op name="convexhull" arg1="A">
+    POLYGON(
+      (50 120, 50 320, 140 320, 210 180, 210 120, 50 120))
+  </op>
+</test>
+</case>
+
+</run>

Added: packages/jts/branches/upstream/current/test/vivid/TestFunctionAA.xml
===================================================================
--- packages/jts/branches/upstream/current/test/vivid/TestFunctionAA.xml	                        (rev 0)
+++ packages/jts/branches/upstream/current/test/vivid/TestFunctionAA.xml	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,635 @@
+<run>
+  <precisionModel scale="1.0" offsetx="0.0" offsety="0.0"/>
+
+<case>
+  <desc>AA - simple polygons</desc>
+  <a>
+    POLYGON(
+      (10 10, 100 10, 100 100, 10 100, 10 10))
+  </a>
+  <b>
+    POLYGON(
+      (50 50, 200 50, 200 200, 50 200, 50 50))
+  </b>
+<test>
+  <op name="intersection" arg1="A" arg2="B">
+    POLYGON(
+      (50 50, 50 100, 100 100, 100 50, 50 50))
+  </op>
+</test>
+<test>
+  <op name="union" arg1="A" arg2="B">
+    POLYGON(
+      (10 10, 10 100, 50 100, 50 200, 200 200, 200 50, 100 50, 100 10, 10 10))
+  </op>
+</test>
+<test>
+  <op name="difference" arg1="A" arg2="B">
+    POLYGON(
+      (10 10, 10 100, 50 100, 50 50, 100 50, 100 10, 10 10))
+  </op>
+</test>
+<test>
+  <op name="symdifference" arg1="A" arg2="B">
+    MULTIPOLYGON(
+      (
+        (10 10, 10 100, 50 100, 50 50, 100 50, 100 10, 10 10)), 
+      (
+        (50 100, 50 200, 200 200, 200 50, 100 50, 100 100, 50 100)))
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>AA - A with hole intersecting B</desc>
+  <a>
+    POLYGON(
+      (20 20, 20 160, 160 160, 160 20, 20 20), 
+      (140 140, 40 140, 40 40, 140 40, 140 140))
+  </a>
+  <b>
+    POLYGON(
+      (80 100, 220 100, 220 240, 80 240, 80 100))
+  </b>
+<test>
+  <op name="intersection" arg1="A" arg2="B">
+    POLYGON(
+      (80 140, 80 160, 160 160, 160 100, 140 100, 140 140, 80 140))
+  </op>
+</test>
+<test>
+  <op name="union" arg1="A" arg2="B">
+    POLYGON(
+      (20 20, 20 160, 80 160, 80 240, 220 240, 220 100, 160 100, 160 20, 20 20), 
+      (80 100, 80 140, 40 140, 40 40, 140 40, 140 100, 80 100))
+  </op>
+</test>
+<test>
+  <op name="difference" arg1="A" arg2="B">
+    POLYGON(
+      (20 20, 20 160, 80 160, 80 140, 40 140, 40 40, 140 40, 140 100, 160 100, 
+      160 20, 20 20))
+  </op>
+</test>
+<test>
+  <op name="symdifference" arg1="A" arg2="B">
+    MULTIPOLYGON(
+      (
+        (20 20, 20 160, 80 160, 80 140, 40 140, 40 40, 140 40, 140 100, 160 100, 
+        160 20, 20 20)), 
+      (
+        (80 100, 80 140, 140 140, 140 100, 80 100)), 
+      (
+        (80 160, 80 240, 220 240, 220 100, 160 100, 160 160, 80 160)))
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>AA - simple polygons #2</desc>
+  <a>
+    POLYGON(
+      (20 340, 330 380, 50 40, 20 340))
+  </a>
+  <b>
+    POLYGON(
+      (210 320, 140 270, 0 270, 140 220, 210 320))
+  </b>
+<test>
+  <op name="intersection" arg1="A" arg2="B">
+    POLYGON(
+      (27 270, 140 270, 210 320, 140 220, 28 260, 27 270))
+  </op>
+</test>
+<test>
+  <op name="union" arg1="A" arg2="B">
+    POLYGON(
+      (20 340, 330 380, 50 40, 28 260, 0 270, 27 270, 20 340))
+  </op>
+</test>
+<test>
+  <op name="difference" arg1="A" arg2="B">
+    POLYGON(
+      (20 340, 330 380, 50 40, 28 260, 140 220, 210 320, 140 270, 27 270, 20 340))
+  </op>
+</test>
+<test>
+  <op name="symdifference" arg1="A" arg2="B">
+    MULTIPOLYGON(
+      (
+        (20 340, 330 380, 50 40, 28 260, 140 220, 210 320, 140 270, 27 270, 20 340)), 
+      (
+        (27 270, 28 260, 0 270, 27 270)))
+  </op>
+</test>
+</case>
+
+<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))
+  </a>
+  <b>
+    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">
+    GEOMETRYCOLLECTION(
+      POINT(110 260), 
+      LINESTRING(110 0, 110 60), 
+      POLYGON(
+        (110 100, 40 140, 110 180, 180 140, 110 100)))
+  </op>
+</test>
+<test>
+  <op name="union" arg1="A" arg2="B">
+    POLYGON(
+      (110 0, 0 0, 0 260, 110 260, 220 260, 220 0, 110 0), 
+      (110 260, 40 220, 110 180, 180 220, 110 260), 
+      (110 100, 40 60, 110 60, 180 60, 110 100))
+  </op>
+</test>
+<test>
+  <op name="difference" arg1="A" arg2="B">
+    POLYGON(
+      (110 0, 0 0, 0 260, 110 260, 40 220, 110 180, 40 140, 110 100, 40 60, 
+      110 60, 110 0))
+  </op>
+</test>
+<test>
+  <op name="symdifference" arg1="A" arg2="B">
+    POLYGON(
+      (110 0, 0 0, 0 260, 110 260, 220 260, 220 0, 110 0), 
+      (110 260, 40 220, 110 180, 180 220, 110 260), 
+      (110 180, 40 140, 110 100, 180 140, 110 180), 
+      (110 100, 40 60, 110 60, 180 60, 110 100))
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>AA - simple polygons with two touching holes in their symDifference</desc>
+  <a>
+    POLYGON(
+      (0 0, 120 0, 120 50, 50 50, 120 100, 50 150, 120 150, 120 190, 0 190, 
+      0 0))
+  </a>
+  <b>
+    POLYGON(
+      (230 0, 120 0, 120 50, 190 50, 120 100, 190 150, 120 150, 120 190, 230 190, 
+      230 0))
+  </b>
+<test>
+  <op name="symdifference" arg1="A" arg2="B">
+    POLYGON(
+      (120 0, 0 0, 0 190, 120 190, 230 190, 230 0, 120 0), 
+      (120 100, 50 50, 120 50, 190 50, 120 100), 
+      (120 100, 190 150, 120 150, 50 150, 120 100))
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>AmA - A simple, symDiff contains inversion</desc>
+  <a>
+    POLYGON(
+      (0 0, 210 0, 210 230, 0 230, 0 0))
+  </a>
+  <b>
+    MULTIPOLYGON(
+      (
+        (40 20, 0 0, 20 40, 60 60, 40 20)), 
+      (
+        (60 90, 60 60, 90 60, 90 90, 60 90)), 
+      (
+        (70 120, 90 90, 100 120, 70 120)), 
+      (
+        (120 70, 90 90, 120 100, 120 70)))
+  </b>
+<test>
+  <op name="symdifference" arg1="A" arg2="B">
+    POLYGON(
+      (0 0, 0 230, 210 230, 210 0, 0 0), 
+      (0 0, 40 20, 60 60, 20 40, 0 0), 
+      (60 60, 90 60, 90 90, 60 90, 60 60), 
+      (90 90, 120 70, 120 100, 90 90), 
+      (90 90, 100 120, 70 120, 90 90))
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>AmA - A simple, B connected multiPolygon touching A at vertex</desc>
+  <a>
+    POLYGON(
+      (0 0, 340 0, 340 300, 0 300, 0 0))
+  </a>
+  <b>
+    MULTIPOLYGON(
+      (
+        (40 20, 0 0, 20 40, 60 60, 40 20)), 
+      (
+        (60 100, 60 60, 100 60, 100 100, 60 100)))
+  </b>
+<test>
+  <op name="intersection" arg1="A" arg2="B">
+    MULTIPOLYGON(
+      (
+        (40 20, 0 0, 20 40, 60 60, 40 20)), 
+      (
+        (60 60, 60 100, 100 100, 100 60, 60 60)))
+  </op>
+</test>
+<test>
+  <op name="symdifference" arg1="A" arg2="B">
+    POLYGON(
+      (0 0, 0 300, 340 300, 340 0, 0 0), 
+      (0 0, 40 20, 60 60, 20 40, 0 0), 
+      (60 60, 100 60, 100 100, 60 100, 60 60))
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>AmA - A simple, B connected multiPolygon touching A at interior of edge</desc>
+  <a>
+    POLYGON(
+      (0 0, 120 0, 120 120, 0 120, 0 0))
+  </a>
+  <b>
+    MULTIPOLYGON(
+      (
+        (60 20, 0 20, 60 60, 60 20)), 
+      (
+        (60 100, 60 60, 100 60, 100 100, 60 100)))
+  </b>
+<test>
+  <op name="intersection" arg1="A" arg2="B">
+    MULTIPOLYGON(
+      (
+        (60 20, 0 20, 60 60, 60 20)), 
+      (
+        (60 60, 60 100, 100 100, 100 60, 60 60)))
+  </op>
+</test>
+<test>
+  <op name="union" arg1="A" arg2="B">
+    POLYGON(
+      (0 20, 0 120, 120 120, 120 0, 0 0, 0 20))
+  </op>
+</test>
+<test>
+  <op name="difference" arg1="A" arg2="B">
+    POLYGON(
+      (0 20, 0 120, 120 120, 120 0, 0 0, 0 20), 
+      (0 20, 60 20, 60 60, 0 20), 
+      (60 60, 100 60, 100 100, 60 100, 60 60))
+  </op>
+</test>
+<test>
+  <op name="symdifference" arg1="A" arg2="B">
+    POLYGON(
+      (0 20, 0 120, 120 120, 120 0, 0 0, 0 20), 
+      (0 20, 60 20, 60 60, 0 20), 
+      (60 60, 100 60, 100 100, 60 100, 60 60))
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>AA - simple polygons with holes</desc>
+  <a>
+    POLYGON(
+      (160 330, 60 260, 20 150, 60 40, 190 20, 270 130, 260 250, 160 330), 
+      (140 240, 80 190, 90 100, 160 70, 210 130, 210 210, 140 240))
+  </a>
+  <b>
+    POLYGON(
+      (300 330, 190 270, 150 170, 150 110, 250 30, 380 50, 380 250, 300 330), 
+      (290 240, 240 200, 240 110, 290 80, 330 170, 290 240))
+  </b>
+<test>
+  <op name="intersection" arg1="A" arg2="B">
+    POLYGON(
+      (251 104, 217 57, 176 89, 210 130, 210 210, 172 226, 190 270, 217 285, 260 250, 
+      263 218, 240 200, 240 110, 251 104))
+  </op>
+</test>
+<test>
+  <op name="symdifference" arg1="A" arg2="B">
+    MULTIPOLYGON(
+      (
+        (217 57, 190 20, 60 40, 20 150, 60 260, 160 330, 217 285, 190 270, 172 226, 
+        140 240, 80 190, 90 100, 160 70, 176 89, 217 57)), 
+      (
+        (217 57, 251 104, 290 80, 330 170, 290 240, 263 218, 260 250, 217 285, 300 330, 
+        380 250, 380 50, 250 30, 217 57)), 
+      (
+        (263 218, 270 130, 251 104, 240 110, 240 200, 263 218)), 
+      (
+        (172 226, 210 210, 210 130, 176 89, 150 110, 150 170, 172 226)))
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>AA - simple polygons with hole touching shell</desc>
+  <a>
+    POLYGON ((20 0, 20 160, 200 160, 200 0, 20 0))
+  </a>
+  <b>
+    POLYGON ((220 80, 0 80, 0 240, 220 240, 220 80), 
+  	(100 80, 120 120, 80 120, 100 80))
+  </b>
+<test>
+  <op name="intersection" arg1="A" arg2="B">
+    POLYGON ((20 80, 20 160, 200 160, 200 80, 100 80, 20 80), 
+  (100 80, 120 120, 80 120, 100 80))
+  </op>
+</test>
+<test>
+  <op name="union" arg1="A" arg2="B">
+    POLYGON ((20 0, 20 80, 0 80, 0 240, 220 240, 220 80, 200 80, 200 0, 20 0))
+  </op>
+</test>
+<test>
+  <op name="difference" arg1="A" arg2="B">
+    MULTIPOLYGON (((20 0, 20 80, 100 80, 200 80, 200 0, 20 0)), 
+  ((100 80, 80 120, 120 120, 100 80)))
+  </op>
+</test>
+<test>
+  <op name="symdifference" arg1="A" arg2="B">
+    MULTIPOLYGON (((20 0, 20 80, 100 80, 200 80, 200 0, 20 0)), 
+  ((200 80, 200 160, 20 160, 20 80, 0 80, 0 240, 220 240, 220 80, 200 80)), 
+  ((100 80, 80 120, 120 120, 100 80)))
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>mAmA - complex polygons touching and overlapping</desc>
+  <a>
+    MULTIPOLYGON(
+      (
+        (120 340, 120 200, 140 200, 140 280, 160 280, 160 200, 180 200, 180 280, 200 280, 
+        200 200, 220 200, 220 340, 120 340)), 
+      (
+        (360 200, 220 200, 220 180, 300 180, 300 160, 220 160, 220 140, 300 140, 300 120, 
+        220 120, 220 100, 360 100, 360 200)))
+  </a>
+  <b>
+    MULTIPOLYGON(
+      (
+        (100 220, 100 200, 300 200, 300 220, 100 220)), 
+      (
+        (280 180, 280 160, 300 160, 300 180, 280 180)), 
+      (
+        (220 140, 220 120, 240 120, 240 140, 220 140)), 
+      (
+        (180 220, 160 240, 200 240, 180 220)))
+  </b>
+<test>
+  <op name="getboundary" arg1="A">
+    MULTILINESTRING(
+      (120 340, 120 200, 140 200, 140 280, 160 280, 160 200, 180 200, 180 280, 200 280, 
+      200 200, 220 200, 220 340, 120 340), 
+      (360 200, 220 200, 220 180, 300 180, 300 160, 220 160, 220 140, 300 140, 300 120, 
+      220 120, 220 100, 360 100, 360 200))
+  </op>
+</test>
+<test>
+  <op name="convexhull" arg1="A">
+    POLYGON(
+      (220 100, 120 200, 120 340, 220 340, 360 200, 360 100, 220 100))
+  </op>
+</test>
+<test>
+  <op name="intersection" arg1="A" arg2="B">
+    GEOMETRYCOLLECTION(
+      POINT(200 240), 
+      LINESTRING(300 200, 220 200), 
+      LINESTRING(280 180, 300 180), 
+      LINESTRING(300 180, 300 160), 
+      LINESTRING(300 160, 280 160), 
+      LINESTRING(220 140, 240 140), 
+      LINESTRING(240 120, 220 120), 
+      POLYGON(
+        (120 200, 120 220, 140 220, 140 200, 120 200)), 
+      POLYGON(
+        (160 200, 160 220, 180 220, 180 200, 160 200)), 
+      POLYGON(
+        (180 240, 180 220, 160 240, 180 240)), 
+      POLYGON(
+        (200 200, 200 220, 220 220, 220 200, 200 200)))
+  </op>
+</test>
+<test>
+  <op name="union" arg1="A" arg2="B">
+    POLYGON(
+      (120 220, 120 340, 220 340, 220 220, 300 220, 300 200, 360 200, 360 100, 220 100, 
+      220 120, 220 140, 220 160, 280 160, 280 180, 220 180, 220 200, 200 200, 180 200, 160 200, 
+      140 200, 120 200, 100 200, 100 220, 120 220), 
+      (200 240, 200 280, 180 280, 180 240, 200 240), 
+      (200 240, 180 220, 200 220, 200 240), 
+      (160 240, 160 280, 140 280, 140 220, 160 220, 160 240), 
+      (240 120, 300 120, 300 140, 240 140, 240 120))
+  </op>
+</test>
+<test>
+  <op name="difference" arg1="A" arg2="B">
+    MULTIPOLYGON(
+      (
+        (120 220, 120 340, 220 340, 220 220, 200 220, 200 240, 200 280, 180 280, 180 240, 
+        160 240, 160 280, 140 280, 140 220, 120 220)), 
+      (
+        (160 220, 160 240, 180 220, 160 220)), 
+      (
+        (300 200, 360 200, 360 100, 220 100, 220 120, 240 120, 300 120, 300 140, 240 140, 
+        220 140, 220 160, 280 160, 300 160, 300 180, 280 180, 220 180, 220 200, 300 200)))
+  </op>
+</test>
+<test>
+  <op name="symdifference" arg1="A" arg2="B">
+    MULTIPOLYGON(
+      (
+        (120 220, 120 340, 220 340, 220 220, 200 220, 200 240, 200 280, 180 280, 180 240, 
+        160 240, 160 280, 140 280, 140 220, 120 220)), 
+      (
+        (120 220, 120 200, 100 200, 100 220, 120 220)), 
+      (
+        (140 200, 140 220, 160 220, 160 200, 140 200)), 
+      (
+        (160 220, 160 240, 180 220, 160 220)), 
+      (
+        (180 200, 180 220, 200 220, 200 200, 180 200)), 
+      (
+        (180 220, 180 240, 200 240, 180 220)), 
+      (
+        (220 200, 220 220, 300 220, 300 200, 360 200, 360 100, 220 100, 220 120, 220 140, 
+        220 160, 280 160, 280 180, 220 180, 220 200), 
+        (240 120, 300 120, 300 140, 240 140, 240 120)))
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>mAmA - complex polygons touching</desc>
+  <a>
+    MULTIPOLYGON(
+      (
+        (100 200, 100 180, 120 180, 120 200, 100 200)), 
+      (
+        (60 240, 60 140, 220 140, 220 160, 160 160, 160 180, 200 180, 200 200, 160 200, 
+        160 220, 220 220, 220 240, 60 240), 
+        (80 220, 80 160, 140 160, 140 220, 80 220)), 
+      (
+        (280 220, 240 180, 260 160, 300 200, 280 220)))
+  </a>
+  <b>
+    MULTIPOLYGON(
+      (
+        (80 220, 80 160, 140 160, 140 220, 80 220), 
+        (100 200, 100 180, 120 180, 120 200, 100 200)), 
+      (
+        (220 240, 220 220, 160 220, 160 200, 220 200, 220 180, 160 180, 160 160, 220 160, 
+        220 140, 320 140, 320 240, 220 240), 
+        (240 220, 240 160, 300 160, 300 220, 240 220)))
+  </b>
+<test>
+  <op name="getboundary" arg1="A">
+    MULTILINESTRING(
+      (100 200, 100 180, 120 180, 120 200, 100 200), 
+      (60 240, 60 140, 220 140, 220 160, 160 160, 160 180, 200 180, 200 200, 160 200, 
+      160 220, 220 220, 220 240, 60 240), 
+      (80 220, 80 160, 140 160, 140 220, 80 220), 
+      (280 220, 240 180, 260 160, 300 200, 280 220))
+  </op>
+</test>
+<test>
+  <op name="convexhull" arg1="A">
+    POLYGON(
+      (60 140, 60 240, 220 240, 280 220, 300 200, 260 160, 220 140, 60 140))
+  </op>
+</test>
+<test>
+  <op name="intersection" arg1="A" arg2="B">
+    GEOMETRYCOLLECTION(
+      POINT(240 180), 
+      POINT(260 160), 
+      POINT(280 220), 
+      POINT(300 200), 
+      LINESTRING(100 200, 100 180), 
+      LINESTRING(100 180, 120 180), 
+      LINESTRING(120 180, 120 200), 
+      LINESTRING(120 200, 100 200), 
+      LINESTRING(220 140, 220 160), 
+      LINESTRING(220 160, 160 160), 
+      LINESTRING(160 160, 160 180), 
+      LINESTRING(160 180, 200 180), 
+      LINESTRING(200 200, 160 200), 
+      LINESTRING(160 200, 160 220), 
+      LINESTRING(160 220, 220 220), 
+      LINESTRING(220 220, 220 240), 
+      LINESTRING(80 220, 80 160), 
+      LINESTRING(80 160, 140 160), 
+      LINESTRING(140 160, 140 220), 
+      LINESTRING(140 220, 80 220))
+  </op>
+</test>
+<test>
+  <op name="union" arg1="A" arg2="B">
+    MULTIPOLYGON(
+      (
+        (220 140, 60 140, 60 240, 220 240, 320 240, 320 140, 220 140), 
+        (200 200, 200 180, 220 180, 220 200, 200 200), 
+        (240 220, 240 180, 240 160, 260 160, 300 160, 300 200, 300 220, 280 220, 240 220)), 
+      (
+        (240 180, 280 220, 300 200, 260 160, 240 180)))
+  </op>
+</test>
+<test>
+  <op name="difference" arg1="A" arg2="B">
+    MULTIPOLYGON(
+      (
+        (100 180, 100 200, 120 200, 120 180, 100 180)), 
+      (
+        (220 140, 60 140, 60 240, 220 240, 220 220, 160 220, 160 200, 200 200, 200 180, 
+        160 180, 160 160, 220 160, 220 140), 
+        (80 220, 80 160, 140 160, 140 220, 80 220)), 
+      (
+        (240 180, 280 220, 300 200, 260 160, 240 180)))
+  </op>
+</test>
+<test>
+  <op name="symdifference" arg1="A" arg2="B">
+    MULTIPOLYGON(
+      (
+        (220 140, 60 140, 60 240, 220 240, 320 240, 320 140, 220 140), 
+        (200 200, 200 180, 220 180, 220 200, 200 200), 
+        (240 220, 240 180, 240 160, 260 160, 300 160, 300 200, 300 220, 280 220, 240 220)), 
+      (
+        (240 180, 280 220, 300 200, 260 160, 240 180)))
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>AA - hole intersecting boundary to produce line</desc>
+  <a>
+    POLYGON(
+      (60 160, 140 160, 140 60, 60 60, 60 160))
+  </a>
+  <b>
+    POLYGON(
+      (160 160, 100 160, 100 100, 160 100, 160 160), 
+      (140 140, 120 140, 120 120, 140 120, 140 140))
+  </b>
+<test>
+  <op name="getboundary" arg1="A">
+    LINESTRING(60 160, 140 160, 140 60, 60 60, 60 160)
+  </op>
+</test>
+<test>
+  <op name="convexhull" arg1="A">
+    POLYGON(
+      (60 60, 60 160, 140 160, 140 60, 60 60))
+  </op>
+</test>
+<test>
+  <op name="intersection" arg1="A" arg2="B">
+    GEOMETRYCOLLECTION(
+      LINESTRING(140 140, 140 120), 
+      POLYGON(
+        (100 160, 140 160, 140 140, 120 140, 120 120, 140 120, 140 100, 100 100, 100 160)))
+  </op>
+</test>
+<test>
+  <op name="union" arg1="A" arg2="B">
+    POLYGON(
+      (60 160, 100 160, 140 160, 160 160, 160 100, 140 100, 140 60, 60 60, 60 160))
+  </op>
+</test>
+<test>
+  <op name="difference" arg1="A" arg2="B">
+    MULTIPOLYGON(
+      (
+        (60 160, 100 160, 100 100, 140 100, 140 60, 60 60, 60 160)), 
+      (
+        (140 140, 140 120, 120 120, 120 140, 140 140)))
+  </op>
+</test>
+<test>
+  <op name="symdifference" arg1="A" arg2="B">
+    MULTIPOLYGON(
+      (
+        (60 160, 100 160, 100 100, 140 100, 140 60, 60 60, 60 160)), 
+      (
+        (140 140, 140 160, 160 160, 160 100, 140 100, 140 120, 120 120, 120 140, 140 140)))
+  </op>
+</test>
+</case>
+
+</run>

Added: packages/jts/branches/upstream/current/test/vivid/TestFunctionAAPrec.xml
===================================================================
--- packages/jts/branches/upstream/current/test/vivid/TestFunctionAAPrec.xml	                        (rev 0)
+++ packages/jts/branches/upstream/current/test/vivid/TestFunctionAAPrec.xml	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,830 @@
+<run>
+  <precisionModel scale="1.0" offsetx="0.0" offsety="0.0"/>
+
+<case>
+  <desc>AA - sliver triangle, cut by polygon</desc>
+  <a>
+    POLYGON(
+      (10 10, 100 10, 10 11, 10 10))
+  </a>
+  <b>
+    POLYGON(
+      (90 0, 200 0, 200 200, 90 200, 90 0))
+  </b>
+<test>
+  <op name="relate" arg3="212101212" arg1="A" arg2="B">
+    true
+  </op>
+</test>
+<test>
+  <op name="intersection" arg1="A" arg2="B">
+    LINESTRING(90 10, 100 10)
+  </op>
+</test>
+<test>
+  <op name="union" arg1="A" arg2="B">
+    MULTIPOLYGON(
+      (
+        (90 10, 10 10, 10 11, 90 10)), 
+      (
+        (90 10, 90 200, 200 200, 200 0, 90 0, 90 10)))
+  </op>
+</test>
+<test>
+  <op name="difference" arg1="A" arg2="B">
+    POLYGON(
+      (90 10, 10 10, 10 11, 90 10))
+  </op>
+</test>
+<test>
+  <op name="symdifference" arg1="A" arg2="B">
+    MULTIPOLYGON(
+      (
+        (90 10, 10 10, 10 11, 90 10)), 
+      (
+        (90 10, 90 200, 200 200, 200 0, 90 0, 90 10)))
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>AA - polygon with outward sliver, cut by polygon</desc>
+  <a>
+    POLYGON(
+      (100 10, 10 10, 90 11, 90 20, 100 20, 100 10))
+  </a>
+  <b>
+    POLYGON(
+      (20 20, 0 20, 0 0, 20 0, 20 20))
+  </b>
+<test>
+  <op name="relate" arg3="212101212" arg1="A" arg2="B">
+    true
+  </op>
+</test>
+<test>
+  <op name="intersection" arg1="A" arg2="B">
+    LINESTRING(20 10, 10 10)
+  </op>
+</test>
+</case>
+
+<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))
+  </a>
+  <b>
+    POLYGON(
+      (60 40, 40 40, 40 20, 60 20, 60 40))
+  </b>
+<test>
+  <op name="relate" arg3="212101212" arg1="A" arg2="B">
+    true
+  </op>
+</test>
+<test>
+  <op name="intersection" arg1="A" arg2="B">
+    POLYGON(
+      (50 40, 50 20, 40 20, 40 30, 40 40, 50 40))
+  </op>
+</test>
+<test>
+  <op name="union" arg1="A" arg2="B">
+    POLYGON(
+      (50 20, 50 10, 10 10, 10 30, 40 30, 10 31, 10 50, 50 50, 50 40, 
+      60 40, 60 20, 50 20))
+  </op>
+</test>
+<test>
+  <op name="difference" arg1="A" arg2="B">
+    MULTIPOLYGON(
+      (
+        (50 20, 50 10, 10 10, 10 30, 40 30, 40 20, 50 20)), 
+      (
+        (40 30, 10 31, 10 50, 50 50, 50 40, 40 40, 40 30)))
+  </op>
+</test>
+<test>
+  <op name="symdifference" arg1="A" arg2="B">
+    MULTIPOLYGON(
+      (
+        (50 20, 50 10, 10 10, 10 30, 40 30, 40 20, 50 20)), 
+      (
+        (50 20, 50 40, 60 40, 60 20, 50 20)), 
+      (
+        (40 30, 10 31, 10 50, 50 50, 50 40, 40 40, 40 30)))
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>AA - hole close to shell</desc>
+  <a>
+    POLYGON(
+      (10 100, 10 10, 100 10, 100 100, 10 100), 
+      (90 90, 11 90, 10 10, 90 11, 90 90))
+  </a>
+  <b>
+    POLYGON(
+      (0 30, 0 0, 30 0, 30 30, 0 30))
+  </b>
+<test>
+  <op name="relate" arg3="212101212" arg1="A" arg2="B">
+    true
+  </op>
+</test>
+<test>
+  <op name="intersection" arg1="A" arg2="B">
+    MULTILINESTRING(
+      (10 30, 10 10), 
+      (10 10, 30 10))
+  </op>
+</test>
+<test>
+  <op name="union" arg1="A" arg2="B">
+    MULTIPOLYGON(
+      (
+        (10 30, 10 100, 100 100, 100 10, 30 10, 90 11, 90 90, 11 90, 10 30)), 
+      (
+        (30 10, 30 0, 0 0, 0 30, 10 30, 30 30, 30 10)))
+  </op>
+</test>
+<test>
+  <op name="difference" arg1="A" arg2="B">
+    POLYGON(
+      (10 30, 10 100, 100 100, 100 10, 30 10, 90 11, 90 90, 11 90, 10 30))
+  </op>
+</test>
+<test>
+  <op name="symdifference" arg1="A" arg2="B">
+    MULTIPOLYGON(
+      (
+        (10 30, 10 100, 100 100, 100 10, 30 10, 90 11, 90 90, 11 90, 10 30)), 
+      (
+        (30 10, 30 0, 0 0, 0 30, 10 30, 30 30, 30 10)))
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>mAA - shells close together</desc>
+  <a>
+    MULTIPOLYGON(
+      (
+        (0 0, 100 0, 100 20, 0 20, 0 0)), 
+      (
+        (0 40, 0 21, 100 20, 100 40, 0 40)))
+  </a>
+  <b>
+    POLYGON(
+      (110 30, 90 30, 90 10, 110 10, 110 30))
+  </b>
+<test>
+  <op name="relate" arg3="212101212" arg1="A" arg2="B">
+    true
+  </op>
+</test>
+<test>
+  <op name="intersection" arg1="A" arg2="B">
+    GEOMETRYCOLLECTION(
+      LINESTRING(100 20, 90 20), 
+      POLYGON(
+        (100 20, 100 10, 90 10, 90 20, 90 30, 100 30, 100 20)))
+  </op>
+</test>
+<test>
+  <op name="union" arg1="A" arg2="B">
+    POLYGON(
+      (100 10, 100 0, 0 0, 0 20, 90 20, 0 21, 0 40, 100 40, 100 30, 
+      110 30, 110 10, 100 10))
+  </op>
+</test>
+<test>
+  <op name="difference" arg1="A" arg2="B">
+    MULTIPOLYGON(
+      (
+        (100 10, 100 0, 0 0, 0 20, 90 20, 90 10, 100 10)), 
+      (
+        (90 20, 0 21, 0 40, 100 40, 100 30, 90 30, 90 20)))
+  </op>
+</test>
+<test>
+  <op name="symdifference" arg1="A" arg2="B">
+    MULTIPOLYGON(
+      (
+        (100 10, 100 0, 0 0, 0 20, 90 20, 90 10, 100 10)), 
+      (
+        (100 10, 100 20, 100 30, 110 30, 110 10, 100 10)), 
+      (
+        (90 20, 0 21, 0 40, 100 40, 100 30, 90 30, 90 20)))
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>AA - A sliver triangle cutting all the way across B</desc>
+  <a>
+    POLYGON(
+      (100 10, 0 10, 100 11, 100 10))
+  </a>
+  <b>
+    POLYGON(
+      (20 20, 0 20, 0 0, 20 0, 20 20))
+  </b>
+<test>
+  <op name="relate" arg3="212101212" arg1="A" arg2="B">
+    true
+  </op>
+</test>
+<test>
+  <op name="intersection" arg1="A" arg2="B">
+    LINESTRING(20 10, 0 10)
+  </op>
+</test>
+<test>
+  <op name="union" arg1="A" arg2="B">
+    MULTIPOLYGON(
+      (
+        (100 10, 20 10, 100 11, 100 10)), 
+      (
+        (0 10, 0 20, 20 20, 20 10, 20 0, 0 0, 0 10)))
+  </op>
+</test>
+<test>
+  <op name="difference" arg1="A" arg2="B">
+    POLYGON(
+      (100 10, 20 10, 100 11, 100 10))
+  </op>
+</test>
+<test>
+  <op name="symdifference" arg1="A" arg2="B">
+    MULTIPOLYGON(
+      (
+        (100 10, 20 10, 100 11, 100 10)), 
+      (
+        (0 10, 0 20, 20 20, 20 10, 20 0, 0 0, 0 10)))
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>AA - A polygon with sliver cutting all the way across B</desc>
+  <a>
+    POLYGON(
+      (100 10, 0 10, 90 11, 90 20, 100 20, 100 10))
+  </a>
+  <b>
+    POLYGON(
+      (20 20, 0 20, 0 0, 20 0, 20 20))
+  </b>
+<test>
+  <op name="relate" arg3="212101212" arg1="A" arg2="B">
+    true
+  </op>
+</test>
+<test>
+  <op name="intersection" arg1="A" arg2="B">
+    LINESTRING(20 10, 0 10)
+  </op>
+</test>
+<test>
+  <op name="union" arg1="A" arg2="B">
+    MULTIPOLYGON(
+      (
+        (100 10, 20 10, 90 11, 90 20, 100 20, 100 10)), 
+      (
+        (0 10, 0 20, 20 20, 20 10, 20 0, 0 0, 0 10)))
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>AA - hole close to shell, B coincident with A</desc>
+  <a>
+    POLYGON(
+      (10 100, 10 10, 100 10, 100 100, 10 100), 
+      (90 90, 11 90, 10 10, 90 11, 90 90))
+  </a>
+  <b>
+    POLYGON(
+      (10 30, 10 0, 30 10, 30 30, 10 30))
+  </b>
+<test>
+  <op name="relate" arg3="212111212" arg1="A" arg2="B">
+    true
+  </op>
+</test>
+<test>
+  <op name="intersection" arg1="A" arg2="B">
+    MULTILINESTRING(
+      (10 30, 10 10), 
+      (10 10, 30 10))
+  </op>
+</test>
+<test>
+  <op name="union" arg1="A" arg2="B">
+    MULTIPOLYGON(
+      (
+        (10 30, 10 100, 100 100, 100 10, 30 10, 90 11, 90 90, 11 90, 10 30)), 
+      (
+        (10 10, 10 30, 30 30, 30 10, 10 0, 10 10)))
+  </op>
+</test>
+<test>
+  <op name="difference" arg1="A" arg2="B">
+    POLYGON(
+      (10 30, 10 100, 100 100, 100 10, 30 10, 90 11, 90 90, 11 90, 10 30))
+  </op>
+</test>
+<test>
+  <op name="symdifference" arg1="A" arg2="B">
+    MULTIPOLYGON(
+      (
+        (10 30, 10 100, 100 100, 100 10, 30 10, 90 11, 90 90, 11 90, 10 30)), 
+      (
+        (10 10, 10 30, 30 30, 30 10, 10 0, 10 10)))
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>AA - A hole close to shell, B coincident with A</desc>
+  <a>
+    POLYGON(
+      (10 100, 10 10, 100 10, 100 100, 10 100), 
+      (90 90, 11 90, 10 10, 90 11, 90 90))
+  </a>
+  <b>
+    POLYGON(
+      (10 30, 10 10, 30 10, 30 30, 10 30))
+  </b>
+<test>
+  <op name="relate" arg3="212111212" arg1="A" arg2="B">
+    true
+  </op>
+</test>
+<test>
+  <op name="intersection" arg1="A" arg2="B">
+    MULTILINESTRING(
+      (10 30, 10 10), 
+      (10 10, 30 10))
+  </op>
+</test>
+<test>
+  <op name="union" arg1="A" arg2="B">
+    MULTIPOLYGON(
+      (
+        (10 30, 10 100, 100 100, 100 10, 30 10, 90 11, 90 90, 11 90, 10 30)), 
+      (
+        (10 10, 10 30, 30 30, 30 10, 10 10)))
+  </op>
+</test>
+<test>
+  <op name="difference" arg1="A" arg2="B">
+    POLYGON(
+      (10 30, 10 100, 100 100, 100 10, 30 10, 90 11, 90 90, 11 90, 10 30))
+  </op>
+</test>
+<test>
+  <op name="symdifference" arg1="A" arg2="B">
+    MULTIPOLYGON(
+      (
+        (10 30, 10 100, 100 100, 100 10, 30 10, 90 11, 90 90, 11 90, 10 30)), 
+      (
+        (10 10, 10 30, 30 30, 30 10, 10 10)))
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>AA - B hole close to shell, A coincident with B</desc>
+  <a>
+    POLYGON(
+      (10 30, 10 10, 30 10, 30 30, 10 30))
+  </a>
+  <b>
+    POLYGON(
+      (10 100, 10 10, 100 10, 100 100, 10 100), 
+      (90 90, 11 90, 10 10, 90 11, 90 90))
+  </b>
+<test>
+  <op name="relate" arg3="212111212" arg1="A" arg2="B">
+    true
+  </op>
+</test>
+<test>
+  <op name="intersection" arg1="A" arg2="B">
+    MULTILINESTRING(
+      (10 30, 10 10), 
+      (10 10, 30 10))
+  </op>
+</test>
+<test>
+  <op name="union" arg1="A" arg2="B">
+    MULTIPOLYGON(
+      (
+        (10 30, 10 100, 100 100, 100 10, 30 10, 90 11, 90 90, 11 90, 10 30)), 
+      (
+        (10 10, 10 30, 30 30, 30 10, 10 10)))
+  </op>
+</test>
+<test>
+  <op name="difference" arg1="A" arg2="B">
+    POLYGON(
+      (10 10, 10 30, 30 30, 30 10, 10 10))
+  </op>
+</test>
+<test>
+  <op name="symdifference" arg1="A" arg2="B">
+    MULTIPOLYGON(
+      (
+        (10 30, 10 100, 100 100, 100 10, 30 10, 90 11, 90 90, 11 90, 10 30)), 
+      (
+        (10 10, 10 30, 30 30, 30 10, 10 10)))
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>AA - B sliver crossing A triangle in line segment with length &lt; 1</desc>
+  <a>
+    POLYGON(
+      (0 0, 200 0, 0 198, 0 0))
+  </a>
+  <b>
+    POLYGON(
+      (280 60, 139 60, 280 70, 280 60))
+  </b>
+<test>
+  <op name="relate" arg3="212101212" arg1="A" arg2="B">
+    true
+  </op>
+</test>
+<test>
+  <op name="intersection" arg1="A" arg2="B">
+    POINT(139 60)
+  </op>
+</test>
+<test>
+  <op name="union" arg1="A" arg2="B">
+    MULTIPOLYGON(
+      (
+        (139 60, 200 0, 0 0, 0 198, 139 60)), 
+      (
+        (280 60, 139 60, 280 70, 280 60)))
+  </op>
+</test>
+<test>
+  <op name="difference" arg1="A" arg2="B">
+    POLYGON(
+      (139 60, 200 0, 0 0, 0 198, 139 60))
+  </op>
+</test>
+<test>
+  <op name="symdifference" arg1="A" arg2="B">
+    MULTIPOLYGON(
+      (
+        (139 60, 200 0, 0 0, 0 198, 139 60)), 
+      (
+        (280 60, 139 60, 280 70, 280 60)))
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>AA - sliver triangles, at angle to each other</desc>
+  <a>
+    POLYGON(
+      (0 0, 140 10, 0 20, 0 0))
+  </a>
+  <b>
+    POLYGON(
+      (280 0, 139 10, 280 1, 280 0))
+  </b>
+<test>
+  <op name="intersection" arg1="A" arg2="B">
+    LINESTRING(140 10, 139 10)
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>AA - sliver triangle with multiple intersecting boxes</desc>
+  <a>
+    MULTIPOLYGON(
+      (
+        (1 4, 1 1, 2 1, 2 4, 1 4)), 
+      (
+        (3 4, 3 1, 4 1, 4 4, 3 4)), 
+      (
+        (5 4, 5 1, 6 1, 6 4, 5 4)), 
+      (
+        (7 4, 7 1, 8 1, 8 4, 7 4)), 
+      (
+        (9 4, 9 1, 10 1, 10 4, 9 4)))
+  </a>
+  <b>
+    POLYGON(
+      (0 2, 11 3, 11 2, 0 2))
+  </b>
+<test>
+  <op name="convexhull" arg1="A">
+    POLYGON(
+      (1 1, 1 4, 10 4, 10 1, 1 1))
+  </op>
+</test>
+<test>
+  <op name="intersection" arg1="A" arg2="B">
+    GEOMETRYCOLLECTION(
+      LINESTRING(1 2, 2 2), 
+      LINESTRING(3 2, 4 2), 
+      POLYGON(
+        (6 3, 6 2, 5 2, 6 3)), 
+      POLYGON(
+        (7 2, 7 3, 8 3, 8 2, 7 2)), 
+      POLYGON(
+        (9 2, 9 3, 10 3, 10 2, 9 2)))
+  </op>
+</test>
+<test>
+  <op name="union" arg1="A" arg2="B">
+GEOMETRYCOLLECTION(
+  LINESTRING(0 2, 1 2), 
+  LINESTRING(2 2, 3 2), 
+  LINESTRING(4 2, 5 2), 
+  POLYGON(
+    (1 2, 1 4, 2 4, 2 2, 2 1, 1 1, 1 2)), 
+  POLYGON(
+    (3 2, 3 4, 4 4, 4 2, 4 1, 3 1, 3 2)), 
+  POLYGON(
+    (5 2, 5 4, 6 4, 6 3, 7 3, 7 4, 8 4, 8 3, 9 3, 
+    9 4, 10 4, 10 3, 11 3, 11 2, 10 2, 10 1, 9 1, 9 2, 8 2, 
+    8 1, 7 1, 7 2, 6 2, 6 1, 5 1, 5 2)))  </op>
+</test>
+<test>
+  <op name="difference" arg1="A" arg2="B">
+    MULTIPOLYGON(
+      (
+        (1 2, 1 4, 2 4, 2 2, 2 1, 1 1, 1 2)), 
+      (
+        (3 2, 3 4, 4 4, 4 2, 4 1, 3 1, 3 2)), 
+      (
+        (5 2, 5 4, 6 4, 6 3, 5 2)), 
+      (
+        (6 2, 6 1, 5 1, 5 2, 6 2)), 
+      (
+        (7 3, 7 4, 8 4, 8 3, 7 3)), 
+      (
+        (8 2, 8 1, 7 1, 7 2, 8 2)), 
+      (
+        (9 3, 9 4, 10 4, 10 3, 9 3)), 
+      (
+        (10 2, 10 1, 9 1, 9 2, 10 2)))
+  </op>
+</test>
+<test>
+  <op name="symdifference" arg1="A" arg2="B">
+GEOMETRYCOLLECTION(
+  LINESTRING(0 2, 1 2), 
+  LINESTRING(2 2, 3 2), 
+  LINESTRING(4 2, 5 2), 
+  POLYGON(
+    (1 2, 1 4, 2 4, 2 2, 2 1, 1 1, 1 2)), 
+  POLYGON(
+    (3 2, 3 4, 4 4, 4 2, 4 1, 3 1, 3 2)), 
+  POLYGON(
+    (5 2, 5 4, 6 4, 6 3, 5 2)), 
+  POLYGON(
+    (6 2, 6 1, 5 1, 5 2, 6 2)), 
+  POLYGON(
+    (6 2, 6 3, 7 3, 7 2, 6 2)), 
+  POLYGON(
+    (7 3, 7 4, 8 4, 8 3, 7 3)), 
+  POLYGON(
+    (8 2, 8 1, 7 1, 7 2, 8 2)), 
+  POLYGON(
+    (8 2, 8 3, 9 3, 9 2, 8 2)), 
+  POLYGON(
+    (9 3, 9 4, 10 4, 10 3, 9 3)), 
+  POLYGON(
+    (10 2, 10 1, 9 1, 9 2, 10 2)), 
+  POLYGON(
+    (10 2, 10 3, 11 3, 11 2, 10 2)))
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>AA - Polygon with hole with outward sliver, cut by polygon</desc>
+  <a>
+    POLYGON(
+      (20 40, 20 200, 180 200, 180 40, 20 40), 
+      (180 120, 120 120, 120 160, 60 120, 120 80, 120 119, 180 120))
+  </a>
+  <b>
+    POLYGON(
+      (200 160, 160 160, 160 80, 200 80, 200 160))
+  </b>
+<test>
+  <op name="convexhull" arg1="A">
+    POLYGON(
+      (20 40, 20 200, 180 200, 180 40, 20 40))
+  </op>
+</test>
+<test>
+  <op name="intersection" arg1="A" arg2="B">
+    GEOMETRYCOLLECTION(
+      LINESTRING(180 120, 160 120), 
+      POLYGON(
+        (180 160, 180 120, 180 80, 160 80, 160 120, 160 160, 180 160)))
+  </op>
+</test>
+<test>
+  <op name="union" arg1="A" arg2="B">
+    POLYGON(
+      (20 40, 20 200, 180 200, 180 160, 200 160, 200 80, 180 80, 180 40, 20 40), 
+      (160 120, 120 120, 120 160, 60 120, 120 80, 120 119, 160 120))
+  </op>
+</test>
+<test>
+  <op name="difference" arg1="A" arg2="B">
+    POLYGON(
+      (20 40, 20 200, 180 200, 180 160, 160 160, 160 120, 160 80, 180 80, 180 40, 
+      20 40), 
+      (160 120, 120 120, 120 160, 60 120, 120 80, 120 119, 160 120))
+  </op>
+</test>
+<test>
+  <op name="symdifference" arg1="A" arg2="B">
+    MULTIPOLYGON(
+      (
+        (20 40, 20 200, 180 200, 180 160, 160 160, 160 120, 160 80, 180 80, 180 40, 
+        20 40), 
+        (160 120, 120 120, 120 160, 60 120, 120 80, 120 119, 160 120)), 
+      (
+        (180 120, 180 160, 200 160, 200 80, 180 80, 180 120)))
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>AA - Polygon with hole with outward sliver, cut by line</desc>
+  <a>
+    POLYGON(
+      (20 40, 20 200, 180 200, 180 40, 20 40), 
+      (180 120, 120 120, 120 160, 60 120, 120 80, 120 119, 180 120))
+  </a>
+  <b>
+    LINESTRING(160 140, 160 100)
+  </b>
+<test>
+  <op name="convexhull" arg1="A">
+    POLYGON(
+      (20 40, 20 200, 180 200, 180 40, 20 40))
+  </op>
+</test>
+<test>
+  <op name="intersection" arg1="A" arg2="B">
+    MULTILINESTRING(
+      (160 140, 160 120), 
+      (160 120, 160 100))
+  </op>
+</test>
+<test>
+  <op name="union" arg1="A" arg2="B">
+    POLYGON(
+      (20 40, 20 200, 180 200, 180 120, 180 40, 20 40), 
+      (160 120, 120 120, 120 160, 60 120, 120 80, 120 119, 160 120))
+  </op>
+</test>
+<test>
+  <op name="difference" arg1="A" arg2="B">
+    POLYGON(
+      (20 40, 20 200, 180 200, 180 120, 180 40, 20 40), 
+      (160 120, 120 120, 120 160, 60 120, 120 80, 120 119, 160 120))
+  </op>
+</test>
+<test>
+  <op name="symdifference" arg1="A" arg2="B">
+    POLYGON(
+      (20 40, 20 200, 180 200, 180 120, 180 40, 20 40), 
+      (160 120, 120 120, 120 160, 60 120, 120 80, 120 119, 160 120))
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>AA - Polygon with inward sliver touching hole, cut by polygon</desc>
+  <a>
+    POLYGON(
+      (20 40, 20 200, 180 200, 180 120, 140 120, 180 119, 180 40, 20 40), 
+      (140 160, 80 120, 140 80, 140 160))
+  </a>
+  <b>
+    POLYGON(
+      (200 160, 150 160, 150 80, 200 80, 200 160))
+  </b>
+<test>
+  <op name="convexhull" arg1="A">
+    POLYGON(
+      (20 40, 20 200, 180 200, 180 40, 20 40))
+  </op>
+</test>
+<test>
+  <op name="intersection" arg1="A" arg2="B">
+    MULTIPOLYGON(
+      (
+        (180 160, 180 120, 150 120, 150 160, 180 160)), 
+      (
+        (150 120, 180 119, 180 80, 150 80, 150 120)))
+  </op>
+</test>
+<test>
+  <op name="union" arg1="A" arg2="B">
+    POLYGON(
+      (20 40, 20 200, 180 200, 180 160, 200 160, 200 80, 180 80, 180 40, 20 40), 
+      (140 160, 80 120, 140 80, 140 120, 140 160))
+  </op>
+</test>
+<test>
+  <op name="difference" arg1="A" arg2="B">
+    POLYGON(
+      (20 40, 20 200, 180 200, 180 160, 150 160, 150 120, 150 80, 180 80, 180 40, 
+      20 40), 
+      (140 160, 80 120, 140 80, 140 120, 140 160))
+  </op>
+</test>
+<test>
+  <op name="symdifference" arg1="A" arg2="B">
+    MULTIPOLYGON(
+      (
+        (20 40, 20 200, 180 200, 180 160, 150 160, 150 120, 150 80, 180 80, 180 40, 
+        20 40), 
+        (140 160, 80 120, 140 80, 140 120, 140 160)), 
+      (
+        (150 120, 180 120, 180 160, 200 160, 200 80, 180 80, 180 119, 150 120)))
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>AA - intersecting slivers, dimensional collapse</desc>
+  <a>
+    POLYGON(
+      (83 33, 62 402, 68 402, 83 33))
+  </a>
+  <b>
+    POLYGON(
+      (78 39, 574 76, 576 60, 78 39))
+  </b>
+<test>
+  <op name="getboundary" arg1="A">
+    LINESTRING(83 33, 62 402, 68 402, 83 33)
+  </op>
+</test>
+<test>
+  <op name="convexhull" arg1="A">
+    POLYGON(
+      (83 33, 62 402, 68 402, 83 33))
+  </op>
+</test>
+<test>
+  <op name="intersection" arg1="A" arg2="B">
+    POINT(83 39)
+  </op>
+</test>
+<test>
+  <op name="union" arg1="A" arg2="B">
+    GEOMETRYCOLLECTION(
+      LINESTRING(78 39, 83 39), 
+      LINESTRING(83 33, 83 39), 
+      POLYGON(
+        (83 39, 62 402, 68 402, 83 39)), 
+      POLYGON(
+        (83 39, 574 76, 576 60, 83 39)))
+  </op>
+</test>
+<test>
+  <op name="difference" arg1="A" arg2="B">
+    GEOMETRYCOLLECTION(
+      LINESTRING(83 33, 83 39), 
+      POLYGON(
+        (83 39, 62 402, 68 402, 83 39)))
+  </op>
+</test>
+<test>
+  <op name="symdifference" arg1="A" arg2="B">
+    GEOMETRYCOLLECTION(
+      LINESTRING(78 39, 83 39), 
+      LINESTRING(83 33, 83 39), 
+      POLYGON(
+        (83 39, 62 402, 68 402, 83 39)), 
+      POLYGON(
+        (83 39, 574 76, 576 60, 83 39)))
+  </op>
+</test>
+</case>
+
+</run>

Added: packages/jts/branches/upstream/current/test/vivid/TestFunctionLA.xml
===================================================================
--- packages/jts/branches/upstream/current/test/vivid/TestFunctionLA.xml	                        (rev 0)
+++ packages/jts/branches/upstream/current/test/vivid/TestFunctionLA.xml	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,522 @@
+<run>
+  <precisionModel scale="1.0" offsetx="0.0" offsety="0.0"/>
+
+<case>
+  <desc>LA - A and B simple</desc>
+  <a>
+    LINESTRING(240 190, 120 120)
+  </a>
+  <b>
+    POLYGON(
+      (110 240, 50 80, 240 70, 110 240))
+  </b>
+<test>
+  <op name="intersection" arg1="A" arg2="B">
+    LINESTRING(177 153, 120 120)
+  </op>
+</test>
+<test>
+  <op name="union" arg1="A" arg2="B">
+    GEOMETRYCOLLECTION(
+      LINESTRING(240 190, 177 153), 
+      POLYGON(
+        (177 153, 240 70, 50 80, 110 240, 177 153)))
+  </op>
+</test>
+<test>
+  <op name="difference" arg1="A" arg2="B">
+    LINESTRING(240 190, 177 153)
+  </op>
+</test>
+<test>
+  <op name="symdifference" arg1="A" arg2="B">
+    GEOMETRYCOLLECTION(
+      LINESTRING(240 190, 177 153), 
+      POLYGON(
+        (177 153, 240 70, 50 80, 110 240, 177 153)))
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>LA - A intersects B-hole</desc>
+  <a>
+    LINESTRING(0 100, 100 100, 200 200)
+  </a>
+  <b>
+    POLYGON(
+      (30 240, 260 30, 30 30, 30 240), 
+      (80 140, 80 80, 140 80, 80 140))
+  </b>
+<test>
+  <op name="intersection" arg1="A" arg2="B">
+    MULTILINESTRING(
+      (30 100, 80 100), 
+      (110 110, 140 140))
+  </op>
+</test>
+<test>
+  <op name="union" arg1="A" arg2="B">
+    GEOMETRYCOLLECTION(
+      LINESTRING(0 100, 30 100), 
+      LINESTRING(80 100, 100 100, 110 110), 
+      LINESTRING(140 140, 200 200), 
+      POLYGON(
+        (30 240, 140 140, 260 30, 30 30, 30 100, 30 240), 
+        (80 140, 80 100, 80 80, 140 80, 110 110, 80 140)))
+  </op>
+</test>
+<test>
+  <op name="difference" arg1="A" arg2="B">
+    MULTILINESTRING(
+      (0 100, 30 100), 
+      (80 100, 100 100, 110 110), 
+      (140 140, 200 200))
+  </op>
+</test>
+<test>
+  <op name="symdifference" arg1="A" arg2="B">
+    GEOMETRYCOLLECTION(
+      LINESTRING(0 100, 30 100), 
+      LINESTRING(80 100, 100 100, 110 110), 
+      LINESTRING(140 140, 200 200), 
+      POLYGON(
+        (30 240, 140 140, 260 30, 30 30, 30 100, 30 240), 
+        (80 140, 80 100, 80 80, 140 80, 110 110, 80 140)))
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>LA - A intersects B-hole #2</desc>
+  <a>
+    LINESTRING(40 340, 200 250, 120 180, 160 110, 270 40)
+  </a>
+  <b>
+    POLYGON(
+      (160 330, 60 260, 20 150, 60 40, 190 20, 270 130, 260 250, 160 330), 
+      (140 240, 80 190, 90 100, 160 70, 210 130, 210 210, 140 240))
+  </b>
+<test>
+  <op name="intersection" arg1="A" arg2="B">
+    MULTILINESTRING(
+      (114 298, 200 250, 173 226), 
+      (182 96, 225 68))
+  </op>
+</test>
+<test>
+  <op name="union" arg1="A" arg2="B">
+    GEOMETRYCOLLECTION(
+      LINESTRING(40 340, 114 298), 
+      LINESTRING(173 226, 120 180, 160 110, 182 96), 
+      LINESTRING(225 68, 270 40), 
+      POLYGON(
+        (114 298, 160 330, 260 250, 270 130, 225 68, 190 20, 60 40, 20 150, 60 260, 
+        114 298), 
+        (140 240, 80 190, 90 100, 160 70, 182 96, 210 130, 210 210, 173 226, 140 240)))
+  </op>
+</test>
+<test>
+  <op name="difference" arg1="A" arg2="B">
+    MULTILINESTRING(
+      (40 340, 114 298), 
+      (173 226, 120 180, 160 110, 182 96), 
+      (225 68, 270 40))
+  </op>
+</test>
+<test>
+  <op name="symdifference" arg1="A" arg2="B">
+    GEOMETRYCOLLECTION(
+      LINESTRING(40 340, 114 298), 
+      LINESTRING(173 226, 120 180, 160 110, 182 96), 
+      LINESTRING(225 68, 270 40), 
+      POLYGON(
+        (114 298, 160 330, 260 250, 270 130, 225 68, 190 20, 60 40, 20 150, 60 260, 
+        114 298), 
+        (140 240, 80 190, 90 100, 160 70, 182 96, 210 130, 210 210, 173 226, 140 240)))
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>mLmA - A and B complex, disjoint</desc>
+  <a>
+    MULTIPOLYGON(
+      (
+        (60 320, 60 80, 300 80, 60 320), 
+        (80 280, 80 100, 260 100, 80 280)), 
+      (
+        (120 160, 140 160, 140 140, 120 160)))
+  </a>
+  <b>
+    MULTILINESTRING(
+      (100 240, 100 180, 160 180, 160 120, 220 120), 
+      (40 360, 40 60, 340 60, 40 360, 40 20), 
+      (120 120, 120 140, 100 140, 100 120, 140 120))
+  </b>
+<test>
+  <op name="convexhull" pattern="FFFFFFFFF" arg1="A">
+    POLYGON(
+      (60 80, 60 320, 300 80, 60 80))
+  </op>
+</test>
+<test>
+  <op name="getboundary" pattern="FFFFFFFFF" arg1="A">
+    MULTILINESTRING(
+      (60 320, 60 80, 300 80, 60 320), 
+      (80 280, 80 100, 260 100, 80 280), 
+      (120 160, 140 160, 140 140, 120 160))
+  </op>
+</test>
+<test>
+  <op name="symdifference" pattern="FFFFFFFFF" arg1="A" arg2="B">
+    GEOMETRYCOLLECTION(
+      LINESTRING(100 240, 100 180, 160 180, 160 120, 220 120), 
+      LINESTRING(40 360, 40 60), 
+      LINESTRING(40 60, 340 60, 40 360), 
+      LINESTRING(40 60, 40 20), 
+      LINESTRING(120 120, 120 140, 100 140, 100 120, 120 120), 
+      LINESTRING(120 120, 140 120), 
+      POLYGON(
+        (60 320, 300 80, 60 80, 60 320), 
+        (80 280, 80 100, 260 100, 80 280)), 
+      POLYGON(
+        (120 160, 140 160, 140 140, 120 160)))
+  </op>
+</test>
+<test>
+  <op name="difference" pattern="FFFFFFFFF" arg1="A" arg2="B">
+    MULTIPOLYGON(
+      (
+        (60 320, 300 80, 60 80, 60 320), 
+        (80 280, 80 100, 260 100, 80 280)), 
+      (
+        (120 160, 140 160, 140 140, 120 160)))
+  </op>
+</test>
+<test>
+  <op name="union" pattern="FFFFFFFFF" arg1="A" arg2="B">
+    GEOMETRYCOLLECTION(
+      LINESTRING(100 240, 100 180, 160 180, 160 120, 220 120), 
+      LINESTRING(40 360, 40 60), 
+      LINESTRING(40 60, 340 60, 40 360), 
+      LINESTRING(40 60, 40 20), 
+      LINESTRING(120 120, 120 140, 100 140, 100 120, 120 120), 
+      LINESTRING(120 120, 140 120), 
+      POLYGON(
+        (60 320, 300 80, 60 80, 60 320), 
+        (80 280, 80 100, 260 100, 80 280)), 
+      POLYGON(
+        (120 160, 140 160, 140 140, 120 160)))
+  </op>
+</test>
+<test>
+  <op name="intersection" pattern="FFFFFFFFF" arg1="A" arg2="B">
+    GEOMETRYCOLLECTION EMPTY
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>mLmA - A and B complex, overlapping and touching #1</desc>
+  <a>
+    MULTIPOLYGON(
+      (
+        (60 260, 60 120, 220 120, 220 260, 60 260), 
+        (80 240, 80 140, 200 140, 200 240, 80 240)), 
+      (
+        (100 220, 100 160, 180 160, 180 220, 100 220), 
+        (120 200, 120 180, 160 180, 160 200, 120 200)))
+  </a>
+  <b>
+    MULTILINESTRING(
+      (40 260, 240 260, 240 240, 40 240, 40 220, 240 220), 
+      (120 300, 120 80, 140 80, 140 300, 140 80, 120 80, 120 320))
+  </b>
+  <test>
+    <op name="getboundary" arg1="A">
+      MULTILINESTRING(
+        (60 260, 60 120, 220 120, 220 260, 60 260), 
+        (80 240, 80 140, 200 140, 200 240, 80 240), 
+        (100 220, 100 160, 180 160, 180 220, 100 220), 
+        (120 200, 120 180, 160 180, 160 200, 120 200))
+          </op>
+  </test>
+  <test>
+    <op name="convexhull" arg1="A">
+      POLYGON(
+        (60 120, 60 260, 220 260, 220 120, 60 120))
+          </op>
+  </test>
+  <test>
+    <op name="intersection" arg1="A" arg2="B">
+      MULTILINESTRING(
+        (220 260, 140 260), 
+        (140 260, 120 260), 
+        (120 260, 60 260), 
+        (200 240, 140 240), 
+        (140 240, 120 240), 
+        (120 240, 80 240), 
+        (180 220, 140 220), 
+        (140 220, 120 220), 
+        (120 220, 100 220), 
+        (120 200, 120 180), 
+        (220 240, 200 240), 
+        (80 240, 60 240), 
+        (60 220, 80 220), 
+        (200 220, 220 220), 
+        (120 260, 120 240), 
+        (120 220, 120 200), 
+        (120 180, 120 160), 
+        (120 140, 120 120), 
+        (140 120, 140 140), 
+        (140 160, 140 180), 
+        (140 200, 140 220), 
+        (140 240, 140 260))
+          </op>
+  </test>
+  <test>
+    <op name="union" arg1="A" arg2="B">
+      GEOMETRYCOLLECTION(
+        LINESTRING(40 260, 60 260), 
+        LINESTRING(220 260, 240 260, 240 240, 220 240), 
+        LINESTRING(60 240, 40 240, 40 220, 60 220), 
+        LINESTRING(80 220, 100 220), 
+        LINESTRING(180 220, 200 220), 
+        LINESTRING(220 220, 240 220), 
+        LINESTRING(120 300, 120 260), 
+        LINESTRING(120 240, 120 220), 
+        LINESTRING(120 160, 120 140), 
+        LINESTRING(120 120, 120 80), 
+        LINESTRING(120 80, 140 80), 
+        LINESTRING(140 80, 140 120), 
+        LINESTRING(140 140, 140 160), 
+        LINESTRING(140 180, 140 200), 
+        LINESTRING(140 220, 140 240), 
+        LINESTRING(140 260, 140 300), 
+        LINESTRING(120 300, 120 320), 
+        POLYGON(
+          (60 240, 60 260, 120 260, 140 260, 220 260, 220 240, 220 220, 220 120, 140 120, 
+          120 120, 60 120, 60 220, 60 240), 
+          (80 240, 80 220, 80 140, 120 140, 140 140, 200 140, 200 220, 200 240, 140 240, 
+          120 240, 80 240)), 
+        POLYGON(
+          (120 160, 100 160, 100 220, 120 220, 140 220, 180 220, 180 160, 140 160, 120 160), 
+          (120 200, 120 180, 140 180, 160 180, 160 200, 140 200, 120 200)))
+          </op>
+  </test>
+  <test>
+    <op name="difference" arg1="A" arg2="B">
+      MULTIPOLYGON(
+        (
+          (60 240, 60 260, 120 260, 140 260, 220 260, 220 240, 220 220, 220 120, 140 120, 
+          120 120, 60 120, 60 220, 60 240), 
+          (80 240, 80 220, 80 140, 120 140, 140 140, 200 140, 200 220, 200 240, 140 240, 
+          120 240, 80 240)), 
+        (
+          (120 160, 100 160, 100 220, 120 220, 140 220, 180 220, 180 160, 140 160, 120 160), 
+          (120 200, 120 180, 140 180, 160 180, 160 200, 140 200, 120 200)))
+          </op>
+  </test>
+  <test>
+    <op name="symdifference" arg1="A" arg2="B">
+      GEOMETRYCOLLECTION(
+        LINESTRING(40 260, 60 260), 
+        LINESTRING(220 260, 240 260, 240 240, 220 240), 
+        LINESTRING(60 240, 40 240, 40 220, 60 220), 
+        LINESTRING(80 220, 100 220), 
+        LINESTRING(180 220, 200 220), 
+        LINESTRING(220 220, 240 220), 
+        LINESTRING(120 300, 120 260), 
+        LINESTRING(120 240, 120 220), 
+        LINESTRING(120 160, 120 140), 
+        LINESTRING(120 120, 120 80), 
+        LINESTRING(120 80, 140 80), 
+        LINESTRING(140 80, 140 120), 
+        LINESTRING(140 140, 140 160), 
+        LINESTRING(140 180, 140 200), 
+        LINESTRING(140 220, 140 240), 
+        LINESTRING(140 260, 140 300), 
+        LINESTRING(120 300, 120 320), 
+        POLYGON(
+          (60 240, 60 260, 120 260, 140 260, 220 260, 220 240, 220 220, 220 120, 140 120, 
+          120 120, 60 120, 60 220, 60 240), 
+          (80 240, 80 220, 80 140, 120 140, 140 140, 200 140, 200 220, 200 240, 140 240, 
+          120 240, 80 240)), 
+        POLYGON(
+          (120 160, 100 160, 100 220, 120 220, 140 220, 180 220, 180 160, 140 160, 120 160), 
+          (120 200, 120 180, 140 180, 160 180, 160 200, 140 200, 120 200)))
+          </op>
+  </test>
+</case><case>
+  <desc>mLmA - A and B complex, overlapping and touching #2</desc>
+  <a>
+    MULTIPOLYGON(
+      (
+        (60 320, 60 120, 280 120, 280 320, 60 320), 
+        (120 260, 120 180, 240 180, 240 260, 120 260)), 
+      (
+        (280 400, 320 400, 320 360, 280 360, 280 400)), 
+      (
+        (300 240, 300 220, 320 220, 320 240, 300 240)))
+  </a>
+  <b>
+    MULTILINESTRING(
+      (80 300, 80 160, 260 160, 260 300, 80 300, 80 140), 
+      (220 360, 220 240, 300 240, 300 360))
+  </b>
+<test>
+  <op name="convexhull" pattern="FFFFFFFFF" arg1="A">
+    POLYGON(
+      (60 120, 60 320, 280 400, 320 400, 320 220, 280 120, 60 120))
+  </op>
+</test>
+<test>
+  <op name="getboundary" pattern="FFFFFFFFF" arg1="A">
+    MULTILINESTRING(
+      (60 320, 60 120, 280 120, 280 320, 60 320), 
+      (120 260, 120 180, 240 180, 240 260, 120 260), 
+      (280 400, 320 400, 320 360, 280 360, 280 400), 
+      (300 240, 300 220, 320 220, 320 240, 300 240))
+  </op>
+</test>
+<test>
+  <op name="symdifference" pattern="FFFFFFFFF" arg1="A" arg2="B">
+    GEOMETRYCOLLECTION(
+      LINESTRING(220 360, 220 320), 
+      LINESTRING(220 260, 220 240, 240 240), 
+      LINESTRING(280 240, 300 240), 
+      LINESTRING(300 240, 300 360), 
+      POLYGON(
+        (280 240, 280 120, 60 120, 60 320, 220 320, 280 320, 280 240), 
+        (120 260, 120 180, 240 180, 240 240, 240 260, 220 260, 120 260)), 
+      POLYGON(
+        (280 400, 320 400, 320 360, 300 360, 280 360, 280 400)), 
+      POLYGON(
+        (300 240, 320 240, 320 220, 300 220, 300 240)))
+  </op>
+</test>
+<test>
+  <op name="difference" pattern="FFFFFFFFF" arg1="A" arg2="B">
+    MULTIPOLYGON(
+      (
+        (280 240, 280 120, 60 120, 60 320, 220 320, 280 320, 280 240), 
+        (120 260, 120 180, 240 180, 240 240, 240 260, 220 260, 120 260)), 
+      (
+        (280 400, 320 400, 320 360, 300 360, 280 360, 280 400)), 
+      (
+        (300 240, 320 240, 320 220, 300 220, 300 240)))
+  </op>
+</test>
+<test>
+  <op name="union" pattern="FFFFFFFFF" arg1="A" arg2="B">
+    GEOMETRYCOLLECTION(
+      LINESTRING(220 360, 220 320), 
+      LINESTRING(220 260, 220 240, 240 240), 
+      LINESTRING(280 240, 300 240), 
+      LINESTRING(300 240, 300 360), 
+      POLYGON(
+        (280 240, 280 120, 60 120, 60 320, 220 320, 280 320, 280 240), 
+        (120 260, 120 180, 240 180, 240 240, 240 260, 220 260, 120 260)), 
+      POLYGON(
+        (280 400, 320 400, 320 360, 300 360, 280 360, 280 400)), 
+      POLYGON(
+        (300 240, 320 240, 320 220, 300 220, 300 240)))
+  </op>
+</test>
+<test>
+  <op name="intersection" pattern="FFFFFFFFF" arg1="A" arg2="B">
+    GEOMETRYCOLLECTION(
+      POINT(300 240), 
+      POINT(300 360), 
+      LINESTRING(80 300, 80 160), 
+      LINESTRING(80 160, 260 160, 260 240), 
+      LINESTRING(260 240, 260 300, 220 300), 
+      LINESTRING(220 300, 80 300), 
+      LINESTRING(80 160, 80 140), 
+      LINESTRING(220 320, 220 300), 
+      LINESTRING(220 300, 220 260), 
+      LINESTRING(240 240, 260 240), 
+      LINESTRING(260 240, 280 240))
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>mLmA - A and B complex, overlapping and touching #3</desc>
+  <a>
+    MULTIPOLYGON(
+      (
+        (120 180, 60 80, 180 80, 120 180)), 
+      (
+        (100 240, 140 240, 120 220, 100 240)))
+  </a>
+  <b>
+    MULTILINESTRING(
+      (180 260, 120 180, 60 260, 180 260), 
+      (60 300, 60 40), 
+      (100 100, 140 100))
+  </b>
+<test>
+  <op name="convexhull" pattern="FFFFFFFFF" arg1="A">
+    POLYGON(
+      (60 80, 100 240, 140 240, 180 80, 60 80))
+  </op>
+</test>
+<test>
+  <op name="getboundary" pattern="FFFFFFFFF" arg1="A">
+    MULTILINESTRING(
+      (120 180, 60 80, 180 80, 120 180), 
+      (100 240, 140 240, 120 220, 100 240))
+  </op>
+</test>
+<test>
+  <op name="symdifference" pattern="FFFFFFFFF" arg1="A" arg2="B">
+    GEOMETRYCOLLECTION(
+      LINESTRING(180 260, 120 180), 
+      LINESTRING(120 180, 60 260), 
+      LINESTRING(60 260, 180 260), 
+      LINESTRING(60 300, 60 260), 
+      LINESTRING(60 260, 60 80), 
+      LINESTRING(60 80, 60 40), 
+      POLYGON(
+        (60 80, 120 180, 180 80, 60 80)), 
+      POLYGON(
+        (100 240, 140 240, 120 220, 100 240)))
+  </op>
+</test>
+<test>
+  <op name="difference" pattern="FFFFFFFFF" arg1="A" arg2="B">
+    MULTIPOLYGON(
+      (
+        (60 80, 120 180, 180 80, 60 80)), 
+      (
+        (100 240, 140 240, 120 220, 100 240)))
+  </op>
+</test>
+<test>
+  <op name="union" pattern="FFFFFFFFF" arg1="A" arg2="B">
+    GEOMETRYCOLLECTION(
+      LINESTRING(180 260, 120 180), 
+      LINESTRING(120 180, 60 260), 
+      LINESTRING(60 260, 180 260), 
+      LINESTRING(60 300, 60 260), 
+      LINESTRING(60 260, 60 80), 
+      LINESTRING(60 80, 60 40), 
+      POLYGON(
+        (60 80, 120 180, 180 80, 60 80)), 
+      POLYGON(
+        (100 240, 140 240, 120 220, 100 240)))
+  </op>
+</test>
+<test>
+  <op name="intersection" pattern="FFFFFFFFF" arg1="A" arg2="B">
+    GEOMETRYCOLLECTION(
+      POINT(60 80), 
+      POINT(120 180), 
+      LINESTRING(100 100, 140 100))
+  </op>
+</test>
+</case>
+
+</run>

Added: packages/jts/branches/upstream/current/test/vivid/TestFunctionLAPrec.xml
===================================================================
--- packages/jts/branches/upstream/current/test/vivid/TestFunctionLAPrec.xml	                        (rev 0)
+++ packages/jts/branches/upstream/current/test/vivid/TestFunctionLAPrec.xml	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,59 @@
+<run>
+  <precisionModel scale="1.0" offsetx="0.0" offsety="0.0"/>
+
+<case>
+  <desc>LA - line and sliver intersecting, dimensional collapse</desc>
+  <a>
+    POLYGON(
+      (95 9, 81 414, 87 414, 95 9))
+  </a>
+  <b>
+    LINESTRING(93 13, 96 13)
+  </b>
+<test>
+  <op name="getboundary" arg1="A">
+    LINESTRING(95 9, 81 414, 87 414, 95 9)
+  </op>
+</test>
+<test>
+  <op name="convexhull" arg1="A">
+    POLYGON(
+      (95 9, 81 414, 87 414, 95 9))
+  </op>
+</test>
+<test>
+  <op name="intersection" arg1="A" arg2="B">
+    POINT(95 13)
+  </op>
+</test>
+<test>
+  <op name="union" arg1="A" arg2="B">
+    GEOMETRYCOLLECTION(
+      LINESTRING(95 9, 95 13), 
+      POLYGON(
+        (95 13, 81 414, 87 414, 95 13)), 
+      LINESTRING(93 13, 95 13), 
+      LINESTRING(95 13, 96 13))
+  </op>
+</test>
+<test>
+  <op name="difference" arg1="A" arg2="B">
+    GEOMETRYCOLLECTION(
+      LINESTRING(95 9, 95 13), 
+      POLYGON(
+        (95 13, 81 414, 87 414, 95 13)))
+  </op>
+</test>
+<test>
+  <op name="symdifference" arg1="A" arg2="B">
+    GEOMETRYCOLLECTION(
+      LINESTRING(95 9, 95 13), 
+      POLYGON(
+        (95 13, 81 414, 87 414, 95 13)), 
+      LINESTRING(93 13, 95 13), 
+      LINESTRING(95 13, 96 13))
+  </op>
+</test>
+</case>
+
+</run>

Added: packages/jts/branches/upstream/current/test/vivid/TestFunctionLL.xml
===================================================================
--- packages/jts/branches/upstream/current/test/vivid/TestFunctionLL.xml	                        (rev 0)
+++ packages/jts/branches/upstream/current/test/vivid/TestFunctionLL.xml	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,360 @@
+<run>
+  <precisionModel scale="1.0" offsetx="0.0" offsety="0.0"/>
+
+<case>
+  <desc>LL - A crosses B</desc>
+  <a>
+    LINESTRING(0 0, 100 100)
+  </a>
+  <b>
+    LINESTRING(0 100, 100 0)
+  </b>
+<test>
+  <op name="intersection" arg1="A" arg2="B">
+    POINT(50 50)
+  </op>
+</test>
+<test>
+  <op name="union" arg1="A" arg2="B">
+    MULTILINESTRING(
+      (0 0, 50 50), 
+      (0 100, 50 50), 
+      (50 50, 100 100), 
+      (50 50, 100 0))
+  </op>
+</test>
+<test>
+  <op name="difference" arg1="A" arg2="B">
+    MULTILINESTRING(
+      (0 0, 50 50), 
+      (50 50, 100 100))
+  </op>
+</test>
+<test>
+  <op name="symdifference" arg1="A" arg2="B">
+    MULTILINESTRING(
+      (0 0, 50 50), 
+      (0 100, 50 50), 
+      (50 50, 100 100), 
+      (50 50, 100 0))
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>LL - A shares one segment with B</desc>
+  <a>
+    LINESTRING(0 0, 100 100, 200 0)
+  </a>
+  <b>
+    LINESTRING(0 0, 100 100, 200 200)
+  </b>
+<test>
+  <op name="intersection" arg1="A" arg2="B">
+    LINESTRING(0 0, 100 100)
+  </op>
+</test>
+<test>
+  <op name="union" arg1="A" arg2="B">
+    MULTILINESTRING(
+      (0 0, 100 100), 
+      (100 100, 200 200), 
+      (100 100, 200 0))
+  </op>
+</test>
+<test>
+  <op name="difference" arg1="A" arg2="B">
+    LINESTRING(100 100, 200 0)
+  </op>
+</test>
+<test>
+  <op name="symdifference" arg1="A" arg2="B">
+    MULTILINESTRING(
+      (100 100, 200 200), 
+      (100 100, 200 0))
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>LL - A and B disjoint</desc>
+  <a>
+    LINESTRING(40 360, 40 220, 120 360)
+  </a>
+  <b>
+    LINESTRING(120 340, 60 220, 140 220, 140 360)
+  </b>
+<test>
+  <op name="getboundary" arg1="A">
+    MULTIPOINT(40 360, 120 360)
+  </op>
+</test>
+<test>
+  <op name="convexhull" arg1="A">
+    POLYGON(
+      (40 220, 40 360, 120 360, 40 220))
+  </op>
+</test>
+<test>
+  <op name="intersection" arg1="A" arg2="B">
+    GEOMETRYCOLLECTION EMPTY
+  </op>
+</test>
+<test>
+  <op name="union" arg1="A" arg2="B">
+    MULTILINESTRING(
+      (40 360, 40 220, 120 360), 
+      (120 340, 60 220, 140 220, 140 360))
+  </op>
+</test>
+<test>
+  <op name="difference" arg1="A" arg2="B">
+    LINESTRING(40 360, 40 220, 120 360)
+  </op>
+</test>
+<test>
+  <op name="symdifference" arg1="A" arg2="B">
+    MULTILINESTRING(
+      (40 360, 40 220, 120 360), 
+      (120 340, 60 220, 140 220, 140 360))
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>LL - A and B intersect frequently</desc>
+  <a>
+    LINESTRING(220 240, 200 220, 60 320, 40 300, 180 200, 160 180, 20 280)
+  </a>
+  <b>
+    LINESTRING(220 240, 140 160, 120 180, 220 280, 200 300, 100 200)
+  </b>
+<test>
+  <op name="getboundary" arg1="A">
+    MULTIPOINT(220 240, 20 280)
+  </op>
+</test>
+<test>
+  <op name="convexhull" arg1="A">
+    POLYGON(
+      (160 180, 20 280, 60 320, 220 240, 160 180))
+  </op>
+</test>
+<test>
+  <op name="intersection" arg1="A" arg2="B">
+    GEOMETRYCOLLECTION(
+      POINT(113 213), 
+      POINT(133 233), 
+      POINT(137 197), 
+      POINT(153 253), 
+      POINT(157 217), 
+      POINT(177 237), 
+      LINESTRING(180 200, 160 180), 
+      LINESTRING(220 240, 200 220))
+  </op>
+</test>
+<test>
+  <op name="union" arg1="A" arg2="B">
+    MULTILINESTRING(
+      (113 213, 20 280), 
+      (133 233, 113 213), 
+      (113 213, 100 200), 
+      (137 197, 113 213), 
+      (153 253, 133 233), 
+      (153 253, 60 320, 40 300, 133 233), 
+      (133 233, 157 217), 
+      (137 197, 157 217), 
+      (160 180, 140 160, 120 180, 137 197), 
+      (160 180, 137 197), 
+      (177 237, 220 280, 200 300, 153 253), 
+      (177 237, 153 253), 
+      (157 217, 177 237), 
+      (157 217, 180 200), 
+      (180 200, 160 180), 
+      (200 220, 177 237), 
+      (200 220, 180 200), 
+      (220 240, 200 220))
+  </op>
+</test>
+<test>
+  <op name="difference" arg1="A" arg2="B">
+    MULTILINESTRING(
+      (200 220, 177 237), 
+      (177 237, 153 253), 
+      (153 253, 60 320, 40 300, 133 233), 
+      (133 233, 157 217), 
+      (157 217, 180 200), 
+      (160 180, 137 197), 
+      (137 197, 113 213), 
+      (113 213, 20 280))
+  </op>
+</test>
+<test>
+  <op name="symdifference" arg1="A" arg2="B">
+    MULTILINESTRING(
+      (200 220, 177 237), 
+      (177 237, 153 253), 
+      (153 253, 60 320, 40 300, 133 233), 
+      (133 233, 157 217), 
+      (157 217, 180 200), 
+      (160 180, 137 197), 
+      (137 197, 113 213), 
+      (113 213, 20 280), 
+      (200 220, 180 200), 
+      (160 180, 140 160, 120 180, 137 197), 
+      (137 197, 157 217), 
+      (157 217, 177 237), 
+      (177 237, 220 280, 200 300, 153 253), 
+      (153 253, 133 233), 
+      (133 233, 113 213), 
+      (113 213, 100 200))
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>LL - A and B equal</desc>
+  <a>
+    LINESTRING(80 320, 220 320, 220 160, 80 300)
+  </a>
+  <b>
+    LINESTRING(80 320, 220 320, 220 160, 80 300)
+  </b>
+<test>
+  <op name="getboundary" arg1="A">
+    MULTIPOINT(80 320, 80 300)
+  </op>
+</test>
+<test>
+  <op name="convexhull" arg1="A">
+    POLYGON(
+      (220 160, 80 300, 80 320, 220 320, 220 160))
+  </op>
+</test>
+<test>
+  <op name="intersection" arg1="A" arg2="B">
+    MULTILINESTRING(
+      (220 160, 80 300), 
+      (80 320, 220 320), 
+      (220 320, 220 160))
+  </op>
+</test>
+<test>
+  <op name="union" arg1="A" arg2="B">
+    MULTILINESTRING(
+      (220 160, 80 300), 
+      (80 320, 220 320), 
+      (220 320, 220 160))
+  </op>
+</test>
+<test>
+  <op name="difference" arg1="A" arg2="B">
+    GEOMETRYCOLLECTION EMPTY
+  </op>
+</test>
+<test>
+  <op name="symdifference" arg1="A" arg2="B">
+    GEOMETRYCOLLECTION EMPTY
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>LL - A and B touch ends</desc>
+  <a>
+    LINESTRING(60 200, 60 260, 140 200)
+  </a>
+  <b>
+    LINESTRING(60 200, 60 140, 140 200)
+  </b>
+<test>
+  <op name="getboundary" arg1="A">
+    MULTIPOINT(60 200, 140 200)
+  </op>
+</test>
+<test>
+  <op name="convexhull" arg1="A">
+    POLYGON(
+      (60 200, 60 260, 140 200, 60 200))
+  </op>
+</test>
+<test>
+  <op name="intersection" arg1="A" arg2="B">
+    MULTIPOINT(60 200, 140 200)
+  </op>
+</test>
+<test>
+  <op name="union" arg1="A" arg2="B">
+    MULTILINESTRING(
+      (60 200, 60 260, 140 200), 
+      (60 200, 60 140, 140 200))
+  </op>
+</test>
+<test>
+  <op name="difference" arg1="A" arg2="B">
+    LINESTRING(60 200, 60 260, 140 200)
+  </op>
+</test>
+<test>
+  <op name="symdifference" arg1="A" arg2="B">
+    MULTILINESTRING(
+      (60 200, 60 260, 140 200), 
+      (60 200, 60 140, 140 200))
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>LL - intersecting rings</desc>
+  <a>
+    LINESTRING(180 200, 100 280, 20 200, 100 120, 180 200)
+  </a>
+  <b>
+    LINESTRING(100 200, 220 200, 220 80, 100 80, 100 200)
+  </b>
+<test>
+  <op name="getboundary" arg1="A">
+    MULTIPOINT EMPTY
+  </op>
+</test>
+<test>
+  <op name="convexhull" arg1="A">
+    POLYGON(
+      (100 120, 20 200, 100 280, 180 200, 100 120))
+  </op>
+</test>
+<test>
+  <op name="intersection" arg1="A" arg2="B">
+    MULTIPOINT(100 120, 180 200)
+  </op>
+</test>
+<test>
+  <op name="union" arg1="A" arg2="B">
+    MULTILINESTRING(
+      (100 120, 180 200), 
+      (100 120, 100 200), 
+      (180 200, 100 280, 20 200, 100 120), 
+      (180 200, 220 200, 220 80, 100 80, 100 120), 
+      (100 200, 180 200))
+  </op>
+</test>
+<test>
+  <op name="difference" arg1="A" arg2="B">
+    MULTILINESTRING(
+      (100 120, 180 200), 
+      (180 200, 100 280, 20 200, 100 120))
+  </op>
+</test>
+<test>
+  <op name="symdifference" arg1="A" arg2="B">
+    MULTILINESTRING(
+      (100 120, 180 200), 
+      (100 120, 100 200), 
+      (180 200, 100 280, 20 200, 100 120), 
+      (180 200, 220 200, 220 80, 100 80, 100 120), 
+      (100 200, 180 200))
+  </op>
+</test>
+</case>
+
+</run>

Added: packages/jts/branches/upstream/current/test/vivid/TestFunctionLLPrec.xml
===================================================================
--- packages/jts/branches/upstream/current/test/vivid/TestFunctionLLPrec.xml	                        (rev 0)
+++ packages/jts/branches/upstream/current/test/vivid/TestFunctionLLPrec.xml	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,28 @@
+<run>
+  <precisionModel scale="1.0" offsetx="0.0" offsety="0.0"/>
+
+<case>
+  <desc>LL - narrow V</desc>
+  <a>
+    LINESTRING(0 10, 620 10, 0 11)
+  </a>
+  <b>
+    LINESTRING(400 60, 400 10)
+  </b>
+<test>
+  <op name="intersection" arg1="A" arg2="B">
+    POINT(400 10)
+  </op>
+</test>
+<test>
+  <op name="union" arg1="A" arg2="B">
+    MULTILINESTRING(
+      (0 10, 400 10), 
+      (400 10, 620 10, 400 10), 
+      (400 10, 0 11), 
+      (400 60, 400 10))
+  </op>
+</test>
+</case>
+
+</run>
\ No newline at end of file

Added: packages/jts/branches/upstream/current/test/vivid/TestFunctionPA.xml
===================================================================
--- packages/jts/branches/upstream/current/test/vivid/TestFunctionPA.xml	                        (rev 0)
+++ packages/jts/branches/upstream/current/test/vivid/TestFunctionPA.xml	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,155 @@
+<run>
+  <precisionModel scale="1.0" offsetx="0.0" offsety="0.0"/>
+
+<case>
+  <desc>PA - point contained in simple polygon</desc>
+  <a>
+    POINT(100 100)
+  </a>
+  <b>
+    POLYGON(
+      (50 50, 200 50, 200 200, 50 200, 50 50))
+  </b>
+<test>
+  <op name="intersection" arg1="A" arg2="B">
+    POINT(100 100)
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>mPmA - points on I, B and E of touching triangles</desc>
+  <a>
+    MULTIPOLYGON(
+      (
+        (120 320, 180 200, 240 320, 120 320)), 
+      (
+        (180 200, 240 80, 300 200, 180 200)))
+  </a>
+  <b>
+    MULTIPOINT(120 320, 180 260, 180 320, 180 200, 300 200, 200 220)
+  </b>
+<test>
+  <op name="getboundary" arg1="A">
+    MULTILINESTRING(
+      (120 320, 180 200, 240 320, 120 320), 
+      (180 200, 240 80, 300 200, 180 200))
+  </op>
+</test>
+<test>
+  <op name="convexhull" arg1="A">
+    POLYGON(
+      (240 80, 120 320, 240 320, 300 200, 240 80))
+  </op>
+</test>
+<test>
+  <op name="intersection" arg1="A" arg2="B">
+    MULTIPOINT(120 320, 180 200, 180 260, 180 320, 300 200)
+  </op>
+</test>
+<test>
+  <op name="union" arg1="A" arg2="B">
+    GEOMETRYCOLLECTION(
+      POINT(200 220), 
+      POLYGON(
+        (180 200, 120 320, 240 320, 180 200)), 
+      POLYGON(
+        (180 200, 300 200, 240 80, 180 200)))
+  </op>
+</test>
+<test>
+  <op name="difference" arg1="A" arg2="B">
+    MULTIPOLYGON(
+      (
+        (180 200, 120 320, 240 320, 180 200)), 
+      (
+        (180 200, 300 200, 240 80, 180 200)))
+  </op>
+</test>
+<test>
+  <op name="symdifference" arg1="A" arg2="B">
+    GEOMETRYCOLLECTION(
+      POINT(200 220), 
+      POLYGON(
+        (180 200, 120 320, 240 320, 180 200)), 
+      POLYGON(
+        (180 200, 300 200, 240 80, 180 200)))
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>mPmA - points on I, B and E of concentric doughnuts</desc>
+  <a>
+    MULTIPOLYGON(
+      (
+        (120 80, 420 80, 420 340, 120 340, 120 80), 
+        (160 300, 160 120, 380 120, 380 300, 160 300)), 
+      (
+        (200 260, 200 160, 340 160, 340 260, 200 260), 
+        (240 220, 240 200, 300 200, 300 220, 240 220)))
+  </a>
+  <b>
+    MULTIPOINT(200 360, 420 340, 400 100, 340 120, 200 140, 200 160, 220 180, 260 200, 200 360, 
+    420 340, 400 100, 340 120, 200 140, 200 160, 220 180, 260 200)
+  </b>
+<test>
+  <op name="getboundary" arg1="A">
+    MULTILINESTRING(
+      (120 80, 420 80, 420 340, 120 340, 120 80), 
+      (160 300, 160 120, 380 120, 380 300, 160 300), 
+      (200 260, 200 160, 340 160, 340 260, 200 260), 
+      (240 220, 240 200, 300 200, 300 220, 240 220))
+  </op>
+</test>
+<test>
+  <op name="convexhull" arg1="A">
+    POLYGON(
+      (120 80, 120 340, 420 340, 420 80, 120 80))
+  </op>
+</test>
+<test>
+  <op name="intersection" arg1="A" arg2="B">
+    MULTIPOINT(200 160, 220 180, 260 200, 340 120, 400 100, 420 340)
+  </op>
+</test>
+<test>
+  <op name="union" arg1="A" arg2="B">
+    GEOMETRYCOLLECTION(
+      POINT(200 140), 
+      POINT(200 360), 
+      POLYGON(
+        (120 80, 120 340, 420 340, 420 80, 120 80), 
+        (160 300, 160 120, 380 120, 380 300, 160 300)), 
+      POLYGON(
+        (200 260, 340 260, 340 160, 200 160, 200 260), 
+        (240 220, 240 200, 300 200, 300 220, 240 220)))
+  </op>
+</test>
+<test>
+  <op name="difference" arg1="A" arg2="B">
+    MULTIPOLYGON(
+      (
+        (120 80, 120 340, 420 340, 420 80, 120 80), 
+        (160 300, 160 120, 380 120, 380 300, 160 300)), 
+      (
+        (200 260, 340 260, 340 160, 200 160, 200 260), 
+        (240 220, 240 200, 300 200, 300 220, 240 220)))
+  </op>
+</test>
+<test>
+  <op name="symdifference" arg1="A" arg2="B">
+    GEOMETRYCOLLECTION(
+      POINT(200 140), 
+      POINT(200 360), 
+      POLYGON(
+        (120 80, 120 340, 420 340, 420 80, 120 80), 
+        (160 300, 160 120, 380 120, 380 300, 160 300)), 
+      POLYGON(
+        (200 260, 340 260, 340 160, 200 160, 200 260), 
+        (240 220, 240 200, 300 200, 300 220, 240 220)))
+  </op>
+</test>
+</case>
+
+</run>

Added: packages/jts/branches/upstream/current/test/vivid/TestFunctionPL.xml
===================================================================
--- packages/jts/branches/upstream/current/test/vivid/TestFunctionPL.xml	                        (rev 0)
+++ packages/jts/branches/upstream/current/test/vivid/TestFunctionPL.xml	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,295 @@
+<run>
+  <precisionModel scale="1.0" offsetx="0.0" offsety="0.0"/>
+
+<case>
+  <desc>mPL - points in I and E of line</desc>
+  <a>
+    MULTIPOINT(40 90, 20 20, 70 70)
+  </a>
+  <b>
+    LINESTRING(20 20, 100 100)
+  </b>
+<test>
+  <op name="intersection" arg1="A" arg2="B">
+    MULTIPOINT(20 20, 70 70)
+  </op>
+</test>
+<test>
+  <op name="union" arg1="A" arg2="B">
+    GEOMETRYCOLLECTION(
+      POINT(40 90), 
+      LINESTRING(20 20, 100 100))
+  </op>
+</test>
+<test>
+  <op name="difference" arg1="A" arg2="B">
+    POINT(40 90)
+  </op>
+</test>
+<test>
+  <op name="symdifference" arg1="A" arg2="B">
+    GEOMETRYCOLLECTION(
+      POINT(40 90), 
+      LINESTRING(20 20, 100 100))
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>mPL - points in I and E of line, line self-intersecting</desc>
+  <a>
+    MULTIPOINT(40 90, 20 20, 70 70)
+  </a>
+  <b>
+    LINESTRING(20 20, 110 110, 170 50, 130 10, 70 70)
+  </b>
+<test>
+  <op name="intersection" arg1="A" arg2="B">
+    MULTIPOINT(20 20, 70 70)
+  </op>
+</test>
+<test>
+  <op name="union" arg1="A" arg2="B">
+    GEOMETRYCOLLECTION(
+      POINT(40 90), 
+      LINESTRING(20 20, 70 70), 
+      LINESTRING(70 70, 110 110, 170 50, 130 10, 70 70))
+  </op>
+</test>
+<test>
+  <op name="difference" arg1="A" arg2="B">
+    POINT(40 90)
+  </op>
+</test>
+<test>
+  <op name="symdifference" arg1="A" arg2="B">
+    GEOMETRYCOLLECTION(
+      POINT(40 90), 
+      LINESTRING(20 20, 70 70), 
+      LINESTRING(70 70, 110 110, 170 50, 130 10, 70 70))
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>mPmL - points in I, B and E of lines, lines overlap, points overlap</desc>
+  <a>
+    MULTILINESTRING(
+      (100 320, 100 220), 
+      (100 180, 200 180), 
+      (220 180, 220 320), 
+      (220 320, 160 320), 
+      (100 320, 100 220), 
+      (100 180, 200 180), 
+      (220 180, 220 320), 
+      (220 320, 160 320), 
+      (100 220, 100 320))
+  </a>
+  <b>
+    MULTIPOINT(100 320, 100 260, 100 220, 100 200, 100 180, 120 180, 200 180, 220 180, 220 260, 
+    220 320, 200 320, 160 320, 140 320, 120 320, 100 320, 100 260, 100 220, 100 200, 100 180, 
+    120 180, 200 180, 220 180, 220 260, 220 320, 200 320, 160 320, 140 320, 120 320)
+  </b>
+<test>
+  <op name="getboundary" arg1="A">
+    MULTIPOINT(100 220, 100 320)
+  </op>
+</test>
+<test>
+  <op name="convexhull" arg1="A">
+    POLYGON(
+      (100 180, 100 320, 220 320, 220 180, 100 180))
+  </op>
+</test>
+<test>
+  <op name="intersection" arg1="A" arg2="B">
+    MULTIPOINT(100 180, 100 220, 100 260, 100 320, 120 180, 160 320, 200 180, 200 320, 220 180, 
+    220 260, 220 320)
+  </op>
+</test>
+<test>
+  <op name="union" arg1="A" arg2="B">
+    GEOMETRYCOLLECTION(
+      POINT(100 200), 
+      POINT(120 320), 
+      POINT(140 320), 
+      LINESTRING(100 320, 100 220), 
+      LINESTRING(100 180, 200 180), 
+      LINESTRING(220 180, 220 320), 
+      LINESTRING(220 320, 160 320))
+  </op>
+</test>
+<test>
+  <op name="difference" arg1="A" arg2="B">
+    MULTILINESTRING(
+      (100 320, 100 220), 
+      (100 180, 200 180), 
+      (220 180, 220 320), 
+      (220 320, 160 320))
+  </op>
+</test>
+<test>
+  <op name="symdifference" arg1="A" arg2="B">
+    GEOMETRYCOLLECTION(
+      POINT(100 200), 
+      POINT(120 320), 
+      POINT(140 320), 
+      LINESTRING(100 320, 100 220), 
+      LINESTRING(100 180, 200 180), 
+      LINESTRING(220 180, 220 320), 
+      LINESTRING(220 320, 160 320))
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>mPmL - points in I, B and E of lines, lines overlap, points overlap, x &lt;0, y &lt; 0</desc>
+  <a>
+    MULTILINESTRING(
+      (-500 -140, -500 -280, -320 -280, -320 -140, -500 -140, -500 -340), 
+      (-500 -140, -320 -140, -500 -140, -320 -140, -500 -140))
+  </a>
+  <b>
+    MULTIPOINT(-560 -180, -420 -180, -500 -220, -500 -340, -500 -280, -500 -140, -320 -140, -420 -140, -320 -180, 
+    -280 -140, -320 -120, -560 -180, -420 -180, -500 -220, -500 -340, -500 -280, -500 -140, -320 -140, -420 -140, 
+    -320 -180, -280 -140, -320 -120)
+  </b>
+<test>
+  <op name="getboundary" arg1="A">
+    MULTIPOINT(-500 -340, -500 -140)
+  </op>
+</test>
+<test>
+  <op name="convexhull" arg1="A">
+    POLYGON(
+      (-500 -340, -500 -140, -320 -140, -320 -280, -500 -340))
+  </op>
+</test>
+<test>
+  <op name="intersection" arg1="A" arg2="B">
+    MULTIPOINT(-500 -340, -500 -280, -500 -220, -500 -140, -420 -140, -320 -180, -320 -140)
+  </op>
+</test>
+<test>
+  <op name="union" arg1="A" arg2="B">
+    GEOMETRYCOLLECTION(
+      POINT(-560 -180), 
+      POINT(-420 -180), 
+      POINT(-320 -120), 
+      POINT(-280 -140), 
+      LINESTRING(-500 -140, -500 -280), 
+      LINESTRING(-500 -280, -320 -280, -320 -140), 
+      LINESTRING(-320 -140, -500 -140), 
+      LINESTRING(-500 -280, -500 -340))
+  </op>
+</test>
+<test>
+  <op name="difference" arg1="A" arg2="B">
+    MULTILINESTRING(
+      (-500 -140, -500 -280), 
+      (-500 -280, -320 -280, -320 -140), 
+      (-320 -140, -500 -140), 
+      (-500 -280, -500 -340))
+  </op>
+</test>
+<test>
+  <op name="symdifference" arg1="A" arg2="B">
+    GEOMETRYCOLLECTION(
+      POINT(-560 -180), 
+      POINT(-420 -180), 
+      POINT(-320 -120), 
+      POINT(-280 -140), 
+      LINESTRING(-500 -140, -500 -280), 
+      LINESTRING(-500 -280, -320 -280, -320 -140), 
+      LINESTRING(-320 -140, -500 -140), 
+      LINESTRING(-500 -280, -500 -340))
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>mL - lines intersect at 1 point</desc>
+  <a>
+    MULTILINESTRING(
+      (180 100, 140 280, 240 140, 220 120, 140 280), 
+      (140 280, 100 400, 80 380, 140 280, 40 380, 20 360, 140 280))
+  </a>
+<test>
+  <op name="getboundary" arg1="A">
+    MULTIPOINT(180 100, 140 280)
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>mPmL - points in I, B and E of lines, lines overlap, points overlap</desc>
+  <a>
+    MULTILINESTRING(
+      (100 320, 100 220), 
+      (100 180, 200 180), 
+      (220 180, 220 320), 
+      (220 320, 160 320), 
+      (100 320, 100 220), 
+      (100 180, 200 180), 
+      (220 180, 220 320), 
+      (220 320, 160 320), 
+      (100 220, 100 320))
+  </a>
+  <b>
+    MULTIPOINT(100 320, 100 260, 100 220, 100 200, 100 180, 120 180, 200 180, 220 180, 220 260, 
+    220 320, 200 320, 160 320, 140 320, 120 320, 100 320, 100 260, 100 220, 100 200, 100 180, 
+    120 180, 200 180, 220 180, 220 260, 220 320, 200 320, 160 320, 140 320, 120 320)
+  </b>
+<test>
+  <op name="getboundary" arg1="A">
+    MULTIPOINT(100 220, 100 320)
+  </op>
+</test>
+<test>
+  <op name="convexhull" arg1="A">
+    POLYGON(
+      (100 180, 100 320, 220 320, 220 180, 100 180))
+  </op>
+</test>
+<test>
+  <op name="intersection" arg1="A" arg2="B">
+    MULTIPOINT(100 180, 100 220, 100 260, 100 320, 120 180, 160 320, 200 180, 200 320, 220 180, 
+    220 260, 220 320)
+  </op>
+</test>
+<test>
+  <op name="union" arg1="A" arg2="B">
+    GEOMETRYCOLLECTION(
+      POINT(100 200), 
+      POINT(120 320), 
+      POINT(140 320), 
+      LINESTRING(100 320, 100 220), 
+      LINESTRING(100 180, 200 180), 
+      LINESTRING(220 180, 220 320), 
+      LINESTRING(220 320, 160 320))
+  </op>
+</test>
+<test>
+  <op name="difference" arg1="A" arg2="B">
+    MULTILINESTRING(
+      (100 320, 100 220), 
+      (100 180, 200 180), 
+      (220 180, 220 320), 
+      (220 320, 160 320))
+  </op>
+</test>
+<test>
+  <op name="symdifference" arg1="A" arg2="B">
+    GEOMETRYCOLLECTION(
+      POINT(100 200), 
+      POINT(120 320), 
+      POINT(140 320), 
+      LINESTRING(100 320, 100 220), 
+      LINESTRING(100 180, 200 180), 
+      LINESTRING(220 180, 220 320), 
+      LINESTRING(220 320, 160 320))
+  </op>
+</test>
+</case>
+
+</run>

Added: packages/jts/branches/upstream/current/test/vivid/TestFunctionPLPrec.xml
===================================================================
--- packages/jts/branches/upstream/current/test/vivid/TestFunctionPLPrec.xml	                        (rev 0)
+++ packages/jts/branches/upstream/current/test/vivid/TestFunctionPLPrec.xml	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,19 @@
+<run>
+  <precisionModel scale="1.0" offsetx="0.0" offsety="0.0"/>
+
+<case>
+  <desc>PP - Point just off line. Causes non-robust algorithms to fail.</desc>
+  <a>
+    LINESTRING(-123456789 -40, 381039468754763 123456789)
+  </a>
+  <b>
+    POINT(0 0)
+  </b>
+<test>
+  <op name="intersects">
+    false
+  </op>
+</test>
+</case>
+
+</run>

Added: packages/jts/branches/upstream/current/test/vivid/TestFunctionPP.xml
===================================================================
--- packages/jts/branches/upstream/current/test/vivid/TestFunctionPP.xml	                        (rev 0)
+++ packages/jts/branches/upstream/current/test/vivid/TestFunctionPP.xml	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,269 @@
+<run>
+  <precisionModel scale="1.0" offsetx="0.0" offsety="0.0"/>
+
+<case>
+  <desc>PP - point contained in both A and B</desc>
+  <a>
+    POINT(100 100)
+  </a>
+  <b>
+    POINT(100 100)
+  </b>
+<test>
+  <op name="intersection" arg1="A" arg2="B">
+    POINT(100 100)
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>PP - A different from B</desc>
+  <a>
+    POINT(100 100)
+  </a>
+  <b>
+    POINT(200 200)
+  </b>
+<test>
+  <op name="intersection" arg1="A" arg2="B">
+    GEOMETRYCOLLECTION EMPTY
+  </op>
+</test>
+<test>
+  <op name="union" arg1="A" arg2="B">
+    MULTIPOINT(100 100, 200 200)
+  </op>
+</test>
+<test>
+  <op name="difference" arg1="A" arg2="B">
+    POINT(100 100)
+  </op>
+</test>
+<test>
+  <op name="symdifference" arg1="A" arg2="B">
+    MULTIPOINT(100 100, 200 200)
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>PmP - point in A contained in B</desc>
+  <a>
+    POINT(100 100)
+  </a>
+  <b>
+    MULTIPOINT(100 100, 200 200)
+  </b>
+<test>
+  <op name="intersection" arg1="A" arg2="B">
+    POINT(100 100)
+  </op>
+</test>
+<test>
+  <op name="union" arg1="A" arg2="B">
+    MULTIPOINT(100 100, 200 200)
+  </op>
+</test>
+<test>
+  <op name="difference" arg1="A" arg2="B">
+    GEOMETRYCOLLECTION EMPTY
+  </op>
+</test>
+<test>
+  <op name="symdifference" arg1="A" arg2="B">
+    POINT(200 200)
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>mPmP - points in A only, B only, and in both</desc>
+  <a>
+    MULTIPOINT(100 100, 200 200, 300 300, 500 500)
+  </a>
+  <b>
+    MULTIPOINT(100 100, 200 200, 400 400, 600 600)
+  </b>
+<test>
+  <op name="intersection" arg1="A" arg2="B">
+    MULTIPOINT(100 100, 200 200)
+  </op>
+</test>
+<test>
+  <op name="union" arg1="A" arg2="B">
+    MULTIPOINT(100 100, 200 200, 300 300, 400 400, 500 500, 600 600)
+  </op>
+</test>
+<test>
+  <op name="difference" arg1="A" arg2="B">
+    MULTIPOINT(300 300, 500 500)
+  </op>
+</test>
+<test>
+  <op name="symdifference" arg1="A" arg2="B">
+    MULTIPOINT(300 300, 400 400, 500 500, 600 600)
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>PP - point contained in both A and B</desc>
+  <a>
+    POINT(80 200)
+  </a>
+  <b>
+    POINT(80 200)
+  </b>
+<test>
+  <op name="getboundary" arg1="A">
+    GEOMETRYCOLLECTION EMPTY
+  </op>
+</test>
+<test>
+  <op name="convexhull" arg1="A">
+    POINT(80 200)
+  </op>
+</test>
+<test>
+  <op name="intersection" arg1="A" arg2="B">
+    POINT(80 200)
+  </op>
+</test>
+<test>
+  <op name="union" arg1="A" arg2="B">
+    POINT(80 200)
+  </op>
+</test>
+<test>
+  <op name="difference" arg1="A" arg2="B">
+    GEOMETRYCOLLECTION EMPTY
+  </op>
+</test>
+<test>
+  <op name="symdifference" arg1="A" arg2="B">
+    GEOMETRYCOLLECTION EMPTY
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>PP - A different from B</desc>
+  <a>
+    POINT(80 200)
+  </a>
+  <b>
+    POINT(260 80)
+  </b>
+<test>
+  <op name="getboundary" arg1="A">
+    GEOMETRYCOLLECTION EMPTY
+  </op>
+</test>
+<test>
+  <op name="convexhull" arg1="A">
+    POINT(80 200)
+  </op>
+</test>
+<test>
+  <op name="intersection" arg1="A" arg2="B">
+    GEOMETRYCOLLECTION EMPTY
+  </op>
+</test>
+<test>
+  <op name="union" arg1="A" arg2="B">
+    MULTIPOINT(80 200, 260 80)
+  </op>
+</test>
+<test>
+  <op name="difference" arg1="A" arg2="B">
+    POINT(80 200)
+  </op>
+</test>
+<test>
+  <op name="symdifference" arg1="A" arg2="B">
+    MULTIPOINT(80 200, 260 80)
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>PP - A different from B, same y</desc>
+  <a>
+    POINT(60 260)
+  </a>
+  <b>
+    POINT(120 260)
+  </b>
+<test>
+  <op name="getboundary" arg1="A">
+    GEOMETRYCOLLECTION EMPTY
+  </op>
+</test>
+<test>
+  <op name="convexhull" arg1="A">
+    POINT(60 260)
+  </op>
+</test>
+<test>
+  <op name="intersection" arg1="A" arg2="B">
+    GEOMETRYCOLLECTION EMPTY
+  </op>
+</test>
+<test>
+  <op name="union" arg1="A" arg2="B">
+    MULTIPOINT(60 260, 120 260)
+  </op>
+</test>
+<test>
+  <op name="difference" arg1="A" arg2="B">
+    POINT(60 260)
+  </op>
+</test>
+<test>
+  <op name="symdifference" arg1="A" arg2="B">
+    MULTIPOINT(60 260, 120 260)
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>PP - A different from B, same x</desc>
+  <a>
+    POINT(80 80)
+  </a>
+  <b>
+    POINT(80 280)
+  </b>
+<test>
+  <op name="getboundary" arg1="A">
+    GEOMETRYCOLLECTION EMPTY
+  </op>
+</test>
+<test>
+  <op name="convexhull" arg1="A">
+    POINT(80 80)
+  </op>
+</test>
+<test>
+  <op name="intersection" arg1="A" arg2="B">
+    GEOMETRYCOLLECTION EMPTY
+  </op>
+</test>
+<test>
+  <op name="union" arg1="A" arg2="B">
+    MULTIPOINT(80 80, 80 280)
+  </op>
+</test>
+<test>
+  <op name="difference" arg1="A" arg2="B">
+    POINT(80 80)
+  </op>
+</test>
+<test>
+  <op name="symdifference" arg1="A" arg2="B">
+    MULTIPOINT(80 80, 80 280)
+  </op>
+</test>
+</case>
+
+</run>

Added: packages/jts/branches/upstream/current/test/vivid/TestInteriorPoint.xml
===================================================================
--- packages/jts/branches/upstream/current/test/vivid/TestInteriorPoint.xml	                        (rev 0)
+++ packages/jts/branches/upstream/current/test/vivid/TestInteriorPoint.xml	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,57 @@
+<run>
+  <precisionModel scale="1.0" offsetx="0.0" offsety="0.0"/>
+
+<case>
+  <desc>P - single point</desc>
+  <a>    POINT(10 10)  </a>
+<test><op name="getInteriorPoint" arg1="A" >    POINT(10 10)   </op></test>
+</case>
+
+<case>
+  <desc>P - single point</desc>
+  <a>    MULTIPOINT (60 300, 200 200, 240 240, 200 300, 40 140, 80 240, 140 240, 100 160, 140 200, 60 200)
+	</a>
+<test><op name="getInteriorPoint" arg1="A" >    POINT (140 240)   </op></test>
+</case>
+
+<case>
+  <desc>L - linestring with single segment</desc>
+  <a>    LINESTRING (0 0, 7 14)
+	</a>
+<test><op name="getInteriorPoint" arg1="A" >   POINT (7 14)   </op></test>
+</case>
+
+<case>
+  <desc>L - linestring with multiple segments </desc>
+  <a>    LINESTRING (0 0, 3 15, 6 2, 11 14, 16 5, 16 18, 2 22)
+	</a>
+<test><op name="getInteriorPoint" arg1="A" >   POINT (11 14)  </op></test>
+</case>
+
+<case>
+  <desc>mL - complex linestrings</desc>
+  <a>    MULTILINESTRING ((60 240, 140 300, 180 200, 40 140, 100 100, 120 220), 
+  (240 80, 260 160, 200 240, 180 340, 280 340, 240 180, 180 140, 40 200, 140 260))
+	</a>
+<test><op name="getInteriorPoint" arg1="A" >    POINT (180 200)   </op></test>
+</case>
+
+<case>
+  <desc>A - box</desc>
+  <a>    POLYGON ((0 0, 0 10, 10 10, 10 0, 0 0))
+	</a>
+<test><op name="getInteriorPoint" arg1="A" >    POINT (5 5)   </op></test>
+</case>
+
+<case>
+  <desc>mA - polygons with holes</desc>
+  <a>    MULTIPOLYGON (((60 320, 240 340, 260 100, 20 60, 120 180, 60 320), 
+  (200 280, 140 260, 180 160, 240 140, 200 280)), 
+  ((380 280, 300 260, 340 100, 440 80, 380 280), 
+    (380 220, 340 200, 400 100, 380 220)))
+	</a>
+<test><op name="getInteriorPoint" arg1="A" >    POINT (138 200)  </op></test>
+</case>
+
+
+</run>

Added: packages/jts/branches/upstream/current/test/vivid/TestRectanglePredicate.xml
===================================================================
--- packages/jts/branches/upstream/current/test/vivid/TestRectanglePredicate.xml	                        (rev 0)
+++ packages/jts/branches/upstream/current/test/vivid/TestRectanglePredicate.xml	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,101 @@
+<run>
+  <precisionModel scale="1.0" offsetx="0.0" offsety="0.0"/>
+
+<case>
+  <desc>A disjoint</desc>
+  <a>
+    POLYGON(
+      (0 0, 80 0, 80 80, 0 80, 0 0))
+  </a>
+  <b>
+    POLYGON(
+      (100 200, 100 140, 180 140, 180 200, 100 200))
+  </b>
+<test>  <op name="intersects" arg1="A" arg2="B">   false   </op> </test>
+<test>  <op name="contains" arg1="A" arg2="B">   false   </op> </test>
+</case>
+
+<case>
+  <desc>A contained in rectangle</desc>
+  <a>
+    POLYGON((0 0, 100 0, 100 100, 0 100, 0 0))
+  </a>
+  <b>
+    POLYGON((10 10, 10 90, 90 90, 90 10, 10 10))
+  </b>
+<test>  <op name="intersects" arg1="A" arg2="B">   true   </op> </test>
+<test>  <op name="contains" arg1="A" arg2="B">   true   </op> </test>
+</case>
+
+<case>
+  <desc>A containing rectangle</desc>
+  <a>
+    POLYGON((0 0, 100 0, 100 100, 0 100, 0 0))
+  </a>
+  <b>
+    POLYGON ((60 180, -100 120, -140 60, -40 20, -100 -80, 40 -20, 140 -100, 140 40, 260 160, 80 120, 60 180))
+  </b>
+<test>  <op name="intersects" arg1="A" arg2="B">   true   </op> </test>
+<test>  <op name="contains" arg1="A" arg2="B">   false   </op> </test>
+</case>
+
+<case>
+  <desc>mA containing rectangle</desc>
+  <a>
+    POLYGON((0 0, 100 0, 100 100, 0 100, 0 0))
+  </a>
+  <b>
+	MULTIPOLYGON (((-60 180, -60 -60, 40 -20, 140 -100, 180 120, -20 140, -60 180)), 
+  	((20 280, 0 180, 180 160, 200 280, 20 280)))
+  </b>
+<test>  <op name="intersects" arg1="A" arg2="B">   true   </op> </test>
+<test>  <op name="contains" arg1="A" arg2="B">   false   </op> </test>
+</case>
+
+<case>
+  <desc>L overlaps thru Y axis side</desc>
+  <a>
+    POLYGON((0 0, 100 0, 100 100, 0 100, 0 0))
+  </a>
+  <b>
+    LINESTRING(10 10, 200 10)
+  </b>
+<test>  <op name="intersects" arg1="A" arg2="B">   true   </op> </test>
+</case>
+
+<case>
+  <desc>L overlaps thru X axis side</desc>
+  <a>
+    POLYGON((0 0, 100 0, 100 100, 0 100, 0 0))
+  </a>
+  <b>
+    LINESTRING(10 10, 10 2000)
+  </b>
+<test>  <op name="intersects" arg1="A" arg2="B">   true   </op> </test>
+</case>
+
+<case>
+  <desc>L line intersection</desc>
+  <a>
+    POLYGON((0 0, 100 0, 100 100, 0 100, 0 0))
+  </a>
+  <b>
+    LINESTRING( 10 10, -10 -20 )
+  </b>
+<test>  <op name="intersects" arg1="A" arg2="B">   true   </op> </test>
+</case>
+
+<case>
+  <desc>mL with one component contained</desc>
+  <a>
+    POLYGON((0 0, 100 0, 100 100, 0 100, 0 0))
+  </a>
+  <b>
+    MULTILINESTRING( (10 10, 10 20), (200 10, 200 20) )
+  </b>
+<test>  <op name="intersects" arg1="A" arg2="B">   true   </op> </test>
+<test>  <op name="contains" arg1="A" arg2="B">   false   </op> </test>
+</case>
+
+
+</run>

Added: packages/jts/branches/upstream/current/test/vivid/TestRelateAA.xml
===================================================================
--- packages/jts/branches/upstream/current/test/vivid/TestRelateAA.xml	                        (rev 0)
+++ packages/jts/branches/upstream/current/test/vivid/TestRelateAA.xml	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,221 @@
+<run>
+  <precisionModel scale="1.0" offsetx="0.0" offsety="0.0"/>
+
+<case>
+  <desc>AA disjoint</desc>
+  <a>
+    POLYGON(
+      (0 0, 80 0, 80 80, 0 80, 0 0))
+  </a>
+  <b>
+    POLYGON(
+      (100 200, 100 140, 180 140, 180 200, 100 200))
+  </b>
+<test>
+  <op name="relate" arg3="FF2FF1212" arg1="A" arg2="B">
+    true
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>AA equal but opposite orientation</desc>
+  <a>
+    POLYGON(
+      (0 0, 140 0, 140 140, 0 140, 0 0))
+  </a>
+  <b>
+    POLYGON(
+      (140 0, 0 0, 0 140, 140 140, 140 0))
+  </b>
+<test>
+  <op name="relate" arg3="2FFF1FFF2" arg1="A" arg2="B">
+    true
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>AA A-shell contains B-shell</desc>
+  <a>
+    POLYGON(
+      (40 60, 360 60, 360 300, 40 300, 40 60))
+  </a>
+  <b>
+    POLYGON(
+      (120 100, 280 100, 280 240, 120 240, 120 100))
+  </b>
+<test>
+  <op name="relate" arg3="212FF1FF2" arg1="A" arg2="B">
+    true
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>AA A-shell contains B-shell contains A-hole</desc>
+  <a>
+    POLYGON(
+      (40 60, 420 60, 420 320, 40 320, 40 60), 
+      (200 140, 160 220, 260 200, 200 140))
+  </a>
+  <b>
+    POLYGON(
+      (80 100, 360 100, 360 280, 80 280, 80 100))
+  </b>
+<test>
+  <op name="relate" arg3="2121F12F2" arg1="A" arg2="B">
+    true
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>AA A-shell contains B-shell contains A-hole contains B-hole</desc>
+  <a>
+    POLYGON(
+      (0 280, 0 0, 260 0, 260 280, 0 280), 
+      (220 240, 40 240, 40 40, 220 40, 220 240))
+  </a>
+  <b>
+    POLYGON(
+      (20 260, 240 260, 240 20, 20 20, 20 260), 
+      (160 180, 80 180, 120 120, 160 180))
+  </b>
+<test>
+  <op name="relate" arg3="2121F1212" arg1="A" arg2="B">
+    true
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>AA A-shell overlapping B-shell</desc>
+  <a>
+    POLYGON(
+      (60 80, 200 80, 200 220, 60 220, 60 80))
+  </a>
+  <b>
+    POLYGON(
+      (120 140, 260 140, 260 260, 120 260, 120 140))
+  </b>
+<test>
+  <op name="relate" arg3="212101212" arg1="A" arg2="B">
+    true
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>AA A-shell overlapping B-shell at B-vertex</desc>
+  <a>
+    POLYGON(
+      (60 220, 220 220, 140 140, 60 220))
+  </a>
+  <b>
+    POLYGON(
+      (100 180, 180 180, 180 100, 100 100, 100 180))
+  </b>
+<test>
+  <op name="relate" arg3="212101212" arg1="A" arg2="B">
+    true
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>AA A-shell overlapping B-shell at A &amp; B-vertex</desc>
+  <a>
+    POLYGON(
+      (40 40, 180 40, 180 180, 40 180, 40 40))
+  </a>
+  <b>
+    POLYGON(
+      (180 40, 40 180, 160 280, 300 140, 180 40))
+  </b>
+<test>
+  <op name="relate" arg3="212101212" arg1="A" arg2="B">
+    true
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>AmA A-shells overlapping B-shell at A-vertex</desc>
+  <a>
+    POLYGON(
+      (100 60, 140 100, 100 140, 60 100, 100 60))
+  </a>
+  <b>
+    MULTIPOLYGON(
+      (
+        (80 40, 120 40, 120 80, 80 80, 80 40)), 
+      (
+        (120 80, 160 80, 160 120, 120 120, 120 80)), 
+      (
+        (80 120, 120 120, 120 160, 80 160, 80 120)), 
+      (
+        (40 80, 80 80, 80 120, 40 120, 40 80)))
+  </b>
+<test>
+  <op name="relate" arg3="21210F212" arg1="A" arg2="B">
+    true
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>AA A-shell touches B-shell, which contains A-hole</desc>
+  <a>
+    POLYGON(
+      (40 280, 200 280, 200 100, 40 100, 40 280), 
+      (100 220, 120 220, 120 200, 100 180, 100 220))
+  </a>
+  <b>
+    POLYGON(
+      (40 280, 180 260, 180 120, 60 120, 40 280))
+  </b>
+<test>
+  <op name="relate" arg3="2121012F2" arg1="A" arg2="B">
+    true
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>AA - A-hole contains B, boundaries touch in line</desc>
+  <a>
+    POLYGON(
+      (0 200, 0 0, 200 0, 200 200, 0 200), 
+      (20 180, 130 180, 130 30, 20 30, 20 180))
+  </a>
+  <b>
+    POLYGON(
+      (60 90, 130 90, 130 30, 60 30, 60 90))
+  </b>
+<test>
+  <op name="relate" arg3="FF2F11212" arg1="A" arg2="B">
+    true
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>AA - A-hole contains B, boundaries touch in points</desc>
+  <a>
+    POLYGON(
+      (150 150, 410 150, 280 20, 20 20, 150 150), 
+      (170 120, 330 120, 260 50, 100 50, 170 120))
+  </a>
+  <b>
+    POLYGON(
+      (270 90, 200 50, 150 80, 210 120, 270 90))
+  </b>
+<test>
+  <op name="relate" arg3="FF2F01212" arg1="A" arg2="B">
+    true
+  </op>
+</test>
+</case>
+
+</run>

Added: packages/jts/branches/upstream/current/test/vivid/TestRelateAC.xml
===================================================================
--- packages/jts/branches/upstream/current/test/vivid/TestRelateAC.xml	                        (rev 0)
+++ packages/jts/branches/upstream/current/test/vivid/TestRelateAC.xml	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,28 @@
+<run>
+  <precisionModel scale="1.0" offsetx="0.0" offsety="0.0"/>
+
+<case>
+  <desc>AC A-shells overlapping B-shell at A-vertex</desc>
+  <a>
+    POLYGON(
+      (100 60, 140 100, 100 140, 60 100, 100 60))
+  </a>
+  <b>
+    MULTIPOLYGON(
+      (
+        (80 40, 120 40, 120 80, 80 80, 80 40)), 
+      (
+        (120 80, 160 80, 160 120, 120 120, 120 80)), 
+      (
+        (80 120, 120 120, 120 160, 80 160, 80 120)), 
+      (
+        (40 80, 80 80, 80 120, 40 120, 40 80)))
+  </b>
+<test>
+  <op name="relate" arg3="21210F212" arg1="A" arg2="B">
+    true
+  </op>
+</test>
+</case>
+
+</run>

Added: packages/jts/branches/upstream/current/test/vivid/TestRelateLA.xml
===================================================================
--- packages/jts/branches/upstream/current/test/vivid/TestRelateLA.xml	                        (rev 0)
+++ packages/jts/branches/upstream/current/test/vivid/TestRelateLA.xml	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,190 @@
+<run>
+  <precisionModel scale="1.0" offsetx="0.0" offsety="0.0"/>
+
+<case>
+  <desc>LA - intersection at NV: {A-Bdy, A-Int} = {B-Bdy, B-Int}</desc>
+  <a>
+    LINESTRING(100 120, 100 240)
+  </a>
+  <b>
+    POLYGON(
+      (40 60, 160 60, 160 180, 40 180, 40 60))
+  </b>
+<test>
+  <op name="relate" arg3="1010F0212" arg1="A" arg2="B">
+    true
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>LA - intersection at V: {A-Bdy, A-Int} = {B-Bdy, B-Int}</desc>
+  <a>
+    LINESTRING(80 80, 140 140, 200 200)
+  </a>
+  <b>
+    POLYGON(
+      (40 40, 140 40, 140 140, 40 140, 40 40))
+  </b>
+<test>
+  <op name="relate" arg3="1010F0212" arg1="A" arg2="B">
+    true
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>LmA - intersection at NV, L contained in A</desc>
+  <a>
+    LINESTRING(70 50, 70 150)
+  </a>
+  <b>
+    MULTIPOLYGON(
+      (
+        (0 0, 0 100, 140 100, 140 0, 0 0)), 
+      (
+        (20 170, 70 100, 130 170, 20 170)))
+  </b>
+<test>
+  <op name="relate" arg3="10F0FF212" arg1="A" arg2="B">
+    true
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>LA - A crosses B at {shell-NV, hole-V}</desc>
+  <a>
+    LINESTRING(60 160, 150 70)
+  </a>
+  <b>
+    POLYGON(
+      (190 190, 360 20, 20 20, 190 190), 
+      (110 110, 250 100, 140 30, 110 110))
+  </b>
+<test>
+  <op name="relate" arg3="F01FF0212" arg1="A" arg2="B">
+    true
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>LA - A intersects B at {shell-NV}, B-Int, {hole-V}</desc>
+  <a>
+    LINESTRING(60 160, 150 70)
+  </a>
+  <b>
+    POLYGON(
+      (190 190, 360 20, 20 20, 190 190), 
+      (111 110, 250 100, 140 30, 111 110))
+  </b>
+<test>
+  <op name="relate" arg3="101FF0212" arg1="A" arg2="B">
+    true
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>LA - A crosses B hole at {hole1-V, hole2-NV}</desc>
+  <a>
+    LINESTRING(80 110, 170 110)
+  </a>
+  <b>
+    POLYGON(
+      (20 200, 20 20, 240 20, 240 200, 20 200), 
+      (130 110, 60 40, 60 180, 130 110), 
+      (130 180, 130 40, 200 110, 130 180))
+  </b>
+<test>
+  <op name="relate" arg3="F01FF0212" arg1="A" arg2="B">
+    true
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>LA - A crosses B hole at {hole1-V}, B-Int, {hole2-NV}</desc>
+  <a>
+    LINESTRING(80 110, 170 110)
+  </a>
+  <b>
+    POLYGON(
+      (20 200, 20 20, 240 20, 240 200, 20 200), 
+      (130 110, 60 40, 60 180, 130 110), 
+      (130 180, 131 40, 200 110, 130 180))
+  </b>
+<test>
+  <op name="relate" arg3="101FF0212" arg1="A" arg2="B">
+    true
+  </op>
+</test>
+</case>
+
+<case>
+<desc>LA - Line with endpoints in interior but crossing exterior of multipolygon</desc>
+  <a>
+    LINESTRING(160 70, 320 230)
+  </a>
+  <b>
+    MULTIPOLYGON(
+      (
+        (140 110, 260 110, 170 20, 50 20, 140 110)), 
+      (
+        (300 270, 420 270, 340 190, 220 190, 300 270)))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="1010FF212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>LA - Line with a very small piece in the exterior between parts of a multipolygon</desc>
+  <a>
+    LINESTRING(100 140, 100 40)
+  </a>
+  <b>
+    MULTIPOLYGON(
+      (
+        (20 80, 180 79, 100 0, 20 80)), 
+      (
+        (20 160, 180 160, 100 80, 20 160)))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="1010FF212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>LA - Line contained completely and spanning parts of multipolygon</desc>
+  <a>
+    LINESTRING(100 140, 100 40)
+  </a>
+  <b>
+    MULTIPOLYGON(
+      (
+        (20 80, 180 80, 100 0, 20 80)), 
+      (
+        (20 160, 180 160, 100 80, 20 160)))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="10F0FF212">true</op>
+  </test>
+</case>
+
+<case>
+<desc>LA - overlapping ring and triangle</desc>
+  <a>
+    LINESTRING(110 60, 20 150, 200 150, 110 60)
+  </a>
+  <b>
+    POLYGON(
+      (20 20, 200 20, 110 110, 20 20))
+  </b>
+  <test>
+    <op name="relate" arg1="A" arg2="B" arg3="101FFF212">true</op>
+  </test>
+</case>
+
+</run>

Added: packages/jts/branches/upstream/current/test/vivid/TestRelateLC.xml
===================================================================
--- packages/jts/branches/upstream/current/test/vivid/TestRelateLC.xml	                        (rev 0)
+++ packages/jts/branches/upstream/current/test/vivid/TestRelateLC.xml	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,79 @@
+<run>
+  <precisionModel scale="1.0" offsetx="0.0" offsety="0.0"/>
+
+<case>
+  <desc>LC - topographically equal with no boundary</desc>
+  <a>
+    LINESTRING(0 0, 0 50, 50 50, 50 0, 0 0)
+  </a>
+  <b>
+    MULTILINESTRING(
+      (0 0, 0 50), 
+      (0 50, 50 50), 
+      (50 50, 50 0), 
+      (50 0, 0 0))
+  </b>
+<test>
+  <op name="relate" arg3="1FFFFFFF2" arg1="A" arg2="B">
+    true
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>LC - intersection (containment) along mod-2 A-Int line segment</desc>
+  <a>
+    LINESTRING(40 180, 140 180)
+  </a>
+  <b>
+    MULTIPOLYGON(
+      (
+        (20 320, 180 320, 180 180, 20 180, 20 320)), 
+      (
+        (20 180, 20 80, 180 80, 180 180, 20 180)))
+  </b>
+<test>
+  <op name="relate" arg3="1FF0FF212" arg1="A" arg2="B">
+    true
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>LC - intersection (overlap) along mod-2 A-Int line segment</desc>
+  <a>
+    LINESTRING(40 180, 140 180)
+  </a>
+  <b>
+    MULTIPOLYGON(
+      (
+        (20 320, 180 320, 180 180, 20 180, 20 320)), 
+      (
+        (60 180, 60 80, 180 80, 180 180, 60 180)))
+  </b>
+<test>
+  <op name="relate" arg3="11F00F212" arg1="A" arg2="B">
+    true
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>LC - equal with boundary intersection</desc>
+  <a>
+    LINESTRING(0 0, 60 0, 60 60, 60 0, 120 0)
+  </a>
+  <b>
+    MULTILINESTRING(
+      (0 0, 60 0), 
+      (60 0, 120 0), 
+      (60 0, 60 60))
+  </b>
+<test>
+  <op name="relate" arg3="10FF0FFF2" arg1="A" arg2="B">
+    true
+  </op>
+</test>
+</case>
+
+</run>

Added: packages/jts/branches/upstream/current/test/vivid/TestRelateLL.xml
===================================================================
--- packages/jts/branches/upstream/current/test/vivid/TestRelateLL.xml	                        (rev 0)
+++ packages/jts/branches/upstream/current/test/vivid/TestRelateLL.xml	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,297 @@
+<run>
+  <precisionModel scale="1.0" offsetx="0.0" offsety="0.0"/>
+
+<case>
+  <desc>LL - disjoint, non-overlapping envelopes</desc>
+  <a>
+    LINESTRING(60 0, 20 80, 100 80, 80 120, 40 140)
+  </a>
+  <b>
+    LINESTRING(140 300, 220 160, 260 200, 240 260)
+  </b>
+<test>
+  <op name="relate" arg3="FF1FF0102" arg1="A" arg2="B">
+    true
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>LL - disjoint, overlapping envelopes</desc>
+  <a>
+    LINESTRING(60 0, 20 80, 100 80, 80 120, 40 140)
+  </a>
+  <b>
+    LINESTRING(60 40, 140 40, 140 160, 0 160)
+  </b>
+<test>
+  <op name="relate" arg3="FF1FF0102" arg1="A" arg2="B">
+    true
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>LL - disjoint, non-overlapping envelopes, B closed</desc>
+  <a>
+    LINESTRING(60 0, 20 80, 100 80, 80 120, 40 140)
+  </a>
+  <b>
+    LINESTRING(140 280, 240 280, 240 180, 140 180, 140 280)
+  </b>
+<test>
+  <op name="relate" arg3="FF1FF01F2" arg1="A" arg2="B">
+    true
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>LL - disjoint, overlapping envelopes, B closed</desc>
+  <a>
+    LINESTRING(140 0, 0 0, 40 60, 0 120, 60 200, 220 160, 220 40)
+  </a>
+  <b>
+    LINESTRING(80 140, 180 100, 160 40, 100 40, 60 100, 80 140)
+  </b>
+<test>
+  <op name="relate" arg3="FF1FF01F2" arg1="A" arg2="B">
+    true
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>Line vs line - pointwise equal</desc>
+  <a>
+    LINESTRING(20 20, 80 80)
+  </a>
+  <b>
+    LINESTRING(20 20, 80 80)
+  </b>
+<test>
+  <op name="relate" arg3="1FFF0FFF2" arg1="A" arg2="B">
+    true
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>Line vs line - pointwise equal</desc>
+  <a>
+    LINESTRING(40 40, 160 160, 200 60, 60 140)
+  </a>
+  <b>
+    LINESTRING(40 40, 160 160, 200 60, 60 140)
+  </b>
+<test>
+  <op name="relate" arg3="1FFF0FFF2" arg1="A" arg2="B">
+    true
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>Line vs line - topologically equal</desc>
+  <a>
+    LINESTRING(40 40, 200 40)
+  </a>
+  <b>
+    LINESTRING(200 40, 140 40, 40 40)
+  </b>
+<test>
+  <op name="relate" arg3="1FFF0FFF2" arg1="A" arg2="B">
+    true
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>LL - topographically equal with self-intersection</desc>
+  <a>
+    LINESTRING(0 0, 110 0, 60 0)
+  </a>
+  <b>
+    LINESTRING(0 0, 110 0)
+  </b>
+<test>
+  <op name="relate" arg3="10F00FFF2" arg1="A" arg2="B">
+    true
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>LmL - topographically equal with no boundary</desc>
+  <a>
+    LINESTRING(0 0, 0 50, 50 50, 50 0, 0 0)
+  </a>
+  <b>
+    MULTILINESTRING(
+      (0 0, 0 50), 
+      (0 50, 50 50), 
+      (50 50, 50 0), 
+      (50 0, 0 0))
+  </b>
+<test>
+  <op name="relate" arg3="1FFFFFFF2" arg1="A" arg2="B">
+    true
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>LmL - topographically equal with self intersections</desc>
+  <a>
+    LINESTRING(0 0, 80 0, 80 60, 80 0, 170 0)
+  </a>
+  <b>
+    MULTILINESTRING(
+      (0 0, 170 0), 
+      (80 0, 80 60))
+  </b>
+<test>
+  <op name="relate" arg3="10FF0FFF2" arg1="A" arg2="B">
+    true
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>LL - A-IntNV = B-IntNV</desc>
+  <a>
+    LINESTRING(80 100, 180 200)
+  </a>
+  <b>
+    LINESTRING(80 180, 180 120)
+  </b>
+<test>
+  <op name="relate" arg3="0F1FF0102" arg1="A" arg2="B">
+    true
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>intersect in Int NV</desc>
+  <a>
+    LINESTRING(40 40, 100 100, 160 160)
+  </a>
+  <b>
+    LINESTRING(160 60, 100 100, 60 140)
+  </b>
+<test>
+  <op name="relate" arg3="0F1FF0102" arg1="A" arg2="B">
+    true
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>LL - intersection: {A-Bdy, A-IntV} = B-IntNV</desc>
+  <a>
+    LINESTRING(40 40, 100 100, 180 100, 180 180, 100 180, 100 100)
+  </a>
+  <b>
+    LINESTRING(140 60, 60 140)
+  </b>
+<test>
+  <op name="relate" arg3="FF10F0102" arg1="A" arg2="B">
+    true
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>LL - intersection: {A-Bdy, A-IntNV} = B-IntNV</desc>
+  <a>
+    LINESTRING(40 40, 180 180, 100 180, 100 100)
+  </a>
+  <b>
+    LINESTRING(140 60, 60 140)
+  </b>
+<test>
+  <op name="relate" arg3="FF10F0102" arg1="A" arg2="B">
+    true
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>LL - intersection: A-IntNV = {B-Bdy, B-IntNV}</desc>
+  <a>
+    LINESTRING(20 110, 200 110)
+  </a>
+  <b>
+    LINESTRING(200 200, 20 20, 200 20, 110 110, 20 200, 110 200, 110 110)
+  </b>
+<test>
+  <op name="relate" arg3="F01FF0102" arg1="A" arg2="B">
+    true
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>LL - one segment overlapping, one distinct</desc>
+  <a>
+    LINESTRING(80 90, 50 50, 0 0)
+  </a>
+  <b>
+    LINESTRING(0 0, 100 100)
+  </b>
+<test>
+  <op name="relate" arg3="1F1F00102" arg1="A" arg2="B">
+    true
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>LL - A contained in B</desc>
+  <a>
+    LINESTRING(40 140, 240 140)
+  </a>
+  <b>
+    LINESTRING(40 140, 100 140, 80 80, 120 60, 100 140, 160 140, 160 100, 200 100, 160 140, 
+    240 140)
+  </b>
+<test>
+  <op name="relate" arg3="1FFF0F1F2" arg1="A" arg2="B">
+    true
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>LL - simple overlapping lines</desc>
+  <a>
+    LINESTRING(20 20, 100 20, 20 20)
+  </a>
+  <b>
+    LINESTRING(60 20, 200 20)
+  </b>
+<test>
+  <op name="relate" arg3="101FFF102" arg1="A" arg2="B">
+    true
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>LL - A-spiral, B-contained</desc>
+  <a>
+    LINESTRING(40 60, 180 60, 180 140, 100 140, 100 60, 220 60, 220 180, 80 180, 80 60, 
+    280 60)
+  </a>
+  <b>
+    LINESTRING(140 60, 180 60, 220 60, 260 60)
+  </b>
+<test>
+  <op name="relate" arg3="101FF0FF2" arg1="A" arg2="B">
+    true
+  </op>
+</test>
+</case>
+
+</run>

Added: packages/jts/branches/upstream/current/test/vivid/TestRelatePA.xml
===================================================================
--- packages/jts/branches/upstream/current/test/vivid/TestRelatePA.xml	                        (rev 0)
+++ packages/jts/branches/upstream/current/test/vivid/TestRelatePA.xml	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,103 @@
+<run>
+  <precisionModel scale="1.0" offsetx="0.0" offsety="0.0"/>
+
+<case>
+  <desc>PA - disjoint</desc>
+  <a>
+    POINT(20 20)
+  </a>
+  <b>
+    POLYGON(
+      (60 120, 60 40, 160 40, 160 120, 60 120))
+  </b>
+<test>
+  <op name="relate" arg3="FF0FFF212" arg1="A" arg2="B">
+    true
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>mPA - points in B: E, I</desc>
+  <a>
+    MULTIPOINT(0 20, 40 20)
+  </a>
+  <b>
+    POLYGON(
+      (20 40, 20 0, 60 0, 60 40, 20 40))
+  </b>
+<test>
+  <op name="relate" arg3="0F0FFF212" arg1="A" arg2="B">
+    true
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>mPA - points in B: E, B</desc>
+  <a>
+    MULTIPOINT(0 20, 20 20)
+  </a>
+  <b>
+    POLYGON(
+      (20 40, 20 0, 60 0, 60 40, 20 40))
+  </b>
+<test>
+  <op name="relate" arg3="F00FFF212" arg1="A" arg2="B">
+    true
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>mPA - points in B: B, I</desc>
+  <a>
+    MULTIPOINT(20 20, 40 20)
+  </a>
+  <b>
+    POLYGON(
+      (20 40, 20 0, 60 0, 60 40, 20 40))
+  </b>
+<test>
+  <op name="relate" arg3="00FFFF212" arg1="A" arg2="B">
+    true
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>mPA - points in B: I, B, E</desc>
+  <a>
+    MULTIPOINT(80 260, 140 260, 180 260)
+  </a>
+  <b>
+    POLYGON(
+      (40 320, 140 320, 140 200, 40 200, 40 320))
+  </b>
+<test>
+  <op name="relate" arg3="000FFF212" arg1="A" arg2="B">
+    true
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>PmA - point in B: mod-2 I</desc>
+  <a>
+    POINT(40 40)
+  </a>
+  <b>
+    MULTIPOLYGON(
+      (
+        (0 40, 0 0, 40 0, 40 40, 0 40)), 
+      (
+        (40 80, 40 40, 80 40, 80 80, 40 80)))
+  </b>
+<test>
+  <op name="relate" arg3="F0FFFF212" arg1="A" arg2="B">
+    true
+  </op>
+</test>
+</case>
+
+</run>

Added: packages/jts/branches/upstream/current/test/vivid/TestRelatePL.xml
===================================================================
--- packages/jts/branches/upstream/current/test/vivid/TestRelatePL.xml	                        (rev 0)
+++ packages/jts/branches/upstream/current/test/vivid/TestRelatePL.xml	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,124 @@
+<run>
+  <precisionModel scale="1.0" offsetx="0.0" offsety="0.0"/>
+
+<case>
+  <desc>PL - disjoint</desc>
+  <a>
+    POINT(60 120)
+  </a>
+  <b>
+    LINESTRING(40 40, 120 120, 200 120)
+  </b>
+<test>
+  <op name="relate" arg3="FF0FFF102" arg1="A" arg2="B">
+    true
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>PL - touches Bdy</desc>
+  <a>
+    POINT(40 40)
+  </a>
+  <b>
+    LINESTRING(40 40, 100 100, 160 100)
+  </b>
+<test>
+  <op name="relate" arg3="F0FFFF102" arg1="A" arg2="B">
+    true
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>PL - touches non-vertex</desc>
+  <a>
+    POINT(60 60)
+  </a>
+  <b>
+    LINESTRING(40 40, 100 100)
+  </b>
+<test>
+  <op name="relate" arg3="0FFFFF102" arg1="A" arg2="B">
+    true
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>mPL - touches Bdy and Ext</desc>
+  <a>
+    MULTIPOINT(40 40, 100 40)
+  </a>
+  <b>
+    LINESTRING(40 40, 80 80)
+  </b>
+<test>
+  <op name="relate" arg3="F00FFF102" arg1="A" arg2="B">
+    true
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>mPL - touches Int and Bdy</desc>
+  <a>
+    MULTIPOINT(40 40, 60 60)
+  </a>
+  <b>
+    LINESTRING(40 40, 80 80)
+  </b>
+<test>
+  <op name="relate" arg3="00FFFF102" arg1="A" arg2="B">
+    true
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>mPL - touches Int and Ext</desc>
+  <a>
+    MULTIPOINT(60 60, 100 100)
+  </a>
+  <b>
+    LINESTRING(40 40, 80 80)
+  </b>
+<test>
+  <op name="relate" arg3="0F0FFF102" arg1="A" arg2="B">
+    true
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>mPL - touches IntNV and Ext</desc>
+  <a>
+    MULTIPOINT(60 60, 100 100)
+  </a>
+  <b>
+    LINESTRING(40 40, 80 80)
+  </b>
+<test>
+  <op name="relate" arg3="0F0FFF102" arg1="A" arg2="B">
+    true
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>mPL - touches IntV and Ext</desc>
+  <a>
+    MULTIPOINT(60 60, 100 100)
+  </a>
+  <b>
+    LINESTRING(40 40, 60 60, 80 80)
+  </b>
+<test>
+  <op name="relate" arg3="0F0FFF102" arg1="A" arg2="B">
+    true
+  </op>
+</test>
+</case>
+
+</run>

Added: packages/jts/branches/upstream/current/test/vivid/TestRelatePP.xml
===================================================================
--- packages/jts/branches/upstream/current/test/vivid/TestRelatePP.xml	                        (rev 0)
+++ packages/jts/branches/upstream/current/test/vivid/TestRelatePP.xml	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,64 @@
+<run>
+  <precisionModel scale="1.0" offsetx="0.0" offsety="0.0"/>
+
+<case>
+  <desc>same point</desc>
+  <a>
+    POINT(20 20)
+  </a>
+  <b>
+    POINT(20 20)
+  </b>
+<test>
+  <op name="relate" arg3="0FFFFFFF2" arg1="A" arg2="B">
+    true
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>different point</desc>
+  <a>
+    POINT(20 20)
+  </a>
+  <b>
+    POINT(20 30)
+  </b>
+<test>
+  <op name="relate" arg3="FF0FFF0F2" arg1="A" arg2="B">
+    true
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>some same, some different points</desc>
+  <a>
+    MULTIPOINT(40 40, 80 60, 40 100)
+  </a>
+  <b>
+    MULTIPOINT(40 40, 80 60, 120 100)
+  </b>
+<test>
+  <op name="relate" arg3="0F0FFF0F2" arg1="A" arg2="B">
+    true
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>same points</desc>
+  <a>
+    MULTIPOINT(40 40, 80 60, 120 100)
+  </a>
+  <b>
+    MULTIPOINT(40 40, 80 60, 120 100)
+  </b>
+<test>
+  <op name="relate" arg3="0FFFFFFF2" arg1="A" arg2="B">
+    true
+  </op>
+</test>
+</case>
+
+</run>

Added: packages/jts/branches/upstream/current/test/vivid/TestSimple.xml
===================================================================
--- packages/jts/branches/upstream/current/test/vivid/TestSimple.xml	                        (rev 0)
+++ packages/jts/branches/upstream/current/test/vivid/TestSimple.xml	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,296 @@
+<run>
+  <precisionModel scale="1.0" offsetx="0.0" offsety="0.0"/>
+
+<case>
+  <desc>P - point</desc>
+  <a>
+    POINT(10 10)
+  </a>
+<test>
+  <op name="isSimple" arg1="A">
+    true
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>mP - multipoint with repeated points</desc>
+  <a>
+    MULTIPOINT (80 280, 80 220, 160 220, 80 220)
+  </a>
+<test>
+  <op name="isSimple" arg1="A">
+    false
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>mP - multipoint with no repeated points</desc>
+  <a>
+    MULTIPOINT (80 280, 80 220, 160 220)
+  </a>
+<test>
+  <op name="isSimple" arg1="A">
+    true
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>mP - empty</desc>
+  <a>
+    MULTIPOINT EMPTY
+  </a>
+<test>
+  <op name="isSimple" arg1="A">
+    true
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>L - simple line</desc>
+  <a>
+    LINESTRING(10 10, 20 20)
+  </a>
+<test>
+  <op name="isSimple" arg1="A">
+    true
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>L - non-simple, proper interior intersection</desc>
+  <a>
+    LINESTRING (20 60, 160 60, 80 160, 80 20)
+  </a>
+<test>
+  <op name="isSimple" arg1="A">
+    false
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>L - non-simple, interior intersection at vertices</desc>
+  <a>
+    LINESTRING (20 80, 80 20, 80 80, 140 60, 80 20, 160 20)
+  </a>
+<test>
+  <op name="isSimple" arg1="A">
+    false
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>L - non-simple, interior intersection at Bdy/non-vertex</desc>
+  <a>
+    LINESTRING (20 60, 100 60, 60 100, 60 60)
+  </a>
+<test>
+  <op name="isSimple" arg1="A">
+    false
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>L - non-simple, interior intersection at Bdy/vertex</desc>
+  <a>
+    LINESTRING (20 60, 60 60, 100 60, 60 100, 60 60)
+  </a>
+<test>
+  <op name="isSimple" arg1="A">
+    false
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>L - simple, intersection at Bdy/Bdy (ring)</desc>
+  <a>
+    LINESTRING (20 20, 80 20, 80 80, 20 20)
+  </a>
+<test>
+  <op name="isSimple" arg1="A">
+    true
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>L - simple, intersection at Bdy/Bdy + non-vertex</desc>
+  <a>
+    LINESTRING (80 80, 20 20, 20 80, 140 80, 140 140, 80 80)
+  </a>
+<test>
+  <op name="isSimple" arg1="A">
+    false
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>L - empty</desc>
+  <a>
+    LINESTRING EMPTY
+  </a>
+<test>
+  <op name="isSimple" arg1="A">
+    true
+  </op>
+</test>
+</case>
+
+
+<case>
+  <desc>mL - intersection between elements at non-vertex</desc>
+  <a>
+    MULTILINESTRING(
+  (40 140, 160 40), 
+  (160 140, 40 40))
+  </a>
+<test>
+  <op name="isSimple" arg1="A">
+    false
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>mL - no intersection between elements</desc>
+  <a>
+    MULTILINESTRING(
+  (20 160, 20 20), 
+  (100 160, 100 20))
+  </a>
+<test>
+  <op name="isSimple" arg1="A">
+    true
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>mL - mutual intersection at endpoints only</desc>
+  <a>
+    MULTILINESTRING ((60 140, 20 80, 60 40), 
+  (60 40, 100 80, 60 140))
+  </a>
+<test>
+  <op name="isSimple" arg1="A">
+    true
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>mL - one element is non-simple</desc>
+  <a>
+    MULTILINESTRING ((60 40, 140 40, 100 120, 100 0), 
+  (100 200, 200 120))
+  </a>
+<test>
+  <op name="isSimple" arg1="A">
+    false
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>mL - proper intersection between elements at vertex</desc>
+  <a>
+    MULTILINESTRING ((40 120, 100 60), 
+  (160 120, 100 60), 
+  (40 60, 160 60))
+  </a>
+<test>
+  <op name="isSimple" arg1="A">
+    false
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>mL - intersection between closed lines</desc>
+  <a>
+    MULTILINESTRING ((80 160, 40 220, 40 100, 80 160), 
+  (80 160, 120 220, 120 100, 80 160))
+  </a>
+<test>
+  <op name="isSimple" arg1="A">
+    false
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>mL - intersection between closed and open lines</desc>
+  <a>
+    MULTILINESTRING ((80 160, 40 220), 
+  (80 160, 120 220, 120 100, 80 160), 
+  (40 100, 80 160))
+  </a>
+<test>
+  <op name="isSimple" arg1="A">
+    false
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>A</desc>
+  <a>
+    POLYGON ((180 260, 80 300, 40 180, 160 120, 180 260))
+  </a>
+<test>
+  <op name="isSimple" arg1="A">
+    true
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>A - empty</desc>
+  <a>
+    POLYGON EMPTY
+  </a>
+<test>
+  <op name="isSimple" arg1="A">
+    true
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>mA</desc>
+  <a>
+    MULTIPOLYGON (((240 160, 140 220, 80 60, 220 40, 240 160)), 
+  ((160 380, 100 240, 20 380, 160 380), 
+    (120 340, 60 360, 80 320, 120 340)))
+  </a>
+<test>
+  <op name="isSimple" arg1="A">
+    true
+  </op>
+</test>
+</case>
+
+<case>
+  <desc>mA - with touching elements</desc>
+  <a>
+    MULTIPOLYGON (((240 160, 100 240, 80 60, 220 40, 240 160)), 
+  ((160 380, 100 240, 20 380, 160 380), 
+    (120 340, 60 360, 80 320, 120 340)))
+  </a>
+<test>
+  <op name="isSimple" arg1="A">
+    true
+  </op>
+</test>
+</case>
+
+</run>

Added: packages/jts/branches/upstream/current/test/vivid/TestValid.xml
===================================================================
--- packages/jts/branches/upstream/current/test/vivid/TestValid.xml	                        (rev 0)
+++ packages/jts/branches/upstream/current/test/vivid/TestValid.xml	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,645 @@
+<run>
+   <precisionModel scale="1.0" offsetx="0.0" offsety="0.0"/>
+
+   <case>
+      <desc>L - linear-ring bowtie</desc>
+      <a>LINEARRING(0 0, 100 100, 100 0, 0 100, 0 0)</a>
+      <test>
+         <op name="isValid" arg1="A">false</op>
+      </test>
+   </case>
+
+   <case>
+      <desc>L - linestring bowtie</desc>
+      <a>LINESTRING(0 0, 100 100, 100 0, 0 100, 0 0)</a>
+      <test>
+         <op name="isValid" arg1="A">true</op>
+      </test>
+   </case>
+
+   <case>
+      <desc>P - point</desc>
+      <a>
+    POINT(10 10)
+  </a>
+      <test>
+         <op name="isValid" arg1="A">
+    true
+  </op>
+      </test>
+   </case>
+
+   <case>
+      <desc>mP - no repeated points</desc>
+      <a>
+    MULTIPOINT(10 10, 20 20, 30 30)
+  </a>
+      <test>
+         <op name="isValid" arg1="A">    true  </op>
+      </test>
+   </case>
+
+   <case>
+      <desc>P - repeated points</desc>
+      <a>
+    MULTIPOINT(10 10, 20 20, 30 30, 10 10)
+  </a>
+      <test>
+         <op name="isValid" arg1="A">    true  </op>
+      </test>
+   </case>
+
+   <case>
+      <desc>L - no repeated points</desc>
+      <a>
+LINESTRING (40 180, 120 120, 140 200, 200 140, 240 200)
+  </a>
+      <test>
+         <op name="isValid" arg1="A">    true  </op>
+      </test>
+   </case>
+
+   <case>
+      <desc>L - repeated points</desc>
+      <a>
+LINESTRING (40 180, 120 120, 140 200, 140 200, 200 140, 240 200)
+  </a>
+      <test>
+         <op name="isValid" arg1="A">    true  </op>
+      </test>
+   </case>
+
+   <case>
+      <desc>L - linestring with two identical points </desc>
+      <a>LINESTRING(0 0, 0 0)</a>
+      <test>
+         <op name="isValid" arg1="A">      false      </op>
+      </test>
+   </case>
+
+   <case>
+      <desc>A - zero-area polygon </desc>
+      <a>POLYGON ((0 0, 0 0, 0 0, 0 0, 0 0))</a>
+      <test>
+         <op name="isValid" arg1="A"> false </op>
+      </test>
+   </case>
+
+   <case>
+      <desc>A - polygon with too few points </desc>
+      <a>POLYGON ((0 0, 10 0, 20 0, 0 0, 0 0))</a>
+      <test>
+         <op name="isValid" arg1="A"> false </op>
+      </test>
+   </case>
+
+   <case>
+      <desc>A - polygon with repeated point </desc>
+      <a>POLYGON ((107 246, 107 246, 250 285, 294 137, 151 90, 15 125, 157 174, 107 246))</a>
+      <test>
+         <op name="isValid" arg1="A"> true </op>
+      </test>
+   </case>
+
+   <case>
+      <desc>A - polygon with degenerate hole ring (A-B-A) </desc>
+      <a>POLYGON ((0 0, 0 240, 260 240, 260 0, 0 0), 
+  (220 200, 40 200, 40 20, 40 200, 220 200, 220 200))</a>
+      <test>
+         <op name="isValid" arg1="A"> false </op>
+      </test>
+   </case>
+
+   <case>
+      <desc>mA - multipolygon with component with too few points </desc>
+      <a>MULTIPOLYGON ( ((100 20, 180 20, 180 100, 100 100, 100 20)),
+((20 100, 100 100, 100 180, 20 180, 20 100)),
+((100 180, 180 180, 180 260, 100 260, 100 180)),
+((180 100, 180 180, 180 180, 180 100)))</a>
+      <test>
+         <op name="isValid" arg1="A"> false </op>
+      </test>
+   </case>
+
+   <case>
+      <desc>A - polygon self-intersects at non-vertex</desc>
+      <a>POLYGON ((0 40, 0 0, 40 40, 40 0, 0 40))</a>
+      <test>
+         <op name="isValid" arg1="A"> false </op>
+      </test>
+   </case>
+   <case>
+      <desc>A - polygon self-intersects at vertex</desc>
+      <a>MULTIPOLYGON ( ((0 40, 20 20, 40 0, 40 40, 20 20, 0 0, 0 40)) ) </a>
+      <test>
+         <op name="isValid" arg1="A"> false </op>
+      </test>
+   </case>
+   <case>
+      <desc>A - polygon self-intersects at vertex/non-vertex</desc>
+      <a>POLYGON ((0 40, 20 20, 40 0, 40 40, 0 0, 0 40))</a>
+      <test>
+         <op name="isValid" arg1="A">false</op>
+      </test>
+   </case>
+   <case>
+      <desc>A - hole self-intersects at non-vertex</desc>
+      <a>POLYGON ((-10 50, 50 50, 50 -10, -10 -10, -10 50), (0 40, 0 0, 40 40, 40 0, 0 40))</a>
+      <test>
+         <op name="isValid" arg1="A">false</op>
+      </test>
+   </case>
+   <case>
+      <desc>A - polygon self-intersects at vertex</desc>
+      <a>POLYGON ((-10 50, 50 50, 50 -10, -10 -10, -10 50), (0 40, 20 20, 40 0, 40 40, 20 20, 0 0, 0 40))</a>
+      <test>
+         <op name="isValid" arg1="A">false</op>
+      </test>
+   </case>
+   <case>
+      <desc>A - polygon self-intersects at vertex/non-vertex</desc>
+      <a>POLYGON ((-10 50, 50 50, 50 -10, -10 -10, -10 50), (0 40, 20 20, 40 0, 40 40, 0 0, 0 40))</a>
+      <test>
+         <op name="isValid" arg1="A">false</op>
+      </test>
+   </case>
+   <case>
+      <desc>A - Valid doughnut</desc>
+      <a>POLYGON ((0 60, 0 0, 60 0, 60 60, 0 60), (20 40, 20 20, 40 20, 40 40, 20 40))</a>
+      <test>
+         <op name="isValid" arg1="A">true</op>
+      </test>
+   </case>
+   <case>
+      <desc>A - shell has repeated points</desc>
+      <a>POLYGON ((0 60, 0 0, 0 0, 60 0, 60 60, 0 60), (20 40, 20 20, 40 20, 40 40, 20 40))</a>
+      <test>
+         <op name="isValid" arg1="A">true</op>
+      </test>
+   </case>
+
+   <case>
+      <desc>A - shell touches hole without crossing it (valid)</desc>
+      <a>POLYGON ((0 60, 0 0, 60 0, 60 60, 0 60), (20 40, 20 20, 60 20, 20 40))</a>
+      <test>
+         <op name="isValid" arg1="A">true</op>
+      </test>
+   </case>
+   <case>
+      <desc>A - shell touches hole without crossing it, but does so twice (invalid)</desc>
+      <a>POLYGON ((0 60, 0 0, 60 0, 60 60, 0 60), (0 40, 20 20, 60 20, 0 40))</a>
+      <test>
+         <op name="isValid" arg1="A">false</op>
+      </test>
+   </case>
+   <case>
+      <desc>A - hole touches hole without crossing it (valid)</desc>
+      <a>POLYGON ((0 120, 0 0, 140 0, 140 120, 0 120), (100 100, 100 20, 120 20, 120 100, 100 100), (20 100, 20 40, 100 40, 20 100))</a>
+      <test>
+         <op name="isValid" arg1="A">true</op>
+      </test>
+   </case>
+   <case>
+      <desc>A - holel touches hole without crossing it, but does so twice (invalid)</desc>
+      <a>POLYGON ((0 120, 0 0, 140 0, 140 120, 0 120), 
+		(100 100, 100 20, 120 20, 120 100, 100 100), 
+		(20 100, 20 40, 100 40, 80 60, 100 80, 20 100))</a>
+      <test>
+         <op name="isValid" arg1="A">false</op>
+      </test>
+   </case>
+   <case>
+      <desc>A - hole touches hole without crossing it, but does so at an infinite number of points (invalid)</desc>
+      <a>POLYGON ((0 120, 0 0, 140 0, 140 120, 0 120), 
+		(100 100, 100 20, 120 20, 120 100, 100 100), 
+		(20 100, 20 40, 100 40, 100 80, 20 100))</a>
+      <test>
+         <op name="isValid" arg1="A">false</op>
+      </test>
+   </case>
+   <case>
+      <desc>A - spike (invalid)</desc>
+      <a>POLYGON ((0 60, 0 0, 60 0, 60 20, 100 20, 60 20, 60 60, 0 60))</a>
+      <test>
+         <op name="isValid" arg1="A">false</op>
+      </test>
+   </case>
+   <case>
+      <desc>A - puncture (invalid)</desc>
+      <a>POLYGON ((0 60, 0 0, 60 0, 60 20, 20 20, 60 20, 60 60, 0 60))</a>
+      <test>
+         <op name="isValid" arg1="A">false</op>
+      </test>
+   </case>
+   <case>
+      <desc>A - hole within a hole (invalid)</desc>
+      <a>POLYGON ((0 140, 0 0, 180 0, 180 140, 0 140), (20 20, 160 20, 160 120, 20 120, 20 20), (40 100, 40 40, 140 40, 140 100, 40 100))</a>
+      <test>
+         <op name="isValid" arg1="A">false</op>
+      </test>
+   </case>
+   <case>
+      <desc>A - empty shell and holes (valid)</desc>
+      <a>POLYGON (EMPTY, EMPTY, EMPTY)</a>
+      <test>
+         <op name="isValid" arg1="A">true</op>
+      </test>
+   </case>
+
+   <case>
+      <desc>A - hole overlapping shell at non-vertex</desc>
+      <a>
+POLYGON ((60 280, 260 180, 60 80, 60 280), 
+  (140 80, 120 180, 200 180, 140 80))
+  </a>
+      <test>
+         <op name="isValid" arg1="A">
+    false
+  </op>
+      </test>
+   </case>
+
+   <case>
+      <desc>A - shell self-overlaps </desc>
+      <a>
+POLYGON ((60 340, 60 100, 340 100, 340 280, 340 200, 340 340, 60 340))
+  </a>
+      <test>
+         <op name="isValid" arg1="A">    false  </op>
+      </test>
+   </case>
+
+   <case>
+      <desc>A - hole with repeated points</desc>
+      <a>
+POLYGON ((40 260, 40 60, 120 60, 180 160, 240 60, 300 60, 300 260, 40 260), 
+  (70 230, 80 230, 80 220, 80 220, 70 230))  </a>
+      <test>
+         <op name="isValid" arg1="A">
+    true
+  </op>
+      </test>
+   </case>
+   <case>
+      <desc>A - hole outside but adjacent to shell</desc>
+      <a>
+POLYGON ((40 260, 40 60, 120 60, 180 160, 240 60, 300 60, 300 260, 40 260), 
+  (180 160, 240 60, 120 60, 180 160))  </a>
+      <test>
+         <op name="isValid" arg1="A">
+    false
+  </op>
+      </test>
+   </case>
+
+   <case>
+      <desc>A - hole touches shell at two points</desc>
+      <a>
+POLYGON ((240 260, 40 260, 40 80, 240 80, 240 260), 
+  (140 180, 40 180, 140 260, 140 180))
+  </a>
+      <test>
+         <op name="isValid" arg1="A">
+    false
+  </op>
+      </test>
+   </case>
+
+   <case>
+      <desc>A - hole touches shell at one non-vertex point</desc>
+      <a>
+POLYGON ((240 260, 40 260, 40 80, 240 80, 240 260), 
+  (140 180, 40 180, 140 240, 140 180))
+  </a>
+      <test>
+         <op name="isValid" arg1="A">    true  </op>
+      </test>
+   </case>
+
+   <case>
+      <desc>A - hole touches shell at one vertex point</desc>
+      <a>
+POLYGON ((240 260, 40 260, 40 80, 240 80, 240 260), 
+  (140 180, 40 260, 140 240, 140 180))
+  </a>
+      <test>
+         <op name="isValid" arg1="A">    true  </op>
+      </test>
+   </case>
+
+   <case>
+      <desc>A - hole outside shell</desc>
+      <a>
+POLYGON ((20 180, 20 20, 140 20, 140 180, 20 180), 
+  (160 120, 180 100, 160 80, 160 120))  
+</a>
+      <test>
+         <op name="isValid" arg1="A">
+    false
+  </op>
+      </test>
+   </case>
+
+   <case>
+      <desc>A - hole identical to shell</desc>
+      <a>
+POLYGON ((20 180, 20 20, 140 20, 140 180, 20 180), 
+  (20 180, 20 20, 140 20, 140 180, 20 180))  
+</a>
+      <test>
+         <op name="isValid" arg1="A">
+    false
+  </op>
+      </test>
+   </case>
+
+   <case>
+      <desc>A - hole identical to shell</desc>
+      <a>
+POLYGON ((20 180, 20 20, 140 20, 140 180, 20 180), 
+  (20 180, 20 20, 140 20, 140 180, 20 180))  
+</a>
+      <test>
+         <op name="isValid" arg1="A">
+    false
+  </op>
+      </test>
+   </case>
+
+   <case>
+      <desc>A - hole self-intersects </desc>
+      <a>
+POLYGON ((380 340, 40 340, 40 20, 380 20, 380 340), 
+  (120 300, 300 280, 320 200, 160 140, 200 80, 320 120, 320 200, 360 60, 120 40, 120 300))
+	</a>
+      <test>
+         <op name="isValid" arg1="A">      false      </op>
+      </test>
+   </case>
+
+   <case>
+      <desc>A - holes overlap, first point is identical </desc>
+      <a>
+POLYGON ((20 320, 260 320, 260 20, 20 20, 20 320), 
+  (140 280, 80 100, 200 100, 140 280), 
+  (140 280, 40 80, 240 80, 140 280))
+	</a>
+      <test>
+         <op name="isValid" arg1="A">      false      </op>
+      </test>
+   </case>
+
+   <case>
+      <desc>A - holes do not overlap, first point is identical </desc>
+      <a>
+POLYGON ((20 320, 240 320, 240 40, 20 40, 20 320), 
+  (140 180, 60 120, 60 240, 140 180), 
+  (140 180, 200 120, 200 240, 140 180))
+	</a>
+      <test>
+         <op name="isValid" arg1="A">      true      </op>
+      </test>
+   </case>
+
+   <case>
+      <desc>A - shell self-touches at vertex </desc>
+      <a>
+POLYGON ((340 320, 340 200, 200 280, 200 80, 340 200, 340 20, 60 20, 60 340, 340 320))
+	</a>
+      <test>
+         <op name="isValid" arg1="A">      false      </op>
+      </test>
+   </case>
+
+   <case>
+      <desc>A - shell self-touches at non-vertex </desc>
+      <a>
+POLYGON ((300 320, 300 220, 260 260, 180 220, 360 220, 360 140, 120 140, 120 320, 300 320))	</a>
+      <test>
+         <op name="isValid" arg1="A">      false      </op>
+      </test>
+   </case>
+
+    <case>
+      <desc>A - chain of holes surrounds an island inside the polygon </desc>
+      <a>
+POLYGON ((40 300, 40 20, 280 20, 280 300, 40 300), 
+  (120 240, 80 180, 160 220, 120 240), 
+  (220 240, 160 220, 220 160, 220 240), 
+  (160 100, 80 180, 100 80, 160 100), 
+  (160 100, 220 160, 240 100, 160 100))	</a>
+      <test>
+         <op name="isValid" arg1="A">      false      </op>
+      </test>
+   </case>
+
+   <case>
+      <desc>A - chain of holes splits polygon in two (touching at vertices) </desc>
+      <a>
+POLYGON ((40 320, 340 320, 340 20, 40 20, 40 320), 
+  (100 120, 40 20, 180 100, 100 120), 
+  (200 200, 180 100, 240 160, 200 200), 
+  (260 260, 240 160, 300 200, 260 260), 
+  (300 300, 300 200, 340 320, 300 300))	
+	</a>
+      <test>
+         <op name="isValid" arg1="A">      false      </op>
+      </test>
+   </case>
+
+   <case>
+      <desc>A - chain of holes splits polygon in two (touching at non-vertex) </desc>
+      <a>
+POLYGON ((40 320, 340 320, 340 20, 40 20, 40 320), 
+  (100 120, 40 20, 180 100, 100 120), 
+  (200 200, 180 100, 240 160, 200 200), 
+  (260 260, 240 160, 300 200, 260 260), 
+  (300 300, 300 200, 340 260, 300 300))
+	</a>
+      <test>
+         <op name="isValid" arg1="A">      false      </op>
+      </test>
+   </case>
+
+   <case>
+      <desc>A - holes touch in one point </desc>
+      <a>
+POLYGON ((190 190, 360 20, 20 20, 190 190), 
+  (90 50, 150 110, 190 50, 90 50), 
+  (190 50, 230 110, 290 50, 190 50))
+	</a>
+      <test>
+         <op name="isValid" arg1="A">      true      </op>
+      </test>
+   </case>
+
+   <case>
+      <desc>A - duplicate holes </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))
+	</a>
+      <test>
+         <op name="isValid" arg1="A">      false      </op>
+      </test>
+   </case>
+
+  <case>
+      <desc>mA - adjacent shells (shared vertices) </desc>
+      <a>
+MULTIPOLYGON (((40 120, 140 120, 140 40, 40 40, 40 120)), 
+  ((140 120, 40 120, 40 200, 140 200, 140 120)))
+	</a>
+      <test>
+         <op name="isValid" arg1="A">
+    false
+  </op>
+      </test>
+   </case>
+
+   <case>
+      <desc>mA - adjacent shells (different vertices) </desc>
+      <a>
+MULTIPOLYGON (((40 120, 140 120, 140 40, 40 40, 40 120)), 
+  ((160 120, 60 120, 40 200, 140 200, 160 120)))
+	</a>
+      <test>
+         <op name="isValid" arg1="A">
+    false
+  </op>
+      </test>
+   </case>
+
+   <case>
+      <desc>mA - nested overlapping shells </desc>
+      <a>
+MULTIPOLYGON (((80 260, 240 260, 240 100, 80 100, 80 260)), 
+  ((120 240, 220 240, 220 140, 120 140, 120 240)))
+	</a>
+      <test>
+         <op name="isValid" arg1="A">      false      </op>
+      </test>
+   </case>
+
+   <case>
+      <desc>mA - nested non-overlapping shells </desc>
+      <a>
+MULTIPOLYGON (((60 320, 60 80, 300 80, 60 320), 
+  (80 280, 80 100, 260 100, 80 280)), 
+  ((120 160, 140 160, 140 140, 120 160)))
+	</a>
+      <test>
+         <op name="isValid" arg1="A">      true      </op>
+      </test>
+   </case>
+
+   <case>
+      <desc>mA - nested non-overlapping shells, all vertices touch </desc>
+      <a>
+MULTIPOLYGON (((20 380, 420 380, 420 20, 20 20, 20 380), 
+  (220 340, 180 240, 60 200, 180 160, 340 60, 240 220, 220 340)), 
+  ((180 240, 180 160, 240 220, 180 240)))
+	</a>
+      <test>
+         <op name="isValid" arg1="A">      true      </op>
+      </test>
+   </case>
+
+   <case>
+      <desc>mA - nested overlapping shells, all vertices touch </desc>
+      <a>
+MULTIPOLYGON (((20 380, 420 380, 420 20, 20 20, 20 380), 
+  (220 340, 180 240, 60 200, 140 100, 340 60, 300 240, 220 340)), 
+  ((60 200, 340 60, 220 340, 60 200)))
+	</a>
+      <test>
+         <op name="isValid" arg1="A">      false      </op>
+      </test>
+   </case>
+
+   <case>
+      <desc>mA - nested non-overlapping shells, all vertices touch </desc>
+      <a>
+MULTIPOLYGON (((20 380, 420 380, 420 20, 20 20, 20 380), 
+  (220 340, 80 320, 60 200, 140 100, 340 60, 300 240, 220 340)), 
+  ((60 200, 340 60, 220 340, 60 200)))
+	</a>
+      <test>
+         <op name="isValid" arg1="A">      true      </op>
+      </test>
+   </case>
+
+   <case>
+      <desc>mA - nested overlapping shells, all vertices touch </desc>
+      <a>
+MULTIPOLYGON (((20 380, 420 380, 420 20, 20 20, 20 380), 
+  (220 340, 180 240, 60 200, 200 180, 340 60, 240 220, 220 340)), 
+  ((60 200, 340 60, 220 340, 60 200)))
+	</a>
+      <test>
+         <op name="isValid" arg1="A">      false      </op>
+      </test>
+   </case>
+
+   <case>
+      <desc>mA - disconnected exterior </desc>
+      <a>
+MULTIPOLYGON (((100 20, 180 20, 180 100, 100 100, 100 20)), 
+  ((20 100, 100 100, 100 180, 20 180, 20 100)), 
+  ((100 180, 180 180, 180 260, 100 260, 100 180)), 
+  ((180 100, 260 100, 260 180, 180 180, 180 100)))
+	</a>
+      <test>
+         <op name="isValid" arg1="A">      true      </op>
+      </test>
+   </case>
+
+   <case>
+      <desc>mA - shells touch in single point </desc>
+      <a>
+MULTIPOLYGON (((110 110, 70 200, 150 200, 110 110)), 
+  ((110 110, 150 20, 70 20, 110 110)))
+	</a>
+      <test>
+         <op name="isValid" arg1="A">      true      </op>
+      </test>
+   </case>
+
+   <case>
+      <desc>mA - duplicate shells </desc>
+      <a>
+MULTIPOLYGON (((60 300, 320 220, 260 60, 60 100, 60 300)), 
+  ((60 300, 320 220, 260 60, 60 100, 60 300)))
+	</a>
+      <test>
+         <op name="isValid" arg1="A">      false      </op>
+      </test>
+   </case>
+
+   <case>
+      <desc>mA - shells are not nested but share all vertices </desc>
+      <a>
+MULTIPOLYGON (((180 60, 240 160, 300 60, 180 60)), 
+  ((80 80, 180 60, 160 140, 240 160, 360 140, 300 60, 420 100, 320 280, 120 260, 80 80)))
+	</a>
+      <test>
+         <op name="isValid" arg1="A">      true      </op>
+      </test>
+   </case>
+
+   <case>
+      <desc>mA - shell is nested inside first hole </desc>
+      <a>
+MULTIPOLYGON (((0 0, 0 8, 8 8, 8 0, 0 0), 
+  (3 3, 7 3, 7 7, 3 7, 3 3), 
+  (1 1, 2 1, 2 2, 1 2, 1 1)), 
+  ((4 4, 4 6, 6 6, 6 4, 4 4)))
+	</a>
+      <test>
+         <op name="isValid" arg1="A">      true      </op>
+      </test>
+   </case>
+
+
+</run>

Added: packages/jts/branches/upstream/current/test/vivid/TestValid2-big.xml
===================================================================
--- packages/jts/branches/upstream/current/test/vivid/TestValid2-big.xml	                        (rev 0)
+++ packages/jts/branches/upstream/current/test/vivid/TestValid2-big.xml	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,18 @@
+<run>
+   <precisionModel type="FLOATING"/>
+<case>
+  <desc>Test 92</desc>
+  <a>
+    POLYGON ((100 100, 1000000000000000 110, 1000000000000000 100, 100 100))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 558</desc>
+  <a>
+    MULTIPOINT (-1000000000000000000000000 -1000000000000000000000000, 1000000000000000000000000 -1000000000000000000000000, 1000000000000000000000000 1000000000000000000000000, -1000000000000000000000000 1000000000000000000000000, 0 0)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+
+</run>

Added: packages/jts/branches/upstream/current/test/vivid/TestValid2.xml
===================================================================
--- packages/jts/branches/upstream/current/test/vivid/TestValid2.xml	                        (rev 0)
+++ packages/jts/branches/upstream/current/test/vivid/TestValid2.xml	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,5246 @@
+<run>
+   <precisionModel type="FLOATING"/>
+<case>
+  <desc>Test 1</desc>
+  <a>
+    LINESTRING (-123456789 -40, 381039468754763 123456789)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 2</desc>
+  <a>
+    POINT (0 0)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 3</desc>
+  <a>
+    POLYGON ((20 20, 20 100, 120 100, 140 20, 20 20))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 4</desc>
+  <a>
+    POLYGON ((20 20, 140 20, 120 100, 20 100, 20 20))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 5</desc>
+  <a>
+    POLYGON ((120 100, 140 20, 20 20, 20 100, 120 100))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 6</desc>
+  <a>
+    POLYGON ((20 100, 60 100, 120 100, 140 20, 80 20, 20 20, 20 100))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 7</desc>
+  <a>
+    POLYGON ((0 0, 80 0, 80 80, 0 80, 0 0))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 8</desc>
+  <a>
+    POLYGON ((100 200, 100 140, 180 140, 180 200, 100 200))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 9</desc>
+  <a>
+    POLYGON ((140 120, 160 20, 20 20, 20 120, 140 120))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 10</desc>
+  <a>
+    POLYGON ((140 120, 140 200, 240 200, 240 120, 140 120))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 11</desc>
+  <a>
+    POLYGON ((80 180, 140 260, 260 200, 200 60, 80 180))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 12</desc>
+  <a>
+    POLYGON ((240 80, 140 120, 180 240, 280 200, 240 80))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 13</desc>
+  <a>
+    POLYGON ((140 160, 20 20, 270 20, 150 160, 230 40, 60 40, 140 160))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 14</desc>
+  <a>
+    POLYGON ((140 40, 180 80, 120 100, 140 40))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 15</desc>
+  <a>
+    POLYGON ((120 100, 180 80, 130 40, 120 100))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 16</desc>
+  <a>
+    POLYGON ((20 20, 180 20, 140 140, 20 140, 20 20))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 17</desc>
+  <a>
+    POLYGON ((180 100, 80 200, 180 280, 260 200, 180 100))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 18</desc>
+  <a>
+    POLYGON ((140 140, 20 120, 0 220, 120 240, 140 140))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 19</desc>
+  <a>
+    POLYGON ((160 200, 210 70, 120 70, 160 200))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 20</desc>
+  <a>
+    POLYGON ((160 200, 260 40, 70 40, 160 200, 20 20, 310 20, 160 200))
+  </a>
+  <test> <op name="isValid" arg1="A"> false </op> </test>
+</case>
+<case>
+  <desc>Test 21</desc>
+  <a>
+    POLYGON ((110 140, 200 70, 200 160, 110 140))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 22</desc>
+  <a>
+    POLYGON ((110 140, 110 50, 60 50, 60 90, 160 190, 20 110, 20 20, 200 20, 110 140))
+  </a>
+  <test> <op name="isValid" arg1="A"> false </op> </test>
+</case>
+<case>
+  <desc>Test 23</desc>
+  <a>
+    POLYGON ((20 120, 20 20, 260 20, 260 120, 200 40, 140 120, 80 40, 20 120))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 24</desc>
+  <a>
+    POLYGON ((20 120, 20 240, 260 240, 260 120, 200 200, 140 120, 80 200, 20 120))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 25</desc>
+  <a>
+    POLYGON ((20 120, 20 20, 260 20, 260 120, 180 40, 140 120, 100 40, 20 120))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 26</desc>
+  <a>
+    POLYGON ((20 120, 300 120, 140 240, 20 120))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 27</desc>
+  <a>
+    POLYGON ((20 20, 20 300, 280 300, 280 260, 220 260, 60 100, 60 60, 280 60, 280 20, 20 20))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 28</desc>
+  <a>
+    POLYGON ((100 140, 160 80, 280 180, 200 240, 220 160, 160 200, 180 120, 100 140))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 29</desc>
+  <a>
+    POLYGON ((260 200, 180 80, 120 160, 200 160, 180 220, 260 200))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 30</desc>
+  <a>
+    POLYGON ((20 20, 280 20, 280 140, 220 60, 140 140, 80 60, 20 140, 20 20))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 31</desc>
+  <a>
+    POLYGON ((0 140, 300 140, 140 240, 0 140))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 32</desc>
+  <a>
+    POLYGON ((20 240, 20 140, 320 140, 180 240, 20 240))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 33</desc>
+  <a>
+    POLYGON ((20 240, 20 140, 80 180, 140 140, 220 180, 280 140, 280 240, 20 240))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 34</desc>
+  <a>
+    POLYGON ((120 120, 180 60, 20 20, 20 120, 120 120))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 35</desc>
+  <a>
+    POLYGON ((120 120, 220 20, 280 20, 240 160, 120 120))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 36</desc>
+  <a>
+    POLYGON ((140 120, 160 20, 260 120, 220 200, 140 120))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 37</desc>
+  <a>
+    POLYGON ((20 140, 120 40, 20 40, 20 140))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 38</desc>
+  <a>
+    POLYGON ((190 140, 190 20, 140 20, 20 140, 190 140))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 39</desc>
+  <a>
+    POLYGON ((300 20, 220 20, 120 120, 260 160, 300 20))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 40</desc>
+  <a>
+    POLYGON ((140 120, 240 160, 280 60, 160 20, 140 120))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 41</desc>
+  <a>
+    POLYGON ((280 60, 180 60, 120 120, 260 180, 280 60))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 42</desc>
+  <a>
+    POLYGON ((120 200, 120 120, 40 120, 40 200, 120 200))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 43</desc>
+  <a>
+    POLYGON ((160 220, 140 120, 60 120, 40 220, 160 220))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 44</desc>
+  <a>
+    POLYGON ((140 120, 20 120, 20 220, 140 220, 140 120))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 45</desc>
+  <a>
+    POLYGON ((320 20, 220 20, 80 160, 240 140, 320 20))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 46</desc>
+  <a>
+    POLYGON ((20 20, 20 180, 220 180, 220 20, 20 20))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 47</desc>
+  <a>
+    POLYGON ((60 40, 60 140, 180 140, 180 40, 60 40))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 48</desc>
+  <a>
+    POLYGON ((20 20, 80 140, 160 60, 20 20))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 49</desc>
+  <a>
+    POLYGON ((160 60, 20 20, 100 140, 160 60))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 50</desc>
+  <a>
+    POLYGON ((20 100, 140 160, 160 40, 20 100))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 51</desc>
+  <a>
+    POLYGON ((160 40, 20 100, 160 160, 160 40))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 52</desc>
+  <a>
+    POLYGON ((20 180, 180 120, 80 40, 20 180))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 53</desc>
+  <a>
+    POLYGON ((180 120, 100 40, 20 180, 180 120))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 54</desc>
+  <a>
+    POLYGON ((20 20, 140 40, 140 120, 20 160, 80 80, 20 20))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 55</desc>
+  <a>
+    POLYGON ((20 20, 140 40, 140 140, 20 180, 80 100, 20 20))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 56</desc>
+  <a>
+    POLYGON ((40 180, 60 100, 180 100, 200 180, 120 120, 40 180))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 57</desc>
+  <a>
+    POLYGON ((20 180, 60 80, 180 80, 220 180, 120 120, 20 180))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 58</desc>
+  <a>
+    POLYGON ((40 60, 20 180, 100 100, 140 180, 160 120, 220 100, 140 40, 40 60))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 59</desc>
+  <a>
+    POLYGON ((60 100, 180 100, 220 180, 120 140, 20 180, 60 100))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 60</desc>
+  <a>
+    POLYGON ((20 20, 20 140, 120 120, 120 40, 20 20))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 61</desc>
+  <a>
+    POLYGON ((20 20, 20 180, 140 140, 140 60, 20 20))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 62</desc>
+  <a>
+    POLYGON ((20 20, 120 40, 120 120, 20 140, 20 20))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 63</desc>
+  <a>
+    POLYGON ((120 40, 20 20, 20 140, 120 120, 120 40))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 64</desc>
+  <a>
+    POLYGON ((20 20, 140 60, 140 140, 20 180, 20 20))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 65</desc>
+  <a>
+    POLYGON ((140 60, 20 20, 20 180, 140 140, 140 60))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 66</desc>
+  <a>
+    POLYGON ((20 20, 60 120, 140 120, 180 20, 20 20))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 67</desc>
+  <a>
+    POLYGON ((20 40, 120 40, 120 120, 20 140, 20 40))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 68</desc>
+  <a>
+    POLYGON ((20 20, 20 180, 60 120, 100 180, 140 120, 220 180, 200 120, 140 60, 20 20))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 69</desc>
+  <a>
+    POLYGON ((150 150, 330 150, 250 70, 70 70, 150 150))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 70</desc>
+  <a>
+    POLYGON ((150 150, 270 150, 140 20, 20 20, 150 150))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 71</desc>
+  <a>
+    POLYGON ((150 150, 270 150, 330 150, 250 70, 190 70, 70 70, 150 150))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 72</desc>
+  <a>
+    POLYGON ((150 150, 270 150, 190 70, 140 20, 20 20, 70 70, 150 150))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 73</desc>
+  <a>
+    POLYGON ((20 20, 60 50, 20 40, 60 70, 20 60, 60 90, 20 90, 70 110, 20 130, 80 130, 20 150, 80 160, 20 170, 80 180, 20 200, 80 200, 30 240, 80 220, 50 260, 100 220, 100 260, 120 220, 130 260, 140 220, 150 280, 150 190, 160 280, 170 190, 180 280, 190 190, 200 280, 210 190, 220 280, 230 190, 240 260, 250 230, 260 260, 260 220, 290 270, 290 220, 330 260, 300 210, 340 240, 290 180, 340 210, 290 170, 350 170, 240 150, 350 150, 240 140, 350 130, 240 120, 350 120, 240 110, 350 110, 240 100, 350 100, 240 90, 350 90, 240 80, 350 80, 300 70, 340 60, 290 60, 340 40, 300 50, 340 20, 270 60, 310 20, 250 60, 270 20, 230 60, 240 20, 210 60, 210 20, 190 70, 190 20, 180 90, 170 20, 160 90, 150 20, 140 90, 130 20, 120 90, 110 20, 100 90, 100 20, 90 60, 80 20, 70 40, 20 20))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 74</desc>
+  <a>
+    POLYGON ((190 140, 140 130, 200 160, 130 150, 210 170, 130 170, 210 180, 120 190, 220 200, 120 200, 250 210, 120 210, 250 220, 120 220, 250 230, 120 240, 230 240, 120 250, 240 260, 120 260, 240 270, 120 270, 270 290, 120 290, 230 300, 150 310, 250 310, 180 320, 250 320, 200 360, 260 330, 240 360, 280 320, 290 370, 290 320, 320 360, 310 320, 360 360, 310 310, 380 340, 310 290, 390 330, 310 280, 410 310, 310 270, 420 280, 310 260, 430 250, 300 250, 440 240, 300 240, 450 230, 280 220, 440 220, 280 210, 440 210, 300 200, 430 190, 300 190, 440 180, 330 180, 430 150, 320 180, 420 130, 300 180, 410 120, 280 180, 400 110, 280 170, 390 90, 280 160, 400 70, 270 160, 450 30, 260 160, 420 30, 250 160, 390 30, 240 160, 370 30, 230 160, 360 30, 230 150, 330 50, 240 130, 330 30, 230 130, 310 30, 220 130, 280 30, 230 100, 270 40, 220 110, 250 30, 210 130, 240 30, 210 100, 220 40, 200 90, 200 20, 190 100, 180 30, 20 20, 180 40, 20 30, 180 50, 20 50, 180 60, 30 60, 180 70, 20 70, 170 80, 80 80, 170 90, 20 80, 180 100, 40 100, 200 110, 60 110, 200 120, 120 120, 190 140))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 75</desc>
+  <a>
+    POLYGON ((70 150, 20 160, 110 160, 20 180, 100 200, 20 200, 190 210, 20 210, 160 220, 20 220, 150 230, 60 240, 180 250, 20 260, 170 260, 60 270, 160 270, 100 310, 170 280, 200 260, 180 230, 210 260, 130 330, 230 250, 210 290, 240 250, 230 210, 260 300, 250 230, 270 300, 270 240, 300 340, 280 250, 320 330, 290 250, 340 350, 290 240, 350 360, 270 190, 350 340, 290 200, 350 330, 300 190, 360 320, 310 190, 360 300, 320 200, 360 280, 330 200, 360 260, 340 200, 370 260, 340 180, 390 290, 340 170, 400 260, 350 170, 400 250, 350 160, 410 240, 350 150, 400 170, 350 140, 310 170, 340 140, 270 180, 330 140, 260 170, 310 140, 240 170, 290 140, 200 190, 270 140, 180 190, 260 140, 170 190, 260 130, 170 180, 250 130, 170 170, 240 120, 170 160, 210 120, 170 150, 210 110, 340 130, 230 110, 420 140, 220 100, 410 130, 220 90, 400 120, 220 80, 390 110, 220 70, 420 110, 240 70, 420 100, 260 70, 420 90, 280 70, 430 80, 230 60, 430 60, 270 50, 450 40, 210 50, 370 40, 260 40, 460 30, 160 40, 210 60, 200 110, 190 60, 190 120, 170 50, 180 130, 150 30, 170 130, 140 20, 160 120, 130 20, 160 150, 120 20, 160 170, 110 20, 160 190, 100 20, 150 190, 90 20, 140 180, 80 20, 120 140, 70 20, 120 150, 60 20, 110 150, 50 20, 100 140, 50 30, 90 130, 40 30, 80 120, 30 30, 80 130, 30 40, 80 140, 20 40, 70 140, 40 90, 60 130, 20 90, 60 140, 20 130, 70 150))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 76</desc>
+  <a>
+    POLYGON ((60 160, 220 160, 220 20, 60 20, 60 160))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 77</desc>
+  <a>
+    POLYGON ((60 160, 20 200, 260 200, 220 160, 140 80, 60 160))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 78</desc>
+  <a>
+    POLYGON ((60 160, 20 200, 260 200, 140 80, 60 160))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 79</desc>
+  <a>
+    POLYGON ((20 200, 140 80, 260 200, 20 200))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 80</desc>
+  <a>
+    POLYGON ((20 200, 60 160, 140 80, 220 160, 260 200, 20 200))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 81</desc>
+  <a>
+    POLYGON ((20 200, 60 160, 140 80, 260 200, 20 200))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 82</desc>
+  <a>
+    POLYGON ((0 0, 0 200, 200 200, 200 0, 0 0))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 83</desc>
+  <a>
+    POLYGON ((100 100, 1000000 110, 10000000 100, 100 100))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 84</desc>
+  <a>
+    POLYGON ((100 0, 100 200, 200 200, 200 0, 100 0))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 85</desc>
+  <a>
+    POLYGON ((120 0, 120 200, 200 200, 200 0, 120 0))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 86</desc>
+  <a>
+    POLYGON ((0 0, 0 200, 110 200, 110 0, 0 0))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 87</desc>
+  <a>
+    POLYGON ((100 100, 100 200, 200 200, 200 100, 100 100))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 88</desc>
+  <a>
+    POLYGON ((100 100, 2100 110, 2100 100, 100 100))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 89</desc>
+  <a>
+    POLYGON ((100 100, 2101 110, 2101 100, 100 100))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 90</desc>
+  <a>
+    POLYGON ((100 100, 200 200, 200 100, 100 100))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 91</desc>
+  <a>
+    POLYGON ((100 100, 1000000 110, 1000000 100, 100 100))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 93</desc>
+  <a>
+    POLYGON ((120 100, 120 200, 200 200, 200 100, 120 100))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 94</desc>
+  <a>
+    POLYGON ((100 100, 500 110, 500 100, 100 100))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 95</desc>
+  <a>
+    POLYGON ((100 100, 501 110, 501 100, 100 100))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 96</desc>
+  <a>
+    POLYGON ((120 100, 130 200, 200 200, 200 100, 120 100))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 97</desc>
+  <a>
+    POLYGON ((120 100, 17 200, 200 200, 200 100, 120 100))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 98</desc>
+  <a>
+    POLYGON ((101 99, 101 1000000, 102 1000000, 101 99))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 99</desc>
+  <a>
+    POLYGON ((100 100, 200 101, 200 100, 100 100))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 100</desc>
+  <a>
+    POLYGON ((16 319, 150 39, 25 302, 160 20, 265 20, 127 317, 16 319))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 101</desc>
+  <a>
+    POLYGON ((10 307, 22 307, 153 34, 22 34, 10 307))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 102</desc>
+  <a>
+    POLYGON ((160 200, 310 20, 20 20, 160 200), (160 200, 260 40, 70 40, 160 200))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 103</desc>
+  <a>
+    POLYGON ((170 120, 240 100, 260 50, 190 70, 170 120))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 104</desc>
+  <a>
+    POLYGON ((150 150, 410 150, 280 20, 20 20, 150 150), (170 120, 330 120, 260 50, 100 50, 170 120))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 105</desc>
+  <a>
+    POLYGON ((270 90, 200 50, 150 80, 210 120, 270 90))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 106</desc>
+  <a>
+    POLYGON ((170 120, 260 100, 240 60, 150 80, 170 120))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 107</desc>
+  <a>
+    POLYGON ((220 120, 270 80, 200 60, 160 100, 220 120))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 108</desc>
+  <a>
+    POLYGON ((260 50, 180 70, 180 110, 260 90, 260 50))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 109</desc>
+  <a>
+    POLYGON ((230 110, 290 80, 190 60, 140 90, 230 110))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 110</desc>
+  <a>
+    POLYGON ((170 120, 330 120, 260 50, 100 50, 170 120))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 111</desc>
+  <a>
+    POLYGON ((170 120, 330 120, 280 70, 120 70, 170 120))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 112</desc>
+  <a>
+    POLYGON ((170 120, 300 120, 250 70, 120 70, 170 120))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 113</desc>
+  <a>
+    POLYGON ((190 100, 310 100, 260 50, 140 50, 190 100))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 114</desc>
+  <a>
+    POLYGON ((280 130, 360 130, 270 40, 190 40, 280 130))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 115</desc>
+  <a>
+    POLYGON ((150 150, 410 150, 280 20, 20 20, 150 150), (170 120, 250 120, 180 50, 100 50, 170 120))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 116</desc>
+  <a>
+    POLYGON ((220 80, 180 40, 80 40, 170 130, 270 130, 230 90, 300 90, 250 30, 280 30, 390 140, 150 140, 40 30, 230 30, 280 80, 220 80))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 117</desc>
+  <a>
+    POLYGON ((260 130, 360 130, 280 40, 170 40, 260 130))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 118</desc>
+  <a>
+    POLYGON ((240 110, 340 110, 290 60, 190 60, 240 110))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 119</desc>
+  <a>
+    POLYGON ((250 120, 350 120, 280 50, 180 50, 250 120))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 120</desc>
+  <a>
+    POLYGON ((230 210, 230 20, 20 20, 20 210, 230 210), (120 180, 50 50, 200 50, 120 180))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 121</desc>
+  <a>
+    POLYGON ((230 210, 230 20, 20 20, 20 210, 230 210), (140 40, 40 40, 40 170, 140 40), (110 190, 210 190, 210 50, 110 190))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 122</desc>
+  <a>
+    POLYGON ((280 190, 330 150, 200 110, 150 150, 280 190))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 123</desc>
+  <a>
+    MULTIPOLYGON (((140 110, 260 110, 170 20, 50 20, 140 110)), ((300 270, 420 270, 340 190, 220 190, 300 270)))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 124</desc>
+  <a>
+    POLYGON ((80 190, 220 190, 140 110, 0 110, 80 190))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 125</desc>
+  <a>
+    POLYGON ((330 150, 200 110, 150 150, 280 190, 330 150))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 126</desc>
+  <a>
+    POLYGON ((290 190, 340 150, 220 120, 170 170, 290 190))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 127</desc>
+  <a>
+    POLYGON ((220 190, 340 190, 260 110, 140 110, 220 190))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 128</desc>
+  <a>
+    POLYGON ((140 190, 220 190, 100 70, 20 70, 140 190))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 129</desc>
+  <a>
+    POLYGON ((140 220, 60 140, 140 60, 220 140, 140 220))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 130</desc>
+  <a>
+    MULTIPOLYGON (((100 20, 180 20, 180 100, 100 100, 100 20)), ((20 100, 100 100, 100 180, 20 180, 20 100)), ((100 180, 180 180, 180 260, 100 260, 100 180)), ((180 100, 260 100, 260 180, 180 180, 180 100)))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 131</desc>
+  <a>
+    MULTIPOLYGON (((110 110, 70 200, 150 200, 110 110)), ((110 110, 150 20, 70 20, 110 110)))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 132</desc>
+  <a>
+    MULTIPOLYGON (((110 110, 160 160, 210 110, 160 60, 110 110)), ((110 110, 60 60, 10 110, 60 160, 110 110)))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 133</desc>
+  <a>
+    MULTIPOLYGON (((110 110, 70 200, 150 200, 110 110), (110 110, 100 180, 120 180, 110 110)), ((110 110, 150 20, 70 20, 110 110), (110 110, 120 40, 100 40, 110 110)))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 134</desc>
+  <a>
+    MULTIPOLYGON (((110 110, 160 160, 210 110, 160 60, 110 110), (110 110, 160 130, 160 90, 110 110)), ((110 110, 60 60, 10 110, 60 160, 110 110), (110 110, 60 90, 60 130, 110 110)))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 135</desc>
+  <a>
+    MULTIPOLYGON (((110 110, 70 200, 200 200, 110 110), (110 110, 100 180, 120 180, 110 110)), ((110 110, 200 20, 70 20, 110 110), (110 110, 120 40, 100 40, 110 110)))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 136</desc>
+  <a>
+    MULTIPOLYGON (((110 110, 20 200, 200 200, 110 110), (110 110, 100 180, 120 180, 110 110)), ((110 110, 200 20, 20 20, 110 110), (110 110, 120 40, 100 40, 110 110)))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 137</desc>
+  <a>
+    MULTIPOLYGON (((110 110, 70 200, 210 110, 70 20, 110 110), (110 110, 110 140, 150 110, 110 80, 110 110)), ((110 110, 60 60, 10 110, 60 160, 110 110), (110 110, 60 90, 60 130, 110 110)))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 138</desc>
+  <a>
+    POLYGON ((100 60, 140 100, 100 140, 60 100, 100 60))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 139</desc>
+  <a>
+    MULTIPOLYGON (((80 40, 120 40, 120 80, 80 80, 80 40)), ((120 80, 160 80, 160 120, 120 120, 120 80)), ((80 120, 120 120, 120 160, 80 160, 80 120)), ((40 80, 80 80, 80 120, 40 120, 40 80)))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 140</desc>
+  <a>
+    LINESTRING (150 150, 40 230)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 141</desc>
+  <a>
+    POLYGON ((150 150, 410 150, 280 20, 20 20, 150 150))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 142</desc>
+  <a>
+    LINESTRING (40 40, 50 130, 130 130)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 143</desc>
+  <a>
+    LINESTRING (40 230, 150 150)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 144</desc>
+  <a>
+    LINESTRING (210 150, 330 150)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 145</desc>
+  <a>
+    LINESTRING (200 150, 310 150, 360 220)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 146</desc>
+  <a>
+    LINESTRING (180 150, 250 150, 230 250, 370 250, 410 150)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 147</desc>
+  <a>
+    LINESTRING (210 210, 220 150, 320 150, 370 210)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 148</desc>
+  <a>
+    LINESTRING (20 60, 150 60)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 149</desc>
+  <a>
+    LINESTRING (60 90, 310 180)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 150</desc>
+  <a>
+    LINESTRING (90 210, 210 90)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 151</desc>
+  <a>
+    LINESTRING (290 10, 130 170)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 152</desc>
+  <a>
+    LINESTRING (30 100, 100 100, 180 100)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 153</desc>
+  <a>
+    LINESTRING (20 100, 100 100, 360 100, 410 100)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 154</desc>
+  <a>
+    LINESTRING (90 210, 150 150, 210 90)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 155</desc>
+  <a>
+    LINESTRING (180 90, 280 120)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 156</desc>
+  <a>
+    LINESTRING (70 70, 80 20)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 157</desc>
+  <a>
+    LINESTRING (130 20, 150 60)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 158</desc>
+  <a>
+    LINESTRING (70 70, 80 20, 140 20, 150 60)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 159</desc>
+  <a>
+    LINESTRING (170 50, 170 20, 240 20, 260 60)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 160</desc>
+  <a>
+    LINESTRING (50 100, 140 190, 280 190)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 161</desc>
+  <a>
+    LINESTRING (140 60, 180 100, 290 100)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 162</desc>
+  <a>
+    LINESTRING (170 120, 210 80, 270 80)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 163</desc>
+  <a>
+    LINESTRING (170 120, 260 50)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 164</desc>
+  <a>
+    LINESTRING (190 90, 190 270)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 165</desc>
+  <a>
+    POLYGON ((190 190, 360 20, 20 20, 190 190), (190 190, 280 50, 100 50, 190 190))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 166</desc>
+  <a>
+    LINESTRING (60 160, 150 70)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 167</desc>
+  <a>
+    POLYGON ((190 190, 360 20, 20 20, 190 190), (110 110, 250 100, 140 30, 110 110))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 168</desc>
+  <a>
+    POLYGON ((190 190, 20 20, 360 20, 190 190), (250 100, 110 110, 140 30, 250 100))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 169</desc>
+  <a>
+    LINESTRING (190 90, 190 190, 190 270)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 170</desc>
+  <a>
+    LINESTRING (60 160, 110 110, 150 70)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 171</desc>
+  <a>
+    POLYGON ((190 190, 110 110, 20 20, 360 20, 190 190), (250 100, 110 110, 140 30, 250 100))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 172</desc>
+  <a>
+    LINESTRING (130 110, 180 110, 190 60)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 173</desc>
+  <a>
+    POLYGON ((20 200, 240 200, 240 20, 20 20, 20 200), (130 110, 60 180, 60 40, 130 110), (130 110, 200 40, 200 180, 130 110))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 174</desc>
+  <a>
+    LINESTRING (80 110, 180 110)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 175</desc>
+  <a>
+    POLYGON ((20 200, 20 20, 240 20, 240 200, 20 200), (60 180, 130 110, 60 40, 60 180), (130 110, 200 40, 200 180, 130 110))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 176</desc>
+  <a>
+    LINESTRING (80 110, 170 110)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 177</desc>
+  <a>
+    POLYGON ((20 200, 20 20, 240 20, 240 200, 20 200), (130 110, 60 40, 60 180, 130 110), (130 180, 130 40, 200 110, 130 180))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 178</desc>
+  <a>
+    LINESTRING (80 110, 130 110, 170 110)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 179</desc>
+  <a>
+    LINESTRING (80 110, 130 110, 180 110)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 180</desc>
+  <a>
+    LINESTRING (160 70, 320 230)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 181</desc>
+  <a>
+    LINESTRING (160 70, 200 110, 280 190, 320 230)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 182</desc>
+  <a>
+    LINESTRING (70 50, 70 150)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 183</desc>
+  <a>
+    MULTIPOLYGON (((0 0, 0 100, 140 100, 140 0, 0 0)), ((20 170, 70 100, 130 170, 20 170)))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 184</desc>
+  <a>
+    LINESTRING (110 110, 20 200, 200 200, 110 110)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 185</desc>
+  <a>
+    POLYGON ((20 20, 200 20, 110 110, 20 20))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 186</desc>
+  <a>
+    LINESTRING (150 70, 160 110, 200 60, 150 70)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 187</desc>
+  <a>
+    LINESTRING (80 60, 120 40, 120 70, 80 60)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 188</desc>
+  <a>
+    POLYGON ((110 110, 200 20, 20 20, 110 110), (110 90, 50 30, 170 30, 110 90))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 189</desc>
+  <a>
+    LINESTRING (20 20, 200 20, 110 110, 20 20)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 190</desc>
+  <a>
+    LINESTRING (110 90, 170 30, 50 30, 110 90)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 191</desc>
+  <a>
+    LINESTRING (110 110, 170 50, 170 110, 110 110)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 192</desc>
+  <a>
+    LINESTRING (110 90, 70 50, 130 50, 110 90)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 193</desc>
+  <a>
+    LINESTRING (110 60, 20 150, 200 150, 110 60)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 194</desc>
+  <a>
+    LINESTRING (110 130, 110 70, 200 100, 110 130)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 195</desc>
+  <a>
+    LINESTRING (110 90, 160 40, 60 40, 110 90)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 196</desc>
+  <a>
+    LINESTRING (110 100, 40 30, 180 30, 110 100)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 197</desc>
+  <a>
+    POLYGON ((110 110, 200 20, 20 20, 110 110), (110 90, 60 40, 160 40, 110 90))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 198</desc>
+  <a>
+    LINESTRING (110 110, 180 30, 40 30, 110 110)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 199</desc>
+  <a>
+    LINESTRING (110 90, 180 30, 40 30, 110 90)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 200</desc>
+  <a>
+    LINESTRING (110 90, 50 30, 180 30, 110 90)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 201</desc>
+  <a>
+    LINESTRING (110 110, 200 200, 200 110, 110 200)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 202</desc>
+  <a>
+    POLYGON ((110 110, 200 20, 20 20, 110 110))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 203</desc>
+  <a>
+    LINESTRING (110 110, 200 200, 110 110, 20 200, 20 110, 200 110)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 204</desc>
+  <a>
+    LINESTRING (110 110, 20 110, 200 110, 50 110, 110 170)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 205</desc>
+  <a>
+    LINESTRING (110 110, 20 200, 110 200, 110 110, 200 200)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 206</desc>
+  <a>
+    LINESTRING (110 110, 170 50, 20 200, 20 110, 200 110)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 207</desc>
+  <a>
+    LINESTRING (110 110, 180 40, 110 40, 110 180)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 208</desc>
+  <a>
+    LINESTRING (110 60, 50 30, 170 30, 90 70)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 209</desc>
+  <a>
+    LINESTRING (110 110, 180 40, 110 40, 110 110, 70 40)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 210</desc>
+  <a>
+    LINESTRING (230 70, 170 120, 190 60, 140 60, 170 120, 270 90)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 211</desc>
+  <a>
+    MULTILINESTRING ((20 110, 200 110), (200 200, 110 110, 20 210, 110 110))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 212</desc>
+  <a>
+    MULTILINESTRING ((20 110, 200 110), (60 180, 60 110, 160 110, 110 110))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 213</desc>
+  <a>
+    MULTILINESTRING ((20 110, 200 110), (200 200, 110 110, 20 200, 110 200, 110 110))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 214</desc>
+  <a>
+    MULTILINESTRING ((20 110, 200 110), (110 50, 110 170, 110 70, 110 150, 200 150))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 215</desc>
+  <a>
+    MULTILINESTRING ((20 110, 200 110), (50 110, 170 110, 110 170, 110 50, 110 170, 110 50))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 216</desc>
+  <a>
+    MULTILINESTRING ((20 110, 200 110), (110 60, 110 160, 200 160))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 217</desc>
+  <a>
+    MULTILINESTRING ((110 100, 40 30, 180 30), (170 30, 110 90, 50 30))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 218</desc>
+  <a>
+    MULTILINESTRING ((110 110, 60 40, 70 20, 150 20, 170 40), (180 30, 40 30, 110 80))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 219</desc>
+  <a>
+    MULTILINESTRING ((20 110, 200 110, 200 160), (110 110, 200 110, 200 70, 20 150))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 220</desc>
+  <a>
+    MULTIPOLYGON (((110 110, 20 20, 200 20, 110 110)), ((110 110, 20 200, 200 200, 110 110)))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 221</desc>
+  <a>
+    MULTILINESTRING ((20 160, 70 110, 150 110, 200 160), (110 110, 20 110, 50 80, 70 110, 200 110))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 222</desc>
+  <a>
+    MULTILINESTRING ((20 110, 200 110), (110 110, 20 170, 20 130, 200 90))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 223</desc>
+  <a>
+    LINESTRING (0 0, 0 50, 50 50, 50 0, 0 0)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 224</desc>
+  <a>
+    MULTILINESTRING ((0 0, 0 50), (0 50, 50 50), (50 50, 50 0), (50 0, 0 0))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 225</desc>
+  <a>
+    LINESTRING (40 180, 140 180)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 226</desc>
+  <a>
+    MULTIPOLYGON (((20 320, 180 320, 180 180, 20 180, 20 320)), ((20 180, 20 80, 180 80, 180 180, 20 180)))
+  </a>
+  <test> <op name="isValid" arg1="A"> false </op> </test>
+</case>
+<case>
+  <desc>Test 227</desc>
+  <a>
+    MULTIPOLYGON (((20 320, 180 320, 180 180, 20 180, 20 320)), ((60 180, 60 80, 180 80, 180 180, 60 180)))
+  </a>
+  <test> <op name="isValid" arg1="A"> false </op> </test>
+</case>
+<case>
+  <desc>Test 228</desc>
+  <a>
+    LINESTRING (0 0, 60 0, 60 60, 60 0, 120 0)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 229</desc>
+  <a>
+    MULTILINESTRING ((0 0, 60 0), (60 0, 120 0), (60 0, 60 60))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 230</desc>
+  <a>
+    LINESTRING (40 40, 120 120)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 231</desc>
+  <a>
+    LINESTRING (40 40, 60 120)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 232</desc>
+  <a>
+    LINESTRING (60 240, 40 40)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 233</desc>
+  <a>
+    LINESTRING (40 40, 180 180)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 234</desc>
+  <a>
+    LINESTRING (120 120, 20 200)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 235</desc>
+  <a>
+    LINESTRING (60 240, 120 120)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 236</desc>
+  <a>
+    LINESTRING (20 180, 140 140)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 237</desc>
+  <a>
+    LINESTRING (40 120, 120 40)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 238</desc>
+  <a>
+    LINESTRING (40 40, 100 100)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 239</desc>
+  <a>
+    LINESTRING (100 100, 40 40)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 240</desc>
+  <a>
+    LINESTRING (40 120, 120 160)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 241</desc>
+  <a>
+    LINESTRING (20 20, 180 180)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 242</desc>
+  <a>
+    LINESTRING (20 20, 110 110)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 243</desc>
+  <a>
+    LINESTRING (50 50, 140 140)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 244</desc>
+  <a>
+    LINESTRING (180 180, 40 40)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 245</desc>
+  <a>
+    LINESTRING (120 120, 260 260)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 246</desc>
+  <a>
+    LINESTRING (260 260, 120 120)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 247</desc>
+  <a>
+    LINESTRING (40 40, 100 100, 200 120, 80 240)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 248</desc>
+  <a>
+    LINESTRING (40 40, 20 100, 40 160, 20 200)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 249</desc>
+  <a>
+    LINESTRING (20 200, 40 160, 20 100, 40 40)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 250</desc>
+  <a>
+    LINESTRING (80 240, 200 120, 100 100, 40 40)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 251</desc>
+  <a>
+    LINESTRING (60 60, 60 230, 140 230, 250 160)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 252</desc>
+  <a>
+    LINESTRING (20 20, 60 60, 250 160, 310 230)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 253</desc>
+  <a>
+    LINESTRING (20 20, 110 110, 200 110, 320 230)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 254</desc>
+  <a>
+    LINESTRING (60 110, 60 250, 360 210)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 255</desc>
+  <a>
+    LINESTRING (60 110, 110 160, 250 160, 310 160, 360 210)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 256</desc>
+  <a>
+    LINESTRING (360 210, 310 160, 110 160, 60 110)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 257</desc>
+  <a>
+    LINESTRING (160 160, 240 240)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 258</desc>
+  <a>
+    LINESTRING (240 240, 160 160)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 259</desc>
+  <a>
+    LINESTRING (60 150, 110 100, 170 100, 110 230)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 260</desc>
+  <a>
+    LINESTRING (200 120, 200 190, 150 240, 200 240)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 261</desc>
+  <a>
+    LINESTRING (200 240, 150 240, 200 200, 200 120)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 262</desc>
+  <a>
+    LINESTRING (60 230, 80 140, 120 140, 140 230)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 263</desc>
+  <a>
+    LINESTRING (60 110, 200 110, 250 160, 300 210)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 264</desc>
+  <a>
+    LINESTRING (60 110, 200 110, 250 160, 300 210, 360 210)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 265</desc>
+  <a>
+    LINESTRING (60 110, 220 110, 250 160, 280 110)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 266</desc>
+  <a>
+    LINESTRING (60 110, 150 110, 200 160, 250 110, 360 110, 360 210)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 267</desc>
+  <a>
+    LINESTRING (130 160, 160 110, 220 110, 250 160, 250 210)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 268</desc>
+  <a>
+    LINESTRING (130 160, 160 110, 190 110, 230 210)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 269</desc>
+  <a>
+    LINESTRING (130 160, 160 110, 200 110, 230 160, 260 210, 360 210)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 270</desc>
+  <a>
+    LINESTRING (130 160, 160 110, 200 110, 230 160, 260 210, 360 210, 380 210)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 271</desc>
+  <a>
+    LINESTRING (130 160, 160 110, 200 110, 230 160, 260 210, 380 210)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 272</desc>
+  <a>
+    LINESTRING (110 160, 160 110, 200 110, 250 160, 250 210)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 273</desc>
+  <a>
+    LINESTRING (110 160, 180 110, 250 160, 320 110)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 274</desc>
+  <a>
+    LINESTRING (140 160, 180 80, 220 160, 250 80)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 275</desc>
+  <a>
+    LINESTRING (40 40, 100 100, 200 120, 130 190)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 276</desc>
+  <a>
+    LINESTRING (20 130, 70 130, 160 40)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 277</desc>
+  <a>
+    LINESTRING (40 160, 40 100, 110 40, 170 40)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 278</desc>
+  <a>
+    LINESTRING (130 110, 180 160, 230 110, 280 160, 330 110)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 279</desc>
+  <a>
+    LINESTRING (30 140, 80 140, 100 100, 200 30)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 280</desc>
+  <a>
+    LINESTRING (110 110, 110 160, 180 110, 250 160, 250 110)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 281</desc>
+  <a>
+    LINESTRING (20 20, 80 80, 160 80, 240 80, 300 140)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 282</desc>
+  <a>
+    LINESTRING (20 60, 60 60, 60 140, 80 80, 100 20, 140 140, 180 20, 200 80, 220 20, 240 80, 300 80, 270 110, 200 110)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 283</desc>
+  <a>
+    LINESTRING (20 20, 230 20, 20 30, 170 30, 20 40, 230 40, 20 50, 230 60, 60 60, 230 70, 20 70, 180 80, 60 80, 230 90, 20 90, 230 100, 30 100, 210 110, 20 110, 80 120, 20 130, 170 130, 90 120, 230 130, 170 140, 230 140, 80 150, 160 140, 20 140, 70 150, 20 150, 230 160, 80 160, 230 170, 20 160, 180 170, 20 170, 230 180, 20 180, 40 190, 230 190, 20 200, 230 200)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 284</desc>
+  <a>
+    LINESTRING (30 210, 30 60, 40 210, 40 30, 50 190, 50 20, 60 160, 60 50, 70 220, 70 50, 80 20, 80 210, 90 50, 90 150, 100 30, 100 210, 110 20, 110 190, 120 50, 120 180, 130 210, 120 20, 140 210, 130 50, 150 210, 130 20, 160 210, 140 30, 170 210, 150 20, 180 210, 160 20, 190 210, 180 80, 170 50, 170 20, 180 70, 180 20, 190 190, 190 30, 200 210, 200 30, 210 210, 210 20, 220 150, 220 20)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 285</desc>
+  <a>
+    LINESTRING (80 240, 120 200, 200 120, 100 100, 80 80, 40 40)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 286</desc>
+  <a>
+    LINESTRING (260 210, 240 130, 280 120, 260 40)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 287</desc>
+  <a>
+    LINESTRING (100 20, 20 20, 20 160, 210 160, 210 20, 110 20, 50 120, 120 150, 200 150)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 288</desc>
+  <a>
+    LINESTRING (140 130, 100 110, 120 60, 170 60)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 289</desc>
+  <a>
+    LINESTRING (60 110, 110 160, 310 160, 360 210)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 290</desc>
+  <a>
+    LINESTRING (60 110, 110 160, 250 160)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 291</desc>
+  <a>
+    LINESTRING (110 160, 310 160, 340 190)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 292</desc>
+  <a>
+    LINESTRING (140 160, 250 160, 310 160, 340 190)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 293</desc>
+  <a>
+    LINESTRING (110 160, 250 160, 310 160)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 294</desc>
+  <a>
+    LINESTRING (200 120, 100 100, 40 40, 140 80, 200 40)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 295</desc>
+  <a>
+    LINESTRING (280 240, 240 140, 200 120, 100 100, 40 40)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 296</desc>
+  <a>
+    LINESTRING (80 190, 140 140, 40 40)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 297</desc>
+  <a>
+    LINESTRING (240 200, 200 260, 80 240, 140 180)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 298</desc>
+  <a>
+    LINESTRING (140 180, 80 240, 200 260, 240 200)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 299</desc>
+  <a>
+    LINESTRING (280 240, 240 140, 200 120, 80 240)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 300</desc>
+  <a>
+    LINESTRING (20 80, 120 80, 200 80, 260 20)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 301</desc>
+  <a>
+    LINESTRING (100 100, 200 120, 240 140, 280 240)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 302</desc>
+  <a>
+    LINESTRING (280 240, 240 140, 200 120, 100 100)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 303</desc>
+  <a>
+    LINESTRING (80 20, 80 80, 240 80, 300 20)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 304</desc>
+  <a>
+    LINESTRING (20 80, 80 80, 120 80, 140 140, 160 80, 200 80, 220 20, 240 80, 270 110, 300 80)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 305</desc>
+  <a>
+    LINESTRING (100 100, 20 180, 180 180)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 306</desc>
+  <a>
+    LINESTRING (100 100, 180 20, 20 20, 100 100)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 307</desc>
+  <a>
+    LINESTRING (20 100, 180 100, 100 180)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 308</desc>
+  <a>
+    LINESTRING (100 40, 100 160, 180 160)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 309</desc>
+  <a>
+    LINESTRING (20 100, 100 100, 180 100, 100 180)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 310</desc>
+  <a>
+    LINESTRING (100 100, 160 40)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 311</desc>
+  <a>
+    LINESTRING (100 100, 180 20)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 312</desc>
+  <a>
+    LINESTRING (60 60, 100 100, 140 60)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 313</desc>
+  <a>
+    LINESTRING (100 100, 190 10, 190 100)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 314</desc>
+  <a>
+    LINESTRING (100 100, 160 40, 160 100)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 315</desc>
+  <a>
+    LINESTRING (60 140, 160 40, 160 140)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 316</desc>
+  <a>
+    LINESTRING (20 20, 140 140)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 317</desc>
+  <a>
+    LINESTRING (80 80, 20 80, 140 80, 80 20, 80 140)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 318</desc>
+  <a>
+    LINESTRING (80 80, 20 80, 140 80)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 319</desc>
+  <a>
+    LINESTRING (80 80, 140 80, 80 20, 80 140)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 320</desc>
+  <a>
+    LINESTRING (80 80, 20 80, 140 80, 80 20, 80 80)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 321</desc>
+  <a>
+    LINESTRING (80 80, 20 80, 140 80, 80 80)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 322</desc>
+  <a>
+    LINESTRING (80 80, 20 80, 20 140, 140 20, 80 20, 80 80)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 323</desc>
+  <a>
+    LINESTRING (20 140, 140 20, 100 20, 100 80)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 324</desc>
+  <a>
+    LINESTRING (140 80, 20 80, 120 80, 80 20, 80 140)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 325</desc>
+  <a>
+    LINESTRING (140 80, 20 80, 140 80)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 326</desc>
+  <a>
+    LINESTRING (140 80, 20 80, 80 140, 80 20)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 327</desc>
+  <a>
+    LINESTRING (140 80, 80 80, 20 80, 50 140, 50 60)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 328</desc>
+  <a>
+    LINESTRING (140 80, 20 80, 120 80, 80 20, 80 80, 80 140)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 329</desc>
+  <a>
+    LINESTRING (140 80, 20 80, 80 80, 140 80)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 330</desc>
+  <a>
+    LINESTRING (140 80, 20 80, 80 140, 80 80, 80 20)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 331</desc>
+  <a>
+    LINESTRING (130 150, 220 150, 220 240)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 332</desc>
+  <a>
+    LINESTRING (130 240, 130 150, 220 20, 50 20, 130 150)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 333</desc>
+  <a>
+    LINESTRING (30 150, 130 150, 250 150)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 334</desc>
+  <a>
+    LINESTRING (30 150, 250 150)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 335</desc>
+  <a>
+    LINESTRING (130 240, 130 20, 30 20, 130 150)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 336</desc>
+  <a>
+    LINESTRING (120 240, 120 20, 20 20, 120 170)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 337</desc>
+  <a>
+    LINESTRING (200 200, 20 20, 200 20, 110 110, 20 200, 110 200, 110 110)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 338</desc>
+  <a>
+    LINESTRING (110 110, 200 110)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 339</desc>
+  <a>
+    LINESTRING (20 110, 200 110)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 340</desc>
+  <a>
+    LINESTRING (90 200, 90 130, 110 110, 150 200)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 341</desc>
+  <a>
+    LINESTRING (200 200, 20 20, 200 20, 20 200, 20 130, 90 130)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 342</desc>
+  <a>
+    LINESTRING (200 110, 110 110, 90 130, 90 200)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 343</desc>
+  <a>
+    LINESTRING (80 80, 150 80, 210 80)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 344</desc>
+  <a>
+    MULTILINESTRING ((20 20, 140 140), (20 140, 140 20))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 345</desc>
+  <a>
+    LINESTRING (40 80, 160 200, 260 20, 40 80)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 346</desc>
+  <a>
+    LINESTRING (40 80, 260 20, 160 200, 40 80)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 347</desc>
+  <a>
+    LINESTRING (260 20, 40 80, 160 200, 260 20)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 348</desc>
+  <a>
+    LINESTRING (100 140, 160 200, 260 20, 40 80, 100 140)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 349</desc>
+  <a>
+    LINESTRING (100 100, 180 180, 20 180, 100 100)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 350</desc>
+  <a>
+    LINESTRING (40 150, 40 40, 150 40, 150 150, 40 150)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 351</desc>
+  <a>
+    LINESTRING (40 150, 150 40, 170 20, 170 190, 40 150)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 352</desc>
+  <a>
+    LINESTRING (180 100, 20 100, 100 180, 180 100)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 353</desc>
+  <a>
+    LINESTRING (180 180, 100 100, 20 180, 180 180)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 354</desc>
+  <a>
+    LINESTRING (20 180, 100 100, 20 20, 20 180)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 355</desc>
+  <a>
+    LINESTRING (100 20, 100 180, 180 100, 100 20)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 356</desc>
+  <a>
+    LINESTRING (170 20, 20 170, 170 170, 170 20)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 357</desc>
+  <a>
+    LINESTRING (40 150, 150 150, 90 210, 40 150)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 358</desc>
+  <a>
+    LINESTRING (20 150, 170 150, 90 230, 20 150)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 359</desc>
+  <a>
+    LINESTRING (40 150, 150 150, 150 40, 20 40, 20 150, 40 150)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 360</desc>
+  <a>
+    LINESTRING (110 110, 200 20, 20 20, 110 110)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 361</desc>
+  <a>
+    LINESTRING (200 20, 20 200, 200 200, 110 110, 110 40)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 362</desc>
+  <a>
+    LINESTRING (200 20, 20 200, 200 200, 20 20)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 363</desc>
+  <a>
+    LINESTRING (110 110, 20 110, 110 20, 20 20, 110 110)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 364</desc>
+  <a>
+    LINESTRING (110 110, 200 200, 110 200, 200 110, 110 110)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 365</desc>
+  <a>
+    LINESTRING (20 120, 120 120, 20 20, 120 20, 20 120)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 366</desc>
+  <a>
+    LINESTRING (170 100, 70 100, 170 170, 70 170, 170 100)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 367</desc>
+  <a>
+    LINESTRING (20 110, 110 110, 20 20, 110 20, 20 110)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 368</desc>
+  <a>
+    LINESTRING (110 160, 70 110, 60 160, 20 130, 110 160)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 369</desc>
+  <a>
+    LINESTRING (20 200, 200 200, 20 20, 200 20, 20 200)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 370</desc>
+  <a>
+    LINESTRING (20 110, 200 110, 200 160, 20 60, 20 110)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 371</desc>
+  <a>
+    LINESTRING (200 200, 110 110, 200 110, 110 200, 200 200)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 372</desc>
+  <a>
+    LINESTRING (220 120, 120 20, 220 20, 120 120, 220 120)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 373</desc>
+  <a>
+    MULTILINESTRING ((70 20, 20 90, 70 170), (70 170, 120 90, 70 20))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 374</desc>
+  <a>
+    MULTILINESTRING ((20 20, 90 20, 170 20), (90 20, 90 80, 90 140))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 375</desc>
+  <a>
+    MULTILINESTRING ((90 140, 90 60, 90 20), (170 20, 130 20, 20 20))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 376</desc>
+  <a>
+    MULTILINESTRING ((90 20, 170 100, 170 140), (170 60, 90 20, 20 60), (130 100, 130 60, 90 20, 50 90))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 377</desc>
+  <a>
+    MULTILINESTRING ((90 20, 170 100, 170 140), (130 140, 130 60, 90 20, 20 90, 90 20, 130 60, 170 60))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 378</desc>
+  <a>
+    MULTILINESTRING ((90 20, 170 100, 170 140), (170 60, 90 20, 20 60))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 379</desc>
+  <a>
+    MULTILINESTRING ((90 20, 170 100, 170 140), (170 60, 90 20, 20 60), (130 100, 90 20))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 380</desc>
+  <a>
+    MULTILINESTRING ((90 20, 170 100, 170 140), (170 60, 90 20, 20 60), (120 100, 170 100, 90 20))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 381</desc>
+  <a>
+    MULTILINESTRING ((90 20, 170 100, 170 140), (130 140, 130 60, 90 20, 20 90, 90 20))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 382</desc>
+  <a>
+    MULTILINESTRING ((90 20, 170 100, 170 140), (170 60, 90 20, 20 60, 20 140, 90 20))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 383</desc>
+  <a>
+    MULTILINESTRING ((20 20, 90 90, 20 160), (90 160, 90 20))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 384</desc>
+  <a>
+    MULTILINESTRING ((160 160, 90 90, 160 20), (160 120, 120 120, 90 90, 160 60))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 385</desc>
+  <a>
+    MULTILINESTRING ((160 160, 90 90, 160 20), (160 120, 120 120, 90 90, 120 60, 160 60))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 386</desc>
+  <a>
+    MULTILINESTRING ((160 160, 90 90, 160 20), (160 120, 90 90, 160 60))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 387</desc>
+  <a>
+    POINT (20 20)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 388</desc>
+  <a>
+    POLYGON ((60 120, 60 40, 160 40, 160 120, 60 120))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 389</desc>
+  <a>
+    POINT (70 170)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 390</desc>
+  <a>
+    POLYGON ((110 230, 80 160, 20 160, 20 20, 200 20, 200 160, 140 160, 110 230))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 391</desc>
+  <a>
+    POINT (110 130)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 392</desc>
+  <a>
+    POLYGON ((20 160, 80 160, 110 100, 140 160, 200 160, 200 20, 20 20, 20 160))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 393</desc>
+  <a>
+    POINT (100 70)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 394</desc>
+  <a>
+    POLYGON ((20 150, 100 150, 40 50, 170 50, 110 150, 190 150, 190 20, 20 20, 20 150))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 395</desc>
+  <a>
+    POLYGON ((20 150, 100 150, 40 50, 160 50, 100 150, 180 150, 180 20, 20 20, 20 150))
+  </a>
+  <test> <op name="isValid" arg1="A"> false </op> </test>
+</case>
+<case>
+  <desc>Test 396</desc>
+  <a>
+    POINT (60 120)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 397</desc>
+  <a>
+    POINT (110 120)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 398</desc>
+  <a>
+    POINT (160 120)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 399</desc>
+  <a>
+    POINT (100 150)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 400</desc>
+  <a>
+    POINT (100 80)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 401</desc>
+  <a>
+    POINT (60 160)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 402</desc>
+  <a>
+    POLYGON ((190 190, 360 20, 20 20, 190 190), (280 50, 100 50, 190 140, 280 50))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 403</desc>
+  <a>
+    POINT (190 90)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 404</desc>
+  <a>
+    POINT (190 190)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 405</desc>
+  <a>
+    POINT (360 20)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 406</desc>
+  <a>
+    POINT (130 130)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 407</desc>
+  <a>
+    POINT (280 50)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 408</desc>
+  <a>
+    POINT (150 100)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 409</desc>
+  <a>
+    POINT (100 50)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 410</desc>
+  <a>
+    POINT (140 120)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 411</desc>
+  <a>
+    POINT (190 50)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 412</desc>
+  <a>
+    POLYGON ((190 190, 360 20, 20 20, 190 190), (90 50, 150 110, 190 50, 90 50), (190 50, 230 110, 290 50, 190 50))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 413</desc>
+  <a>
+    POINT (180 90)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 414</desc>
+  <a>
+    POLYGON ((190 190, 360 20, 20 20, 190 190), (180 140, 180 40, 80 40, 180 140), (180 90, 210 140, 310 40, 230 40, 180 90))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 415</desc>
+  <a>
+    MULTIPOINT (20 80, 110 160, 20 160)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 416</desc>
+  <a>
+    MULTIPOINT (20 80, 60 120, 20 160)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 417</desc>
+  <a>
+    MULTIPOINT (10 80, 110 170, 110 120)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 418</desc>
+  <a>
+    MULTIPOINT (10 80, 110 170, 160 120)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 419</desc>
+  <a>
+    MULTIPOINT (20 120, 60 120, 110 120, 160 120, 200 120)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 420</desc>
+  <a>
+    MULTIPOINT (60 120, 110 120, 160 120)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 421</desc>
+  <a>
+    MULTIPOINT (60 120, 160 120, 160 40, 60 40)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 422</desc>
+  <a>
+    MULTIPOINT (20 150, 60 120, 110 80)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 423</desc>
+  <a>
+    MULTIPOINT (110 80, 160 120, 200 160)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 424</desc>
+  <a>
+    MULTIPOINT (110 80, 110 120, 110 160)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 425</desc>
+  <a>
+    MULTIPOINT (110 170, 110 80)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 426</desc>
+  <a>
+    MULTIPOINT (60 120, 160 120, 110 80, 110 170)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 427</desc>
+  <a>
+    MULTIPOINT (90 80, 130 80)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 428</desc>
+  <a>
+    MULTIPOINT (60 120, 160 120, 110 80)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 429</desc>
+  <a>
+    MULTIPOINT (40 170, 40 90, 130 170)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 430</desc>
+  <a>
+    MULTIPOINT (90 170, 280 170, 190 90)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 431</desc>
+  <a>
+    MULTIPOINT (190 110, 150 70, 230 70)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 432</desc>
+  <a>
+    POINT (100 100)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 433</desc>
+  <a>
+    MULTIPOLYGON (((20 100, 20 20, 100 20, 100 100, 20 100)), ((100 180, 100 100, 180 100, 180 180, 100 180)))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 434</desc>
+  <a>
+    POINT (20 100)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 435</desc>
+  <a>
+    POINT (60 100)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 436</desc>
+  <a>
+    POINT (110 110)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 437</desc>
+  <a>
+    MULTIPOLYGON (((110 110, 20 200, 200 200, 110 110), (110 110, 80 180, 140 180, 110 110)), ((110 110, 20 20, 200 20, 110 110), (110 110, 80 40, 140 40, 110 110)))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 438</desc>
+  <a>
+    POINT (110 200)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 439</desc>
+  <a>
+    LINESTRING (90 80, 160 150, 300 150, 340 150, 340 240)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 440</desc>
+  <a>
+    POINT (90 80)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 441</desc>
+  <a>
+    POINT (340 240)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 442</desc>
+  <a>
+    POINT (230 150)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 443</desc>
+  <a>
+    POINT (160 150)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 444</desc>
+  <a>
+    POINT (90 150)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 445</desc>
+  <a>
+    LINESTRING (150 150, 20 20, 280 20, 150 150)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 446</desc>
+  <a>
+    POINT (150 80)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 447</desc>
+  <a>
+    POINT (150 150)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 448</desc>
+  <a>
+    POINT (100 20)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 449</desc>
+  <a>
+    POINT (220 220)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 450</desc>
+  <a>
+    LINESTRING (110 110, 220 20, 20 20, 110 110, 220 220)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 451</desc>
+  <a>
+    LINESTRING (110 110, 220 20, 20 20, 220 220)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 452</desc>
+  <a>
+    POINT (110 20)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 453</desc>
+  <a>
+    POINT (220 20)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 454</desc>
+  <a>
+    LINESTRING (220 220, 20 20, 220 20, 110 110)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 455</desc>
+  <a>
+    POINT (20 110)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 456</desc>
+  <a>
+    LINESTRING (20 200, 20 20, 110 20, 20 110, 110 200)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 457</desc>
+  <a>
+    POINT (20 200)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 458</desc>
+  <a>
+    LINESTRING (20 200, 200 20, 20 20, 200 200)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 459</desc>
+  <a>
+    LINESTRING (20 200, 200 20, 140 20, 140 80, 80 140, 20 140)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 460</desc>
+  <a>
+    POINT (80 140)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 461</desc>
+  <a>
+    LINESTRING (20 200, 110 110, 200 20, 140 20, 140 80, 110 110, 80 140, 20 140)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 462</desc>
+  <a>
+    LINESTRING (20 200, 200 20, 140 20, 140 80, 110 110, 80 140, 20 140)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 463</desc>
+  <a>
+    LINESTRING (20 200, 110 110, 200 20, 20 20, 110 110, 200 200)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 464</desc>
+  <a>
+    LINESTRING (20 200, 200 20, 20 20, 110 110, 200 200)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 465</desc>
+  <a>
+    LINESTRING (20 200, 110 110, 20 20, 200 20, 110 110, 200 200)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 466</desc>
+  <a>
+    LINESTRING (110 110, 110 200, 20 200, 110 110, 200 20, 140 20, 140 80, 110 110, 80 140, 20 140)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 467</desc>
+  <a>
+    LINESTRING (110 110, 110 200, 20 200, 200 20, 140 20, 140 80, 110 110, 80 140, 20 140)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 468</desc>
+  <a>
+    LINESTRING (110 110, 110 200, 20 200, 200 20, 140 20, 140 80, 80 140, 20 140)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 469</desc>
+  <a>
+    LINESTRING (110 110, 110 200, 20 200, 110 110, 200 20, 20 20, 110 110, 200 200)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 470</desc>
+  <a>
+    LINESTRING (110 110, 110 200, 20 200, 200 20, 20 20, 110 110, 200 200)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 471</desc>
+  <a>
+    LINESTRING (110 110, 110 200, 20 200, 200 20, 20 20, 200 200)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 472</desc>
+  <a>
+    LINESTRING (110 110, 110 200, 20 200, 110 110, 20 20, 200 20, 110 110, 200 200)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 473</desc>
+  <a>
+    LINESTRING (110 110, 110 200, 20 200, 200 20, 200 110, 110 110, 200 200)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 474</desc>
+  <a>
+    LINESTRING (200 200, 110 110, 20 20, 200 20, 110 110, 20 200, 110 200, 110 110)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 475</desc>
+  <a>
+    LINESTRING (200 200, 20 20, 200 20, 20 200, 110 200, 110 110)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 476</desc>
+  <a>
+    LINESTRING (200 200, 110 110, 200 20, 20 20, 110 110, 20 200, 110 200, 110 110)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 477</desc>
+  <a>
+    LINESTRING (200 200, 20 20, 20 110, 110 110, 20 200, 110 200, 110 110)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 478</desc>
+  <a>
+    POINT (110 160)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 479</desc>
+  <a>
+    LINESTRING (110 160, 200 250, 110 250, 110 160, 110 110, 110 20, 20 20, 110 110)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 480</desc>
+  <a>
+    LINESTRING (110 160, 200 250, 110 250, 110 110, 110 20, 20 20, 110 110)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 481</desc>
+  <a>
+    LINESTRING (110 160, 200 250, 110 250, 110 160, 110 20, 20 20, 110 110)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 482</desc>
+  <a>
+    LINESTRING (110 110, 200 200, 110 200, 110 110, 110 20, 20 20, 110 110)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 483</desc>
+  <a>
+    LINESTRING (110 110, 200 200, 110 200, 110 20, 20 20, 110 110)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 484</desc>
+  <a>
+    POINT (140 200)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 485</desc>
+  <a>
+    LINESTRING (110 110, 200 200, 110 200, 110 110, 110 20, 200 20, 110 110)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 486</desc>
+  <a>
+    POINT (90 130)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 487</desc>
+  <a>
+    LINESTRING (90 130, 20 130, 20 200, 90 130, 200 20, 20 20, 200 200)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 488</desc>
+  <a>
+    LINESTRING (90 130, 20 130, 20 200, 200 20, 20 20, 200 200)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 489</desc>
+  <a>
+    LINESTRING (200 200, 20 20, 200 20, 90 130, 20 200, 20 130, 90 130)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 490</desc>
+  <a>
+    LINESTRING (110 110, 20 130, 20 200, 110 110, 200 20, 20 20, 110 110, 200 200, 200 130, 110 110)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 491</desc>
+  <a>
+    LINESTRING (110 110, 20 130, 20 200, 200 20, 20 20, 200 200, 200 130, 110 110)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 492</desc>
+  <a>
+    LINESTRING (110 110, 80 200, 20 200, 110 110, 200 20, 20 20, 110 110, 200 200, 140 200, 110 110)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 493</desc>
+  <a>
+    LINESTRING (110 110, 80 200, 20 200, 200 20, 20 20, 200 200, 140 200, 110 110)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 494</desc>
+  <a>
+    LINESTRING (200 200, 20 20, 200 20, 20 200, 200 200)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 495</desc>
+  <a>
+    LINESTRING (200 200, 110 110, 20 20, 200 20, 110 110, 20 200, 200 200)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 496</desc>
+  <a>
+    LINESTRING (200 200, 110 110, 200 20, 20 20, 110 110, 20 200, 200 200)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 497</desc>
+  <a>
+    LINESTRING (90 130, 20 130, 20 200, 90 130, 110 110, 200 20, 20 20, 110 110, 200 200, 90 130)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 498</desc>
+  <a>
+    LINESTRING (90 130, 20 130, 20 200, 110 110, 200 20, 20 20, 110 110, 200 200, 90 130)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 499</desc>
+  <a>
+    LINESTRING (90 130, 90 200, 20 200, 90 130, 110 110, 200 20, 20 20, 110 110, 200 200, 90 130)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 500</desc>
+  <a>
+    LINESTRING (90 130, 90 200, 20 200, 200 20, 20 20, 200 200, 90 130)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 501</desc>
+  <a>
+    LINESTRING (90 130, 90 200, 20 200, 110 110, 200 20, 20 20, 110 110, 200 200, 90 130)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 502</desc>
+  <a>
+    LINESTRING (110 200, 110 110, 20 20, 200 20, 110 110, 110 200, 200 200)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 503</desc>
+  <a>
+    POINT (110 150)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 504</desc>
+  <a>
+    LINESTRING (110 200, 110 110, 20 20, 200 20, 110 110, 110 200)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 505</desc>
+  <a>
+    LINESTRING (20 200, 110 200, 110 110, 20 20, 200 20, 110 110, 110 200, 200 200)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 506</desc>
+  <a>
+    MULTIPOINT (50 250, 90 220, 130 190)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 507</desc>
+  <a>
+    MULTIPOINT (180 180, 230 130, 280 80)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 508</desc>
+  <a>
+    MULTIPOINT (50 120, 90 80, 130 40)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 509</desc>
+  <a>
+    MULTIPOINT (300 280, 340 240, 380 200)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 510</desc>
+  <a>
+    MULTIPOINT (230 150, 260 120, 290 90)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 511</desc>
+  <a>
+    MULTIPOINT (200 190, 240 150, 270 110)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 512</desc>
+  <a>
+    MULTIPOINT (160 150, 190 120, 220 90)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 513</desc>
+  <a>
+    MULTIPOINT (120 190, 160 150, 200 110)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 514</desc>
+  <a>
+    MULTIPOINT (90 80, 160 150, 340 240)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 515</desc>
+  <a>
+    MULTIPOINT (90 80, 160 150, 300 150)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 516</desc>
+  <a>
+    MULTIPOINT (90 80, 160 150, 240 150)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 517</desc>
+  <a>
+    MULTIPOINT (90 80, 130 120, 210 150)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 518</desc>
+  <a>
+    MULTIPOINT (130 120, 210 150, 340 200)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 519</desc>
+  <a>
+    MULTIPOINT (160 150, 240 150, 340 210)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 520</desc>
+  <a>
+    MULTIPOINT (160 150, 300 150, 340 150)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 521</desc>
+  <a>
+    MULTIPOINT (160 150, 240 150, 340 240)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 522</desc>
+  <a>
+    POINT (40 60)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 523</desc>
+  <a>
+    POINT (40 40)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 524</desc>
+  <a>
+    MULTIPOINT (20 20, 80 80, 20 120)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 525</desc>
+  <a>
+    MULTIPOINT (40 40, 80 60, 120 100)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 526</desc>
+  <a>
+    MULTIPOINT (40 40, 120 100, 80 60)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 527</desc>
+  <a>
+    MULTIPOINT (40 40, 60 100, 100 60, 120 120)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 528</desc>
+  <a>
+    MULTIPOINT (20 120, 60 60, 100 100, 140 40)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 529</desc>
+  <a>
+    MULTIPOINT (20 20, 80 70, 140 120, 200 170)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 530</desc>
+  <a>
+    MULTIPOINT (20 20, 140 120, 80 70, 200 170)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 531</desc>
+  <a>
+    MULTIPOINT (80 70, 20 20, 200 170, 140 120)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 532</desc>
+  <a>
+    MULTIPOINT (80 70, 140 120)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 533</desc>
+  <a>
+    MULTIPOINT (140 120, 80 70)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 534</desc>
+  <a>
+    MULTIPOINT (80 170, 140 120, 200 80)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 535</desc>
+  <a>
+    MULTIPOINT (80 170, 140 120, 200 80, 80 70)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 536</desc>
+  <a>
+    POINT (10 10)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 537</desc>
+  <a>
+    MULTIPOINT (10 10, 20 20)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 538</desc>
+  <a>
+    LINESTRING (10 10, 20 20)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 539</desc>
+  <a>
+    LINESTRING (10 10, 20 20, 20 10, 10 10)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 540</desc>
+  <a>
+    LINESTRING (40 40, 100 100, 180 100, 180 180, 100 180, 100 100)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 541</desc>
+  <a>
+    MULTILINESTRING ((10 10, 20 20), (20 20, 30 30))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 542</desc>
+  <a>
+    MULTILINESTRING ((10 10, 20 20), (20 20, 30 20), (20 20, 30 30))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 543</desc>
+  <a>
+    MULTILINESTRING ((10 10, 20 20), (20 20, 30 20), (20 20, 30 30), (20 20, 30 40))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 544</desc>
+  <a>
+    MULTILINESTRING ((10 10, 20 20), (20 20, 20 30, 30 30, 30 20, 20 20))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 545</desc>
+  <a>
+    MULTILINESTRING ((10 10, 20 20, 20 30, 30 30, 30 20, 20 20))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 546</desc>
+  <a>
+    POLYGON ((40 60, 420 60, 420 320, 40 320, 40 60))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 547</desc>
+  <a>
+    POLYGON ((40 60, 420 60, 420 320, 40 320, 40 60), (200 140, 160 220, 260 200, 200 140))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 548</desc>
+  <a>
+    MULTIPOINT (130 240, 130 240, 130 240, 570 240, 570 240, 570 240, 650 240)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 549</desc>
+  <a>
+    POLYGON ((10 10, 100 10, 100 100, 10 100, 10 10))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 550</desc>
+  <a>
+    LINESTRING (30 220, 240 220, 240 220)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 551</desc>
+  <a>
+    LINESTRING (110 290, 110 100, 110 100)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 552</desc>
+  <a>
+    LINESTRING (120 230, 120 200, 150 180, 180 220, 160 260, 90 250, 80 190, 140 110, 230 150, 240 230, 180 320, 60 310, 40 160, 140 50, 280 140)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 553</desc>
+  <a>
+    POLYGON ((200 360, 230 210, 100 190, 270 150, 360 10, 320 200, 490 230, 280 240, 200 360), (220 300, 250 200, 150 190, 290 150, 330 70, 310 210, 390 230, 280 230, 220 300))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 554</desc>
+  <a>
+    MULTIPOINT (70 340, 70 50, 430 50, 420 340, 340 120, 390 110, 390 70, 350 100, 350 50, 370 90, 320 80, 360 120, 350 80, 390 90, 420 80, 410 60, 410 100, 370 100, 380 60, 370 80, 380 100, 360 80, 370 80, 380 70, 390 80, 390 70, 410 70, 400 60, 410 60, 410 60, 410 60, 370 70, 410 50, 410 50, 410 50, 410 50, 410 50, 410 50, 410 50)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 555</desc>
+  <a>
+    MULTIPOINT (140 350, 510 140, 110 140, 250 290, 250 50, 300 370, 450 310, 440 160, 290 280, 220 160, 100 260, 320 230, 200 280, 360 130, 330 210, 380 80, 220 210, 380 310, 260 150, 260 110, 170 130)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 556</desc>
+  <a>
+    GEOMETRYCOLLECTION (POINT (110 300), POINT (100 110), POINT (130 210), POINT (150 210), POINT (150 180), POINT (130 170), POINT (140 190), POINT (130 200), LINESTRING (240 50, 210 120, 270 80, 250 140, 330 70, 300 160, 340 130, 340 130), POLYGON ((210 340, 220 260, 150 270, 230 220, 230 140, 270 210, 360 240, 260 250, 260 280, 240 270, 210 340), (230 270, 230 250, 200 250, 240 220, 240 190, 260 220, 290 230, 250 230, 230 270)))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 557</desc>
+  <a>
+    MULTIPOINT (50 320, 50 280, 50 230, 50 160, 50 120, 100 120, 160 120, 210 120, 210 180, 210 150, 180 180, 140 180, 140 210, 140 260, 160 180, 140 300, 140 320, 110 320, 80 320)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 559</desc>
+  <a>
+    POLYGON ((50 50, 200 50, 200 200, 50 200, 50 50))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 560</desc>
+  <a>
+    POLYGON ((20 20, 20 160, 160 160, 160 20, 20 20), (140 140, 40 140, 40 40, 140 40, 140 140))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 561</desc>
+  <a>
+    POLYGON ((80 100, 220 100, 220 240, 80 240, 80 100))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 562</desc>
+  <a>
+    POLYGON ((20 340, 330 380, 50 40, 20 340))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 563</desc>
+  <a>
+    POLYGON ((210 320, 140 270, 0 270, 140 220, 210 320))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 564</desc>
+  <a>
+    POLYGON ((0 0, 110 0, 110 60, 40 60, 180 140, 40 220, 110 260, 0 260, 0 0))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 565</desc>
+  <a>
+    POLYGON ((220 0, 110 0, 110 60, 180 60, 40 140, 180 220, 110 260, 220 260, 220 0))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 566</desc>
+  <a>
+    POLYGON ((0 0, 120 0, 120 50, 50 50, 120 100, 50 150, 120 150, 120 190, 0 190, 0 0))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 567</desc>
+  <a>
+    POLYGON ((230 0, 120 0, 120 50, 190 50, 120 100, 190 150, 120 150, 120 190, 230 190, 230 0))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 568</desc>
+  <a>
+    POLYGON ((0 0, 210 0, 210 230, 0 230, 0 0))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 569</desc>
+  <a>
+    MULTIPOLYGON (((40 20, 0 0, 20 40, 60 60, 40 20)), ((60 90, 60 60, 90 60, 90 90, 60 90)), ((70 120, 90 90, 100 120, 70 120)), ((120 70, 90 90, 120 100, 120 70)))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 570</desc>
+  <a>
+    POLYGON ((0 0, 340 0, 340 300, 0 300, 0 0))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 571</desc>
+  <a>
+    MULTIPOLYGON (((40 20, 0 0, 20 40, 60 60, 40 20)), ((60 100, 60 60, 100 60, 100 100, 60 100)))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 572</desc>
+  <a>
+    POLYGON ((0 0, 120 0, 120 120, 0 120, 0 0))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 573</desc>
+  <a>
+    MULTIPOLYGON (((60 20, 0 20, 60 60, 60 20)), ((60 100, 60 60, 100 60, 100 100, 60 100)))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 574</desc>
+  <a>
+    POLYGON ((160 330, 60 260, 20 150, 60 40, 190 20, 270 130, 260 250, 160 330), (140 240, 80 190, 90 100, 160 70, 210 130, 210 210, 140 240))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 575</desc>
+  <a>
+    POLYGON ((300 330, 190 270, 150 170, 150 110, 250 30, 380 50, 380 250, 300 330), (290 240, 240 200, 240 110, 290 80, 330 170, 290 240))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 576</desc>
+  <a>
+    MULTIPOLYGON (((120 340, 120 200, 140 200, 140 280, 160 280, 160 200, 180 200, 180 280, 200 280, 200 200, 220 200, 220 340, 120 340)), ((360 200, 220 200, 220 180, 300 180, 300 160, 220 160, 220 140, 300 140, 300 120, 220 120, 220 100, 360 100, 360 200)))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 577</desc>
+  <a>
+    MULTIPOLYGON (((100 220, 100 200, 300 200, 300 220, 100 220)), ((280 180, 280 160, 300 160, 300 180, 280 180)), ((220 140, 220 120, 240 120, 240 140, 220 140)), ((180 220, 160 240, 200 240, 180 220)))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 578</desc>
+  <a>
+    MULTIPOLYGON (((100 200, 100 180, 120 180, 120 200, 100 200)), ((60 240, 60 140, 220 140, 220 160, 160 160, 160 180, 200 180, 200 200, 160 200, 160 220, 220 220, 220 240, 60 240), (80 220, 80 160, 140 160, 140 220, 80 220)), ((280 220, 240 180, 260 160, 300 200, 280 220)))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 579</desc>
+  <a>
+    MULTIPOLYGON (((80 220, 80 160, 140 160, 140 220, 80 220), (100 200, 100 180, 120 180, 120 200, 100 200)), ((220 240, 220 220, 160 220, 160 200, 220 200, 220 180, 160 180, 160 160, 220 160, 220 140, 320 140, 320 240, 220 240), (240 220, 240 160, 300 160, 300 220, 240 220)))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 580</desc>
+  <a>
+    POLYGON ((60 160, 140 160, 140 60, 60 60, 60 160))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 581</desc>
+  <a>
+    POLYGON ((160 160, 100 160, 100 100, 160 100, 160 160), (140 140, 120 140, 120 120, 140 120, 140 140))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 582</desc>
+  <a>
+    POLYGON ((10 10, 100 10, 10 11, 10 10))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 583</desc>
+  <a>
+    POLYGON ((90 0, 200 0, 200 200, 90 200, 90 0))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 584</desc>
+  <a>
+    POLYGON ((100 10, 10 10, 90 11, 90 20, 100 20, 100 10))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 585</desc>
+  <a>
+    POLYGON ((20 20, 0 20, 0 0, 20 0, 20 20))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 586</desc>
+  <a>
+    POLYGON ((10 10, 50 10, 50 50, 10 50, 10 31, 49 30, 10 30, 10 10))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 587</desc>
+  <a>
+    POLYGON ((60 40, 40 40, 40 20, 60 20, 60 40))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 588</desc>
+  <a>
+    POLYGON ((10 100, 10 10, 100 10, 100 100, 10 100), (90 90, 11 90, 10 10, 90 11, 90 90))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 589</desc>
+  <a>
+    POLYGON ((0 30, 0 0, 30 0, 30 30, 0 30))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 590</desc>
+  <a>
+    MULTIPOLYGON (((0 0, 100 0, 100 20, 0 20, 0 0)), ((0 40, 0 21, 100 20, 100 40, 0 40)))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 591</desc>
+  <a>
+    POLYGON ((110 30, 90 30, 90 10, 110 10, 110 30))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 592</desc>
+  <a>
+    POLYGON ((100 10, 0 10, 100 11, 100 10))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 593</desc>
+  <a>
+    POLYGON ((100 10, 0 10, 90 11, 90 20, 100 20, 100 10))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 594</desc>
+  <a>
+    POLYGON ((10 30, 10 0, 30 10, 30 30, 10 30))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 595</desc>
+  <a>
+    POLYGON ((10 30, 10 10, 30 10, 30 30, 10 30))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 596</desc>
+  <a>
+    POLYGON ((0 0, 200 0, 0 198, 0 0))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 597</desc>
+  <a>
+    POLYGON ((280 60, 139 60, 280 70, 280 60))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 598</desc>
+  <a>
+    POLYGON ((0 0, 140 10, 0 20, 0 0))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 599</desc>
+  <a>
+    POLYGON ((280 0, 139 10, 280 1, 280 0))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 600</desc>
+  <a>
+    MULTIPOLYGON (((1 4, 1 1, 2 1, 2 4, 1 4)), ((3 4, 3 1, 4 1, 4 4, 3 4)), ((5 4, 5 1, 6 1, 6 4, 5 4)), ((7 4, 7 1, 8 1, 8 4, 7 4)), ((9 4, 9 1, 10 1, 10 4, 9 4)))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 601</desc>
+  <a>
+    POLYGON ((0 2, 11 3, 11 2, 0 2))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 602</desc>
+  <a>
+    POLYGON ((20 40, 20 200, 180 200, 180 40, 20 40), (180 120, 120 120, 120 160, 60 120, 120 80, 120 119, 180 120))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 603</desc>
+  <a>
+    POLYGON ((200 160, 160 160, 160 80, 200 80, 200 160))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 604</desc>
+  <a>
+    LINESTRING (160 140, 160 100)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 605</desc>
+  <a>
+    POLYGON ((20 40, 20 200, 180 200, 180 120, 140 120, 180 119, 180 40, 20 40), (140 160, 80 120, 140 80, 140 160))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 606</desc>
+  <a>
+    POLYGON ((200 160, 150 160, 150 80, 200 80, 200 160))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 607</desc>
+  <a>
+    POLYGON ((83 33, 62 402, 68 402, 83 33))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 608</desc>
+  <a>
+    POLYGON ((78 39, 574 76, 576 60, 78 39))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 609</desc>
+  <a>
+    LINESTRING (240 190, 120 120)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 610</desc>
+  <a>
+    POLYGON ((110 240, 50 80, 240 70, 110 240))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 611</desc>
+  <a>
+    LINESTRING (0 100, 100 100, 200 200)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 612</desc>
+  <a>
+    POLYGON ((30 240, 260 30, 30 30, 30 240), (80 140, 80 80, 140 80, 80 140))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 613</desc>
+  <a>
+    LINESTRING (40 340, 200 250, 120 180, 160 110, 270 40)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 614</desc>
+  <a>
+    MULTIPOLYGON (((60 320, 60 80, 300 80, 60 320), (80 280, 80 100, 260 100, 80 280)), ((120 160, 140 160, 140 140, 120 160)))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 615</desc>
+  <a>
+    MULTILINESTRING ((100 240, 100 180, 160 180, 160 120, 220 120), (40 360, 40 60, 340 60, 40 360, 40 20), (120 120, 120 140, 100 140, 100 120, 140 120))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 616</desc>
+  <a>
+    MULTIPOLYGON (((60 260, 60 120, 220 120, 220 260, 60 260), (80 240, 80 140, 200 140, 200 240, 80 240)), ((100 220, 100 160, 180 160, 180 220, 100 220), (120 200, 120 180, 160 180, 160 200, 120 200)))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 617</desc>
+  <a>
+    MULTILINESTRING ((40 260, 240 260, 240 240, 40 240, 40 220, 240 220), (120 300, 120 80, 140 80, 140 300, 140 80, 120 80, 120 320))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 618</desc>
+  <a>
+    MULTIPOLYGON (((60 320, 60 120, 280 120, 280 320, 60 320), (120 260, 120 180, 240 180, 240 260, 120 260)), ((280 400, 320 400, 320 360, 280 360, 280 400)), ((300 240, 300 220, 320 220, 320 240, 300 240)))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 619</desc>
+  <a>
+    MULTILINESTRING ((80 300, 80 160, 260 160, 260 300, 80 300, 80 140), (220 360, 220 240, 300 240, 300 360))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 620</desc>
+  <a>
+    MULTIPOLYGON (((120 180, 60 80, 180 80, 120 180)), ((100 240, 140 240, 120 220, 100 240)))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 621</desc>
+  <a>
+    MULTILINESTRING ((180 260, 120 180, 60 260, 180 260), (60 300, 60 40), (100 100, 140 100))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 622</desc>
+  <a>
+    POLYGON ((95 9, 81 414, 87 414, 95 9))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 623</desc>
+  <a>
+    LINESTRING (93 13, 96 13)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 624</desc>
+  <a>
+    LINESTRING (0 0, 100 100)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 625</desc>
+  <a>
+    LINESTRING (0 100, 100 0)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 626</desc>
+  <a>
+    LINESTRING (0 0, 100 100, 200 0)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 627</desc>
+  <a>
+    LINESTRING (0 0, 100 100, 200 200)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 628</desc>
+  <a>
+    LINESTRING (40 360, 40 220, 120 360)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 629</desc>
+  <a>
+    LINESTRING (120 340, 60 220, 140 220, 140 360)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 630</desc>
+  <a>
+    LINESTRING (220 240, 200 220, 60 320, 40 300, 180 200, 160 180, 20 280)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 631</desc>
+  <a>
+    LINESTRING (220 240, 140 160, 120 180, 220 280, 200 300, 100 200)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 632</desc>
+  <a>
+    LINESTRING (80 320, 220 320, 220 160, 80 300)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 633</desc>
+  <a>
+    LINESTRING (60 200, 60 260, 140 200)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 634</desc>
+  <a>
+    LINESTRING (60 200, 60 140, 140 200)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 635</desc>
+  <a>
+    LINESTRING (180 200, 100 280, 20 200, 100 120, 180 200)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 636</desc>
+  <a>
+    LINESTRING (100 200, 220 200, 220 80, 100 80, 100 200)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 637</desc>
+  <a>
+    LINESTRING (0 10, 620 10, 0 11)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 638</desc>
+  <a>
+    LINESTRING (400 60, 400 10)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 639</desc>
+  <a>
+    MULTIPOLYGON (((120 320, 180 200, 240 320, 120 320)), ((180 200, 240 80, 300 200, 180 200)))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 640</desc>
+  <a>
+    MULTIPOINT (120 320, 180 260, 180 320, 180 200, 300 200, 200 220)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 641</desc>
+  <a>
+    MULTIPOLYGON (((120 80, 420 80, 420 340, 120 340, 120 80), (160 300, 160 120, 380 120, 380 300, 160 300)), ((200 260, 200 160, 340 160, 340 260, 200 260), (240 220, 240 200, 300 200, 300 220, 240 220)))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 642</desc>
+  <a>
+    MULTIPOINT (200 360, 420 340, 400 100, 340 120, 200 140, 200 160, 220 180, 260 200, 200 360, 420 340, 400 100, 340 120, 200 140, 200 160, 220 180, 260 200)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 643</desc>
+  <a>
+    MULTIPOINT (40 90, 20 20, 70 70)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 644</desc>
+  <a>
+    LINESTRING (20 20, 100 100)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 645</desc>
+  <a>
+    LINESTRING (20 20, 110 110, 170 50, 130 10, 70 70)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 646</desc>
+  <a>
+    MULTILINESTRING ((100 320, 100 220), (100 180, 200 180), (220 180, 220 320), (220 320, 160 320), (100 320, 100 220), (100 180, 200 180), (220 180, 220 320), (220 320, 160 320), (100 220, 100 320))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 647</desc>
+  <a>
+    MULTIPOINT (100 320, 100 260, 100 220, 100 200, 100 180, 120 180, 200 180, 220 180, 220 260, 220 320, 200 320, 160 320, 140 320, 120 320, 100 320, 100 260, 100 220, 100 200, 100 180, 120 180, 200 180, 220 180, 220 260, 220 320, 200 320, 160 320, 140 320, 120 320)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 648</desc>
+  <a>
+    MULTILINESTRING ((-500 -140, -500 -280, -320 -280, -320 -140, -500 -140, -500 -340), (-500 -140, -320 -140, -500 -140, -320 -140, -500 -140))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 649</desc>
+  <a>
+    MULTIPOINT (-560 -180, -420 -180, -500 -220, -500 -340, -500 -280, -500 -140, -320 -140, -420 -140, -320 -180, -280 -140, -320 -120, -560 -180, -420 -180, -500 -220, -500 -340, -500 -280, -500 -140, -320 -140, -420 -140, -320 -180, -280 -140, -320 -120)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 650</desc>
+  <a>
+    MULTILINESTRING ((180 100, 140 280, 240 140, 220 120, 140 280), (140 280, 100 400, 80 380, 140 280, 40 380, 20 360, 140 280))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 651</desc>
+  <a>
+    POINT (200 200)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 652</desc>
+  <a>
+    MULTIPOINT (100 100, 200 200)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 653</desc>
+  <a>
+    MULTIPOINT (100 100, 200 200, 300 300, 500 500)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 654</desc>
+  <a>
+    MULTIPOINT (100 100, 200 200, 400 400, 600 600)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 655</desc>
+  <a>
+    POINT (80 200)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 656</desc>
+  <a>
+    POINT (260 80)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 657</desc>
+  <a>
+    POINT (60 260)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 658</desc>
+  <a>
+    POINT (120 260)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 659</desc>
+  <a>
+    POINT (80 80)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 660</desc>
+  <a>
+    POINT (80 280)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 661</desc>
+  <a>
+    POLYGON ((0 0, 140 0, 140 140, 0 140, 0 0))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 662</desc>
+  <a>
+    POLYGON ((140 0, 0 0, 0 140, 140 140, 140 0))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 663</desc>
+  <a>
+    POLYGON ((40 60, 360 60, 360 300, 40 300, 40 60))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 664</desc>
+  <a>
+    POLYGON ((120 100, 280 100, 280 240, 120 240, 120 100))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 665</desc>
+  <a>
+    POLYGON ((80 100, 360 100, 360 280, 80 280, 80 100))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 666</desc>
+  <a>
+    POLYGON ((0 280, 0 0, 260 0, 260 280, 0 280), (220 240, 40 240, 40 40, 220 40, 220 240))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 667</desc>
+  <a>
+    POLYGON ((20 260, 240 260, 240 20, 20 20, 20 260), (160 180, 80 180, 120 120, 160 180))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 668</desc>
+  <a>
+    POLYGON ((60 80, 200 80, 200 220, 60 220, 60 80))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 669</desc>
+  <a>
+    POLYGON ((120 140, 260 140, 260 260, 120 260, 120 140))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 670</desc>
+  <a>
+    POLYGON ((60 220, 220 220, 140 140, 60 220))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 671</desc>
+  <a>
+    POLYGON ((100 180, 180 180, 180 100, 100 100, 100 180))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 672</desc>
+  <a>
+    POLYGON ((40 40, 180 40, 180 180, 40 180, 40 40))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 673</desc>
+  <a>
+    POLYGON ((180 40, 40 180, 160 280, 300 140, 180 40))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 674</desc>
+  <a>
+    POLYGON ((40 280, 200 280, 200 100, 40 100, 40 280), (100 220, 120 220, 120 200, 100 180, 100 220))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 675</desc>
+  <a>
+    POLYGON ((40 280, 180 260, 180 120, 60 120, 40 280))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 676</desc>
+  <a>
+    POLYGON ((0 200, 0 0, 200 0, 200 200, 0 200), (20 180, 130 180, 130 30, 20 30, 20 180))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 677</desc>
+  <a>
+    POLYGON ((60 90, 130 90, 130 30, 60 30, 60 90))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 678</desc>
+  <a>
+    LINESTRING (100 120, 100 240)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 679</desc>
+  <a>
+    POLYGON ((40 60, 160 60, 160 180, 40 180, 40 60))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 680</desc>
+  <a>
+    LINESTRING (80 80, 140 140, 200 200)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 681</desc>
+  <a>
+    POLYGON ((40 40, 140 40, 140 140, 40 140, 40 40))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 682</desc>
+  <a>
+    POLYGON ((190 190, 360 20, 20 20, 190 190), (111 110, 250 100, 140 30, 111 110))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 683</desc>
+  <a>
+    POLYGON ((20 200, 20 20, 240 20, 240 200, 20 200), (130 110, 60 40, 60 180, 130 110), (130 180, 131 40, 200 110, 130 180))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 684</desc>
+  <a>
+    LINESTRING (100 140, 100 40)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 685</desc>
+  <a>
+    MULTIPOLYGON (((20 80, 180 79, 100 0, 20 80)), ((20 160, 180 160, 100 80, 20 160)))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 686</desc>
+  <a>
+    MULTIPOLYGON (((20 80, 180 80, 100 0, 20 80)), ((20 160, 180 160, 100 80, 20 160)))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 687</desc>
+  <a>
+    LINESTRING (60 0, 20 80, 100 80, 80 120, 40 140)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 688</desc>
+  <a>
+    LINESTRING (140 300, 220 160, 260 200, 240 260)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 689</desc>
+  <a>
+    LINESTRING (60 40, 140 40, 140 160, 0 160)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 690</desc>
+  <a>
+    LINESTRING (140 280, 240 280, 240 180, 140 180, 140 280)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 691</desc>
+  <a>
+    LINESTRING (140 0, 0 0, 40 60, 0 120, 60 200, 220 160, 220 40)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 692</desc>
+  <a>
+    LINESTRING (80 140, 180 100, 160 40, 100 40, 60 100, 80 140)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 693</desc>
+  <a>
+    LINESTRING (20 20, 80 80)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 694</desc>
+  <a>
+    LINESTRING (40 40, 160 160, 200 60, 60 140)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 695</desc>
+  <a>
+    LINESTRING (40 40, 200 40)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 696</desc>
+  <a>
+    LINESTRING (200 40, 140 40, 40 40)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 697</desc>
+  <a>
+    LINESTRING (0 0, 110 0, 60 0)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 698</desc>
+  <a>
+    LINESTRING (0 0, 110 0)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 699</desc>
+  <a>
+    LINESTRING (0 0, 80 0, 80 60, 80 0, 170 0)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 700</desc>
+  <a>
+    MULTILINESTRING ((0 0, 170 0), (80 0, 80 60))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 701</desc>
+  <a>
+    LINESTRING (80 100, 180 200)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 702</desc>
+  <a>
+    LINESTRING (80 180, 180 120)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 703</desc>
+  <a>
+    LINESTRING (40 40, 100 100, 160 160)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 704</desc>
+  <a>
+    LINESTRING (160 60, 100 100, 60 140)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 705</desc>
+  <a>
+    LINESTRING (140 60, 60 140)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 706</desc>
+  <a>
+    LINESTRING (40 40, 180 180, 100 180, 100 100)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 707</desc>
+  <a>
+    LINESTRING (80 90, 50 50, 0 0)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 708</desc>
+  <a>
+    LINESTRING (40 140, 240 140)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 709</desc>
+  <a>
+    LINESTRING (40 140, 100 140, 80 80, 120 60, 100 140, 160 140, 160 100, 200 100, 160 140, 240 140)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 710</desc>
+  <a>
+    LINESTRING (20 20, 100 20, 20 20)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 711</desc>
+  <a>
+    LINESTRING (60 20, 200 20)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 712</desc>
+  <a>
+    LINESTRING (40 60, 180 60, 180 140, 100 140, 100 60, 220 60, 220 180, 80 180, 80 60, 280 60)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 713</desc>
+  <a>
+    LINESTRING (140 60, 180 60, 220 60, 260 60)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 714</desc>
+  <a>
+    MULTIPOINT (0 20, 40 20)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 715</desc>
+  <a>
+    POLYGON ((20 40, 20 0, 60 0, 60 40, 20 40))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 716</desc>
+  <a>
+    MULTIPOINT (0 20, 20 20)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 717</desc>
+  <a>
+    MULTIPOINT (20 20, 40 20)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 718</desc>
+  <a>
+    MULTIPOINT (80 260, 140 260, 180 260)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 719</desc>
+  <a>
+    POLYGON ((40 320, 140 320, 140 200, 40 200, 40 320))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 720</desc>
+  <a>
+    MULTIPOLYGON (((0 40, 0 0, 40 0, 40 40, 0 40)), ((40 80, 40 40, 80 40, 80 80, 40 80)))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 721</desc>
+  <a>
+    LINESTRING (40 40, 120 120, 200 120)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 722</desc>
+  <a>
+    LINESTRING (40 40, 100 100, 160 100)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 723</desc>
+  <a>
+    POINT (60 60)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 724</desc>
+  <a>
+    MULTIPOINT (40 40, 100 40)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 725</desc>
+  <a>
+    LINESTRING (40 40, 80 80)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 726</desc>
+  <a>
+    MULTIPOINT (40 40, 60 60)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 727</desc>
+  <a>
+    MULTIPOINT (60 60, 100 100)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 728</desc>
+  <a>
+    LINESTRING (40 40, 60 60, 80 80)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 729</desc>
+  <a>
+    POINT (20 30)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 730</desc>
+  <a>
+    MULTIPOINT (40 40, 80 60, 40 100)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 731</desc>
+  <a>
+    MULTIPOINT (80 280, 80 220, 160 220, 80 220)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 732</desc>
+  <a>
+    MULTIPOINT (80 280, 80 220, 160 220)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 733</desc>
+  <a>
+    MULTIPOINT EMPTY
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 734</desc>
+  <a>
+    LINESTRING (20 60, 160 60, 80 160, 80 20)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 735</desc>
+  <a>
+    LINESTRING (20 80, 80 20, 80 80, 140 60, 80 20, 160 20)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 736</desc>
+  <a>
+    LINESTRING (20 60, 100 60, 60 100, 60 60)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 737</desc>
+  <a>
+    LINESTRING (20 60, 60 60, 100 60, 60 100, 60 60)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 738</desc>
+  <a>
+    LINESTRING (20 20, 80 20, 80 80, 20 20)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 739</desc>
+  <a>
+    LINESTRING (80 80, 20 20, 20 80, 140 80, 140 140, 80 80)
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 740</desc>
+  <a>
+    LINESTRING EMPTY
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 741</desc>
+  <a>
+    MULTILINESTRING ((40 140, 160 40), (160 140, 40 40))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 742</desc>
+  <a>
+    MULTILINESTRING ((20 160, 20 20), (100 160, 100 20))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 743</desc>
+  <a>
+    MULTILINESTRING ((60 140, 20 80, 60 40), (60 40, 100 80, 60 140))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 744</desc>
+  <a>
+    MULTILINESTRING ((60 40, 140 40, 100 120, 100 0), (100 200, 200 120))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 745</desc>
+  <a>
+    MULTILINESTRING ((40 120, 100 60), (160 120, 100 60), (40 60, 160 60))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 746</desc>
+  <a>
+    MULTILINESTRING ((80 160, 40 220, 40 100, 80 160), (80 160, 120 220, 120 100, 80 160))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 747</desc>
+  <a>
+    MULTILINESTRING ((80 160, 40 220), (80 160, 120 220, 120 100, 80 160), (40 100, 80 160))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 748</desc>
+  <a>
+    POLYGON ((180 260, 80 300, 40 180, 160 120, 180 260))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 749</desc>
+  <a>
+    POLYGON EMPTY
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 750</desc>
+  <a>
+    MULTIPOLYGON (((240 160, 140 220, 80 60, 220 40, 240 160)), ((160 380, 100 240, 20 380, 160 380), (120 340, 60 360, 80 320, 120 340)))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+<case>
+  <desc>Test 751</desc>
+  <a>
+    MULTIPOLYGON (((240 160, 100 240, 80 60, 220 40, 240 160)), ((160 380, 100 240, 20 380, 160 380), (120 340, 60 360, 80 320, 120 340)))
+  </a>
+  <test> <op name="isValid" arg1="A"> true </op> </test>
+</case>
+</run>

Added: packages/jts/branches/upstream/current/test/vivid/TestWithinDistance.xml
===================================================================
--- packages/jts/branches/upstream/current/test/vivid/TestWithinDistance.xml	                        (rev 0)
+++ packages/jts/branches/upstream/current/test/vivid/TestWithinDistance.xml	2007-06-15 19:34:59 UTC (rev 877)
@@ -0,0 +1,92 @@
+<run>
+  <precisionModel scale="1.0" offsetx="0.0" offsety="0.0"/>
+
+<case>
+  <desc>PP - disjoint points</desc>
+  <a>    POINT(10 10)  </a>
+  <b>    POINT(100 100)  </b>
+<test><op name="isWithinDistance" arg1="A" arg2="B" arg3="200">    true   </op></test>
+<test><op name="isWithinDistance" arg1="A" arg2="B" arg3="100">    false  </op></test>
+</case>
+
+<case>
+  <desc>PP - overlapping points</desc>
+  <a>    POINT(10 10)  </a>
+  <b>    POINT(10 10)  </b>
+<test><op name="isWithinDistance" arg1="A" arg2="B" arg3="200">  true   </op></test>
+<test><op name="isWithinDistance" arg1="A" arg2="B" arg3="0">    true  </op></test>
+</case>
+
+<case>
+  <desc>PL - point on linestring</desc>
+  <a>    POINT (340 200)  </a>
+  <b>    LINESTRING (80 280, 340 200, 80 80)  </b>
+<test><op name="isWithinDistance" arg1="A" arg2="B" arg3="0">    true   </op></test>
+<test><op name="isWithinDistance" arg1="A" arg2="B" arg3="10">   true  </op></test>
+</case>
+
+<case>
+  <desc>PL - point not on linestring</desc>
+  <a>    LINESTRING (100 100, 200 100, 200 200, 100 200, 100 100)  </a>
+  <b>    POINT (10 10)  </b>
+<test><op name="isWithinDistance" arg1="A" arg2="B" arg3="128">    true   </op></test>
+<test><op name="isWithinDistance" arg1="A" arg2="B" arg3="127">    false  </op></test>
+</case>
+
+<case>
+  <desc>PA - point inside polygon</desc>
+  <a>    POINT (240 160)  </a>
+  <b>    POLYGON ((100 260, 340 180, 100 60, 180 160, 100 260))  </b>
+<test><op name="isWithinDistance" arg1="A" arg2="B" arg3="0">    true   </op></test>
+<test><op name="isWithinDistance" arg1="A" arg2="B" arg3="10">   true  </op></test>
+</case>
+
+<case>
+  <desc>mPA - points outside polygon</desc>
+  <a>    POLYGON ((200 180, 60 140, 60 260, 200 180))  </a>
+  <b>    MULTIPOINT (140 280, 140 320)  </b>
+<test><op name="isWithinDistance" arg1="A" arg2="B" arg3="60">    true   </op></test>
+<test><op name="isWithinDistance" arg1="A" arg2="B" arg3="57">    false  </op></test>
+</case>
+
+<case>
+  <desc>LL - disjoint linestrings</desc>
+  <a>    LINESTRING (40 300, 240 260, 60 160, 140 60)  </a>
+  <b>    LINESTRING (140 360, 260 280, 240 120, 120 160)  </b>
+<test><op name="isWithinDistance" arg1="A" arg2="B" arg3="18">    true   </op></test>
+<test><op name="isWithinDistance" arg1="A" arg2="B" arg3="17">    false  </op></test>
+</case>
+
+<case>
+  <desc>LL - crossing linestrings</desc>
+  <a>    LINESTRING (40 300, 280 220, 60 160, 140 60)  </a>
+  <b>    LINESTRING (140 360, 260 280, 240 120, 120 160)  </b>
+<test><op name="isWithinDistance" arg1="A" arg2="B" arg3="0">     true   </op></test>
+<test><op name="isWithinDistance" arg1="A" arg2="B" arg3="10">    true  </op></test>
+</case>
+
+<case>
+  <desc>AA - overlapping polygons</desc>
+  <a>    POLYGON ((60 260, 260 180, 100 60, 60 160, 60 260))  </a>
+  <b>    POLYGON ((220 280, 120 160, 300 60, 360 220, 220 280))  </b>
+<test><op name="isWithinDistance" arg1="A" arg2="B" arg3="0">     true   </op></test>
+<test><op name="isWithinDistance" arg1="A" arg2="B" arg3="10">    true  </op></test>
+</case>
+
+<case>
+  <desc>AA - disjoint polygons</desc>
+  <a>    POLYGON ((100 320, 60 120, 240 180, 200 260, 100 320))  </a>
+  <b>    POLYGON ((420 320, 280 260, 400 100, 420 320))  </b>
+<test><op name="isWithinDistance" arg1="A" arg2="B" arg3="72">    true   </op></test>
+<test><op name="isWithinDistance" arg1="A" arg2="B" arg3="71">    false  </op></test>
+</case>
+
+<case>
+  <desc>mAmA - overlapping multipolygons</desc>
+  <a>    MULTIPOLYGON (((40 240, 160 320, 40 380, 40 240)),   ((100 240, 240 60, 40 40, 100 240)))  </a>
+  <b>    MULTIPOLYGON (((220 280, 120 160, 300 60, 360 220, 220 280)),   ((240 380, 280 300, 420 340, 240 380)))  </b>
+<test><op name="isWithinDistance" arg1="A" arg2="B" arg3="0">    true   </op></test>
+<test><op name="isWithinDistance" arg1="A" arg2="B" arg3="10">    true  </op></test>
+</case>
+
+</run>




More information about the Pkg-grass-devel mailing list