[geos-commits] [SCM] GEOS branch master updated. 64c8540bfd7d0d95adf3efe5807abb0ffcde7ec1

git at osgeo.org git at osgeo.org
Mon Sep 14 15:57:39 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  64c8540bfd7d0d95adf3efe5807abb0ffcde7ec1 (commit)
      from  dfb6d579ad64e769770653987ff9e50d52f2087a (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 64c8540bfd7d0d95adf3efe5807abb0ffcde7ec1
Author: Paul Ramsey <pramsey at cleverelephant.ca>
Date:   Mon Sep 14 15:57:26 2020 -0700

    Allow OverlayNG results to include collapsed edges https://github.com/dr-jts/jts/commit/764bb49cd461686d51d77f743441591b3fe70080
    use OverlayNG in GeometryPrecisionReducer https://github.com/dr-jts/jts/commit/c6f2208dd906f76c1075fcc410d19057382f587e
    Update GEOSGeom_setPrecision to use NG PrecisionReducer
    Add debugging output to overlayng::Edge
    Handle differing cases of changing/retaining factories in precision reduction

diff --git a/capi/geos_ts_c.cpp b/capi/geos_ts_c.cpp
index a6d6fde..413d154 100644
--- a/capi/geos_ts_c.cpp
+++ b/capi/geos_ts_c.cpp
@@ -67,6 +67,7 @@
 #include <geos/operation/overlayng/PrecisionReducer.h>
 #include <geos/operation/overlayng/OverlayNG.h>
 #include <geos/operation/overlayng/OverlayNGSnapIfNeeded.h>
+#include <geos/operation/overlayng/UnaryUnionNG.h>
 #include <geos/operation/intersection/Rectangle.h>
 #include <geos/operation/intersection/RectangleIntersection.h>
 #include <geos/operation/polygonize/Polygonizer.h>
@@ -158,6 +159,7 @@ using geos::operation::buffer::BufferParameters;
 using geos::operation::distance::IndexedFacetDistance;
 using geos::operation::geounion::CascadedPolygonUnion;
 using geos::operation::overlayng::OverlayNG;
+using geos::operation::overlayng::UnaryUnionNG;
 using geos::operation::overlayng::OverlayNGSnapIfNeeded;
 
 using geos::precision::GeometryPrecisionReducer;
@@ -1381,7 +1383,7 @@ extern "C" {
                 pm.reset(new PrecisionModel());
             }
             auto g3 = gridSize != 0 ?
-              OverlayNG::geomunion(g1, pm.get())
+              UnaryUnionNG::Union(g1, *pm)
               :
               OverlayNGSnapIfNeeded::Union(g1);
             g3->setSRID(g1->getSRID());
@@ -2433,7 +2435,7 @@ extern "C" {
         });
     }
 
