osrm-backend/src/engine/douglas_peucker.cpp

129 lines
4.4 KiB
C++
Raw Normal View History

2016-01-02 11:13:44 -05:00
#include "engine/douglas_peucker.hpp"
#include "util/coordinate_calculation.hpp"
#include "util/web_mercator.hpp"
2016-01-28 10:28:44 -05:00
#include "util/coordinate.hpp"
2014-11-28 04:07:06 -05:00
#include <boost/assert.hpp>
2016-01-28 10:28:44 -05:00
#include <boost/range/irange.hpp>
2014-11-28 04:07:06 -05:00
#include <cmath>
#include <algorithm>
2015-02-09 11:38:40 -05:00
#include <iterator>
#include <stack>
#include <utility>
2014-11-28 04:07:06 -05:00
2016-01-05 10:51:13 -05:00
namespace osrm
{
namespace engine
{
struct FastPerpendicularDistance
{
FastPerpendicularDistance(const util::Coordinate start, const util::Coordinate target)
: projected_start(util::web_mercator::fromWGS84(start)),
projected_target(util::web_mercator::fromWGS84(target))
{
}
// Normed to the thresholds table
std::uint64_t operator()(const util::Coordinate coordinate) const
{
auto projected = util::web_mercator::fromWGS84(coordinate);
util::FloatCoordinate projected_point_on_segment;
std::tie(std::ignore, projected_point_on_segment) =
util::coordinate_calculation::projectPointOnSegment(projected_start, projected_target,
projected);
auto squared_distance = util::coordinate_calculation::squaredEuclideanDistance(projected,
projected_point_on_segment);
return squared_distance;
}
const util::FloatCoordinate projected_start;
const util::FloatCoordinate projected_target;
};
std::vector<util::Coordinate> douglasPeucker(std::vector<util::Coordinate>::const_iterator begin,
std::vector<util::Coordinate>::const_iterator end,
const unsigned zoom_level)
2014-11-28 04:07:06 -05:00
{
2016-01-28 10:28:44 -05:00
BOOST_ASSERT_MSG(zoom_level < detail::DOUGLAS_PEUCKER_THRESHOLDS_SIZE,
"unsupported zoom level");
2014-11-28 04:07:06 -05:00
2016-04-06 03:47:17 -04:00
const std::size_t size = std::distance(begin, end);
2014-11-28 04:07:06 -05:00
if (size < 2)
{
2016-01-28 10:28:44 -05:00
return {};
2014-11-28 04:07:06 -05:00
}
2016-01-28 10:28:44 -05:00
std::vector<bool> is_necessary(size, false);
BOOST_ASSERT(is_necessary.size() >= 2);
is_necessary.front() = true;
is_necessary.back() = true;
using GeometryRange = std::pair<std::size_t, std::size_t>;
2014-11-28 04:07:06 -05:00
2016-01-28 10:28:44 -05:00
std::stack<GeometryRange> recursion_stack;
recursion_stack.emplace(0UL, size - 1);
2014-11-28 04:07:06 -05:00
// mark locations as 'necessary' by divide-and-conquer
while (!recursion_stack.empty())
{
// pop next element
const GeometryRange pair = recursion_stack.top();
recursion_stack.pop();
// sanity checks
2016-01-28 10:28:44 -05:00
BOOST_ASSERT_MSG(is_necessary[pair.first], "left border must be necessary");
BOOST_ASSERT_MSG(is_necessary[pair.second], "right border must be necessary");
BOOST_ASSERT_MSG(pair.second < size, "right border outside of geometry");
BOOST_ASSERT_MSG(pair.first <= pair.second, "left border on the wrong side");
2014-11-28 04:07:06 -05:00
std::uint64_t max_distance = 0;
2016-01-28 10:28:44 -05:00
auto farthest_entry_index = pair.second;
2014-11-28 04:07:06 -05:00
FastPerpendicularDistance perpendicular_distance(begin[pair.first], begin[pair.second]);
2014-11-28 04:07:06 -05:00
// sweep over range to find the maximum
2016-01-28 10:28:44 -05:00
for (auto idx = pair.first + 1; idx != pair.second; ++idx)
2014-11-28 04:07:06 -05:00
{
using namespace util::coordinate_calculation;
const auto distance = perpendicular_distance(begin[idx]);
2014-11-28 04:07:06 -05:00
// found new feasible maximum?
if (distance > max_distance &&
distance > detail::DOUGLAS_PEUCKER_THRESHOLDS[zoom_level])
2014-11-28 04:07:06 -05:00
{
2016-01-28 10:28:44 -05:00
farthest_entry_index = idx;
max_distance = distance;
2014-11-28 04:07:06 -05:00
}
}
// check if maximum violates a zoom level dependent threshold
if (max_distance > detail::DOUGLAS_PEUCKER_THRESHOLDS[zoom_level])
2014-11-28 04:07:06 -05:00
{
// mark idx as necessary
2016-01-28 10:28:44 -05:00
is_necessary[farthest_entry_index] = true;
if (pair.first < farthest_entry_index)
2014-11-28 04:07:06 -05:00
{
2016-01-28 10:28:44 -05:00
recursion_stack.emplace(pair.first, farthest_entry_index);
2014-11-28 04:07:06 -05:00
}
2016-01-28 10:28:44 -05:00
if (farthest_entry_index < pair.second)
2014-11-28 04:07:06 -05:00
{
2016-01-28 10:28:44 -05:00
recursion_stack.emplace(farthest_entry_index, pair.second);
2014-11-28 04:07:06 -05:00
}
}
}
2016-01-28 10:28:44 -05:00
auto simplified_size = std::count(is_necessary.begin(), is_necessary.end(), true);
std::vector<util::Coordinate> simplified_geometry;
2016-01-28 10:28:44 -05:00
simplified_geometry.reserve(simplified_size);
for (auto idx : boost::irange<std::size_t>(0UL, size))
{
if (is_necessary[idx])
{
simplified_geometry.push_back(begin[idx]);
}
}
return simplified_geometry;
2014-11-28 04:07:06 -05:00
}
} // ns engine
} // ns osrm