简体   繁体   中英

boost::adjancency_list with polymorphic vertices and edges

I'm learning about BGL and pieced together a program (from various related StackOverflow answers) to build a graph that should be able to deal with polymorphic vertices and edges. The graph should be able to handle parallel edges (of the same type or not). I'd like to get dijkstra_shortest_paths from a starting vertex_descriptor to: 1. all Park's; 2. all Park's and City's; 3. all City's via Railway only. Is that even possible? Thank you.

#include <iostream>
#include <deque>

#include "boost/graph/adjacency_list.hpp"
#include "boost/graph/topological_sort.hpp"
#include <boost/graph/dijkstra_shortest_paths.hpp>
#include <boost/property_map/property_map.hpp>
#include <boost/variant.hpp>


#include <boost/graph/adj_list_serialize.hpp>
#include <boost/property_map/function_property_map.hpp>
#include <boost/property_map/transform_value_property_map.hpp>

using namespace boost;

struct Highway {
  std::string name;
  int miles;
  int speed_limit;
  int lanes;
  bool divided;
};

struct Railway { 
  std::string name; 
  int km;
};

struct City {
  std::string name;
  int population;
  std::vector<int> zipcodes;
};

struct Park {
  std::string name;
  int population;
  std::vector<int> zipcodes;
};


struct Country {
  std::string name;
  bool use_right;   // Drive on the left or right
  bool use_metric;  // mph or km/h
};

