GraphMLGraphAdapter question?

Jan 11, 2013 at 11:39 AM

    I use GraphMLGraphAdapter to import data form standard GraphML file. The code look like this:

GraphMLGraphAdapter graphMlAdapter = new Adapters.GraphMLGraphAdapter();
nodexl.Graph = graphMlAdapter.LoadGraphFromFile(fileName);

 

    But I found the nodexl's graph only got the vertex and the edge's id from GraphML file without GraphML-attribute's value,such as vertex's color, shape...How can I get it? Should I use GraphMLAttribute? If I use GraphMLAttribute, I think I have to read every XmlNode from GraphML file just like reading a normal XML file.

Coordinator
Jan 11, 2013 at 5:45 PM

Doug:

If your GraphML file contains valid GraphML, then GraphMLGraphAdapter.LoadGraphFromFile() will automatically read the GraphML-attributes contained in the file.  One of two things may be happening in your case:

1. The GraphML-attributes aren't properly specified in the file.

2. You're not looking in the right place for the attribute values after LoadGraphFromFile() returns.  The way GraphMLGraphAdapter works with GraphML-attributes is documented in the "GraphMLGraphAdapter Class" topic in the NodeXLApi.chm help file.  For reference, I'll include part of that documentation in my next post.

If you still can't get things to work, then post a small (not large!) but complete sample GraphML file here as text and I'll see if I can tell what's wrong.

-- Tony

Coordinator
Jan 11, 2013 at 5:45 PM
Edited Jan 11, 2013 at 5:46 PM

From the "GraphMLGraphAdapter Class" topic in the NodeXLApi.chm help file:

Edge and vertex attributes, which GraphML calls "GraphML-attributes," are supported by this class. When loading a graph, if an edge or vertex has a GraphML-attribute, it gets added to the metadata of the IEdge or IVertex. The metadata key is the GraphML-attribute's attr.name value and the metadata value is the GraphML-attribute's value. When saving a graph, every metadata value on every edge and vertex gets converted to a GraphML-attribute in the saved GraphML.

To make it possible for the caller to determine which metadata keys were added to the graph's edges and vertices, the LoadXX methods add AllEdgeMetadataKeys and AllVertexMetadataKeys keys to the returned graph. The key values are of type String[].

If there is an optional "description" attribute on the "graph" XML node, the LoadXX methods copy its value to a GraphDescription key on the returned graph.

If there is an optional "suggestedFileNameNoExtension" attribute on the "graph" XML node, the LoadXX methods copy its value to a SuggestedFileNameNoExtension key on the returned graph.

When saving a graph, the SaveGraph caller must add AllEdgeMetadataKeys and AllVertexMetadataKeys keys to the graph before calling SaveGraph.

Jan 12, 2013 at 5:17 AM

Here is my GraphML file:

<?xml version="1.0" encoding="UTF-8"?>
<graphml xmlns="http://graphml.graphdrawing.org/xmlns">
  <key id="V-Color" for="node" attr.name="Color" attr.type="string" />
  <key id="V-Shape" for="node" attr.name="Shape" attr.type="string" />
  <key id="V-Size" for="node" attr.name="Size" attr.type="string" />
  <key id="V-Opacity" for="node" attr.name="Opacity" attr.type="string" />
  <key id="V-Image File" for="node" attr.name="Image File" attr.type="string" />
  <key id="V-Visibility" for="node" attr.name="Visibility" attr.type="string" />
  <key id="V-Label" for="node" attr.name="Label" attr.type="string" />
  <key id="V-Label Fill Color" for="node" attr.name="Label Fill Color" attr.type="string" />
  <key id="V-Label Position" for="node" attr.name="Label Position" attr.type="string" />
  <key id="V-Tooltip" for="node" attr.name="Tooltip" attr.type="string" />
  <key id="V-Layout Order" for="node" attr.name="Layout Order" attr.type="string" />
  <key id="V-X" for="node" attr.name="X" attr.type="string" />
  <key id="V-Y" for="node" attr.name="Y" attr.type="string" />
  <key id="V-Locked?" for="node" attr.name="Locked?" attr.type="string" />
  <key id="V-Polar R" for="node" attr.name="Polar R" attr.type="string" />
  <key id="V-Polar Angle" for="node" attr.name="Polar Angle" attr.type="string" />
  <key id="V-Degree" for="node" attr.name="Degree" attr.type="string" />
  <key id="V-In-Degree" for="node" attr.name="In-Degree" attr.type="string" />
  <key id="V-Out-Degree" for="node" attr.name="Out-Degree" attr.type="string" />
  <key id="V-Betweenness Centrality" for="node" attr.name="Betweenness Centrality" attr.type="string" />
  <key id="V-Closeness Centrality" for="node" attr.name="Closeness Centrality" attr.type="string" />
  <key id="V-Eigenvector Centrality" for="node" attr.name="Eigenvector Centrality" attr.type="string" />
  <key id="V-PageRank" for="node" attr.name="PageRank" attr.type="string" />
  <key id="V-Clustering Coefficient" for="node" attr.name="Clustering Coefficient" attr.type="string" />
  <key id="V-Reciprocated Vertex Pair Ratio" for="node" attr.name="Reciprocated Vertex Pair Ratio" attr.type="string" />
  <key id="V-ID" for="node" attr.name="ID" attr.type="string" />
  <key id="V-Dynamic Filter" for="node" attr.name="Dynamic Filter" attr.type="string" />
  <key id="V-Add Your Own Columns Here" for="node" attr.name="Add Your Own Columns Here" attr.type="string" />
  <key id="E-Color" for="edge" attr.name="Color" attr.type="string" />
  <key id="E-Width" for="edge" attr.name="Width" attr.type="string" />
  <key id="E-Style" for="edge" attr.name="Style" attr.type="string" />
  <key id="E-Opacity" for="edge" attr.name="Opacity" attr.type="string" />
  <key id="E-Visibility" for="edge" attr.name="Visibility" attr.type="string" />
  <key id="E-Label" for="edge" attr.name="Label" attr.type="string" />
  <key id="E-Label Text Color" for="edge" attr.name="Label Text Color" attr.type="string" />
  <key id="E-Label Font Size" for="edge" attr.name="Label Font Size" attr.type="string" />
  <key id="E-Reciprocated?" for="edge" attr.name="Reciprocated?" attr.type="string" />
  <key id="E-ID" for="edge" attr.name="ID" attr.type="string" />
  <key id="E-Dynamic Filter" for="edge" attr.name="Dynamic Filter" attr.type="string" />
  <key id="E-Add Your Own Columns Here" for="edge" attr.name="Add Your Own Columns Here" attr.type="string" />
  <key id="E-Edge Weight" for="edge" attr.name="Edge Weight" attr.type="string" />
  <graph edgedefault="undirected">
    <node id="John">
      <data key="V-Shape">Label</data>
      <data key="V-Size">10</data>
      <data key="V-Label">John</data>
      <data key="V-Label Fill Color">blue</data>
      <data key="V-X">5652.482421875</data>
      <data key="V-Y">9777.9560546875</data>
      <data key="V-ID">3</data>
    </node>
    <node id="Tom">
      <data key="V-Shape">Label</data>
      <data key="V-Size">8</data>
      <data key="V-Label">Tom</data>
      <data key="V-Label Fill Color">red</data>
      <data key="V-X">8825.255859375</data>
      <data key="V-Y">2819.51000976563</data>
      <data key="V-ID">4</data>
    </node>
    <node id="Smith">
      <data key="V-Shape">Label</data>
      <data key="V-Size">5</data>
      <data key="V-Label">Smith</data>
      <data key="V-Label Fill Color">green</data>
      <data key="V-X">9702.2998046875</data>
      <data key="V-Y">7912.7001953125</data>
      <data key="V-ID">5</data>
    </node>
    <node id="Rose">
      <data key="V-Shape">Label</data>
      <data key="V-Size">8</data>
      <data key="V-Label">Rose</data>
      <data key="V-Label Fill Color">black</data>
      <data key="V-X">3850.38891601563</data>
      <data key="V-Y">216.112274169922</data>
      <data key="V-ID">6</data>
    </node>
    <node id="Ani">
      <data key="V-Shape">Label</data>
      <data key="V-Size">5</data>
      <data key="V-Label">Ani</data>
      <data key="V-Label Fill Color">yellow</data>
      <data key="V-X">374.644775390625</data>
      <data key="V-Y">3849.798828125</data>
      <data key="V-ID">7</data>
    </node>
    <node id="Kam">
      <data key="V-Shape">Label</data>
      <data key="V-Size">8</data>
      <data key="V-Label">Kam</data>
      <data key="V-Label Fill Color">orange</data>
      <data key="V-X">270.791107177734</data>
      <data key="V-Y">8892.8857421875</data>
      <data key="V-ID">8</data>
    </node>
    <edge source="Rose" target="Kam">
      <data key="E-ID">7</data>
      <data key="E-Edge Weight">1</data>
    </edge>
    <edge source="John" target="Ani">
      <data key="E-ID">6</data>
      <data key="E-Edge Weight">1</data>
    </edge>
    <edge source="John" target="Rose">
      <data key="E-ID">5</data>
      <data key="E-Edge Weight">1</data>
    </edge>
    <edge source="John" target="Smith">
      <data key="E-ID">4</data>
      <data key="E-Edge Weight">1</data>
    </edge>
    <edge source="John" target="Tom">
      <data key="E-ID">3</data>
      <data key="E-Edge Weight">1</data>
    </edge>
  </graph>
