diff --git a/defines.hpp b/defines.hpp index eed20fce9f..efc0bc6080 100644 --- a/defines.hpp +++ b/defines.hpp @@ -94,6 +94,7 @@ #define REPLACED_TAGS_FILE "replaced_tags.txt" #define MIXED_TAGS_FILE "mixed_tags.txt" +#define MIXED_NODES_FILE "mixed_nodes.txt" #define LOCALIZATION_DESCRIPTION_SUFFIX " Description" diff --git a/generator/CMakeLists.txt b/generator/CMakeLists.txt index 6484804fa8..a6d53ba53d 100644 --- a/generator/CMakeLists.txt +++ b/generator/CMakeLists.txt @@ -40,6 +40,8 @@ set(SRC intermediate_elements.hpp metalines_builder.cpp metalines_builder.hpp + node_mixer.hpp + node_mixer.cpp opentable_dataset.cpp opentable_dataset.hpp opentable_scoring.cpp diff --git a/generator/generator_tests/CMakeLists.txt b/generator/generator_tests/CMakeLists.txt index 8fc3691b1b..3d9a6e80c3 100644 --- a/generator/generator_tests/CMakeLists.txt +++ b/generator/generator_tests/CMakeLists.txt @@ -8,6 +8,7 @@ set( feature_builder_test.cpp feature_merger_test.cpp metadata_parser_test.cpp + node_mixer_test.cpp osm2meta_test.cpp osm_id_test.cpp osm_o5m_source_test.cpp diff --git a/generator/generator_tests/node_mixer_test.cpp b/generator/generator_tests/node_mixer_test.cpp new file mode 100644 index 0000000000..13fc56f136 --- /dev/null +++ b/generator/generator_tests/node_mixer_test.cpp @@ -0,0 +1,57 @@ +#include "testing/testing.hpp" + +#include "generator/node_mixer.hpp" +#include "generator/osm_element.hpp" + +#include + +UNIT_TEST(NodeMixerTests) +{ + std::istringstream stream1(""); + generator::MixFakeNodes(stream1, [](OsmElement * p) { + TEST(false, ("Returned an object for an empty input stream.")); + }); + + std::istringstream stream2("shop=gift\nname=Shop\n"); + generator::MixFakeNodes(stream2, [](OsmElement * p) { + TEST(false, ("Returned an object for a source without coordinates.")); + }); + + std::istringstream stream3("lat=4.0\nlon=-4.1\n"); + generator::MixFakeNodes(stream3, [](OsmElement * p) { + TEST(false, ("Returned an object for a source without tags.")); + }); + + std::istringstream stream4("lat=10.0\nlon=-4.8\nshop=gift\nname=Shop"); + int count4 = 0; + generator::MixFakeNodes(stream4, [&](OsmElement * p) { + count4++; + TEST_EQUAL(p->type, OsmElement::EntityType::Node, ()); + TEST_EQUAL(p->lat, 10.0, ()); + TEST_EQUAL(p->lon, -4.8, ()); + TEST_EQUAL(p->Tags().size(), 2, ()); + TEST_EQUAL(p->GetTag("name"), "Shop", ()); + }); + TEST_EQUAL(count4, 1, ()); + + std::istringstream stream5("lat=10.0\nlon=-4.8\nid=1\nname=First\n\nid=2\nlat=60\nlon=1\nname=Second\n\n\n"); + int count5 = 0; + generator::MixFakeNodes(stream5, [&](OsmElement * p) { + count5++; + TEST_EQUAL(p->type, OsmElement::EntityType::Node, ()); + TEST_EQUAL(p->Tags().size(), 2, ()); + std::string id = p->GetTag("id"); + TEST(!id.empty(), ("No id tag when every object has it.")); + TEST_EQUAL(p->GetTag("name"), id == "1" ? "First" : "Second", ()); + }); + TEST_EQUAL(count5, 2, ()); + + std::istringstream stream6("lat=0\nlon=-4.8\nshop=mall"); + int count6 = 0; + generator::MixFakeNodes(stream6, [&](OsmElement * p) { + count6++; + TEST_EQUAL(p->lat, 0.0, ()); + TEST_EQUAL(p->lon, -4.8, ()); + }); + TEST_EQUAL(count6, 1, ()); +} diff --git a/generator/node_mixer.cpp b/generator/node_mixer.cpp new file mode 100644 index 0000000000..e84e06b7bd --- /dev/null +++ b/generator/node_mixer.cpp @@ -0,0 +1,78 @@ +#include "generator/node_mixer.hpp" + +#include "base/logging.hpp" +#include "base/string_utils.hpp" + +using namespace std; + +namespace generator +{ +void MixFakeNodes(istream & stream, function processor) +{ + if (stream.fail()) + return; + + // Max node id on 12.02.2018 times hundred — good enough until ~2030. + uint64_t constexpr baseNodeId = 5396734321 * 100; + uint8_t constexpr kCFLat = 1; + uint8_t constexpr kCFLon = 2; + uint8_t constexpr kCFTags = 4; + uint8_t constexpr kCFAll = kCFLat | kCFLon | kCFTags; + uint64_t count = 0; + uint8_t completionFlag = 0; + OsmElement p; + p.id = baseNodeId; + p.type = OsmElement::EntityType::Node; + + string line; + while (getline(stream, line)) + { + if (line.empty()) + { + if (completionFlag == kCFAll) + { + processor(&p); + count++; + p.Clear(); + p.id = baseNodeId + count; + p.type = OsmElement::EntityType::Node; + completionFlag = 0; + } + continue; + } + + auto const eqPos = line.find('='); + if (eqPos != string::npos) + { + string key = line.substr(0, eqPos); + string value = line.substr(eqPos + 1); + strings::Trim(key); + strings::Trim(value); + + if (key == "lat") + { + if (strings::to_double(value, p.lat)) + completionFlag |= kCFLat; + } + else if (key == "lon") + { + if (strings::to_double(value, p.lon)) + completionFlag |= kCFLon; + } + else + { + p.AddTag(key, value); + completionFlag |= kCFTags; + } + } + } + + if (completionFlag == kCFAll) + { + processor(&p); + count++; + } + + LOG(LINFO, ("Added", count, "fake nodes.")); +} +} // namespace generator diff --git a/generator/node_mixer.hpp b/generator/node_mixer.hpp new file mode 100644 index 0000000000..ecea49605c --- /dev/null +++ b/generator/node_mixer.hpp @@ -0,0 +1,18 @@ +#pragma once + +#include "generator/osm_element.hpp" + +#include +#include +#include + +namespace generator +{ +void MixFakeNodes(std::istream & stream, std::function processor); + +inline void MixFakeNodes(std::string const filePath, std::function processor) +{ + std::ifstream stream(filePath); + MixFakeNodes(stream, processor); +} +} // namespace generator diff --git a/generator/osm_source.cpp b/generator/osm_source.cpp index 49366a21df..e134801ae2 100644 --- a/generator/osm_source.cpp +++ b/generator/osm_source.cpp @@ -5,6 +5,7 @@ #include "generator/feature_generator.hpp" #include "generator/intermediate_data.hpp" #include "generator/intermediate_elements.hpp" +#include "generator/node_mixer.hpp" #include "generator/osm_element.hpp" #include "generator/osm_o5m_source.hpp" #include "generator/osm_translator.hpp" @@ -773,6 +774,8 @@ bool GenerateFeaturesImpl(feature::GenerateInfo & info, EmitterBase & emitter) LOG(LINFO, ("Processing", info.m_osmFileName, "done.")); + generator::MixFakeNodes(GetPlatform().ResourcesDir() + MIXED_NODES_FILE, fn); + // Stop if coasts are not merged and FLAG_fail_on_coasts is set if (!emitter.Finish()) return false; diff --git a/xcode/generator/generator.xcodeproj/project.pbxproj b/xcode/generator/generator.xcodeproj/project.pbxproj index 79cfe53a52..426d34e537 100644 --- a/xcode/generator/generator.xcodeproj/project.pbxproj +++ b/xcode/generator/generator.xcodeproj/project.pbxproj @@ -108,6 +108,8 @@ 67C79BB41E2CEEAB00C40034 /* restriction_writer.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 67C79BAC1E2CEEAB00C40034 /* restriction_writer.hpp */; }; 67C79BB51E2CEEAB00C40034 /* traffic_generator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 67C79BAD1E2CEEAB00C40034 /* traffic_generator.cpp */; }; 67C79BB61E2CEEAB00C40034 /* traffic_generator.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 67C79BAE1E2CEEAB00C40034 /* traffic_generator.hpp */; }; + 9D1B3F1B2034624A00278AC8 /* node_mixer.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 9D1B3F192034624900278AC8 /* node_mixer.hpp */; }; + 9D1B3F1C2034624A00278AC8 /* node_mixer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9D1B3F1A2034624900278AC8 /* node_mixer.cpp */; }; E9502E331D34012200CAB86B /* booking_scoring.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E9502E311D34012200CAB86B /* booking_scoring.cpp */; }; /* End PBXBuildFile section */ @@ -221,6 +223,8 @@ 67C79BAD1E2CEEAB00C40034 /* traffic_generator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = traffic_generator.cpp; sourceTree = ""; }; 67C79BAE1E2CEEAB00C40034 /* traffic_generator.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = traffic_generator.hpp; sourceTree = ""; }; 67F0F6761B8C9DCE003F52FF /* osm_xml_source.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = osm_xml_source.hpp; sourceTree = ""; }; + 9D1B3F192034624900278AC8 /* node_mixer.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = node_mixer.hpp; sourceTree = ""; }; + 9D1B3F1A2034624900278AC8 /* node_mixer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = node_mixer.cpp; sourceTree = ""; }; E9502E311D34012200CAB86B /* booking_scoring.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = booking_scoring.cpp; sourceTree = ""; }; /* End PBXFileReference section */ @@ -265,6 +269,8 @@ 6753401D1A3F2A1B00A0A8C3 /* generator */ = { isa = PBXGroup; children = ( + 9D1B3F1A2034624900278AC8 /* node_mixer.cpp */, + 9D1B3F192034624900278AC8 /* node_mixer.hpp */, 39B2B9761FB468CC00AB85A1 /* cities_boundaries_builder.cpp */, 39B2B9771FB468CC00AB85A1 /* cities_boundaries_builder.hpp */, 3D74EF051F86841C0081202C /* ugc_section_builder.cpp */, @@ -413,6 +419,7 @@ 675340741A3F2A7400A0A8C3 /* generate_info.hpp in Headers */, 3D51BC571D5E512500F1FA8D /* region_meta.hpp in Headers */, 677E2A161CAACC5F001DC42A /* tag_admixer.hpp in Headers */, + 9D1B3F1B2034624A00278AC8 /* node_mixer.hpp in Headers */, 568762611F6A9B18002C22A6 /* transit_generator.hpp in Headers */, 675340861A3F2A7400A0A8C3 /* tesselator.hpp in Headers */, 67A0FEBF1CEB467F008F2A61 /* booking_dataset.hpp in Headers */, @@ -578,6 +585,7 @@ 568762601F6A9B18002C22A6 /* transit_generator.cpp in Sources */, 675340601A3F2A7400A0A8C3 /* check_model.cpp in Sources */, 675340851A3F2A7400A0A8C3 /* tesselator.cpp in Sources */, + 9D1B3F1C2034624A00278AC8 /* node_mixer.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; };