mirror of
https://codeberg.org/comaps/comaps
synced 2026-01-07 21:13:55 +00:00
[routing] OSM tag based time and turn penalties
Signed-off-by: Henry Sternberg <dev@bluelightmaps.com> Co-Authored-By: eisa01 <eisa01@gmail.com> Co-Authored-By: x7z4w <x7z4w@noreply.codeberg.org> Co-Authored-By: Yannik Bloscheck <git@yannikbloscheck.com>
This commit is contained in:
@@ -186,6 +186,8 @@ set(SRC
|
||||
restriction_writer.hpp
|
||||
road_access_generator.cpp
|
||||
road_access_generator.hpp
|
||||
road_penalty_generator.cpp
|
||||
road_penalty_generator.hpp
|
||||
routing_city_boundaries_processor.cpp
|
||||
routing_city_boundaries_processor.hpp
|
||||
routing_helpers.cpp
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "generator/raw_generator.hpp"
|
||||
#include "generator/restriction_generator.hpp"
|
||||
#include "generator/road_access_generator.hpp"
|
||||
#include "generator/road_penalty_generator.hpp"
|
||||
#include "generator/routing_index_generator.hpp"
|
||||
#include "generator/routing_world_roads_generator.hpp"
|
||||
#include "generator/search_index_builder.hpp"
|
||||
@@ -466,6 +467,7 @@ MAIN_WITH_ERROR_HANDLING([](int argc, char ** argv)
|
||||
|
||||
string const restrictionsFilename = genInfo.GetIntermediateFileName(RESTRICTIONS_FILENAME);
|
||||
string const roadAccessFilename = genInfo.GetIntermediateFileName(ROAD_ACCESS_FILENAME);
|
||||
string const roadPenaltyFilename = genInfo.GetIntermediateFileName(ROAD_PENALTY_FILENAME);
|
||||
|
||||
BuildRoutingIndex(dataFile, country, *countryParentGetter);
|
||||
auto routingGraph = CreateIndexGraph(dataFile, country, *countryParentGetter);
|
||||
@@ -475,7 +477,8 @@ MAIN_WITH_ERROR_HANDLING([](int argc, char ** argv)
|
||||
|
||||
/// @todo CHECK return result doesn't work now for some small countries like Somalie.
|
||||
if (!BuildRoadRestrictions(*routingGraph, dataFile, restrictionsFilename, osmToFeatureFilename) ||
|
||||
!BuildRoadAccessInfo(dataFile, roadAccessFilename, *osm2feature))
|
||||
!BuildRoadAccessInfo(dataFile, roadAccessFilename, *osm2feature) ||
|
||||
!BuildRoadPenalty(dataFile, roadPenaltyFilename, *osm2feature))
|
||||
{
|
||||
LOG(LERROR, ("Routing build failed for", dataFile));
|
||||
}
|
||||
|
||||
253
generator/road_penalty_generator.cpp
Normal file
253
generator/road_penalty_generator.cpp
Normal file
@@ -0,0 +1,253 @@
|
||||
#include "generator/road_penalty_generator.hpp"
|
||||
|
||||
#include "generator/feature_builder.hpp"
|
||||
#include "generator/intermediate_data.hpp"
|
||||
#include "generator/osm_element.hpp"
|
||||
#include "generator/routing_helpers.hpp"
|
||||
|
||||
#include "routing/road_penalty_serialization.hpp"
|
||||
#include "routing/routing_helpers.hpp"
|
||||
#include "routing/vehicle_mask.hpp"
|
||||
|
||||
#include "coding/file_reader.hpp"
|
||||
#include "coding/file_writer.hpp"
|
||||
#include "coding/files_container.hpp"
|
||||
#include "coding/internal/file_data.hpp"
|
||||
#include "coding/varint.hpp"
|
||||
#include "coding/write_to_sink.hpp"
|
||||
|
||||
#include "base/assert.hpp"
|
||||
#include "base/logging.hpp"
|
||||
|
||||
#include <optional>
|
||||
|
||||
namespace routing_builder
|
||||
{
|
||||
using namespace feature;
|
||||
using namespace generator;
|
||||
using namespace routing;
|
||||
using std::string, std::vector;
|
||||
|
||||
namespace
|
||||
{
|
||||
// Unified penalty mapping for all OSM tags that create road penalties
|
||||
std::map<OsmElement::Tag, RoadPenalty::Type> const kUnifiedPenaltyMapping = {
|
||||
// Traffic calming measures
|
||||
|
||||
{OsmElement::Tag("traffic_calming", "rumble_strip"), RoadPenalty::Type::SmallCalming},
|
||||
{OsmElement::Tag("traffic_calming", "island"), RoadPenalty::Type::SmallCalming},
|
||||
|
||||
{OsmElement::Tag("traffic_calming", "cushion"), RoadPenalty::Type::MediumCalming},
|
||||
{OsmElement::Tag("traffic_calming", "chicane"), RoadPenalty::Type::MediumCalming},
|
||||
{OsmElement::Tag("traffic_calming", "choker"), RoadPenalty::Type::MediumCalming},
|
||||
{OsmElement::Tag("traffic_calming", "table"), RoadPenalty::Type::MediumCalming},
|
||||
|
||||
{OsmElement::Tag("traffic_calming", "bump"), RoadPenalty::Type::LargeCalming},
|
||||
{OsmElement::Tag("traffic_calming", "hump"), RoadPenalty::Type::LargeCalming},
|
||||
|
||||
{OsmElement::Tag("crossing", "unmarked"), RoadPenalty::Type::LargeCalming},
|
||||
|
||||
// Barrier penalties
|
||||
{OsmElement::Tag("barrier", "gate"), RoadPenalty::Type::Gate},
|
||||
{OsmElement::Tag("barrier", "lift_gate"), RoadPenalty::Type::Gate},
|
||||
{OsmElement::Tag("barrier", "swing_gate"), RoadPenalty::Type::Gate},
|
||||
|
||||
// Junction penalties
|
||||
{OsmElement::Tag("highway", "stop"), RoadPenalty::Type::UncontrolledJunction},
|
||||
{OsmElement::Tag("highway", "give_way"), RoadPenalty::Type::UncontrolledJunction},
|
||||
{OsmElement::Tag("crossing", "uncontrolled"), RoadPenalty::Type::UncontrolledJunction},
|
||||
{OsmElement::Tag("crossing", "marked"), RoadPenalty::Type::UncontrolledJunction},
|
||||
// highway=crossing ommitted as often accompanied by traffic_signals
|
||||
|
||||
{OsmElement::Tag("highway", "traffic_signals"), RoadPenalty::Type::ControlledJunction},
|
||||
{OsmElement::Tag("railway", "crossing"), RoadPenalty::Type::ControlledJunction},
|
||||
{OsmElement::Tag("railway", "level_crossing"), RoadPenalty::Type::ControlledJunction},
|
||||
};
|
||||
|
||||
std::optional<RoadPenalty::Type> GetPenaltyByMapping(OsmElement const & elem)
|
||||
{
|
||||
for (auto const & tag : elem.m_tags)
|
||||
{
|
||||
auto const it = kUnifiedPenaltyMapping.find(tag);
|
||||
if (it != kUnifiedPenaltyMapping.cend())
|
||||
return it->second;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
void ReadRoadPenalty(std::string const & penaltyPath, routing::OsmWay2FeaturePoint & way2Feature,
|
||||
RoadPenaltyByVehicleType & penalties)
|
||||
{
|
||||
FileReader reader(penaltyPath);
|
||||
ReaderSource src(reader);
|
||||
|
||||
// Read node penalties grouped by way
|
||||
uint64_t nodeWayCount = ReadPrimitiveFromSource<uint64_t>(src);
|
||||
while (nodeWayCount-- > 0)
|
||||
{
|
||||
uint64_t wayID = ReadPrimitiveFromSource<uint64_t>(src);
|
||||
uint32_t nodeCount = ReadPrimitiveFromSource<uint32_t>(src);
|
||||
|
||||
for (uint32_t i = 0; i < nodeCount; ++i)
|
||||
{
|
||||
uint32_t nodeIdx = ReadPrimitiveFromSource<uint32_t>(src);
|
||||
m2::PointU coord;
|
||||
coord.x = ReadPrimitiveFromSource<uint32_t>(src);
|
||||
coord.y = ReadPrimitiveFromSource<uint32_t>(src);
|
||||
VehicleMask vehicleMask = ReadPrimitiveFromSource<VehicleMask>(src);
|
||||
|
||||
// Read penalty type and derive time based on vehicle type
|
||||
RoadPenalty::Type type;
|
||||
routing::Load(src, type);
|
||||
|
||||
// For each vehicle type, create penalty with vehicle-specific time
|
||||
for (uint8_t vType = 0; vType < static_cast<uint8_t>(VehicleType::Count); ++vType)
|
||||
{
|
||||
if (vehicleMask & (1 << vType))
|
||||
{
|
||||
// Create penalty with time specific to this vehicle type
|
||||
auto vehicleSpecificPenalty = RoadPenalty::Penalty(type, static_cast<VehicleType>(vType));
|
||||
|
||||
// Process this location only once
|
||||
RoadPenalty::PointToPenalty pointToPenalty;
|
||||
way2Feature.ForEachNodeIdx(wayID, nodeIdx, coord, [&](uint32_t featureID, uint32_t nodeIdx)
|
||||
{ pointToPenalty[RoadPoint(featureID, nodeIdx)] = vehicleSpecificPenalty; });
|
||||
|
||||
if (!pointToPenalty.empty())
|
||||
{
|
||||
auto existingPenalties = penalties[vType].GetPointToPenalty();
|
||||
existingPenalties.insert(pointToPenalty.begin(), pointToPenalty.end());
|
||||
penalties[vType].SetPointPenalties(std::move(existingPenalties));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
// RoadPenaltyCollector implementation
|
||||
RoadPenaltyCollector::RoadPenaltyCollector(string const & filename, IDRInterfacePtr cache)
|
||||
: generator::CollectorInterface(filename)
|
||||
, m_cache(std::move(cache))
|
||||
{
|
||||
// Empty - initialization handled in member initializer list
|
||||
}
|
||||
|
||||
std::shared_ptr<CollectorInterface> RoadPenaltyCollector::Clone(IDRInterfacePtr const & cache) const
|
||||
{
|
||||
return std::make_shared<RoadPenaltyCollector>(GetFilename(), cache ? cache : m_cache);
|
||||
}
|
||||
|
||||
void RoadPenaltyCollector::CollectFeature(feature::FeatureBuilder const & fb, OsmElement const & elem)
|
||||
{
|
||||
// Track roads for barrier processing
|
||||
if (elem.IsWay() && routing::IsCarRoad(fb.GetTypes()))
|
||||
m_roads.AddWay(elem);
|
||||
|
||||
// Process only nodes for penalty types
|
||||
if (elem.IsNode())
|
||||
{
|
||||
auto penaltyType = GetPenaltyByMapping(elem);
|
||||
if (penaltyType)
|
||||
m_nodesWithType.Add(elem.m_id, ms::LatLon(elem.m_lat, elem.m_lon), *penaltyType);
|
||||
}
|
||||
}
|
||||
|
||||
void RoadPenaltyCollector::MergeInto(RoadPenaltyCollector & collector) const
|
||||
{
|
||||
m_nodesWithType.MergeInto(collector.m_nodesWithType);
|
||||
m_roads.MergeInto(collector.m_roads);
|
||||
}
|
||||
|
||||
void RoadPenaltyCollector::Save()
|
||||
{
|
||||
auto const fileName = GetFilename();
|
||||
LOG(LINFO, ("Saving road penalty values to", fileName));
|
||||
FileWriter writer(fileName);
|
||||
|
||||
// All vehicles use the same penalty types
|
||||
|
||||
// Group node penalties by way
|
||||
struct NodePenaltyEntry
|
||||
{
|
||||
uint32_t nodeIdx = 0;
|
||||
m2::PointU coord;
|
||||
RoadPenalty::Type type = RoadPenalty::Type::None;
|
||||
};
|
||||
|
||||
std::map<uint64_t, std::vector<NodePenaltyEntry>> penaltiesByWay;
|
||||
|
||||
m_roads.ForEachWayWithIndex([&](uint64_t wayID, std::vector<uint64_t> const & nodes, size_t idx)
|
||||
{
|
||||
std::vector<NodePenaltyEntry> wayNodePenalties;
|
||||
|
||||
for (uint32_t nodeIdx = 0; nodeIdx < nodes.size(); ++nodeIdx)
|
||||
{
|
||||
uint64_t const nodeID = nodes[nodeIdx];
|
||||
auto const * barrierEntry = m_nodesWithType.Find(nodeID);
|
||||
if (barrierEntry == nullptr)
|
||||
continue;
|
||||
|
||||
NodePenaltyEntry entry;
|
||||
entry.nodeIdx = nodeIdx;
|
||||
entry.coord = barrierEntry->m_coord;
|
||||
entry.type = barrierEntry->m_t;
|
||||
wayNodePenalties.push_back(entry);
|
||||
}
|
||||
|
||||
if (!wayNodePenalties.empty())
|
||||
penaltiesByWay[wayID] = std::move(wayNodePenalties);
|
||||
}, m_cache);
|
||||
|
||||
// Write node penalties grouped by way
|
||||
uint64_t const wayCount = penaltiesByWay.size();
|
||||
WriteToSink(writer, wayCount);
|
||||
for (auto const & [wayID, entries] : penaltiesByWay)
|
||||
{
|
||||
WriteToSink(writer, wayID);
|
||||
WriteToSink(writer, static_cast<uint32_t>(entries.size()));
|
||||
|
||||
for (auto const & entry : entries)
|
||||
{
|
||||
WriteToSink(writer, entry.nodeIdx);
|
||||
WriteToSink(writer, entry.coord.x);
|
||||
WriteToSink(writer, entry.coord.y);
|
||||
WriteToSink(writer, kAllVehiclesMask);
|
||||
|
||||
// Store only penalty type (time derived from vehicle type when loading)
|
||||
routing::Save(writer, entry.type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool BuildRoadPenalty(string const & dataFilePath, string const & roadPenaltyPath,
|
||||
routing::OsmWay2FeaturePoint & way2feature)
|
||||
{
|
||||
LOG(LINFO, ("Generating road penalty info for", dataFilePath));
|
||||
|
||||
try
|
||||
{
|
||||
RoadPenaltyByVehicleType penalties;
|
||||
ReadRoadPenalty(roadPenaltyPath, way2feature, penalties);
|
||||
|
||||
FilesContainerW cont(dataFilePath, FileWriter::OP_WRITE_EXISTING);
|
||||
auto writer = cont.GetWriter(ROAD_PENALTY_FILE_TAG);
|
||||
|
||||
// Write number of vehicle types
|
||||
uint32_t const vehicleTypeCount = penalties.size();
|
||||
WriteToSink(*writer, vehicleTypeCount);
|
||||
|
||||
// Write penalty data for each vehicle type
|
||||
for (size_t i = 0; i < penalties.size(); ++i)
|
||||
RoadPenaltySerializer::Serialize(*writer, penalties[i]);
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (RootException const & ex)
|
||||
{
|
||||
LOG(LWARNING, ("No road penalty created:", ex.Msg()));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} // namespace routing_builder
|
||||
70
generator/road_penalty_generator.hpp
Normal file
70
generator/road_penalty_generator.hpp
Normal file
@@ -0,0 +1,70 @@
|
||||
#pragma once
|
||||
|
||||
#include "generator/collector_interface.hpp"
|
||||
#include "generator/feature_builder.hpp"
|
||||
#include "generator/osm_element.hpp"
|
||||
#include "generator/way_nodes_mapper.hpp"
|
||||
|
||||
#include "routing/road_penalty.hpp"
|
||||
#include "routing/vehicle_mask.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace routing
|
||||
{
|
||||
class OsmWay2FeaturePoint;
|
||||
|
||||
template <class Sink>
|
||||
void Save(Sink & sink, RoadPenalty::Type const & type)
|
||||
{
|
||||
WriteToSink(sink, static_cast<uint8_t>(type));
|
||||
}
|
||||
|
||||
template <class Source>
|
||||
void Load(Source & src, RoadPenalty::Type & type)
|
||||
{
|
||||
uint8_t const res = ReadPrimitiveFromSource<uint8_t>(src);
|
||||
CHECK_LESS(res, static_cast<uint8_t>(RoadPenalty::Type::Count), ());
|
||||
type = static_cast<RoadPenalty::Type>(res);
|
||||
}
|
||||
} // namespace routing
|
||||
|
||||
namespace routing_builder
|
||||
{
|
||||
using RoadPenalty = routing::RoadPenalty;
|
||||
using VehicleType = routing::VehicleType;
|
||||
|
||||
using RoadPenaltyByVehicleType = std::array<RoadPenalty, static_cast<size_t>(VehicleType::Count)>;
|
||||
|
||||
class RoadPenaltyCollector : public generator::CollectorInterface
|
||||
{
|
||||
public:
|
||||
RoadPenaltyCollector(std::string const & filename, IDRInterfacePtr cache);
|
||||
|
||||
std::shared_ptr<CollectorInterface> Clone(IDRInterfacePtr const & cache = {}) const override;
|
||||
|
||||
void CollectFeature(feature::FeatureBuilder const & fb, OsmElement const & elem) override;
|
||||
|
||||
IMPLEMENT_COLLECTOR_IFACE(RoadPenaltyCollector);
|
||||
void MergeInto(RoadPenaltyCollector & collector) const;
|
||||
|
||||
protected:
|
||||
void Save() override;
|
||||
|
||||
private:
|
||||
IDRInterfacePtr m_cache;
|
||||
|
||||
// Store only penalty types during collection phase
|
||||
generator::WayNodesMapper<RoadPenalty::Type> m_nodesWithType;
|
||||
generator::WaysIDHolder m_roads;
|
||||
};
|
||||
|
||||
bool BuildRoadPenalty(std::string const & dataFilePath, std::string const & roadPenaltyPath,
|
||||
routing::OsmWay2FeaturePoint & way2feature);
|
||||
} // namespace routing_builder
|
||||
@@ -88,9 +88,16 @@ public:
|
||||
{
|
||||
auto ft = m_featureGetter.GetFeatureByIndex(featureID);
|
||||
CHECK(ft, (featureID));
|
||||
CHECK(ft->GetGeomType() == feature::GeomType::Line, (featureID));
|
||||
|
||||
// Converison should work with the same logic as in WayNodesMapper::EncodePoint.
|
||||
// Skip non-line features (e.g., barriers on area boundaries)
|
||||
if (ft->GetGeomType() != feature::GeomType::Line)
|
||||
{
|
||||
// Use LDEBUG to reduce log spam for features processed multiple times per vehicle type
|
||||
LOG(LDEBUG, ("Skipping non-line feature", featureID, "for node penalty mapping"));
|
||||
continue;
|
||||
}
|
||||
|
||||
// Conversion should work with the same logic as in WayNodesMapper::EncodePoint.
|
||||
auto const mercatorPt = PointUToPointD(pt, kPointCoordBits, fullRect);
|
||||
double minSquareDist = 1.0E6;
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
// #include "generator/node_mixer.hpp"
|
||||
#include "generator/restriction_writer.hpp"
|
||||
#include "generator/road_access_generator.hpp"
|
||||
#include "generator/road_penalty_generator.hpp"
|
||||
|
||||
#include "platform/platform.hpp"
|
||||
|
||||
@@ -107,6 +108,8 @@ TranslatorCountry::TranslatorCountry(std::shared_ptr<FeatureProcessorInterface>
|
||||
info.GetIntermediateFileName(RESTRICTIONS_FILENAME), cache->GetCache()));
|
||||
collectors->Append(std::make_shared<routing_builder::RoadAccessCollector>(
|
||||
info.GetIntermediateFileName(ROAD_ACCESS_FILENAME), cache->GetCache()));
|
||||
collectors->Append(std::make_shared<routing_builder::RoadPenaltyCollector>(
|
||||
info.GetIntermediateFileName(ROAD_PENALTY_FILENAME), cache->GetCache()));
|
||||
collectors->Append(std::make_shared<routing_builder::CameraCollector>(
|
||||
info.GetIntermediateFileName(CAMERAS_TO_WAYS_FILENAME), cache->GetCache()));
|
||||
collectors->Append(std::make_shared<MiniRoundaboutCollector>(info.GetIntermediateFileName(MINI_ROUNDABOUTS_FILENAME),
|
||||
|
||||
@@ -146,6 +146,13 @@ public:
|
||||
|
||||
void MergeInto(WaysMapper & mapper) const { mapper.m_ways.insert(m_ways.begin(), m_ways.end()); }
|
||||
|
||||
template <class Fn>
|
||||
void ForEach(Fn && fn) const
|
||||
{
|
||||
for (auto const & [id, value] : m_ways)
|
||||
fn(id, value);
|
||||
}
|
||||
|
||||
template <class Sink>
|
||||
void Serialize(Sink & sink) const
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user