[generator] World lines: adjust short lines filtering, merge motorways into trunks #5655

Merged
root merged 4 commits from pastk-generator-world-roads-merge into master 2023-12-03 17:24:06 +00:00
5 changed files with 53 additions and 14 deletions

View file

@ -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<double>::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;

View file

@ -51,7 +51,8 @@ public:
std::pair<m2::PointD, bool> 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.

View file

@ -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<serialization_policy::MaxAccuracy>(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();
}

View file

@ -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<feature::FeatureBuilder> & parts)
{
double constexpr kExtension = 0.01;

View file

@ -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"});
biodranik commented 2023-11-30 17:52:22 +00:00 (Migrated from github.com)
Review

constexpr

constexpr
Review

GetTypeByPath() is not constexpr

GetTypeByPath() is not constexpr
biodranik commented 2023-11-30 22:34:31 +00:00 (Migrated from github.com)
Review

Right! It would be great to make these expressions constexpr in the future...

Right! It would be great to make these expressions constexpr in the future...
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"));