BaseParser::BaseParser( ExtractorCallbacks * extractor_callbacks, ScriptingEnvironment & scripting_environment ) : extractor_callbacks(extractor_callbacks), lua_state(scripting_environment.getLuaStateForThreadID(0)), scripting_environment(scripting_environment), use_turn_restrictions(true) { ReadUseRestrictionsSetting(); ReadRestrictionExceptions(); }
RestrictionParser::RestrictionParser(ScriptingEnvironment &scripting_environment) : use_turn_restrictions(scripting_environment.GetProfileProperties().use_turn_restrictions) { if (use_turn_restrictions) { restrictions = scripting_environment.GetRestrictions(); const unsigned count = restrictions.size(); if (count > 0) { util::SimpleLogger().Write() << "Found " << count << " turn restriction tags:"; for (const std::string &str : restrictions) { util::SimpleLogger().Write() << " " << str; } } else { util::SimpleLogger().Write() << "Found no turn restriction tags"; } } }
BaseParser::BaseParser(ExtractorCallbacks* ec, ScriptingEnvironment& se) : extractor_callbacks(ec), scriptingEnvironment(se), luaState(NULL), use_turn_restrictions(true) { luaState = se.getLuaStateForThreadID(0); ReadUseRestrictionsSetting(); ReadRestrictionExceptions(); }
void GraphCompressor::Compress( const std::unordered_set<NodeID> &barrier_nodes, const std::unordered_set<NodeID> &traffic_signals, ScriptingEnvironment &scripting_environment, std::vector<TurnRestriction> &turn_restrictions, std::vector<ConditionalTurnRestriction> &conditional_turn_restrictions, std::vector<UnresolvedManeuverOverride> &maneuver_overrides, util::NodeBasedDynamicGraph &graph, const std::vector<NodeBasedEdgeAnnotation> &node_data_container, CompressedEdgeContainer &geometry_compressor) { const unsigned original_number_of_nodes = graph.GetNumberOfNodes(); const unsigned original_number_of_edges = graph.GetNumberOfEdges(); RestrictionCompressor restriction_compressor( turn_restrictions, conditional_turn_restrictions, maneuver_overrides); // we do not compress turn restrictions on degree two nodes. These nodes are usually used to // indicated `directed` barriers std::unordered_set<NodeID> restriction_via_nodes; const auto remember_via_nodes = [&](const auto &restriction) { if (restriction.Type() == RestrictionType::NODE_RESTRICTION) { const auto &node = restriction.AsNodeRestriction(); restriction_via_nodes.insert(node.via); } else { BOOST_ASSERT(restriction.Type() == RestrictionType::WAY_RESTRICTION); const auto &way = restriction.AsWayRestriction(); restriction_via_nodes.insert(way.in_restriction.via); restriction_via_nodes.insert(way.out_restriction.via); } }; std::for_each(turn_restrictions.begin(), turn_restrictions.end(), remember_via_nodes); std::for_each(conditional_turn_restrictions.begin(), conditional_turn_restrictions.end(), remember_via_nodes); { const auto weight_multiplier = scripting_environment.GetProfileProperties().GetWeightMultiplier(); util::UnbufferedLog log; util::Percent progress(log, original_number_of_nodes); for (const NodeID node_v : util::irange(0u, original_number_of_nodes)) { progress.PrintStatus(node_v); // only contract degree 2 vertices if (2 != graph.GetOutDegree(node_v)) { continue; } // don't contract barrier node if (barrier_nodes.end() != barrier_nodes.find(node_v)) { continue; } // check if v is a via node for a turn restriction, i.e. a 'directed' barrier node if (restriction_via_nodes.count(node_v)) { continue; } // reverse_e2 forward_e2 // u <---------- v -----------> w // ----------> <----------- // forward_e1 reverse_e1 // // Will be compressed to: // // reverse_e1 // u <---------- w // ----------> // forward_e1 // // If the edges are compatible. const bool reverse_edge_order = graph.GetEdgeData(graph.BeginEdges(node_v)).reversed; const EdgeID forward_e2 = graph.BeginEdges(node_v) + reverse_edge_order; BOOST_ASSERT(SPECIAL_EDGEID != forward_e2); BOOST_ASSERT(forward_e2 >= graph.BeginEdges(node_v) && forward_e2 < graph.EndEdges(node_v)); const EdgeID reverse_e2 = graph.BeginEdges(node_v) + 1 - reverse_edge_order; BOOST_ASSERT(SPECIAL_EDGEID != reverse_e2); BOOST_ASSERT(reverse_e2 >= graph.BeginEdges(node_v) && reverse_e2 < graph.EndEdges(node_v)); const EdgeData &fwd_edge_data2 = graph.GetEdgeData(forward_e2); const EdgeData &rev_edge_data2 = graph.GetEdgeData(reverse_e2); const NodeID node_w = graph.GetTarget(forward_e2); BOOST_ASSERT(SPECIAL_NODEID != node_w); BOOST_ASSERT(node_v != node_w); const NodeID node_u = graph.GetTarget(reverse_e2); BOOST_ASSERT(SPECIAL_NODEID != node_u); BOOST_ASSERT(node_u != node_v); const EdgeID forward_e1 = graph.FindEdge(node_u, node_v); BOOST_ASSERT(SPECIAL_EDGEID != forward_e1); BOOST_ASSERT(node_v == graph.GetTarget(forward_e1)); const EdgeID reverse_e1 = graph.FindEdge(node_w, node_v); BOOST_ASSERT(SPECIAL_EDGEID != reverse_e1); BOOST_ASSERT(node_v == graph.GetTarget(reverse_e1)); const EdgeData &fwd_edge_data1 = graph.GetEdgeData(forward_e1); const EdgeData &rev_edge_data1 = graph.GetEdgeData(reverse_e1); const auto fwd_annotation_data1 = node_data_container[fwd_edge_data1.annotation_data]; const auto fwd_annotation_data2 = node_data_container[fwd_edge_data2.annotation_data]; const auto rev_annotation_data1 = node_data_container[rev_edge_data1.annotation_data]; const auto rev_annotation_data2 = node_data_container[rev_edge_data2.annotation_data]; if (graph.FindEdgeInEitherDirection(node_u, node_w) != SPECIAL_EDGEID) { continue; } // this case can happen if two ways with different names overlap if ((fwd_annotation_data1.name_id != rev_annotation_data1.name_id) || (fwd_annotation_data2.name_id != rev_annotation_data2.name_id)) { continue; } if ((fwd_edge_data1.flags == fwd_edge_data2.flags) && (rev_edge_data1.flags == rev_edge_data2.flags) && (fwd_edge_data1.reversed == fwd_edge_data2.reversed) && (rev_edge_data1.reversed == rev_edge_data2.reversed) && // annotations need to match, except for the lane-id which can differ fwd_annotation_data1.CanCombineWith(fwd_annotation_data2) && rev_annotation_data1.CanCombineWith(rev_annotation_data2)) { BOOST_ASSERT(!(graph.GetEdgeData(forward_e1).reversed && graph.GetEdgeData(reverse_e1).reversed)); /* * Remember Lane Data for compressed parts. This handles scenarios where lane-data * is * only kept up until a traffic light. * * | | * ---------------- | * -^ | | * ----------- | * -v | | * --------------- | * | | * * u ------- v ---- w * * Since the edge is compressable, we can transfer: * "left|right" (uv) and "" (uw) into a string with "left|right" (uw) for the * compressed * edge. * Doing so, we might mess up the point from where the lanes are shown. It should be * reasonable, since the announcements have to come early anyhow. So there is a * potential danger in here, but it saves us from adding a lot of additional edges * for * turn-lanes. Without this,we would have to treat any turn-lane beginning/ending * just * like a barrier. */ const auto selectAnnotation = [&node_data_container]( const AnnotationID front_annotation, const AnnotationID back_annotation) { // A lane has tags: u - (front) - v - (back) - w // During contraction, we keep only one of the tags. Usually the one closer // to the intersection is preferred. If its empty, however, we keep the // non-empty one if (node_data_container[back_annotation].lane_description_id == INVALID_LANE_DESCRIPTIONID) return front_annotation; return back_annotation; }; graph.GetEdgeData(forward_e1).annotation_data = selectAnnotation( fwd_edge_data1.annotation_data, fwd_edge_data2.annotation_data); graph.GetEdgeData(reverse_e1).annotation_data = selectAnnotation( rev_edge_data1.annotation_data, rev_edge_data2.annotation_data); graph.GetEdgeData(forward_e2).annotation_data = selectAnnotation( fwd_edge_data2.annotation_data, fwd_edge_data1.annotation_data); graph.GetEdgeData(reverse_e2).annotation_data = selectAnnotation( rev_edge_data2.annotation_data, rev_edge_data1.annotation_data); /* // Do not compress edge if it crosses a traffic signal. // This can't be done in CanCombineWith, becase we only store the // traffic signals in the `traffic signal` list, which EdgeData // doesn't have access to. */ const bool has_node_penalty = traffic_signals.find(node_v) != traffic_signals.end(); EdgeDuration node_duration_penalty = MAXIMAL_EDGE_DURATION; EdgeWeight node_weight_penalty = INVALID_EDGE_WEIGHT; if (has_node_penalty) { // we cannot handle this as node penalty, if it depends on turn direction if (fwd_edge_data1.flags.restricted != fwd_edge_data2.flags.restricted) continue; // generate an artifical turn for the turn penalty generation std::vector<ExtractionTurnLeg> roads_on_the_right; std::vector<ExtractionTurnLeg> roads_on_the_left; ExtractionTurn extraction_turn(0, 2, false, true, false, false, TRAVEL_MODE_DRIVING, false, false, 1, 0, 0, 0, 0, false, TRAVEL_MODE_DRIVING, false, false, 1, 0, 0, 0, 0, roads_on_the_right, roads_on_the_left); scripting_environment.ProcessTurn(extraction_turn); node_duration_penalty = extraction_turn.duration * 10; node_weight_penalty = extraction_turn.weight * weight_multiplier; } // Get weights before graph is modified const auto forward_weight1 = fwd_edge_data1.weight; const auto forward_weight2 = fwd_edge_data2.weight; const auto forward_duration1 = fwd_edge_data1.duration; const auto forward_duration2 = fwd_edge_data2.duration; const auto forward_distance2 = fwd_edge_data2.distance; BOOST_ASSERT(0 != forward_weight1); BOOST_ASSERT(0 != forward_weight2); const auto reverse_weight1 = rev_edge_data1.weight; const auto reverse_weight2 = rev_edge_data2.weight; const auto reverse_duration1 = rev_edge_data1.duration; const auto reverse_duration2 = rev_edge_data2.duration; const auto reverse_distance2 = rev_edge_data2.distance; #ifndef NDEBUG // Because distances are symmetrical, we only need one // per edge - here we double-check that they match // their mirrors. const auto reverse_distance1 = rev_edge_data1.distance; const auto forward_distance1 = fwd_edge_data1.distance; BOOST_ASSERT(forward_distance1 == reverse_distance2); BOOST_ASSERT(forward_distance2 == reverse_distance1); #endif BOOST_ASSERT(0 != reverse_weight1); BOOST_ASSERT(0 != reverse_weight2); // add weight of e2's to e1 graph.GetEdgeData(forward_e1).weight += forward_weight2; graph.GetEdgeData(reverse_e1).weight += reverse_weight2; // add duration of e2's to e1 graph.GetEdgeData(forward_e1).duration += forward_duration2; graph.GetEdgeData(reverse_e1).duration += reverse_duration2; // add distance of e2's to e1 graph.GetEdgeData(forward_e1).distance += forward_distance2; graph.GetEdgeData(reverse_e1).distance += reverse_distance2; if (node_weight_penalty != INVALID_EDGE_WEIGHT && node_duration_penalty != MAXIMAL_EDGE_DURATION) { graph.GetEdgeData(forward_e1).weight += node_weight_penalty; graph.GetEdgeData(reverse_e1).weight += node_weight_penalty; graph.GetEdgeData(forward_e1).duration += node_duration_penalty; graph.GetEdgeData(reverse_e1).duration += node_duration_penalty; // Note: no penalties for distances } // extend e1's to targets of e2's graph.SetTarget(forward_e1, node_w); graph.SetTarget(reverse_e1, node_u); // remove e2's (if bidir, otherwise only one) graph.DeleteEdge(node_v, forward_e2); graph.DeleteEdge(node_v, reverse_e2); // update any involved turn restrictions restriction_compressor.Compress(node_u, node_v, node_w); // store compressed geometry in container geometry_compressor.CompressEdge(forward_e1, forward_e2, node_v, node_w, forward_weight1, forward_weight2, forward_duration1, forward_duration2, node_weight_penalty, node_duration_penalty); geometry_compressor.CompressEdge(reverse_e1, reverse_e2, node_v, node_u, reverse_weight1, reverse_weight2, reverse_duration1, reverse_duration2, node_weight_penalty, node_duration_penalty); } } } PrintStatistics(original_number_of_nodes, original_number_of_edges, graph); // Repeate the loop, but now add all edges as uncompressed values. // The function AddUncompressedEdge does nothing if the edge is already // in the CompressedEdgeContainer. for (const NodeID node_u : util::irange(0u, original_number_of_nodes)) { for (const auto edge_id : util::irange(graph.BeginEdges(node_u), graph.EndEdges(node_u))) { const EdgeData &data = graph.GetEdgeData(edge_id); const NodeID target = graph.GetTarget(edge_id); geometry_compressor.AddUncompressedEdge(edge_id, target, data.weight, data.duration); } } }
/// Actually it also generates OriginalEdgeData and serializes them... void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges( ScriptingEnvironment &scripting_environment, const std::string &original_edge_data_filename, const std::string &turn_lane_data_filename, const std::string &edge_segment_lookup_filename, const std::string &edge_fixed_penalties_filename, const bool generate_edge_lookup) { util::SimpleLogger().Write() << "generating edge-expanded edges"; std::size_t node_based_edge_counter = 0; std::size_t original_edges_counter = 0; restricted_turns_counter = 0; skipped_uturns_counter = 0; skipped_barrier_turns_counter = 0; std::ofstream edge_data_file(original_edge_data_filename.c_str(), std::ios::binary); std::ofstream edge_segment_file; std::ofstream edge_penalty_file; if (generate_edge_lookup) { edge_segment_file.open(edge_segment_lookup_filename.c_str(), std::ios::binary); edge_penalty_file.open(edge_fixed_penalties_filename.c_str(), std::ios::binary); } // Writes a dummy value at the front that is updated later with the total length const unsigned length_prefix_empty_space{0}; edge_data_file.write(reinterpret_cast<const char *>(&length_prefix_empty_space), sizeof(length_prefix_empty_space)); std::vector<OriginalEdgeData> original_edge_data_vector; original_edge_data_vector.reserve(1024 * 1024); // Loop over all turns and generate new set of edges. // Three nested loop look super-linear, but we are dealing with a (kind of) // linear number of turns only. util::Percent progress(m_node_based_graph->GetNumberOfNodes()); SuffixTable street_name_suffix_table(scripting_environment); guidance::TurnAnalysis turn_analysis(*m_node_based_graph, m_node_info_list, *m_restriction_map, m_barrier_nodes, m_compressed_edge_container, name_table, street_name_suffix_table); guidance::lanes::TurnLaneHandler turn_lane_handler( *m_node_based_graph, turn_lane_offsets, turn_lane_masks, m_node_info_list, turn_analysis); bearing_class_by_node_based_node.resize(m_node_based_graph->GetNumberOfNodes(), std::numeric_limits<std::uint32_t>::max()); guidance::LaneDataIdMap lane_data_map; for (const auto node_u : util::irange(0u, m_node_based_graph->GetNumberOfNodes())) { progress.PrintStatus(node_u); for (const EdgeID edge_from_u : m_node_based_graph->GetAdjacentEdgeRange(node_u)) { if (m_node_based_graph->GetEdgeData(edge_from_u).reversed) { continue; } const NodeID node_v = m_node_based_graph->GetTarget(edge_from_u); ++node_based_edge_counter; auto intersection = turn_analysis.getIntersection(node_u, edge_from_u); intersection = turn_analysis.assignTurnTypes(node_u, edge_from_u, std::move(intersection)); intersection = turn_lane_handler.assignTurnLanes( node_u, edge_from_u, std::move(intersection), lane_data_map); const auto possible_turns = turn_analysis.transformIntersectionIntoTurns(intersection); // the entry class depends on the turn, so we have to classify the interesction for // every edge const auto turn_classification = classifyIntersection(node_v, intersection, *m_node_based_graph, m_compressed_edge_container, m_node_info_list); const auto entry_class_id = [&](const util::guidance::EntryClass entry_class) { if (0 == entry_class_hash.count(entry_class)) { const auto id = static_cast<std::uint16_t>(entry_class_hash.size()); entry_class_hash[entry_class] = id; return id; } else { return entry_class_hash.find(entry_class)->second; } }(turn_classification.first); const auto bearing_class_id = [&](const util::guidance::BearingClass bearing_class) { if (0 == bearing_class_hash.count(bearing_class)) { const auto id = static_cast<std::uint32_t>(bearing_class_hash.size()); bearing_class_hash[bearing_class] = id; return id; } else { return bearing_class_hash.find(bearing_class)->second; } }(turn_classification.second); bearing_class_by_node_based_node[node_v] = bearing_class_id; for (const auto turn : possible_turns) { // only add an edge if turn is not prohibited const EdgeData &edge_data1 = m_node_based_graph->GetEdgeData(edge_from_u); const EdgeData &edge_data2 = m_node_based_graph->GetEdgeData(turn.eid); BOOST_ASSERT(edge_data1.edge_id != edge_data2.edge_id); BOOST_ASSERT(!edge_data1.reversed); BOOST_ASSERT(!edge_data2.reversed); // the following is the core of the loop. unsigned distance = edge_data1.distance; if (m_traffic_lights.find(node_v) != m_traffic_lights.end()) { distance += profile_properties.traffic_signal_penalty; } const int32_t turn_penalty = scripting_environment.GetTurnPenalty(180. - turn.angle); const auto turn_instruction = turn.instruction; if (guidance::isUturn(turn_instruction)) { distance += profile_properties.u_turn_penalty; } distance += turn_penalty; BOOST_ASSERT(m_compressed_edge_container.HasEntryForID(edge_from_u)); original_edge_data_vector.emplace_back( m_compressed_edge_container.GetPositionForID(edge_from_u), edge_data1.name_id, turn.lane_data_id, turn_instruction, entry_class_id, edge_data1.travel_mode); ++original_edges_counter; if (original_edge_data_vector.size() > 1024 * 1024 * 10) { FlushVectorToStream(edge_data_file, original_edge_data_vector); } BOOST_ASSERT(SPECIAL_NODEID != edge_data1.edge_id); BOOST_ASSERT(SPECIAL_NODEID != edge_data2.edge_id); // NOTE: potential overflow here if we hit 2^32 routable edges BOOST_ASSERT(m_edge_based_edge_list.size() <= std::numeric_limits<NodeID>::max()); m_edge_based_edge_list.emplace_back(edge_data1.edge_id, edge_data2.edge_id, m_edge_based_edge_list.size(), distance, true, false); // Here is where we write out the mapping between the edge-expanded edges, and // the node-based edges that are originally used to calculate the `distance` // for the edge-expanded edges. About 40 lines back, there is: // // unsigned distance = edge_data1.distance; // // This tells us that the weight for an edge-expanded-edge is based on the weight // of the *source* node-based edge. Therefore, we will look up the individual // segments of the source node-based edge, and write out a mapping between // those and the edge-based-edge ID. // External programs can then use this mapping to quickly perform // updates to the edge-expanded-edge based directly on its ID. if (generate_edge_lookup) { const auto node_based_edges = m_compressed_edge_container.GetBucketReference(edge_from_u); NodeID previous = node_u; const unsigned node_count = node_based_edges.size() + 1; const QueryNode &first_node = m_node_info_list[previous]; lookup::SegmentHeaderBlock header = {node_count, first_node.node_id}; edge_segment_file.write(reinterpret_cast<const char *>(&header), sizeof(header)); for (auto target_node : node_based_edges) { const QueryNode &from = m_node_info_list[previous]; const QueryNode &to = m_node_info_list[target_node.node_id]; const double segment_length = util::coordinate_calculation::greatCircleDistance(from, to); lookup::SegmentBlock nodeblock = { to.node_id, segment_length, target_node.weight}; edge_segment_file.write(reinterpret_cast<const char *>(&nodeblock), sizeof(nodeblock)); previous = target_node.node_id; } // We also now write out the mapping between the edge-expanded edges and the // original nodes. Since each edge represents a possible maneuver, external // programs can use this to quickly perform updates to edge weights in order // to penalize certain turns. // If this edge is 'trivial' -- where the compressed edge corresponds // exactly to an original OSM segment -- we can pull the turn's preceding // node ID directly with `node_u`; otherwise, we need to look up the node // immediately preceding the turn from the compressed edge container. const bool isTrivial = m_compressed_edge_container.IsTrivial(edge_from_u); const auto &from_node = isTrivial ? m_node_info_list[node_u] : m_node_info_list[m_compressed_edge_container.GetLastEdgeSourceID( edge_from_u)]; const auto &via_node = m_node_info_list[m_compressed_edge_container.GetLastEdgeTargetID( edge_from_u)]; const auto &to_node = m_node_info_list[m_compressed_edge_container.GetFirstEdgeTargetID( turn.eid)]; const unsigned fixed_penalty = distance - edge_data1.distance; lookup::PenaltyBlock penaltyblock = { fixed_penalty, from_node.node_id, via_node.node_id, to_node.node_id}; edge_penalty_file.write(reinterpret_cast<const char *>(&penaltyblock), sizeof(penaltyblock)); } } } } util::SimpleLogger().Write() << "Created " << entry_class_hash.size() << " entry classes and " << bearing_class_hash.size() << " Bearing Classes"; util::SimpleLogger().Write() << "Writing Turn Lane Data to File..."; std::ofstream turn_lane_data_file(turn_lane_data_filename.c_str(), std::ios::binary); std::vector<util::guidance::LaneTupelIdPair> lane_data(lane_data_map.size()); // extract lane data sorted by ID for (auto itr : lane_data_map) lane_data[itr.second] = itr.first; std::uint64_t size = lane_data.size(); turn_lane_data_file.write(reinterpret_cast<const char *>(&size), sizeof(size)); if (!lane_data.empty()) turn_lane_data_file.write(reinterpret_cast<const char *>(&lane_data[0]), sizeof(util::guidance::LaneTupelIdPair) * lane_data.size()); util::SimpleLogger().Write() << "done."; FlushVectorToStream(edge_data_file, original_edge_data_vector); // Finally jump back to the empty space at the beginning and write length prefix edge_data_file.seekp(std::ios::beg); const auto length_prefix = boost::numeric_cast<unsigned>(original_edges_counter); static_assert(sizeof(length_prefix_empty_space) == sizeof(length_prefix), "type mismatch"); edge_data_file.write(reinterpret_cast<const char *>(&length_prefix), sizeof(length_prefix)); util::SimpleLogger().Write() << "Generated " << m_edge_based_node_list.size() << " edge based nodes"; util::SimpleLogger().Write() << "Node-based graph contains " << node_based_edge_counter << " edges"; util::SimpleLogger().Write() << "Edge-expanded graph ..."; util::SimpleLogger().Write() << " contains " << m_edge_based_edge_list.size() << " edges"; util::SimpleLogger().Write() << " skips " << restricted_turns_counter << " turns, " "defined by " << m_restriction_map->size() << " restrictions"; util::SimpleLogger().Write() << " skips " << skipped_uturns_counter << " U turns"; util::SimpleLogger().Write() << " skips " << skipped_barrier_turns_counter << " turns over barriers"; }