From 1006b18ce2e9cfa443a341d049c3f5fc2e8ca13b Mon Sep 17 00:00:00 2001 From: Alexey Zakharenkov Date: Tue, 15 Dec 2020 11:24:23 +0300 Subject: [PATCH] [generator] Mark building with 'has_parts' property based on real buiding and building:part-s geometry --- generator/boost_helpers.hpp | 22 ++++---- generator/final_processor_country.cpp | 80 +++++++++++++++++++++++++++ generator/final_processor_country.hpp | 1 + generator/hierarchy.cpp | 1 - generator/osm2type.cpp | 2 - generator/relation_tags.cpp | 5 -- 6 files changed, 93 insertions(+), 18 deletions(-) diff --git a/generator/boost_helpers.hpp b/generator/boost_helpers.hpp index 373e449d52..4fea844a62 100644 --- a/generator/boost_helpers.hpp +++ b/generator/boost_helpers.hpp @@ -10,12 +10,13 @@ namespace generator { namespace boost_helpers { -using BoostPoint = boost::geometry::model::point; -using BoostPolygon = boost::geometry::model::polygon; -template +template void FillBoostGeometry(BoostGeometry & geometry, FbGeometry const & fbGeometry) { + using BoostPoint = typename boost::geometry::point_type::type; + + geometry.clear(); geometry.reserve(fbGeometry.size()); for (auto const & p : fbGeometry) boost::geometry::append(geometry, BoostPoint{p.x, p.y}); @@ -24,16 +25,17 @@ void FillBoostGeometry(BoostGeometry & geometry, FbGeometry const & fbGeometry) template void FillPolygon(BoostPolygon & polygon, feature::FeatureBuilder const & fb) { - using BoostPoint = typename BoostPolygon::point_type; auto const & fbGeometry = fb.GetGeometry(); CHECK(!fbGeometry.empty(), ()); - auto it = std::begin(fbGeometry); - FillBoostGeometry(polygon.outer(), *it); + + polygon.clear(); + FillBoostGeometry(polygon.outer(), *fbGeometry.begin()); polygon.inners().resize(fbGeometry.size() - 1); - int i = 0; - ++it; - for (; it != std::end(fbGeometry); ++it) - FillBoostGeometry(polygon.inners()[i++], *it); + + size_t i = 0; + + for (auto it = std::next(fbGeometry.begin()); it != fbGeometry.end(); ++it) + FillBoostGeometry(polygon.inners()[i++], *it); boost::geometry::correct(polygon); } diff --git a/generator/final_processor_country.cpp b/generator/final_processor_country.cpp index 543c7653dc..645077de21 100644 --- a/generator/final_processor_country.cpp +++ b/generator/final_processor_country.cpp @@ -2,6 +2,7 @@ #include "generator/affiliation.hpp" #include "generator/booking_dataset.hpp" +#include "generator/boost_helpers.hpp" #include "generator/feature_builder.hpp" #include "generator/final_processor_utils.hpp" #include "generator/isolines_generator.hpp" @@ -23,6 +24,13 @@ #include #include +#include "3party/boost/boost/geometry.hpp" +#include "3party/boost/boost/geometry/geometries/register/point.hpp" +#include "3party/boost/boost/geometry/geometries/register/ring.hpp" + +BOOST_GEOMETRY_REGISTER_POINT_2D(m2::PointD, double, boost::geometry::cs::cartesian, x, y) +BOOST_GEOMETRY_REGISTER_RING(std::vector) + using namespace base::thread_pool::computational; using namespace feature; @@ -111,6 +119,7 @@ void CountryFinalProcessor::Process() AddIsolines(); DropProhibitedSpeedCameras(); + ProcessBuildingParts(); Finish(); } @@ -195,6 +204,77 @@ void CountryFinalProcessor::ProcessRoundabouts() }, m_threadsCount); } +bool DoesBuildingConsistOfParts(FeatureBuilder const & fbBuilding, + m4::Tree const & buildingPartsKDTree) +{ + namespace bg = boost::geometry; + using BoostPoint = bg::model::point; + using BoostPolygon = bg::model::polygon; + using BoostMultiPolygon = bg::model::multi_polygon; + + BoostPolygon building; + BoostMultiPolygon partsUnion; + + buildingPartsKDTree.ForEachInRect(fbBuilding.GetLimitRect(), [&](auto const & fbPart) { + // Lazy initialization that will not occur with high probability + if (bg::is_empty(building)) + generator::boost_helpers::FillPolygon(building, fbBuilding); + + BoostPolygon part; + generator::boost_helpers::FillPolygon(part, fbPart); + + BoostMultiPolygon newPartsUnion; + bg::union_(partsUnion, part, newPartsUnion); + partsUnion = std::move(newPartsUnion); + }); + + if (bg::is_empty(building)) + return false; + + BoostMultiPolygon partsWithinBuilding; + bg::intersection(building, partsUnion, partsWithinBuilding); + + // Consider a building as consisting of parts if the building footprint + // is covered with parts at least by 90%. + return bg::area(partsWithinBuilding) >= 0.9 * bg::area(building); +} + +void CountryFinalProcessor::ProcessBuildingParts() +{ + static auto const & classificator = classif(); + static auto const buildingClassifType = classificator.GetTypeByPath({"building"}); + static auto const buildingPartClassifType = classificator.GetTypeByPath({"building:part"}); + static auto const buildingWithPartsClassifType = classificator.GetTypeByPath({"building", "has_parts"}); + + ForEachMwmTmp(m_temporaryMwmPath, [&](auto const & name, auto const & path) { + if (!IsCountry(name)) + return; + + // All "building:part" features in MWM + m4::Tree buildingPartsKDTree; + + ForEachFeatureRawFormat(path, [&](auto && fb, auto /* pos */) { + if (!(fb.IsArea() && fb.IsValid())) + return; + + if (fb.HasType(buildingPartClassifType)) + buildingPartsKDTree.Add(fb); + }); + + FeatureBuilderWriter writer(path, true /* mangleName */); + ForEachFeatureRawFormat(path, [&](auto && fb, auto /* pos */) { + if (fb.IsArea() && fb.IsValid() && + fb.HasType(buildingClassifType) && + DoesBuildingConsistOfParts(fb, buildingPartsKDTree)) + { + fb.AddType(buildingWithPartsClassifType); + } + + writer.Write(fb); + }); + }, m_threadsCount); +} + void CountryFinalProcessor::AddIsolines() { // For generated isolines must be built isolines_info section based on the same diff --git a/generator/final_processor_country.hpp b/generator/final_processor_country.hpp index 7f1230f953..64d66ca69d 100644 --- a/generator/final_processor_country.hpp +++ b/generator/final_processor_country.hpp @@ -48,6 +48,7 @@ private: void AddIsolines(); void DropProhibitedSpeedCameras(); void Finish(); + void ProcessBuildingParts(); bool IsCountry(std::string const & filename); diff --git a/generator/hierarchy.cpp b/generator/hierarchy.cpp index e7546a5480..afd7a3b2fb 100644 --- a/generator/hierarchy.cpp +++ b/generator/hierarchy.cpp @@ -20,7 +20,6 @@ #include #include #include -#include BOOST_GEOMETRY_REGISTER_POINT_2D(m2::PointD, double, boost::geometry::cs::cartesian, x, y); BOOST_GEOMETRY_REGISTER_RING(std::vector); diff --git a/generator/osm2type.cpp b/generator/osm2type.cpp index ac11f64acd..4edb30188e 100644 --- a/generator/osm2type.cpp +++ b/generator/osm2type.cpp @@ -720,8 +720,6 @@ void PostprocessElement(OsmElement * p, FeatureBuilderParams & params) {"wheelchair", "designated", [¶ms] { params.AddType(types.Get(CachedTypes::Type::WheelchairYes)); }}, {"wifi", "~", [¶ms] { params.AddType(types.Get(CachedTypes::Type::Wlan)); }}, - {"building:part", "no", [¶ms] { params.AddType(types.Get(CachedTypes::Type::HasParts)); }}, - {"building:parts", "~", [¶ms] { params.AddType(types.Get(CachedTypes::Type::HasParts)); }}, }); bool highwayDone = false; diff --git a/generator/relation_tags.cpp b/generator/relation_tags.cpp index 2099e06b0c..5488a8fc2e 100644 --- a/generator/relation_tags.cpp +++ b/generator/relation_tags.cpp @@ -98,12 +98,7 @@ void RelationTagsWay::Process(RelationElement const & e) } if (type == "building") - { - // If this way has "outline" role, add [building=has_parts] type. - if (e.GetWayRole(m_current->m_id) == "outline") - Base::AddCustomTag({"building", "has_parts"}); return; - } bool const isBoundary = (type == "boundary") && IsAcceptBoundary(e); bool const processAssociatedStreet = type == "associatedStreet" &&