[generator] Improved processing of city boundaries in region kv

This commit is contained in:
Maksim Andrianov 2018-09-11 16:12:19 +03:00 committed by mpimenov
parent a36d03d9db
commit 7fd1ee481e
24 changed files with 1189 additions and 773 deletions

View file

@ -73,6 +73,8 @@ public:
// Returns the source type of the object.
Type GetType() const;
bool IsValid() const { return m_encodedId != kInvalid; }
bool operator<(GeoObjectId const & other) const { return m_encodedId < other.m_encodedId; }
bool operator==(GeoObjectId const & other) const { return m_encodedId == other.m_encodedId; }
bool operator!=(GeoObjectId const & other) const { return !(*this == other); }

View file

@ -87,12 +87,23 @@ set(SRC
place.cpp
relation_tags.cpp
relation_tags.hpp
region_info_collector.cpp
region_info_collector.hpp
region_meta.cpp
region_meta.hpp
regions.cpp
regions.hpp
regions/city.hpp
regions/node.cpp
regions/node.hpp
regions/region.cpp
regions/region.hpp
regions/region_base.cpp
regions/region_base.hpp
regions/region_info_collector.cpp
regions/region_info_collector.hpp
regions/regions_builder.cpp
regions/regions_builder.hpp
regions/to_string_policy.cpp
regions/to_string_policy.hpp
restriction_collector.cpp
restriction_collector.hpp
restriction_generator.cpp

View file

@ -55,6 +55,7 @@ public:
void SetAreaAddHoles(Geometry const & holes);
void SetArea() { m_params.SetGeomType(feature::GEOM_AREA); }
bool IsPoint() const { return (GetGeomType() == feature::GEOM_POINT); }
bool IsLine() const { return (GetGeomType() == feature::GEOM_LINE); }
bool IsArea() const { return (GetGeomType() == feature::GEOM_AREA); }

View file

@ -1,7 +1,7 @@
#include "testing/testing.hpp"
#include "generator/osm_element.hpp"
#include "generator/region_info_collector.hpp"
#include "generator/regions/region_info_collector.hpp"
#include "coding/file_name_utils.hpp"
@ -15,6 +15,8 @@
#include <utility>
#include <vector>
using namespace generator::regions;
namespace
{
using Tags = std::vector<std::pair<std::string, std::string>>;
@ -45,13 +47,13 @@ base::GeoObjectId CastId(uint64_t id)
UNIT_TEST(RegionInfoCollector_Add)
{
generator::RegionInfoCollector regionInfoCollector;
RegionInfoCollector regionInfoCollector;
regionInfoCollector.Add(CastId(kOsmElementCity.id), kOsmElementCity);
{
auto const regionData = regionInfoCollector.Get(CastId(kOsmElementCity.id));
TEST_EQUAL(regionData.GetOsmId(), CastId(kOsmElementCity.id), ());
TEST_EQUAL(regionData.GetAdminLevel(), generator::AdminLevel::Six, ());
TEST_EQUAL(regionData.GetPlaceType(), generator::PlaceType::City, ());
TEST_EQUAL(regionData.GetAdminLevel(), AdminLevel::Six, ());
TEST_EQUAL(regionData.GetPlaceType(), PlaceType::City, ());
TEST(!regionData.HasIsoCodeAlpha2(), ());
TEST(!regionData.HasIsoCodeAlpha3(), ());
TEST(!regionData.HasIsoCodeAlphaNumeric(), ());
@ -61,8 +63,8 @@ UNIT_TEST(RegionInfoCollector_Add)
{
auto const regionData = regionInfoCollector.Get(CastId(kOsmElementCountry.id));
TEST_EQUAL(regionData.GetOsmId(), CastId(kOsmElementCountry.id), ());
TEST_EQUAL(regionData.GetAdminLevel(), generator::AdminLevel::Two, ());
TEST_EQUAL(regionData.GetPlaceType(), generator::PlaceType::Unknown, ());
TEST_EQUAL(regionData.GetAdminLevel(), AdminLevel::Two, ());
TEST_EQUAL(regionData.GetPlaceType(), PlaceType::Unknown, ());
TEST(regionData.HasIsoCodeAlpha2(), ());
TEST(regionData.HasIsoCodeAlpha3(), ());
@ -76,8 +78,8 @@ UNIT_TEST(RegionInfoCollector_Add)
{
auto const regionDataEmpty = regionInfoCollector.Get(CastId(kOsmElementEmpty.id));
TEST_EQUAL(regionDataEmpty.GetOsmId(), CastId(kOsmElementEmpty.id), ());
TEST_EQUAL(regionDataEmpty.GetAdminLevel(), generator::AdminLevel::Unknown, ());
TEST_EQUAL(regionDataEmpty.GetPlaceType(), generator::PlaceType::Unknown, ());
TEST_EQUAL(regionDataEmpty.GetAdminLevel(), AdminLevel::Unknown, ());
TEST_EQUAL(regionDataEmpty.GetPlaceType(), PlaceType::Unknown, ());
TEST(!regionDataEmpty.HasIsoCodeAlpha2(), ());
TEST(!regionDataEmpty.HasIsoCodeAlpha3(), ());
TEST(!regionDataEmpty.HasIsoCodeAlphaNumeric(), ());
@ -86,18 +88,18 @@ UNIT_TEST(RegionInfoCollector_Add)
UNIT_TEST(RegionInfoCollector_Get)
{
generator::RegionInfoCollector regionInfoCollector;
RegionInfoCollector regionInfoCollector;
regionInfoCollector.Add(CastId(kOsmElementCity.id), kOsmElementCity);
auto const regionData = regionInfoCollector.Get(CastId(kOsmElementCity.id));
TEST_EQUAL(regionData.GetOsmId(), CastId(kOsmElementCity.id), ());
TEST_EQUAL(regionData.GetAdminLevel(), generator::AdminLevel::Six, ());
TEST_EQUAL(regionData.GetPlaceType(), generator::PlaceType::City, ());
TEST_EQUAL(regionData.GetAdminLevel(), AdminLevel::Six, ());
TEST_EQUAL(regionData.GetPlaceType(), PlaceType::City, ());
}
UNIT_TEST(RegionInfoCollector_Exists)
{
generator::RegionInfoCollector regionInfoCollector;
RegionInfoCollector regionInfoCollector;
regionInfoCollector.Add(CastId(kOsmElementCity.id), kOsmElementCity);
regionInfoCollector.Add(CastId(kOsmElementCountry.id), kOsmElementCountry);
@ -131,7 +133,7 @@ UNIT_TEST(RegionInfoCollector_Exists)
UNIT_TEST(RegionInfoCollector_Save)
{
generator::RegionInfoCollector regionInfoCollector;
RegionInfoCollector regionInfoCollector;
regionInfoCollector.Add(CastId(kOsmElementCity.id), kOsmElementCity);
auto const regionCity = regionInfoCollector.Get(CastId(kOsmElementCity.id));
regionInfoCollector.Add(CastId(kOsmElementCountry.id), kOsmElementCountry);
@ -143,7 +145,7 @@ UNIT_TEST(RegionInfoCollector_Save)
auto const name = base::JoinPath(tmpDir, "RegionInfoCollector.bin");
regionInfoCollector.Save(name);
{
generator::RegionInfoCollector regionInfoCollector(name);
RegionInfoCollector regionInfoCollector(name);
auto const rRegionData = regionInfoCollector.Get(CastId(kOsmElementCity.id));
TEST_EQUAL(regionCity.GetOsmId(), rRegionData.GetOsmId(), ());
@ -155,7 +157,7 @@ UNIT_TEST(RegionInfoCollector_Save)
}
{
generator::RegionInfoCollector regionInfoCollector(name);
RegionInfoCollector regionInfoCollector(name);
auto const rRegionData = regionInfoCollector.Get(CastId(kOsmElementCountry.id));
TEST_EQUAL(regionCountry.GetOsmId(), rRegionData.GetOsmId(), ());

View file

@ -2,7 +2,9 @@
#include "generator/osm_element.hpp"
#include "generator/regions.hpp"
#include "generator/region_info_collector.hpp"
#include "generator/regions/region_info_collector.hpp"
#include "generator/regions/regions_builder.hpp"
#include "generator/regions/to_string_policy.hpp"
#include "platform/platform.hpp"
@ -18,7 +20,6 @@
#include <vector>
#include <utility>
using namespace generator;
using namespace generator::regions;
namespace

View file

@ -450,7 +450,7 @@ int main(int argc, char ** argv)
if (FLAGS_generate_cameras)
{
string const camerasFilename =
genInfo.GetIntermediateFileName(CAMERAS_TO_WAYS_FILENAME);
genInfo.GetIntermediateFileName(CAMERAS_TO_WAYS_FILENAME);
BuildCamerasInfo(datFile, camerasFilename, osmToFeatureFilename);
}

View file

@ -11,7 +11,7 @@
#include "generator/osm_o5m_source.hpp"
#include "generator/osm_xml_source.hpp"
#include "generator/polygonizer.hpp"
#include "generator/region_info_collector.hpp"
#include "generator/regions/region_info_collector.hpp"
#include "generator/tag_admixer.hpp"
#include "generator/towns_dumper.hpp"
#include "generator/translator_factory.hpp"
@ -301,13 +301,13 @@ bool GenerateRegionFeatures(feature::GenerateInfo & info, shared_ptr<EmitterInte
{
auto preEmit = [](OsmElement * e) { return true; };
auto cache = LoadCache(info);
RegionInfoCollector regionInfoCollector;
regions::RegionInfoCollector regionInfoCollector;
auto translator = CreateTranslator(TranslatorType::Region, emitter, cache, regionInfoCollector);
if (!GenerateRaw(info, emitter, preEmit, translator))
return false;
auto const filename = info.GetTmpFileName(info.m_fileName, RegionInfoCollector::kDefaultExt);
auto const filename = info.GetTmpFileName(info.m_fileName, regions::RegionInfoCollector::kDefaultExt);
regionInfoCollector.Save(filename);
return true;
}

View file

@ -2,666 +2,186 @@
#include "generator/feature_builder.hpp"
#include "generator/generate_info.hpp"
#include "generator/regions/city.hpp"
#include "generator/regions/node.hpp"
#include "generator/regions/regions_builder.hpp"
#include "generator/regions/to_string_policy.hpp"
#include "platform/platform.hpp"
#include "coding/file_name_utils.hpp"
#include "coding/transliteration.hpp"
#include "base/control_flow.hpp"
#include "base/macros.hpp"
#include "base/logging.hpp"
#include "base/timer.hpp"
#include <chrono>
#include <algorithm>
#include <fstream>
#include <numeric>
#include <memory>
#include <queue>
#include <set>
#include <string>
#include <thread>
#include <tuple>
#include <unordered_set>
#include <vector>
#include "defines.hpp"
#include "3party/jansson/myjansson.hpp"
#include "3party/ThreadPool/ThreadPool.h"
#include "3party/boost/boost/math/special_functions/relative_difference.hpp"
#include "3party/boost/boost/range/adaptor/reversed.hpp"
namespace generator
{
namespace regions
{
namespace
{
using MergeFunc = std::function<Node::Ptr(Node::Ptr, Node::Ptr)>;
using PointCitiesMap = std::unordered_map<base::GeoObjectId, City>;
class JsonPolicy : public ToStringPolicyInterface
struct RegionsFixer
{
public:
JsonPolicy(bool extendedOutput = false)
: m_extendedOutput(extendedOutput)
RegionsFixer(RegionsBuilder::Regions & regions, PointCitiesMap const & pointCitiesMap)
: m_regions(regions), m_pointCitiesMap(pointCitiesMap)
{
SplitRegionsByAdminCenter();
CreateNameRegionMap();
}
std::string ToString(Node::PtrList const & nodePtrList) override
RegionsBuilder::Regions & FixRegions()
{
auto const & main = nodePtrList.front()->GetData();
auto const & country = nodePtrList.back()->GetData();
auto geometry = base::NewJSONObject();
ToJSONObject(*geometry, "type", "Point");
auto coordinates = base::NewJSONArray();
auto const center = main.GetCenter();
ToJSONArray(*coordinates, center.get<0>());
ToJSONArray(*coordinates, center.get<1>());
ToJSONObject(*geometry, "coordinates", coordinates);
auto localeEn = base::NewJSONObject();
auto address = base::NewJSONObject();
for (auto const & p : boost::adaptors::reverse(nodePtrList))
SortRegionsByArea();
std::vector<bool> unsuitable;
unsuitable.resize(m_regionsWithAdminCenter.size());
for (size_t i = 0; i < m_regionsWithAdminCenter.size(); ++i)
{
auto const & region = p->GetData();
auto const label = region.GetLabel();
ToJSONObject(*address, label, region.GetName());
if (m_extendedOutput)
if (unsuitable[i])
continue;
auto & regionWithAdminCenter = m_regionsWithAdminCenter[i];
if (regionWithAdminCenter.IsCountry())
continue;
auto const id = regionWithAdminCenter.GetAdminCenterId();
if (!m_pointCitiesMap.count(id))
continue;
auto const & adminCenter = m_pointCitiesMap.at(id);
auto const placeType = adminCenter.GetPlaceType();
if (placeType == PlaceType::Town || placeType == PlaceType::City)
{
ToJSONObject(*address, label + "_i", region.GetId().GetSerialId());
ToJSONObject(*address, label + "_a", region.GetArea());
ToJSONObject(*address, label + "_r", region.GetRank());
for (size_t j = i + 1; j < m_regionsWithAdminCenter.size() - 1; ++j)
{
if (m_regionsWithAdminCenter[j].ContainsRect(regionWithAdminCenter))
unsuitable[j] = true;
}
}
ToJSONObject(*localeEn, label, region.GetEnglishOrTransliteratedName());
if (ExistsRegionAsCity(adminCenter))
continue;
regionWithAdminCenter.SetInfo(adminCenter);
}
auto locales = base::NewJSONObject();
ToJSONObject(*locales, "en", localeEn);
auto properties = base::NewJSONObject();
ToJSONObject(*properties, "name", main.GetName());
ToJSONObject(*properties, "rank", main.GetRank());
ToJSONObject(*properties, "address", address);
ToJSONObject(*properties, "locales", locales);
if (country.HasIsoCode())
ToJSONObject(*properties, "code", country.GetIsoCode());
auto feature = base::NewJSONObject();
ToJSONObject(*feature, "type", "Feature");
ToJSONObject(*feature, "geometry", geometry);
ToJSONObject(*feature, "properties", properties);
auto const cstr = json_dumps(feature.get(), JSON_COMPACT);
std::unique_ptr<char, JSONFreeDeleter> buffer(cstr);
return buffer.get();
std::move(std::begin(m_regionsWithAdminCenter), std::end(m_regionsWithAdminCenter),
std::back_inserter(m_regions));
m_regionsWithAdminCenter = {};
return m_regions;
}
private:
bool m_extendedOutput;
bool ExistsRegionAsCity(const City & city)
{
auto const range = m_nameRegionMap.equal_range(city.GetName());
for (auto it = range.first; it != range.second; ++it)
{
Region const & r = it->second;
if (city.GetRank() == r.GetRank() && r.Contains(city))
return true;
}
return false;
}
void SplitRegionsByAdminCenter()
{
auto const pred = [](Region const & region) { return region.GetAdminCenterId().IsValid(); };
std::copy_if(std::begin(m_regions), std::end(m_regions),
std::back_inserter(m_regionsWithAdminCenter), pred);
auto const it = std::remove_if(std::begin(m_regions), std::end(m_regions), pred);
m_regions.erase(it, std::end(m_regions));
}
void CreateNameRegionMap()
{
for (auto const & region : m_regions)
{
auto const name = region.GetName();
if (region.GetLabel() == "locality" && !name.empty())
m_nameRegionMap.emplace(name, region);
}
}
void SortRegionsByArea()
{
auto const cmp = [](Region const & l, Region const & r) { return l.GetArea() < r.GetArea(); };
std::sort(std::begin(m_regions), std::end(m_regions), cmp);
}
RegionsBuilder::Regions & m_regions;
PointCitiesMap const & m_pointCitiesMap;
RegionsBuilder::Regions m_regionsWithAdminCenter;
std::multimap<std::string, std::reference_wrapper<Region const>> m_nameRegionMap;
};
// This function is for debugging only and can be used for statistics collection.
size_t TreeSize(Node::Ptr node)
{
if (node == nullptr)
return 0;
size_t size = 1;
for (auto const & n : node->GetChildren())
size += TreeSize(n);
return size;
}
// This function is for debugging only and can be used for statistics collection.
size_t MaxDepth(Node::Ptr node)
{
if (node == nullptr)
return 0;
size_t depth = 1;
for (auto const & n : node->GetChildren())
depth = std::max(MaxDepth(n), depth);
return depth;
}
// This function is for debugging only and can be used for statistics collection.
void PrintTree(Node::Ptr node, std::ostream & stream = std::cout, std::string prefix = "",
bool isTail = true)
{
auto const & childern = node->GetChildren();
stream << prefix;
if (isTail)
{
stream << "└───";
prefix += " ";
}
else
{
stream << "├───";
prefix += "";
}
auto const & d = node->GetData();
auto const point = d.GetCenter();
stream << d.GetName() << "<" << d.GetEnglishOrTransliteratedName() << "> ("
<< d.GetId()
<< ";" << d.GetLabel()
<< ";" << static_cast<size_t>(d.GetRank())
<< ";[" << point.get<0>() << "," << point.get<1>() << "])"
<< std::endl;
for (size_t i = 0, size = childern.size(); i < size; ++i)
PrintTree(childern[i], stream, prefix, i == size - 1);
}
void DebugPrintCountry(Node::Ptr tree, std::ostream & stream = std::cout)
{
stream << "COUNTRY: " << tree->GetData().GetName() << std::endl;
stream << "MAX DEPTH: " << MaxDepth(tree) << std::endl;
stream << "TREE SIZE: " << TreeSize(tree) << std::endl;
PrintTree(tree, stream);
stream << std::endl;
}
template <typename BoostGeometry, typename FbGeometry>
void FillBoostGeometry(BoostGeometry & geometry, FbGeometry const & fbGeometry)
{
geometry.reserve(fbGeometry.size());
for (auto const & p : fbGeometry)
boost::geometry::append(geometry, Region::BoostPoint{p.x, p.y});
}
RegionsBuilder::Regions ReadRegionsFromTmpMwm(feature::GenerateInfo const & genInfo,
RegionInfoCollector const & regionsInfoCollector)
std::tuple<RegionsBuilder::Regions, PointCitiesMap>
ReadDatasetFromTmpMwm(feature::GenerateInfo const & genInfo, RegionInfoCollector const & collector)
{
RegionsBuilder::Regions regions;
PointCitiesMap pointCitiesMap;
auto const tmpMwmFilename = genInfo.GetTmpFileName(genInfo.m_fileName);
auto const toDo = [&regions, &regionsInfoCollector](FeatureBuilder1 const & fb, uint64_t /* currPos */)
auto const toDo = [&regions, &pointCitiesMap, &collector](FeatureBuilder1 const & fb, uint64_t /* currPos */)
{
// We expect only the type of osm of the object - relation. But some settlements can be
// presented as ways. We must remember about this.
if (!fb.IsArea() || !fb.IsGeometryClosed())
return;
auto const id = fb.GetMostGenericOsmId();
auto region = Region(fb, regionsInfoCollector.Get(id));
auto const & label = region.GetLabel();
auto const & name = region.GetName();
if (label.empty() || name.empty())
return;
regions.emplace_back(std::move(region));
if (fb.IsArea() && fb.IsGeometryClosed())
{
auto const id = fb.GetMostGenericOsmId();
auto region = Region(fb, collector.Get(id));
regions.emplace_back(std::move(region));
}
else if (fb.IsPoint())
{
auto const id = fb.GetMostGenericOsmId();
pointCitiesMap.emplace(id, City(fb, collector.Get(id)));
}
};
feature::ForEachFromDatRawFormat(tmpMwmFilename, toDo);
return regions;
return std::make_tuple(regions, pointCitiesMap);
}
bool LessNodePtrByName(Node::Ptr l, Node::Ptr r)
void FilterRegions(RegionsBuilder::Regions & regions)
{
auto const & lRegion = l->GetData();
auto const & rRegion = r->GetData();
return lRegion.GetName() < rRegion.GetName();
}
Node::PtrList MergeChildren(Node::PtrList const & l, Node::PtrList const & r, Node::Ptr newParent)
{
Node::PtrList result(l);
std::copy(std::begin(r), std::end(r), std::back_inserter(result));
for (auto & p : result)
p->SetParent(newParent);
std::sort(std::begin(result), std::end(result), LessNodePtrByName);
return result;
}
Node::PtrList NormalizeChildren(Node::PtrList const & children, MergeFunc mergeTree)
{
Node::PtrList uniqueChildren;
auto const pred = [](Node::Ptr l, Node::Ptr r)
auto const pred = [](Region const & region)
{
auto const & lRegion = l->GetData();
auto const & rRegion = r->GetData();
return lRegion.GetName() == rRegion.GetName();
auto const & label = region.GetLabel();
auto const & name = region.GetName();
return label.empty() || name.empty();
};
std::unique_copy(std::begin(children), std::end(children),
std::back_inserter(uniqueChildren), pred);
Node::PtrList result;
for (auto const & ch : uniqueChildren)
{
auto const bounds = std::equal_range(std::begin(children), std::end(children),
ch, LessNodePtrByName);
auto merged = std::accumulate(bounds.first, bounds.second, Node::Ptr(), mergeTree);
result.emplace_back(std::move(merged));
}
return result;
}
Node::Ptr MergeHelper(Node::Ptr l, Node::Ptr r, MergeFunc mergeTree)
{
auto const & lChildren = l->GetChildren();
auto const & rChildren = r->GetChildren();
auto const children = MergeChildren(lChildren, rChildren, l);
if (children.empty())
return l;
auto resultChildren = NormalizeChildren(children, mergeTree);
l->SetChildren(std::move(resultChildren));
r->RemoveChildren();
return l;
}
// This function merges two trees if the roots have the same name.
Node::Ptr MergeTree(Node::Ptr l, Node::Ptr r)
{
if (l == nullptr)
return r;
if (r == nullptr)
return l;
auto const & lRegion = l->GetData();
auto const & rRegion = r->GetData();
if (lRegion.GetName() != rRegion.GetName())
return nullptr;
if (lRegion.GetArea() > rRegion.GetArea())
return MergeHelper(l, r, MergeTree);
else
return MergeHelper(r, l, MergeTree);
}
// This function corrects the tree. It traverses the whole node and unites children with
// the same names.
void NormalizeTree(Node::Ptr tree)
{
if (tree == nullptr)
return;
auto & children = tree->GetChildren();
std::sort(std::begin(children), std::end(children), LessNodePtrByName);
auto newChildren = NormalizeChildren(children, MergeTree);
tree->SetChildren(std::move(newChildren));
for (auto const & ch : tree->GetChildren())
NormalizeTree(ch);
}
} // namespace
Region::Region(FeatureBuilder1 const & fb, RegionDataProxy const & rd)
: m_name(fb.GetParams().name),
m_regionData(rd),
m_polygon(std::make_shared<BoostPolygon>())
{
FillPolygon(fb);
auto rect = fb.GetLimitRect();
m_rect = BoostRect({{rect.minX(), rect.minY()}, {rect.maxX(), rect.maxY()}});
m_area = boost::geometry::area(*m_polygon);
}
std::string Region::GetName(int8_t lang) const
{
std::string s;
VERIFY(m_name.GetString(lang, s) != s.empty(), ());
return s;
}
std::string Region::GetEnglishOrTransliteratedName() const
{
std::string s = GetName(StringUtf8Multilang::kEnglishCode);
if (!s.empty())
return s;
auto const fn = [&s](int8_t code, std::string const & name)
{
if (code != StringUtf8Multilang::kDefaultCode &&
Transliteration::Instance().Transliterate(name, code, s))
{
return base::ControlFlow::Break;
}
return base::ControlFlow::Continue;
};
m_name.ForEach(fn);
return s;
}
void Region::DeletePolygon()
{
m_polygon = nullptr;
}
void Region::FillPolygon(FeatureBuilder1 const & fb)
{
CHECK(m_polygon, ());
auto const & fbGeometry = fb.GetGeometry();
CHECK(!fbGeometry.empty(), ());
auto it = std::begin(fbGeometry);
FillBoostGeometry(m_polygon->outer(), *it);
m_polygon->inners().resize(fbGeometry.size() - 1);
int i = 0;
++it;
for (; it != std::end(fbGeometry); ++it)
FillBoostGeometry(m_polygon->inners()[i++], *it);
boost::geometry::correct(*m_polygon);
}
bool Region::IsCountry() const
{
static auto const kAdminLevelCountry = AdminLevel::Two;
return m_regionData.GetAdminLevel() == kAdminLevelCountry;
}
bool Region::HasIsoCode() const
{
return m_regionData.HasIsoCodeAlpha2();
}
std::string Region::GetIsoCode() const
{
return m_regionData.GetIsoCodeAlpha2();
}
bool Region::Contains(Region const & smaller) const
{
CHECK(m_polygon, ());
CHECK(smaller.m_polygon, ());
return boost::geometry::covered_by(*smaller.m_polygon, *m_polygon);
}
double Region::CalculateOverlapPercentage(Region const & other) const
{
CHECK(m_polygon, ());
CHECK(other.m_polygon, ());
std::vector<BoostPolygon> coll;
boost::geometry::intersection(*other.m_polygon, *m_polygon, coll);
auto const min = std::min(boost::geometry::area(*other.m_polygon),
boost::geometry::area(*m_polygon));
auto const binOp = [] (double x, BoostPolygon const & y) { return x + boost::geometry::area(y); };
auto const sum = std::accumulate(std::begin(coll), std::end(coll), 0., binOp);
return (sum / min) * 100;
}
bool Region::ContainsRect(Region const & smaller) const
{
return boost::geometry::covered_by(smaller.m_rect, m_rect);
}
// The values of the administrative level and place are indirectly dependent.
// This is used when calculating the rank.
uint8_t Region::GetRank() const
{
auto const adminLevel = m_regionData.GetAdminLevel();
auto const placeType = m_regionData.GetPlaceType();
switch (adminLevel)
{
case AdminLevel::Two:
case AdminLevel::Four: return static_cast<uint8_t>(adminLevel);
default: break;
}
switch (placeType)
{
case PlaceType::City:
case PlaceType::Town:
case PlaceType::Village:
case PlaceType::Hamlet: return static_cast<uint8_t>(placeType);
default: break;
}
switch (adminLevel)
{
case AdminLevel::Six: return static_cast<uint8_t>(adminLevel);
default: break;
}
switch (placeType)
{
case PlaceType::Suburb:
case PlaceType::Neighbourhood:
case PlaceType::Locality:
case PlaceType::IsolatedDwelling: return static_cast<uint8_t>(placeType);
default: break;
}
return kNoRank;
}
std::string Region::GetLabel() const
{
auto const adminLevel = m_regionData.GetAdminLevel();
auto const placeType = m_regionData.GetPlaceType();
switch (adminLevel)
{
case AdminLevel::Two: return "country";
case AdminLevel::Four: return "region";
default: break;
}
switch (placeType)
{
case PlaceType::City:
case PlaceType::Town:
case PlaceType::Village:
case PlaceType::Hamlet: return "locality";
default: break;
}
switch (adminLevel)
{
case AdminLevel::Six: return "subregion";
default: break;
}
switch (placeType)
{
case PlaceType::Suburb:
case PlaceType::Neighbourhood: return "suburb";
case PlaceType::Locality:
case PlaceType::IsolatedDwelling: return "sublocality";
default: break;
}
return "";
}
Region::BoostPoint Region::GetCenter() const
{
BoostPoint p;
boost::geometry::centroid(m_rect, p);
return p;
}
Region::BoostRect const & Region::GetRect() const
{
return m_rect;
}
std::shared_ptr<Region::BoostPolygon> const Region::GetPolygon() const
{
return m_polygon;
}
double Region::GetArea() const
{
return m_area;
}
base::GeoObjectId Region::GetId() const
{
return m_regionData.GetOsmId();
}
RegionsBuilder::RegionsBuilder(Regions && regions)
: RegionsBuilder(std::move(regions), std::make_unique<JsonPolicy>())
{
}
RegionsBuilder::RegionsBuilder(Regions && regions,
std::unique_ptr<ToStringPolicyInterface> toStringPolicy)
: m_toStringPolicy(std::move(toStringPolicy))
{
ASSERT(m_toStringPolicy, ());
auto const isCountry = [](Region const & r){ return r.IsCountry(); };
std::copy_if(std::begin(regions), std::end(regions), std::back_inserter(m_countries), isCountry);
auto const it = std::remove_if(std::begin(regions), std::end(regions), isCountry);
auto const it = std::remove_if(std::begin(regions), std::end(regions), pred);
regions.erase(it, std::end(regions));
auto const cmp = [](Region const & l, Region const & r) { return l.GetArea() > r.GetArea(); };
std::sort(std::begin(m_countries), std::end(m_countries), cmp);
MakeCountryTrees(regions);
}
RegionsBuilder::Regions const & RegionsBuilder::GetCountries() const
RegionsBuilder::Regions ReadData(feature::GenerateInfo const & genInfo,
RegionInfoCollector const & regionsInfoCollector)
{
return m_countries;
RegionsBuilder::Regions regions;
PointCitiesMap pointCitiesMap;
std::tie(regions, pointCitiesMap) = ReadDatasetFromTmpMwm(genInfo, regionsInfoCollector);
RegionsFixer fixer(regions, pointCitiesMap);
regions = fixer.FixRegions();
FilterRegions(regions);
return std::move(regions);
}
RegionsBuilder::StringsList RegionsBuilder::GetCountryNames() const
{
StringsList result;
std::unordered_set<std::string> set;
for (auto const & c : GetCountries())
{
auto name = c.GetName();
if (set.insert(name).second)
result.emplace_back(std::move(name));
}
return result;
}
RegionsBuilder::CountryTrees const & RegionsBuilder::GetCountryTrees() const
{
return m_countryTrees;
}
RegionsBuilder::IdStringList RegionsBuilder::ToIdStringList(Node::Ptr tree) const
{
IdStringList result;
std::queue<Node::Ptr> queue;
queue.push(tree);
while (!queue.empty())
{
const auto el = queue.front();
queue.pop();
Node::PtrList nodes;
auto current = el;
while (current)
{
nodes.push_back(current);
current = current->GetParent();
}
auto string = m_toStringPolicy->ToString(nodes);
auto const id = nodes.front()->GetData().GetId();
result.emplace_back(std::make_pair(id, std::move(string)));
for (auto const & n : el->GetChildren())
queue.push(n);
}
return result;
}
Node::PtrList RegionsBuilder::MakeSelectedRegionsByCountry(Region const & country,
Regions const & allRegions)
{
Regions regionsInCountry;
auto filterCopy = [&country] (const Region & r) { return country.ContainsRect(r); };
std::copy_if(std::begin(allRegions), std::end(allRegions),
std::back_inserter(regionsInCountry), filterCopy);
regionsInCountry.emplace_back(country);
auto const comp = [](const Region & l, const Region & r)
{
auto const lArea = l.GetArea();
auto const rArea = r.GetArea();
return lArea != rArea ? lArea > rArea : l.GetRank() < r.GetRank();
};
std::sort(std::begin(regionsInCountry), std::end(regionsInCountry), comp);
Node::PtrList nodes;
nodes.reserve(regionsInCountry.size());
for (auto && region : regionsInCountry)
nodes.emplace_back(std::make_shared<Node>(std::move(region)));
return nodes;
}
Node::Ptr RegionsBuilder::BuildCountryRegionTree(Region const & country,
Regions const & allRegions)
{
auto nodes = MakeSelectedRegionsByCountry(country, allRegions);
while (nodes.size() > 1)
{
auto itFirstNode = std::rbegin(nodes);
auto & firstRegion = (*itFirstNode)->GetData();
auto itCurr = itFirstNode + 1;
for (; itCurr != std::rend(nodes); ++itCurr)
{
auto const & currRegion = (*itCurr)->GetData();
// If Contains returns false, then we calculate the percent overlap of polygons.
// We believe that if one polygon overlaps by 98 percent, then we can assume that one
// contains another.
auto const kAvaliableOverlapPercentage = 98;
if (currRegion.ContainsRect(firstRegion) &&
(currRegion.Contains(firstRegion) ||
currRegion.CalculateOverlapPercentage(firstRegion) > kAvaliableOverlapPercentage))
{
// In general, we assume that a region with the larger rank has the larger area.
// But sometimes it does not. In this case, we will make an inversion.
if (firstRegion.GetRank() < currRegion.GetRank())
{
(*itCurr)->SetParent(*itFirstNode);
(*itFirstNode)->AddChild(*itCurr);
}
else
{
(*itFirstNode)->SetParent(*itCurr);
(*itCurr)->AddChild(*itFirstNode);
}
// We want to free up memory.
firstRegion.DeletePolygon();
nodes.pop_back();
break;
}
}
if (itCurr == std::rend(nodes))
nodes.pop_back();
}
return nodes.empty() ? std::shared_ptr<Node>() : nodes.front();
}
void RegionsBuilder::MakeCountryTrees(Regions const & regions)
{
std::vector<std::future<Node::Ptr>> results;
{
auto const cpuCount = std::thread::hardware_concurrency();
ASSERT_GREATER(cpuCount, 0, ());
ThreadPool threadPool(cpuCount);
for (auto const & country : GetCountries())
{
auto f = threadPool.enqueue(&RegionsBuilder::BuildCountryRegionTree, country, regions);
results.emplace_back(std::move(f));
}
}
for (auto & r : results)
{
auto tree = r.get();
m_countryTrees.emplace(tree->GetData().GetName(), std::move(tree));
}
}
} // namespace
} // namespace regions
bool GenerateRegions(feature::GenerateInfo const & genInfo)
@ -672,12 +192,10 @@ bool GenerateRegions(feature::GenerateInfo const & genInfo)
auto timer = base::Timer();
Transliteration::Instance().Init(GetPlatform().ResourcesDir());
auto const collectorFilename =
genInfo.GetTmpFileName(genInfo.m_fileName, RegionInfoCollector::kDefaultExt);
auto const collectorFilename = genInfo.GetTmpFileName(genInfo.m_fileName,
RegionInfoCollector::kDefaultExt);
RegionInfoCollector regionsInfoCollector(collectorFilename);
auto regions = ReadRegionsFromTmpMwm(genInfo, regionsInfoCollector);
RegionsBuilder::Regions regions = ReadData(genInfo, regionsInfoCollector);
auto jsonPolicy = std::make_unique<JsonPolicy>(genInfo.m_verbose);
auto kvBuilder = std::make_unique<RegionsBuilder>(std::move(regions), std::move(jsonPolicy));
auto const countryTrees = kvBuilder->GetCountryTrees();
@ -688,21 +206,14 @@ bool GenerateRegions(feature::GenerateInfo const & genInfo)
size_t countIds = 0;
for (auto const & countryName : kvBuilder->GetCountryNames())
{
auto const keyRange = countryTrees.equal_range(countryName);
using countryTreeItem = typename RegionsBuilder::CountryTrees::value_type;
auto const binOp = [](Node::Ptr l, countryTreeItem r) { return MergeTree(l, r.second); };
Node::Ptr mergedTree = std::accumulate(keyRange.first, keyRange.second, Node::Ptr(), binOp);
if (!mergedTree)
continue;
NormalizeTree(mergedTree);
auto const tree = kvBuilder->GetNormalizedCountryTree(countryName);
if (genInfo.m_verbose)
DebugPrintCountry(mergedTree);
DebugPrintTree(tree);
auto const idStringList = kvBuilder->ToIdStringList(mergedTree);
auto const idStringList = kvBuilder->ToIdStringList(tree);
for (auto const & s : idStringList)
{
ofs << s.first << " " << s.second << std::endl;
ofs << static_cast<int64_t>(s.first.GetEncodedId()) << " " << s.second << std::endl;
++countIds;
if (!setIds.insert(s.first).second)
LOG(LWARNING, ("Id alredy exists:", s.first));

View file

@ -1,11 +1,11 @@
#pragma once
#include "generator/feature_builder.hpp"
#include "generator/region_info_collector.hpp"
#include "generator/regions/region_info_collector.hpp"
#include "geometry/rect2d.hpp"
#include "coding/multilang_utf8_string.hpp"
#include "base/geo_object_id.hpp"
@ -23,116 +23,5 @@ struct GenerateInfo;
namespace generator
{
namespace regions
{
// This is a helper class that is needed to represent the region.
// With this view, further processing is simplified.
struct Region
{
static uint8_t constexpr kNoRank = 0;
using Point = FeatureBuilder1::PointSeq::value_type;
using BoostPoint = boost::geometry::model::point<double, 2, boost::geometry::cs::cartesian>;
using BoostPolygon = boost::geometry::model::polygon<BoostPoint>;
using BoostRect = boost::geometry::model::box<BoostPoint>;
explicit Region(FeatureBuilder1 const & fb, RegionDataProxy const & rd);
void DeletePolygon();
// This function will take the following steps:
// 1. Return the english name if it exists.
// 2. Return transliteration if it succeeds.
// 3. Otherwise, return empty string.
std::string GetEnglishOrTransliteratedName() const;
std::string GetName(int8_t lang = StringUtf8Multilang::kDefaultCode) const;
bool IsCountry() const;
bool HasIsoCode() const;
std::string GetIsoCode() const;
bool Contains(Region const & smaller) const;
bool ContainsRect(Region const & smaller) const;
double CalculateOverlapPercentage(Region const & other) const;
// Absolute rank values do not mean anything. But if the rank of the first object is more than the
// rank of the second object, then the first object is considered more nested.
uint8_t GetRank() const;
std::string GetLabel() const;
BoostPoint GetCenter() const;
std::shared_ptr<BoostPolygon> const GetPolygon() const;
BoostRect const & GetRect() const;
double GetArea() const;
base::GeoObjectId GetId() const;
private:
void FillPolygon(FeatureBuilder1 const & fb);
StringUtf8Multilang m_name;
RegionDataProxy m_regionData;
std::shared_ptr<BoostPolygon> m_polygon;
BoostRect m_rect;
double m_area;
};
struct Node
{
using Ptr = std::shared_ptr<Node>;
using WeakPtr = std::weak_ptr<Node>;
using PtrList = std::vector<Ptr>;
explicit Node(Region && region) : m_region(std::move(region)) {}
void AddChild(Ptr child) { m_children.push_back(child); }
PtrList const & GetChildren() const { return m_children; }
PtrList & GetChildren() { return m_children; }
void SetChildren(PtrList const children) { m_children = children; }
void RemoveChildren() { m_children.clear(); }
bool HasChildren() { return m_children.size(); }
void SetParent(Ptr parent) { m_parent = parent; }
Ptr GetParent() const { return m_parent.lock(); }
Region & GetData() { return m_region; }
private:
Region m_region;
PtrList m_children;
WeakPtr m_parent;
};
class ToStringPolicyInterface
{
public:
virtual ~ToStringPolicyInterface() = default;
virtual std::string ToString(Node::PtrList const & nodePtrList) = 0;
};
// This class is needed to build a hierarchy of regions. We can have several nodes for a region
// with the same name, represented by a multi-polygon (several polygons).
class RegionsBuilder
{
public:
using Regions = std::vector<Region>;
using StringsList = std::vector<std::string>;
using IdStringList = std::vector<std::pair<base::GeoObjectId, std::string>>;
using CountryTrees = std::multimap<std::string, Node::Ptr>;
explicit RegionsBuilder(Regions && regions);
explicit RegionsBuilder(Regions && regions,
std::unique_ptr<ToStringPolicyInterface> toStringPolicy);
Regions const & GetCountries() const;
StringsList GetCountryNames() const;
CountryTrees const & GetCountryTrees() const;
IdStringList ToIdStringList(Node::Ptr tree) const;
private:
static Node::PtrList MakeSelectedRegionsByCountry(Region const & country,
Regions const & allRegions);
static Node::Ptr BuildCountryRegionTree(Region const & country, Regions const & allRegions);
void MakeCountryTrees(Regions const & regions);
std::unique_ptr<ToStringPolicyInterface> m_toStringPolicy;
CountryTrees m_countryTrees;
Regions m_countries;
};
} // namespace regions
bool GenerateRegions(feature::GenerateInfo const & genInfo);
} // namespace generator

View file

@ -0,0 +1,27 @@
#pragma once
#include "generator/feature_builder.hpp"
#include "generator/regions/region_base.hpp"
#include "generator/regions/region_info_collector.hpp"
namespace generator
{
namespace regions
{
struct City : public RegionWithName, public RegionWithData
{
explicit City(FeatureBuilder1 const & fb, RegionDataProxy const & rd)
: RegionWithName(fb.GetParams().name),
RegionWithData(rd)
{
auto const p = fb.GetKeyPoint();
m_center = {p.x, p.y};
}
BoostPoint GetCenter() const { return m_center; }
private:
BoostPoint m_center;
};
} // namespace regions
} // namespace generator

163
generator/regions/node.cpp Normal file
View file

@ -0,0 +1,163 @@
#include "generator/regions/node.hpp"
#include <algorithm>
#include <numeric>
namespace generator
{
namespace regions
{
namespace
{
using MergeFunc = std::function<Node::Ptr(Node::Ptr, Node::Ptr)>;
bool LessNodePtrById(Node::Ptr l, Node::Ptr r)
{
auto const & lRegion = l->GetData();
auto const & rRegion = r->GetData();
return lRegion.GetId() < rRegion.GetId();
}
Node::PtrList MergeChildren(Node::PtrList const & l, Node::PtrList const & r, Node::Ptr newParent)
{
Node::PtrList result(l);
std::copy(std::begin(r), std::end(r), std::back_inserter(result));
for (auto & p : result)
p->SetParent(newParent);
std::sort(std::begin(result), std::end(result), LessNodePtrById);
return result;
}
Node::PtrList NormalizeChildren(Node::PtrList const & children, MergeFunc mergeTree)
{
Node::PtrList uniqueChildren;
auto const pred = [](Node::Ptr l, Node::Ptr r)
{
auto const & lRegion = l->GetData();
auto const & rRegion = r->GetData();
return lRegion.GetId() == rRegion.GetId();
};
std::unique_copy(std::begin(children), std::end(children),
std::back_inserter(uniqueChildren), pred);
Node::PtrList result;
for (auto const & ch : uniqueChildren)
{
auto const bounds = std::equal_range(std::begin(children), std::end(children), ch,
LessNodePtrById);
auto merged = std::accumulate(bounds.first, bounds.second, Node::Ptr(), mergeTree);
result.emplace_back(std::move(merged));
}
return result;
}
Node::Ptr MergeHelper(Node::Ptr l, Node::Ptr r, MergeFunc mergeTree)
{
auto const & lChildren = l->GetChildren();
auto const & rChildren = r->GetChildren();
auto const children = MergeChildren(lChildren, rChildren, l);
if (children.empty())
return l;
auto resultChildren = NormalizeChildren(children, mergeTree);
l->SetChildren(std::move(resultChildren));
r->RemoveChildren();
return l;
}
} // nmespace
size_t TreeSize(Node::Ptr node)
{
if (node == nullptr)
return 0;
size_t size = 1;
for (auto const & n : node->GetChildren())
size += TreeSize(n);
return size;
}
size_t MaxDepth(Node::Ptr node)
{
if (node == nullptr)
return 0;
size_t depth = 1;
for (auto const & n : node->GetChildren())
depth = std::max(MaxDepth(n), depth);
return depth;
}
void PrintTree(Node::Ptr node, std::ostream & stream = std::cout, std::string prefix = "",
bool isTail = true)
{
auto const & childern = node->GetChildren();
stream << prefix;
if (isTail)
{
stream << "└───";
prefix += " ";
}
else
{
stream << "├───";
prefix += "";
}
auto const & d = node->GetData();
auto const point = d.GetCenter();
stream << d.GetName() << "<" << d.GetEnglishOrTransliteratedName() << "> ("
<< d.GetId()
<< ";" << d.GetLabel()
<< ";" << static_cast<size_t>(d.GetRank())
<< ";[" << point.get<0>() << "," << point.get<1>() << "])"
<< std::endl;
for (size_t i = 0, size = childern.size(); i < size; ++i)
PrintTree(childern[i], stream, prefix, i == size - 1);
}
void DebugPrintTree(Node::Ptr tree, std::ostream & stream)
{
stream << "ROOT NAME: " << tree->GetData().GetName() << std::endl;
stream << "MAX DEPTH: " << MaxDepth(tree) << std::endl;
stream << "TREE SIZE: " << TreeSize(tree) << std::endl;
PrintTree(tree, stream);
stream << std::endl;
}
Node::Ptr MergeTree(Node::Ptr l, Node::Ptr r)
{
if (l == nullptr)
return r;
if (r == nullptr)
return l;
auto const & lRegion = l->GetData();
auto const & rRegion = r->GetData();
if (lRegion.GetId() != rRegion.GetId())
return nullptr;
if (lRegion.GetArea() > rRegion.GetArea())
return MergeHelper(l, r, MergeTree);
else
return MergeHelper(r, l, MergeTree);
}
void NormalizeTree(Node::Ptr tree)
{
if (tree == nullptr)
return;
auto & children = tree->GetChildren();
std::sort(std::begin(children), std::end(children), LessNodePtrById);
auto newChildren = NormalizeChildren(children, MergeTree);
tree->SetChildren(std::move(newChildren));
for (auto const & ch : tree->GetChildren())
NormalizeTree(ch);
}
} // namespace regions
} // namespace generator

View file

@ -0,0 +1,49 @@
#pragma once
#include "generator/regions/region.hpp"
#include <iostream>
#include <memory>
#include <vector>
#include "3party/boost/boost/geometry.hpp"
namespace generator
{
namespace regions
{
struct Node
{
using Ptr = std::shared_ptr<Node>;
using WeakPtr = std::weak_ptr<Node>;
using PtrList = std::vector<Ptr>;
explicit Node(Region && region) : m_region(std::move(region)) {}
void AddChild(Ptr child) { m_children.push_back(child); }
PtrList const & GetChildren() const { return m_children; }
PtrList & GetChildren() { return m_children; }
void SetChildren(PtrList const children) { m_children = children; }
void RemoveChildren() { m_children.clear(); }
bool HasChildren() { return m_children.size(); }
void SetParent(Ptr parent) { m_parent = parent; }
Ptr GetParent() const { return m_parent.lock(); }
Region & GetData() { return m_region; }
private:
Region m_region;
PtrList m_children;
WeakPtr m_parent;
};
size_t TreeSize(Node::Ptr node);
size_t MaxDepth(Node::Ptr node);
void DebugPrintTree(Node::Ptr tree, std::ostream & stream = std::cout);
// This function merges two trees if the roots have the same ids.
Node::Ptr MergeTree(Node::Ptr l, Node::Ptr r);
// This function corrects the tree. It traverses the whole node and unites children with
// the same ids.
void NormalizeTree(Node::Ptr tree);
} // namespace regions
} // namespace generator

View file

@ -0,0 +1,115 @@
#include "generator/regions/region.hpp"
#include "generator/regions/city.hpp"
#include "generator/regions/region_info_collector.hpp"
#include "base/assert.hpp"
#include <algorithm>
#include <numeric>
#include "3party/boost/boost/geometry.hpp"
namespace generator
{
namespace regions
{
namespace
{
template <typename BoostGeometry, typename FbGeometry>
void FillBoostGeometry(BoostGeometry & geometry, FbGeometry const & fbGeometry)
{
geometry.reserve(fbGeometry.size());
for (auto const & p : fbGeometry)
boost::geometry::append(geometry, BoostPoint{p.x, p.y});
}
} // namespace
Region::Region(FeatureBuilder1 const & fb, RegionDataProxy const & rd)
: RegionWithName(fb.GetParams().name),
RegionWithData(rd),
m_polygon(std::make_shared<BoostPolygon>())
{
FillPolygon(fb);
auto rect = fb.GetLimitRect();
m_rect = BoostRect({{rect.minX(), rect.minY()}, {rect.maxX(), rect.maxY()}});
m_area = boost::geometry::area(*m_polygon);
}
void Region::DeletePolygon()
{
m_polygon = nullptr;
}
void Region::FillPolygon(FeatureBuilder1 const & fb)
{
CHECK(m_polygon, ());
auto const & fbGeometry = fb.GetGeometry();
CHECK(!fbGeometry.empty(), ());
auto it = std::begin(fbGeometry);
FillBoostGeometry(m_polygon->outer(), *it);
m_polygon->inners().resize(fbGeometry.size() - 1);
int i = 0;
++it;
for (; it != std::end(fbGeometry); ++it)
FillBoostGeometry(m_polygon->inners()[i++], *it);
boost::geometry::correct(*m_polygon);
}
bool Region::IsCountry() const
{
static auto const kAdminLevelCountry = AdminLevel::Two;
return !HasPlaceType() && GetAdminLevel() == kAdminLevelCountry;
}
bool Region::Contains(Region const & smaller) const
{
CHECK(m_polygon, ());
CHECK(smaller.m_polygon, ());
return boost::geometry::covered_by(*smaller.m_polygon, *m_polygon);
}
double Region::CalculateOverlapPercentage(Region const & other) const
{
CHECK(m_polygon, ());
CHECK(other.m_polygon, ());
std::vector<BoostPolygon> coll;
boost::geometry::intersection(*other.m_polygon, *m_polygon, coll);
auto const min = std::min(boost::geometry::area(*other.m_polygon),
boost::geometry::area(*m_polygon));
auto const binOp = [] (double x, BoostPolygon const & y) { return x + boost::geometry::area(y); };
auto const sum = std::accumulate(std::begin(coll), std::end(coll), 0., binOp);
return (sum / min) * 100;
}
bool Region::ContainsRect(Region const & smaller) const
{
return boost::geometry::covered_by(smaller.m_rect, m_rect);
}
BoostPoint Region::GetCenter() const
{
BoostPoint p;
boost::geometry::centroid(m_rect, p);
return p;
}
bool Region::Contains(City const & cityPoint) const
{
CHECK(m_polygon, ());
return boost::geometry::covered_by(cityPoint.GetCenter(), *m_polygon);
}
void Region::SetInfo(City const & cityPoint)
{
SetStringUtf8MultilangName(cityPoint.GetStringUtf8MultilangName());
SetAdminLevel(cityPoint.GetAdminLevel());
SetPlaceType(cityPoint.GetPlaceType());
}
} // namespace regions
} // namespace generator

View file

@ -0,0 +1,44 @@
#pragma once
#include "generator/feature_builder.hpp"
#include "generator/regions/region_base.hpp"
#include <memory>
class FeatureBuilder1;
namespace generator
{
class RegionDataProxy;
namespace regions
{
class City;
// This is a helper class that is needed to represent the region.
// With this view, further processing is simplified.
struct Region : public RegionWithName, public RegionWithData
{
explicit Region(FeatureBuilder1 const & fb, RegionDataProxy const & rd);
void DeletePolygon();
bool IsCountry() const;
bool Contains(Region const & smaller) const;
bool ContainsRect(Region const & smaller) const;
double CalculateOverlapPercentage(Region const & other) const;
BoostPoint GetCenter() const;
std::shared_ptr<BoostPolygon> const GetPolygon() const { return m_polygon; }
BoostRect const & GetRect() const { return m_rect; }
double GetArea() const { return m_area; }
bool Contains(City const & cityPoint) const;
void SetInfo(City const & cityPoint);
private:
void FillPolygon(FeatureBuilder1 const & fb);
std::shared_ptr<BoostPolygon> m_polygon;
BoostRect m_rect;
double m_area;
};
} // namespace regions
} // namespace generator

View file

@ -0,0 +1,135 @@
#include "generator/regions/region_base.hpp"
#include "coding/transliteration.hpp"
#include "base/assert.hpp"
#include "base/control_flow.hpp"
namespace generator
{
namespace regions
{
std::string RegionWithName::GetName(int8_t lang) const
{
std::string s;
VERIFY(m_name.GetString(lang, s) != s.empty(), ());
return s;
}
std::string RegionWithName::GetEnglishOrTransliteratedName() const
{
std::string s = GetName(StringUtf8Multilang::kEnglishCode);
if (!s.empty())
return s;
auto const fn = [&s](int8_t code, std::string const & name)
{
if (code != StringUtf8Multilang::kDefaultCode &&
Transliteration::Instance().Transliterate(name, code, s))
{
return base::ControlFlow::Break;
}
return base::ControlFlow::Continue;
};
m_name.ForEach(fn);
return s;
}
StringUtf8Multilang const & RegionWithName::GetStringUtf8MultilangName() const
{
return m_name;
}
void RegionWithName::SetStringUtf8MultilangName(StringUtf8Multilang const & name)
{
m_name = name;
}
base::GeoObjectId RegionWithData::GetId() const
{
return m_regionData.GetOsmId();
}
bool RegionWithData::HasAdminCenter() const
{
return m_regionData.HasAdminCenter();
}
base::GeoObjectId RegionWithData::GetAdminCenterId() const
{
return m_regionData.GetAdminCenter();
}
bool RegionWithData::HasIsoCode() const
{
return m_regionData.HasIsoCodeAlpha2();
}
std::string RegionWithData::GetIsoCode() const
{
return m_regionData.GetIsoCodeAlpha2();
}
// The values of the administrative level and place are indirectly dependent.
// This is used when calculating the rank.
uint8_t RegionWithData::GetRank() const
{
auto const adminLevel = GetAdminLevel();
auto const placeType = GetPlaceType();
switch (placeType)
{
case PlaceType::City:
case PlaceType::Town:
case PlaceType::Village:
case PlaceType::Hamlet:
case PlaceType::Suburb:
case PlaceType::Neighbourhood:
case PlaceType::Locality:
case PlaceType::IsolatedDwelling: return static_cast<uint8_t>(placeType);
default: break;
}
switch (adminLevel)
{
case AdminLevel::Two:
case AdminLevel::Four:
case AdminLevel::Six: return static_cast<uint8_t>(adminLevel);
default: break;
}
return kNoRank;
}
std::string RegionWithData::GetLabel() const
{
auto const adminLevel = GetAdminLevel();
auto const placeType = GetPlaceType();
switch (placeType)
{
case PlaceType::City:
case PlaceType::Town:
case PlaceType::Village:
case PlaceType::Hamlet: return "locality";
case PlaceType::Suburb:
case PlaceType::Neighbourhood: return "suburb";
case PlaceType::Locality:
case PlaceType::IsolatedDwelling: return "sublocality";
default: break;
}
switch (adminLevel)
{
case AdminLevel::Two: return "country";
case AdminLevel::Four: return "region";
case AdminLevel::Six: return "subregion";
default: break;
}
return "";
}
} // namespace regions
} // namespace generator

View file

@ -0,0 +1,72 @@
#pragma once
#include "generator/feature_builder.hpp"
#include "generator/regions/region_info_collector.hpp"
#include "geometry/rect2d.hpp"
#include "coding/multilang_utf8_string.hpp"
#include "base/geo_object_id.hpp"
#include <cstdint>
#include <string>
#include "3party/boost/boost/geometry.hpp"
namespace generator
{
namespace regions
{
using Point = FeatureBuilder1::PointSeq::value_type;
using BoostPoint = boost::geometry::model::point<double, 2, boost::geometry::cs::cartesian>;
using BoostPolygon = boost::geometry::model::polygon<BoostPoint>;
using BoostRect = boost::geometry::model::box<BoostPoint>;
struct RegionWithName
{
RegionWithName(StringUtf8Multilang const & name) : m_name(name) {}
// This function will take the following steps:
// 1. Return the english name if it exists.
// 2. Return transliteration if it succeeds.
// 3. Otherwise, return empty string.
std::string GetEnglishOrTransliteratedName() const;
std::string GetName(int8_t lang = StringUtf8Multilang::kDefaultCode) const;
StringUtf8Multilang const & GetStringUtf8MultilangName() const;
void SetStringUtf8MultilangName(StringUtf8Multilang const & name);
protected:
StringUtf8Multilang m_name;
};
struct RegionWithData
{
static uint8_t constexpr kNoRank = 0;
RegionWithData(RegionDataProxy const & regionData) : m_regionData(regionData) {}
base::GeoObjectId GetId() const;
bool HasAdminCenter() const;
base::GeoObjectId GetAdminCenterId() const;
bool HasIsoCode() const;
std::string GetIsoCode() const;
// Absolute rank values do not mean anything. But if the rank of the first object is more than the
// rank of the second object, then the first object is considered more nested.
uint8_t GetRank() const;
std::string GetLabel() const;
AdminLevel GetAdminLevel() const { return m_regionData.GetAdminLevel(); }
PlaceType GetPlaceType() const { return m_regionData.GetPlaceType(); }
void SetAdminLevel(AdminLevel adminLevel) { m_regionData.SetAdminLevel(adminLevel); }
void SetPlaceType(PlaceType placeType) { m_regionData.SetPlaceType(placeType); }
bool HasAdminLevel() const { return m_regionData.HasAdminLevel(); }
bool HasPlaceType() const { return m_regionData.HasPlaceType(); }
protected:
RegionDataProxy m_regionData;
};
} // namespace regions
} // namespace generator

View file

@ -1,4 +1,4 @@
#include "generator/region_info_collector.hpp"
#include "generator/regions/region_info_collector.hpp"
#include "generator/feature_builder.hpp"
#include "generator/osm_element.hpp"
@ -19,6 +19,8 @@ uint8_t const kVersion = 0;
namespace generator
{
namespace regions
{
std::string const RegionInfoCollector::kDefaultExt = ".regions.bin";
PlaceType EncodePlaceType(std::string const & place)
@ -124,8 +126,14 @@ void RegionInfoCollector::FillRegionData(base::GeoObjectId const & osmId, OsmEle
{
rd.m_osmId = osmId;
rd.m_place = EncodePlaceType(el.GetTag("place"));
auto const al = el.GetTag("admin_level");
auto const & members = el.Members();
auto const it = std::find_if(std::begin(members), std::end(members),
[](OsmElement::Member const & m) { return m.role == "admin_centre"; });
if (it != std::end(members))
rd.m_osmIdAdminCenter = base::MakeOsmNode(it->ref);
auto const al = el.GetTag("admin_level");
if (al.empty())
return;
@ -165,11 +173,13 @@ RegionInfoCollector const & RegionDataProxy::GetCollector() const
return m_regionInfoCollector;
}
RegionInfoCollector::MapRegionData const & RegionDataProxy::GetMapRegionData() const
{
return GetCollector().m_mapRegionData;
}
RegionInfoCollector::MapIsoCode const & RegionDataProxy::GetMapIsoCode() const
{
return GetCollector().m_mapIsoCode;
@ -190,6 +200,16 @@ PlaceType RegionDataProxy::GetPlaceType() const
return GetMapRegionData().at(m_osmId).m_place;
}
void RegionDataProxy::SetAdminLevel(AdminLevel adminLevel)
{
const_cast<AdminLevel &>(GetMapRegionData().at(m_osmId).m_adminLevel) = adminLevel;
}
void RegionDataProxy::SetPlaceType(PlaceType placeType)
{
const_cast<PlaceType &>(GetMapRegionData().at(m_osmId).m_place) = placeType;
}
bool RegionDataProxy::HasAdminLevel() const
{
return (GetMapRegionData().count(m_osmId) != 0) &&
@ -236,4 +256,16 @@ std::string RegionDataProxy::GetIsoCodeAlphaNumeric() const
{
return GetMapIsoCode().at(m_osmId).GetNumeric();
}
bool RegionDataProxy::HasAdminCenter() const
{
return (GetMapRegionData().count(m_osmId) != 0) &&
(GetMapRegionData().at(m_osmId).m_osmIdAdminCenter.IsValid());
}
base::GeoObjectId RegionDataProxy::GetAdminCenter() const
{
return GetMapRegionData().at(m_osmId).m_osmIdAdminCenter;
}
} // namespace regions
} // namespace generator

View file

@ -20,6 +20,8 @@ class FileWriter;
namespace generator
{
namespace regions
{
// https://wiki.openstreetmap.org/wiki/Tag:boundary=administrative
enum class AdminLevel : uint8_t
{
@ -101,6 +103,7 @@ private:
base::GeoObjectId m_osmId;
AdminLevel m_adminLevel = AdminLevel::Unknown;
PlaceType m_place = PlaceType::Unknown;
base::GeoObjectId m_osmIdAdminCenter;
};
using MapRegionData = std::unordered_map<base::GeoObjectId, RegionData>;
@ -147,6 +150,9 @@ public:
AdminLevel GetAdminLevel() const;
PlaceType GetPlaceType() const;
void SetAdminLevel(AdminLevel adminLevel);
void SetPlaceType(PlaceType placeType);
bool HasAdminLevel() const;
bool HasPlaceType() const;
@ -158,6 +164,9 @@ public:
std::string GetIsoCodeAlpha3() const;
std::string GetIsoCodeAlphaNumeric() const;
bool HasAdminCenter() const;
base::GeoObjectId GetAdminCenter() const;
private:
bool HasIsoCode() const;
RegionInfoCollector const & GetCollector() const;
@ -179,4 +188,5 @@ inline std::ostream & operator<<(std::ostream & out, PlaceType const & t)
out << static_cast<int>(t);
return out;
}
} // namespace regions
} // namespace generator

View file

@ -0,0 +1,200 @@
#include "generator/regions/regions_builder.hpp"
#include "base/assert.hpp"
#include <algorithm>
#include <chrono>
#include <fstream>
#include <functional>
#include <numeric>
#include <memory>
#include <queue>
#include <string>
#include <thread>
#include <unordered_set>
#include <vector>
#include "3party/ThreadPool/ThreadPool.h"
namespace generator
{
namespace regions
{
RegionsBuilder::RegionsBuilder(Regions && regions,
std::unique_ptr<ToStringPolicyInterface> toStringPolicy,
int cpuCount)
: m_toStringPolicy(std::move(toStringPolicy)),
m_cpuCount(cpuCount)
{
ASSERT(m_toStringPolicy, ());
ASSERT(m_cpuCount != 0, ());
auto const isCountry = [](Region const & r){ return r.IsCountry(); };
std::copy_if(std::begin(regions), std::end(regions), std::back_inserter(m_countries), isCountry);
auto const it = std::remove_if(std::begin(regions), std::end(regions), isCountry);
regions.erase(it, std::end(regions));
auto const cmp = [](Region const & l, Region const & r) { return l.GetArea() > r.GetArea(); };
std::sort(std::begin(m_countries), std::end(m_countries), cmp);
MakeCountryTrees(regions);
}
RegionsBuilder::RegionsBuilder(Regions && regions, int cpuCount)
: RegionsBuilder(std::move(regions), std::make_unique<JsonPolicy>(), cpuCount) {}
RegionsBuilder::Regions const & RegionsBuilder::GetCountries() const
{
return m_countries;
}
RegionsBuilder::StringsList RegionsBuilder::GetCountryNames() const
{
StringsList result;
std::unordered_set<std::string> set;
for (auto const & c : GetCountries())
{
auto name = c.GetName();
if (set.insert(name).second)
result.emplace_back(std::move(name));
}
return result;
}
RegionsBuilder::CountryTrees const & RegionsBuilder::GetCountryTrees() const
{
return m_countryTrees;
}
RegionsBuilder::IdStringList RegionsBuilder::ToIdStringList(Node::Ptr tree) const
{
IdStringList result;
std::queue<Node::Ptr> queue;
queue.push(tree);
while (!queue.empty())
{
const auto el = queue.front();
queue.pop();
Node::PtrList nodes;
auto current = el;
while (current)
{
nodes.push_back(current);
current = current->GetParent();
}
auto string = m_toStringPolicy->ToString(nodes);
auto const id = nodes.front()->GetData().GetId();
result.emplace_back(std::make_pair(id, std::move(string)));
for (auto const & n : el->GetChildren())
queue.push(n);
}
return result;
}
Node::PtrList RegionsBuilder::MakeSelectedRegionsByCountry(Region const & country,
Regions const & allRegions)
{
Regions regionsInCountry;
auto filterCopy = [&country] (const Region & r) { return country.ContainsRect(r); };
std::copy_if(std::begin(allRegions), std::end(allRegions),
std::back_inserter(regionsInCountry), filterCopy);
regionsInCountry.emplace_back(country);
auto const comp = [](const Region & l, const Region & r)
{
auto const lArea = l.GetArea();
auto const rArea = r.GetArea();
return lArea != rArea ? lArea > rArea : l.GetRank() < r.GetRank();
};
std::sort(std::begin(regionsInCountry), std::end(regionsInCountry), comp);
Node::PtrList nodes;
nodes.reserve(regionsInCountry.size());
for (auto && region : regionsInCountry)
nodes.emplace_back(std::make_shared<Node>(std::move(region)));
return nodes;
}
Node::Ptr RegionsBuilder::BuildCountryRegionTree(Region const & country,
Regions const & allRegions)
{
auto nodes = MakeSelectedRegionsByCountry(country, allRegions);
while (nodes.size() > 1)
{
auto itFirstNode = std::rbegin(nodes);
auto & firstRegion = (*itFirstNode)->GetData();
auto itCurr = itFirstNode + 1;
for (; itCurr != std::rend(nodes); ++itCurr)
{
auto const & currRegion = (*itCurr)->GetData();
// If Contains returns false, then we calculate the percent overlap of polygons.
// We believe that if one polygon overlaps by 98 percent, then we can assume that one
// contains another.
auto const kAvaliableOverlapPercentage = 98;
if (currRegion.ContainsRect(firstRegion) &&
(currRegion.Contains(firstRegion) ||
currRegion.CalculateOverlapPercentage(firstRegion) > kAvaliableOverlapPercentage))
{
// In general, we assume that a region with the larger rank has the larger area.
// But sometimes it does not. In this case, we will make an inversion.
if (firstRegion.GetRank() < currRegion.GetRank())
{
(*itCurr)->SetParent(*itFirstNode);
(*itFirstNode)->AddChild(*itCurr);
}
else
{
(*itFirstNode)->SetParent(*itCurr);
(*itCurr)->AddChild(*itFirstNode);
}
// We want to free up memory.
firstRegion.DeletePolygon();
nodes.pop_back();
break;
}
}
if (itCurr == std::rend(nodes))
nodes.pop_back();
}
return nodes.empty() ? std::shared_ptr<Node>() : nodes.front();
}
void RegionsBuilder::MakeCountryTrees(Regions const & regions)
{
std::vector<std::future<Node::Ptr>> results;
{
int const cpuCount = m_cpuCount > 0 ? m_cpuCount : std::thread::hardware_concurrency();
ASSERT_GREATER(cpuCount, 0, ());
ThreadPool threadPool(cpuCount);
for (auto const & country : GetCountries())
{
auto f = threadPool.enqueue(&RegionsBuilder::BuildCountryRegionTree, country, regions);
results.emplace_back(std::move(f));
}
}
for (auto & r : results)
{
auto tree = r.get();
m_countryTrees.emplace(tree->GetData().GetName(), std::move(tree));
}
}
Node::Ptr RegionsBuilder::GetNormalizedCountryTree(std::string const & name)
{
Node::Ptr mergedTree;
CountryTrees const & countryTrees = GetCountryTrees();
auto const keyRange = countryTrees.equal_range(name);
using countryTreeItem = typename RegionsBuilder::CountryTrees::value_type;
auto const binOp = [](Node::Ptr l, countryTreeItem r) { return MergeTree(l, r.second); };
mergedTree = std::accumulate(keyRange.first, keyRange.second, Node::Ptr(), binOp);
NormalizeTree(mergedTree);
return mergedTree;
}
} // namespace regions
} // namespace generator

View file

@ -0,0 +1,47 @@
#pragma once
#include "generator/regions/node.hpp"
#include "generator/regions/region.hpp"
#include "generator/regions/to_string_policy.hpp"
#include <memory>
namespace generator
{
namespace regions
{
// This class is needed to build a hierarchy of regions. We can have several nodes for a region
// with the same name, represented by a multi-polygon (several polygons).
class RegionsBuilder
{
public:
using Regions = std::vector<Region>;
using StringsList = std::vector<std::string>;
using IdStringList = std::vector<std::pair<base::GeoObjectId, std::string>>;
using CountryTrees = std::multimap<std::string, Node::Ptr>;
explicit RegionsBuilder(Regions && regions,
std::unique_ptr<ToStringPolicyInterface> toStringPolicy,
int cpuCount = -1);
explicit RegionsBuilder(Regions && regions, int cpuCount = -1);
Regions const & GetCountries() const;
StringsList GetCountryNames() const;
CountryTrees const & GetCountryTrees() const;
IdStringList ToIdStringList(Node::Ptr tree) const;
Node::Ptr GetNormalizedCountryTree(std::string const & name);
private:
static Node::PtrList MakeSelectedRegionsByCountry(Region const & country,
Regions const & allRegions);
static Node::Ptr BuildCountryRegionTree(Region const & country, Regions const & allRegions);
void MakeCountryTrees(Regions const & regions);
std::unique_ptr<ToStringPolicyInterface> m_toStringPolicy;
CountryTrees m_countryTrees;
Regions m_countries;
int m_cpuCount;
};
} // namespace regions
} // namespace generator

View file

@ -0,0 +1,64 @@
#include "generator/regions/to_string_policy.hpp"
#include <memory>
#include "3party/jansson/myjansson.hpp"
#include "3party/boost/boost/range/adaptor/reversed.hpp"
namespace generator
{
namespace regions
{
std::string JsonPolicy::ToString(Node::PtrList const & nodePtrList)
{
auto const & main = nodePtrList.front()->GetData();
auto const & country = nodePtrList.back()->GetData();
auto geometry = base::NewJSONObject();
ToJSONObject(*geometry, "type", "Point");
auto coordinates = base::NewJSONArray();
auto const center = main.GetCenter();
ToJSONArray(*coordinates, center.get<0>());
ToJSONArray(*coordinates, center.get<1>());
ToJSONObject(*geometry, "coordinates", coordinates);
auto localeEn = base::NewJSONObject();
auto address = base::NewJSONObject();
for (auto const & p : boost::adaptors::reverse(nodePtrList))
{
auto const & region = p->GetData();
auto const label = region.GetLabel();
ToJSONObject(*address, label, region.GetName());
if (m_extendedOutput)
{
ToJSONObject(*address, label + "_i", region.GetId().GetSerialId());
ToJSONObject(*address, label + "_a", region.GetArea());
ToJSONObject(*address, label + "_r", region.GetRank());
}
ToJSONObject(*localeEn, label, region.GetEnglishOrTransliteratedName());
}
auto locales = base::NewJSONObject();
ToJSONObject(*locales, "en", localeEn);
auto properties = base::NewJSONObject();
ToJSONObject(*properties, "name", main.GetName());
ToJSONObject(*properties, "rank", main.GetRank());
ToJSONObject(*properties, "address", address);
ToJSONObject(*properties, "locales", locales);
if (country.HasIsoCode())
ToJSONObject(*properties, "code", country.GetIsoCode());
auto feature = base::NewJSONObject();
ToJSONObject(*feature, "type", "Feature");
ToJSONObject(*feature, "geometry", geometry);
ToJSONObject(*feature, "properties", properties);
auto const cstr = json_dumps(feature.get(), JSON_COMPACT);
std::unique_ptr<char, JSONFreeDeleter> buffer(cstr);
return buffer.get();
}
} // namespace regions
} // namespace generator

View file

@ -0,0 +1,31 @@
#pragma once
#include "generator/regions/node.hpp"
#include <string>
namespace generator
{
namespace regions
{
class ToStringPolicyInterface
{
public:
virtual ~ToStringPolicyInterface() = default;
virtual std::string ToString(Node::PtrList const & nodePtrList) = 0;
};
class JsonPolicy : public ToStringPolicyInterface
{
public:
JsonPolicy(bool extendedOutput = false) : m_extendedOutput(extendedOutput) {}
std::string ToString(Node::PtrList const & nodePtrList) override;
private:
bool m_extendedOutput;
};
} // namespace regions
} // namespace generator

View file

@ -7,7 +7,7 @@
#include "generator/intermediate_data.hpp"
#include "generator/osm2type.hpp"
#include "generator/osm_element.hpp"
#include "generator/region_info_collector.hpp"
#include "generator/regions/region_info_collector.hpp"
#include "indexer/classificator.hpp"
@ -23,7 +23,7 @@ namespace generator
{
TranslatorRegion::TranslatorRegion(std::shared_ptr<EmitterInterface> emitter,
cache::IntermediateDataReader & holder,
RegionInfoCollector & regionInfoCollector)
regions::RegionInfoCollector & regionInfoCollector)
: m_emitter(emitter),
m_holder(holder),
m_regionInfoCollector(regionInfoCollector)
@ -40,16 +40,15 @@ void TranslatorRegion::EmitElement(OsmElement * p)
switch (p->type)
{
case OsmElement::EntityType::Node:
BuildFeatureAndEmitFromNode(p, params);
break;
case OsmElement::EntityType::Relation:
{
BuildFeatureAndEmitFromRelation(p, params);
break;
}
case OsmElement::EntityType::Way:
{
BuildFeatureAndEmitFromWay(p, params);
break;
}
default:
break;
}
@ -57,26 +56,21 @@ void TranslatorRegion::EmitElement(OsmElement * p)
bool TranslatorRegion::IsSuitableElement(OsmElement const * p) const
{
static std::set<std::string> const adminLevels = {"2", "4", "5", "6", "7", "8"};
static std::set<std::string> const places = {"city", "town", "village", "suburb", "neighbourhood",
"hamlet", "locality", "isolated_dwelling"};
bool haveBoundary = false;
bool haveAdminLevel = false;
bool haveName = false;
for (auto const & t : p->Tags())
{
if (t.key == "place" && places.find(t.value) != places.end())
return true;
if (t.key == "boundary" && t.value == "administrative")
haveBoundary = true;
else if (t.key == "admin_level" && adminLevels.find(t.value) != adminLevels.end())
haveAdminLevel = true;
else if (t.key == "name" && !t.value.empty())
haveName = true;
auto const & members = p->Members();
auto const pred = [](OsmElement::Member const & m) { return m.role == "admin_centre"; };
if (t.key == "boundary" && t.value == "police" &&
std::find_if(std::begin(members), std::end(members), pred) != std::end(members))
return true;
if (haveBoundary && haveAdminLevel && haveName)
if (t.key == "boundary" && t.value == "administrative")
return true;
}
@ -146,4 +140,16 @@ void TranslatorRegion::BuildFeatureAndEmitFromWay(OsmElement const * p, FeatureP
AddInfoAboutRegion(p, id);
(*m_emitter)(fb);
}
void TranslatorRegion::BuildFeatureAndEmitFromNode(OsmElement const * p, FeatureParams & params)
{
m2::PointD const pt = MercatorBounds::FromLatLon(p->lat, p->lon);
FeatureBuilder1 fb;
fb.SetCenter(pt);
auto const id = base::MakeOsmNode(p->id);
fb.SetOsmId(id);
fb.SetParams(params);
AddInfoAboutRegion(p, id);
(*m_emitter)(fb);
}
} // namespace generator

View file

@ -18,7 +18,10 @@ struct GenerateInfo;
namespace generator
{
class EmitterInterface;
namespace regions
{
class RegionInfoCollector;
} // namespace regions
namespace cache
{
class IntermediateDataReader;
@ -30,7 +33,7 @@ class TranslatorRegion : public TranslatorInterface
public:
explicit TranslatorRegion(std::shared_ptr<EmitterInterface> emitter,
cache::IntermediateDataReader & holder,
RegionInfoCollector & regionInfoCollector);
regions::RegionInfoCollector & regionInfoCollector);
void EmitElement(OsmElement * p) override;
@ -40,10 +43,11 @@ private:
bool ParseParams(OsmElement * p, FeatureParams & params) const;
void BuildFeatureAndEmitFromRelation(OsmElement const * p, FeatureParams & params);
void BuildFeatureAndEmitFromWay(OsmElement const * p, FeatureParams & params);
void BuildFeatureAndEmitFromNode(OsmElement const * p, FeatureParams & params);
private:
std::shared_ptr<EmitterInterface> m_emitter;
cache::IntermediateDataReader & m_holder;
RegionInfoCollector & m_regionInfoCollector;
regions::RegionInfoCollector & m_regionInfoCollector;
};
} // namespace generator