-    GEOSGeometry*
+    Geometry*
     GEOSGeom_setPrecision_r(GEOSContextHandle_t extHandle, const GEOSGeometry* g,
                             double gridSize, int flags)
     {
@@ -2444,7 +2446,7 @@ extern "C" {
             double cursize = pm->isFloating() ? 0 : 1.0 / pm->getScale();
             std::unique_ptr<PrecisionModel> newpm;
             if(gridSize != 0) {
-                newpm.reset(new PrecisionModel(1.0 / gridSize));
+                newpm.reset(new PrecisionModel(1.0 / std::abs(gridSize)));
             }
             else {
                 newpm.reset(new PrecisionModel());
@@ -2453,19 +2455,12 @@ extern "C" {
             GeometryFactory::Ptr gf =
                 GeometryFactory::create(newpm.get(), g->getSRID());
             if(gridSize != 0 && cursize != gridSize) {
-                // We need to snap the geometry
-                if (flags) {
-                    GeometryPrecisionReducer reducer(*gf);
-                    reducer.setPointwise(flags & GEOS_PREC_NO_TOPO);
-                    reducer.setRemoveCollapsedComponents(!(flags & GEOS_PREC_KEEP_COLLAPSED));
-                    ret = reducer.reduce(*g).release();
-                }
-                else {
-                    // OverlayNG reducer preserves topology and drops collapsed elements
-                    // All it really is, is a call to OverlayNG::geomunion()
-                    auto reducedGeom = geos::operation::overlayng::PrecisionReducer::reducePrecision(g, newpm.get());
-                    ret = reducedGeom.release();
-                }
+                GeometryPrecisionReducer reducer(*gf);
+                reducer.setChangePrecisionModel(true);
+                reducer.setUseAreaReducer(!(flags & GEOS_PREC_NO_TOPO));
+                reducer.setPointwise(flags & GEOS_PREC_NO_TOPO);
+                reducer.setRemoveCollapsedComponents(!(flags & GEOS_PREC_KEEP_COLLAPSED));
+                ret = reducer.reduce(*g).release();
             }
             else {
                 // No need or willing to snap, just change the factory
diff --git a/include/geos/operation/overlayng/Edge.h b/include/geos/operation/overlayng/Edge.h
index 58b94e2..ded8647 100644
--- a/include/geos/operation/overlayng/Edge.h
+++ b/include/geos/operation/overlayng/Edge.h
@@ -121,6 +121,8 @@ public:
         , pts(nullptr)
         {};
 
+    friend std::ostream& operator<<(std::ostream& os, const Edge& e);
+
     static bool isCollapsed(const geom::CoordinateSequence* pts);
 
     // takes ownership of pts from caller
diff --git a/include/geos/operation/overlayng/OverlayLabel.h b/include/geos/operation/overlayng/OverlayLabel.h
index 73077b7..6b7db1d 100644
--- a/include/geos/operation/overlayng/OverlayLabel.h
+++ b/include/geos/operation/overlayng/OverlayLabel.h
@@ -210,6 +210,18 @@ public:
     Location getLineLocation(int index) const;
 
     /**
+    * Tests if a label is a Collapse has location INTERIOR,
+    * to at least one source geometry.
+    */
+    bool isInteriorCollapse() const;
+
+    /**
+    * Tests if a label is a Collapse
+    * and NotPart with location INTERIOR for the other geometry.
+    */
+    bool isCollapseAndNotPartInterior() const;
+
+    /**
     * Tests if a line is in the interior of a source geometry.
     *
     * @param index source geometry
diff --git a/include/geos/operation/overlayng/OverlayNG.h b/include/geos/operation/overlayng/OverlayNG.h
index 96ff5fc..c694517 100644
--- a/include/geos/operation/overlayng/OverlayNG.h
+++ b/include/geos/operation/overlayng/OverlayNG.h
@@ -86,6 +86,13 @@ class GEOS_DLL OverlayNG {
 
 private:
 
+    /**
+    * Indicates whether the old overlay result semantics are used:
+    * - Intersection result can be mixed-dimension
+    * - Results can include lines caused by Area topology collapse
+    */
+    static constexpr bool USE_OLD_RESULT_SEMANTICS = true;
+
     // Members
     const geom::PrecisionModel* pm;
     InputGeometry inputGeom;
@@ -93,6 +100,7 @@ private:
     int opCode;
     noding::Noder* noder;
     bool isOptimized;
+    bool isAreaResultOnly;
     bool isOutputEdges;
     bool isOutputResultEdges;
     bool isOutputNodedEdges;
@@ -126,7 +134,22 @@ public:
     static constexpr int UNION          = overlay::OverlayOp::opUNION;
     static constexpr int DIFFERENCE     = overlay::OverlayOp::opDIFFERENCE;
     static constexpr int SYMDIFFERENCE  = overlay::OverlayOp::opSYMDIFFERENCE;
-    static constexpr bool ALLOW_INT_MIXED_RESULT = true;
+
+
+    /**
+    * Indicates whether intersections are allowed to produce
+    * heterogeneous results including proper boundary touches.
+    * This does not control inclusion of touches along collapses.
+    * True provides the original JTS semantics.
+    */
+    static constexpr bool ALLOW_INT_MIXED_RESULT = USE_OLD_RESULT_SEMANTICS;
+
+    /**
+    * Allow lines created by area topology collapses
+    * to appear in the result.
+    * True provides the original JTS semantics.
+    */
+    static constexpr bool ALLOW_COLLAPSE_LINES = USE_OLD_RESULT_SEMANTICS;
 
     /**
     * Creates an overlay operation on the given geometries,
@@ -140,6 +163,7 @@ public:
         , opCode(p_opCode)
         , noder(nullptr)
         , isOptimized(true)
+        , isAreaResultOnly(false)
         , isOutputEdges(false)
         , isOutputResultEdges(false)
         , isOutputNodedEdges(false)
@@ -157,6 +181,7 @@ public:
         , opCode(p_opCode)
         , noder(nullptr)
         , isOptimized(true)
+        , isAreaResultOnly(false)
         , isOutputEdges(false)
         , isOutputResultEdges(false)
         , isOutputNodedEdges(false)
@@ -192,6 +217,7 @@ public:
     * @param p_isOptimized whether to optimize processing
     */
     void setOptimized(bool p_isOptimized) { isOptimized = p_isOptimized; }
+    void setAreaResultOnly(bool p_areaResultOnly) { isAreaResultOnly = p_areaResultOnly; }
     void setOutputEdges(bool p_isOutputEdges) { isOutputEdges = p_isOutputEdges; }
     void setOutputResultEdges(bool p_isOutputResultEdges) { isOutputResultEdges = p_isOutputResultEdges; }
     void setNoder(noding::Noder* p_noder) { noder = p_noder; }
@@ -305,6 +331,7 @@ public:
     *
     * The input must be a valid geometry.
     * Collections must be homogeneous.
+    * IMPORTANT: You probably want OverlayNGUnaryUnion, not this.
     *
     * @param geom the geometry
     * @param pm the precision model to use
@@ -324,6 +351,10 @@ public:
     *
     * The primary use of this is to support coverage union.
     *
+    * The input must be a valid geometry.
+    * Collections must be homogeneous.
+    * IMPORTANT: You probably want OverlayNGUnaryUnion, not this.
+    *
     * @param geom the geometry to union
     * @param pm the precision model to use (maybe be null)
     * @param noder the noder to use
diff --git a/include/geos/operation/overlayng/PrecisionReducer.h b/include/geos/operation/overlayng/PrecisionReducer.h
index 87e7447..c01bb6e 100644
--- a/include/geos/operation/overlayng/PrecisionReducer.h
+++ b/include/geos/operation/overlayng/PrecisionReducer.h
@@ -62,7 +62,7 @@ public:
 
     PrecisionReducer() {};
 
-    static std::unique_ptr<Geometry> reducePrecision(const Geometry* geom, const PrecisionModel* pm);
+    static std::unique_ptr<Geometry> reducePrecision(const Geometry* geom, const PrecisionModel* pm, bool replacePrecisionModel = false);
 
 
 
diff --git a/include/geos/precision/GeometryPrecisionReducer.h b/include/geos/precision/GeometryPrecisionReducer.h
index 250c626..51c659d 100644
--- a/include/geos/precision/GeometryPrecisionReducer.h
+++ b/include/geos/precision/GeometryPrecisionReducer.h
@@ -38,7 +38,22 @@ namespace precision { // geos.precision
 /** \brief
  * Reduces the precision of a {@link geom::Geometry}
  * according to the supplied {@link geom::PrecisionModel},
- * ensuring that the result is topologically valid.
+ * ensuring that the result is valid (unless specified otherwise).
+ *
+ * By default the reduced result is topologically valid
+ * To ensure this a polygonal geometry is reduced in a topologically valid fashion
+ * (technically, by using snap-rounding).
+ * It can be forced to be reduced pointwise by using setPointwise(boolean).
+ * Note that in this case the result geometry may be invalid.
+ * Linear and point geometry is always reduced pointwise (i.e. without further change to
+ * its topology or stucture), since this does not change validity.
+ *
+ * By default the geometry precision model is not changed.
+ * This can be overridden by usingsetChangePrecisionModel(boolean).
+ *
+ * Normally collapsed components (e.g. lines collapsing to a point)
+ * are not included in the result.
+ * This behavior can be changed by using setRemoveCollapsedComponents(boolean).
  */
 class GEOS_DLL GeometryPrecisionReducer {
 
@@ -50,13 +65,13 @@ private:
     const geom::PrecisionModel& targetPM;
 
     bool removeCollapsed;
-
+    bool changePrecisionModel;
+    bool useAreaReducer;
     bool isPointwise;
 
     std::unique_ptr<geom::Geometry> reducePointwise(const geom::Geometry& geom);
 
-    std::unique_ptr<geom::Geometry> fixPolygonalTopology(
-        const geom::Geometry& geom);
+    std::unique_ptr<geom::Geometry> fixPolygonalTopology(const geom::Geometry& geom);
 
     geom::GeometryFactory::Ptr createFactory(
         const geom::GeometryFactory& oldGF,
@@ -109,11 +124,11 @@ public:
     }
 
     GeometryPrecisionReducer(const geom::PrecisionModel& pm)
-        :
-        newFactory(nullptr),
-        targetPM(pm),
-        removeCollapsed(true),
-        isPointwise(false)
+        : newFactory(nullptr)
+        , targetPM(pm)
+        , removeCollapsed(true)
+        , changePrecisionModel(false)
+        , isPointwise(false)
     {}
 
     /**
@@ -133,7 +148,7 @@ public:
      * being removed completely, or simply being collapsed to an (invalid)
      * Geometry of the same type.
      *
-     * @param remove if <code>true</code> collapsed components will be removed
+     * @param remove if true collapsed components will be removed
      */
     void
     setRemoveCollapsedComponents(bool remove)
@@ -142,6 +157,28 @@ public:
     }
 
     /** \brief
+    * Sets whether the {@link geom::PrecisionModel} of the new reduced Geometry
+    * will be changed to be the {@link geom::PrecisionModel} supplied to
+    * specify the precision reduction.
+    * The default is to not change the precision model
+    *
+    * @param change if true the precision
+    * model of the created Geometry will be the
+    * the precisionModel supplied in the constructor.
+    */
+    void
+    setChangePrecisionModel(bool change)
+    {
+        changePrecisionModel = change;
+    }
+
+    void
+    setUseAreaReducer(bool useAR)
+    {
+        useAreaReducer = useAR;
+    }
+
+    /** \brief
      * Sets whether the precision reduction will be done
      * in pointwise fashion only.
      *
diff --git a/src/geom/HeuristicOverlay.cpp b/src/geom/HeuristicOverlay.cpp
index c1fd19b..0e8bbf2 100644
--- a/src/geom/HeuristicOverlay.cpp
+++ b/src/geom/HeuristicOverlay.cpp
@@ -70,7 +70,7 @@
 
 #include <geos/operation/overlay/snap/GeometrySnapper.h>
 
-#define GEOS_DEBUG_HEURISTICOVERLAY 1
+#define GEOS_DEBUG_HEURISTICOVERLAY 0
 #define GEOS_DEBUG_HEURISTICOVERLAY_PRINT_INVALID 0
 
 
@@ -526,6 +526,8 @@ HeuristicOverlay(const Geometry* g0, const Geometry* g1, int opCode)
 #endif
 
             precision::GeometryPrecisionReducer reducer(*gf);
+            reducer.setUseAreaReducer(false);
+            reducer.setChangePrecisionModel(true);
             GeomPtr rG0(reducer.reduce(*g0));
             GeomPtr rG1(reducer.reduce(*g1));
 
diff --git a/src/operation/overlayng/Edge.cpp b/src/operation/overlayng/Edge.cpp
index 30e3ff2..a45e5e3 100644
--- a/src/operation/overlayng/Edge.cpp
+++ b/src/operation/overlayng/Edge.cpp
@@ -17,9 +17,12 @@
 #include <geos/geom/Dimension.h>
 #include <geos/geom/Location.h>
 #include <geos/geom/Coordinate.h>
+#include <geos/geom/GeometryFactory.h>
+#include <geos/geom/LineString.h>
 #include <geos/geom/CoordinateSequence.h>
 #include <geos/geom/CoordinateSequence.h>
 #include <geos/util/GEOSException.h>
+#include <geos/io/WKBWriter.h>
 
 
 namespace geos {      // geos
@@ -346,49 +349,18 @@ Edge::populateLabel(OverlayLabel &lbl) const
 }
 
 
+/*public friend*/
+std::ostream&
+operator<<(std::ostream& os, const Edge& e)
+{
+    auto gf = GeometryFactory::create();
+    auto cs = e.getCoordinatesRO();
+    auto line = gf->createLineString(cs->clone());
+    io::WKBWriter w;
+    w.writeHEX(*line, os);
+    return os;
+}
 
-// public String toString() {
-
-//   String ptsStr = toStringPts(pts);
-
-//   String aInfo = infoString(0, aDim, aIsHole, aDepthDelta );
-//   String bInfo = infoString(1, bDim, bIsHole, bDepthDelta );
-
-//   return "Edge( " + ptsStr  + " ) "
-//       + aInfo + "/" + bInfo;
-// }
-// public String toLineString() {
-//   return WKTWriter.toLineString(pts);
-// }
-
-// private static String toStringPts(Coordinate[] pts) {
-//   Coordinate orig = pts[0];
-//   Coordinate dest = pts[pts.length - 1];
-//   String dirPtStr = (pts.length > 2)
-//       ? ", " + WKTWriter.format(pts[1])
-//           : "";
-//   String ptsStr = WKTWriter.format(orig)
-//       + dirPtStr
-//       + " .. " + WKTWriter.format(dest);
-//   return ptsStr;
-// }
-
-// public static String infoString(int index, int dim, boolean isHole, int depthDelta) {
-//   return
-//       (index == 0 ? "A:" : "B:")
-//       + OverlayLabel.dimensionSymbol(dim)
-//       + ringRoleSymbol( dim, isHole )
-//       + Integer.toString(depthDelta);  // force to string
-// }
-
-// public static String ringRoleSymbol(int dim, boolean isHole) {
-//   if (hasAreaParent(dim)) return "" + OverlayLabel.ringRoleSymbol(isHole);
-//   return "";
-// }
-
-// private static boolean hasAreaParent(int dim) {
-//   return dim == OverlayLabel.DIM_BOUNDARY || dim == OverlayLabel.DIM_COLLAPSE;
-// }
 
 bool EdgeComparator(const Edge* a, const Edge* b)
 {
diff --git a/src/operation/overlayng/InputGeometry.cpp b/src/operation/overlayng/InputGeometry.cpp
index bc68bda..04d70f3 100644
--- a/src/operation/overlayng/InputGeometry.cpp
+++ b/src/operation/overlayng/InputGeometry.cpp
@@ -91,7 +91,7 @@ InputGeometry::getAreaIndex() const
 bool
 InputGeometry::isLine(int geomIndex) const
 {
-    return geom[geomIndex]->getDimension() == 1;
+    return getDimension(geomIndex) == 1;
 }
 
 /*public*/
diff --git a/src/operation/overlayng/LineBuilder.cpp b/src/operation/overlayng/LineBuilder.cpp
index 3306a04..52fa91e 100644
--- a/src/operation/overlayng/LineBuilder.cpp
+++ b/src/operation/overlayng/LineBuilder.cpp
@@ -68,21 +68,58 @@ bool
 LineBuilder::isResultLine(const OverlayLabel* lbl) const
 {
     /**
-     * An edge which is a boundary of a single geometry cannot form a line result.
-     * This is a short-circuit for area edges if no collapses or lines are present.
-     */
+    * Omit edge which is a boundary of a single geometry
+    * (i.e. not a collapse or line edge as well).
+    * These are only included if part of a result area.
+    * This is a short-circuit for the most common area edge case
+    */
     if (lbl->isBoundarySingleton())
         return false;
 
     /**
-    * Edges which are collapses along boundaries are not output.
+    * Omit edge which is a collapse along a boundary.
     * I.e a result line edge must be from a input line
-    * or two coincident area boundaries.
+    * OR two coincident area boundaries.
+    * This logic is only used if not including collapse lines in result.
     */
-    if (lbl->isBoundaryCollapse())
+    if (! OverlayNG::ALLOW_COLLAPSE_LINES && lbl->isBoundaryCollapse())
+        return false;
+
+    /**
+     * Omit edge which is a collapse interior to its parent area.
+     * (E.g. a narrow gore, or spike off a hole)
+     */
+    if (lbl->isInteriorCollapse())
         return false;
 
+    /**
+    * For ops other than Intersection, omit a line edge
+    * if it is interior to the other area.
+    *
+    * For Intersection, a line edge interior to an area is included.
+    */
+    if (opCode != OverlayNG::INTERSECTION) {
+        /**
+        * Omit collapsed edge in other area interior.
+        */
+        if (lbl->isCollapseAndNotPartInterior())
+            return false;
 
+        /**
+        * If there is a result area, omit line edge inside it.
+        * It is sufficient to check against the input area rather
+        * than the result area,
+        * because if line edges are present then there is only one input area,
+        * and the result area must be the same as the input area.
+        */
+        if (hasResultArea && lbl->isLineInArea(inputAreaIndex))
+            return false;
+    }
+
+    /**
+    * Include line edge formed by touching area boundaries,
+    * if enabled.
+    */
     if (OverlayNG::ALLOW_INT_MIXED_RESULT &&
         opCode == OverlayNG::INTERSECTION &&
         lbl->isBoundaryTouch()) {
@@ -90,21 +127,11 @@ LineBuilder::isResultLine(const OverlayLabel* lbl) const
     }
 
     /**
-    * Skip edges that are inside result area, if there is one.
-    * It is sufficient to check against an input area rather
-    * than the result area, since
-    * if lines are being included then the result area
-    * must be the same as the input area.
-    * This logic relies on the semantic that if both inputs
-    * are areas, lines are only output if there is no
-    * result area.
+    * Finally, determine included line edge
+    * according to overlay op boolean logic.
     */
-    if (hasResultArea && lbl->isLineInArea(inputAreaIndex))
-        return false;
-
     Location aLoc = effectiveLocation(lbl, 0);
     Location bLoc = effectiveLocation(lbl, 1);
-
     bool inResult = OverlayNG::isResultOfOp(opCode, aLoc, bLoc);
     return inResult;
 }
diff --git a/src/operation/overlayng/OverlayLabel.cpp b/src/operation/overlayng/OverlayLabel.cpp
index 385dab3..bd2a83a 100644
--- a/src/operation/overlayng/OverlayLabel.cpp
+++ b/src/operation/overlayng/OverlayLabel.cpp
@@ -254,12 +254,42 @@ OverlayLabel::isHole(int index) const
 }
 
 /*public*/
-bool OverlayLabel::isCollapse(int index) const
+bool
+OverlayLabel::isCollapse(int index) const
 {
     return dimension(index) == DIM_COLLAPSE;
 }
 
 /*public*/
+bool
+OverlayLabel::isInteriorCollapse() const
+{
+    if (aDim == DIM_COLLAPSE && aLocLine == Location::INTERIOR)
+        return true;
+    if (bDim == DIM_COLLAPSE && bLocLine == Location::INTERIOR)
+        return true;
+
+    return false;
+}
+
+/*public*/
+bool
+OverlayLabel::isCollapseAndNotPartInterior() const
+{
+    if (aDim == DIM_COLLAPSE &&
+        bDim == DIM_NOT_PART &&
+        bLocLine == Location::INTERIOR)
+        return true;
+
+    if (bDim == DIM_COLLAPSE &&
+        aDim == DIM_NOT_PART &&
+        aLocLine == Location::INTERIOR)
+        return true;
+
+    return false;
+}
+
+/*public*/
 Location
 OverlayLabel::getLineLocation(int index) const
 {
diff --git a/src/operation/overlayng/OverlayNG.cpp b/src/operation/overlayng/OverlayNG.cpp
index 96da319..eb80103 100644
--- a/src/operation/overlayng/OverlayNG.cpp
+++ b/src/operation/overlayng/OverlayNG.cpp
@@ -88,7 +88,7 @@ OverlayNG::overlay(const Geometry* geom0, const Geometry* geom1,
 
 /*public static*/
 std::unique_ptr<Geometry>
-overlay(const Geometry* geom0, const Geometry* geom1,
+OverlayNG::overlay(const Geometry* geom0, const Geometry* geom1,
         int opCode, const PrecisionModel* pm, noding::Noder* noder)
 {
     OverlayNG ov(geom0, geom1, pm, opCode);
@@ -236,25 +236,27 @@ OverlayNG::extractResult(int p_opCode, OverlayGraph* graph)
     std::vector<std::unique_ptr<Polygon>> resultPolyList = polyBuilder.getPolygons();
     bool hasResultAreaComponents = resultPolyList.size() > 0;
 
-    //--- Build lines
     std::vector<std::unique_ptr<LineString>> resultLineList;
-    bool allowResultLines = ! hasResultAreaComponents || ALLOW_INT_MIXED_RESULT;
-    if(allowResultLines) {
-        LineBuilder lineBuilder(&inputGeom, graph, hasResultAreaComponents, p_opCode, geomFact);
-        resultLineList = lineBuilder.getLines();
-    }
-
-    bool hasResultComponents = hasResultAreaComponents || resultLineList.size() > 0;
-    /**
-     * Operations with point inputs are handled elsewhere.
-     * Only an intersection op can produce point results
-     * from non-point inputs.
-     */
     std::vector<std::unique_ptr<Point>> resultPointList;
-    bool allowResultPoints = ! hasResultComponents || ALLOW_INT_MIXED_RESULT;
-    if (opCode == INTERSECTION && allowResultPoints) {
-        IntersectionPointBuilder pointBuilder(graph, geomFact);
-        resultPointList = pointBuilder.getPoints();
+
+    if (!isAreaResultOnly) {
+        //--- Build lines
+        bool allowResultLines = ! hasResultAreaComponents || ALLOW_INT_MIXED_RESULT;
+        if (allowResultLines) {
+            LineBuilder lineBuilder(&inputGeom, graph, hasResultAreaComponents, p_opCode, geomFact);
+            resultLineList = lineBuilder.getLines();
+        }
+        /**
+         * Operations with point inputs are handled elsewhere.
+         * Only an intersection op can produce point results
+         * from non-point inputs.
+         */
+        bool hasResultComponents = hasResultAreaComponents || resultLineList.size() > 0;
+        bool allowResultPoints = ! hasResultComponents || ALLOW_INT_MIXED_RESULT;
+        if (opCode == INTERSECTION && allowResultPoints) {
+            IntersectionPointBuilder pointBuilder(graph, geomFact);
+            resultPointList = pointBuilder.getPoints();
+        }
     }
 
     if (resultPolyList.size() == 0 &&
diff --git a/src/operation/overlayng/PrecisionReducer.cpp b/src/operation/overlayng/PrecisionReducer.cpp
index 811cbb7..85dbebd 100644
--- a/src/operation/overlayng/PrecisionReducer.cpp
+++ b/src/operation/overlayng/PrecisionReducer.cpp
@@ -24,12 +24,27 @@ namespace overlayng { // geos.operation.overlayng
 
 /*public static*/
 std::unique_ptr<Geometry>
-PrecisionReducer::reducePrecision(const Geometry* geom, const PrecisionModel* pm)
+PrecisionReducer::reducePrecision(const Geometry* geom, const PrecisionModel* pm, bool replacePrecisionModel)
 {
-    auto gf = GeometryFactory::create(pm, geom->getSRID());
-    // OverlayNG(const geom::Geometry* geom0, const geom::Geometry* geom1, const geom::GeometryFactory* p_geomFact, int p_opCode)
-    OverlayNG ov(geom, nullptr, gf.get(), OverlayNG::UNION);
-    return ov.getResult();
+    if (replacePrecisionModel) {
+        auto gf = GeometryFactory::create(pm, geom->getSRID());
+        OverlayNG ov(geom, nullptr, gf.get(), OverlayNG::UNION);
+        /**
+         * Ensure reducing a area only produces polygonal result.
+         * (I.e. collapse lines are not output)
+         */
+        if (geom->getDimension() == 2)
+            ov.setAreaResultOnly(true);
+
+        return ov.getResult();
+    }
+    else {
+        OverlayNG ov(geom, nullptr, pm, OverlayNG::UNION);
+        if (geom->getDimension() == 2)
+            ov.setAreaResultOnly(true);
+
+        return ov.getResult();
+    }
 }
 
 
diff --git a/src/precision/GeometryPrecisionReducer.cpp b/src/precision/GeometryPrecisionReducer.cpp
index e4d824d..86003ad 100644
--- a/src/precision/GeometryPrecisionReducer.cpp
+++ b/src/precision/GeometryPrecisionReducer.cpp
@@ -28,11 +28,11 @@
 #include <geos/geom/GeometryFactory.h>
 #include <geos/geom/LineString.h>
 #include <geos/geom/LinearRing.h>
+#include <geos/operation/overlayng/PrecisionReducer.h>
 
 #include <vector>
 #include <typeinfo>
 
-using namespace std;
 using namespace geos::geom;
 using namespace geos::geom::util;
 
@@ -40,39 +40,42 @@ namespace geos {
 namespace precision { // geos.precision
 
 
-/* private */
-unique_ptr<Geometry>
-GeometryPrecisionReducer::reducePointwise(const Geometry& geom)
-{
-    GeometryEditor geomEdit(newFactory);
-
-    /*
-     * For polygonal geometries, collapses are always removed, in order
-     * to produce correct topology
-     */
-    bool finalRemoveCollapsed = removeCollapsed;
-    if(geom.getDimension() >= 2) {
-        finalRemoveCollapsed = true;
-    }
-
-    PrecisionReducerCoordinateOperation prco(targetPM, finalRemoveCollapsed);
-
-    std::unique_ptr<Geometry> g(geomEdit.edit(&geom, &prco));
+/* public */
+GeometryPrecisionReducer::GeometryPrecisionReducer(const GeometryFactory& changeFactory)
+    :
+    newFactory(&changeFactory),
+    targetPM(*(changeFactory.getPrecisionModel())),
+    removeCollapsed(true),
+    useAreaReducer(false),
+    isPointwise(false)
+{}
 
-    return g;
-}
 
 /* public */
-unique_ptr<Geometry>
+std::unique_ptr<Geometry>
 GeometryPrecisionReducer::reduce(const Geometry& geom)
 {
-    unique_ptr<Geometry> reducePW = reducePointwise(geom);
+    if (useAreaReducer && geom.isPolygonal()) {
+        std::unique_ptr<Geometry> reduced =
+            operation::overlayng::PrecisionReducer::reducePrecision(
+                &geom, &targetPM, changePrecisionModel);
+        // GEOS overlayng::PrecisionReducer::reducePrecision
+        // takes changePrecisionModel directly to avoid extra steps in JTS
+        // if (changePrecisionModel) {
+        //     return changePM(reduced, targetPM);
+        // }
+        return reduced;
+    }
 
-    if(isPointwise) {
+    /**
+     * Process pointwise reduction
+     * (which is only strategy used for linear and point geoms)
+     */
+    std::unique_ptr<Geometry> reducePW = reducePointwise(geom);
+
+    if (isPointwise)
         return reducePW;
-    }
 
-    //TODO: handle GeometryCollections containing polys
     if(!reducePW->isPolygonal()) {
         return reducePW;
     }
@@ -83,30 +86,47 @@ GeometryPrecisionReducer::reduce(const Geometry& geom)
     }
 
     // hack to fix topology.
-    // TODO: implement snap-rounding and use that.
     return fixPolygonalTopology(*reducePW);
-
 }
 
 
-/* public */
-GeometryPrecisionReducer::GeometryPrecisionReducer(const GeometryFactory& changeFactory)
-    :
-    newFactory(&changeFactory),
-    targetPM(*(changeFactory.getPrecisionModel())),
-    removeCollapsed(true),
-    isPointwise(false)
-{}
+/* private */
+std::unique_ptr<Geometry>
+GeometryPrecisionReducer::reducePointwise(const Geometry& geom)
+{
+    std::unique_ptr<GeometryEditor> geomEdit;
+
+    if (changePrecisionModel) {
+        geomEdit.reset(new GeometryEditor(newFactory));
+    }
+    else {
+        geomEdit.reset(new GeometryEditor());
+    }
+
+    /*
+     * For polygonal geometries, collapses are always removed, in order
+     * to produce correct topology
+     */
+    bool finalRemoveCollapsed = removeCollapsed;
+    if(geom.getDimension() >= 2) {
+        finalRemoveCollapsed = true;
+    }
+
+    PrecisionReducerCoordinateOperation prco(targetPM, finalRemoveCollapsed);
+    std::unique_ptr<Geometry> g(geomEdit->edit(&geom, &prco));
+    return g;
+}
+
 
 /* private */
-unique_ptr<Geometry>
-GeometryPrecisionReducer::fixPolygonalTopology(const geom::Geometry& geom)
+std::unique_ptr<Geometry>
+GeometryPrecisionReducer::fixPolygonalTopology(const Geometry& geom)
 {
     /*
      * If precision model was *not* changed, need to flip
      * geometry to targetPM, buffer in that model, then flip back
      */
-    unique_ptr<Geometry> tmp;
+    std::unique_ptr<geom::Geometry> tmp;
     GeometryFactory::Ptr tmpFactory;
 
     const Geometry* geomToBuffer = &geom;
@@ -117,7 +137,7 @@ GeometryPrecisionReducer::fixPolygonalTopology(const geom::Geometry& geom)
         geomToBuffer = tmp.get();
     }
 
-    unique_ptr<Geometry> bufGeom(geomToBuffer->buffer(0));
+    std::unique_ptr<Geometry> bufGeom(geomToBuffer->buffer(0));
 
     if(! newFactory) {
         // a slick way to copy the geometry with the original precision factory
@@ -140,6 +160,8 @@ GeometryPrecisionReducer::createFactory(const GeometryFactory& oldGF,
     return p_newFactory;
 }
 
+
+
 } // namespace geos.precision
 } // namespace geos
 
diff --git a/tests/unit/capi/GEOSGeom_setPrecisionTest.cpp b/tests/unit/capi/GEOSGeom_setPrecisionTest.cpp
index 0f84e21..c11c62b 100644
--- a/tests/unit/capi/GEOSGeom_setPrecisionTest.cpp
+++ b/tests/unit/capi/GEOSGeom_setPrecisionTest.cpp
@@ -21,6 +21,7 @@ struct test_capigeosgeomsetprecision_data : public capitest::utility {
         ensure(g != 0);
         return g;
     }
+
     std::string
     toWKT(GEOSGeometry* g)
     {
@@ -31,9 +32,8 @@ struct test_capigeosgeomsetprecision_data : public capitest::utility {
     }
 
     test_capigeosgeomsetprecision_data()
-        : geom1_(nullptr), geom2_(nullptr), geom3_(nullptr)
+        : geom1_(0), geom2_(0), geom3_(0), wktw_(0)
     {
-        initGEOS(notice, notice);
         wktw_ = GEOSWKTWriter_create();
         GEOSWKTWriter_setTrim(wktw_, 1);
         GEOSWKTWriter_setRoundingPrecision(wktw_, 10);
@@ -44,13 +44,13 @@ struct test_capigeosgeomsetprecision_data : public capitest::utility {
     {
         GEOSGeom_destroy(geom1_);
         GEOSGeom_destroy(geom2_);
-        if(geom3_) {
+        if(geom3_)
             GEOSGeom_destroy(geom3_);
-        }
-        GEOSWKTWriter_destroy(wktw_);
+        if (wktw_)
+            GEOSWKTWriter_destroy(wktw_);
 
-        geom1_ = geom2_ = geom3_ = nullptr;
-        finishGEOS();
+        geom1_ = geom2_ = geom3_ = 0;
+        wktw_ = 0;
     }
 
 };
diff --git a/tests/unit/operation/overlayng/OverlayNGTest.cpp b/tests/unit/operation/overlayng/OverlayNGTest.cpp
index a748208..479597c 100644
--- a/tests/unit/operation/overlayng/OverlayNGTest.cpp
+++ b/tests/unit/operation/overlayng/OverlayNGTest.cpp
@@ -29,11 +29,16 @@ struct test_overlayng_data {
     void
     testOverlay(const std::string& a, const std::string& b, const std::string& expected, int opCode, double scaleFactor)
     {
-        PrecisionModel pm(scaleFactor);
+        std::unique_ptr<PrecisionModel> pm;
+        if (scaleFactor > 0)
+            pm.reset(new PrecisionModel(scaleFactor));
+        else
+            pm.reset(new PrecisionModel());
+
         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::unique_ptr<Geometry> geom_result = OverlayNG::overlay(geom_a.get(), geom_b.get(), opCode, pm.get());
         // 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());
@@ -424,7 +429,7 @@ void object::test<33> ()
 {
     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)";
+    std::string exp = "LINESTRING (8 1, 9 1)";
     testOverlay(a, b, exp, OverlayNG::INTERSECTION, 1);
 }
 
@@ -468,7 +473,7 @@ 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";
+    std::string exp = "MULTILINESTRING ((1 1, 1 0), (1 3, 1 1), (1 1, 2 1), (2 1, 3 1))";
     testOverlay(a, b, exp, OverlayNG::INTERSECTION, 1);
 }
 
@@ -505,5 +510,25 @@ void object::test<40> ()
     testOverlay(a, b, exp, OverlayNG::INTERSECTION, 1);
 }
 
+// testCollapseTriBoxesIntersection
+template<>
+template<>
+void object::test<41> ()
+{
+    std::string a = "MULTIPOLYGON (((1 4, 1 1, 2 1, 2 4, 1 4)), ((9 4, 9 1, 10 1, 10 4, 9 4)))";
+    std::string b = "POLYGON ((0 2, 11 3, 11 2, 0 2))";
+    std::string exp = "GEOMETRYCOLLECTION (LINESTRING (1 2, 2 2), POLYGON ((9 2, 9 3, 10 3, 10 2, 9 2)))";
+    testOverlay(a, b, exp, OverlayNG::INTERSECTION, 1);
+}
+
+template<>
+template<>
+void object::test<42> ()
+{
+    std::string a = "POLYGON ((0 0, 4 0, 4 4, 0 4, 0 0), (1 1, 1 2, 2 1, 1 1), (1 2, 1 3, 2 3, 1 2), (2 3, 3 3, 3 2, 2 3))";
+    std::string b = "POLYGON ((2 1, 3 1, 3 2, 2 1))";
+    std::string exp = "POLYGON ((3 2, 3 1, 2 1, 3 2))";
+    testOverlay(a, b, exp, OverlayNG::INTERSECTION, 0);
+}
 
 } // namespace tut
diff --git a/tests/unit/operation/overlayng/PrecisionReducerTest.cpp b/tests/unit/operation/overlayng/PrecisionReducerTest.cpp
index 4a8b613..3b3f4f7 100644
--- a/tests/unit/operation/overlayng/PrecisionReducerTest.cpp
+++ b/tests/unit/operation/overlayng/PrecisionReducerTest.cpp
@@ -27,11 +27,11 @@ struct test_precisionreducer_data {
     WKTWriter w;
 
     void
-    checkReduce(const std::string& wkt, double scaleFactor, const std::string& wkt_expected)
+    checkReduce(const std::string& wkt, double gridSize, const std::string& wkt_expected)
     {
         std::unique_ptr<Geometry> geom = r.read(wkt);
         std::unique_ptr<Geometry> expected = r.read(wkt_expected);
-        PrecisionModel pm(scaleFactor);
+        PrecisionModel pm(1.0/gridSize);
         std::unique_ptr<Geometry> result = PrecisionReducer::reducePrecision(geom.get(), &pm);
         ensure_equals_geometry(result.get(), expected.get());
     }
@@ -118,8 +118,60 @@ void object::test<8> ()
         1, "POLYGON ((9 1, 1 1, 1 4, 1 9, 9 9, 9 4, 9 1), (6 4, 4 6, 3 4, 6 4))");
 }
 
+// testPolygonBoxEmpty
+template<>
+template<>
+void object::test<9> ()
+{
+    checkReduce("POLYGON ((1 1.4, 7.3 1.4, 7.3 1.2, 1 1.2, 1 1.4))",
+        1, "POLYGON EMPTY");
+}
 
+// testPolygonThinEmpty
+template<>
+template<>
+void object::test<10> ()
+{
+    checkReduce("POLYGON ((1 1.4, 3.05 1.4, 3 4.1, 6 5, 3.2 4, 3.2 1.4, 7.3 1.4, 7.3 1.2, 1 1.2, 1 1.4))",
+        1, "POLYGON EMPTY");
+}
 
+//
+template<>
+template<>
+void object::test<11> ()
+{
+    checkReduce("LINESTRING(-3 6, 9 1)",
+        2, "LINESTRING (-2 6, 10 2)");
+}
+
+// testCollapsedLine
+template<>
+template<>
+void object::test<12> ()
+{
+    checkReduce("LINESTRING(1 1, 1 9, 1.1 1)",
+        1, "LINESTRING (1 1, 1 9)");
+}
+
+// testCollapsedNodedLine
+template<>
+template<>
+void object::test<13> ()
+{
+    checkReduce("LINESTRING(1 1, 3 3, 9 9, 5.1 5, 2.1 2)",
+        1, "MULTILINESTRING ((1 1, 2 2), (2 2, 3 3), (3 3, 5 5), (5 5, 9 9))");
+}
+
+//
+template<>
+template<>
+void object::test<14> ()
+{
+    checkReduce("POLYGON ((2 1, 3 1, 3 2, 2 1))",
+    // checkReduce("POLYGON ((0 0, 4 0, 4 4, 0 4, 0 0), (1 1, 1 2, 2 1, 1 1), (1 2, 1 3, 2 3, 1 2), (2 3, 3 3, 3 2, 2 3))",
+        1.0/10, "POLYGON ((2 1, 3 1, 3 2, 2 1))");
+}
 
 
 
diff --git a/tests/unit/precision/GeometryPrecisionReducerTest.cpp b/tests/unit/precision/GeometryPrecisionReducerTest.cpp
index 27c0093..031d892 100644
--- a/tests/unit/precision/GeometryPrecisionReducerTest.cpp
+++ b/tests/unit/precision/GeometryPrecisionReducerTest.cpp
@@ -4,6 +4,7 @@
 
 // tut
 #include <tut/tut.hpp>
+#include <utility.h>
 // geos
 #include <geos/precision/GeometryPrecisionReducer.h>
 #include <geos/geom/Geometry.h>
@@ -45,6 +46,7 @@ struct test_gpr_data {
         reducerChangePM_(*factory_fixed_)
     {
         reducerKeepCollapse_.setRemoveCollapsedComponents(false);
+        reducerChangePM_.setChangePrecisionModel(true);
     }
 };
 
@@ -68,7 +70,7 @@ void object::test<1>
 
     GeometryPtr result(reducer_.reduce(*g1));
 
-    ensure(result->equalsExact(g2.get()));
+    ensure_equals_geometry(g2.get(), result.get());
     ensure(result->getFactory() == g2->getFactory());
 }
 
@@ -83,7 +85,7 @@ void object::test<2>
 
     GeometryPtr result(reducer_.reduce(*g1));
 
-    ensure(result->equalsExact(g2.get()));
+    ensure_equals_geometry(g2.get(), result.get());
     ensure(result->getFactory() == g2->getFactory());
 }
 
@@ -98,7 +100,7 @@ void object::test<3>
 
     GeometryPtr result(reducer_.reduce(*g1));
 
-    ensure(result->equalsExact(g2.get()));
+    ensure_equals_geometry(g2.get(), result.get());
     ensure(result->getFactory() == g2->getFactory());
 }
 
@@ -117,7 +119,7 @@ void object::test<4>
 
     GeometryPtr result(reducerKeepCollapse_.reduce(*g1));
 
-    ensure(result->equalsExact(g2.get()));
+    ensure_equals_geometry(g2.get(), result.get());
     ensure(result->getFactory() == g2->getFactory());
 }
 
@@ -132,7 +134,7 @@ void object::test<5>
 
     GeometryPtr result(reducer_.reduce(*g1));
 
-    ensure(result->equalsExact(g2.get()));
+    ensure_equals_geometry(g2.get(), result.get());
     ensure(result->getFactory() == g2->getFactory());
 }
 
@@ -147,7 +149,7 @@ void object::test<6>
 
     GeometryPtr result(reducer_.reduce(*g1));
 
-    ensure(result->equalsExact(g2.get()));
+    ensure_equals_geometry(g2.get(), result.get());
     ensure(result->getFactory() == g2->getFactory());
 }
 
@@ -162,7 +164,7 @@ void object::test<7>
 
     GeometryPtr result(reducerKeepCollapse_.reduce(*g1));
 
-    ensure(result->equalsExact(g2.get()));
+    ensure_equals_geometry(g2.get(), result.get());
     ensure(result->getFactory() == g2->getFactory());
 }
 
@@ -177,8 +179,8 @@ void object::test<8>
 
     GeometryPtr result(reducerChangePM_.reduce(*g1));
 
-    ensure(result->equalsExact(g2.get()));
-    ensure(result->getFactory() == factory_fixed_.get());
+    ensure_equals_geometry(g2.get(), result.get());
+    ensure(result->getFactory() != g2->getFactory());
 }
 
 // Test points with changed PM
@@ -192,8 +194,8 @@ void object::test<9>
 
     GeometryPtr result(reducerChangePM_.reduce(*g1));
 
-    ensure(result->equalsExact(g2.get()));
-    ensure(result->getFactory() == factory_fixed_.get());
+    ensure_equals_geometry(g2.get(), result.get());
+    ensure(result->getFactory() != g2->getFactory());
 }
 
 
diff --git a/tests/unit/utility.h b/tests/unit/utility.h
index fc40855..40db8bc 100644
--- a/tests/unit/utility.h
+++ b/tests/unit/utility.h
@@ -156,6 +156,9 @@ ensure_equals_geometry(T const* lhs_in, T const* rhs_in, double tolerance = 0.0)
                   lhs->getNumPoints(), rhs->getNumPoints());
 
     bool areEqual = lhs->equalsExact(rhs.get(), tolerance);
+    if(!areEqual) {
+        std::cout << std::endl << rhs->toText() << std::endl << lhs->toText() << std::endl;
+    }
 
     ensure("coordinates do not match", areEqual);
     // Dispatch to run more specific testes
diff --git a/tests/xmltester/XMLTester.cpp b/tests/xmltester/XMLTester.cpp
index d9567dc..1e2889a 100644
--- a/tests/xmltester/XMLTester.cpp
+++ b/tests/xmltester/XMLTester.cpp
@@ -39,6 +39,7 @@
 #include <geos/operation/overlay/snap/GeometrySnapper.h>
 #include <geos/operation/overlayng/OverlayNG.h>
 #include <geos/operation/overlayng/OverlayNGSnapIfNeeded.h>
+#include <geos/operation/overlayng/UnaryUnionNG.h>
 #include <geos/operation/buffer/BufferBuilder.h>
 #include <geos/operation/buffer/BufferParameters.h>
 #include <geos/operation/buffer/BufferOp.h>
@@ -90,6 +91,7 @@ using namespace geos::operation::linemerge;
 using namespace geos::geom::prep;
 using std::runtime_error;
 using geos::operation::overlayng::OverlayNG;
+using geos::operation::overlayng::UnaryUnionNG;
 using geos::operation::overlayng::OverlayNGSnapIfNeeded;
 
 namespace {
@@ -1182,16 +1184,24 @@ XMLTester::parseTest(const tinyxml2::XMLNode* node)
             GeomPtr gRes(parseGeometry(opRes, "expected"));
             gRes->normalize();
             double precision = 1.0;
+            GeomPtr gRealRes;
 
-            if(opArg3 != "") {
-                precision = std::atof(opArg3.c_str());
+            if (gB) {
+                geom::PrecisionModel precMod(precision);
+                gRealRes = OverlayNG::overlay(gA, gB, OverlayNG::UNION, &precMod);
+                if(opArg3 != "") {
+                    precision = std::atof(opArg3.c_str());
+                }
             }
+            else {
+                geom::PrecisionModel precMod(precision);
 
-            profile.start();
-            geom::PrecisionModel precMod(precision);
-            GeomPtr gRealRes = OverlayNG::overlay(gA, gB, OverlayNG::UNION, &precMod);
-
-            profile.stop();
+                // gRealRes = OverlayNG::geomunion(gA, &precMod);
+                gRealRes = UnaryUnionNG::Union(gA, precMod);
+                if(opArg2 != "") {
+                    precision = std::atof(opArg2.c_str());
+                }
+            }
 
             gRealRes->normalize();
 
diff --git a/tests/xmltester/tests/general/TestNGOverlayAPrec.xml b/tests/xmltester/tests/general/TestNGOverlayAPrec.xml
index 9e99822..e20a8ed 100644
--- a/tests/xmltester/tests/general/TestNGOverlayAPrec.xml
+++ b/tests/xmltester/tests/general/TestNGOverlayAPrec.xml
@@ -4,8 +4,8 @@ Covers topological situations with precision collapse,
 Uses snap-rounding.
 </desc>
 <!--  Precision must be specified for each operation.
-      rather than a global precision model,
-      since that rounds input coordinates -->
+      rather than a global precision model, 
+      since that rounds input coordinates -->  
 
 <case>
   <desc>AA - box-triangle collapse</desc>
@@ -16,7 +16,7 @@ POLYGON ((1 2, 1 1, 9 1, 1 2))
 POLYGON ((9 2, 9 1, 8 1, 8 2, 9 2))
   </b>
 <test>  <op name="intersectionSR" arg1="A" arg2="B" arg3="1">
-POINT (8 1)
+LINESTRING (8 1, 9 1)
   </op></test>
 <test>  <op name="unionSR" arg1="A" arg2="B" arg3="1">
 MULTIPOLYGON (((8 1, 8 2, 9 2, 9 1, 8 1)), ((1 2, 8 1, 1 1, 1 2)))
@@ -41,10 +41,11 @@ 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))
 POLYGON ((1 3, 3 3, 3 1, 1.3 0.9, 1 0.4, 1 3))
   </b>
 <test>  <op name="intersectionSR" arg1="A" arg2="B" arg3="1">
-POLYGON EMPTY
+MULTILINESTRING ((1 2, 1 1), (1 1, 2 1), (1 1, 1 0))
   </op></test>
 <test>  <op name="unionSR" arg1="A" arg2="B" arg3="1">
-POLYGON ((1 2, 1 3, 3 3, 3 1, 2 1, 1 1, 1 2))
+GEOMETRYCOLLECTION (POLYGON ((1 2, 1 3, 3 3, 3 1, 2 1, 1 1, 1 2)), 
+  LINESTRING (1 1, 1 0))
   </op></test>
 <test>  <op name="differenceSR" arg1="A" arg2="B" arg3="1">
 POLYGON EMPTY
@@ -66,10 +67,14 @@ 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))
 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))
   </b>
 <test>  <op name="intersectionSR" arg1="A" arg2="B" arg3="1">
