diff --git a/generator/feature_builder.cpp b/generator/feature_builder.cpp index 9fc668017a..5fe8f87c68 100644 --- a/generator/feature_builder.cpp +++ b/generator/feature_builder.cpp @@ -47,6 +47,20 @@ m2::PointD FeatureBuilder1::GetGeometryCenter() const return ret / count; } +m2::PointD FeatureBuilder1::GetKeyPoint() const +{ + switch (GetGeomType()) + { + case GEOM_POINT: + return m_center; + case GEOM_LINE: case GEOM_AREA: + return GetGeometryCenter(); + default: + ASSERT(false, ()); + return m2::PointD(0, 0); + } +} + void FeatureBuilder1::SetCenter(m2::PointD const & p) { m_center = p; @@ -444,10 +458,18 @@ void FeatureBuilder1::SetCoastCell(int64_t iCell, string const & strCell) { m_coastCell = iCell; - ASSERT ( m_params.name.IsEmpty(), () ); + ASSERT(m_params.name.IsEmpty(), ()); m_params.name.AddString(0, strCell); } +string FeatureBuilder1::GetName(int8_t lang) const +{ + string s; + bool const res = m_params.name.GetString(lang, s); + ASSERT(res != s.empty(), ()); + return s; +} + string DebugPrint(FeatureBuilder1 const & f) { ostringstream out; diff --git a/generator/feature_builder.hpp b/generator/feature_builder.hpp index dc3de46bb6..5ad67d6321 100644 --- a/generator/feature_builder.hpp +++ b/generator/feature_builder.hpp @@ -85,6 +85,7 @@ public: bool IsGeometryClosed() const; m2::PointD GetGeometryCenter() const; + m2::PointD GetKeyPoint() const; inline size_t GetPointsCount() const { return GetGeometry().size(); } inline size_t GetPolygonsCount() const { return m_polygons.size(); } @@ -157,12 +158,7 @@ public: else return false; } - inline string GetName(int8_t lang = StringUtf8Multilang::DEFAULT_CODE) const - { - string s; - m_params.name.GetString(lang, s); - return s; - } + string GetName(int8_t lang = StringUtf8Multilang::DEFAULT_CODE) const; /// @name For diagnostic use only. //@{ diff --git a/generator/feature_generator.cpp b/generator/feature_generator.cpp index 57d1e11008..9d3004b9b9 100644 --- a/generator/feature_generator.cpp +++ b/generator/feature_generator.cpp @@ -440,9 +440,12 @@ bool GenerateImpl(GenerateInfo & info, string const & osmFileName = string()) else ParseXMLFromFile(parser, osmFileName); + parser.Finish(); + // Stop if coasts are not merged and FLAG_fail_on_coasts is set if (!bucketer.Finish()) return false; + bucketer.GetNames(info.m_bucketNames); } catch (Reader::Exception const & e) diff --git a/generator/osm_element.hpp b/generator/osm_element.hpp index b69bf578f5..b8258eed9b 100644 --- a/generator/osm_element.hpp +++ b/generator/osm_element.hpp @@ -7,6 +7,9 @@ #include "../indexer/ftypes_matcher.hpp" #include "../indexer/feature_visibility.hpp" +#include "../indexer/classificator.hpp" + +#include "../geometry/tree4d.hpp" #include "../base/string_utils.hpp" #include "../base/logging.hpp" @@ -264,6 +267,48 @@ class SecondPassParser : public BaseOSMParser return m_addrWriter && checker(params.m_Types); } + class Place + { + FeatureBuilderT m_ft; + m2::PointD m_pt; + uint32_t m_type; + + static constexpr long double THRESHOLD_M = 5000.0; + + bool IsPoint() const { return (m_ft.GetGeomType() == feature::GEOM_POINT); } + + public: + Place(FeatureBuilderT const & ft, uint32_t type) + : m_ft(ft), m_pt(ft.GetKeyPoint()), m_type(type) + { + } + + FeatureBuilderT const & GetFeature() const { return m_ft; } + + m2::RectD GetLimitRect() const + { + return MercatorBounds::RectByCenterXYAndSizeInMeters(m_pt, THRESHOLD_M); + } + + /// @name Always replace point features and leave area features. + //@{ + bool IsEqual(Place const & r) const + { + return (m_type == r.m_type && + m_ft.GetName() == r.m_ft.GetName() && + (IsPoint() || r.IsPoint()) && + MercatorBounds::DistanceOnEarth(m_pt, r.m_pt) < THRESHOLD_M); + } + + bool NeedReplace(Place const & r) const + { + return r.IsPoint(); + } + //@} + }; + + m4::Tree m_places; + void EmitFeatureBase(FeatureBuilderT & ft, FeatureParams const & params) { ft.SetParams(params); @@ -273,7 +318,17 @@ class SecondPassParser : public BaseOSMParser if (NeedWriteAddress(params) && ft.FormatFullAddress(addr)) m_addrWriter->Write(addr.c_str(), addr.size()); - m_emitter(ft); + static uint32_t const placeType = classif().GetTypeByPath({"place"}); + uint32_t const type = params.FindType(placeType, 1); + + if (type != ftype::GetEmptyValue() && !ft.GetName().empty()) + { + m_places.ReplaceEqualInRect(Place(ft, type), + bind(&Place::IsEqual, _1, _2), + bind(&Place::NeedReplace, _1, _2)); + } + else + m_emitter(ft); } } @@ -435,4 +490,12 @@ public: if (!addrFilePath.empty()) m_addrWriter.reset(new FileWriter(addrFilePath)); } + + void Finish() + { + m_places.ForEach([this] (Place const & p) + { + m_emitter(p.GetFeature()); + }); + } }; diff --git a/indexer/feature_data.cpp b/indexer/feature_data.cpp index 2488338e8a..bb36d54d62 100644 --- a/indexer/feature_data.cpp +++ b/indexer/feature_data.cpp @@ -306,6 +306,18 @@ bool FeatureParams::IsTypeExist(uint32_t t) const return (find(m_Types.begin(), m_Types.end(), t) != m_Types.end()); } +uint32_t FeatureParams::FindType(uint32_t comp, uint8_t level) const +{ + for (uint32_t const type : m_Types) + { + uint32_t t = type; + ftype::TruncValue(t, level); + if (t == comp) + return type; + } + return ftype::GetEmptyValue(); +} + bool FeatureParams::CheckValid() const { CHECK(!m_Types.empty() && m_Types.size() <= max_types_count, ()); diff --git a/indexer/feature_data.hpp b/indexer/feature_data.hpp index a638a61e02..7719afebb2 100644 --- a/indexer/feature_data.hpp +++ b/indexer/feature_data.hpp @@ -258,6 +258,9 @@ public: bool PopExactType(uint32_t t); bool IsTypeExist(uint32_t t) const; + /// Find type that matches "comp" with "level" in classificator hierarchy. + uint32_t FindType(uint32_t comp, uint8_t level) const; + bool CheckValid() const; uint8_t GetHeader() const;