diff --git a/base/internal/message.hpp b/base/internal/message.hpp index a5b55df979..0810045ad0 100644 --- a/base/internal/message.hpp +++ b/base/internal/message.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -78,7 +79,9 @@ inline std::string DebugPrint(unsigned char t) inline std::string DebugPrint(std::chrono::time_point const & ts) { auto t = std::chrono::system_clock::to_time_t(ts); - return std::ctime(&t); + std::string str = std::ctime(&t); + str.erase(std::remove(str.begin(), str.end(), '\n'), str.end()); + return str; } template inline std::string DebugPrint(std::pair const & p) diff --git a/kml/CMakeLists.txt b/kml/CMakeLists.txt index 97b0b4b43e..da1fc4754d 100644 --- a/kml/CMakeLists.txt +++ b/kml/CMakeLists.txt @@ -13,4 +13,6 @@ set( ) add_library(${PROJECT_NAME} ${SRC}) + +omim_add_pybindings_subdirectory(pykmlib) omim_add_test_subdirectory(kml_tests) diff --git a/kml/pykmlib/CMakeLists.txt b/kml/pykmlib/CMakeLists.txt new file mode 100644 index 0000000000..e4875e63d0 --- /dev/null +++ b/kml/pykmlib/CMakeLists.txt @@ -0,0 +1,23 @@ +project(pykmlib) + +set( + SRC + bindings.cpp +) + +add_library(${PROJECT_NAME} MODULE ${SRC}) + +omim_link_libraries( + ${PROJECT_NAME} + ${PYTHON_LIBRARIES} + ${Boost_LIBRARIES} + kml + platform + coding + geometry + base + expat + stats_client +) + +set_target_properties(${PROJECT_NAME} PROPERTIES PREFIX "") diff --git a/kml/pykmlib/bindings.cpp b/kml/pykmlib/bindings.cpp new file mode 100644 index 0000000000..070f252999 --- /dev/null +++ b/kml/pykmlib/bindings.cpp @@ -0,0 +1,753 @@ +#include "kml/serdes.hpp" +#include "kml/serdes_binary.hpp" + +#include "coding/multilang_utf8_string.hpp" + +#include "geometry/latlon.hpp" +#include "geometry/mercator.hpp" + +#include "base/assert.hpp" + +// This header should be included due to a python compilation error. +// pyport.h overwrites defined macros and replaces it with its own. +// However, in the OS X c++ libraries, these are not macros but functions, +// hence the error. See https://bugs.python.org/issue10910 +#include +#include +#include +#include +#include + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wreorder" +#pragma GCC diagnostic ignored "-Wunused-local-typedefs" +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-local-typedef" +#endif + +#include "pyhelpers/vector_uint8.hpp" +#include "pyhelpers/vector_list_conversion.hpp" + +#include +#include +#include +#include +#include + +#if defined(__clang__) +#pragma clang diagnostic pop +#endif +#pragma GCC diagnostic pop + +using namespace kml; +using namespace boost::python; + +namespace +{ +struct LocalizableStringAdapter +{ + static std::string const & Get(LocalizableString const & str, std::string const & lang) + { + auto const langIndex = StringUtf8Multilang::GetLangIndex(lang); + auto const it = str.find(langIndex); + if (it != str.end()) + return it->second; + throw std::runtime_error("Language not found"); + } + + static void Set(LocalizableString & str, std::string const & lang, std::string const & val) + { + auto const langIndex = StringUtf8Multilang::GetLangIndex(lang); + if (langIndex == StringUtf8Multilang::kUnsupportedLanguageCode) + throw std::runtime_error("Unsupported language"); + str[langIndex] = val; + } + + static void Delete(LocalizableString & str, std::string const & lang) + { + auto const langIndex = StringUtf8Multilang::GetLangIndex(lang); + auto const it = str.find(langIndex); + if (it != str.end()) + str.erase(it); + else + throw std::runtime_error("Language not found"); + } + + static std::string GetDefault(LocalizableString const & str) + { + return GetDefaultStr(str); + } + + static void SetDefault(LocalizableString & str, std::string const & val) + { + SetDefaultStr(str, val); + } + + static boost::python::dict GetDict(LocalizableString const & str) + { + boost::python::dict d; + for (auto const & s : str) + { + std::string lang = StringUtf8Multilang::GetLangByCode(s.first); + if (lang.empty()) + throw std::runtime_error("Language not found"); + d[lang] = s.second; + } + return d; + } + + static void SetDict(LocalizableString & str, boost::python::dict & dict) + { + str.clear(); + auto const langs = python_list_to_std_vector(dict.keys()); + for (auto const & lang : langs) + { + auto const langIndex = StringUtf8Multilang::GetLangIndex(lang); + if (langIndex == StringUtf8Multilang::kUnsupportedLanguageCode) + throw std::runtime_error("Unsupported language"); + + str[langIndex] = extract(dict[lang]); + } + } + + static std::string ToString(LocalizableString const & str) + { + std::ostringstream out; + out << "["; + for (auto it = str.begin(); it != str.end(); ++it) + { + out << "'"<< StringUtf8Multilang::GetLangByCode(it->first) << "':'" << it->second << "'"; + auto it2 = it; + it2++; + if (it2 != str.end()) + out << ", "; + } + out << "]"; + return out.str(); + } +}; + +struct PropertiesAdapter +{ + static std::string const & Get(Properties const & props, std::string const & key) + { + auto const it = props.find(key); + if (it != props.end()) + return it->second; + throw std::runtime_error("Property not found"); + } + + static void Set(Properties & props, std::string const & key, std::string const & val) + { + props[key] = val; + } + + static void Delete(Properties & props, std::string const & key) + { + auto const it = props.find(key); + if (it != props.end()) + props.erase(it); + else + throw std::runtime_error("Property not found"); + } + + static boost::python::dict GetDict(Properties const & props) + { + boost::python::dict d; + for (auto const & p : props) + d[p.first] = p.second; + return d; + } + + static void SetDict(Properties & props, boost::python::dict & dict) + { + props.clear(); + auto const keys = python_list_to_std_vector(dict.keys()); + for (auto const & k : keys) + props[k] = extract(dict[k]); + } + + static std::string ToString(Properties const & props) + { + std::ostringstream out; + out << "["; + for (auto it = props.begin(); it != props.end(); ++it) + { + out << "'"<< it->first << "':'" << it->second << "'"; + auto it2 = it; + it2++; + if (it2 != props.end()) + out << ", "; + } + out << "]"; + return out.str(); + } +}; + +template +struct VectorAdapter +{ + static boost::python::list Get(std::vector const & v) + { + return std_vector_to_python_list(v); + } + + static void Set(std::vector & v, boost::python::object const & iterable) + { + v = python_list_to_std_vector(iterable); + } + + static void PrintType(std::ostringstream & out, T const & t) + { + out << t; + } + + static std::string ToString(std::vector const & v) + { + std::ostringstream out; + out << "["; + for (size_t i = 0; i < v.size(); ++i) + { + PrintType(out, v[i]); + if (i + 1 != v.size()) + out << ", "; + } + out << "]"; + return out.str(); + } +}; + +template<> +void VectorAdapter::PrintType(std::ostringstream & out, uint8_t const & t) +{ + out << static_cast(t); +} + +template<> +void VectorAdapter::PrintType(std::ostringstream & out, std::string const & s) +{ + out << "'" << s << "'"; +} + +std::string TrackLayerToString(TrackLayer const & trackLayer); +template<> +void VectorAdapter::PrintType(std::ostringstream & out, TrackLayer const & t) +{ + out << TrackLayerToString(t); +} + +template<> +boost::python::list VectorAdapter::Get(std::vector const & points) +{ + std::vector latLonArray; + latLonArray.reserve(points.size()); + for (size_t i = 0; i < points.size(); ++i) + latLonArray.emplace_back(MercatorBounds::YToLat(points[i].y), MercatorBounds::XToLon(points[i].x)); + return std_vector_to_python_list(latLonArray); +} + +template<> +void VectorAdapter::Set(std::vector & v, boost::python::object const & iterable) +{ + auto const latLon = python_list_to_std_vector(iterable); + v.clear(); + v.reserve(latLon.size()); + for (size_t i = 0; i < latLon.size(); ++i) + v.emplace_back(MercatorBounds::LonToX(latLon[i].lon), MercatorBounds::LatToY(latLon[i].lat)); +} + +std::string LatLonToString(ms::LatLon const & latLon); +template<> +void VectorAdapter::PrintType(std::ostringstream & out, m2::PointD const & pt) +{ + ms::LatLon const latLon(MercatorBounds::YToLat(pt.y), MercatorBounds::XToLon(pt.x)); + out << LatLonToString(latLon); +} + +std::string BookmarkDataToString(BookmarkData const & bm); +template<> +void VectorAdapter::PrintType(std::ostringstream & out, BookmarkData const & bm) +{ + out << BookmarkDataToString(bm); +} + +std::string TrackDataToString(TrackData const & t); +template<> +void VectorAdapter::PrintType(std::ostringstream & out, TrackData const & t) +{ + out << TrackDataToString(t); +} + +std::string PredefinedColorToString(PredefinedColor c) +{ + switch (c) + { + case PredefinedColor::None: return "NONE"; + case PredefinedColor::Red: return "RED"; + case PredefinedColor::Blue: return "BLUE"; + case PredefinedColor::Purple: return "PURPLE"; + case PredefinedColor::Yellow: return "YELLOW"; + case PredefinedColor::Pink: return "PINK"; + case PredefinedColor::Brown: return "BROWN"; + case PredefinedColor::Green: return "GREEN"; + case PredefinedColor::Orange: return "ORANGE"; + case PredefinedColor::Count: + CHECK(false, ("Unknown predefined color")); + return {}; + } +} + +std::string AccessRulesToString(AccessRules accessRules) +{ + switch (accessRules) + { + case AccessRules::Private: return "PRIVATE"; + case AccessRules::Public: return "PUBLIC"; + case AccessRules::Count: + CHECK(false, ("Unknown access rules")); + return {}; + } +} + +std::string BookmarkIconToString(BookmarkIcon icon) +{ + switch (icon) + { + case BookmarkIcon::None: return "NONE"; + case BookmarkIcon::Count: + CHECK(false, ("Unknown bookmark icon")); + return {}; + } +} + +std::string ColorDataToString(ColorData const & c) +{ + std::ostringstream out; + out << "[" + << "predefined_color:" + << PredefinedColorToString(c.m_predefinedColor) + << ", " + << "rgba:" << c.m_rgba << "]"; + return out.str(); +} + +std::string LatLonToString(ms::LatLon const & latLon) +{ + std::ostringstream out; + out << "[" + << "lat:" << latLon.lat << ", " + << "lon:" << latLon.lon + << "]"; + return out.str(); +} + +std::string BookmarkDataToString(BookmarkData const & bm) +{ + std::ostringstream out; + ms::LatLon const latLon(MercatorBounds::YToLat(bm.m_point.y), MercatorBounds::XToLon(bm.m_point.x)); + out << "[" + << "name:" << LocalizableStringAdapter::ToString(bm.m_name) << ", " + << "description:" << LocalizableStringAdapter::ToString(bm.m_description) << ", " + << "feature_types:" << VectorAdapter::ToString(bm.m_featureTypes) << ", " + << "custom_name:" << LocalizableStringAdapter::ToString(bm.m_customName) << ", " + << "color:" << ColorDataToString(bm.m_color) << ", " + << "icon:" << BookmarkIconToString(bm.m_icon) << ", " + << "viewport_scale:" << static_cast(bm.m_viewportScale) << ", " + << "timestamp:" << DebugPrint(bm.m_timestamp) << ", " + << "point:" << LatLonToString(latLon) << ", " + << "bound_tracks:" << VectorAdapter::ToString(bm.m_boundTracks) + << "]"; + return out.str(); +} + +std::string TrackLayerToString(TrackLayer const & trackLayer) +{ + std::ostringstream out; + out << "[" + << "line_width:" << trackLayer.m_lineWidth << ", " + << "color:" << ColorDataToString(trackLayer.m_color) + << "]"; + return out.str(); +} + +std::string TrackDataToString(TrackData const & t) +{ + std::ostringstream out; + out << "[" + << "local_id:" << static_cast(t.m_localId) << ", " + << "name:" << LocalizableStringAdapter::ToString(t.m_name) << ", " + << "description:" << LocalizableStringAdapter::ToString(t.m_description) << ", " + << "timestamp:" << DebugPrint(t.m_timestamp) << ", " + << "layers:" << VectorAdapter::ToString(t.m_layers) << ", " + << "points:" << VectorAdapter::ToString(t.m_points) + << "]"; + return out.str(); +} + +std::string LanguagesListToString(std::vector const & langs) +{ + std::ostringstream out; + out << "["; + for (size_t i = 0; i < langs.size(); ++i) + { + out << "'" << StringUtf8Multilang::GetLangByCode(langs[i]) << "'"; + if (i + 1 != langs.size()) + out << ", "; + } + out << "]"; + return out.str(); +} + +std::string CategoryDataToString(CategoryData const & c) +{ + std::ostringstream out; + out << "[" + << "name:" << LocalizableStringAdapter::ToString(c.m_name) << ", " + << "annotation:" << LocalizableStringAdapter::ToString(c.m_annotation) << ", " + << "description:" << LocalizableStringAdapter::ToString(c.m_description) << ", " + << "image_url:'" << c.m_imageUrl << "', " + << "visible:" << (c.m_visible ? "True" : "False") << ", " + << "author_name:'" << c.m_authorName << "', " + << "author_id:'" << c.m_authorId << "', " + << "last_modified:" << DebugPrint(c.m_lastModified) << ", " + << "rating:" << c.m_rating << ", " + << "reviews_number:" << c.m_reviewsNumber << ", " + << "access_rules:" << AccessRulesToString(c.m_accessRules) << ", " + << "tags:" << VectorAdapter::ToString(c.m_tags) << ", " + << "cities:" << VectorAdapter::ToString(c.m_cities) << ", " + << "languages:" << LanguagesListToString(c.m_languageCodes) << ", " + << "properties:" << PropertiesAdapter::ToString(c.m_properties) + << "]"; + return out.str(); +} + +std::string FileDataToString(FileData const & c) +{ + std::ostringstream out; + out << "[" + << "category:" << CategoryDataToString(c.m_categoryData) << ", " + << "bookmarks:" << VectorAdapter::ToString(c.m_bookmarksData) << ", " + << "tracks:" << VectorAdapter::ToString(c.m_tracksData) + << "]"; + return out.str(); +} + +struct TimestampConverter +{ + TimestampConverter() + { + to_python_converter(); + converter::registry::push_back(&convertible, &construct, type_id()); + } + + static PyObject * convert(Timestamp const & timestamp) + { + return boost::python::incref(boost::python::object(ToSecondsSinceEpoch(timestamp)).ptr()); + } + + static void * convertible(PyObject * objPtr) + { + extract checker(objPtr); + if (!checker.check()) + return nullptr; + return objPtr; + } + + static void construct(PyObject * objPtr, converter::rvalue_from_python_stage1_data * data) + { + auto const ts = FromSecondsSinceEpoch(extract(objPtr)); + void * storage = + reinterpret_cast *>(data)->storage.bytes; + new (storage) Timestamp(ts); + data->convertible = storage; + } +}; + +struct LatLonConverter +{ + LatLonConverter() + { + to_python_converter(); + converter::registry::push_back(&convertible, &construct, type_id()); + } + + static PyObject * convert(m2::PointD const & pt) + { + ms::LatLon latLon(MercatorBounds::YToLat(pt.y), MercatorBounds::XToLon(pt.x)); + return boost::python::incref(boost::python::object(latLon).ptr()); + } + + static void * convertible(PyObject * objPtr) + { + extract checker(objPtr); + if (!checker.check()) + return nullptr; + return objPtr; + } + + static void construct(PyObject * objPtr, converter::rvalue_from_python_stage1_data * data) + { + ms::LatLon latLon = extract(objPtr); + m2::PointD pt(MercatorBounds::LonToX(latLon.lon), MercatorBounds::LatToY(latLon.lat)); + void * storage = + reinterpret_cast *>(data)->storage.bytes; + new (storage) m2::PointD(pt); + data->convertible = storage; + } +}; + +void TranslateRuntimeError(std::runtime_error const & e) +{ + PyErr_SetString(PyExc_RuntimeError, e.what()); +} + +boost::python::list GetLanguages(std::vector const & langs) +{ + std::vector result; + result.reserve(langs.size()); + for (auto const & langCode : langs) + { + std::string lang = StringUtf8Multilang::GetLangByCode(langCode); + if (lang.empty()) + throw std::runtime_error("Language not found"); + result.emplace_back(std::move(lang)); + } + return std_vector_to_python_list(result); +} + +void SetLanguages(std::vector & langs, boost::python::object const & iterable) +{ + auto const langStr = python_list_to_std_vector(iterable); + langs.clear(); + langs.reserve(langStr.size()); + for (auto const & lang : langStr) + { + auto const langIndex = StringUtf8Multilang::GetLangIndex(lang); + if (langIndex == StringUtf8Multilang::kUnsupportedLanguageCode) + throw std::runtime_error("Unsupported language"); + langs.emplace_back(langIndex); + } +} + +boost::python::list GetSupportedLanguages() +{ + auto const & supportedLangs = StringUtf8Multilang::GetSupportedLanguages(); + std::vector langs; + langs.reserve(supportedLangs.size()); + for (auto const & lang : supportedLangs) + langs.emplace_back(lang.m_code); + return std_vector_to_python_list(langs); +} + +boost::python::object GetLanguageIndex(std::string const & lang) +{ + auto const langIndex = StringUtf8Multilang::GetLangIndex(lang); + if (langIndex == StringUtf8Multilang::kUnsupportedLanguageCode) + throw std::runtime_error("Unsupported language"); + return boost::python::object(langIndex); +} + +std::string ExportKml(FileData const & fileData) +{ + std::string resultBuffer; + { + MemWriter sink(resultBuffer); + kml::SerializerKml ser(fileData); + ser.Serialize(sink); + } + return resultBuffer; +} + +FileData ImportKml(std::string const & str) +{ + kml::FileData data; + try + { + kml::DeserializerKml des(data); + MemReader reader(str.data(), str.size()); + des.Deserialize(reader); + } + catch (kml::DeserializerKml::DeserializeException const & exc) + { + throw std::runtime_error(std::string("Import error: ") + exc.what()); + } + return data; +} +} // namespace + +BOOST_PYTHON_MODULE(pykmlib) +{ + register_exception_translator(&TranslateRuntimeError); + TimestampConverter(); + LatLonConverter(); + + enum_("PredefinedColor") + .value(PredefinedColorToString(PredefinedColor::None).c_str(), PredefinedColor::None) + .value(PredefinedColorToString(PredefinedColor::Red).c_str(), PredefinedColor::Red) + .value(PredefinedColorToString(PredefinedColor::Blue).c_str(), PredefinedColor::Blue) + .value(PredefinedColorToString(PredefinedColor::Purple).c_str(), PredefinedColor::Purple) + .value(PredefinedColorToString(PredefinedColor::Yellow).c_str(), PredefinedColor::Yellow) + .value(PredefinedColorToString(PredefinedColor::Pink).c_str(), PredefinedColor::Pink) + .value(PredefinedColorToString(PredefinedColor::Brown).c_str(), PredefinedColor::Brown) + .value(PredefinedColorToString(PredefinedColor::Green).c_str(), PredefinedColor::Green) + .value(PredefinedColorToString(PredefinedColor::Orange).c_str(), PredefinedColor::Orange) + .export_values(); + + enum_("AccessRules") + .value(AccessRulesToString(AccessRules::Private).c_str(), AccessRules::Private) + .value(AccessRulesToString(AccessRules::Public).c_str(), AccessRules::Public) + .export_values(); + + enum_("BookmarkIcon") + .value(BookmarkIconToString(BookmarkIcon::None).c_str(), BookmarkIcon::None) + .export_values(); + + class_("ColorData") + .def_readwrite("predefined_color", &ColorData::m_predefinedColor) + .def_readwrite("rgba", &ColorData::m_rgba) + .def("__eq__", &ColorData::operator==) + .def("__ne__", &ColorData::operator!=) + .def("__str__", &ColorDataToString); + + class_("LocalizableString") + .def("__len__", &LocalizableString::size) + .def("clear", &LocalizableString::clear) + .def("__getitem__", &LocalizableStringAdapter::Get, return_value_policy()) + .def("__setitem__", &LocalizableStringAdapter::Set, with_custodian_and_ward<1,2>()) + .def("__delitem__", &LocalizableStringAdapter::Delete) + .def("get_default", &LocalizableStringAdapter::GetDefault) + .def("set_default", &LocalizableStringAdapter::SetDefault) + .def("get_dict", &LocalizableStringAdapter::GetDict) + .def("set_dict", &LocalizableStringAdapter::SetDict) + .def("__str__", &LocalizableStringAdapter::ToString); + + class_>("StringList") + .def(vector_indexing_suite>()) + .def("get_list", &VectorAdapter::Get) + .def("set_list", &VectorAdapter::Set) + .def("__str__", &VectorAdapter::ToString); + + class_>("Uint32List") + .def(vector_indexing_suite>()) + .def("get_list", &VectorAdapter::Get) + .def("set_list", &VectorAdapter::Set) + .def("__str__", &VectorAdapter::ToString); + + class_>("Uint8List") + .def(vector_indexing_suite>()) + .def("get_list", &VectorAdapter::Get) + .def("set_list", &VectorAdapter::Set) + .def("__str__", &VectorAdapter::ToString); + + class_("LatLon", init()) + .def_readwrite("lat", &ms::LatLon::lat) + .def_readwrite("lon", &ms::LatLon::lon) + .def("__str__", &LatLonToString); + + class_("BookmarkData") + .def_readwrite("name", &BookmarkData::m_name) + .def_readwrite("description", &BookmarkData::m_description) + .def_readwrite("feature_types", &BookmarkData::m_featureTypes) + .def_readwrite("custom_name", &BookmarkData::m_customName) + .def_readwrite("color", &BookmarkData::m_color) + .def_readwrite("icon", &BookmarkData::m_icon) + .def_readwrite("viewport_scale", &BookmarkData::m_viewportScale) + .def_readwrite("timestamp", &BookmarkData::m_timestamp) + .def_readwrite("point", &BookmarkData::m_point) + .def_readwrite("bound_tracks", &BookmarkData::m_boundTracks) + .def("__eq__", &BookmarkData::operator==) + .def("__ne__", &BookmarkData::operator!=) + .def("__str__", &BookmarkDataToString); + + class_("TrackLayer") + .def_readwrite("line_width", &TrackLayer::m_lineWidth) + .def_readwrite("color", &TrackLayer::m_color) + .def("__eq__", &TrackLayer::operator==) + .def("__ne__", &TrackLayer::operator!=) + .def("__str__", &TrackLayerToString); + + class_>("TrackLayerList") + .def(vector_indexing_suite>()) + .def("get_list", &VectorAdapter::Get) + .def("set_list", &VectorAdapter::Set) + .def("__str__", &VectorAdapter::ToString); + + class_>("PointDList") + .def(vector_indexing_suite>()) + .def("get_list", &VectorAdapter::Get) + .def("set_list", &VectorAdapter::Set) + .def("__str__", &VectorAdapter::ToString); + + class_("TrackData") + .def_readwrite("local_id", &TrackData::m_localId) + .def_readwrite("name", &TrackData::m_name) + .def_readwrite("description", &TrackData::m_description) + .def_readwrite("timestamp", &TrackData::m_timestamp) + .def_readwrite("layers", &TrackData::m_layers) + .def_readwrite("points", &TrackData::m_points) + .def("__eq__", &TrackData::operator==) + .def("__ne__", &TrackData::operator!=) + .def("__str__", &TrackDataToString); + + class_>("LanguageList") + .def(vector_indexing_suite>()) + .def("get_list", &GetLanguages) + .def("set_list", &SetLanguages) + .def("__str__", &LanguagesListToString); + + class_("Properties") + .def("__len__", &Properties::size) + .def("clear", &Properties::clear) + .def("__getitem__", &PropertiesAdapter::Get, return_value_policy()) + .def("__setitem__", &PropertiesAdapter::Set, with_custodian_and_ward<1,2>()) + .def("__delitem__", &PropertiesAdapter::Delete) + .def("get_dict", &PropertiesAdapter::GetDict) + .def("set_dict", &PropertiesAdapter::SetDict) + .def("__str__", &PropertiesAdapter::ToString); + + class_("CategoryData") + .def_readwrite("name", &CategoryData::m_name) + .def_readwrite("annotation", &CategoryData::m_annotation) + .def_readwrite("description", &CategoryData::m_description) + .def_readwrite("image_url", &CategoryData::m_imageUrl) + .def_readwrite("visible", &CategoryData::m_visible) + .def_readwrite("author_name", &CategoryData::m_authorName) + .def_readwrite("author_id", &CategoryData::m_authorId) + .def_readwrite("last_modified", &CategoryData::m_lastModified) + .def_readwrite("rating", &CategoryData::m_rating) + .def_readwrite("reviews_number", &CategoryData::m_reviewsNumber) + .def_readwrite("access_rules", &CategoryData::m_accessRules) + .def_readwrite("tags", &CategoryData::m_tags) + .def_readwrite("cities", &CategoryData::m_cities) + .def_readwrite("languages", &CategoryData::m_languageCodes) + .def_readwrite("properties", &CategoryData::m_properties) + .def("__eq__", &CategoryData::operator==) + .def("__ne__", &CategoryData::operator!=) + .def("__str__", &CategoryDataToString); + + class_>("BookmarkList") + .def(vector_indexing_suite>()) + .def("get_list", &VectorAdapter::Get) + .def("set_list", &VectorAdapter::Set) + .def("__str__", &VectorAdapter::ToString); + + class_>("TrackList") + .def(vector_indexing_suite>()) + .def("get_list", &VectorAdapter::Get) + .def("set_list", &VectorAdapter::Set) + .def("__str__", &VectorAdapter::ToString); + + class_("FileData") + .def_readwrite("category", &FileData::m_categoryData) + .def_readwrite("bookmarks", &FileData::m_bookmarksData) + .def_readwrite("tracks", &FileData::m_tracksData) + .def("__eq__", &FileData::operator==) + .def("__ne__", &FileData::operator!=) + .def("__str__", &FileDataToString); + + def("get_supported_languages", GetSupportedLanguages); + def("get_language_index", GetLanguageIndex); + def("export_kml", ExportKml); + def("import_kml", ImportKml); +} diff --git a/kml/pykmlib/bindings_test.py b/kml/pykmlib/bindings_test.py new file mode 100644 index 0000000000..8fb8e36f49 --- /dev/null +++ b/kml/pykmlib/bindings_test.py @@ -0,0 +1,75 @@ +import unittest +import datetime +import pykmlib + +class PyKmlibAdsTest(unittest.TestCase): + + def test_smoke(self): + category = pykmlib.CategoryData() + category.name.set_default('Test category') + category.name['ru'] = 'Тестовая категория' + category.description.set_default('Test description') + category.description['ru'] = 'Тестовое описание' + category.annotation.set_default('Test annotation') + category.annotation['en'] = 'Test annotation' + category.image_url = 'https://localhost/123.png' + category.visible = True + category.author_name = 'Maps.Me' + category.author_id = '12345' + category.rating = 8.9 + category.reviews_number = 567 + category.last_modified = int(datetime.datetime.now().timestamp()) + category.access_rules = pykmlib.AccessRules.PUBLIC + category.tags.set_list(['mountains', 'ski', 'snowboard']) + category.cities.set_list([pykmlib.LatLon(35.2424, 56.2164), pykmlib.LatLon(34.2443, 46.3536)]) + category.languages.set_list(['en', 'ru', 'de']) + category.properties.set_dict({'property1':'value1', 'property2':'value2'}) + + bookmark = pykmlib.BookmarkData() + bookmark.name.set_default('Test bookmark') + bookmark.name['ru'] = 'Тестовая метка' + bookmark.description.set_default('Test bookmark description') + bookmark.description['ru'] = 'Тестовое описание метки' + bookmark.feature_types.set_list([8, 13, 34, 565]) + bookmark.custom_name.set_default('Мое любимое место') + bookmark.custom_name['en'] = 'My favorite place' + bookmark.color.predefined_color = pykmlib.PredefinedColor.BLUE + bookmark.color.rgba = 0 + bookmark.icon = pykmlib.BookmarkIcon.NONE + bookmark.viewport_scale = 15 + bookmark.timestamp = int(datetime.datetime.now().timestamp()) + bookmark.point = pykmlib.LatLon(45.9242, 56.8679) + bookmark.bound_tracks.set_list([0]) + + layer1 = pykmlib.TrackLayer() + layer1.line_width = 6.0 + layer1.color.rgba = 0xff0000ff + layer2 = pykmlib.TrackLayer() + layer2.line_width = 7.0 + layer2.color.rgba = 0x00ff00ff + + track = pykmlib.TrackData() + track.local_id = 1 + track.name.set_default('Test track') + track.name['ru'] = 'Тестовый трек' + track.description.set_default('Test track description') + track.description['ru'] = 'Тестовое описание трека' + track.timestamp = int(datetime.datetime.now().timestamp()) + track.layers.set_list([layer1, layer2]) + track.points.set_list([ + pykmlib.LatLon(45.9242, 56.8679), + pykmlib.LatLon(45.2244, 56.2786), + pykmlib.LatLon(45.1964, 56.9832)]) + + file_data = pykmlib.FileData() + file_data.category = category + file_data.bookmarks.append(bookmark) + file_data.tracks.append(track) + + s = pykmlib.export_kml(file_data) + imported_file_data = pykmlib.import_kml(s) + self.assertEqual(file_data, imported_file_data) + + +if __name__ == "__main__": + unittest.main()