[geos-commits] [SCM] GEOS branch master updated. aaafcce227add7d80bf46d11e2630df2e6bda173

git at osgeo.org git at osgeo.org
Fri Aug 7 09:46:53 PDT 2020


This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "GEOS".

The branch, master has been updated
       via  aaafcce227add7d80bf46d11e2630df2e6bda173 (commit)
      from  98afa17c5d4ccfb860509043289226dd7de4cb27 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit aaafcce227add7d80bf46d11e2630df2e6bda173
Author: Paul Ramsey <pramsey at cleverelephant.ca>
Date:   Fri Aug 7 09:46:43 2020 -0700

    Port https://github.com/dr-jts/jts/commit/2a385fe46dc12e7b765569d010fb50c19e9bca46 Change line output behaviour to preserve noding

diff --git a/include/geos/index/kdtree/KdNode.h b/include/geos/index/kdtree/KdNode.h
index e41035a..b561c12 100644
--- a/include/geos/index/kdtree/KdNode.h
+++ b/include/geos/index/kdtree/KdNode.h
@@ -29,20 +29,20 @@ class GEOS_DLL KdNode {
 private:
 
     geom::Coordinate p;
-    const void* data;
+    void* data;
     KdNode* left;
     KdNode* right;
     std::size_t count;
 
 public:
 
-    KdNode(double p_x, double p_y, const void* p_data);
-    KdNode(const geom::Coordinate& p_p, const void* p_data);
+    KdNode(double p_x, double p_y, void* p_data);
+    KdNode(const geom::Coordinate& p_p, void* p_data);
 
     double getX() { return p.x; }
     double getY() { return p.y; }
     const geom::Coordinate& getCoordinate() { return p; }
-    const void* getData() { return data; }
+    void* getData() { return data; }
     KdNode* getLeft() { return left; }
     KdNode* getRight() { return right; }
     void increment() { count++; }
diff --git a/include/geos/index/kdtree/KdTree.h b/include/geos/index/kdtree/KdTree.h
index d87104e..c57215a 100644
--- a/include/geos/index/kdtree/KdTree.h
+++ b/include/geos/index/kdtree/KdTree.h
@@ -62,7 +62,7 @@ private:
     double tolerance;
 
     KdNode* findBestMatchNode(const geom::Coordinate& p);
-    KdNode* insertExact(const geom::Coordinate& p, const void* data);
+    KdNode* insertExact(const geom::Coordinate& p, void* data);
 
     void queryNode(KdNode* currentNode, const geom::Envelope& queryEnv, bool odd, KdNodeVisitor& visitor);
     KdNode* queryNodePoint(KdNode* currentNode, const geom::Coordinate& queryPt, bool odd);
@@ -71,7 +71,7 @@ private:
     * Create a node on a locally managed deque to allow easy
     * disposal and hopefully faster allocation as well.
     */
-    KdNode* createNode(const geom::Coordinate& p, const void* data);
+    KdNode* createNode(const geom::Coordinate& p, void* data);
 
 
     /**
@@ -158,7 +158,7 @@ public:
     * Inserts a new point in the kd-tree.
     */
     KdNode* insert(const geom::Coordinate& p);
-    KdNode* insert(const geom::Coordinate& p, const void* data);
+    KdNode* insert(const geom::Coordinate& p, void* data);
 
     /**
     * Performs a range search of the points in the index and visits all nodes found.
diff --git a/include/geos/noding/snapround/HotPixel.h b/include/geos/noding/snapround/HotPixel.h
index 8ae2643..d221f12 100644
--- a/include/geos/noding/snapround/HotPixel.h
+++ b/include/geos/noding/snapround/HotPixel.h
@@ -76,6 +76,7 @@ private:
     geom::Coordinate ptHot;
     geom::Coordinate originalPt;
 
+    bool hpIsNode;
     double scaleFactor;
 
     double minx;
@@ -134,6 +135,16 @@ public:
     bool intersects(const geom::Coordinate& p0,
                     const geom::Coordinate& p1) const;
 
+    /**
+    * Tests whether a coordinate lies in (intersects) this hot pixel.
+    *
+    * @param p the coordinate to test
+    * @return true if the coordinate intersects this hot pixel
+    */
+    bool intersects(const geom::Coordinate& p) const;
+
+    bool isNode() const { return hpIsNode; };
+    void setToNode() { hpIsNode = true; };
 
     std::ostream& operator<< (std::ostream& os);
 };
diff --git a/include/geos/noding/snapround/HotPixelIndex.h b/include/geos/noding/snapround/HotPixelIndex.h
index 9ec1b58..2240a4f 100644
--- a/include/geos/noding/snapround/HotPixelIndex.h
+++ b/include/geos/noding/snapround/HotPixelIndex.h
@@ -68,15 +68,24 @@ private:
 
     /* methods */
     geom::Coordinate round(const geom::Coordinate& c);
-    const HotPixel* find(const geom::Coordinate& pixelPt);
+    HotPixel* find(const geom::Coordinate& pixelPt);
 
 public:
 
     HotPixelIndex(const geom::PrecisionModel* p_pm);
-    const HotPixel* add(const geom::Coordinate& pt);
+    HotPixel* add(const geom::Coordinate& pt);
     void add(const geom::CoordinateSequence* pts);
     void add(const std::vector<geom::Coordinate>& pts);
-    void query(const geom::Coordinate& p0, const geom::Coordinate& p1, index::kdtree::KdNodeVisitor& visitor);
+    void addNodes(const geom::CoordinateSequence* pts);
+    void addNodes(const std::vector<geom::Coordinate>& pts);
+
+    /**
+    * Visits all the hot pixels which may intersect a segment (p0-p1).
+    * The visitor must determine whether each hot pixel actually intersects
+    * the segment.
+    */
+    void query(const geom::Coordinate& p0, const geom::Coordinate& p1,
+               index::kdtree::KdNodeVisitor& visitor);
 
 };
 
