Move files in src/ include/

This commit is contained in:
Patrick Niklaus
2016-01-02 13:55:06 +01:00
parent 758d402305
commit bfc6c9b89d
184 changed files with 0 additions and 608 deletions
+247
View File
@@ -0,0 +1,247 @@
/*
Copyright (c) 2015, Project OSRM contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this list
of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef DISTANCE_TABLE_HPP
#define DISTANCE_TABLE_HPP
#include "plugin_base.hpp"
#include "../algorithms/object_encoder.hpp"
#include "../data_structures/query_edge.hpp"
#include "../data_structures/search_engine.hpp"
#include "../descriptors/descriptor_base.hpp"
#include "../util/json_renderer.hpp"
#include "../util/make_unique.hpp"
#include "../util/string_util.hpp"
#include "../util/timing_util.hpp"
#include <osrm/json_container.hpp>
#include <cstdlib>
#include <algorithm>
#include <memory>
#include <unordered_map>
#include <string>
#include <vector>
template <class DataFacadeT> class DistanceTablePlugin final : public BasePlugin
{
private:
std::unique_ptr<SearchEngine<DataFacadeT>> search_engine_ptr;
int max_locations_distance_table;
public:
explicit DistanceTablePlugin(DataFacadeT *facade, const int max_locations_distance_table)
: max_locations_distance_table(max_locations_distance_table), descriptor_string("table"),
facade(facade)
{
search_engine_ptr = osrm::make_unique<SearchEngine<DataFacadeT>>(facade);
}
virtual ~DistanceTablePlugin() {}
const std::string GetDescriptor() const override final { return descriptor_string; }
Status HandleRequest(const RouteParameters &route_parameters,
osrm::json::Object &json_result) override final
{
if (!check_all_coordinates(route_parameters.coordinates))
{
json_result.values["status_message"] = "Coordinates are invalid";
return Status::Error;
}
const auto &input_bearings = route_parameters.bearings;
if (input_bearings.size() > 0 &&
route_parameters.coordinates.size() != input_bearings.size())
{
json_result.values["status_message"] =
"Number of bearings does not match number of coordinates";
return Status::Error;
}
auto number_of_sources =
std::count_if(route_parameters.is_source.begin(), route_parameters.is_source.end(),
[](const bool is_source)
{
return is_source;
});
auto number_of_destination =
std::count_if(route_parameters.is_destination.begin(),
route_parameters.is_destination.end(), [](const bool is_destination)
{
return is_destination;
});
if (max_locations_distance_table > 0 &&
(number_of_sources * number_of_destination >
max_locations_distance_table * max_locations_distance_table))
{
json_result.values["status_message"] =
"Number of entries " + std::to_string(number_of_sources * number_of_destination) +
" is higher than current maximum (" +
std::to_string(max_locations_distance_table * max_locations_distance_table) + ")";
return Status::Error;
}
const bool checksum_OK = (route_parameters.check_sum == facade->GetCheckSum());
std::vector<PhantomNodePair> phantom_node_source_vector(number_of_sources);
std::vector<PhantomNodePair> phantom_node_target_vector(number_of_destination);
auto phantom_node_source_out_iter = phantom_node_source_vector.begin();
auto phantom_node_target_out_iter = phantom_node_target_vector.begin();
for (const auto i : osrm::irange<std::size_t>(0u, route_parameters.coordinates.size()))
{
if (checksum_OK && i < route_parameters.hints.size() &&
!route_parameters.hints[i].empty())
{
PhantomNode current_phantom_node;
ObjectEncoder::DecodeFromBase64(route_parameters.hints[i], current_phantom_node);
if (current_phantom_node.is_valid(facade->GetNumberOfNodes()))
{
if (route_parameters.is_source[i])
{
*phantom_node_source_out_iter =
std::make_pair(current_phantom_node, current_phantom_node);
if (route_parameters.is_destination[i])
{
*phantom_node_target_out_iter = *phantom_node_source_out_iter;
phantom_node_target_out_iter++;
}
phantom_node_source_out_iter++;
}
else
{
BOOST_ASSERT(route_parameters.is_destination[i] &&
!route_parameters.is_source[i]);
*phantom_node_target_out_iter =
std::make_pair(current_phantom_node, current_phantom_node);
phantom_node_target_out_iter++;
}
continue;
}
}
const int bearing = input_bearings.size() > 0 ? input_bearings[i].first : 0;
const int range = input_bearings.size() > 0
? (input_bearings[i].second ? *input_bearings[i].second : 10)
: 180;
if (route_parameters.is_source[i])
{
*phantom_node_source_out_iter =
facade->NearestPhantomNodeWithAlternativeFromBigComponent(
route_parameters.coordinates[i], bearing, range);
// we didn't found a fitting node, return error
if (!phantom_node_source_out_iter->first.is_valid(facade->GetNumberOfNodes()))
{
json_result.values["status_message"] =
std::string("Could not find a matching segment for coordinate ") + std::to_string(i);
return Status::NoSegment;
}
if (route_parameters.is_destination[i])
{
*phantom_node_target_out_iter = *phantom_node_source_out_iter;
phantom_node_target_out_iter++;
}
phantom_node_source_out_iter++;
}
else
{
BOOST_ASSERT(route_parameters.is_destination[i] && !route_parameters.is_source[i]);
*phantom_node_target_out_iter =
facade->NearestPhantomNodeWithAlternativeFromBigComponent(
route_parameters.coordinates[i], bearing, range);
// we didn't found a fitting node, return error
if (!phantom_node_target_out_iter->first.is_valid(facade->GetNumberOfNodes()))
{
json_result.values["status_message"] =
std::string("Could not find a matching segment for coordinate ") + std::to_string(i);
return Status::NoSegment;
}
phantom_node_target_out_iter++;
}
}
BOOST_ASSERT((phantom_node_source_out_iter - phantom_node_source_vector.begin()) ==
number_of_sources);
BOOST_ASSERT((phantom_node_target_out_iter - phantom_node_target_vector.begin()) ==
number_of_destination);
// FIXME we should clear phantom_node_source_vector and phantom_node_target_vector after
// this
auto snapped_source_phantoms = snapPhantomNodes(phantom_node_source_vector);
auto snapped_target_phantoms = snapPhantomNodes(phantom_node_target_vector);
auto result_table =
search_engine_ptr->distance_table(snapped_source_phantoms, snapped_target_phantoms);
if (!result_table)
{
json_result.values["status_message"] = "No distance table found";
return Status::EmptyResult;
}
osrm::json::Array matrix_json_array;
for (const auto row : osrm::irange<std::size_t>(0, number_of_sources))
{
osrm::json::Array json_row;
auto row_begin_iterator = result_table->begin() + (row * number_of_destination);
auto row_end_iterator = result_table->begin() + ((row + 1) * number_of_destination);
json_row.values.insert(json_row.values.end(), row_begin_iterator, row_end_iterator);
matrix_json_array.values.push_back(json_row);
}
json_result.values["distance_table"] = matrix_json_array;
osrm::json::Array target_coord_json_array;
for (const auto &phantom : snapped_target_phantoms)
{
osrm::json::Array json_coord;
json_coord.values.push_back(phantom.location.lat / COORDINATE_PRECISION);
json_coord.values.push_back(phantom.location.lon / COORDINATE_PRECISION);
target_coord_json_array.values.push_back(json_coord);
}
json_result.values["destination_coordinates"] = target_coord_json_array;
osrm::json::Array source_coord_json_array;
for (const auto &phantom : snapped_source_phantoms)
{
osrm::json::Array json_coord;
json_coord.values.push_back(phantom.location.lat / COORDINATE_PRECISION);
json_coord.values.push_back(phantom.location.lon / COORDINATE_PRECISION);
source_coord_json_array.values.push_back(json_coord);
}
json_result.values["source_coordinates"] = source_coord_json_array;
return Status::Ok;
}
private:
std::string descriptor_string;
DataFacadeT *facade;
};
#endif // DISTANCE_TABLE_HPP
+106
View File
@@ -0,0 +1,106 @@
/*
Copyright (c) 2015, Project OSRM contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this list
of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef HELLO_WORLD_HPP
#define HELLO_WORLD_HPP
#include "plugin_base.hpp"
#include "../util/json_renderer.hpp"
#include <osrm/json_container.hpp>
#include <string>
class HelloWorldPlugin final : public BasePlugin
{
private:
std::string temp_string;
public:
HelloWorldPlugin() : descriptor_string("hello") {}
virtual ~HelloWorldPlugin() {}
const std::string GetDescriptor() const override final { return descriptor_string; }
Status HandleRequest(const RouteParameters &routeParameters,
osrm::json::Object &json_result) override final
{
std::string temp_string;
json_result.values["title"] = "Hello World";
temp_string = std::to_string(routeParameters.zoom_level);
json_result.values["zoom_level"] = temp_string;
temp_string = std::to_string(routeParameters.check_sum);
json_result.values["check_sum"] = temp_string;
json_result.values["instructions"] = (routeParameters.print_instructions ? "yes" : "no");
json_result.values["geometry"] = (routeParameters.geometry ? "yes" : "no");
json_result.values["compression"] = (routeParameters.compression ? "yes" : "no");
json_result.values["output_format"] =
(!routeParameters.output_format.empty() ? "yes" : "no");
json_result.values["jsonp_parameter"] =
(!routeParameters.jsonp_parameter.empty() ? "yes" : "no");
json_result.values["language"] = (!routeParameters.language.empty() ? "yes" : "no");
temp_string = std::to_string(routeParameters.coordinates.size());
json_result.values["location_count"] = temp_string;
osrm::json::Array json_locations;
unsigned counter = 0;
for (const FixedPointCoordinate &coordinate : routeParameters.coordinates)
{
osrm::json::Object json_location;
osrm::json::Array json_coordinates;
json_coordinates.values.push_back(
static_cast<double>(coordinate.lat / COORDINATE_PRECISION));
json_coordinates.values.push_back(
static_cast<double>(coordinate.lon / COORDINATE_PRECISION));
json_location.values[std::to_string(counter)] = json_coordinates;
json_locations.values.push_back(json_location);
++counter;
}
json_result.values["locations"] = json_locations;
json_result.values["hint_count"] = routeParameters.hints.size();
osrm::json::Array json_hints;
counter = 0;
for (const std::string &current_hint : routeParameters.hints)
{
json_hints.values.push_back(current_hint);
++counter;
}
json_result.values["hints"] = json_hints;
return Status::Ok;
}
private:
std::string descriptor_string;
};
#endif // HELLO_WORLD_HPP
+417
View File
@@ -0,0 +1,417 @@
/*
Copyright (c) 2015, Project OSRM contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this list
of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef MATCH_HPP
#define MATCH_HPP
#include "plugin_base.hpp"
#include "../algorithms/bayes_classifier.hpp"
#include "../algorithms/object_encoder.hpp"
#include "../data_structures/search_engine.hpp"
#include "../descriptors/descriptor_base.hpp"
#include "../descriptors/json_descriptor.hpp"
#include "../routing_algorithms/map_matching.hpp"
#include "../util/compute_angle.hpp"
#include "../util/integer_range.hpp"
#include "../util/json_logger.hpp"
#include "../util/json_util.hpp"
#include "../util/string_util.hpp"
#include <cstdlib>
#include <algorithm>
#include <memory>
#include <string>
#include <vector>
template <class DataFacadeT> class MapMatchingPlugin : public BasePlugin
{
constexpr static const unsigned max_number_of_candidates = 10;
std::shared_ptr<SearchEngine<DataFacadeT>> search_engine_ptr;
using ClassifierT = BayesClassifier<LaplaceDistribution, LaplaceDistribution, double>;
using TraceClassification = ClassifierT::ClassificationT;
public:
MapMatchingPlugin(DataFacadeT *facade, const int max_locations_map_matching)
: descriptor_string("match"), facade(facade),
max_locations_map_matching(max_locations_map_matching),
// the values where derived from fitting a laplace distribution
// to the values of manually classified traces
classifier(LaplaceDistribution(0.005986, 0.016646),
LaplaceDistribution(0.054385, 0.458432),
0.696774) // valid apriori probability
{
search_engine_ptr = std::make_shared<SearchEngine<DataFacadeT>>(facade);
}
virtual ~MapMatchingPlugin() {}
const std::string GetDescriptor() const final override { return descriptor_string; }
TraceClassification
classify(const float trace_length, const float matched_length, const int removed_points) const
{
(void)removed_points; // unused
const double distance_feature = -std::log(trace_length) + std::log(matched_length);
// matched to the same point
if (!std::isfinite(distance_feature))
{
return std::make_pair(ClassifierT::ClassLabel::NEGATIVE, 1.0);
}
const auto label_with_confidence = classifier.classify(distance_feature);
return label_with_confidence;
}
osrm::matching::CandidateLists getCandidates(
const std::vector<FixedPointCoordinate> &input_coords,
const std::vector<std::pair<const int, const boost::optional<int>>> &input_bearings,
const double gps_precision,
std::vector<double> &sub_trace_lengths)
{
osrm::matching::CandidateLists candidates_lists;
double query_radius = 10 * gps_precision;
double last_distance =
coordinate_calculation::haversine_distance(input_coords[0], input_coords[1]);
sub_trace_lengths.resize(input_coords.size());
sub_trace_lengths[0] = 0;
for (const auto current_coordinate : osrm::irange<std::size_t>(0, input_coords.size()))
{
bool allow_uturn = false;
if (0 < current_coordinate)
{
last_distance = coordinate_calculation::haversine_distance(
input_coords[current_coordinate - 1], input_coords[current_coordinate]);
sub_trace_lengths[current_coordinate] +=
sub_trace_lengths[current_coordinate - 1] + last_distance;
}
if (input_coords.size() - 1 > current_coordinate && 0 < current_coordinate)
{
double turn_angle = ComputeAngle::OfThreeFixedPointCoordinates(
input_coords[current_coordinate - 1], input_coords[current_coordinate],
input_coords[current_coordinate + 1]);
// sharp turns indicate a possible uturn
if (turn_angle <= 90.0 || turn_angle >= 270.0)
{
allow_uturn = true;
}
}
// Use bearing values if supplied, otherwise fallback to 0,180 defaults
auto bearing = input_bearings.size() > 0 ? input_bearings[current_coordinate].first : 0;
auto range = input_bearings.size() > 0
? (input_bearings[current_coordinate].second
? *input_bearings[current_coordinate].second
: 10)
: 180;
auto candidates = facade->NearestPhantomNodesInRange(input_coords[current_coordinate],
query_radius, bearing, range);
if (candidates.size() == 0)
{
break;
}
// sort by foward id, then by reverse id and then by distance
std::sort(
candidates.begin(), candidates.end(),
[](const PhantomNodeWithDistance &lhs, const PhantomNodeWithDistance &rhs)
{
return lhs.phantom_node.forward_node_id < rhs.phantom_node.forward_node_id ||
(lhs.phantom_node.forward_node_id == rhs.phantom_node.forward_node_id &&
(lhs.phantom_node.reverse_node_id < rhs.phantom_node.reverse_node_id ||
(lhs.phantom_node.reverse_node_id ==
rhs.phantom_node.reverse_node_id &&
lhs.distance < rhs.distance)));
});
auto new_end = std::unique(
candidates.begin(), candidates.end(),
[](const PhantomNodeWithDistance &lhs, const PhantomNodeWithDistance &rhs)
{
return lhs.phantom_node.forward_node_id == rhs.phantom_node.forward_node_id &&
lhs.phantom_node.reverse_node_id == rhs.phantom_node.reverse_node_id;
});
candidates.resize(new_end - candidates.begin());
if (!allow_uturn)
{
const auto compact_size = candidates.size();
for (const auto i : osrm::irange<std::size_t>(0, compact_size))
{
// Split edge if it is bidirectional and append reverse direction to end of list
if (candidates[i].phantom_node.forward_node_id != SPECIAL_NODEID &&
candidates[i].phantom_node.reverse_node_id != SPECIAL_NODEID)
{
PhantomNode reverse_node(candidates[i].phantom_node);
reverse_node.forward_node_id = SPECIAL_NODEID;
candidates.push_back(
PhantomNodeWithDistance{reverse_node, candidates[i].distance});
candidates[i].phantom_node.reverse_node_id = SPECIAL_NODEID;
}
}
}
// sort by distance to make pruning effective
std::sort(candidates.begin(), candidates.end(),
[](const PhantomNodeWithDistance &lhs, const PhantomNodeWithDistance &rhs)
{
return lhs.distance < rhs.distance;
});
candidates_lists.push_back(std::move(candidates));
}
return candidates_lists;
}
osrm::json::Object submatchingToJSON(const osrm::matching::SubMatching &sub,
const RouteParameters &route_parameters,
const InternalRouteResult &raw_route)
{
osrm::json::Object subtrace;
if (route_parameters.classify)
{
subtrace.values["confidence"] = sub.confidence;
}
JSONDescriptor<DataFacadeT> json_descriptor(facade);
json_descriptor.SetConfig(route_parameters);
subtrace.values["hint_data"] = json_descriptor.BuildHintData(raw_route);
if (route_parameters.geometry || route_parameters.print_instructions)
{
DescriptionFactory factory;
FixedPointCoordinate current_coordinate;
factory.SetStartSegment(raw_route.segment_end_coordinates.front().source_phantom,
raw_route.source_traversed_in_reverse.front());
for (const auto i :
osrm::irange<std::size_t>(0, raw_route.unpacked_path_segments.size()))
{
for (const PathData &path_data : raw_route.unpacked_path_segments[i])
{
current_coordinate = facade->GetCoordinateOfNode(path_data.node);
factory.AppendSegment(current_coordinate, path_data);
}
factory.SetEndSegment(raw_route.segment_end_coordinates[i].target_phantom,
raw_route.target_traversed_in_reverse[i],
raw_route.is_via_leg(i));
}
factory.Run(route_parameters.zoom_level);
// we need because we don't run path simplification
for (auto &segment : factory.path_description)
{
segment.necessary = true;
}
if (route_parameters.geometry)
{
subtrace.values["geometry"] =
factory.AppendGeometryString(route_parameters.compression);
}
if (route_parameters.print_instructions)
{
std::vector<typename JSONDescriptor<DataFacadeT>::Segment> temp_segments;
subtrace.values["instructions"] =
json_descriptor.BuildTextualDescription(factory, temp_segments);
}
factory.BuildRouteSummary(factory.get_entire_length(), raw_route.shortest_path_length);
osrm::json::Object json_route_summary;
json_route_summary.values["total_distance"] = factory.summary.distance;
json_route_summary.values["total_time"] = factory.summary.duration;
subtrace.values["route_summary"] = json_route_summary;
}
subtrace.values["indices"] = osrm::json::make_array(sub.indices);
osrm::json::Array points;
for (const auto &node : sub.nodes)
{
points.values.emplace_back(
osrm::json::make_array(node.location.lat / COORDINATE_PRECISION,
node.location.lon / COORDINATE_PRECISION));
}
subtrace.values["matched_points"] = points;
osrm::json::Array names;
for (const auto &node : sub.nodes)
{
names.values.emplace_back(facade->get_name_for_id(node.name_id));
}
subtrace.values["matched_names"] = names;
return subtrace;
}
Status HandleRequest(const RouteParameters &route_parameters,
osrm::json::Object &json_result) final override
{
// enforce maximum number of locations for performance reasons
if (max_locations_map_matching > 0 &&
static_cast<int>(route_parameters.coordinates.size()) > max_locations_map_matching)
{
json_result.values["status_message"] = "Too many coodindates";
return Status::Error;
}
// check number of parameters
if (!check_all_coordinates(route_parameters.coordinates))
{
json_result.values["status_message"] = "Invalid coordinates";
return Status::Error;
}
std::vector<double> sub_trace_lengths;
const auto &input_coords = route_parameters.coordinates;
const auto &input_timestamps = route_parameters.timestamps;
const auto &input_bearings = route_parameters.bearings;
if (input_timestamps.size() > 0 && input_coords.size() != input_timestamps.size())
{
json_result.values["status_message"] =
"Number of timestamps does not match number of coordinates";
return Status::Error;
}
if (input_bearings.size() > 0 && input_coords.size() != input_bearings.size())
{
json_result.values["status_message"] =
"Number of bearings does not match number of coordinates";
return Status::Error;
}
// enforce maximum number of locations for performance reasons
if (static_cast<int>(input_coords.size()) < 2)
{
json_result.values["status_message"] = "At least two coordinates needed";
return Status::Error;
}
const auto candidates_lists = getCandidates(
input_coords, input_bearings, route_parameters.gps_precision, sub_trace_lengths);
if (candidates_lists.size() != input_coords.size())
{
BOOST_ASSERT(candidates_lists.size() < input_coords.size());
json_result.values["status_message"] =
std::string("Could not find a matching segment for coordinate ") +
std::to_string(candidates_lists.size());
return Status::NoSegment;
}
// setup logging if enabled
if (osrm::json::Logger::get())
osrm::json::Logger::get()->initialize("matching");
// call the actual map matching
osrm::matching::SubMatchingList sub_matchings;
search_engine_ptr->map_matching(candidates_lists, input_coords, input_timestamps,
route_parameters.matching_beta,
route_parameters.gps_precision, sub_matchings);
osrm::json::Array matchings;
for (auto &sub : sub_matchings)
{
// classify result
if (route_parameters.classify)
{
double trace_length =
sub_trace_lengths[sub.indices.back()] - sub_trace_lengths[sub.indices.front()];
TraceClassification classification =
classify(trace_length, sub.length,
(sub.indices.back() - sub.indices.front() + 1) - sub.nodes.size());
if (classification.first == ClassifierT::ClassLabel::POSITIVE)
{
sub.confidence = classification.second;
}
else
{
sub.confidence = 1 - classification.second;
}
}
BOOST_ASSERT(sub.nodes.size() > 1);
// FIXME we only run this to obtain the geometry
// The clean way would be to get this directly from the map matching plugin
InternalRouteResult raw_route;
PhantomNodes current_phantom_node_pair;
for (unsigned i = 0; i < sub.nodes.size() - 1; ++i)
{
current_phantom_node_pair.source_phantom = sub.nodes[i];
current_phantom_node_pair.target_phantom = sub.nodes[i + 1];
BOOST_ASSERT(current_phantom_node_pair.source_phantom.is_valid());
BOOST_ASSERT(current_phantom_node_pair.target_phantom.is_valid());
raw_route.segment_end_coordinates.emplace_back(current_phantom_node_pair);
}
search_engine_ptr->shortest_path(
raw_route.segment_end_coordinates,
std::vector<bool>(raw_route.segment_end_coordinates.size() + 1, true), raw_route);
BOOST_ASSERT(raw_route.shortest_path_length != INVALID_EDGE_WEIGHT);
matchings.values.emplace_back(submatchingToJSON(sub, route_parameters, raw_route));
}
if (osrm::json::Logger::get())
osrm::json::Logger::get()->render("matching", json_result);
json_result.values["matchings"] = matchings;
if (sub_matchings.empty())
{
json_result.values["status_message"] = "Cannot find matchings";
return Status::EmptyResult;
}
json_result.values["status_message"] = "Found matchings";
return Status::Ok;
}
private:
std::string descriptor_string;
DataFacadeT *facade;
int max_locations_map_matching;
ClassifierT classifier;
};
#endif // MATCH_HPP
+128
View File
@@ -0,0 +1,128 @@
/*
Copyright (c) 2015, Project OSRM contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this list
of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef NEAREST_HPP
#define NEAREST_HPP
#include "plugin_base.hpp"
#include "../data_structures/phantom_node.hpp"
#include "../util/integer_range.hpp"
#include "../util/json_renderer.hpp"
#include <osrm/json_container.hpp>
#include <string>
/*
* This Plugin locates the nearest point on a street in the road network for a given coordinate.
*/
template <class DataFacadeT> class NearestPlugin final : public BasePlugin
{
public:
explicit NearestPlugin(DataFacadeT *facade) : facade(facade), descriptor_string("nearest") {}
const std::string GetDescriptor() const override final { return descriptor_string; }
Status HandleRequest(const RouteParameters &route_parameters,
osrm::json::Object &json_result) override final
{
// check number of parameters
if (route_parameters.coordinates.empty() ||
!route_parameters.coordinates.front().is_valid())
{
return Status::Error;
}
const auto &input_bearings = route_parameters.bearings;
if (input_bearings.size() > 0 &&
route_parameters.coordinates.size() != input_bearings.size())
{
json_result.values["status_message"] =
"Number of bearings does not match number of coordinates";
return Status::Error;
}
auto number_of_results = static_cast<std::size_t>(route_parameters.num_results);
const int bearing = input_bearings.size() > 0 ? input_bearings.front().first : 0;
const int range =
input_bearings.size() > 0
? (input_bearings.front().second ? *input_bearings.front().second : 10)
: 180;
auto phantom_node_vector = facade->NearestPhantomNodes(route_parameters.coordinates.front(),
number_of_results, bearing, range);
if (phantom_node_vector.empty())
{
json_result.values["status_message"] =
std::string("Could not find a matching segments for coordinate");
return Status::NoSegment;
}
else
{
json_result.values["status_message"] = "Found nearest edge";
if (number_of_results > 1)
{
osrm::json::Array results;
auto vector_length = phantom_node_vector.size();
for (const auto i :
osrm::irange<std::size_t>(0, std::min(number_of_results, vector_length)))
{
const auto &node = phantom_node_vector[i].phantom_node;
osrm::json::Array json_coordinate;
osrm::json::Object result;
json_coordinate.values.push_back(node.location.lat / COORDINATE_PRECISION);
json_coordinate.values.push_back(node.location.lon / COORDINATE_PRECISION);
result.values["mapped coordinate"] = json_coordinate;
result.values["name"] = facade->get_name_for_id(node.name_id);
results.values.push_back(result);
}
json_result.values["results"] = results;
}
else
{
osrm::json::Array json_coordinate;
json_coordinate.values.push_back(
phantom_node_vector.front().phantom_node.location.lat / COORDINATE_PRECISION);
json_coordinate.values.push_back(
phantom_node_vector.front().phantom_node.location.lon / COORDINATE_PRECISION);
json_result.values["mapped_coordinate"] = json_coordinate;
json_result.values["name"] =
facade->get_name_for_id(phantom_node_vector.front().phantom_node.name_id);
}
}
return Status::Ok;
}
private:
DataFacadeT *facade;
std::string descriptor_string;
};
#endif /* NEAREST_HPP */
+136
View File
@@ -0,0 +1,136 @@
/*
Copyright (c) 2015, Project OSRM contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this list
of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef BASE_PLUGIN_HPP
#define BASE_PLUGIN_HPP
#include "../data_structures/phantom_node.hpp"
#include <osrm/coordinate.hpp>
#include <osrm/json_container.hpp>
#include <osrm/route_parameters.hpp>
#include <algorithm>
#include <string>
#include <vector>
class BasePlugin
{
public:
enum class Status : int
{
Ok = 200,
EmptyResult = 207,
NoSegment = 208,
Error = 400
};
BasePlugin() {}
// Maybe someone can explain the pure virtual destructor thing to me (dennis)
virtual ~BasePlugin() {}
virtual const std::string GetDescriptor() const = 0;
virtual Status HandleRequest(const RouteParameters &, osrm::json::Object &) = 0;
virtual bool check_all_coordinates(const std::vector<FixedPointCoordinate> &coordinates,
const unsigned min = 2) const final
{
if (min > coordinates.size() || std::any_of(std::begin(coordinates), std::end(coordinates),
[](const FixedPointCoordinate &coordinate)
{
return !coordinate.is_valid();
}))
{
return false;
}
return true;
}
// Decides whether to use the phantom node from a big or small component if both are found.
// Returns true if all phantom nodes are in the same component after snapping.
std::vector<PhantomNode> snapPhantomNodes(
const std::vector<std::pair<PhantomNode, PhantomNode>> &phantom_node_pair_list) const
{
const auto check_component_id_is_tiny =
[](const std::pair<PhantomNode, PhantomNode> &phantom_pair)
{
return phantom_pair.first.component.is_tiny;
};
// are all phantoms from a tiny cc?
const auto check_all_in_same_component =
[](const std::vector<std::pair<PhantomNode, PhantomNode>> &nodes)
{
const auto component_id = nodes.front().first.component.id;
return std::all_of(std::begin(nodes), std::end(nodes),
[component_id](const PhantomNodePair &phantom_pair)
{
return component_id == phantom_pair.first.component.id;
});
};
const auto fallback_to_big_component =
[](const std::pair<PhantomNode, PhantomNode> &phantom_pair)
{
if (phantom_pair.first.component.is_tiny && phantom_pair.second.is_valid() &&
!phantom_pair.second.component.is_tiny)
{
return phantom_pair.second;
}
return phantom_pair.first;
};
const auto use_closed_phantom = [](const std::pair<PhantomNode, PhantomNode> &phantom_pair)
{
return phantom_pair.first;
};
const bool every_phantom_is_in_tiny_cc =
std::all_of(std::begin(phantom_node_pair_list), std::end(phantom_node_pair_list),
check_component_id_is_tiny);
auto all_in_same_component = check_all_in_same_component(phantom_node_pair_list);
std::vector<PhantomNode> snapped_phantoms;
snapped_phantoms.reserve(phantom_node_pair_list.size());
// The only case we don't snap to the big component if all phantoms are in the same small
// component
if (every_phantom_is_in_tiny_cc && all_in_same_component)
{
std::transform(phantom_node_pair_list.begin(), phantom_node_pair_list.end(),
std::back_inserter(snapped_phantoms), use_closed_phantom);
}
else
{
std::transform(phantom_node_pair_list.begin(), phantom_node_pair_list.end(),
std::back_inserter(snapped_phantoms), fallback_to_big_component);
}
return snapped_phantoms;
}
};
#endif /* BASE_PLUGIN_HPP */
+62
View File
@@ -0,0 +1,62 @@
/*
Copyright (c) 2015, Project OSRM contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this list
of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef TIMESTAMP_PLUGIN_H
#define TIMESTAMP_PLUGIN_H
#include "plugin_base.hpp"
#include "../util/json_renderer.hpp"
#include <osrm/json_container.hpp>
#include <string>
template <class DataFacadeT> class TimestampPlugin final : public BasePlugin
{
public:
explicit TimestampPlugin(const DataFacadeT *facade)
: facade(facade), descriptor_string("timestamp")
{
}
const std::string GetDescriptor() const override final { return descriptor_string; }
Status HandleRequest(const RouteParameters &route_parameters,
osrm::json::Object &json_result) override final
{
(void)route_parameters; // unused
const std::string timestamp = facade->GetTimestamp();
json_result.values["timestamp"] = timestamp;
return Status::Ok;
}
private:
const DataFacadeT *facade;
std::string descriptor_string;
};
#endif /* TIMESTAMP_PLUGIN_H */
+400
View File
@@ -0,0 +1,400 @@
/*
Copyright (c) 2015, Project OSRM contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this list
of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef TRIP_HPP
#define TRIP_HPP
#include "plugin_base.hpp"
#include "../algorithms/object_encoder.hpp"
#include "../algorithms/tarjan_scc.hpp"
#include "../algorithms/trip_nearest_neighbour.hpp"
#include "../algorithms/trip_farthest_insertion.hpp"
#include "../algorithms/trip_brute_force.hpp"
#include "../data_structures/search_engine.hpp"
#include "../data_structures/matrix_graph_wrapper.hpp" // wrapper to use tarjan
// scc on dist table
#include "../descriptors/descriptor_base.hpp" // to make json output
#include "../descriptors/json_descriptor.hpp" // to make json output
#include "../util/make_unique.hpp"
#include "../util/timing_util.hpp" // to time runtime
//#include "../util/simple_logger.hpp" // for logging output
#include "../util/dist_table_wrapper.hpp" // to access the dist
// table more easily
#include <osrm/json_container.hpp>
#include <boost/assert.hpp>
#include <cstdlib>
#include <algorithm>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include <iterator>
template <class DataFacadeT> class RoundTripPlugin final : public BasePlugin
{
private:
std::string descriptor_string;
DataFacadeT *facade;
std::unique_ptr<SearchEngine<DataFacadeT>> search_engine_ptr;
int max_locations_trip;
public:
explicit RoundTripPlugin(DataFacadeT *facade, int max_locations_trip)
: descriptor_string("trip"), facade(facade), max_locations_trip(max_locations_trip)
{
search_engine_ptr = osrm::make_unique<SearchEngine<DataFacadeT>>(facade);
}
const std::string GetDescriptor() const override final { return descriptor_string; }
std::vector<PhantomNode> GetPhantomNodes(const RouteParameters &route_parameters)
{
const bool checksum_OK = (route_parameters.check_sum == facade->GetCheckSum());
const auto &input_bearings = route_parameters.bearings;
std::vector<PhantomNode> phantom_node_list;
phantom_node_list.reserve(route_parameters.coordinates.size());
// find phantom nodes for all input coords
for (const auto i : osrm::irange<std::size_t>(0, route_parameters.coordinates.size()))
{
// if client hints are helpful, encode hints
if (checksum_OK && i < route_parameters.hints.size() &&
!route_parameters.hints[i].empty())
{
PhantomNode current_phantom_node;
ObjectEncoder::DecodeFromBase64(route_parameters.hints[i], current_phantom_node);
if (current_phantom_node.is_valid(facade->GetNumberOfNodes()))
{
phantom_node_list.push_back(std::move(current_phantom_node));
continue;
}
}
const int bearing = input_bearings.size() > 0 ? input_bearings[i].first : 0;
const int range = input_bearings.size() > 0
? (input_bearings[i].second ? *input_bearings[i].second : 10)
: 180;
auto results = facade->NearestPhantomNodes(route_parameters.coordinates[i], 1, bearing, range);
if (results.empty())
{
break;
}
phantom_node_list.push_back(std::move(results.front().phantom_node));
BOOST_ASSERT(phantom_node_list.back().is_valid(facade->GetNumberOfNodes()));
}
return phantom_node_list;
}
// Object to hold all strongly connected components (scc) of a graph
// to access all graphs with component ID i, get the iterators by:
// auto start = std::begin(scc_component.component) + scc_component.range[i];
// auto end = std::begin(scc_component.component) + scc_component.range[i+1];
struct SCC_Component
{
// in_component: all NodeIDs sorted by component ID
// in_range: index where a new component starts
//
// example: NodeID 0, 1, 2, 4, 5 are in component 0
// NodeID 3, 6, 7, 8 are in component 1
// => in_component = [0, 1, 2, 4, 5, 3, 6, 7, 8]
// => in_range = [0, 5]
SCC_Component(std::vector<NodeID> in_component, std::vector<size_t> in_range)
: component(std::move(in_component)), range(std::move(in_range))
{
range.push_back(component.size());
BOOST_ASSERT_MSG(component.size() >= range.size(),
"scc component and its ranges do not match");
BOOST_ASSERT_MSG(component.size() > 0, "there's no scc component");
BOOST_ASSERT_MSG(*std::max_element(range.begin(), range.end()) <= component.size(),
"scc component ranges are out of bound");
BOOST_ASSERT_MSG(*std::min_element(range.begin(), range.end()) >= 0,
"invalid scc component range");
BOOST_ASSERT_MSG(std::is_sorted(std::begin(range), std::end(range)),
"invalid component ranges");
};
// constructor to use when whole graph is one single scc
SCC_Component(std::vector<NodeID> in_component)
: component(std::move(in_component)), range({0, component.size()}){};
std::size_t GetNumberOfComponents() const
{
BOOST_ASSERT_MSG(range.size() > 0, "there's no range");
return range.size() - 1;
}
const std::vector<NodeID> component;
std::vector<std::size_t> range;
};
// takes the number of locations and its distance matrix,
// identifies and splits the graph in its strongly connected components (scc)
// and returns an SCC_Component
SCC_Component SplitUnaccessibleLocations(const std::size_t number_of_locations,
const DistTableWrapper<EdgeWeight> &result_table)
{
if (std::find(std::begin(result_table), std::end(result_table), INVALID_EDGE_WEIGHT) ==
std::end(result_table))
{
// whole graph is one scc
std::vector<NodeID> location_ids(number_of_locations);
std::iota(std::begin(location_ids), std::end(location_ids), 0);
return SCC_Component(std::move(location_ids));
}
// Run TarjanSCC
auto wrapper = std::make_shared<MatrixGraphWrapper<EdgeWeight>>(result_table.GetTable(),
number_of_locations);
auto scc = TarjanSCC<MatrixGraphWrapper<EdgeWeight>>(wrapper);
scc.run();
const auto number_of_components = scc.get_number_of_components();
std::vector<std::size_t> range_insertion;
std::vector<std::size_t> range;
range_insertion.reserve(number_of_components);
range.reserve(number_of_components);
std::vector<NodeID> components(number_of_locations, 0);
std::size_t prefix = 0;
for (std::size_t j = 0; j < number_of_components; ++j)
{
range_insertion.push_back(prefix);
range.push_back(prefix);
prefix += scc.get_component_size(j);
}
for (std::size_t i = 0; i < number_of_locations; ++i)
{
components[range_insertion[scc.get_component_id(i)]] = i;
++range_insertion[scc.get_component_id(i)];
}
return SCC_Component(std::move(components), std::move(range));
}
void SetLocPermutationOutput(const std::vector<NodeID> &permutation,
osrm::json::Object &json_result)
{
osrm::json::Array json_permutation;
json_permutation.values.insert(std::end(json_permutation.values), std::begin(permutation),
std::end(permutation));
json_result.values["permutation"] = json_permutation;
}
InternalRouteResult ComputeRoute(const std::vector<PhantomNode> &phantom_node_list,
const RouteParameters &route_parameters,
const std::vector<NodeID> &trip)
{
InternalRouteResult min_route;
// given he final trip, compute total distance and return the route and location permutation
PhantomNodes viapoint;
const auto start = std::begin(trip);
const auto end = std::end(trip);
// computes a roundtrip from the nodes in trip
for (auto it = start; it != end; ++it)
{
const auto from_node = *it;
// if from_node is the last node, compute the route from the last to the first location
const auto to_node = std::next(it) != end ? *std::next(it) : *start;
viapoint = PhantomNodes{phantom_node_list[from_node], phantom_node_list[to_node]};
min_route.segment_end_coordinates.emplace_back(viapoint);
}
BOOST_ASSERT(min_route.segment_end_coordinates.size() == trip.size());
std::vector<bool> uturns(trip.size() + 1);
std::transform(trip.begin(), trip.end(), uturns.begin(),
[&route_parameters](const NodeID idx)
{
return route_parameters.uturns[idx];
});
uturns.back() = route_parameters.uturns[trip.front()];
search_engine_ptr->shortest_path(min_route.segment_end_coordinates, uturns, min_route);
BOOST_ASSERT_MSG(min_route.shortest_path_length < INVALID_EDGE_WEIGHT, "unroutable route");
return min_route;
}
Status HandleRequest(const RouteParameters &route_parameters,
osrm::json::Object &json_result) override final
{
if (max_locations_trip > 0 &&
(static_cast<int>(route_parameters.coordinates.size()) > max_locations_trip))
{
json_result.values["status_message"] =
"Number of entries " + std::to_string(route_parameters.coordinates.size()) +
" is higher than current maximum (" + std::to_string(max_locations_trip) + ")";
return Status::Error;
}
// check if all inputs are coordinates
if (!check_all_coordinates(route_parameters.coordinates))
{
json_result.values["status_message"] = "Invalid coordinates";
return Status::Error;
}
const auto &input_bearings = route_parameters.bearings;
if (input_bearings.size() > 0 &&
route_parameters.coordinates.size() != input_bearings.size())
{
json_result.values["status_message"] =
"Number of bearings does not match number of coordinates";
return Status::Error;
}
// get phantom nodes
auto phantom_node_list = GetPhantomNodes(route_parameters);
if (phantom_node_list.size() != route_parameters.coordinates.size())
{
BOOST_ASSERT(phantom_node_list.size() < route_parameters.coordinates.size());
json_result.values["status_message"] =
std::string("Could not find a matching segment for coordinate ") +
std::to_string(phantom_node_list.size());
return Status::NoSegment;
}
const auto number_of_locations = phantom_node_list.size();
// compute the distance table of all phantom nodes
const auto result_table = DistTableWrapper<EdgeWeight>(
*search_engine_ptr->distance_table(phantom_node_list, phantom_node_list),
number_of_locations);
if (result_table.size() == 0)
{
return Status::Error;
}
const constexpr std::size_t BF_MAX_FEASABLE = 10;
BOOST_ASSERT_MSG(result_table.size() == number_of_locations * number_of_locations,
"Distance Table has wrong size");
// get scc components
SCC_Component scc = SplitUnaccessibleLocations(number_of_locations, result_table);
using NodeIDIterator = typename std::vector<NodeID>::const_iterator;
std::vector<std::vector<NodeID>> route_result;
route_result.reserve(scc.GetNumberOfComponents());
TIMER_START(TRIP_TIMER);
// run Trip computation for every SCC
for (std::size_t k = 0; k < scc.GetNumberOfComponents(); ++k)
{
const auto component_size = scc.range[k + 1] - scc.range[k];
BOOST_ASSERT_MSG(component_size >= 0, "invalid component size");
if (component_size > 1)
{
std::vector<NodeID> scc_route;
NodeIDIterator start = std::begin(scc.component) + scc.range[k];
NodeIDIterator end = std::begin(scc.component) + scc.range[k + 1];
if (component_size < BF_MAX_FEASABLE)
{
scc_route =
osrm::trip::BruteForceTrip(start, end, number_of_locations, result_table);
}
else
{
scc_route = osrm::trip::FarthestInsertionTrip(start, end, number_of_locations,
result_table);
}
// use this output if debugging of route is needed:
// SimpleLogger().Write() << "Route #" << k << ": " << [&scc_route]()
// {
// std::string s = "";
// for (auto x : scc_route)
// {
// s += std::to_string(x) + " ";
// }
// return s;
// }();
route_result.push_back(std::move(scc_route));
}
else
{
// if component only consists of one node, add it to the result routes
route_result.emplace_back(scc.component[scc.range[k]]);
}
}
// compute all round trip routes
std::vector<InternalRouteResult> comp_route;
comp_route.reserve(route_result.size());
for (auto &elem : route_result)
{
comp_route.push_back(ComputeRoute(phantom_node_list, route_parameters, elem));
}
TIMER_STOP(TRIP_TIMER);
// prepare JSON output
// create a json object for every trip
osrm::json::Array trip;
for (std::size_t i = 0; i < route_result.size(); ++i)
{
std::unique_ptr<BaseDescriptor<DataFacadeT>> descriptor =
osrm::make_unique<JSONDescriptor<DataFacadeT>>(facade);
descriptor->SetConfig(route_parameters);
osrm::json::Object scc_trip;
// set permutation output
SetLocPermutationOutput(route_result[i], scc_trip);
// set viaroute output
descriptor->Run(comp_route[i], scc_trip);
trip.values.push_back(std::move(scc_trip));
}
if (trip.values.empty())
{
json_result.values["status_message"] = "Cannot find trips";
return Status::EmptyResult;
}
json_result.values["trips"] = std::move(trip);
json_result.values["status_message"] = "Found trips";
return Status::Ok;
}
};
#endif // TRIP_HPP
+212
View File
@@ -0,0 +1,212 @@
/*
Copyright (c) 2015, Project OSRM contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this list
of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef VIA_ROUTE_HPP
#define VIA_ROUTE_HPP
#include "plugin_base.hpp"
#include "../algorithms/object_encoder.hpp"
#include "../data_structures/search_engine.hpp"
#include "../descriptors/descriptor_base.hpp"
#include "../descriptors/gpx_descriptor.hpp"
#include "../descriptors/json_descriptor.hpp"
#include "../util/integer_range.hpp"
#include "../util/json_renderer.hpp"
#include "../util/make_unique.hpp"
#include "../util/simple_logger.hpp"
#include "../util/timing_util.hpp"
#include <osrm/json_container.hpp>
#include <cstdlib>
#include <algorithm>
#include <memory>
#include <string>
#include <vector>
template <class DataFacadeT> class ViaRoutePlugin final : public BasePlugin
{
private:
DescriptorTable descriptor_table;
std::string descriptor_string;
std::unique_ptr<SearchEngine<DataFacadeT>> search_engine_ptr;
DataFacadeT *facade;
int max_locations_viaroute;
public:
explicit ViaRoutePlugin(DataFacadeT *facade, int max_locations_viaroute)
: descriptor_string("viaroute"), facade(facade),
max_locations_viaroute(max_locations_viaroute)
{
search_engine_ptr = osrm::make_unique<SearchEngine<DataFacadeT>>(facade);
descriptor_table.emplace("json", 0);
descriptor_table.emplace("gpx", 1);
// descriptor_table.emplace("geojson", 2);
}
virtual ~ViaRoutePlugin() {}
const std::string GetDescriptor() const override final { return descriptor_string; }
Status HandleRequest(const RouteParameters &route_parameters,
osrm::json::Object &json_result) override final
{
if (max_locations_viaroute > 0 &&
(static_cast<int>(route_parameters.coordinates.size()) > max_locations_viaroute))
{
json_result.values["status_message"] =
"Number of entries " + std::to_string(route_parameters.coordinates.size()) +
" is higher than current maximum (" + std::to_string(max_locations_viaroute) + ")";
return Status::Error;
}
if (!check_all_coordinates(route_parameters.coordinates))
{
json_result.values["status_message"] = "Invalid coordinates";
return Status::Error;
}
const auto &input_bearings = route_parameters.bearings;
if (input_bearings.size() > 0 &&
route_parameters.coordinates.size() != input_bearings.size())
{
json_result.values["status_message"] =
"Number of bearings does not match number of coordinate";
return Status::Error;
}
std::vector<PhantomNodePair> phantom_node_pair_list(route_parameters.coordinates.size());
const bool checksum_OK = (route_parameters.check_sum == facade->GetCheckSum());
for (const auto i : osrm::irange<std::size_t>(0, route_parameters.coordinates.size()))
{
if (checksum_OK && i < route_parameters.hints.size() &&
!route_parameters.hints[i].empty())
{
ObjectEncoder::DecodeFromBase64(route_parameters.hints[i],
phantom_node_pair_list[i].first);
if (phantom_node_pair_list[i].first.is_valid(facade->GetNumberOfNodes()))
{
continue;
}
}
const int bearing = input_bearings.size() > 0 ? input_bearings[i].first : 0;
const int range = input_bearings.size() > 0
? (input_bearings[i].second ? *input_bearings[i].second : 10)
: 180;
phantom_node_pair_list[i] = facade->NearestPhantomNodeWithAlternativeFromBigComponent(
route_parameters.coordinates[i], bearing, range);
// we didn't found a fitting node, return error
if (!phantom_node_pair_list[i].first.is_valid(facade->GetNumberOfNodes()))
{
json_result.values["status_message"] =
std::string("Could not find a matching segment for coordinate ") +
std::to_string(i);
return Status::NoSegment;
}
BOOST_ASSERT(phantom_node_pair_list[i].first.is_valid(facade->GetNumberOfNodes()));
BOOST_ASSERT(phantom_node_pair_list[i].second.is_valid(facade->GetNumberOfNodes()));
}
auto snapped_phantoms = snapPhantomNodes(phantom_node_pair_list);
InternalRouteResult raw_route;
auto build_phantom_pairs = [&raw_route](const PhantomNode &first_node,
const PhantomNode &second_node)
{
raw_route.segment_end_coordinates.push_back(PhantomNodes{first_node, second_node});
};
osrm::for_each_pair(snapped_phantoms, build_phantom_pairs);
if (1 == raw_route.segment_end_coordinates.size())
{
if (route_parameters.alternate_route)
{
search_engine_ptr->alternative_path(raw_route.segment_end_coordinates.front(),
raw_route);
}
else
{
search_engine_ptr->direct_shortest_path(raw_route.segment_end_coordinates,
route_parameters.uturns, raw_route);
}
}
else
{
search_engine_ptr->shortest_path(raw_route.segment_end_coordinates,
route_parameters.uturns, raw_route);
}
bool no_route = INVALID_EDGE_WEIGHT == raw_route.shortest_path_length;
std::unique_ptr<BaseDescriptor<DataFacadeT>> descriptor;
switch (descriptor_table.get_id(route_parameters.output_format))
{
case 1:
descriptor = osrm::make_unique<GPXDescriptor<DataFacadeT>>(facade);
break;
// case 2:
// descriptor = osrm::make_unique<GEOJSONDescriptor<DataFacadeT>>();
// break;
default:
descriptor = osrm::make_unique<JSONDescriptor<DataFacadeT>>(facade);
break;
}
descriptor->SetConfig(route_parameters);
descriptor->Run(raw_route, json_result);
// we can only know this after the fact, different SCC ids still
// allow for connection in one direction.
if (no_route)
{
auto first_component_id = snapped_phantoms.front().component.id;
auto not_in_same_component =
std::any_of(snapped_phantoms.begin(), snapped_phantoms.end(),
[first_component_id](const PhantomNode &node)
{
return node.component.id != first_component_id;
});
if (not_in_same_component)
{
json_result.values["status_message"] = "Impossible route between points";
return Status::EmptyResult;
}
}
else
{
json_result.values["status_message"] = "Found route between points";
}
return Status::Ok;
}
};
#endif // VIA_ROUTE_HPP