Repo created

This commit is contained in:
Fr4nz D13trich 2025-11-22 13:58:55 +01:00
parent 4af19165ec
commit 68073add76
12458 changed files with 12350765 additions and 2 deletions

View 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
)

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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