From 2c34655148e710c81872bf5cfe1ef9baecc9c796 Mon Sep 17 00:00:00 2001 From: Maksim Andrianov Date: Fri, 28 Jun 2019 11:40:12 +0300 Subject: [PATCH] Optimization step 'generate_features': Collectors can merge, new metalines builder. --- generator/collector_addresses.cpp | 37 ++- generator/collector_addresses.hpp | 17 +- generator/collector_camera.cpp | 76 ++++- generator/collector_camera.hpp | 26 +- generator/collector_city_boundary.cpp | 54 +++ generator/collector_city_boundary.hpp | 34 ++ generator/collector_collection.cpp | 26 ++ generator/collector_collection.hpp | 11 + generator/collector_interface.hpp | 54 ++- generator/collector_tag.cpp | 48 ++- generator/collector_tag.hpp | 20 +- generator/feature_builder.hpp | 1 + generator/generator_tests/CMakeLists.txt | 2 + .../merge_collectors_tests.cpp | 92 ++++++ generator/generator_tests/metalines_tests.cpp | 148 +++++++++ generator/maxspeeds_collector.cpp | 47 ++- generator/maxspeeds_collector.hpp | 18 +- generator/metalines_builder.cpp | 308 ++++++++++-------- generator/metalines_builder.hpp | 62 +++- generator/regions/collector_region_info.cpp | 29 +- generator/regions/collector_region_info.hpp | 14 +- generator/restriction_writer.cpp | 54 +-- generator/restriction_writer.hpp | 28 +- generator/road_access_generator.cpp | 230 +++++++------ generator/road_access_generator.hpp | 32 +- 25 files changed, 1120 insertions(+), 348 deletions(-) create mode 100644 generator/collector_city_boundary.cpp create mode 100644 generator/collector_city_boundary.hpp create mode 100644 generator/generator_tests/merge_collectors_tests.cpp create mode 100644 generator/generator_tests/metalines_tests.cpp diff --git a/generator/collector_addresses.cpp b/generator/collector_addresses.cpp index 06c9d65d6d..8d8552ea60 100644 --- a/generator/collector_addresses.cpp +++ b/generator/collector_addresses.cpp @@ -1,21 +1,52 @@ #include "generator/collector_addresses.hpp" #include "generator/feature_builder.hpp" +#include "generator/intermediate_data.hpp" #include "indexer/ftypes_matcher.hpp" using namespace feature; +#include + namespace generator { CollectorAddresses::CollectorAddresses(std::string const & filename) - : m_addrWriter(std::make_unique(filename)) {} + : CollectorInterface(filename) {} -void CollectorAddresses::CollectFeature(FeatureBuilder const & feature, OsmElement const &) +std::shared_ptr +CollectorAddresses::Clone(std::shared_ptr const &) const +{ + return std::make_shared(GetFilename()); +} + +void CollectorAddresses::CollectFeature(feature::FeatureBuilder const & feature, OsmElement const &) { std::string addr; auto const & checker = ftypes::IsBuildingChecker::Instance(); if (checker(feature.GetTypes()) && feature.FormatFullAddress(addr)) - m_addrWriter->Write(addr.c_str(), addr.size()); + m_stringStream << addr << "\n"; +} + +void CollectorAddresses::Save() +{ + std::ofstream stream; + stream.exceptions(std::fstream::failbit | std::fstream::badbit); + stream.open(GetFilename()); + stream << m_stringStream.str(); +} + +void CollectorAddresses::Merge(CollectorInterface const * collector) +{ + CHECK(collector, ()); + + collector->MergeInto(const_cast(this)); +} + +void CollectorAddresses::MergeInto(CollectorAddresses * collector) const +{ + CHECK(collector, ()); + + collector->m_stringStream << m_stringStream.str(); } } // namespace generator diff --git a/generator/collector_addresses.hpp b/generator/collector_addresses.hpp index 9df5c83e03..ff8be67c90 100644 --- a/generator/collector_addresses.hpp +++ b/generator/collector_addresses.hpp @@ -2,13 +2,16 @@ #include "generator/collector_interface.hpp" -#include "coding/file_writer.hpp" - #include +#include #include namespace generator { +namespace cache +{ +class IntermediateDataReader; +} // The class CollectorAddresses is responsible for the collecting addresses to the file. class CollectorAddresses : public CollectorInterface { @@ -16,10 +19,16 @@ public: CollectorAddresses(std::string const & filename); // CollectorInterface overrides: + std::shared_ptr + Clone(std::shared_ptr const & = {}) const override; + void CollectFeature(feature::FeatureBuilder const & feature, OsmElement const &) override; - void Save() override {} + void Save() override; + + void Merge(CollectorInterface const * collector) override; + void MergeInto(CollectorAddresses * collector) const override; private: - std::unique_ptr m_addrWriter; + std::stringstream m_stringStream; }; } // namespace generator diff --git a/generator/collector_camera.cpp b/generator/collector_camera.cpp index d2ea040e57..cf4edea654 100644 --- a/generator/collector_camera.cpp +++ b/generator/collector_camera.cpp @@ -1,6 +1,7 @@ #include "generator/collector_camera.hpp" #include "generator/feature_builder.hpp" +#include "generator/intermediate_data.hpp" #include "generator/osm_element.hpp" #include "generator/maxspeeds_parser.hpp" @@ -21,8 +22,12 @@ #include "base/logging.hpp" #include "base/string_utils.hpp" +#include +#include + using namespace feature; + namespace routing { size_t const CameraProcessor::kMaxSpeedSpeedStringLength = 32; @@ -58,13 +63,21 @@ void CameraProcessor::ForEachCamera(Fn && toDo) const void CameraProcessor::ProcessWay(OsmElement const & element) { - for (auto const node : element.m_nodes) - { - if (m_speedCameras.find(node) == m_speedCameras.cend()) - continue; + m_ways[element.m_id] = element.m_nodes; +} - auto & ways = m_cameraToWays[node]; - ways.push_back(element.m_id); +void CameraProcessor::FillCameraInWays() +{ + for (auto const & way : m_ways) + { + for (auto const & node : way.second) + { + auto const itCamera = m_speedCameras.find(node); + if (itCamera == m_speedCameras.cend()) + continue; + + m_cameraToWays[itCamera->first].push_back(way.first); + } } } @@ -75,8 +88,23 @@ void CameraProcessor::ProcessNode(OsmElement const & element) m_speedCameras.emplace(element.m_id, std::move(camera)); } -CameraCollector::CameraCollector(std::string const & writerFile) : - m_fileWriter(writerFile) {} +void CameraProcessor::Merge(CameraProcessor const & cameraProcessor) +{ + auto const & otherCameras = cameraProcessor.m_speedCameras; + m_speedCameras.insert(std::begin(otherCameras), std::end(otherCameras)); + + auto const & otherWays = cameraProcessor.m_ways; + m_ways.insert(std::begin(otherWays), std::end(otherWays)); +} + +CameraCollector::CameraCollector(std::string const & filename) : + generator::CollectorInterface(filename) {} + +std::shared_ptr +CameraCollector::Clone(std::shared_ptr const &) const +{ + return std::make_shared(GetFilename()); +} void CameraCollector::CollectFeature(FeatureBuilder const & feature, OsmElement const & element) { @@ -99,7 +127,8 @@ void CameraCollector::CollectFeature(FeatureBuilder const & feature, OsmElement } } -void CameraCollector::Write(CameraProcessor::CameraInfo const & camera, std::vector const & ways) +void CameraCollector::Write(FileWriter & writer, CameraProcessor::CameraInfo const & camera, + std::vector const & ways) { std::string maxSpeedStringKmPH = camera.m_speed; int32_t maxSpeedKmPH = 0; @@ -110,23 +139,40 @@ void CameraCollector::Write(CameraProcessor::CameraInfo const & camera, std::vec uint32_t const lat = DoubleToUint32(camera.m_lat, ms::LatLon::kMinLat, ms::LatLon::kMaxLat, kPointCoordBits); - WriteToSink(m_fileWriter, lat); + WriteToSink(writer, lat); uint32_t const lon = DoubleToUint32(camera.m_lon, ms::LatLon::kMinLon, ms::LatLon::kMaxLon, kPointCoordBits); - WriteToSink(m_fileWriter, lon); + WriteToSink(writer, lon); - WriteToSink(m_fileWriter, static_cast(maxSpeedKmPH)); + WriteToSink(writer, static_cast(maxSpeedKmPH)); auto const size = static_cast(ways.size()); - WriteToSink(m_fileWriter, size); + WriteToSink(writer, size); for (auto wayId : ways) - WriteToSink(m_fileWriter, wayId); + WriteToSink(writer, wayId); } void CameraCollector::Save() { + LOG(LINFO, ("void CameraCollector::Save()")); using namespace std::placeholders; - m_processor.ForEachCamera(std::bind(&CameraCollector::Write, this, _1, _2)); + m_processor.FillCameraInWays(); + FileWriter writer(GetFilename()); + m_processor.ForEachCamera(std::bind(&CameraCollector::Write, this, std::ref(writer), _1, _2)); +} + +void CameraCollector::Merge(generator::CollectorInterface const * collector) +{ + CHECK(collector, ()); + + collector->MergeInto(const_cast(this)); +} + +void CameraCollector::MergeInto(CameraCollector * collector) const +{ + CHECK(collector, ()); + + collector->m_processor.Merge(this->m_processor); } } // namespace routing diff --git a/generator/collector_camera.hpp b/generator/collector_camera.hpp index 4e9059ea4a..55cd068245 100644 --- a/generator/collector_camera.hpp +++ b/generator/collector_camera.hpp @@ -15,6 +15,13 @@ namespace generator_tests { class TestCameraCollector; } // namespace generator_tests +namespace generator +{ +namespace cache +{ +class IntermediateDataReader; +} // namespace cache +} // namespace generator struct OsmElement; @@ -58,7 +65,12 @@ public: void ProcessNode(OsmElement const & element); void ProcessWay(OsmElement const & element); + void FillCameraInWays(); + + void Merge(CameraProcessor const & cameraProcessor); + private: + std::unordered_map> m_ways; std::unordered_map m_speedCameras; std::unordered_map> m_cameraToWays; }; @@ -68,18 +80,24 @@ class CameraCollector : public generator::CollectorInterface public: friend class generator_tests::TestCameraCollector; - explicit CameraCollector(std::string const & writerFile); + explicit CameraCollector(std::string const & filename); // generator::CollectorInterface overrides: + std::shared_ptr + Clone(std::shared_ptr const & = {}) const override; // We will process all nodes before ways because of o5m format: // all nodes are first, then all ways, then all relations. void CollectFeature(feature::FeatureBuilder const & feature, OsmElement const & element) override; void Save() override; -private: - void Write(CameraProcessor::CameraInfo const & camera, std::vector const & ways); + void Merge(generator::CollectorInterface const * collector) override; + void MergeInto(CameraCollector * collector) const override; - FileWriter m_fileWriter; +private: + void Write(FileWriter & writer, CameraProcessor::CameraInfo const & camera, + std::vector const & ways); + + std::vector m_buffer; CameraProcessor m_processor; }; } // namespace routing diff --git a/generator/collector_city_boundary.cpp b/generator/collector_city_boundary.cpp new file mode 100644 index 0000000000..b0bb6f25d9 --- /dev/null +++ b/generator/collector_city_boundary.cpp @@ -0,0 +1,54 @@ +#include "generator/collector_city_boundary.hpp" + +#include "generator/feature_generator.hpp" +#include "generator/intermediate_data.hpp" + +#include "indexer/ftypes_matcher.hpp" + +#include +#include + +using namespace feature; + +namespace generator +{ +CityBoundaryCollector::CityBoundaryCollector(std::string const & filename) + : CollectorInterface(filename) {} + +std::shared_ptr +CityBoundaryCollector::Clone(std::shared_ptr const &) const +{ + return std::make_shared(GetFilename()); +} + +void CityBoundaryCollector::CollectFeature(FeatureBuilder const & feature, OsmElement const &) +{ + if (feature.IsArea() && ftypes::IsCityTownOrVillage(feature.GetTypes())) + m_boundaries.emplace_back(feature); +} + +void CityBoundaryCollector::Save() +{ + FeatureBuilderWriter collector(GetFilename()); + for (auto & boundary : m_boundaries) + { + if (boundary.PreSerialize()) + collector.Write(boundary); + } +} + +void CityBoundaryCollector::Merge(generator::CollectorInterface const * collector) +{ + CHECK(collector, ()); + + collector->MergeInto(const_cast(this)); +} + +void CityBoundaryCollector::MergeInto(CityBoundaryCollector * collector) const +{ + CHECK(collector, ()); + + std::copy(std::begin(m_boundaries), std::end(m_boundaries), + std::back_inserter(collector->m_boundaries)); +} +} // namespace generator diff --git a/generator/collector_city_boundary.hpp b/generator/collector_city_boundary.hpp new file mode 100644 index 0000000000..e51c35043c --- /dev/null +++ b/generator/collector_city_boundary.hpp @@ -0,0 +1,34 @@ +#pragma once + +#include "generator/collector_interface.hpp" +#include "generator/feature_builder.hpp" + +#include +#include + +namespace generator +{ +namespace cache +{ +class IntermediateDataReader; +} // namespace cache + +class CityBoundaryCollector : public CollectorInterface +{ +public: + explicit CityBoundaryCollector(std::string const & filename); + + // CollectorInterface overrides: + std::shared_ptr + Clone(std::shared_ptr const & = {}) const override; + + void CollectFeature(feature::FeatureBuilder const & feature, OsmElement const &) override; + void Save() override; + + void Merge(generator::CollectorInterface const * collector) override; + void MergeInto(CityBoundaryCollector * collector) const override; + +private: + std::vector m_boundaries; +}; +} // namespace generator diff --git a/generator/collector_collection.cpp b/generator/collector_collection.cpp index 7e817103fc..afcea638e0 100644 --- a/generator/collector_collection.cpp +++ b/generator/collector_collection.cpp @@ -8,6 +8,15 @@ using namespace feature; namespace generator { +std::shared_ptr +CollectorCollection::Clone(std::shared_ptr const & cache) const +{ + auto p = std::make_shared(); + for (auto const & c : m_collection) + p->Append(c->Clone(cache)); + return p; +} + void CollectorCollection::Collect(OsmElement const & element) { for (auto & c : m_collection) @@ -31,4 +40,21 @@ void CollectorCollection::Save() for (auto & c : m_collection) c->Save(); } + +void CollectorCollection::Merge(CollectorInterface const * collector) +{ + CHECK(collector, ()); + + collector->MergeInto(const_cast(this)); +} + +void CollectorCollection::MergeInto(CollectorCollection * collector) const +{ + CHECK(collector, ()); + + auto & otherCollection = collector->m_collection; + CHECK_EQUAL(m_collection.size(), otherCollection.size(), ()); + for (size_t i = 0; i < m_collection.size(); ++i) + otherCollection[i]->Merge(m_collection[i].get()); +} } // namespace generator diff --git a/generator/collector_collection.hpp b/generator/collector_collection.hpp index 09637def1e..8a3f89e18f 100644 --- a/generator/collector_collection.hpp +++ b/generator/collector_collection.hpp @@ -15,14 +15,25 @@ class FeatureBuilder; namespace generator { +namespace cache +{ +class IntermediateDataReader; +} // namespace cache + // This class allows you to work with a group of collectors as with one. class CollectorCollection : public CollectionBase>, public CollectorInterface { public: // CollectorInterface overrides: + std::shared_ptr + Clone(std::shared_ptr const & cache = {}) const override; + void Collect(OsmElement const & element) override; void CollectRelation(RelationElement const & element) override; void CollectFeature(feature::FeatureBuilder const & feature, OsmElement const & element) override; void Save() override; + + void Merge(CollectorInterface const * collector) override; + void MergeInto(CollectorCollection * collector) const override; }; } // namespace generator diff --git a/generator/collector_interface.hpp b/generator/collector_interface.hpp index bf22ba6c82..76bf4322c5 100644 --- a/generator/collector_interface.hpp +++ b/generator/collector_interface.hpp @@ -1,5 +1,8 @@ #pragma once +#include "base/assert.hpp" + +#include #include struct OsmElement; @@ -14,19 +17,66 @@ namespace base { class GeoObjectId; } // namespace base - +namespace feature +{ +class MetalinesBuilder; +} // namespace feature +namespace routing +{ +class CameraCollector; +class RestrictionWriter; +class RoadAccessWriter; +} // namespace routing namespace generator { +class CollectorAddresses; +class CollectorCollection; +class CollectorTag; +class MaxspeedsCollector; +class CityBoundaryCollector; +namespace cache +{ +class IntermediateDataReader; +} // namespace cache +namespace regions +{ +class CollectorRegionInfo; +} // namespace regions + // Implementing this interface allows an object to collect data from RelationElement, -// OsmElement and FeatureBuilder1 elements. +// OsmElement and FeatureBuilder elements. class CollectorInterface { public: + CollectorInterface(std::string const & filename = {}) : m_filename(filename) {} virtual ~CollectorInterface() = default; + virtual std::shared_ptr + Clone(std::shared_ptr const & = {}) const = 0; + virtual void Collect(OsmElement const &) {} virtual void CollectRelation(RelationElement const &) {} virtual void CollectFeature(feature::FeatureBuilder const &, OsmElement const &) {} virtual void Save() = 0; + + virtual void Merge(CollectorInterface const *) = 0; + + virtual void MergeInto(CityBoundaryCollector *) const { FailIfMethodUnsuppirted(); } + virtual void MergeInto(routing::CameraCollector *) const { FailIfMethodUnsuppirted(); } + virtual void MergeInto(routing::RestrictionWriter *) const { FailIfMethodUnsuppirted(); } + virtual void MergeInto(routing::RoadAccessWriter *) const { FailIfMethodUnsuppirted(); } + virtual void MergeInto(CollectorAddresses *) const { FailIfMethodUnsuppirted(); } + virtual void MergeInto(CollectorTag *) const { FailIfMethodUnsuppirted(); } + virtual void MergeInto(MaxspeedsCollector *) const { FailIfMethodUnsuppirted(); } + virtual void MergeInto(feature::MetalinesBuilder *) const { FailIfMethodUnsuppirted(); } + virtual void MergeInto(regions::CollectorRegionInfo *) const { FailIfMethodUnsuppirted(); } + virtual void MergeInto(CollectorCollection *) const { FailIfMethodUnsuppirted(); } + + std::string const & GetFilename() const { return m_filename; } + +private: + void FailIfMethodUnsuppirted() const { CHECK(false, ("This method is unsupported.")); } + + std::string m_filename; }; } // namespace generator diff --git a/generator/collector_tag.cpp b/generator/collector_tag.cpp index 291cb1468f..9fb437b052 100644 --- a/generator/collector_tag.cpp +++ b/generator/collector_tag.cpp @@ -1,5 +1,6 @@ #include "generator/collector_tag.hpp" +#include "generator/intermediate_data.hpp" #include "generator/osm_element.hpp" #include "base/geo_object_id.hpp" @@ -9,19 +10,38 @@ namespace generator { CollectorTag::CollectorTag(std::string const & filename, std::string const & tagKey, Validator const & validator, bool ignoreIfNotOpen) - : m_tagKey(tagKey), m_validator(validator), m_needCollect(true) + : CollectorInterface(filename) + , m_tagKey(tagKey) + , m_validator(validator) + , m_ignoreIfNotOpen(ignoreIfNotOpen) {} + +std::shared_ptr +CollectorTag::Clone(std::shared_ptr const &) const { - m_stream.exceptions(std::fstream::failbit | std::fstream::badbit); + return std::make_shared(GetFilename(), m_tagKey, m_validator, m_ignoreIfNotOpen); +} + +void CollectorTag::Collect(OsmElement const & el) +{ + auto const tag = el.GetTag(m_tagKey); + if (!tag.empty() && m_validator(tag)) + m_stream << GetGeoObjectId(el).GetEncodedId() << "\t" << tag << "\n"; +} + +void CollectorTag::Save() +{ + std::ofstream stream; + stream.exceptions(std::fstream::failbit | std::fstream::badbit); try { - m_stream.open(filename); + stream.open(GetFilename()); + stream << m_stream.str(); } catch (std::ios::failure const & e) { - if (ignoreIfNotOpen) + if (m_ignoreIfNotOpen) { - m_needCollect = false; - LOG(LINFO, ("Сould not open file", filename, ". This was ignored.")); + LOG(LINFO, ("Сould not open file", GetFilename(), ". This was ignored.")); } else { @@ -30,13 +50,17 @@ CollectorTag::CollectorTag(std::string const & filename, std::string const & tag } } -void CollectorTag::Collect(OsmElement const & el) +void CollectorTag::Merge(CollectorInterface const * collector) { - if (!m_needCollect) - return; + CHECK(collector, ()); - auto const tag = el.GetTag(m_tagKey); - if (!tag.empty() && m_validator(tag)) - m_stream << GetGeoObjectId(el).GetEncodedId() << "\t" << tag << "\n"; + collector->MergeInto(const_cast(this)); +} + +void CollectorTag::MergeInto(CollectorTag * collector) const +{ + CHECK(collector, ()); + + collector->m_stream << this->m_stream.str(); } } // namespace generator diff --git a/generator/collector_tag.hpp b/generator/collector_tag.hpp index 25c43bdd55..07b28865d0 100644 --- a/generator/collector_tag.hpp +++ b/generator/collector_tag.hpp @@ -4,6 +4,8 @@ #include #include +#include +#include #include struct OsmElement; @@ -14,6 +16,11 @@ class GeoObjectId; namespace generator { +namespace cache +{ +class IntermediateDataReader; +} // namespace cache + // CollectorTag class collects validated value of a tag and saves it to file with following // format: osmIdtagValue. class CollectorTag : public CollectorInterface @@ -25,13 +32,18 @@ public: Validator const & validator, bool ignoreIfNotOpen = false); // CollectorInterface overrides: - void Collect(OsmElement const & el) override; - void Save() override {} + std::shared_ptr + Clone(std::shared_ptr const & = {}) const override; + void Collect(OsmElement const & el) override; + void Save() override; + + void Merge(CollectorInterface const * collector) override; + void MergeInto(CollectorTag * collector) const override; private: - std::ofstream m_stream; + std::stringstream m_stream; std::string m_tagKey; Validator m_validator; - bool m_needCollect; + bool m_ignoreIfNotOpen; }; } // namespace generator diff --git a/generator/feature_builder.hpp b/generator/feature_builder.hpp index 6de52006a7..4a7e89441e 100644 --- a/generator/feature_builder.hpp +++ b/generator/feature_builder.hpp @@ -135,6 +135,7 @@ public: } bool HasType(uint32_t t) const { return m_params.IsTypeExist(t); } + bool HasType(uint32_t t, uint8_t level) const { return m_params.IsTypeExist(t, level); } uint32_t FindType(uint32_t comp, uint8_t level) const { return m_params.FindType(comp, level); } FeatureParams::Types const & GetTypes() const { return m_params.m_types; } diff --git a/generator/generator_tests/CMakeLists.txt b/generator/generator_tests/CMakeLists.txt index 94ba972b5c..6ba1e4d23d 100644 --- a/generator/generator_tests/CMakeLists.txt +++ b/generator/generator_tests/CMakeLists.txt @@ -17,7 +17,9 @@ set( filter_elements_tests.cpp intermediate_data_test.cpp maxspeeds_tests.cpp + merge_collectors_tests.cpp metadata_parser_test.cpp + metalines_tests.cpp node_mixer_test.cpp osm2meta_test.cpp osm_o5m_source_test.cpp diff --git a/generator/generator_tests/merge_collectors_tests.cpp b/generator/generator_tests/merge_collectors_tests.cpp new file mode 100644 index 0000000000..1f244e3908 --- /dev/null +++ b/generator/generator_tests/merge_collectors_tests.cpp @@ -0,0 +1,92 @@ +#include "testing/testing.hpp" + +#include "generator/generator_tests/common.hpp" + +#include "generator/collector_collection.hpp" +#include "generator/collector_interface.hpp" +#include "generator/collector_tag.hpp" + +#include "base/geo_object_id.hpp" + +#include "platform/platform.hpp" + +#include +#include +#include +#include + +using namespace base; +using namespace generator; +using namespace generator_tests; + +namespace +{ +auto const kEmptyValidator = [](auto const &) { return true; }; +} + +UNIT_TEST(MergeCollector_Case1) +{ + auto const filename = GetFileName(); + std::string const tagKey = "admin_level"; + auto collector1 = std::make_shared(filename, tagKey, kEmptyValidator); + + collector1->Collect(MakeOsmElement(1 /* id */, {{"admin_level", "1"}}, OsmElement::EntityType::Relation)); + collector1->Collect(MakeOsmElement(2 /* id */, {{"admin_level", "2"}}, OsmElement::EntityType::Relation)); + + auto collector2 = collector1->Clone(); + + collector2->Collect(MakeOsmElement(3 /* id */, {{"admin_level", "3"}}, OsmElement::EntityType::Relation)); + collector2->Collect(MakeOsmElement(4 /* id */, {{"admin_level", "4"}}, OsmElement::EntityType::Relation)); + + collector1->Merge(collector2.get()); + collector1->Save(); + + std::vector const answers = { + std::to_string(GeoObjectId(GeoObjectId::Type::ObsoleteOsmRelation, 1 /* id */).GetEncodedId()) + "\t1", + std::to_string(GeoObjectId(GeoObjectId::Type::ObsoleteOsmRelation, 2 /* id */).GetEncodedId()) + "\t2", + std::to_string(GeoObjectId(GeoObjectId::Type::ObsoleteOsmRelation, 3 /* id */).GetEncodedId()) + "\t3", + std::to_string(GeoObjectId(GeoObjectId::Type::ObsoleteOsmRelation, 4 /* id */).GetEncodedId()) + "\t4", + }; + + std::ifstream stream; + stream.exceptions(std::ios::badbit); + stream.open(filename); + size_t pos = 0; + std::string line; + while (std::getline(stream, line)) + TEST_EQUAL(line, answers[pos++], ()); +} + +UNIT_TEST(MergeCollector_Case2) +{ + auto const filename = GetFileName(); + std::string const tagKey = "admin_level"; + auto collection1 = std::make_shared(); + collection1->Append(std::make_shared(filename, tagKey, kEmptyValidator)); + + collection1->Collect(MakeOsmElement(1 /* id */, {{"admin_level", "1"}}, OsmElement::EntityType::Relation)); + collection1->Collect(MakeOsmElement(2 /* id */, {{"admin_level", "2"}}, OsmElement::EntityType::Relation)); + + auto collection2 = collection1->Clone(); + + collection2->Collect(MakeOsmElement(3 /* id */, {{"admin_level", "3"}}, OsmElement::EntityType::Relation)); + collection2->Collect(MakeOsmElement(4 /* id */, {{"admin_level", "4"}}, OsmElement::EntityType::Relation)); + + collection1->Merge(collection2.get()); + collection1->Save(); + + std::vector const answers = { + std::to_string(GeoObjectId(GeoObjectId::Type::ObsoleteOsmRelation, 1 /* id */).GetEncodedId()) + "\t1", + std::to_string(GeoObjectId(GeoObjectId::Type::ObsoleteOsmRelation, 2 /* id */).GetEncodedId()) + "\t2", + std::to_string(GeoObjectId(GeoObjectId::Type::ObsoleteOsmRelation, 3 /* id */).GetEncodedId()) + "\t3", + std::to_string(GeoObjectId(GeoObjectId::Type::ObsoleteOsmRelation, 4 /* id */).GetEncodedId()) + "\t4", + }; + + std::ifstream stream; + stream.exceptions(std::ios::badbit); + stream.open(filename); + size_t pos = 0; + std::string line; + while (std::getline(stream, line)) + TEST_EQUAL(line, answers[pos++], ()); +} diff --git a/generator/generator_tests/metalines_tests.cpp b/generator/generator_tests/metalines_tests.cpp new file mode 100644 index 0000000000..d6658b859e --- /dev/null +++ b/generator/generator_tests/metalines_tests.cpp @@ -0,0 +1,148 @@ +#include "testing/testing.hpp" + +#include "generator/metalines_builder.hpp" +#include "generator/osm_element.hpp" + +#include +#include +#include +#include +#include +#include + +using namespace feature; + +namespace +{ +OsmElement MakeHighway(uint64_t id, std::string const & name, std::vector const & nodes, bool isOneway = false) +{ + OsmElement element; + element.m_id = id; + element.m_type = OsmElement::EntityType::Way; + element.AddTag("ref", ""); + element.AddTag("name", name); + element.AddTag("highway", "primary"); + if (isOneway) + element.AddTag("oneway", "yes"); + element.m_nodes = nodes; + return element; +} + +size_t MakeKey(OsmElement const & element) +{ + auto const name = element.GetTag("name"); + auto const ref = element.GetTag("ref"); + return std::hash{}(name + '\0' + ref); +} + +LineStringMerger::InputData MakeInputData(std::vector const & elements) +{ + LineStringMerger::InputData inputData; + for (auto const & element : elements) + inputData.emplace(MakeKey(element), std::make_shared(element)); + + return inputData; +}; + +bool IsEquil(LineStringMerger::LinePtr const & lineString, std::vector const & ways) +{ + auto const & w = lineString->GetWays(); + return w == ways; +} + +auto const w1 = MakeHighway(1/* id */, "w" /* name */, {1, 2, 3} /* nodes */); +auto const w2 = MakeHighway(2/* id */, "w" /* name */, {3, 4, 5} /* nodes */); +auto const w3 = MakeHighway(3/* id */, "w" /* name */, {5, 6, 7} /* nodes */); + +auto const w4 = MakeHighway(4/* id */, "w" /* name */, {7, 8, 9} /* nodes */); +auto const w5 = MakeHighway(5/* id */, "w" /* name */, {9, 10, 11} /* nodes */); + +auto const wo6 = MakeHighway(6/* id */, "w" /* name */, {13, 12, 3} /* nodes */, true /* isOneway */); +auto const wo7 = MakeHighway(7/* id */, "w" /* name */, {15, 14, 13} /* nodes */, true /* isOneway */); +auto const wo8 = MakeHighway(8/* id */, "w" /* name */, {17, 16, 15} /* nodes */, true /* isOneway */); + +auto const b1 = MakeHighway(1/* id */, "b" /* name */, {1, 2, 3} /* nodes */); +auto const b2 = MakeHighway(2/* id */, "b" /* name */, {3, 4, 5} /* nodes */); +} // namespace + +UNIT_TEST(MetalinesTest_Case0) +{ + auto const inputData = MakeInputData({w1}); + auto outputData = LineStringMerger::Merge(inputData); + TEST_EQUAL(outputData.size(), 0 /* unique names roads count */, ()); + + outputData = LineStringMerger::Merge({}); + TEST_EQUAL(outputData.size(), 0 /* unique names roads count */, ()); +} + +UNIT_TEST(MetalinesTest_Case1) +{ + auto const inputData = MakeInputData({w1, w2}); + auto const outputData = LineStringMerger::Merge(inputData); + + auto const key = MakeKey(w1); + TEST_EQUAL(outputData.size(), 1 /* unique names roads count */, ()); + TEST_EQUAL(outputData.at(key)[0]->GetWays().size(), 2 /* merged way size */, ()); + TEST(IsEquil(outputData.at(key)[0], {1, 2}) /* merged way */, ()); +} + +UNIT_TEST(MetalinesTest_Case2) +{ + auto const inputData = MakeInputData({w1, w3, w2}); + auto const outputData = LineStringMerger::Merge(inputData); + + auto const key = MakeKey(w1); + TEST_EQUAL(outputData.size(), 1 /* unique names roads count */, ()); + TEST_EQUAL(outputData.at(key)[0]->GetWays().size(), 3 /* merged way size */, ()); + TEST(IsEquil(outputData.at(key)[0], {1, 2, 3}) /* merged way */, ()); +} + +UNIT_TEST(MetalinesTest_Case3) +{ + auto const inputData = MakeInputData({w1, w4, w2, w5,}); + auto const outputData = LineStringMerger::Merge(inputData); + + auto const key = MakeKey(w1); + TEST_EQUAL(outputData.size(), 1 /* unique names roads count */, ()); + TEST_EQUAL(outputData.at(key).size(), 2 /* ways count */, ()); + + TEST_EQUAL(outputData.at(key)[0]->GetWays().size(), 2 /* merged way size */, ()); + TEST(IsEquil(outputData.at(key)[0], {1, 2}) /* merged way */, ()); + + TEST_EQUAL(outputData.at(key)[1]->GetWays().size(), 2 /* merged way size */, ()); + TEST(IsEquil(outputData.at(key)[1], {4, 5}) /* merged way */, ()); +} + +UNIT_TEST(MetalinesTest_Case4) +{ + auto const inputData = MakeInputData({w1, wo6,}); + auto const outputData = LineStringMerger::Merge(inputData); + + auto const key = MakeKey(w1); + TEST_EQUAL(outputData.size(), 1 /* unique names roads count */, ()); + TEST_EQUAL(outputData.at(key).size(), 1 /* ways count */, ()); + TEST(IsEquil(outputData.at(key)[0], {6, -1}) /* merged way */, ()); +} + +UNIT_TEST(MetalinesTest_Case5) +{ + auto const inputData = MakeInputData({w1, w2, wo6,}); + auto const outputData = LineStringMerger::Merge(inputData); + + auto const key = MakeKey(w1); + TEST_EQUAL(outputData.size(), 1 /* unique names roads count */, ()); + TEST_EQUAL(outputData.at(key).size(), 1 /* ways count */, ()); + TEST(IsEquil(outputData.at(key)[0], {1, 2}) /* merged way */, ()); +} + +UNIT_TEST(MetalinesTest_Case6) +{ + auto const inputData = MakeInputData({w1, b1, w2, b2,}); + auto const outputData = LineStringMerger::Merge(inputData); + + auto const keyW = MakeKey(w1); + auto const keyB = MakeKey(b1); + TEST_EQUAL(outputData.size(), 2 /* unique names roads count */, ()); + TEST_EQUAL(outputData.at(keyW).size(), 1 /* ways count */, ()); + TEST_EQUAL(outputData.at(keyB).size(), 1 /* ways count */, ()); +} diff --git a/generator/maxspeeds_collector.cpp b/generator/maxspeeds_collector.cpp index ff1ff33de4..39d4681b66 100644 --- a/generator/maxspeeds_collector.cpp +++ b/generator/maxspeeds_collector.cpp @@ -10,7 +10,9 @@ #include "base/logging.hpp" #include "base/string_utils.hpp" +#include #include +#include #include using namespace base; @@ -33,6 +35,16 @@ bool ParseMaxspeedAndWriteToStream(string const & maxspeed, SpeedInUnits & speed namespace generator { +MaxspeedsCollector::MaxspeedsCollector(string const & filename) + : CollectorInterface(filename) {} + + +std::shared_ptr +MaxspeedsCollector::Clone(std::shared_ptr const &) const +{ + return std::make_shared(GetFilename()); +} + void MaxspeedsCollector::CollectFeature(FeatureBuilder const &, OsmElement const & p) { if (!p.IsWay()) @@ -101,26 +113,29 @@ void MaxspeedsCollector::CollectFeature(FeatureBuilder const &, OsmElement const void MaxspeedsCollector::Save() { - Flush(); -} + LOG(LINFO, ("Saving maxspeed tag values to", GetFilename())); -void MaxspeedsCollector::Flush() -{ - LOG(LINFO, ("Saving maxspeed tag values to", m_filePath)); - ofstream stream(m_filePath); - - if (!stream) - { - LOG(LERROR, ("Cannot open file", m_filePath)); - return; - } + ofstream stream; + stream.exceptions(fstream::failbit | fstream::badbit); + stream.open(GetFilename()); for (auto const & s : m_data) stream << s << '\n'; - if (stream.fail()) - LOG(LERROR, ("Cannot write to file", m_filePath)); - else - LOG(LINFO, ("Wrote", m_data.size(), "maxspeed tags to", m_filePath)); + LOG(LINFO, ("Wrote", m_data.size(), "maxspeed tags to", GetFilename())); +} + +void MaxspeedsCollector::Merge(CollectorInterface const * collector) +{ + CHECK(collector, ()); + + collector->MergeInto(const_cast(this)); +} + +void MaxspeedsCollector::MergeInto(MaxspeedsCollector * collector) const +{ + CHECK(collector, ()); + + copy(begin(m_data), end(m_data), back_inserter(collector->m_data)); } } // namespace generator diff --git a/generator/maxspeeds_collector.hpp b/generator/maxspeeds_collector.hpp index bd10dc61bb..3f03e77b2b 100644 --- a/generator/maxspeeds_collector.hpp +++ b/generator/maxspeeds_collector.hpp @@ -1,13 +1,20 @@ #pragma once #include "generator/collector_interface.hpp" +#include "generator/feature_builder.hpp" #include "generator/osm_element.hpp" +#include #include #include namespace generator { +namespace cache +{ +class IntermediateDataReader; +} // namespace cache + /// \brief Collects all maxspeed tags value and saves them to a csv file. /// Every line describes maxspeed, maxspeed:forward and maxspeed:backward /// tags of features. The format of the lines is described below. @@ -15,15 +22,19 @@ class MaxspeedsCollector : public CollectorInterface { public: /// \param filePath path to csv file. - explicit MaxspeedsCollector(std::string const & filePath) : m_filePath(filePath) {} + explicit MaxspeedsCollector(std::string const & filename); // CollectorInterface overrides: + std::shared_ptr + Clone(std::shared_ptr const & = {}) const override; + void CollectFeature(feature::FeatureBuilder const &, OsmElement const & p) override; void Save() override; -private: - void Flush(); + void Merge(CollectorInterface const * collector) override; + void MergeInto(MaxspeedsCollector * collector) const override; +private: // |m_data| contains strings with maxspeed tags value for corresponding features in one of the // following formats // 1. osm id,units kmh or mph,maxspeed value @@ -47,6 +58,5 @@ private: // are converted to an appropriate speed value and macro "none" and "walk" are converted // to |kNoneMaxSpeed| and |kWalkMaxSpeed|. std::vector m_data; - std::string m_filePath; }; } // namespace generator diff --git a/generator/metalines_builder.cpp b/generator/metalines_builder.cpp index 1b97d48ead..65ef8a8a96 100644 --- a/generator/metalines_builder.cpp +++ b/generator/metalines_builder.cpp @@ -1,5 +1,6 @@ #include "generator/metalines_builder.hpp" +#include "generator/intermediate_data.hpp" #include "generator/routing_helpers.hpp" #include "indexer/classificator.hpp" @@ -20,181 +21,208 @@ namespace { uint8_t const kMetaLinesSectionVersion = 1; - -using Ways = std::vector; - -/// A string of connected ways. -class LineString -{ - Ways m_ways; - uint64_t m_start; - uint64_t m_end; - bool m_oneway; - -public: - explicit LineString(OsmElement const & way) - { - std::string const oneway = way.GetTag("oneway"); - m_oneway = !oneway.empty() && oneway != "no"; - int32_t const wayId = base::checked_cast(way.m_id); - m_ways.push_back(oneway == "-1" ? -wayId : wayId); - CHECK_GREATER_OR_EQUAL(way.Nodes().size(), 2, ()); - m_start = way.Nodes().front(); - m_end = way.Nodes().back(); - } - - Ways const & GetWays() const { return m_ways; } - - void Reverse() - { - ASSERT(!m_oneway, ("Trying to reverse a one-way road.")); - std::swap(m_start, m_end); - std::reverse(m_ways.begin(), m_ways.end()); - for (auto & p : m_ways) - p = -p; - } - - bool Add(LineString & line) - { - if (m_start == line.m_start || m_end == line.m_end) - { - if (!line.m_oneway) - line.Reverse(); - else if (!m_oneway) - Reverse(); - else - return false; - } - if (m_end == line.m_start) - { - m_ways.insert(m_ways.end(), line.m_ways.begin(), line.m_ways.end()); - m_end = line.m_end; - m_oneway = m_oneway || line.m_oneway; - } - else if (m_start == line.m_end) - { - m_ways.insert(m_ways.begin(), line.m_ways.begin(), line.m_ways.end()); - m_start = line.m_start; - m_oneway = m_oneway || line.m_oneway; - } - else - { - return false; - } - return true; - } -}; } // namespace namespace feature { -/// A list of segments, that is, LineStrings, sharing the same attributes. -class Segments +LineString::LineString(OsmElement const & way) { - std::list m_parts; + std::string const oneway = way.GetTag("oneway"); + m_oneway = !oneway.empty() && oneway != "no"; + int32_t const wayId = base::checked_cast(way.m_id); + m_ways.push_back(oneway == "-1" ? -wayId : wayId); + CHECK_GREATER_OR_EQUAL(way.Nodes().size(), 2, ()); + m_start = way.Nodes().front(); + m_end = way.Nodes().back(); +} -public: - explicit Segments(OsmElement const & way) { m_parts.emplace_back(way); } +void LineString::Reverse() +{ + ASSERT(!m_oneway, ("Trying to reverse a one-way road.")); + std::swap(m_start, m_end); + std::reverse(m_ways.begin(), m_ways.end()); + for (auto & p : m_ways) + p = -p; +} - void Add(OsmElement const & way) +bool LineString::Add(LineString & line) +{ + if (m_start == line.m_start || m_end == line.m_end) { - LineString line(way); - auto found = m_parts.end(); - for (auto i = m_parts.begin(); i != m_parts.end(); ++i) + if (!line.m_oneway) + line.Reverse(); + else if (!m_oneway) + Reverse(); + else + return false; + } + + if (m_end == line.m_start) + { + m_ways.insert(m_ways.end(), line.m_ways.begin(), line.m_ways.end()); + m_end = line.m_end; + m_oneway = m_oneway || line.m_oneway; + } + else if (m_start == line.m_end) + { + m_ways.insert(m_ways.begin(), line.m_ways.begin(), line.m_ways.end()); + m_start = line.m_start; + m_oneway = m_oneway || line.m_oneway; + } + else + { + return false; + } + return true; +} + +// static +LineStringMerger::OutputData LineStringMerger::Merge(InputData const & data) +{ + InputData mergedLines; + auto const intermediateData = OrderData(data); + for (auto & p : intermediateData) + { + Buffer buffer; + for (auto & lineString : p.second) + TryMerge(lineString, buffer); + + std::unordered_set uniqLineStrings; + for (auto const & pb : buffer) { - if (i->Add(line)) - { - found = i; - break; - } - } - // If no LineString accepted the way in its Add method, create a new LineString with it. - if (found == m_parts.cend()) - { - m_parts.push_back(line); - return; - } - // Otherwise check if the extended LineString can be merged with some other LineString. - for (LineString & part : m_parts) - { - if (part.Add(*found)) - { - m_parts.erase(found); - break; - } + auto const & ways = pb.second->GetWays(); + if (uniqLineStrings.emplace(pb.second).second && ways.size() > 1) + mergedLines.emplace(p.first, pb.second); } } - std::vector GetLongWays() const + return OrderData(mergedLines); +} + +// static +bool LineStringMerger::TryMerge(LinePtr const & lineString, Buffer & buffer) +{ + bool merged = false; + while(TryMergeOne(lineString, buffer)) + merged = true; + + buffer.emplace(lineString->GetStart(), lineString); + buffer.emplace(lineString->GetEnd(), lineString); + return merged; +} + +// static +bool LineStringMerger::TryMergeOne(LinePtr const & lineString, Buffer & buffer) +{ + auto static const kUndef = std::numeric_limits::max(); + uint64_t index = kUndef; + if (buffer.count(lineString->GetStart()) != 0) + index = lineString->GetStart(); + else if (buffer.count(lineString->GetEnd()) != 0) + index = lineString->GetEnd(); + + if (index != kUndef) { - std::vector result; - for (LineString const & line : m_parts) + auto bufferedLineString = buffer[index]; + buffer.erase(bufferedLineString->GetStart()); + buffer.erase(bufferedLineString->GetEnd()); + if (!lineString->Add(*bufferedLineString)) { - if (line.GetWays().size() > 1) - { - result.push_back(line.GetWays()); - } + buffer.emplace(bufferedLineString->GetStart(), bufferedLineString); + buffer.emplace(bufferedLineString->GetEnd(), bufferedLineString); + + buffer.emplace(lineString->GetStart(), lineString); + buffer.emplace(lineString->GetEnd(), lineString); + return false; } - return result; } -}; + + return index != kUndef; +} + +// static +LineStringMerger::OutputData LineStringMerger::OrderData(InputData const & data) +{ + OutputData intermediateData; + for (auto const & p : data) + intermediateData[p.first].emplace_back(p.second); + + for (auto & p : intermediateData) + { + auto & lineStrings = intermediateData[p.first]; + std::sort(std::begin(lineStrings), std::end(lineStrings), [](auto const & l, auto const & r) { + auto const & lways = l->GetWays(); + auto const & rways = r->GetWays(); + return lways.size() == rways.size() ? lways.front() < rways.front() : lways.size() > rways.size(); + }); + } + + return intermediateData; +} // MetalinesBuilder -------------------------------------------------------------------------------- +MetalinesBuilder::MetalinesBuilder(std::string const & filename) + : generator::CollectorInterface(filename) {} + +std::shared_ptr +MetalinesBuilder::Clone(std::shared_ptr const &) const +{ + return std::make_shared(GetFilename()); +} + void MetalinesBuilder::CollectFeature(FeatureBuilder const & feature, OsmElement const & element) { if (!feature.IsLine()) return; - auto const & params = feature.GetParams(); - static uint32_t const highwayType = classif().GetTypeByPath({"highway"}); - if (params.FindType(highwayType, 1) == ftype::GetEmptyValue() || - element.Nodes().front() == element.Nodes().back()) - { + static auto const highwayType = classif().GetTypeByPath({"highway"}); + if (!feature.HasType(highwayType, 1 /* level */) || element.Nodes().front() == element.Nodes().back()) return; - } - std::string name; - params.name.GetString(StringUtf8Multilang::kDefaultCode, name); + auto const & params = feature.GetParams(); + auto const name = feature.GetName(); if (name.empty() && params.ref.empty()) return; - size_t const key = std::hash{}(name + '\0' + params.ref); - auto segment = m_data.find(key); - if (segment == m_data.cend()) - m_data.emplace(key, std::make_shared(element)); - else - segment->second->Add(element); + auto const key = std::hash{}(name + '\0' + params.ref); + m_data.emplace(key, std::make_shared(element)); } void MetalinesBuilder::Save() { - Flush(); + FileWriter writer(GetFilename()); + uint32_t countLines = 0; + uint32_t countWays = 0; + auto const mergedData = LineStringMerger::Merge(m_data); + for (auto const & p : mergedData) + { + for (auto const & lineString : p.second) + { + auto const & ways = lineString->GetWays(); + uint16_t size = base::checked_cast(ways.size()); + WriteToSink(writer, size); + countWays += ways.size(); + for (int32_t const way : ways) + WriteToSink(writer, way); + ++countLines; + } + } + + LOG_SHORT(LINFO, ("Wrote", countLines, "metalines [with", countWays , + "ways] with OSM IDs for the entire planet to", GetFilename())); } -void MetalinesBuilder::Flush() +void MetalinesBuilder::Merge(generator::CollectorInterface const * collector) { - try - { - uint32_t count = 0; - FileWriter writer(m_filePath); - for (auto const & seg : m_data) - { - auto const & longWays = seg.second->GetLongWays(); - for (Ways const & ways : longWays) - { - uint16_t size = base::checked_cast(ways.size()); - WriteToSink(writer, size); - for (int32_t const way : ways) - WriteToSink(writer, way); - ++count; - } - } - LOG_SHORT(LINFO, ("Wrote", count, "metalines with OSM IDs for the entire planet to", m_filePath)); - } - catch (RootException const & e) - { - LOG(LERROR, ("An exception happened while saving metalines to", m_filePath, ":", e.what())); - } + CHECK(collector, ()); + + collector->MergeInto(const_cast(this)); +} + +void MetalinesBuilder::MergeInto(MetalinesBuilder * collector) const +{ + CHECK(collector, ()); + collector->m_data.insert(std::begin(m_data), std::end(m_data)); } // Functions -------------------------------------------------------------------------------- diff --git a/generator/metalines_builder.hpp b/generator/metalines_builder.hpp index d2e9577444..1b6cfba551 100644 --- a/generator/metalines_builder.hpp +++ b/generator/metalines_builder.hpp @@ -5,35 +5,79 @@ #include "generator/osm_element.hpp" #include +#include #include #include #include +namespace generator +{ +namespace cache +{ +class IntermediateDataReader; +} // namespace cache +} // namespace generator + namespace feature { -class Segments; +// A string of connected ways. +class LineString +{ +public: + using Ways = std::vector; -/// Merges road segments with similar name and ref values into groups called metalines. + explicit LineString(OsmElement const & way); + + bool Add(LineString & line); + void Reverse(); + + Ways const & GetWays() const { return m_ways; } + uint64_t GetStart() const { return m_start; } + uint64_t GetEnd() const { return m_end; } + +private: + uint64_t m_start; + uint64_t m_end; + bool m_oneway; + Ways m_ways; +}; + +class LineStringMerger +{ +public: + using LinePtr = std::shared_ptr; + using InputData = std::unordered_multimap; + using OutputData = std::map>; + using Buffer = std::unordered_map; + + static OutputData Merge(InputData const & data); + static bool TryMerge(LinePtr const & lineString, Buffer & buffer); + static bool TryMergeOne(LinePtr const & lineString, Buffer & buffer); + static OutputData OrderData(InputData const & data); +}; + +// Merges road segments with similar name and ref values into groups called metalines. class MetalinesBuilder : public generator::CollectorInterface { public: - explicit MetalinesBuilder(std::string const & filePath) : m_filePath(filePath) {} + explicit MetalinesBuilder(std::string const & filename); // CollectorInterface overrides: + std::shared_ptr + Clone(std::shared_ptr const & = {}) const override; + /// Add a highway segment to the collection of metalines. void CollectFeature(FeatureBuilder const & feature, OsmElement const & element) override; - void Save() override; - /// Write all metalines to the intermediate file. - void Flush(); + void Merge(generator::CollectorInterface const * collector) override; + void MergeInto(MetalinesBuilder * collector) const override; private: - std::unordered_map> m_data; - std::string m_filePath; + std::unordered_multimap> m_data; }; -/// Read an intermediate file from MetalinesBuilder and convert it to an mwm section. +// Read an intermediate file from MetalinesBuilder and convert it to an mwm section. bool WriteMetalinesSection(std::string const & mwmPath, std::string const & metalinesPath, std::string const & osmIdsToFeatureIdsPath); } diff --git a/generator/regions/collector_region_info.cpp b/generator/regions/collector_region_info.cpp index efe3a26a3f..f41536746d 100644 --- a/generator/regions/collector_region_info.cpp +++ b/generator/regions/collector_region_info.cpp @@ -1,6 +1,6 @@ #include "generator/regions/collector_region_info.hpp" -#include "generator/feature_builder.hpp" +#include "generator/intermediate_data.hpp" #include "generator/osm_element.hpp" #include "coding/file_writer.hpp" @@ -67,7 +67,15 @@ char const * GetLabel(PlaceLevel level) UNREACHABLE(); } -CollectorRegionInfo::CollectorRegionInfo(std::string const & filename) : m_filename(filename) {} +CollectorRegionInfo::CollectorRegionInfo(std::string const & filename) + : CollectorInterface(filename) {} + + +std::shared_ptr +CollectorRegionInfo::Clone(std::shared_ptr const &) const +{ + return std::make_shared(GetFilename()); +} void CollectorRegionInfo::Collect(OsmElement const & el) { @@ -86,12 +94,27 @@ void CollectorRegionInfo::Collect(OsmElement const & el) void CollectorRegionInfo::Save() { - FileWriter writer(m_filename); + FileWriter writer(GetFilename()); WriteToSink(writer, kVersion); WriteMap(writer, m_mapRegionData); WriteMap(writer, m_mapIsoCode); } +void CollectorRegionInfo::Merge(CollectorInterface const * collector) +{ + CHECK(collector, ()); + + collector->MergeInto(const_cast(this)); +} + +void CollectorRegionInfo::MergeInto(CollectorRegionInfo * collector) const +{ + CHECK(collector, ()); + + collector->m_mapRegionData.insert(std::begin(m_mapRegionData), std::end(m_mapRegionData)); + collector->m_mapIsoCode.insert(std::begin(m_mapIsoCode), std::end(m_mapIsoCode)); +} + void CollectorRegionInfo::FillRegionData(base::GeoObjectId const & osmId, OsmElement const & el, RegionData & rd) { diff --git a/generator/regions/collector_region_info.hpp b/generator/regions/collector_region_info.hpp index 380fd43f7c..201b960b46 100644 --- a/generator/regions/collector_region_info.hpp +++ b/generator/regions/collector_region_info.hpp @@ -1,6 +1,7 @@ #pragma once #include "generator/collector_interface.hpp" +#include "generator/feature_builder.hpp" #include "platform/platform.hpp" @@ -10,6 +11,7 @@ #include #include +#include #include #include #include @@ -20,6 +22,11 @@ class FileWriter; namespace generator { +namespace cache +{ +class IntermediateDataReader; +} // namespace cache + namespace regions { // https://wiki.openstreetmap.org/wiki/Tag:boundary=administrative @@ -120,8 +127,14 @@ public: // CollectorInterface overrides: void Collect(OsmElement const & el) override; + std::shared_ptr + Clone(std::shared_ptr const & = {}) const override; + void Save() override; + void Merge(CollectorInterface const * collector) override; + void MergeInto(CollectorRegionInfo * collector) const override; + private: template void WriteMap(Sink & sink, Map & seq) @@ -136,7 +149,6 @@ private: void FillRegionData(base::GeoObjectId const & osmId, OsmElement const & el, RegionData & rd); void FillIsoCode(base::GeoObjectId const & osmId, OsmElement const & el, IsoCode & rd); - std::string m_filename; MapRegionData m_mapRegionData; MapIsoCode m_mapIsoCode; }; diff --git a/generator/restriction_writer.cpp b/generator/restriction_writer.cpp index af9d0f25bc..47ff739958 100644 --- a/generator/restriction_writer.cpp +++ b/generator/restriction_writer.cpp @@ -1,5 +1,6 @@ #include "generator/restriction_writer.hpp" +#include "generator/intermediate_data.hpp" #include "generator/intermediate_elements.hpp" #include "generator/osm_element.hpp" #include "generator/restriction_collector.hpp" @@ -90,11 +91,19 @@ namespace routing std::string const RestrictionWriter::kNodeString = "node"; std::string const RestrictionWriter::kWayString = "way"; -RestrictionWriter::RestrictionWriter(std::string const & fullPath, +RestrictionWriter::RestrictionWriter(std::string const & filename, generator::cache::IntermediateDataReader const & cache) - : m_cache(cache) + : generator::CollectorInterface(filename) + , m_cache(cache) { - Open(fullPath); + m_stream << std::setprecision(20); +} + +std::shared_ptr +RestrictionWriter::Clone(std::shared_ptr const & cache) const +{ +// auto c = cache ? cache : m_cache; + return std::make_shared(GetFilename(), *cache); } //static @@ -109,17 +118,6 @@ RestrictionWriter::ViaType RestrictionWriter::ConvertFromString(std::string cons UNREACHABLE(); } -void RestrictionWriter::Open(std::string const & fullPath) -{ - LOG(LINFO, ("Saving road restrictions in osm id terms to", fullPath)); - m_stream.open(fullPath, std::ofstream::out); - - if (!IsOpened()) - LOG(LINFO, ("Cannot open file", fullPath)); - - m_stream << std::setprecision(20); -} - bool ValidateOsmRestriction(std::vector & from, std::vector & via, std::vector & to, @@ -156,12 +154,6 @@ bool ValidateOsmRestriction(std::vector & from, void RestrictionWriter::CollectRelation(RelationElement const & relationElement) { - if (!IsOpened()) - { - LOG(LWARNING, ("Tried to write to a closed restrictions writer")); - return; - } - std::vector from; std::vector via; std::vector to; @@ -212,7 +204,27 @@ void RestrictionWriter::CollectRelation(RelationElement const & relationElement) m_stream << toOsmId << '\n'; } -bool RestrictionWriter::IsOpened() const { return m_stream && m_stream.is_open(); } +void RestrictionWriter::Save() +{ + std::ofstream stream; + stream.exceptions(std::fstream::failbit | std::fstream::badbit); + stream.open(GetFilename()); + stream << m_stream.str(); +} + +void RestrictionWriter::Merge(generator::CollectorInterface const * collector) +{ + CHECK(collector, ()); + + collector->MergeInto(const_cast(this)); +} + +void RestrictionWriter::MergeInto(RestrictionWriter * collector) const +{ + CHECK(collector, ()); + + collector->m_stream << m_stream.str(); +} std::string DebugPrint(RestrictionWriter::ViaType const & type) { diff --git a/generator/restriction_writer.hpp b/generator/restriction_writer.hpp index e561f28631..7c2dc76083 100644 --- a/generator/restriction_writer.hpp +++ b/generator/restriction_writer.hpp @@ -3,11 +3,20 @@ #include "generator/collector_interface.hpp" #include "generator/intermediate_data.hpp" -#include +#include +#include #include class RelationElement; +namespace generator +{ +namespace cache +{ +class IntermediateDataReader; +} // namespace cache +} // namespace generator + namespace routing { class RestrictionWriter : public generator::CollectorInterface @@ -25,22 +34,23 @@ public: static std::string const kNodeString; static std::string const kWayString; - RestrictionWriter(std::string const & fullPath, + RestrictionWriter(std::string const & filename, generator::cache::IntermediateDataReader const & cache); // generator::CollectorInterface overrides: - // @{ + std::shared_ptr + Clone(std::shared_ptr const & cache = {}) const override; + void CollectRelation(RelationElement const & relationElement) override; - void Save() override {} - // @} + void Save() override; + + void Merge(generator::CollectorInterface const * collector) override; + void MergeInto(RestrictionWriter * collector) const override; static ViaType ConvertFromString(std::string const & str); private: - void Open(std::string const & fullPath); - bool IsOpened() const; - - std::ofstream m_stream; + std::stringstream m_stream; generator::cache::IntermediateDataReader const & m_cache; }; diff --git a/generator/road_access_generator.cpp b/generator/road_access_generator.cpp index 15028edc9b..03ffc8dd73 100644 --- a/generator/road_access_generator.cpp +++ b/generator/road_access_generator.cpp @@ -1,9 +1,11 @@ #include "generator/road_access_generator.hpp" +#include "generator/feature_builder.hpp" #include "generator/routing_helpers.hpp" #include "routing/road_access.hpp" #include "routing/road_access_serialization.hpp" +#include "routing/routing_helpers.hpp" #include "indexer/classificator.hpp" #include "indexer/feature.hpp" @@ -39,68 +41,68 @@ char constexpr kDelim[] = " \t\r\n"; using TagMapping = routing::RoadAccessTagProcessor::TagMapping; TagMapping const kMotorCarTagMapping = { - {OsmElement::Tag("motorcar", "yes"), RoadAccess::Type::Yes}, - {OsmElement::Tag("motorcar", "designated"), RoadAccess::Type::Yes}, - {OsmElement::Tag("motorcar", "permissive"), RoadAccess::Type::Yes}, - {OsmElement::Tag("motorcar", "no"), RoadAccess::Type::No}, - {OsmElement::Tag("motorcar", "private"), RoadAccess::Type::Private}, - {OsmElement::Tag("motorcar", "destination"), RoadAccess::Type::Destination}, + {OsmElement::Tag("motorcar", "yes"), RoadAccess::Type::Yes}, + {OsmElement::Tag("motorcar", "designated"), RoadAccess::Type::Yes}, + {OsmElement::Tag("motorcar", "permissive"), RoadAccess::Type::Yes}, + {OsmElement::Tag("motorcar", "no"), RoadAccess::Type::No}, + {OsmElement::Tag("motorcar", "private"), RoadAccess::Type::Private}, + {OsmElement::Tag("motorcar", "destination"), RoadAccess::Type::Destination}, }; TagMapping const kMotorVehicleTagMapping = { - {OsmElement::Tag("motor_vehicle", "yes"), RoadAccess::Type::Yes}, - {OsmElement::Tag("motor_vehicle", "designated"), RoadAccess::Type::Yes}, - {OsmElement::Tag("motor_vehicle", "permissive"), RoadAccess::Type::Yes}, - {OsmElement::Tag("motor_vehicle", "no"), RoadAccess::Type::No}, - {OsmElement::Tag("motor_vehicle", "private"), RoadAccess::Type::Private}, - {OsmElement::Tag("motor_vehicle", "destination"), RoadAccess::Type::Destination}, + {OsmElement::Tag("motor_vehicle", "yes"), RoadAccess::Type::Yes}, + {OsmElement::Tag("motor_vehicle", "designated"), RoadAccess::Type::Yes}, + {OsmElement::Tag("motor_vehicle", "permissive"), RoadAccess::Type::Yes}, + {OsmElement::Tag("motor_vehicle", "no"), RoadAccess::Type::No}, + {OsmElement::Tag("motor_vehicle", "private"), RoadAccess::Type::Private}, + {OsmElement::Tag("motor_vehicle", "destination"), RoadAccess::Type::Destination}, }; TagMapping const kVehicleTagMapping = { - {OsmElement::Tag("vehicle", "yes"), RoadAccess::Type::Yes}, - {OsmElement::Tag("vehicle", "designated"), RoadAccess::Type::Yes}, - {OsmElement::Tag("vehicle", "permissive"), RoadAccess::Type::Yes}, - {OsmElement::Tag("vehicle", "no"), RoadAccess::Type::No}, - {OsmElement::Tag("vehicle", "private"), RoadAccess::Type::Private}, - {OsmElement::Tag("vehicle", "destination"), RoadAccess::Type::Destination}, + {OsmElement::Tag("vehicle", "yes"), RoadAccess::Type::Yes}, + {OsmElement::Tag("vehicle", "designated"), RoadAccess::Type::Yes}, + {OsmElement::Tag("vehicle", "permissive"), RoadAccess::Type::Yes}, + {OsmElement::Tag("vehicle", "no"), RoadAccess::Type::No}, + {OsmElement::Tag("vehicle", "private"), RoadAccess::Type::Private}, + {OsmElement::Tag("vehicle", "destination"), RoadAccess::Type::Destination}, }; TagMapping const kCarBarriersTagMapping = { - {OsmElement::Tag("barrier", "block"), RoadAccess::Type::No}, - {OsmElement::Tag("barrier", "bollard"), RoadAccess::Type::No}, - {OsmElement::Tag("barrier", "cycle_barrier"), RoadAccess::Type::No}, - {OsmElement::Tag("barrier", "gate"), RoadAccess::Type::Private}, - {OsmElement::Tag("barrier", "lift_gate"), RoadAccess::Type::Private}, -// TODO (@gmoryes) The types below should be added. -// {OsmElement::Tag("barrier", "chain"), RoadAccess::Type::No}, -// {OsmElement::Tag("barrier", "swing_gate"), RoadAccess::Type::Private} -// {OsmElement::Tag("barrier", "log"), RoadAccess::Type::No}, -// {OsmElement::Tag("barrier", "motorcycle_barrier"), RoadAccess::Type::No}, + {OsmElement::Tag("barrier", "block"), RoadAccess::Type::No}, + {OsmElement::Tag("barrier", "bollard"), RoadAccess::Type::No}, + {OsmElement::Tag("barrier", "cycle_barrier"), RoadAccess::Type::No}, + {OsmElement::Tag("barrier", "gate"), RoadAccess::Type::Private}, + {OsmElement::Tag("barrier", "lift_gate"), RoadAccess::Type::Private}, + // TODO (@gmoryes) The types below should be added. + // {OsmElement::Tag("barrier", "chain"), RoadAccess::Type::No}, + // {OsmElement::Tag("barrier", "swing_gate"), RoadAccess::Type::Private} + // {OsmElement::Tag("barrier", "log"), RoadAccess::Type::No}, + // {OsmElement::Tag("barrier", "motorcycle_barrier"), RoadAccess::Type::No}, }; TagMapping const kPedestrianTagMapping = { - {OsmElement::Tag("foot", "yes"), RoadAccess::Type::Yes}, - {OsmElement::Tag("foot", "designated"), RoadAccess::Type::Yes}, - {OsmElement::Tag("foot", "permissive"), RoadAccess::Type::Yes}, - {OsmElement::Tag("foot", "no"), RoadAccess::Type::No}, - {OsmElement::Tag("foot", "private"), RoadAccess::Type::Private}, - {OsmElement::Tag("foot", "destination"), RoadAccess::Type::Destination}, + {OsmElement::Tag("foot", "yes"), RoadAccess::Type::Yes}, + {OsmElement::Tag("foot", "designated"), RoadAccess::Type::Yes}, + {OsmElement::Tag("foot", "permissive"), RoadAccess::Type::Yes}, + {OsmElement::Tag("foot", "no"), RoadAccess::Type::No}, + {OsmElement::Tag("foot", "private"), RoadAccess::Type::Private}, + {OsmElement::Tag("foot", "destination"), RoadAccess::Type::Destination}, }; TagMapping const kBicycleTagMapping = { - {OsmElement::Tag("bicycle", "yes"), RoadAccess::Type::Yes}, - {OsmElement::Tag("bicycle", "designated"), RoadAccess::Type::Yes}, - {OsmElement::Tag("bicycle", "permissive"), RoadAccess::Type::Yes}, - {OsmElement::Tag("bicycle", "no"), RoadAccess::Type::No}, - {OsmElement::Tag("bicycle", "private"), RoadAccess::Type::Private}, - {OsmElement::Tag("bicycle", "destination"), RoadAccess::Type::Destination}, + {OsmElement::Tag("bicycle", "yes"), RoadAccess::Type::Yes}, + {OsmElement::Tag("bicycle", "designated"), RoadAccess::Type::Yes}, + {OsmElement::Tag("bicycle", "permissive"), RoadAccess::Type::Yes}, + {OsmElement::Tag("bicycle", "no"), RoadAccess::Type::No}, + {OsmElement::Tag("bicycle", "private"), RoadAccess::Type::Private}, + {OsmElement::Tag("bicycle", "destination"), RoadAccess::Type::Destination}, }; TagMapping const kBicycleBarriersTagMapping = { - {OsmElement::Tag("barrier", "cycle_barrier"), RoadAccess::Type::No}, - {OsmElement::Tag("barrier", "gate"), RoadAccess::Type::Private}, -// TODO (@gmoryes) The types below should be added. -// {OsmElement::Tag("barrier", "kissing_gate"), RoadAccess::Type::Private}, + {OsmElement::Tag("barrier", "cycle_barrier"), RoadAccess::Type::No}, + {OsmElement::Tag("barrier", "gate"), RoadAccess::Type::Private}, + // TODO (@gmoryes) The types below should be added. + // {OsmElement::Tag("barrier", "kissing_gate"), RoadAccess::Type::Private}, }; // Allow everything to keep transit section empty. We'll use pedestrian section for @@ -109,24 +111,24 @@ TagMapping const kBicycleBarriersTagMapping = { TagMapping const kTransitTagMapping = {}; TagMapping const kDefaultTagMapping = { - {OsmElement::Tag("access", "yes"), RoadAccess::Type::Yes}, - {OsmElement::Tag("access", "permissive"), RoadAccess::Type::Yes}, - {OsmElement::Tag("access", "no"), RoadAccess::Type::No}, - {OsmElement::Tag("access", "private"), RoadAccess::Type::Private}, - {OsmElement::Tag("access", "destination"), RoadAccess::Type::Destination}, + {OsmElement::Tag("access", "yes"), RoadAccess::Type::Yes}, + {OsmElement::Tag("access", "permissive"), RoadAccess::Type::Yes}, + {OsmElement::Tag("access", "no"), RoadAccess::Type::No}, + {OsmElement::Tag("access", "private"), RoadAccess::Type::Private}, + {OsmElement::Tag("access", "destination"), RoadAccess::Type::Destination}, }; set const kHighwaysWhereIgnorePrivateAccessForCar = { - {OsmElement::Tag("highway", "motorway")}, - {OsmElement::Tag("highway", "motorway_link")}, - {OsmElement::Tag("highway", "primary")}, - {OsmElement::Tag("highway", "primary_link")}, - {OsmElement::Tag("highway", "secondary")}, - {OsmElement::Tag("highway", "secondary_link")}, - {OsmElement::Tag("highway", "tertiary")}, - {OsmElement::Tag("highway", "tertiary_link")}, - {OsmElement::Tag("highway", "trunk")}, - {OsmElement::Tag("highway", "trunk_link")} + {OsmElement::Tag("highway", "motorway")}, + {OsmElement::Tag("highway", "motorway_link")}, + {OsmElement::Tag("highway", "primary")}, + {OsmElement::Tag("highway", "primary_link")}, + {OsmElement::Tag("highway", "secondary")}, + {OsmElement::Tag("highway", "secondary_link")}, + {OsmElement::Tag("highway", "tertiary")}, + {OsmElement::Tag("highway", "tertiary_link")}, + {OsmElement::Tag("highway", "trunk")}, + {OsmElement::Tag("highway", "trunk_link")} }; set const kHighwaysWhereIgnorePrivateAccessEmpty = {}; @@ -147,7 +149,7 @@ bool ParseRoadAccess(string const & roadAccessPath, unordered_map pointType[static_cast(VehicleType::Count)]; auto addFeature = [&](uint32_t featureId, VehicleType vehicleType, - RoadAccess::Type roadAccessType, uint64_t osmId) { + RoadAccess::Type roadAccessType, uint64_t osmId) { auto & m = featureType[static_cast(vehicleType)]; auto const emplaceRes = m.emplace(featureId, roadAccessType); if (!emplaceRes.second && emplaceRes.first->second != roadAccessType) @@ -158,7 +160,7 @@ bool ParseRoadAccess(string const & roadAccessPath, }; auto addPoint = [&](RoadPoint const & point, VehicleType vehicleType, - RoadAccess::Type roadAccessType) { + RoadAccess::Type roadAccessType) { auto & m = pointType[static_cast(vehicleType)]; auto const emplaceRes = m.emplace(point, roadAccessType); if (!emplaceRes.second && emplaceRes.first->second != roadAccessType) @@ -287,7 +289,7 @@ RoadAccessTagProcessor::RoadAccessTagProcessor(VehicleType vehicleType) } } -void RoadAccessTagProcessor::Process(OsmElement const & elem, ofstream & oss) +void RoadAccessTagProcessor::Process(FeatureBuilder const & fb, OsmElement const & elem) { // We will process all nodes before ways because of o5m format: // all nodes are first, then all ways, then all relations. @@ -295,33 +297,58 @@ void RoadAccessTagProcessor::Process(OsmElement const & elem, ofstream & oss) { RoadAccess::Type accessType = GetAccessType(elem); if (accessType != RoadAccess::Type::Yes) - m_barriers[elem.m_id] = accessType; + m_barriers.emplace(elem.m_id, accessType); return; } if (elem.m_type != OsmElement::EntityType::Way) return; - // All feature tags. auto const accessType = GetAccessType(elem); if (accessType != RoadAccess::Type::Yes) - oss << ToString(m_vehicleType) << " " << ToString(accessType) << " " << elem.m_id << " " - << 0 /* wildcard segment Idx */ << endl; + m_wayToAccess.emplace(elem.m_id, accessType); + + if (!routing::IsRoad(fb.GetTypes())) + return; + + m_roads.emplace(elem.m_id, elem.m_nodes); +} + +void RoadAccessTagProcessor::Write(std::stringstream & stream) +{ + // All feature tags. + for (auto const & i : m_wayToAccess) + { + stream << ToString(m_vehicleType) << " " << ToString(i.second) << " " << i.first << " " + << 0 /* wildcard segment Idx */ << endl; + } // Barrier tags. - for (size_t pointIdx = 0; pointIdx < elem.m_nodes.size(); ++pointIdx) + for (auto const & i : m_roads) { - auto const it = m_barriers.find(elem.m_nodes[pointIdx]); - if (it == m_barriers.cend()) - continue; + for (size_t pointIdx = 0; pointIdx < i.second.size(); ++pointIdx) + { + auto const it = m_barriers.find(i.second[pointIdx]); + if (it == m_barriers.cend()) + continue; - RoadAccess::Type const roadAccessType = it->second; - // idx == 0 used as wildcard segment Idx, for nodes we store |pointIdx + 1| instead of |pointIdx|. - oss << ToString(m_vehicleType) << " " << ToString(roadAccessType) << " " << elem.m_id << " " - << pointIdx + 1 << endl; + RoadAccess::Type const roadAccessType = it->second; + // idx == 0 used as wildcard segment Idx, for nodes we store |pointIdx + 1| instead of |pointIdx|. + stream << ToString(m_vehicleType) << " " << ToString(roadAccessType) << " " << i.first << " " + << pointIdx + 1 << endl; + } } } +void RoadAccessTagProcessor::Merge(RoadAccessTagProcessor const & other) +{ + CHECK_EQUAL(m_vehicleType, other.m_vehicleType, ()); + + m_barriers.insert(std::begin(other.m_barriers), std::end(other.m_barriers)); + m_wayToAccess.insert(std::begin(other.m_wayToAccess), std::end(other.m_wayToAccess)); + m_roads.insert(std::begin(other.m_roads), std::end(other.m_roads)); +} + bool RoadAccessTagProcessor::ShouldIgnoreBarrierWithoutAccess(OsmElement const & osmElement) const { CHECK(m_hwIgnoreBarriersWithoutAccess, ()); @@ -362,37 +389,54 @@ RoadAccess::Type RoadAccessTagProcessor::GetAccessType(OsmElement const & elem) } // RoadAccessWriter ------------------------------------------------------------ -RoadAccessWriter::RoadAccessWriter(string const & filePath) +RoadAccessWriter::RoadAccessWriter(string const & filename) + : generator::CollectorInterface(filename) { for (size_t i = 0; i < static_cast(VehicleType::Count); ++i) m_tagProcessors.emplace_back(static_cast(i)); - - Open(filePath); } -void RoadAccessWriter::Open(string const & filePath) +std::shared_ptr +RoadAccessWriter::Clone(std::shared_ptr const &) const { - LOG(LINFO, - ("Saving information about barriers and road access classes in osm id terms to", filePath)); - m_stream.open(filePath, ofstream::out); - - if (!IsOpened()) - LOG(LINFO, ("Cannot open file", filePath)); + return std::make_shared(GetFilename()); } -void RoadAccessWriter::CollectFeature(FeatureBuilder const &, OsmElement const & elem) +void RoadAccessWriter::CollectFeature(FeatureBuilder const & fb, OsmElement const & elem) { - if (!IsOpened()) - { - LOG(LWARNING, ("Tried to write to a closed barriers writer")); - return; - } - for (auto & p : m_tagProcessors) - p.Process(elem, m_stream); + p.Process(fb, elem); } -bool RoadAccessWriter::IsOpened() const { return m_stream && m_stream.is_open(); } +void RoadAccessWriter::Save() +{ + std::stringstream stream; + for (auto & p : m_tagProcessors) + p.Write(stream); + + std::ofstream out; + out.exceptions(std::fstream::failbit | std::fstream::badbit); + out.open(GetFilename()); + out << stream.str(); +} + +void RoadAccessWriter::Merge(generator::CollectorInterface const * collector) +{ + CHECK(collector, ()); + + collector->MergeInto(const_cast(this)); +} + +void RoadAccessWriter::MergeInto(RoadAccessWriter * collector) const +{ + CHECK(collector, ()); + + auto & otherProcessors = collector->m_tagProcessors; + CHECK_EQUAL(m_tagProcessors.size(), otherProcessors.size(), ()); + + for (size_t i = 0; i < otherProcessors.size(); ++i) + otherProcessors[i].Merge(m_tagProcessors[i]); +} // RoadAccessCollector ---------------------------------------------------------- RoadAccessCollector::RoadAccessCollector(string const & dataFilePath, string const & roadAccessPath, diff --git a/generator/road_access_generator.hpp b/generator/road_access_generator.hpp index 5e2ec3a160..79f7f63050 100644 --- a/generator/road_access_generator.hpp +++ b/generator/road_access_generator.hpp @@ -1,6 +1,7 @@ #pragma once #include "generator/collector_interface.hpp" +#include "generator/feature_builder.hpp" #include "generator/intermediate_elements.hpp" #include "generator/osm_element.hpp" @@ -13,12 +14,21 @@ #include #include #include +#include #include #include struct OsmElement; class FeatureParams; +namespace generator +{ +namespace cache +{ +class IntermediateDataReader; +} // namespace cache +} // namespace generator + // The road accessibility information is collected in the same manner // as the restrictions are. // See generator/restriction_generator.hpp for details. @@ -31,7 +41,9 @@ public: explicit RoadAccessTagProcessor(VehicleType vehicleType); - void Process(OsmElement const & elem, std::ofstream & oss); + void Process(feature::FeatureBuilder const & fb, OsmElement const & elem); + void Write(std::stringstream & stream); + void Merge(RoadAccessTagProcessor const & roadAccessTagProcessor); private: bool ShouldIgnoreBarrierWithoutAccess(OsmElement const & osmElement) const; @@ -49,22 +61,26 @@ private: // because we almost always do not need to add penalty for passes through such nodes. std::set const * m_hwIgnoreBarriersWithoutAccess; - std::map m_barriers; + std::unordered_map m_barriers; + std::unordered_map m_wayToAccess; + std::unordered_map> m_roads; }; class RoadAccessWriter : public generator::CollectorInterface { public: - RoadAccessWriter(std::string const & filePath); + RoadAccessWriter(std::string const & filename); // CollectorInterface overrides: - void CollectFeature(feature::FeatureBuilder const &, OsmElement const & elem) override; - void Save() override {} + std::shared_ptr + Clone(std::shared_ptr const & = {}) const override; + void CollectFeature(feature::FeatureBuilder const & fb, OsmElement const & elem) override; + void Save() override; + + void Merge(generator::CollectorInterface const * collector) override; + void MergeInto(RoadAccessWriter * collector) const override; private: - void Open(std::string const & filePath); - bool IsOpened() const; - std::ofstream m_stream; std::vector m_tagProcessors; };