Merge pull request #4197 from ygorshenin/search-engine-pylib

[search] Search engine pylib
This commit is contained in:
mpimenov 2016-09-05 13:21:05 +03:00 committed by GitHub
commit f2dc446a27
5 changed files with 403 additions and 0 deletions

View file

@ -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

View file

@ -0,0 +1,37 @@
This document describes how to use this module.
1. How to build?
To build the module you need Python2.7 and Boost Python. Also, you
need Qt5.5 (or higher), but you need it in any case, if you're
planning to build the project. On MacOS, Python2.7 should be
installed by default, to get Boost via Brew just type in the shell:
brew update
brew install boost --with-python
brew install boost-python
On Debian, type in the shell:
sudo apt-get install libboost-*
Note that on MacOS Boost is built by default with libc++, on Debian
Boost is built by default with libstdc++. Therefore, you can use
only macx-clang and linux-clang specs correspondingly. It's wrong
to use linux-clang-libc++ because it's generally a bad idea to have
two implementations of the C++ standard library in the same
application.
Then, invoke qmake from the shell, for example:
qmake CONFIG+=search_engine_pylib path-to-omim.pro
make -k -j8 all
2. How to use?
As search_engine_pylib is a custom Python module, all that you need
is to customize PYTHONPATH environment variable before running your
scripts. For example:
PYTHONPATH=path-to-the-directory-with-search_engine_pylib.so \
./search/search_engine_pylib/run_search_engine.py

View file

@ -0,0 +1,255 @@
#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 <boost/python.hpp>
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<platform::LocalCountryFile> const & /* localFile */)
{
}
bool WillDelete(storage::TCountryId const & /* countryId */,
shared_ptr<platform::LocalCountryFile> const & /* localFile */)
{
return false;
}
unique_ptr<storage::Storage> 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<storage::Storage>(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() was not called."));
auto & platform = GetPlatform();
auto infoGetter = storage::CountryInfoReader::CreateCountryInfoReader(platform);
infoGetter->InitAffiliationsInfo(&g_storage->GetAffiliations());
m_engine = make_shared<search::tests_support::TestSearchEngine>(
move(infoGetter), make_unique<search::ProcessorFactory>(), search::Engine::Params{});
vector<platform::LocalCountryFile> mwms;
platform::FindAllLocalMapsAndCleanup(numeric_limits<int64_t>::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<search::tests_support::TestSearchEngine> m_engine;
};
} // namespace
BOOST_PYTHON_MODULE(search_engine_pylib)
{
using namespace boost::python;
def("init", &Init);
class_<LatLon>("LatLon")
.def(init<double, double>())
.def_readwrite("lat", &LatLon::m_lat)
.def_readwrite("lon", &LatLon::m_lon)
.def("to_string", &LatLon::ToString);
class_<Mercator>("Mercator")
.def(init<double, double>())
.def_readwrite("x", &Mercator::m_x)
.def_readwrite("y", &Mercator::m_y)
.def("to_string", &Mercator::ToString);
class_<Viewport>("Viewport")
.def(init<Mercator const &, Mercator const &>())
.def_readwrite("min", &Viewport::m_min)
.def_readwrite("max", &Viewport::m_max)
.def("to_string", &Viewport::ToString);
class_<Params>("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>("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_<SearchEngineProxy>("SearchEngine").def("query", &SearchEngineProxy::Query);
}

View file

@ -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())

View file

@ -0,0 +1,75 @@
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 {
QMAKE_LFLAGS_PLUGIN += -bundle
LIBRARY_PYTHON=/Library/Frameworks/Python.framework/Versions/2.7
SYSTEM_LIBRARY_PYTHON=/System/Library/Frameworks/Python.framework/Versions/2.7
exists($$LIBRARY_PYTHON) {
INCLUDEPATH += $$LIBRARY_PYTHON/include/python2.7
LIBS += -L$$LIBRARY_PYTHON/lib -lpython2.7
} else:exists($$SYSTEM_LIBRARY_PYTHON) {
INCLUDEPATH += $$SYSTEM_LIBRARY_PYTHON/include/python2.7
LIBS += -L$$SYSTEM_LIBRARY_PYTHON/lib -lpython2.7
} else {
error("Can't find Python2.7")
}
LIBS *= "-framework IOKit"
LIBS *= "-framework SystemConfiguration"
LIBS *= -L/usr/local/opt/qt5/lib
LIBS *= "-framework QtCore"
LIBS *= "-framework QtNetwork"
INCLUDEPATH += /usr/local/include
LIBS += -L/usr/local/lib -lboost_python
INCLUDEPATH += /usr/local/opt/qt5/include
} 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 \