Repo created
This commit is contained in:
parent
4af19165ec
commit
68073add76
12458 changed files with 12350765 additions and 2 deletions
19
libs/routing/routes_builder/CMakeLists.txt
Normal file
19
libs/routing/routes_builder/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
project(routes_builder)
|
||||
|
||||
set(SRC
|
||||
data_source_storage.cpp
|
||||
data_source_storage.hpp
|
||||
routes_builder.cpp
|
||||
routes_builder.hpp
|
||||
)
|
||||
|
||||
omim_add_library(${PROJECT_NAME} ${SRC})
|
||||
|
||||
target_link_libraries(${PROJECT_NAME}
|
||||
routing
|
||||
indexer
|
||||
platform
|
||||
storage
|
||||
)
|
||||
|
||||
omim_add_tool_subdirectory(routes_builder_tool)
|
||||
26
libs/routing/routes_builder/data_source_storage.cpp
Normal file
26
libs/routing/routes_builder/data_source_storage.cpp
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
#include "routing/routes_builder/data_source_storage.hpp"
|
||||
|
||||
#include "base/assert.hpp"
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace routing
|
||||
{
|
||||
namespace routes_builder
|
||||
{
|
||||
void DataSourceStorage::PushDataSource(std::unique_ptr<FrozenDataSource> && ptr)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
m_freeDataSources.emplace_back(std::move(ptr));
|
||||
}
|
||||
|
||||
std::unique_ptr<FrozenDataSource> DataSourceStorage::GetDataSource()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
CHECK(!m_freeDataSources.empty(), ());
|
||||
std::unique_ptr<FrozenDataSource> front = std::move(m_freeDataSources.front());
|
||||
m_freeDataSources.pop_front();
|
||||
return front;
|
||||
}
|
||||
} // namespace routes_builder
|
||||
} // namespace routing
|
||||
24
libs/routing/routes_builder/data_source_storage.hpp
Normal file
24
libs/routing/routes_builder/data_source_storage.hpp
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
#pragma once
|
||||
|
||||
#include "indexer/data_source.hpp"
|
||||
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
namespace routing
|
||||
{
|
||||
namespace routes_builder
|
||||
{
|
||||
class DataSourceStorage
|
||||
{
|
||||
public:
|
||||
void PushDataSource(std::unique_ptr<FrozenDataSource> && ptr);
|
||||
std::unique_ptr<FrozenDataSource> GetDataSource();
|
||||
|
||||
private:
|
||||
std::mutex m_mutex;
|
||||
std::list<std::unique_ptr<FrozenDataSource>> m_freeDataSources;
|
||||
};
|
||||
} // namespace routes_builder
|
||||
} // namespace routing
|
||||
323
libs/routing/routes_builder/routes_builder.cpp
Normal file
323
libs/routing/routes_builder/routes_builder.cpp
Normal file
|
|
@ -0,0 +1,323 @@
|
|||
#include "routing/routes_builder/routes_builder.hpp"
|
||||
|
||||
#include "routing/vehicle_mask.hpp"
|
||||
|
||||
#include "storage/routing_helpers.hpp"
|
||||
|
||||
#include "indexer/classificator_loader.hpp"
|
||||
|
||||
#include "platform/local_country_file.hpp"
|
||||
#include "platform/local_country_file_utils.hpp"
|
||||
|
||||
#include "coding/write_to_sink.hpp"
|
||||
|
||||
#include "geometry/mercator.hpp"
|
||||
|
||||
#include "base/assert.hpp"
|
||||
#include "base/logging.hpp"
|
||||
#include "base/scope_guard.hpp"
|
||||
|
||||
#include <limits>
|
||||
|
||||
namespace
|
||||
{
|
||||
void DumpPointDVector(std::vector<m2::PointD> const & points, FileWriter & writer)
|
||||
{
|
||||
WriteToSink(writer, points.size());
|
||||
for (auto const & point : points)
|
||||
{
|
||||
writer.Write(&point.x, sizeof(point.x));
|
||||
writer.Write(&point.y, sizeof(point.y));
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<m2::PointD> LoadPointDVector(ReaderSource<FileReader> & src)
|
||||
{
|
||||
std::vector<m2::PointD> points;
|
||||
auto const n = ReadPrimitiveFromSource<size_t>(src);
|
||||
points.reserve(n);
|
||||
for (size_t i = 0; i < n; ++i)
|
||||
{
|
||||
auto const x = ReadPrimitiveFromSource<double>(src);
|
||||
auto const y = ReadPrimitiveFromSource<double>(src);
|
||||
|
||||
points.emplace_back(x, y);
|
||||
}
|
||||
|
||||
return points;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace routing
|
||||
{
|
||||
namespace routes_builder
|
||||
{
|
||||
// RoutesBuilder::Params ---------------------------------------------------------------------------
|
||||
|
||||
RoutesBuilder::Params::Params(VehicleType type, ms::LatLon const & start, ms::LatLon const & finish)
|
||||
: Params(type, {mercator::FromLatLon(start), mercator::FromLatLon(finish)})
|
||||
{}
|
||||
|
||||
RoutesBuilder::Params::Params(VehicleType type, std::vector<m2::PointD> && checkpoints)
|
||||
: m_type(type)
|
||||
, m_checkpoints(std::move(checkpoints))
|
||||
{}
|
||||
|
||||
// static
|
||||
void RoutesBuilder::Params::Dump(Params const & params, FileWriter & writer)
|
||||
{
|
||||
WriteToSink(writer, static_cast<int>(params.m_type));
|
||||
DumpPointDVector(params.m_checkpoints.GetPoints(), writer);
|
||||
}
|
||||
|
||||
// static
|
||||
RoutesBuilder::Params RoutesBuilder::Params::Load(ReaderSource<FileReader> & src)
|
||||
{
|
||||
Params params;
|
||||
|
||||
auto const type = ReadPrimitiveFromSource<int>(src);
|
||||
CHECK_LESS(type, static_cast<int>(VehicleType::Count), ());
|
||||
|
||||
params.m_type = static_cast<VehicleType>(type);
|
||||
params.m_checkpoints = Checkpoints(LoadPointDVector(src));
|
||||
|
||||
return params;
|
||||
}
|
||||
|
||||
// RoutesBuilder -----------------------------------------------------------------------------------
|
||||
|
||||
// static
|
||||
RoutesBuilder & RoutesBuilder::GetSimpleRoutesBuilder()
|
||||
{
|
||||
static RoutesBuilder routesBuilder(1 /* threadsNumber */);
|
||||
return routesBuilder;
|
||||
}
|
||||
RoutesBuilder::RoutesBuilder(size_t threadsNumber) : m_threadPool(threadsNumber)
|
||||
{
|
||||
CHECK_GREATER(threadsNumber, 0, ());
|
||||
LOG(LINFO, ("Threads number:", threadsNumber));
|
||||
CHECK(m_cig, ());
|
||||
CHECK(m_cpg, ());
|
||||
|
||||
classificator::Load();
|
||||
std::vector<platform::LocalCountryFile> localFiles;
|
||||
platform::FindAllLocalMapsAndCleanup(std::numeric_limits<int64_t>::max(), localFiles);
|
||||
|
||||
std::vector<std::unique_ptr<FrozenDataSource>> dataSources;
|
||||
for (size_t i = 0; i < threadsNumber; ++i)
|
||||
dataSources.emplace_back(std::make_unique<FrozenDataSource>());
|
||||
|
||||
for (auto const & localFile : localFiles)
|
||||
{
|
||||
auto const & countryFile = localFile.GetCountryFile();
|
||||
// Only maps from countries.txt should be used.
|
||||
if (!m_cpg->GetStorageForTesting().IsLeaf(countryFile.GetName()))
|
||||
continue;
|
||||
|
||||
m_numMwmIds->RegisterFile(countryFile);
|
||||
|
||||
for (auto & dataSource : dataSources)
|
||||
{
|
||||
auto const result = dataSource->RegisterMap(localFile);
|
||||
CHECK_EQUAL(result.second, MwmSet::RegResult::Success, ("Can't register mwm:", localFile));
|
||||
|
||||
auto const mwmId = dataSource->GetMwmIdByCountryFile(countryFile);
|
||||
if (!mwmId.IsAlive())
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto & dataSource : dataSources)
|
||||
m_dataSourcesStorage.PushDataSource(std::move(dataSource));
|
||||
}
|
||||
|
||||
RoutesBuilder::Result RoutesBuilder::ProcessTask(Params const & params)
|
||||
{
|
||||
Processor processor(m_numMwmIds, m_dataSourcesStorage, m_cpg, m_cig);
|
||||
return processor(params);
|
||||
}
|
||||
|
||||
std::future<RoutesBuilder::Result> RoutesBuilder::ProcessTaskAsync(Params const & params)
|
||||
{
|
||||
// Should be copyable to workaround MSVC bug (https://developercommunity.visualstudio.com/t/108672)
|
||||
auto task = [processor = std::make_shared<Processor>(m_numMwmIds, m_dataSourcesStorage, m_cpg, m_cig)](
|
||||
Params const & params) -> Result { return (*processor)(params); };
|
||||
return m_threadPool.Submit(std::move(task), params);
|
||||
}
|
||||
|
||||
// RoutesBuilder::Result ---------------------------------------------------------------------------
|
||||
|
||||
// static
|
||||
std::string const RoutesBuilder::Result::kDumpExtension = ".mapsme.dump";
|
||||
|
||||
// static
|
||||
void RoutesBuilder::Result::Dump(Result const & result, std::string const & filePath)
|
||||
{
|
||||
FileWriter writer(filePath);
|
||||
WriteToSink(writer, static_cast<int>(result.m_code));
|
||||
|
||||
writer.Write(&result.m_buildTimeSeconds, sizeof(m_buildTimeSeconds));
|
||||
|
||||
RoutesBuilder::Params::Dump(result.m_params, writer);
|
||||
|
||||
size_t const routesNumber = result.m_routes.size();
|
||||
writer.Write(&routesNumber, sizeof(routesNumber));
|
||||
for (auto const & route : result.m_routes)
|
||||
RoutesBuilder::Route::Dump(route, writer);
|
||||
}
|
||||
|
||||
// static
|
||||
RoutesBuilder::Result RoutesBuilder::Result::Load(std::string const & filePath)
|
||||
{
|
||||
Result result;
|
||||
FileReader reader(filePath);
|
||||
ReaderSource<FileReader> src(reader);
|
||||
|
||||
auto const code = ReadPrimitiveFromSource<int>(src);
|
||||
result.m_code = static_cast<RouterResultCode>(code);
|
||||
result.m_buildTimeSeconds = ReadPrimitiveFromSource<double>(src);
|
||||
result.m_params = RoutesBuilder::Params::Load(src);
|
||||
|
||||
auto const routesNumber = ReadPrimitiveFromSource<size_t>(src);
|
||||
result.m_routes.resize(routesNumber);
|
||||
for (size_t i = 0; i < routesNumber; ++i)
|
||||
result.m_routes[i] = RoutesBuilder::Route::Load(src);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// RoutesBuilder::Route ----------------------------------------------------------------------------
|
||||
|
||||
// static
|
||||
void RoutesBuilder::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));
|
||||
|
||||
DumpPointDVector(route.m_followedPolyline.GetPolyline().GetPoints(), writer);
|
||||
}
|
||||
|
||||
// static
|
||||
RoutesBuilder::Route RoutesBuilder::Route::Load(ReaderSource<FileReader> & src)
|
||||
{
|
||||
Route route;
|
||||
|
||||
route.m_eta = ReadPrimitiveFromSource<double>(src);
|
||||
route.m_distance = ReadPrimitiveFromSource<double>(src);
|
||||
|
||||
std::vector<m2::PointD> points = LoadPointDVector(src);
|
||||
if (points.empty())
|
||||
return route;
|
||||
|
||||
FollowedPolyline poly(points.begin(), points.end());
|
||||
route.m_followedPolyline.Swap(poly);
|
||||
|
||||
return route;
|
||||
}
|
||||
|
||||
std::vector<ms::LatLon> RoutesBuilder::Route::GetWaypoints() const
|
||||
{
|
||||
auto const & points = m_followedPolyline.GetPolyline().GetPoints();
|
||||
std::vector<ms::LatLon> latlonPoints;
|
||||
latlonPoints.reserve(points.size());
|
||||
for (auto const & point : points)
|
||||
latlonPoints.emplace_back(mercator::ToLatLon(point));
|
||||
|
||||
return latlonPoints;
|
||||
}
|
||||
|
||||
// RoutesBuilder::Processor ------------------------------------------------------------------------
|
||||
|
||||
RoutesBuilder::Processor::Processor(std::shared_ptr<NumMwmIds> numMwmIds, DataSourceStorage & dataSourceStorage,
|
||||
std::weak_ptr<storage::CountryParentGetter> cpg,
|
||||
std::weak_ptr<storage::CountryInfoGetter> cig)
|
||||
: m_numMwmIds(std::move(numMwmIds))
|
||||
, m_dataSourceStorage(dataSourceStorage)
|
||||
, m_cpg(std::move(cpg))
|
||||
, m_cig(std::move(cig))
|
||||
{}
|
||||
|
||||
RoutesBuilder::Processor::Processor(Processor && rhs) noexcept : m_dataSourceStorage(rhs.m_dataSourceStorage)
|
||||
{
|
||||
m_start = rhs.m_start;
|
||||
m_finish = rhs.m_finish;
|
||||
|
||||
m_router = std::move(rhs.m_router);
|
||||
m_delegate = std::move(rhs.m_delegate);
|
||||
m_numMwmIds = std::move(rhs.m_numMwmIds);
|
||||
m_trafficCache = std::move(rhs.m_trafficCache);
|
||||
m_cpg = std::move(rhs.m_cpg);
|
||||
m_cig = std::move(rhs.m_cig);
|
||||
m_dataSource = std::move(rhs.m_dataSource);
|
||||
}
|
||||
|
||||
void RoutesBuilder::Processor::InitRouter(VehicleType type)
|
||||
{
|
||||
if (m_router && m_router->GetVehicleType() == type)
|
||||
return;
|
||||
|
||||
auto const & cig = m_cig;
|
||||
auto const countryFileGetter = [cig](m2::PointD const & pt)
|
||||
{
|
||||
auto const cigSharedPtr = cig.lock();
|
||||
return cigSharedPtr->GetRegionCountryId(pt);
|
||||
};
|
||||
|
||||
auto const getMwmRectByName = [cig](std::string const & countryId)
|
||||
{
|
||||
auto const cigSharedPtr = cig.lock();
|
||||
return cigSharedPtr->GetLimitRectForLeaf(countryId);
|
||||
};
|
||||
|
||||
bool const loadAltitudes = type != VehicleType::Car;
|
||||
if (!m_dataSource)
|
||||
m_dataSource = m_dataSourceStorage.GetDataSource();
|
||||
|
||||
m_router = std::make_unique<IndexRouter>(type, loadAltitudes, *m_cpg.lock(), countryFileGetter, getMwmRectByName,
|
||||
m_numMwmIds, MakeNumMwmTree(*m_numMwmIds, *m_cig.lock()), *m_trafficCache,
|
||||
*m_dataSource);
|
||||
}
|
||||
|
||||
RoutesBuilder::Result RoutesBuilder::Processor::operator()(Params const & params)
|
||||
{
|
||||
InitRouter(params.m_type);
|
||||
SCOPE_GUARD(returnDataSource, [&]() { m_dataSourceStorage.PushDataSource(std::move(m_dataSource)); });
|
||||
|
||||
LOG(LINFO, ("Start building route, checkpoints:", params.m_checkpoints));
|
||||
|
||||
RouterResultCode resultCode = RouterResultCode::RouteNotFound;
|
||||
routing::Route route("" /* router */, 0 /* routeId */);
|
||||
|
||||
CHECK(m_dataSource, ());
|
||||
|
||||
double timeSum = 0.0;
|
||||
for (size_t i = 0; i < params.m_launchesNumber; ++i)
|
||||
{
|
||||
m_delegate->SetTimeout(params.m_timeoutSeconds);
|
||||
base::Timer timer;
|
||||
resultCode = m_router->CalculateRoute(params.m_checkpoints, m2::PointD::Zero(), false /* adjustToPrevRoute */,
|
||||
*m_delegate, route);
|
||||
|
||||
if (resultCode != RouterResultCode::NoError)
|
||||
break;
|
||||
|
||||
timeSum += timer.ElapsedSeconds();
|
||||
}
|
||||
|
||||
Result result;
|
||||
result.m_params.m_checkpoints = params.m_checkpoints;
|
||||
result.m_code = resultCode;
|
||||
result.m_buildTimeSeconds = timeSum / static_cast<double>(params.m_launchesNumber);
|
||||
|
||||
RoutesBuilder::Route routeResult;
|
||||
routeResult.m_distance = route.GetTotalDistanceMeters();
|
||||
routeResult.m_eta = route.GetTotalTimeSec();
|
||||
|
||||
routeResult.m_followedPolyline = route.GetFollowedPolyline();
|
||||
|
||||
result.m_routes.emplace_back(std::move(routeResult));
|
||||
|
||||
return result;
|
||||
}
|
||||
} // namespace routes_builder
|
||||
} // namespace routing
|
||||
141
libs/routing/routes_builder/routes_builder.hpp
Normal file
141
libs/routing/routes_builder/routes_builder.hpp
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
#pragma once
|
||||
|
||||
#include "routing/routes_builder/data_source_storage.hpp"
|
||||
|
||||
#include "routing/checkpoints.hpp"
|
||||
#include "routing/index_router.hpp"
|
||||
#include "routing/router_delegate.hpp"
|
||||
#include "routing/routing_callbacks.hpp"
|
||||
#include "routing/segment.hpp"
|
||||
#include "routing/vehicle_mask.hpp"
|
||||
|
||||
#include "traffic/traffic_cache.hpp"
|
||||
|
||||
#include "storage/country_info_getter.hpp"
|
||||
#include "storage/country_parent_getter.hpp"
|
||||
|
||||
#include "routing_common/num_mwm_id.hpp"
|
||||
|
||||
#include "routing/base/followed_polyline.hpp"
|
||||
|
||||
#include "platform/platform.hpp"
|
||||
|
||||
#include "coding/file_reader.hpp"
|
||||
#include "coding/file_writer.hpp"
|
||||
#include "coding/reader.hpp"
|
||||
|
||||
#include "geometry/latlon.hpp"
|
||||
|
||||
#include "base/macros.hpp"
|
||||
#include "base/thread_pool_computational.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
#include <future>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace routing
|
||||
{
|
||||
namespace routes_builder
|
||||
{
|
||||
// TODO (@gmoryes)
|
||||
// Reuse this class for routing_integration_tests
|
||||
class RoutesBuilder
|
||||
{
|
||||
public:
|
||||
explicit RoutesBuilder(size_t threadsNumber);
|
||||
DISALLOW_COPY(RoutesBuilder);
|
||||
|
||||
static RoutesBuilder & GetSimpleRoutesBuilder();
|
||||
|
||||
struct Params
|
||||
{
|
||||
static void Dump(Params const & params, FileWriter & writer);
|
||||
static Params Load(ReaderSource<FileReader> & src);
|
||||
|
||||
Params() = default;
|
||||
Params(VehicleType type, ms::LatLon const & start, ms::LatLon const & finish);
|
||||
Params(VehicleType type, std::vector<m2::PointD> && checkpoints);
|
||||
|
||||
VehicleType m_type = VehicleType::Car;
|
||||
Checkpoints m_checkpoints;
|
||||
uint32_t m_timeoutSeconds = RouterDelegate::kNoTimeout;
|
||||
uint32_t m_launchesNumber = 1;
|
||||
};
|
||||
|
||||
struct Route
|
||||
{
|
||||
static void Dump(Route const & route, FileWriter & writer);
|
||||
static Route Load(ReaderSource<FileReader> & src);
|
||||
|
||||
std::vector<ms::LatLon> GetWaypoints() const;
|
||||
double GetETA() const { return m_eta; }
|
||||
|
||||
double m_eta = 0.0;
|
||||
double m_distance = 0.0;
|
||||
FollowedPolyline m_followedPolyline;
|
||||
};
|
||||
|
||||
struct Result
|
||||
{
|
||||
static std::string const kDumpExtension;
|
||||
static void Dump(Result const & result, std::string const & filePath);
|
||||
static Result Load(std::string const & filePath);
|
||||
|
||||
VehicleType GetVehicleType() const { return m_params.m_type; }
|
||||
m2::PointD const & GetStartPoint() const { return m_params.m_checkpoints.GetPointFrom(); }
|
||||
m2::PointD const & GetFinishPoint() const { return m_params.m_checkpoints.GetPointTo(); }
|
||||
bool IsCodeOK() const { return m_code == RouterResultCode::NoError; }
|
||||
std::vector<Route> const & GetRoutes() const { return m_routes; }
|
||||
|
||||
RouterResultCode m_code = RouterResultCode::RouteNotFound;
|
||||
Params m_params;
|
||||
std::vector<Route> m_routes;
|
||||
double m_buildTimeSeconds = 0.0;
|
||||
};
|
||||
|
||||
Result ProcessTask(Params const & params);
|
||||
std::future<Result> ProcessTaskAsync(Params const & params);
|
||||
|
||||
private:
|
||||
class Processor
|
||||
{
|
||||
public:
|
||||
Processor(std::shared_ptr<NumMwmIds> numMwmIds, DataSourceStorage & dataSourceStorage,
|
||||
std::weak_ptr<storage::CountryParentGetter> cpg, std::weak_ptr<storage::CountryInfoGetter> cig);
|
||||
|
||||
Processor(Processor && rhs) noexcept;
|
||||
|
||||
Result operator()(Params const & params);
|
||||
|
||||
private:
|
||||
void InitRouter(VehicleType type);
|
||||
|
||||
ms::LatLon m_start;
|
||||
ms::LatLon m_finish;
|
||||
|
||||
std::unique_ptr<IndexRouter> m_router;
|
||||
std::shared_ptr<RouterDelegate> m_delegate = std::make_shared<RouterDelegate>();
|
||||
|
||||
std::shared_ptr<NumMwmIds> m_numMwmIds;
|
||||
std::shared_ptr<traffic::TrafficCache> m_trafficCache = std::make_shared<traffic::TrafficCache>();
|
||||
DataSourceStorage & m_dataSourceStorage;
|
||||
std::weak_ptr<storage::CountryParentGetter> m_cpg;
|
||||
std::weak_ptr<storage::CountryInfoGetter> m_cig;
|
||||
std::unique_ptr<FrozenDataSource> m_dataSource;
|
||||
};
|
||||
|
||||
base::ComputationalThreadPool m_threadPool;
|
||||
|
||||
std::shared_ptr<storage::CountryParentGetter> m_cpg = std::make_shared<storage::CountryParentGetter>();
|
||||
|
||||
std::shared_ptr<storage::CountryInfoGetter> m_cig =
|
||||
storage::CountryInfoReader::CreateCountryInfoGetter(GetPlatform());
|
||||
|
||||
std::shared_ptr<NumMwmIds> m_numMwmIds = std::make_shared<NumMwmIds>();
|
||||
|
||||
DataSourceStorage m_dataSourcesStorage;
|
||||
};
|
||||
} // namespace routes_builder
|
||||
} // namespace routing
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
project(routes_builder_tool)
|
||||
|
||||
set(SRC
|
||||
routes_builder_tool.cpp
|
||||
utils.cpp
|
||||
utils.hpp
|
||||
)
|
||||
|
||||
omim_add_executable(${PROJECT_NAME} ${SRC})
|
||||
|
||||
target_link_libraries(${PROJECT_NAME}
|
||||
routes_builder
|
||||
routing_api
|
||||
gflags::gflags
|
||||
)
|
||||
|
|
@ -0,0 +1,139 @@
|
|||
#include "routing/routes_builder/routes_builder_tool/utils.hpp"
|
||||
|
||||
#include "routing/routes_builder/routes_builder.hpp"
|
||||
|
||||
#include "platform/platform.hpp"
|
||||
|
||||
#include "base/assert.hpp"
|
||||
#include "base/logging.hpp"
|
||||
|
||||
#include <exception>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <gflags/gflags.h>
|
||||
|
||||
DEFINE_uint64(threads, 0, "The number of threads. std::thread::hardware_concurrency() is used by default.");
|
||||
|
||||
DEFINE_string(routes_file, "",
|
||||
"Path to file with routes in format: \n\t"
|
||||
"first_start_lat first_start_lon first_finish_lat first_finish_lon\n\t"
|
||||
"second_start_lat second_start_lon second_finish_lat second_finish_lon\n\t"
|
||||
"...");
|
||||
|
||||
DEFINE_string(dump_path, "",
|
||||
"Path where routes will be dumped after building."
|
||||
"Useful for intermediate results, because routes building "
|
||||
"is a long process.");
|
||||
|
||||
DEFINE_string(data_path, "", "Data path.");
|
||||
DEFINE_string(resources_path, "", "Resources path.");
|
||||
|
||||
DEFINE_string(api_name, "", "Api name, current options: mapbox,google");
|
||||
DEFINE_string(api_token, "", "Token for chosen api.");
|
||||
|
||||
DEFINE_uint64(start_from, 0, "The line number from which the tool should start reading.");
|
||||
|
||||
DEFINE_int32(timeout, 10 * 60,
|
||||
"Timeout in seconds for each route building. "
|
||||
"0 means without timeout (default: 10 minutes).");
|
||||
|
||||
DEFINE_bool(verbose, false, "Verbose logging (default: false)");
|
||||
|
||||
DEFINE_int32(launches_number, 1, "Number of launches of routes buildings. Needs for benchmarking (default: 1)");
|
||||
DEFINE_string(vehicle_type, "car", "Vehicle type: car|pedestrian|bicycle|transit. (Only for mapsme).");
|
||||
|
||||
using namespace routing;
|
||||
using namespace routes_builder;
|
||||
using namespace routing_quality;
|
||||
|
||||
namespace
|
||||
{
|
||||
bool IsLocalBuild()
|
||||
{
|
||||
return !FLAGS_routes_file.empty() && FLAGS_api_name.empty() && FLAGS_api_token.empty();
|
||||
}
|
||||
|
||||
bool IsApiBuild()
|
||||
{
|
||||
return !FLAGS_routes_file.empty() && !FLAGS_api_name.empty() && !FLAGS_api_token.empty();
|
||||
}
|
||||
|
||||
void CheckDirExistence(std::string const & dir)
|
||||
{
|
||||
CHECK(Platform::IsDirectory(dir), ("Can not find directory:", dir));
|
||||
}
|
||||
} // namespace
|
||||
|
||||
int Main(int argc, char ** argv)
|
||||
{
|
||||
gflags::SetUsageMessage("This tool provides routes building for them further analyze.");
|
||||
gflags::ParseCommandLineFlags(&argc, &argv, true);
|
||||
|
||||
CHECK_GREATER_OR_EQUAL(FLAGS_timeout, 0, ("Timeout should be greater than zero."));
|
||||
|
||||
CHECK(!FLAGS_routes_file.empty(), ("\n\n\t--routes_file is required.", "\n\nType --help for usage."));
|
||||
|
||||
if (!FLAGS_data_path.empty())
|
||||
GetPlatform().SetWritableDirForTests(FLAGS_data_path);
|
||||
|
||||
if (!FLAGS_resources_path.empty())
|
||||
GetPlatform().SetResourceDir(FLAGS_resources_path);
|
||||
|
||||
CHECK(IsLocalBuild() || IsApiBuild(),
|
||||
("\n\n\t--routes_file empty is:", FLAGS_routes_file.empty(), "\n\t--api_name empty is:", FLAGS_api_name.empty(),
|
||||
"\n\t--api_token empty is:", FLAGS_api_token.empty(), "\n\nType --help for usage."));
|
||||
|
||||
CHECK(!FLAGS_dump_path.empty(),
|
||||
("\n\n\t--dump_path is empty. It makes no sense to run this tool. No result will be saved.",
|
||||
"\n\nType --help for usage."));
|
||||
|
||||
CHECK_GREATER_OR_EQUAL(FLAGS_launches_number, 1, ());
|
||||
|
||||
if (Platform::IsFileExistsByFullPath(FLAGS_dump_path))
|
||||
CheckDirExistence(FLAGS_dump_path);
|
||||
else
|
||||
CHECK_EQUAL(Platform::MkDir(FLAGS_dump_path), Platform::EError::ERR_OK, ());
|
||||
|
||||
if (IsLocalBuild())
|
||||
{
|
||||
auto const launchesNumber = static_cast<uint32_t>(FLAGS_launches_number);
|
||||
if (launchesNumber > 1)
|
||||
LOG(LINFO, ("Benchmark mode is activated. Each route will be built", launchesNumber, "times."));
|
||||
|
||||
BuildRoutes(FLAGS_routes_file, FLAGS_dump_path, FLAGS_start_from, FLAGS_threads, FLAGS_timeout, FLAGS_vehicle_type,
|
||||
FLAGS_verbose, launchesNumber);
|
||||
}
|
||||
|
||||
if (IsApiBuild())
|
||||
{
|
||||
auto api = CreateRoutingApi(FLAGS_api_name, FLAGS_api_token);
|
||||
BuildRoutesWithApi(std::move(api), FLAGS_routes_file, FLAGS_dump_path, FLAGS_start_from);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char ** argv)
|
||||
{
|
||||
try
|
||||
{
|
||||
Main(argc, argv);
|
||||
}
|
||||
catch (RootException const & e)
|
||||
{
|
||||
LOG(LERROR, ("Core exception:", e.Msg()));
|
||||
}
|
||||
catch (std::exception const & e)
|
||||
{
|
||||
LOG(LERROR, ("Std exception:", e.what()));
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
LOG(LERROR, ("Unknown exception."));
|
||||
}
|
||||
|
||||
LOG(LINFO, ("Done."));
|
||||
return 0;
|
||||
}
|
||||
277
libs/routing/routes_builder/routes_builder_tool/utils.cpp
Normal file
277
libs/routing/routes_builder/routes_builder_tool/utils.cpp
Normal file
|
|
@ -0,0 +1,277 @@
|
|||
#include "routing/routes_builder/routes_builder_tool/utils.hpp"
|
||||
|
||||
#include "routing/routing_quality/api/google/google_api.hpp"
|
||||
#include "routing/routing_quality/api/mapbox/mapbox_api.hpp"
|
||||
|
||||
#include "routing/checkpoints.hpp"
|
||||
#include "routing/vehicle_mask.hpp"
|
||||
|
||||
#include "platform/platform.hpp"
|
||||
|
||||
#include "geometry/latlon.hpp"
|
||||
#include "geometry/mercator.hpp"
|
||||
|
||||
#include "base/assert.hpp"
|
||||
#include "base/file_name_utils.hpp"
|
||||
#include "base/logging.hpp"
|
||||
#include "base/timer.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <chrono>
|
||||
#include <future>
|
||||
#include <iostream>
|
||||
#include <optional>
|
||||
#include <thread>
|
||||
#include <tuple>
|
||||
|
||||
namespace routing
|
||||
{
|
||||
namespace routes_builder
|
||||
{
|
||||
using namespace routing_quality;
|
||||
|
||||
namespace
|
||||
{
|
||||
size_t GetNumberOfLines(std::string const & filename)
|
||||
{
|
||||
std::ifstream input(filename);
|
||||
CHECK(input.good(), ("Error during opening:", filename));
|
||||
|
||||
size_t count = 0;
|
||||
std::string line;
|
||||
while (std::getline(input, line))
|
||||
++count;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
routing::VehicleType ConvertVehicleTypeFromString(std::string const & str)
|
||||
{
|
||||
if (str == "car")
|
||||
return routing::VehicleType::Car;
|
||||
if (str == "pedestrian")
|
||||
return routing::VehicleType::Pedestrian;
|
||||
if (str == "bicycle")
|
||||
return routing::VehicleType::Bicycle;
|
||||
if (str == "transit")
|
||||
return routing::VehicleType::Transit;
|
||||
|
||||
CHECK(false, ("Unknown vehicle type:", str));
|
||||
UNREACHABLE();
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void BuildRoutes(std::string const & routesPath, std::string const & dumpPath, uint64_t startFrom,
|
||||
uint64_t threadsNumber, uint32_t timeoutPerRouteSeconds, std::string const & vehicleTypeStr,
|
||||
bool verbose, uint32_t launchesNumber)
|
||||
{
|
||||
CHECK(Platform::IsFileExistsByFullPath(routesPath), ("Can not find file:", routesPath));
|
||||
CHECK(!dumpPath.empty(), ("Empty dumpPath."));
|
||||
|
||||
std::ifstream input(routesPath);
|
||||
CHECK(input.good(), ("Error during opening:", routesPath));
|
||||
|
||||
if (!threadsNumber)
|
||||
{
|
||||
auto const hardwareConcurrency = std::thread::hardware_concurrency();
|
||||
threadsNumber = hardwareConcurrency > 0 ? hardwareConcurrency : 2;
|
||||
}
|
||||
|
||||
RoutesBuilder routesBuilder(threadsNumber);
|
||||
|
||||
std::vector<std::future<RoutesBuilder::Result>> tasks;
|
||||
double lastPercent = 0.0;
|
||||
|
||||
auto const vehicleType = ConvertVehicleTypeFromString(vehicleTypeStr);
|
||||
{
|
||||
RoutesBuilder::Params params;
|
||||
params.m_type = vehicleType;
|
||||
params.m_timeoutSeconds = timeoutPerRouteSeconds;
|
||||
params.m_launchesNumber = launchesNumber;
|
||||
|
||||
base::ScopedLogLevelChanger changer(verbose ? base::LogLevel::LINFO : base::LogLevel::LERROR);
|
||||
ms::LatLon start;
|
||||
ms::LatLon finish;
|
||||
size_t startFromCopy = startFrom;
|
||||
while (input >> start.m_lat >> start.m_lon >> finish.m_lat >> finish.m_lon)
|
||||
{
|
||||
if (startFromCopy > 0)
|
||||
{
|
||||
--startFromCopy;
|
||||
continue;
|
||||
}
|
||||
|
||||
auto const startPoint = mercator::FromLatLon(start);
|
||||
auto const finishPoint = mercator::FromLatLon(finish);
|
||||
|
||||
params.m_checkpoints = Checkpoints(std::vector<m2::PointD>({startPoint, finishPoint}));
|
||||
tasks.emplace_back(routesBuilder.ProcessTaskAsync(params));
|
||||
}
|
||||
|
||||
LOG_FORCE(LINFO, ("Created:", tasks.size(), "tasks, vehicle type:", vehicleType));
|
||||
base::Timer timer;
|
||||
for (size_t i = 0; i < tasks.size(); ++i)
|
||||
{
|
||||
size_t shiftIndex = i + startFrom;
|
||||
auto & task = tasks[i];
|
||||
task.wait();
|
||||
|
||||
auto const result = task.get();
|
||||
if (result.m_code == RouterResultCode::Cancelled)
|
||||
LOG_FORCE(LINFO, ("Route:", i, "(", i + 1, "line of file) was building too long."));
|
||||
|
||||
std::string const fullPath =
|
||||
base::JoinPath(dumpPath, std::to_string(shiftIndex) + RoutesBuilder::Result::kDumpExtension);
|
||||
|
||||
RoutesBuilder::Result::Dump(result, fullPath);
|
||||
|
||||
double const curPercent = static_cast<double>(shiftIndex + 1) / (tasks.size() + startFrom) * 100.0;
|
||||
|
||||
if (curPercent - lastPercent > 1.0 || shiftIndex + 1 == tasks.size())
|
||||
{
|
||||
lastPercent = curPercent;
|
||||
LOG_FORCE(LINFO, ("Progress:", lastPercent, "%"));
|
||||
}
|
||||
}
|
||||
LOG_FORCE(LINFO, ("BuildRoutes() took:", timer.ElapsedSeconds(), "seconds."));
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<std::tuple<ms::LatLon, ms::LatLon, int32_t>> ParseApiLine(std::ifstream & input)
|
||||
{
|
||||
std::string line;
|
||||
if (!std::getline(input, line))
|
||||
return {};
|
||||
|
||||
std::stringstream ss;
|
||||
ss << line;
|
||||
|
||||
ms::LatLon start;
|
||||
ms::LatLon finish;
|
||||
ss >> start.m_lat >> start.m_lon >> finish.m_lat >> finish.m_lon;
|
||||
|
||||
int32_t utcOffset = 0;
|
||||
if (!(ss >> utcOffset))
|
||||
utcOffset = 0;
|
||||
|
||||
return {{start, finish, utcOffset}};
|
||||
}
|
||||
|
||||
void BuildRoutesWithApi(std::unique_ptr<api::RoutingApi> routingApi, std::string const & routesPath,
|
||||
std::string const & dumpPath, int64_t startFrom)
|
||||
{
|
||||
std::ifstream input(routesPath);
|
||||
CHECK(input.good(), ("Error during opening:", routesPath));
|
||||
|
||||
size_t constexpr kResponseBuffer = 50;
|
||||
std::array<api::Response, kResponseBuffer> result;
|
||||
|
||||
api::Params params;
|
||||
params.m_type = api::VehicleType::Car;
|
||||
|
||||
size_t rps = 0;
|
||||
base::HighResTimer timer;
|
||||
auto const getElapsedMilliSeconds = [&timer]()
|
||||
{
|
||||
auto const ms = timer.ElapsedMilliseconds();
|
||||
LOG(LDEBUG, ("Elapsed:", ms, "ms"));
|
||||
return ms;
|
||||
};
|
||||
|
||||
auto const drop = [&]()
|
||||
{
|
||||
rps = 0;
|
||||
timer.Reset();
|
||||
};
|
||||
|
||||
auto const sleepIfNeed = [&]()
|
||||
{
|
||||
// Greater than 1 second.
|
||||
if (getElapsedMilliSeconds() > 1000)
|
||||
{
|
||||
drop();
|
||||
return;
|
||||
}
|
||||
|
||||
++rps;
|
||||
if (rps >= routingApi->GetMaxRPS())
|
||||
{
|
||||
LOG(LDEBUG, ("Sleep for 2 seconds."));
|
||||
std::this_thread::sleep_for(std::chrono::seconds(2));
|
||||
drop();
|
||||
}
|
||||
};
|
||||
|
||||
size_t count = 0;
|
||||
size_t prevDumpedNumber = 0;
|
||||
auto const dump = [&]()
|
||||
{
|
||||
for (size_t i = 0; i < count; ++i)
|
||||
{
|
||||
std::string filepath =
|
||||
base::JoinPath(dumpPath, std::to_string(prevDumpedNumber + i) + api::Response::kDumpExtension);
|
||||
|
||||
api::Response::Dump(filepath, result[i]);
|
||||
}
|
||||
|
||||
prevDumpedNumber += count;
|
||||
count = 0;
|
||||
};
|
||||
|
||||
size_t const tasksNumber = GetNumberOfLines(routesPath);
|
||||
LOG(LINFO, ("Receive:", tasksNumber, "for api:", routingApi->GetApiName()));
|
||||
double lastPercent = 0.0;
|
||||
|
||||
ms::LatLon start;
|
||||
ms::LatLon finish;
|
||||
int32_t utcOffset;
|
||||
while (auto parsedData = ParseApiLine(input))
|
||||
{
|
||||
if (startFrom > 0)
|
||||
{
|
||||
--startFrom;
|
||||
++count;
|
||||
if (count == kResponseBuffer)
|
||||
{
|
||||
prevDumpedNumber += count;
|
||||
count = 0;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (count == kResponseBuffer)
|
||||
dump();
|
||||
|
||||
double const curPercent = static_cast<double>(prevDumpedNumber + count) / tasksNumber * 100.0;
|
||||
if (curPercent - lastPercent > 1.0)
|
||||
{
|
||||
lastPercent = curPercent;
|
||||
LOG(LINFO, ("Progress:", lastPercent, "%"));
|
||||
}
|
||||
|
||||
std::tie(start, finish, utcOffset) = *parsedData;
|
||||
params.m_waypoints = routing::Checkpoints(mercator::FromLatLon(start), mercator::FromLatLon(finish));
|
||||
|
||||
result[count] = routingApi->CalculateRoute(params, utcOffset);
|
||||
|
||||
sleepIfNeed();
|
||||
++count;
|
||||
}
|
||||
|
||||
dump();
|
||||
}
|
||||
|
||||
std::unique_ptr<routing_quality::api::RoutingApi> CreateRoutingApi(std::string const & name, std::string const & token)
|
||||
{
|
||||
if (name == "mapbox")
|
||||
return std::make_unique<routing_quality::api::mapbox::MapboxApi>(token);
|
||||
if (name == "google")
|
||||
return std::make_unique<routing_quality::api::google::GoogleApi>(token);
|
||||
|
||||
LOG(LERROR, ("Unsupported api name:", name));
|
||||
UNREACHABLE();
|
||||
}
|
||||
} // namespace routes_builder
|
||||
} // namespace routing
|
||||
25
libs/routing/routes_builder/routes_builder_tool/utils.hpp
Normal file
25
libs/routing/routes_builder/routes_builder_tool/utils.hpp
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
#pragma once
|
||||
|
||||
#include "routing/routing_quality/api/api.hpp"
|
||||
|
||||
#include "routing/routes_builder/routes_builder.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace routing
|
||||
{
|
||||
namespace routes_builder
|
||||
{
|
||||
void BuildRoutes(std::string const & routesPath, std::string const & dumpPath, uint64_t startFrom,
|
||||
uint64_t threadsNumber, uint32_t timeoutPerRouteSeconds, std::string const & vehicleType, bool verbose,
|
||||
uint32_t launchesNumber);
|
||||
|
||||
void BuildRoutesWithApi(std::unique_ptr<routing_quality::api::RoutingApi> routingApi, std::string const & routesPath,
|
||||
std::string const & dumpPath, int64_t startFrom);
|
||||
|
||||
std::unique_ptr<routing_quality::api::RoutingApi> CreateRoutingApi(std::string const & name, std::string const & token);
|
||||
} // namespace routes_builder
|
||||
} // namespace routing
|
||||
Loading…
Add table
Add a link
Reference in a new issue