0

I am looking for a way to include turn restrictions and/or turn penalties in my application of Boost Graph Library, using a directed graph. I need to restrict for instance U-turns (which can be a very high penalty if that is the best way).

I cannot find an explicit mechanism in Boost Graph to add such penalties / restrictions, what would be the best way to deal with turn restrictions?

The only solution I see so far is expanding the graph with 'internal edges' for each possible turn in vertices, omitting 'internal edges' for restricted turns. This seems very brute force to me, is there a better way?

Example graph: enter image description here

Assume there are two sources A and E, and the shortest path to each of the vertices is calculated using Dijkstra (see code example below).

The two shortest path trees are (output of printShortestPath() ):

Shortest path from A to all vertices: 
distances and parents:
distance(A) = 0, parent(A) = A
distance(B) = 100, parent(B) = A
distance(C) = 200, parent(C) = B
distance(D) = 200, parent(D) = B
distance(E) = 1.79769e+308, parent(E) = E
-------------
Shortest path from E to all vertices: 
distances and parents:
distance(A) = 1.79769e+308, parent(A) = A
distance(B) = 100, parent(B) = E
distance(C) = 200, parent(C) = B
distance(D) = 200, parent(D) = B
distance(E) = 0, parent(E) = E

What is an elegant way to prohibit this turning (e1 to e4) in Boost::Graph?

A straightforward solution would be expanding the graph to have additional edges representing turning movements (with high cost for prohibited ones), but I hope there is a more elegant way.

Code:

#include <boost/config.hpp>
#include <iostream>
#include <boost/graph/graph_traits.hpp>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/dijkstra_shortest_paths.hpp>
#include <QString>
#include <vector>

using namespace boost;

struct GraphNode
{
    GraphNode() {}
    GraphNode(const QString& name_):name(name_) {}
    QString name;
};

struct GraphLink
{
    GraphLink() {}
    GraphLink(const QString& name_, double length_, double travelTime_):name(name_),length(length_), travelTime(travelTime_) {}
    QString name;
    double length; // in meters
    double travelTime; // in seconds
};

typedef boost::adjacency_list<boost::listS, boost::vecS, boost::directedS, GraphNode, GraphLink> NetworkGraph;
typedef graph_traits < NetworkGraph >::vertex_descriptor vertex_descriptor;
typedef std::vector<decltype (GraphLink::length)> distancesT;

void printShortestPath (const NetworkGraph& map, distancesT distances,std::vector<vertex_descriptor> p )
{
    std::cout << "distances and parents:" << std::endl;
    graph_traits < NetworkGraph >::vertex_iterator vi, vend;
    for (auto vs=vertices(map); vs.first!=vs.second;++vs.first)
    {
        auto currEdge=map[*vs.first];
        auto parent=p[*vs.first];
        QString parentName=map[parent].name;
              QString name =currEdge.name;
              std::cout << "distance(" << name.toStdString() << ") = " << distances[*vs.first] << ", ";
              std::cout << "parent(" << name.toStdString() << ") = " << parentName.toStdString() << std::
                endl;
    }

}


