Преглед изворни кода

Preparing phylogeny module or release

Stephen Ficklin пре 8 година
родитељ
комит
ae14e34547

+ 0 - 155
tripal_phylogeny/includes/tripal_phylogeny.chado_node.inc

@@ -749,161 +749,6 @@ function chado_phylotree_node_access($node, $op, $account) {
   }
 }
 
-/**
- * phylotree_by_name
- *
- * Lookup tree by name, and redirect to that drupal node. This could
- * also be done for example by URL aliases, for example using pathauto
- * module.
- *
- * @param int $phylotree_id
- * @return string json
- * @ingroup tripal_phylogeny
- */
-function phylotree_by_name($name) {
-
-    $sql = <<<SQL
- SELECT cp.nid as node_id
- FROM chado.phylotree t
- LEFT JOIN chado_phylotree cp on cp.phylotree_id = t.phylotree_id
- WHERE t.name = :name
-SQL;
-
-    $args = array(':name' => $name);
-    $result = chado_query( $sql, $args );
-    $nid = $result->fetchField();
-    drupal_goto("node/$nid");
-}
-
-/**
- * phylotree_json
- *  Get json representation of a phylotree id. See menu item for JSON service
- *  endpoint.
- *
- * @param int $phylotree_id
- * @return string json
- * @ingroup tripal_phylogeny
- */
-function phylotree_json($phylotree_id) {
-
-  $phylotree = chado_generate_var('phylotree', array('phylotree_id' => $phylotree_id));
-
-  // This SQL gets all of the phylonodes for a given tree as well as the
-  // features and organisms with which it is assocaited.  Each phylonode
-  // can be associated with an orgnaism in one of two ways: 1) via a
-  // feature linked by the phylonode.feature_id field or 2) via a
-  // a record in the phylonde_organsim table.  Therefore both types of
-  // organism records are returned in the query below, but those
-  // retrieved via a FK link on features are prefixed with 'fo_'.
-  $sql = "
-    SELECT
-      n.phylonode_id, n.parent_phylonode_id, n.label AS name, n.distance AS length,
-      f.feature_id, f.name AS feature_name,
-      cvt.name AS cvterm_name,
-      o.organism_id, o.common_name, o.abbreviation, o.genus, o.species,
-      fo.organism_id AS fo_organism_id, fo.common_name AS fo_common_name,
-      fo.abbreviation AS fo_abbreviation, fo.genus as fo_genus, fo.species AS fo_species,
-      cf.nid AS feature_node_id,
-      fco.nid AS fo_organism_node_id,
-      co.nid AS organism_node_id
-    FROM {phylonode} n
-      LEFT OUTER JOIN {cvterm} cvt              ON n.type_id = cvt.cvterm_id
-      LEFT OUTER JOIN {feature} f               ON n.feature_id = f.feature_id
-      LEFT OUTER JOIN public.chado_feature cf   ON cf.feature_id = f.feature_id
-      LEFT OUTER JOIN {organism} fo             ON f.organism_id = fo.organism_id
-      LEFT OUTER JOIN public.chado_organism fco ON fco.organism_id = fo.organism_id
-      LEFT OUTER JOIN {phylonode_organism} po   ON po.phylonode_id = n.phylonode_id
-      LEFT OUTER JOIN {organism} o              ON PO.organism_id = o.organism_id
-      LEFT OUTER JOIN public.chado_organism co  ON co.organism_id = o.organism_id
-    WHERE n.phylotree_id = :phylotree_id
-  ";
-  $args = array(':phylotree_id' => $phylotree_id);
-  $result = chado_query($sql, $args);
-
-  // Fetch all the phylonodes into an assoc array indexed by phylonode_id.
-  // Convert from resultset record to array, fixing datatypes. chado_query
-  // returns numeric as string and fun stuff like that.
-  $phylonodes = array();
-  $root_phylonode_ref = null;
-
-  // Get the tree properties
-  $root_size = variable_get('tripal_phylogeny_default_root_node_size', 5);
-  $internal_size = variable_get('tripal_phylogeny_default_internal_node_size', 5);
-  $leaf_size = variable_get('tripal_phylogeny_default_leaf_node_size', 5);
-
-
-  foreach ($result as $r) {
-    $phylonode_id = (int) $r->phylonode_id;
-
-    // expect all nodes to have these properties
-    $node = array(
-      'phylonode_id' => $phylonode_id,
-      'parent_phylonode_id' => (int) $r->parent_phylonode_id,
-      'length' => (double) $r->length,
-      'cvterm_name' => $r->cvterm_name
-    );
-
-    // If the nodes are taxonomic then set an equal distnace
-    if ($phylotree->type_id->name == 'taxonomy') {
-        $node['length'] = 0.001;
-    }
-
-    // Other props may exist only for leaf nodes
-    if ($r->name) {
-      $node['name'] = $r->name;
-    }
-    // If this node is associated with a feature then add in the details
-    if ($r->feature_id) {
-      $node['feature_id'] = (int) $r->feature_id;
-      $node['feature_name'] = $r->feature_name;
-      $node['feature_node_id'] = (int) $r->feature_node_id;
-    }
-    // Add in the organism fields when they are available via the
-    // phylonode_organism table.
-    if ($r->organism_id) {
-      $node['organism_id'] = (int) $r->organism_id;
-      $node['common_name'] = $r->common_name;
-      $node['abbreviation'] = $r->abbreviation;
-      $node['genus'] = $r->genus;
-      $node['species'] = $r->species;
-      $node['organism_node_id'] = (int) $r->organism_node_id;
-      // If the node does not have a name but is linked to an organism
-      // then set the name to be that of the genus and species.
-      if (!$r->name) {
-        $node['name'] = $r->genus . ' ' . $r->species;
-      }
-    }
-    // Add in the organism fields when they are available via the
-    // the phylonode.feature_id FK relationship.
-    if ($r->fo_organism_id) {
-      $node['fo_organism_id'] = (int) $r->fo_organism_id;
-      $node['fo_common_name'] = $r->fo_common_name;
-      $node['fo_abbreviation'] = $r->fo_abbreviation;
-      $node['fo_genus'] = $r->fo_genus;
-      $node['fo_species'] = $r->fo_species;
-      $node['fo_organism_node_id'] = (int) $r->fo_organism_node_id;
-    }
-
-    // Add this node to the list, organized by ID.
-    $phylonodes[$phylonode_id] = $node;
-  }
-
-  // Populate the children[] arrays for each node.
-  foreach ($phylonodes as $key => &$node) {
-    if ($node['parent_phylonode_id'] !== 0) {
-      $parent_ref = &$phylonodes[ $node['parent_phylonode_id']];
-      // Append node refernce to children.
-      $parent_ref['children'][] = &$node;
-    }
-    else {
-      $root_phylonode_ref = &$node;
-    }
-  }
-
-  // dump datastructure as json to browser. drupal sets the mime-type correctly.
-  drupal_json_output($root_phylonode_ref);
-}
-
 /**
  *  Phylotree feature summary.
  *

+ 16 - 1
tripal_phylogeny/includes/tripal_phylogeny.taxonomy.inc

@@ -1,5 +1,20 @@
 <?php
 
+/**
+ * Generates a page that contains the taxonomy view.
+ */
+function tripal_phylogeny_taxonomy_view() {
+  $values = array(
+    'type_id' => array(
+      'name' => 'taxonomy',
+    ),
+  );
+  $phylotree = chado_generate_var('phylotree', $values);
+  $node = new stdClass();
+  $node->phylotree = $phylotree;
+
+  return theme('tripal_phylogeny_taxonomic_tree', array('node' => $node));
+}
 /**
  *
  */