-POLYGON EMPTY
+MULTILINESTRING ((1 1, 2 1), 
+  (1 1, 1 0), 
+  (1 3, 1 1), 
+  (2 1, 3 1))
   </op></test>
 <test>  <op name="unionSR" arg1="A" arg2="B" arg3="1">
-POLYGON ((1 1, 1 3, 3 3, 3 1, 2 1, 1 1))
+GEOMETRYCOLLECTION (POLYGON ((1 1, 1 3, 3 3, 3 1, 2 1, 1 1)), 
+  LINESTRING (1 1, 1 0))
   </op></test>
 <test>  <op name="differenceSR" arg1="A" arg2="B" arg3="1">
 POLYGON EMPTY
@@ -166,21 +171,31 @@ 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,
 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))
   </b>
 <test>  <op name="intersectionSR" arg1="A" arg2="B" arg3="1">
-GEOMETRYCOLLECTION (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)),
+GEOMETRYCOLLECTION (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)), 
   LINESTRING (8 5, 8 2))
   </op></test>
 <test>  <op name="unionSR" arg1="A" arg2="B" arg3="1">
-POLYGON ((0 7, 9 7, 10 7, 10 5, 9 5, 9 2, 10 2, 10 0, 9 0, 0 0, 0 7))
+GEOMETRYCOLLECTION (POLYGON ((0 7, 9 7, 10 7, 10 5, 9 5, 9 2, 10 2, 10 0, 9 0, 0 0, 0 7)), 
+  LINESTRING (10 5, 10 2))
   </op></test>
 <test>  <op name="differenceSR" arg1="A" arg2="B" arg3="1">
 POLYGON ((8 2, 8 5, 9 5, 9 2, 8 2))
   </op></test>
 <test>  <op name="differenceSR" arg1="B" arg2="A" arg3="1">
