From 77059ed4fcf2b5dd32c7c8eb15010ff2c3677e9f Mon Sep 17 00:00:00 2001 From: Sergey Magidovich Date: Tue, 1 Mar 2016 13:48:18 +0300 Subject: [PATCH] Add editor config. --- coding/reader.hpp | 4 +- editor/editor.pro | 2 + editor/editor_config.cpp | 159 +++++++++++++ editor/editor_config.hpp | 74 ++++++ editor/editor_tests/editor_config_test.cpp | 55 +++++ editor/editor_tests/editor_tests.pro | 7 +- editor/xml_feature.cpp | 2 - indexer/editable_map_object.hpp | 9 + indexer/feature_data.cpp | 8 + indexer/feature_data.hpp | 2 + indexer/osm_editor.cpp | 214 +----------------- indexer/osm_editor.hpp | 6 +- omim.pro | 2 +- search/query_saver.cpp | 4 +- .../search_quality_tests.pro | 7 +- 15 files changed, 335 insertions(+), 220 deletions(-) create mode 100644 editor/editor_config.cpp create mode 100644 editor/editor_config.hpp create mode 100644 editor/editor_tests/editor_config_test.cpp diff --git a/coding/reader.hpp b/coding/reader.hpp index f97217eaae..bfc03cc797 100644 --- a/coding/reader.hpp +++ b/coding/reader.hpp @@ -20,12 +20,10 @@ public: DECLARE_EXCEPTION(SizeException, Exception); DECLARE_EXCEPTION(ReadException, Exception); - using TReaderPtr = unique_ptr; - virtual ~Reader() {} virtual uint64_t Size() const = 0; virtual void Read(uint64_t pos, void * p, size_t size) const = 0; - virtual TReaderPtr CreateSubReader(uint64_t pos, uint64_t size) const = 0; + virtual unique_ptr CreateSubReader(uint64_t pos, uint64_t size) const = 0; void ReadAsString(string & s) const; diff --git a/editor/editor.pro b/editor/editor.pro index 3313a91da0..26adbfe0b1 100644 --- a/editor/editor.pro +++ b/editor/editor.pro @@ -10,6 +10,7 @@ include($$ROOT_DIR/common.pri) SOURCES += \ changeset_wrapper.cpp \ + editor_config.cpp \ opening_hours_ui.cpp \ osm_auth.cpp \ osm_feature_matcher.cpp \ @@ -19,6 +20,7 @@ SOURCES += \ HEADERS += \ changeset_wrapper.hpp \ + editor_config.hpp \ new_feature_categories.hpp \ opening_hours_ui.hpp \ osm_auth.hpp \ diff --git a/editor/editor_config.cpp b/editor/editor_config.cpp new file mode 100644 index 0000000000..214e5524a5 --- /dev/null +++ b/editor/editor_config.cpp @@ -0,0 +1,159 @@ +#include "editor/editor_config.hpp" + +#include "indexer/classificator.hpp" + +#include "platform/platform.hpp" + +#include "coding/reader.hpp" + +#include "std/algorithm.hpp" +#include "std/cstring.hpp" +#include "std/unordered_map.hpp" + +namespace +{ +using EType = feature::Metadata::EType; + +// TODO(mgsergio): It would be nice to have this map generated from editor.xml. +static unordered_map const kNamesToFMD= { + {"cuisine", feature::Metadata::FMD_CUISINE}, + {"opening_hours", feature::Metadata::FMD_OPEN_HOURS}, + {"phone", feature::Metadata::FMD_PHONE_NUMBER}, + {"fax", feature::Metadata::FMD_FAX_NUMBER}, + {"stars", feature::Metadata::FMD_STARS}, + {"operator", feature::Metadata::FMD_OPERATOR}, + // {"", feature::Metadata::FMD_URL}, + {"website", feature::Metadata::FMD_WEBSITE}, + {"internet", feature::Metadata::FMD_INTERNET}, + {"ele", feature::Metadata::FMD_ELE}, + // {"", feature::Metadata::FMD_TURN_LANES}, + // {"", feature::Metadata::FMD_TURN_LANES_FORWARD}, + // {"", feature::Metadata::FMD_TURN_LANES_BACKWARD}, + {"email", feature::Metadata::FMD_EMAIL}, + {"postcode", feature::Metadata::FMD_POSTCODE}, + {"wikipedia", feature::Metadata::FMD_WIKIPEDIA}, + // {"", feature::Metadata::FMD_MAXSPEED}, + {"flats", feature::Metadata::FMD_FLATS}, + {"height", feature::Metadata::FMD_HEIGHT}, + // {"", feature::Metadata::FMD_MIN_HEIGHT}, + {"denomination", feature::Metadata::FMD_DENOMINATION}, + {"building_levels", feature::Metadata::FMD_BUILDING_LEVELS} + // description +}; + +editor::TypeAggregatedDescription TypeDescriptionFromXml(pugi::xml_node const & root, + pugi::xml_node const & node) +{ + if (!node || strcmp(node.attribute("editable").value(), "no") == 0) + return {}; + + bool name = false; + bool address = false; + editor::TypeAggregatedDescription::TFeatureFields editableFields; + + auto const handleField = + [&name, &address, &editableFields](string const & fieldName) + { + if (fieldName == "name") + { + name = true; + return; + } + + if (fieldName == "street" || fieldName == "housenumber") + { + address = true; + return; + } + + auto const it = kNamesToFMD.find(fieldName); + ASSERT(it != end(kNamesToFMD), ("Wrong field:", fieldName)); + editableFields.insert(it->second); + }; + + for (auto const xNode : node.select_nodes("include[@group]")) + { + auto const node = xNode.node(); + string const groupName = node.attribute("group").value(); + + string const xpath = "/mapsme/editor/fields/field_group[@name='" + groupName + "']"; + auto const group = root.select_node(xpath.data()).node(); + ASSERT(group, ("No such group", groupName)); + + for (auto const fieldRefXName : group.select_nodes("field_ref/@name")) + { + auto const fieldName = fieldRefXName.attribute().value(); + handleField(fieldName); + } + } + + for (auto const xNode : node.select_nodes("include[@field]")) + { + auto const node = xNode.node(); + string const fieldName = node.attribute("field").value(); + handleField(fieldName); + } + + return {editableFields, name, address}; +} + +/// The priority is definde by elems order, except elemts with priority="high". +vector GetPrioritizedTypes(pugi::xml_node const & node) +{ + vector result; + for (auto const xNode : node.select_nodes("/mapsme/editor/types/type[@id]")) + result.push_back(xNode.node()); + stable_sort(begin(result), end(result), + [](pugi::xml_node const & a, pugi::xml_node const & b) + { + if (strcmp(a.attribute("priority").value(), "high") != 0 && + strcmp(b.attribute("priority").value(), "high") == 0) + return true; + return false; + }); + return result; +} +} // namespace + +namespace editor +{ +EditorConfig::EditorConfig(string const & fileName) + : m_fileName(fileName) +{ + Reload(); +} + +TypeAggregatedDescription +EditorConfig::GetTypeDescription(vector const & classificatorTypes) const +{ + auto const typeNodes = GetPrioritizedTypes(m_document); + + auto const it = find_if(begin(typeNodes), end(typeNodes), + [&classificatorTypes](pugi::xml_node const & node) + { + return find(begin(classificatorTypes), end(classificatorTypes), + node.attribute("id").value()) != end(classificatorTypes); + }); + + ASSERT(it != end(typeNodes), ("Cannot find any matching type in config")); + return TypeDescriptionFromXml(m_document, *it); +} + +vector EditorConfig::GetTypesThatCanBeAdded() const +{ + auto const xpathResult = m_document.select_nodes("/mapsme/editor/types/type[not(@can_add='no')]"); + vector result; + for (auto const xNode : xpathResult) + result.emplace_back(xNode.node().attribute("id").value()); + return result; +} + +void EditorConfig::Reload() +{ + string content; + auto const reader = GetPlatform().GetReader(m_fileName); + reader->ReadAsString(content); + if (!m_document.load_buffer(content.data(), content.size())) + MYTHROW(ConfigLoadError, ("Can't parse config")); +} +} // namespace editor diff --git a/editor/editor_config.hpp b/editor/editor_config.hpp new file mode 100644 index 0000000000..c817d7e622 --- /dev/null +++ b/editor/editor_config.hpp @@ -0,0 +1,74 @@ +#pragma once + +#include "indexer/feature_meta.hpp" + +#include "base/exception.hpp" + +#include "std/set.hpp" +#include "std/string.hpp" +#include "std/unique_ptr.hpp" +#include "std/vector.hpp" + +#include "3party/pugixml/src/pugixml.hpp" + +class Reader; + +namespace editor +{ +class TypeAggregatedDescription +{ +public: + using EType = feature::Metadata::EType; + using TFeatureFields = set; + + TypeAggregatedDescription(TFeatureFields const & editableFields, + bool const name, bool const address) + : m_editableFields(editableFields), + m_name(name), + m_address(address) + { + } + + TypeAggregatedDescription() + : m_editableFields(), + m_name(false), + m_address(false) + { + } + + bool IsEmpty() const + { + return IsNameEditable() || IsAddressEditable() || !m_editableFields.empty(); + } + + TFeatureFields const & GetEditableFields() const { return m_editableFields; } + + bool IsNameEditable() const { return m_name; }; + bool IsAddressEditable() const { return m_address; } + +private: + TFeatureFields m_editableFields; + + bool m_name; + bool m_address; +}; + +DECLARE_EXCEPTION(ConfigLoadError, RootException); + +class EditorConfig +{ +public: + EditorConfig(string const & fileName); + + TypeAggregatedDescription GetTypeDescription(vector const & classificatorTypes) const; + vector GetTypesThatCanBeAdded() const; + + bool EditingEnable() const; + + void Reload(); + +private: + string const m_fileName; + pugi::xml_document m_document; +}; +} // namespace editor diff --git a/editor/editor_tests/editor_config_test.cpp b/editor/editor_tests/editor_config_test.cpp new file mode 100644 index 0000000000..8a059e3b90 --- /dev/null +++ b/editor/editor_tests/editor_config_test.cpp @@ -0,0 +1,55 @@ +#include "testing/testing.hpp" + +#include "editor/editor_config.hpp" + +#include "std/set.hpp" + +using namespace editor; + +UNIT_TEST(EditorConfig_TypeDescription) +{ + using EType = feature::Metadata::EType; + + set const poi = { + feature::Metadata::FMD_OPEN_HOURS, + feature::Metadata::FMD_PHONE_NUMBER, + feature::Metadata::FMD_WEBSITE, + feature::Metadata::FMD_EMAIL + }; + + EditorConfig config("editor.xml"); + + { + auto const desc = config.GetTypeDescription({"amenity-hunting_stand"}); + TEST(desc.IsNameEditable(), ()); + TEST(!desc.IsAddressEditable(), ()); + TEST_EQUAL(desc.GetEditableFields(), (set{EType::FMD_HEIGHT}), ()); + } + { + auto const desc = config.GetTypeDescription({"shop-toys"}); + TEST(desc.IsNameEditable(), ()); + TEST(desc.IsAddressEditable(), ()); + auto fields = poi; + fields.insert(EType::FMD_INTERNET); + TEST_EQUAL(desc.GetEditableFields(), fields, ()); + } + { + // Select ameniry-bank cause it goes fierst in config + auto const desc = config.GetTypeDescription({"amenity-bar", "amenity-bank"}); + TEST(desc.IsNameEditable(), ()); + TEST(desc.IsAddressEditable(), ()); + auto fields = poi; + fields.insert(EType::FMD_OPERATOR); + TEST_EQUAL(desc.GetEditableFields(), fields, ()); + } + // TODO(mgsergio): Test case with priority="high" when there is one on editor.xml. +} + +UNIT_TEST(EditorConfig_GetTypesThatGenBeAdded) +{ + EditorConfig config("editor.xml"); + + auto const types = config.GetTypesThatCanBeAdded(); + TEST(find(begin(types), end(types), "amenity-cafe") != end(types), ()); + TEST(find(begin(types), end(types), "natural-peak") == end(types), ()); +} diff --git a/editor/editor_tests/editor_tests.pro b/editor/editor_tests/editor_tests.pro index 07c62a11d0..93a6ee6426 100644 --- a/editor/editor_tests/editor_tests.pro +++ b/editor/editor_tests/editor_tests.pro @@ -14,9 +14,10 @@ HEADERS += \ SOURCES += \ $$ROOT_DIR/testing/testingmain.cpp \ + editor_config_test.cpp \ opening_hours_ui_test.cpp \ - server_api_test.cpp \ - xml_feature_test.cpp \ - ui2oh_test.cpp \ osm_auth_test.cpp \ osm_feature_matcher_test.cpp \ + server_api_test.cpp \ + ui2oh_test.cpp \ + xml_feature_test.cpp \ diff --git a/editor/xml_feature.cpp b/editor/xml_feature.cpp index c94e8e0e32..30e5332edf 100644 --- a/editor/xml_feature.cpp +++ b/editor/xml_feature.cpp @@ -6,8 +6,6 @@ #include "geometry/latlon.hpp" -#include "3party/pugixml/src/pugixml.hpp" - namespace { constexpr char const * kTimestamp = "timestamp"; diff --git a/indexer/editable_map_object.hpp b/indexer/editable_map_object.hpp index 5805555dcf..526c08af73 100644 --- a/indexer/editable_map_object.hpp +++ b/indexer/editable_map_object.hpp @@ -17,6 +17,15 @@ namespace osm /// Holds information to construct editor's UI. struct EditableProperties { + EditableProperties() = default; + EditableProperties(vector const & metadata, + bool name, bool address) + : m_name(name), + m_address(address), + m_metadata(metadata) + { + } + bool m_name = false; /// If true, enables editing of house number, street address and post code. bool m_address = false; diff --git a/indexer/feature_data.cpp b/indexer/feature_data.cpp index 435b2b824f..53492f84a9 100644 --- a/indexer/feature_data.cpp +++ b/indexer/feature_data.cpp @@ -166,6 +166,14 @@ void TypesHolder::SortBySpec() (void) RemoveIfKeepValid(m_types, m_types + m_size, bind(cref(checker), _1)); } +vector TypesHolder::ToObjectNames() const +{ + vector result; + for (auto type : *this) + result.push_back(classif().GetReadableObjectName(type)); + return result; +} + //////////////////////////////////////////////////////////////////////////////////// // FeatureParamsBase implementation //////////////////////////////////////////////////////////////////////////////////// diff --git a/indexer/feature_data.hpp b/indexer/feature_data.hpp index 75e6d07e4c..901c67b216 100644 --- a/indexer/feature_data.hpp +++ b/indexer/feature_data.hpp @@ -115,6 +115,8 @@ namespace feature /// Returns true if this->m_types and other.m_types contain same values /// in any order. Works in O(n log n). bool Equals(TypesHolder const & other) const; + + vector ToObjectNames() const; }; string DebugPrint(TypesHolder const & holder); diff --git a/indexer/osm_editor.cpp b/indexer/osm_editor.cpp index 9fb7069bdc..570b0eee9b 100644 --- a/indexer/osm_editor.cpp +++ b/indexer/osm_editor.cpp @@ -65,168 +65,6 @@ bool NeedsUpload(string const & uploadStatus) } string GetEditorFilePath() { return GetPlatform().WritablePathForFile(kEditorXMLFileName); } -// TODO(mgsergio): Replace hard-coded value with reading from file. -/// type:string -> description:pair, editName:bool, editAddr:bool> - -using EType = feature::Metadata::EType; -using TEditableFields = vector; - -struct TypeDescription -{ - TEditableFields const m_fields; - bool const m_name; - // Address == true implies Street, House Number, Phone, Fax, Opening Hours, Website, EMail, Postcode. - bool const m_address; -}; - -static unordered_map const gEditableTypes = { - {"aeroway-aerodrome", {{EType::FMD_ELE, EType::FMD_OPERATOR}, false, true}}, - {"aeroway-airport", {{EType::FMD_ELE, EType::FMD_OPERATOR}, false, true}}, - {"amenity-atm", {{EType::FMD_OPERATOR, EType::FMD_WEBSITE, EType::FMD_OPEN_HOURS}, true, false}}, - {"amenity-bank", {{EType::FMD_OPERATOR}, true, true}}, - {"amenity-bar", {{EType::FMD_CUISINE, EType::FMD_INTERNET}, true, true}}, - {"amenity-bicycle_rental", {{EType::FMD_OPERATOR}, true, false}}, - {"amenity-bureau_de_change", {{EType::FMD_OPERATOR}, true, true}}, - {"amenity-bus_station", {{EType::FMD_OPERATOR, EType::FMD_INTERNET}, true, true}}, - {"amenity-cafe", {{EType::FMD_CUISINE, EType::FMD_OPERATOR, EType::FMD_INTERNET}, true, true}}, - {"amenity-car_rental", {{EType::FMD_OPERATOR, EType::FMD_INTERNET}, true, true}}, - {"amenity-car_sharing", {{EType::FMD_OPERATOR, EType::FMD_WEBSITE}, true, false}}, - {"amenity-casino", {{EType::FMD_OPERATOR, EType::FMD_INTERNET}, true, true}}, - {"amenity-cinema", {{EType::FMD_OPERATOR, EType::FMD_INTERNET}, true, true}}, - {"amenity-clinic", {{EType::FMD_OPERATOR, EType::FMD_INTERNET}, true, true}}, - {"amenity-college", {{EType::FMD_OPERATOR}, true, true}}, - {"amenity-doctors", {{EType::FMD_INTERNET}, true, true}}, - {"amenity-dentist", {{EType::FMD_INTERNET}, true, true}}, - {"amenity-drinking_water", {{}, true, false}}, - {"amenity-embassy", {{}, true, true}}, - {"amenity-fast_food", {{EType::FMD_OPERATOR, EType::FMD_CUISINE, EType::FMD_INTERNET}, true, true}}, - {"amenity-ferry_terminal", {{EType::FMD_OPERATOR, EType::FMD_INTERNET}, true, true}}, - {"amenity-fire_station", {{}, true, true}}, - {"amenity-fountain", {{}, true, false}}, - {"amenity-fuel", {{EType::FMD_OPERATOR, EType::FMD_INTERNET}, true, true}}, - {"amenity-grave_yard", {{}, true, false}}, - {"amenity-hospital", {{EType::FMD_INTERNET}, true, true}}, - {"amenity-hunting_stand", {{EType::FMD_HEIGHT}, true, false}}, - {"amenity-kindergarten", {{EType::FMD_OPERATOR}, true, true}}, - {"amenity-library", {{EType::FMD_INTERNET}, true, true}}, - {"amenity-marketplace", {{EType::FMD_OPERATOR}, true, true}}, - {"amenity-nightclub", {{EType::FMD_OPERATOR, EType::FMD_INTERNET}, true, true}}, - {"amenity-parking", {{EType::FMD_OPERATOR}, true, true}}, - {"amenity-pharmacy", {{EType::FMD_OPERATOR}, true, true}}, - {"amenity-place_of_worship", {{}, true, true}}, - {"amenity-police", {{}, true, true}}, - {"amenity-post_box", {{EType::FMD_OPERATOR, EType::FMD_POSTCODE}, true, false}}, - {"amenity-post_office", {{EType::FMD_OPERATOR, EType::FMD_POSTCODE, EType::FMD_INTERNET}, true, true}}, - {"amenity-pub", {{EType::FMD_OPERATOR, EType::FMD_CUISINE, EType::FMD_INTERNET}, true, true}}, - {"amenity-recycling", {{EType::FMD_OPERATOR, EType::FMD_WEBSITE, EType::FMD_PHONE_NUMBER}, true, false}}, - {"amenity-restaurant", {{EType::FMD_OPERATOR, EType::FMD_CUISINE, EType::FMD_INTERNET}, true, true}}, - {"amenity-school", {{EType::FMD_OPERATOR}, true, true}}, - {"amenity-taxi", {{EType::FMD_OPERATOR}, true, false}}, - {"amenity-telephone", {{EType::FMD_OPERATOR, EType::FMD_PHONE_NUMBER}, false, false}}, - {"amenity-theatre", {{}, true, true}}, - {"amenity-toilets", {{EType::FMD_OPERATOR, EType::FMD_OPEN_HOURS}, true, false}}, - {"amenity-townhall", {{}, true, true}}, - {"amenity-university", {{}, true, true}}, - {"amenity-waste_disposal", {{EType::FMD_OPERATOR}, false, false}}, - {"craft", {{}, true, true}}, - {"craft-brewery", {{}, true, true}}, - {"craft-carpenter", {{}, true, true}}, - {"craft-electrician", {{}, true, true}}, - {"craft-gardener", {{}, true, true}}, - {"craft-hvac", {{}, true, true}}, - {"craft-metal_construction", {{}, true, true}}, - {"craft-painter", {{}, true, true}}, - {"craft-photographer", {{}, true, true}}, - {"craft-plumber", {{}, true, true}}, - {"craft-shoemaker", {{}, true, true}}, - {"craft-tailor", {{}, true, true}}, -// {"highway-bus_stop", {{EType::FMD_OPERATOR}, true, false}}, - {"historic-archaeological_site", {{EType::FMD_WIKIPEDIA}, true, false}}, - {"historic-castle", {{EType::FMD_WIKIPEDIA}, true, false}}, - {"historic-memorial", {{EType::FMD_WIKIPEDIA}, true, false}}, - {"historic-monument", {{EType::FMD_WIKIPEDIA}, true, false}}, - {"historic-ruins", {{EType::FMD_WIKIPEDIA}, true, false}}, - {"internet_access", {{EType::FMD_INTERNET}, false, false}}, - {"internet_access|wlan", {{EType::FMD_INTERNET}, false, false}}, - {"landuse-cemetery", {{EType::FMD_WIKIPEDIA}, true, false}}, - {"leisure-garden", {{EType::FMD_OPEN_HOURS, EType::FMD_INTERNET}, true, false}}, - {"leisure-sports_centre", {{EType::FMD_INTERNET}, true, true}}, - {"leisure-stadium", {{EType::FMD_WIKIPEDIA, EType::FMD_OPERATOR}, true, true}}, - {"leisure-swimming_pool", {{EType::FMD_OPERATOR}, true, true}}, - {"natural-peak", {{EType::FMD_WIKIPEDIA, EType::FMD_ELE}, true, false}}, - {"natural-spring", {{EType::FMD_WIKIPEDIA}, true, false}}, - {"natural-waterfall", {{EType::FMD_WIKIPEDIA, EType::FMD_HEIGHT}, true, false}}, - {"office", {{EType::FMD_INTERNET}, true, true}}, - {"office-company", {{}, true, true}}, - {"office-government", {{}, true, true}}, - {"office-lawyer", {{}, true, true}}, - {"office-telecommunication", {{EType::FMD_INTERNET, EType::FMD_OPERATOR}, true, true}}, - {"place-farm", {{EType::FMD_WIKIPEDIA}, true, false}}, - {"place-hamlet", {{EType::FMD_WIKIPEDIA}, true, false}}, - {"place-village", {{EType::FMD_WIKIPEDIA}, true, false}}, -// {"railway-halt", {{}, true, false}}, -// {"railway-station", {{EType::FMD_OPERATOR}, true, false}}, -// {"railway-subway_entrance", {{}, true, false}}, -// {"railway-tram_stop", {{EType::FMD_OPERATOR}, true, false}}, - {"shop", {{EType::FMD_INTERNET}, true, true}}, - {"shop-alcohol", {{EType::FMD_INTERNET}, true, true}}, - {"shop-bakery", {{EType::FMD_INTERNET}, true, true}}, - {"shop-beauty", {{EType::FMD_INTERNET}, true, true}}, - {"shop-beverages", {{EType::FMD_INTERNET}, true, true}}, - {"shop-bicycle", {{EType::FMD_OPERATOR, EType::FMD_INTERNET}, true, true}}, - {"shop-books", {{EType::FMD_OPERATOR, EType::FMD_INTERNET}, true, true}}, - {"shop-butcher", {{EType::FMD_INTERNET}, true, true}}, - {"shop-car", {{EType::FMD_OPERATOR, EType::FMD_INTERNET}, true, true}}, - {"shop-car_repair", {{EType::FMD_OPERATOR, EType::FMD_INTERNET}, true, true}}, - {"shop-chemist", {{EType::FMD_INTERNET}, true, true}}, - {"shop-clothes", {{EType::FMD_OPERATOR, EType::FMD_INTERNET}, true, true}}, - {"shop-computer", {{EType::FMD_INTERNET}, true, true}}, - {"shop-confectionery", {{EType::FMD_INTERNET}, true, true }}, - {"shop-convenience", {{EType::FMD_OPERATOR, EType::FMD_INTERNET}, true, true}}, - {"shop-department_store", {{EType::FMD_OPERATOR, EType::FMD_INTERNET}, false, true}}, - {"shop-doityourself", {{EType::FMD_OPERATOR, EType::FMD_INTERNET}, true, true}}, - {"shop-electronics", {{EType::FMD_OPERATOR, EType::FMD_INTERNET}, true, true}}, - {"shop-florist", {{EType::FMD_INTERNET}, true, true}}, - {"shop-furniture", {{EType::FMD_INTERNET}, true, true}}, - {"shop-garden_centre", {{EType::FMD_INTERNET}, true, true}}, - {"shop-gift", {{EType::FMD_INTERNET}, true, true}}, - {"shop-greengrocer", {{EType::FMD_INTERNET}, true, true}}, - {"shop-hairdresser", {{EType::FMD_INTERNET}, true, true}}, - {"shop-hardware", {{EType::FMD_INTERNET}, true, true}}, - {"shop-jewelry", {{EType::FMD_INTERNET}, true, true}}, - {"shop-kiosk", {{EType::FMD_OPERATOR, EType::FMD_INTERNET}, true, true}}, - {"shop-laundry", {{EType::FMD_OPERATOR, EType::FMD_INTERNET}, true, true}}, - {"shop-mall", {{EType::FMD_OPERATOR, EType::FMD_INTERNET}, true, true}}, - {"shop-mobile_phone", {{EType::FMD_OPERATOR, EType::FMD_INTERNET}, true, true}}, - {"shop-optician", {{EType::FMD_INTERNET}, true, true}}, - {"shop-shoes", {{EType::FMD_INTERNET}, true, true}}, - {"shop-sports", {{EType::FMD_INTERNET}, true, true}}, - {"shop-supermarket", {{EType::FMD_OPERATOR, EType::FMD_INTERNET}, true, true}}, - {"shop-toys", {{EType::FMD_INTERNET}, true, true}}, - {"tourism-alpine_hut", {{EType::FMD_ELE, EType::FMD_OPEN_HOURS, EType::FMD_OPERATOR, EType::FMD_WEBSITE, EType::FMD_INTERNET}, true, false}}, - {"tourism-artwork", {{EType::FMD_WIKIPEDIA}, true, false}}, - {"tourism-attraction", {{EType::FMD_WIKIPEDIA, EType::FMD_WEBSITE}, true, false}}, - {"tourism-camp_site", {{EType::FMD_OPERATOR, EType::FMD_WEBSITE, EType::FMD_OPEN_HOURS, EType::FMD_INTERNET}, true, false}}, - {"tourism-caravan_site", {{EType::FMD_WEBSITE, EType::FMD_OPERATOR, EType::FMD_INTERNET}, true, false}}, - {"tourism-guest_house", {{EType::FMD_OPERATOR, EType::FMD_INTERNET}, true, true}}, - {"tourism-hostel", {{EType::FMD_OPERATOR, EType::FMD_INTERNET}, true, true}}, - {"tourism-hotel", {{EType::FMD_OPERATOR, EType::FMD_INTERNET}, true, true}}, - {"tourism-information", {{}, true, false}}, - {"tourism-motel", {{EType::FMD_OPERATOR, EType::FMD_INTERNET}, true, true}}, - {"tourism-museum", {{EType::FMD_OPERATOR, EType::FMD_INTERNET}, true, true}}, - {"tourism-viewpoint", {{}, true, false}}, - {"waterway-waterfall", {{EType::FMD_WIKIPEDIA, EType::FMD_HEIGHT}, true, false}}}; - -TypeDescription const * GetTypeDescription(uint32_t type, uint8_t typeTruncateLevel = 2) -{ - // Truncate is needed to match, for example, amenity-restaurant-vegan as amenity-restaurant. - ftype::TruncValue(type, typeTruncateLevel); - auto const readableType = classif().GetReadableObjectName(type); - auto const it = gEditableTypes.find(readableType); - if (it != end(gEditableTypes)) - return &it->second; - return nullptr; -} /// Compares editable fields connected with feature ignoring street. bool AreFeaturesEqualButStreet(FeatureType const & a, FeatureType const & b) @@ -276,6 +114,8 @@ namespace osm // TODO(AlexZ): Normalize osm multivalue strings for correct merging // (e.g. insert/remove spaces after ';' delimeter); +Editor::Editor() : m_config("editor.xml") { } + Editor & Editor::Instance() { static Editor instance; @@ -612,47 +452,11 @@ vector Editor::GetFeaturesByStatus(MwmSet::MwmId const & mwmId, Featur EditableProperties Editor::GetEditableProperties(FeatureType const & feature) const { - // TODO(mgsergio): Load editable fields into memory from XML config and query them here. - EditableProperties editable; + feature::TypesHolder const types(feature); - for (uint32_t const type : types) - { - // TODO(mgsergio): If some fields for one type are marked as "NOT edited" in the config, - // they should have priority over same "edited" fields in other feature's types. - auto const * desc = GetTypeDescription(type); - if (desc) - { - editable.m_name = desc->m_name; - editable.m_address = desc->m_address; - - for (EType const field : desc->m_fields) - editable.m_metadata.push_back(field); - } - } - // If address is editable, many metadata fields are editable too. - if (editable.m_address) - { - // TODO(mgsergio): Load address-related editable properties from XML config. - editable.m_metadata.push_back(EType::FMD_EMAIL); - editable.m_metadata.push_back(EType::FMD_OPEN_HOURS); - editable.m_metadata.push_back(EType::FMD_PHONE_NUMBER); - editable.m_metadata.push_back(EType::FMD_WEBSITE); - // Post boxes and post offices should have editable postcode field defined separately. - editable.m_metadata.push_back(EType::FMD_POSTCODE); - } - - // Buildings are processed separately. - // Please note that only house number, street and post code should be editable for buildings. - // TODO(mgsergio): Activate this code by XML config variable. - if (ftypes::IsBuildingChecker::Instance()(feature)) - { - editable.m_address = true; - editable.m_metadata.push_back(EType::FMD_POSTCODE); - } - - // Avoid possible duplicates. - my::SortUnique(editable.m_metadata); - return editable; + auto const desc = m_config.GetTypeDescription(types.ToObjectNames()); + return {{begin(desc.GetEditableFields()), end(desc.GetEditableFields())}, + desc.IsNameEditable(), desc.IsAddressEditable()}; } bool Editor::HaveSomethingToUpload() const @@ -837,12 +641,12 @@ NewFeatureCategories Editor::GetNewFeatureCategories() const int8_t const locale = CategoriesHolder::MapLocaleToInteger(languages::GetCurrentOrig()); Classificator const & cl = classif(); NewFeatureCategories res; - for (auto const & elem : gEditableTypes) + for (auto const & classificatorType : m_config.GetTypesThatCanBeAdded()) { - uint32_t const type = cl.GetTypeByReadableObjectName(elem.first); + uint32_t const type = cl.GetTypeByReadableObjectName(classificatorType); if (type == 0) { - LOG(LWARNING, ("Unknown type in Editor's config:", elem.first)); + LOG(LWARNING, ("Unknown type in Editor's config:", classificatorType)); continue; } res.m_allSorted.emplace_back(type, cats.GetReadableFeatureType(type, locale)); diff --git a/indexer/osm_editor.hpp b/indexer/osm_editor.hpp index 7f63b5e3d4..4833f08933 100644 --- a/indexer/osm_editor.hpp +++ b/indexer/osm_editor.hpp @@ -7,6 +7,7 @@ #include "indexer/feature_meta.hpp" #include "indexer/mwm_set.hpp" +#include "editor/editor_config.hpp" #include "editor/new_feature_categories.hpp" #include "editor/xml_feature.hpp" @@ -22,7 +23,7 @@ namespace osm { class Editor final { - Editor() = default; + Editor(); public: using TFeatureTypeFn = function; // Mimics Framework::TFeatureTypeFn. @@ -160,6 +161,9 @@ private: TFeatureOriginalStreetFn m_getOriginalFeatureStreetFn; /// Iterate over all features in some area that includes given point. TForEachFeaturesNearByFn m_forEachFeatureAtPointFn; + + /// Contains information about what and how can be edited. + editor::EditorConfig m_config; }; // class Editor string DebugPrint(Editor::FeatureStatus fs); diff --git a/omim.pro b/omim.pro index 473fb909ef..358a14f771 100644 --- a/omim.pro +++ b/omim.pro @@ -108,7 +108,7 @@ SUBDIRS = 3party base coding geometry editor indexer routing search search_tests.depends = 3party base coding geometry platform indexer search SUBDIRS *= search_tests - MapDepLibs = 3party base coding geometry platform storage indexer search map \ + MapDepLibs = 3party base coding geometry editor platform storage indexer search map \ routing drape drape_frontend # @TODO storage_tests.depends is equal to map_tests because now storage/migrate_tests.cpp diff --git a/search/query_saver.cpp b/search/query_saver.cpp index 26fb0d7511..7a9077f14f 100644 --- a/search/query_saver.cpp +++ b/search/query_saver.cpp @@ -34,12 +34,12 @@ public: { } - inline uint64_t Size() const + inline uint64_t Size() const override { return m_size; } - inline void Read(uint64_t pos, void * p, size_t size) const + inline void Read(uint64_t pos, void * p, size_t size) const override { CheckPosAndSize(pos, size); memcpy(p, m_pData + pos, size); diff --git a/search/search_quality_tests/search_quality_tests.pro b/search/search_quality_tests/search_quality_tests.pro index 556272420e..1fc7a87cca 100644 --- a/search/search_quality_tests/search_quality_tests.pro +++ b/search/search_quality_tests/search_quality_tests.pro @@ -7,9 +7,10 @@ TEMPLATE = app ROOT_DIR = ../.. # todo(@m) revise -DEPENDENCIES = map drape_frontend routing search_tests_support search storage indexer drape platform geometry coding base \ - freetype expat fribidi tomcrypt gflags jansson protobuf osrm stats_client minizip succinct \ - opening_hours +DEPENDENCIES = map drape_frontend routing search_tests_support search storage indexer drape \ + platform editor geometry coding base freetype expat fribidi tomcrypt gflags \ + jansson protobuf osrm stats_client minizip succinct \ + opening_hours pugixml include($$ROOT_DIR/common.pri)