@@ -80,7 +95,7 @@ function tripal_phylogeny_ncbi_taxonomy_import($job_id) {
       // Add the taxonomic tree.
       $options = array(
         'name' =>  $site_name . 'Taxonomy Tree',
-        'description' => 'The taxonomic tree of organisms represented in this database.',
+        'description' => 'The taxonomic tree of species present on this site.',
         'leaf_type' => 'taxonomy',
         'analysis_id' => $analysis->analysis_id,
         'tree_file' => '/dev/null',

+ 26 - 13
tripal_phylogeny/theme/js/tripal_phylogeny.js

@@ -6,6 +6,7 @@
 
   $(document).ready( function () {
 
+    // Callback function to determine node size.
     var nodeSize = function(d) {
       var size;
       if (d.cvterm_name == "phylo_root") {
@@ -17,40 +18,43 @@
       if (d.cvterm_name == "phylo_leaf") {
         size = treeOptions['leaf_node_size']; 
       }
-        return size;
+      return size;
     }
 
-    // function to generate color based on the organism genus and species
-    // on graph node d
+    // Callback function to determine the node color.
     var organismColor = function(d) {
       var color = null;
       if (d.fo_genus) {
         color = organismColors[d.fo_genus + ' ' + d.fo_species];
       }
-      if (color) { return color; }
-      else { return 'grey'; }
+      if (color) { 
+        return color; 
+      }
+      else { 
+        return 'grey'; 
+      }
     };
 
-    // callback for mouseover event on graph node d
+    // Callback for mouseover event on graph node d.
     var nodeMouseOver = function(d) {
-      var el =$(this);
+      var el = $(this);
       el.attr('cursor', 'pointer');
       var circle = el.find('circle');
       // highlight in yellow no matter if leaf or interior node
       circle.attr('fill', 'yellow');
-      if(! d.children) {
+      if(!d.children) {
         // only leaf nodes have descriptive text
         var txt = el.find('text');
         txt.attr('font-weight', 'bold');
       }
     };
     
-    // callback for mouseout event on graph node d
+    // Callback for mouseout event on graph node d.
     var nodeMouseOut = function(d) {
       var el = $(this);
       el.attr('cursor', 'default');
       var circle = el.find('circle');
-      if(! d.children) {
+      if(!d.children) {
         // restore the color based on organism id for leaf nodes
         circle.attr('fill', organismColor(d));
         var txt = el.find('text');
@@ -62,7 +66,7 @@
       }
     };
     
-    // callback for mousedown/click event on graph node d
+    // Callback for mousedown/click event on graph node d.
     var nodeMouseDown = function(d) {
       var el = $(this);
       var title = (! d.children ) ? d.name : 'interior node ' + d.phylonode_id;
@@ -76,15 +80,23 @@
         }
       }
       else {
+        // If this node is not associated with a feature but it has an 
+        // organism node then this is a taxonomic node and we want to
+        // link it to the organism page.
+        if (!d.feature_id && d.organism_node_id) {
+          window.location.replace(baseurl + '/node/' + d.organism_node_id);
+        }
         // leaf node
       }
     };
 
+    // AJAX function for retrieving the tree data.
     $.getJSON(phylotreeDataURL, function(treeData) {
       displayData(treeData);
-      $('.phylogram-ajax-loader').remove();
+      $('.phylogram-ajax-loader').hide();
     });
 
+    // Creates the tree using the d3.phylogram.js library.
     function displayData(treeData) {
       height = graphHeight(treeData);
       d3.phylogram.build('#phylogram', treeData, {
@@ -94,7 +106,8 @@
         'size' : nodeSize,
         'nodeMouseOver' : nodeMouseOver,
         'nodeMouseOut' : nodeMouseOut,
-        'nodeMouseDown' : nodeMouseDown
+        'nodeMouseDown' : nodeMouseDown,
+        'skipTicks' : treeOptions['skipTicks']
       });
     }
 

+ 2 - 1
tripal_phylogeny/theme/templates/tripal_phylogeny_taxonomic_tree.tpl.php

@@ -2,7 +2,8 @@
 $phylotree = $variables['node']->phylotree;
 $phylotree = chado_expand_var($phylotree,'field','phylotree.comment');
 
-if ($phylotree->type_id->name == "taxonomy" and $phylotree->has_nodes) { ?>
+if ($phylotree->type_id->name == "taxonomy" and $phylotree->has_nodes) {
+  print $phylotree->comment ?>
   <div id="phylogram">
     <!-- d3js will add svg to this div, and remove the loader gif prefix with / for absolute url --><?php
     $ajax_loader = url(drupal_get_path('module', 'tripal_phylogeny') . '/theme/images/ajax-loader.gif'); ?>

+ 45 - 56
tripal_phylogeny/theme/tripal_phylogeny.theme.inc

@@ -1,11 +1,13 @@
 <?php
-/**
- * Implements hook_preprocess_hook()
- *
- * @param $variables
- */
-function tripal_phylogeny_preprocess_tripal_phylogeny_phylogram(&$variables) {
-  $phylotree = $variables['node']->phylotree;
+
+function tripal_phylogeny_prepare_tree_viewer($phylotree) {
+
+  // Don't prepare for viewing more than once.
+  if (property_exists($phylotree, 'prepared_to_view') and
+      $phylotree->prepared_to_view == TRUE) {
+    return;
+  }
+
   $module_path = drupal_get_path('module', 'tripal_phylogeny');
 
   drupal_add_js('https://d3js.org/d3.v3.min.js', 'external');
@@ -16,12 +18,19 @@ function tripal_phylogeny_preprocess_tripal_phylogeny_phylogram(&$variables) {
 
   drupal_add_library('system', 'ui.dialog');
 
+  // Don't show tick marks for the taxonomy tree.
+  $skip_ticks = 0;
+  if ($phylotree->type_id->name == 'taxonomy') {
+    $skip_ticks = 1;
+  }
+
   // Get the tree options as set by the administrator.
   $options = json_encode(array(
     'phylogram_width' => variable_get('tripal_phylogeny_default_phylogram_width', 350),
     'root_node_size' => variable_get('tripal_phylogeny_default_root_node_size', 3),
     'interior_node_size' => variable_get('tripal_phylogeny_default_interior_node_size', 1),
     'leaf_node_size' => variable_get('tripal_phylogeny_default_leaf_node_size', 6),
+    'skipTicks' => $skip_ticks,
   ));
 
   // Get the node colors as set by the administrator.
@@ -35,62 +44,21 @@ function tripal_phylogeny_preprocess_tripal_phylogeny_phylogram(&$variables) {
   $colors = json_encode($colors);
 
   // Add javascript data needed for this tree.
-  drupal_add_js('
-      // var having URL of json data source for charting
-      var phylotreeDataURL =  baseurl + "/chado_phylotree/' . $phylotree->phylotree_id . '/json";
+  drupal_add_js(
+    ' // var having URL of json data source for charting
+      var phylotreeDataURL =  baseurl + "/ajax/chado_phylotree/' . $phylotree->phylotree_id . '/json";
+
       // var with path to our theme, for use by javascript functions.
-      var pathToTheme = "/' . $module_path . '/theme";
+      var pathToTheme = baseurl + "/' . $module_path . '/theme";
+
       // var with custom options
       var treeOptions = ' . $options . ';
-      // var with the organism colors
-      var organismColors = ' . $colors . ';
-    ',
-    'inline'
-  );
-  tripal_phylogeny_set_tree_vars($phylotree);
-}
-
-/**
- * Implements hook_preprocess_hook()
- *
- * @param $variables
- */
-function tripal_phylogeny_preprocess_tripal_phylogeny_taxonomic_tree(&$variables) {
-  $phylotree = $variables['node']->phylotree;
-  $module_path = drupal_get_path('module', 'tripal_phylogeny');
-
-  drupal_add_js('https://d3js.org/d3.v4.js', 'external');
-
-  drupal_add_js("$module_path/theme/js/d3.phylogram.js");
-  drupal_add_js("$module_path/theme/js/tripal_phylogeny.js");
-  drupal_add_css("$module_path/theme/css/tripal_phylogeny.css");
-
-  drupal_add_library('system', 'ui.dialog');
 
-  drupal_add_js('
-    // var having URL of json data source for charting
-    var phylotreeDataURL = baseurl + "/chado_phylotree/' . $phylotree->phylotree_id . '/json";
-    // var with path to our theme, for use by javascript functions.
-    var pathToTheme = "/' . $module_path . '/theme";',
+      // var with the organism colors
+      var organismColors = ' . $colors . ';',
     'inline'
   );
-  tripal_phylogeny_set_tree_vars($phylotree);
-}
-
-/**
- * Implements hook_preprocess_hook();
- *
- * @param $variables
- */
-function tripal_phylogeny_preprocess_tripal_phylogeny_organisms(&$variables) {
-  $phylotree = $variables['node']->phylotree;
-  tripal_phylogeny_set_tree_vars($phylotree);
-}
 
-/**
- * Helper function for the preprocess hooks that adds a 'has_nodes' to the phylotree object.
- */
-function tripal_phylogeny_set_tree_vars(&$phylotree) {
   if (!property_exists($phylotree, 'has_nodes')) {
     // If the nodes haven't loaded then set a value so the template can
     // choose not to show the phylogram.
@@ -110,4 +78,25 @@ function tripal_phylogeny_set_tree_vars(&$phylotree) {
     ";
     $phylotree->has_features = chado_query($sql, array(':phylotree_id' => $phylotree->phylotree_id))->fetchField();
   }
+
+  $phylotree->prepared_to_view = TRUE;
+}
+/**
+ * Implements hook_preprocess_hook()
+ *
+ * @param $variables
+ */
+function tripal_phylogeny_preprocess_tripal_phylogeny_phylogram(&$variables) {
+  $phylotree = $variables['node']->phylotree;
+  tripal_phylogeny_prepare_tree_viewer($phylotree);
+}
+
+/**
+ * Implements hook_preprocess_hook()
+ *
+ * @param $variables
+ */
+function tripal_phylogeny_preprocess_tripal_phylogeny_taxonomic_tree(&$variables) {
+  $phylotree = $variables['node']->phylotree;
+  tripal_phylogeny_prepare_tree_viewer($phylotree);
 }

+ 138 - 23
tripal_phylogeny/tripal_phylogeny.module

@@ -114,23 +114,7 @@ function tripal_phylogeny_menu() {
     'access arguments' => array('administer tripal phylotree'),
     'type' => MENU_CALLBACK,
   );
-
-  $items['chado_phylotree/%'] = array(
-    'page callback' => 'phylotree_by_name',
-    'page arguments' => array(1),
-    // allow all anonymous http clients
-    'access callback' => TRUE
-  );
-
-   // create a route for viewing json of all phylonodes having this phylotree_id
-   $items['chado_phylotree/%/json'] = array(
-    'page callback' => 'phylotree_json',
-    'page arguments' => array(1),
-     // allow all anonymous http clients
-    'access callback' => TRUE
-   );
-
-   // Data Loaders
+  // Data Loaders
    $items['admin/tripal/loaders/newic_phylotree_loader'] = array(
      'title' => 'Phylogenetic Trees (Newic format)',
      'description' => 'Loads phylogenetic trees in Newic format. (Redirects to create a phylogenetic tree content type)',
@@ -151,15 +135,22 @@ function tripal_phylogeny_menu() {
    );
 
    $items['taxonomy_view'] = array(
-     'title' => 'Site Taxonomy',
-     'description' => 'A taxonomy viewer for the species on this site.',
-     'page callback' => 'drupal_get_form',
-     'page arguments' => array('tripal_phylogeny_taxonomy_viewer'),
+     'title' => 'Taxonomy',
+     'description' => 'Taxonomic view of the species available on this site.',
+     'page callback' => 'tripal_phylogeny_taxonomy_view',
      'access arguments' => array('access taxonomy content'),
      'file' => '/includes/tripal_phylogeny.taxonomy.inc',
      'type' => MENU_NORMAL_ITEM,
    );
 
+   // create a route for viewing json of all phylonodes having this phylotree_id
+   $items['ajax/chado_phylotree/%/json'] = array(
+     'page callback' => 'tripal_phylogeny_ajax_get_tree_json',
+     'page arguments' => array(2),
+     // allow all anonymous http clients
+     'access callback' => TRUE
+   );
+
   return $items;
 }
 
@@ -277,7 +268,131 @@ function tripal_phylogeny_help ($path, $arg) {
   }
 }
 
-// hack to suppress the 'Submitted by...' info drupal adds to nodes
-$variables['display_submitted'] = FALSE;
+/**
+ * Get json representation of a phylotree id.
+ *
+ * This function is meant to be called via AJAX.
+ *
+ * @param int $phylotree_id
+ *   the ID of the phylotree node.
+ *
+ * @return string json
+ *
+ * @ingroup tripal_phylogeny
+ */
+function tripal_phylogeny_ajax_get_tree_json($phylotree_id) {
+
+  $phylotree = chado_generate_var('phylotree', array('phylotree_id' => $phylotree_id));
+
+  // This SQL gets all of the phylonodes for a given tree as well as the
+  // features and organisms with which it is assocaited.  Each phylonode
+  // can be associated with an orgnaism in one of two ways: 1) via a
+  // feature linked by the phylonode.feature_id field or 2) via a
+  // a record in the phylonde_organsim table.  Therefore both types of
+  // organism records are returned in the query below, but those
+  // retrieved via a FK link on features are prefixed with 'fo_'.
+  $sql = "
+    SELECT
+      n.phylonode_id, n.parent_phylonode_id, n.label AS name, n.distance AS length,
+      f.feature_id, f.name AS feature_name,
+      cvt.name AS cvterm_name,
+      o.organism_id, o.common_name, o.abbreviation, o.genus, o.species,
+      fo.organism_id AS fo_organism_id, fo.common_name AS fo_common_name,
+      fo.abbreviation AS fo_abbreviation, fo.genus as fo_genus, fo.species AS fo_species,
+      cf.nid AS feature_node_id,
+      fco.nid AS fo_organism_node_id,
+      co.nid AS organism_node_id
+    FROM {phylonode} n
+      LEFT OUTER JOIN {cvterm} cvt              ON n.type_id = cvt.cvterm_id
+      LEFT OUTER JOIN {feature} f               ON n.feature_id = f.feature_id
+      LEFT OUTER JOIN [chado_feature] cf        ON cf.feature_id = f.feature_id
+      LEFT OUTER JOIN {organism} fo             ON f.organism_id = fo.organism_id
+      LEFT OUTER JOIN [chado_organism] fco      ON fco.organism_id = fo.organism_id
+      LEFT OUTER JOIN {phylonode_organism} po   ON po.phylonode_id = n.phylonode_id
+      LEFT OUTER JOIN {organism} o              ON PO.organism_id = o.organism_id
+      LEFT OUTER JOIN [chado_organism] co       ON co.organism_id = o.organism_id
+    WHERE n.phylotree_id = :phylotree_id
+  ";
+  $args = array(':phylotree_id' => $phylotree_id);
+  $result = chado_query($sql, $args);
+
+  // Fetch all the phylonodes into an assoc array indexed by phylonode_id.
+  // Convert from resultset record to array, fixing datatypes. chado_query
+  // returns numeric as string and fun stuff like that.
+  $phylonodes = array();
+  $root_phylonode_ref = null;
+
+  foreach ($result as $r) {
+    $phylonode_id = (int) $r->phylonode_id;
+
+    // expect all nodes to have these properties
+    $node = array(
+      'phylonode_id' => $phylonode_id,
+      'parent_phylonode_id' => (int) $r->parent_phylonode_id,
+      'length' => (double) $r->length,
+      'cvterm_name' => $r->cvterm_name
+    );
+
+    // If the nodes are taxonomic then set an equal distance
+    if ($phylotree->type_id->name == 'taxonomy') {
+      $node['length'] = 0.001;
+    }
+
+    // Other props may exist only for leaf nodes
+    if ($r->name) {
+      $node['name'] = $r->name;
+    }
+    // If this node is associated with a feature then add in the details
+    if ($r->feature_id) {
+      $node['feature_id'] = (int) $r->feature_id;
+      $node['feature_name'] = $r->feature_name;
+      $node['feature_node_id'] = (int) $r->feature_node_id;
+    }
+    // Add in the organism fields when they are available via the
+    // phylonode_organism table.
+    if ($r->organism_id) {
+      $node['organism_id'] = (int) $r->organism_id;
+      $node['common_name'] = $r->common_name;
+      $node['abbreviation'] = $r->abbreviation;
+      $node['genus'] = $r->genus;
+      $node['species'] = $r->species;
+      $node['organism_node_id'] = (int) $r->organism_node_id;
+      // If the node does not have a name but is linked to an organism
+      // then set the name to be that of the genus and species.
+      if (!$r->name) {
+        $node['name'] = $r->genus . ' ' . $r->species;
+      }
+    }
+    // Add in the organism fields when they are available via the
+    // the phylonode.feature_id FK relationship.
+    if ($r->fo_organism_id) {
+      $node['fo_organism_id'] = (int) $r->fo_organism_id;
+      $node['fo_common_name'] = $r->fo_common_name;
+      $node['fo_abbreviation'] = $r->fo_abbreviation;
+      $node['fo_genus'] = $r->fo_genus;
+      $node['fo_species'] = $r->fo_species;
+      $node['fo_organism_node_id'] = (int) $r->fo_organism_node_id;
+    }
+
+    // Add this node to the list, organized by ID.
+    $phylonodes[$phylonode_id] = $node;
+  }
+
+  // Populate the children[] arrays for each node.
+  foreach ($phylonodes as $key => &$node) {
+    if ($node['parent_phylonode_id'] !== 0) {
+      $parent_ref = &$phylonodes[ $node['parent_phylonode_id']];
+      // Append node refernce to children.
+      $parent_ref['children'][] = &$node;
+    }
+    else {
+      $root_phylonode_ref = &$node;
+    }
+  }
+
+  // dump datastructure as json to browser. drupal sets the mime-type correctly.
+  drupal_json_output($root_phylonode_ref);
+}
+