-MULTIPOLYGON (((2 6, 4 1, 1 1, 1 6, 2 6)), ((10 7, 10 5, 9 5, 9 7, 10 7)), ((7 1, 6 6, 8 6, 8 5, 8 2, 8 1, 7 1)), ((9 2, 10 2, 10 0, 9 0, 9 2)))
+GEOMETRYCOLLECTION (POLYGON ((2 6, 4 1, 1 1, 1 6, 2 6)), 
+  POLYGON ((9 2, 10 2, 10 0, 9 0, 9 2)), 
+  POLYGON ((10 7, 10 5, 9 5, 9 7, 10 7)), 
+  POLYGON ((8 6, 8 5, 8 2, 8 1, 7 1, 6 6, 8 6)), 
+  LINESTRING (10 5, 10 2))
   </op></test>
 <test>  <op name="symDifferenceSR" arg1="A" arg2="B" arg3="1">
-MULTIPOLYGON (((2 6, 4 1, 1 1, 1 6, 2 6)), ((10 7, 10 5, 9 5, 9 7, 10 7)), ((7 1, 6 6, 8 6, 8 5, 9 5, 9 2, 8 2, 8 1, 7 1)), ((9 2, 10 2, 10 0, 9 0, 9 2)))  </op></test>
+GEOMETRYCOLLECTION (POLYGON ((2 6, 4 1, 1 1, 1 6, 2 6)), 
+  POLYGON ((9 2, 10 2, 10 0, 9 0, 9 2)), 
+  POLYGON ((10 7, 10 5, 9 5, 9 7, 10 7)), 
+  POLYGON ((8 6, 8 5, 9 5, 9 2, 8 2, 8 1, 7 1, 6 6, 8 6)), 
+  LINESTRING (10 5, 10 2))
+  </op></test>
 </case>
 
 <case>
