[generator] Separated osm_translator

This commit is contained in:
Maksim Andrianov 2018-08-02 16:05:53 +03:00 committed by Vladimir Byko-Ianko
parent af2717c9d1
commit 2fbe30b352
15 changed files with 900 additions and 759 deletions

View file

@ -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

View file

@ -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"

75
generator/holes.cpp Normal file
View file

@ -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<uint64_t> 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

60
generator/holes.hpp Normal file
View file

@ -0,0 +1,60 @@
#pragma once
#include "generator/feature_builder.hpp"
#include "generator/ways_merger.hpp"
#include <cstdint>
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

View file

@ -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<void(OsmElement *
using PreEmit = std::function<bool(OsmElement *)>;
static bool GenerateRaw(feature::GenerateInfo & info, std::shared_ptr<EmitterInterface> emitter,
PreEmit const & preEmit, std::unique_ptr<OsmToFeatureTranslatorInterface> parser)
PreEmit const & preEmit, std::shared_ptr<TranslatorInterface> 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<EmitterInter
};
auto cache = LoadCache(info);
auto parser = std::make_unique<OsmToFeatureTranslator>(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<EmitterInterface> emitter)
{
auto preEmit = [](OsmElement * e) { return true; };
auto cache = LoadCache(info);
auto parser = std::make_unique<OsmToFeatureTranslatorRegion>(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)

View file

@ -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<std::string, std::string> 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<uint64_t> 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<EmitterInterface> 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<uint64_t> 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<EmitterInterface> 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<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;
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<uint64_t> 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

View file

@ -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 <cstring>
#include <list>
#include <string>
#include <type_traits>
#include <unordered_set>
#include <utility>
#include <vector>
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 <class TReader>
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<std::string, std::string> const & p);
virtual void Process(RelationElement const & e) = 0;
protected:
uint64_t m_featureID;
OsmElement * m_current;
routing::TagsProcessor & m_routingTagsProcessor;
private:
my::Cache<uint64_t, RelationElement> 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<std::string>;
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<EmitterInterface> 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 <class MakeFnT>
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<EmitterInterface> m_emitter;
cache::IntermediateDataReader & m_holder;
uint32_t m_coastType;
std::unique_ptr<FileWriter> 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<EmitterInterface> 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<EmitterInterface> m_emitter;
cache::IntermediateDataReader & m_holder;
};
} // namespace generator

170
generator/relation_tags.cpp Normal file
View file

@ -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<std::string, std::string> 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

View file

@ -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 <cstdint>
#include <string>
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 <class TReader>
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<std::string, std::string> const & p);
virtual void Process(RelationElement const & e) = 0;
uint64_t m_featureID;
OsmElement * m_current;
routing::TagsProcessor & m_routingTagsProcessor;
private:
my::Cache<uint64_t, RelationElement> 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<std::string>;
bool IsAcceptBoundary(RelationElement const & e) const;
protected:
void Process(RelationElement const & e) override;
};
} // namespace generator

View file

@ -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 <memory>
#include <utility>
namespace generator
{
enum class TranslatorType
{
PLANET,
REGION
};
template <class... Args>
std::shared_ptr<TranslatorInterface> CreateTranslator(TranslatorType type, Args&&... args)
{
switch (type)
{
case TranslatorType::PLANET:
return create<TranslatorPlanet>(std::forward<Args>(args)...);
case TranslatorType::REGION:
return create<TranslatorRegion>(std::forward<Args>(args)...);
}
CHECK_SWITCH();
}
} // namespace generator

View file

@ -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

View file

@ -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 <string>
#include <vector>
namespace generator
{
TranslatorPlanet::TranslatorPlanet(std::shared_ptr<EmitterInterface> 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<uint64_t> 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<void(FeatureBuilder1 &)> 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

View file

@ -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 <functional>
#include <memory>
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<EmitterInterface> 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<void(FeatureBuilder1 &)> fn);
private:
std::shared_ptr<EmitterInterface> m_emitter;
cache::IntermediateDataReader & m_holder;
uint32_t m_coastType;
std::unique_ptr<FileWriter> m_addrWriter;
routing::TagsProcessor m_routingTagsProcessor;
RelationTagsNode m_nodeRelations;
RelationTagsWay m_wayRelations;
feature::MetalinesBuilder m_metalinesBuilder;
};
} // namespace generator

View file

@ -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 <set>
#include <string>
#include <vector>
namespace generator
{
TranslatorRegion::TranslatorRegion(std::shared_ptr<EmitterInterface> 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<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;
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<uint64_t> 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

View file

@ -0,0 +1,42 @@
#pragma once
#include "generator/translator_interface.hpp"
#include "indexer/feature_data.hpp"
#include <memory>
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<EmitterInterface> 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<EmitterInterface> m_emitter;
cache::IntermediateDataReader & m_holder;
};
} // namespace generator