From 922bd20adb48c488e1f9062a902e85618c9e3073 Mon Sep 17 00:00:00 2001 From: Mikhail Gorbushin Date: Tue, 29 Oct 2019 17:00:24 +0300 Subject: [PATCH] [generator] Improve city boundaries with relations boundary=administrative --- defines.hpp | 2 + generator/CMakeLists.txt | 8 +- generator/collector_interface.hpp | 2 + .../collector_routing_city_boundaries.cpp | 288 +++++++++++++++++ .../collector_routing_city_boundaries.hpp | 99 ++++++ generator/feature_builder.hpp | 10 +- .../final_processor_intermediate_mwm.cpp | 20 ++ .../final_processor_intermediate_mwm.hpp | 7 + generator/generator_tests/CMakeLists.txt | 2 + ...ollector_routing_city_boundaries_tests.cpp | 289 ++++++++++++++++++ .../osm_element_helpers_tests.cpp | 33 ++ generator/osm2type.cpp | 9 +- generator/osm_element_helpers.cpp | 27 +- generator/osm_element_helpers.hpp | 5 + generator/raw_generator.cpp | 3 + .../routing_city_boundaries_processor.cpp | 211 +++++++++++++ .../routing_city_boundaries_processor.hpp | 36 +++ generator/translator_country.cpp | 6 +- indexer/ftypes_matcher.cpp | 34 ++- indexer/ftypes_matcher.hpp | 2 + 20 files changed, 1080 insertions(+), 13 deletions(-) create mode 100644 generator/collector_routing_city_boundaries.cpp create mode 100644 generator/collector_routing_city_boundaries.hpp create mode 100644 generator/generator_tests/collector_routing_city_boundaries_tests.cpp create mode 100644 generator/generator_tests/osm_element_helpers_tests.cpp create mode 100644 generator/routing_city_boundaries_processor.cpp create mode 100644 generator/routing_city_boundaries_processor.hpp diff --git a/defines.hpp b/defines.hpp index 1e572a23ad..e579a87579 100644 --- a/defines.hpp +++ b/defines.hpp @@ -101,6 +101,8 @@ #define MINI_ROUNDABOUTS_FILENAME "mini_roundabouts.bin" #define MAXSPEEDS_FILENAME "maxspeeds.csv" #define CITIES_AREAS_TMP_FILENAME "cities_areas" DATA_FILE_EXTENSION_TMP +#define ROUTING_CITY_BOUNDARIES_TMP_FILENAME "routing_city_boundaries" DATA_FILE_EXTENSION_TMP +#define ROUTING_CITY_BOUNDARIES_DUMP_FILENAME "routing_city_boundaries.bin" #define CROSS_MWM_OSM_WAYS_DIR "cross_mwm_osm_ways" #define TRAFFIC_FILE_EXTENSION ".traffic" diff --git a/generator/CMakeLists.txt b/generator/CMakeLists.txt index 5045ca60a3..bfe03d1473 100644 --- a/generator/CMakeLists.txt +++ b/generator/CMakeLists.txt @@ -47,6 +47,8 @@ set( collector_interface.hpp collector_mini_roundabout.cpp collector_mini_roundabout.hpp + collector_routing_city_boundaries.cpp + collector_routing_city_boundaries.hpp collector_tag.cpp collector_tag.hpp complex_loader.cpp @@ -78,8 +80,6 @@ set( feature_sorter.cpp feature_sorter.hpp features_processing_helpers.hpp - filter_roads.cpp - filter_roads.hpp filter_collection.cpp filter_collection.hpp filter_complex.cpp @@ -89,6 +89,8 @@ set( filter_interface.hpp filter_planet.cpp filter_planet.hpp + filter_roads.cpp + filter_roads.hpp filter_world.cpp filter_world.hpp final_processor_intermediate_mwm.cpp @@ -175,6 +177,8 @@ set( restriction_writer.hpp road_access_generator.cpp road_access_generator.hpp + routing_city_boundaries_processor.cpp + routing_city_boundaries_processor.hpp routing_helpers.cpp routing_helpers.hpp routing_index_generator.cpp diff --git a/generator/collector_interface.hpp b/generator/collector_interface.hpp index 00dcdda493..ad2f03985e 100644 --- a/generator/collector_interface.hpp +++ b/generator/collector_interface.hpp @@ -41,6 +41,7 @@ class MaxspeedsCollector; class MiniRoundaboutCollector; class CityAreaCollector; class CrossMwmOsmWaysCollector; +class RoutingCityBoundariesCollector; namespace cache { class IntermediateDataReader; @@ -81,6 +82,7 @@ public: virtual void MergeInto(regions::CollectorRegionInfo &) const { FailIfMethodUnsupported(); } virtual void MergeInto(CollectorCollection &) const { FailIfMethodUnsupported(); } virtual void MergeInto(CrossMwmOsmWaysCollector &) const { FailIfMethodUnsupported(); } + virtual void MergeInto(RoutingCityBoundariesCollector &) const { FailIfMethodUnsupported(); } std::string GetTmpFilename() const { return m_filename + "." + std::to_string(m_id); } std::string const & GetFilename() const { return m_filename; } diff --git a/generator/collector_routing_city_boundaries.cpp b/generator/collector_routing_city_boundaries.cpp new file mode 100644 index 0000000000..eaba3bc4af --- /dev/null +++ b/generator/collector_routing_city_boundaries.cpp @@ -0,0 +1,288 @@ +#include "generator/collector_routing_city_boundaries.hpp" + +#include "generator/intermediate_data.hpp" +#include "generator/osm_element.hpp" +#include "generator/osm_element_helpers.hpp" + +#include "indexer/classificator.hpp" +#include "indexer/ftypes_matcher.hpp" + +#include "coding/internal/file_data.hpp" +#include "coding/point_coding.cpp" + +#include "geometry/area_on_earth.hpp" +#include "geometry/mercator.hpp" + +#include "base/assert.hpp" +#include "base/string_utils.hpp" + +#include +#include + +using namespace feature; +using namespace feature::serialization_policy; + +namespace +{ +boost::optional GetPlaceNodeFromMembers(OsmElement const & element) +{ + uint64_t adminCentreRef = 0; + uint64_t labelRef = 0; + for (auto const & member : element.m_members) + { + if (member.m_type == OsmElement::EntityType::Node) + { + if (member.m_role == "admin_centre") + adminCentreRef = member.m_ref; + else if (member.m_role == "label") + labelRef = member.m_ref; + } + } + + if (labelRef) + return labelRef; + + if (adminCentreRef) + return adminCentreRef; + + return {}; +} + +bool IsSuitablePlaceType(ftypes::LocalityType localityType) +{ + switch (localityType) + { + case ftypes::LocalityType::City: + case ftypes::LocalityType::Town: + case ftypes::LocalityType::Village: return true; + default: return false; + } +} + +ftypes::LocalityType GetPlaceType(FeatureBuilder const & feature) +{ + return ftypes::IsLocalityChecker::Instance().GetType(feature.GetTypesHolder()); +} +} // namespace + +namespace generator +{ +// RoutingCityBoundariesCollector::LocalityData ---------------------------------------------------- + +void RoutingCityBoundariesCollector::LocalityData::Serialize(FileWriter & writer, + LocalityData const & localityData) +{ + writer.Write(&localityData.m_population, sizeof(localityData.m_population)); + + auto const placeType = static_cast(localityData.m_place); + writer.Write(&placeType, sizeof(placeType)); + + auto const pointU = PointDToPointU(localityData.m_position, kPointCoordBits); + writer.Write(&pointU.x, sizeof(pointU.x)); + writer.Write(&pointU.y, sizeof(pointU.y)); +} + +RoutingCityBoundariesCollector::LocalityData +RoutingCityBoundariesCollector::LocalityData::Deserialize(ReaderSource & reader) +{ + LocalityData localityData; + reader.Read(&localityData.m_population, sizeof(localityData.m_population)); + + uint32_t placeType = 0; + reader.Read(&placeType, sizeof(placeType)); + localityData.m_place = static_cast(placeType); + + m2::PointU pointU; + reader.Read(&pointU.x, sizeof(pointU.x)); + reader.Read(&pointU.x, sizeof(pointU.y)); + localityData.m_position = PointUToPointD(pointU, kPointCoordBits); + + return localityData; +} + +// RoutingCityBoundariesCollector ------------------------------------------------------------------ + +RoutingCityBoundariesCollector::RoutingCityBoundariesCollector( + std::string const & filename, std::shared_ptr cache) + : CollectorInterface(filename) + , m_writer(std::make_unique(GetTmpFilename())) + , m_cache(std::move(cache)) + , m_featureMakerSimple(m_cache) +{ +} + +std::shared_ptr RoutingCityBoundariesCollector::Clone( + std::shared_ptr const &) const +{ + return std::make_shared(GetFilename(), m_cache->Clone()); +} + +void RoutingCityBoundariesCollector::Collect(OsmElement const & osmElement) +{ + auto osmElementCopy = osmElement; + feature::FeatureBuilder feature; + m_featureMakerSimple.Add(osmElementCopy); + + while (m_featureMakerSimple.GetNextFeature(feature)) + { + if (feature.GetParams().IsValid()) + Process(feature, osmElementCopy); + } +} + +void RoutingCityBoundariesCollector::Process(feature::FeatureBuilder & feature, + OsmElement const & osmElement) +{ + if (feature.IsArea() && IsSuitablePlaceType(GetPlaceType(feature))) + { + if (feature.PreSerialize()) + m_writer->Process(feature); + return; + } + + if (feature.IsArea()) + { + auto const placeOsmIdOp = GetPlaceNodeFromMembers(osmElement); + if (!placeOsmIdOp) + return; + + auto const placeOsmId = *placeOsmIdOp; + + if (feature.PreSerialize()) + m_writer->Process(placeOsmId, feature); + return; + } + else if (feature.IsPoint()) + { + auto const placeType = GetPlaceType(feature); + if (!IsSuitablePlaceType(placeType)) + return; + + uint64_t const population = osm_element::GetPopulation(osmElement); + if (population == 0) + return; + + uint64_t nodeOsmId = osmElement.m_id; + m2::PointD const center = mercator::FromLatLon(osmElement.m_lat, osmElement.m_lon); + m_writer->Process(nodeOsmId, LocalityData(population, placeType, center)); + } +} + +void RoutingCityBoundariesCollector::Finish() { m_writer->Reset(); } + +void RoutingCityBoundariesCollector::Save() +{ + m_writer->Save(GetFilename()); +} + +void RoutingCityBoundariesCollector::Merge(generator::CollectorInterface const & collector) +{ + collector.MergeInto(*this); +} + +void RoutingCityBoundariesCollector::MergeInto(RoutingCityBoundariesCollector & collector) const +{ + m_writer->MergeInto(*collector.m_writer); +} + +// RoutingCityBoundariesWriter --------------------------------------------------------------------- + +// static +std::string RoutingCityBoundariesWriter::GetNodeToLocalityDataFilename(std::string const & filename) +{ + return filename + ".nodeId2locality"; +} + +// static +std::string RoutingCityBoundariesWriter::GetNodeToBoundariesFilename(std::string const & filename) +{ + return filename + ".nodeId2Boundaries"; +} + +// static +std::string RoutingCityBoundariesWriter::GetFeaturesBuilderFilename(std::string const & filename) +{ + return filename + ".features"; +} + +RoutingCityBoundariesWriter::RoutingCityBoundariesWriter(std::string const & filename) + : m_nodeOsmIdToLocalityDataFilename(GetNodeToLocalityDataFilename(filename)) + , m_nodeOsmIdToBoundariesFilename(GetNodeToBoundariesFilename(filename)) + , m_featureBuilderFilename(GetFeaturesBuilderFilename(filename)) + , m_nodeOsmIdToLocalityDataWriter(std::make_unique(m_nodeOsmIdToLocalityDataFilename)) + , m_nodeOsmIdToBoundariesWriter(std::make_unique(m_nodeOsmIdToBoundariesFilename)) + , m_featureBuilderWriter(std::make_unique(m_featureBuilderFilename)) +{ +} + +void RoutingCityBoundariesWriter::Process(uint64_t nodeOsmId, LocalityData const & localityData) +{ + m_nodeOsmIdToLocalityDataWriter->Write(&nodeOsmId, sizeof(nodeOsmId)); + LocalityData::Serialize(*m_nodeOsmIdToLocalityDataWriter, localityData); + + ++m_nodeOsmIdToLocalityDataCount; +} + +void RoutingCityBoundariesWriter::Process(uint64_t nodeOsmId, + feature::FeatureBuilder const & feature) +{ + m_nodeOsmIdToBoundariesWriter->Write(&nodeOsmId, sizeof(nodeOsmId)); + FeatureWriter::Write(*m_nodeOsmIdToBoundariesWriter, feature); + + ++m_nodeOsmIdToBoundariesCount; +} + +void RoutingCityBoundariesWriter::Process(feature::FeatureBuilder const & feature) +{ + m_featureBuilderWriter->Write(feature); +} + +void RoutingCityBoundariesWriter::Reset() +{ + m_nodeOsmIdToLocalityDataWriter.reset({}); + m_nodeOsmIdToBoundariesWriter.reset({}); + m_featureBuilderWriter.reset({}); +} + +void RoutingCityBoundariesWriter::MergeInto(RoutingCityBoundariesWriter & writer) +{ + CHECK(!m_nodeOsmIdToLocalityDataWriter || !writer.m_nodeOsmIdToLocalityDataWriter, + ("Finish() has not been called.")); + base::AppendFileToFile(m_nodeOsmIdToLocalityDataFilename, + writer.m_nodeOsmIdToLocalityDataFilename); + + CHECK(!m_nodeOsmIdToBoundariesWriter || !writer.m_nodeOsmIdToBoundariesWriter, + ("Finish() has not been called.")); + base::AppendFileToFile(m_nodeOsmIdToBoundariesFilename, + writer.m_nodeOsmIdToBoundariesFilename); + + CHECK(!m_featureBuilderWriter || !writer.m_featureBuilderWriter, + ("Finish() has not been called.")); + base::AppendFileToFile(m_featureBuilderFilename, + writer.m_featureBuilderFilename); + + writer.m_nodeOsmIdToLocalityDataCount += m_nodeOsmIdToLocalityDataCount; + writer.m_nodeOsmIdToBoundariesCount += m_nodeOsmIdToBoundariesCount; +} + +void RoutingCityBoundariesWriter::Save(std::string const & finalFileName) +{ + auto const nodeToLocalityFilename = GetNodeToLocalityDataFilename(finalFileName); + auto const nodeToBoundariesFilename = GetNodeToBoundariesFilename(finalFileName); + + { + FileWriter writer(nodeToLocalityFilename, FileWriter::Op::OP_WRITE_TRUNCATE); + writer.Write(&m_nodeOsmIdToLocalityDataCount, sizeof(m_nodeOsmIdToLocalityDataCount)); + } + { + FileWriter writer(nodeToBoundariesFilename, FileWriter::Op::OP_WRITE_TRUNCATE); + writer.Write(&m_nodeOsmIdToBoundariesCount, sizeof(m_nodeOsmIdToBoundariesCount)); + } + + base::AppendFileToFile(m_nodeOsmIdToLocalityDataFilename, nodeToLocalityFilename); + base::AppendFileToFile(m_nodeOsmIdToBoundariesFilename, nodeToBoundariesFilename); + + CHECK(base::CopyFileX(m_featureBuilderFilename, + GetFeaturesBuilderFilename(finalFileName)), ()); +} +} // namespace generator diff --git a/generator/collector_routing_city_boundaries.hpp b/generator/collector_routing_city_boundaries.hpp new file mode 100644 index 0000000000..515e371385 --- /dev/null +++ b/generator/collector_routing_city_boundaries.hpp @@ -0,0 +1,99 @@ +#pragma once + +#include "generator/collector_interface.hpp" +#include "generator/feature_builder.hpp" +#include "generator/feature_maker.hpp" +#include "generator/intermediate_data.hpp" + +#include "indexer/ftypes_matcher.hpp" + +#include "coding/reader.hpp" + +#include +#include + +namespace generator +{ +namespace cache +{ +class IntermediateDataReader; +} // namespace cache + +class RoutingCityBoundariesWriter; + +class RoutingCityBoundariesCollector : public CollectorInterface +{ +public: + struct LocalityData + { + LocalityData() = default; + LocalityData(uint64_t population, ftypes::LocalityType place, m2::PointD const & position) + : m_population(population), m_place(place), m_position(position) + { + } + + static void Serialize(FileWriter & writer, LocalityData const & localityData); + static LocalityData Deserialize(ReaderSource & reader); + + uint64_t m_population = 0; + ftypes::LocalityType m_place = ftypes::LocalityType::None; + m2::PointD m_position = m2::PointD::Zero(); + }; + + RoutingCityBoundariesCollector(std::string const & filename, + std::shared_ptr cache); + + // CollectorInterface overrides: + std::shared_ptr Clone( + std::shared_ptr const & = {}) const override; + + void Collect(OsmElement const & osmElement) override; + void Finish() override; + void Save() override; + + void Merge(generator::CollectorInterface const & collector) override; + void MergeInto(RoutingCityBoundariesCollector & collector) const override; + + void Process(feature::FeatureBuilder & feature, OsmElement const & osmElement); +private: + + std::unique_ptr m_writer; + std::shared_ptr m_cache; + FeatureMakerSimple m_featureMakerSimple; +}; + +class RoutingCityBoundariesWriter +{ +public: + using LocalityData = RoutingCityBoundariesCollector::LocalityData; + + static std::string GetNodeToLocalityDataFilename(std::string const & filename); + static std::string GetNodeToBoundariesFilename(std::string const & filename); + static std::string GetFeaturesBuilderFilename(std::string const & filename); + + explicit RoutingCityBoundariesWriter(std::string const & filename); + + void Process(uint64_t nodeOsmId, LocalityData const & localityData); + void Process(uint64_t nodeOsmId, feature::FeatureBuilder const & feature); + void Process(feature::FeatureBuilder const & feature); + + void Reset(); + void MergeInto(RoutingCityBoundariesWriter & writer); + void Save(std::string const & finalFileName); + +private: + using MinAccuracy = feature::serialization_policy::MinSize; + using FeatureWriter = feature::FeatureBuilderWriter; + + std::string m_nodeOsmIdToLocalityDataFilename; + std::string m_nodeOsmIdToBoundariesFilename; + std::string m_featureBuilderFilename; + + uint64_t m_nodeOsmIdToLocalityDataCount = 0; + uint64_t m_nodeOsmIdToBoundariesCount = 0; + + std::unique_ptr m_nodeOsmIdToLocalityDataWriter; + std::unique_ptr m_nodeOsmIdToBoundariesWriter; + std::unique_ptr m_featureBuilderWriter; +}; +} // namespace generator diff --git a/generator/feature_builder.hpp b/generator/feature_builder.hpp index 5f125aae8d..cf427d5a9a 100644 --- a/generator/feature_builder.hpp +++ b/generator/feature_builder.hpp @@ -402,11 +402,17 @@ public: } void Write(FeatureBuilder const & fb) + { + Write(m_writer, fb); + } + + template + static void Write(Sink & writer, FeatureBuilder const & fb) { FeatureBuilder::Buffer buffer; SerializationPolicy::Serialize(fb, buffer); - WriteVarUint(m_writer, static_cast(buffer.size())); - m_writer.Write(buffer.data(), buffer.size() * sizeof(FeatureBuilder::Buffer::value_type)); + WriteVarUint(writer, static_cast(buffer.size())); + writer.Write(buffer.data(), buffer.size() * sizeof(FeatureBuilder::Buffer::value_type)); } private: diff --git a/generator/final_processor_intermediate_mwm.cpp b/generator/final_processor_intermediate_mwm.cpp index a0dcaf1c54..d2f3f549ff 100644 --- a/generator/final_processor_intermediate_mwm.cpp +++ b/generator/final_processor_intermediate_mwm.cpp @@ -9,6 +9,7 @@ #include "generator/osm2type.hpp" #include "generator/place_processor.hpp" #include "generator/promo_catalog_cities.hpp" +#include "generator/routing_city_boundaries_processor.hpp" #include "generator/type_helper.hpp" #include "generator/utils.hpp" @@ -294,6 +295,13 @@ void CountryFinalProcessor::DumpCitiesBoundaries(std::string const & filename) m_citiesBoundariesFilename = filename; } +void CountryFinalProcessor::DumpRoutingCitiesBoundariesFilename( + std::string const & collectorFilename, std::string const & dumpPath) +{ + m_routingCityBoundariesCollectorFilename = collectorFilename; + m_routingCityBoundariesDumpPath = dumpPath; +} + void CountryFinalProcessor::SetCoastlines(std::string const & coastlineGeomFilename, std::string const & worldCoastsFilename) { @@ -315,6 +323,8 @@ void CountryFinalProcessor::Process() { if (!m_hotelsFilename.empty()) ProcessBooking(); + if (!m_routingCityBoundariesCollectorFilename.empty()) + ProcessRoutingCityBoundaries(); if (!m_citiesAreasTmpFilename.empty() || !m_citiesFilename.empty()) ProcessCities(); if (!m_coastlineGeomFilename.empty()) @@ -390,6 +400,16 @@ void CountryFinalProcessor::ProcessRoundabouts() } } +void CountryFinalProcessor::ProcessRoutingCityBoundaries() +{ + CHECK(!m_routingCityBoundariesCollectorFilename.empty() && + !m_routingCityBoundariesDumpPath.empty(), ()); + + RoutingCityBoundariesProcessor processor(m_routingCityBoundariesCollectorFilename); + processor.ProcessDataFromCollector(); + processor.DumpBoundaries(m_routingCityBoundariesDumpPath); +} + void CountryFinalProcessor::ProcessCities() { auto const affiliation = CountriesFilesIndexAffiliation(m_borderPath, m_haveBordersForWholeWorld); diff --git a/generator/final_processor_intermediate_mwm.hpp b/generator/final_processor_intermediate_mwm.hpp index f286edb0b9..6fc28864d1 100644 --- a/generator/final_processor_intermediate_mwm.hpp +++ b/generator/final_processor_intermediate_mwm.hpp @@ -57,12 +57,15 @@ public: void SetMiniRoundabouts(std::string const & filename); void DumpCitiesBoundaries(std::string const & filename); + void DumpRoutingCitiesBoundariesFilename(std::string const & collectorFilename, + std::string const & dumpPath); // FinalProcessorIntermediateMwmInterface overrides: void Process() override; private: void ProcessBooking(); + void ProcessRoutingCityBoundaries(); void ProcessCities(); void ProcessCoastline(); void ProcessRoundabouts(); @@ -79,6 +82,10 @@ private: std::string m_citiesFilename; std::string m_fakeNodesFilename; std::string m_miniRoundaboutsFilename; + + std::string m_routingCityBoundariesCollectorFilename; + std::string m_routingCityBoundariesDumpPath; + bool m_haveBordersForWholeWorld; size_t m_threadsCount; }; diff --git a/generator/generator_tests/CMakeLists.txt b/generator/generator_tests/CMakeLists.txt index bf4f29dead..1f7b36c096 100644 --- a/generator/generator_tests/CMakeLists.txt +++ b/generator/generator_tests/CMakeLists.txt @@ -13,6 +13,7 @@ set( cluster_finder_tests.cpp coasts_test.cpp collector_city_area_tests.cpp + collector_routing_city_boundaries_tests.cpp common.cpp common.hpp complex_loader_tests.cpp @@ -31,6 +32,7 @@ set( mini_roundabout_tests.cpp node_mixer_test.cpp osm2meta_test.cpp + osm_element_helpers_tests.cpp osm_o5m_source_test.cpp osm_type_test.cpp place_processor_tests.cpp diff --git a/generator/generator_tests/collector_routing_city_boundaries_tests.cpp b/generator/generator_tests/collector_routing_city_boundaries_tests.cpp new file mode 100644 index 0000000000..0fd60fb611 --- /dev/null +++ b/generator/generator_tests/collector_routing_city_boundaries_tests.cpp @@ -0,0 +1,289 @@ +#include "testing/testing.hpp" + +#include "generator/generator_tests_support/test_with_classificator.hpp" + +#include "generator/collector_routing_city_boundaries.hpp" +#include "generator/generator_tests/common.hpp" +#include "generator/osm2type.hpp" +#include "generator/osm_element.hpp" +#include "generator/routing_city_boundaries_processor.hpp" + +#include "platform/platform.hpp" + +#include "geometry/point2d.hpp" +#include "geometry/area_on_earth.hpp" + +#include "base/geo_object_id.hpp" +#include "base/scope_guard.hpp" + +#include +#include +#include + +using namespace generator_tests; +using namespace generator; +using namespace tests_support; +using namespace feature; + +namespace +{ +using BoundariesCollector = RoutingCityBoundariesCollector; + +std::vector GetFeatureBuilderGeometry() +{ + static std::vector const kPolygon = {{0, 0}, {0, 2}, {2, 2}, {2, 0}, {0, 0}}; + return kPolygon; +} + +feature::FeatureBuilder MakeAreaFeatureBuilder(OsmElement element) +{ + feature::FeatureBuilder result; + auto const filterType = [](uint32_t) { return true; }; + ftype::GetNameAndType(&element, result.GetParams(), filterType); + result.SetOsmId(base::MakeOsmRelation(element.m_id)); + auto polygon = GetFeatureBuilderGeometry(); + result.AddPolygon(polygon); + result.SetArea(); + return result; +} + +feature::FeatureBuilder MakeNodeFeatureBuilder(OsmElement element) +{ + feature::FeatureBuilder result; + auto const filterType = [](uint32_t) { return true; }; + ftype::GetNameAndType(&element, result.GetParams(), filterType); + result.SetOsmId(base::MakeOsmNode(element.m_id)); + result.SetCenter(mercator::FromLatLon(element.m_lat, element.m_lon)); + return result; +} + +OsmElement MakeAreaWithPlaceNode(uint64_t id, uint64_t placeId, std::string const & role) +{ + auto area = MakeOsmElement(id, {{"boundary", "administrative"}}, OsmElement::EntityType::Relation); + area.m_members.emplace_back(placeId, OsmElement::EntityType::Node, role); + return area; +} + +bool HasRelationWithId(std::vector const & fbs, uint64_t id) { + return std::find_if(std::begin(fbs), std::end(fbs), [&](auto const & fb) { + return fb.GetMostGenericOsmId() == base::MakeOsmRelation(id); + }) != std::end(fbs); +}; + +auto const placeRelation1 = MakeOsmElement(1 /* id */, {{"place", "city"}}, OsmElement::EntityType::Relation); +auto const placeRelation2 = MakeOsmElement(2 /* id */, {{"place", "town"}}, OsmElement::EntityType::Relation); +auto const placeRelation3 = MakeOsmElement(3 /* id */, {{"place", "village"}}, OsmElement::EntityType::Relation); +auto const placeRelation4 = MakeOsmElement(4 /* id */, {{"place", "country"}}, OsmElement::EntityType::Relation); + +auto const placeNode1 = MakeOsmElement(9 /* id */, {{"place", "city"}, {"population", "200.000"}}, OsmElement::EntityType::Node); +auto const placeNode2 = MakeOsmElement(10 /* id */, {{"place", "town"}, {"population", "10 000"}}, OsmElement::EntityType::Node); +auto const placeNode3 = MakeOsmElement(11 /* id */, {{"place", "village"}, {"population", "1000"}}, OsmElement::EntityType::Node); +auto const placeNode4 = MakeOsmElement(12 /* id */, {{"place", "country"}, {"population", "147000000"}}, OsmElement::EntityType::Node); + +auto const relationWithLabel1 = MakeAreaWithPlaceNode(5 /* id */, 9 /* placeId */, "label" /* role */); +auto const relationWithLabel2 = MakeAreaWithPlaceNode(6 /* id */, 10 /* placeId */, "admin_centre" /* role */); +auto const relationWithLabel3 = MakeAreaWithPlaceNode(7 /* id */, 11 /* placeId */, "label" /* role */); +auto const relationWithLabel4 = MakeAreaWithPlaceNode(8 /* id */, 12 /* placeId */, "country" /* role */); + +void Collect(BoundariesCollector & collector, std::vector const & elements) +{ + for (auto const & element : elements) + { + auto featureBuilder = element.IsNode() ? MakeNodeFeatureBuilder(element) + : MakeAreaFeatureBuilder(element); + collector.Process(featureBuilder, element); + } +} + +void Collect(std::shared_ptr & collector, + std::vector const & elements) +{ + auto boundariesCollector = dynamic_cast(collector.get()); + Collect(*boundariesCollector, elements); +} + +void Check(std::string const & filename) +{ + using Writer = RoutingCityBoundariesWriter; + + auto const featuresFileName = Writer::GetFeaturesBuilderFilename(filename); + + auto const fbs = ReadAllDatRawFormat(featuresFileName); + TEST(HasRelationWithId(fbs, 1), ()); + TEST(HasRelationWithId(fbs, 2), ()); + TEST(HasRelationWithId(fbs, 3), ()); + TEST(!HasRelationWithId(fbs, 4), ()); + + auto const nodeToBoundaryFilename = Writer::GetNodeToBoundariesFilename(filename); + auto nodeToBoundary = routing_city_boundaries::LoadNodeToBoundariesData(nodeToBoundaryFilename); + TEST(nodeToBoundary.count(9), ()); + TEST(nodeToBoundary.count(10), ()); + TEST(nodeToBoundary.count(11), ()); + TEST(!nodeToBoundary.count(12), ()); + + auto const truePolygon = GetFeatureBuilderGeometry(); + for (size_t id = 9; id <= 11; ++id) + { + auto const & geometryFromFile = nodeToBoundary[id].back().GetOuterGeometry(); + for (size_t i = 0; i < geometryFromFile.size(); ++i) + TEST_ALMOST_EQUAL_ABS(geometryFromFile[i], truePolygon[i], 1e-6, ()); + } + + auto const nodeToLocalityFilename = Writer::GetNodeToLocalityDataFilename(filename); + auto nodeToLocality = routing_city_boundaries::LoadNodeToLocalityData(nodeToLocalityFilename); + TEST(nodeToLocality.count(9), ()); + TEST(nodeToLocality.count(10), ()); + TEST(nodeToLocality.count(11), ()); + TEST(!nodeToLocality.count(12), ()); + + TEST_EQUAL(nodeToLocality[9].m_place, ftypes::LocalityType::City, ()); + TEST_EQUAL(nodeToLocality[9].m_population, 200000, ()); + + TEST_EQUAL(nodeToLocality[10].m_place, ftypes::LocalityType::Town, ()); + TEST_EQUAL(nodeToLocality[10].m_population, 10000, ()); + + TEST_EQUAL(nodeToLocality[11].m_place, ftypes::LocalityType::Village, ()); + TEST_EQUAL(nodeToLocality[11].m_population, 1000, ()); +} + +std::vector FromLatLons(std::vector const & latlons) +{ + std::vector points; + for (auto const & latlon : latlons) + points.emplace_back(mercator::FromLatLon(latlon)); + return points; +} + +double CalculateEarthAreaForConvexPolygon(std::vector const & latlons) +{ + CHECK(!latlons.empty(), ()); + double area = 0.0; + auto const & base = latlons.front(); + for (size_t i = 1; i < latlons.size() - 1; ++i) + area += ms::AreaOnEarth(base, latlons[i], latlons[i + 1]); + + return area; +} +} // namespace + +UNIT_CLASS_TEST(TestWithClassificator, CollectorRoutingCityBoundaries_1) +{ + auto const filename = generator_tests::GetFileName(); + SCOPE_GUARD(_, std::bind(Platform::RemoveFileIfExists, std::cref(filename))); + + std::shared_ptr cache; + auto c1 = std::make_shared(filename, cache); + + Collect(*c1, {placeRelation1, placeRelation2, placeRelation3, placeRelation4}); + Collect(*c1, {relationWithLabel1, relationWithLabel2, relationWithLabel3, relationWithLabel4}); + Collect(*c1, {placeNode1, placeNode2, placeNode3, placeNode4}); + + c1->Finish(); + c1->Save(); + + Check(filename); +} + +UNIT_CLASS_TEST(TestWithClassificator, CollectorRoutingCityBoundaries_2) +{ + auto const filename = generator_tests::GetFileName(); + SCOPE_GUARD(_, std::bind(Platform::RemoveFileIfExists, std::cref(filename))); + + std::shared_ptr cache; + auto c1 = std::make_shared(filename, cache); + auto c2 = c1->Clone(); + + Collect(*c1, {placeRelation1, placeRelation2}); + Collect(c2, {placeRelation3, placeRelation4}); + + Collect(*c1, {relationWithLabel1, relationWithLabel2}); + Collect(c2, {relationWithLabel3, relationWithLabel4}); + + Collect(*c1, {placeNode1, placeNode2}); + Collect(c2, {placeNode3, placeNode4}); + + c1->Finish(); + c2->Finish(); + c1->Merge(*c2); + c1->Save(); + + Check(filename); +} + +UNIT_TEST(AreaOnEarth_Convex_Polygon_1) +{ + auto const a = ms::LatLon(55.8034965, 37.696754); + auto const b = ms::LatLon(55.7997909, 37.7830427); + auto const c = ms::LatLon(55.8274225, 37.8150381); + auto const d = ms::LatLon(55.843037, 37.7401892); + auto const e = ms::LatLon(55.8452096, 37.7019668); + + std::vector const latlons = {a, b, c, d, e}; + std::vector const points = FromLatLons(latlons); + + double const areaTriangulated = + ms::AreaOnEarth(a, b, c) + ms::AreaOnEarth(a, c, d) + ms::AreaOnEarth(a, d, e); + + double const areaOnEarth = generator::routing_city_boundaries::AreaOnEarth(points); + + TEST(base::AlmostEqualRel(areaTriangulated, + areaOnEarth, + 1e-6), (areaTriangulated, areaOnEarth)); +} + +UNIT_TEST(AreaOnEarth_Convex_Polygon_2) +{ + std::vector const latlons = { + {55.6348484, 36.025526}, + {55.0294112, 36.8959709}, + {54.9262448, 38.3719426}, + {55.3561515, 39.3275397}, + {55.7548279, 39.4458067}, + {56.3020039, 39.3322704}, + {56.5140099, 38.6368606}, + {56.768935, 37.0473526}, + {56.4330113, 35.6234183}, + }; + + std::vector const points = FromLatLons(latlons); + + double const areaOnEarth = generator::routing_city_boundaries::AreaOnEarth(points); + double const areaForConvexPolygon = CalculateEarthAreaForConvexPolygon(latlons); + + TEST(base::AlmostEqualRel(areaForConvexPolygon, + areaOnEarth, + 1e-6), (areaForConvexPolygon, areaOnEarth)); +} + +UNIT_TEST(AreaOnEarth_Concave_Polygon) +{ + auto const a = ms::LatLon(40.3429746, -7.6513617); + auto const b = ms::LatLon(40.0226711, -7.7356029); + auto const c = ms::LatLon(39.7887079, -7.0626206); + auto const d = ms::LatLon(40.1038622, -7.0394143); + auto const e = ms::LatLon(40.0759637, -6.7145263); + auto const f = ms::LatLon(40.2861884, -6.8637096); + auto const g = ms::LatLon(40.4175634, -6.5123); + auto const h = ms::LatLon(40.4352289, -6.9101221); + auto const i = ms::LatLon(40.6040786, -7.0825117); + auto const j = ms::LatLon(40.4301821, -7.2482709); + + std::vector const latlons = {a, b, c, d, e, f, g, h, i, j}; + std::vector const points = FromLatLons(latlons); + + double areaTriangulated = + ms::AreaOnEarth(a, b, c) + + ms::AreaOnEarth(a, c, d) + + ms::AreaOnEarth(a, d, f) + + ms::AreaOnEarth(d, e, f) + + ms::AreaOnEarth(a, f, j) + + ms::AreaOnEarth(f, h, j) + + ms::AreaOnEarth(f, g, h) + + ms::AreaOnEarth(h, i, j); + + double const areaOnEarth = generator::routing_city_boundaries::AreaOnEarth(points); + + TEST(base::AlmostEqualRel(areaTriangulated, + areaOnEarth, + 1e-6), ()); +} diff --git a/generator/generator_tests/osm_element_helpers_tests.cpp b/generator/generator_tests/osm_element_helpers_tests.cpp new file mode 100644 index 0000000000..8257588a30 --- /dev/null +++ b/generator/generator_tests/osm_element_helpers_tests.cpp @@ -0,0 +1,33 @@ +#include "testing/testing.hpp" + +#include "generator/osm_element_helpers.hpp" + +#include "geometry/point2d.hpp" + +namespace +{ +using namespace generator::osm_element; + +OsmElement CreateOsmElement(std::string const & populationStr) +{ + OsmElement element; + element.m_tags.emplace_back("population", populationStr); + return element; +} + +UNIT_TEST(ParsePopulation) +{ + TEST_EQUAL(GetPopulation(CreateOsmElement("123")), 123, ()); + TEST_EQUAL(GetPopulation(CreateOsmElement("123 123")), 123123, ()); + TEST_EQUAL(GetPopulation(CreateOsmElement("12,300")), 12300, ()); + TEST_EQUAL(GetPopulation(CreateOsmElement("000")), 0, ()); + TEST_EQUAL(GetPopulation(CreateOsmElement("123.321")), 123321, ()); + TEST_EQUAL(GetPopulation(CreateOsmElement("123 000 000 (Moscow Info)")), 123000000, ()); + TEST_EQUAL(GetPopulation(CreateOsmElement("123 000 000 (123)")), 123000000, ()); + TEST_EQUAL(GetPopulation(CreateOsmElement("")), 0, ()); + TEST_EQUAL(GetPopulation(CreateOsmElement(" ")), 0, ()); + TEST_EQUAL(GetPopulation(CreateOsmElement("asd")), 0, ()); + TEST_EQUAL(GetPopulation(CreateOsmElement("sfa843r")), 0, ()); + TEST_EQUAL(GetPopulation(OsmElement()), 0, ()); +} +} // namespace diff --git a/generator/osm2type.cpp b/generator/osm2type.cpp index e10e840538..49d72595f7 100644 --- a/generator/osm2type.cpp +++ b/generator/osm2type.cpp @@ -2,6 +2,7 @@ #include "generator/osm2meta.hpp" #include "generator/osm_element.hpp" +#include "generator/osm_element_helpers.hpp" #include "indexer/classificator.hpp" #include "indexer/feature_impl.hpp" @@ -873,11 +874,9 @@ void GetNameAndType(OsmElement * p, FeatureParams & params, function #include -#include namespace generator { @@ -41,5 +40,31 @@ bool HasStreet(OsmElement const & osmElement) return t.m_key == "addr:street"; }); } + +uint64_t GetPopulation(std::string const & populationStr) +{ + std::string number; + for (auto const c : populationStr) + { + if (isdigit(c)) + number += c; + else if (c == '.' || c == ',' || c == ' ') + continue; + else + break; + } + + if (number.empty()) + return 0; + + uint64_t result = 0; + CHECK(strings::to_uint64(number, result), (number)); + return result; +} + +uint64_t GetPopulation(OsmElement const & osmElement) +{ + return GetPopulation(osmElement.GetTag("population")); +} } // namespace osm_element } // namespace generator diff --git a/generator/osm_element_helpers.hpp b/generator/osm_element_helpers.hpp index 2b67f990cb..33a2959a66 100644 --- a/generator/osm_element_helpers.hpp +++ b/generator/osm_element_helpers.hpp @@ -2,6 +2,8 @@ #include "generator/osm_element.hpp" +#include + namespace generator { namespace osm_element @@ -14,5 +16,8 @@ bool IsBuilding(OsmElement const & osmElement); bool HasHouse(OsmElement const & osmElement); bool HasStreet(OsmElement const & osmElement); + +uint64_t GetPopulation(std::string const & populationStr); +uint64_t GetPopulation(OsmElement const & osmElement); } // namespace osm_element } // namespace generator diff --git a/generator/raw_generator.cpp b/generator/raw_generator.cpp index b378ac7ec6..4816b548a0 100644 --- a/generator/raw_generator.cpp +++ b/generator/raw_generator.cpp @@ -121,6 +121,9 @@ RawGenerator::FinalProcessorPtr RawGenerator::CreateCountryFinalProcessor(bool a } finalProcessor->DumpCitiesBoundaries(m_genInfo.m_citiesBoundariesFilename); + finalProcessor->DumpRoutingCitiesBoundariesFilename( + m_genInfo.GetIntermediateFileName(ROUTING_CITY_BOUNDARIES_TMP_FILENAME), + m_genInfo.GetIntermediateFileName(ROUTING_CITY_BOUNDARIES_DUMP_FILENAME)); return finalProcessor; } diff --git a/generator/routing_city_boundaries_processor.cpp b/generator/routing_city_boundaries_processor.cpp new file mode 100644 index 0000000000..8f753328dc --- /dev/null +++ b/generator/routing_city_boundaries_processor.cpp @@ -0,0 +1,211 @@ +#include "generator/routing_city_boundaries_processor.hpp" + +#include "generator/collector_routing_city_boundaries.hpp" +#include "generator/feature_builder.hpp" + +#include "indexer/classificator.hpp" + +#include "coding/file_reader.hpp" + +#include "geometry/area_on_earth.hpp" +#include "geometry/circle_on_earth.hpp" +#include "geometry/distance_on_sphere.hpp" +#include "geometry/mercator.hpp" + +#include "base/logging.hpp" + +#include "3party/boost/boost/geometry.hpp" +#include "3party/boost/boost/geometry/geometries/point_xy.hpp" +#include "3party/boost/boost/geometry/geometries/polygon.hpp" + +#include +#include +#include +#include + +namespace generator +{ +namespace routing_city_boundaries +{ +using LocalityData = RoutingCityBoundariesWriter::LocalityData; + +std::unordered_map LoadNodeToLocalityData(std::string const & filename) +{ + FileReader reader(filename); + ReaderSource source(reader); + uint64_t n = 0; + source.Read(&n, sizeof(n)); + + std::unordered_map result; + for (size_t i = 0; i < n; ++i) + { + uint64_t nodeId = 0; + source.Read(&nodeId, sizeof(nodeId)); + + LocalityData localityData = LocalityData::Deserialize(source); + result[nodeId] = localityData; + } + + return result; +} + +std::unordered_map> LoadNodeToBoundariesData( + std::string const & filename) +{ + using MinAccuracy = feature::serialization_policy::MinSize; + FileReader reader(filename); + ReaderSource source(reader); + uint64_t n = 0; + source.Read(&n, sizeof(n)); + + std::unordered_map> result; + for (size_t i = 0; i < n; ++i) + { + uint64_t nodeId = 0; + source.Read(&nodeId, sizeof(nodeId)); + + feature::FeatureBuilder featureBuilder; + feature::ReadFromSourceRawFormat(source, featureBuilder); + result[nodeId].emplace_back(std::move(featureBuilder)); + } + + return result; +} + +double AreaOnEarth(std::vector const & points) +{ + namespace bg = boost::geometry; + using LonLatCoords = bg::cs::spherical_equatorial; + bg::model::ring> sphericalPolygon; + + auto const addPoint = [&sphericalPolygon](auto const & point) { + auto const latlon = mercator::ToLatLon(point); + sphericalPolygon.emplace_back(latlon.m_lon, latlon.m_lat); + }; + + sphericalPolygon.reserve(points.size()); + for (auto point : points) + addPoint(point); + + addPoint(points.front()); + + bg::strategy::area::spherical<> areaCalculationStrategy(ms::kEarthRadiusMeters); + + double const area = bg::area(sphericalPolygon, areaCalculationStrategy); + return fabs(area); +} + +std::pair GetBoundaryWithSmallestArea( + std::vector const & boundaries) +{ + size_t bestIndex = 0; + double minArea = std::numeric_limits::max(); + for (size_t i = 0; i < boundaries.size(); ++i) + { + auto const & geometry = boundaries[i].GetOuterGeometry(); + double const area = AreaOnEarth(geometry); + + if (minArea > area) + { + minArea = area; + bestIndex = i; + } + } + + return {boundaries[bestIndex], minArea}; +} + +void TransformPointToCircle(feature::FeatureBuilder & feature, m2::PointD const & center, + double radiusMeters) +{ + auto circleGeometry = ms::CreateCircleGeometryOnEarth(mercator::ToLatLon(center), radiusMeters, + 30.0 /* angleStepDegree */); + + feature.SetArea(); + feature.ResetGeometry(); + feature.AddPolygon(circleGeometry); +} +} // routing_city_boundaries +} // namespace generator + +namespace generator +{ +using namespace routing_city_boundaries; + +RoutingCityBoundariesProcessor::RoutingCityBoundariesProcessor(std::string filename) + : m_filename(std::move(filename)) +{ +} + +void RoutingCityBoundariesProcessor::ProcessDataFromCollector() +{ + auto const nodeOsmIdToLocalityData = LoadNodeToLocalityData( + RoutingCityBoundariesWriter::GetNodeToLocalityDataFilename(m_filename)); + + auto const nodeOsmIdToBoundaries = LoadNodeToBoundariesData( + RoutingCityBoundariesWriter::GetNodeToBoundariesFilename(m_filename)); + + using MinAccuracy = feature::serialization_policy::MinSize; + using FeatureWriter = feature::FeatureBuilderWriter; + FeatureWriter featuresWriter(RoutingCityBoundariesWriter::GetFeaturesBuilderFilename(m_filename), + FileWriter::Op::OP_APPEND); + + uint32_t pointToCircle = 0; + uint32_t matchedBoundary = 0; + + for (auto const & item : nodeOsmIdToBoundaries) + { + uint64_t const nodeOsmId = item.first; + auto const & boundaries = item.second; + + auto const it = nodeOsmIdToLocalityData.find(nodeOsmId); + if (it == nodeOsmIdToLocalityData.cend()) + continue; + + auto const & localityData = it->second; + if (localityData.m_population == 0) + continue; + + double bestFeatureBuilderArea = 0.0; + feature::FeatureBuilder bestFeatureBuilder; + std::tie(bestFeatureBuilder, bestFeatureBuilderArea) = GetBoundaryWithSmallestArea(boundaries); + + double const radiusMeters = + ftypes::GetRadiusByPopulationForRouting(localityData.m_population, localityData.m_place); + double const areaUpperBound = ms::CircleAreaOnEarth(radiusMeters); + + if (bestFeatureBuilderArea > areaUpperBound) + { + ++pointToCircle; + TransformPointToCircle(bestFeatureBuilder, localityData.m_position, radiusMeters); + } + else + { + ++matchedBoundary; + } + + if (bestFeatureBuilder.PreSerialize()) + featuresWriter.Write(bestFeatureBuilder); + } + + LOG(LINFO, (pointToCircle, "places were transformed to circle.")); + LOG(LINFO, (matchedBoundary, "boundaries were approved as city/town/village boundary.")); +} + +void RoutingCityBoundariesProcessor::DumpBoundaries(std::string const & dumpFilename) +{ + using MinAccuracy = feature::serialization_policy::MinSize; + + auto const features = feature::ReadAllDatRawFormat( + RoutingCityBoundariesWriter::GetFeaturesBuilderFilename(m_filename)); + + std::vector boundariesRects; + boundariesRects.reserve(features.size()); + + for (auto const & feature : features) + boundariesRects.emplace_back(feature.GetLimitRect()); + + FileWriter writer(dumpFilename); + rw::WriteVectorOfPOD(writer, boundariesRects); +} +} // namespace generator diff --git a/generator/routing_city_boundaries_processor.hpp b/generator/routing_city_boundaries_processor.hpp new file mode 100644 index 0000000000..eaf5800a84 --- /dev/null +++ b/generator/routing_city_boundaries_processor.hpp @@ -0,0 +1,36 @@ +#pragma once + +#include "generator/collector_routing_city_boundaries.hpp" +#include "generator/feature_builder.hpp" + +#include "geometry/point2d.hpp" + +#include +#include +#include + +namespace generator +{ +namespace routing_city_boundaries +{ +std::unordered_map LoadNodeToLocalityData( + std::string const & filename); + +std::unordered_map> LoadNodeToBoundariesData( + std::string const & filename); + +double AreaOnEarth(std::vector const & points); +} // namespace routing_city_boundaries + +class RoutingCityBoundariesProcessor +{ +public: + explicit RoutingCityBoundariesProcessor(std::string filename); + + void ProcessDataFromCollector(); + void DumpBoundaries(std::string const & dumpFilename); + +private: + std::string m_filename; +}; +} // namespace generator diff --git a/generator/translator_country.cpp b/generator/translator_country.cpp index 8dc05e374b..de853a8e7e 100644 --- a/generator/translator_country.cpp +++ b/generator/translator_country.cpp @@ -5,6 +5,7 @@ #include "generator/collector_collection.hpp" #include "generator/collector_interface.hpp" #include "generator/collector_mini_roundabout.hpp" +#include "generator/collector_routing_city_boundaries.hpp" #include "generator/collector_tag.hpp" #include "generator/cross_mwm_osm_ways_collector.hpp" #include "generator/feature_maker.hpp" @@ -104,8 +105,9 @@ TranslatorCountry::TranslatorCountry(std::shared_ptr info.GetIntermediateFileName(METALINES_FILENAME))); collectors->Append( std::make_shared(info.GetIntermediateFileName(CITIES_AREAS_TMP_FILENAME))); - // Collectors for gathering of additional information for the future building of - // routing section. + // Collectors for gathering of additional information for the future building of routing section. + collectors->Append(std::make_shared( + info.GetIntermediateFileName(ROUTING_CITY_BOUNDARIES_TMP_FILENAME), cache)); collectors->Append( std::make_shared(info.GetIntermediateFileName(MAXSPEEDS_FILENAME))); collectors->Append(std::make_shared( diff --git a/indexer/ftypes_matcher.cpp b/indexer/ftypes_matcher.cpp index ffaa96a855..eaf2eb1d72 100644 --- a/indexer/ftypes_matcher.cpp +++ b/indexer/ftypes_matcher.cpp @@ -103,6 +103,21 @@ string DebugPrint(HighwayClass const cls) return out.str(); } +std::string DebugPrint(LocalityType const localityType) +{ + switch (localityType) + { + case LocalityType::None: return "None"; + case LocalityType::Country: return "Country"; + case LocalityType::State: return "State"; + case LocalityType::City: return "City"; + case LocalityType::Town: return "Town"; + case LocalityType::Village: return "Village"; + case LocalityType::Count: return "Count"; + } + UNREACHABLE(); +} + HighwayClass GetHighwayClass(feature::TypesHolder const & types) { uint8_t constexpr kTruncLevel = 2; @@ -706,7 +721,24 @@ uint64_t GetPopulation(FeatureType & ft) double GetRadiusByPopulation(uint64_t p) { - return pow(static_cast(p), 0.277778) * 550.0; + return pow(static_cast(p), 1 / 3.6) * 550.0; +} + +// Look to: https://confluence.mail.ru/pages/viewpage.action?pageId=287950469 +// for details about factors. +// Shortly, we assume: radius = (population ^ (1 / base)) * mult +// We knew area info about some cities|towns|villages and did grid search. +// Interval for base: [0.1, 100]. +// Interval for mult: [10, 1000]. +double GetRadiusByPopulationForRouting(uint64_t p, LocalityType localityType) +{ + switch (localityType) + { + case LocalityType::City: return pow(static_cast(p), 1.0 / 2.6) * 50; + case LocalityType::Town: return pow(static_cast(p), 1.0 / 4.4) * 210.0; + case LocalityType::Village: return pow(static_cast(p), 1.0 / 15.3) * 730.0; + default: UNREACHABLE(); + } } uint64_t GetPopulationByRadius(double r) diff --git a/indexer/ftypes_matcher.hpp b/indexer/ftypes_matcher.hpp index efd63f0523..8dcb0d52c9 100644 --- a/indexer/ftypes_matcher.hpp +++ b/indexer/ftypes_matcher.hpp @@ -425,6 +425,7 @@ bool IsCityTownOrVillage(Types const & types) //@{ uint64_t GetPopulation(FeatureType & ft); double GetRadiusByPopulation(uint64_t p); +double GetRadiusByPopulationForRouting(uint64_t p, LocalityType localityType); uint64_t GetPopulationByRadius(double r); //@} @@ -452,6 +453,7 @@ enum class HighwayClass }; std::string DebugPrint(HighwayClass const cls); +std::string DebugPrint(LocalityType const localityType); HighwayClass GetHighwayClass(feature::TypesHolder const & types); } // namespace ftypes