Search code examples
c++boostboost-graph

How to set c++ boost graphml node and edge id?


I am using the Boost graph to store a set of nodes and edges and then write it to a graphml format. Whatever I do, I cannot find a way to access or set the node id (n0, n1) or edge id (e0) attributes. It seems to be automatically set.

Is there a way to access and set it manually ?

<?xml version="1.0" encoding="UTF-8"?>
<graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://graphml.graphdrawing.org/xmlns/1.0/graphml.xsd">
  <key id="key0" for="node" attr.name="id" attr.type="int" />
  <key id="key1" for="edge" attr.name="length" attr.type="double" />
  <key id="key2" for="edge" attr.name="max_speed" attr.type="double" />
  <key id="key3" for="node" attr.name="name" attr.type="string" />
  <key id="key4" for="edge" attr.name="name" attr.type="string" />
  <key id="key5" for="edge" attr.name="source" attr.type="int" />
  <key id="key6" for="node" attr.name="station" attr.type="boolean" />
  <key id="key7" for="edge" attr.name="target" attr.type="int" />
  <key id="key8" for="node" attr.name="theta" attr.type="double" />
  <key id="key9" for="node" attr.name="x" attr.type="double" />
  <key id="key10" for="node" attr.name="y" attr.type="double" />
  <graph id="G" edgedefault="directed" parse.nodeids="canonical" parse.edgeids="canonical" parse.order="nodesfirst">
    <node id="n0">
      <data key="key0">10000</data>
      <data key="key3">node1</data>
      <data key="key6">0</data>
      <data key="key8">0</data>
      <data key="key9">6.95279e-310</data>
      <data key="key10">0</data>
    </node>
    <node id="n1">
      <data key="key0">10001</data>
      <data key="key3">node1</data>
      <data key="key6">0</data>
      <data key="key8">0</data>
      <data key="key9">6.95279e-310</data>
      <data key="key10">0</data>
    </node>
    <edge id="e0" source="n0" target="n1">
      <data key="key1">6.95279e-310</data>
      <data key="key2">150</data>
      <data key="key4"></data>
      <data key="key5">-127787376</data>
      <data key="key7">21994</data>
    </edge>
  </graph>
</graphml>

My graph


typedef typename boost::adjacency_list<boost::vecS, boost::vecS, boost::directedS,
                              GpNode, GpEdge>
    DirectedGraph;

Where GpNode and GpEdge are custom class definitions.

Thanks in advance


