Skip to content

Commit

Permalink
Port dr-jts/jts@ea13dc3 Switch to outputting all components for inter…
Browse files Browse the repository at this point in the history
…section op
  • Loading branch information
pramsey committed Aug 7, 2020
1 parent b3cea2a commit 8e340a7
Show file tree
Hide file tree
Showing 12 changed files with 123 additions and 84 deletions.
10 changes: 1 addition & 9 deletions include/geos/operation/overlayng/LineBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,12 +80,6 @@ class GEOS_DLL LineBuilder {

void markResultLines();

/**
* If the edge linework is already in the result,
* this edge does not need to be included as a line.
*/
bool isInResult(OverlayEdge* edge) const;

/**
* Checks if the topology indicated by an edge label
* determines that this edge should be part of a result line.
Expand All @@ -106,7 +100,7 @@ class GEOS_DLL LineBuilder {
* (For instance, the intersection of line edge and a collapsed boundary
* is included in the result).
*/
geom::Location effectiveLocation(int geomIndex, const OverlayLabel* lbl) const;
geom::Location effectiveLocation(const OverlayLabel* lbl, int geomIndex) const;

void addResultLines();
void addResultLinesMerged();
Expand Down Expand Up @@ -162,8 +156,6 @@ class GEOS_DLL LineBuilder {

std::vector<std::unique_ptr<geom::LineString>> getLines();



};


Expand Down
2 changes: 2 additions & 0 deletions include/geos/operation/overlayng/OverlayEdge.h
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,8 @@ class GEOS_DLL OverlayEdge : public edgegraph::HalfEdge {

bool isInResult() const;

bool isInResultEither() const;

void setNextResult(OverlayEdge* e);

OverlayEdge* nextResult() const;
Expand Down
81 changes: 40 additions & 41 deletions include/geos/operation/overlayng/OverlayLabel.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,50 +43,43 @@ namespace overlayng { // geos.operation.overlayng
* of the originating ring (which allows
* determination of the edge role in collapse cases).
*
* For each input geometry, the label indicates that an edge is in one of the following states
* (denoted by the <code>dim</code> field).
* For each input geometry, the label indicates that an edge
* is in one of the following states (identified by the "dim" field).
* Each state has some additional information about the edge.
*
* <li>A <b>Boundary</b> edge of an input Area (polygon)
* <ul>
* <li><code>dim</code> = DIM_BOUNDARY</li>
* <li><code>locLeft, locRight</code> : the locations of the edge sides for the input Area</li>
* <li><code>isHole</code> : whether the
* edge was in a shell or a hole</li>
* </ul>
* </li>
* <li>A <b>Collapsed</b> edge of an input Area
* (which had two or more parent edges)
* <ul>
* <li><code>dim</code> = DIM_COLLAPSE</li>
* <li><code>locLine</code> : the location of the
* edge relative to the input Area</li>
* <li><code>isHole</code> : whether some
* contributing edge was in a shell (<code>false</code>),
* or otherwise that all were in holes</li> (<code>true</code>)
* </ul>
* </li>
* <li>An edge from an input <b>Line</b>
* <ul>
* <li><code>dim</code> = DIM_LINE</li>
* <li><code>locLine</code> : initialized to LOC_UNKNOWN,
* to simplify logic.</li>
* </ul>
* </li>
* <li>An edge which is <b>Not Part</b> of an input geometry
* (and thus must be part of the other geometry).
* <ul>
* <li><code>dim</code> = NOT_PART</li>
* </ul>
* </li>
* </ul>
* * A Boundary edge of an input Area (polygon)
*
* * dim = DIM_BOUNDARY
* * locLeft, locRight : the locations of the edge sides for the input Area
* * isHole : whether the edge was in a shell or a hole
*
* * A Collapsed edge of an input Area
* (which had two or more parent edges)
*
* * dim = DIM_COLLAPSE
* * locLine : the location of the
* edge relative to the input Area
* * isHole : whether some
* contributing edge was in a shell (false),
* or otherwise that all were in holes (true)
*
* * An edge from an input Line
*
* * dim = DIM_LINE
* * locLine : initialized to LOC_UNKNOWN, to simplify logic.
*
* * An edge which is Not Part of an input geometry
* (and thus must be part of the other geometry).
*
* * dim = NOT_PART
*
* Note that:
* <ul>
* <li>an edge cannot be both a Collapse edge and a Line edge in the same input geometry,
* because each input geometry must be homogeneous.
* <li>an edge may be an Boundary edge in one input geometry
* and a Line or Collapse edge in the other input.
* </ul>
*
* * an edge cannot be both a Collapse edge and a Line edge in the same input geometry,
* because each input geometry must be homogeneous.
* * an edge may be an Boundary edge in one input geometry
* and a Line or Collapse edge in the other input.
*
* @author Martin Davis
*/

Expand Down Expand Up @@ -191,6 +184,12 @@ class GEOS_DLL OverlayLabel {
* @return true if the label is for a collapse coincident with a boundary
*/
bool isBoundaryCollapse() const;

/**
* Tests if a label is for an edge where two
* area touch along their boundary.
*/
bool isBoundaryTouch() const;
bool isBoundary(int index) const;
bool isLineLocationUnknown(int index) const;

Expand Down
1 change: 1 addition & 0 deletions include/geos/operation/overlayng/OverlayNG.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ class GEOS_DLL OverlayNG {
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_INT_RESULT = true;

/**
* Creates an overlay operation on the given geometries,
Expand Down
36 changes: 22 additions & 14 deletions src/operation/overlayng/LineBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,34 +47,41 @@ LineBuilder::markResultLines()
{
std::vector<OverlayEdge*>& edges = graph->getEdges();
for (OverlayEdge* edge : edges) {
if (isInResult(edge))
/**
* If the edge linework is already marked as in the result,
* it is not included as a line.
* This occurs when an edge either is in a result area
* or has already been included as a line.
*/
if (edge->isInResultEither()) {
continue;
}
if (isResultLine(edge->getLabel())) {
edge->markInResultLine();
}
}
}

/*private*/
bool
LineBuilder::isInResult(OverlayEdge* edge) const
{
return edge->isInResult() || edge->symOE()->isInResult();
}

/*private*/
bool
LineBuilder::isResultLine(const OverlayLabel* lbl) const
{
/**
* Edges which are just collapses along boundaries
* are not output.
* In other words, an edge must be from a source line
* or two (coincident) area boundaries.
* Edges which are collapses along boundaries are not output.
* I.e a result line edge must be from a input line
* or two coincident area boundaries.
*/
if (lbl->isBoundaryCollapse())
return false;


if (OverlayNG::ALLOW_INT_MIXED_INT_RESULT &&
opCode == OverlayNG::INTERSECTION &&
lbl->isBoundaryTouch()) {
return true;
}

/**
* Skip edges that are inside result area, if there is one.
* It is sufficient to check against an input area rather
Expand All @@ -88,16 +95,16 @@ LineBuilder::isResultLine(const OverlayLabel* lbl) const
if (hasResultArea && lbl->isLineInArea(inputAreaIndex))
return false;

Location aLoc = effectiveLocation(0, lbl);
Location bLoc = effectiveLocation(1, lbl);
Location aLoc = effectiveLocation(lbl, 0);
Location bLoc = effectiveLocation(lbl, 1);

bool inResult = OverlayNG::isResultOfOp(opCode, aLoc, bLoc);
return inResult;
}

/*private*/
Location
LineBuilder::effectiveLocation(int geomIndex, const OverlayLabel* lbl) const
LineBuilder::effectiveLocation(const OverlayLabel* lbl, int geomIndex) const
{
if (lbl->isCollapse(geomIndex)) {
return Location::INTERIOR;
Expand All @@ -108,6 +115,7 @@ LineBuilder::effectiveLocation(int geomIndex, const OverlayLabel* lbl) const
return lbl->getLineLocation(geomIndex);
}


/*private*/
void
LineBuilder::addResultLinesMerged()
Expand Down
7 changes: 7 additions & 0 deletions src/operation/overlayng/OverlayEdge.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,13 @@ OverlayEdge::isInResultAreaBoth() const
return m_isInResultArea && symOE()->m_isInResultArea;
}

/*public*/
bool
OverlayEdge::isInResultEither() const
{
return isInResult() || symOE()->isInResult();
}

/*public*/
void
OverlayEdge::unmarkFromResultAreaBoth()
Expand Down
8 changes: 8 additions & 0 deletions src/operation/overlayng/OverlayLabel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,14 @@ OverlayLabel::isBoundaryCollapse() const
return ! isBoundaryBoth();
}

/*public*/
bool
OverlayLabel::isBoundaryTouch() const
{
return isBoundaryBoth() &&
getLocation(0, Position::RIGHT, true) != getLocation(1, Position::RIGHT, true);
}

/*public*/
bool
OverlayLabel::isBoundary(int index) const
Expand Down
11 changes: 7 additions & 4 deletions src/operation/overlayng/OverlayNG.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -229,19 +229,22 @@ OverlayNG::extractResult(int p_opCode, OverlayGraph* graph)
bool hasResultComponents = resultPolyList.size() > 0;

//--- Build lines
bool allowMixedIntResult = ! hasResultComponents || ALLOW_INT_MIXED_INT_RESULT;
std::vector<std::unique_ptr<LineString>> resultLineList;
if (p_opCode != INTERSECTION || ! hasResultComponents) {
if (p_opCode != INTERSECTION || allowMixedIntResult) {
LineBuilder lineBuilder(&inputGeom, graph, hasResultComponents, p_opCode, geomFact);
resultLineList = lineBuilder.getLines();
hasResultComponents = resultLineList.size() > 0;
}

hasResultComponents = hasResultComponents || resultLineList.size() > 0;
/**
* Since operations with point inputs are handled elsewhere,
* this only handles the case where non-point inputs
* intersect in points ONLY.
* intersect in points.
*/
std::vector<std::unique_ptr<Point>> resultPointList;
if (opCode == INTERSECTION && ! hasResultComponents) {
allowMixedIntResult = ! hasResultComponents || ALLOW_INT_MIXED_INT_RESULT;
if (opCode == INTERSECTION && allowMixedIntResult) {
IntersectionPointBuilder pointBuilder(graph, geomFact);
resultPointList = pointBuilder.getPoints();
}
Expand Down
25 changes: 19 additions & 6 deletions tests/unit/operation/overlayng/OverlayNGTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -195,14 +195,15 @@ void object::test<12> ()
testOverlay(a, b, exp, OverlayNG::INTERSECTION, 1);
}

// testPolygoPolygonWithLineTouchIntersection
// testAreaLineIntersection
template<>
template<>
void object::test<13> ()
{
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))";
// std::string exp = "POLYGON ((220 200, 240 200, 240 180, 220 180, 220 200))";
std::string exp = "GEOMETRYCOLLECTION (LINESTRING (280 180, 300 180), LINESTRING (300 160, 300 180), POLYGON ((220 180, 220 200, 240 200, 240 180, 220 180)))";
testOverlay(a, b, exp, OverlayNG::INTERSECTION, 1);
}

Expand Down Expand Up @@ -471,17 +472,29 @@ void object::test<37> ()
testOverlay(a, b, exp, OverlayNG::INTERSECTION, 1);
}

// infiniteLoop
// testAreaLinePointIntersection
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))";
std::string a = "POLYGON ((100 100, 200 100, 200 150, 250 100, 300 100, 300 150, 350 100, 350 200, 100 200, 100 100))";
std::string b = "POLYGON ((100 140, 170 140, 200 100, 400 100, 400 30, 100 30, 100 140))";
std::string exp = "GEOMETRYCOLLECTION (POINT (350 100), LINESTRING (250 100, 300 100), POLYGON ((100 100, 100 140, 170 140, 200 100, 100 100)))";
testOverlay(a, b, exp, OverlayNG::INTERSECTION, 1);
}

