Move places handling from translator to MainFratureEmitter.

This commit is contained in:
Sergey Magidovich 2016-07-21 13:31:16 +03:00
parent 30a47a10c3
commit 80d669896f
2 changed files with 143 additions and 133 deletions

View file

@ -18,6 +18,7 @@
#include "platform/platform.hpp"
#include "geometry/mercator.hpp"
#include "geometry/tree4d.hpp"
#include "coding/parse_xml.hpp"
@ -183,6 +184,78 @@ public:
}
};
// TODO(mgsergio): comment
class Place
{
FeatureBuilder1 m_ft;
m2::PointD m_pt;
uint32_t m_type;
double m_thresholdM;
bool IsPoint() const { return (m_ft.GetGeomType() == feature::GEOM_POINT); }
static bool IsEqualTypes(uint32_t t1, uint32_t t2)
{
// Use 2-arity places comparison for filtering.
// ("place-city-capital-2" is equal to "place-city")
ftype::TruncValue(t1, 2);
ftype::TruncValue(t2, 2);
return (t1 == t2);
}
public:
Place(FeatureBuilder1 const & ft, uint32_t type) : m_ft(ft), m_pt(ft.GetKeyPoint()), m_type(type)
{
using namespace ftypes;
switch (IsLocalityChecker::Instance().GetType(m_type))
{
case COUNTRY: m_thresholdM = 300000.0; break;
case STATE: m_thresholdM = 100000.0; break;
case CITY: m_thresholdM = 30000.0; break;
case TOWN: m_thresholdM = 20000.0; break;
case VILLAGE: m_thresholdM = 10000.0; break;
default: m_thresholdM = 10000.0; break;
}
}
FeatureBuilder1 const & GetFeature() const { return m_ft; }
m2::RectD GetLimitRect() const
{
return MercatorBounds::RectByCenterXYAndSizeInMeters(m_pt, m_thresholdM);
}
bool IsEqual(Place const & r) const
{
return (IsEqualTypes(m_type, r.m_type) &&
m_ft.GetName() == r.m_ft.GetName() &&
(IsPoint() || r.IsPoint()) &&
MercatorBounds::DistanceOnEarth(m_pt, r.m_pt) < m_thresholdM);
}
/// Check whether we need to replace place @r with place @this.
bool IsBetterThan(Place const & r) const
{
// Check ranks.
uint8_t const r1 = m_ft.GetRank();
uint8_t const r2 = r.m_ft.GetRank();
if (r1 != r2)
return (r2 < r1);
// Check types length.
// ("place-city-capital-2" is better than "place-city").
uint8_t const l1 = ftype::GetLevel(m_type);
uint8_t const l2 = ftype::GetLevel(r.m_type);
if (l1 != l2)
return (l2 < l1);
// Assume that area places has better priority than point places at the very end ...
/// @todo It was usefull when place=XXX type has any area fill style.
/// Need to review priority logic here (leave the native osm label).
return !IsPoint();
}
};
class MainFeaturesEmitter
{
using TWorldGenerator = WorldMapGenerator<feature::FeaturesCollector>;
@ -196,6 +269,9 @@ class MainFeaturesEmitter
string m_srcCoastsFile;
bool m_failOnCoasts;
// TODO(mgsergio): comment.
m4::Tree<Place> m_places;
enum TypeIndex
{
NATURAL_COASTLINE,
@ -248,47 +324,33 @@ public:
m_world.reset(new TWorldGenerator(info));
}
void operator()(FeatureBuilder1 fb)
void operator()(FeatureBuilder1 & fb)
{
uint32_t const coastType = Type(NATURAL_COASTLINE);
bool const hasCoast = fb.HasType(coastType);
static uint32_t const placeType = classif().GetTypeByPath({"place"});
uint32_t const type = fb.GetParams().FindType(placeType, 1);
if (m_coasts)
if (type != ftype::GetEmptyValue() && !fb.GetName().empty())
{
if (hasCoast)
{
CHECK(fb.GetGeomType() != feature::GEOM_POINT, ());
// leave only coastline type
fb.SetType(coastType);
(*m_coasts)(fb);
}
return;
m_places.ReplaceEqualInRect(
Place(fb, type),
[](Place const & p1, Place const & p2) { return p1.IsEqual(p2); },
[](Place const & p1, Place const & p2) { return p1.IsBetterThan(p2); });
}
if (hasCoast)
else
{
fb.PopExactType(Type(NATURAL_LAND));
fb.PopExactType(coastType);
Emit(fb);
}
else if ((fb.HasType(Type(PLACE_ISLAND)) || fb.HasType(Type(PLACE_ISLET))) &&
fb.GetGeomType() == feature::GEOM_AREA)
{
fb.AddType(Type(NATURAL_LAND));
}
if (!fb.RemoveInvalidTypes())
return;
if (m_world)
(*m_world)(fb);
if (m_countries)
(*m_countries)(fb);
}
/// @return false if coasts are not merged and FLAG_fail_on_coasts is set
bool Finish()
{
m_places.ForEach([this](Place const & p)
{
// m_places are no longer used after this point.
Emit(const_cast<FeatureBuilder1 &>(p.GetFeature()));
});
if (m_world)
m_world->DoMerge();
@ -347,6 +409,45 @@ public:
else
names.clear();
}
private:
void Emit(FeatureBuilder1 & fb)
{
uint32_t const coastType = Type(NATURAL_COASTLINE);
bool const hasCoast = fb.HasType(coastType);
if (m_coasts)
{
if (hasCoast)
{
CHECK(fb.GetGeomType() != feature::GEOM_POINT, ());
// leave only coastline type
fb.SetType(coastType);
(*m_coasts)(fb);
}
return;
}
if (hasCoast)
{
fb.PopExactType(Type(NATURAL_LAND));
fb.PopExactType(coastType);
}
else if ((fb.HasType(Type(PLACE_ISLAND)) || fb.HasType(Type(PLACE_ISLET))) &&
fb.GetGeomType() == feature::GEOM_AREA)
{
fb.AddType(Type(NATURAL_LAND));
}
if (!fb.RemoveInvalidTypes())
return;
if (m_world)
(*m_world)(fb);
if (m_countries)
(*m_countries)(fb);
}
};
} // anonymous namespace
@ -521,7 +622,7 @@ bool GenerateFeaturesImpl(feature::GenerateInfo & info)
info.m_bookingReferenceDir);
stringstream skippedElements;
// Here we can add new tags to element!!!
auto const fn = [&](OsmElement * e)
{
@ -534,7 +635,7 @@ bool GenerateFeaturesImpl(feature::GenerateInfo & info)
skippedElements << e->id << endl;
return;
}
parser.EmitElement(e);
};
@ -567,8 +668,6 @@ bool GenerateFeaturesImpl(feature::GenerateInfo & info)
LOG(LERROR, ("Can't output into", skippedElementsPath));
}
}
parser.Finish();
// Stop if coasts are not merged and FLAG_fail_on_coasts is set
if (!bucketer.Finish())

