From c590596dbe6fbf0f6a343e4cde463c32726fb03a Mon Sep 17 00:00:00 2001 From: "Daniel J. Hofmann" Date: Fri, 8 Jan 2016 12:55:37 +0100 Subject: [PATCH] Make DouglasPeucker a free standing function --- include/engine/api_response_generator.hpp | 4 +- include/engine/douglas_peucker.hpp | 52 +++++++++++------------ include/engine/guidance/segment_list.hpp | 7 ++- src/engine/douglas_peucker.cpp | 28 ++++++------ unit_tests/engine/douglas_peucker.cpp | 52 ++++++++++++----------- 5 files changed, 73 insertions(+), 70 deletions(-) diff --git a/include/engine/api_response_generator.hpp b/include/engine/api_response_generator.hpp index 77a220d7e..747a3639b 100644 --- a/include/engine/api_response_generator.hpp +++ b/include/engine/api_response_generator.hpp @@ -4,7 +4,6 @@ #include "guidance/segment_list.hpp" #include "guidance/textual_route_annotation.hpp" -#include "engine/douglas_peucker.hpp" #include "engine/internal_route_result.hpp" #include "engine/object_encoder.hpp" #include "engine/phantom_node.hpp" @@ -261,7 +260,8 @@ ApiResponseGenerator::BuildRouteSegments(const Segments &segment_li (extractor::TurnInstruction::EnterRoundAbout != current_turn)) { - detail::Segment seg = {segment.name_id, static_cast(segment.length), + detail::Segment seg = {segment.name_id, + static_cast(segment.length), static_cast(result.size())}; result.emplace_back(std::move(seg)); } diff --git a/include/engine/douglas_peucker.hpp b/include/engine/douglas_peucker.hpp index df4fe921d..a100f0f04 100644 --- a/include/engine/douglas_peucker.hpp +++ b/include/engine/douglas_peucker.hpp @@ -3,24 +3,16 @@ #include "engine/segment_information.hpp" -#include -#include -#include #include +#include namespace osrm { namespace engine { - -/* This class object computes the bitvector of indicating generalized input - * points according to the (Ramer-)Douglas-Peucker algorithm. - * - * Input is vector of pairs. Each pair consists of the point information and a - * bit indicating if the points is present in the generalization. - * Note: points may also be pre-selected*/ - -static const std::array DOUGLAS_PEUCKER_THRESHOLDS{{ +namespace detail +{ +const constexpr int DOUGLAS_PEUCKER_THRESHOLDS[19] = { 512440, // z0 256720, // z1 122560, // z2 @@ -39,22 +31,28 @@ static const std::array DOUGLAS_PEUCKER_THRESHOLDS{{ 20, // z15 8, // z16 6, // z17 - 4 // z18 -}}; - -class DouglasPeucker -{ - public: - using RandomAccessIt = std::vector::iterator; - - using GeometryRange = std::pair; - // Stack to simulate the recursion - std::stack recursion_stack; - - public: - void Run(RandomAccessIt begin, RandomAccessIt end, const unsigned zoom_level); - void Run(std::vector &input_geometry, const unsigned zoom_level); + 4, // z18 }; + +const constexpr auto DOUGLAS_PEUCKER_THRESHOLDS_SIZE = + sizeof(DOUGLAS_PEUCKER_THRESHOLDS) / sizeof(*DOUGLAS_PEUCKER_THRESHOLDS); +} // ns detail + +// These functions compute the bitvector of indicating generalized input +// points according to the (Ramer-)Douglas-Peucker algorithm. +// +// Input is vector of pairs. Each pair consists of the point information and a +// bit indicating if the points is present in the generalization. +// Note: points may also be pre-selected*/ +void douglasPeucker(std::vector::iterator begin, + std::vector::iterator end, + const unsigned zoom_level); + +// Convenience range-based function +inline void douglasPeucker(std::vector &geometry, const unsigned zoom_level) +{ + douglasPeucker(begin(geometry), end(geometry), zoom_level); +} } } diff --git a/include/engine/guidance/segment_list.hpp b/include/engine/guidance/segment_list.hpp index 51a1695bd..e75ab41a0 100644 --- a/include/engine/guidance/segment_list.hpp +++ b/include/engine/guidance/segment_list.hpp @@ -272,14 +272,13 @@ void SegmentList::Finalize(const bool extract_alternative, if (allow_simplification) { - DouglasPeucker polyline_generalizer; - polyline_generalizer.Run(segments.begin(), segments.end(), zoom_level); + douglasPeucker(segments, zoom_level); } std::uint32_t necessary_segments = 0; // a running index that counts the necessary pieces via_indices.push_back(0); - const auto markNecessarySegments = [this, &necessary_segments](SegmentInformation &first, - const SegmentInformation &second) + const auto markNecessarySegments = + [this, &necessary_segments](SegmentInformation &first, const SegmentInformation &second) { if (!first.necessary) return; diff --git a/src/engine/douglas_peucker.cpp b/src/engine/douglas_peucker.cpp index cced730b2..f9635d0ea 100644 --- a/src/engine/douglas_peucker.cpp +++ b/src/engine/douglas_peucker.cpp @@ -1,13 +1,13 @@ #include "engine/douglas_peucker.hpp" -#include "engine/segment_information.hpp" - #include #include "osrm/coordinate.hpp" #include #include #include +#include +#include namespace osrm { @@ -59,13 +59,15 @@ struct CoordinatePairCalculator }; } -void DouglasPeucker::Run(std::vector &input_geometry, const unsigned zoom_level) +void douglasPeucker(std::vector::iterator begin, + std::vector::iterator end, + const unsigned zoom_level) { - Run(std::begin(input_geometry), std::end(input_geometry), zoom_level); -} + using Iter = decltype(begin); + using GeometryRange = std::pair; + + std::stack recursion_stack; -void DouglasPeucker::Run(RandomAccessIt begin, RandomAccessIt end, const unsigned zoom_level) -{ const auto size = std::distance(begin, end); if (size < 2) { @@ -76,7 +78,8 @@ void DouglasPeucker::Run(RandomAccessIt begin, RandomAccessIt end, const unsigne std::prev(end)->necessary = true; { - BOOST_ASSERT_MSG(zoom_level < DOUGLAS_PEUCKER_THRESHOLDS.size(), "unsupported zoom level"); + BOOST_ASSERT_MSG(zoom_level < detail::DOUGLAS_PEUCKER_THRESHOLDS_SIZE, + "unsupported zoom level"); auto left_border = begin; auto right_border = std::next(begin); // Sweep over array and identify those ranges that need to be checked @@ -117,7 +120,8 @@ void DouglasPeucker::Run(RandomAccessIt begin, RandomAccessIt end, const unsigne { const int distance = dist_calc(it->location); // found new feasible maximum? - if (distance > max_int_distance && distance > DOUGLAS_PEUCKER_THRESHOLDS[zoom_level]) + if (distance > max_int_distance && + distance > detail::DOUGLAS_PEUCKER_THRESHOLDS[zoom_level]) { farthest_entry_it = it; max_int_distance = distance; @@ -125,7 +129,7 @@ void DouglasPeucker::Run(RandomAccessIt begin, RandomAccessIt end, const unsigne } // check if maximum violates a zoom level dependent threshold - if (max_int_distance > DOUGLAS_PEUCKER_THRESHOLDS[zoom_level]) + if (max_int_distance > detail::DOUGLAS_PEUCKER_THRESHOLDS[zoom_level]) { // mark idx as necessary farthest_entry_it->necessary = true; @@ -140,5 +144,5 @@ void DouglasPeucker::Run(RandomAccessIt begin, RandomAccessIt end, const unsigne } } } -} -} +} // ns engine +} // ns osrm diff --git a/unit_tests/engine/douglas_peucker.cpp b/unit_tests/engine/douglas_peucker.cpp index 4c165c1a6..ec25a88c6 100644 --- a/unit_tests/engine/douglas_peucker.cpp +++ b/unit_tests/engine/douglas_peucker.cpp @@ -8,7 +8,7 @@ #include -BOOST_AUTO_TEST_SUITE(douglas_peucker) +BOOST_AUTO_TEST_SUITE(douglas_peucker_simplification) using namespace osrm; using namespace osrm::engine; @@ -22,18 +22,20 @@ SegmentInformation getTestInfo(int lat, int lon, bool necessary) BOOST_AUTO_TEST_CASE(all_necessary_test) { /* - * x - * / \ - * x \ - * / \ - * x x - */ - std::vector info = {getTestInfo(5, 5, true), getTestInfo(6, 6, true), - getTestInfo(10, 10, true), getTestInfo(5, 15, true)}; - DouglasPeucker dp; - for (unsigned z = 0; z < DOUGLAS_PEUCKER_THRESHOLDS.size(); z++) + x + / \ + x \ + / \ + x x + / + */ + std::vector info = {getTestInfo(5, 5, true), + getTestInfo(6, 6, true), + getTestInfo(10, 10, true), + getTestInfo(5, 15, true)}; + for (unsigned z = 0; z < detail::DOUGLAS_PEUCKER_THRESHOLDS_SIZE; z++) { - dp.Run(info, z); + douglasPeucker(info, z); for (const auto &i : info) { BOOST_CHECK_EQUAL(i.necessary, true); @@ -43,29 +45,29 @@ BOOST_AUTO_TEST_CASE(all_necessary_test) BOOST_AUTO_TEST_CASE(remove_second_node_test) { - DouglasPeucker dp; - for (unsigned z = 0; z < DOUGLAS_PEUCKER_THRESHOLDS.size(); z++) + for (unsigned z = 0; z < detail::DOUGLAS_PEUCKER_THRESHOLDS_SIZE; z++) { /* - * x--x - * | \ - * x-x x - * | - * x - */ + x--x + | \ + x-x x + | + x + */ std::vector info = { getTestInfo(5 * COORDINATE_PRECISION, 5 * COORDINATE_PRECISION, true), getTestInfo(5 * COORDINATE_PRECISION, - 5 * COORDINATE_PRECISION + DOUGLAS_PEUCKER_THRESHOLDS[z], false), + 5 * COORDINATE_PRECISION + detail::DOUGLAS_PEUCKER_THRESHOLDS[z], false), getTestInfo(10 * COORDINATE_PRECISION, 10 * COORDINATE_PRECISION, false), getTestInfo(10 * COORDINATE_PRECISION, - 10 + COORDINATE_PRECISION + DOUGLAS_PEUCKER_THRESHOLDS[z] * 2, false), + 10 + COORDINATE_PRECISION + detail::DOUGLAS_PEUCKER_THRESHOLDS[z] * 2, + false), getTestInfo(5 * COORDINATE_PRECISION, 15 * COORDINATE_PRECISION, false), - getTestInfo(5 * COORDINATE_PRECISION + DOUGLAS_PEUCKER_THRESHOLDS[z], + getTestInfo(5 * COORDINATE_PRECISION + detail::DOUGLAS_PEUCKER_THRESHOLDS[z], 15 * COORDINATE_PRECISION, true), }; - BOOST_TEST_MESSAGE("Threshold (" << z << "): " << DOUGLAS_PEUCKER_THRESHOLDS[z]); - dp.Run(info, z); + BOOST_TEST_MESSAGE("Threshold (" << z << "): " << detail::DOUGLAS_PEUCKER_THRESHOLDS[z]); + douglasPeucker(info, z); BOOST_CHECK_EQUAL(info[0].necessary, true); BOOST_CHECK_EQUAL(info[1].necessary, false); BOOST_CHECK_EQUAL(info[2].necessary, true);