int main(int, char *[])
{
    //Create the vertices

    NetworkGraph map;
    NetworkGraph::vertex_descriptor vertexA = add_vertex(map);
    map[vertexA].name ="A";
    NetworkGraph::vertex_descriptor vertexB = add_vertex(map);
    map[vertexB].name ="B";
    NetworkGraph::vertex_descriptor vertexC = add_vertex(map);
    map[vertexC].name ="C";
    NetworkGraph::vertex_descriptor vertexD = add_vertex(map);
    map[vertexD].name ="D";
    NetworkGraph::vertex_descriptor vertexE = add_vertex(map);
    map[vertexE].name ="E";

    // Create the edges

    NetworkGraph::edge_descriptor e1 = add_edge(vertexA,vertexB,map).first;
    map[e1].name ="e1";
    map[e1].length =100.0;

    NetworkGraph::edge_descriptor e2 = add_edge(vertexB,vertexC,map).first;
    map[e2].name ="e2";
    map[e2].length =100.0;

    NetworkGraph::edge_descriptor e3 = add_edge(vertexC,vertexD,map).first;
    map[e3].name ="e3";
    map[e3].length =100.0;

    NetworkGraph::edge_descriptor e4 = add_edge(vertexB,vertexD,map).first;
    map[e4].name ="e4";
    map[e4].length =100.0;

    NetworkGraph::edge_descriptor e5 = add_edge(vertexE,vertexB,map).first;
    map[e5].name ="e5";
    map[e5].length =100.0;

    distancesT distances (num_vertices(map)); // distances
    std::vector<vertex_descriptor> p (num_vertices(map)); // parents

    //calculate shortest paths from vertex A using Dijkstra, based on GraphLink::length as edge cost
    dijkstra_shortest_paths (map,
                             vertexA,
                             weight_map (get(&GraphLink::length, map))
                                .distance_map (make_iterator_property_map(distances.begin(), get(vertex_index, map)))
                                .predecessor_map (make_iterator_property_map(p.begin(), get(vertex_index, map))) );

    std::cout << "Shortest path from A to all vertices: " << std::endl;
    printShortestPath (map, distances, p);

    //calculate shortest paths from vertex E using Dijkstra, based on GraphLink::length as edge cost
    dijkstra_shortest_paths (map,
                             vertexE,
                             weight_map (get(&GraphLink::length, map))
                                .distance_map (make_iterator_property_map(distances.begin(), get(vertex_index, map)))
                                .predecessor_map (make_iterator_property_map(p.begin(), get(vertex_index, map))) );

    std::cout << "-------------" << std::endl;
    std::cout << "Shortest path from E to all vertices: " << std::endl;
    printShortestPath (map, distances, p);
    return EXIT_SUCCESS;
}
WiBu
  • 34
  • 6

1 Answers1

0

Since there are no answers forthcoming yet, I will post a default answer, that I hope someone can improve upon.

