07_a_graph_route_example.cpp

The graph route example shows how GGL can be combined with Boost.Graph. The sample does the following things:

- it reads roads (included in the distribution, stored on disk in the form of a text file containing geometries and names)

- it reads cities

- it creates a graph from the roads

- it connects each city to the nearest vertex in the graph

- it calculates the shortest route between each pair of cities

- it outputs the distance over the road, and also of the air

- it creates an SVG image with the roads, the cities, and the first calculated route

Note that this example is useful, but it is only an example. It could be built in many different ways. For example:

- the roads/cities could be read from a database using SOCI, or from a shapefile using shapelib

- it could support oneway roads and roads on different levels (disconnected bridges)

- it currently uses tuples but that could be anything

- etc

The SVG looks like:

The output screen looks like:

    //Boost.Geometry(akaGGL,GenericGeometryLibrary)

    //Copyright(c)2007-2012BarendGehrels,Amsterdam,theNetherlands.
    //Copyright(c)2008-2012BrunoLalande,Paris,France.
    //Copyright(c)2009-2012MateuszLoskot,London,UK.

    //Use,modificationanddistributionissubjecttotheBoostSoftwareLicense,
    //Version1.0.(SeeaccompanyingfileLICENSE_1_0.txtorcopyat
    //http://www.boost.org/LICENSE_1_0.txt)
    //
    //ExampleshowingBoost.GeometrycombinedwithBoost.Graph,calculatingshortestroutes
    //input:twoWKT's,providedinsubfolderdata
    //output:text,+anSVG,displayableine.g.Firefox)

    #include<iostream>
    #include<fstream>
    #include<iomanip>
    #include<limits>

    #include<boost/tuple/tuple.hpp>
    #include<boost/foreach.hpp>

    #include<boost/graph/adjacency_list.hpp>
    #include<boost/graph/dijkstra_shortest_paths.hpp>

    #include<boost/geometry/geometry.hpp>
    #include<boost/geometry/geometries/linestring.hpp>
    #include<boost/geometry/io/wkt/read.hpp>

    //Foroutput:
    #include<boost/geometry/io/svg/svg_mapper.hpp>

    //Fordistance-calculationsovertheEarth:
    //#include<boost/geometry/extensions/gis/geographic/strategies/andoyer.hpp>

    //ReadanASCIIfilecontainingWKT's,fillavectoroftuples
    //Thetuplesconsistofatleast<0>ageometryand<1>anidentifyingstring
    template<typenameGeometry,typenameTuple,typenameBox>
    voidread_wkt(std::stringconst&filename,std::vector<Tuple>&tuples,Box&box)
    {
    std::ifstreamcpp_file(filename.c_str());
    if(cpp_file.is_open())
    {
    while(!cpp_file.eof())
    {
    std::stringline;
    std::getline(cpp_file,line);
    Geometrygeometry;
    boost::trim(line);
    if(!line.empty()&&!boost::starts_with(line,"#"))
    {
    std::stringname;

    //Splitat';',ifany
    std::string::size_typepos=line.find(";");
    if(pos!=std::string::npos)
    {
    name=line.substr(pos+1);
    line.erase(pos);

    boost::trim(line);
    boost::trim(name);
    }

    Geometrygeometry;
    boost::geometry::read_wkt(line,geometry);

    Tupletuple(geometry,name);

    tuples.push_back(tuple);
    boost::geometry::expand(box,boost::geometry::return_envelope<Box>(geometry));
    }
    }
    }
    }

    //CodetodefinepropertiesforBoostGraph's
    enumvertex_bg_property_t{vertex_bg_property};
    enumedge_bg_property_t{edge_bg_property};
    namespaceboost
    {
    BOOST_INSTALL_PROPERTY(vertex,bg_property);
    BOOST_INSTALL_PROPERTY(edge,bg_property);
    }

    //Tocalculatedistance,declareandconstructastrategywithaverageearthradius
    boost::geometry::strategy::distance::haversine<double>consthaversine(6372795.0);

    //Definepropertiesforvertex
    template<typenamePoint>
    structbg_vertex_property
    {
    bg_vertex_property()
    {
    boost::geometry::assign_zero(location);
    }
    bg_vertex_property(Pointconst&loc)
    :location(loc)
    {
    }

    Pointlocation;
    };

    //Definepropertiesforedge
    template<typenameLinestring>
    structbg_edge_property
    {
    bg_edge_property(Linestringconst&line)
    :m_length(boost::geometry::length(line,haversine))
    ,m_line(line)
    {
    }

    inlineoperatordouble()const
    {
    returnm_length;
    }

    inlineLinestringconst&line()const
    {
    returnm_line;
    }

    private:
    doublem_length;
    Linestringm_line;
    };

    //Utilityfunctiontoaddavertextoagraph.Itmightexistalready.Thendonotinsert,
    //butreturnvertexdescriptorback.Itmightnotexist.Thenaddit(andreturn).
    //Toefficientlyhandlethis,astd::mapisused.
    template<typenameM,typenameK,typenameG>
    inlinetypenameboost::graph_traits<G>::vertex_descriptorfind_or_insert(M&map,Kconst&key,G&graph)
    {
    typenameM::const_iteratorit=map.find(key);
    if(it==map.end())
    {
    //Addavertextothegraph
    typenameboost::graph_traits<G>::vertex_descriptornew_vertex
    =boost::add_vertex(graph);

    //Settheproperty(=location)
    boost::put(boost::get(vertex_bg_property,graph),new_vertex,
    bg_vertex_property<typenameM::key_type>(key));

    //Addtothemap,usingPOINTaskey
    map[key]=new_vertex;
    returnnew_vertex;
    }
    returnit->second;
    }

    template
    <
    typenameGraph,
    typenameRoadTupleVector,
    typenameCityTupleVector
    >
    voidadd_roads_and_connect_cities(Graph&graph,
    RoadTupleVectorconst&roads,
    CityTupleVector&cities)
    {
    typedeftypenameboost::range_value<RoadTupleVector>::typeroad_type;
    typedeftypenameboost::tuples::element<0,road_type>::typeline_type;
    typedeftypenameboost::geometry::point_type<line_type>::typepoint_type;

    typedeftypenameboost::graph_traits<Graph>::vertex_descriptorvertex_type;

    //Defineamaptobeusedduringgraphfilling
    //Mapsfrompointtovertex-id's
    typedefstd::map<point_type,vertex_type,boost::geometry::less<point_type>>map_type;
    map_typemap;

    //Fillthegraph
    BOOST_FOREACH(road_typeconst&road,roads)
    {
    line_typeconst&line=road.templateget<0>();
    //Findoraddbegin/endpointoftheseline
    vertex_typefrom=find_or_insert(map,line.front(),graph);
    vertex_typeto=find_or_insert(map,line.back(),graph);
    boost::add_edge(from,to,bg_edge_property<line_type>(line),graph);
    }

    //Findnearestgraphvertexforeachcity,usingthemap
    typedeftypenameboost::range_value<CityTupleVector>::typecity_type;
    BOOST_FOREACH(city_type&city,cities)
    {
    doublemin_distance=1e300;
    for(typenamemap_type::const_iteratorit=map.begin();it!=map.end();++it)
    {
    doubledist=boost::geometry::distance(it->first,city.templateget<0>());
    if(dist<min_distance)
    {
    min_distance=dist;
    //Setthevertex
    city.templateget<2>()=it->second;
    }
    }
    }
    }

    template<typenameGraph,typenameRoute>
    inlinevoidadd_edge_to_route(Graphconst&graph,
    typenameboost::graph_traits<Graph>::vertex_descriptorvertex1,
    typenameboost::graph_traits<Graph>::vertex_descriptorvertex2,
    Route&route)
    {
    std::pair
    <
    typenameboost::graph_traits<Graph>::edge_descriptor,
    bool
    >opt_edge=boost::edge(vertex1,vertex2,graph);
    if(opt_edge.second)
    {
    //Getpropertiesofedgeandofvertex
    bg_edge_property<Route>const&edge_prop=
    boost::get(boost::get(edge_bg_property,graph),opt_edge.first);

    bg_vertex_property<typenameboost::geometry::point_type<Route>::type>const&vertex_prop=
    boost::get(boost::get(vertex_bg_property,graph),vertex2);

    //Dependingonhowedgeconnectstovertex,copyitforwardorbackward
    if(boost::geometry::equals(edge_prop.line().front(),vertex_prop.location))
    {
    std::copy(edge_prop.line().begin(),edge_prop.line().end(),
    std::back_inserter(route));
    }
    else
    {
    std::reverse_copy(edge_prop.line().begin(),edge_prop.line().end(),
    std::back_inserter(route));
    }
    }
    }

    template<typenameGraph,typenameRoute>
    inlinevoidbuild_route(Graphconst&graph,
    std::vector<typenameboost::graph_traits<Graph>::vertex_descriptor>const&predecessors,
    typenameboost::graph_traits<Graph>::vertex_descriptorvertex1,
    typenameboost::graph_traits<Graph>::vertex_descriptorvertex2,
    Route&route)
    {
    typedeftypenameboost::graph_traits<Graph>::vertex_descriptorvertex_type;
    vertex_typepred=predecessors[vertex2];

    add_edge_to_route(graph,vertex2,pred,route);
    while(pred!=vertex1)
    {
    add_edge_to_route(graph,predecessors[pred],pred,route);
    pred=predecessors[pred];
    }
    }

    intmain()
    {
    //DefineapointintheGeographiccoordinatesystem(currentlySpherical)
    //(geographiccalculationsareinanextension;forsampleitmakesnodifference)
    typedefboost::geometry::model::point
    <
    double,2,boost::geometry::cs::spherical_equatorial<boost::geometry::degree>
    >point_type;

    typedefboost::geometry::model::linestring<point_type>line_type;

    //Definethegraph,lateroncontainingtheroadnetwork
    typedefboost::adjacency_list
    <
    boost::vecS,boost::vecS,boost::undirectedS
    ,boost::property<vertex_bg_property_t,bg_vertex_property<point_type>>
    ,boost::property<edge_bg_property_t,bg_edge_property<line_type>>
    >graph_type;

    typedefboost::graph_traits<graph_type>::vertex_descriptorvertex_type;

    //Initaboundingbox,lateronusedtodefineSVGmap
    boost::geometry::model::box<point_type>box;
    boost::geometry::assign_inverse(box);

    //Readthecities
    typedefboost::tuple<point_type,std::string,vertex_type>city_type;
    std::vector<city_type>cities;
    read_wkt<point_type>("data/cities.wkt",cities,box);

    //Readtheroadnetwork
    typedefboost::tuple<line_type,std::string>road_type;
    std::vector<road_type>roads;
    read_wkt<line_type>("data/roads.wkt",roads,box);

    graph_typegraph;

    //Addroadsandconnectcities
    add_roads_and_connect_cities(graph,roads,cities);

    doubleconstkm=1000.0;
    std::cout<<"distances,allinKM"<<std::endl
    <<std::fixed<<std::setprecision(0);

    //Mainfunctionality:calculateshortestroutesfrom/toallcities

    //Forthefirstone,thecompleterouteisstoredasalinestring
    boolfirst=true;
    line_typeroute;

    intconstn=boost::num_vertices(graph);
    BOOST_FOREACH(city_typeconst&city1,cities)
    {
    std::vector<vertex_type>predecessors(n);
    std::vector<double>costs(n);

    //CallDijkstra(withoutnamed-parametertobecompatiblewithallVC)
    boost::dijkstra_shortest_paths(graph,city1.get<2>(),
    &predecessors[0],&costs[0],
    boost::get(edge_bg_property,graph),
    boost::get(boost::vertex_index,graph),
    std::less<double>(),std::plus<double>(),
    (std::numeric_limits<double>::max)(),double(),
    boost::dijkstra_visitor<boost::null_visitor>());

    BOOST_FOREACH(city_typeconst&city2,cities)
    {
    if(!boost::equals(city1.get<1>(),city2.get<1>()))
    {
    doubledistance=costs[city2.get<2>()]/km;
    doubleacof=boost::geometry::distance(city1.get<0>(),city2.get<0>(),haversine)/km;

    std::cout
    <<std::setiosflags(std::ios_base::left)<<std::setw(15)
    <<city1.get<1>()<<"-"
    <<std::setiosflags(std::ios_base::left)<<std::setw(15)
    <<city2.get<1>()
    <<"->throughtheair:"<<std::setw(4)<<acof
    <<",overtheroad:"<<std::setw(4)<<distance
    <<std::endl;

    if(first)
    {
    build_route(graph,predecessors,
    city1.get<2>(),city2.get<2>(),
    route);
    first=false;
    }
    }
    }
    }

    #ifdefined(HAVE_SVG)
    //CreatetheSVG
    std::ofstreamstream("routes.svg");
    boost::geometry::svg_mapper<point_type>mapper(stream,600,600);

    //Maproads
    BOOST_FOREACH(road_typeconst&road,roads)
    {
    mapper.add(road.get<0>());
    }

    BOOST_FOREACH(road_typeconst&road,roads)
    {
    mapper.map(road.get<0>(),
    "stroke:rgb(128,128,128);stroke-width:1");
    }

    mapper.map(route,
    "stroke:rgb(0,255,0);stroke-width:6;opacity:0.5");

    //Mapcities
    BOOST_FOREACH(city_typeconst&city,cities)
    {
    mapper.map(city.get<0>(),
    "fill:rgb(255,255,0);stroke:rgb(0,0,0);stroke-width:1");
    mapper.text(city.get<0>(),city.get<1>(),
    "fill:rgb(0,0,0);font-family:Arial;font-size:10px",5,5);
    }
    #endif

    return0;
    }