</graphml>

   After using LoadGraphFromFile, I DrawGraph(true). This  is  only a simple graph without color, size and shape.

Coordinator
Jan 12, 2013 at 7:24 AM
Edited Jan 12, 2013 at 7:24 AM

The resulting graph contains everything you need.  You are probably looking for the attribute values incorrectly.  Please note, from the NodeXL documentation (italics added):

"When loading a graph, if an edge or vertex has a GraphML-attribute, it gets added to the metadata of the IEdge or IVertex. The metadata key is the GraphML-attribute's attr.name value and the metadata value is the GraphML-attribute's value."

The metadata key is not the GraphML-attribute's id value.  So if you want to know a vertex's shape, for example, you would look for it in the vertex's "Shape" key, not in a "V-Shape" key, which doesn't exist.

I'll include a complete working sample in my next post to illustrate what you need to do.

-- Tony

Coordinator
Jan 12, 2013 at 7:25 AM

using System;
using Smrf.NodeXL.Core;
using Smrf.NodeXL.Adapters;
using System.Diagnostics;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            GraphMLGraphAdapter graphMlAdapter = new GraphMLGraphAdapter();
            IGraph graph = graphMlAdapter.LoadGraphFromFile(@"..\..\GraphML.graphml");

            IVertex vertex;
           
            Boolean found = graph.Vertices.Find("John", out vertex);
            Debug.Assert(found);

            Console.WriteLine((String)vertex.GetValue("Shape"));

            // Results: "Label"
        }
    }
}

Coordinator
Jan 12, 2013 at 7:48 AM

Oh, wait...  I think I misunderstood the problem.  You're assigning this graph to the NodeXLControl.Graph property and then drawing it?  If that's the case, then you are using incorrect GraphML-attribute names.  The metadata key that the NodelXLControl uses for vertex shape, for example, is not "Shape"; it's the value of the constant ReservedMetadataKeys.PerVertexShape.

The GraphML you posted was exported by the Excel Template program, right?  That GraphML can be imported back into the Excel Template, but it is not meant to be imported directly into a low-level Graph object.  The Excel Template has a translation layer that converts attribute names between the friendly names shown to the user ("Shape") and the low-level names used by the NodeXLControl (ReservedMetadataKey.PerVertexShape).  That's because we don't want Excel columns to have mysterious names like "~VDShape", which happens to be the value of the constant ReservedMetadataKeys.PerVertexShape.