int main(int argc, char *argv[]) {

  typedef boost::adjacency_list<
    boost::listS, boost::vecS, boost::bidirectionalS,
    boost::variant<City, Park>, boost::variant<Highway, Railway>, Country>
      Map;

  City c1 = {"San Jose", 1200000, {95126, 95128}};
  City c2 = {"San Francisco", 500000, {95008, 95009}};
  City c3 = {"Santa Cruz", 300000, {94001, 94002}};
  City c4 = {"Oakland", 800000, {93001, 93002}};
  City c5 = {"San Diego", 2800000, {92001, 92002}};

  Map map; // load the map
  Map::vertex_descriptor c1d = boost::add_vertex(c1, map),
    c2d = boost::add_vertex(c2, map),
    c3d = boost::add_vertex(c3, map),
    c4d = boost::add_vertex(c4, map), 
    c5d = boost::add_vertex(c5, map); 
  add_edge(c1d, c2d, Highway{"101", 42, 65, 3, true}, map);
  add_edge(c2d, c3d, Highway{"280", 52, 60, 1, true}, map);
  add_edge(c2d, c3d, Highway{"92", 13, 60, 1, true}, map);
  //add_edge(c2, c3, map);
  //add_edge(c1, c3, map);

  map[graph_bundle].name = "United States";
  map[graph_bundle].use_right = true;
  map[graph_bundle].use_metric = false;

  using vertex_descriptor = boost::graph_traits<Map>::vertex_descriptor;

  Map::vertex_descriptor from = *vertices(map).first;
  std::vector<int> distances(num_vertices(map));
  auto v_index = get(boost::vertex_index, map);
  auto v_bundle = get(boost::vertex_bundle, map);
  dijkstra_shortest_paths(map, from,
      weight_map(get(get(&Highway::miles, map))
      .distance_map(make_iterator_property_map(distances.begin(),
      get(vertex_index, map))));
  std::cout << "distances and parents:" << std::endl;
  graph_traits <Map>::vertex_iterator vi, vend;
  for (boost::tie(vi, vend) = vertices(map); vi != vend; ++vi) {
    std::cout << "distance(" << get<City>(&City::name, map[*vi]) << ") = " << distances[*vi] << ", " << "\n";
  }
  std::cout << std::endl;

  return 0;
}

So, in short, you want to access a property from a variant element type. You could make your own property map, facilitate your own set of accessors or use a property map adaptor.

The latter being least work:

auto highway_miles = boost::make_transform_value_property_map(
    [](Interconnect const& conn) {
        if (auto* c = boost::get<Highway>(&conn)) {
            return c->miles;
        } else {
            return std::numeric_limits<Distance>::max();
        }
    },
    get(boost::edge_bundle, map));

dijkstra_shortest_paths(
    map, from,
    weight_map(highway_miles)
        .distance_map(make_iterator_property_map(
            distances.begin(), v_index)));

In fact with vecS chosen vertex container selector, you could write it more succinctly:

dijkstra_shortest_paths(
    map, from, weight_map(highway_miles).distance_map(distances.data()));

Live Demo

Live on Compiler Explorer

#include <deque>
#include <iostream>

#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/dijkstra_shortest_paths.hpp>
#include <boost/graph/topological_sort.hpp>
#include <boost/property_map/function_property_map.hpp>
#include <boost/property_map/transform_value_property_map.hpp>
#include <boost/variant.hpp>

using Distance = int;

struct Highway {
    std::string name;
    Distance    miles;
    unsigned    speed_limit, lanes;
    bool        divided;
};

struct Railway {
    std::string name;
    Distance    km;
};

struct City {
    std::string           name;
    unsigned              population;
    std::vector<unsigned> zipcodes;
};

struct Park {
    std::string           name;
    unsigned              population;
    std::vector<unsigned> zipcodes;
};

using Interconnect = boost::variant<Highway, Railway>;
using Area = boost::variant<City, Park>;

enum class RoadOrientation { right, left };
enum class UnitSystem { metric, imperial };

struct Country {
    std::string     name;
    RoadOrientation driving;
    UnitSystem      measure;
};

using Map = boost::adjacency_list<                    //
    boost::listS, boost::vecS, boost::bidirectionalS, //
    Area, Interconnect, Country>;

int main()
{
    Map map;
    map[boost::graph_bundle] = {
        "United States",
        RoadOrientation::right,
        UnitSystem::imperial,
    };

    auto c1d = add_vertex(City{"San Jose",      1200000, {95126, 95128}}, map);
    auto c2d = add_vertex(City{"San Francisco", 500000,  {95008, 95009}}, map);
    auto c3d = add_vertex(City{"Santa Cruz",    300000,  {94001, 94002}}, map);
               add_vertex(City{"Oakland",       800000,  {93001, 93002}}, map);
               add_vertex(City{"San Diego",     2800000, {92001, 92002}}, map);

    add_edge(c1d, c2d, Highway{"101", 42, 65, 3, true}, map);
    add_edge(c2d, c3d, Highway{"280", 52, 60, 1, true}, map);
    add_edge(c2d, c3d, Highway{"92",  13, 60, 1, true}, map);

    using V = Map::vertex_descriptor;

    V                from = vertex(0, map);
    std::vector<int> distances(num_vertices(map));

    auto v_index       = get(boost::vertex_index, map);
    auto highway_miles = boost::make_transform_value_property_map(
        [](Interconnect const& conn) {
            if (auto* c = boost::get<Highway>(&conn)) {
                return c->miles;
            } else {
                return std::numeric_limits<Distance>::max();
            }
        },
        get(boost::edge_bundle, map));

    dijkstra_shortest_paths(
        map, from,
        weight_map(highway_miles)
            .distance_map(make_iterator_property_map(
                distances.begin(), v_index)));

    dijkstra_shortest_paths(
        map, from, weight_map(highway_miles).distance_map(distances.data()));

    std::cout << "distances and parents:" << std::endl;

    auto name_of = [](auto const& area) {
        return boost::apply_visitor(
            [](auto const& c_or_p) { return c_or_p.name; }, area);
    };

    for (auto v : boost::make_iterator_range(vertices(map)))
        std::cout << "distance(" << name_of(map[v]) << ") = " << distances[v] << "\n";
}

Prints

distances and parents:
distance(San Jose) = 0
distance(San Francisco) = 42
distance(Santa Cruz) = 55
distance(Oakland) = 2147483647
distance(San Diego) = 2147483647

More Ideas

I've provided recommendations/alternatives in the past:

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM