Skip to content

Commit

Permalink
Fix OverlayNG line ordering
Browse files Browse the repository at this point in the history
  • Loading branch information
dr-jts committed Jan 25, 2021
1 parent 4f5e533 commit 701e490
Show file tree
Hide file tree
Showing 6 changed files with 86 additions and 52 deletions.
19 changes: 5 additions & 14 deletions include/geos/operation/overlayng/EdgeMerger.h
Expand Up @@ -59,32 +59,23 @@ namespace overlayng { // geos.operation.overlayng
* This ensures that the overlay output line direction will be as consistent
* as possible with input lines.
*
* The merger also preserves the order of the edges in the input.
* This means that for polygon-line overlay
* the result lines will be in the same order as in the input
* (possibly with multiple result lines for a single input line).
*
* @author mdavis
*
*/
class GEOS_DLL EdgeMerger {

private:


// Members
std::vector<Edge*>& edges;
std::map<EdgeKey, Edge*> edgeMap;

public:

// Methods
EdgeMerger(std::vector<Edge*>& p_edges);

static std::vector<Edge*> merge(std::vector<Edge*>& edges);

std::vector<Edge*> merge();


};


} // namespace geos.operation.overlayng
} // namespace geos.operation
} // namespace geos

19 changes: 2 additions & 17 deletions src/operation/overlayng/EdgeMerger.cpp
Expand Up @@ -23,30 +23,20 @@ namespace geos { // geos
namespace operation { // geos.operation
namespace overlayng { // geos.operation.overlayng

EdgeMerger::EdgeMerger(std::vector<Edge*>& p_edges)
: edges(p_edges) {}

/*public static */
std::vector<Edge*>
EdgeMerger::merge(std::vector<Edge*>& edges)
{
EdgeMerger merger(edges);
return merger.merge();
}


/*public static */
std::vector<Edge*>
EdgeMerger::merge()
{
std::vector<Edge*> mergedEdges;
std::map<EdgeKey, Edge*> edgeMap;

for (Edge* edge : edges) {
EdgeKey edgeKey(edge);
auto it = edgeMap.find(edgeKey);
if (it == edgeMap.end()) {
// this is the first (and maybe only) edge for this line
edgeMap[edgeKey] = edge;
mergedEdges.push_back(edge);
//Debug.println("edge added: " + edge);
//Debug.println(edge.toLineString());
}
Expand All @@ -69,11 +59,6 @@ EdgeMerger::merge()
//Debug.println(edge.toLineString());
}
}

// copy map values into return vector
for (auto it: edgeMap) {
mergedEdges.push_back(it.second);
}
return mergedEdges;
}

Expand Down
25 changes: 6 additions & 19 deletions tests/unit/index/chain/MonotoneChainBuilderTest.cpp
Expand Up @@ -2,6 +2,7 @@
// Test Suite for geos::index::chain::MonotoneChainBuilder class.

#include <tut/tut.hpp>
#include <utility.h>
// geos
#include <geos/io/WKTReader.h>
#include <geos/io/WKTWriter.h>
Expand Down Expand Up @@ -46,28 +47,14 @@ void object::test<1>
std::string wkt2("POLYGON((0.1 0.1, 4.0 0.1, 4.0 1.9, 0.1 1.9, 0.1 0.1))");
std::unique_ptr<geos::geom::Geometry> g2(wktreader.read(wkt2));

// POLYGON ((2.0 1.9, 4.0 1.9, 4.0 0.1, 2.0 0.1, 2.0 1.9))
double x[5] = {2.0, 4.0, 4.0, 2.0, 2.0};
double y[5] = {1.9, 1.9, 0.1, 0.1, 1.9};
auto result = g2->difference(g1.get());
result->normalize();

auto g3 = g2->difference(g1.get());
auto cs = g3->getCoordinates();

// std::string wkt3 = wktwriter.write(g3.get());
// std::cout << wkt3 << std::endl;

for (int i = 0; i < 5; ++i)
{
auto csx = cs->getOrdinate(i, 0);
auto csy = cs->getOrdinate(i, 1);
// std::cout << csx << ", " << csy << std::endl;
// std::cout << x[i] << ", " << y[i] << std::endl;
ensure_equals("x", csx, x[i], 0.01);
ensure_equals("y", csy, y[i], 0.01);
}
std::string wktExpected("POLYGON ((2 0.1, 2 1.9, 4 1.9, 4 0.1, 2 0.1))");
std::unique_ptr<geos::geom::Geometry> expected(wktreader.read(wktExpected));

ensure_equals_geometry(expected.get(), result.get());
}


} // namespace tut

