Skip to content

Commit 6302200

Browse files
authored
Merge pull request #10495 from vmihaylenko/track-generator-bindings
[routing] Track generator bindings.
2 parents f347bac + 5770f68 commit 6302200

File tree

4 files changed

+237
-0
lines changed

4 files changed

+237
-0
lines changed

track_generator/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,5 +36,7 @@ omim_link_libraries(
3636
${LIBZ}
3737
)
3838

39+
omim_add_pybindings_subdirectory(pytrack_generator)
40+
3941
link_qt5_core(${PROJECT_NAME})
4042
link_qt5_network(${PROJECT_NAME})
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
project(pytrack_generator)
2+
3+
set(
4+
SRC
5+
bindings.cpp
6+
)
7+
8+
include_directories(${CMAKE_BINARY_DIR})
9+
10+
omim_add_library(${PROJECT_NAME} MODULE ${SRC})
11+
12+
omim_link_libraries(
13+
${PROJECT_NAME}
14+
${PYTHON_LIBRARIES}
15+
${Boost_LIBRARIES}
16+
routing_quality
17+
routing
18+
routing_common
19+
coding
20+
geometry
21+
base
22+
stats_client
23+
)
24+
25+
set_target_properties(${PROJECT_NAME} PROPERTIES PREFIX "")
Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
#include "routing/route.hpp"
2+
#include "routing/routing_callbacks.hpp"
3+
#include "routing/routing_quality/utils.hpp"
4+
5+
#include "platform/platform.hpp"
6+
7+
#include "geometry/latlon.hpp"
8+
9+
#include <exception>
10+
#include <sstream>
11+
#include <string>
12+
#include <vector>
13+
14+
#pragma GCC diagnostic push
15+
#pragma GCC diagnostic ignored "-Wreorder"
16+
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
17+
#if defined(__clang__)
18+
#pragma clang diagnostic push
19+
#pragma clang diagnostic ignored "-Wunused-local-typedef"
20+
#endif
21+
22+
#include "pyhelpers/module_version.hpp"
23+
#include "pyhelpers/vector_list_conversion.hpp"
24+
25+
#include <boost/python.hpp>
26+
#include <boost/python/exception_translator.hpp>
27+
#include <boost/python/suite/indexing/vector_indexing_suite.hpp>
28+
29+
using namespace std;
30+
31+
namespace
32+
{
33+
class RouteNotFoundException : public exception
34+
{
35+
public:
36+
RouteNotFoundException(string const & msg) : m_msg(msg) {}
37+
38+
virtual ~RouteNotFoundException() noexcept = default;
39+
40+
char const * what() const noexcept override { return m_msg.c_str(); }
41+
42+
private:
43+
string m_msg;
44+
};
45+
46+
PyObject * kRouteNotFoundException = nullptr;
47+
48+
PyObject * CreateExceptionClass(char const * name)
49+
{
50+
using namespace boost::python;
51+
string const scopeName = extract<string>(scope().attr("__name__"));
52+
string const qualifiedName = scopeName + "." + name;
53+
PyObject * ex = PyErr_NewException(qualifiedName.c_str(), PyExc_Exception, nullptr);
54+
CHECK(ex, ());
55+
scope().attr(name) = handle<>(borrowed(ex));
56+
return ex;
57+
}
58+
59+
template <typename Exception>
60+
void Translate(PyObject * object, Exception const & e)
61+
{
62+
PyErr_SetString(object, e.what());
63+
}
64+
65+
struct Params
66+
{
67+
Params(string const & data, string const & userResources) : m_dataPath(data), m_userResourcesPath(userResources)
68+
{
69+
if (m_dataPath.empty())
70+
throw runtime_error("data_path parameter not specified");
71+
72+
if (m_userResourcesPath.empty())
73+
throw runtime_error("user_resources_path parameter not specified");
74+
}
75+
76+
string DebugPrint() const
77+
{
78+
ostringstream ss;
79+
ss << "Params(data path: " << m_dataPath << ", user resources path: " << m_userResourcesPath << ")";
80+
return ss.str();
81+
}
82+
83+
string m_dataPath;
84+
string m_userResourcesPath;
85+
};
86+
87+
using Track = vector<ms::LatLon>;
88+
89+
Track GetTrackFrom(routing::Route const & route)
90+
{
91+
CHECK(route.IsValid(), ());
92+
auto const & segments = route.GetRouteSegments();
93+
Track res;
94+
res.reserve(segments.size());
95+
for (auto const & s : segments)
96+
res.emplace_back(MercatorBounds::ToLatLon(s.GetJunction().GetPoint()));
97+
98+
return res;
99+
}
100+
101+
struct Generator
102+
{
103+
explicit Generator(Params const & params) : m_params(params)
104+
{
105+
Platform & pl = GetPlatform();
106+
pl.SetWritableDirForTests(m_params.m_dataPath);
107+
pl.SetResourceDir(m_params.m_userResourcesPath);
108+
}
109+
110+
Track Generate(boost::python::object const & iterable) const
111+
{
112+
using namespace routing_quality;
113+
114+
Track const coordinates = python_list_to_std_vector<ms::LatLon>(iterable);
115+
auto result = GetRoute(FromLatLon(coordinates), routing::VehicleType::Pedestrian);
116+
if (result.m_code != routing::RouterResultCode::NoError)
117+
throw RouteNotFoundException("Can't build route");
118+
119+
return GetTrackFrom(result.m_route);
120+
}
121+
122+
Params m_params;
123+
};
124+
} // namespace
125+
126+
using namespace boost::python;
127+
128+
BOOST_PYTHON_MODULE(pytrack_generator)
129+
{
130+
scope().attr("__version__") = PYBINDINGS_VERSION;
131+
register_exception_translator<runtime_error>([](auto const & e) { Translate(PyExc_RuntimeError, e); });
132+
133+
kRouteNotFoundException = CreateExceptionClass("RouteNotFoundException");
134+
register_exception_translator<RouteNotFoundException>([](auto const & e) { Translate(kRouteNotFoundException, e); });
135+
136+
class_<Params>("Params", init<string, string>())
137+
.def("__str__", &Params::DebugPrint)
138+
.def_readonly("data_path", &Params::m_dataPath)
139+
.def_readonly("user_resources_path", &Params::m_userResourcesPath);
140+
141+
class_<ms::LatLon>("LatLon", init<double, double>())
142+
.def("__str__", &ms::DebugPrint)
143+
.def_readonly("lat", &ms::LatLon::lat)
144+
.def_readonly("lon", &ms::LatLon::lon);
145+
146+
class_<vector<ms::LatLon>>("LatLonList")
147+
.def(vector_indexing_suite<vector<ms::LatLon>>());
148+
149+
class_<Generator>("Generator", init<Params>())
150+
.def("generate", &Generator::Generate)
151+
.def_readonly("params", &Generator::m_params);
152+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import argparse
2+
import importlib.util
3+
import sys
4+
5+
def _usage():
6+
print("pytrack_generator_tests.py \
7+
--module_path path/to/pytrack_generator.so \
8+
--data_path path/to/omim/data \
9+
--user_resource_path path/to/omim/data")
10+
11+
def _main():
12+
parser = argparse.ArgumentParser()
13+
parser.add_argument(
14+
'--module_path',
15+
type=str
16+
)
17+
parser.add_argument(
18+
'--data_path',
19+
type=str
20+
)
21+
22+
parser.add_argument(
23+
'--user_resources_path',
24+
type=str,
25+
)
26+
27+
args = parser.parse_args(sys.argv[1:])
28+
if not args.module_path or not args.data_path or not args.user_resources_path:
29+
_usage()
30+
sys.exit(2)
31+
32+
spec = importlib.util.spec_from_file_location("pytrack_generator", args.module_path)
33+
ge = importlib.util.module_from_spec(spec)
34+
spec.loader.exec_module(ge)
35+
36+
params = ge.Params(args.data_path, args.user_resources_path)
37+
generator = ge.Generator(params)
38+
39+
points = ge.LatLonList()
40+
points.append(ge.LatLon(55.796993, 37.537640))
41+
points.append(ge.LatLon(55.798087, 37.539002))
42+
43+
result = generator.generate(points)
44+
assert len(result) > len(points)
45+
46+
try:
47+
invalid_points = ge.LatLonList()
48+
invalid_points.append(ge.LatLon(20, 20))
49+
invalid_points.append(ge.LatLon(20, 20))
50+
generator.generate(invalid_points)
51+
except ge.RouteNotFoundException as ex:
52+
print(ex)
53+
return
54+
55+
assert False
56+
57+
if __name__ == '__main__':
58+
_main()

0 commit comments

Comments
 (0)