Using kd-tree for select appropriate water polygons

This commit is contained in:
Sergey Yershov 2015-07-15 16:41:22 +03:00 committed by Alex Zolotarev
parent 388d0bcb02
commit 5d95ae6ed5
3 changed files with 57 additions and 53 deletions

View file

@ -145,6 +145,8 @@ public:
/// Set all the parameters, except geometry type (it's set by other functions).
inline void SetParams(FeatureParams const & params) { m_params.SetParams(params); }
inline FeatureParams const & GetParams() const { return m_params; }
/// @name For OSM debugging, store original OSM id
//@{
void AddOsmId(osm::Id id);

View file

@ -3,13 +3,16 @@
#include "generator/feature_merger.hpp"
#include "generator/generate_info.hpp"
#include "geometry/tree4d.hpp"
#include "indexer/scales.hpp"
#include "coding/file_name_utils.hpp"
#include "base/logging.hpp"
#include "defines.hpp"
/// Process FeatureBuilder1 for world map. Main functions:
/// - check for visibility in world map
/// - merge linear features
@ -20,32 +23,31 @@ class WorldMapGenerator
{
FeatureOutT m_output;
uint32_t m_boundaryType;
list<m2::RegionD> m_waterRegions;
deque<m2::RegionD> m_waterRegions;
m4::Tree<size_t> m_tree;
public:
explicit EmitterImpl(feature::GenerateInfo const & info)
: m_output(info.GetTmpFileName(WORLD_FILE_NAME))
{
m_boundaryType = classif().GetTypeByPath({ "boundary", "administrative"});
LoadWatersRegionsDump(info.m_intermediateDir + WORLD_COASTS_FILE_NAME + ".rawdump");
m_boundaryType = classif().GetTypeByPath({"boundary", "administrative"});
LoadWaterGeometry(my::JoinFoldersToPath({info.m_intermediateDir},
string(WORLD_COASTS_FILE_NAME) + ".rawdump"));
LOG(LINFO, ("Output World file:", info.GetTmpFileName(WORLD_FILE_NAME)));
}
void LoadWatersRegionsDump(string const &dumpFileName)
void LoadWaterGeometry(string const & rawGeometryFileName)
{
LOG(LINFO, ("Load water polygons:", dumpFileName));
ifstream file;
file.exceptions(ifstream::badbit);
file.open(dumpFileName);
if (!file.is_open())
LOG(LCRITICAL, ("Can't open water polygons"));
LOG(LINFO, ("Loading water geometry:", rawGeometryFileName));
FileReader reader(rawGeometryFileName);
ReaderSource<FileReader> file(reader);
size_t total = 0;
while (true)
{
uint64_t numGeometries = 0;
file.read(reinterpret_cast<char *>(&numGeometries), sizeof(numGeometries));
file.Read(&numGeometries, sizeof(numGeometries));
if (numGeometries == 0)
break;
@ -56,67 +58,66 @@ class WorldMapGenerator
for (size_t i = 0; i < numGeometries; ++i)
{
uint64_t numPoints = 0;
file.read(reinterpret_cast<char *>(&numPoints), sizeof(numPoints));
file.Read(&numPoints, sizeof(numPoints));
points.resize(numPoints);
file.read(reinterpret_cast<char *>(points.data()), sizeof(m2::PointD) * numPoints);
file.Read(points.data(), sizeof(m2::PointD) * numPoints);
m_waterRegions.push_back(m2::RegionD());
m_waterRegions.back().Assign(points.begin(), points.end());
m_tree.Add(m_waterRegions.size() - 1, m_waterRegions.back().GetRect());
}
}
LOG(LINFO, ("Load", total, "water polygons"));
LOG(LINFO, ("Load", total, "water geometries"));
}
/// This function is called after merging linear features.
virtual void operator() (FeatureBuilder1 const & fb)
virtual void operator()(FeatureBuilder1 const & fb)
{
// do additional check for suitable size of feature
if (NeedPushToWorld(fb) && scales::IsGoodForLevel(scales::GetUpperWorldScale(), fb.GetLimitRect()))
if (NeedPushToWorld(fb) &&
scales::IsGoodForLevel(scales::GetUpperWorldScale(), fb.GetLimitRect()))
PushSure(fb);
}
bool IsWaterBoundaries(FeatureBuilder1 const & fb) const
{
return false;
if (fb.FindType(m_boundaryType, 2) == ftype::GetEmptyValue())
return false;
m2::PointD pts[3] = {{0, 0}, {0, 0}, {0, 0}};
size_t hits[3] = {0, 0, 0};
pts[0] = fb.GetGeometry().front().front();
pts[1] = *(fb.GetGeometry().front().begin() + fb.GetGeometry().front().size()/2);
pts[2] = fb.GetGeometry().front().back();
// For check we select first, last and middle point in line.
auto const & line = fb.GetGeometry().front();
pts[0] = line.front();
pts[1] = *(line.cbegin() + line.size() / 2);
pts[2] = line.back();
for (m2::RegionD const & region : m_waterRegions)
m_tree.ForEachInRect(fb.GetLimitRect(), [&](size_t index)
{
hits[0] += region.Contains(pts[0]) ? 1 : 0;
hits[1] += region.Contains(pts[1]) ? 1 : 0;
hits[2] += region.Contains(pts[2]) ? 1 : 0;
}
hits[0] += m_waterRegions[index].Contains(pts[0]) ? 1 : 0;
hits[1] += m_waterRegions[index].Contains(pts[1]) ? 1 : 0;
hits[2] += m_waterRegions[index].Contains(pts[2]) ? 1 : 0;
});
size_t state = (hits[0] & 0x01) + (hits[1] & 0x01) + (hits[2] & 0x01);
LOG(LINFO, ("hits:", hits, "Boundary state:", state, "Dump:", DebugPrint(fb)));
// whole border on land
if (state == 0)
return false;
size_t const state = (hits[0] & 0x01) + (hits[1] & 0x01) + (hits[2] & 0x01);
// whole border on water
if (state == 3)
{
LOG(LINFO, ("Boundary", (state == 3 ? "deleted" : "kept"), "hits:", hits,
DebugPrint(fb.GetParams()), fb.GetOsmIdsString()));
return true;
}
LOG(LINFO, ("Found partial boundary"));
// state == 0 whole border on land, else partial intersection
return false;
}
bool NeedPushToWorld(FeatureBuilder1 const & fb) const
{
if (IsWaterBoundaries(fb))
{
LOG(LINFO, ("Skip boundary"));
return false;
}
// GetMinFeatureDrawScale also checks suitable size for AREA features
return (scales::GetUpperWorldScale() >= fb.GetMinFeatureDrawScale());
}
@ -131,21 +132,19 @@ class WorldMapGenerator
public:
template <class TInfo>
explicit WorldMapGenerator(TInfo const & info)
: m_worldBucket(info),
m_merger(POINT_COORD_BITS - (scales::GetUpperScale() - scales::GetUpperWorldScale()) / 2)
: m_worldBucket(info),
m_merger(POINT_COORD_BITS - (scales::GetUpperScale() - scales::GetUpperWorldScale()) / 2)
{
// Do not strip last types for given tags,
// for example, do not cut 'admin_level' in 'boundary-administrative-XXX'.
char const * arr1[][3] = {
{ "boundary", "administrative", "2" },
{ "boundary", "administrative", "3" },
{ "boundary", "administrative", "4" }
};
char const * arr1[][3] = {{"boundary", "administrative", "2"},
{"boundary", "administrative", "3"},
{"boundary", "administrative", "4"}};
for (size_t i = 0; i < ARRAY_SIZE(arr1); ++i)
m_typesCorrector.SetDontNormalizeType(arr1[i]);
char const * arr2[] = { "boundary", "administrative", "4", "state" };
char const * arr2[] = {"boundary", "administrative", "4", "state"};
m_typesCorrector.SetDontNormalizeType(arr2);
/// @todo It's not obvious to integrate link->way conversion.
@ -174,6 +173,10 @@ public:
{
if (m_worldBucket.NeedPushToWorld(fb))
{
// skip visible water boundary
if (m_worldBucket.IsWaterBoundaries(fb))
return;
if (fb.GetGeomType() == feature::GEOM_LINE)
{
MergedFeatureBuilder1 * p = m_typesCorrector(fb);
@ -188,10 +191,7 @@ public:
}
}
void DoMerge()
{
m_merger.DoMerge(m_worldBucket);
}
void DoMerge() { m_merger.DoMerge(m_worldBucket); }
};
template <class FeatureOutT>
@ -201,7 +201,10 @@ class CountryMapGenerator
public:
template <class TInfo>
explicit CountryMapGenerator(TInfo const & info) : m_bucket(info) {}
explicit CountryMapGenerator(TInfo const & info)
: m_bucket(info)
{
}
void operator()(FeatureBuilder1 fb)
{

View file

@ -431,7 +431,6 @@ string DebugPrint(FeatureParams const & p)
string res = "Types";
for (size_t i = 0; i < p.m_Types.size(); ++i)
res += (" : " + c.GetReadableObjectName(p.m_Types[i]));
res += "\n";
return (res + p.DebugString());
}