forked from organicmaps/organicmaps
[routing] Routing quality tool with mapbox support.
This commit is contained in:
parent
c0d3133e67
commit
e8ade13cce
7 changed files with 481 additions and 0 deletions
|
@ -2,6 +2,9 @@ project(routing_quality)
|
|||
|
||||
set(
|
||||
SRC
|
||||
mapbox/api.cpp
|
||||
mapbox/api.hpp
|
||||
mapbox/types.hpp
|
||||
utils.cpp
|
||||
utils.hpp
|
||||
waypoints.cpp
|
||||
|
@ -32,6 +35,8 @@ omim_link_libraries(
|
|||
${LIBZ}
|
||||
)
|
||||
|
||||
add_subdirectory(routing_quality_tool)
|
||||
|
||||
omim_add_test_subdirectory(routing_quality_tests)
|
||||
|
||||
link_qt5_core(${PROJECT_NAME})
|
||||
|
|
176
routing/routing_quality/mapbox/api.cpp
Normal file
176
routing/routing_quality/mapbox/api.cpp
Normal file
|
@ -0,0 +1,176 @@
|
|||
#include "routing/routing_quality/mapbox/api.hpp"
|
||||
|
||||
#include "routing/vehicle_mask.hpp"
|
||||
|
||||
#include "coding/file_writer.hpp"
|
||||
#include "coding/serdes_json.hpp"
|
||||
#include "coding/url_encode.hpp"
|
||||
#include "coding/writer.hpp"
|
||||
|
||||
#include "platform/http_client.hpp"
|
||||
|
||||
#include "geometry/latlon.hpp"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
using namespace std;
|
||||
using namespace string_literals;
|
||||
|
||||
namespace
|
||||
{
|
||||
string const kBaseURL = "https://api.mapbox.com/";
|
||||
string const kDirectionsApiVersion = "v5";
|
||||
string const kStylesApiVersion = "v1";
|
||||
|
||||
string VehicleTypeToMapboxType(routing::VehicleType type)
|
||||
{
|
||||
using routing::VehicleType;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case VehicleType::Car:
|
||||
return "mapbox/driving"s;
|
||||
case VehicleType::Pedestrian:
|
||||
return "mapbox/walking"s;
|
||||
case VehicleType::Bicycle:
|
||||
return "mapbox/cycling"s;
|
||||
case VehicleType::Transit:
|
||||
case VehicleType::Count:
|
||||
CHECK(false, ());
|
||||
return ""s;
|
||||
}
|
||||
|
||||
CHECK_SWITCH();
|
||||
}
|
||||
|
||||
string LatLonsToString(vector<ms::LatLon> const & coords)
|
||||
{
|
||||
ostringstream oss;
|
||||
auto const size = coords.size();
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
{
|
||||
auto const & ll = coords[i];
|
||||
oss << ll.lon << "," << ll.lat;
|
||||
if (i + 1 != size)
|
||||
oss << ";";
|
||||
}
|
||||
|
||||
oss << ".json";
|
||||
return UrlEncode(oss.str());
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace routing_quality
|
||||
{
|
||||
namespace mapbox
|
||||
{
|
||||
DirectionsResponse Api::MakeDirectionsRequest(RouteParams const & params) const
|
||||
{
|
||||
CHECK(!m_accessToken.empty(), ());
|
||||
platform::HttpClient request(GetDirectionsURL(params));
|
||||
DirectionsResponse ret;
|
||||
if (request.RunHttpRequest() && !request.WasRedirected() && request.ErrorCode() == 200)
|
||||
{
|
||||
auto const & response = request.ServerResponse();
|
||||
CHECK(!response.empty(), ());
|
||||
{
|
||||
coding::DeserializerJson des(response);
|
||||
des(ret);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CHECK(false, (request.ErrorCode(), request.ServerResponse()));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void Api::DrawRoutes(Geometry const & mapboxRoute, Geometry const & mapsmeRoute, string const & snapshotPath) const
|
||||
{
|
||||
CHECK(!m_accessToken.empty(), ());
|
||||
CHECK(!snapshotPath.empty(), ());
|
||||
|
||||
platform::HttpClient request(GetRoutesRepresentationURL(mapboxRoute, mapsmeRoute));
|
||||
if (request.RunHttpRequest() && !request.WasRedirected() && request.ErrorCode() == 200)
|
||||
{
|
||||
auto const & response = request.ServerResponse();
|
||||
FileWriter w(snapshotPath);
|
||||
w.Write(response.data(), response.size());
|
||||
return;
|
||||
}
|
||||
|
||||
CHECK(false, ("Mapbox api error:", request.ErrorCode()));
|
||||
}
|
||||
|
||||
// static
|
||||
vector<string> Api::GenerateWaypointsBasedOn(DirectionsResponse response)
|
||||
{
|
||||
auto & routes = response.m_routes;
|
||||
CHECK(!routes.empty(), ());
|
||||
vector<string> res;
|
||||
res.reserve(routes.size());
|
||||
for (auto & r : routes)
|
||||
{
|
||||
auto & coords = r.m_geometry.m_coordinates;
|
||||
// Leave at most 10 waypoints from mapbox route.
|
||||
Api::Sieve(coords, 10 /* maxRemainingNumber */);
|
||||
ostringstream oss;
|
||||
oss << "{";
|
||||
auto const size = coords.size();
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
{
|
||||
auto const & lonLat = coords[i];
|
||||
CHECK_EQUAL(lonLat.size(), 2, ());
|
||||
oss << "{" << lonLat[1] << ", " << lonLat[0] << "}";
|
||||
if (i + 1 != size)
|
||||
oss << ",\n";
|
||||
}
|
||||
|
||||
oss << "}";
|
||||
res.emplace_back(oss.str());
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
string Api::GetDirectionsURL(RouteParams const & params) const
|
||||
{
|
||||
ostringstream oss;
|
||||
oss << kBaseURL << "directions/" << kDirectionsApiVersion << "/" << VehicleTypeToMapboxType(params.m_type) << "/";
|
||||
oss << LatLonsToString(params.m_waypoints) << "?";
|
||||
oss << "access_token=" << m_accessToken << "&";
|
||||
oss << "overview=simplified&" << "geometries=geojson";
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
string Api::GetRoutesRepresentationURL(Geometry const & mapboxRoute, Geometry const & mapsmeRoute) const
|
||||
{
|
||||
using Sink = MemWriter<string>;
|
||||
|
||||
string mapboxData;
|
||||
{
|
||||
Sink sink(mapboxData);
|
||||
coding::SerializerJson<Sink> ser(sink);
|
||||
ser(mapboxRoute);
|
||||
}
|
||||
|
||||
mapboxData = UrlEncode(mapboxData);
|
||||
|
||||
string mapsmeData;
|
||||
{
|
||||
Sink sink(mapsmeData);
|
||||
coding::SerializerJson<Sink> ser(sink);
|
||||
ser(mapsmeRoute);
|
||||
}
|
||||
|
||||
mapsmeData = UrlEncode(mapsmeData);
|
||||
|
||||
ostringstream oss;
|
||||
oss << kBaseURL << "styles/" << kStylesApiVersion << "/mapbox/streets-v10/static/";
|
||||
oss << "geojson(" << mapboxData << ")"
|
||||
<< ",geojson(" << mapsmeData << ")"<< "/auto/1000x1000?" << "access_token=" << m_accessToken;
|
||||
return oss.str();
|
||||
}
|
||||
} // namespace mapbox
|
||||
} // namespace routing_quality
|
51
routing/routing_quality/mapbox/api.hpp
Normal file
51
routing/routing_quality/mapbox/api.hpp
Normal file
|
@ -0,0 +1,51 @@
|
|||
#pragma once
|
||||
|
||||
#include "routing/routing_quality/mapbox/types.hpp"
|
||||
#include "routing/routing_quality/utils.hpp"
|
||||
|
||||
#include "base/assert.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace routing_quality
|
||||
{
|
||||
namespace mapbox
|
||||
{
|
||||
class Api
|
||||
{
|
||||
public:
|
||||
explicit Api(std::string const & accessToken) : m_accessToken(accessToken) {}
|
||||
|
||||
DirectionsResponse MakeDirectionsRequest(RouteParams const & params) const;
|
||||
void DrawRoutes(Geometry const & mapboxRoute, Geometry const & mapsmeRoute, std::string const & snapshotPath) const;
|
||||
|
||||
static std::vector<std::string> GenerateWaypointsBasedOn(DirectionsResponse response);
|
||||
|
||||
template <typename Container>
|
||||
static void Sieve(Container & cont, size_t maxRemainingNumber)
|
||||
{
|
||||
CHECK_GREATER(maxRemainingNumber, 0, ());
|
||||
auto const size = cont.size();
|
||||
if (size <= maxRemainingNumber)
|
||||
return;
|
||||
|
||||
Container res;
|
||||
res.reserve(maxRemainingNumber);
|
||||
auto const step = size / maxRemainingNumber;
|
||||
for (size_t i = 0; i < size; i += step)
|
||||
res.emplace_back(cont[i]);
|
||||
|
||||
cont = std::move(res);
|
||||
}
|
||||
|
||||
private:
|
||||
std::string GetDirectionsURL(RouteParams const & params) const;
|
||||
std::string GetRoutesRepresentationURL(Geometry const & mapboxRoute, Geometry const & mapsmeRoute) const;
|
||||
|
||||
std::string m_accessToken;
|
||||
};
|
||||
} // namespace mapbox
|
||||
} // namespace routing_quality
|
56
routing/routing_quality/mapbox/types.hpp
Normal file
56
routing/routing_quality/mapbox/types.hpp
Normal file
|
@ -0,0 +1,56 @@
|
|||
#pragma once
|
||||
|
||||
#include "geometry/latlon.hpp"
|
||||
#include "geometry/mercator.hpp"
|
||||
#include "geometry/point2d.hpp"
|
||||
|
||||
#include "base/visitor.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace routing_quality
|
||||
{
|
||||
namespace mapbox
|
||||
{
|
||||
using LonLat = std::vector<double>;
|
||||
using Coordinates = std::vector<LonLat>;
|
||||
|
||||
struct Geometry
|
||||
{
|
||||
DECLARE_VISITOR(visitor(m_coordinates, "coordinates"),
|
||||
visitor(m_type, "type"))
|
||||
|
||||
void Build(std::vector<m2::PointD> const & from)
|
||||
{
|
||||
m_coordinates.reserve(from.size());
|
||||
for (auto const & p : from)
|
||||
{
|
||||
auto const ll = MercatorBounds::ToLatLon(p);
|
||||
m_coordinates.push_back({ll.lon, ll.lat});
|
||||
}
|
||||
|
||||
m_type = "LineString";
|
||||
}
|
||||
|
||||
Coordinates m_coordinates;
|
||||
std::string m_type;
|
||||
};
|
||||
|
||||
struct Route
|
||||
{
|
||||
DECLARE_VISITOR(visitor(m_geometry, "geometry"),
|
||||
visitor(m_duration, "duration"))
|
||||
|
||||
Geometry m_geometry;
|
||||
double m_duration = 0.0;
|
||||
};
|
||||
|
||||
struct DirectionsResponse
|
||||
{
|
||||
DECLARE_VISITOR(visitor(m_routes, "routes"))
|
||||
|
||||
std::vector<Route> m_routes;
|
||||
};
|
||||
} // namespace mapbox
|
||||
} // namespace routing_quality
|
38
routing/routing_quality/routing_quality_tool/CMakeLists.txt
Normal file
38
routing/routing_quality/routing_quality_tool/CMakeLists.txt
Normal file
|
@ -0,0 +1,38 @@
|
|||
project(routing_quality_tool)
|
||||
|
||||
include_directories(${OMIM_ROOT}/3party/gflags/src)
|
||||
|
||||
set(
|
||||
SRC
|
||||
parse_input_params.hpp
|
||||
routing_quality_tool.cpp
|
||||
)
|
||||
|
||||
omim_add_executable(${PROJECT_NAME} ${SRC})
|
||||
|
||||
omim_link_libraries(
|
||||
${PROJECT_NAME}
|
||||
routing_quality
|
||||
generator
|
||||
routing
|
||||
traffic
|
||||
routing_common
|
||||
transit
|
||||
storage
|
||||
indexer
|
||||
platform
|
||||
mwm_diff
|
||||
bsdiff
|
||||
geometry
|
||||
coding
|
||||
base
|
||||
icu
|
||||
jansson
|
||||
protobuf
|
||||
stats_client
|
||||
gflags
|
||||
${LIBZ}
|
||||
)
|
||||
|
||||
link_qt5_core(${PROJECT_NAME})
|
||||
link_qt5_network(${PROJECT_NAME})
|
|
@ -0,0 +1,73 @@
|
|||
#pragma once
|
||||
|
||||
#include "routing/routing_quality/utils.hpp"
|
||||
|
||||
#include "coding/file_reader.hpp"
|
||||
|
||||
#include "geometry/mercator.hpp"
|
||||
|
||||
#include "base/assert.hpp"
|
||||
#include "base/string_utils.hpp"
|
||||
|
||||
#include <iterator>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace routing_quality
|
||||
{
|
||||
template <typename Iter, typename Container>
|
||||
void SerializeLatLon(Iter begin, Iter end, Container & c)
|
||||
{
|
||||
c.reserve(std::distance(begin, end));
|
||||
for (auto it = begin; it != end; ++it)
|
||||
{
|
||||
auto const & str = *it;
|
||||
auto const coords = strings::Tokenize(str, ",");
|
||||
CHECK_EQUAL(coords.size(), 2, ("Incorrect string", str));
|
||||
|
||||
double lat = 0.0;
|
||||
double lon = 0.0;
|
||||
CHECK(strings::to_double(coords[0], lat), ("Incorrect string", coords[0]));
|
||||
CHECK(strings::to_double(coords[1], lon), ("Incorrect string", coords[1]));
|
||||
CHECK(MercatorBounds::ValidLat(lat), ("Incorrect lat", lat));
|
||||
CHECK(MercatorBounds::ValidLon(lon), ("Incorrect lon", lon));
|
||||
|
||||
c.emplace_back(lat, lon);
|
||||
}
|
||||
}
|
||||
|
||||
RouteParams SerializeRouteParamsFromFile(std::string const & routeParamsFilePath)
|
||||
{
|
||||
FileReader r(routeParamsFilePath);
|
||||
std::string data;
|
||||
r.ReadAsString(data);
|
||||
|
||||
auto const tokenized = strings::Tokenize(data, ";");
|
||||
auto const size = tokenized.size();
|
||||
CHECK_GREATER_OR_EQUAL(size, 3, ("Incorrect string", data));
|
||||
|
||||
RouteParams params;
|
||||
SerializeLatLon(tokenized.begin(), tokenized.end() - 1, params.m_waypoints);
|
||||
|
||||
int type = 0;
|
||||
CHECK(strings::to_int(tokenized[size - 1], type), ("Incorrect string", tokenized[size - 1]));
|
||||
CHECK_LESS(type, static_cast<int>(routing::VehicleType::Count), ("Incorrect vehicle type", type));
|
||||
params.m_type = static_cast<routing::VehicleType>(type);
|
||||
return params;
|
||||
}
|
||||
|
||||
RouteParams SerializeRouteParamsFromString(std::string const & waypoints, int vehicleType)
|
||||
{
|
||||
auto const tokenized = strings::Tokenize(waypoints, ";");
|
||||
auto const size = tokenized.size();
|
||||
CHECK_GREATER_OR_EQUAL(size, 2, ("Incorrect string", waypoints));
|
||||
|
||||
RouteParams params;
|
||||
SerializeLatLon(tokenized.begin(), tokenized.end(), params.m_waypoints);
|
||||
|
||||
CHECK_LESS(vehicleType, static_cast<int>(routing::VehicleType::Count), ("Incorrect vehicle type", vehicleType));
|
||||
CHECK_GREATER_OR_EQUAL(vehicleType, 0, ("Incorrect vehicle type", vehicleType));
|
||||
params.m_type = static_cast<routing::VehicleType>(vehicleType);
|
||||
return params;
|
||||
}
|
||||
} // namespace routinq_quality
|
|
@ -0,0 +1,82 @@
|
|||
#include "routing/routing_quality/routing_quality_tool/parse_input_params.hpp"
|
||||
|
||||
#include "routing/routing_quality/mapbox/api.hpp"
|
||||
#include "routing/routing_quality/utils.hpp"
|
||||
#include "routing/routing_quality/waypoints.hpp"
|
||||
|
||||
#include "base/assert.hpp"
|
||||
#include "base/logging.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "3party/gflags/src/gflags/gflags.h"
|
||||
|
||||
DEFINE_string(cmd, "", "command:\n"
|
||||
"generate - generate route waypoints for testing.");
|
||||
|
||||
DEFINE_string(routeParamsFile, "", "File contains two or more route points listed as latitude and longitude "
|
||||
"separated by comma. Each coordinate should be separated by semicolon. "
|
||||
"Last symbol is a numeric value of routing::VehicleType enum. "
|
||||
"At least two points and vehicle type are required. "
|
||||
"All points and vehicle type will be serialized into routing_quality::RouteParams.");
|
||||
|
||||
DEFINE_string(routeWaypoints, "", "Two or more route points listed as latitude and longitude "
|
||||
"separated by comma. Each coordinate should be separated by semicolon.");
|
||||
|
||||
DEFINE_int32(vehicleType, 2, "Numeric value of routing::VehicleType enum.");
|
||||
|
||||
DEFINE_string(mapboxAccessToken, "", "Access token for mapbox api.");
|
||||
|
||||
DEFINE_bool(showHelp, false, "Show help on all flags.");
|
||||
|
||||
using namespace std;
|
||||
using namespace routing_quality;
|
||||
using namespace mapbox;
|
||||
|
||||
int main(int argc, char ** argv)
|
||||
{
|
||||
google::SetUsageMessage("The tool is used to generate points by mapbox route engine and then "
|
||||
"use these points as referenced waypoints. The usage could be as following: "
|
||||
"-cmd=generate "
|
||||
"-routeWaypoints=55.8840156,37.4403484;55.4173592,37.895966 "
|
||||
"-mapboxAccessToken=accessToken. "
|
||||
"You can use the access token from confluence or just get your own. "
|
||||
"The tool will generate the representation of waypoints which you can use directly in code "
|
||||
"as a vector's initializer list.");
|
||||
|
||||
if (argc == 1 || FLAGS_showHelp)
|
||||
{
|
||||
google::ShowUsageWithFlags(argv[0]);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
google::ParseCommandLineFlags(&argc, &argv, true /* remove_flags */);
|
||||
|
||||
auto const & cmd = FLAGS_cmd;
|
||||
CHECK(!cmd.empty(), ("cmd parameter is empty"));
|
||||
|
||||
if (cmd != "generate")
|
||||
{
|
||||
CHECK(false, ("Incorrect command", cmd));
|
||||
return 1;
|
||||
}
|
||||
|
||||
CHECK(!FLAGS_mapboxAccessToken.empty(), ());
|
||||
|
||||
RouteParams params;
|
||||
if (FLAGS_routeParamsFile.empty())
|
||||
{
|
||||
CHECK(!FLAGS_routeWaypoints.empty(), ("At least two waypoints are necessary"));
|
||||
params = SerializeRouteParamsFromString(FLAGS_routeWaypoints, FLAGS_vehicleType);
|
||||
}
|
||||
else
|
||||
{
|
||||
params = SerializeRouteParamsFromFile(FLAGS_routeParamsFile);
|
||||
}
|
||||
|
||||
Api const api(FLAGS_mapboxAccessToken);
|
||||
auto const generatedWaypoints = Api::GenerateWaypointsBasedOn(api.MakeDirectionsRequest(params));
|
||||
|
||||
LOG(LINFO, ("Result waypoints", generatedWaypoints));
|
||||
return 0;
|
||||
}
|
Loading…
Add table
Reference in a new issue