Repo created
This commit is contained in:
parent
4af19165ec
commit
68073add76
12458 changed files with 12350765 additions and 2 deletions
17
libs/routing/routing_quality/CMakeLists.txt
Normal file
17
libs/routing/routing_quality/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
project(routing_quality)
|
||||
|
||||
set(SRC
|
||||
waypoints.cpp
|
||||
waypoints.hpp
|
||||
)
|
||||
|
||||
omim_add_library(${PROJECT_NAME} ${SRC})
|
||||
|
||||
target_link_libraries(${PROJECT_NAME}
|
||||
routes_builder
|
||||
)
|
||||
|
||||
add_subdirectory(api)
|
||||
omim_add_tool_subdirectory(routing_quality_tool)
|
||||
|
||||
omim_add_test_subdirectory(routing_quality_tests)
|
||||
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
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
project(routing_quality_tests)
|
||||
|
||||
set(SRC
|
||||
barriers_tests.cpp
|
||||
bigger_roads_tests.cpp
|
||||
ferry_tests.cpp
|
||||
is_built_tests.cpp
|
||||
leaps_postprocessing_tests.cpp
|
||||
passby_roads_tests.cpp
|
||||
waypoints_tests.cpp
|
||||
)
|
||||
|
||||
omim_add_test(${PROJECT_NAME} ${SRC})
|
||||
|
||||
target_link_libraries(${PROJECT_NAME}
|
||||
routing_quality
|
||||
)
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
#include "testing/testing.hpp"
|
||||
|
||||
#include "routing/routing_quality/waypoints.hpp"
|
||||
|
||||
using namespace routing_quality;
|
||||
|
||||
// There is a category of barriers through which no road must be built:
|
||||
// ice roads (highway = ice_road).
|
||||
// And there is a category of barriers through which the road should be built:
|
||||
// fords (highway = ford).
|
||||
// Tests on this kind of cases are grouped in this file.
|
||||
namespace
|
||||
{
|
||||
UNIT_TEST(RoutingQuality_Broad_Node_Jamaica)
|
||||
{
|
||||
TEST(CheckCarRoute({17.94727, -76.25429} /* start */, {17.94499, -76.25459} /* finish */,
|
||||
{{{17.945150, -76.25442}}} /* reference point */),
|
||||
());
|
||||
}
|
||||
|
||||
UNIT_TEST(RoutingQuality_Broad_Way_Jamaica)
|
||||
{
|
||||
TEST(CheckCarRoute({18.10260, -76.98374} /* start */, {18.10031, -76.98374} /* finish */,
|
||||
{{{18.10078, -76.98412}}} /* reference point */),
|
||||
());
|
||||
}
|
||||
|
||||
UNIT_TEST(RoutingQuality_Broad_Node_Spain)
|
||||
{
|
||||
TEST(CheckCarRoute({41.95027, -0.54596} /* start */, {56.98358, 9.77815} /* finish */,
|
||||
{{{41.95026, -0.54562}}} /* reference point */),
|
||||
());
|
||||
}
|
||||
|
||||
UNIT_TEST(RoutingQuality_Broad_Node_Norway)
|
||||
{
|
||||
TEST(CheckCarRoute({56.20247, 8.77519} /* start */, {56.19732, 8.79190} /* finish */,
|
||||
{{{56.20172, 8.77879}}} /* reference point */),
|
||||
());
|
||||
}
|
||||
} // namespace
|
||||
|
|
@ -0,0 +1,131 @@
|
|||
#include "testing/testing.hpp"
|
||||
|
||||
#include "routing/routing_quality/waypoints.hpp"
|
||||
|
||||
using namespace routing_quality;
|
||||
|
||||
// Test on preferring better but longer roads should be grouped in this file.
|
||||
namespace
|
||||
{
|
||||
// Secondary should be preferred against residential.
|
||||
UNIT_TEST(RoutingQuality_RussiaMoscowTushino)
|
||||
{
|
||||
TEST(CheckCarRoute({55.84367, 37.44724} /* start */, {55.85489, 37.43784} /* finish */,
|
||||
{{{55.84343, 37.43949}}} /* reference track */),
|
||||
());
|
||||
}
|
||||
|
||||
UNIT_TEST(RoutingQuality_TurkeyIzmirArea)
|
||||
{
|
||||
TEST(CheckCarRoute({38.80146, 26.97696} /* start */, {39.0837, 26.90977} /* finish */,
|
||||
{{{39.08124, 27.11829}}} /* reference track */),
|
||||
());
|
||||
}
|
||||
|
||||
UNIT_TEST(RoutingQuality_BosniaAndHerzegovina)
|
||||
{
|
||||
TEST(CheckCarRoute({42.71401, 18.30412} /* start */, {42.95101, 18.08966} /* finish */,
|
||||
{{{42.88222, 17.9919}}} /* reference track */),
|
||||
());
|
||||
}
|
||||
|
||||
UNIT_TEST(RoutingQuality_CzechiaPrague)
|
||||
{
|
||||
TEST(CheckCarRoute({50.10159, 14.43324} /* start */, {50.20976, 14.43361} /* finish */,
|
||||
{{{50.15078, 14.49205}}} /* reference track */),
|
||||
());
|
||||
}
|
||||
|
||||
UNIT_TEST(RoutingQuality_FinlandHelsinki)
|
||||
{
|
||||
TEST(CheckCarRoute({60.16741, 24.94255} /* start */, {64.13182, 28.38784} /* finish */,
|
||||
{{{60.95453, 25.6951}}} /* reference track */),
|
||||
());
|
||||
}
|
||||
|
||||
UNIT_TEST(RoutingQuality_USAOklahoma)
|
||||
{
|
||||
TEST(CheckCarRoute({35.39166, -97.55402} /* start */, {35.38452, -97.5742} /* finish */,
|
||||
{{{35.39912, -97.57622}}} /* reference track */),
|
||||
());
|
||||
}
|
||||
|
||||
UNIT_TEST(RoutingQuality_IranSouth)
|
||||
{
|
||||
TEST(CheckCarRoute({32.45088, 51.76419} /* start */, {32.97067, 51.50399} /* finish */,
|
||||
{{{32.67021, 51.64323}, {32.68752, 51.63387}},
|
||||
{{32.67021, 51.64323}, {32.7501, 51.64661}}} /* reference track */),
|
||||
());
|
||||
}
|
||||
|
||||
UNIT_TEST(RoutingQuality_EindhovenNetherlands)
|
||||
{
|
||||
TEST(CheckCarRoute({50.91974, 5.33535} /* start */, {51.92532, 5.49066} /* finish */,
|
||||
{{{51.42016, 5.42881}, {51.44316, 5.42723}}} /* reference track */),
|
||||
());
|
||||
}
|
||||
|
||||
UNIT_TEST(RoutingQuality_GeteborgasSweden)
|
||||
{
|
||||
TEST(CheckCarRoute({57.77064, 11.88079} /* start */, {57.71231, 11.93157} /* finish */,
|
||||
{{{57.74912, 11.87343}}} /* reference track */),
|
||||
());
|
||||
}
|
||||
|
||||
UNIT_TEST(RoutingQuality_CigilTurkey)
|
||||
{
|
||||
TEST(CheckCarRoute({38.48175, 27.12952} /* start */, {38.47558, 27.06765} /* finish */,
|
||||
{{{38.4898049, 27.1016266}}} /* reference track */),
|
||||
());
|
||||
}
|
||||
|
||||
UNIT_TEST(RoutingQuality_KatowicePoland)
|
||||
{
|
||||
TEST(CheckCarRoute({50.37282, 18.75667} /* start */, {50.83499, 19.14612} /* finish */,
|
||||
{{{50.422229, 19.04746}}} /* reference track */),
|
||||
());
|
||||
}
|
||||
|
||||
UNIT_TEST(RoutingQuality_KrasnoyarskBratsk)
|
||||
{
|
||||
TEST(CheckCarRoute({56.009, 92.873} /* start */, {56.163, 101.611} /* finish */,
|
||||
{{{55.89285, 97.99953}, {54.59928, 100.60402}}} /* reference track */),
|
||||
());
|
||||
}
|
||||
|
||||
UNIT_TEST(RoutingQuality_VoronezhSochi)
|
||||
{
|
||||
TEST(CheckCarRoute({51.65487, 39.21293} /* start */, {43.58547, 39.72311} /* finish */,
|
||||
{{{46.14169, 39.85306},
|
||||
{45.17069, 39.10869},
|
||||
{45.02157, 39.12510},
|
||||
{44.54344, 38.95853}}} /* reference track */),
|
||||
());
|
||||
}
|
||||
|
||||
UNIT_TEST(RoutingQuality_BerlinkaWarsawPoland)
|
||||
{
|
||||
TEST(CheckCarRoute({54.41616, 20.05675} /* start */, {52.18937, 20.94026} /* finish */,
|
||||
{{{54.24278, 19.66106},
|
||||
{54.13679, 19.45166},
|
||||
{54.06452, 19.62416},
|
||||
{53.69769, 19.98204},
|
||||
{53.11194, 20.40002},
|
||||
{52.62966, 20.38488}}} /* reference track */),
|
||||
());
|
||||
}
|
||||
|
||||
UNIT_TEST(RoutingQuality_MosOblBadPaving)
|
||||
{
|
||||
TEST(CheckCarRoute({55.92961, 36.04081} /* start */, {55.93567, 36.0533} /* finish */,
|
||||
{{{55.92321, 36.04630}}} /* reference track */),
|
||||
());
|
||||
}
|
||||
|
||||
UNIT_TEST(RoutingQuality_LatviaUnpaved)
|
||||
{
|
||||
TEST(CheckCarRoute({56.62992, 25.77175} /* start */, {56.61453, 25.78400} /* finish */,
|
||||
{{{56.62377, 25.81015}, {56.61755, 25.80894}}} /* reference track */),
|
||||
());
|
||||
}
|
||||
} // namespace
|
||||
|
|
@ -0,0 +1,133 @@
|
|||
#include "testing/testing.hpp"
|
||||
|
||||
#include "routing/routing_quality/waypoints.hpp"
|
||||
|
||||
using namespace routing_quality;
|
||||
|
||||
namespace
|
||||
{
|
||||
UNIT_TEST(Ferry_RoutingQuality_FinlandBridgeInsteadOfFerry)
|
||||
{
|
||||
TEST(CheckCarRoute({56.11155, 12.81101} /* start */, {55.59857, 12.3069} /* finish */,
|
||||
{{{55.56602, 12.88537}}} /* reference track */),
|
||||
());
|
||||
}
|
||||
// TODO: This test doesn't pass because routing::RouteWeight::operator<
|
||||
// prefer roads with less number of barriers. It will be more useful to consider
|
||||
// barriers only with access=no/private/etc tag.
|
||||
// UNIT_TEST(Ferry_RoutingQuality_RussiaToCrimeaFerry)
|
||||
//{
|
||||
// // From Russia to Crimea
|
||||
// TEST(CheckCarRoute({45.34123, 36.67679} /* start */, {45.36479, 36.62194} /* finish */,
|
||||
// {{{45.3532, 36.64912}}} /* reference track */),
|
||||
// ());
|
||||
//}
|
||||
|
||||
UNIT_TEST(Ferry_RoutingQuality_RussiaFromCrimeaFerry)
|
||||
{
|
||||
TEST(CheckCarRoute({45.36479, 36.62194} /* start */, {45.34123, 36.67679} /* finish */,
|
||||
{{{45.3532, 36.64912}}} /* reference track */),
|
||||
());
|
||||
}
|
||||
|
||||
// For tests Ferry_RoutingQuality_1 - Ferry_RoutingQuality_15
|
||||
// Look at: https://confluence.mail.ru/display/MAPSME/Ferries for more details.
|
||||
|
||||
UNIT_TEST(Ferry_RoutingQuality_1)
|
||||
{
|
||||
TEST(CheckCarRoute({67.89425, 13.00747} /* start */, {67.28024, 14.37397} /* finish */,
|
||||
{{{67.60291, 13.65267}}} /* reference track */),
|
||||
());
|
||||
}
|
||||
|
||||
UNIT_TEST(Ferry_RoutingQuality_2)
|
||||
{
|
||||
TEST(CheckCarRoute({51.91347, 5.24265} /* start */, {51.98885, 5.20058} /* finish */,
|
||||
{{{51.97494, 5.10631}}} /* reference track */),
|
||||
());
|
||||
}
|
||||
|
||||
UNIT_TEST(Ferry_RoutingQuality_3)
|
||||
{
|
||||
TEST(CheckCarRoute({50.68323, 7.15683} /* start */, {50.58042, 7.32259} /* finish */,
|
||||
{{{50.72206, 7.14872}, {50.70747, 7.17801}}} /* reference track */),
|
||||
());
|
||||
}
|
||||
|
||||
UNIT_TEST(Ferry_RoutingQuality_4)
|
||||
{
|
||||
TEST(CheckCarRoute({49.37098, 0.84813} /* start */, {49.47950, 0.80918} /* finish */,
|
||||
{{{49.36829, 0.81359}, {49.41177, 0.79639}}} /* reference track */),
|
||||
());
|
||||
}
|
||||
|
||||
UNIT_TEST(Ferry_RoutingQuality_5)
|
||||
{
|
||||
TEST(CheckCarRoute({53.59885, 9.32285} /* start */, {53.93504, 9.48396} /* finish */,
|
||||
{{{53.55170, 9.89848}}} /* reference track */),
|
||||
());
|
||||
}
|
||||
|
||||
UNIT_TEST(Ferry_RoutingQuality_6)
|
||||
{
|
||||
TEST(CheckCarRoute({48.268548, 21.483862} /* start */, {48.24756, 21.54246} /* finish */,
|
||||
{{{48.32574, 21.56094}}} /* reference track */),
|
||||
());
|
||||
}
|
||||
|
||||
UNIT_TEST(Ferry_RoutingQuality_7)
|
||||
{
|
||||
TEST(CheckCarRoute({52.09319, 25.96368} /* start */, {52.02190, 25.98767} /* finish */,
|
||||
{{{52.08914, 26.13001}}} /* reference track */),
|
||||
());
|
||||
}
|
||||
|
||||
UNIT_TEST(Ferry_RoutingQuality_8)
|
||||
{
|
||||
TEST(CheckCarRoute({53.36914, 17.95644} /* start */, {53.46632, 18.01876} /* finish */,
|
||||
{{{53.31395, 17.98559}}} /* reference track */),
|
||||
());
|
||||
}
|
||||
|
||||
UNIT_TEST(Ferry_RoutingQuality_9)
|
||||
{
|
||||
TEST(CheckCarRoute({52.57264, 14.83948} /* start */, {52.74278, 14.95435} /* finish */,
|
||||
{{{52.65097, 14.99906}}} /* reference track */),
|
||||
());
|
||||
}
|
||||
|
||||
UNIT_TEST(Ferry_RoutingQuality_10)
|
||||
{
|
||||
TEST(CheckCarRoute({63.33900, 10.27831} /* start */, {63.33338, 10.22694} /* finish */,
|
||||
{{{63.32261, 10.26896}}} /* reference track */),
|
||||
());
|
||||
}
|
||||
|
||||
UNIT_TEST(Ferry_RoutingQuality_11)
|
||||
{
|
||||
TEST(CheckCarRoute({56.51167, 10.28726} /* start */, {56.57695, 10.11188} /* finish */,
|
||||
{{{56.44610, 10.26030}}} /* reference track */),
|
||||
());
|
||||
}
|
||||
|
||||
UNIT_TEST(Ferry_RoutingQuality_13)
|
||||
{
|
||||
TEST(CheckCarRoute({51.53923, 11.78523} /* start */, {51.62372, 11.86635} /* finish */,
|
||||
{{{51.50772, 11.94096}}} /* reference track */),
|
||||
());
|
||||
}
|
||||
|
||||
UNIT_TEST(Ferry_RoutingQuality_14)
|
||||
{
|
||||
TEST(CheckCarRoute({52.68467, 16.24031} /* start */, {52.74559, 16.27202} /* finish */,
|
||||
{{{52.71631, 16.37917}}} /* reference track */),
|
||||
());
|
||||
}
|
||||
|
||||
UNIT_TEST(Ferry_RoutingQuality_15)
|
||||
{
|
||||
TEST(CheckCarRoute({48.29162, 22.21412} /* start */, {48.27409, 22.46155} /* finish */,
|
||||
{{{48.43306, 22.23317}}} /* reference track */),
|
||||
());
|
||||
}
|
||||
} // namespace
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
#include "testing/testing.hpp"
|
||||
|
||||
#include "routing/routing_quality/waypoints.hpp"
|
||||
|
||||
using namespace routing_quality;
|
||||
|
||||
// Tests on availability to build route.
|
||||
namespace
|
||||
{
|
||||
// Test on building route from and to Bilbau Airport.
|
||||
UNIT_TEST(RoutingQuality_BilbauAirport)
|
||||
{
|
||||
// From Bilbau Airport.
|
||||
TEST(CheckCarRoute({43.3017, -2.9151} /* start */, {43.27637, -2.86924} /* finish */,
|
||||
{{{43.27759, -2.87367}}} /* reference track */),
|
||||
());
|
||||
|
||||
// To Bilbau Airport.
|
||||
TEST(CheckCarRoute({43.27651, -2.86918} /* start */, {43.2805, -2.87853} /* finish */,
|
||||
{{{43.27788, -2.87385}}} /* reference track */),
|
||||
());
|
||||
}
|
||||
|
||||
// Test on building route from and to Fairbanks International Airport.
|
||||
UNIT_TEST(RoutingQuality_FairbanksAirport)
|
||||
{
|
||||
// From Fairbanks International Airport.
|
||||
TEST(CheckCarRoute({64.81398, -147.85897} /* start */, {64.85873, -147.69372} /* finish */,
|
||||
{{{64.85886, -147.70319}}} /* reference track */),
|
||||
());
|
||||
|
||||
// To Fairbanks International Airport.
|
||||
TEST(CheckCarRoute({64.85893, -147.69438} /* start */, {64.81398, -147.85897} /* finish */,
|
||||
{{{64.85825, -147.71106}}} /* reference track */),
|
||||
());
|
||||
}
|
||||
} // namespace
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
#include "testing/testing.hpp"
|
||||
|
||||
#include "routing/vehicle_mask.hpp"
|
||||
|
||||
#include "routing/routing_quality/waypoints.hpp"
|
||||
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace leaps_postprocessing_tests
|
||||
{
|
||||
using namespace routing;
|
||||
using namespace routing_quality;
|
||||
using namespace std;
|
||||
|
||||
UNIT_TEST(RoutingQuality_NoLoop_Canada)
|
||||
{
|
||||
TEST(!CheckCarRoute({53.53540, -113.50798} /* start */, {69.44402, -133.03189} /* finish */,
|
||||
{{{61.74073, -121.21379}}} /* reference point */),
|
||||
());
|
||||
}
|
||||
|
||||
UNIT_TEST(RoutingQuality_NoLoop_ZhitomirTver)
|
||||
{
|
||||
TEST(!CheckCarRoute({50.94928, 28.64163} /* start */, {54.50750, 30.47854} /* finish */,
|
||||
{{{51.62925, 29.08458}}} /* reference point */),
|
||||
());
|
||||
}
|
||||
|
||||
UNIT_TEST(RoutingQuality_NoLoop_MacedoniaMontenegro_1)
|
||||
{
|
||||
TEST(!CheckCarRoute({42.02901, 21.44826} /* start */, {42.77394, 18.94886} /* finish */,
|
||||
{{{42.66290, 20.20949}}} /* reference point */),
|
||||
());
|
||||
}
|
||||
|
||||
UNIT_TEST(RoutingQuality_NoLoop_MacedoniaMontenegro_2)
|
||||
{
|
||||
TEST(!CheckCarRoute({41.14033, 22.50236} /* start */, {42.42424, 18.77128} /* finish */,
|
||||
{{{42.66181, 20.28980}}} /* reference point */),
|
||||
());
|
||||
}
|
||||
|
||||
UNIT_TEST(RoutingQuality_NoLoop_MacedoniaMontenegro_3)
|
||||
{
|
||||
TEST(!CheckCarRoute({42.00375, 21.50582} /* start */, {42.14698, 19.04367} /* finish */,
|
||||
{{{42.69257, 20.08659}}} /* reference point */),
|
||||
());
|
||||
}
|
||||
|
||||
UNIT_TEST(RoutingQuality_NoLoop_AbkhaziaDonetsk)
|
||||
{
|
||||
TEST(!CheckCarRoute({43.17286, 40.39015} /* start */, {48.01587, 37.80132} /* finish */,
|
||||
{{{47.69821, 38.67685}}} /* reference point */),
|
||||
());
|
||||
}
|
||||
|
||||
UNIT_TEST(RoutingQuality_NoLoop_RussiaKomiChuvashia)
|
||||
{
|
||||
TEST(!CheckCarRoute({60.175920, 49.641070} /* start */, {56.077370, 47.875380} /* finish */,
|
||||
{{{56.16673, 47.80223}}} /* reference point */),
|
||||
());
|
||||
}
|
||||
|
||||
UNIT_TEST(RoutingQuality_NoLoop_SaratovMoscow)
|
||||
{
|
||||
TEST(!CheckCarRoute({51.54151, 46.23666} /* start */, {56.12105, 37.61638} /* finish */,
|
||||
{{{55.91385, 37.58531}}} /* reference point */),
|
||||
());
|
||||
}
|
||||
|
||||
UNIT_TEST(RoutingQuality_NoLoop_BucharestMontenegro)
|
||||
{
|
||||
TEST(!CheckCarRoute({44.41418, 26.11567} /* start */, {42.80962, 19.50849} /* finish */,
|
||||
{{{42.66125, 20.26862}}} /* reference point */),
|
||||
());
|
||||
}
|
||||
|
||||
UNIT_TEST(RoutingQuality_NoLoop_RyazanOblastGorshkovo)
|
||||
{
|
||||
TEST(!CheckCarRoute({54.30282, 38.93610} /* start */, {56.37584, 37.40839} /* finish */,
|
||||
{{{55.91318, 37.58136}}} /* reference point */),
|
||||
());
|
||||
}
|
||||
|
||||
UNIT_TEST(RoutingQuality_NoLoop_BulgariaEastMontenegro)
|
||||
{
|
||||
TEST(!CheckCarRoute({42.82058, 27.87946} /* start */, {42.42889, 18.70008} /* finish */,
|
||||
{{{42.70045, 20.11199}}} /* reference point */),
|
||||
());
|
||||
}
|
||||
|
||||
UNIT_TEST(RoutingQuality_NoLoop_SkopjeMontenegro)
|
||||
{
|
||||
TEST(!CheckCarRoute({41.99137, 21.44921} /* start */, {42.25520, 18.89884} /* finish */,
|
||||
{{{42.68296, 20.18158}}} /* reference point */),
|
||||
());
|
||||
}
|
||||
} // namespace leaps_postprocessing_tests
|
||||
|
|
@ -0,0 +1,149 @@
|
|||
#include "testing/testing.hpp"
|
||||
|
||||
#include "routing/routing_quality/waypoints.hpp"
|
||||
|
||||
using namespace routing_quality;
|
||||
|
||||
// In most cases a passby road should be preferred in case of going pass a city.
|
||||
// Test on such cases should be grouped in this file.
|
||||
namespace
|
||||
{
|
||||
UNIT_TEST(RoutingQuality_RussiaZelegonrad2Domodedovo)
|
||||
{
|
||||
// From Zelenograd to Domodedovo. MKAD should be preferred.
|
||||
TEST(CheckCarRoute({55.98301, 37.21141} /* start */, {55.42081, 37.89361} /* finish */,
|
||||
{{{55.99751, 37.23804}, // Through M-11 and MKAD.
|
||||
{56.00719, 37.28533},
|
||||
{55.88759, 37.48068},
|
||||
{55.83513, 37.39569}},
|
||||
{{55.99775, 37.24941}, // Through M-10 and MKAD.
|
||||
{55.88627, 37.43915},
|
||||
{55.86882, 37.40784},
|
||||
{55.58645, 37.71672},
|
||||
{55.57855, 37.75468}}} /* reference tracks */),
|
||||
());
|
||||
}
|
||||
|
||||
UNIT_TEST(RoutingQuality_BelarusKobrin)
|
||||
{
|
||||
// Test on using a passby road around Kobirn.
|
||||
TEST(CheckCarRoute({52.18429, 24.20225} /* start */, {52.24404, 24.45842} /* finish */,
|
||||
{{{52.18694, 24.39903}}} /* reference point */),
|
||||
());
|
||||
}
|
||||
|
||||
UNIT_TEST(RoutingQuality_BelarusBobruisk)
|
||||
{
|
||||
TEST(CheckCarRoute({53.24596, 28.93816} /* start */, {53.04386, 29.58098} /* finish */,
|
||||
{{{53.24592, 29.29409}}} /* reference point */),
|
||||
());
|
||||
}
|
||||
|
||||
UNIT_TEST(RoutingQuality_RussiaStPetersburg)
|
||||
{
|
||||
TEST(CheckCarRoute({60.08634, 30.10277} /* start */, {59.94584, 30.57703} /* finish */,
|
||||
{{{60.03478, 30.44084}}} /* reference point */),
|
||||
());
|
||||
}
|
||||
|
||||
UNIT_TEST(RoutingQuality_BelarusMinsk)
|
||||
{
|
||||
TEST(CheckCarRoute({53.75958, 28.005} /* start */, {54.03957, 26.83097} /* finish */,
|
||||
{{{53.70668, 27.4487}}} /* reference point */),
|
||||
());
|
||||
}
|
||||
|
||||
UNIT_TEST(RoutingQuality_BelarusMinskMKAD)
|
||||
{
|
||||
TEST(CheckCarRoute({53.81784, 27.76789} /* start */, {53.94655, 27.36398} /* finish */,
|
||||
{{{53.95037, 27.65361}}} /* reference point */),
|
||||
());
|
||||
}
|
||||
|
||||
UNIT_TEST(RoutingQuality_EnglandLondon)
|
||||
{
|
||||
TEST(CheckCarRoute({51.90356, -0.20133} /* start */, {51.23253, -0.33076} /* finish */,
|
||||
{{{51.57098, -0.53503}}} /* reference point */),
|
||||
());
|
||||
}
|
||||
|
||||
// After map update to 190719 the route starts go throw Chernigov instead of using
|
||||
// passby way. It should be fix and the test should be uncommented.
|
||||
// UNIT_TEST(RoutingQuality_UkraineChernigov)
|
||||
//{
|
||||
// TEST(CheckCarRoute({51.29419, 31.25718} /* start */, {51.62678, 31.21787} /* finish */,
|
||||
// {{{51.48362, 31.18757}}} /* reference point */),
|
||||
// ());
|
||||
//}
|
||||
|
||||
UNIT_TEST(RoutingQuality_PolandSiedlce)
|
||||
{
|
||||
TEST(CheckCarRoute({52.17525, 22.19702} /* start */, {52.119802, 22.35855} /* finish */,
|
||||
{{{52.14355, 22.231}}} /* reference point */),
|
||||
());
|
||||
}
|
||||
|
||||
UNIT_TEST(RoutingQuality_HungarySzolnok)
|
||||
{
|
||||
TEST(CheckCarRoute({47.18462, 20.04432} /* start */, {47.17919, 20.33486} /* finish */,
|
||||
{{{47.14467, 20.17032}}} /* reference point */),
|
||||
());
|
||||
}
|
||||
|
||||
UNIT_TEST(RoutingQuality_USATexasAbilene)
|
||||
{
|
||||
TEST(CheckCarRoute({32.46041, -99.93058} /* start */, {32.43085, -99.59475} /* finish */,
|
||||
{{{32.49038, -99.7269}}} /* reference point */),
|
||||
());
|
||||
}
|
||||
|
||||
UNIT_TEST(RoutingQuality_ItalyParma)
|
||||
{
|
||||
TEST(CheckCarRoute({44.81937, 10.2403} /* start */, {44.78228, 10.38824} /* finish */,
|
||||
{{{44.81625, 10.34545}}} /* reference point */),
|
||||
());
|
||||
}
|
||||
|
||||
UNIT_TEST(RoutingQuality_SlovenijaLjubljana)
|
||||
{
|
||||
TEST(CheckCarRoute({45.99272, 14.59186} /* start */, {46.10318, 14.46829} /* finish */,
|
||||
{{{46.04449, 14.44669}}} /* reference point */),
|
||||
());
|
||||
}
|
||||
|
||||
// TODO: Uncomment this test when correct city boundaries or crossroads will be ready.
|
||||
// UNIT_TEST(RoutingQuality_FrancePoitiers)
|
||||
//{
|
||||
// TEST(CheckCarRoute({46.63612, 0.35762} /* start */, {46.49, 0.36787} /* finish */,
|
||||
// {{{46.58706, 0.39232}}} /* reference point */),
|
||||
// ());
|
||||
//}
|
||||
|
||||
UNIT_TEST(RoutingQuality_FranceLoudun)
|
||||
{
|
||||
TEST(CheckCarRoute({47.03437, 0.04437} /* start */, {46.97887, 0.09692} /* finish */,
|
||||
{{{47.00307, 0.06713}}} /* reference point */),
|
||||
());
|
||||
}
|
||||
|
||||
UNIT_TEST(RoutingQuality_FranceDoueIaFontaine)
|
||||
{
|
||||
TEST(CheckCarRoute({47.22972, -0.30962} /* start */, {47.17023, -0.2185} /* finish */,
|
||||
{{{47.19117, -0.31334}}} /* reference point */),
|
||||
());
|
||||
}
|
||||
|
||||
UNIT_TEST(RoutingQuality_BelgiumBrussel)
|
||||
{
|
||||
TEST(CheckCarRoute({50.88374, 4.2195} /* start */, {50.91494, 4.38122} /* finish */,
|
||||
{{{50.91727, 4.36858}}} /* reference point */),
|
||||
());
|
||||
}
|
||||
|
||||
UNIT_TEST(RoutingQuality_SouthernDenmarkPastUnclassified)
|
||||
{
|
||||
TEST(CheckCarRoute({55.44681, 10.29} /* start */, {55.45877, 10.26456} /* finish */,
|
||||
{{{55.45505, 10.26972}}} /* reference point */),
|
||||
());
|
||||
}
|
||||
} // namespace
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
#include "testing/testing.hpp"
|
||||
|
||||
#include "routing/vehicle_mask.hpp"
|
||||
|
||||
#include "routing/routing_quality/waypoints.hpp"
|
||||
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace waypoins_tests
|
||||
{
|
||||
using namespace routing;
|
||||
using namespace routing_quality;
|
||||
using namespace std;
|
||||
|
||||
UNIT_TEST(RoutingQuality_CompareSmoke)
|
||||
{
|
||||
// From office to Aseeva 6.
|
||||
TEST(CheckCarRoute({55.79723, 37.53777} /* start */, {55.80634, 37.52886} /* finish */,
|
||||
{{{55.79676, 37.54138},
|
||||
{55.79914, 37.53582},
|
||||
{55.80353, 37.52478},
|
||||
{55.80556, 37.52770}}} /* reference track */),
|
||||
());
|
||||
}
|
||||
|
||||
UNIT_TEST(RoutingQuality_Sokol2Mayakovskaya)
|
||||
{
|
||||
// From Sokol to Mayakovskaya through Leningradsky Avenue but not through its alternate.
|
||||
Params params(VehicleType::Car, {55.80432, 37.51603} /* start */, {55.77019, 37.59558} /* finish */);
|
||||
|
||||
// All points lie on the alternate so the result should be 0.
|
||||
ReferenceRoutes waypoints = {{{55.79599, 37.54114}, {55.78142, 37.57364}, {55.77863, 37.57989}}};
|
||||
|
||||
TEST_EQUAL(CheckWaypoints(params, std::move(waypoints)), 0.0, ());
|
||||
}
|
||||
} // namespace waypoins_tests
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
project(routing_quality_tool)
|
||||
|
||||
set(SRC
|
||||
benchmark_results.cpp
|
||||
benchmark_results.hpp
|
||||
benchmark_stat.cpp
|
||||
benchmark_stat.hpp
|
||||
error_type_counter.cpp
|
||||
error_type_counter.hpp
|
||||
routing_quality_tool.cpp
|
||||
utils.cpp
|
||||
utils.hpp
|
||||
)
|
||||
|
||||
omim_add_executable(${PROJECT_NAME} ${SRC})
|
||||
|
||||
target_link_libraries(${PROJECT_NAME}
|
||||
routing_quality
|
||||
routing_api
|
||||
kml
|
||||
gflags::gflags
|
||||
)
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
#include "routing/routing_quality/routing_quality_tool/benchmark_results.hpp"
|
||||
|
||||
namespace routing_quality::routing_quality_tool
|
||||
{
|
||||
double BenchmarkResults::GetAverageBuildTime() const
|
||||
{
|
||||
if (m_buildTimes.empty())
|
||||
return 0.0;
|
||||
|
||||
return m_summaryBuildTimeSeconds / static_cast<double>(m_buildTimes.size());
|
||||
}
|
||||
|
||||
void BenchmarkResults::PushBuildTime(double time)
|
||||
{
|
||||
m_summaryBuildTimeSeconds += time;
|
||||
m_buildTimes.emplace_back(time);
|
||||
}
|
||||
} // namespace routing_quality::routing_quality_tool
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
#pragma once
|
||||
|
||||
#include "routing/routing_callbacks.hpp"
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace routing_quality::routing_quality_tool
|
||||
{
|
||||
class BenchmarkResults
|
||||
{
|
||||
public:
|
||||
double GetAverageBuildTime() const;
|
||||
void PushBuildTime(double time);
|
||||
|
||||
std::vector<double> const & GetBuildTimes() const { return m_buildTimes; }
|
||||
|
||||
private:
|
||||
double m_summaryBuildTimeSeconds = 0.0;
|
||||
// m_buildTimes[i] stores build time of i-th route.
|
||||
std::vector<double> m_buildTimes;
|
||||
};
|
||||
|
||||
struct TimeInfo
|
||||
{
|
||||
TimeInfo(double oldTime, double newTime) : m_oldTime(oldTime), m_newTime(newTime) {}
|
||||
double m_oldTime;
|
||||
double m_newTime;
|
||||
};
|
||||
} // namespace routing_quality::routing_quality_tool
|
||||
|
|
@ -0,0 +1,303 @@
|
|||
#include "routing/routing_quality/routing_quality_tool/benchmark_stat.hpp"
|
||||
|
||||
#include "routing/routing_quality/routing_quality_tool/benchmark_results.hpp"
|
||||
#include "routing/routing_quality/routing_quality_tool/error_type_counter.hpp"
|
||||
#include "routing/routing_quality/routing_quality_tool/utils.hpp"
|
||||
|
||||
#include "routing/routing_callbacks.hpp"
|
||||
|
||||
#include "geometry/point2d.hpp"
|
||||
|
||||
#include "base/assert.hpp"
|
||||
#include "base/file_name_utils.hpp"
|
||||
#include "base/logging.hpp"
|
||||
#include "base/string_utils.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace
|
||||
{
|
||||
using namespace routing;
|
||||
using namespace routes_builder;
|
||||
using namespace routing_quality::routing_quality_tool;
|
||||
|
||||
bool IsErrorCode(RouterResultCode code)
|
||||
{
|
||||
return code != RouterResultCode::NoError;
|
||||
}
|
||||
|
||||
void LogIfNotConsistent(RoutesBuilder::Result const & oldRes, RoutesBuilder::Result const & newRes)
|
||||
{
|
||||
auto const start = mercator::ToLatLon(oldRes.m_params.m_checkpoints.GetStart());
|
||||
auto const finish = mercator::ToLatLon(oldRes.m_params.m_checkpoints.GetFinish());
|
||||
CHECK(!oldRes.m_routes.empty(), ());
|
||||
CHECK(!newRes.m_routes.empty(), ());
|
||||
auto const & oldRoute = oldRes.m_routes.back();
|
||||
auto const & newRoute = newRes.m_routes.back();
|
||||
|
||||
bool const sameETA = AlmostEqualAbs(oldRoute.m_eta, newRoute.m_eta, 1.0);
|
||||
bool const sameDistance = AlmostEqualAbs(oldRoute.m_distance, newRoute.m_distance, 1.0);
|
||||
if (!sameETA || !sameDistance)
|
||||
{
|
||||
LOG(LINFO, ("old ETA:", oldRoute.m_eta, "old distance:", oldRoute.m_distance, "new ETA:", newRoute.m_eta,
|
||||
"new distance:", newRoute.m_distance, start, finish));
|
||||
}
|
||||
}
|
||||
|
||||
/// \brief Helps to compare route time building for routes group by old time building.
|
||||
void FillInfoAboutBuildTimeGroupByPreviousResults(std::vector<std::string> & labels,
|
||||
std::vector<std::vector<double>> & bars,
|
||||
std::vector<TimeInfo> && times)
|
||||
{
|
||||
bars.clear();
|
||||
labels.clear();
|
||||
|
||||
std::sort(times.begin(), times.end(), [](auto const & a, auto const & b) { return a.m_oldTime < b.m_oldTime; });
|
||||
|
||||
size_t constexpr kSteps = 10;
|
||||
size_t const step = times.size() / kSteps;
|
||||
|
||||
size_t startFrom = 0;
|
||||
size_t curCount = 0;
|
||||
bars.resize(2);
|
||||
double curSumOld = 0;
|
||||
double curSumNew = 0;
|
||||
for (size_t i = 0; i < times.size(); ++i)
|
||||
{
|
||||
if (curCount < step && i + 1 != times.size())
|
||||
{
|
||||
++curCount;
|
||||
curSumOld += times[i].m_oldTime;
|
||||
curSumNew += times[i].m_newTime;
|
||||
continue;
|
||||
}
|
||||
|
||||
double const curLeft = times[startFrom].m_oldTime;
|
||||
startFrom = i + 1;
|
||||
double const curRight = times[i].m_oldTime;
|
||||
labels.emplace_back("[" + strings::to_string_dac(curLeft, 2 /* dac */) + "s, " +
|
||||
strings::to_string_dac(curRight, 2 /* dac */) + "s]\\n" + "Routes count:\\n" +
|
||||
std::to_string(curCount));
|
||||
if (curCount != 0)
|
||||
{
|
||||
curSumOld /= curCount;
|
||||
curSumNew /= curCount;
|
||||
}
|
||||
double const k = curSumOld == 0.0 ? 0.0 : curSumNew / curSumOld;
|
||||
bars[0].emplace_back(100.0);
|
||||
bars[1].emplace_back(100.0 * k);
|
||||
curCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<double> GetBoostPercents(BenchmarkResults const & oldResults, BenchmarkResults const & newResults)
|
||||
{
|
||||
std::vector<double> boostPercents;
|
||||
for (size_t i = 0; i < oldResults.GetBuildTimes().size(); ++i)
|
||||
{
|
||||
auto const oldTime = oldResults.GetBuildTimes()[i];
|
||||
auto const newTime = newResults.GetBuildTimes()[i];
|
||||
if (AlmostEqualAbs(oldTime, newTime, 1e-2))
|
||||
continue;
|
||||
|
||||
auto const diffPercent = (oldTime - newTime) / oldTime * 100.0;
|
||||
boostPercents.emplace_back(diffPercent);
|
||||
}
|
||||
|
||||
return boostPercents;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace routing_quality::routing_quality_tool
|
||||
{
|
||||
using namespace routing;
|
||||
using namespace routes_builder;
|
||||
// Shows distribution of routes time building.
|
||||
static std::string const kPythonDistTimeBuilding = "show_route_time_building_dist.py";
|
||||
// Shows graph of "how many routes in percents build in less time than some T".
|
||||
static std::string const kPythonGraphTimeAndCount = "show_time_count_graph.py";
|
||||
// Bar graph of routing errors. Labels - string representation of errors, heights - number of such
|
||||
// errors.
|
||||
static std::string const kPythonBarError = "show_errors_bar.py";
|
||||
// Shows percent of boost distribution. Where boost percent equals:
|
||||
// 100.0 * (old_time - new_time) / old_time
|
||||
static std::string const kPythonBarBoostPercentDistr = "show_boost_distr.py";
|
||||
// Shows distribution of non-zero difference of ETA between old and new mapsme version.
|
||||
static std::string const kPythonEtaDiff = "eta_diff.py";
|
||||
// The same as above but in percents.
|
||||
static std::string const kPythonEtaDiffPercent = "eta_diff_percent.py";
|
||||
// Groups routes by previous time building and draws two types of bars. The first one (old mapsme)
|
||||
// has the same height in all groups and the second ones' height is proportional less or more
|
||||
// according to difference in average time building between old and new version. The example you can
|
||||
// see here: https://github.com/mapsme/omim/pull/12401
|
||||
static std::string const kPythonSmartDistr = "show_smart_boost_distr.py";
|
||||
|
||||
void RunBenchmarkStat(std::vector<std::pair<RoutesBuilder::Result, std::string>> const & mapsmeResults,
|
||||
std::string const & dirForResults)
|
||||
{
|
||||
BenchmarkResults benchmarkResults;
|
||||
ErrorTypeCounter errorTypeCounter;
|
||||
for (auto const & resultItem : mapsmeResults)
|
||||
{
|
||||
auto const & result = resultItem.first;
|
||||
errorTypeCounter.PushError(result.m_code);
|
||||
if (result.m_code != RouterResultCode::NoError)
|
||||
continue;
|
||||
|
||||
CHECK(!result.m_routes.empty(), ());
|
||||
benchmarkResults.PushBuildTime(result.m_buildTimeSeconds);
|
||||
}
|
||||
|
||||
auto pythonScriptPath = base::JoinPath(dirForResults, kPythonDistTimeBuilding);
|
||||
CreatePythonScriptForDistribution(pythonScriptPath, "Route building time, seconds", benchmarkResults.GetBuildTimes());
|
||||
|
||||
LOG(LINFO, ("Average route time building:", benchmarkResults.GetAverageBuildTime(), "seconds."));
|
||||
|
||||
auto times = benchmarkResults.GetBuildTimes();
|
||||
std::sort(times.begin(), times.end());
|
||||
std::vector<m2::PointD> countToTimes(times.size());
|
||||
for (size_t i = 0; i < countToTimes.size(); ++i)
|
||||
{
|
||||
countToTimes[i].x = times[i];
|
||||
countToTimes[i].y = static_cast<double>(i + 1) / times.size() * 100.0;
|
||||
}
|
||||
|
||||
pythonScriptPath = base::JoinPath(dirForResults, kPythonGraphTimeAndCount);
|
||||
CreatePythonGraphByPointsXY(pythonScriptPath, "Building time, seconds" /* xlabel */,
|
||||
"Percent of routes built less than" /* ylabel */, {countToTimes},
|
||||
{"mapsme"} /* legends */);
|
||||
|
||||
pythonScriptPath = base::JoinPath(dirForResults, kPythonBarError);
|
||||
|
||||
std::vector<std::string> labels;
|
||||
std::vector<double> heights;
|
||||
FillLabelsAndErrorTypeDistribution(labels, heights, errorTypeCounter);
|
||||
|
||||
CreatePythonBarByMap(pythonScriptPath, labels, {heights}, {"mapsme"} /* legends */, "Type of errors" /* xlabel */,
|
||||
"Number of errors" /* ylabel */);
|
||||
}
|
||||
|
||||
void RunBenchmarkComparison(std::vector<std::pair<RoutesBuilder::Result, std::string>> && mapsmeResults,
|
||||
std::vector<std::pair<RoutesBuilder::Result, std::string>> && mapsmeOldResults,
|
||||
std::string const & dirForResults)
|
||||
{
|
||||
BenchmarkResults benchmarkResults;
|
||||
BenchmarkResults benchmarkOldResults;
|
||||
ErrorTypeCounter errorTypeCounter;
|
||||
ErrorTypeCounter errorTypeCounterOld;
|
||||
std::vector<double> etaDiffsPercent;
|
||||
std::vector<double> etaDiffs;
|
||||
|
||||
std::vector<TimeInfo> times;
|
||||
|
||||
for (size_t i = 0; i < mapsmeResults.size(); ++i)
|
||||
{
|
||||
auto const & mapsmeResult = mapsmeResults[i].first;
|
||||
|
||||
std::pair<RoutesBuilder::Result, std::string> mapsmeOldResultPair;
|
||||
if (!FindAnotherResponse(mapsmeResult, mapsmeOldResults, mapsmeOldResultPair))
|
||||
{
|
||||
LOG(LDEBUG, ("Can not find pair for:", i));
|
||||
continue;
|
||||
}
|
||||
|
||||
auto const & mapsmeOldResult = mapsmeOldResultPair.first;
|
||||
|
||||
errorTypeCounter.PushError(mapsmeResult.m_code);
|
||||
errorTypeCounterOld.PushError(mapsmeOldResult.m_code);
|
||||
|
||||
if (IsErrorCode(mapsmeResult.m_code) && !IsErrorCode(mapsmeOldResult.m_code))
|
||||
{
|
||||
auto const start = mercator::ToLatLon(mapsmeResult.m_params.m_checkpoints.GetStart());
|
||||
auto const finish = mercator::ToLatLon(mapsmeResult.m_params.m_checkpoints.GetFinish());
|
||||
LOG_FORCE(LWARNING, ("New version returns error code:", mapsmeResult.m_code,
|
||||
"but old returns NoError for:", start, finish));
|
||||
}
|
||||
|
||||
if (IsErrorCode(mapsmeResult.m_code) || IsErrorCode(mapsmeOldResult.m_code))
|
||||
continue;
|
||||
|
||||
LogIfNotConsistent(mapsmeOldResult, mapsmeResult);
|
||||
|
||||
CHECK(!mapsmeOldResult.m_routes.empty() && !mapsmeResult.m_routes.empty(), ());
|
||||
auto const etaDiff = (mapsmeOldResult.m_routes.back().m_eta - mapsmeResult.m_routes.back().m_eta);
|
||||
auto const etaDiffPercent = etaDiff / mapsmeOldResult.m_routes.back().m_eta * 100.0;
|
||||
|
||||
etaDiffs.emplace_back(etaDiff);
|
||||
etaDiffsPercent.emplace_back(etaDiffPercent);
|
||||
|
||||
auto const oldTime = mapsmeOldResult.m_buildTimeSeconds;
|
||||
auto const newTime = mapsmeResult.m_buildTimeSeconds;
|
||||
auto const diffPercent = (oldTime - newTime) / oldTime * 100.0;
|
||||
// Warn about routes building time degradation.
|
||||
double constexpr kSlowdownPercent = -10.0;
|
||||
if (diffPercent < kSlowdownPercent)
|
||||
{
|
||||
auto const start = mercator::ToLatLon(mapsmeResult.m_params.m_checkpoints.GetStart());
|
||||
auto const finish = mercator::ToLatLon(mapsmeResult.m_params.m_checkpoints.GetFinish());
|
||||
LOG(LINFO, ("oldTime:", oldTime, "newTime:", newTime, "diffPercent:", diffPercent, start, finish));
|
||||
}
|
||||
|
||||
benchmarkResults.PushBuildTime(mapsmeResult.m_buildTimeSeconds);
|
||||
benchmarkOldResults.PushBuildTime(mapsmeOldResult.m_buildTimeSeconds);
|
||||
|
||||
times.emplace_back(mapsmeOldResult.m_buildTimeSeconds, mapsmeResult.m_buildTimeSeconds);
|
||||
}
|
||||
|
||||
LOG(LINFO, ("Comparing", benchmarkResults.GetBuildTimes().size(), "routes."));
|
||||
|
||||
auto const oldAverage = benchmarkOldResults.GetAverageBuildTime();
|
||||
auto const newAverage = benchmarkResults.GetAverageBuildTime();
|
||||
auto const averageTimeDiff = (oldAverage - newAverage) / oldAverage * 100.0;
|
||||
LOG(LINFO, ("Average route time building. "
|
||||
"Old version:",
|
||||
oldAverage, "New version:", newAverage, "(", -averageTimeDiff, "% )"));
|
||||
|
||||
std::vector<std::vector<m2::PointD>> graphics;
|
||||
for (auto const & results : {benchmarkOldResults, benchmarkResults})
|
||||
{
|
||||
auto times = results.GetBuildTimes();
|
||||
std::sort(times.begin(), times.end());
|
||||
std::vector<m2::PointD> countToTimes(times.size());
|
||||
for (size_t i = 0; i < countToTimes.size(); ++i)
|
||||
{
|
||||
countToTimes[i].x = times[i];
|
||||
countToTimes[i].y = static_cast<double>(i + 1) / times.size() * 100.0;
|
||||
}
|
||||
|
||||
graphics.emplace_back(std::move(countToTimes));
|
||||
}
|
||||
|
||||
auto pythonScriptPath = base::JoinPath(dirForResults, kPythonGraphTimeAndCount);
|
||||
CreatePythonGraphByPointsXY(pythonScriptPath, "Building time, seconds" /* xlabel */,
|
||||
"Percent of routes built less than" /* ylabel */, graphics,
|
||||
{"old mapsme", "new mapsme"} /* legends */);
|
||||
|
||||
std::vector<std::string> labels;
|
||||
std::vector<std::vector<double>> errorsCount;
|
||||
FillLabelsAndErrorTypeDistribution(labels, errorsCount, errorTypeCounter, errorTypeCounterOld);
|
||||
|
||||
pythonScriptPath = base::JoinPath(dirForResults, kPythonBarError);
|
||||
CreatePythonBarByMap(pythonScriptPath, labels, errorsCount, {"old mapsme", "new mapsme"} /* legends */,
|
||||
"Type of errors" /* xlabel */, "Number of errors" /* ylabel */);
|
||||
|
||||
auto const boostPercents = GetBoostPercents(benchmarkOldResults, benchmarkResults);
|
||||
pythonScriptPath = base::JoinPath(dirForResults, kPythonBarBoostPercentDistr);
|
||||
CreatePythonScriptForDistribution(pythonScriptPath, "Boost percent" /* title */, boostPercents);
|
||||
|
||||
pythonScriptPath = base::JoinPath(dirForResults, kPythonEtaDiff);
|
||||
CreatePythonScriptForDistribution(pythonScriptPath, "ETA diff distribution" /* title */, etaDiffs);
|
||||
|
||||
pythonScriptPath = base::JoinPath(dirForResults, kPythonEtaDiffPercent);
|
||||
CreatePythonScriptForDistribution(pythonScriptPath, "ETA diff percent distribution" /* title */, etaDiffsPercent);
|
||||
|
||||
std::vector<std::vector<double>> bars;
|
||||
FillInfoAboutBuildTimeGroupByPreviousResults(labels, bars, std::move(times));
|
||||
pythonScriptPath = base::JoinPath(dirForResults, kPythonSmartDistr);
|
||||
CreatePythonBarByMap(pythonScriptPath, labels, bars, {"old mapsme", "new mapsme"} /* legends */,
|
||||
"Intervals of groups (build time in old mapsme)" /* xlabel */,
|
||||
"Boost\\nRight column is so lower/higher than the left\\n how much the average build time "
|
||||
"has decreased in each group)" /* ylabel */,
|
||||
false /* drawPercents */);
|
||||
}
|
||||
} // namespace routing_quality::routing_quality_tool
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
#pragma once
|
||||
|
||||
#include "routing/routes_builder/routes_builder.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace routing_quality::routing_quality_tool
|
||||
{
|
||||
using RouteResult = routing::routes_builder::RoutesBuilder::Result;
|
||||
void RunBenchmarkStat(std::vector<std::pair<RouteResult, std::string>> const & mapsmeResults,
|
||||
std::string const & dirForResults);
|
||||
|
||||
void RunBenchmarkComparison(std::vector<std::pair<RouteResult, std::string>> && mapsmeResults,
|
||||
std::vector<std::pair<RouteResult, std::string>> && mapsmeOldResults,
|
||||
std::string const & dirForResults);
|
||||
} // namespace routing_quality::routing_quality_tool
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
#include "routing/routing_quality/routing_quality_tool/error_type_counter.hpp"
|
||||
|
||||
#include "base/assert.hpp"
|
||||
|
||||
namespace routing_quality::routing_quality_tool
|
||||
{
|
||||
void ErrorTypeCounter::PushError(routing::RouterResultCode code)
|
||||
{
|
||||
++m_errorCounter[routing::ToString(code)];
|
||||
}
|
||||
|
||||
void ErrorTypeCounter::PushError(api::ResultCode code)
|
||||
{
|
||||
routing::RouterResultCode routingCode = routing::RouterResultCode::InternalError;
|
||||
switch (code)
|
||||
{
|
||||
case api::ResultCode::ResponseOK: routingCode = routing::RouterResultCode::NoError; break;
|
||||
case api::ResultCode::Error: routingCode = routing::RouterResultCode::RouteNotFound; break;
|
||||
}
|
||||
CHECK_NOT_EQUAL(routingCode, routing::RouterResultCode::InternalError,
|
||||
("Wrong value of api::ResultCode:", static_cast<int>(code)));
|
||||
PushError(routingCode);
|
||||
}
|
||||
|
||||
void FillLabelsAndErrorTypeDistribution(std::vector<std::string> & labels, std::vector<double> & errorsTypeDistribution,
|
||||
ErrorTypeCounter const & counter)
|
||||
{
|
||||
errorsTypeDistribution.clear();
|
||||
|
||||
for (auto const & [errorName, errorCount] : counter.GetErrorsDistribution())
|
||||
{
|
||||
labels.emplace_back(errorName);
|
||||
errorsTypeDistribution.emplace_back(errorCount);
|
||||
}
|
||||
}
|
||||
|
||||
void FillLabelsAndErrorTypeDistribution(std::vector<std::string> & labels,
|
||||
std::vector<std::vector<double>> & errorsTypeDistribution,
|
||||
ErrorTypeCounter const & counter, ErrorTypeCounter const & counterOld)
|
||||
{
|
||||
errorsTypeDistribution.clear();
|
||||
errorsTypeDistribution.resize(2);
|
||||
|
||||
FillLabelsAndErrorTypeDistribution(labels, errorsTypeDistribution[0], counter);
|
||||
|
||||
for (auto const & [_, errorCount] : counterOld.GetErrorsDistribution())
|
||||
errorsTypeDistribution[1].emplace_back(errorCount);
|
||||
}
|
||||
} // namespace routing_quality::routing_quality_tool
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
#pragma once
|
||||
|
||||
#include "routing/routing_quality/api/api.hpp"
|
||||
|
||||
#include "routing/routing_callbacks.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
namespace routing_quality::routing_quality_tool
|
||||
{
|
||||
class ErrorTypeCounter
|
||||
{
|
||||
public:
|
||||
void PushError(routing::RouterResultCode code);
|
||||
void PushError(api::ResultCode code);
|
||||
std::map<std::string, size_t> const & GetErrorsDistribution() const { return m_errorCounter; }
|
||||
|
||||
private:
|
||||
// string representation of RouterResultCode to number of such codes.
|
||||
std::map<std::string, size_t> m_errorCounter;
|
||||
};
|
||||
|
||||
void FillLabelsAndErrorTypeDistribution(std::vector<std::string> & labels, std::vector<double> & errorsTypeDistribution,
|
||||
ErrorTypeCounter const & counter);
|
||||
|
||||
void FillLabelsAndErrorTypeDistribution(std::vector<std::string> & labels,
|
||||
std::vector<std::vector<double>> & errorsTypeDistribution,
|
||||
ErrorTypeCounter const & counter, ErrorTypeCounter const & counterOld);
|
||||
} // namespace routing_quality::routing_quality_tool
|
||||
|
|
@ -0,0 +1,336 @@
|
|||
#include "routing/routing_quality/routing_quality_tool/benchmark_stat.hpp"
|
||||
#include "routing/routing_quality/routing_quality_tool/error_type_counter.hpp"
|
||||
#include "routing/routing_quality/routing_quality_tool/utils.hpp"
|
||||
|
||||
#include "routing/routing_quality/api/api.hpp"
|
||||
|
||||
#include "routing/routing_quality/waypoints.hpp"
|
||||
|
||||
#include "routing/routes_builder/routes_builder.hpp"
|
||||
|
||||
#include "platform/platform.hpp"
|
||||
|
||||
#include "base/exception.hpp"
|
||||
#include "base/file_name_utils.hpp"
|
||||
#include "base/logging.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <exception>
|
||||
#include <limits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <gflags/gflags.h>
|
||||
|
||||
DEFINE_string(mapsme_old_results, "", "Path to directory with previous mapsme router results.");
|
||||
DEFINE_string(mapsme_results, "", "Path to directory with mapsme router results.");
|
||||
DEFINE_string(api_results, "", "Path to directory with api router results.");
|
||||
|
||||
DEFINE_string(save_results, "", "The directory where results of tool will be saved.");
|
||||
|
||||
DEFINE_double(kml_percent, 0.0,
|
||||
"The percent of routes for which kml file will be generated."
|
||||
"With kml files you can make screenshots with a desktop app");
|
||||
|
||||
DEFINE_bool(benchmark_stat, false, "Dump statistics about route time building.");
|
||||
|
||||
namespace
|
||||
{
|
||||
// Shows distribution of simularity in comparison mode.
|
||||
static std::string const kPythonDistribution = "show_distribution.py";
|
||||
static std::string const kPythonBarDistributionError = "show_errors_distribution.py";
|
||||
|
||||
double constexpr kBadETADiffPercent = std::numeric_limits<double>::max();
|
||||
|
||||
void PrintHelpAndExit()
|
||||
{
|
||||
std::stringstream usage;
|
||||
usage << std::boolalpha;
|
||||
usage << R"(Description:
|
||||
This tool takes two paths to directory with routes, that were dumped by routes_builder_tool and calculate some metrics. Here possible options of execution:
|
||||
|
||||
--mapsme_results and --api_results are required to compare mapsme and api.
|
||||
or
|
||||
--mapsme_results and --mapsme_old_results are required to compare mapsme and old mapsme version.
|
||||
or
|
||||
--mapsme_results and --benchmark_stat are required to calculate benchmark statistic of mapsme.
|
||||
or
|
||||
--mapsme_results and --mapsme_old_results and --benchmark_stat are required to calculate different statistics of comparison mapsme and old mapsme version.
|
||||
|
||||
--save_results is required for saving results in this directory.
|
||||
--kml_percent may be used in non-benchamrk mode for dumping kmls and visual comparison different routes.
|
||||
)";
|
||||
|
||||
auto const addStringInfo = [&usage](auto const & arg, auto const & argValue)
|
||||
{
|
||||
using T = decltype(argValue);
|
||||
usage << "\n\t" << arg << " is";
|
||||
if (argValue == T{})
|
||||
usage << " not set.";
|
||||
else
|
||||
usage << " set to: " << argValue;
|
||||
};
|
||||
|
||||
addStringInfo("--mapsme_results", FLAGS_mapsme_results);
|
||||
addStringInfo("--api_results", FLAGS_api_results);
|
||||
addStringInfo("--mapsme_old_results", FLAGS_mapsme_old_results);
|
||||
addStringInfo("--benchmark_stat", FLAGS_benchmark_stat);
|
||||
addStringInfo("--save_results", FLAGS_save_results);
|
||||
addStringInfo("--kml_percent", FLAGS_kml_percent);
|
||||
|
||||
usage << "\n\nType --help for usage.\n";
|
||||
std::cout << usage.str();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
bool HasHelpFlags(int argc, char ** argv)
|
||||
{
|
||||
for (int i = 0; i < argc; ++i)
|
||||
{
|
||||
auto const value = std::string(argv[i]);
|
||||
if (value == "--help" || value == "-h")
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
using namespace routing;
|
||||
using namespace routes_builder;
|
||||
using namespace routing_quality;
|
||||
using namespace routing_quality_tool;
|
||||
|
||||
void PrintResults(std::vector<Result> && results, RoutesSaver & routesSaver)
|
||||
{
|
||||
double sumSimilarity = 0.0;
|
||||
double sumETADiffPercent = 0.0;
|
||||
double goodETANumber = 0.0;
|
||||
for (auto const & result : results)
|
||||
{
|
||||
sumSimilarity += result.m_similarity;
|
||||
if (result.m_etaDiffPercent != kBadETADiffPercent)
|
||||
{
|
||||
sumETADiffPercent += result.m_etaDiffPercent;
|
||||
goodETANumber += 1;
|
||||
}
|
||||
}
|
||||
|
||||
LOG(LINFO, ("Matched routes:", results.size()));
|
||||
LOG(LINFO, ("Average similarity:", sumSimilarity / results.size()));
|
||||
LOG(LINFO, ("Average eta difference by fullmathed routes:", sumETADiffPercent / goodETANumber, "%"));
|
||||
|
||||
auto const pythonScriptPath = base::JoinPath(FLAGS_save_results, kPythonDistribution);
|
||||
|
||||
std::vector<double> values;
|
||||
values.reserve(results.size());
|
||||
for (auto const & result : results)
|
||||
values.emplace_back(result.m_similarity);
|
||||
|
||||
CreatePythonScriptForDistribution(pythonScriptPath, "Simularity distribution", values);
|
||||
|
||||
SimilarityCounter similarityCounter(routesSaver);
|
||||
|
||||
std::sort(results.begin(), results.end());
|
||||
for (auto const & result : results)
|
||||
similarityCounter.Push(result);
|
||||
|
||||
similarityCounter.CreateKmlFiles(FLAGS_kml_percent, results);
|
||||
}
|
||||
|
||||
bool IsMapsmeVsApi()
|
||||
{
|
||||
return !FLAGS_mapsme_results.empty() && !FLAGS_api_results.empty();
|
||||
}
|
||||
|
||||
bool IsMapsmeVsMapsme()
|
||||
{
|
||||
return !FLAGS_mapsme_results.empty() && !FLAGS_mapsme_old_results.empty() && !FLAGS_benchmark_stat;
|
||||
}
|
||||
|
||||
bool IsMapsmeBenchmarkStat()
|
||||
{
|
||||
return !FLAGS_mapsme_results.empty() && FLAGS_benchmark_stat && FLAGS_mapsme_old_results.empty();
|
||||
}
|
||||
|
||||
bool IsMapsmeVsMapsmeBenchmarkStat()
|
||||
{
|
||||
return !FLAGS_mapsme_results.empty() && FLAGS_benchmark_stat && !FLAGS_mapsme_old_results.empty();
|
||||
}
|
||||
|
||||
void CheckDirExistence(std::string const & dir)
|
||||
{
|
||||
CHECK(Platform::IsDirectory(dir), ("Can not find directory:", dir));
|
||||
}
|
||||
|
||||
template <typename AnotherResult>
|
||||
void RunComparison(std::vector<std::pair<RoutesBuilder::Result, std::string>> && mapsmeResults,
|
||||
std::vector<std::pair<AnotherResult, std::string>> && anotherResults)
|
||||
{
|
||||
ErrorTypeCounter mapsmeErrorCounter;
|
||||
ErrorTypeCounter anotherErrorCounter;
|
||||
ComparisonType type = IsMapsmeVsApi() ? ComparisonType::MapsmeVsApi : ComparisonType::MapsmeVsMapsme;
|
||||
RoutesSaver routesSaver(FLAGS_save_results, type);
|
||||
std::vector<Result> results;
|
||||
size_t apiErrors = 0;
|
||||
|
||||
for (size_t i = 0; i < mapsmeResults.size(); ++i)
|
||||
{
|
||||
auto const & mapsmeResult = mapsmeResults[i].first;
|
||||
auto const & mapsmeFile = mapsmeResults[i].second;
|
||||
|
||||
std::pair<AnotherResult, std::string> anotherResultPair;
|
||||
if (!FindAnotherResponse(mapsmeResult, anotherResults, anotherResultPair))
|
||||
{
|
||||
LOG(LDEBUG, ("Can not find pair for:", i));
|
||||
continue;
|
||||
}
|
||||
|
||||
auto const & anotherResult = anotherResultPair.first;
|
||||
auto const & anotherFile = anotherResultPair.second;
|
||||
|
||||
auto const & startLatLon = mercator::ToLatLon(anotherResult.GetStartPoint());
|
||||
auto const & finishLatLon = mercator::ToLatLon(anotherResult.GetFinishPoint());
|
||||
|
||||
mapsmeErrorCounter.PushError(mapsmeResult.m_code);
|
||||
anotherErrorCounter.PushError(anotherResult.m_code);
|
||||
|
||||
if (!mapsmeResult.IsCodeOK() && anotherResult.IsCodeOK())
|
||||
{
|
||||
routesSaver.PushError(mapsmeResult.m_code, startLatLon, finishLatLon);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (anotherResult.GetRoutes().empty() || !anotherResult.IsCodeOK())
|
||||
{
|
||||
routesSaver.PushRivalError(startLatLon, finishLatLon);
|
||||
++apiErrors;
|
||||
continue;
|
||||
}
|
||||
|
||||
auto maxSimilarity = std::numeric_limits<Similarity>::min();
|
||||
double etaDiff = kBadETADiffPercent;
|
||||
auto const & mapsmeRoute = mapsmeResult.GetRoutes().back();
|
||||
for (auto const & route : anotherResult.GetRoutes())
|
||||
{
|
||||
auto const similarity =
|
||||
metrics::CompareByNumberOfMatchedWaypoints(mapsmeRoute.m_followedPolyline, route.GetWaypoints());
|
||||
|
||||
if (maxSimilarity < similarity)
|
||||
{
|
||||
maxSimilarity = similarity;
|
||||
if (maxSimilarity == 1.0)
|
||||
{
|
||||
etaDiff =
|
||||
100.0 * std::abs(route.GetETA() - mapsmeRoute.GetETA()) / std::max(route.GetETA(), mapsmeRoute.GetETA());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
results.emplace_back(mapsmeFile, anotherFile, startLatLon, finishLatLon, maxSimilarity, etaDiff);
|
||||
}
|
||||
|
||||
std::string const anotherSourceName = IsMapsmeVsMapsme() ? "old mapsme," : "api,";
|
||||
LOG(LINFO, (apiErrors, "routes can not build via", anotherSourceName, "but mapsme do built them."));
|
||||
|
||||
PrintResults(std::move(results), routesSaver);
|
||||
|
||||
std::vector<std::string> errorLabels;
|
||||
std::vector<std::vector<double>> errorsCount;
|
||||
FillLabelsAndErrorTypeDistribution(errorLabels, errorsCount, mapsmeErrorCounter, anotherErrorCounter);
|
||||
|
||||
auto const pythonScriptPath = base::JoinPath(FLAGS_save_results, kPythonBarDistributionError);
|
||||
CreatePythonBarByMap(pythonScriptPath, errorLabels, errorsCount,
|
||||
{"mapsme", IsMapsmeVsMapsme() ? "old mapsme" : "api"} /* legends */,
|
||||
"Type of errors" /* xlabel */, "Number of errors" /* ylabel */);
|
||||
}
|
||||
|
||||
void CheckArgs()
|
||||
{
|
||||
bool const modeIsChosen =
|
||||
IsMapsmeVsApi() || IsMapsmeVsMapsme() || IsMapsmeBenchmarkStat() || IsMapsmeVsMapsmeBenchmarkStat();
|
||||
if (!modeIsChosen)
|
||||
PrintHelpAndExit();
|
||||
|
||||
CHECK(!FLAGS_save_results.empty(),
|
||||
("\n\n\t--save_results is required. Tool will save results there.", "\n\nType --help for usage."));
|
||||
}
|
||||
|
||||
int Main(int argc, char ** argv)
|
||||
{
|
||||
if (HasHelpFlags(argc, argv))
|
||||
PrintHelpAndExit();
|
||||
|
||||
gflags::ParseCommandLineNonHelpFlags(&argc, &argv, true);
|
||||
|
||||
CheckArgs();
|
||||
|
||||
if (Platform::IsFileExistsByFullPath(FLAGS_save_results))
|
||||
CheckDirExistence(FLAGS_save_results);
|
||||
else
|
||||
CHECK_EQUAL(Platform::MkDir(FLAGS_save_results), Platform::EError::ERR_OK, ());
|
||||
|
||||
CheckDirExistence(FLAGS_mapsme_results);
|
||||
|
||||
CHECK(0.0 <= FLAGS_kml_percent && FLAGS_kml_percent <= 100.0, ("--kml_percent should be in interval: [0.0, 100.0]."));
|
||||
|
||||
LOG(LINFO, ("Start loading mapsme results."));
|
||||
auto mapsmeResults = LoadResults<RoutesBuilder::Result>(FLAGS_mapsme_results);
|
||||
LOG(LINFO, ("Receive:", mapsmeResults.size(), "routes from --mapsme_results."));
|
||||
|
||||
if (IsMapsmeVsApi())
|
||||
{
|
||||
LOG(LINFO, ("Start loading api results."));
|
||||
auto apiResults = LoadResults<api::Response>(FLAGS_api_results);
|
||||
LOG(LINFO, ("Receive:", apiResults.size(), "routes from --api_results."));
|
||||
RunComparison(std::move(mapsmeResults), std::move(apiResults));
|
||||
}
|
||||
else if (IsMapsmeVsMapsmeBenchmarkStat())
|
||||
{
|
||||
LOG(LINFO, ("Benchmark different mapsme versions. Start loading old mapsme results."));
|
||||
auto oldMapsmeResults = LoadResults<RoutesBuilder::Result>(FLAGS_mapsme_old_results);
|
||||
LOG(LINFO, ("Receive:", oldMapsmeResults.size(), "routes from --mapsme_old_results."));
|
||||
RunBenchmarkComparison(std::move(mapsmeResults), std::move(oldMapsmeResults), FLAGS_save_results);
|
||||
}
|
||||
else if (IsMapsmeVsMapsme())
|
||||
{
|
||||
LOG(LINFO, ("Start loading another mapsme results."));
|
||||
auto oldMapsmeResults = LoadResults<RoutesBuilder::Result>(FLAGS_mapsme_old_results);
|
||||
LOG(LINFO, ("Receive:", oldMapsmeResults.size(), "routes from --mapsme_old_results."));
|
||||
RunComparison(std::move(mapsmeResults), std::move(oldMapsmeResults));
|
||||
}
|
||||
else if (IsMapsmeBenchmarkStat())
|
||||
{
|
||||
LOG(LINFO, ("Running in benchmark stat mode."));
|
||||
RunBenchmarkStat(mapsmeResults, FLAGS_save_results);
|
||||
}
|
||||
else
|
||||
{
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char ** argv)
|
||||
{
|
||||
try
|
||||
{
|
||||
return Main(argc, argv);
|
||||
}
|
||||
catch (RootException const & e)
|
||||
{
|
||||
LOG(LCRITICAL, ("Core exception:", e.Msg()));
|
||||
}
|
||||
catch (std::exception const & e)
|
||||
{
|
||||
LOG(LCRITICAL, ("Std exception:", e.what()));
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
LOG(LCRITICAL, ("Unknown exception."));
|
||||
}
|
||||
|
||||
LOG(LINFO, ("Done."));
|
||||
|
||||
return 0;
|
||||
}
|
||||
514
libs/routing/routing_quality/routing_quality_tool/utils.cpp
Normal file
514
libs/routing/routing_quality/routing_quality_tool/utils.cpp
Normal file
|
|
@ -0,0 +1,514 @@
|
|||
#include "routing/routing_quality/routing_quality_tool/utils.hpp"
|
||||
|
||||
#include "kml/serdes.hpp"
|
||||
#include "kml/types.hpp"
|
||||
|
||||
#include "geometry/point_with_altitude.hpp"
|
||||
|
||||
#include "base/assert.hpp"
|
||||
#include "base/file_name_utils.hpp"
|
||||
#include "base/logging.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <iomanip>
|
||||
#include <utility>
|
||||
|
||||
using namespace routing::routes_builder;
|
||||
|
||||
namespace
|
||||
{
|
||||
using namespace routing_quality;
|
||||
|
||||
void PrintWithSpaces(std::string const & str, size_t maxN)
|
||||
{
|
||||
std::cout << str;
|
||||
if (maxN <= str.size())
|
||||
return;
|
||||
|
||||
maxN -= str.size();
|
||||
for (size_t i = 0; i < maxN; ++i)
|
||||
std::cout << " ";
|
||||
}
|
||||
|
||||
std::vector<geometry::PointWithAltitude> ConvertToPointsWithAltitudes(std::vector<ms::LatLon> const & latlons)
|
||||
{
|
||||
std::vector<geometry::PointWithAltitude> result;
|
||||
result.reserve(latlons.size());
|
||||
for (size_t i = 0; i < latlons.size(); ++i)
|
||||
result.emplace_back(mercator::FromLatLon(latlons[i]), geometry::kDefaultAltitudeMeters);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string MakeStringFromPercent(double percent)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << std::setprecision(2);
|
||||
ss << percent;
|
||||
std::string percentStr;
|
||||
ss >> percentStr;
|
||||
percentStr += "%";
|
||||
|
||||
return percentStr;
|
||||
}
|
||||
|
||||
kml::BookmarkData CreateBookmark(m2::PointD const & point, bool isStart)
|
||||
{
|
||||
kml::BookmarkData bookmarkData;
|
||||
bookmarkData.m_color = {isStart ? kml::PredefinedColor::Red : kml::PredefinedColor::Blue, 0};
|
||||
bookmarkData.m_point = point;
|
||||
|
||||
return bookmarkData;
|
||||
}
|
||||
|
||||
template <typename AnotherResult>
|
||||
void SaveKmlFileDataTo(RoutesBuilder::Result const & mapsmeResult, AnotherResult const & apiResult,
|
||||
std::string const & kmlFile)
|
||||
{
|
||||
static std::array<uint32_t, 5> const kColors = {
|
||||
0xff0000ff, // Red
|
||||
0x0000ffff, // Blue
|
||||
0x00ff00ff, // Green
|
||||
0xffa500ff, // Orange
|
||||
0xa52a2aff // Brown
|
||||
};
|
||||
|
||||
kml::FileData kml;
|
||||
|
||||
size_t colorNumber = 0;
|
||||
auto const addTrack = [&](kml::MultiGeometry::LineT && line)
|
||||
{
|
||||
kml::TrackData track;
|
||||
track.m_geometry.m_lines.push_back(std::move(line));
|
||||
|
||||
CHECK_LESS(colorNumber, kColors.size(), ());
|
||||
track.m_layers = {{5.0 /* lineWidth */, {kml::PredefinedColor::None, kColors[colorNumber++]}}};
|
||||
kml.m_tracksData.emplace_back(std::move(track));
|
||||
};
|
||||
|
||||
auto const & start = apiResult.GetStartPoint();
|
||||
auto const & finish = apiResult.GetFinishPoint();
|
||||
|
||||
kml.m_bookmarksData.emplace_back(CreateBookmark(start, true /* isStart */));
|
||||
kml.m_bookmarksData.emplace_back(CreateBookmark(finish, false /* isStart */));
|
||||
|
||||
auto const & resultPoints = mapsmeResult.GetRoutes().back().m_followedPolyline.GetPolyline().GetPoints();
|
||||
|
||||
kml::MultiGeometry::LineT mmTrack;
|
||||
mmTrack.reserve(resultPoints.size());
|
||||
for (auto const & pt : resultPoints)
|
||||
mmTrack.emplace_back(pt, geometry::kDefaultAltitudeMeters);
|
||||
|
||||
addTrack(std::move(mmTrack));
|
||||
|
||||
for (auto const & route : apiResult.GetRoutes())
|
||||
addTrack(ConvertToPointsWithAltitudes(route.GetWaypoints()));
|
||||
|
||||
kml::SerializerKml ser(kml);
|
||||
FileWriter sink(kmlFile);
|
||||
ser.Serialize(sink);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::string CreatePythonArray(std::vector<T> const & data, bool isString = false)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "[";
|
||||
for (auto const & item : data)
|
||||
{
|
||||
if (isString)
|
||||
ss << "\"";
|
||||
ss << item;
|
||||
if (isString)
|
||||
ss << "\"";
|
||||
ss << ",";
|
||||
}
|
||||
|
||||
auto result = ss.str();
|
||||
if (data.empty())
|
||||
result += "]";
|
||||
else
|
||||
result.back() = ']';
|
||||
|
||||
return result;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace routing_quality
|
||||
{
|
||||
namespace routing_quality_tool
|
||||
{
|
||||
Result::Result(std::string mapsmeDumpPath, std::string anotherDumpPath, ms::LatLon const & start,
|
||||
ms::LatLon const & finish, double similarity, double etaDiffPercent)
|
||||
: m_mapsmeDumpPath(std::move(mapsmeDumpPath))
|
||||
, m_anotherDumpPath(std::move(anotherDumpPath))
|
||||
, m_start(start)
|
||||
, m_finish(finish)
|
||||
, m_similarity(similarity)
|
||||
, m_etaDiffPercent(etaDiffPercent)
|
||||
{}
|
||||
|
||||
bool Result::operator<(Result const & rhs) const
|
||||
{
|
||||
return m_similarity < rhs.m_similarity;
|
||||
}
|
||||
|
||||
RoutesSaver::RoutesSaver(std::string targetDir, ComparisonType comparisonType)
|
||||
: m_targetDir(std::move(targetDir))
|
||||
, m_comparisonType(comparisonType)
|
||||
{
|
||||
OpenOutputStream();
|
||||
}
|
||||
|
||||
void RoutesSaver::PushRoute(Result const & result)
|
||||
{
|
||||
WriteStartAndFinish(m_output, result.m_start, result.m_finish);
|
||||
m_output << result.m_mapsmeDumpPath << " " << result.m_anotherDumpPath << std::endl;
|
||||
}
|
||||
|
||||
void RoutesSaver::PushError(routing::RouterResultCode code, ms::LatLon const & start, ms::LatLon const & finish)
|
||||
{
|
||||
CHECK_NOT_EQUAL(code, routing::RouterResultCode::NoError, ("Only errors codes."));
|
||||
|
||||
auto & ofstream = m_errorRoutes[code];
|
||||
if (!ofstream.is_open())
|
||||
{
|
||||
std::string const fullpath = base::JoinPath(m_targetDir, DebugPrint(code) + ".routes");
|
||||
|
||||
ofstream.open(fullpath);
|
||||
CHECK(ofstream.good(), ("Can not open:", fullpath, "for writing."));
|
||||
|
||||
ofstream << std::setprecision(20);
|
||||
LOG(LINFO, ("Save routes with error:", code, "to:", fullpath));
|
||||
}
|
||||
|
||||
WriteStartAndFinish(ofstream, start, finish);
|
||||
}
|
||||
|
||||
void RoutesSaver::PushRivalError(ms::LatLon const & start, ms::LatLon const & finish)
|
||||
{
|
||||
static std::string const kApiErrorsFile = "api_errors.routes";
|
||||
if (!m_apiErrors.is_open())
|
||||
{
|
||||
std::string const & fullpath = base::JoinPath(m_targetDir, kApiErrorsFile);
|
||||
m_apiErrors.open(fullpath);
|
||||
CHECK(m_apiErrors.good(), ("Can not open:", fullpath, "for writing."));
|
||||
m_apiErrors << std::setprecision(20);
|
||||
LOG(LINFO, ("Routes that api can not build, but mapsme do, placed:", fullpath));
|
||||
}
|
||||
|
||||
WriteStartAndFinish(m_apiErrors, start, finish);
|
||||
}
|
||||
|
||||
void RoutesSaver::TurnToNextFile()
|
||||
{
|
||||
++m_fileNumber;
|
||||
|
||||
if (m_output.is_open())
|
||||
m_output.close();
|
||||
|
||||
OpenOutputStream();
|
||||
}
|
||||
|
||||
std::string RoutesSaver::GetCurrentPath() const
|
||||
{
|
||||
std::string const fullpath = base::JoinPath(m_targetDir, std::to_string(m_fileNumber) + ".routes");
|
||||
|
||||
return fullpath;
|
||||
}
|
||||
|
||||
std::string const & RoutesSaver::GetTargetDir() const
|
||||
{
|
||||
return m_targetDir;
|
||||
}
|
||||
|
||||
ComparisonType RoutesSaver::GetComparsionType() const
|
||||
{
|
||||
return m_comparisonType;
|
||||
}
|
||||
|
||||
void RoutesSaver::OpenOutputStream()
|
||||
{
|
||||
std::string const & fullpath = GetCurrentPath();
|
||||
m_output.open(fullpath);
|
||||
CHECK(m_output.good(), ("Can not open:", fullpath, "for writing."));
|
||||
m_output << std::setprecision(20);
|
||||
}
|
||||
|
||||
void RoutesSaver::WriteStartAndFinish(std::ofstream & output, ms::LatLon const & start, ms::LatLon const & finish)
|
||||
{
|
||||
output << start.m_lat << " " << start.m_lon << " " << finish.m_lat << " " << finish.m_lon << std::endl;
|
||||
}
|
||||
|
||||
// This function creates python script that shows the distribution error.
|
||||
void CreatePythonScriptForDistribution(std::string const & pythonScriptPath, std::string const & title,
|
||||
std::vector<double> const & values)
|
||||
{
|
||||
std::ofstream python(pythonScriptPath);
|
||||
CHECK(python.good(), ("Can not open:", pythonScriptPath, "for writing."));
|
||||
|
||||
std::string pythonArray = "[";
|
||||
for (auto const & value : values)
|
||||
pythonArray += std::to_string(value) + ",";
|
||||
|
||||
if (values.empty())
|
||||
pythonArray += "]";
|
||||
else
|
||||
pythonArray.back() = ']';
|
||||
|
||||
python << R"(
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
a = np.hstack()" +
|
||||
pythonArray + R"()
|
||||
plt.hist(a, bins='auto') # arguments are passed to np.histogram
|
||||
plt.title(")" + title +
|
||||
R"(")
|
||||
plt.show()
|
||||
)";
|
||||
|
||||
LOG(LINFO, ("Run: python", pythonScriptPath, "to look at:", title));
|
||||
}
|
||||
|
||||
void CreatePythonGraphByPointsXY(std::string const & pythonScriptPath, std::string const & xlabel,
|
||||
std::string const & ylabel, std::vector<std::vector<m2::PointD>> const & graphics,
|
||||
std::vector<std::string> const & legends)
|
||||
{
|
||||
CHECK_EQUAL(legends.size(), graphics.size(), ());
|
||||
std::ofstream python(pythonScriptPath);
|
||||
CHECK(python.good(), ("Can not open:", pythonScriptPath, "for writing."));
|
||||
|
||||
std::string pythonArrayX = "[";
|
||||
std::string pythonArrayY = "[";
|
||||
std::string legendsArray = "[";
|
||||
for (size_t i = 0; i < graphics.size(); ++i)
|
||||
{
|
||||
auto const & points = graphics[i];
|
||||
pythonArrayX += "[";
|
||||
pythonArrayY += "[";
|
||||
legendsArray += "\"" + legends[i] + "\",";
|
||||
for (auto const & point : points)
|
||||
{
|
||||
pythonArrayX += std::to_string(point.x) + ",";
|
||||
pythonArrayY += std::to_string(point.y) + ",";
|
||||
}
|
||||
|
||||
pythonArrayX += "],";
|
||||
pythonArrayY += "],";
|
||||
}
|
||||
|
||||
pythonArrayX.back() = ']';
|
||||
pythonArrayY.back() = ']';
|
||||
legendsArray.back() = ']';
|
||||
|
||||
python << R"(
|
||||
import pylab
|
||||
|
||||
legends = )" + legendsArray +
|
||||
R"(
|
||||
xlist = )" + pythonArrayX +
|
||||
R"(
|
||||
ylist = )" + pythonArrayY +
|
||||
R"(
|
||||
for (x, y, l) in zip(xlist, ylist, legends):
|
||||
pylab.plot(x, y, label=l)
|
||||
|
||||
pylab.xlabel(")" +
|
||||
xlabel + R"(")
|
||||
pylab.ylabel(")" +
|
||||
ylabel + R"(")
|
||||
pylab.legend()
|
||||
pylab.tight_layout()
|
||||
pylab.show()
|
||||
)";
|
||||
|
||||
LOG(LINFO, ("Run: python", pythonScriptPath, "to look at:", ylabel, "versus", xlabel));
|
||||
}
|
||||
|
||||
void CreatePythonBarByMap(std::string const & pythonScriptPath, std::vector<std::string> const & barLabels,
|
||||
std::vector<std::vector<double>> const & barHeights, std::vector<std::string> const & legends,
|
||||
std::string const & xlabel, std::string const & ylabel, bool drawPercents)
|
||||
{
|
||||
std::ofstream python(pythonScriptPath);
|
||||
CHECK(python.good(), ("Can not open:", pythonScriptPath, "for writing."));
|
||||
|
||||
std::string labelsArray = CreatePythonArray(barLabels, true /* isString */);
|
||||
std::string legendsArray = CreatePythonArray(legends, true /* isString */);
|
||||
std::string counts = "[";
|
||||
for (auto const & heights : barHeights)
|
||||
counts += CreatePythonArray(heights) + ",";
|
||||
|
||||
if (barHeights.empty())
|
||||
counts += "]";
|
||||
else
|
||||
counts.back() = ']';
|
||||
|
||||
std::string const formatString =
|
||||
drawPercents ? "f'{round(height, 2)}({round(height / summ * 100, 2)}%)'" : "f'{round(height, 2)}'";
|
||||
|
||||
python << R"(
|
||||
import matplotlib
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
|
||||
bar_width = 0.35
|
||||
labels = )" + labelsArray +
|
||||
R"(
|
||||
legends = )" + legendsArray +
|
||||
R"(
|
||||
counts = )" + counts +
|
||||
R"(
|
||||
|
||||
x = np.arange(len(labels)) # the label locations
|
||||
width = 0.35 # the width of the bars
|
||||
|
||||
fig, ax = plt.subplots()
|
||||
bars = []
|
||||
for i in range(len(counts)):
|
||||
bar = ax.bar(x + i * bar_width, counts[i], bar_width, label=legends[i])
|
||||
bars.append(bar)
|
||||
|
||||
ax.set_ylabel(')" +
|
||||
ylabel + R"(')
|
||||
ax.set_title(')" +
|
||||
xlabel + R"(')
|
||||
pos = (bar_width * (len(counts) - 1)) / 2
|
||||
ax.set_xticks(x + pos)
|
||||
ax.set_xticklabels(labels)
|
||||
ax.legend()
|
||||
|
||||
def autolabel(rects, counts_ith):
|
||||
summ = 0
|
||||
for count in counts_ith:
|
||||
summ += count
|
||||
|
||||
for rect in rects:
|
||||
height = rect.get_height()
|
||||
ax.annotate()" +
|
||||
formatString + R"(,
|
||||
xy=(rect.get_x() + rect.get_width() / 2, height),
|
||||
xytext=(0, 3), # 3 points vertical offset
|
||||
textcoords="offset points",
|
||||
ha='center', va='bottom')
|
||||
|
||||
for i in range(len(counts)):
|
||||
autolabel(bars[i], counts[i])
|
||||
fig.tight_layout()
|
||||
plt.show()
|
||||
)";
|
||||
|
||||
LOG(LINFO, ("Run: python", pythonScriptPath, "to look at bar:", ylabel, "versus", xlabel));
|
||||
}
|
||||
|
||||
/// \brief |SimilarityCounter| groups routes that we compare by similarity, here we tune these groups.
|
||||
// static
|
||||
std::vector<SimilarityCounter::Interval> const SimilarityCounter::kIntervals = {
|
||||
{"[0.0, 0.0]", 0, 0 + 1e-5}, {"[0.0, 0.1)", 0, 0.1}, {"[0.1, 0.2)", 0.1, 0.2}, {"[0.2, 0.3)", 0.2, 0.3},
|
||||
{"[0.3, 0.6)", 0.3, 0.6}, {"[0.6, 0.8)", 0.6, 0.8}, {"[0.8, 1.0)", 0.8, 1.0}, {"[1.0, 1.0]", 1.0, 1.0 + 1e-5},
|
||||
};
|
||||
|
||||
SimilarityCounter::SimilarityCounter(RoutesSaver & routesSaver) : m_routesSaver(routesSaver)
|
||||
{
|
||||
for (auto const & interval : kIntervals)
|
||||
m_routesCounter.emplace_back(interval.m_name, 0);
|
||||
}
|
||||
|
||||
SimilarityCounter::~SimilarityCounter()
|
||||
{
|
||||
size_t maxNumberLength = 0;
|
||||
size_t maxKeyLength = 0;
|
||||
size_t sum = 0;
|
||||
for (auto const & item : m_routesCounter)
|
||||
{
|
||||
sum += item.m_routesNumber;
|
||||
maxNumberLength = std::max(maxNumberLength, std::to_string(item.m_routesNumber).size());
|
||||
maxKeyLength = std::max(maxKeyLength, item.m_intervalName.size());
|
||||
}
|
||||
|
||||
for (auto const & item : m_routesCounter)
|
||||
{
|
||||
auto const percent = static_cast<double>(item.m_routesNumber) / sum * 100.0;
|
||||
auto const percentStr = MakeStringFromPercent(percent);
|
||||
|
||||
PrintWithSpaces(item.m_intervalName, maxKeyLength + 1);
|
||||
std::cout << " => ";
|
||||
PrintWithSpaces(std::to_string(item.m_routesNumber), maxNumberLength + 1);
|
||||
std::cout << "( " << percentStr << " )" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void SimilarityCounter::Push(Result const & result)
|
||||
{
|
||||
auto left = kIntervals[m_currentInterval].m_left;
|
||||
auto right = kIntervals[m_currentInterval].m_right;
|
||||
while (!(left <= result.m_similarity && result.m_similarity < right))
|
||||
{
|
||||
++m_currentInterval;
|
||||
m_routesSaver.TurnToNextFile();
|
||||
CHECK_LESS(m_currentInterval, m_routesCounter.size(), ());
|
||||
left = kIntervals[m_currentInterval].m_left;
|
||||
right = kIntervals[m_currentInterval].m_right;
|
||||
}
|
||||
|
||||
CHECK_LESS(m_currentInterval, m_routesCounter.size(), ());
|
||||
if (m_routesCounter[m_currentInterval].m_routesNumber == 0)
|
||||
{
|
||||
LOG(LINFO, ("Save routes with:", m_routesCounter[m_currentInterval].m_intervalName,
|
||||
"similarity to:", m_routesSaver.GetCurrentPath()));
|
||||
}
|
||||
|
||||
CHECK_LESS(m_currentInterval, m_routesCounter.size(), ());
|
||||
++m_routesCounter[m_currentInterval].m_routesNumber;
|
||||
m_routesSaver.PushRoute(result);
|
||||
}
|
||||
|
||||
void SimilarityCounter::CreateKmlFiles(double percent, std::vector<Result> const & results)
|
||||
{
|
||||
size_t realResultIndex = 0;
|
||||
for (size_t intervalId = 0; intervalId < m_routesCounter.size(); ++intervalId)
|
||||
{
|
||||
std::string savePath = base::JoinPath(m_routesSaver.GetTargetDir(), std::to_string(intervalId));
|
||||
|
||||
auto const currentSize = m_routesCounter[intervalId].m_routesNumber;
|
||||
auto const resultSize = static_cast<size_t>(currentSize * percent / 100.0);
|
||||
if (resultSize == 0)
|
||||
continue;
|
||||
|
||||
auto const mkdirRes = Platform::MkDir(savePath);
|
||||
CHECK(mkdirRes == Platform::EError::ERR_OK || mkdirRes == Platform::EError::ERR_FILE_ALREADY_EXISTS,
|
||||
("Cannot create dir:", savePath));
|
||||
|
||||
LOG(LINFO, ("Saving", resultSize, "kmls for:", m_routesCounter[intervalId].m_intervalName, "to:", savePath));
|
||||
|
||||
auto const step = static_cast<size_t>(currentSize / resultSize);
|
||||
|
||||
for (size_t i = 0; i < currentSize; ++i)
|
||||
{
|
||||
if (i % step != 0)
|
||||
{
|
||||
++realResultIndex;
|
||||
continue;
|
||||
}
|
||||
|
||||
CHECK_LESS(realResultIndex, results.size(), ());
|
||||
auto const mapsmeResult = RoutesBuilder::Result::Load(results[realResultIndex].m_mapsmeDumpPath);
|
||||
|
||||
std::string const kmlFile = base::JoinPath(savePath, std::to_string(i) + ".kml");
|
||||
if (m_routesSaver.GetComparsionType() == ComparisonType::MapsmeVsApi)
|
||||
{
|
||||
auto const apiResult = api::Response::Load(results[realResultIndex].m_anotherDumpPath);
|
||||
SaveKmlFileDataTo(mapsmeResult, apiResult, kmlFile);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto const mapsmeAnotherResult = RoutesBuilder::Result::Load(results[realResultIndex].m_anotherDumpPath);
|
||||
SaveKmlFileDataTo(mapsmeResult, mapsmeAnotherResult, kmlFile);
|
||||
}
|
||||
|
||||
++realResultIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace routing_quality_tool
|
||||
} // namespace routing_quality
|
||||
184
libs/routing/routing_quality/routing_quality_tool/utils.hpp
Normal file
184
libs/routing/routing_quality/routing_quality_tool/utils.hpp
Normal file
|
|
@ -0,0 +1,184 @@
|
|||
#pragma once
|
||||
|
||||
#include "routing/routes_builder/routes_builder.hpp"
|
||||
#include "routing/routing_quality/api/api.hpp"
|
||||
|
||||
#include "routing/routing_callbacks.hpp"
|
||||
|
||||
#include "platform/platform.hpp"
|
||||
|
||||
#include "geometry/latlon.hpp"
|
||||
|
||||
#include "base/file_name_utils.hpp"
|
||||
#include "base/logging.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <fstream>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace routing_quality
|
||||
{
|
||||
namespace routing_quality_tool
|
||||
{
|
||||
enum class ComparisonType
|
||||
{
|
||||
MapsmeVsMapsme,
|
||||
MapsmeVsApi
|
||||
};
|
||||
|
||||
template <typename Result>
|
||||
std::vector<std::pair<Result, std::string>> LoadResults(std::string const & dir)
|
||||
{
|
||||
std::vector<std::pair<Result, std::string>> result;
|
||||
|
||||
std::vector<std::string> files;
|
||||
Platform::GetFilesByExt(dir, Result::kDumpExtension, files);
|
||||
std::sort(files.begin(), files.end());
|
||||
|
||||
double lastPercent = 0.0;
|
||||
double count = 0;
|
||||
|
||||
for (auto const & file : files)
|
||||
{
|
||||
double const curPercent = (count + 1) / files.size() * 100.0;
|
||||
if (curPercent - lastPercent > 5.0 || count + 1 == files.size())
|
||||
{
|
||||
lastPercent = curPercent;
|
||||
LOG(LINFO, ("Progress:", lastPercent, "%"));
|
||||
}
|
||||
|
||||
auto const fullPath = base::JoinPath(dir, file);
|
||||
result.emplace_back(Result::Load(fullPath), fullPath);
|
||||
++count;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename AnotherResult>
|
||||
bool AreRoutesWithSameEnds(routing::routes_builder::RoutesBuilder::Result const & mapsmeResult,
|
||||
AnotherResult const & anotherResult)
|
||||
{
|
||||
CHECK_EQUAL(mapsmeResult.m_params.m_checkpoints.GetPoints().size(), 2, ());
|
||||
CHECK_EQUAL(mapsmeResult.GetVehicleType(), anotherResult.GetVehicleType(), ());
|
||||
|
||||
auto const & start = mapsmeResult.GetStartPoint();
|
||||
auto const & finish = mapsmeResult.GetFinishPoint();
|
||||
|
||||
auto const & anotherStart = anotherResult.GetStartPoint();
|
||||
auto const & anotherFinish = anotherResult.GetFinishPoint();
|
||||
|
||||
double constexpr kEps = 1e-10;
|
||||
return AlmostEqualAbs(start, anotherStart, kEps) && AlmostEqualAbs(finish, anotherFinish, kEps);
|
||||
}
|
||||
|
||||
template <typename AnotherResult>
|
||||
bool FindAnotherResponse(routing::routes_builder::RoutesBuilder::Result const & mapsmeResult,
|
||||
std::vector<std::pair<AnotherResult, std::string>> const & apiResults,
|
||||
std::pair<AnotherResult, std::string> & anotherResult)
|
||||
{
|
||||
for (auto const & result : apiResults)
|
||||
{
|
||||
if (!AreRoutesWithSameEnds(mapsmeResult, result.first))
|
||||
continue;
|
||||
|
||||
anotherResult = result;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
struct Result
|
||||
{
|
||||
Result(std::string mapsmeDumpPath, std::string anotherDumpPath, ms::LatLon const & start, ms::LatLon const & finish,
|
||||
double similarity, double etaDiffPercent);
|
||||
|
||||
bool operator<(Result const & rhs) const;
|
||||
|
||||
std::string m_mapsmeDumpPath;
|
||||
std::string m_anotherDumpPath;
|
||||
|
||||
ms::LatLon m_start;
|
||||
ms::LatLon m_finish;
|
||||
|
||||
// value in range: [0, 1]
|
||||
double m_similarity;
|
||||
double m_etaDiffPercent;
|
||||
};
|
||||
|
||||
class RoutesSaver
|
||||
{
|
||||
public:
|
||||
RoutesSaver(std::string targetDir, ComparisonType comparisonType);
|
||||
|
||||
void PushRoute(Result const & result);
|
||||
void PushError(routing::RouterResultCode code, ms::LatLon const & start, ms::LatLon const & finish);
|
||||
void PushRivalError(ms::LatLon const & start, ms::LatLon const & finish);
|
||||
void TurnToNextFile();
|
||||
std::string GetCurrentPath() const;
|
||||
std::string const & GetTargetDir() const;
|
||||
ComparisonType GetComparsionType() const;
|
||||
|
||||
private:
|
||||
void OpenOutputStream();
|
||||
void WriteStartAndFinish(std::ofstream & output, ms::LatLon const & start, ms::LatLon const & finish);
|
||||
|
||||
std::ofstream m_apiErrors;
|
||||
std::string m_targetDir;
|
||||
uint32_t m_fileNumber = 0;
|
||||
std::ofstream m_output;
|
||||
std::map<routing::RouterResultCode, std::ofstream> m_errorRoutes;
|
||||
ComparisonType m_comparisonType;
|
||||
};
|
||||
|
||||
class SimilarityCounter
|
||||
{
|
||||
public:
|
||||
struct Interval
|
||||
{
|
||||
std::string m_name;
|
||||
// [m_left, m_right)
|
||||
double m_left;
|
||||
double m_right;
|
||||
};
|
||||
|
||||
struct Item
|
||||
{
|
||||
Item(std::string name, uint32_t n) : m_intervalName(std::move(name)), m_routesNumber(n) {}
|
||||
|
||||
std::string m_intervalName;
|
||||
uint32_t m_routesNumber = 0;
|
||||
};
|
||||
|
||||
static std::vector<Interval> const kIntervals;
|
||||
|
||||
explicit SimilarityCounter(RoutesSaver & routesSaver);
|
||||
~SimilarityCounter();
|
||||
|
||||
void Push(Result const & result);
|
||||
void CreateKmlFiles(double percent, std::vector<Result> const & results);
|
||||
|
||||
private:
|
||||
RoutesSaver & m_routesSaver;
|
||||
size_t m_currentInterval = 0;
|
||||
std::vector<Item> m_routesCounter;
|
||||
};
|
||||
|
||||
void CreatePythonScriptForDistribution(std::string const & pythonScriptPath, std::string const & title,
|
||||
std::vector<double> const & values);
|
||||
|
||||
void CreatePythonGraphByPointsXY(std::string const & pythonScriptPath, std::string const & xlabel,
|
||||
std::string const & ylabel, std::vector<std::vector<m2::PointD>> const & graphics,
|
||||
std::vector<std::string> const & legends);
|
||||
|
||||
/// \brief Create python file, that show bar graph, where labels of bars are keys of |stat| and
|
||||
/// heights area values of |stat|.
|
||||
void CreatePythonBarByMap(std::string const & pythonScriptPath, std::vector<std::string> const & barLabels,
|
||||
std::vector<std::vector<double>> const & barHeights, std::vector<std::string> const & legends,
|
||||
std::string const & xlabel, std::string const & ylabel, bool drawPercents = true);
|
||||
} // namespace routing_quality_tool
|
||||
} // namespace routing_quality
|
||||
79
libs/routing/routing_quality/waypoints.cpp
Normal file
79
libs/routing/routing_quality/waypoints.cpp
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
#include "routing/routing_quality/waypoints.hpp"
|
||||
|
||||
#include "routing/base/followed_polyline.hpp"
|
||||
|
||||
#include "geometry/mercator.hpp"
|
||||
#include "geometry/point2d.hpp"
|
||||
|
||||
#include "base/assert.hpp"
|
||||
#include "base/logging.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
|
||||
namespace routing_quality
|
||||
{
|
||||
namespace metrics
|
||||
{
|
||||
Similarity CompareByNumberOfMatchedWaypoints(routing::FollowedPolyline polyline, Waypoints const & waypoints)
|
||||
{
|
||||
double constexpr kMaxDistanceFromRouteM = 15.0;
|
||||
|
||||
auto const size = waypoints.size();
|
||||
CHECK_GREATER(size, 0, ());
|
||||
size_t numberOfErrors = 0;
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
{
|
||||
auto const & ll = waypoints[i];
|
||||
m2::RectD const rect = mercator::MetersToXY(ll.m_lon, ll.m_lat, kMaxDistanceFromRouteM /* metresR */);
|
||||
auto const iter = polyline.UpdateProjection(rect);
|
||||
if (iter.IsValid())
|
||||
{
|
||||
auto const distFromRouteM = mercator::DistanceOnEarth(iter.m_pt, mercator::FromLatLon(ll));
|
||||
if (distFromRouteM <= kMaxDistanceFromRouteM)
|
||||
continue;
|
||||
}
|
||||
|
||||
++numberOfErrors;
|
||||
}
|
||||
|
||||
CHECK_LESS_OR_EQUAL(numberOfErrors, size, ());
|
||||
auto const result = ((size - numberOfErrors) / static_cast<double>(size));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Similarity CompareByNumberOfMatchedWaypoints(routing::FollowedPolyline const & polyline, ReferenceRoutes && candidates)
|
||||
{
|
||||
Similarity bestResult = 0.0;
|
||||
for (size_t j = 0; j < candidates.size(); ++j)
|
||||
{
|
||||
auto const result = CompareByNumberOfMatchedWaypoints(polyline, candidates[j]);
|
||||
bestResult = std::max(bestResult, result);
|
||||
}
|
||||
|
||||
LOG(LDEBUG, ("Best result:", bestResult));
|
||||
return bestResult;
|
||||
}
|
||||
} // namespace metrics
|
||||
|
||||
Similarity CheckWaypoints(Params const & params, ReferenceRoutes && referenceRoutes)
|
||||
{
|
||||
auto & builder = routing::routes_builder::RoutesBuilder::GetSimpleRoutesBuilder();
|
||||
auto result = builder.ProcessTask(params);
|
||||
|
||||
return metrics::CompareByNumberOfMatchedWaypoints(result.GetRoutes().back().m_followedPolyline,
|
||||
std::move(referenceRoutes));
|
||||
}
|
||||
|
||||
bool CheckRoute(Params const & params, ReferenceRoutes && referenceRoutes)
|
||||
{
|
||||
return CheckWaypoints(params, std::move(referenceRoutes)) == 1.0;
|
||||
}
|
||||
|
||||
bool CheckCarRoute(ms::LatLon const & start, ms::LatLon const & finish, ReferenceRoutes && referenceTracks)
|
||||
{
|
||||
Params const params(routing::VehicleType::Car, start, finish);
|
||||
return CheckRoute(params, std::move(referenceTracks));
|
||||
}
|
||||
} // namespace routing_quality
|
||||
39
libs/routing/routing_quality/waypoints.hpp
Normal file
39
libs/routing/routing_quality/waypoints.hpp
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
#pragma once
|
||||
|
||||
#include "routing/routes_builder/routes_builder.hpp"
|
||||
|
||||
#include "routing/vehicle_mask.hpp"
|
||||
|
||||
#include "routing/base/followed_polyline.hpp"
|
||||
|
||||
#include "geometry/latlon.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace routing_quality
|
||||
{
|
||||
using Params = routing::routes_builder::RoutesBuilder::Params;
|
||||
|
||||
using Waypoints = std::vector<ms::LatLon>;
|
||||
|
||||
/// \brief There can be more than one reference route.
|
||||
using ReferenceRoutes = std::vector<Waypoints>;
|
||||
|
||||
// Value in range: [0, 1]
|
||||
using Similarity = double;
|
||||
|
||||
namespace metrics
|
||||
{
|
||||
Similarity CompareByNumberOfMatchedWaypoints(routing::FollowedPolyline polyline, Waypoints const & waypoints);
|
||||
Similarity CompareByNumberOfMatchedWaypoints(routing::FollowedPolyline const & polyline, ReferenceRoutes && candidates);
|
||||
} // namespace metrics
|
||||
|
||||
/// \brief Checks how many reference waypoints the route contains.
|
||||
/// \returns normalized value in range [0.0; 1.0].
|
||||
Similarity CheckWaypoints(Params const & params, ReferenceRoutes && referenceRoutes);
|
||||
|
||||
/// \returns true if route from |start| to |finish| fully conforms one of |candidates|
|
||||
/// and false otherwise.
|
||||
bool CheckRoute(Params const & params, ReferenceRoutes && referenceRoutes);
|
||||
bool CheckCarRoute(ms::LatLon const & start, ms::LatLon const & finish, ReferenceRoutes && referenceRoutes);
|
||||
} // namespace routing_quality
|
||||
Loading…
Add table
Add a link
Reference in a new issue