Quantcast
Channel: XML, System.Xml, MSXML and XmlLite forum
Viewing all articles
Browse latest Browse all 935

How to map XML to graph/visualize the TREE OF LIFE?

$
0
0

Hi all

If this is the wrong place to post please let me know :)

THE GOAL

To build a 3D tree of life visualization (see photo below) in Unity for a education project that is real-time intractable like at https://itol.embl.de/itol.cgi (for eg if you mouse over a line it triggers the line changes color, triggers pop-up information from the nodes, etc). 

THE CHALLENGE / OPPORTUNITY?

Understand this is basic for ppl who get data structures and C# and I'm happy to keep pushing. However after a few days, I'm still struggling to converting the XML data into something usable in Unity.

THE QUESTIONS

1) what's the best way to convert phyloXML data into objects I can vizualize as a 3D TREE OF LIFE?

2) Am I the right track (see below)? If no, what am I missing? What mistakes am I making? And what else do I need to consider to make this real-time interactible tree of life? 

WHAT I'VE DONE THUS FAR

From my research, most trees of life are represented in a phyloXML data format (such as https://itol.embl.de/itol.cgi) and happily, I found this phyloXML file which I've been using to form the tree: http://www.wellcometreeoflife.org/resources/tree-of-life-files/?sort=filetype&order=asc#xml

But how to convert this data into a 3d tree that surrounds a user?
My understanding (please correct if there's a better way), is I need to:
1) have some code that parses through the entire XML file and deserializes it into new data objects I can use to build a graph in Unity 
2) use some more code in Unity to instantiate Node and Link objects (from the classes generated in 1) to produce the 3d tree 

Re 1 – I came across this tutorial http://collaboradev.com/2014/03/12/visualizing-3d-network-topologies-using-unity/which was helpful in teaching me that I probably need the StreamReader and XMLDocument classes. However, its data uses a different schema (Graph ML) and after hours of playing around as a c# noob, I've been unable to map it to my tree.xml (phyloXML) data. 
See github of my adaptation of Jason's tutorial and attempt on github here: https://github.com/maxmagna2k/treelife/tree/master/ToL.01

Code (doesn't fn) here but the key parts aimed at mapping XML to node (or in pholoXML speak a 'clade' is lines 55-71):

using UnityEngine;
using System.Collections;
using System.Xml;
using System.IO;
using UnityEngine.UI;
namespace Topology
{
   public class GraphController : MonoBehaviour
   {
       public Node nodePrefab;
       public Link linkPrefab;
       private Hashtable nodes;
       private Hashtable links;
       private Text statusText;
       private int nodeCount = 0;
       private int linkCount = 0;
       private Text nodeCountText;
       private Text linkCountText;
       //Method for loading the GraphML layout file
       private IEnumerator LoadLayout()
       {
           string sourceFile = Application.dataPath + "/Data/tree-added-phylo-xml-schema.xml";
           statusText.text = "Loading file: " + sourceFile;
           //determine which platform to load for
           string xml = null;
           StreamReader sr = new StreamReader(sourceFile);
           xml = sr.ReadToEnd();
           sr.Close();
           XmlDocument xmlDoc = new XmlDocument();
           xmlDoc.LoadXml(xml);
           statusText.text = "Loading Topology";
           int scale = 1;
           XmlElement root = xmlDoc.FirstChild as XmlElement;
           for (int i = 0; i < root.ChildNodes.Count; i++)
           {
               XmlElement xmlGraph = root.ChildNodes[i] as XmlElement;
               for (int j = 0; j < xmlGraph.ChildNodes.Count; j++)
               {
                   XmlElement xmlNode = xmlGraph.ChildNodes[j] as XmlElement;
                   //create nodes
                   if (xmlNode.Name == "clade")
                   {
                       //float x = float.Parse(xmlNode.Attributes["x"].Value) / scale;
                       float y = float.Parse(xmlNode.Attributes["age_mya"].Value) / scale;
                       //float z = float.Parse(xmlNode.Attributes["z"].Value) / scale;
                       Node nodeObject = Instantiate(nodePrefab, new Vector3(0,y,0), Quaternion.identity) as Node;
                       nodeObject.nodeText.text = xmlNode.Attributes["name"].Value;
                       nodeObject.id = xmlNode.Attributes["name"].Value;
                       //skip other node attributes for now (branch_length, age_mya, etc)
                       nodes.Add(nodeObject.id, nodeObject);
                       statusText.text = "Loading Topology: Node " + nodeObject.id;
                       nodeCount++;
                       nodeCountText.text = "Nodes: " + nodeCount;
                   }
                   //create links
                   if (xmlNode.Name == "edge")
                   {
                       Link linkObject = Instantiate(linkPrefab, new Vector3(0, 0, 0), Quaternion.identity) as Link;
                       linkObject.id = xmlNode.Attributes["id"].Value;
                       linkObject.sourceId = xmlNode.Attributes["source"].Value;
                       linkObject.targetId = xmlNode.Attributes["target"].Value;
                       linkObject.status = xmlNode.Attributes["status"].Value;
                       links.Add(linkObject.id, linkObject);
                       statusText.text = "Loading Topology: Edge " + linkObject.id;
                       linkCount++;
                       linkCountText.text = "Edges: " + linkCount;
                   }
                   //every 100 cycles return control to unity
                   if (j % 100 == 0)
                       yield return true;
               }
           }
           //map node edges
           MapLinkNodes();
           statusText.text = "";
       }
       //Method for mapping links to nodes
       private void MapLinkNodes()
       {
           foreach (string key in links.Keys)
           {
               Link link = links[key] as Link;
               link.source = nodes[link.sourceId] as Node;
               link.target = nodes[link.targetId] as Node;
           }
       }
       void Start()
       {
           nodes = new Hashtable();
           links = new Hashtable();
           //initial stats
           nodeCountText = GameObject.Find("NodeCount").GetComponent<Text>();
           nodeCountText.text = "Nodes: 0";
           linkCountText = GameObject.Find("LinkCount").GetComponent<Text>();
           linkCountText.text = "Edges: 0";
           statusText = GameObject.Find("Status").GetComponent<Text>();
           statusText.text = "";
           StartCoroutine(LoadLayout());
       }
   }
}

Going to be great to get this up and running ;)




Viewing all articles
Browse latest Browse all 935

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>