Solution

  • write_graphml takes a dynamic_properties. Let's configure that:

    Live On Coliru

    #include <boost/graph/adjacency_list.hpp>
    #include <boost/graph/graphml.hpp>
    
    struct GpNode {
        int         id;
        std::string name;
        bool        station;
        double      theta;
        double      x;
        double      y;
    };
    
    struct GpEdge {
        double      length;
        double      max_speed;
        std::string name;
        int         source;
        int         target;
    };
    
    using DirectedGraph = boost::adjacency_list<boost::vecS, boost::vecS,
                                                boost::directedS, GpNode, GpEdge>;
    
    int main() {
        std::ifstream ifs("input.xml");
        DirectedGraph g;
    
        auto n0 = add_vertex(
            GpNode{
                10000,        // id
                "node1",      // name
                0,            // station
                0,            // theta
                6.95279e-310, // x
                0,            // y
            }, g);
    
        auto n1 = add_vertex(
            GpNode{
                10001,        // id
                "node1",      // name
                0,            // station
                0,            // theta
                6.95279e-310, // x
                0,            // y
            }, g);
    
        /*auto e0 = */add_edge(n0, n1,
             GpEdge{
                 6.95279e-310, // length
                 150,          // max_speed
                 "",           // name
                 -127787376,   // source
                 21994,        // target
             }, g);
    
        auto vindex = get(&GpNode::id, g);
        boost::dynamic_properties dp;
    
        //dp.property("node_id",   vindex);
        dp.property("id",        vindex);
        dp.property("name",      get(&GpNode::name,      g));
        dp.property("station",   get(&GpNode::station,   g));
        dp.property("theta",     get(&GpNode::theta,     g));
        dp.property("x",         get(&GpNode::x,         g));
        dp.property("y",         get(&GpNode::y,         g));
    
        dp.property("length",    get(&GpEdge::length,    g));
        dp.property("max_speed", get(&GpEdge::max_speed, g));
        dp.property("name",      get(&GpEdge::name,      g));
        dp.property("source",    get(&GpEdge::source,    g));
        dp.property("target",    get(&GpEdge::target,    g));
    
        boost::write_graphml(std::cout, g, dp);
    }
    

    Prints

    <?xml version="1.0" encoding="UTF-8"?>
    <graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://graphml.graphdrawing.org/xmlns/1.0/graphml.xsd">
      <key id="key0" for="node" attr.name="id" attr.type="int" />
      <key id="key1" for="edge" attr.name="length" attr.type="double" />
      <key id="key2" for="edge" attr.name="max_speed" attr.type="double" />
      <key id="key3" for="node" attr.name="name" attr.type="string" />
      <key id="key4" for="edge" attr.name="name" attr.type="string" />
      <key id="key5" for="edge" attr.name="source" attr.type="int" />
      <key id="key6" for="node" attr.name="station" attr.type="boolean" />
      <key id="key7" for="edge" attr.name="target" attr.type="int" />
      <key id="key8" for="node" attr.name="theta" attr.type="double" />
      <key id="key9" for="node" attr.name="x" attr.type="double" />
      <key id="key10" for="node" attr.name="y" attr.type="double" />
      <graph id="G" edgedefault="directed" parse.nodeids="free" parse.edgeids="canonical" parse.order="nodesfirst">
        <node id="n0">
          <data key="key0">10000</data>
          <data key="key3">node1</data>
          <data key="key6">0</data>
          <data key="key8">0</data>
          <data key="key9">6.95279e-310</data>
          <data key="key10">0</data>
        </node>
        <node id="n1">
          <data key="key0">10001</data>
          <data key="key3">node1</data>
          <data key="key6">0</data>
          <data key="key8">0</data>
          <data key="key9">6.95279e-310</data>
          <data key="key10">0</data>
        </node>
        <edge id="e0" source="n0" target="n1">
          <data key="key1">6.95279e-310</data>
          <data key="key2">150</data>
          <data key="key4"></data>
          <data key="key5">-127787376</data>
          <data key="key7">21994</data>
        </edge>
      </graph>
    </graphml>
    

    Mmm. That took a while. But now I see. I know that write_graphviz_dp assumes node_id is the node id property, but as you can see, I tried and it didn't help

    Docs To The Rescue

    But, wait, docs show a second overload that also takes VertexIndexMap. Let's.... try that?

    boost::write_graphml(std::cout, g, vindex, dp);
    

    Now it prints

    Live On Coliru

    <?xml version="1.0" encoding="UTF-8"?>
    <graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://graphml.graphdrawing.org/xmlns/1.0/graphml.xsd">
      <key id="key0" for="node" attr.name="id" attr.type="int" />
      <key id="key1" for="edge" attr.name="length" attr.type="double" />
      <key id="key2" for="edge" attr.name="max_speed" attr.type="double" />
      <key id="key3" for="node" attr.name="name" attr.type="string" />
      <key id="key4" for="edge" attr.name="name" attr.type="string" />
      <key id="key5" for="edge" attr.name="source" attr.type="int" />
      <key id="key6" for="node" attr.name="station" attr.type="boolean" />
      <key id="key7" for="edge" attr.name="target" attr.type="int" />
      <key id="key8" for="node" attr.name="theta" attr.type="double" />
      <key id="key9" for="node" attr.name="x" attr.type="double" />
      <key id="key10" for="node" attr.name="y" attr.type="double" />
      <graph id="G" edgedefault="directed" parse.nodeids="free" parse.edgeids="canonical" parse.order="nodesfirst">
        <node id="n10000">
          <data key="key0">10000</data>
          <data key="key3">node1</data>
          <data key="key6">0</data>
          <data key="key8">0</data>
          <data key="key9">6.95279e-310</data>
          <data key="key10">0</data>
        </node>
        <node id="n10001">
          <data key="key0">10001</data>
          <data key="key3">node1</data>
          <data key="key6">0</data>
          <data key="key8">0</data>
          <data key="key9">6.95279e-310</data>
          <data key="key10">0</data>
        </node>
        <edge id="e0" source="n10000" target="n10001">
          <data key="key1">6.95279e-310</data>
          <data key="key2">150</data>
          <data key="key4"></data>
          <data key="key5">-127787376</data>
          <data key="key7">21994</data>
        </edge>
      </graph>
    </graphml>
    

    That's likely as close as you'll get with it. Changing the id type to std::string doesn't prevent the "n" prefix. (I suppose it's to allow edges to have id's colliding with nodes without problems?)