diff --git a/generator/feature_merger.cpp b/generator/feature_merger.cpp index f7766339a3..c4f20552c7 100644 --- a/generator/feature_merger.cpp +++ b/generator/feature_merger.cpp @@ -109,14 +109,14 @@ size_t MergedFeatureBuilder::GetKeyPointsCount() const return m_roundBounds[0].size() + m_roundBounds[1].size() + 2; } -double MergedFeatureBuilder::GetPriority() const +double MergedFeatureBuilder::GetSquaredLength() const { PointSeq const & poly = GetOuterGeometry(); - double pr = 0.0; + double sqLen = 0.0; for (size_t i = 1; i < poly.size(); ++i) - pr += poly[i-1].SquaredLength(poly[i]); - return pr; + sqLen += poly[i-1].SquaredLength(poly[i]); + return sqLen; } @@ -145,6 +145,8 @@ void FeatureMergeProcessor::operator() (MergedFeatureBuilder * p) m_map[k2].push_back(p); else { + // All of roundabout's points are considered for possible continuation of the line. + // Effectively a roundabout itself is discarded and is used only for merging adjoining lines together. ///@ todo Do it only for small round features! p->SetRound(); @@ -207,6 +209,7 @@ void FeatureMergeProcessor::DoMerge(FeatureEmitterIFace & emitter) curr.SetType(type); // Iterate through key points while merging. + // Key points are either ends of the line or any point on the "roundabout" if the line ends with it. size_t ind = 0; while (ind < curr.GetKeyPointsCount()) // GetKeyPointsCount() can be different on each iteration { @@ -216,17 +219,19 @@ void FeatureMergeProcessor::DoMerge(FeatureEmitterIFace & emitter) MergedFeatureBuilder * pp = 0; if (it != m_map.end()) { - // Find best feature to continue. - double bestPr = -1.0; + // Find the shortest connected line feature to continue, + // it helps to spread points more evenly between the features and to avoid producing too long lines. + double bestPr = std::numeric_limits::max(); for (size_t i = 0; i < it->second.size(); ++i) { MergedFeatureBuilder * pTest = it->second[i]; if (pTest->HasType(type)) { - double const pr = pTest->GetPriority(); + double const pr = pTest->GetSquaredLength(); // It's not necessery assert, because it's possible in source data -// ASSERT_GREATER ( pr, 0.0, () ); - if (pr > bestPr) + // TODO(pastk) : likely caused by degenerate closed lines. + // ASSERT_GREATER(pr, 0.0, ()); + if (pr < bestPr) { pp = pTest; bestPr = pr; @@ -234,7 +239,7 @@ void FeatureMergeProcessor::DoMerge(FeatureEmitterIFace & emitter) } } - // Merge current feature with best feature. + // Merge the current feature with the best connected feature. if (pp) { bool const toBack = pt.second; diff --git a/generator/feature_merger.hpp b/generator/feature_merger.hpp index 42ecec0f5b..0392ff0c51 100644 --- a/generator/feature_merger.hpp +++ b/generator/feature_merger.hpp @@ -51,7 +51,8 @@ public: std::pair GetKeyPoint(size_t i) const; size_t GetKeyPointsCount() const; - double GetPriority() const; + // Used to determine which connected line to merge. + double GetSquaredLength() const; }; /// Feature merger. diff --git a/generator/final_processor_world.cpp b/generator/final_processor_world.cpp index a6d12e7c36..ab420d238b 100644 --- a/generator/final_processor_world.cpp +++ b/generator/final_processor_world.cpp @@ -2,6 +2,8 @@ #include "generator/feature_builder.hpp" #include "generator/final_processor_utils.hpp" +#include "base/logging.hpp" + #include "defines.hpp" namespace generator @@ -23,9 +25,11 @@ void WorldFinalProcessor::Process() auto fbs = ReadAllDatRawFormat(m_worldTmpFilename); Order(fbs); WorldGenerator generator(m_worldTmpFilename, m_coastlineGeomFilename, m_popularPlacesFilename); + LOG(LINFO, ("Process World features")); for (auto & fb : fbs) generator.Process(fb); + LOG(LINFO, ("Merge World lines")); generator.DoMerge(); } diff --git a/generator/water_boundary_checker.hpp b/generator/water_boundary_checker.hpp index 7472725a81..5653f1edc5 100644 --- a/generator/water_boundary_checker.hpp +++ b/generator/water_boundary_checker.hpp @@ -76,6 +76,10 @@ public: Earth }; + // TODO(pastk): boundaries along the coast are being "torn" into small pieces instead of being discarded completely. + // Likely it happens because an already simplified coastline is used, while boundary lines are not simplified yet. + // It causes these lines to intersect each other often. + // https://github.com/organicmaps/organicmaps/issues/6445 void ProcessBoundary(feature::FeatureBuilder const & boundary, std::vector & parts) { double constexpr kExtension = 0.01; diff --git a/generator/world_map_generator.hpp b/generator/world_map_generator.hpp index 957efe43c4..91e6897bab 100644 --- a/generator/world_map_generator.hpp +++ b/generator/world_map_generator.hpp @@ -53,8 +53,27 @@ class WorldMapGenerator // This functor is called by m_merger after merging linear features. void operator()(feature::FeatureBuilder const & fb) override { - // Skip small ways. This check is enough, because classifier types check was made in m_typesCorrector. - if (scales::IsGoodForLevel(scales::GetUpperWorldScale(), fb.GetLimitRect())) + static const uint32_t ferryType = classif().GetTypeByPath({"route", "ferry"}); + static const uint32_t boundaryType = classif().GetTypeByPath({"boundary", "administrative"}); + static const uint32_t highwayType = classif().GetTypeByPath({"highway"}); + + int thresholdLevel = scales::GetUpperWorldScale(); + if (fb.HasType(ferryType) || fb.HasType(boundaryType, 2)) + { + // Discard too short ferry and boundary lines + // (boundaries along the coast are being "torn" into small pieces + // by the coastline in WaterBoundaryChecker::ProcessBoundary()). + thresholdLevel = scales::GetUpperWorldScale() - 2; + } + else if (fb.HasType(highwayType, 1)) + { + // Discard too short roads incl. V-like approaches to roundabouts / other roads + // and small roundabouts that were not merged into longer roads for some reason. + thresholdLevel = scales::GetUpperWorldScale() + 2; + } + + // TODO(pastk): there seems to be two area size checks: here and in PushFeature(). + if (scales::IsGoodForLevel(thresholdLevel, fb.GetLimitRect())) PushSure(fb); } @@ -88,7 +107,7 @@ public: WorldMapGenerator(std::string const & worldFilename, std::string const & coastGeomFilename, std::string const & popularPlacesFilename) : m_worldBucket(worldFilename) - , m_merger(kPointCoordBits - (scales::GetUpperScale() - scales::GetUpperWorldScale()) / 2) + , m_merger(kFeatureSorterPointCoordBits - (scales::GetUpperScale() - scales::GetUpperWorldScale()) / 2) , m_popularPlacesFilename(popularPlacesFilename) { // Do not strip last types for given tags, @@ -100,6 +119,12 @@ public: for (size_t i = 0; i < ARRAY_SIZE(arr1); ++i) m_typesCorrector.SetDontNormalizeType(arr1[i]); + // Merge motorways into trunks. + // TODO : merge e.g. highway-trunk_link into highway-trunk? + char const * marr1[2] = {"highway", "motorway"}, + * marr2[2] = {"highway", "trunk"}; + m_typesCorrector.SetMappingTypes(marr1, marr2); + if (popularPlacesFilename.empty()) LOG(LWARNING, ("popular_places_data option not set. Popular attractions will not be added to World.mwm"));