Repo created

This commit is contained in:
Fr4nz D13trich 2025-11-22 13:58:55 +01:00
parent 4af19165ec
commit 68073add76
12458 changed files with 12350765 additions and 2 deletions

View file

@ -0,0 +1,15 @@
project(world_roads_builder)
set(SRC
world_roads_builder.cpp
world_roads_builder.hpp
)
omim_add_library(${PROJECT_NAME} ${SRC})
target_link_libraries(${PROJECT_NAME}
generator
geometry
)
omim_add_tool_subdirectory(world_roads_builder_tool)

View file

@ -0,0 +1,288 @@
#include "generator/world_roads_builder/world_roads_builder.hpp"
#include "base/assert.hpp"
#include "base/logging.hpp"
#include <algorithm>
#include <string>
namespace
{
using namespace routing;
size_t constexpr kMaxRoadsCount = 5;
std::string const kDelim = " ";
double constexpr kMinSegmentLengthM = 200.0;
double constexpr kHalfSegmentLengthM = kMinSegmentLengthM / 2.0;
// Returns true if roads count between |mwmId1| and |mwmId2| in |graph| exceeds max value.
bool MwmRoadsAreFilled(routing::NumMwmId const & mwmId1, routing::NumMwmId const & mwmId2, CrossBorderGraph & graph)
{
auto const it = graph.m_mwms.find(mwmId1);
if (it == graph.m_mwms.end())
return false;
size_t count = 0;
for (auto segId : it->second)
{
auto const segDataIt = graph.m_segments.find(segId);
CHECK(segDataIt != graph.m_segments.end(), (segId));
auto const & segData = segDataIt->second;
if (segData.m_start.m_numMwmId == mwmId2 || segData.m_end.m_numMwmId == mwmId2)
++count;
}
return count >= kMaxRoadsCount;
}
void WriteEndingToSteam(CrossBorderSegmentEnding const & segEnding, std::ofstream & output)
{
auto const & point = segEnding.m_point.GetLatLon();
output << kDelim << point.m_lat << kDelim << point.m_lon << kDelim << segEnding.m_numMwmId;
}
void WriteSegmentToStream(RegionSegmentId const & segId, CrossBorderSegment const & seg, std::ofstream & output)
{
CHECK(output.is_open(), ());
output << segId << kDelim << seg.m_weight;
WriteEndingToSteam(seg.m_start, output);
WriteEndingToSteam(seg.m_end, output);
output << std::endl;
}
} // namespace
namespace routing
{
RoadsFromOsm GetRoadsFromOsm(generator::SourceReader & reader, feature::CountriesFilesAffiliation const & mwmMatcher,
std::vector<std::string> const & highways)
{
RoadsFromOsm roadsFromOsm;
ProcessOsmElementsFromO5M(reader, [&roadsFromOsm, &highways](OsmElement && e)
{
if (e.IsWay())
{
std::string const & highway = e.GetTag("highway");
if (!highway.empty() && base::IsExist(highways, highway))
{
auto id = e.m_id;
roadsFromOsm.m_ways[highway].emplace(id, RoadData({}, std::move(e)));
}
}
else if (e.IsNode())
{
roadsFromOsm.m_nodes.emplace(e.m_id, ms::LatLon(e.m_lat, e.m_lon));
}
});
LOG(LINFO, ("Extracted", roadsFromOsm.m_nodes.size(), "nodes from OSM."));
for (auto & [highway, ways] : roadsFromOsm.m_ways)
{
LOG(LINFO, ("Extracted", ways.size(), highway, "highways from OSM."));
for (auto & [wayId, wayData] : ways)
{
for (auto const & nodeId : {wayData.m_way.Nodes().front(), wayData.m_way.Nodes().back()})
{
auto const nodeIt = roadsFromOsm.m_nodes.find(nodeId);
CHECK(nodeIt != roadsFromOsm.m_nodes.end(), (nodeId));
m2::PointD const & point = mercator::FromLatLon(nodeIt->second);
auto const & regions = mwmMatcher.GetAffiliations(point);
for (auto const & region : regions)
wayData.m_regions.emplace(region);
}
}
}
LOG(LINFO, ("Filled regions for ways."));
return roadsFromOsm;
}
struct NodePoint
{
NodePoint(m2::PointD const & point, NumMwmId const & mwm) : m_point(point), m_mwm(mwm) {}
m2::PointD m_point;
NumMwmId m_mwm = 0;
};
using NodePoints = std::vector<NodePoint>;
using CrossBorderIndexes = std::vector<size_t>;
std::pair<NodePoints, CrossBorderIndexes> GetCrossBorderPoints(
std::vector<uint64_t> const & nodeIds, std::unordered_map<uint64_t, ms::LatLon> const & nodes,
feature::CountriesFilesAffiliation const & mwmMatcher,
std::unordered_map<std::string, NumMwmId> const & regionToIdMap)
{
NodePoints nodePoints;
CrossBorderIndexes crossBorderIndexes;
std::string prevRegion;
m2::PointD prevPoint;
for (auto const & nodeId : nodeIds)
{
auto const itNodes = nodes.find(nodeId);
CHECK(itNodes != nodes.end(), (nodeId));
m2::PointD const & curPoint = mercator::FromLatLon(itNodes->second);
auto const & regions = mwmMatcher.GetAffiliations(curPoint);
if (regions.size() > 1)
{
LOG(LWARNING, ("Point", itNodes->second, "belongs to multiple mwms:", regions));
continue;
}
if (regions.empty())
{
LOG(LWARNING, ("Point", itNodes->second, "doesn't belong to any mwm."));
continue;
}
auto const & curRegion = regions[0];
auto const & curMwmId = regionToIdMap.at(curRegion);
nodePoints.emplace_back(curPoint, curMwmId);
if (curRegion != prevRegion)
{
if (!prevRegion.empty())
{
CHECK_GREATER(nodePoints.size(), 1, ());
// We add index of the previous point.
crossBorderIndexes.push_back(nodePoints.size() - 2);
}
prevRegion = curRegion;
}
prevPoint = curPoint;
}
return std::make_pair(nodePoints, crossBorderIndexes);
}
std::optional<std::pair<m2::PointD, double>> GetPointInMwm(NodePoints const & points, size_t index, bool forward)
{
auto const & pointOnBorder = points[index];
m2::PointD newPoint = pointOnBorder.m_point;
double dist = 0.0;
while ((!forward && index > 0) || (forward && index < points.size() - 1))
{
if (forward)
++index;
else
--index;
auto const & point = points[index];
if (point.m_mwm != pointOnBorder.m_mwm)
break;
double const curDist = mercator::DistanceOnEarth(pointOnBorder.m_point, point.m_point);
if (curDist >= kHalfSegmentLengthM)
return std::make_pair(point.m_point, curDist);
newPoint = point.m_point;
dist = curDist;
}
return std::make_pair(newPoint, dist);
}
bool FillCrossBorderGraph(CrossBorderGraph & graph, RegionSegmentId & curSegmentId,
std::vector<uint64_t> const & nodeIds, std::unordered_map<uint64_t, ms::LatLon> const & nodes,
feature::CountriesFilesAffiliation const & mwmMatcher,
std::unordered_map<std::string, NumMwmId> const & regionToIdMap)
{
auto const & [nodePoints, crossBorderIndexes] = GetCrossBorderPoints(nodeIds, nodes, mwmMatcher, regionToIdMap);
bool insertedRoad = false;
for (auto i : crossBorderIndexes)
{
auto const & p1 = nodePoints[i];
auto const & p2 = nodePoints[i + 1];
if (MwmRoadsAreFilled(p1.m_mwm, p2.m_mwm, graph))
continue;
auto const pMwm1 = GetPointInMwm(nodePoints, i, false /* forward */);
if (!pMwm1)
continue;
auto const pMwm2 = GetPointInMwm(nodePoints, i + 1, true /* forward */);
if (!pMwm2)
continue;
double const d = mercator::DistanceOnEarth(p1.m_point, p2.m_point);
CrossBorderSegment seg;
seg.m_weight = pMwm1->second + d + pMwm2->second;
seg.m_start = CrossBorderSegmentEnding(pMwm1->first /* point */, p1.m_mwm);
seg.m_end = CrossBorderSegmentEnding(pMwm2->first /* point */, p2.m_mwm);
graph.AddCrossBorderSegment(curSegmentId++, seg);
insertedRoad = true;
}
return insertedRoad;
}
bool WriteGraphToFile(CrossBorderGraph const & graph, std::string const & path, bool overwrite)
{
std::ofstream output;
output.exceptions(std::ofstream::failbit | std::ofstream::badbit);
try
{
std::ios_base::openmode mode = overwrite ? std::ofstream::trunc : std::ofstream::app;
output.open(path, mode);
output << std::setprecision(12);
for (auto const & [segId, segData] : graph.m_segments)
WriteSegmentToStream(segId, segData, output);
}
catch (std::ofstream::failure const & se)
{
LOG(LWARNING, ("Exception saving roads to file", path, se.what()));
return false;
}
return true;
}
void ShowRegionsStats(CrossBorderGraph const & graph, std::shared_ptr<routing::NumMwmIds> numMwmIds)
{
for (auto const & [mwmId, segmentIds] : graph.m_mwms)
LOG(LINFO, (numMwmIds->GetFile(mwmId).GetName(), "roads:", segmentIds.size()));
std::string const delimRegions = " - ";
std::map<std::string, size_t> statsByMwm;
for (auto const & [segId, segData] : graph.m_segments)
{
std::string reg1 = numMwmIds->GetFile(segData.m_start.m_numMwmId).GetName();
std::string reg2 = numMwmIds->GetFile(segData.m_end.m_numMwmId).GetName();
std::string k = reg1 < reg2 ? reg1 + delimRegions + reg2 : reg2 + delimRegions + reg1;
++statsByMwm[k];
}
LOG(LINFO, ("Count of roads between mwm pairs:"));
for (auto const & [k, count] : statsByMwm)
LOG(LINFO, (k, " -> ", count));
}
} // namespace routing

