/// <summary> /// Writes a GEXF graph file for the specified graph nodes and edges /// </summary> /// <param name="Filename">The file name to write</param> /// <param name="Description">The description to include in the graph file's metadata</param> /// <param name="GraphNodes">List of all graph nodes. Index order is important and must match with the individual node Id members!</param> /// <param name="GraphEdges">List of all graph edges. Index order is important and must match with the individual edge Id members!</param> public static void WriteGraphFile(string Filename, string Description, List <GraphNode> GraphNodes, List <GraphEdge> GraphEdges) { XmlWriterSettings Settings = new XmlWriterSettings(); Settings.Indent = true; Settings.IndentChars = " "; // Figure out all of the custom attribute types we're dealing with Dictionary <string, GraphAttribute> AllAttributes = new Dictionary <string, GraphAttribute>(StringComparer.InvariantCultureIgnoreCase); int NextAttributeID = 0; foreach (GraphNode GraphNode in GraphNodes) { foreach (string AttributeName in GraphNode.Attributes.Keys) { object AttributeValue = GraphNode.Attributes[AttributeName]; string AttributeTypeName; if (AttributeValue.GetType() == typeof(int)) { AttributeTypeName = "integer"; } else if (AttributeValue.GetType() == typeof(float)) { AttributeTypeName = "float"; } else if (AttributeValue.GetType() == typeof(double)) { AttributeTypeName = "double"; } else if (AttributeValue.GetType() == typeof(string)) { AttributeTypeName = "string"; } else if (AttributeValue.GetType() == typeof(bool)) { AttributeTypeName = "boolean"; } else { // No other types supported yet! throw new InvalidOperationException("Unsupported attribute data type encountered on graph node!"); } GraphAttribute Attribute; if (!AllAttributes.TryGetValue(AttributeName, out Attribute)) { Attribute = new GraphAttribute(); Attribute.ID = NextAttributeID++; Attribute.Name = AttributeName; Attribute.TypeName = AttributeTypeName; AllAttributes[AttributeName] = Attribute; } else { if (!Attribute.TypeName.Equals(AttributeTypeName)) { throw new InvalidOperationException("Multiple graph nodes with the same attribute name but different types encountered!"); } } } } using (XmlWriter Writer = XmlWriter.Create(Filename, Settings)) { // NOTE: The GEXF XML format is defined here: http://gexf.net/1.2draft/gexf-12draft-primer.pdf string GEXFNamespace = "http://www.gexf.net/1.2-draft"; string SchemaNamespace = "http://www.w3.org/2001/XMLSchema-instance"; string VizNamespace = "http://www.gexf.net/1.2draft/viz"; Writer.WriteStartElement("gexf", GEXFNamespace); Writer.WriteAttributeString("xmlns", "xsi", null, SchemaNamespace); Writer.WriteAttributeString("schemaLocation", SchemaNamespace, "http://www.gexf.net/1.2draft http://www.gexf.net/1.2draft/gexf.xsd"); Writer.WriteAttributeString("xmlns", "viz", null, VizNamespace); Writer.WriteAttributeString("version", "1.2"); Writer.WriteStartElement("meta"); { Writer.WriteAttributeString("creator", "UnrealBuildTool"); Writer.WriteAttributeString("description", Description); } Writer.WriteEndElement(); // meta { Writer.WriteStartElement("graph"); { Writer.WriteAttributeString("mode", "static"); Writer.WriteAttributeString("defaultedgetype", "directed"); if (AllAttributes.Count > 0) { Writer.WriteStartElement("attributes"); { // @todo: Add support for edge attributes, not just node attributes Writer.WriteAttributeString("class", "node"); // Node attributes, not edges! foreach (GraphAttribute Attribute in AllAttributes.Values) { Writer.WriteStartElement("attribute"); { Writer.WriteAttributeString("id", Attribute.ID.ToString()); Writer.WriteAttributeString("title", Attribute.Name); Writer.WriteAttributeString("type", Attribute.TypeName); } Writer.WriteEndElement(); // attribute } // @todo: Add support for attribute type default values } Writer.WriteEndElement(); // attributes } Writer.WriteStartElement("nodes"); { foreach (GraphNode GraphNode in GraphNodes) { Writer.WriteStartElement("node"); { Writer.WriteAttributeString("id", GraphNode.Id.ToString()); Writer.WriteAttributeString("label", GraphNode.Label); Writer.WriteStartElement("color", VizNamespace); { Writer.WriteAttributeString("r", ((int)(GraphNode.Color.R * 255.0f)).ToString()); Writer.WriteAttributeString("g", ((int)(GraphNode.Color.G * 255.0f)).ToString()); Writer.WriteAttributeString("b", ((int)(GraphNode.Color.B * 255.0f)).ToString()); Writer.WriteAttributeString("a", GraphNode.Color.A.ToString()); } Writer.WriteEndElement(); // viz:color Writer.WriteStartElement("size", VizNamespace); { Writer.WriteAttributeString("value", GraphNode.Size.ToString()); } Writer.WriteEndElement(); // viz:size Writer.WriteStartElement("shape", VizNamespace); { // NOTE: Valid shapes are: disc, square, triangle, diamond, image Writer.WriteAttributeString("value", "disc"); } Writer.WriteEndElement(); // viz:shape if (GraphNode.Attributes.Count > 0) { Writer.WriteStartElement("attvalues"); { foreach (KeyValuePair <string, object> AttributeHashEntry in GraphNode.Attributes) { string AttributeName = AttributeHashEntry.Key; object AttributeValue = AttributeHashEntry.Value; GraphAttribute Attribute = AllAttributes[AttributeName]; Writer.WriteStartElement("attvalue"); { Writer.WriteAttributeString("for", Attribute.ID.ToString()); Writer.WriteAttributeString("value", AttributeValue.ToString()); } Writer.WriteEndElement(); } } Writer.WriteEndElement(); // attvalues } } Writer.WriteEndElement(); // node } } Writer.WriteEndElement(); // nodes Writer.WriteStartElement("edges"); { foreach (GraphEdge GraphEdge in GraphEdges) { Writer.WriteStartElement("edge"); { Writer.WriteAttributeString("id", GraphEdge.Id.ToString()); Writer.WriteAttributeString("source", GraphEdge.Source.Id.ToString()); Writer.WriteAttributeString("target", GraphEdge.Target.Id.ToString()); Writer.WriteAttributeString("weight", GraphEdge.Weight.ToString()); Writer.WriteStartElement("color", VizNamespace); { Writer.WriteAttributeString("r", ((int)(GraphEdge.Color.R * 255.0f)).ToString()); Writer.WriteAttributeString("g", ((int)(GraphEdge.Color.G * 255.0f)).ToString()); Writer.WriteAttributeString("b", ((int)(GraphEdge.Color.B * 255.0f)).ToString()); Writer.WriteAttributeString("a", GraphEdge.Color.A.ToString()); } Writer.WriteEndElement(); // viz:color Writer.WriteStartElement("thickness", VizNamespace); { Writer.WriteAttributeString("value", GraphEdge.Thickness.ToString()); } Writer.WriteEndElement(); // viz:thickness Writer.WriteStartElement("shape", VizNamespace); { // NOTE: Valid shapes are: solid, dotted, dashed, double Writer.WriteAttributeString("value", "solid"); } Writer.WriteEndElement(); // viz:shape } Writer.WriteEndElement(); // edge } } Writer.WriteEndElement(); // nodes } Writer.WriteEndElement(); // graph } Writer.WriteEndElement(); // gexf Writer.Flush(); } }
/// <summary> /// Writes a GEXF graph file for the specified graph nodes and edges /// </summary> /// <param name="Filename">The file name to write</param> /// <param name="Description">The description to include in the graph file's metadata</param> /// <param name="GraphNodes">List of all graph nodes. Index order is important and must match with the individual node Id members!</param> /// <param name="GraphEdges">List of all graph edges. Index order is important and must match with the individual edge Id members!</param> public static void WriteGraphFile( string Filename, string Description, List<GraphNode> GraphNodes, List<GraphEdge> GraphEdges ) { var Settings = new XmlWriterSettings(); Settings.Indent = true; Settings.IndentChars = " "; // Figure out all of the custom attribute types we're dealing with var AllAttributes = new Dictionary<string, GraphAttribute>( StringComparer.InvariantCultureIgnoreCase ); int NextAttributeID = 0; foreach( var GraphNode in GraphNodes ) { foreach( var AttributeName in GraphNode.Attributes.Keys ) { var AttributeValue = GraphNode.Attributes[ AttributeName ]; string AttributeTypeName; if( AttributeValue.GetType() == typeof(int) ) { AttributeTypeName = "integer"; } else if( AttributeValue.GetType() == typeof(float) ) { AttributeTypeName = "float"; } else if( AttributeValue.GetType() == typeof(double) ) { AttributeTypeName = "double"; } else if( AttributeValue.GetType() == typeof(string) ) { AttributeTypeName = "string"; } else if( AttributeValue.GetType() == typeof(bool) ) { AttributeTypeName = "boolean"; } else { // No other types supported yet! throw new InvalidOperationException( "Unsupported attribute data type encountered on graph node!" ); } GraphAttribute Attribute; if( !AllAttributes.TryGetValue( AttributeName, out Attribute ) ) { Attribute = new GraphAttribute(); Attribute.ID = NextAttributeID++; Attribute.Name = AttributeName; Attribute.TypeName = AttributeTypeName; AllAttributes[ AttributeName ] = Attribute; } else { if( !Attribute.TypeName.Equals( AttributeTypeName ) ) { throw new InvalidOperationException( "Multiple graph nodes with the same attribute name but different types encountered!"); } } } } using( var Writer = XmlWriter.Create( Filename, Settings ) ) { // NOTE: The GEXF XML format is defined here: http://gexf.net/1.2draft/gexf-12draft-primer.pdf var GEXFNamespace = "http://www.gexf.net/1.2-draft"; var SchemaNamespace = "http://www.w3.org/2001/XMLSchema-instance"; var VizNamespace = "http://www.gexf.net/1.2draft/viz"; Writer.WriteStartElement( "gexf", GEXFNamespace ); Writer.WriteAttributeString( "xmlns", "xsi", null, SchemaNamespace ); Writer.WriteAttributeString( "schemaLocation", SchemaNamespace, "http://www.gexf.net/1.2draft http://www.gexf.net/1.2draft/gexf.xsd" ); Writer.WriteAttributeString( "xmlns", "viz", null, VizNamespace ); Writer.WriteAttributeString( "version", "1.2" ); Writer.WriteStartElement( "meta" ); { Writer.WriteAttributeString( "creator", "UnrealBuildTool" ); Writer.WriteAttributeString( "description", Description ); } Writer.WriteEndElement(); // meta { Writer.WriteStartElement( "graph" ); { Writer.WriteAttributeString( "mode", "static" ); Writer.WriteAttributeString( "defaultedgetype", "directed" ); if( AllAttributes.Count > 0 ) { Writer.WriteStartElement( "attributes" ); { // @todo: Add support for edge attributes, not just node attributes Writer.WriteAttributeString( "class", "node" ); // Node attributes, not edges! foreach( var Attribute in AllAttributes.Values ) { Writer.WriteStartElement( "attribute" ); { Writer.WriteAttributeString( "id", Attribute.ID.ToString() ); Writer.WriteAttributeString( "title", Attribute.Name ); Writer.WriteAttributeString( "type", Attribute.TypeName ); } Writer.WriteEndElement(); // attribute } // @todo: Add support for attribute type default values } Writer.WriteEndElement(); // attributes } Writer.WriteStartElement( "nodes" ); { foreach( var GraphNode in GraphNodes ) { Writer.WriteStartElement( "node" ); { Writer.WriteAttributeString( "id", GraphNode.Id.ToString() ); Writer.WriteAttributeString( "label", GraphNode.Label ); Writer.WriteStartElement( "color", VizNamespace ); { Writer.WriteAttributeString( "r", ((int)( GraphNode.Color.R * 255.0f )).ToString() ); Writer.WriteAttributeString( "g", ((int)( GraphNode.Color.G * 255.0f )).ToString() ); Writer.WriteAttributeString( "b", ((int)( GraphNode.Color.B * 255.0f )).ToString() ); Writer.WriteAttributeString( "a", GraphNode.Color.A.ToString() ); } Writer.WriteEndElement(); // viz:color Writer.WriteStartElement( "size", VizNamespace ); { Writer.WriteAttributeString( "value", GraphNode.Size.ToString() ); } Writer.WriteEndElement(); // viz:size Writer.WriteStartElement( "shape", VizNamespace ); { // NOTE: Valid shapes are: disc, square, triangle, diamond, image Writer.WriteAttributeString( "value", "disc" ); } Writer.WriteEndElement(); // viz:shape if( GraphNode.Attributes.Count > 0 ) { Writer.WriteStartElement( "attvalues" ); { foreach( var AttributeHashEntry in GraphNode.Attributes ) { var AttributeName = AttributeHashEntry.Key; var AttributeValue = AttributeHashEntry.Value; var Attribute = AllAttributes[ AttributeName ]; Writer.WriteStartElement( "attvalue" ); { Writer.WriteAttributeString( "for", Attribute.ID.ToString() ); Writer.WriteAttributeString( "value", AttributeValue.ToString() ); } Writer.WriteEndElement(); } } Writer.WriteEndElement(); // attvalues } } Writer.WriteEndElement(); // node } } Writer.WriteEndElement(); // nodes Writer.WriteStartElement( "edges" ); { foreach( var GraphEdge in GraphEdges ) { Writer.WriteStartElement( "edge" ); { Writer.WriteAttributeString( "id", GraphEdge.Id.ToString() ); Writer.WriteAttributeString( "source", GraphEdge.Source.Id.ToString() ); Writer.WriteAttributeString( "target", GraphEdge.Target.Id.ToString() ); Writer.WriteAttributeString( "weight", GraphEdge.Weight.ToString() ); Writer.WriteStartElement( "color", VizNamespace ); { Writer.WriteAttributeString( "r", ((int)( GraphEdge.Color.R * 255.0f )).ToString() ); Writer.WriteAttributeString( "g", ((int)( GraphEdge.Color.G * 255.0f )).ToString() ); Writer.WriteAttributeString( "b", ((int)( GraphEdge.Color.B * 255.0f )).ToString() ); Writer.WriteAttributeString( "a", GraphEdge.Color.A.ToString() ); } Writer.WriteEndElement(); // viz:color Writer.WriteStartElement( "thickness", VizNamespace ); { Writer.WriteAttributeString( "value", GraphEdge.Thickness.ToString() ); } Writer.WriteEndElement(); // viz:thickness Writer.WriteStartElement( "shape", VizNamespace ); { // NOTE: Valid shapes are: solid, dotted, dashed, double Writer.WriteAttributeString( "value", "solid" ); } Writer.WriteEndElement(); // viz:shape } Writer.WriteEndElement(); // edge } } Writer.WriteEndElement(); // nodes } Writer.WriteEndElement(); // graph } Writer.WriteEndElement(); // gexf Writer.Flush(); } }