diff --git a/omim.pro b/omim.pro index 6433acd7b7..3a84c20031 100644 --- a/omim.pro +++ b/omim.pro @@ -90,6 +90,13 @@ SUBDIRS = 3party base coding geometry editor indexer routing search search_tests_support.subdir = search/search_tests_support SUBDIRS *= search_tests_support + # Desktop-only library, can be used as a search interface for Python2.7. + CONFIG(search_engine_pylib) { + search_engine_pylib.subdir = search/search_engine_pylib + search_engine_pylib.depends = $$SUBDIRS + SUBDIRS *= search_engine_pylib + } + search_quality.subdir = search/search_quality search_quality.depends = $$SUBDIRS SUBDIRS *= search_quality diff --git a/search/run_search_engine.py b/search/run_search_engine.py new file mode 100755 index 0000000000..a8fe4de3ee --- /dev/null +++ b/search/run_search_engine.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python2.7 +# -*- coding: utf-8 -*- + +from __future__ import print_function +import argparse +import os +import search_engine_pylib as search + + +DIR = os.path.dirname(__file__) +RESOURCE_PATH = os.path.realpath(os.path.join(DIR, '..', 'data')) +MWM_PATH = os.path.realpath(os.path.join(DIR, '..', 'data')) + +parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter) +parser.add_argument('-r', metavar='RESOURCE_PATH', default=RESOURCE_PATH, help='Path to resources directory.') +parser.add_argument('-m', metavar='MWM_PATH', default=MWM_PATH, help='Path to mwm files.') +args = parser.parse_args() + +search.init(args.r, args.m) +engine = search.SearchEngine() + +params = search.Params() +params.query = 'кафе юность' +params.locale = 'ru' +params.lat_lon = search.LatLon(55.751633, 37.618705) +params.viewport = search.Viewport(search.Mercator(37.1336, 67.1349), + search.Mercator(38.0314, 67.7348)) +for result in engine.query(params): + print(result.to_string()) diff --git a/search/search_engine_pylib/api.cpp b/search/search_engine_pylib/api.cpp new file mode 100644 index 0000000000..6eef117cdc --- /dev/null +++ b/search/search_engine_pylib/api.cpp @@ -0,0 +1,254 @@ +#include "search/engine.hpp" +#include "search/processor_factory.hpp" +#include "search/search_tests_support/test_search_engine.hpp" +#include "search/search_tests_support/test_search_request.hpp" + +#include "indexer/classificator_loader.hpp" + +#include "storage/country_info_getter.hpp" +#include "storage/index.hpp" +#include "storage/storage.hpp" + +#include "platform/local_country_file.hpp" +#include "platform/local_country_file_utils.hpp" +#include "platform/platform.hpp" + +#include "coding/file_name_utils.hpp" + +#include "geometry/rect2d.hpp" + +#include "std/limits.hpp" +#include "std/shared_ptr.hpp" +#include "std/sstream.hpp" +#include "std/string.hpp" +#include "std/unique_ptr.hpp" +#include "std/vector.hpp" + +#include "base/logging.hpp" + +#include "defines.hpp" + +#include + +namespace +{ +// TODO (@y, @m): following code is quite similar to +// features_collector_tool and search_quality_tool. Need to replace +// both tools by python scripts that use this library. + +void DidDownload(storage::TCountryId const & /* countryId */, + shared_ptr const & /* localFile */) +{ +} + +bool WillDelete(storage::TCountryId const & /* countryId */, + shared_ptr const & /* localFile */) +{ + return false; +} + +unique_ptr g_storage; + +void Init(string const & resource_path, string const & mwm_path) +{ + auto & platform = GetPlatform(); + + string countriesFile = COUNTRIES_FILE; + + if (!resource_path.empty()) + { + platform.SetResourceDir(resource_path); + countriesFile = my::JoinFoldersToPath(resource_path, COUNTRIES_FILE); + } + + if (!mwm_path.empty()) + platform.SetWritableDirForTests(mwm_path); + + classificator::Load(); + + g_storage = make_unique(countriesFile, mwm_path); + g_storage->Init(&DidDownload, &WillDelete); +} + +struct LatLon +{ + LatLon() = default; + LatLon(double lat, double lon) : m_lat(lat), m_lon(lon) {} + + string ToString() const + { + ostringstream os; + os << "lat:" << m_lat << ", lon:" << m_lon; + return os.str(); + } + + double m_lat = 0.0; + double m_lon = 0.0; +}; + +struct Mercator +{ + Mercator() = default; + Mercator(double x, double y) : m_x(x), m_y(y) {} + Mercator(m2::PointD const & m): m_x(m.x), m_y(m.y) {} + + string ToString() const + { + ostringstream os; + os << "x: " << m_x << ", y: " << m_y; + return os.str(); + } + + double m_x = 0.0; + double m_y = 0.0; +}; + +struct Viewport +{ + Viewport() = default; + Viewport(Mercator const & min, Mercator const & max) : m_min(min), m_max(max) {} + + string ToString() const + { + ostringstream os; + os << "[" << m_min.ToString() << ", " << m_max.ToString() << "]"; + return os.str(); + } + + Mercator m_min; + Mercator m_max; +}; + +struct Params +{ + string ToString() const + { + ostringstream os; + os << m_query << ", " << m_locale << ", " << m_latLon.ToString() << ", " + << m_viewport.ToString(); + return os.str(); + } + + string m_query; + string m_locale; + LatLon m_latLon; + Viewport m_viewport; +}; + +struct Result +{ + Result() = default; + + Result(search::Result const & r) + { + m_name = r.GetString(); + m_address = r.GetAddress(); + m_hasCenter = r.HasPoint(); + if (m_hasCenter) + m_center = r.GetFeatureCenter(); + } + + string ToString() const + { + ostringstream os; + os << m_name << " [ " << m_address; + if (m_hasCenter) + os << ", " << m_center.ToString(); + os << " ]"; + return os.str(); + } + + string m_name; + string m_address; + bool m_hasCenter = false; + Mercator m_center; +}; + +struct SearchEngineProxy +{ + SearchEngineProxy() + { + CHECK(g_storage.get() != nullptr, ("init() is not called.")); + auto & platform = GetPlatform(); + auto infoGetter = storage::CountryInfoReader::CreateCountryInfoReader(platform); + infoGetter->InitAffiliationsInfo(&g_storage->GetAffiliations()); + + m_engine = make_shared( + move(infoGetter), make_unique(), search::Engine::Params{}); + + vector mwms; + platform::FindAllLocalMapsAndCleanup(numeric_limits::max() /* the latest version */, + mwms); + for (auto & mwm : mwms) + { + mwm.SyncWithDisk(); + m_engine->RegisterMap(mwm); + } + } + + boost::python::list Query(Params const & params) const + { + m_engine->SetLocale(params.m_locale); + + search::SearchParams sp; + sp.m_query = params.m_query; + sp.m_inputLocale = params.m_locale; + sp.SetMode(search::Mode::Everywhere); + sp.SetPosition(params.m_latLon.m_lat, params.m_latLon.m_lon); + sp.SetSuggestsEnabled(false); + + auto const & bottomLeft = params.m_viewport.m_min; + auto const & topRight = params.m_viewport.m_max; + m2::RectD const viewport(bottomLeft.m_x, bottomLeft.m_y, topRight.m_x, topRight.m_y); + search::tests_support::TestSearchRequest request(*m_engine, sp, viewport); + request.Run(); + + boost::python::list results; + for (auto const & result : request.Results()) + results.append(Result(result)); + return results; + } + shared_ptr m_engine; +}; +} // namespace + +BOOST_PYTHON_MODULE(search_engine_pylib) +{ + using namespace boost::python; + + def("init", &Init); + + class_("LatLon") + .def(init()) + .def_readwrite("lat", &LatLon::m_lat) + .def_readwrite("lon", &LatLon::m_lon) + .def("to_string", &LatLon::ToString); + + class_("Mercator") + .def(init()) + .def_readwrite("x", &Mercator::m_x) + .def_readwrite("y", &Mercator::m_y) + .def("to_string", &Mercator::ToString); + + class_("Viewport") + .def(init()) + .def_readwrite("min", &Viewport::m_min) + .def_readwrite("max", &Viewport::m_max) + .def("to_string", &Viewport::ToString); + + class_("Params") + .def_readwrite("query", &Params::m_query) + .def_readwrite("locale", &Params::m_locale) + .def_readwrite("lat_lon", &Params::m_latLon) + .def_readwrite("viewport", &Params::m_viewport) + .def("to_string", &Params::ToString); + + class_("Result") + .def_readwrite("name", &Result::m_name) + .def_readwrite("address", &Result::m_address) + .def_readwrite("has_center", &Result::m_hasCenter) + .def_readwrite("center", &Result::m_center) + .def("to_string", &Result::ToString); + + class_("SearchEngine").def("query", &SearchEngineProxy::Query); +} diff --git a/search/search_engine_pylib/search_engine_pylib.pro b/search/search_engine_pylib/search_engine_pylib.pro new file mode 100644 index 0000000000..469ca97d3e --- /dev/null +++ b/search/search_engine_pylib/search_engine_pylib.pro @@ -0,0 +1,54 @@ +TARGET = search_engine_pylib + +TEMPLATE = lib +CONFIG += plugin no_plugin_name_prefix + +QMAKE_LFLAGS_PLUGIN -= -dynamiclib +QMAKE_EXTENSION_SHLIB = so +QT *= core network + +ROOT_DIR = ../.. +include($$ROOT_DIR/common.pri) + +DEFINES += BOOST_PYTHON_DYNAMIC_LIB BOOST_BIND_NO_PLACEHOLDERS + +INCLUDEPATH -= $$ROOT_DIR/3party/boost + +# For Mac boost is built with libc++, for Linux with libstdc++. +# We do not support search_engine_pylib for other combinations of +# OS and c++ standard library. +macx-clang { + INCLUDEPATH += /System/Library/Frameworks/Python.framework/Versions/2.7/include/python2.7 + LIBS += -L/System/Library/Frameworks/Python.framework/Versions/2.7/lib -lpython2.7 + + INCLUDEPATH += /usr/local/include + LIBS += -L/usr/local/lib -lboost_python +} else:linux-clang { + INCLUDEPATH += /usr/include + LIBS += -L/usr/lib/x86_64-linux-gnu/ -lboost_python + + INCLUDEPATH += /usr/include/python2.7 + LIBS += -L/usr/lib -lpython2.7 +} else { + error("Can't build search_engine_pylib - wrong spec $$QMAKESPEC") +} + +LIBS += -lsearch_tests_support \ + -lsearch \ + -lstorage \ + -lindexer \ + -leditor \ + -lgeometry \ + -lplatform \ + -lcoding \ + -lbase \ + -lopening_hours \ + -ltomcrypt \ + -lsuccinct \ + -lpugixml \ + -lprotobuf \ + -ljansson \ + -loauthcpp \ + -lstats_client + +SOURCES += api.cpp \