Repo created
This commit is contained in:
parent
4af19165ec
commit
68073add76
12458 changed files with 12350765 additions and 2 deletions
22
libs/routing/routing_quality/api/CMakeLists.txt
Normal file
22
libs/routing/routing_quality/api/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
project(routing_api)
|
||||
|
||||
set(SRC
|
||||
api.cpp
|
||||
api.hpp
|
||||
google/google_api.cpp
|
||||
google/google_api.hpp
|
||||
google/types.cpp
|
||||
google/types.hpp
|
||||
mapbox/mapbox_api.cpp
|
||||
mapbox/mapbox_api.hpp
|
||||
mapbox/types.hpp
|
||||
)
|
||||
|
||||
omim_add_library(${PROJECT_NAME} ${SRC})
|
||||
|
||||
target_link_libraries(${PROJECT_NAME}
|
||||
PUBLIC
|
||||
coding
|
||||
geometry
|
||||
routing
|
||||
)
|
||||
156
libs/routing/routing_quality/api/api.cpp
Normal file
156
libs/routing/routing_quality/api/api.cpp
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
#include "routing/routing_quality/api/api.hpp"
|
||||
|
||||
#include "coding/write_to_sink.hpp"
|
||||
|
||||
#include "geometry/mercator.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace routing_quality
|
||||
{
|
||||
namespace api
|
||||
{
|
||||
|
||||
// static
|
||||
void Params::Dump(Params const & route, FileWriter & writer)
|
||||
{
|
||||
auto const start = mercator::ToLatLon(route.m_waypoints.GetPointFrom());
|
||||
auto const finish = mercator::ToLatLon(route.m_waypoints.GetPointTo());
|
||||
|
||||
WriteToSink(writer, static_cast<int>(route.m_type));
|
||||
|
||||
writer.Write(&start.m_lat, sizeof(start.m_lat));
|
||||
writer.Write(&start.m_lon, sizeof(start.m_lon));
|
||||
writer.Write(&finish.m_lat, sizeof(finish.m_lat));
|
||||
writer.Write(&finish.m_lon, sizeof(finish.m_lon));
|
||||
}
|
||||
|
||||
// static
|
||||
Params Params::Load(ReaderSource<FileReader> & src)
|
||||
{
|
||||
Params params;
|
||||
|
||||
params.m_type = static_cast<VehicleType>(ReadPrimitiveFromSource<int>(src));
|
||||
|
||||
ms::LatLon start;
|
||||
ms::LatLon finish;
|
||||
|
||||
start.m_lat = ReadPrimitiveFromSource<double>(src);
|
||||
start.m_lon = ReadPrimitiveFromSource<double>(src);
|
||||
finish.m_lat = ReadPrimitiveFromSource<double>(src);
|
||||
finish.m_lon = ReadPrimitiveFromSource<double>(src);
|
||||
|
||||
auto const startPoint = mercator::FromLatLon(start);
|
||||
auto const finishPoint = mercator::FromLatLon(finish);
|
||||
|
||||
params.m_waypoints = routing::Checkpoints(startPoint, finishPoint);
|
||||
|
||||
return params;
|
||||
}
|
||||
|
||||
// static
|
||||
void Route::Dump(Route const & route, FileWriter & writer)
|
||||
{
|
||||
writer.Write(&route.m_eta, sizeof(route.m_eta));
|
||||
writer.Write(&route.m_distance, sizeof(route.m_distance));
|
||||
|
||||
WriteToSink(writer, route.m_waypoints.size());
|
||||
for (auto const & latlon : route.m_waypoints)
|
||||
{
|
||||
writer.Write(&latlon.m_lat, sizeof(latlon.m_lat));
|
||||
writer.Write(&latlon.m_lon, sizeof(latlon.m_lon));
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
Route Route::Load(ReaderSource<FileReader> & src)
|
||||
{
|
||||
Route route;
|
||||
|
||||
route.m_eta = ReadPrimitiveFromSource<double>(src);
|
||||
route.m_distance = ReadPrimitiveFromSource<double>(src);
|
||||
|
||||
auto const n = ReadPrimitiveFromSource<size_t>(src);
|
||||
route.m_waypoints.resize(n);
|
||||
ms::LatLon latlon;
|
||||
for (size_t i = 0; i < n; ++i)
|
||||
{
|
||||
latlon.m_lat = ReadPrimitiveFromSource<double>(src);
|
||||
latlon.m_lon = ReadPrimitiveFromSource<double>(src);
|
||||
route.m_waypoints[i] = latlon;
|
||||
}
|
||||
|
||||
return route;
|
||||
}
|
||||
|
||||
// static
|
||||
std::string const Response::kDumpExtension = ".api.dump";
|
||||
|
||||
// static
|
||||
void Response::Dump(std::string const & filepath, Response const & response)
|
||||
{
|
||||
FileWriter writer(filepath);
|
||||
|
||||
WriteToSink(writer, static_cast<int>(response.m_code));
|
||||
|
||||
Params::Dump(response.m_params, writer);
|
||||
|
||||
WriteToSink(writer, response.m_routes.size());
|
||||
for (auto const & route : response.m_routes)
|
||||
Route::Dump(route, writer);
|
||||
}
|
||||
|
||||
// static
|
||||
Response Response::Load(std::string const & filepath)
|
||||
{
|
||||
FileReader reader(filepath);
|
||||
ReaderSource<FileReader> src(reader);
|
||||
|
||||
Response response;
|
||||
response.m_code = static_cast<ResultCode>(ReadPrimitiveFromSource<int>(src));
|
||||
response.m_params = Params::Load(src);
|
||||
|
||||
auto const n = ReadPrimitiveFromSource<size_t>(src);
|
||||
response.m_routes.resize(n);
|
||||
for (size_t i = 0; i < n; ++i)
|
||||
response.m_routes[i] = Route::Load(src);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
routing::VehicleType Response::GetVehicleType() const
|
||||
{
|
||||
switch (m_params.m_type)
|
||||
{
|
||||
case VehicleType::Car: return routing::VehicleType::Car;
|
||||
}
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
RoutingApi::RoutingApi(std::string name, std::string token, uint32_t maxRPS)
|
||||
: m_apiName(std::move(name))
|
||||
, m_accessToken(std::move(token))
|
||||
, m_maxRPS(maxRPS)
|
||||
{}
|
||||
|
||||
Response RoutingApi::CalculateRoute(Params const & params, int32_t startTimeZoneUTC) const
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
uint32_t RoutingApi::GetMaxRPS() const
|
||||
{
|
||||
return m_maxRPS;
|
||||
}
|
||||
|
||||
std::string const & RoutingApi::GetApiName() const
|
||||
{
|
||||
return m_apiName;
|
||||
}
|
||||
|
||||
std::string const & RoutingApi::GetAccessToken() const
|
||||
{
|
||||
return m_accessToken;
|
||||
}
|
||||
} // namespace api
|
||||
} // namespace routing_quality
|
||||
102
libs/routing/routing_quality/api/api.hpp
Normal file
102
libs/routing/routing_quality/api/api.hpp
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
#pragma once
|
||||
|
||||
#include "routing/checkpoints.hpp"
|
||||
#include "routing/routing_callbacks.hpp"
|
||||
#include "routing/vehicle_mask.hpp"
|
||||
|
||||
#include "coding/file_reader.hpp"
|
||||
#include "coding/file_writer.hpp"
|
||||
#include "coding/reader.hpp"
|
||||
|
||||
#include "geometry/latlon.hpp"
|
||||
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace routing_quality
|
||||
{
|
||||
namespace api
|
||||
{
|
||||
enum class ResultCode
|
||||
{
|
||||
ResponseOK,
|
||||
Error
|
||||
};
|
||||
|
||||
enum class VehicleType
|
||||
{
|
||||
Car
|
||||
};
|
||||
|
||||
struct Params
|
||||
{
|
||||
static void Dump(Params const & route, FileWriter & writer);
|
||||
static Params Load(ReaderSource<FileReader> & src);
|
||||
|
||||
VehicleType m_type = VehicleType::Car;
|
||||
routing::Checkpoints m_waypoints;
|
||||
};
|
||||
|
||||
struct Route
|
||||
{
|
||||
static void Dump(Route const & route, FileWriter & writer);
|
||||
static Route Load(ReaderSource<FileReader> & src);
|
||||
|
||||
std::vector<ms::LatLon> const & GetWaypoints() const { return m_waypoints; }
|
||||
double GetETA() const { return m_eta; }
|
||||
|
||||
double m_eta = 0.0;
|
||||
double m_distance = 0.0;
|
||||
std::vector<ms::LatLon> m_waypoints;
|
||||
};
|
||||
|
||||
struct Response
|
||||
{
|
||||
static std::string const kDumpExtension;
|
||||
|
||||
static void Dump(std::string const & filepath, Response const & response);
|
||||
static Response Load(std::string const & filepath);
|
||||
|
||||
routing::VehicleType GetVehicleType() const;
|
||||
m2::PointD const & GetStartPoint() const { return m_params.m_waypoints.GetPointFrom(); }
|
||||
m2::PointD const & GetFinishPoint() const { return m_params.m_waypoints.GetPointTo(); }
|
||||
bool IsCodeOK() const { return m_code == api::ResultCode::ResponseOK; }
|
||||
std::vector<Route> const & GetRoutes() const { return m_routes; }
|
||||
|
||||
ResultCode m_code = ResultCode::Error;
|
||||
Params m_params;
|
||||
std::vector<Route> m_routes;
|
||||
};
|
||||
|
||||
class RoutingApiInterface
|
||||
{
|
||||
public:
|
||||
virtual ~RoutingApiInterface() = default;
|
||||
virtual Response CalculateRoute(Params const & params, int32_t startTimeZoneUTC) const = 0;
|
||||
};
|
||||
|
||||
class RoutingApi : public RoutingApiInterface
|
||||
{
|
||||
public:
|
||||
RoutingApi(std::string name, std::string token, uint32_t maxRPS = 1);
|
||||
|
||||
// RoutingApiInterface overrides:
|
||||
// @{
|
||||
Response CalculateRoute(Params const & params, int32_t startTimeZoneUTC) const override;
|
||||
// @}
|
||||
|
||||
uint32_t GetMaxRPS() const;
|
||||
|
||||
std::string const & GetApiName() const;
|
||||
std::string const & GetAccessToken() const;
|
||||
|
||||
private:
|
||||
std::string m_apiName;
|
||||
std::string m_accessToken;
|
||||
|
||||
// Maximum requests per second provided with api
|
||||
uint32_t m_maxRPS;
|
||||
};
|
||||
} // namespace api
|
||||
} // namespace routing_quality
|
||||
145
libs/routing/routing_quality/api/google/google_api.cpp
Normal file
145
libs/routing/routing_quality/api/google/google_api.cpp
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
#include "routing/routing_quality/api/google/google_api.hpp"
|
||||
|
||||
#include "platform/http_client.hpp"
|
||||
|
||||
#include "coding/serdes_json.hpp"
|
||||
|
||||
#include "geometry/mercator.hpp"
|
||||
|
||||
#include "base/assert.hpp"
|
||||
#include "base/logging.hpp"
|
||||
|
||||
#include <chrono>
|
||||
#include <ctime>
|
||||
#include <sstream>
|
||||
#include <utility>
|
||||
|
||||
namespace
|
||||
{
|
||||
auto GetNextDayAtNight(int32_t startTimeZoneUTC)
|
||||
{
|
||||
auto now = std::chrono::system_clock::now();
|
||||
|
||||
std::time_t nowTimeT = std::chrono::system_clock::to_time_t(now);
|
||||
std::tm * date = std::localtime(&nowTimeT);
|
||||
|
||||
std::time_t constexpr kSecondsInDay = 24 * 60 * 60;
|
||||
std::time_t nextDay = std::mktime(date) + kSecondsInDay;
|
||||
|
||||
std::tm * nextDayDate = std::localtime(&nextDay);
|
||||
|
||||
long constexpr kSecondsInHour = 3600;
|
||||
int const curUTCOffset = static_cast<int>(nextDayDate->tm_gmtoff / kSecondsInHour);
|
||||
|
||||
nextDayDate->tm_sec = 0;
|
||||
nextDayDate->tm_min = 0;
|
||||
|
||||
int constexpr kStartHour = 22;
|
||||
// If in Moscow (UTC+3) it is 20:00, in New York (UTC-5):
|
||||
// 20:00 - 03:00 (Moscow) = 19:00 (UTC+0), so in New York: 19:00 - 05:00 = 14:00 (UTC-5).
|
||||
// Thus if we want to find such time (Y hours) in Moscow (UTC+3) so that in New York (UTC-5)
|
||||
// it's must be X hours, we use such formula:
|
||||
// 1) Y (UTC+3) - 03:00 = UTC+0
|
||||
// 2) Y (UTC+3) - 03:00 + 05:00 = X (UTC+5)
|
||||
// 2 => Y = X + 03:00 - 05:00
|
||||
// For google api we want to find such Y, that it's will be |kStartHour| hours in any place.
|
||||
nextDayDate->tm_hour = kStartHour + curUTCOffset - startTimeZoneUTC;
|
||||
|
||||
return std::chrono::system_clock::from_time_t(std::mktime(nextDayDate));
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace routing_quality
|
||||
{
|
||||
namespace api
|
||||
{
|
||||
namespace google
|
||||
{
|
||||
// static
|
||||
std::string const GoogleApi::kApiName = "google";
|
||||
|
||||
GoogleApi::GoogleApi(std::string const & token) : RoutingApi(kApiName, token, kMaxRPS) {}
|
||||
|
||||
Response GoogleApi::CalculateRoute(Params const & params, int32_t startTimeZoneUTC) const
|
||||
{
|
||||
Response response;
|
||||
GoogleResponse googleResponse = MakeRequest(params, startTimeZoneUTC);
|
||||
|
||||
response.m_params = params;
|
||||
response.m_code = googleResponse.m_code;
|
||||
for (auto const & route : googleResponse.m_routes)
|
||||
{
|
||||
CHECK_EQUAL(route.m_legs.size(), 1, ("No waypoints support for google api."));
|
||||
auto const & leg = route.m_legs.back();
|
||||
|
||||
api::Route apiRoute;
|
||||
apiRoute.m_eta = leg.m_duration.m_seconds;
|
||||
apiRoute.m_distance = leg.m_distance.m_meters;
|
||||
|
||||
auto const startLatLon = leg.m_steps.front().m_startLocation;
|
||||
apiRoute.m_waypoints.emplace_back(startLatLon.m_lat, startLatLon.m_lon);
|
||||
for (auto const & step : leg.m_steps)
|
||||
{
|
||||
auto const & prev = step.m_startLocation;
|
||||
auto const & next = step.m_endLocation;
|
||||
|
||||
auto const & prevWaypoint = apiRoute.m_waypoints.back();
|
||||
CHECK(prevWaypoint.m_lat == prev.m_lat && prevWaypoint.m_lon == prev.m_lon, ());
|
||||
|
||||
apiRoute.m_waypoints.emplace_back(next.m_lat, next.m_lon);
|
||||
}
|
||||
|
||||
response.m_routes.emplace_back(std::move(apiRoute));
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
GoogleResponse GoogleApi::MakeRequest(Params const & params, int32_t startTimeZoneUTC) const
|
||||
{
|
||||
GoogleResponse googleResponse;
|
||||
platform::HttpClient request(GetDirectionsURL(params, startTimeZoneUTC));
|
||||
|
||||
if (request.RunHttpRequest() && !request.WasRedirected() && request.ErrorCode() == 200)
|
||||
{
|
||||
auto const & response = request.ServerResponse();
|
||||
CHECK(!response.empty(), ());
|
||||
{
|
||||
googleResponse.m_code = ResultCode::ResponseOK;
|
||||
coding::DeserializerJson des(response);
|
||||
des(googleResponse);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG(LWARNING, (request.ErrorCode(), request.ServerResponse()));
|
||||
googleResponse.m_code = ResultCode::Error;
|
||||
}
|
||||
|
||||
return googleResponse;
|
||||
}
|
||||
|
||||
std::string GoogleApi::GetDirectionsURL(Params const & params, int32_t startTimeZoneUTC) const
|
||||
{
|
||||
CHECK(-12 <= startTimeZoneUTC && startTimeZoneUTC <= 12, ("Invalid UTC zone."));
|
||||
|
||||
static std::string const kBaseUrl = "https://maps.googleapis.com/maps/api/directions/json?";
|
||||
|
||||
auto const start = mercator::ToLatLon(params.m_waypoints.GetPointFrom());
|
||||
auto const finish = mercator::ToLatLon(params.m_waypoints.GetPointTo());
|
||||
|
||||
auto const nextDayAtNight = GetNextDayAtNight(startTimeZoneUTC);
|
||||
auto const secondFromEpoch = nextDayAtNight.time_since_epoch().count() / 1000000;
|
||||
LOG(LDEBUG, ("&departure_time =", secondFromEpoch));
|
||||
|
||||
std::stringstream ss;
|
||||
ss << kBaseUrl << "&origin=" << std::to_string(start.m_lat) << "," << std::to_string(start.m_lon)
|
||||
<< "&destination=" << std::to_string(finish.m_lat) << "," << std::to_string(finish.m_lon)
|
||||
<< "&key=" << GetAccessToken() << "&alternatives=true"
|
||||
<< "&departure_time=" << secondFromEpoch;
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
} // namespace google
|
||||
} // namespace api
|
||||
} // namespace routing_quality
|
||||
37
libs/routing/routing_quality/api/google/google_api.hpp
Normal file
37
libs/routing/routing_quality/api/google/google_api.hpp
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
#pragma once
|
||||
|
||||
#include "routing/routing_quality/api/api.hpp"
|
||||
|
||||
#include "routing/routing_quality/api/google/types.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
namespace routing_quality
|
||||
{
|
||||
namespace api
|
||||
{
|
||||
namespace google
|
||||
{
|
||||
class GoogleApi : public RoutingApi
|
||||
{
|
||||
public:
|
||||
explicit GoogleApi(std::string const & token);
|
||||
|
||||
// According to:
|
||||
// https://developers.google.com/maps/faq#usage_apis
|
||||
static uint32_t constexpr kMaxRPS = 50;
|
||||
static std::string const kApiName;
|
||||
|
||||
// RoutingApi overrides:
|
||||
// @{
|
||||
Response CalculateRoute(Params const & params, int32_t startTimeZoneUTC) const override;
|
||||
// @}
|
||||
|
||||
private:
|
||||
GoogleResponse MakeRequest(Params const & params, int32_t startTimeZoneUTC) const;
|
||||
std::string GetDirectionsURL(Params const & params, int32_t startTimeZoneUTC) const;
|
||||
};
|
||||
} // namespace google
|
||||
} // namespace api
|
||||
} // namespace routing_quality
|
||||
32
libs/routing/routing_quality/api/google/types.cpp
Normal file
32
libs/routing/routing_quality/api/google/types.cpp
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
#include "routing/routing_quality/api/google/types.hpp"
|
||||
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
#include <tuple>
|
||||
|
||||
namespace routing_quality
|
||||
{
|
||||
namespace api
|
||||
{
|
||||
namespace google
|
||||
{
|
||||
bool LatLon::operator==(LatLon const & rhs) const
|
||||
{
|
||||
return std::tie(m_lat, m_lon) == std::tie(rhs.m_lat, rhs.m_lon);
|
||||
}
|
||||
|
||||
bool Step::operator==(Step const & rhs) const
|
||||
{
|
||||
return std::tie(m_startLocation, m_endLocation) == std::tie(rhs.m_startLocation, rhs.m_endLocation);
|
||||
}
|
||||
|
||||
std::string DebugPrint(LatLon const & latlon)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << std::setprecision(20);
|
||||
ss << "google::LatLon(" << latlon.m_lat << ", " << latlon.m_lon << ")";
|
||||
return ss.str();
|
||||
}
|
||||
} // namespace google
|
||||
} // namespace api
|
||||
} // namespace routing_quality
|
||||
77
libs/routing/routing_quality/api/google/types.hpp
Normal file
77
libs/routing/routing_quality/api/google/types.hpp
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
#pragma once
|
||||
|
||||
#include "routing/routing_quality/api/api.hpp"
|
||||
|
||||
#include "base/visitor.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace routing_quality
|
||||
{
|
||||
namespace api
|
||||
{
|
||||
namespace google
|
||||
{
|
||||
struct LatLon
|
||||
{
|
||||
DECLARE_VISITOR(visitor(m_lat, "lat"), visitor(m_lon, "lng"))
|
||||
|
||||
bool operator==(LatLon const & rhs) const;
|
||||
|
||||
double m_lat = 0.0;
|
||||
double m_lon = 0.0;
|
||||
};
|
||||
|
||||
struct Step
|
||||
{
|
||||
DECLARE_VISITOR(visitor(m_startLocation, "start_location"), visitor(m_endLocation, "end_location"))
|
||||
|
||||
bool operator==(Step const & rhs) const;
|
||||
|
||||
LatLon m_startLocation;
|
||||
LatLon m_endLocation;
|
||||
};
|
||||
|
||||
struct Duration
|
||||
{
|
||||
DECLARE_VISITOR(visitor(m_seconds, "value"))
|
||||
|
||||
double m_seconds = 0.0;
|
||||
};
|
||||
|
||||
struct Distance
|
||||
{
|
||||
DECLARE_VISITOR(visitor(m_meters, "value"))
|
||||
|
||||
double m_meters = 0.0;
|
||||
};
|
||||
|
||||
struct Leg
|
||||
{
|
||||
DECLARE_VISITOR(visitor(m_distance, "distance"), visitor(m_duration, "duration"), visitor(m_steps, "steps"))
|
||||
|
||||
Distance m_distance;
|
||||
Duration m_duration;
|
||||
std::vector<Step> m_steps;
|
||||
};
|
||||
|
||||
struct Route
|
||||
{
|
||||
DECLARE_VISITOR(visitor(m_legs, "legs"))
|
||||
|
||||
std::vector<Leg> m_legs;
|
||||
};
|
||||
|
||||
struct GoogleResponse
|
||||
{
|
||||
DECLARE_VISITOR(visitor(m_routes, "routes"))
|
||||
|
||||
ResultCode m_code = ResultCode::Error;
|
||||
std::vector<Route> m_routes;
|
||||
};
|
||||
|
||||
std::string DebugPrint(LatLon const & latlon);
|
||||
} // namespace google
|
||||
} // namespace api
|
||||
} // namespace routing_quality
|
||||
130
libs/routing/routing_quality/api/mapbox/mapbox_api.cpp
Normal file
130
libs/routing/routing_quality/api/mapbox/mapbox_api.cpp
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
#include "routing/routing_quality/api/mapbox/mapbox_api.hpp"
|
||||
|
||||
#include "routing/vehicle_mask.hpp"
|
||||
|
||||
#include "coding/file_writer.hpp"
|
||||
#include "coding/serdes_json.hpp"
|
||||
#include "coding/url.hpp"
|
||||
#include "coding/writer.hpp"
|
||||
|
||||
#include "platform/http_client.hpp"
|
||||
|
||||
#include "geometry/latlon.hpp"
|
||||
#include "geometry/mercator.hpp"
|
||||
|
||||
#include "base/assert.hpp"
|
||||
#include "base/logging.hpp"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
namespace
|
||||
{
|
||||
std::string const kBaseURL = "https://api.mapbox.com/";
|
||||
std::string const kDirectionsApiVersion = "v5";
|
||||
std::string const kCarType = "mapbox/driving";
|
||||
|
||||
std::string VehicleTypeToMapboxType(routing_quality::api::VehicleType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case routing_quality::api::VehicleType::Car: return kCarType;
|
||||
}
|
||||
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
std::string LatLonsToString(std::vector<ms::LatLon> const & coords)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
auto const size = coords.size();
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
{
|
||||
auto const & ll = coords[i];
|
||||
oss << ll.m_lon << "," << ll.m_lat;
|
||||
if (i + 1 != size)
|
||||
oss << ";";
|
||||
}
|
||||
|
||||
oss << ".json";
|
||||
return url::UrlEncode(oss.str());
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace routing_quality::api::mapbox
|
||||
{
|
||||
// static
|
||||
std::string const MapboxApi::kApiName = "mapbox";
|
||||
|
||||
MapboxApi::MapboxApi(std::string const & token) : RoutingApi(kApiName, token, kMaxRPS) {}
|
||||
|
||||
Response MapboxApi::CalculateRoute(Params const & params, int32_t /* startTimeZoneUTC */) const
|
||||
{
|
||||
Response response;
|
||||
MapboxResponse mapboxResponse = MakeRequest(params);
|
||||
|
||||
response.m_params = params;
|
||||
response.m_code = mapboxResponse.m_code;
|
||||
for (auto const & route : mapboxResponse.m_routes)
|
||||
{
|
||||
api::Route apiRoute;
|
||||
apiRoute.m_eta = route.m_duration;
|
||||
apiRoute.m_distance = route.m_distance;
|
||||
for (auto const & lonlat : route.m_geometry.m_coordinates)
|
||||
{
|
||||
CHECK_EQUAL(lonlat.size(), 2, ());
|
||||
|
||||
auto const lon = lonlat.front();
|
||||
auto const lat = lonlat.back();
|
||||
apiRoute.m_waypoints.emplace_back(lat, lon);
|
||||
}
|
||||
|
||||
response.m_routes.emplace_back(std::move(apiRoute));
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
MapboxResponse MapboxApi::MakeRequest(Params const & params) const
|
||||
{
|
||||
MapboxResponse mapboxResponse;
|
||||
platform::HttpClient request(GetDirectionsURL(params));
|
||||
|
||||
if (request.RunHttpRequest() && !request.WasRedirected() && request.ErrorCode() == 200)
|
||||
{
|
||||
auto const & response = request.ServerResponse();
|
||||
CHECK(!response.empty(), ());
|
||||
{
|
||||
mapboxResponse.m_code = ResultCode::ResponseOK;
|
||||
coding::DeserializerJson des(response);
|
||||
des(mapboxResponse);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG(LWARNING, (request.ErrorCode(), request.ServerResponse()));
|
||||
mapboxResponse.m_code = ResultCode::Error;
|
||||
}
|
||||
|
||||
return mapboxResponse;
|
||||
}
|
||||
|
||||
std::string MapboxApi::GetDirectionsURL(Params const & params) const
|
||||
{
|
||||
CHECK(!GetAccessToken().empty(), ());
|
||||
|
||||
std::vector<ms::LatLon> coords;
|
||||
coords.reserve(params.m_waypoints.GetPoints().size());
|
||||
for (auto const & point : params.m_waypoints.GetPoints())
|
||||
coords.emplace_back(mercator::ToLatLon(point));
|
||||
|
||||
std::ostringstream oss;
|
||||
oss << kBaseURL << "directions/" << kDirectionsApiVersion << "/" << VehicleTypeToMapboxType(params.m_type) << "/";
|
||||
oss << LatLonsToString(coords) << "?";
|
||||
oss << "access_token=" << GetAccessToken() << "&";
|
||||
oss << "overview=simplified&"
|
||||
<< "geometries=geojson";
|
||||
oss << "&alternatives=true";
|
||||
|
||||
return oss.str();
|
||||
}
|
||||
} // namespace routing_quality::api::mapbox
|
||||
39
libs/routing/routing_quality/api/mapbox/mapbox_api.hpp
Normal file
39
libs/routing/routing_quality/api/mapbox/mapbox_api.hpp
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
#pragma once
|
||||
|
||||
#include "routing/routing_quality/api/api.hpp"
|
||||
|
||||
#include "routing/routing_quality/api/mapbox/types.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace routing_quality
|
||||
{
|
||||
namespace api
|
||||
{
|
||||
namespace mapbox
|
||||
{
|
||||
class MapboxApi : public RoutingApi
|
||||
{
|
||||
public:
|
||||
explicit MapboxApi(std::string const & token);
|
||||
|
||||
// According to:
|
||||
// https://docs.mapbox.com/api/navigation/#directions-api-restrictions-and-limits
|
||||
static uint32_t constexpr kMaxRPS = 5;
|
||||
static std::string const kApiName;
|
||||
|
||||
// RoutingApi overrides:
|
||||
// @{
|
||||
Response CalculateRoute(Params const & params, int32_t /* startTimeZoneUTC */) const override;
|
||||
// @}
|
||||
|
||||
private:
|
||||
MapboxResponse MakeRequest(Params const & params) const;
|
||||
std::string GetDirectionsURL(Params const & params) const;
|
||||
};
|
||||
} // namespace mapbox
|
||||
} // namespace api
|
||||
} // namespace routing_quality
|
||||
46
libs/routing/routing_quality/api/mapbox/types.hpp
Normal file
46
libs/routing/routing_quality/api/mapbox/types.hpp
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
#pragma once
|
||||
|
||||
#include "routing/routing_quality/api/api.hpp"
|
||||
|
||||
#include "geometry/latlon.hpp"
|
||||
#include "geometry/mercator.hpp"
|
||||
#include "geometry/point2d.hpp"
|
||||
|
||||
#include "base/visitor.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace routing_quality
|
||||
{
|
||||
namespace api
|
||||
{
|
||||
namespace mapbox
|
||||
{
|
||||
using LonLat = std::vector<double>;
|
||||
|
||||
struct Geometry
|
||||
{
|
||||
DECLARE_VISITOR(visitor(m_coordinates, "coordinates"))
|
||||
std::vector<LonLat> m_coordinates;
|
||||
};
|
||||
|
||||
struct Route
|
||||
{
|
||||
DECLARE_VISITOR(visitor(m_geometry, "geometry"), visitor(m_duration, "duration"), visitor(m_distance, "distance"))
|
||||
|
||||
Geometry m_geometry;
|
||||
double m_duration = 0.0;
|
||||
double m_distance = 0.0;
|
||||
};
|
||||
|
||||
struct MapboxResponse
|
||||
{
|
||||
DECLARE_VISITOR(visitor(m_routes, "routes"))
|
||||
|
||||
ResultCode m_code = ResultCode::Error;
|
||||
std::vector<Route> m_routes;
|
||||
};
|
||||
} // namespace mapbox
|
||||
} // namespace api
|
||||
} // namespace routing_quality
|
||||
Loading…
Add table
Add a link
Reference in a new issue