// testPolyPolyTouchIntersection
template<>
template<>
void object::test<39> ()
{
std::string a = "POLYGON ((300 0, 100 0, 100 100, 300 100, 300 0))";
std::string b = "POLYGON ((100 200, 300 200, 300 100, 200 100, 200 0, 100 0, 100 200))";
std::string exp = "GEOMETRYCOLLECTION (LINESTRING (200 100, 300 100), POLYGON ((200 0, 100 0, 100 100, 200 100, 200 0)))";
testOverlay(a, b, exp, OverlayNG::INTERSECTION, 1);
}




} // namespace tut
12 changes: 8 additions & 4 deletions tests/xmltester/tests/general/TestNGOverlayA.xml
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ Uses a floating precision model.
</b>
<test>
<op name="intersectionNG" arg1="A" arg2="B">
LINESTRING (30 25, 30 20)
GEOMETRYCOLLECTION (LINESTRING (30 25, 30 20), POINT (30 15))
</op>
</test>
<test>
Expand Down Expand Up @@ -163,7 +163,8 @@ Uses a floating precision model.
</b>
<test>
<op name="intersectionNG" arg1="A" arg2="B">
POLYGON ((30 25, 30 20, 25 25, 30 25))
GEOMETRYCOLLECTION (POLYGON ((25 25, 30 25, 30 20, 25 25)),
LINESTRING (30 15, 30 10))
</op>
</test>
<test>
Expand Down Expand Up @@ -400,7 +401,7 @@ POLYGON ((135 176, 180 176, 180 130, 135 130, 135 176))
<op name="symdifferenceNG" arg1="A" arg2="B">
MULTIPOLYGON(
(
(20 340, 330 380, 50 40, 28 260, 140 220, 210 320, 140 270, 27 270, 20 340)),
(20 340, 330 380, 50 40, 28 260, 140 220, 210 320, 140 270, 27 270, 20 340)),
(
(27 270, 28 260, 0 270, 27 270)))
</op>
Expand Down Expand Up @@ -479,7 +480,10 @@ POLYGON ((13 27, 27 27, 27 13, 13 13, 13 27), (15 25, 20 25, 20 20, 15 20, 15 25
</b>
<test>
<op name="intersectionNG" arg1="A" arg2="B">
MULTIPOLYGON (((22 22, 27 22, 27 20, 20 20, 20 25, 20 27, 22 27, 22 22)), ((15 13, 15 15, 13 15, 13 20, 15 20, 20 20, 20 13, 15 13)))
GEOMETRYCOLLECTION (POLYGON ((22 22, 27 22, 27 20, 20 20, 20 25, 20 27, 22 27, 22 22)),
POLYGON ((15 20, 20 20, 20 13, 15 13, 15 15, 13 15, 13 20, 15 20)),
LINESTRING (22 27, 27 27),
LINESTRING (27 27, 27 22))
</op>
</test>
<test>
Expand Down
Loading

0 comments on commit 8e340a7

Please sign in to comment.