209 lines
6.5 KiB
C++
209 lines
6.5 KiB
C++
#ifndef COORDINATE_TIDY
|
|
#define COORDINATE_TIDY
|
|
|
|
#include <algorithm>
|
|
#include <cstdint>
|
|
#include <iterator>
|
|
|
|
#include "engine/api/match_parameters.hpp"
|
|
#include "util/coordinate_calculation.hpp"
|
|
|
|
#include <boost/assert.hpp>
|
|
#include <boost/dynamic_bitset.hpp>
|
|
|
|
namespace osrm
|
|
{
|
|
namespace engine
|
|
{
|
|
namespace api
|
|
{
|
|
namespace tidy
|
|
{
|
|
|
|
struct Thresholds
|
|
{
|
|
double distance_in_meters;
|
|
std::int32_t duration_in_seconds;
|
|
};
|
|
|
|
using Mask = boost::dynamic_bitset<>;
|
|
using Mapping = std::vector<std::size_t>;
|
|
|
|
struct Result
|
|
{
|
|
// Tidied parameters
|
|
MatchParameters parameters;
|
|
// Masking the MatchParameter parallel arrays for items which should be removed.
|
|
Mask can_be_removed;
|
|
// Maps the MatchParameter's original items to items which should not be removed.
|
|
Mapping tidied_to_original;
|
|
// Masking the MatchParameter coordinates for items whose indices were present in the
|
|
// `waypoints` parameter.
|
|
Mask was_waypoint;
|
|
};
|
|
|
|
inline Result keep_all(const MatchParameters ¶ms)
|
|
{
|
|
Result result;
|
|
|
|
result.can_be_removed.resize(params.coordinates.size(), false);
|
|
result.was_waypoint.resize(params.coordinates.size(), true);
|
|
// by default all input coordinates are treated as waypoints
|
|
if (!params.waypoints.empty())
|
|
{
|
|
for (const auto p : params.waypoints)
|
|
{
|
|
result.was_waypoint.set(p, false);
|
|
}
|
|
// logic is a little funny, uses inversion to set the bitfield
|
|
result.was_waypoint.flip();
|
|
}
|
|
result.tidied_to_original.reserve(params.coordinates.size());
|
|
for (std::size_t current = 0; current < params.coordinates.size(); ++current)
|
|
{
|
|
result.tidied_to_original.push_back(current);
|
|
}
|
|
|
|
BOOST_ASSERT(result.can_be_removed.size() == params.coordinates.size());
|
|
|
|
// We have to filter parallel arrays that may be empty or the exact same size.
|
|
// result.parameters contains an empty MatchParameters at this point: conditionally fill.
|
|
|
|
for (std::size_t i = 0; i < result.can_be_removed.size(); ++i)
|
|
{
|
|
if (!result.can_be_removed[i])
|
|
{
|
|
result.parameters.coordinates.push_back(params.coordinates[i]);
|
|
|
|
if (result.was_waypoint[i])
|
|
result.parameters.waypoints.push_back(result.parameters.coordinates.size() - 1);
|
|
if (!params.hints.empty())
|
|
result.parameters.hints.push_back(params.hints[i]);
|
|
|
|
if (!params.radiuses.empty())
|
|
result.parameters.radiuses.push_back(params.radiuses[i]);
|
|
|
|
if (!params.bearings.empty())
|
|
result.parameters.bearings.push_back(params.bearings[i]);
|
|
|
|
if (!params.timestamps.empty())
|
|
result.parameters.timestamps.push_back(params.timestamps[i]);
|
|
}
|
|
}
|
|
if (params.waypoints.empty())
|
|
result.parameters.waypoints.clear();
|
|
|
|
return result;
|
|
}
|
|
|
|
inline Result tidy(const MatchParameters ¶ms, Thresholds cfg = {15., 5})
|
|
{
|
|
BOOST_ASSERT(!params.coordinates.empty());
|
|
|
|
Result result;
|
|
|
|
result.can_be_removed.resize(params.coordinates.size(), false);
|
|
result.was_waypoint.resize(params.coordinates.size(), true);
|
|
if (!params.waypoints.empty())
|
|
{
|
|
for (const auto p : params.waypoints)
|
|
{
|
|
result.was_waypoint.set(p, false);
|
|
}
|
|
result.was_waypoint.flip();
|
|
}
|
|
|
|
result.tidied_to_original.push_back(0);
|
|
|
|
const auto uses_timestamps = !params.timestamps.empty();
|
|
|
|
Thresholds running{0., 0};
|
|
|
|
// Walk over adjacent (coord, ts)-pairs, with rhs being the candidate to discard or keep
|
|
for (std::size_t current = 0, next = 1; next < params.coordinates.size() - 1; ++current, ++next)
|
|
{
|
|
auto distance_delta = util::coordinate_calculation::haversineDistance(
|
|
params.coordinates[current], params.coordinates[next]);
|
|
running.distance_in_meters += distance_delta;
|
|
const auto over_distance = running.distance_in_meters >= cfg.distance_in_meters;
|
|
|
|
if (uses_timestamps)
|
|
{
|
|
auto duration_delta = params.timestamps[next] - params.timestamps[current];
|
|
running.duration_in_seconds += duration_delta;
|
|
const auto over_duration = running.duration_in_seconds >= cfg.duration_in_seconds;
|
|
|
|
if (over_distance && over_duration)
|
|
{
|
|
result.tidied_to_original.push_back(next);
|
|
running = {0., 0}; // reset running distance and time
|
|
}
|
|
else
|
|
{
|
|
result.can_be_removed.set(next, true);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (over_distance)
|
|
{
|
|
result.tidied_to_original.push_back(next);
|
|
running = {0., 0}; // reset running distance and time
|
|
}
|
|
else
|
|
{
|
|
result.can_be_removed.set(next, true);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Always use the last coordinate if more than two original coordinates
|
|
if (params.coordinates.size() > 1)
|
|
{
|
|
result.tidied_to_original.push_back(params.coordinates.size() - 1);
|
|
}
|
|
|
|
// We have to filter parallel arrays that may be empty or the exact same size.
|
|
// result.parameters contains an empty MatchParameters at this point: conditionally fill.
|
|
for (std::size_t i = 0; i < result.can_be_removed.size(); ++i)
|
|
{
|
|
if (!result.can_be_removed[i])
|
|
{
|
|
result.parameters.coordinates.push_back(params.coordinates[i]);
|
|
|
|
if (result.was_waypoint[i])
|
|
result.parameters.waypoints.push_back(result.parameters.coordinates.size() - 1);
|
|
if (!params.hints.empty())
|
|
result.parameters.hints.push_back(params.hints[i]);
|
|
|
|
if (!params.radiuses.empty())
|
|
result.parameters.radiuses.push_back(params.radiuses[i]);
|
|
|
|
if (!params.bearings.empty())
|
|
result.parameters.bearings.push_back(params.bearings[i]);
|
|
|
|
if (!params.timestamps.empty())
|
|
result.parameters.timestamps.push_back(params.timestamps[i]);
|
|
}
|
|
else
|
|
{
|
|
// one of the coordinates meant to be used as a waypoint was marked for removal
|
|
// update the original waypoint index to the new representative coordinate
|
|
const auto last_idx = result.parameters.coordinates.size() - 1;
|
|
if (result.was_waypoint[i] && (result.parameters.waypoints.back() != last_idx))
|
|
{
|
|
result.parameters.waypoints.push_back(last_idx);
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
} // ns tidy
|
|
} // ns api
|
|
} // ns engine
|
|
} // ns osrm
|
|
|
|
#endif
|