diff --git a/generator/generator_tool/generator_tool.cpp b/generator/generator_tool/generator_tool.cpp index 497d9ff75d..0ac866a34c 100644 --- a/generator/generator_tool/generator_tool.cpp +++ b/generator/generator_tool/generator_tool.cpp @@ -87,6 +87,8 @@ DEFINE_uint64(planet_version, my::SecondsSinceEpoch(), // Preprocessing and feature generator. DEFINE_bool(preprocess, false, "1st pass - create nodes/ways/relations data."); DEFINE_bool(generate_features, false, "2nd pass - generate intermediate features."); +DEFINE_bool(generate_region_features, false, + "Generate intermediate features for regions to use in regions index and borders generation."); DEFINE_bool(generate_geometry, false, "3rd pass - split and simplify geometry and triangles for features."); DEFINE_bool(generate_index, false, "4rd pass - generate index."); @@ -211,10 +213,10 @@ int main(int argc, char ** argv) GetStyleReader().SetCurrentStyle(MapStyleMerged); // Load classificator only when necessary. - if (FLAGS_make_coasts || FLAGS_generate_features || FLAGS_generate_geometry || - FLAGS_generate_geo_objects_index || FLAGS_generate_regions || FLAGS_generate_index || - FLAGS_generate_search_index || FLAGS_generate_cities_boundaries || FLAGS_calc_statistics || - FLAGS_type_statistics || FLAGS_dump_types || FLAGS_dump_prefixes || + if (FLAGS_make_coasts || FLAGS_generate_features || FLAGS_generate_region_features || + FLAGS_generate_geometry || FLAGS_generate_geo_objects_index || FLAGS_generate_regions || + FLAGS_generate_index || FLAGS_generate_search_index || FLAGS_generate_cities_boundaries || + FLAGS_calc_statistics || FLAGS_type_statistics || FLAGS_dump_types || FLAGS_dump_prefixes || FLAGS_dump_feature_names != "" || FLAGS_check_mwm || FLAGS_srtm_path != "" || FLAGS_make_routing_index || FLAGS_make_cross_mwm || FLAGS_make_transit_cross_mwm || FLAGS_generate_traffic_keys || FLAGS_transit_path != "" || FLAGS_ugc_data != "") @@ -232,6 +234,8 @@ int main(int argc, char ** argv) if (FLAGS_generate_features || FLAGS_make_coasts) { LOG(LINFO, ("Generating final data ...")); + CHECK(!FLAGS_generate_region_features, ("FLAGS_generate_features and FLAGS_make_coasts should " + "not be used with FLAGS_generate_region_features")); genInfo.m_splitByPolygons = FLAGS_split_by_polygons; genInfo.m_createWorld = FLAGS_generate_world; @@ -260,12 +264,21 @@ int main(int argc, char ** argv) } } } - else + + if (FLAGS_generate_region_features) { - if (!FLAGS_output.empty()) - genInfo.m_bucketNames.push_back(FLAGS_output); + CHECK(!FLAGS_generate_features && !FLAGS_make_coasts, + ("FLAGS_generate_features and FLAGS_make_coasts should " + "not be used with FLAGS_generate_region_features")); + + genInfo.m_fileName = FLAGS_output; + if (!GenerateRegionFeatures(genInfo)) + return -1; } + if (genInfo.m_bucketNames.empty() && !FLAGS_output.empty()) + genInfo.m_bucketNames.push_back(FLAGS_output); + if (FLAGS_generate_geo_objects_index || FLAGS_generate_regions) { if (FLAGS_output.empty() || FLAGS_intermediate_data_path.empty()) diff --git a/generator/osm_source.cpp b/generator/osm_source.cpp index 18c5d2f42d..81dc3b2053 100644 --- a/generator/osm_source.cpp +++ b/generator/osm_source.cpp @@ -33,6 +33,7 @@ #include "coding/parse_xml.hpp" #include +#include #include "defines.hpp" @@ -728,8 +729,11 @@ void ProcessOsmElementsFromO5M(SourceReader & stream, function; + template -bool GenerateFeaturesImpl(feature::GenerateInfo & info, EmitterBase & emitter) +bool GenerateFeaturesImpl(feature::GenerateInfo & info, EmitterBase & emitter, + PreEmit const & preEmit) { try { @@ -746,19 +750,9 @@ bool GenerateFeaturesImpl(feature::GenerateInfo & info, EmitterBase & emitter) info.GetIntermediateFileName(ROAD_ACCESS_FILENAME, ""), info.GetIntermediateFileName(METALINES_FILENAME, "")); - TagAdmixer tagAdmixer(info.GetIntermediateFileName("ways", ".csv"), - info.GetIntermediateFileName("towns", ".csv")); - TagReplacer tagReplacer(GetPlatform().ResourcesDir() + REPLACED_TAGS_FILE); - OsmTagMixer osmTagMixer(GetPlatform().ResourcesDir() + MIXED_TAGS_FILE); - - // Here we can add new tags to the elements! - auto const fn = [&](OsmElement * e) - { - tagReplacer(e); - tagAdmixer(e); - osmTagMixer(e); - - parser.EmitElement(e); + auto const fn = [&](OsmElement * e) { + if (preEmit(e)) + parser.EmitElement(e); }; SourceReader reader = info.m_osmFileName.empty() ? SourceReader() : SourceReader(info.m_osmFileName); @@ -825,21 +819,86 @@ bool GenerateIntermediateDataImpl(feature::GenerateInfo & info) return true; } -bool GenerateFeatures(feature::GenerateInfo & info, EmitterFactory factory) +bool GenerateRaw(feature::GenerateInfo & info, std::unique_ptr emitter, PreEmit const & preEmit) { - auto emitter = factory(info); switch (info.m_nodeStorageType) { case feature::GenerateInfo::NodeStorageType::File: - return GenerateFeaturesImpl>(info, *emitter); + return GenerateFeaturesImpl>(info, *emitter, preEmit); case feature::GenerateInfo::NodeStorageType::Index: - return GenerateFeaturesImpl>(info, *emitter); + return GenerateFeaturesImpl>(info, *emitter, preEmit); case feature::GenerateInfo::NodeStorageType::Memory: - return GenerateFeaturesImpl>(info, *emitter); + return GenerateFeaturesImpl>(info, *emitter, preEmit); } return false; } +bool GenerateFeatures(feature::GenerateInfo & info, EmitterFactory factory) +{ + TagAdmixer tagAdmixer(info.GetIntermediateFileName("ways", ".csv"), + info.GetIntermediateFileName("towns", ".csv")); + TagReplacer tagReplacer(GetPlatform().ResourcesDir() + REPLACED_TAGS_FILE); + OsmTagMixer osmTagMixer(GetPlatform().ResourcesDir() + MIXED_TAGS_FILE); + + auto preEmit = [&](OsmElement * e) { + // Here we can add new tags to the elements! + tagReplacer(e); + tagAdmixer(e); + osmTagMixer(e); + return true; + }; + + return GenerateRaw(info, factory(info), preEmit); +} + +bool GenerateRegionFeatures(feature::GenerateInfo & info, EmitterFactory factory) +{ + set const adminLevels = {"2", "4", "5", "6"}; + set const places = {"city", "town", "village", "hamlet", "suburb"}; + + auto isRegion = [&adminLevels, &places](OsmElement const & e) { + // We do not make any assumptions about shape of places without explicit border for now. + if (e.type != OsmElement::EntityType::Way && e.type != OsmElement::EntityType::Relation) + return false; + + bool haveBoundary = false; + bool haveAdminLevel = false; + for (auto const & t : e.Tags()) + { + if (t.key == "boundary" && t.value == "administrative") + haveBoundary = true; + + if (t.key == "admin_level" && adminLevels.find(t.value) != adminLevels.end()) + haveAdminLevel = true; + + if (haveBoundary && haveAdminLevel) + return true; + + if (t.key == "place" && places.find(t.value) != places.end()) + return true; + } + + return false; + }; + + auto preEmit = [&isRegion](OsmElement * e) { + if (isRegion(*e)) + { + // Emit feature with original geometry and visible "natural = land" tag. + // Now emitter does not have a single place of decision which elements to emit and which to + // ignore. So the only way to make it emit element is to construct "good" element. + // This code should be removed in case of emitter refactoring. + e->m_tags = {}; + e->AddTag("natural", "land"); + e->AddTag("type", "multipolygon"); + return true; + } + return false; + }; + + return GenerateRaw(info, factory(info), preEmit); +} + bool GenerateIntermediateData(feature::GenerateInfo & info) { switch (info.m_nodeStorageType) diff --git a/generator/osm_source.hpp b/generator/osm_source.hpp index 779c81efb0..e627ad581e 100644 --- a/generator/osm_source.hpp +++ b/generator/osm_source.hpp @@ -61,6 +61,8 @@ using EmitterFactory = std::function(feature::Gener bool GenerateFeatures(feature::GenerateInfo & info, EmitterFactory factory = MakeMainFeatureEmitter); +bool GenerateRegionFeatures(feature::GenerateInfo & info, + EmitterFactory factory = MakeMainFeatureEmitter); bool GenerateIntermediateData(feature::GenerateInfo & info); void ProcessOsmElementsFromO5M(SourceReader & stream, std::function processor);