#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 "coding/read_write_utils.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}; } } // routing_city_boundaries } // namespace generator namespace generator { using namespace routing_city_boundaries; RoutingCityBoundariesProcessor::RoutingCityBoundariesProcessor(std::string tmpFilename, std::string dumpFilename) : m_tmpFilename(std::move(tmpFilename)) , m_dumpFilename(std::move(dumpFilename)) { } void RoutingCityBoundariesProcessor::ProcessDataFromCollector() { auto const nodeOsmIdToLocalityData = LoadNodeToLocalityData( RoutingCityBoundariesWriter::GetNodeToLocalityDataFilename(m_tmpFilename)); auto const nodeOsmIdToBoundaries = LoadNodeToBoundariesData( RoutingCityBoundariesWriter::GetNodeToBoundariesFilename(m_tmpFilename)); FileWriter boundariesWriter(m_dumpFilename, FileWriter::Op::OP_APPEND); uint32_t pointToCircle = 0; uint32_t matchedBoundary = 0; for (auto const & item : nodeOsmIdToLocalityData) { uint64_t const nodeOsmId = item.first; auto const & localityData = item.second; if (localityData.m_population == 0) continue; double const radiusMeters = ftypes::GetRadiusByPopulationForRouting(localityData.m_population, localityData.m_place); double const areaUpperBound = ms::CircleAreaOnEarth(radiusMeters); feature::FeatureBuilder feature; auto const boundariesIt = nodeOsmIdToBoundaries.find(nodeOsmId); if (boundariesIt != nodeOsmIdToBoundaries.cend()) { auto const & boundaries = boundariesIt->second; double bestFeatureBuilderArea = 0.0; std::tie(feature, bestFeatureBuilderArea) = GetBoundaryWithSmallestArea(boundaries); if (bestFeatureBuilderArea <= areaUpperBound) { ++matchedBoundary; rw::WriteVectorOfPOD(boundariesWriter, feature.GetOuterGeometry()); continue; } } auto const circleGeometry = ms::CreateCircleGeometryOnEarth( mercator::ToLatLon(localityData.m_position), radiusMeters, 30.0 /* angleStepDegree */); ++pointToCircle; rw::WriteVectorOfPOD(boundariesWriter, circleGeometry); } LOG(LINFO, (pointToCircle, "places were transformed to circle.")); LOG(LINFO, (matchedBoundary, "boundaries were approved as city/town/village boundary.")); } } // namespace generator