@@ -283,4 +298,29 @@ MULTIPOLYGON (((0 1, 0 3, 1 3, 1 1, 0 1)), ((4 4, 4 1, 3 1, 3 3, 1 3, 1 4, 4 4))
   </op></test>
 </case>
 
-</run>
+<case>
+  <desc>AA - partially overlapping spikes collapsing to lines</desc>
+  <a>
+POLYGON ((1 1, 1 4, 3 4, 3 1.3, 7 1, 1 1))
+  </a>
+  <b>
+POLYGON ((8 4, 9 4, 9 1, 4 1, 8 1.3, 8 4))
+  </b>
+<test>  <op name="intersectionSR" arg1="A" arg2="B" arg3="1">
+MULTILINESTRING ((6 1, 7 1), (4 1, 6 1))
+  </op></test>
+<test>  <op name="unionSR" arg1="A" arg2="B" arg3="1">
+GEOMETRYCOLLECTION (POLYGON ((9 4, 9 1, 8 1, 8 4, 9 4)), POLYGON ((1 4, 3 4, 3 1, 1 1, 1 4)), LINESTRING (8 1, 7 1), LINESTRING (6 1, 7 1), LINESTRING (3 1, 4 1), LINESTRING (4 1, 6 1))
+  </op></test>
+<test>  <op name="differenceSR" arg1="A" arg2="B" arg3="1">
+GEOMETRYCOLLECTION (POLYGON ((1 4, 3 4, 3 1, 1 1, 1 4)), LINESTRING (3 1, 4 1))
+  </op></test>
+<test>  <op name="differenceSR" arg1="B" arg2="A" arg3="1">
+GEOMETRYCOLLECTION (POLYGON ((9 4, 9 1, 8 1, 8 4, 9 4)), LINESTRING (8 1, 7 1))
+  </op></test>
+<test>  <op name="symDifferenceSR" arg1="A" arg2="B" arg3="1">
+GEOMETRYCOLLECTION (POLYGON ((9 4, 9 1, 8 1, 8 4, 9 4)), POLYGON ((1 4, 3 4, 3 1, 1 1, 1 4)), LINESTRING (8 1, 7 1), LINESTRING (3 1, 4 1))
+  </op></test>
+</case>
+
+</run>
\ No newline at end of file

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

Summary of changes:
 capi/geos_ts_c.cpp                                 |  27 +++---
 include/geos/operation/overlayng/Edge.h            |   2 +
 include/geos/operation/overlayng/OverlayLabel.h    |  12 +++
 include/geos/operation/overlayng/OverlayNG.h       |  33 ++++++-
 .../geos/operation/overlayng/PrecisionReducer.h    |   2 +-
 include/geos/precision/GeometryPrecisionReducer.h  |  57 ++++++++++--
 src/geom/HeuristicOverlay.cpp                      |   4 +-
 src/operation/overlayng/Edge.cpp                   |  56 +++--------
 src/operation/overlayng/InputGeometry.cpp          |   2 +-
 src/operation/overlayng/LineBuilder.cpp            |  63 +++++++++----
 src/operation/overlayng/OverlayLabel.cpp           |  32 ++++++-
 src/operation/overlayng/OverlayNG.cpp              |  38 ++++----
 src/operation/overlayng/PrecisionReducer.cpp       |  25 ++++-
 src/precision/GeometryPrecisionReducer.cpp         | 102 +++++++++++++--------
 tests/unit/capi/GEOSGeom_setPrecisionTest.cpp      |  14 +--
 tests/unit/operation/overlayng/OverlayNGTest.cpp   |  33 ++++++-
 .../operation/overlayng/PrecisionReducerTest.cpp   |  56 ++++++++++-
 .../precision/GeometryPrecisionReducerTest.cpp     |  24 ++---
 tests/unit/utility.h                               |   3 +
 tests/xmltester/XMLTester.cpp                      |  24 +++--
 .../xmltester/tests/general/TestNGOverlayAPrec.xml |  66 ++++++++++---
 21 files changed, 477 insertions(+), 198 deletions(-)


hooks/post-receive
-- 
GEOS


More information about the geos-commits mailing list