diff --git a/include/geos/noding/snapround/SnapRoundingIntersectionAdder.h b/include/geos/noding/snapround/SnapRoundingIntersectionAdder.h
index 0998c52..644801f 100644
--- a/include/geos/noding/snapround/SnapRoundingIntersectionAdder.h
+++ b/include/geos/noding/snapround/SnapRoundingIntersectionAdder.h
@@ -46,10 +46,29 @@ namespace geos {
 namespace noding { // geos::noding
 namespace snapround { // geos::noding::snapround
 
+/**
+ * Finds intersections between line segments which will be snap-rounded,
+ * and adds them as nodes to the segments.
+ *
+ * Intersections are detected and computed using full precision.
+ * Snapping takes place in a subsequent phase.
+ *
+ * The intersection points are recorded, so that HotPixels can be created for them.
+ *
+ * To avoid robustness issues with vertices which lie very close to line segments
+ * a heuristic is used:
+ * nodes are created if a vertex lies within a tolerance distance
+ * of the interior of a segment.
+ * The tolerance distance is chosen to be significantly below the snap-rounding grid size.
+ * This has empirically proven to eliminate noding failures.
+ */
 class GEOS_DLL SnapRoundingIntersectionAdder: public SegmentIntersector { // implements SegmentIntersector
 
 private:
-
+    /**
+    * The division factor used to determine
+    * nearness distance tolerance for interior intersection detection.
+    */
     static constexpr int NEARNESS_FACTOR = 100;
 
     algorithm::LineIntersector li;
@@ -59,10 +78,10 @@ private:
 
     /**
     * If an endpoint of one segment is near
-    * the <i>interior</i> of the other segment, add it as an intersection.
+    * the interior of the other segment, add it as an intersection.
     * EXCEPT if the endpoint is also close to a segment endpoint
     * (since this can introduce "zigs" in the linework).
-    * <p>
+    *
     * This resolves situations where
     * a segment A endpoint is extremely close to another segment B,
     * but is not quite crossing.  Due to robustness issues
@@ -84,7 +103,7 @@ public:
     * This method is called by clients
     * of the {@link SegmentIntersector} class to process
     * intersections for two segments of the {@link SegmentString}s being intersected.
-    * Note that some clients (such as <code>MonotoneChain</code>s) may optimize away
+    * Note that some clients (such as MonotoneChains) may optimize away
     * this call for segment pairs which they have determined do not intersect
     * (e.g. by an disjoint envelope test).
     */
diff --git a/include/geos/noding/snapround/SnapRoundingNoder.h b/include/geos/noding/snapround/SnapRoundingNoder.h
index 16252d6..2ceab38 100644
--- a/include/geos/noding/snapround/SnapRoundingNoder.h
+++ b/include/geos/noding/snapround/SnapRoundingNoder.h
@@ -41,6 +41,33 @@ namespace geos {
 namespace noding { // geos::noding
 namespace snapround { // geos::noding::snapround
 
+/**
+ * Uses Snap Rounding to compute a rounded,
+ * fully noded arrangement from a set of {@link SegmentString}s,
+ * in a performant way, and avoiding unnecessary noding.
+ *
+ * Implements the Snap Rounding technique described in
+ * the papers by Hobby, Guibas & Marimont, and Goodrich et al.
+ * Snap Rounding enforces that all output vertices lie on a uniform grid,
+ * which is determined by the provided {@link PrecisionModel}.
+ *
+ * Input vertices do not have to be rounded to the grid beforehand;
+ * this is done during the snap-rounding process.
+ * In fact, rounding cannot be done a priori,
+ * since rounding vertices by themselves can distort the rounded topology
+ * of the arrangement (i.e. by moving segments away from hot pixels
+ * that would otherwise intersect them, or by moving vertices
+ * across segments).
+ *
+ * To minimize the number of introduced nodes,
+ * the Snap-Rounding Noder avoids creating nodes
+ * at edge vertices if there is no intersection or snap at that location.
+ * However, if two different input edges contain identical segments,
+ * each of the segment vertices will be noded.
+ * This still provides fully-noded output.
+ * This is the same behaviour provided by other noders,
+ * such as {@link MCIndexNoder} and {@link SnappingNoder}.
+ */
 class GEOS_DLL SnapRoundingNoder : public Noder {
 
 private:
@@ -51,12 +78,23 @@ private:
     std::vector<SegmentString*> snappedResult;
 
     // Methods
-    void snapRound(const std::vector<SegmentString*>& inputSegStrings, std::vector<SegmentString*>& resultNodedSegments);
+    void snapRound(std::vector<SegmentString*>& inputSegStrings, std::vector<SegmentString*>& resultNodedSegments);
 
-    static void createNodedStrings(const std::vector<SegmentString*>& segStrings,
-        std::vector<SegmentString*>& nodedStrings);
+    /**
+    * Creates HotPixels for each vertex in the input segStrings.
+    * The HotPixels are not marked as nodes, since they will
+    * only be nodes in the final line arrangement
+    * if they interact with other segments (or they are already
+    * created as intersection nodes).
+    */
+    void addVertexPixels(std::vector<SegmentString*>& segStrings);
 
-    void addVertexPixels(const std::vector<SegmentString*>& segStrings);
+    /**
+    * Detects interior intersections in the collection of {@link SegmentString}s,
+    * and adds nodes for them to the segment strings.
+    * Also creates HotPixel nodes for the intersection points.
+    */
+    void addIntersectionPixels(std::vector<SegmentString*>& segStrings);
 
     void round(const geom::Coordinate& pt, geom::Coordinate& ptOut);
 
@@ -70,24 +108,14 @@ private:
     std::unique_ptr<std::vector<geom::Coordinate>> round(const std::vector<geom::Coordinate>& pts);
 
     /**
-    * Computes all interior intersections in the collection of {@link SegmentString}s,
-    * and returns their {@link Coordinate}s.
-    *
-    * Also adds the intersection nodes to the segments.
-    *
-    * @return a list of Coordinates for the intersections
-    */
-    std::unique_ptr<std::vector<geom::Coordinate>> findInteriorIntersections(std::vector<SegmentString*>& inputSS);
-
-    /**
     * Computes new segment strings which are rounded and contain
-    * any intersections added as a result of snapping segments to snap points (hot pixels).
+    * intersections added as a result of snapping segments to snap points (hot pixels).
     *
     * @param segStrings segments to snap
     * @return the snapped segment strings
     */
     void computeSnaps(const std::vector<SegmentString*>& segStrings, std::vector<SegmentString*>& snapped);
-    NodedSegmentString* computeSnaps(NodedSegmentString* ss);
+    NodedSegmentString* computeSegmentSnaps(NodedSegmentString* ss);
 
     /**
     * Snaps a segment in a segmentString to HotPixels that it intersects.
@@ -99,6 +127,13 @@ private:
     */
     void snapSegment(geom::Coordinate& p0, geom::Coordinate& p1, NodedSegmentString* ss, size_t segIndex);
 
+    /**
+    * Add nodes for any vertices in hot pixels that were
+    * added as nodes during segment noding.
+    */
+    void addVertexNodeSnaps(NodedSegmentString* ss);
+
+    void snapVertexNode(const geom::Coordinate& p0, NodedSegmentString* ss, size_t segIndex);
 
 public:
 
@@ -112,6 +147,10 @@ public:
     */
     std::vector<SegmentString*>* getNodedSubstrings() const override;
 
+    /**
+    * Computes the nodes in the snap-rounding line arrangement.
+    * The nodes are added to the {@link NodedSegmentString}s provided as the input.
+    */
     void computeNodes(std::vector<SegmentString*>* inputSegStrings) override; //override
 
 };
diff --git a/include/geos/operation/overlayng/LineBuilder.h b/include/geos/operation/overlayng/LineBuilder.h
index c636daa..9e8e1bb 100644
--- a/include/geos/operation/overlayng/LineBuilder.h
+++ b/include/geos/operation/overlayng/LineBuilder.h
@@ -109,6 +109,9 @@ private:
     geom::Location effectiveLocation(int geomIndex, const OverlayLabel* lbl) const;
 
     void addResultLines();
+    void addResultLinesMerged();
+
+    std::unique_ptr<geom::LineString> toLine(OverlayEdge* edge);
 
     void addResultLinesForNodes();
 
diff --git a/include/geos/operation/overlayng/OverlayLabel.h b/include/geos/operation/overlayng/OverlayLabel.h
index 3644943..275afe2 100644
--- a/include/geos/operation/overlayng/OverlayLabel.h
+++ b/include/geos/operation/overlayng/OverlayLabel.h
@@ -235,7 +235,6 @@ public:
     Location getLocation(int index, int position, bool isForward) const;
     bool hasSides(int index) const;
 
-    OverlayLabel copyFlip() const;
     OverlayLabel copy() const;
 
 
diff --git a/src/index/kdtree/KdNode.cpp b/src/index/kdtree/KdNode.cpp
index 99b3884..403f9f5 100644
--- a/src/index/kdtree/KdNode.cpp
+++ b/src/index/kdtree/KdNode.cpp
@@ -20,14 +20,14 @@ namespace geos {
 namespace index { // geos.index
 namespace kdtree { // geos.index.kdtree
 
-KdNode::KdNode(double p_x, double p_y, const void* p_data) :
+KdNode::KdNode(double p_x, double p_y, void* p_data) :
     p(p_x, p_y),
     data(p_data),
     left(nullptr),
     right(nullptr),
     count(1) {}
 
-KdNode::KdNode(const Coordinate& p_p, const void* p_data) :
+KdNode::KdNode(const Coordinate& p_p, void* p_data) :
     p(p_p),
     data(p_data),
     left(nullptr),
diff --git a/src/index/kdtree/KdTree.cpp b/src/index/kdtree/KdTree.cpp
index 20f6624..f2891fb 100644
--- a/src/index/kdtree/KdTree.cpp
+++ b/src/index/kdtree/KdTree.cpp
@@ -51,7 +51,7 @@ KdTree::toCoordinates(std::vector<KdNode*>& kdnodes, bool includeRepeated)
 
 /*private*/
 KdNode*
-KdTree::createNode(const Coordinate& p, const void* data)
+KdTree::createNode(const Coordinate& p, void* data)
 {
     auto it = nodeQue.emplace(nodeQue.end(), p, data);
     return &(*it);
@@ -66,7 +66,7 @@ KdTree::insert(const Coordinate& p)
 
 /*public*/
 KdNode*
-KdTree::insert(const Coordinate& p, const void* data)
+KdTree::insert(const Coordinate& p, void* data)
 {
     if (root == nullptr) {
         root = createNode(p, data);
@@ -98,7 +98,7 @@ KdTree::findBestMatchNode(const Coordinate& p) {
 }
 
 KdNode*
-KdTree::insertExact(const geom::Coordinate& p, const void* data)
+KdTree::insertExact(const geom::Coordinate& p, void* data)
 {
     KdNode* currentNode = root;
     KdNode* leafNode = root;
diff --git a/src/noding/snapround/HotPixel.cpp b/src/noding/snapround/HotPixel.cpp
index 181ff42..d0f90cd 100644
--- a/src/noding/snapround/HotPixel.cpp
+++ b/src/noding/snapround/HotPixel.cpp
@@ -16,8 +16,9 @@
  *
  **********************************************************************/
 
-#include <geos/algorithm/CGAlgorithmsDD.h>
 #include <geos/noding/snapround/HotPixel.h>
+
+#include <geos/algorithm/CGAlgorithmsDD.h>
 #include <geos/noding/NodedSegmentString.h>
 #include <geos/algorithm/LineIntersector.h>
 #include <geos/geom/Coordinate.h>
@@ -39,10 +40,10 @@ namespace noding { // geos.noding
 namespace snapround { // geos.noding.snapround
 
 HotPixel::HotPixel(const Coordinate& newPt, double newScaleFactor)
-    :
-    ptHot(newPt),
-    originalPt(newPt),
-    scaleFactor(newScaleFactor)
+    : ptHot(newPt)
+    , originalPt(newPt)
+    , hpIsNode(false)
+    , scaleFactor(newScaleFactor)
 {
     if(scaleFactor <= 0.0) {
         throw util::IllegalArgumentException("Scale factor must be non-zero");
@@ -59,10 +60,27 @@ HotPixel::HotPixel(const Coordinate& newPt, double newScaleFactor)
 
 /*public*/
 const geom::Coordinate&
-HotPixel::getCoordinate() const {
+HotPixel::getCoordinate() const
+{
     return originalPt;
 }
 
+/* public */
+bool
+HotPixel::intersects(const Coordinate& p) const
+{
+    double x = scale(p.x);
+    double y = scale(p.y);
+    if (x >= maxx) return false;
+    // check Left side
+    if (x < minx) return false;
+    // check Top side
+    if (y >= maxy) return false;
+    // check Bottom side
+    if (y < miny) return false;
+    // finally
+    return true;
+}
 
 /*public*/
 bool
diff --git a/src/noding/snapround/HotPixelIndex.cpp b/src/noding/snapround/HotPixelIndex.cpp
index abd7827..f302320 100644
--- a/src/noding/snapround/HotPixelIndex.cpp
+++ b/src/noding/snapround/HotPixelIndex.cpp
@@ -41,18 +41,31 @@ HotPixelIndex::HotPixelIndex(const PrecisionModel* p_pm)
 
 
 /*public*/
-const HotPixel*
+HotPixel*
 HotPixelIndex::add(const Coordinate& p)
 {
     Coordinate pRound = round(p);
-    const HotPixel* hp = find(p);
-    if (hp != nullptr)
+    HotPixel* hp = find(pRound);
+
+    /**
+     * Hot Pixels which are added more than once
+     * must have more than one vertex in them
+     * and thus must be nodes.
+     */
+    if (hp != nullptr) {
+        hp->setToNode();
         return hp;
+    }
+    /**
+     * A pixel containing the point was not found, so create a new one.
+     * It is initially set to NOT be a node
+     * (but may become one later on).
+     */
 
     // Store the HotPixel in a std::deque to avoid individually
     // allocating a pile of HotPixels on the heap and to
-    // get them freed automatically when the std::deque
-    // goes away when this object is disposed.
+    // get them freed automatically as the std::deque
+    // goes away when this HotPixelIndex is deleted.
     hotPixelQue.emplace_back(pRound, scaleFactor);
 
     // Pick up a pointer to the most recently added
@@ -61,7 +74,6 @@ HotPixelIndex::add(const Coordinate& p)
 
     index->insert(hp->getCoordinate(), (void*)hp);
     return hp;
-
 }
 
 /*public*/
@@ -69,7 +81,7 @@ void
 HotPixelIndex::add(const CoordinateSequence *pts)
 {
     for (size_t i = 0, sz = pts->size(); i < sz; i++) {
-        add(pts->getAt(i));
+        HotPixel* hp = add(pts->getAt(i));
     }
 }
 
@@ -78,19 +90,39 @@ void
 HotPixelIndex::add(const std::vector<geom::Coordinate>& pts)
 {
     for (auto pt: pts) {
-        add(pt);
+        HotPixel* hp = add(pt);
+    }
+}
+
+/*public*/
+void
+HotPixelIndex::addNodes(const CoordinateSequence *pts)
+{
+    for (size_t i = 0, sz = pts->size(); i < sz; i++) {
+        HotPixel* hp = add(pts->getAt(i));
+        hp->setToNode();
+    }
+}
+
+/*public*/
+void
+HotPixelIndex::addNodes(const std::vector<geom::Coordinate>& pts)
+{
+    for (auto pt: pts) {
+        HotPixel* hp = add(pt);
+        hp->setToNode();
     }
 }
 
 /*private*/
-const HotPixel*
+HotPixel*
 HotPixelIndex::find(const geom::Coordinate& pixelPt)
 {
     index::kdtree::KdNode *kdNode = index->query(pixelPt);
     if (kdNode == nullptr) {
         return nullptr;
     }
-    return (const HotPixel*)(kdNode->getData());
+    return (HotPixel*)(kdNode->getData());
 }
 
 /*private*/
diff --git a/src/noding/snapround/SnapRoundingNoder.cpp b/src/noding/snapround/SnapRoundingNoder.cpp
index 6b4917f..1b771d3 100644
--- a/src/noding/snapround/SnapRoundingNoder.cpp
+++ b/src/noding/snapround/SnapRoundingNoder.cpp
@@ -40,9 +40,10 @@ std::vector<SegmentString*>*
 SnapRoundingNoder::getNodedSubstrings() const
 {
     std::vector<SegmentString*>* nssResult = NodedSegmentString::getNodedSubstrings(snappedResult);
-    for (auto nss: snappedResult) {
+
+    // Intermediate SegmentStrings are no longer needed
+    for (auto nss: snappedResult)
         delete nss;
-    }
 
     return nssResult;
 }
@@ -57,44 +58,36 @@ SnapRoundingNoder::computeNodes(std::vector<SegmentString*>* inputSegStrings)
 
 /*private*/
 void
-SnapRoundingNoder::snapRound(const std::vector<SegmentString*>& inputSegStrings, std::vector<SegmentString*>& resultNodedSegments)
+SnapRoundingNoder::snapRound(std::vector<SegmentString*>& inputSegStrings, std::vector<SegmentString*>& resultNodedSegments)
 {
-    std::vector<SegmentString*> inputSS;
-    createNodedStrings(inputSegStrings, inputSS);
-
     /**
-     * Determine hot pixels for intersections and vertices.
-     * This is done BEFORE the input lines are rounded,
-     * to avoid distorting the line arrangement
-     * (rounding can cause vertices to move across edges).
-     */
-    std::unique_ptr<std::vector<Coordinate>> intersections = findInteriorIntersections(inputSS);
-    pixelIndex.add(*intersections);
+    * Determine hot pixels for intersections and vertices.
+    * This is done BEFORE the input lines are rounded,
+    * to avoid distorting the line arrangement
+    * (rounding can cause vertices to move across edges).
+    */
+    addIntersectionPixels(inputSegStrings);
     addVertexPixels(inputSegStrings);
 
-    computeSnaps(inputSS, resultNodedSegments);
-
-    // computeSnaps returns new NodedSegmentStrings with their
-    // own copy of the data, so free the inputs
-    for (auto nss: inputSS) {
-        delete nss;
-    }
+    computeSnaps(inputSegStrings, resultNodedSegments);
     return;
 }
 
-/*private static*/
+/*private*/
 void
-SnapRoundingNoder::createNodedStrings(const std::vector<SegmentString*>& segStrings, std::vector<SegmentString*>& nodedStrings)
+SnapRoundingNoder::addIntersectionPixels(std::vector<SegmentString*>& segStrings)
 {
-    for (SegmentString* ss : segStrings) {
-        nodedStrings.emplace_back(new NodedSegmentString(ss));
-    }
-    return;
+    SnapRoundingIntersectionAdder intAdder(pm);
+    MCIndexNoder noder;
+    noder.setSegmentIntersector(&intAdder);
+    noder.computeNodes(&segStrings);
+    std::unique_ptr<std::vector<Coordinate>> intPts = intAdder.getIntersections();
+    pixelIndex.addNodes(*intPts);
 }
 
 /*private void*/
 void
-SnapRoundingNoder::addVertexPixels(const std::vector<SegmentString*>& segStrings)
+SnapRoundingNoder::addVertexPixels(std::vector<SegmentString*>& segStrings)
 {
     for (SegmentString* nss : segStrings) {
         const CoordinateSequence* pts = nss->getCoordinates();
@@ -127,30 +120,26 @@ SnapRoundingNoder::round(const std::vector<Coordinate>& pts)
 }
 
 /*private*/
-std::unique_ptr<std::vector<Coordinate>>
-SnapRoundingNoder::findInteriorIntersections(std::vector<SegmentString*>& inputSS)
-{
-    SnapRoundingIntersectionAdder intAdder(pm);
-    MCIndexNoder noder;
-    noder.setSegmentIntersector(&intAdder);
-    noder.computeNodes(&inputSS);
-    return intAdder.getIntersections();
-}
-
-/*private*/
 void
 SnapRoundingNoder::computeSnaps(const std::vector<SegmentString*>& segStrings, std::vector<SegmentString*>& snapped)
 {
     for (SegmentString* ss: segStrings) {
-        NodedSegmentString* snappedSS = computeSnaps(detail::down_cast<NodedSegmentString*>(ss));
-        if (snappedSS != nullptr)
+        NodedSegmentString* snappedSS = computeSegmentSnaps(detail::down_cast<NodedSegmentString*>(ss));
+        if (snappedSS != nullptr) {
+            /**
+             * Some intersection hot pixels may have been marked as nodes in the previous
+             * loop, so add nodes for them.
+             */
             snapped.push_back(snappedSS);
+        }
+    }
+    for (SegmentString* ss: snapped) {
+        NodedSegmentString* nss = detail::down_cast<NodedSegmentString*>(ss);
+        addVertexNodeSnaps(nss);
     }
     return;
 }
 
-
-
 /**
 * Add snapped vertices to a segment string.
 * If the segment string collapses completely due to rounding,
@@ -161,7 +150,7 @@ SnapRoundingNoder::computeSnaps(const std::vector<SegmentString*>& segStrings, s
 */
 /*private*/
 NodedSegmentString*
-SnapRoundingNoder::computeSnaps(NodedSegmentString* ss)
+SnapRoundingNoder::computeSegmentSnaps(NodedSegmentString* ss)
 {
     /**
     * Get edge coordinates, including added intersection nodes.
@@ -218,28 +207,91 @@ SnapRoundingNoder::computeSnaps(NodedSegmentString* ss)
 void
 SnapRoundingNoder::snapSegment(Coordinate& p0, Coordinate& p1, NodedSegmentString* ss, size_t segIndex)
 {
+    /* First define a visitor to use in the pixelIndex.query() */
     struct SnapRoundingVisitor : KdNodeVisitor {
-        Coordinate& p0;
-        Coordinate& p1;
+        const Coordinate& p0;
+        const Coordinate& p1;
         NodedSegmentString* ss;
         size_t segIndex;
 
-        SnapRoundingVisitor(Coordinate& pp0, Coordinate& pp1, NodedSegmentString* pss, size_t psegIndex)
+        SnapRoundingVisitor(const Coordinate& pp0, const Coordinate& pp1, NodedSegmentString* pss, size_t psegIndex)
             : p0(pp0), p1(pp1), ss(pss), segIndex(psegIndex) {};
 
         void visit(KdNode* node) override {
-            const HotPixel* hp = static_cast<const HotPixel*>(node->getData());
+            HotPixel* hp = static_cast<HotPixel*>(node->getData());
+            /**
+            * If the hot pixel is not a node, and it contains one of the segment vertices,
+            * then that vertex is the source for the hot pixel.
+            * To avoid over-noding a node is not added at this point.
+            * The hot pixel may be subsequently marked as a node,
+            * in which case the intersection will be added during the final vertex noding phase.
+            */
+            if (! hp->isNode()) {
+                if (hp->intersects(p0) || hp->intersects(p1)) {
+                    return;
+                }
+            }
+            /**
+            * Add a node if the segment intersects the pixel.
+            * Mark the HotPixel as a node (since it may not have been one before).
+            * This ensures the vertex for it is added as a node during the final vertex noding phase.
+            */
             if (hp->intersects(p0, p1)) {
                 ss->addIntersection(hp->getCoordinate(), segIndex);
+                hp->setToNode();
             }
         }
     };
 
+    /* Then run the query with the visitor */
     SnapRoundingVisitor srv(p0, p1, ss, segIndex);
     pixelIndex.query(p0, p1, srv);
 }
 
 
+/*private*/
+void
+SnapRoundingNoder::addVertexNodeSnaps(NodedSegmentString* ss)
+{
+    const CoordinateSequence* pts = ss->getCoordinates();
+    for (int i = 1; i < pts->size() - 1; i++) {
+        const Coordinate& p0 = pts->getAt(i);
+        snapVertexNode(p0, ss, i);
+    }
+}
+
+void
+SnapRoundingNoder::snapVertexNode(const Coordinate& p0, NodedSegmentString* ss, size_t segIndex)
+{
+
+    /* First define a visitor to use in the pixelIndex.query() */
+    struct SnapRoundingVertexNodeVisitor : KdNodeVisitor {
+
+        const Coordinate& p0;
+        NodedSegmentString* ss;
+        size_t segIndex;
+
+        SnapRoundingVertexNodeVisitor(const Coordinate& pp0, NodedSegmentString* pss, size_t psegIndex)
+            : p0(pp0), ss(pss), segIndex(psegIndex) {};
+
+        void visit(KdNode* node) override {
+            HotPixel* hp = static_cast<HotPixel*>(node->getData());
+
+            /**
+            * If vertex pixel is a node, add it.
+            */
+            if (hp->isNode() && hp->getCoordinate().equals2D(p0)) {
+                ss->addIntersection(p0, segIndex);
+            }
+        }
+    };
+
+    /* Then run the query with the visitor */
+    SnapRoundingVertexNodeVisitor srv(p0, ss, segIndex);
+    pixelIndex.query(p0, p0, srv);
+}
+
+
 
 
 
diff --git a/src/operation/overlayng/LineBuilder.cpp b/src/operation/overlayng/LineBuilder.cpp
index 1c90ba3..ac5cf68 100644
--- a/src/operation/overlayng/LineBuilder.cpp
+++ b/src/operation/overlayng/LineBuilder.cpp
@@ -110,12 +110,39 @@ LineBuilder::effectiveLocation(int geomIndex, const OverlayLabel* lbl) const
 
 /*private*/
 void
-LineBuilder::addResultLines()
+LineBuilder::addResultLinesMerged()
 {
     addResultLinesForNodes();
     addResultLinesRings();
 }
 
+/*private*/
+void
+LineBuilder::addResultLines()
+{
+    std::vector<OverlayEdge*>& edges = graph->getEdges();
+
+    for (OverlayEdge* edge : edges) {
+        if (! edge->isInResultLine())
+            continue;
+        if (edge->isVisited())
+            continue;
+
+        lines.push_back(toLine(edge));
+        edge->markVisitedBoth();
+    }
+}
+
+std::unique_ptr<LineString>
+LineBuilder::toLine(OverlayEdge* edge)
+{
+    bool isForward = edge->isForward();
+    std::unique_ptr<CoordinateArraySequence> pts(new CoordinateArraySequence());
+    pts->add(edge->orig(), false);
+    edge->addCoordinates(pts.get());
+    return geometryFactory->createLineString(std::move(pts));
+}
+
 /**
 * FUTURE: To implement a strategy preserving input lines,
 * the label must carry an id for each input LineString.
diff --git a/src/operation/overlayng/OverlayLabel.cpp b/src/operation/overlayng/OverlayLabel.cpp
index 2483ccc..7fb8fe8 100644
--- a/src/operation/overlayng/OverlayLabel.cpp
+++ b/src/operation/overlayng/OverlayLabel.cpp
@@ -325,24 +325,6 @@ OverlayLabel::copy() const
     return lbl;
 }
 
-/*public*/
-OverlayLabel
-OverlayLabel::copyFlip() const
-{
-    OverlayLabel lbl;
-
-    lbl.aLocLeft = aLocRight;
-    lbl.aLocRight = aLocLeft;
-    lbl.aLocLine = aLocLine;
-    lbl.aDim = aDim;
-
-    lbl.bLocLeft = bLocRight;
-    lbl.bLocRight = bLocLeft;
-    lbl.bLocLine = bLocLine;
-    lbl.bDim = bDim;
-
-    return lbl;
-}
 
 /*private*/
 std::string
diff --git a/src/operation/overlayng/OverlayLabeller.cpp b/src/operation/overlayng/OverlayLabeller.cpp
index bf332c5..0ec2880 100644
--- a/src/operation/overlayng/OverlayLabeller.cpp
+++ b/src/operation/overlayng/OverlayLabeller.cpp
@@ -175,6 +175,7 @@ OverlayLabeller::propagateLinearLocations(int geomIndex)
     if (linearEdges.size() <= 0) return;
 
     std::deque<OverlayEdge*> edgeStack;
+    edgeStack.insert(edgeStack.begin(), linearEdges.begin(), linearEdges.end());
     bool isInputLine = inputGeometry->isLine(geomIndex);
     // traverse connected linear edges, labeling unknown ones
     while (! edgeStack.empty()) {
diff --git a/src/operation/overlayng/OverlayNG.cpp b/src/operation/overlayng/OverlayNG.cpp
index db7fe55..ba5ee3f 100644
--- a/src/operation/overlayng/OverlayNG.cpp
+++ b/src/operation/overlayng/OverlayNG.cpp
@@ -198,6 +198,8 @@ OverlayNG::computeEdgeOverlay()
 
     labelGraph(&graph);
 
+    // std::cout << std::endl << graph << std::endl;
+
     if (isOutputEdges || isOutputResultEdges) {
         return OverlayUtil::toLines(&graph, isOutputEdges, geomFact);
     }
diff --git a/tests/unit/Makefile.am b/tests/unit/Makefile.am
index 71cb0a4..44b2ad2 100644
--- a/tests/unit/Makefile.am
+++ b/tests/unit/Makefile.am
@@ -188,7 +188,6 @@ geos_unit_SOURCES = \
 	operation/overlayng/LineLimiterTest.cpp \
 	operation/overlayng/OverlayGraphTest.cpp \
 	operation/overlayng/OverlayNGFloatingNoderTest.cpp \
-	operation/overlayng/OverlayNGOneTest.cpp \
 	operation/overlayng/OverlayNGPointsTest.cpp \
 	operation/overlayng/OverlayNGMixedPointsTest.cpp \
 	operation/overlayng/OverlayNGSnappingNoderTest.cpp \
diff --git a/tests/unit/geom/EnvelopeTest.cpp b/tests/unit/geom/EnvelopeTest.cpp
index a0744c3..195fb89 100644
--- a/tests/unit/geom/EnvelopeTest.cpp
+++ b/tests/unit/geom/EnvelopeTest.cpp
@@ -261,11 +261,11 @@ void object::test<10>
     // 20 21 22 23 24
     std::vector<Coordinate> c(25);
 
-    std::cout<<std::endl;
+    // std::cout<<std::endl;
     for (size_t i = 0; i < c.size(); i++) {
         c[i].x = static_cast<double>(i % 5);
         c[i].y = static_cast<double>(5 - (i / 5));
-        std::cout<< c[i] << std::endl;
+        // std::cout<< c[i] << std::endl;
     }
 
     // point contained in envelope
diff --git a/tests/unit/noding/snapround/SnapRoundingNoderTest.cpp b/tests/unit/noding/snapround/SnapRoundingNoderTest.cpp
index 50fa1ff..908aa90 100644
--- a/tests/unit/noding/snapround/SnapRoundingNoderTest.cpp
+++ b/tests/unit/noding/snapround/SnapRoundingNoderTest.cpp
@@ -38,6 +38,7 @@ namespace tut {
 struct test_snaproundingnoder_data {
 
     WKTReader r;
+    WKTWriter w;
 
     void
     checkRounding(std::string& wkt, double scale, std::string& expected_wkt)
@@ -51,7 +52,13 @@ struct test_snaproundingnoder_data {
         if (expected_wkt.size() == 0) return;
 
         std::unique_ptr<Geometry> expected = r.read(expected_wkt);
-        ensure_equals_geometry(expected.get(), result.get());
+
+        // std::cout << std::endl << "result" << std::endl;
+        // std::cout << std::endl << w.write(result.get()) << std::endl;
+        // std::cout << std::endl << "expected" << std::endl;
+        // std::cout << std::endl << w.write(expected.get()) << std::endl;
+
+        ensure_equals_geometry(result.get(), expected.get());
     }
 
 
@@ -77,13 +84,34 @@ void object::test<1> ()
     checkRounding(wkt, 1, expected);
 }
 
+// testSnappedDiagonalLine
+template<>
+template<>
+void object::test<2> ()
+{
+    std::string wkt = "LINESTRING (2 3, 3 3, 3 2, 2 3)";
+    std::string expected = "MULTILINESTRING ((2 3, 3 3), (2 3, 3 3), (3 2, 3 3), (3 2, 3 3))";
+    checkRounding(wkt, 1, expected);
+}
+
+// testRingsWithParallelNarrowSpikes
+template<>
+template<>
+void object::test<3> ()
+{
+    std::string wkt = "MULTILINESTRING ((1 3.3, 1.3 1.4, 3.1 1.4, 3.1 0.9, 1.3 0.9, 1 -0.2, 0.8 1.3, 1 3.3), (1 2.9, 2.9 2.9, 2.9 1.3, 1.7 1, 1.3 0.9, 1 0.4, 1 2.9))";
+    std::string expected = "MULTILINESTRING ((1 3, 1 1), (1 1, 2 1), (2 1, 3 1), (3 1, 2 1), (2 1, 1 1), (1 1, 1 0), (1 0, 1 1), (1 1, 1 3), (1 3, 3 3, 3 1), (3 1, 2 1), (2 1, 1 1), (1 1, 1 0), (1 0, 1 1), (1 1, 1 3))";
+    checkRounding(wkt, 1, expected);
+}
+
+
 /**
 * This test checks the HotPixel test for overlapping horizontal line
 * testHorizontalLinesWithMiddleNode
 */
 template<>
 template<>
-void object::test<2> ()
+void object::test<4> ()
 {
     std::string wkt = "MULTILINESTRING ((2.5117493 49.0278625, 2.5144958 49.0278625), (2.511749 49.027863, 2.513123 49.027863, 2.514496 49.027863))";
     std::string expected = "MULTILINESTRING ((2.511749 49.027863, 2.513123 49.027863), (2.511749 49.027863, 2.513123 49.027863), (2.513123 49.027863, 2.514496 49.027863), (2.513123 49.027863, 2.514496 49.027863))";
@@ -93,17 +121,17 @@ void object::test<2> ()
 // testSlantAndHorizontalLineWithMiddleNode
 template<>
 template<>
-void object::test<3> ()
+void object::test<5> ()
 {
     std::string wkt = "MULTILINESTRING ((0.1565552 49.5277405, 0.1579285 49.5277405, 0.1593018 49.5277405), (0.1568985 49.5280838, 0.1589584 49.5273972))";
-    std::string expected = "MULTILINESTRING ((0.156555 49.527741, 0.157928 49.527741), (0.156899 49.528084, 0.157928 49.527741), (0.157928 49.527741, 0.157929 49.527741), (0.157928 49.527741, 0.158958 49.527397), (0.157929 49.527741, 0.159302 49.527741))";
+    std::string expected = "MULTILINESTRING ((0.156555 49.527741, 0.157928 49.527741), (0.156899 49.528084, 0.157928 49.527741), (0.157928 49.527741, 0.157929 49.527741, 0.159302 49.527741), (0.157928 49.527741, 0.158958 49.527397))";
     checkRounding(wkt, 1000000.0, expected);
 }
 
 // testNearbyCorner
 template<>
 template<>
-void object::test<4> ()
+void object::test<6> ()
 {
     std::string wkt = "MULTILINESTRING ((0.2 1.1, 1.6 1.4, 1.9 2.9), (0.9 0.9, 2.3 1.7))";
     std::string expected = "MULTILINESTRING ((0 1, 1 1), (1 1, 2 1), (1 1, 2 1), (2 1, 2 2), (2 1, 2 2), (2 2, 2 3))";
@@ -113,7 +141,7 @@ void object::test<4> ()
 // testNearbyShape
 template<>
 template<>
-void object::test<5> ()
+void object::test<7> ()
 {
     std::string wkt = "MULTILINESTRING ((1.3 0.1, 2.4 3.9), (0 1, 1.53 1.48, 0 4))";
     std::string expected = "MULTILINESTRING ((1 0, 2 1), (2 1, 2 4), (0 1, 2 1), (2 1, 0 4))";
@@ -127,7 +155,7 @@ void object::test<5> ()
 // testIntOnGridCorner
 template<>
 template<>
-void object::test<6> ()
+void object::test<8> ()
 {
     std::string wkt = "MULTILINESTRING ((4.30166242 45.53438188, 4.30166243 45.53438187), (4.3011475 45.5328371, 4.3018341 45.5348969))";
     std::string expected = "";
@@ -140,7 +168,7 @@ void object::test<6> ()
 // testVertexCrossesLine
 template<>
 template<>
-void object::test<7> ()
+void object::test<9> ()
 {
     std::string wkt = "MULTILINESTRING ((2.2164917 48.8864136, 2.2175217 48.8867569), (2.2175217 48.8867569, 2.2182083 48.8874435), (2.2182083 48.8874435, 2.2161484 48.8853836))";
     std::string expected = "";
@@ -154,7 +182,7 @@ void object::test<7> ()
 // testVertexCrossesLine2
 template<>
 template<>
-void object::test<8> ()
+void object::test<10> ()
 {
     std::string wkt = "MULTILINESTRING ((2.276916574988164 49.06082147500638, 2.2769165 49.0608215), (2.2769165 49.0608215, 2.2755432 49.0608215), (2.2762299 49.0615082, 2.276916574988164 49.06082147500638))";
     std::string expected = "";
@@ -168,7 +196,7 @@ void object::test<8> ()
 // testShortLineNodeNotAdded
 template<>
 template<>
-void object::test<9> ()
+void object::test<11> ()
 {
     std::string wkt = "LINESTRING (2.1279144 48.8445282, 2.126884443750796 48.84555818124935, 2.1268845 48.8455582, 2.1268845 48.8462448)";
     std::string expected = "MULTILINESTRING ((2.127914 48.844528, 2.126885 48.845558), (2.126885 48.845558, 2.126884 48.845558), (2.126884 48.845558, 2.126885 48.845558), (2.126885 48.845558, 2.126885 48.846245))";
@@ -183,7 +211,7 @@ void object::test<9> ()
 // testDiagonalNotNodedRightUp
 template<>
 template<>
-void object::test<10> ()
+void object::test<12> ()
 {
     std::string wkt = "MULTILINESTRING ((0 0, 10 10), ( 0 2, 4.55 5.4, 9 10 ))";
     std::string expected = "";
@@ -196,7 +224,7 @@ void object::test<10> ()
 // testDiagonalNotNodedLeftUp
 template<>
 template<>
-void object::test<11> ()
+void object::test<13> ()
 {
     std::string wkt = "MULTILINESTRING ((10 0, 0 10), ( 10 2, 5.45 5.45, 1 10 ))";
     std::string expected = "";
@@ -210,7 +238,7 @@ void object::test<11> ()
 // testDiagonalNotNodedOriginal
 template<>
 template<>
-void object::test<12> ()
+void object::test<14> ()
 {
     std::string wkt = "MULTILINESTRING (( 2.45167 48.96709, 2.45768 48.9731 ), (2.4526978 48.968811, 2.4537277 48.9691544, 2.4578476 48.9732742))";
     std::string expected = "";
@@ -225,13 +253,25 @@ void object::test<12> ()
 // testNearVertexNotNoded
 template<>
 template<>
-void object::test<13> ()
+void object::test<15> ()
 {
     std::string wkt = "MULTILINESTRING ((2.4829102 48.8726807, 2.4830818249999997 48.873195575, 2.4839401 48.8723373), ( 2.4829102 48.8726807, 2.4832535 48.8737106 ))";
     std::string expected = "";
     checkRounding(wkt, 100000000, expected);
 }
 
+// testLoopBackCreatesNode
+template<>
+template<>
+void object::test<16> ()
+{
+    std::string wkt = "LINESTRING (2 2, 5 2, 8 4, 5 6, 4.8 2.3, 2 5)";
+    std::string expected = "MULTILINESTRING ((2 2, 5 2), (5 2, 8 4, 5 6, 5 2), (5 2, 2 5))";
+    checkRounding(wkt, 1, expected);
+}
+
+
+
 
 
 } // namespace tut
diff --git a/tests/unit/operation/overlayng/OverlayNGOneTest.cpp b/tests/unit/operation/overlayng/OverlayNGOneTest.cpp
deleted file mode 100644
index e843638..0000000
--- a/tests/unit/operation/overlayng/OverlayNGOneTest.cpp
+++ /dev/null
@@ -1,587 +0,0 @@
-//
-// Test Suite for geos::operation::overlayng::OverlayNG class with SnappingNoder.
-
-#include <tut/tut.hpp>
-#include <utility.h>
-
-// geos
-#include <geos/operation/overlayng/OverlayNG.h>
-
-// std
-#include <memory>
-
-using namespace geos::geom;
-using namespace geos::operation::overlayng;
-using geos::io::WKTReader;
-using geos::io::WKTWriter;
-
-namespace tut {
-//
-// Test Group
-//
-
-// Common data used by all tests
-struct test_overlayngone_data {
-
-    WKTReader r;
-    WKTWriter w;
-
-    void
-    geomTest(const std::string& a, const std::string& b, const std::string& expected, int opCode, double scaleFactor)
-    {
-        geos::geom::PrecisionModel pm(scaleFactor);
-        std::unique_ptr<Geometry> geom_a = r.read(a);
-        std::unique_ptr<Geometry> geom_b = r.read(b);
-        std::unique_ptr<Geometry> geom_expected = r.read(expected);
-        std::unique_ptr<Geometry> geom_result = OverlayNG::overlay(geom_a.get(), geom_b.get(), opCode, &pm);
-        // std::string wkt_result = w.write(geom_result.get());
-        // std::cout << std::endl << wkt_result << std::endl;
-        ensure_equals_geometry(geom_expected.get(), geom_result.get());
-    }
-
-};
-
-typedef test_group<test_overlayngone_data> group;
-typedef group::object object;
-
-group test_overlayngone_group("geos::operation::overlayng::OverlayNGOne");
-
-//
-// Test Cases
-//
-
-// testRoundedBoxesIntersection
-template<>
-template<>
-void object::test<1> ()
-{
-    std::string a = "POLYGON ((0.6 0.1, 0.6 1.9, 2.9 1.9, 2.9 0.1, 0.6 0.1))";
-    std::string b = "POLYGON ((1.1 3.9, 2.9 3.9, 2.9 2.1, 1.1 2.1, 1.1 3.9))";
-    std::string exp = "LINESTRING (1 2, 3 2)";
-    geomTest(a, b, exp, OverlayNG::INTERSECTION, 1);
-}
-
-// xtestRoundedLinesIntersection
-template<>
-template<>
-void object::test<2> ()
-{
-    std::string a = "LINESTRING (3 2, 3 4)";
-    std::string b = "LINESTRING (1.1 1.6, 3.8 1.9)";
-    std::string exp = "POINT (3 2)";
-    geomTest(a, b, exp, OverlayNG::INTERSECTION, 1);
-}
-
-// xtestRoundedPointsIntersection
-template<>
-template<>
-void object::test<3> ()
-{
-    std::string a = "POINT (10.1 10)";
-    std::string b = "POINT (10 10.1)";
-    std::string exp = "POINT (10 10)";
-    geomTest(a, b, exp, OverlayNG::INTERSECTION, 1);
-}
-
-// xtestLineLineIntersectionFloat
-template<>
-template<>
-void object::test<4> ()
-{
-    std::string a = "LINESTRING (10 10, 20 20)";
-    std::string b = "LINESTRING (13 13, 10 10, 10 20, 20 20, 17 17)";
-    std::string exp = "LINESTRING (10 10, 10 20, 20 20, 17 17, 13 13, 10 10)";
-    geomTest(a, b, exp, OverlayNG::UNION, 10);
-}
-
-// xtestPolygonPointIntersection
-template<>
-template<>
-void object::test<5> ()
-{
-    std::string a = "POLYGON ((100 200, 200 200, 200 100, 100 100, 100 200))";
-    std::string b = "MULTIPOINT ((150 150), (250 150))";
-    std::string exp = "POINT (150 150)";
-    geomTest(a, b, exp, OverlayNG::INTERSECTION, 1);
-}
-
-// xtestPolygonPointUnion
-template<>
-template<>
-void object::test<6> ()
-{
-    std::string a = "POLYGON ((100 200, 200 200, 200 100, 100 100, 100 200))";
-    std::string b = "MULTIPOINT ((150 150), (250 150))";
-    std::string exp = "GEOMETRYCOLLECTION (POINT (250 150), POLYGON ((100 200, 200 200, 200 100, 100 100, 100 200)))";
-    geomTest(a, b, exp, OverlayNG::UNION, 1);
-}
-
-// xtestPolygoPolygonWithLineTouchIntersection
-template<>
-template<>
-void object::test<7> ()
-{
-    std::string a = "POLYGON ((360 200, 220 200, 220 180, 300 180, 300 160, 300 140, 360 200))";
-    std::string b = "MULTIPOLYGON (((280 180, 280 160, 300 160, 300 180, 280 180)), ((220 230, 240 230, 240 180, 220 180, 220 230)))";
-    std::string exp = "POLYGON ((220 200, 240 200, 240 180, 220 180, 220 200))";
-    geomTest(a, b, exp, OverlayNG::INTERSECTION, 1);
-}
-
-// xtestLinePolygonIntersectionAlongCollapse
-template<>
-template<>
-void object::test<8> ()
-{
-    std::string a = "POLYGON ((100 300, 300 300, 300 200, 130 200, 300 199.9, 300 100, 100 100, 100 300))";
-    std::string b = "LINESTRING (130 200, 200 200)";
-    std::string exp = "LINESTRING (130 200, 200 200)";
-    geomTest(a, b, exp, OverlayNG::INTERSECTION, 1);
-}
-
-// xtestLinePolygonIntersectionAlongPolyBoundary
-template<>
-template<>
-void object::test<9> ()
-{
-    std::string a = "LINESTRING (150 300, 250 300)";
-    std::string b = "POLYGON ((100 400, 200 400, 200 300, 100 300, 100 400))";
-    std::string exp = "LINESTRING (200 300, 150 300)";
-    geomTest(a, b, exp, OverlayNG::INTERSECTION, 1);
-}
-
-// xtestPolygonMultiLineUnion
-template<>
-template<>
-void object::test<10> ()
-{
-    std::string a = "POLYGON ((100 200, 200 200, 200 100, 100 100, 100 200))";
-    std::string b = "MULTILINESTRING ((150 250, 150 50), (250 250, 250 50))";
-    std::string exp = "GEOMETRYCOLLECTION (LINESTRING (150 50, 150 100), LINESTRING (150 200, 150 250), LINESTRING (250 50, 250 250), POLYGON ((100 100, 100 200, 150 200, 200 200, 200 100, 150 100, 100 100)))";
-    geomTest(a, b, exp, OverlayNG::UNION, 1);
-}
-
-// xtestLinePolygonUnion
-template<>
-template<>
-void object::test<11> ()
-{
-    std::string a = "LINESTRING (50 150, 150 150)";
-    std::string b = "POLYGON ((100 200, 200 200, 200 100, 100 100, 100 200))";
-    std::string exp = "GEOMETRYCOLLECTION (LINESTRING (50 150, 100 150), POLYGON ((100 200, 200 200, 200 100, 100 100, 100 150, 100 200)))";
-    geomTest(a, b, exp, OverlayNG::UNION, 1);
-}
-
-// xtestBoxGoreIntersection
-template<>
-template<>
-void object::test<12> ()
-{
-    std::string a = "MULTIPOLYGON (((1 1, 5 1, 5 0, 1 0, 1 1)), ((1 1, 5 2, 5 4, 1 4, 1 1)))";
-    std::string b = "POLYGON ((1 0, 1 2, 2 2, 2 0, 1 0))";
-    std::string exp = "POLYGON ((2 0, 1 0, 1 1, 1 2, 2 2, 2 1, 2 0))";
-    geomTest(a, b, exp, OverlayNG::INTERSECTION, 1);
-}
-
-// xtestBoxGoreUnion
-template<>
-template<>
-void object::test<13> ()
-{
-    std::string a = "MULTIPOLYGON (((1 1, 5 1, 5 0, 1 0, 1 1)), ((1 1, 5 2, 5 4, 1 4, 1 1)))";
-    std::string b = "POLYGON ((1 0, 1 2, 2 2, 2 0, 1 0))";
-    std::string exp = "POLYGON ((2 0, 1 0, 1 1, 1 2, 1 4, 5 4, 5 2, 2 1, 5 1, 5 0, 2 0))";
-    geomTest(a, b, exp, OverlayNG::UNION, 1);
-}
-
-// xtestCollapseBoxGoreIntersection
-template<>
-template<>
-void object::test<14> ()
-{
-    std::string a = "MULTIPOLYGON (((1 1, 5 1, 5 0, 1 0, 1 1)), ((1 1, 5 2, 5 4, 1 4, 1 1)))";
-    std::string b = "POLYGON ((1 0, 1 2, 2 2, 2 0, 1 0))";
-    std::string exp = "POLYGON ((2 0, 1 0, 1 1, 1 2, 2 2, 2 1, 2 0))";
-    geomTest(a, b, exp, OverlayNG::INTERSECTION, 1);
-}
-
-// xtestCollapseTriBoxIntersection
-template<>
-template<>
-void object::test<15> ()
-{
-    std::string a = "POLYGON ((1 2, 1 1, 9 1, 1 2))";
-    std::string b = "POLYGON ((9 2, 9 1, 8 1, 8 2, 9 2))";
-    std::string exp = "POINT (8 1)";
-    geomTest(a, b, exp, OverlayNG::INTERSECTION, 1);
-}
-
-// XtestCollapseTriBoxUnion
-template<>
-template<>
-void object::test<16> ()
-{
-    std::string a = "POLYGON ((1 2, 1 1, 9 1, 1 2))";
-    std::string b = "POLYGON ((9 2, 9 1, 8 1, 8 2, 9 2))";
-    std::string exp = "MULTIPOLYGON (((1 1, 1 2, 8 1, 1 1)), ((8 1, 8 2, 9 2, 9 1, 8 1)))";
-    geomTest(a, b, exp, OverlayNG::UNION, 1);
-}
-
-// xtestAdjacentBoxesUnion
-template<>
-template<>
-void object::test<17> ()
-{
-    std::string a = "POLYGON ((100 200, 200 200, 200 100, 100 100, 100 200))";
-    std::string b = "POLYGON ((300 200, 300 100, 200 100, 200 200, 300 200))";
-    std::string exp = "POLYGON ((100 100, 100 200, 200 200, 300 200, 300 100, 200 100, 100 100))";
-    geomTest(a, b, exp, OverlayNG::UNION, 1);
-}
-
-// xtestBoxTriIntersection
-template<>
-template<>
-void object::test<18> ()
-{
-    std::string a = "POLYGON ((0 6, 4 6, 4 2, 0 2, 0 6))";
-    std::string b = "POLYGON ((1 0, 2 5, 3 0, 1 0))";
-    std::string exp = "POLYGON ((3 2, 1 2, 2 5, 3 2))";
-    geomTest(a, b, exp, OverlayNG::INTERSECTION, 1);
-}
-
-
-// xtestBoxTriUnion
-template<>
-template<>
-void object::test<19> ()
-{
-    std::string a = "POLYGON ((0 6, 4 6, 4 2, 0 2, 0 6))";
-    std::string b = "POLYGON ((1 0, 2 5, 3 0, 1 0))";
-    std::string exp = "POLYGON ((0 6, 4 6, 4 2, 3 2, 3 0, 1 0, 1 2, 0 2, 0 6))";
-    geomTest(a, b, exp, OverlayNG::UNION, 1);
-}
-
-// xtestMultiHoleBoxUnion
-template<>
-template<>
-void object::test<20> ()
-{
-    std::string a = "MULTIPOLYGON (((0 200, 200 200, 200 0, 0 0, 0 200), (50 50, 190 50, 50 200, 50 50), (20 20, 20 50, 50 50, 50 20, 20 20)), ((60 100, 50 50, 100 60, 60 100)))";
-    std::string b = "POLYGON ((60 110, 100 110, 100 60, 60 60, 60 110))";
-    std::string exp = "MULTIPOLYGON (((0 200, 50 200, 200 200, 200 0, 0 0, 0 200), (50 50, 190 50, 50 200, 50 50), (20 50, 20 20, 50 20, 50 50, 20 50)), ((60 100, 60 110, 100 110, 100 60, 50 50, 60 100)))";
-    geomTest(a, b, exp, OverlayNG::UNION, 1);
-}
-
-
-
-// xtestNestedPolysUnion
-template<>
-template<>
-void object::test<21> ()
-{
-    std::string a = "MULTIPOLYGON (((0 200, 200 200, 200 0, 0 0, 0 200), (50 50, 190 50, 50 200, 50 50)), ((60 100, 100 60, 50 50, 60 100)))";
-    std::string b = "POLYGON ((135 176, 180 176, 180 130, 135 130, 135 176))";
-    std::string exp = "MULTIPOLYGON (((0 0, 0 200, 50 200, 200 200, 200 0, 0 0), (50 50, 190 50, 50 200, 50 50)), ((50 50, 60 100, 100 60, 50 50)))";
-    geomTest(a, b, exp, OverlayNG::UNION, 1);
-}
-
-// TODO: check this when it has be implemented...
-// xtestMultiHoleSideTouchingBoxUnion
-template<>
-template<>
-void object::test<22> ()
-{
-    std::string a = "MULTIPOLYGON (((0 200, 200 200, 200 0, 0 0, 0 200), (50 50, 190 50, 50 200, 50 50), (20 20, 20 50, 50 50, 50 20, 20 20)))";
-    std::string b = "POLYGON ((100 100, 100 50, 50 50, 50 100, 100 100))";
-    std::string exp = "LINESTRING (50 100.0000000000000000, 50 50, 100 50)";
-    geomTest(a, b, exp, OverlayNG::INTERSECTION, 1);
-}
-
-// xtestNestedShellsIntersection
-template<>
-template<>
-void object::test<23> ()
-{
-    std::string a = "POLYGON ((100 200, 200 200, 200 100, 100 100, 100 200))";
-    std::string b = "POLYGON ((120 180, 180 180, 180 120, 120 120, 120 180))";
-    std::string exp = "POLYGON ((120 180, 180 180, 180 120, 120 120, 120 180))";
-    geomTest(a, b, exp, OverlayNG::INTERSECTION, 1);
-}
-
-// xtestNestedShellsUnion
-template<>
-template<>
-void object::test<24> ()
-{
-    std::string a = "POLYGON ((100 200, 200 200, 200 100, 100 100, 100 200))";
-    std::string b = "POLYGON ((120 180, 180 180, 180 120, 120 120, 120 180))";
-    std::string exp = "POLYGON ((100 200, 200 200, 200 100, 100 100, 100 200))";
-    geomTest(a, b, exp, OverlayNG::UNION, 1);
-}
-
-// xtestBoxLineIntersection
-template<>
-template<>
-void object::test<25> ()
-{
-    std::string a = "POLYGON ((100 200, 200 200, 200 100, 100 100, 100 200))";
-    std::string b = "LINESTRING (50 150, 150 150)";
-    std::string exp = "LINESTRING (100 150, 150 150)";
-    geomTest(a, b, exp, OverlayNG::INTERSECTION, 1);
-}
-// xtestBoxLineUnion
-template<>
-template<>
-void object::test<26> ()
-{
-    std::string a = "POLYGON ((100 200, 200 200, 200 100, 100 100, 100 200))";
-    std::string b = "LINESTRING (50 150, 150 150)";
-    std::string exp = "GEOMETRYCOLLECTION (LINESTRING (50 150, 100 150), POLYGON ((100 200, 200 200, 200 100, 100 100, 100 150, 100 200)))";
-    geomTest(a, b, exp, OverlayNG::UNION, 1);
-}
-
-// xtestAdjacentBoxesIntersection
-template<>
-template<>
-void object::test<27> ()
-{
-    std::string a = "POLYGON ((100 200, 200 200, 200 100, 100 100, 100 200))";
-    std::string b = "POLYGON ((300 200, 300 100, 200 100, 200 200, 300 200))";
-    std::string exp = "LINESTRING (200 100, 200 200)";
-    geomTest(a, b, exp, OverlayNG::INTERSECTION, 1);
-}
-
-// xtestBoxContainingPolygonCollapseIntersection
-template<>
-template<>
-void object::test<28> ()
-{
-    std::string a = "POLYGON ((100 200, 300 200, 300 0, 100 0, 100 200))";
-    std::string b = "POLYGON ((250 100, 150 100, 150 100.4, 250 100))";
-    std::string exp = "POLYGON EMPTY";
-    geomTest(a, b, exp, OverlayNG::INTERSECTION, 1);
-}
-
-// xtestBoxContainingPolygonCollapseManyPtsIntersection
-template<>
-template<>
-void object::test<29> ()
-{
-    std::string a = "POLYGON ((100 200, 300 200, 300 0, 100 0, 100 200))";
-    std::string b = "POLYGON ((250 100, 150 100, 150 100.4, 160 100.2, 170 100.1, 250 100))";
-    std::string exp = "POLYGON EMPTY";
-    geomTest(a, b, exp, OverlayNG::INTERSECTION, 1);
-}
-
-// xtestPolygonsSpikeCollapseIntersection
-template<>
-template<>
-void object::test<30> ()
-{
-    std::string a = "POLYGON ((2.33906 48.78994, 2.33768 48.78857, 2.33768 48.78788, 2.33974 48.78719, 2.34009 48.78616, 2.33974 48.78513, 2.33871 48.78479, 2.33734 48.78479, 2.33631 48.78445, 2.33597 48.78342, 2.33631 48.78239, 2.337 48.7817, 2.33734 48.78067, 2.33734 48.7793, 2.337 48.77827, 2.3178 48.7849, 2.32099 48.79376, 2.33906 48.78994))";
-    std::string b = "POLYGON ((2.33768 48.78857, 2.33768 48.78788, 2.33974 48.78719, 2.34009 48.78616, 2.33974 48.78513, 2.33871 48.78479, 2.33734 48.78479, 2.33631 48.78445, 2.3362 48.7841, 2.33562 48.78582, 2.33425 48.78719, 2.33768 48.78857))";
-    std::string exp = "POLYGON ((2.33425 48.78719, 2.33768 48.78857, 2.33768 48.78788, 2.33974 48.78719, 2.34009 48.78616, 2.33974 48.78513, 2.33871 48.78479, 2.33734 48.78479, 2.33631 48.78445, 2.3362 48.78411, 2.33562 48.78582, 2.33425 48.78719))";
-    geomTest(a, b, exp, OverlayNG::INTERSECTION, 100000);
-}
-
-/**
-* Fails because polygon A collapses totally, but one
-* L edge is still labelled with location A:iL due to being located
-* inside original A polygon by PiP test for incomplete edges.
-* That edge is then marked as in-result-area, but result ring can't
-* be formed because ring is incomplete
-*/
-// xtestCollapseAIncompleteRingUnion
-template<>
-template<>
-void object::test<31> ()
-{
-    std::string a = "POLYGON ((0.9 1.7, 1.3 1.4, 2.1 1.4, 2.1 0.9, 1.3 0.9, 0.9 0, 0.9 1.7))";
-    std::string b = "POLYGON ((1 3, 3 3, 3 1, 1.3 0.9, 1 0.4, 1 3))";
-    std::string exp = "POLYGON ((1 2, 1 3, 3 3, 3 1, 2 1, 1 1, 1 2))";
-    geomTest(a, b, exp, OverlayNG::UNION, 1);
-}
-
-// xtestCollapseHoleAlongEdgeOfBIntersection
-template<>
-template<>
-void object::test<32> ()
-{
-    std::string a = "POLYGON ((0 3, 3 3, 3 0, 0 0, 0 3), (1 1.2, 1 1.1, 2.3 1.1, 1 1.2))";
-    std::string b = "POLYGON ((1 1, 2 1, 2 0, 1 0, 1 1))";
-    std::string exp = "POLYGON ((1 1, 2 1, 2 0, 1 0, 1 1))";
-    geomTest(a, b, exp, OverlayNG::INTERSECTION, 1);
-}
-
-// xtestCollapseResultShouldHavePolygonUnion
-template<>
-template<>
-void object::test<33> ()
-{
-    std::string a = "POLYGON ((1 3.3, 1.3 1.4, 3.1 1.4, 3.1 0.9, 1.3 0.9, 1 -0.2, 0.8 1.3, 1 3.3))";
-    std::string b = "POLYGON ((1 2.9, 2.9 2.9, 2.9 1.3, 1.7 1, 1.3 0.9, 1 0.4, 1 2.9))";
-    std::string exp = "POLYGON ((1 1, 1 3, 3 3, 3 1, 2 1, 1 1))";
-    geomTest(a, b, exp, OverlayNG::UNION, 1);
-}
-
-// xtestVerySmallBIntersection
-template<>
-template<>
-void object::test<34> ()
-{
-    std::string a = "POLYGON ((2.526855443750341 48.82324221874807, 2.5258255 48.8235855, 2.5251389 48.8242722, 2.5241089 48.8246155, 2.5254822 48.8246155, 2.5265121 48.8242722, 2.526855443750341 48.82324221874807))";
-    std::string b = "POLYGON ((2.526512100000002 48.824272199999996, 2.5265120999999953 48.8242722, 2.5265121 48.8242722, 2.526512100000002 48.824272199999996))";
-    std::string exp = "POLYGON EMPTY";
-    geomTest(a, b, exp, OverlayNG::INTERSECTION, 100000000);
-}
-
-/**
-* Currently noding is incorrect, producing one 2pt edge which is coincident
-* with a 3-pt edge.  The EdgeMerger doesn't check that merged edges are identical,
-* so merges the 3pt edge into the 2-pt edge
-*/
-// xtestEdgeDisappears
-template<>
-template<>
-void object::test<35> ()
-{
-    std::string a = "LINESTRING (2.1279144 48.8445282, 2.126884443750796 48.84555818124935, 2.1268845 48.8455582, 2.1268845 48.8462448)";
-    std::string b = "LINESTRING EMPTY";
-    std::string exp = "LINESTRING EMPTY";
-    geomTest(a, b, exp, OverlayNG::INTERSECTION, 1000000);
-}
-
-/**
-* Probably due to B collapsing completely and disconnected edges being located incorrectly in B interior.
-* Have seen other cases of this as well.
-* Also - a B edge is marked as a Hole, which is incorrect
-*/
-// xtestBcollapseLocateIssue
-template<>
-template<>
-void object::test<36> ()
-{
-    std::string a = "POLYGON ((2.3442078 48.9331054, 2.3435211 48.9337921, 2.3428345 48.9358521, 2.3428345 48.9372253, 2.3433495 48.9370537, 2.3440361 48.936367, 2.3442078 48.9358521, 2.3442078 48.9331054))";
-    std::string b = "POLYGON ((2.3442078 48.9331054, 2.3435211 48.9337921, 2.3433494499999985 48.934307100000005, 2.3438644 48.9341354, 2.3442078 48.9331055, 2.3442078 48.9331054))";
-    std::string exp = "POLYGON EMPTY";
-    geomTest(a, b, exp, OverlayNG::INTERSECTION, 1000);
-}
-
-/**
-* A component of B collapses completely.
-* Labelling marks a single collapsed edge as B:i.
-* Edge is only connected to two other edges both marked B:e.
-* B:i edge is included in area result edges, and faild because it does not form a ring.
-*
-* Perhaps a fix is to ignore connected single Bi edges which do not form a ring?
-* This may be dangerous since it may hide other labelling problems?
-*
-* FIXED by requiring both endpoints of edge to lie in Interior to be located as i
-*/
-// xtestBcollapseEdgeLabeledInterior
-template<>
-template<>
-void object::test<37> ()
-{
-    std::string a = "POLYGON ((2.384376506250038 48.91765596875102, 2.3840332 48.916626, 2.3840332 48.9138794, 2.3833466 48.9118195, 2.3812866 48.9111328, 2.37854 48.9111328, 2.3764801 48.9118195, 2.3723602 48.9159393, 2.3703003 48.916626, 2.3723602 48.9173126, 2.3737335 48.9186859, 2.3757935 48.9193726, 2.3812866 48.9193726, 2.3833466 48.9186859, 2.384376506250038 48.91765596875102))";
-    std::string b = "MULTIPOLYGON (((2.3751067666731345 48.919143677778855, 2.3757935 48.9193726, 2.3812866 48.9193726, 2.3812866 48.9179993, 2.3809433 48.9169693, 2.3799133 48.916626, 2.3771667 48.916626, 2.3761368 48.9169693, 2.3754501 48.9190292, 2.3751067666731345 48.919143677778855)), ((2.3826108673454116 48.91893115612326, 2.3833466 48.9186859, 2.3840331750033394 48.91799930833141, 2.3830032 48.9183426, 2.3826108673454116 48.91893115612326)))";
-    std::string exp = "POLYGON ((2.375 48.91833333333334, 2.375 48.92, 2.381666666666667 48.92, 2.381666666666667 48.91833333333334, 2.381666666666667 48.916666666666664, 2.38 48.916666666666664, 2.3766666666666665 48.916666666666664, 2.375 48.91833333333334))";
-    geomTest(a, b, exp, OverlayNG::INTERSECTION, 600);
-}
-
-// xtestBcollapseNullEdgeInRingIssue
-template<>
-template<>
-void object::test<38> ()
-{
-    std::string a = "POLYGON ((2.2494507 48.8864136, 2.2484207 48.8867569, 2.2477341 48.8874435, 2.2470474 48.8874435, 2.2463608 48.8853836, 2.2453308 48.8850403, 2.2439575 48.8850403, 2.2429276 48.8853836, 2.2422409 48.8860703, 2.2360611 48.8970566, 2.2504807 48.8956833, 2.2494507 48.8864136))";
-    std::string b = "POLYGON ((2.247734099999997 48.8874435, 2.2467041 48.8877869, 2.2453308 48.8877869, 2.2443008 48.8881302, 2.243957512499544 48.888473487500455, 2.2443008 48.8888168, 2.2453308 48.8891602, 2.2463608 48.8888168, 2.247734099999997 48.8874435))";
-    std::string exp = "POLYGON EMPTY";
-    geomTest(a, b, exp, OverlayNG::INTERSECTION, 200);
-}
-
-// xtestLineUnion
-template<>
-template<>
-void object::test<39> ()
-{
-    std::string a = "LINESTRING (0 0, 1 1)";
-    std::string b = "LINESTRING (1 1, 2 2)";
-    std::string exp = "LINESTRING (0 0, 1 1, 2 2)";
-    geomTest(a, b, exp, OverlayNG::UNION, 1);
-}
-
-// xtestLine2Union
-template<>
-template<>
-void object::test<40> ()
-{
-    std::string a = "LINESTRING (0 0, 1 1, 0 1)";
-    std::string b = "LINESTRING (1 1, 2 2, 3 3)";
-    std::string exp = "MULTILINESTRING ((0 0, 1 1), (0 1, 1 1), (1 1, 2 2, 3 3))";
-    geomTest(a, b, exp, OverlayNG::UNION, 1);
-}
-
-// xtestLine3Union
-template<>
-template<>
-void object::test<41> ()
-{
-    std::string a = "MULTILINESTRING ((0 1, 1 1), (2 2, 2 0))";
-    std::string b = "LINESTRING (0 0, 1 1, 2 2, 3 3)";
-    std::string exp = "MULTILINESTRING ((0 0, 1 1), (0 1, 1 1), (1 1, 2 2), (2 0, 2 2), (2 2, 3 3))";
-    geomTest(a, b, exp, OverlayNG::UNION, 1);
-}
-
-// xtestLine4Union
-template<>
-template<>
-void object::test<42> ()
-{
-    std::string a = "LINESTRING (100 300, 200 300, 200 100, 100 100)";
-    std::string b = "LINESTRING (300 300, 200 300, 200 300, 200 100, 300 100)";
-    std::string exp = "MULTILINESTRING ((200 100, 100 100), (300 300, 200 300), (200 300, 200 100), (200 100, 300 100), (100 300, 200 300))";
-    geomTest(a, b, exp, OverlayNG::UNION, 1);
-}
-
-// xtestLineFigure8Union
-template<>
-template<>
-void object::test<43> ()
-{
-    std::string a = "LINESTRING (5 1, 2 2, 5 3, 2 4, 5 5)";
-    std::string b = "LINESTRING (5 1, 8 2, 5 3, 8 4, 5 5)";
-    std::string exp = "MULTILINESTRING ((5 3, 2 2, 5 1, 8 2, 5 3), (5 3, 2 4, 5 5, 8 4, 5 3))";
-    geomTest(a, b, exp, OverlayNG::UNION, 1);
-}
-
-// xtestLineRingUnion
-template<>
-template<>
-void object::test<44> ()
-{
-    std::string a = "LINESTRING (1 1, 5 5, 9 1)";
-    std::string b = "LINESTRING (1 1, 9 1)";
-    std::string exp = "LINESTRING (1 1, 5 5, 9 1, 1 1)";
-    geomTest(a, b, exp, OverlayNG::UNION, 1);
-}
-
-/**
-* Failure due to B hole collapsing and edges being labeled Exterior.
-* They are coincident with an A hole edge, but because labeled E are not
-* included in Intersection result.
-*/
-// xtestBCollapsedHoleEdgeLabelledExterior
-template<>
-template<>
-void object::test<45> ()
-{
-    std::string a = "POLYGON ((309500 3477900, 309900 3477900, 309900 3477600, 309500 3477600, 309500 3477900), (309741.87561330193 3477680.6737848604, 309745.53718649445 3477677.607851833, 309779.0333599192 3477653.585555199, 309796.8051681937 3477642.143583868, 309741.87561330193 3477680.6737848604))";
-    std::string b = "POLYGON ((309500 3477900, 309900 3477900, 309900 3477600, 309500 3477600, 309500 3477900), (309636.40806633036 3477777.2910157656, 309692.56085444096 3477721.966349552, 309745.53718649445 3477677.607851833, 309779.0333599192 3477653.585555199, 309792.0991800499 3477645.1734264474, 309779.03383125085 3477653.5853248164, 309745.53756275156 3477677.6076231804, 309692.5613257677 3477721.966119165, 309636.40806633036 3477777.2910157656))";
-    std::string exp = "POLYGON ((309500 3477600, 309500 3477900, 309900 3477900, 309900 3477600, 309500 3477600), (309741.88 3477680.67, 309745.54 3477677.61, 309779.03 3477653.59, 309792.1 3477645.17, 309796.81 3477642.14, 309741.88 3477680.67))";
-    geomTest(a, b, exp, OverlayNG::INTERSECTION, 100);
-}
-
-
-
-
-} // namespace tut
diff --git a/tests/unit/operation/overlayng/OverlayNGTest.cpp b/tests/unit/operation/overlayng/OverlayNGTest.cpp
index 8bb91ba..8a2f0a7 100644
--- a/tests/unit/operation/overlayng/OverlayNGTest.cpp
+++ b/tests/unit/operation/overlayng/OverlayNGTest.cpp
@@ -460,4 +460,28 @@ void object::test<36> ()
     testOverlayNoOpt(a, b, exp, OverlayNG::UNION, 1);
 }
 
+// testCollapseTriBoxUnion
+template<>
+template<>
+void object::test<37> ()
+{
+    std::string a = "POLYGON ((1 3.3, 1.3 1.4, 3.1 1.4, 3.1 0.9, 1.3 0.9, 1 -0.2, 0.8 1.3, 1 3.3))";
+    std::string b = "POLYGON ((1 2.9, 2.9 2.9, 2.9 1.3, 1.7 1, 1.3 0.9, 1 0.4, 1 2.9))";
+    std::string exp = "POLYGON EMPTY";
+    testOverlay(a, b, exp, OverlayNG::INTERSECTION, 1);
+}
+
+// infiniteLoop
+template<>
+template<>
+void object::test<38> ()
+{
+    std::string a = "MULTIPOLYGON (((0 7, 9 7, 9 0, 0 0, 0 7), (1 6, 8 6, 8 1, 1 1, 1 6)), ((1.5 5.7, 3.9 1.2, 7 1.3, 5.5 5.5, 1.5 5.7)))";
+    std::string b = "POLYGON ((0 7, 10 7, 10 0, 0 0, 0 7), (7.8 5, 7.5 2, 9.5 2, 10 5, 7.8 5))";
+    std::string exp = "POLYGON ((0 7, 9 7, 9 5, 8 5, 8 6, 6 6, 7 1, 8 1, 8 2, 9 2, 9 0, 0 0, 0 7), (1 6, 1 1, 4 1, 2 6, 1 6))";
+    testOverlay(a, b, exp, OverlayNG::INTERSECTION, 1);
+}
+
+
+
 } // namespace tut
diff --git a/tests/xmltester/tests/general/TestNGOverlayA.xml b/tests/xmltester/tests/general/TestNGOverlayA.xml
index 8443ad9..fc41537 100644
--- a/tests/xmltester/tests/general/TestNGOverlayA.xml
+++ b/tests/xmltester/tests/general/TestNGOverlayA.xml
@@ -258,7 +258,7 @@ Uses a floating precision model.
   </b>
 <test>
   <op name="intersectionNG" arg1="A" arg2="B">
-    LINESTRING (27 27, 27 13, 13 13, 13 27, 27 27)
+    MULTILINESTRING ((27 27, 27 13), (27 13, 13 13), (13 27, 27 27), (13 13, 13 27))
   </op>
 </test>
 <test>
diff --git a/tests/xmltester/tests/general/TestNGOverlayL.xml b/tests/xmltester/tests/general/TestNGOverlayL.xml
index c6b73d9..6e435c3 100644
--- a/tests/xmltester/tests/general/TestNGOverlayL.xml
+++ b/tests/xmltester/tests/general/TestNGOverlayL.xml
@@ -3,6 +3,7 @@
 Covers topological situations with no precision collapse.
 Uses a floating precision model.
 </desc>
+
 <case>
   <desc>LL - same line</desc>
   <a>
@@ -43,10 +44,10 @@ POINT (15 15)
 MULTILINESTRING ((10 10, 15 15), (15 15, 20 20), (10 20, 15 15), (15 15, 20 10))
   </op></test>
 <test>  <op name="differenceNG" arg1="A" arg2="B">
-LINESTRING (10 10, 15 15, 20 20)
+MULTILINESTRING ((10 10, 15 15), (15 15, 20 20))
   </op></test>
 <test>  <op name="differenceNG" arg1="B" arg2="A">
-LINESTRING (10 20, 15 15, 20 10)
+MULTILINESTRING ((10 20, 15 15), (15 15, 20 10))
   </op></test>
 <test>  <op name="symdifferenceNG" arg1="A" arg2="B">
 MULTILINESTRING ((10 10, 15 15), (15 15, 20 20), (10 20, 15 15), (15 15, 20 10))
@@ -65,7 +66,7 @@ LINESTRING (20 20, 30 30)
 POINT (20 20)
   </op></test>
 <test>  <op name="unionNG" arg1="A" arg2="B">
-LINESTRING (10 10, 20 20, 30 30)
+MULTILINESTRING ((20 20, 30 30), (10 10, 20 20))
   </op></test>
 <test>  <op name="differenceNG" arg1="A" arg2="B">
 LINESTRING (10 10, 20 20)
@@ -74,7 +75,7 @@ LINESTRING (10 10, 20 20)
 LINESTRING (20 20, 30 30)
   </op></test>
 <test>  <op name="symdifferenceNG" arg1="A" arg2="B">
-LINESTRING (10 10, 20 20, 30 30)
+MULTILINESTRING ((20 20, 30 30), (10 10, 20 20))
   </op></test>
 </case>
 
@@ -90,7 +91,7 @@ LINESTRING (15 15, 30 30)
 LINESTRING (15 15, 20 20)
   </op></test>
 <test>  <op name="unionNG" arg1="A" arg2="B">
-LINESTRING (10 10, 15 15, 20 20, 30 30)
+MULTILINESTRING ((10 10, 15 15), (15 15, 20 20), (20 20, 30 30))
   </op></test>
 <test>  <op name="differenceNG" arg1="A" arg2="B">
 LINESTRING (10 10, 15 15)
@@ -115,7 +116,7 @@ LINESTRING (13 13, 10 10, 10 20, 20 20, 17 17)
 MULTILINESTRING ((17 17, 20 20), (10 10, 13 13))
   </op></test>
 <test>  <op name="unionNG" arg1="A" arg2="B">
-LINESTRING (17 17, 20 20, 10 20, 10 10, 13 13, 17 17)
+MULTILINESTRING ((17 17, 20 20), (10 10, 10 20, 20 20), (13 13, 17 17), (10 10, 13 13))
   </op></test>
 <test>  <op name="differenceNG" arg1="A" arg2="B">
 LINESTRING (13 13, 17 17)
@@ -149,7 +150,7 @@ MULTILINESTRING ((0 10, 10 10, 20 20), (30 30, 40 30))
 MULTILINESTRING ((20 0, 20 20), (30 30, 30 40))
   </op></test>
 <test>  <op name="symdifferenceNG" arg1="A" arg2="B">
-MULTILINESTRING ((0 10, 10 10, 20 20, 20 0), (40 30, 30 30, 30 40))
+MULTILINESTRING ((0 10, 10 10, 20 20), (20 0, 20 20), (30 30, 30 40), (30 30, 40 30))
   </op></test>
 </case>
 
@@ -168,13 +169,13 @@ LINESTRING (0 0, 3 3)
 MULTILINESTRING ((5 5, 1 9), (0 0, 3 3), (5 5, 10 10), (3 3, 8 2, 5 5), (3 3, 5 5))
   </op></test>
 <test>  <op name="differenceNG" arg1="A" arg2="B">
-LINESTRING (3 3, 5 5, 10 10)
+MULTILINESTRING ((5 5, 10 10), (3 3, 5 5))
   </op></test>
 <test>  <op name="differenceNG" arg1="B" arg2="A">
-LINESTRING (3 3, 8 2, 5 5, 1 9)
+MULTILINESTRING ((5 5, 1 9), (3 3, 8 2, 5 5))
   </op></test>
 <test>  <op name="symdifferenceNG" arg1="A" arg2="B">
-MULTILINESTRING ((5 5, 1 9), (5 5, 10 10), (5 5, 8 2, 3 3, 5 5))
+MULTILINESTRING ((5 5, 1 9), (5 5, 10 10), (3 3, 8 2, 5 5), (3 3, 5 5))
   </op></test>
 </case>
 
@@ -193,10 +194,10 @@ POINT (150 150)
 MULTILINESTRING ((150 150, 200 200, 300 300, 400 200), (100 100, 150 150), (190 110, 150 150), (150 150, 120 180))
   </op></test>
 <test>  <op name="differenceNG" arg1="A" arg2="B">
-LINESTRING (100 100, 150 150, 200 200, 300 300, 400 200)
+MULTILINESTRING ((150 150, 200 200, 300 300, 400 200), (100 100, 150 150))
   </op></test>
 <test>  <op name="differenceNG" arg1="B" arg2="A">
-LINESTRING (190 110, 150 150, 120 180)
+MULTILINESTRING ((190 110, 150 150), (150 150, 120 180))
   </op></test>
 <test>  <op name="symdifferenceNG" arg1="A" arg2="B">
 MULTILINESTRING ((150 150, 200 200, 300 300, 400 200), (100 100, 150 150), (190 110, 150 150), (150 150, 120 180))
diff --git a/tests/xmltester/tests/general/TestNGOverlayLPrec.xml b/tests/xmltester/tests/general/TestNGOverlayLPrec.xml
index ca9b716..99ec16f 100644
--- a/tests/xmltester/tests/general/TestNGOverlayLPrec.xml
+++ b/tests/xmltester/tests/general/TestNGOverlayLPrec.xml
@@ -22,10 +22,10 @@ MULTIPOINT ((2 2), (3 3))
 MULTILINESTRING ((1 1, 2 2), (2 2, 3 3), (3 3, 4 4), (2 2, 4 2), (3 1, 2 2), (3 4, 3 3), (3 3, 1 3))
   </op></test>
 <test>  <op name="differenceSR" arg1="A" arg2="B" arg3="1">
-LINESTRING (1 1, 2 2, 3 3, 4 4)
+MULTILINESTRING ((1 1, 2 2), (2 2, 3 3), (3 3, 4 4))
   </op></test>
 <test>  <op name="differenceSR" arg1="B" arg2="A" arg3="1">
-MULTILINESTRING ((3 1, 2 2, 4 2), (3 4, 3 3, 1 3))
+MULTILINESTRING ((2 2, 4 2), (3 1, 2 2), (3 4, 3 3), (3 3, 1 3))
   </op></test>
 <test>  <op name="symDifferenceSR" arg1="A" arg2="B" arg3="1">
 MULTILINESTRING ((1 1, 2 2), (2 2, 3 3), (3 3, 4 4), (2 2, 4 2), (3 1, 2 2), (3 4, 3 3), (3 3, 1 3))
@@ -41,10 +41,10 @@ LINESTRING (1.1 1.3, 2.6 2.8, 3.8 3.3)
 LINESTRING (0.3 2.8, 1.4 1.9, 2.7 2.6, 2.9 3.8)
   </b>
 <test>  <op name="intersectionSR" arg1="A" arg2="B" arg3="1">
-LINESTRING (1 2, 2 2, 3 3)
+MULTILINESTRING ((1 2, 2 2), (2 2, 3 3))
   </op></test>
 <test>  <op name="unionSR" arg1="A" arg2="B" arg3="1">
-MULTILINESTRING ((1 1, 1 2), (1 2, 2 2, 3 3), (3 3, 4 3), (0 3, 1 2), (3 3, 3 4))
+MULTILINESTRING ((1 1, 1 2), (1 2, 2 2), (2 2, 3 3), (3 3, 4 3), (0 3, 1 2), (3 3, 3 4))
   </op></test>
 <test>  <op name="differenceSR" arg1="A" arg2="B" arg3="1">
 MULTILINESTRING ((1 1, 1 2), (3 3, 4 3))
@@ -53,7 +53,7 @@ MULTILINESTRING ((1 1, 1 2), (3 3, 4 3))
 MULTILINESTRING ((0 3, 1 2), (3 3, 3 4))
   </op></test>
 <test>  <op name="symDifferenceSR" arg1="A" arg2="B" arg3="1">
-MULTILINESTRING ((1 1, 1 2, 0 3), (3 4, 3 3, 4 3))
+MULTILINESTRING ((1 1, 1 2), (3 3, 4 3), (0 3, 1 2), (3 3, 3 4))
   </op></test>
 </case>
 
@@ -66,10 +66,10 @@ LINESTRING (0 0, 1.8 2.3, 1.1 1.1, 3 3)
 LINESTRING (0.7 0.7, 2.8 2.6)
   </b>
 <test>  <op name="intersectionSR" arg1="A" arg2="B" arg3="1">
-LINESTRING (1 1, 2 2, 3 3)
+MULTILINESTRING ((1 1, 2 2), (2 2, 3 3))
   </op></test>
 <test>  <op name="unionSR" arg1="A" arg2="B" arg3="1">
-LINESTRING (0 0, 1 1, 2 2, 3 3)
+MULTILINESTRING ((0 0, 1 1), (1 1, 2 2), (2 2, 3 3))
   </op></test>
 <test>  <op name="differenceSR" arg1="A" arg2="B" arg3="1">
 LINESTRING (0 0, 1 1)
@@ -82,4 +82,29 @@ LINESTRING (0 0, 1 1)
   </op></test>
 </case>
 
+<case>
+  <desc>LL - partial overlap, showing output lines not split at every vertex</desc>
+  <a>
+LINESTRING (0 1, 0.9 1.1, 1.8 1.1, 3.2 0.9, 5 0.7, 6.1 0.7, 7.3 0.6)
+  </a>
+  <b>
+LINESTRING (0 2, 1.1 1.7, 3.7 1.2, 5.4 1.6, 6.1 2.1, 7.2 2.2)
+  </b>
+<test>  <op name="intersectionSR" arg1="A" arg2="B" arg3="1">
+MULTILINESTRING ((4 1, 5 1), (2 1, 3 1), (3 1, 4 1))
+  </op></test>
+<test>  <op name="unionSR" arg1="A" arg2="B" arg3="1">
+MULTILINESTRING ((5 1, 6 1, 7 1), (0 1, 1 1, 2 1), (0 2, 1 2, 2 1), (4 1, 5 1), (2 1, 3 1), (3 1, 4 1), (5 1, 5 2, 6 2, 7 2))
+  </op></test>
+<test>  <op name="differenceSR" arg1="A" arg2="B" arg3="1">
+MULTILINESTRING ((5 1, 6 1, 7 1), (0 1, 1 1, 2 1))
+  </op></test>
+<test>  <op name="differenceSR" arg1="B" arg2="A" arg3="1">
+MULTILINESTRING ((0 2, 1 2, 2 1), (5 1, 5 2, 6 2, 7 2))
+  </op></test>
+<test>  <op name="symDifferenceSR" arg1="A" arg2="B" arg3="1">
+MULTILINESTRING ((5 1, 6 1, 7 1), (0 1, 1 1, 2 1), (0 2, 1 2, 2 1), (5 1, 5 2, 6 2, 7 2))
+  </op></test>
+</case>
+
 </run>
\ No newline at end of file

-----------------------------------------------------------------------

Summary of changes:
 include/geos/index/kdtree/KdNode.h                 |   8 +-
 include/geos/index/kdtree/KdTree.h                 |   6 +-
 include/geos/noding/snapround/HotPixel.h           |  11 +
 include/geos/noding/snapround/HotPixelIndex.h      |  15 +-
 .../snapround/SnapRoundingIntersectionAdder.h      |  27 +-
 include/geos/noding/snapround/SnapRoundingNoder.h  |  71 ++-
 include/geos/operation/overlayng/LineBuilder.h     |   3 +
 include/geos/operation/overlayng/OverlayLabel.h    |   1 -
 src/index/kdtree/KdNode.cpp                        |   4 +-
 src/index/kdtree/KdTree.cpp                        |   6 +-
 src/noding/snapround/HotPixel.cpp                  |  30 +-
 src/noding/snapround/HotPixelIndex.cpp             |  52 +-
 src/noding/snapround/SnapRoundingNoder.cpp         | 146 +++--
 src/operation/overlayng/LineBuilder.cpp            |  29 +-
 src/operation/overlayng/OverlayLabel.cpp           |  18 -
 src/operation/overlayng/OverlayLabeller.cpp        |   1 +
 src/operation/overlayng/OverlayNG.cpp              |   2 +
 tests/unit/Makefile.am                             |   1 -
 tests/unit/geom/EnvelopeTest.cpp                   |   4 +-
 .../noding/snapround/SnapRoundingNoderTest.cpp     |  68 ++-
 .../unit/operation/overlayng/OverlayNGOneTest.cpp  | 587 ---------------------
 tests/unit/operation/overlayng/OverlayNGTest.cpp   |  24 +
 tests/xmltester/tests/general/TestNGOverlayA.xml   |   2 +-
 tests/xmltester/tests/general/TestNGOverlayL.xml   |  25 +-
 .../xmltester/tests/general/TestNGOverlayLPrec.xml |  39 +-
 25 files changed, 438 insertions(+), 742 deletions(-)
 delete mode 100644 tests/unit/operation/overlayng/OverlayNGOneTest.cpp


hooks/post-receive
-- 
GEOS


More information about the geos-commits mailing list