View file

@ -9,8 +9,6 @@
#include "indexer/feature_visibility.hpp"
#include "indexer/ftypes_matcher.hpp"
#include "geometry/tree4d.hpp"
#include "coding/file_writer.hpp"
#include "base/cache.hpp"
@ -24,77 +22,6 @@
namespace
{
class Place
{
FeatureBuilder1 m_ft;
m2::PointD m_pt;
uint32_t m_type;
double m_thresholdM;
bool IsPoint() const { return (m_ft.GetGeomType() == feature::GEOM_POINT); }
static bool IsEqualTypes(uint32_t t1, uint32_t t2)
{
// Use 2-arity places comparison for filtering.
// ("place-city-capital-2" is equal to "place-city")
ftype::TruncValue(t1, 2);
ftype::TruncValue(t2, 2);
return (t1 == t2);
}
public:
Place(FeatureBuilder1 const & ft, uint32_t type) : m_ft(ft), m_pt(ft.GetKeyPoint()), m_type(type)
{
using namespace ftypes;
switch (IsLocalityChecker::Instance().GetType(m_type))
{
case COUNTRY: m_thresholdM = 300000.0; break;
case STATE: m_thresholdM = 100000.0; break;
case CITY: m_thresholdM = 30000.0; break;
case TOWN: m_thresholdM = 20000.0; break;
case VILLAGE: m_thresholdM = 10000.0; break;
default: m_thresholdM = 10000.0; break;
}
}
FeatureBuilder1 const & GetFeature() const { return m_ft; }
m2::RectD GetLimitRect() const
{
return MercatorBounds::RectByCenterXYAndSizeInMeters(m_pt, m_thresholdM);
}
bool IsEqual(Place const & r) const
{
return (IsEqualTypes(m_type, r.m_type) &&
m_ft.GetName() == r.m_ft.GetName() &&
(IsPoint() || r.IsPoint()) &&
MercatorBounds::DistanceOnEarth(m_pt, r.m_pt) < m_thresholdM);
}
/// Check whether we need to replace place @r with place @this.
bool IsBetterThan(Place const & r) const
{
// Check ranks.
uint8_t const r1 = m_ft.GetRank();
uint8_t const r2 = r.m_ft.GetRank();
if (r1 != r2)
return (r2 < r1);
// Check types length.
// ("place-city-capital-2" is better than "place-city").
uint8_t const l1 = ftype::GetLevel(m_type);
uint8_t const l2 = ftype::GetLevel(r.m_type);
if (l1 != l2)
return (l2 < l1);
// Assume that area places has better priority than point places at the very end ...
/// @todo It was usefull when place=XXX type has any area fill style.
/// Need to review priority logic here (leave the native osm label).
return !IsPoint();
}
};
/// Generated features should include parent relation tags to make
/// full types matching and storing any additional info.
class RelationTagsBase
@ -243,7 +170,6 @@ protected:
}
}
};
} // namespace
/// @param TEmitter Feature accumulating policy
@ -255,7 +181,7 @@ class OsmToFeatureTranslator
TCache & m_holder;
uint32_t m_coastType;
unique_ptr<FileWriter> m_addrWriter;
m4::Tree<Place> m_places;
RelationTagsNode m_nodeRelations;
RelationTagsWay m_wayRelations;
@ -334,32 +260,25 @@ class OsmToFeatureTranslator
return params.IsValid();
}
void EmitFeatureBase(FeatureBuilder1 & ft, FeatureParams const & params)
void EmitFeatureBase(FeatureBuilder1 & ft, FeatureParams const & params) const
{
ft.SetParams(params);
if (ft.PreSerialize())
{
string addr;
if (m_addrWriter && ftypes::IsBuildingChecker::Instance()(params.m_Types) && ft.FormatFullAddress(addr))
m_addrWriter->Write(addr.c_str(), addr.size());
static uint32_t const placeType = classif().GetTypeByPath({"place"});
uint32_t const type = params.FindType(placeType, 1);
if (type != ftype::GetEmptyValue() && !ft.GetName().empty())
if (m_addrWriter && ftypes::IsBuildingChecker::Instance()(params.m_Types) &&
ft.FormatFullAddress(addr))
{
m_places.ReplaceEqualInRect(Place(ft, type),
[](Place const & p1, Place const & p2) { return p1.IsEqual(p2); },
[](Place const & p1, Place const & p2) { return p1.IsBetterThan(p2); });
m_addrWriter->Write(addr.c_str(), addr.size());
}
else
m_emitter(ft);
m_emitter(ft);
}
}
/// @param[in] params Pass by value because it can be modified.
//@{
void EmitPoint(m2::PointD const & pt, FeatureParams params, osm::Id id)
void EmitPoint(m2::PointD const & pt, FeatureParams params, osm::Id id) const
{
if (feature::RemoveNoDrawableTypes(params.m_Types, feature::GEOM_POINT))
{
@ -370,7 +289,7 @@ class OsmToFeatureTranslator
}
}
void EmitLine(FeatureBuilder1 & ft, FeatureParams params, bool isCoastLine)
void EmitLine(FeatureBuilder1 & ft, FeatureParams params, bool isCoastLine) const
{
if (isCoastLine || feature::RemoveNoDrawableTypes(params.m_Types, feature::GEOM_LINE))
{
@ -380,7 +299,7 @@ class OsmToFeatureTranslator
}
template <class MakeFnT>
void EmitArea(FeatureBuilder1 & ft, FeatureParams params, MakeFnT makeFn)
void EmitArea(FeatureBuilder1 & ft, FeatureParams params, MakeFnT makeFn) const
{
using namespace feature;
@ -571,12 +490,4 @@ public:
if (!addrFilePath.empty())
m_addrWriter.reset(new FileWriter(addrFilePath));
}
void Finish()
{
m_places.ForEach([this] (Place const & p)
{
m_emitter(p.GetFeature());
});
}
};