Make DouglasPeucker a free standing function

This commit is contained in:
Daniel J. Hofmann 2016-01-08 12:55:37 +01:00 committed by Patrick Niklaus
parent 23cd4d5ed2
commit c590596dbe
5 changed files with 73 additions and 70 deletions

View File

@ -4,7 +4,6 @@
#include "guidance/segment_list.hpp" #include "guidance/segment_list.hpp"
#include "guidance/textual_route_annotation.hpp" #include "guidance/textual_route_annotation.hpp"
#include "engine/douglas_peucker.hpp"
#include "engine/internal_route_result.hpp" #include "engine/internal_route_result.hpp"
#include "engine/object_encoder.hpp" #include "engine/object_encoder.hpp"
#include "engine/phantom_node.hpp" #include "engine/phantom_node.hpp"
@ -261,7 +260,8 @@ ApiResponseGenerator<DataFacadeT>::BuildRouteSegments(const Segments &segment_li
(extractor::TurnInstruction::EnterRoundAbout != current_turn)) (extractor::TurnInstruction::EnterRoundAbout != current_turn))
{ {
detail::Segment seg = {segment.name_id, static_cast<int32_t>(segment.length), detail::Segment seg = {segment.name_id,
static_cast<int32_t>(segment.length),
static_cast<std::size_t>(result.size())}; static_cast<std::size_t>(result.size())};
result.emplace_back(std::move(seg)); result.emplace_back(std::move(seg));
} }

View File