View file

@ -0,0 +1,62 @@
#pragma once
#include "generator/affiliation.hpp"
#include "generator/osm_element.hpp"
#include "generator/osm_source.hpp"
#include "routing/cross_border_graph.hpp"
#include "routing_common/num_mwm_id.hpp"
#include "geometry/latlon.hpp"
#include "geometry/mercator.hpp"
#include <cstdint>
#include <fstream>
#include <memory>
#include <set>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>
namespace routing
{
// Road way and regions through which it passes.
struct RoadData
{
RoadData() = default;
RoadData(std::set<std::string> regions, OsmElement && way) : m_regions(std::move(regions)), m_way(std::move(way)) {}
std::set<std::string> m_regions;
OsmElement m_way;
};
using RoadsData = std::unordered_map<uint64_t, RoadData>;
using HighwayTypeToRoads = std::unordered_map<std::string, RoadsData>;
// Ways and corresponding nodes of roads extracted from OSM file.
struct RoadsFromOsm
{
HighwayTypeToRoads m_ways;
std::unordered_map<uint64_t, ms::LatLon> m_nodes;
};
// Reads roads from |reader| and finds its mwms with |mwmMatcher|.
RoadsFromOsm GetRoadsFromOsm(generator::SourceReader & reader, feature::CountriesFilesAffiliation const & mwmMatcher,
std::vector<std::string> const & highways);
// Fills |graph| with new segments starting from |curSegmentId|. Segments are calculated from the
// road consisting of |nodeIds| from OSM.
bool FillCrossBorderGraph(CrossBorderGraph & graph, RegionSegmentId & curSegmentId,
std::vector<uint64_t> const & nodeIds, std::unordered_map<uint64_t, ms::LatLon> const & nodes,
feature::CountriesFilesAffiliation const & mwmMatcher,
std::unordered_map<std::string, routing::NumMwmId> const & regionToIdMap);
// Dumps |graph| to |path|.
bool WriteGraphToFile(CrossBorderGraph const & graph, std::string const & path, bool overwrite);
// Logs statistics about segments in |graph|.
void ShowRegionsStats(CrossBorderGraph const & graph, std::shared_ptr<routing::NumMwmIds> numMwmIds);
} // namespace routing

