Repo created
This commit is contained in:
parent
4af19165ec
commit
68073add76
12458 changed files with 12350765 additions and 2 deletions
412
generator/feature_merger.cpp
Normal file
412
generator/feature_merger.cpp
Normal file
|
|
@ -0,0 +1,412 @@
|
|||
#include "generator/feature_merger.hpp"
|
||||
|
||||
#include "indexer/classificator.hpp"
|
||||
#include "indexer/feature_algo.hpp"
|
||||
#include "indexer/feature_visibility.hpp"
|
||||
|
||||
#include "coding/point_coding.hpp"
|
||||
|
||||
using namespace feature;
|
||||
|
||||
MergedFeatureBuilder::MergedFeatureBuilder(FeatureBuilder const & fb) : FeatureBuilder(fb), m_isRound(false)
|
||||
{
|
||||
m_params.FinishAddingTypes();
|
||||
}
|
||||
|
||||
void MergedFeatureBuilder::SetRound()
|
||||
{
|
||||
m_isRound = true;
|
||||
|
||||
m_roundBounds[0] = m_roundBounds[1] = GetOuterGeometry();
|
||||
}
|
||||
|
||||
void MergedFeatureBuilder::AppendFeature(MergedFeatureBuilder const & fb, bool fromBegin, bool toBack)
|
||||
{
|
||||
// Also merge Osm IDs for debugging
|
||||
m_osmIds.insert(m_osmIds.end(), fb.m_osmIds.begin(), fb.m_osmIds.end());
|
||||
|
||||
PointSeq & thisG = m_polygons.front();
|
||||
PointSeq const & fbG = fb.GetOuterGeometry();
|
||||
|
||||
if (fb.m_isRound)
|
||||
{
|
||||
if (toBack)
|
||||
m_roundBounds[1] = fbG;
|
||||
else
|
||||
m_roundBounds[0] = fbG;
|
||||
return;
|
||||
}
|
||||
|
||||
if (toBack)
|
||||
m_roundBounds[1].clear();
|
||||
else
|
||||
m_roundBounds[0].clear();
|
||||
|
||||
m_isRound = false;
|
||||
|
||||
using namespace feature;
|
||||
|
||||
if (fromBegin)
|
||||
{
|
||||
if (toBack)
|
||||
{
|
||||
thisG.insert(thisG.end(), fbG.begin() + 1, fbG.end());
|
||||
CalcRect(fbG.begin() + 1, fbG.end(), m_limitRect);
|
||||
}
|
||||
else
|
||||
{
|
||||
thisG.insert(thisG.begin(), fbG.begin(), fbG.end() - 1);
|
||||
CalcRect(fbG.begin(), fbG.end() - 1, m_limitRect);
|
||||
}
|
||||
}
|
||||
else if (toBack)
|
||||
{
|
||||
thisG.insert(thisG.end(), fbG.rbegin() + 1, fbG.rend());
|
||||
CalcRect(fbG.rbegin() + 1, fbG.rend(), m_limitRect);
|
||||
}
|
||||
else
|
||||
{
|
||||
thisG.insert(thisG.begin(), fbG.rbegin(), fbG.rend() - 1);
|
||||
CalcRect(fbG.rbegin(), fbG.rend() - 1, m_limitRect);
|
||||
}
|
||||
}
|
||||
|
||||
bool MergedFeatureBuilder::EqualGeometry(MergedFeatureBuilder const & fb) const
|
||||
{
|
||||
return (GetOuterGeometry() == fb.GetOuterGeometry());
|
||||
}
|
||||
|
||||
std::pair<m2::PointD, bool> MergedFeatureBuilder::GetKeyPoint(size_t i) const
|
||||
{
|
||||
// 1. check first rounds
|
||||
size_t sz = m_roundBounds[0].size();
|
||||
if (i < sz)
|
||||
return std::make_pair(m_roundBounds[0][i], false);
|
||||
i -= sz;
|
||||
|
||||
// 2. check first point
|
||||
if (i == 0)
|
||||
return std::make_pair(FirstPoint(), false);
|
||||
|
||||
// 3. check last rounds
|
||||
--i;
|
||||
sz = m_roundBounds[1].size();
|
||||
if (i < sz)
|
||||
return std::make_pair(m_roundBounds[1][i], true);
|
||||
i -= sz;
|
||||
|
||||
// 4. return last point
|
||||
ASSERT_EQUAL(i, 0, ());
|
||||
return std::make_pair(LastPoint(), true);
|
||||
}
|
||||
|
||||
size_t MergedFeatureBuilder::GetKeyPointsCount() const
|
||||
{
|
||||
return m_roundBounds[0].size() + m_roundBounds[1].size() + 2;
|
||||
}
|
||||
|
||||
double MergedFeatureBuilder::GetSquaredLength() const
|
||||
{
|
||||
PointSeq const & poly = GetOuterGeometry();
|
||||
|
||||
double sqLen = 0.0;
|
||||
for (size_t i = 1; i < poly.size(); ++i)
|
||||
sqLen += poly[i - 1].SquaredLength(poly[i]);
|
||||
return sqLen;
|
||||
}
|
||||
|
||||
FeatureMergeProcessor::Key FeatureMergeProcessor::GetKey(m2::PointD const & p)
|
||||
{
|
||||
return PointToInt64Obsolete(p, m_coordBits);
|
||||
}
|
||||
|
||||
FeatureMergeProcessor::FeatureMergeProcessor(uint32_t coordBits) : m_coordBits(coordBits) {}
|
||||
|
||||
void FeatureMergeProcessor::operator()(FeatureBuilder const & fb)
|
||||
{
|
||||
this->operator()(new MergedFeatureBuilder(fb));
|
||||
}
|
||||
|
||||
void FeatureMergeProcessor::operator()(MergedFeatureBuilder * p)
|
||||
{
|
||||
Key const k1 = GetKey(p->FirstPoint());
|
||||
Key const k2 = GetKey(p->LastPoint());
|
||||
|
||||
m_map[k1].push_back(p);
|
||||
if (k1 != k2)
|
||||
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();
|
||||
|
||||
p->ForEachMiddlePoints(std::bind(&FeatureMergeProcessor::Insert, this, std::placeholders::_1, p));
|
||||
}
|
||||
}
|
||||
|
||||
void FeatureMergeProcessor::Insert(m2::PointD const & pt, MergedFeatureBuilder * p)
|
||||
{
|
||||
m_map[GetKey(pt)].push_back(p);
|
||||
}
|
||||
|
||||
void FeatureMergeProcessor::Remove(Key key, MergedFeatureBuilder const * p)
|
||||
{
|
||||
auto i = m_map.find(key);
|
||||
if (i != m_map.end())
|
||||
{
|
||||
MergedFeatureBuilders & v = i->second;
|
||||
v.erase(remove(v.begin(), v.end(), p), v.end());
|
||||
if (v.empty())
|
||||
m_map.erase(i);
|
||||
}
|
||||
}
|
||||
|
||||
void FeatureMergeProcessor::Remove(MergedFeatureBuilder const * p)
|
||||
{
|
||||
Key const k1 = GetKey(p->FirstPoint());
|
||||
Key const k2 = GetKey(p->LastPoint());
|
||||
|
||||
Remove(k1, p);
|
||||
if (k1 != k2)
|
||||
Remove(k2, p);
|
||||
else
|
||||
{
|
||||
ASSERT(p->IsRound(), ());
|
||||
|
||||
p->ForEachMiddlePoints(std::bind(&FeatureMergeProcessor::Remove1, this, std::placeholders::_1, p));
|
||||
}
|
||||
}
|
||||
|
||||
void FeatureMergeProcessor::DoMerge(FeatureEmitterIFace & emitter)
|
||||
{
|
||||
while (!m_map.empty())
|
||||
{
|
||||
// Get any starting feature.
|
||||
MergedFeatureBuilders & vS = m_map.begin()->second;
|
||||
CHECK(!vS.empty(), ());
|
||||
MergedFeatureBuilder * p = vS.front(); // may be 'back' is better
|
||||
|
||||
// Remove next processing type. If it's a last type - remove from map.
|
||||
uint32_t type;
|
||||
bool isRemoved = false;
|
||||
if (p->PopAnyType(type))
|
||||
{
|
||||
isRemoved = true;
|
||||
Remove(p);
|
||||
}
|
||||
|
||||
// We will merge to the copy of p.
|
||||
MergedFeatureBuilder curr(*p);
|
||||
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
|
||||
{
|
||||
std::pair<m2::PointD, bool> const pt = curr.GetKeyPoint(ind++);
|
||||
auto it = m_map.find(GetKey(pt.first));
|
||||
|
||||
MergedFeatureBuilder * pp = 0;
|
||||
if (it != m_map.end())
|
||||
{
|
||||
// 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->GetSquaredLength();
|
||||
// It's not necessery assert, because it's possible in source data
|
||||
// TODO(pastk) : likely caused by degenerate closed lines.
|
||||
// ASSERT_GREATER(pr, 0.0, ());
|
||||
if (pr < bestPr)
|
||||
{
|
||||
pp = pTest;
|
||||
bestPr = pr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Merge the current feature with the best connected feature.
|
||||
if (pp)
|
||||
{
|
||||
bool const toBack = pt.second;
|
||||
bool fromBegin = true;
|
||||
if ((pt.first.SquaredLength(pp->FirstPoint()) > pt.first.SquaredLength(pp->LastPoint())) == toBack)
|
||||
fromBegin = false;
|
||||
|
||||
curr.AppendFeature(*pp, fromBegin, toBack);
|
||||
|
||||
if (pp->PopExactType(type))
|
||||
{
|
||||
Remove(pp);
|
||||
delete pp;
|
||||
}
|
||||
|
||||
// start from the beginning if we have a successful merge
|
||||
ind = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (m_last.NotEmpty() && m_last.EqualGeometry(curr))
|
||||
{
|
||||
// curr is equal with m_last by geometry - just add new type to m_last
|
||||
if (!m_last.HasType(type))
|
||||
m_last.AddType(type);
|
||||
}
|
||||
else
|
||||
{
|
||||
// emit m_last and set curr as last processed feature (m_last)
|
||||
if (m_last.NotEmpty())
|
||||
emitter(m_last);
|
||||
m_last = curr;
|
||||
}
|
||||
|
||||
// Delete if the feature was removed from map.
|
||||
if (isRemoved)
|
||||
delete p;
|
||||
}
|
||||
|
||||
if (m_last.NotEmpty())
|
||||
emitter(m_last);
|
||||
}
|
||||
|
||||
uint32_t FeatureTypesProcessor::GetType(char const * arr[], size_t n)
|
||||
{
|
||||
uint32_t const type = classif().GetTypeByPath(std::vector<std::string>(arr, arr + n));
|
||||
CHECK_NOT_EQUAL(type, ftype::GetEmptyValue(), ());
|
||||
return type;
|
||||
}
|
||||
|
||||
void FeatureTypesProcessor::CorrectType(uint32_t & t) const
|
||||
{
|
||||
if (m_dontNormalize.count(t) > 0)
|
||||
return;
|
||||
|
||||
// 1. get normalized type:
|
||||
// highway-motorway-bridge => highway-motorway
|
||||
ftype::TruncValue(t, 2);
|
||||
|
||||
// 2. get mapping type:
|
||||
auto i = m_mapping.find(t);
|
||||
if (i != m_mapping.end())
|
||||
t = i->second;
|
||||
}
|
||||
|
||||
void FeatureTypesProcessor::SetMappingTypes(char const * arr1[2], char const * arr2[2])
|
||||
{
|
||||
m_mapping[GetType(arr1, 2)] = GetType(arr2, 2);
|
||||
}
|
||||
|
||||
MergedFeatureBuilder * FeatureTypesProcessor::operator()(FeatureBuilder const & fb)
|
||||
{
|
||||
MergedFeatureBuilder * p = new MergedFeatureBuilder(fb);
|
||||
|
||||
p->ForEachChangeTypes(do_change_types(*this));
|
||||
|
||||
// do preprocessing after types correction
|
||||
if (!feature::PreprocessForWorldMap(*p))
|
||||
{
|
||||
delete p;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// zero all additional params for world merged features (names, ranks, ...)
|
||||
p->ZeroParams();
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
class TypeCheckBase
|
||||
{
|
||||
protected:
|
||||
static uint32_t GetRegionType()
|
||||
{
|
||||
static uint32_t regionType = classif().GetTypeByPath({"place", "region"});
|
||||
return regionType;
|
||||
}
|
||||
|
||||
public:
|
||||
int m_lowScale, m_upScale;
|
||||
|
||||
TypeCheckBase(int lowScale, int upScale) : m_lowScale(lowScale), m_upScale(upScale) {}
|
||||
|
||||
using RangeT = std::pair<int, int>;
|
||||
static RangeT GetScaleRange(uint32_t type) { return feature::GetDrawableScaleRange(type); }
|
||||
|
||||
static bool IsEmptyRange(RangeT const & range) { return range.first == -1; }
|
||||
bool IsBadRange(RangeT const & range) const { return (range.first > m_upScale || range.second < m_lowScale); }
|
||||
};
|
||||
|
||||
class TypeCheckWorld : public TypeCheckBase
|
||||
{
|
||||
public:
|
||||
bool m_isRegion = false;
|
||||
|
||||
TypeCheckWorld() : TypeCheckBase(0, scales::GetUpperWorldScale()) {}
|
||||
|
||||
/// @return true If |type| should be removed.
|
||||
bool operator()(uint32_t type)
|
||||
{
|
||||
// Keep place=region types in World.mwm for search, even when they are not visible.
|
||||
if (type == GetRegionType())
|
||||
{
|
||||
m_isRegion = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
auto const range = GetScaleRange(type);
|
||||
return IsEmptyRange(range) || IsBadRange(range);
|
||||
}
|
||||
};
|
||||
|
||||
class TypeCheckCountry : public TypeCheckBase
|
||||
{
|
||||
public:
|
||||
TypeCheckCountry() : TypeCheckBase(scales::GetUpperWorldScale() + 1, scales::GetUpperStyleScale()) {}
|
||||
|
||||
/// @return true If |type| should be removed.
|
||||
bool operator()(uint32_t type) const
|
||||
{
|
||||
// Do not keep place=region in countries.
|
||||
if (type == GetRegionType())
|
||||
return true;
|
||||
|
||||
auto const range = GetScaleRange(type);
|
||||
|
||||
// Don't remove non-drawable types here, since this case is processed before
|
||||
// feature::TypeAlwaysExists or FeatureBuilder::RemoveInvalidTypes.
|
||||
if (IsEmptyRange(range))
|
||||
return false;
|
||||
|
||||
return IsBadRange(range);
|
||||
}
|
||||
};
|
||||
|
||||
namespace feature
|
||||
{
|
||||
bool PreprocessForWorldMap(FeatureBuilder & fb)
|
||||
{
|
||||
TypeCheckWorld checker;
|
||||
if (fb.RemoveTypesIf(checker))
|
||||
return false;
|
||||
|
||||
if (!checker.m_isRegion)
|
||||
fb.RemoveNameIfInvisible(checker.m_lowScale, checker.m_upScale);
|
||||
|
||||
/// @todo Do we need all Metadata for point/area World features? We delete meta for linear in ZeroParams.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PreprocessForCountryMap(FeatureBuilder & fb)
|
||||
{
|
||||
return !fb.RemoveTypesIf(TypeCheckCountry());
|
||||
}
|
||||
} // namespace feature
|
||||
Loading…
Add table
Add a link
Reference in a new issue