#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 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 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}, {OsmElement::Tag("barrier", "toll_booth"), 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 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(src); while (nodeWayCount-- > 0) { uint64_t wayID = ReadPrimitiveFromSource(src); uint32_t nodeCount = ReadPrimitiveFromSource(src); for (uint32_t i = 0; i < nodeCount; ++i) { uint32_t nodeIdx = ReadPrimitiveFromSource(src); m2::PointU coord; coord.x = ReadPrimitiveFromSource(src); coord.y = ReadPrimitiveFromSource(src); VehicleMask vehicleMask = ReadPrimitiveFromSource(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(VehicleType::Count); ++vType) { if (vehicleMask & (1 << vType)) { // Create penalty with time specific to this vehicle type auto vehicleSpecificPenalty = RoadPenalty::Penalty(type, static_cast(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 RoadPenaltyCollector::Clone(IDRInterfacePtr const & cache) const { return std::make_shared(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> penaltiesByWay; m_roads.ForEachWayWithIndex([&](uint64_t wayID, std::vector const & nodes, size_t idx) { std::vector 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(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); } } LOG(LINFO, ("Finished saving road penalty values")); } 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