Skip to content

Commit

Permalink
Merge pull request #541 from eyal0/to_geos_no_wkt
Browse files Browse the repository at this point in the history
perf: to_geos without using WKT
  • Loading branch information
eyal0 committed Feb 10, 2021
2 parents 21d4717 + c2994fc commit 8fa7c2d
Show file tree
Hide file tree
Showing 360 changed files with 98,596 additions and 98,348 deletions.
4 changes: 3 additions & 1 deletion Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,8 @@ EXTRA_DIST = millproject

check_PROGRAMS = voronoi_tests eulerian_paths_tests segmentize_tests tsp_solver_tests units_tests \
available_drills_tests gerberimporter_tests options_tests path_finding_tests \
autoleveller_tests common_tests backtrack_tests trim_paths_tests outline_bridges_tests
autoleveller_tests common_tests backtrack_tests trim_paths_tests outline_bridges_tests \
geos_helpers_tests
voronoi_tests_SOURCES = voronoi.hpp voronoi.cpp voronoi_tests.cpp boost_unit_test.cpp
eulerian_paths_tests_SOURCES = eulerian_paths_tests.cpp eulerian_paths.hpp geometry_int.hpp boost_unit_test.cpp bg_operators.hpp bg_operators.cpp bg_helpers.hpp bg_helpers.cpp eulerian_paths.cpp segmentize.cpp merge_near_points.cpp geos_helpers.hpp geos_helpers.cpp
segmentize_tests_SOURCES = segmentize_tests.cpp segmentize.cpp segmentize.hpp merge_near_points.cpp merge_near_points.hpp boost_unit_test.cpp bg_helpers.hpp bg_helpers.cpp eulerian_paths.cpp bg_operators.hpp bg_operators.cpp geos_helpers.hpp geos_helpers.cpp
Expand All @@ -95,6 +96,7 @@ common_tests_SOURCES = common.hpp common.cpp common_tests.cpp boost_unit_test.cp
backtrack_tests_SOURCES = backtrack.hpp backtrack.cpp backtrack_tests.cpp boost_unit_test.cpp bg_helpers.hpp bg_helpers.cpp eulerian_paths.hpp eulerian_paths.cpp segmentize.hpp segmentize.cpp merge_near_points.hpp merge_near_points.cpp bg_operators.hpp bg_operators.cpp geos_helpers.hpp geos_helpers.cpp
trim_paths_tests_SOURCES = trim_paths.hpp trim_paths.cpp trim_paths_tests.cpp boost_unit_test.cpp bg_helpers.hpp bg_helpers.cpp eulerian_paths.hpp eulerian_paths.cpp segmentize.hpp segmentize.cpp merge_near_points.hpp merge_near_points.cpp bg_operators.hpp bg_operators.cpp geos_helpers.hpp geos_helpers.cpp
outline_bridges_tests_SOURCES = outline_bridges_tests.cpp outline_bridges.hpp outline_bridges.cpp bg_operators.hpp bg_operators.cpp bg_helpers.hpp bg_helpers.cpp eulerian_paths.hpp eulerian_paths.cpp segmentize.hpp segmentize.cpp boost_unit_test.cpp merge_near_points.hpp merge_near_points.cpp geos_helpers.hpp geos_helpers.cpp
geos_helpers_tests_SOURCES = geos_helpers_tests.cpp geos_helpers.cpp geos_helpers.hpp boost_unit_test.cpp bg_operators.cpp bg_helpers.cpp eulerian_paths.cpp segmentize.cpp merge_near_points.cpp

TESTS = $(check_PROGRAMS)

Expand Down
29 changes: 26 additions & 3 deletions bg_operators.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ bg::model::multi_polygon<polygon_type_t> operator-(
}

template multi_polygon_type_fp operator-(const multi_polygon_type_fp&, const multi_polygon_type_fp&);
template multi_polygon_type_fp operator-(const multi_polygon_type_fp&, const box_type_fp&);