I would need to know more about your program's workflow before I could give you more advice on your use of GraphML.  But this is why it's not working for you right now.

-- Tony

Jan 12, 2013 at 10:59 AM

Dear tcap479, I wrote codes like that:

string[] vertexMetaKeys = (string[])nodexl.Graph.GetRequiredValue(ReservedMetadataKeys.AllVertexMetadataKeys, typeof(string[]));
if (vertexMetaKeys.Contains("Color"))
{
      foreach (IVertex vertex in nodexl.Graph.Vertices)
      {
           vertex.SetValue(ReservedMetadataKeys.PerColor, GetColor((string)vertex.GetValue("Color")));
      }
}

Maybe I must write these similar codes after GraphMLGraphAdapter loadfromfile, then I can get a complete graph including vertex and edge's color, shape, size, width..........?? :P

 

Coordinator
Jan 13, 2013 at 3:29 AM

I'm still not sure I understand your requirements.  You have a GraphML file that was exported by the ExcelTemplate project, and you need to import this GraphML file into your own custom application that uses the NodeXLControl?

If that is the case, then yes, you will need to "translate" all the GraphML-attributes that you care about into metadata keys that the NodeXLControl understands.  That's because the NodeXLControl understands only certain metadata keys, such as ReservedMetadataKeys.PerColor, and it ignores all metadata keys it doesn't understand, such as "Color".

I think you've found it already, but the names, meanings, and types of all the metadata keys that the NodeXLControl understands are documented in the "ReservedMetadataKeys Members" topic in the NodeXLApi.chm help file.

Also, you might want to look at the classes that the ExcelTemplate project uses to convert NodeXL workbook values (which is what you have in your GraphML file) to values that the NodeXLControl understands.  The class VertexShapeConverter, for example, converts vertex shape strings stored in the NodeXL workbook to VertexShape enumeration values, which are what have to be stored in the ReservedMetadataKeys.PerVertexShape key.  See http://nodexl.codeplex.com/SourceControl/changeset/view/70521#361307.

-- Tony

Jan 13, 2013 at 11:50 AM

"If that is the case, then yes, you will need to "translate" all the GraphML-attributes that you care about into metadata keys that the NodeXLControl understands. That's because the NodeXLControl understands only certain metadata keys, such as ReservedMetadataKeys.PerColor, and it ignores all metadata keys it doesn't understand, such as "Color"."

-------------------This is what I want to know, thanks a lot for your patience guidance!  :P

Feb 4, 2013 at 7:19 PM
Edited Feb 4, 2013 at 7:36 PM
Hey.

I have a pretty similar problem:
I only get the mysterious metadata-names like "~VDShape". But I am adding "ReservedMetadataKeys.AllVertexMetadataKeys" via "getrequiredvalues"

I only use keys that are supported by nodexlcontrol. Is there any common mistake I could be doing while using the "LoadGraphfromFile"-Function?

thanks in advance :)
Coordinator
Feb 4, 2013 at 11:03 PM
Edited Feb 4, 2013 at 11:03 PM
redscorpion:

I don't understand your question. Please tell me specifically what you are doing (saving to a GraphML file, loading from a GraphML file, both, or something else?), what you are expecting to happen, and what is actually happening.

-- Tony
Feb 6, 2013 at 1:23 PM
I am trying to import a GraphML file which I exported before.

error message:
Smrf.NodeXL.Core.Vertex.TryGetValue: The value with the key "~VDRadius" is of type System.String. The expected type is System.Single.
Parametername: key
code to import:
var graphMlAdapter = new GraphMLGraphAdapter();
            IGraph importGraph = graphMlAdapter.LoadGraphFromFile(xmlFileName);
            importGraph.GetRequiredValue(ReservedMetadataKeys.AllEdgeMetadataKeys, typeof(String[]));
            importGraph.GetRequiredValue(ReservedMetadataKeys.AllVertexMetadataKeys, typeof(String[]));

            _nodeXlControl.Graph = importGraph;
            _nodeXlControl.DrawGraph(false);