28 changes: 28 additions & 0 deletions tests/unit/operation/overlayng/OverlayNGTest.cpp
Expand Up @@ -44,6 +44,24 @@ struct test_overlayng_data {
ensure_equals_geometry(geom_expected.get(), geom_result.get());
}

void
testOverlayExact(const std::string& a, const std::string& b, const std::string& expected, int opCode, double 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.get());
// std::string wkt_result = w.write(geom_result.get());
// std::cout << std::endl << wkt_result << std::endl;
ensure_equals_exact_geometry(geom_expected.get(), geom_result.get(), 0);
}

void
testOverlayNoOpt(const std::string& a, const std::string& b, const std::string& expected, int opCode, double scaleFactor)
{
Expand Down Expand Up @@ -531,4 +549,14 @@ void object::test<42> ()
testOverlay(a, b, exp, OverlayNG::INTERSECTION, 0);
}

template<>
template<>
void object::test<43> ()
{
std::string a = "POLYGON ((1 1, 1 9, 9 9, 9 7, 3 7, 3 3, 9 3, 9 1, 1 1))";
std::string b = "MULTILINESTRING ((2 10, 2 0), (4 10, 4 0))";
std::string exp = "MULTILINESTRING ((2 9, 2 1), (4 9, 4 7), (4 3, 4 1))";
testOverlay(a, b, exp, OverlayNG::INTERSECTION, 0);
}

} // namespace tut
44 changes: 43 additions & 1 deletion tests/unit/utility.h
Expand Up @@ -327,6 +327,49 @@ ensure_equals_geometry_xyz(const T *lhs_in,
ensure_equals_exact_geometry_xyz(g1.get(), g2.get(), tolerance);
}

/*
* Checks for geometries exactly equal in XY only
*/

template <typename T> inline void ensure_equals_exact_geometry(const T *lhs_in, const T *rhs_in, double tolerance = 0.0);

template <>
inline void
ensure_equals_exact_geometry(const geos::geom::Geometry *lhs_in,
const geos::geom::Geometry *rhs_in,
double tolerance)
{
assert(nullptr != lhs_in);
assert(nullptr != rhs_in);

using geos::geom::Point;
using geos::geom::LineString;
using geos::geom::Polygon;
using geos::geom::CoordinateSequence;
using geos::geom::GeometryCollection;

ensure_equals("type id do not match",
lhs_in->getGeometryTypeId(), rhs_in->getGeometryTypeId());

if (const Point* gpt1 = dynamic_cast<const Point *>(lhs_in)) {
const Point *gpt2 = static_cast<const Point *>(rhs_in);
return ensure_equals_dims( gpt1->getCoordinatesRO(), gpt2->getCoordinatesRO(), 2, tolerance);
}
else if (const LineString* gln1 = dynamic_cast<const LineString *>(lhs_in)) {
const LineString *gln2 = static_cast<const LineString *>(rhs_in);
return ensure_equals_dims( gln1->getCoordinatesRO(), gln2->getCoordinatesRO(), 2, tolerance);
}
else if (dynamic_cast<const Polygon *>(lhs_in)) {
assert("Not implemented yet" == 0);
}
else if (const GeometryCollection* gc1 = dynamic_cast<const GeometryCollection *>(lhs_in)) {
const GeometryCollection *gc2 = static_cast<const GeometryCollection *>(rhs_in);
for (unsigned int i = 0; i < gc1->getNumGeometries(); i++) {
ensure_equals_exact_geometry(gc1->getGeometryN(i), gc2->getGeometryN(i), tolerance);
}
}
}

//
// Utility functions
//
Expand All @@ -353,4 +396,3 @@ struct wkb_hex_decoder {
} // namespace tut

#endif // #ifndef GEOS_TUT_UTILITY_H_INCLUDED

3 changes: 2 additions & 1 deletion tests/xmltester/XMLTester.cpp
Expand Up @@ -1312,6 +1312,7 @@ XMLTester::parseTest(const tinyxml2::XMLNode* node)

else if(opName == "union") {
GeomPtr gRes(parseGeometry(opRes, "expected"));
gRes->normalize();

profile.start();

Expand All @@ -1324,6 +1325,7 @@ XMLTester::parseTest(const tinyxml2::XMLNode* node)
}

profile.stop();
gRealRes->normalize();

success = checkOverlaySuccess(*gRes, *gRealRes);

Expand Down Expand Up @@ -2414,4 +2416,3 @@ main(int argC, char* argV[])
* Revision 1.29 2006/03/17 14:56:39 strk
* Fixed filename normalizer for sql output
**********************************************************************/

0 comments on commit 701e490

Please sign in to comment.