From 2fbe30b352d740b5570878f69ba1c6456335184b Mon Sep 17 00:00:00 2001 From: Maksim Andrianov Date: Thu, 2 Aug 2018 16:05:53 +0300 Subject: [PATCH] [generator] Separated osm_translator --- generator/CMakeLists.txt | 12 +- generator/feature_generator.cpp | 2 +- generator/holes.cpp | 75 +++++ generator/holes.hpp | 60 ++++ generator/osm_source.cpp | 15 +- generator/osm_translator.cpp | 524 ----------------------------- generator/osm_translator.hpp | 225 ------------- generator/relation_tags.cpp | 170 ++++++++++ generator/relation_tags.hpp | 79 +++++ generator/translator_factory.hpp | 33 ++ generator/translator_interface.hpp | 15 + generator/translator_planet.cpp | 234 +++++++++++++ generator/translator_planet.hpp | 58 ++++ generator/translator_region.cpp | 115 +++++++ generator/translator_region.hpp | 42 +++ 15 files changed, 900 insertions(+), 759 deletions(-) create mode 100644 generator/holes.cpp create mode 100644 generator/holes.hpp delete mode 100644 generator/osm_translator.cpp delete mode 100644 generator/osm_translator.hpp create mode 100644 generator/relation_tags.cpp create mode 100644 generator/relation_tags.hpp create mode 100644 generator/translator_factory.hpp create mode 100644 generator/translator_interface.hpp create mode 100644 generator/translator_planet.cpp create mode 100644 generator/translator_planet.hpp create mode 100644 generator/translator_region.cpp create mode 100644 generator/translator_region.hpp diff --git a/generator/CMakeLists.txt b/generator/CMakeLists.txt index 0660650da9..8da99ecd6e 100644 --- a/generator/CMakeLists.txt +++ b/generator/CMakeLists.txt @@ -49,6 +49,8 @@ set(SRC gen_mwm_info.hpp generate_info.hpp geometry_holder.hpp + holes.cpp + holes.hpp intermediate_data.cpp intermediate_data.hpp intermediate_elements.hpp @@ -69,14 +71,14 @@ set(SRC osm_element.hpp osm_o5m_source.hpp osm_source.cpp - osm_translator.hpp - osm_translator.cpp osm_xml_source.hpp polygonizer.hpp popular_places_section_builder.cpp popular_places_section_builder.hpp place.hpp place.cpp + relation_tags.cpp + relation_tags.hpp region_meta.cpp region_meta.hpp restriction_collector.cpp @@ -112,6 +114,12 @@ set(SRC traffic_generator.hpp transit_generator.cpp transit_generator.hpp + translator_factory.hpp + translator_interface.hpp + translator_planet.cpp + translator_planet.hpp + translator_region.cpp + translator_region.hpp ugc_db.cpp ugc_db.hpp ugc_section_builder.cpp diff --git a/generator/feature_generator.cpp b/generator/feature_generator.cpp index af9737aacf..fa7027005e 100644 --- a/generator/feature_generator.cpp +++ b/generator/feature_generator.cpp @@ -1,8 +1,8 @@ +#include "generator/feature_builder.hpp" #include "generator/feature_generator.hpp" #include "generator/generate_info.hpp" #include "generator/intermediate_data.hpp" #include "generator/intermediate_elements.hpp" -#include "generator/osm_translator.hpp" #include "indexer/cell_id.hpp" #include "indexer/data_header.hpp" diff --git a/generator/holes.cpp b/generator/holes.cpp new file mode 100644 index 0000000000..3a5de8e783 --- /dev/null +++ b/generator/holes.cpp @@ -0,0 +1,75 @@ +#include "generator/holes.hpp" + +#include "generator/intermediate_data.hpp" +#include "generator/osm_element.hpp" + +namespace generator +{ +// HolesAccumulator +HolesAccumulator::HolesAccumulator(cache::IntermediateDataReader & holder) : + m_merger(holder) +{ +} + +FeatureBuilder1::Geometry & HolesAccumulator::GetHoles() +{ + ASSERT(m_holes.empty(), ("Can call only once")); + m_merger.ForEachArea(false, [this](FeatureBuilder1::PointSeq const & v, + std::vector const &) + { + m_holes.push_back(std::move(v)); + }); + return m_holes; +} + +// HolesProcessor +HolesProcessor::HolesProcessor(uint64_t id, cache::IntermediateDataReader & holder) : + m_id(id), + m_holes(holder) +{ +} + +bool HolesProcessor::operator() (uint64_t /*id*/, RelationElement const & e) +{ + std::string const type = e.GetType(); + if (!(type == "multipolygon" || type == "boundary")) + return false; + + std::string role; + if (e.FindWay(m_id, role) && role == "outer") + { + e.ForEachWay(*this); + // stop processing (??? assume that "outer way" exists in one relation only ???) + return true; + } + return false; +} + +void HolesProcessor::operator() (uint64_t id, std::string const & role) +{ + if (id != m_id && role == "inner") + m_holes(id); +} + + +HolesRelation::HolesRelation(cache::IntermediateDataReader & holder) : + m_holes(holder), + m_outer(holder) +{ +} + +void HolesRelation::Build(OsmElement const * p) +{ + // Iterate ways to get 'outer' and 'inner' geometries. + for (auto const & e : p->Members()) + { + if (e.type != OsmElement::EntityType::Way) + continue; + + if (e.role == "outer") + m_outer.AddWay(e.ref); + else if (e.role == "inner") + m_holes(e.ref); + } +} +} // namespace generator diff --git a/generator/holes.hpp b/generator/holes.hpp new file mode 100644 index 0000000000..cd5484d23d --- /dev/null +++ b/generator/holes.hpp @@ -0,0 +1,60 @@ +#pragma once + +#include "generator/feature_builder.hpp" +#include "generator/ways_merger.hpp" + +#include + +struct OsmElement; + +namespace generator +{ +namespace cache +{ +class IntermediateDataReader; +} // namespace cache + +class HolesAccumulator +{ +public: + explicit HolesAccumulator(cache::IntermediateDataReader & holder); + + void operator() (uint64_t id) { m_merger.AddWay(id); } + FeatureBuilder1::Geometry & GetHoles(); + +private: + AreaWayMerger m_merger; + FeatureBuilder1::Geometry m_holes; +}; + +/// Find holes for way with 'id' in first relation. +class HolesProcessor +{ +public: + HolesProcessor(uint64_t id, cache::IntermediateDataReader & holder); + + /// 1. relations process function + bool operator() (uint64_t /*id*/, RelationElement const & e); + /// 2. "ways in relation" process function + void operator() (uint64_t id, std::string const & role); + FeatureBuilder1::Geometry & GetHoles() { return m_holes.GetHoles(); } + +private: + uint64_t m_id; ///< id of way to find it's holes + HolesAccumulator m_holes; +}; + +class HolesRelation +{ +public: + HolesRelation(cache::IntermediateDataReader & holder); + + void Build(OsmElement const * p); + FeatureBuilder1::Geometry & GetHoles() { return m_holes.GetHoles(); } + AreaWayMerger & GetOuter() { return m_outer; } + +private: + HolesAccumulator m_holes; + AreaWayMerger m_outer; +}; +} // namespace generator diff --git a/generator/osm_source.cpp b/generator/osm_source.cpp index f758d86862..11aaf9ecc7 100644 --- a/generator/osm_source.cpp +++ b/generator/osm_source.cpp @@ -8,11 +8,12 @@ #include "generator/node_mixer.hpp" #include "generator/osm_element.hpp" #include "generator/osm_o5m_source.hpp" -#include "generator/osm_translator.hpp" #include "generator/osm_xml_source.hpp" #include "generator/polygonizer.hpp" #include "generator/tag_admixer.hpp" #include "generator/towns_dumper.hpp" +#include "generator/translator_factory.hpp" +#include "generator/translator_interface.hpp" #include "generator/world_map_generator.hpp" #include "generator/booking_dataset.hpp" @@ -219,13 +220,13 @@ void ProcessOsmElementsFromO5M(SourceReader & stream, function; static bool GenerateRaw(feature::GenerateInfo & info, std::shared_ptr emitter, - PreEmit const & preEmit, std::unique_ptr parser) + PreEmit const & preEmit, std::shared_ptr translator) { try { auto const fn = [&](OsmElement * e) { if (preEmit(e)) - parser->EmitElement(e); + translator->EmitElement(e); }; SourceReader reader = info.m_osmFileName.empty() ? SourceReader() : SourceReader(info.m_osmFileName); @@ -282,16 +283,16 @@ bool GenerateFeatures(feature::GenerateInfo & info, std::shared_ptr(emitter, cache, info); - return GenerateRaw(info, emitter, preEmit, std::move(parser)); + auto translator = CreateTranslator(TranslatorType::PLANET, emitter, cache, info); + return GenerateRaw(info, emitter, preEmit, translator); } bool GenerateRegionFeatures(feature::GenerateInfo & info, std::shared_ptr emitter) { auto preEmit = [](OsmElement * e) { return true; }; auto cache = LoadCache(info); - auto parser = std::make_unique(emitter, cache); - return GenerateRaw(info, emitter, preEmit, std::move(parser)); + auto translator = CreateTranslator(TranslatorType::REGION, emitter, cache); + return GenerateRaw(info, emitter, preEmit, translator); } bool GenerateIntermediateData(feature::GenerateInfo & info) diff --git a/generator/osm_translator.cpp b/generator/osm_translator.cpp deleted file mode 100644 index 7fd51c5f13..0000000000 --- a/generator/osm_translator.cpp +++ /dev/null @@ -1,524 +0,0 @@ -#include "generator/osm_translator.hpp" - -namespace generator -{ -namespace -{ -class RelationBorders -{ -public: - RelationBorders(cache::IntermediateDataReader & holder) : - m_holes(holder), - m_outer(holder) - { - } - - void Build(OsmElement const * p) - { - // Iterate ways to get 'outer' and 'inner' geometries. - for (auto const & e : p->Members()) - { - if (e.type != OsmElement::EntityType::Way) - continue; - - if (e.role == "outer") - m_outer.AddWay(e.ref); - else if (e.role == "inner") - m_holes(e.ref); - } - } - - FeatureBuilder1::Geometry & GetHoles() { return m_holes.GetHoles(); } - AreaWayMerger & GetOuter() { return m_outer; } - -private: - HolesAccumulator m_holes; - AreaWayMerger m_outer; -}; -} // namespace - -// RelationTagsBase -RelationTagsBase::RelationTagsBase(routing::TagsProcessor & tagsProcessor) - : m_routingTagsProcessor(tagsProcessor), - m_cache(14 /* logCacheSize */) -{ -} - -void RelationTagsBase::Reset(uint64_t fID, OsmElement * p) -{ - m_featureID = fID; - m_current = p; -} - -bool RelationTagsBase::IsSkipRelation(std::string const & type) -{ - /// @todo Skip special relation types. - return (type == "multipolygon" || type == "bridge"); -} - -bool RelationTagsBase::IsKeyTagExists(std::string const & key) const -{ - auto const & tags = m_current->m_tags; - return std::any_of(std::begin(tags), std::end(tags), [&](OsmElement::Tag const & p) { - return p.key == key; - }); -} - -void RelationTagsBase::AddCustomTag(std::pair const & p) -{ - m_current->AddTag(p.first, p.second); -} - -// RelationTagsNode -RelationTagsNode::RelationTagsNode(routing::TagsProcessor & tagsProcessor) : - RelationTagsBase(tagsProcessor) -{ -} - -void RelationTagsNode::Process(RelationElement const & e) -{ - std::string const & type = e.GetType(); - if (TBase::IsSkipRelation(type)) - return; - - if (type == "restriction") - { - m_routingTagsProcessor.m_restrictionWriter.Write(e); - return; - } - - bool const processAssociatedStreet = type == "associatedStreet" && - TBase::IsKeyTagExists("addr:housenumber") && - !TBase::IsKeyTagExists("addr:street"); - for (auto const & p : e.tags) - { - // - used in railway station processing - // - used in routing information - // - used in building addresses matching - if (p.first == "network" || p.first == "operator" || p.first == "route" || - p.first == "maxspeed" || - strings::StartsWith(p.first, "addr:")) - { - if (!TBase::IsKeyTagExists(p.first)) - TBase::AddCustomTag(p); - } - // Convert associatedStreet relation name to addr:street tag if we don't have one. - else if (p.first == "name" && processAssociatedStreet) - TBase::AddCustomTag({"addr:street", p.second}); - } -} - -// RelationTagsWay -RelationTagsWay::RelationTagsWay(routing::TagsProcessor & routingTagsProcessor) - : RelationTagsBase(routingTagsProcessor) -{ -} - -bool RelationTagsWay::IsAcceptBoundary(RelationElement const & e) const -{ - std::string role; - CHECK(e.FindWay(TBase::m_featureID, role), (TBase::m_featureID)); - - // Do not accumulate boundary types (boundary=administrative) for inner polygons. - // Example: Minsk city border (admin_level=8) is inner for Minsk area border (admin_level=4). - return (role != "inner"); -} - -void RelationTagsWay::Process(RelationElement const & e) -{ - /// @todo Review route relations in future. - /// Actually, now they give a lot of dummy tags. - std::string const & type = e.GetType(); - if (TBase::IsSkipRelation(type)) - return; - - if (type == "route") - { - if (e.GetTagValue("route") == "road") - { - // Append "network/ref" to the feature ref tag. - std::string ref = e.GetTagValue("ref"); - if (!ref.empty()) - { - std::string const & network = e.GetTagValue("network"); - // Not processing networks with more than 15 chars (see road_shields_parser.cpp). - if (!network.empty() && network.find('/') == std::string::npos && network.size() < 15) - ref = network + '/' + ref; - std::string const & refBase = m_current->GetTag("ref"); - if (!refBase.empty()) - ref = refBase + ';' + ref; - TBase::AddCustomTag({"ref", std::move(ref)}); - } - } - return; - } - - if (type == "restriction") - { - m_routingTagsProcessor.m_restrictionWriter.Write(e); - return; - } - - if (type == "building") - { - // If this way has "outline" role, add [building=has_parts] type. - if (e.GetWayRole(m_current->id) == "outline") - TBase::AddCustomTag({"building", "has_parts"}); - return; - } - - bool const isBoundary = (type == "boundary") && IsAcceptBoundary(e); - bool const processAssociatedStreet = type == "associatedStreet" && - TBase::IsKeyTagExists("addr:housenumber") && - !TBase::IsKeyTagExists("addr:street"); - bool const isHighway = TBase::IsKeyTagExists("highway"); - - for (auto const & p : e.tags) - { - /// @todo Skip common key tags. - if (p.first == "type" || p.first == "route" || p.first == "area") - continue; - - // Convert associatedStreet relation name to addr:street tag if we don't have one. - if (p.first == "name" && processAssociatedStreet) - TBase::AddCustomTag({"addr:street", p.second}); - - // Important! Skip all "name" tags. - if (strings::StartsWith(p.first, "name") || p.first == "int_name") - continue; - - if (!isBoundary && p.first == "boundary") - continue; - - if (p.first == "place") - continue; - - // Do not pass "ref" tags from boundaries and other, non-route relations to highways. - if (p.first == "ref" && isHighway) - continue; - - TBase::AddCustomTag(p); - } -} - -// HolesAccumulator -HolesAccumulator::HolesAccumulator(cache::IntermediateDataReader & holder) : - m_merger(holder) -{ -} - -FeatureBuilder1::Geometry & HolesAccumulator::GetHoles() -{ - ASSERT(m_holes.empty(), ("Can call only once")); - m_merger.ForEachArea(false, [this](FeatureBuilder1::PointSeq const & v, - std::vector const &) - { - m_holes.push_back(std::move(v)); - }); - return m_holes; -} - -// HolesProcessor -HolesProcessor::HolesProcessor(uint64_t id, cache::IntermediateDataReader & holder) - : m_id(id) - , m_holes(holder) -{ -} - -bool HolesProcessor::operator() (uint64_t /*id*/, RelationElement const & e) -{ - std::string const type = e.GetType(); - if (!(type == "multipolygon" || type == "boundary")) - return false; - - std::string role; - if (e.FindWay(m_id, role) && role == "outer") - { - e.ForEachWay(*this); - // stop processing (??? assume that "outer way" exists in one relation only ???) - return true; - } - return false; -} - -void HolesProcessor::operator() (uint64_t id, std::string const & role) -{ - if (id != m_id && role == "inner") - m_holes(id); -} - -// OsmToFeatureTranslator -OsmToFeatureTranslator::OsmToFeatureTranslator(std::shared_ptr emitter, - cache::IntermediateDataReader & holder, - feature::GenerateInfo const & info) : - m_emitter(emitter), - m_holder(holder), - m_coastType(info.m_makeCoasts ? classif().GetCoastType() : 0), - m_nodeRelations(m_routingTagsProcessor), - m_wayRelations(m_routingTagsProcessor), - m_metalinesBuilder(info.GetIntermediateFileName(METALINES_FILENAME)) -{ - auto const addrFilePath = info.GetAddressesFileName(); - if (!addrFilePath.empty()) - m_addrWriter.reset(new FileWriter(addrFilePath)); - - auto const restrictionsFilePath = info.GetIntermediateFileName(RESTRICTIONS_FILENAME); - if (!restrictionsFilePath.empty()) - m_routingTagsProcessor.m_restrictionWriter.Open(restrictionsFilePath); - - auto const roadAccessFilePath = info.GetIntermediateFileName(ROAD_ACCESS_FILENAME); - if (!roadAccessFilePath.empty()) - m_routingTagsProcessor.m_roadAccessWriter.Open(roadAccessFilePath); -} - -void OsmToFeatureTranslator::EmitElement(OsmElement * p) -{ - CHECK(p, ("Tried to emit a null OsmElement")); - - FeatureParams params; - switch(p->type) - { - case OsmElement::EntityType::Node: - { - if (p->m_tags.empty()) - break; - - if (!ParseType(p, params)) - break; - - m2::PointD const pt = MercatorBounds::FromLatLon(p->lat, p->lon); - EmitPoint(pt, params, osm::Id::Node(p->id)); - break; - } - case OsmElement::EntityType::Way: - { - FeatureBuilder1 ft; - m2::PointD pt; - // Parse geometry. - for (uint64_t ref : p->Nodes()) - { - if (!m_holder.GetNode(ref, pt.y, pt.x)) - break; - ft.AddPoint(pt); - } - - if (p->Nodes().size() != ft.GetPointsCount()) - break; - - if (ft.GetPointsCount() < 2) - break; - - if (!ParseType(p, params)) - break; - - ft.SetOsmId(osm::Id::Way(p->id)); - bool isCoastLine = (m_coastType != 0 && params.IsTypeExist(m_coastType)); - - EmitArea(ft, params, [&] (FeatureBuilder1 & ft) - { - isCoastLine = false; // emit coastline feature only once - HolesProcessor processor(p->id, m_holder); - m_holder.ForEachRelationByWay(p->id, processor); - ft.SetAreaAddHoles(processor.GetHoles()); - }); - - m_metalinesBuilder(*p, params); - EmitLine(ft, params, isCoastLine); - break; - } - case OsmElement::EntityType::Relation: - { - // 1. Check, if this is our processable relation. Here we process only polygon relations. - if (!p->HasTagValue("type", "multipolygon")) - break; - - if (!ParseType(p, params)) - break; - - RelationBorders helper(m_holder); - helper.Build(p); - - auto const & holesGeometry = helper.GetHoles(); - auto & outer = helper.GetOuter(); - - outer.ForEachArea(true, [&] (FeatureBuilder1::PointSeq const & pts, - std::vector const & ids) - { - FeatureBuilder1 ft; - - for (uint64_t id : ids) - ft.AddOsmId(osm::Id::Way(id)); - - for (auto const & pt : pts) - ft.AddPoint(pt); - - ft.AddOsmId(osm::Id::Relation(p->id)); - EmitArea(ft, params, [&holesGeometry] (FeatureBuilder1 & ft) { - ft.SetAreaAddHoles(holesGeometry); - }); - }); - break; - } - default: - break; - } -} - -bool OsmToFeatureTranslator::ParseType(OsmElement * p, FeatureParams & params) -{ - // Get tags from parent relations. - if (p->IsNode()) - { - m_nodeRelations.Reset(p->id, p); - m_holder.ForEachRelationByNodeCached(p->id, m_nodeRelations); - } - else if (p->IsWay()) - { - m_wayRelations.Reset(p->id, p); - m_holder.ForEachRelationByWayCached(p->id, m_wayRelations); - } - - // Get params from element tags. - ftype::GetNameAndType(p, params); - if (!params.IsValid()) - return false; - - m_routingTagsProcessor.m_roadAccessWriter.Process(*p); - return true; -} - -void OsmToFeatureTranslator::EmitPoint(m2::PointD const & pt, - FeatureParams params, osm::Id id) const -{ - if (feature::RemoveNoDrawableTypes(params.m_types, feature::GEOM_POINT)) - { - FeatureBuilder1 ft; - ft.SetCenter(pt); - ft.SetOsmId(id); - EmitFeatureBase(ft, params); - } -} - -void OsmToFeatureTranslator::EmitLine(FeatureBuilder1 & ft, FeatureParams params, - bool isCoastLine) const -{ - if (isCoastLine || feature::RemoveNoDrawableTypes(params.m_types, feature::GEOM_LINE)) - { - ft.SetLinear(params.m_reverseGeometry); - EmitFeatureBase(ft, params); - } -} - -void OsmToFeatureTranslator::EmitFeatureBase(FeatureBuilder1 & ft, - FeatureParams const & params) const -{ - ft.SetParams(params); - if (ft.PreSerialize()) - { - std::string addr; - if (m_addrWriter && ftypes::IsBuildingChecker::Instance()(params.m_types) && - ft.FormatFullAddress(addr)) - { - m_addrWriter->Write(addr.c_str(), addr.size()); - } - - (*m_emitter)(ft); - } -} - -// OsmToFeatureTranslatorRegion -OsmToFeatureTranslatorRegion::OsmToFeatureTranslatorRegion(std::shared_ptr emitter, - cache::IntermediateDataReader & holder) - : m_emitter(emitter) - , m_holder(holder) -{ -} - -void OsmToFeatureTranslatorRegion::EmitElement(OsmElement * p) -{ - CHECK(p, ("Tried to emit a null OsmElement")); - - switch(p->type) - { - case OsmElement::EntityType::Relation: - { - FeatureParams params; - if (!(IsSuitableElement(p) && ParseParams(p, params))) - return; - - BuildFeatureAndEmit(p, params); - break; - } - default: - break; - } -} - -bool OsmToFeatureTranslatorRegion::IsSuitableElement(OsmElement const * p) const -{ - static std::set const adminLevels = {"2", "4", "5", "6", "7", "8"}; - static std::set 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; - - if (haveBoundary && haveAdminLevel && haveName) - return true; - } - - return false; -} - -void OsmToFeatureTranslatorRegion::AddInfoAboutRegion(OsmElement const * p, - FeatureBuilder1 & ft) const -{ -} - -bool OsmToFeatureTranslatorRegion::ParseParams(OsmElement * p, FeatureParams & params) const -{ - ftype::GetNameAndType(p, params, [] (uint32_t type) { - return classif().IsTypeValid(type); - }); - return params.IsValid(); -} - -void OsmToFeatureTranslatorRegion::BuildFeatureAndEmit(OsmElement const * p, FeatureParams & params) -{ - RelationBorders helper(m_holder); - helper.Build(p); - auto const & holesGeometry = helper.GetHoles(); - auto & outer = helper.GetOuter(); - outer.ForEachArea(true, [&] (FeatureBuilder1::PointSeq const & pts, - std::vector const & ids) - { - FeatureBuilder1 ft; - for (uint64_t id : ids) - ft.AddOsmId(osm::Id::Way(id)); - - for (auto const & pt : pts) - ft.AddPoint(pt); - - ft.AddOsmId(osm::Id::Relation(p->id)); - if (!ft.IsGeometryClosed()) - return; - - ft.SetAreaAddHoles(holesGeometry); - ft.SetParams(params); - AddInfoAboutRegion(p, ft); - (*m_emitter)(ft); - }); -} -} // namespace generator diff --git a/generator/osm_translator.hpp b/generator/osm_translator.hpp deleted file mode 100644 index 820172d37a..0000000000 --- a/generator/osm_translator.hpp +++ /dev/null @@ -1,225 +0,0 @@ -#pragma once - -#include "generator/feature_builder.hpp" -#include "generator/intermediate_data.hpp" -#include "generator/metalines_builder.hpp" -#include "generator/osm2type.hpp" -#include "generator/osm_element.hpp" -#include "generator/osm_source.hpp" -#include "generator/restriction_writer.hpp" -#include "generator/routing_helpers.hpp" -#include "generator/ways_merger.hpp" - -#include "indexer/classificator.hpp" -#include "indexer/feature_visibility.hpp" -#include "indexer/ftypes_matcher.hpp" - -#include "coding/file_writer.hpp" - -#include "base/assert.hpp" -#include "base/cache.hpp" -#include "base/logging.hpp" -#include "base/stl_add.hpp" -#include "base/string_utils.hpp" -#include "base/osm_id.hpp" - -#include -#include -#include -#include -#include -#include -#include - -namespace generator { - -/// Generated features should include parent relation tags to make -/// full types matching and storing any additional info. -class RelationTagsBase -{ -public: - explicit RelationTagsBase(routing::TagsProcessor & tagsProcessor); - - virtual ~RelationTagsBase() {} - - void Reset(uint64_t fID, OsmElement * p); - - template - bool operator() (uint64_t id, TReader & reader) - { - bool exists = false; - RelationElement & e = m_cache.Find(id, exists); - if (!exists) - CHECK(reader.Read(id, e), (id)); - - Process(e); - return false; - } - -protected: - static bool IsSkipRelation(std::string const & type); - bool IsKeyTagExists(std::string const & key) const; - void AddCustomTag(std::pair const & p); - virtual void Process(RelationElement const & e) = 0; - -protected: - uint64_t m_featureID; - OsmElement * m_current; - routing::TagsProcessor & m_routingTagsProcessor; - -private: - my::Cache m_cache; -}; - -class RelationTagsNode : public RelationTagsBase -{ - using TBase = RelationTagsBase; - -public: - explicit RelationTagsNode(routing::TagsProcessor & tagsProcessor); - -protected: - void Process(RelationElement const & e) override; -}; - - -class RelationTagsWay : public RelationTagsBase -{ -public: - explicit RelationTagsWay(routing::TagsProcessor & routingTagsProcessor); - -private: - using TBase = RelationTagsBase; - using TNameKeys = std::unordered_set; - - bool IsAcceptBoundary(RelationElement const & e) const; - -protected: - void Process(RelationElement const & e) override; -}; - - -class HolesAccumulator -{ -public: - explicit HolesAccumulator(cache::IntermediateDataReader & holder); - - void operator() (uint64_t id) { m_merger.AddWay(id); } - FeatureBuilder1::Geometry & GetHoles(); - -private: - AreaWayMerger m_merger; - FeatureBuilder1::Geometry m_holes; -}; - - -/// Find holes for way with 'id' in first relation. -class HolesProcessor -{ -public: - HolesProcessor(uint64_t id, cache::IntermediateDataReader & holder); - - /// 1. relations process function - bool operator() (uint64_t /*id*/, RelationElement const & e); - /// 2. "ways in relation" process function - void operator() (uint64_t id, std::string const & role); - FeatureBuilder1::Geometry & GetHoles() { return m_holes.GetHoles(); } - -private: - uint64_t m_id; ///< id of way to find it's holes - HolesAccumulator m_holes; -}; - - -class OsmToFeatureTranslatorInterface -{ -public: - virtual ~OsmToFeatureTranslatorInterface() {} - - virtual void EmitElement(OsmElement * p) = 0; -}; - - -class OsmToFeatureTranslator : public OsmToFeatureTranslatorInterface -{ -public: - OsmToFeatureTranslator(std::shared_ptr emitter, - cache::IntermediateDataReader & holder, - feature::GenerateInfo const & info); - - /// The main entry point for parsing process. - void EmitElement(OsmElement * p) override; - -private: - bool ParseType(OsmElement * p, FeatureParams & params); - void EmitFeatureBase(FeatureBuilder1 & ft, FeatureParams const & params) const; - /// @param[in] params Pass by value because it can be modified. - void EmitPoint(m2::PointD const & pt, FeatureParams params, osm::Id id) const; - void EmitLine(FeatureBuilder1 & ft, FeatureParams params, bool isCoastLine) const; - - template - void EmitArea(FeatureBuilder1 & ft, FeatureParams params, MakeFnT makeFn) - { - using namespace feature; - - // Ensure that we have closed area geometry. - if (!ft.IsGeometryClosed()) - return; - - if (ftypes::IsTownOrCity(params.m_types)) - { - auto fb = ft; - makeFn(fb); - m_emitter->EmitCityBoundary(fb, params); - } - - // Key point here is that IsDrawableLike and RemoveNoDrawableTypes - // work a bit different for GEOM_AREA. - if (IsDrawableLike(params.m_types, GEOM_AREA)) - { - // Make the area feature if it has unique area styles. - VERIFY(RemoveNoDrawableTypes(params.m_types, GEOM_AREA), (params)); - - makeFn(ft); - - EmitFeatureBase(ft, params); - } - else - { - // Try to make the point feature if it has point styles. - EmitPoint(ft.GetGeometryCenter(), params, ft.GetLastOsmId()); - } - } - -private: - std::shared_ptr m_emitter; - cache::IntermediateDataReader & m_holder; - uint32_t m_coastType; - std::unique_ptr m_addrWriter; - - routing::TagsProcessor m_routingTagsProcessor; - - RelationTagsNode m_nodeRelations; - RelationTagsWay m_wayRelations; - feature::MetalinesBuilder m_metalinesBuilder; -}; - -class OsmToFeatureTranslatorRegion : public OsmToFeatureTranslatorInterface -{ -public: - OsmToFeatureTranslatorRegion(std::shared_ptr emitter, - cache::IntermediateDataReader & holder); - - void EmitElement(OsmElement * p) override; - -private: - bool IsSuitableElement(OsmElement const * p) const; - void AddInfoAboutRegion(OsmElement const * p, FeatureBuilder1 & ft) const; - bool ParseParams(OsmElement * p, FeatureParams & params) const; - void BuildFeatureAndEmit(OsmElement const * p, FeatureParams & params); - -private: - std::shared_ptr m_emitter; - cache::IntermediateDataReader & m_holder; -}; -} // namespace generator diff --git a/generator/relation_tags.cpp b/generator/relation_tags.cpp new file mode 100644 index 0000000000..f2bf92cad7 --- /dev/null +++ b/generator/relation_tags.cpp @@ -0,0 +1,170 @@ +#include "generator/relation_tags.hpp" + +#include "generator/osm_element.hpp" + +namespace generator +{ +// RelationTagsBase +RelationTagsBase::RelationTagsBase(routing::TagsProcessor & tagsProcessor) : + m_routingTagsProcessor(tagsProcessor), + m_cache(14 /* logCacheSize */) +{ +} + +void RelationTagsBase::Reset(uint64_t fID, OsmElement * p) +{ + m_featureID = fID; + m_current = p; +} + +bool RelationTagsBase::IsSkipRelation(std::string const & type) +{ + /// @todo Skip special relation types. + return (type == "multipolygon" || type == "bridge"); +} + +bool RelationTagsBase::IsKeyTagExists(std::string const & key) const +{ + auto const & tags = m_current->m_tags; + return std::any_of(std::begin(tags), std::end(tags), [&](OsmElement::Tag const & p) { + return p.key == key; + }); +} + +void RelationTagsBase::AddCustomTag(std::pair const & p) +{ + m_current->AddTag(p.first, p.second); +} + +// RelationTagsNode +RelationTagsNode::RelationTagsNode(routing::TagsProcessor & tagsProcessor) : + RelationTagsBase(tagsProcessor) +{ +} + +void RelationTagsNode::Process(RelationElement const & e) +{ + std::string const & type = e.GetType(); + if (TBase::IsSkipRelation(type)) + return; + + if (type == "restriction") + { + m_routingTagsProcessor.m_restrictionWriter.Write(e); + return; + } + + bool const processAssociatedStreet = type == "associatedStreet" && + TBase::IsKeyTagExists("addr:housenumber") && + !TBase::IsKeyTagExists("addr:street"); + for (auto const & p : e.tags) + { + // - used in railway station processing + // - used in routing information + // - used in building addresses matching + if (p.first == "network" || p.first == "operator" || p.first == "route" || + p.first == "maxspeed" || + strings::StartsWith(p.first, "addr:")) + { + if (!TBase::IsKeyTagExists(p.first)) + TBase::AddCustomTag(p); + } + // Convert associatedStreet relation name to addr:street tag if we don't have one. + else if (p.first == "name" && processAssociatedStreet) + TBase::AddCustomTag({"addr:street", p.second}); + } +} + +// RelationTagsWay +RelationTagsWay::RelationTagsWay(routing::TagsProcessor & routingTagsProcessor) + : RelationTagsBase(routingTagsProcessor) +{ +} + +bool RelationTagsWay::IsAcceptBoundary(RelationElement const & e) const +{ + std::string role; + CHECK(e.FindWay(TBase::m_featureID, role), (TBase::m_featureID)); + + // Do not accumulate boundary types (boundary=administrative) for inner polygons. + // Example: Minsk city border (admin_level=8) is inner for Minsk area border (admin_level=4). + return (role != "inner"); +} + +void RelationTagsWay::Process(RelationElement const & e) +{ + /// @todo Review route relations in future. + /// Actually, now they give a lot of dummy tags. + std::string const & type = e.GetType(); + if (TBase::IsSkipRelation(type)) + return; + + if (type == "route") + { + if (e.GetTagValue("route") == "road") + { + // Append "network/ref" to the feature ref tag. + std::string ref = e.GetTagValue("ref"); + if (!ref.empty()) + { + std::string const & network = e.GetTagValue("network"); + // Not processing networks with more than 15 chars (see road_shields_parser.cpp). + if (!network.empty() && network.find('/') == std::string::npos && network.size() < 15) + ref = network + '/' + ref; + std::string const & refBase = m_current->GetTag("ref"); + if (!refBase.empty()) + ref = refBase + ';' + ref; + TBase::AddCustomTag({"ref", std::move(ref)}); + } + } + return; + } + + if (type == "restriction") + { + m_routingTagsProcessor.m_restrictionWriter.Write(e); + return; + } + + if (type == "building") + { + // If this way has "outline" role, add [building=has_parts] type. + if (e.GetWayRole(m_current->id) == "outline") + TBase::AddCustomTag({"building", "has_parts"}); + return; + } + + bool const isBoundary = (type == "boundary") && IsAcceptBoundary(e); + bool const processAssociatedStreet = type == "associatedStreet" && + TBase::IsKeyTagExists("addr:housenumber") && + !TBase::IsKeyTagExists("addr:street"); + bool const isHighway = TBase::IsKeyTagExists("highway"); + + for (auto const & p : e.tags) + { + /// @todo Skip common key tags. + if (p.first == "type" || p.first == "route" || p.first == "area") + continue; + + // Convert associatedStreet relation name to addr:street tag if we don't have one. + if (p.first == "name" && processAssociatedStreet) + TBase::AddCustomTag({"addr:street", p.second}); + + // Important! Skip all "name" tags. + if (strings::StartsWith(p.first, "name") || p.first == "int_name") + continue; + + if (!isBoundary && p.first == "boundary") + continue; + + if (p.first == "place") + continue; + + // Do not pass "ref" tags from boundaries and other, non-route relations to highways. + if (p.first == "ref" && isHighway) + continue; + + TBase::AddCustomTag(p); + } +} +} // namespace generator diff --git a/generator/relation_tags.hpp b/generator/relation_tags.hpp new file mode 100644 index 0000000000..d0a08335e9 --- /dev/null +++ b/generator/relation_tags.hpp @@ -0,0 +1,79 @@ +#pragma once + +#include "generator/intermediate_elements.hpp" +#include "generator/routing_helpers.hpp" + +#include "base/assert.hpp" +#include "base/cache.hpp" + +#include +#include + +class OsmElement; + +namespace generator +{ +/// Generated features should include parent relation tags to make +/// full types matching and storing any additional info. +class RelationTagsBase +{ +public: + explicit RelationTagsBase(routing::TagsProcessor & tagsProcessor); + + virtual ~RelationTagsBase() {} + + void Reset(uint64_t fID, OsmElement * p); + + template + bool operator() (uint64_t id, TReader & reader) + { + bool exists = false; + RelationElement & e = m_cache.Find(id, exists); + if (!exists) + CHECK(reader.Read(id, e), (id)); + + Process(e); + return false; + } + +protected: + static bool IsSkipRelation(std::string const & type); + bool IsKeyTagExists(std::string const & key) const; + void AddCustomTag(std::pair const & p); + virtual void Process(RelationElement const & e) = 0; + + uint64_t m_featureID; + OsmElement * m_current; + routing::TagsProcessor & m_routingTagsProcessor; + +private: + my::Cache m_cache; +}; + +class RelationTagsNode : public RelationTagsBase +{ + using TBase = RelationTagsBase; + +public: + explicit RelationTagsNode(routing::TagsProcessor & tagsProcessor); + +protected: + void Process(RelationElement const & e) override; +}; + + +class RelationTagsWay : public RelationTagsBase +{ +public: + explicit RelationTagsWay(routing::TagsProcessor & routingTagsProcessor); + +private: + using TBase = RelationTagsBase; + using TNameKeys = std::unordered_set; + + bool IsAcceptBoundary(RelationElement const & e) const; + +protected: + void Process(RelationElement const & e) override; +}; +} // namespace generator diff --git a/generator/translator_factory.hpp b/generator/translator_factory.hpp new file mode 100644 index 0000000000..1be869b07c --- /dev/null +++ b/generator/translator_factory.hpp @@ -0,0 +1,33 @@ +#pragma once + +#include "generator/factory_utils.hpp" +#include "generator/translator_interface.hpp" +#include "generator/translator_planet.hpp" +#include "generator/translator_region.hpp" + +#include "base/assert.hpp" + +#include +#include + +namespace generator +{ +enum class TranslatorType +{ + PLANET, + REGION +}; + +template +std::shared_ptr CreateTranslator(TranslatorType type, Args&&... args) +{ + switch (type) + { + case TranslatorType::PLANET: + return create(std::forward(args)...); + case TranslatorType::REGION: + return create(std::forward(args)...); + } + CHECK_SWITCH(); +} +} // namespace generator diff --git a/generator/translator_interface.hpp b/generator/translator_interface.hpp new file mode 100644 index 0000000000..ef1aae89be --- /dev/null +++ b/generator/translator_interface.hpp @@ -0,0 +1,15 @@ +#pragma once + +class OsmElement; + +namespace generator +{ +// Osm to feature translator interface. +class TranslatorInterface +{ +public: + virtual ~TranslatorInterface() {} + + virtual void EmitElement(OsmElement * p) = 0; +}; +} // namespace generator diff --git a/generator/translator_planet.cpp b/generator/translator_planet.cpp new file mode 100644 index 0000000000..96465238b1 --- /dev/null +++ b/generator/translator_planet.cpp @@ -0,0 +1,234 @@ +#include "generator/translator_planet.hpp" + +#include "generator/emitter_interface.hpp" +#include "generator/feature_builder.hpp" +#include "generator/generate_info.hpp" +#include "generator/holes.hpp" +#include "generator/intermediate_data.hpp" +#include "generator/osm2type.hpp" +#include "generator/osm_element.hpp" + +#include "indexer/classificator.hpp" +#include "indexer/feature_visibility.hpp" +#include "indexer/ftypes_matcher.hpp" + +#include "geometry/point2d.hpp" + +#include "base/assert.hpp" + +#include +#include + +namespace generator +{ +TranslatorPlanet::TranslatorPlanet(std::shared_ptr emitter, + cache::IntermediateDataReader & holder, + feature::GenerateInfo const & info) : + m_emitter(emitter), + m_holder(holder), + m_coastType(info.m_makeCoasts ? classif().GetCoastType() : 0), + m_nodeRelations(m_routingTagsProcessor), + m_wayRelations(m_routingTagsProcessor), + m_metalinesBuilder(info.GetIntermediateFileName(METALINES_FILENAME)) +{ + auto const addrFilePath = info.GetAddressesFileName(); + if (!addrFilePath.empty()) + m_addrWriter.reset(new FileWriter(addrFilePath)); + + auto const restrictionsFilePath = info.GetIntermediateFileName(RESTRICTIONS_FILENAME); + if (!restrictionsFilePath.empty()) + m_routingTagsProcessor.m_restrictionWriter.Open(restrictionsFilePath); + + auto const roadAccessFilePath = info.GetIntermediateFileName(ROAD_ACCESS_FILENAME); + if (!roadAccessFilePath.empty()) + m_routingTagsProcessor.m_roadAccessWriter.Open(roadAccessFilePath); +} + +void TranslatorPlanet::EmitElement(OsmElement * p) +{ + CHECK(p, ("Tried to emit a null OsmElement")); + + FeatureParams params; + switch(p->type) + { + case OsmElement::EntityType::Node: + { + if (p->m_tags.empty()) + break; + + if (!ParseType(p, params)) + break; + + m2::PointD const pt = MercatorBounds::FromLatLon(p->lat, p->lon); + EmitPoint(pt, params, osm::Id::Node(p->id)); + break; + } + case OsmElement::EntityType::Way: + { + FeatureBuilder1 ft; + m2::PointD pt; + // Parse geometry. + for (uint64_t ref : p->Nodes()) + { + if (!m_holder.GetNode(ref, pt.y, pt.x)) + break; + ft.AddPoint(pt); + } + + if (p->Nodes().size() != ft.GetPointsCount()) + break; + + if (ft.GetPointsCount() < 2) + break; + + if (!ParseType(p, params)) + break; + + ft.SetOsmId(osm::Id::Way(p->id)); + bool isCoastLine = (m_coastType != 0 && params.IsTypeExist(m_coastType)); + + EmitArea(ft, params, [&] (FeatureBuilder1 & ft) + { + isCoastLine = false; // emit coastline feature only once + HolesProcessor processor(p->id, m_holder); + m_holder.ForEachRelationByWay(p->id, processor); + ft.SetAreaAddHoles(processor.GetHoles()); + }); + + m_metalinesBuilder(*p, params); + EmitLine(ft, params, isCoastLine); + break; + } + case OsmElement::EntityType::Relation: + { + // 1. Check, if this is our processable relation. Here we process only polygon relations. + if (!p->HasTagValue("type", "multipolygon")) + break; + + if (!ParseType(p, params)) + break; + + HolesRelation helper(m_holder); + helper.Build(p); + + auto const & holesGeometry = helper.GetHoles(); + auto & outer = helper.GetOuter(); + + outer.ForEachArea(true, [&] (FeatureBuilder1::PointSeq const & pts, + std::vector const & ids) + { + FeatureBuilder1 ft; + + for (uint64_t id : ids) + ft.AddOsmId(osm::Id::Way(id)); + + for (auto const & pt : pts) + ft.AddPoint(pt); + + ft.AddOsmId(osm::Id::Relation(p->id)); + EmitArea(ft, params, [&holesGeometry] (FeatureBuilder1 & ft) { + ft.SetAreaAddHoles(holesGeometry); + }); + }); + break; + } + default: + break; + } +} + +bool TranslatorPlanet::ParseType(OsmElement * p, FeatureParams & params) +{ + // Get tags from parent relations. + if (p->IsNode()) + { + m_nodeRelations.Reset(p->id, p); + m_holder.ForEachRelationByNodeCached(p->id, m_nodeRelations); + } + else if (p->IsWay()) + { + m_wayRelations.Reset(p->id, p); + m_holder.ForEachRelationByWayCached(p->id, m_wayRelations); + } + + // Get params from element tags. + ftype::GetNameAndType(p, params); + if (!params.IsValid()) + return false; + + m_routingTagsProcessor.m_roadAccessWriter.Process(*p); + return true; +} + +void TranslatorPlanet::EmitPoint(m2::PointD const & pt, + FeatureParams params, osm::Id id) const +{ + if (feature::RemoveNoDrawableTypes(params.m_types, feature::GEOM_POINT)) + { + FeatureBuilder1 ft; + ft.SetCenter(pt); + ft.SetOsmId(id); + EmitFeatureBase(ft, params); + } +} + +void TranslatorPlanet::EmitLine(FeatureBuilder1 & ft, FeatureParams params, bool isCoastLine) const +{ + if (isCoastLine || feature::RemoveNoDrawableTypes(params.m_types, feature::GEOM_LINE)) + { + ft.SetLinear(params.m_reverseGeometry); + EmitFeatureBase(ft, params); + } +} + +void TranslatorPlanet::EmitArea(FeatureBuilder1 & ft, FeatureParams params, + std::function fn) +{ + using namespace feature; + + // Ensure that we have closed area geometry. + if (!ft.IsGeometryClosed()) + return; + + if (ftypes::IsTownOrCity(params.m_types)) + { + auto fb = ft; + fn(fb); + m_emitter->EmitCityBoundary(fb, params); + } + + // Key point here is that IsDrawableLike and RemoveNoDrawableTypes + // work a bit different for GEOM_AREA. + if (IsDrawableLike(params.m_types, GEOM_AREA)) + { + // Make the area feature if it has unique area styles. + VERIFY(RemoveNoDrawableTypes(params.m_types, GEOM_AREA), (params)); + + fn(ft); + + EmitFeatureBase(ft, params); + } + else + { + // Try to make the point feature if it has point styles. + EmitPoint(ft.GetGeometryCenter(), params, ft.GetLastOsmId()); + } +} + +void TranslatorPlanet::EmitFeatureBase(FeatureBuilder1 & ft, + FeatureParams const & params) const +{ + ft.SetParams(params); + if (ft.PreSerialize()) + { + std::string addr; + if (m_addrWriter && ftypes::IsBuildingChecker::Instance()(params.m_types) && + ft.FormatFullAddress(addr)) + { + m_addrWriter->Write(addr.c_str(), addr.size()); + } + + (*m_emitter)(ft); + } +} +} // namespace generator diff --git a/generator/translator_planet.hpp b/generator/translator_planet.hpp new file mode 100644 index 0000000000..7e2c8baf3a --- /dev/null +++ b/generator/translator_planet.hpp @@ -0,0 +1,58 @@ +#pragma once + +#include "generator/metalines_builder.hpp" +#include "generator/relation_tags.hpp" +#include "generator/routing_helpers.hpp" +#include "generator/translator_interface.hpp" + +#include "indexer/feature_data.hpp" + +#include "base/osm_id.hpp" + +#include +#include + +class OsmElement; +class FeatureBuilder1; +namespace feature +{ +class GenerateInfo; +} // namespace feature + +namespace generator +{ +class EmitterInterface; +namespace cache +{ +class IntermediateDataReader; +} // namespace cache + +// Osm to feature translator for planet. +class TranslatorPlanet : public TranslatorInterface +{ +public: + TranslatorPlanet(std::shared_ptr emitter, cache::IntermediateDataReader & holder, + feature::GenerateInfo const & info); + + // The main entry point for parsing process. + void EmitElement(OsmElement * p) override; + +private: + bool ParseType(OsmElement * p, FeatureParams & params); + void EmitFeatureBase(FeatureBuilder1 & ft, FeatureParams const & params) const; + /// @param[in] params Pass by value because it can be modified. + void EmitPoint(m2::PointD const & pt, FeatureParams params, osm::Id id) const; + void EmitLine(FeatureBuilder1 & ft, FeatureParams params, bool isCoastLine) const; + void EmitArea(FeatureBuilder1 & ft, FeatureParams params, std::function fn); + +private: + std::shared_ptr m_emitter; + cache::IntermediateDataReader & m_holder; + uint32_t m_coastType; + std::unique_ptr m_addrWriter; + routing::TagsProcessor m_routingTagsProcessor; + RelationTagsNode m_nodeRelations; + RelationTagsWay m_wayRelations; + feature::MetalinesBuilder m_metalinesBuilder; +}; +} // namespace generator diff --git a/generator/translator_region.cpp b/generator/translator_region.cpp new file mode 100644 index 0000000000..4485c638d1 --- /dev/null +++ b/generator/translator_region.cpp @@ -0,0 +1,115 @@ +#include "generator/translator_region.hpp" + +#include "generator/emitter_interface.hpp" +#include "generator/feature_builder.hpp" +#include "generator/generate_info.hpp" +#include "generator/holes.hpp" +#include "generator/intermediate_data.hpp" +#include "generator/osm2type.hpp" +#include "generator/osm_element.hpp" + +#include "indexer/classificator.hpp" + +#include "base/assert.hpp" + +#include +#include +#include + +namespace generator +{ +TranslatorRegion::TranslatorRegion(std::shared_ptr emitter, + cache::IntermediateDataReader & holder) : + m_emitter(emitter), + m_holder(holder) +{ +} + +void TranslatorRegion::EmitElement(OsmElement * p) +{ + CHECK(p, ("Tried to emit a null OsmElement")); + + switch(p->type) + { + case OsmElement::EntityType::Relation: + { + FeatureParams params; + if (!(IsSuitableElement(p) && ParseParams(p, params))) + return; + + BuildFeatureAndEmit(p, params); + break; + } + default: + break; + } +} + +bool TranslatorRegion::IsSuitableElement(OsmElement const * p) const +{ + static std::set const adminLevels = {"2", "4", "5", "6", "7", "8"}; + static std::set 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; + + if (haveBoundary && haveAdminLevel && haveName) + return true; + } + + return false; +} + +void TranslatorRegion::AddInfoAboutRegion(OsmElement const * p, + FeatureBuilder1 & ft) const +{ +} + +bool TranslatorRegion::ParseParams(OsmElement * p, FeatureParams & params) const +{ + ftype::GetNameAndType(p, params, [] (uint32_t type) { + return classif().IsTypeValid(type); + }); + return params.IsValid(); +} + +void TranslatorRegion::BuildFeatureAndEmit(OsmElement const * p, FeatureParams & params) +{ + HolesRelation helper(m_holder); + helper.Build(p); + auto const & holesGeometry = helper.GetHoles(); + auto & outer = helper.GetOuter(); + outer.ForEachArea(true, [&] (FeatureBuilder1::PointSeq const & pts, + std::vector const & ids) + { + FeatureBuilder1 ft; + for (uint64_t id : ids) + ft.AddOsmId(osm::Id::Way(id)); + + for (auto const & pt : pts) + ft.AddPoint(pt); + + ft.AddOsmId(osm::Id::Relation(p->id)); + if (!ft.IsGeometryClosed()) + return; + + ft.SetAreaAddHoles(holesGeometry); + ft.SetParams(params); + AddInfoAboutRegion(p, ft); + (*m_emitter)(ft); + }); +} +} // namespace generator diff --git a/generator/translator_region.hpp b/generator/translator_region.hpp new file mode 100644 index 0000000000..9f1bc86914 --- /dev/null +++ b/generator/translator_region.hpp @@ -0,0 +1,42 @@ +#pragma once + +#include "generator/translator_interface.hpp" + +#include "indexer/feature_data.hpp" + +#include + +class OsmElement; +class FeatureBuilder1; +namespace feature +{ +class GenerateInfo; +} // namespace feature + +namespace generator +{ +class EmitterInterface; +namespace cache +{ +class IntermediateDataReader; +} // namespace cache + +// Osm to feature translator for regions. +class TranslatorRegion : public TranslatorInterface +{ +public: + TranslatorRegion(std::shared_ptr emitter, cache::IntermediateDataReader & holder); + + void EmitElement(OsmElement * p) override; + +private: + bool IsSuitableElement(OsmElement const * p) const; + void AddInfoAboutRegion(OsmElement const * p, FeatureBuilder1 & ft) const; + bool ParseParams(OsmElement * p, FeatureParams & params) const; + void BuildFeatureAndEmit(OsmElement const * p, FeatureParams & params); + +private: + std::shared_ptr m_emitter; + cache::IntermediateDataReader & m_holder; +}; +} // namespace generator