![enter image description here

In the graph above, vertex B has been replaced by vertices B1...B4

For the allowed turning movements, 'internal' edges have been added, and for the disallowed movements they have been omitted. In the case of turning penalties for all movements, this would expand the graph by nrIncoming*nrOutgoing edges for each node.

Resulting shortest paths from source A resp. E:

Shortest path from A to all vertices: 
distances and parents:
distance(A) = 0, parent(A) = A
distance(B1) = 100, parent(B1) = A
distance(B2) = 1.79769e+308, parent(B2) = B2
distance(B3) = 100, parent(B3) = B1
distance(B4) = 1.79769e+308, parent(B4) = B4
distance(C) = 200, parent(C) = B3
distance(D) = 300, parent(D) = C
distance(E) = 1.79769e+308, parent(E) = E
-------------
Shortest path from E to all vertices: 
distances and parents:
distance(A) = 1.79769e+308, parent(A) = A
distance(B1) = 1.79769e+308, parent(B1) = B1
distance(B2) = 100, parent(B2) = E
distance(B3) = 100, parent(B3) = B2
distance(B4) = 100, parent(B4) = B2
distance(C) = 200, parent(C) = B3
distance(D) = 200, parent(D) = B4
distance(E) = 0, parent(E) = E

Now shortest path from A to D is via e1-> e1e2 -> e2 -> e3, with cost 300.

code:

#include <boost/config.hpp>
#include <iostream>
#include <boost/graph/graph_traits.hpp>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/dijkstra_shortest_paths.hpp>
#include <QString>
#include <vector>

using namespace boost;

struct GraphNode
{
    GraphNode() {}
    GraphNode(const QString& name_):name(name_) {}
    QString name;
};

struct GraphLink
{
    GraphLink() {}
    GraphLink(const QString& name_, double length_, double travelTime_):name(name_),length(length_), travelTime(travelTime_) {}
    QString name;
    double length; // in meters
    double travelTime; // in seconds
};

typedef boost::adjacency_list<boost::listS, boost::vecS, boost::directedS, GraphNode, GraphLink> NetworkGraph;
typedef graph_traits < NetworkGraph >::vertex_descriptor vertex_descriptor;
typedef std::vector<decltype (GraphLink::length)> distancesT;

void printShortestPath (const NetworkGraph& map, distancesT distances,std::vector<vertex_descriptor> p )
{
    std::cout << "distances and parents:" << std::endl;
    graph_traits < NetworkGraph >::vertex_iterator vi, vend;
    for (auto vs=vertices(map); vs.first!=vs.second;++vs.first)
    {
        auto currEdge=map[*vs.first];
        auto parent=p[*vs.first];
        QString parentName=map[parent].name;
              QString name =currEdge.name;
              std::cout << "distance(" << name.toStdString() << ") = " << distances[*vs.first] << ", ";
              std::cout << "parent(" << name.toStdString() << ") = " << parentName.toStdString() << std::
                endl;
    }

}


int main(int, char *[])
{
    //Create the vertices

    NetworkGraph map;
    NetworkGraph::vertex_descriptor vertexA = add_vertex(map);
    map[vertexA].name ="A";
    //NetworkGraph::vertex_descriptor vertexB = add_vertex(map);
    //map[vertexB].name ="B";
    // Instead of vertex B we will create vertex B1...B4
    NetworkGraph::vertex_descriptor vertexB1 = add_vertex(map);
    map[vertexB1].name ="B1";
    NetworkGraph::vertex_descriptor vertexB2 = add_vertex(map);
    map[vertexB2].name ="B2";
    NetworkGraph::vertex_descriptor vertexB3 = add_vertex(map);
    map[vertexB3].name ="B3";
    NetworkGraph::vertex_descriptor vertexB4 = add_vertex(map);
    map[vertexB4].name ="B4";

    NetworkGraph::vertex_descriptor vertexC = add_vertex(map);
    map[vertexC].name ="C";
    NetworkGraph::vertex_descriptor vertexD = add_vertex(map);
    map[vertexD].name ="D";
    NetworkGraph::vertex_descriptor vertexE = add_vertex(map);
    map[vertexE].name ="E";

    // Create the edges

    NetworkGraph::edge_descriptor e1 = add_edge(vertexA,vertexB1,map).first;
    map[e1].name ="e1";
    map[e1].length =100.0;

    NetworkGraph::edge_descriptor e2 = add_edge(vertexB3,vertexC,map).first;
    map[e2].name ="e2";
    map[e2].length =100.0;

    NetworkGraph::edge_descriptor e3 = add_edge(vertexC,vertexD,map).first;
    map[e3].name ="e3";
    map[e3].length =100.0;

    NetworkGraph::edge_descriptor e4 = add_edge(vertexB4,vertexD,map).first;
    map[e4].name ="e4";
    map[e4].length =100.0;

    NetworkGraph::edge_descriptor e5 = add_edge(vertexE,vertexB2,map).first;
    map[e5].name ="e5";
    map[e5].length =100.0;

    // add internal edges with 0.0 length
    NetworkGraph::edge_descriptor e1e2 = add_edge(vertexB1,vertexB3,map).first;
    map[e1e2].name ="e1-e2";
    map[e1e2].length =0.0;

    NetworkGraph::edge_descriptor e5e4 = add_edge(vertexB2,vertexB4,map).first;
    map[e5e4].name ="e5-e4";
    map[e5e4].length =0.0;

    NetworkGraph::edge_descriptor e5e2 = add_edge(vertexB2,vertexB3,map).first;
    map[e5e2].name ="e5-e2";
    map[e5e2].length =0.0;

    distancesT distances (num_vertices(map)); // distances
    std::vector<vertex_descriptor> p (num_vertices(map)); // parents

    //calculate shortest paths from vertex A using Dijkstra, based on GraphLink::length as edge cost
    dijkstra_shortest_paths (map,
                             vertexA,
                             weight_map (get(&GraphLink::length, map))
                                .distance_map (make_iterator_property_map(distances.begin(), get(vertex_index, map)))
                                .predecessor_map (make_iterator_property_map(p.begin(), get(vertex_index, map))) );

    std::cout << "Shortest path from A to all vertices: " << std::endl;
    printShortestPath (map, distances, p);

    //calculate shortest paths from vertex E using Dijkstra, based on GraphLink::length as edge cost
    dijkstra_shortest_paths (map,
                             vertexE,
                             weight_map (get(&GraphLink::length, map))
                                .distance_map (make_iterator_property_map(distances.begin(), get(vertex_index, map)))
                                .predecessor_map (make_iterator_property_map(p.begin(), get(vertex_index, map))) );

    std::cout << "-------------" << std::endl;
    std::cout << "Shortest path from E to all vertices: " << std::endl;
    printShortestPath (map, distances, p);
    return EXIT_SUCCESS;
}
WiBu
  • 34
  • 6