View file

@ -0,0 +1,13 @@
project(world_roads_builder_tool)
set(SRC
world_roads_builder_tool.cpp
)
omim_add_executable(${PROJECT_NAME} ${SRC})
target_link_libraries(${PROJECT_NAME}
world_roads_builder
storage
gflags::gflags
)

View file

@ -0,0 +1,98 @@
#include "generator/affiliation.hpp"
#include "generator/osm_element.hpp"
#include "generator/osm_source.hpp"
#include "generator/world_roads_builder/world_roads_builder.hpp"
#include "storage/routing_helpers.hpp"
#include "storage/storage.hpp"
#include "routing_common/num_mwm_id.hpp"
#include "platform/platform.hpp"
#include "base/assert.hpp"
#include "base/file_name_utils.hpp"
#include "base/logging.hpp"
#include <memory>
#include <set>
#include <string>
#include <unordered_map>
#include <vector>
#include <gflags/gflags.h>
DEFINE_string(path_resources, "", "CoMaps resources directory");
DEFINE_string(path_roads_file, "", "OSM file in o5m format.");
DEFINE_string(path_res_file, "", "Path to the resulting file with roads for generator_tool.");
int main(int argc, char ** argv)
{
using namespace routing;
gflags::SetUsageMessage("Reads OSM file, generates text file with main cross-mwm roads for generator_tool.");
gflags::ParseCommandLineFlags(&argc, &argv, true);
auto const toolName = base::FileNameFromFullPath(argv[0]);
if (FLAGS_path_resources.empty() || !Platform::IsDirectory(FLAGS_path_resources) || FLAGS_path_roads_file.empty() ||
FLAGS_path_res_file.empty())
{
gflags::ShowUsageWithFlagsRestrict(argv[0], toolName.c_str());
return EXIT_FAILURE;
}
GetPlatform().SetResourceDir(FLAGS_path_resources);
feature::CountriesFilesAffiliation mwmMatcher(GetPlatform().ResourcesDir(), false /* haveBordersForWholeWorld */);
// These types are used in maps_generator (maps_generator/genrator/steps.py in filter_roads function).
std::vector<std::string> const highwayTypes{"motorway", "trunk", "primary", "secondary", "tertiary"};
generator::SourceReader reader(FLAGS_path_roads_file);
RoadsFromOsm const & roadsFromOsm = GetRoadsFromOsm(reader, mwmMatcher, highwayTypes);
storage::Storage storage;
std::shared_ptr<NumMwmIds> numMwmIds = CreateNumMwmIds(storage);
std::unordered_map<std::string, NumMwmId> regionsToIds;
numMwmIds->ForEachId([&regionsToIds, &numMwmIds](NumMwmId id)
{
std::string const & region = numMwmIds->GetFile(id).GetName();
CHECK(regionsToIds.emplace(region, id).second, (id, region));
});
CrossBorderGraph graph;
RegionSegmentId curSegmentId = 0;
for (auto const & highway : highwayTypes)
{
auto const it = roadsFromOsm.m_ways.find(highway);
CHECK(it != roadsFromOsm.m_ways.end(), (highway));
auto const & ways = it->second;
for (auto const & [wayId, wayData] : ways)
{
if (wayData.m_regions.size() == 1)
continue;
bool const foundSegments = FillCrossBorderGraph(graph, curSegmentId, wayData.m_way.Nodes(), roadsFromOsm.m_nodes,
mwmMatcher, regionsToIds);
LOG(LINFO, ("Found segments for", wayId, ":", foundSegments));
}
}
LOG(LINFO, ("Done handling regions for ways. Segments count:", graph.m_segments.size()));
if (!WriteGraphToFile(graph, FLAGS_path_res_file, true /* overwrite */))
{
LOG(LCRITICAL, ("Failed writing to file", FLAGS_path_res_file));
return EXIT_FAILURE;
}
LOG(LINFO, ("Saved graph to file", FLAGS_path_res_file));
ShowRegionsStats(graph, numMwmIds);
}