@ -3,24 +3,16 @@
#include "engine/segment_information.hpp" #include "engine/segment_information.hpp"
#include <array>
#include <stack>
#include <utility>
#include <vector> #include <vector>
#include <iterator>
namespace osrm namespace osrm
{ {
namespace engine namespace engine
{ {
namespace detail
/* This class object computes the bitvector of indicating generalized input {
* points according to the (Ramer-)Douglas-Peucker algorithm. const constexpr int DOUGLAS_PEUCKER_THRESHOLDS[19] = {
*
* 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<int, 19> DOUGLAS_PEUCKER_THRESHOLDS{{
512440, // z0 512440, // z0
256720, // z1 256720, // z1
122560, // z2 122560, // z2
@ -39,22 +31,28 @@ static const std::array<int, 19> DOUGLAS_PEUCKER_THRESHOLDS{{
20, // z15 20, // z15
8, // z16 8, // z16
6, // z17 6, // z17
4 // z18 4, // z18
}};
class DouglasPeucker
{
public:
using RandomAccessIt = std::vector<SegmentInformation>::iterator;
using GeometryRange = std::pair<RandomAccessIt, RandomAccessIt>;
// Stack to simulate the recursion
std::stack<GeometryRange> recursion_stack;
public:
void Run(RandomAccessIt begin, RandomAccessIt end, const unsigned zoom_level);
void Run(std::vector<SegmentInformation> &input_geometry, const unsigned zoom_level);
}; };
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<SegmentInformation>::iterator begin,
std::vector<SegmentInformation>::iterator end,
const unsigned zoom_level);
// Convenience range-based function
inline void douglasPeucker(std::vector<SegmentInformation> &geometry, const unsigned zoom_level)
{
douglasPeucker(begin(geometry), end(geometry), zoom_level);
}
} }
} }

View File

@ -272,14 +272,13 @@ void SegmentList<DataFacadeT>::Finalize(const bool extract_alternative,
if (allow_simplification) if (allow_simplification)
{ {
DouglasPeucker polyline_generalizer; douglasPeucker(segments, zoom_level);
polyline_generalizer.Run(segments.begin(), segments.end(), zoom_level);
} }
std::uint32_t necessary_segments = 0; // a running index that counts the necessary pieces std::uint32_t necessary_segments = 0; // a running index that counts the necessary pieces
via_indices.push_back(0); via_indices.push_back(0);
const auto markNecessarySegments = [this, &necessary_segments](SegmentInformation &first, const auto markNecessarySegments =
const SegmentInformation &second) [this, &necessary_segments](SegmentInformation &first, const SegmentInformation &second)
{ {
if (!first.necessary) if (!first.necessary)
return; return;

View File

@ -1,13 +1,13 @@
#include "engine/douglas_peucker.hpp" #include "engine/douglas_peucker.hpp"
#include "engine/segment_information.hpp"
#include <boost/assert.hpp> #include <boost/assert.hpp>
#include "osrm/coordinate.hpp" #include "osrm/coordinate.hpp"
#include <cmath> #include <cmath>
#include <algorithm> #include <algorithm>
#include <iterator> #include <iterator>
#include <stack>
#include <utility>
namespace osrm namespace osrm
{ {
@ -59,13 +59,15 @@ struct CoordinatePairCalculator
}; };
} }
void DouglasPeucker::Run(std::vector<SegmentInformation> &input_geometry, const unsigned zoom_level) void douglasPeucker(std::vector<SegmentInformation>::iterator begin,
std::vector<SegmentInformation>::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<Iter, Iter>;
std::stack<GeometryRange> recursion_stack;
void DouglasPeucker::Run(RandomAccessIt begin, RandomAccessIt end, const unsigned zoom_level)
{
const auto size = std::distance(begin, end); const auto size = std::distance(begin, end);
if (size < 2) if (size < 2)
{ {
@ -76,7 +78,8 @@ void DouglasPeucker::Run(RandomAccessIt begin, RandomAccessIt end, const unsigne
std::prev(end)->necessary = true; 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 left_border = begin;
auto right_border = std::next(begin); auto right_border = std::next(begin);
// Sweep over array and identify those ranges that need to be checked // 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); const int distance = dist_calc(it->location);
// found new feasible maximum? // 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; farthest_entry_it = it;
max_int_distance = distance; 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 // 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 // mark idx as necessary
farthest_entry_it->necessary = true; farthest_entry_it->necessary = true;
@ -140,5 +144,5 @@ void DouglasPeucker::Run(RandomAccessIt begin, RandomAccessIt end, const unsigne
} }
} }
} }
} } // ns engine
} } // ns osrm

View File

@ -8,7 +8,7 @@
#include <vector> #include <vector>
BOOST_AUTO_TEST_SUITE(douglas_peucker) BOOST_AUTO_TEST_SUITE(douglas_peucker_simplification)
using namespace osrm; using namespace osrm;
using namespace osrm::engine; using namespace osrm::engine;
@ -22,18 +22,20 @@ SegmentInformation getTestInfo(int lat, int lon, bool necessary)
BOOST_AUTO_TEST_CASE(all_necessary_test) BOOST_AUTO_TEST_CASE(all_necessary_test)
{ {
/* /*
* x x
* / \ / \
* x \ x \
* / \ / \
* x x x x
*/ /
std::vector<SegmentInformation> info = {getTestInfo(5, 5, true), getTestInfo(6, 6, true), */
getTestInfo(10, 10, true), getTestInfo(5, 15, true)}; std::vector<SegmentInformation> info = {getTestInfo(5, 5, true),
DouglasPeucker dp; getTestInfo(6, 6, true),
for (unsigned z = 0; z < DOUGLAS_PEUCKER_THRESHOLDS.size(); z++) 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) for (const auto &i : info)
{ {
BOOST_CHECK_EQUAL(i.necessary, true); 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) BOOST_AUTO_TEST_CASE(remove_second_node_test)
{ {
DouglasPeucker dp; for (unsigned z = 0; z < detail::DOUGLAS_PEUCKER_THRESHOLDS_SIZE; z++)
for (unsigned z = 0; z < DOUGLAS_PEUCKER_THRESHOLDS.size(); z++)
{ {
/* /*
* x--x x--x
* | \ | \
* x-x x x-x x
* | |
* x x
*/ */
std::vector<SegmentInformation> info = { std::vector<SegmentInformation> info = {
getTestInfo(5 * COORDINATE_PRECISION, 5 * COORDINATE_PRECISION, true), getTestInfo(5 * COORDINATE_PRECISION, 5 * COORDINATE_PRECISION, true),
getTestInfo(5 * COORDINATE_PRECISION, 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, false),
getTestInfo(10 * COORDINATE_PRECISION, 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, 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), 15 * COORDINATE_PRECISION, true),
}; };
BOOST_TEST_MESSAGE("Threshold (" << z << "): " << DOUGLAS_PEUCKER_THRESHOLDS[z]); BOOST_TEST_MESSAGE("Threshold (" << z << "): " << detail::DOUGLAS_PEUCKER_THRESHOLDS[z]);
dp.Run(info, z); douglasPeucker(info, z);
BOOST_CHECK_EQUAL(info[0].necessary, true); BOOST_CHECK_EQUAL(info[0].necessary, true);
BOOST_CHECK_EQUAL(info[1].necessary, false); BOOST_CHECK_EQUAL(info[1].necessary, false);
BOOST_CHECK_EQUAL(info[2].necessary, true); BOOST_CHECK_EQUAL(info[2].necessary, true);