hope this shows my problem better.
Coordinator
Feb 6, 2013 at 8:38 PM
Edited Feb 6, 2013 at 10:17 PM
There is a design bug in the GraphMLGraphAdapter that prevents you from "round-tripping" a graph. That is, if you save a graph that has standard metadata keys, like ReservedMetadataKeys.Radius, and then try to load it again, you'll get an exception about incorrect types.

We don't run into this problem in our own Excel Template application because we use a "translation layer" that does its own type conversions appropriate for Excel.

The long-term fix is for us to either modify GraphMLGraphAdapter.Save() to store the correct types in the GraphML file, or to do a bunch of type conversions for the standard metadata keys in GraphMLGraphAdapter.Load(). I don't know when we're going to get to that, so as a short-term fix, you can do the conversions yourself after you call GraphMLGraphAdapter.Load() but before you draw the graph.

In my next post, I'll include a sample of this. It sounds complicated, but it's actually just two lines of code for each of the standard metadata keys that you are using.

-- Tony
Coordinator
Feb 6, 2013 at 8:40 PM
Edited Feb 6, 2013 at 8:42 PM
using System;
using System.Linq;
using Smrf.NodeXL.Core;
using Smrf.NodeXL.Adapters;

namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
// Create a simple graph with just one vertex.

IGraph graphToSave = new Graph(GraphDirectedness.Undirected);

IVertex vertexToSave = graphToSave.Vertices.Add();
vertexToSave.Name = "A";

// Set the vertex radius, which is a Single.

vertexToSave.SetValue(ReservedMetadataKeys.PerVertexRadius, 5F);

// Save the graph to a GraphML file.  You have to tell the
// GraphMLAdapter which metadata keys you want to include in the
// GraphML.
//
// Note that all metadata values are saved as strings, so the vertex
// radius in the file will be the string value "5", not the Single
// value 5F.

graphToSave.SetValue(
    ReservedMetadataKeys.AllVertexMetadataKeys,
    new String[] { ReservedMetadataKeys.PerVertexRadius}
    );

graphToSave.SetValue(
    ReservedMetadataKeys.AllEdgeMetadataKeys,
    new String[0]
    );

const String FileName = "T:\\Temp.graphml";
GraphMLGraphAdapter graphMLGraphAdapter = new GraphMLGraphAdapter();
graphMLGraphAdapter.SaveGraph(graphToSave, FileName);

// Now load the GraphML file into a new graph.

IGraph loadedGraph = graphMLGraphAdapter.LoadGraphFromFile(FileName);

// Retrieve the vertex and get its radius.

IVertex loadedVertex = loadedGraph.Vertices.First();

Object loadedRadius = loadedVertex.GetValue(
    ReservedMetadataKeys.PerVertexRadius);

// The following line prints out "String", because the radius was
// converted to a String when the GraphML file was saved.

Console.WriteLine(loadedRadius.GetType().Name);

// If you were to try to draw the graph at this point, you would get an
// exception caused by the radius being the wrong type.

// Now, TO FIX THE PROBLEM:

// -------------------------------------------------
// Convert each vertex radius from a String to a Single.

foreach (IVertex vertex in loadedGraph.Vertices)
{
    Object radius;

    if ( vertex.TryGetValue(ReservedMetadataKeys.PerVertexRadius,
        out radius) )
    {
        vertex.SetValue(ReservedMetadataKeys.PerVertexRadius,
            Single.Parse( (String)radius ) );
    }
}
// -------------------------------------------------

// The following line prints out "Single", which is the correct type.
// YOU CAN NOW DRAW THE GRAPH.

loadedRadius = loadedVertex.GetValue(
    ReservedMetadataKeys.PerVertexRadius);

Console.WriteLine(loadedRadius.GetType().Name);
}
}
}