template <typename rhs_t>
multi_polygon_type_fp operator-(const box_type_fp& lhs, const rhs_t& rhs) {
Expand Down Expand Up @@ -171,8 +172,8 @@ template multi_polygon_type_fp operator+(const multi_polygon_type_fp&, const mul

template <typename Addition>
multi_polygon_type_fp reduce(const std::vector<multi_polygon_type_fp>& mpolys,
const Addition& adder,
const std::vector<box_type_fp>& bboxes) {
const Addition& adder,
const std::vector<box_type_fp>& bboxes) {
if (mpolys.size() == 0) {
return multi_polygon_type_fp();
} else if (mpolys.size() == 1) {
Expand Down Expand Up @@ -212,6 +213,26 @@ multi_polygon_type_fp reduce(const std::vector<multi_polygon_type_fp>& mpolys,
return reduce(mpolys, adder, bboxes);
}

void round(ring_type_fp& ring) {
for (auto& point : ring) {
point.x(std::round(point.x() * 1e10)/1e10);
point.y(std::round(point.y() * 1e10)/1e10);
}
}

void round(polygon_type_fp& p) {
round(p.outer());
for (auto& inner : p.inners()) {
round(inner);
}
}

void round(multi_polygon_type_fp& mp) {
for (auto& p : mp) {
round(p);
}
}

multi_polygon_type_fp sum(const std::vector<multi_polygon_type_fp>& mpolys) {
if (mpolys.size() == 0) {
return {};
Expand All @@ -225,7 +246,9 @@ multi_polygon_type_fp sum(const std::vector<multi_polygon_type_fp>& mpolys) {
if (bg::area(mpoly) == 0) {
continue;
}
geos_mpolys_tmp.push_back(to_geos(mpoly));
auto mpoly_temp = mpoly;
round(mpoly_temp);
geos_mpolys_tmp.push_back(to_geos(mpoly_temp));
geos_mpolys.push_back(geos_mpolys_tmp.back().get());
}
if (geos_mpolys.size() == 0) {
Expand Down
155 changes: 120 additions & 35 deletions geos_helpers.cpp
Original file line number Diff line number Diff line change
@@ -1,57 +1,142 @@
#ifdef GEOS_VERSION

#include "geos_helpers.hpp"
#include "geometry.hpp"
#include <geos/io/WKTReader.h>
#include <geos/io/WKTReader.inl>
#include <geos/io/WKTWriter.h>
#include <geos/geom/CoordinateSequenceFactory.h>
#include <geos/geom/Coordinate.h>
#include <geos/geom/Coordinate.inl>
#include <geos/geom/GeometryFactory.h>
#include <geos/geom/GeometryFactory.inl>
#include <boost/pointer_cast.hpp>

template <typename T>
std::unique_ptr<geos::geom::Geometry> to_geos(const T& mp) {
geos::io::WKTReader reader;
std::stringstream ss;
ss << bg::wkt(mp);
return reader.read(ss.str());
linestring_type_fp from_geos(const geos::geom::LineString* ls) {
linestring_type_fp ret;
ret.reserve(ls->getNumPoints());
for (size_t i = 0; i < ls->getNumPoints(); i++) {
const auto& p = ls->getPointN(i);
ret.push_back(point_type_fp(p->getX(), p->getY()));
}
return ret;
}
template std::unique_ptr<geos::geom::Geometry> to_geos(const multi_polygon_type_fp& mp);
template std::unique_ptr<geos::geom::Geometry> to_geos(const multi_linestring_type_fp& mls);
template std::unique_ptr<geos::geom::Geometry> to_geos(const linestring_type_fp& mls);

linestring_type_fp from_geos(const std::unique_ptr<geos::geom::LineString>& ls) {
return from_geos(ls.get());
}

template <typename T>
T from_geos(const std::unique_ptr<geos::geom::Geometry>& g);
ring_type_fp from_geos(const geos::geom::LinearRing* ring) {
ring_type_fp ret;
ret.reserve(ring->getNumPoints());
for (size_t i = 0; i < ring->getNumPoints(); i++) {
const auto& p = ring->getPointN(i);
ret.push_back(point_type_fp(p->getX(), p->getY()));
}
return ret;
}

template <>
multi_polygon_type_fp from_geos(const std::unique_ptr<geos::geom::Geometry>& g) {
geos::io::WKTWriter writer;
std::string geos_wkt = writer.write(g.get());
boost::replace_all(geos_wkt, "EMPTY, ", "");
boost::replace_all(geos_wkt, ", EMPTY", "");
ring_type_fp from_geos(const std::unique_ptr<geos::geom::LinearRing>& ring) {
return from_geos(ring.get());
}

polygon_type_fp from_geos(const geos::geom::Polygon* poly) {
polygon_type_fp ret;
ret.outer() = from_geos(poly->getExteriorRing());
ret.inners().reserve(poly->getNumInteriorRing());
for (size_t i = 0; i < poly->getNumInteriorRing(); i++) {
ret.inners().push_back(from_geos(poly->getInteriorRingN(i)));
}
return ret;
}

polygon_type_fp from_geos(const std::unique_ptr<geos::geom::Polygon>& poly) {
return from_geos(poly.get());
}

multi_polygon_type_fp from_geos(const std::unique_ptr<geos::geom::MultiPolygon>& mpoly) {
multi_polygon_type_fp ret;
if (strncmp(geos_wkt.c_str(), "MULTIPOLYGON", 12) == 0) {
bg::read_wkt(geos_wkt, ret);
} else {
polygon_type_fp poly;
bg::read_wkt(geos_wkt, poly);
ret.push_back(poly);
ret.reserve(mpoly->getNumGeometries());
for (size_t i = 0; i < mpoly->getNumGeometries(); i++) {
ret.push_back(from_geos(mpoly->getGeometryN(i)));
}
return ret;
}

template <>
multi_linestring_type_fp from_geos(const std::unique_ptr<geos::geom::Geometry>& g) {
geos::io::WKTWriter writer;
std::string geos_wkt = writer.write(g.get());
boost::replace_all(geos_wkt, "EMPTY, ", "");
boost::replace_all(geos_wkt, ", EMPTY", "");
multi_linestring_type_fp from_geos(const std::unique_ptr<geos::geom::MultiLineString>& mls) {
multi_linestring_type_fp ret;
if (strncmp(geos_wkt.c_str(), "MULTILINESTRING", 12) == 0) {
bg::read_wkt(geos_wkt, ret);
} else {
linestring_type_fp ls;
bg::read_wkt(geos_wkt, ls);
ret.push_back(ls);
ret.reserve(mls->getNumGeometries());
for (size_t i = 0; i < mls->getNumGeometries(); i++) {
ret.push_back(from_geos(mls->getGeometryN(i)));
}
return ret;
}

template <>
multi_polygon_type_fp from_geos(const std::unique_ptr<geos::geom::Geometry>& g) {
if (dynamic_cast<geos::geom::MultiPolygon*>(g.get())) {
auto mp = boost::dynamic_pointer_cast<geos::geom::MultiPolygon>(g->clone());
return from_geos(mp);
}
if (dynamic_cast<geos::geom::Polygon*>(g.get())) {
auto p = boost::dynamic_pointer_cast<geos::geom::Polygon>(g->clone());
return multi_polygon_type_fp{from_geos(p)};
}
geos::io::WKTWriter writer;
throw std::logic_error("Can't convert to multi_polygon_type_fp: " + writer.write(g.get()));
}

std::unique_ptr<geos::geom::LineString> to_geos(
const linestring_type_fp& ls) {
auto geom_factory = geos::geom::GeometryFactory::create();
auto coord_factory = geom_factory->getCoordinateSequenceFactory();
auto coords = coord_factory->create(ls.size(), 2 /* dimensions */);
for (size_t i = 0; i < ls.size(); i++) {
coords->setAt(geos::geom::Coordinate(ls[i].x(), ls[i].y()), i);
}
return geom_factory->createLineString(std::move(coords));
}

std::unique_ptr<geos::geom::LinearRing> to_geos(const ring_type_fp& ring) {
auto geom_factory = geos::geom::GeometryFactory::create();
auto coord_factory = geom_factory->getCoordinateSequenceFactory();
auto coords = coord_factory->create(ring.size(), 2 /* dimensions */);
for (size_t i = 0; i < ring.size(); i++) {
// reverse rings for geos.
coords->setAt(geos::geom::Coordinate(ring[i].x(), ring[i].y()), i);
}
return geom_factory->createLinearRing(std::move(coords));
}

std::unique_ptr<geos::geom::Polygon> to_geos(const polygon_type_fp& poly) {
auto shell = to_geos(poly.outer());
std::vector<std::unique_ptr<geos::geom::LinearRing>> holes;
holes.reserve(poly.inners().size());
for (const auto& inner : poly.inners()) {
holes.push_back(to_geos(inner));
}
auto geom_factory = geos::geom::GeometryFactory::create();
return geom_factory->createPolygon(std::move(shell), std::move(holes));
}

std::unique_ptr<geos::geom::MultiPolygon> to_geos(const multi_polygon_type_fp& mpoly) {
std::vector<std::unique_ptr<geos::geom::Polygon>> polys;
polys.reserve(mpoly.size());
for (const auto& p : mpoly) {
polys.push_back(to_geos(p));
}
auto geom_factory = geos::geom::GeometryFactory::create();
return geom_factory->createMultiPolygon(std::move(polys));
}

std::unique_ptr<geos::geom::MultiLineString> to_geos(const multi_linestring_type_fp& mls) {
std::vector<std::unique_ptr<geos::geom::LineString>> polys;
polys.reserve(mls.size());
for (const auto& p : mls) {
polys.push_back(to_geos(p));
}
auto geom_factory = geos::geom::GeometryFactory::create();
return geom_factory->createMultiLineString(std::move(polys));
}

#endif //GEOS_VERSION
19 changes: 17 additions & 2 deletions geos_helpers.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,27 @@
#include <memory>
#include <geos/geom/Geometry.h>
#include "geometry.hpp"
#include <geos/geom/LinearRing.h>
#include <geos/geom/Polygon.h>
#include <geos/geom/MultiPolygon.h>
#include <geos/geom/MultiLineString.h>

std::unique_ptr<geos::geom::LineString> to_geos(
const linestring_type_fp& ls);
std::unique_ptr<geos::geom::LinearRing> to_geos(const ring_type_fp& ring);
std::unique_ptr<geos::geom::Polygon> to_geos(const polygon_type_fp& poly);
std::unique_ptr<geos::geom::MultiPolygon> to_geos(const multi_polygon_type_fp& mpoly);
std::unique_ptr<geos::geom::MultiLineString> to_geos(const multi_linestring_type_fp& mls);

template <typename T>
std::unique_ptr<geos::geom::Geometry> to_geos(const T& mp);
template <typename T>
T from_geos(const std::unique_ptr<geos::geom::Geometry>& g);

linestring_type_fp from_geos(const std::unique_ptr<geos::geom::LineString>& ls);
ring_type_fp from_geos(const std::unique_ptr<geos::geom::LinearRing>& ring);
polygon_type_fp from_geos(const std::unique_ptr<geos::geom::Polygon>& poly);
multi_polygon_type_fp from_geos(const std::unique_ptr<geos::geom::MultiPolygon>& mpoly);
multi_linestring_type_fp from_geos(const std::unique_ptr<geos::geom::MultiLineString>& mls);

#endif // GEOS_VERSION

#endif //GEOS_HELPERS_HPP
97 changes: 97 additions & 0 deletions geos_helpers_tests.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
#define BOOST_TEST_MODULE geos helpers tests
#include <boost/test/unit_test.hpp>

#include "geometry.hpp"
#include "geos_helpers.hpp"
#include "bg_operators.hpp"
#ifdef GEOS_VERSION
#include <geos/io/WKTReader.h>
#endif // GEOS_VERSION
#include <boost/pointer_cast.hpp>

BOOST_AUTO_TEST_SUITE(geos_helpers_tests)

BOOST_AUTO_TEST_SUITE(boost_geometry)

// Holes are counter-clockwise
BOOST_AUTO_TEST_CASE(polygon_with_holes_direction) {
auto box = bg::return_envelope<box_type_fp>(point_type_fp(0,0));
bg::expand(box, point_type_fp(10,10));
auto hole = bg::return_envelope<box_type_fp>(point_type_fp(3,3));
bg::expand(hole, point_type_fp(7,7));
multi_polygon_type_fp mpoly;
bg::convert(box, mpoly);
mpoly = mpoly - hole;
BOOST_CHECK_EQUAL(mpoly[0].outer()[1], point_type_fp(0,10));
BOOST_CHECK_EQUAL(mpoly[0].inners()[0][1], point_type_fp(7,3));
bg::reverse(mpoly);
BOOST_CHECK_EQUAL(mpoly[0].outer()[1], point_type_fp(10,0));
BOOST_CHECK_EQUAL(mpoly[0].inners()[0][1], point_type_fp(3,7));
bg::correct(mpoly);
BOOST_CHECK_EQUAL(mpoly[0].outer()[1], point_type_fp(0,10));
BOOST_CHECK_EQUAL(mpoly[0].inners()[0][1], point_type_fp(7,3));
}

BOOST_AUTO_TEST_SUITE_END()

#ifdef GEOS_VERSION

BOOST_AUTO_TEST_SUITE(geos_geometry)

BOOST_AUTO_TEST_CASE(polygon_with_holes_direction) {
// Convert it through well-known text, which is sure to create a
// valid polygon in geos.
geos::io::WKTReader reader;
auto geos_geo = reader.read("MULTIPOLYGON(((0 0,0 10,10 10,10 0,0 0),(3 3,7 3,7 7,3 7,3 3)))");
auto* geos_poly = dynamic_cast<geos::geom::MultiPolygon*>(geos_geo.get());
geos_poly->normalize();
BOOST_CHECK_EQUAL(geos_poly->getGeometryN(0)->getExteriorRing()->getPointN(1)->getX(), 0);
BOOST_CHECK_EQUAL(geos_poly->getGeometryN(0)->getExteriorRing()->getPointN(1)->getY(), 10);
BOOST_CHECK_EQUAL(geos_poly->getGeometryN(0)->getInteriorRingN(0)->getPointN(1)->getX(), 7);
BOOST_CHECK_EQUAL(geos_poly->getGeometryN(0)->getInteriorRingN(0)->getPointN(1)->getY(), 3);

// Now convert a reversed version.
geos_geo = reader.read("MULTIPOLYGON(((0 0,10 0,10 10,0 10,0 0),(3 3,3 7,7 7,7 3,3 3)))");
geos_poly = dynamic_cast<geos::geom::MultiPolygon*>(geos_geo.get());
geos_poly->normalize();
BOOST_CHECK_EQUAL(geos_poly->getGeometryN(0)->getExteriorRing()->getPointN(1)->getX(), 0);
BOOST_CHECK_EQUAL(geos_poly->getGeometryN(0)->getExteriorRing()->getPointN(1)->getY(), 10);
BOOST_CHECK_EQUAL(geos_poly->getGeometryN(0)->getInteriorRingN(0)->getPointN(1)->getX(), 7);
BOOST_CHECK_EQUAL(geos_poly->getGeometryN(0)->getInteriorRingN(0)->getPointN(1)->getY(), 3);
}

BOOST_AUTO_TEST_SUITE(roundtrip)

BOOST_AUTO_TEST_CASE(multi_linestring) {
multi_linestring_type_fp mls{{{0,0}, {1,1}}, {{2,2},{3,3}}};
BOOST_CHECK_EQUAL(from_geos(to_geos(mls)), mls);
}

BOOST_AUTO_TEST_CASE(linestring) {
linestring_type_fp ls{{0,0}, {1,1}};
BOOST_CHECK_EQUAL(from_geos(to_geos(ls)), ls);
}

BOOST_AUTO_TEST_CASE(polygon) {
polygon_type_fp poly{{{0,0}, {0,10}, {10,10}, {10,0}, {0,0}}};
BOOST_CHECK(bg::equals(from_geos(to_geos(poly)), poly));
}

BOOST_AUTO_TEST_CASE(ring) {
ring_type_fp ring{{0,0}, {0,10}, {10,10}, {10,0}, {0,0}};
BOOST_CHECK_EQUAL(from_geos(to_geos(ring)), ring);
}

BOOST_AUTO_TEST_SUITE_END()

BOOST_AUTO_TEST_CASE(convert_multi_polygon_exception) {
linestring_type_fp ls{{0,0}, {1,1}};
auto geos_ls = boost::dynamic_pointer_cast<geos::geom::Geometry>(to_geos(ls));
BOOST_CHECK_THROW(from_geos<multi_polygon_type_fp>(geos_ls), std::logic_error);
}

BOOST_AUTO_TEST_SUITE_END()

#endif // GEOS_VERSION

BOOST_AUTO_TEST_SUITE_END()
Loading

0 comments on commit 8fa7c2d

Please sign in to comment.